Skip to main content

Custom Styling

Override default component styles across your entire app using StyleProvider and tailwind-variants.

How it works

The Button component reads its variant styles from Svelte context. StyleProvider.Root sets that context. If no provider is present, built-in defaults are used.

Step 1: Create a styling config

Create a file (e.g., $lib/library/styling.ts) that extends the default variants with your overrides:

import { tv } from 'tailwind-variants'
import { Button } from '@repo/components'

export const buttonVariants = tv({
extend: Button.buttonVariants,
variants: {
variant: {
...Button.buttonVariants.variants.variant,
default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow',
outline: 'border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm',
ghost: 'text-primary bg-transparent hover:bg-accent hover:text-accent-foreground shadow-none',
link: 'bg-transparent hover:bg-transparent shadow-none text-primary underline-offset-4 hover:underline',
cta: 'bg-secondary text-secondary-foreground hover:bg-secondary/90 shadow-sm',
},
},
})

You only need to list the variants you want to change. Unlisted variants keep their defaults.

Step 2: Apply via StyleProvider

Wrap your root layout with StyleProvider.Root, passing your custom variants:

<!-- routes/+layout.svelte -->
<script lang="ts">
import { buttonVariants } from '$lib/library/styling'
import { StyleProvider } from '@repo/components'
import '../app.css'

const { children } = $props()
</script>

<StyleProvider.Root buttonVariants={buttonVariants}>
{@render children?.()}
</StyleProvider.Root>

Every Button (and components that use Button internally, like ConfirmationButton, InsertHeader, DetailHeader) will now use your custom styles.

How the context works

Internally, the Button component calls:

const buttonVariants = getContext<ButtonVariants>('buttonVariants') || defaultButtonVariants

StyleProvider.Root calls setContext('buttonVariants', buttonVariants), making your overrides available to the entire component tree beneath it.

Extending sizes

You can also override or add size variants:

export const buttonVariants = tv({
extend: Button.buttonVariants,
variants: {
size: {
...Button.buttonVariants.variants.size,
xl: 'min-h-12 px-10 text-base',
},
},
})

Scoped overrides

You can nest multiple StyleProvider.Root wrappers for section-specific styling:

<StyleProvider.Root buttonVariants={adminButtonVariants}>
<!-- Admin section uses different button styles -->
{@render adminContent?.()}
</StyleProvider.Root>