classes #

fuz_css has two categories of CSS classes: utilities and builtins. Builtins are baked into the main stylesheet; utility classes have three types, are optional, and require build tool integration.

Utility classes complement semantic styles and style variables. Use them to compose styles across component boundaries, or when you prefer classes to the <style> tag and style attribute. They're optional and generated on-demand to include only what you use.

Compared to TailwindCSS and UnoCSS, fuz_css utility classes follow the grain of semantic HTML rather than being foundational to the design, and the DSL is currently more limited, with interpreters providing a programmatic escape hatch -- see the comparison below.

Compared to the <style> tag, classes:

  • offer shorthand for style variables (p_lg vs padding: var(--space_lg))
  • compose across component boundaries, avoiding fragile :global() selectors
  • let you avoid noisy class names like foo-wrapper and bar-inner

Compared to the style attribute, classes:

  • support powerful modifiers for responsive widths, interaction states (like hover), and dark mode
  • provide more control over specificity
  • compose ergonomically with libraries like clsx, which Svelte supports natively

For cases where classes lack clear advantages, style and <style> are simpler and avoid generating class definitions, which can bloat your builds when overused.

Utility class types
#

Token classes
#

Token classes are technically composite classes with a close relationship to style variables -- each maps design tokens to CSS properties. They're generated programmatically from variant data, making them predictable and systematic. The composites documented below are hand-written and typically represent higher-level semantic concepts. For raw CSS values, use literal classes instead.

<p class="pl_xl3 color_g_5">some token classes</p>

some token classes

Token classes use snake_case because style variables are designed for optional use in JS (imported from variables.ts, but costing nothing otherwise), so each name is consistent across both JS and CSS, instead of converting between kebab-case and camelCase. This also makes token classes visually distinct from literal classes; we find this improves readability.

Spacing

See layout.

  • .p_{xs5-xl15} .p_0
  • .pt_{xs5-xl15} .pt_0
  • .pr_{xs5-xl15} .pr_0
  • .pb_{xs5-xl15} .pb_0
  • .pl_{xs5-xl15} .pl_0
  • .px_{xs5-xl15} .px_0
  • .py_{xs5-xl15} .py_0
  • .m_{xs5-xl15} .m_0 .m_auto
  • .mt_{xs5-xl15} .mt_0 .mt_auto
  • .mr_{xs5-xl15} .mr_0 .mr_auto
  • .mb_{xs5-xl15} .mb_0 .mb_auto
  • .ml_{xs5-xl15} .ml_0 .ml_auto
  • .mx_{xs5-xl15} .mx_0 .mx_auto
  • .my_{xs5-xl15} .my_0 .my_auto
  • .gap_{xs5-xl15}
  • .column_gap_{xs5-xl15}
  • .row_gap_{xs5-xl15}
  • .top_{xs5-xl15}
  • .right_{xs5-xl15}
  • .bottom_{xs5-xl15}
  • .left_{xs5-xl15}
  • .inset_{xs5-xl15}

Sizing

See layout.

  • .width_{xs5-xl15}
  • .height_{xs5-xl15}
  • .width_atmost_{xs-xl} .width_atleast_{xs-xl}
  • .height_atmost_{xs-xl} .height_atleast_{xs-xl}

Colors

See colors.

  • .color_{a-j}_{1-9}
  • .bg .fg
  • .bg_{1-9} .fg_{1-9}
  • .bg_{a-j}_{1-9}
  • .text_color_{0-10}
  • .color_bg .color_fg
  • .color_bg_{1-9} .color_fg_{1-9}
  • .darken_{1-9} .lighten_{1-9}
  • .color_darken_{1-9} .color_lighten_{1-9}
  • .hue_{a-j}

Typography

See typography.

  • .font_family_sans.font_family_serif.font_family_mono
  • .font_size_{xs-xl9}
  • .line_height_{xs-xl}
  • .icon_size_{xs-xl3}

Borders

See borders.

  • .border_color_{1-5}
  • .border_color_{a-j}
  • .border_width_{1-9}
  • .border_radius_{xs3-xl}
  • .border_top_left_radius_{xs3-xl}
  • .border_top_right_radius_{xs3-xl}
  • .border_bottom_left_radius_{xs3-xl}
  • .border_bottom_right_radius_{xs3-xl}
  • .outline_width_{1-9}
  • .outline_width_focus.outline_width_active
  • .outline_color_{1-5}
  • .outline_color_{a-j}

