- Docs
- /
- Getting Started
- /
- Theming
Getting Started
Theming
Svelora uses an OKLCH color system with CSS custom properties. Every color automatically adapts to light and dark mode.
Import Theme
Import `svelora/theme.css` in your layout CSS to load all color tokens.
`src/routes/layout.css`
@import 'tailwindcss';
@import 'svelora/theme.css';
@custom-variant dark (&:where(.dark, .dark *));OKLCH Color Space
Svelora uses OKLCH, a perceptually uniform color space. Colors stay more consistent across lightness levels.
/* oklch(lightness chroma hue) */
/* Lightness: 0 (black) -> 1 (white) */
/* Chroma: 0 (gray) -> 0.4 (vivid) */
/* Hue: 0-360 (color wheel angle) */
--color-primary: oklch(0.546 0.245 262.88);
/* bright vivid blue */Color Tokens
Each semantic color provides base, on-color, container, and on-container tokens.
Each color provides `--color-{name}`, `--color-on-{name}`, `--color-{name}-container`, and `--color-on-{name}-container`.
Surface System
Surface tokens provide depth-based backgrounds for pages, cards, dialogs, drawers, and popovers.
Borders & text
`text-on-surface-variant`
Using in Tailwind
<!-- Background colors -->
<div class="bg-primary">Primary background</div>
<div class="bg-surface-container">Elevated surface</div>
<!-- Text colors -->
<p class="text-on-surface">Primary text</p>
<p class="text-on-surface/60">Muted text (60% opacity)</p>
<!-- Border colors -->
<div class="border-outline-variant">Subtle border</div>
<!-- Combined -->
<div class="bg-primary-container text-on-primary-container">
Tinted container
</div>Customizing Colors
Override CSS custom properties to customize any color. Define both light and dark values.
`src/routes/layout.css`
@import 'tailwindcss';
@import 'svelora/theme.css';
@custom-variant dark (&:where(.dark, .dark *));
:root {
--color-primary: oklch(0.60 0.20 280);
--color-on-primary: oklch(1 0 0);
--color-primary-container: oklch(0.90 0.05 280);
--color-on-primary-container: oklch(0.25 0.10 280);
}
.dark {
--color-primary: oklch(0.75 0.15 280);
--color-on-primary: oklch(0.20 0.05 280);
--color-primary-container: oklch(0.30 0.08 280);
--color-on-primary-container: oklch(0.90 0.05 280);
}Always override both `:root` and `.dark` values to preserve contrast in every theme.
Border Radius
:root {
--radius-xs: 0.125rem;
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
--radius-2xl: 1rem;
--radius-3xl: 1.5rem;
--radius-full: 9999px;
}Fonts Provider
Use the `Fonts` provider to load either Google Fonts or local font files and map them to Svelora variables such as `--font-sans-family`, `--font-heading-family`, and `--font-mono-family`.
Poppins heading
Inter body copy is applied through `--font-sans-family`.
const family = 'font-mono';
`src/routes/+layout.svelte`
<script lang="ts">
import { Fonts } from 'svelora';
import { ModeWatcher } from 'mode-watcher';
import 'svelora/theme.css';
let { children } = $props();
</script>
<Fonts
families={[
{ name: 'Inter', variable: '--font-sans-family', weights: [400, 500, 600, 700] },
{ name: 'Poppins', variable: '--font-heading-family', weights: [600, 700] },
{ name: 'JetBrains Mono', variable: '--font-mono-family', weights: [400, 500, 700] }
]}
/>
<ModeWatcher />
{@render children?.()}`src/svelora.config.ts`
import { defineConfig } from 'svelora';
defineConfig({
fonts: {
families: [
{ name: 'Inter', variable: '--font-sans-family', weights: [400, 500, 600, 700] },
{ name: 'Poppins', variable: '--font-heading-family', weights: [600, 700] },
{ name: 'JetBrains Mono', variable: '--font-mono-family', weights: [400, 500, 700] },
{
provider: 'local',
name: 'Sarabun',
variable: '--font-sarabun-family',
sources: [{ src: '/fonts/Sarabun-Regular.woff2', format: 'woff2', weight: 400 }]
}
]
}
});Top-level config uses `fonts`, `fonts: false` disables defaults, `font-heading` is available as a utility class, and `font-mono` automatically uses `--font-mono-family`.
Custom class: `font-sarabun`
<script lang="ts">
import { Fonts } from 'svelora';
</script>
<Fonts
families={[
{
provider: 'local',
name: 'Sarabun',
variable: '--font-sarabun-family',
sources: [{ src: '/fonts/Sarabun-Regular.woff2', format: 'woff2', weight: 400 }]
}
]}
/>
<p class="font-sarabun">Hello Sarabun</p>Svelora now exposes `font-sarabun` through theme tokens, so you can use it directly in classes across the project after mapping `--font-sarabun-family`.
Mode Watcher (Dark Mode)
Svelora uses mode-watcher to handle dark mode seamlessly without a flash of unstyled content (FOUC).
You must install it as a peer dependency: npm install mode-watcher
Place the <ModeWatcher /> component at the root of your app (in +layout.svelte). You can customize its behavior using props:
<!-- Default behavior (syncs with system, saves to localStorage) -->
<ModeWatcher />
<!-- Force dark mode by default -->
<ModeWatcher defaultMode="dark" />
<!-- Disable saving to localStorage (always use system preference) -->
<ModeWatcher track={false} />You can also import mode and toggleMode directly from mode-watcher to build your own theme toggle buttons.
Global Configuration
Use `defineConfig` to set library-wide defaults for variants, slots, and icons.
`src/lib/config.ts`
import { defineConfig } from 'svelora';
defineConfig({
button: {
defaultVariants: { variant: 'outline', size: 'md' },
slots: { base: 'rounded-full' }
},
avatar: {
defaultVariants: { size: 'lg' },
slots: { root: 'ring-2 ring-primary' }
},
icons: {
loading: 'svg-spinners:ring-resize',
close: 'lucide:x',
chevronDown: 'lucide:chevron-down',
chevronRight: 'lucide:chevron-right',
check: 'lucide:check',
light: 'lucide:sun',
dark: 'lucide:moon'
}
});Per-Component Overrides
Every component accepts a `ui` prop to override specific slot classes on individual instances.
<Button
ui={{ base: 'rounded-full shadow-lg', label: 'uppercase' }}
label="Custom Button"
/>
<Card ui={{ root: 'shadow-xl', header: 'bg-primary/5' }}>
{#snippet header()}
<h3>Styled Card</h3>
{/snippet}
Content here
</Card>