fuz_css: semantic styles (classless element defaults), style variables (design tokens as CSS custom properties), and optional utility classes generated per-project with only used classes.
Import CSS in +layout.svelte (src/routes). First import is universal;
others as needed:
import '$routes/fuz.css'; // generated bundled CSS (all projects)
import '@fuzdev/fuz_code/theme.css'; // package-specific themes (if any)
import '$routes/style.css'; // project-specific global styles (app projects)$routes resolves to src/routes in SvelteKit. Library/tool repos
(fuz_css, fuz_ui, gro, etc.) often import only fuz.css. Application repos
(fuz_template, fuz_blog, zzz, etc.) typically use all three.
Most consumer projects have an identical src/routes/fuz.gen.css.ts:
import {gen_fuz_css} from '@fuzdev/fuz_css/gen_fuz_css.js';
export const gen = gen_fuz_css();No custom options needed — default bundled mode with tree-shaking handles
everything. Run gro gen to regenerate after adding new classes.
fuz_css itself uses gen_fuz_css({additional_variables: 'all'}) to include all
variables for its docs site demos.
Vite plugin alternative: For non-SvelteKit projects (Svelte, React, Preact, Solid):
// vite.config.ts
import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.js';
export default defineConfig({plugins: [vite_plugin_fuz_css()]});
// main.ts
import 'virtual:fuz.css';The Vite plugin supports HMR — source changes automatically trigger CSS regeneration.
style.cssProject-specific global styles in src/routes/style.css:
- Custom element overrides (e.g., heading fonts, textarea styling) - Patterns being prototyped before upstreaming to fuz_css - App-specific layout (e.g., sidebar widths, primary nav height)
Keep minimal — most apps have near-empty style.css files.
| Layer | File | Purpose |
| ------------------ | ----------- | --------------------------------------------------------- |
| 1. Semantic styles | style.css | Reset + element defaults (buttons, inputs, forms, tables) |
| 2. Style variables | theme.css | 600+ design tokens as CSS custom properties |
| 3. Utility classes | fuz.css | Optional, generated per-project with only used classes |
style.css styles HTML elements without classes using low-specificity :where()
selectors. Elements get sensible defaults automatically.
Key behaviors:
- Flow margins: Block elements (p, ul, ol, form, fieldset,
table, textarea, etc.) get margin-bottom: var(--flow_margin, var(--space_lg))
unless :last-child
- Row margin reset: .row > * resets margins to 0 (use gap_* instead)
- Button styling: Fill, border, shadow, hover/active/disabled/selected
states. Hue variants via color_a-color_j classes
- Input styling: Inputs, textareas, selects share consistent sizing and
borders
.unstyled ClassOpts out of opinionated styling (colors, borders, decorative properties) while keeping normalizations (font inheritance, border-collapse):
<ul class="unstyled column gap_xs"> <!-- reset list, use as flex column -->
<a class="unstyled"> <!-- reset link styling -->
<menu class="unstyled row gap_sm"> <!-- reset menu, use as flex row -->Common for navigation menus, custom list components, and links used as buttons. Applied to interactive elements and decorative containers.
.inline ClassForces inline-block display on elements that normally render as block-level, for embedding within paragraph text:
<p>Click <button class="inline">here</button> to continue.</p>
<p>Enter your <input class="inline" /> name.</p>Applies to code, input, textarea, select, and button. These elements
also get inline-block automatically when nested inside <p> tags (no class
needed).
Defined in TypeScript, rendered to CSS. Each can have light and/or dark
values.
10 hues with semantic roles:
- a (primary/blue), b (success/green), c (error/red), d
(secondary/purple), e (tertiary/yellow)
- f (muted/brown), g (decorative/pink), h (caution/orange), i
(info/cyan), j (flourish/teal)
Intensity scale: 13 stops from color_a_00 (lightest) → color_a_50
(base) → color_a_100 (darkest). Steps: 00, 05, 10, 20, 30, 40,
50, 60, 70, 80, 90, 95, 100.
| Prefix | Behavior | Use case |
| ----------- | ------------------------------------------------- | ----------------------------- |
| fg_* | Toward contrast (darkens light, lightens dark) | Foreground overlays that stack |
| bg_* | Toward surface (lightens light, darkens dark) | Background overlays that stack |
| darken_* | Always darkens (agnostic, alpha-based) | Shadows, backdrops |
| lighten_* | Always lightens (agnostic, alpha-based) | Highlights |
| text_* | Opaque, scheme-aware (low=subtle, high=bold) | Text (alpha hurts performance) |
| shade_* | Opaque, tinted neutrals (00→100), scheme-aware | Backgrounds, surfaces |
fg_*/bg_* overlays use alpha and stack when nested (alpha accumulates),
unlike opaque shade_*. Both shade_* and text_* include _min/_max
variants for untinted extremes (pure black/white).
xs5 → xs4 → xs3 → xs2 → xs → sm → md → lg → xl → xl2 →
... → xl15 (23 stops for spacing). Other families use subsets:
- Font sizes: 13 stops (xs-xl9)
- Icon sizes: 7 stops (xs-xl3, in px not rem)
- Border radii: 7 stops (xs3-xl)
- Distances: 5 stops (xs-xl, in px for absolute widths)
- Shadows, line heights: 5 stops (xs-xl)
- border_color_*: Alpha-based tinted borders (00-100 scale)
- shadow_alpha_*: Shadow opacity scale (00-100)
- darken_*/lighten_*: Non-adaptive alpha overlays (00-100)
- border_width_*: Numbered 1-9 (in px)
- duration_*: Numbered 1-6 (0.08s to 3s)
- hue_*: Base hue values for each color (hue_a through hue_j)
- Non-adaptive variants: shade_XX_light/shade_XX_dark and
color_X_XX_light/color_X_XX_dark for fixed appearance regardless of
color scheme
Bundled mode: :root and :root.dark. Runtime theme switching (via
render_theme_style()): selector repeats for higher specificity (default
:root:root and :root:root.dark) to handle unpredictable CSS insertion order.
Colors are HSL-based (OKLCH migration planned).
Three types of utility classes, generated on-demand:
| Type | Example | Purpose |
| --------------------- | ------------------------------------- | ---------------------------- |
| Token classes | .p_md, .color_a_50, .gap_lg | Map to style variables |
| Composite classes | .box, .row, .ellipsis | Multi-property shortcuts |
| Literal classes | .display:flex, .hover:opacity:80% | Arbitrary CSS property:value |
Map directly to style variable values:
- Spacing: p_md, px_lg, mt_xl, gap_sm, mx_auto, m_0
- Text colors: text_70, text_min, color_a_50, color_b_50
- Background colors: shade_00, bg_10, fg_20, darken_30,
bg_a_50 (hue + intensity background)
- Typography: font_size_lg, font_family_mono, line_height_md,
icon_size_sm
- Layout: width_md, width_atmost_lg, height_xl, top_sm,
inset_md
- Borders: border_radius_xs, border_width_2, border_color_30,
border_color_a_50
- Shadows: shadow_md, shadow_top_md, shadow_bottom_lg,
shadow_inset_xs, shadow_inset_top_sm, shadow_inset_bottom_xs,
shadow_alpha_50, shadow_color_umbra (also _highlight, _glow,
_shroud)
- Hue: hue_a through hue_j (sets --hue variable)
| Class | What it does |
| ------------- | ---------------------------------------------------------------- |
| box | Flex column, items centered, justify centered |
| row | Flex row, align-items centered (overrides box direction) |
| column | Flex column (like box but uncentered) |
| panel | Embedded container with tinted background and border-radius |
| pane | Floating container with opaque background and shadow |
| ellipsis | Block with text truncation (nowrap, overflow hidden, ellipsis) |
| clickable | Hover/focus/active scale transform effects (includes state styles) |
| selectable | Button-like fill with hover/active/selected states |
| chip | Inline label with padding and color_X hue variants |
| menuitem | Full-width list item with icon, title, and selected state |
| icon_button | Square button sized to --input_height (flex-shrink: 0) |
| plain | Transparent border/fill/shadow when not hovered |
| pixelated | Crisp pixel-art image rendering |
| circular | border-radius: 50% |
| chevron | Small right-pointing arrow via CSS border trick |
| sm | Tighter sizing by overriding --font_size, --input_height, etc. |
| md | Default sizing reset (reverses sm in a cascade) |
| mb_flow | Flow-aware margin-bottom (responds to --flow_margin) |
| mt_flow | Flow-aware margin-top (responds to --flow_margin) |
Gotcha: Composites with rulesets (clickable, selectable, menuitem,
plain, chip) already include state styles. hover:clickable is redundant.
property:value maps directly to CSS:
<div class="display:flex justify-content:center gap:var(--space_md)">Space encoding: Use ~ for spaces in multi-value properties:
<div class="margin:0~auto padding:var(--space_sm)~var(--space_lg)">
<div class="width:calc(100%~-~20px)"> <!-- calc requires ~ around +/- -->If you need more than 2-3 ~ characters, use a <style> block instead.
State/responsive/color-scheme styling that inline styles can't do:
<!-- Responsive -->
<div class="display:none md:display:flex">
<!-- State -->
<button class="hover:opacity:80% focus:outline:2px~solid~var(--color_a_50)">
<!-- Color-scheme -->
<div class="box-shadow:var(--shadow_lg) dark:box-shadow:var(--shadow_sm)">
<!-- Pseudo-element (explicit content required) -->
<div class='before:content:"" before:display:block before:width:2rem'>Responsive breakpoints: sm: (40rem), md: (48rem), lg: (64rem), xl:
(80rem), 2xl: (96rem). Max-width variants: max-sm:, max-md:, etc.
Arbitrary: min-width(800px):, max-width(600px):
State modifiers — interaction: hover:, focus:, focus-visible:,
focus-within:, active:, visited:, any-link:, link:, target:
State modifiers — form: disabled:, enabled:, checked:,
indeterminate:, valid:, invalid:, user-valid:, user-invalid:,
required:, optional:, autofill:, blank:, default:, in-range:,
out-of-range:, placeholder-shown:, read-only:, read-write:
State modifiers — structural: first:, last:, only:, odd:, even:,
first-of-type:, last-of-type:, only-of-type:, empty:. Parameterized:
nth-child(2n+1):, nth-last-child(2n):, nth-of-type(2n):,
nth-last-of-type(2n):
State modifiers — UI: fullscreen:, modal:, open:, popover-open:,
paused:, playing:
Media features: print:, motion-safe:, motion-reduce:,
contrast-more:, contrast-less:, portrait:, landscape:, forced-colors:
Ancestor modifiers: dark:, light:
Pseudo-elements: before:, after:, placeholder:, selection:,
marker:, first-letter:, first-line:, cue:, file:, backdrop:
[media]:[ancestor]:[state...]:[pseudo-element]:property:value
<!-- Correct -->
<div class="md:dark:hover:opacity:80%">
<div class="md:hover:before:opacity:100%">
<!-- Multiple states must be alphabetical -->
<button class="focus:hover:outline:2px~solid~blue"> <!-- focus < hover -->
<!-- Wrong - will error -->
<div class="dark:md:hover:opacity:80%"> <!-- ancestor before media -->
<div class="hover:focus:opacity:80%"> <!-- h > f, not alphabetical -->Responsive design typically uses @media queries in component <style>
blocks. Modifier classes are most commonly used for hover/focus states on
literal classes. The full responsive modifier system is available but
convention favors <style> for complex responsive layouts.
Classes extracted via AST parsing at build time:
- class="..." attributes
- class={[...]} and class={{...}} (Svelte 5.16+)
- class:name directives
- clsx(), cn(), cx() calls
- Variables ending in classes/className
For dynamically constructed class strings the extractor can't see statically,
use @fuz-classes comments:
// @fuz-classes opacity:50% opacity:75% opacity:100%
const opacity_classes = [50, 75, 100].map((n) => `opacity:${n}%`);Outside fuz_css's own docs site, AST extraction handles all cases and
@fuz-classes is rarely needed.
@fuz-elements declares HTML elements whose base styles should be included
when not statically detectable:
// @fuz-elements button input textarea@fuz-variables ensures specific theme variables are included even when not
detected by the automatic var(--name) scan:
// @fuz-variables shade_40 text_50Automatic variable detection: CSS variables also detected via regex scan of
var(--name) patterns. Only known theme variables included; unknown silently
ignored. Catches usage in component props like size="var(--icon_size_xs)" that
AST-based extraction would miss.
- Auto-detected classes/elements/variables: Silently skip if unresolvable
- @fuz-classes/@fuz-elements/@fuz-variables entries: Error if
unresolvable (explicitly requested), with typo suggestions via string
similarity
Use Svelte's style: directive for runtime CSS variable overrides:
<div style:--docs_menu_width={width}>
<Alert style:--text_color={color}>
<HueInput style:--hue={value}>Components expose CSS variables as their theming API; consumers override inline.
Dark/light mode controlled by dark/light class on the root element.
style.css includes :root.dark { color-scheme: dark; } and
:root.light { color-scheme: light; }. Theme state management (persistence,
system preference) handled by fuz_ui's ThemeState class and ThemeRoot
component.
Three built-in themes: base, low contrast, high contrast. Custom themes
are arrays of StyleVariable overrides. Theme CSS rendered via
render_theme_style() with higher specificity (default :root:root) to
override bundled theme variables regardless of CSS insertion order.
| Need | Style tag | Utility class | Inline style | | ---------------------- | --------- | ------------- | ------------ | | Style own elements | Best | OK | OK | | Style child components | No | Yes | Limited | | Hover/focus/responsive | Yes | Yes | No | | Runtime dynamic values | No | No | Yes | | IDE autocomplete | Yes | No | Partial |
- Literal classes for primary layout — display:flex, gap_md,
justify-content:center appear in nearly every component
- <style> blocks for complex styling — media queries, animations,
complex selectors, multi-property pseudo-elements
- Token classes for design system values — spacing (p_md, gap_lg)
and colors (color_a_50) maintain consistency; avoid hardcoded values
- Inline style:prop for runtime values — dynamic widths, computed
colors, CSS variable overrides
- Keep class strings manageable — if a <div> accumulates 5+ utility
classes, consider a <style> rule
- <style> for responsive layouts — @media queries in component styles
are conventional; reserve responsive modifiers for simple one-off overrides
| Class | CSS |
| --------- | -------------------------------------------------------------- |
| p_md | padding: var(--space_md) |
| px_lg | padding-left: var(--space_lg); padding-right: var(--space_lg) |
| mt_xl | margin-top: var(--space_xl) |
| mx_auto | margin-left: auto; margin-right: auto |
| gap_sm | gap: var(--space_sm) |
| Class / literal | What it does |
| ------------------------------- | ------------------------------- |
| box | Flex column, centered both axes |
| row | Flex row, align-items centered |
| column | Flex column (uncentered) |
| display:flex | Flexbox |
| flex:1 | Flex grow |
| flex-wrap:wrap | Allow wrapping |
| align-items:center | Cross-axis center |
| justify-content:space-between | Even spacing |
| width:100% | Full width |
| Class | What it does |
| ---------------------- | ----------------------------------- |
| font_size_lg | Large text |
| font_family_mono | Monospace font |
| ellipsis | Truncate with ... |
| text-align:center | Center text |
| white-space:pre-wrap | Preserve whitespace, allow wrapping |
Many token classes set both a CSS property and a cascading custom property, enabling children to inherit:
- font_size_lg sets font-size and --font_size
- color_a_50 sets color and --text_color
- border_color_30 sets border-color and --border_color
- shadow_color_umbra sets --shadow_color
Children of font_size_lg can reference var(--font_size) and get the
inherited value.