Shadows

See shadows.

  • .shadow_{xs-xl}
  • .shadow_top_{xs-xl}
  • .shadow_bottom_{xs-xl}
  • .shadow_inset_{xs-xl}
  • .shadow_inset_top_{xs-xl}
  • .shadow_inset_bottom_{xs-xl}
  • .shadow_color_{a-j}
  • .shadow_color_highlight.shadow_color_glow.shadow_color_shroud
  • .shadow_alpha_{1-5}

Composite classes
#

Composites let you name and reuse patterns, extending the class system with your own vocabulary. They have four forms: raw CSS declarations, compositions of other classes, a combination of both, or full rulesets as an escape hatch for multi-selector patterns (child selectors, sibling combinators, etc.).

Four definition forms

All four of these produce the same CSS output for .centered, with the ruleset form additionally demonstrating child selectors (> * + *) which can't be expressed with the other forms.

import type {CssClassDefinition} from '@fuzdev/fuz_css/css_class_generation.js'; export const custom_composites: Record<string, CssClassDefinition> = { // 1. `declaration` only - custom CSS properties centered: { declaration: ` display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; `, }, // 2. `composes` only - compose existing token/composite classes centered: { composes: ['box', 'text-align:center'], }, // 3. `composes` + `declaration` - compose then extend centered: { composes: ['box'], declaration: 'text-align: center;', }, // 4. `ruleset` - full CSS with multiple selectors (not composable) centered: { ruleset: ` .centered { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; } /* child selectors, pseudo-classes on children, etc */ .centered > * + * { margin-top: var(--space_md); } `, }, };

Generated CSS:

.centered { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }

And the ruleset form (4) includes the additional selector:

.centered > * + * { margin-top: var(--space_md); }

Nesting

Composites can compose other composites, enabling layered abstractions. Resolution is depth-first: nested composes are fully resolved before the parent's declaration is appended. Circular references are detected and produce an error.

What composes can reference

The composes property resolves referenced classes and combines their declarations. When both composes and declaration are present, the explicit declaration comes last (winning in the cascade for duplicate properties).

  • token classes (p_lg, color_a_5) - resolved to their declarations
  • composites with declaration - the declaration is included
  • composites with composes - recursively resolved
  • unmodified CSS literals (text-align:center, margin:0~auto, --my-var:value) - parsed and included as declarations

Not allowed: Composites with ruleset cannot be referenced in composes because they define their own selectors. Modified classes (like hover:opacity:80% or md:p_lg) cannot be used in composes arrays because they require wrapper selectors (apply them directly in markup instead). The composes property merges declarations into a single rule, but multi-selector patterns like .clickable:hover { ... } cannot be inlined. These limitations may be revisited in the future; feedback is welcome in the discussions.

Modifiers

Composites support modifiers like any other class. For composes and declaration composites, declarations are combined and wrapped. For ruleset composites, modifiers are applied to each selector (with smart conflict detection):

<!-- hover:foo resolves foo's `composes`, applies :hover --> <div class="hover:foo md:dark:foo md:clickable">

Registering composites

Register custom composites with the Vite plugin or Gro generator:

Vite plugin
// vite.config.ts import {custom_composites} from './src/lib/composites.js'; vite_plugin_fuz_css({ class_definitions: custom_composites, }),
Gro generator
// fuz.gen.css.ts import {custom_composites} from '$lib/composites.js'; export const gen = gen_fuz_css({ class_definitions: custom_composites, }); See Usage for more details.

Builtin composites

Composable (can be used in composes arrays):

  • .box - centered flex container
  • .row - horizontal flex row
  • .column - vertical flex column
  • .ellipsis - text overflow ellipsis
  • .pane - pane container
  • .panel - panel container
  • .icon_button - icon button styling
  • .pixelated - crisp pixel-art rendering
  • .circular - 50% border-radius

Ruleset-based (multi-selector, apply directly in markup):

  • .selectable - selectable element styling
  • .clickable - clickable element styling
  • .plain - plain/reset styling
  • .menu_item - menu item styling
  • .chevron - chevron indicator
  • .chip - chip/tag styling

Literal classes
#

Fuz supports an open-ended CSS-literal syntax: property:value. Any CSS property and value works, offering arbitrary styles without a DSL.

<!-- basic syntax: property:value --> <div class="display:flex justify-content:center"> <!-- multi-value properties use ~ for spaces --> <div class="margin:1px~3rem"> <!-- numeric values --> <div class="opacity:50% font-weight:700 z-index:100"> <!-- arbitrary CSS values --> <div class="width:calc(100%~-~20px)"> <!-- custom properties --> <div class="--foo-bg:#abc">

The ~ character represents a space in class names (since CSS classes can't contain spaces). Use it for multi-value properties like margin:1px~auto.

Custom properties work directly: --my-var:value sets the property on the element. This is useful for scoped variables or passing values to child components.

Modifiers
#

Modifiers prefix any class type -- token, composite, or literal -- to apply styles conditionally based on viewport, state, or color scheme. This is what makes utility classes more powerful than inline styles.

Responsive modifiers

Mobile-first breakpoints:

PrefixWidthCSS
sm:40rem (640px)@media (width >= 40rem)
md:48rem (768px)@media (width >= 48rem)
lg:64rem (1024px)@media (width >= 64rem)
xl:80rem (1280px)@media (width >= 80rem)
2xl:96rem (1536px)@media (width >= 96rem)
<!-- stack on mobile, row on medium screens and up --> <div class="display:flex flex-direction:column md:flex-direction:row"> <!-- hide on mobile --> <nav class="display:none md:display:flex"> <!-- max-width variant --> <div class="max-md:display:none"> <!-- arbitrary breakpoints --> <div class="min-width(800px):color:red max-width(600px):color:blue">

State modifiers

Pseudo-class modifiers for interaction and form states:

<button class="hover:opacity:80% focus:outline-color:blue"> <input class="disabled:opacity:50% invalid:border-color:red"> <li class="first:font-weight:bold odd:background-color:lightgray">

Available state modifiers include:

  • interaction: hover: focus: focus-visible: focus-within: active: link: visited: any-link: target:
  • form: autofill: blank: disabled: enabled: checked: indeterminate: required: optional: valid: invalid: user-valid: user-invalid: in-range: out-of-range: placeholder-shown: read-only: read-write: default:
  • structural: first: last: only: first-of-type: last-of-type: only-of-type: odd: even: empty: nth-child(N): nth-last-child(N): nth-of-type(N): nth-last-of-type(N):
  • UI states: fullscreen: modal: open: popover-open:
  • media: playing: paused:

Color-scheme modifiers

Apply styles in dark or light mode:

<div class="shadow_lg dark:shadow_sm"> <div class="color:black light:color:gray">

dark: and light: use :root.dark and :root.light selectors, matching fuz_css's color scheme mechanism.

Pseudo-element modifiers

Style generated content and element parts:

<span class="before:content:'→' before:margin-right:0.5rem"> <input class="placeholder:opacity:50%">

available: before: after: cue: first-letter: first-line: placeholder: selection: marker: file: backdrop:

Media feature modifiers

Accessibility and context-aware styles:

<div class="motion-reduce:animation:none"> <nav class="print:display:none">

available: print: motion-safe: motion-reduce: contrast-more: contrast-less: portrait: landscape: forced-colors:

Combining modifiers
#

Combined modifiers follow a canonical order enforced with errors that guide you. Multiple states must be alphabetical (focus:hover: not hover:focus:) because both generate equivalent CSS -- canonical ordering prevents duplicates.

[media:][ancestor:][...state:][pseudo-element:]class
  1. media - one of md:, lg:, print:, etc
  2. ancestor - one of dark: or light: (likely rtl:/ltr: in the future)
  3. state - any of hover:, focus:, disabled:, etc, sorted alphabetically
  4. pseudo-element - one of before:, after:, placeholder:, etc
<!-- media + ancestor + state --> <div class="md:dark:hover:opacity:83%"> <!-- media + state + pseudo-element --> <div class="md:hover:before:opacity:100%"> <!-- multiple states must be alphabetical --> <button class="focus:hover:outline:2px~solid~blue">

Generated CSS for md:dark:hover:opacity:83%:

@media (width >= 48rem) { :root.dark .md\:dark\:hover\:opacity\:83\%:hover { opacity: 83%; } }

Usage
#

npm i -D @fuzdev/fuz_css

Import the two required stylesheets:

import '@fuzdev/fuz_css/style.css'; import '@fuzdev/fuz_css/theme.css'; // or bring your own

To enable utility classes, use the Vite plugin for a virtual module, or the alternative Gro generator for a static file. Continue reading for the specifics:

Vite plugin
#

The Vite plugin extracts classes and generates CSS on-demand. It works with Svelte and plain HTML/TS/JS out of the box. JSX frameworks (React, Preact, Solid) require the acorn-jsx plugin -- see React and JSX below.

// vite.config.ts import {defineConfig} from 'vite'; import {sveltekit} from '@sveltejs/kit/vite'; import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.js'; export default defineConfig({ plugins: [sveltekit(), vite_plugin_fuz_css()], });

Import the virtual module in your entry file, src/routes/+layout.svelte for SvelteKit:

import '@fuzdev/fuz_css/style.css'; import '@fuzdev/fuz_css/theme.css'; // or bring your own import 'virtual:fuz.css'; // generated on-demand

The plugin extracts classes from files as Vite processes them, including from node_modules dependencies. It supports HMR -- changes to classes in your code trigger automatic CSS updates.

Plugin options

  • acorn_plugins - required for JSX frameworks, e.g. acorn-jsx
  • include_classes - classes to always include (for dynamic patterns that can't be statically extracted)
  • exclude_classes - classes to exclude from output
  • class_definitions - custom class definitions to merge with defaults; can define new classes or override existing ones (see composite classes)
  • include_default_definitions - set to false to use only your own class_definitions, excluding all default token and composite classes
  • class_interpreters - custom interpreters for dynamic class generation; replaces the default interpreters entirely if provided (most users don't need this)
  • filter_file - custom filter for which files to process. Receives (id: string) and returns boolean, e.g. (id) => !id.includes('/fixtures/')
  • on_error - 'log' or 'throw'; defaults to 'throw' in CI, 'log' otherwise
  • on_warning - 'log', 'throw', or 'ignore'; defaults to 'log'
  • cache_dir - cache location; defaults to .fuz/cache/css

TypeScript setup

Add the virtual module declaration, like to vite-env.d.ts:

/// <reference types="vite/client" /> declare module 'virtual:fuz.css' { const css: string; export default css; }

Gro generator
#

For projects using Gro, create a *.gen.css.ts file anywhere in src/:

// src/routes/fuz.gen.css.ts for SvelteKit, or src/fuz.gen.css.ts, etc. import {gen_fuz_css} from '@fuzdev/fuz_css/gen_fuz_css.js'; export const gen = gen_fuz_css();

Then import the generated file, in src/routes/+layout.svelte for SvelteKit:

import '@fuzdev/fuz_css/style.css'; import '@fuzdev/fuz_css/theme.css'; // or bring your own import './fuz.css'; // generated by Gro

Generator options

The Gro generator accepts the same options as the Vite plugin, plus additional options for batch processing:

  • include_stats - include file statistics in output (file counts, cache hits/misses, class counts)
  • project_root - project root directory; defaults to process.cwd()
  • concurrency - max concurrent file processing for cache reads and extraction; defaults to 8
  • cache_io_concurrency - max concurrent cache writes and deletes; defaults to 50

Class detection
#

The extractor scans your source files and extracts class names using three automatic mechanisms, plus manual hints for edge cases:

1. Direct extraction from class attributes

String literals and expressions in class contexts are extracted directly:

  • class="..." - static strings
  • class={[...]} - array syntax (for clsx-compatible frameworks like Svelte)
  • class={{...}} - object syntax (for clsx-compatible frameworks like Svelte)
  • class={cond ? 'a' : 'b'} - ternary expressions
  • class={(cond && 'a') || 'b'} - logical expressions
  • class:name - class directives (Svelte)
  • clsx(), cn(), cx(), classNames() - utility function calls

2. Naming convention

Variables ending with class, classes, className, classNames, class_name, or class_names (case-insensitive) are always extracted, regardless of where they're used:

// extracted because of naming convention const buttonClasses = 'color_d font_size_lg'; const buttonClass = active ? 'active' : null; const snake_class = 'snake'; const turtle_class_name = 'turtle';

3. Usage tracking

Variables used in class attributes are tracked back to their definitions, even if they don't follow the naming convention:

<script> const styles = 'some-class'; // tracked from class={styles} const variant = 'other-class'; // tracked from clsx() </script> <div class={styles}></div> <button class={clsx('color_d', variant)}></button>

Usage tracking works for variables inside clsx(), arrays, ternaries, and logical expressions within class attributes. Note that standalone clsx() calls outside class attributes don't trigger tracking -- use the naming convention for those cases.

4. Manual hints

For dynamically constructed classes that can't be statically analyzed, use the @fuz-classes comment:

// @fuz-classes opacity:50% opacity:75% opacity:100% const opacity_classes = [50, 75, 100].map((n) => `opacity:${n}%`); /* @fuz-classes color_a_5 color_b_5 color_c_5 */ const color = get_dynamic_color();

Alternatively, use the include_classes option in your config to the Vite plugin or Gro generator:

vite_plugin_fuz_css({ include_classes: ['opacity:50%', 'opacity:75%', 'opacity:100%'], });

Use exclude_classes to filter out false positives from extraction. This also suppresses warnings for these classes, even if they were explicitly annotated:

vite_plugin_fuz_css({ exclude_classes: ['some:false:positive'], });

Builtin classes
#

fuz_css's main stylesheet provides styles for base HTML elements using style variables, acting as a modern CSS reset that adapts to dark mode. It includes CSS classes that provide common generic functionality -- these are called builtin classes.

.unstyled

Default list (styled):

<ul> <li>1</li> <li>2</li> </ul>
  • 1
  • 2

With .unstyled:

<ul class="unstyled"> <li>a</li> <li>b</li> </ul>
  • a
  • b

The .unstyled class lets fuz_css provide solid default element styles with a simple opt-out:

:where(:is(ul, ol, menu):not(.unstyled)) { padding-left: var(--space_xl4); }

This strategy supports semantic hooks for theming:

:where(:is(ul, ol, menu):not(.unstyled)) { padding-left: var(--list_padding_left, var(--space_xl4)); }

See the specific docs sections for more about .unstyled.

Other builtin classes

Framework support
#

fuz_css is Svelte-first, but the base styles (style.css, theme.css) work with any framework and plain HTML. The utility class generator has varying detection support:

frameworkdetectionnotes
Sveltefullall patterns including class: directives and array/object syntax
plain HTMLfullstatic class="..." attributes, script variables
React / JSXfullwith acorn-jsx plugin - className
Preactfullwith acorn-jsx plugin - class
Solidfullwith acorn-jsx plugin - class, classList
Vue JSXfullwith acorn-jsx plugin - class
Vue SFC, Angular, etc.nonetemplate syntax not parsed; use clsx/cx/cn in JS/TS

The include_classes plugin config option is an escape hatch for classes that can't be statically detected. Acorn plugins can be added via acorn_plugins for additional syntax support like JSX.

Out of the box, class generation works only with TypeScript/JS, Svelte, and JSX. Angular is not supported; Vue JSX is supported but their recommended SFC format is not. We could revisit this if there's demand.

Svelte-first
#

The extractor parses and analyzes the AST to understand Svelte's class syntax. Supported constructs:

  • attributes: class="...", class={[...]}, class={{...}} (identifier and string-literal keys), class:name
  • expressions: logical (&&, ||, ??), ternaries, template literals (complete tokens only -- `color_a_5 ${base}` extracts color_a_5, but `color_${hue}_5` cannot be extracted; use @fuz-classes or include_classes)
  • Svelte 5 runes: $derived() and $derived.by() for class variables
  • utility calls: clsx(), cn(), cx(), classNames() with nested arrays, objects, and utility calls
  • scripts: both <script> and <script module>, with naming convention and usage tracking

React and JSX
#

To enable JSX support for React, Preact, Solid, etc, install acorn-jsx and pass it to the plugin or generator:

npm i -D acorn-jsx

Vite plugin

// vite.config.ts import {defineConfig} from 'vite'; import jsx from 'acorn-jsx'; import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.js'; export default defineConfig({ plugins: [ vite_plugin_fuz_css({ acorn_plugins: [jsx()], }), ], });

Gro generator

// fuz.gen.css.ts import {gen_fuz_css} from '@fuzdev/fuz_css/gen_fuz_css.js'; import jsx from 'acorn-jsx'; export const gen = gen_fuz_css({ acorn_plugins: [jsx()], });

Supported JSX patterns:

  • className="..." and class="..." - static strings
  • className={clsx(...)} - utility function calls
  • className={cond ? "a" : "b"} - ternary and logical expressions
  • classList={{active: cond}} - Solid's classList
  • usage tracking: variables in className, class, and classList are tracked back to their definitions (has limitations, room for improvement)
// variable tracking works in JSX too const styles = 'box hover:shadow_lg'; const Component = () => <div className={styles} />;

The acorn_plugins option accepts any Acorn-compatible plugin, so other syntax extensions can be supported the same way.

Custom interpreters
#

Interpreters dynamically generate CSS for class names that aren't in the static definitions (which can be extended via class_definitions or replaced with include_default_definitions: false). The default CSS-literal syntax and modifier support are both implemented as interpreters, which you can extend or replace.

For advanced use cases, you can define custom interpreters that generate CSS from arbitrary class name patterns. This is similar to UnoCSS's dynamic rules, which also use regex + function patterns. An interpreter has a regex pattern and an interpret function that returns CSS (or null to pass):

import type {CssClassDefinitionInterpreter} from '@fuzdev/fuz_css/css_class_generation.js'; // Example: grid-cols-N classes like "grid-cols-4" // Unlike composites, interpreters can parameterize values const grid_cols_interpreter: CssClassDefinitionInterpreter = { pattern: /^grid-cols-(\d+)$/, interpret: (matched) => { const n = parseInt(matched[1]!, 10); if (n < 1 || n > 24) return null; return `.grid-cols-${n} { grid-template-columns: repeat(${n}, minmax(0, 1fr)); }`; }, };

This generates grid-cols-1 through grid-cols-24 on-demand -- something that would require 24 separate composite definitions. Note the classes for this example could also be created as composites with a helper function -- fuz_css uses this strategy internally to create its token classes in css_class_definitions.ts.

Register with the Vite plugin or Gro generator:

import {css_class_interpreters} from '@fuzdev/fuz_css/css_class_interpreters.js'; vite_plugin_fuz_css({ class_interpreters: [grid_cols_interpreter, ...css_class_interpreters], })

The interpreter context provides access to class_definitions, css_properties (for validation), and diagnostics (for errors/warnings). This enables full programmatic control over class-to-CSS generation.

Compared to alternatives
#

TailwindCSS and UnoCSS are utility-first frameworks where classes have primacy. fuz_css is semantic-first: utilities complement HTML defaults rather than being the primary styling mechanism.

TailwindCSSUnoCSSfuz_css
primary syntaxDSL-firstconfig-firsttoken DSL + CSS literals
multi-property@apply, pluginsshortcutscomposites
arbitrary valuesDSL (bg-[#abc])any (presets)CSS syntax (background:#abc)
detectionregexregexAST (more capable, slower)
token sourceCSS (@theme)JS/TS configTS variables (importable)
extensibilitypluginsrules, variants, presetsinterpreters

fuz_css's modifier system is less expressive than TailwindCSS's variants. Missing: parent/sibling/descendant state (group-hover:, peer-invalid:, has-checked:), arbitrary variants ([&.is-dragging]:), child selectors (*:), container queries (@md:), data/ARIA variants, and more. When you need these patterns, fuz_css currently expects you to use rulesets or <style> tags, but the API is still a work in progress, and a more powerful and potentially more TailwindCSS-aligned system is on the table.

For extensibility, all three frameworks allow custom class-to-CSS mappings. UnoCSS's dynamic rules use regex + function patterns similar to fuz_css interpreters, plus separate variants for modifiers. TailwindCSS uses JS plugins and UnoCSS has the more mature extensibility story; fuz_css offers comparable power with interpreters but it's still evolving -- feedback is welcome!

fuz_css fits best when you prefer semantic HTML with styled defaults. Design tokens are defined in TypeScript, naturally adapt to dark mode, and can be imported in TS for typesafe runtime access. The tradeoffs include a more limited DSL and more verbose literal syntax, which nudges you toward <style> tags, tokens when appropriate, or composites for repeated patterns.


fuz_css is still early in development. Your input is welcome in the discussions!