MonoStack
Guides

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}>
  {@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>

On this page