From 1aa9180641e79c1616fffd7f895ce62b63314d4e Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 7 Jul 2026 11:49:45 -0400 Subject: [PATCH 01/33] feat: rework to oklch and add themes --- .changeset/twelve-shrimps-report.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/twelve-shrimps-report.md diff --git a/.changeset/twelve-shrimps-report.md b/.changeset/twelve-shrimps-report.md new file mode 100644 index 000000000..28c4b94f6 --- /dev/null +++ b/.changeset/twelve-shrimps-report.md @@ -0,0 +1,5 @@ +--- +'@fuzdev/fuz_css': minor +--- + +feat: rework to oklch and add themes From 4b59e87713e4c4f85cbda15172863d118ff43a8a Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 7 Jul 2026 14:18:24 -0400 Subject: [PATCH 02/33] feat: themes and oklch --- .changeset/twelve-shrimps-report.md | 56 +- CLAUDE.md | 56 +- examples/vite-preact/src/App.tsx | 10 +- examples/vite-react/src/App.tsx | 10 +- examples/vite-solid/src/App.tsx | 10 +- examples/vite-svelte/src/App.svelte | 14 +- src/lib/css_bundled_resolution.ts | 22 +- src/lib/css_class_composites.ts | 72 +- src/lib/css_class_definitions.ts | 68 +- src/lib/css_class_generators.ts | 2 +- src/lib/css_plugin_options.ts | 5 - src/lib/css_variable_utils.ts | 2 +- src/lib/gen_fuz_css.ts | 2 - src/lib/generate_css.ts | 3 - src/lib/oklch.ts | 178 + src/lib/ramps.ts | 341 ++ src/lib/style.css | 1397 +++--- src/lib/style_rule_parser.ts | 94 +- src/lib/theme.css | 2307 ++++++---- src/lib/theme.gen.css.ts | 4 +- src/lib/theme.ts | 27 +- src/lib/themes.ts | 56 +- src/lib/themes/base.ts | 9 + src/lib/themes/brutalist.ts | 52 + src/lib/themes/dark_only.ts | 26 + src/lib/themes/high_contrast.ts | 15 + src/lib/themes/low_contrast.ts | 13 + src/lib/themes/necromancer.ts | 54 + src/lib/themes/sunset_ember.ts | 36 + src/lib/themes/terminal.ts | 57 + src/lib/variable_graph.ts | 8 +- src/lib/variables.ts | 2673 +++++------ src/lib/vite_plugin_fuz_css.ts | 2 - src/lib/wcag.ts | 31 + src/routes/ThemeForm.svelte | 6 +- src/routes/docs/borders/+page.svelte | 10 +- src/routes/docs/buttons/+page.svelte | 6 +- src/routes/docs/chips/+page.svelte | 8 +- src/routes/docs/classes/+page.svelte | 27 +- src/routes/docs/colors/+page.svelte | 170 +- src/routes/docs/colors/ColorSwatch.svelte | 29 +- src/routes/docs/colors/ColorSwatchItem.svelte | 71 +- src/routes/docs/colors/HueSwatch.svelte | 3 +- src/routes/docs/shading/+page.svelte | 2 +- src/routes/docs/shadows/+page.svelte | 28 +- src/routes/docs/themes/+page.svelte | 27 + src/routes/docs/typography/+page.svelte | 2 +- src/test/class_variable_index.test.ts | 6 +- ...css_bundled_resolution.diagnostics.test.ts | 20 +- src/test/css_bundled_resolution.test.ts | 154 +- .../css_bundled_resolution.variables.test.ts | 17 +- src/test/css_literal.test.ts | 2 +- src/test/css_plugin_options.test.ts | 4 +- src/test/css_ruleset_parser.modifiers.test.ts | 8 +- src/test/css_ruleset_parser.parse.test.ts | 6 +- src/test/css_variable_utils.test.ts | 4 +- src/test/fixtures/css_classes_fixture.json | 4046 +++++------------ src/test/generate_classes_css.test.ts | 4 +- src/test/modified_class_interpreter.test.ts | 4 +- src/test/oklch.test.ts | 137 + src/test/ramps.test.ts | 189 + src/test/style_rule_parser.test.ts | 10 +- src/test/styles.test.ts | 9 +- src/test/themes.test.ts | 30 + src/test/variable.test.ts | 2 +- src/test/variable_graph.test.ts | 42 +- src/test/variables.test.ts | 7 +- src/test/vite_plugin_examples.test.ts | 22 +- 68 files changed, 6465 insertions(+), 6359 deletions(-) create mode 100644 src/lib/oklch.ts create mode 100644 src/lib/ramps.ts create mode 100644 src/lib/themes/base.ts create mode 100644 src/lib/themes/brutalist.ts create mode 100644 src/lib/themes/dark_only.ts create mode 100644 src/lib/themes/high_contrast.ts create mode 100644 src/lib/themes/low_contrast.ts create mode 100644 src/lib/themes/necromancer.ts create mode 100644 src/lib/themes/sunset_ember.ts create mode 100644 src/lib/themes/terminal.ts create mode 100644 src/lib/wcag.ts create mode 100644 src/test/oklch.test.ts create mode 100644 src/test/ramps.test.ts diff --git a/.changeset/twelve-shrimps-report.md b/.changeset/twelve-shrimps-report.md index 28c4b94f6..c217dd2ac 100644 --- a/.changeset/twelve-shrimps-report.md +++ b/.changeset/twelve-shrimps-report.md @@ -2,4 +2,58 @@ '@fuzdev/fuz_css': minor --- -feat: rework to oklch and add themes +feat: rework the color system to derived OKLCH, add semantic roles, cascade layers, and themes + +The color system is now derived: curve knobs → ramp stops → color stops → +utility classes, computed in pure CSS (`calc()`/`pow()`/`oklch()`), fitted to +minimize the perceptual delta from the old HSL palette. Breaking changes: + +- **`color_` family renamed to `palette_`**: variables `--color_a_50` → + `--palette_a_50`, token classes `.color_a_50` → `.palette_a_50`, semantic + component classes `.color_a`–`.color_j` → `.palette_a`–`.palette_j`, and + the hue+intensity class families `border_color_X_NN` → `border_palette_X_NN`, + `outline_color_X_NN` → `outline_palette_X_NN`, `shadow_color_X_NN` → + `shadow_palette_X_NN`. `bg_X_NN` classes keep their names. The + `border_color_NN` alpha-ramp family (no hue letter) keeps its names. +- **`--hue_a`…`--hue_j` values reinterpreted** as OKLCH hue angles (blue is + now `250`, not HSL `210`). Any consumer CSS doing `hsl(var(--hue_x) …)` + breaks — use `oklch( var(--hue_x))` or the palette/role stops. +- **`--tint_hue`/`--tint_saturation` removed** → `--hue_neutral` (defaults to + `var(--hue_f)`) + `--neutral_chroma` (peak chroma of the neutral scales). +- **Absolute `_light`/`_dark` variants removed** (~260 generated variables + like `--color_a_50_light`/`--shade_20_dark` and their classes, including + `shade_XX_light/dark`). Write the literal color or define one custom + property instead. +- **New curve knobs** (the promoted theme API): `--chroma_scale`, + `--hue_shift`, `--palette_lightness_00/_100/_curve` (same trio for + `shade_`/`text_`), `--palette_chroma_min/_max/_curve` (clamped per stop by + baked worst-hue sRGB gamut caps), plus derived per-stop variables + (`--palette_lightness_NN`, `--palette_chroma_NN`, `--chroma_shape_NN`, + `--hue_shift_NN`) that themes can pin individually. +- **New semantic role knobs**: `--hue_accent`, `--hue_positive`, + `--hue_negative`, `--hue_caution`, `--hue_info`, with derived role stops + `--accent_50`, `--accent_60`, `--negative_40`, `--negative_50` and the + `--selection_color` site variable. Links, focus, selection, selected + states, `accent-color`, and disabled-active feedback all route through + them; focus now follows the element color (via `--outline_color`) with the + accent as fallback. +- **Cascade layers**: all shipped CSS is layered `fuz.base` (defaults) < + `fuz.theme` (theme overrides) < `fuz.utilities` (generated classes); + consumers' unlayered styles beat everything. `render_theme_style` lost its + `specificity` option (the `:root:root` hack) and gained + `layer?: string | null` (default `'fuz.theme'`); `generate_theme_css` lost + its specificity parameter; the `theme_specificity` generator option is + removed. +- **`color-mix()` interpolation moved from `in hsl` to `in oklab`** in button + fills/borders, composites, and shadow classes. +- **Themes**: one module per theme under `themes/` + (`@fuzdev/fuz_css/themes/necromancer.ts` etc.); `themes.ts` exports the + curated `default_themes` registry (base, low contrast, high contrast, + terminal green) with unregistered expressive exemplars (necromancer, + sunset ember, brutalist). The contrast themes are rewritten as curve-knob + overrides. +- **New design-time modules**: `ramps.ts` (fitted knob constants, numeric + evaluators, CSS emitters), `oklch.ts` (OKLCH↔sRGB + gamut math), `wcag.ts` + (luminance/contrast), with tests gating every default stop for gamut, + monotonicity, and contrast (AA/AAA thresholds the old palette partly + failed). diff --git a/CLAUDE.md b/CLAUDE.md index c6948b9c9..2c30c03b5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -74,7 +74,7 @@ should answer "what specific gap in the defaults does this close?" — the most common misuse is hand-spacing elements that flow margin already spaces, or re-declaring typography/color the element already carries. When you do style, work down the ladder and stop at the first rung that suffices: right semantic -element → built-in class convention (`.selected`, `.color_a`) → composite +element → built-in class convention (`.selected`, `.palette_a`) → composite (`box`, `row`, `panel`) → token class (`p_md`, `gap_lg`) → literal (`display:flex`) → ` diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte new file mode 100644 index 000000000..97f588a72 --- /dev/null +++ b/src/routes/ThemeEditor.svelte @@ -0,0 +1,221 @@ + + +
+
+
+ + +
+ {editor.is_palette_tier ? 'palette-tier' : 'semantic-tier'} + {#if editor.dirty} + + {/if} +
+
+ {#if name_collides} + + {/if} +
+ + edits write to the {editing_scheme} scheme's slots +
+
+ + +
+
+ {#each quick_knobs as knob (knob.name)} + editor.set_value(knob.name, value, editing_scheme)} + onreset={() => editor.reset(knob.name)} + /> + {/each} +
+
+ + {#each axes as { axis, title } (axis)} + {@const knobs = semantic_knobs(axis)} + {#if knobs.length} +
+

{title}

+
+ {#each knobs as knob (knob.name)} + editor.set_value(knob.name, value, editing_scheme)} + onreset={() => editor.reset(knob.name)} + /> + {/each} +
+ {#if axis === 'color'} +
+ palette hues (the letter slots - moving these makes the theme palette-tier) +
+ {#each palette_knobs as knob (knob.name)} + editor.set_value(knob.name, value, editing_scheme)} + onreset={() => editor.reset(knob.name)} + /> + {/each} +
+
+ {/if} +
+ {/if} + {/each} + +
+

Output

+

+ The copyable Theme object, and the CSS it renders — the theme's own footprint. +

+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ + diff --git a/src/routes/ThemeForm.svelte b/src/routes/ThemeForm.svelte deleted file mode 100644 index 5ce62cd2c..000000000 --- a/src/routes/ThemeForm.svelte +++ /dev/null @@ -1,160 +0,0 @@ - - -
-

- {#if editing}edit{:else}create{/if} theme -

- -
-
-

variables: {light_count} light, {dark_count} dark

- -
-
- - -
-
-
-
- {#each new_variables as variable (variable.name)} - - {/each} -
-
- {#if code} -
- -
- - {/if} -
-
-
-{#if selected_variable} - (selected_variable = null)}> - - {#snippet children({close})} -
- - - -
- {/snippet} -
-
-{/if} - - diff --git a/src/routes/docs/colors/+page.svelte b/src/routes/docs/colors/+page.svelte index 1e082c46c..2a5d1e6c0 100644 --- a/src/routes/docs/colors/+page.svelte +++ b/src/routes/docs/colors/+page.svelte @@ -52,7 +52,8 @@

Hues use letters so themes can reassign colors without breaking semantics -- "a" is blue by default but could be any color. Meaning attaches through the role knobs layered on top: - --hue_accent (links, focus, selection, selected states) defaults to + --hue_accent (links, focus, selection, selected states -- what other systems call + the "primary" color, named here for what it communicates) defaults to --hue_a, --hue_negative to --hue_c, and so on. Retarget a role to move just that meaning; rotate a letter to move the palette.

diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index 1147c58d3..465225cdb 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -1,7 +1,5 @@ @@ -98,9 +142,9 @@ module_path="theme.ts" /> and .

- +

Selecting a theme loads its knobs into the editor below.

- (editing_theme = t)} /> +
@@ -115,21 +159,18 @@ content={`import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts';`} />
- (editing_theme = t)} /> +
+ + +

+ Drag a knob and the whole page rethemes live -- extreme values can make the page hard to read, + which is an honest signal, not a bug. Every edit updates a temporary "{UNSAVED_THEME_NAME}" + theme in the picker above; it persists across navigation until you leave or reset, so copy + the Theme object below to keep it. Knob sizes reflect leverage: the big controls + reshape the whole system, the small ones are surgical escape hatches. +

+ +
- -{#if editing_theme} - (editing_theme = null)}> - - { - console.log(`update theme`, theme); // eslint-disable-line no-console - alert('todo'); // eslint-disable-line no-alert - }} - /> - - -{/if} diff --git a/src/routes/theme_editor_state.svelte.ts b/src/routes/theme_editor_state.svelte.ts new file mode 100644 index 000000000..c3f3e181a --- /dev/null +++ b/src/routes/theme_editor_state.svelte.ts @@ -0,0 +1,219 @@ +import {SvelteMap} from 'svelte/reactivity'; + +import type {Theme} from '$lib/theme.ts'; +import type {StyleVariable} from '$lib/variable.ts'; +import {default_variables} from '$lib/variables.ts'; +import {theme_knob_by_name} from '$lib/knobs.ts'; +import type {ColorSchemeVariant} from '$lib/variable_data.ts'; + +// TODO upsteam to fuz_ui + +/** + * The name of the in-progress theme shown in pickers. Never `'base'`, which + * `render_theme_style`/`ThemeRoot` special-case to render nothing, and never + * a registry/exemplar name, which would collide with `ThemeInput`'s + * name-keyed selection. + */ +export const UNSAVED_THEME_NAME = 'unsaved'; + +const default_variable_by_name: Map = new Map( + default_variables.map((v) => [v.name, v]), +); + +export interface SlotOverride { + light?: string; + dark?: string; +} + +export interface ThemeEditorSnapshotData { + name: string; + based_on: string; + overrides: Array<[string, SlotOverride]>; +} + +/** + * State for the inline theme editor: a base theme selected by name plus a map + * of per-variable slot overrides, merged into a draft `Theme` on the fly. + * + * Scheme semantics: variables whose effective definition is scheme-adaptive + * (dual-slot) edit the slot of the scheme being viewed; single-slot variables + * always edit the light (base) slot so the change applies to both schemes. + * When a fresh light-slot override lands on a variable whose default is + * dual-slot, the merge preserves the default's dark slot explicitly — a + * theme's `:root` block beats the base defaults' `:root.dark` by cascade + * layer order, so omitting it would silently change dark mode too. A base + * theme that itself sets only light slots (e.g. dark-only mirrors) chose + * those cross-scheme semantics deliberately and is left alone. + */ +export class ThemeEditorState { + readonly themes: Array = []; + + name: string = $state.raw('new theme'); + based_on: string = $state.raw('base'); + readonly overrides: SvelteMap = new SvelteMap(); + + constructor(themes: Array) { + this.themes = themes; + } + + readonly base_theme: Theme = $derived( + this.themes.find((t) => t.name === this.based_on) ?? this.themes[0]!, + ); + + readonly base_variable_by_name: Map = $derived( + new Map(this.base_theme.variables.map((v) => [v.name, v])), + ); + + readonly dirty: boolean = $derived(this.overrides.size > 0); + + /** + * True when the draft (or its base) moves palette-tier knobs — the letter + * hues — making it an exemplar-tier theme per the two-tier policy. + */ + readonly is_palette_tier: boolean = $derived.by(() => { + for (const name of this.overrides.keys()) { + if (theme_knob_by_name.get(name)?.tier === 'palette') return true; + } + for (const v of this.base_theme.variables) { + if (theme_knob_by_name.get(v.name)?.tier === 'palette') return true; + } + return false; + }); + + readonly merged_variables: Array = $derived.by(() => { + const merged: Array = []; + const seen: Set = new Set(); + for (const v of this.base_theme.variables) { + seen.add(v.name); + const m = this.#merge_variable(v.name); + if (m) merged.push(m); + } + for (const name of this.overrides.keys()) { + if (seen.has(name)) continue; + const m = this.#merge_variable(name); + if (m) merged.push(m); + } + return merged; + }); + + #merge_variable(name: string): StyleVariable | null { + const o = this.overrides.get(name); + const b = this.base_variable_by_name.get(name); + const light = o?.light ?? b?.light; + let dark = o?.dark ?? b?.dark; + // preserve a scheme-adaptive default's dark slot for fresh light-only + // overrides - see the class comment for the cascade-layer rationale + if (o?.light !== undefined && dark === undefined && !b) { + dark = default_variable_by_name.get(name)?.dark; + } + if (dark !== undefined && dark === light) dark = undefined; + if (light === undefined && dark === undefined) return null; + const merged: StyleVariable = {name}; + if (light !== undefined) merged.light = light; + if (dark !== undefined) merged.dark = dark; + return merged; + } + + /** The live-applied theme, stably named so pickers key it consistently. */ + readonly draft: Theme = $derived({name: UNSAVED_THEME_NAME, variables: this.merged_variables}); + + /** The copyable theme, carrying the user's chosen name. */ + readonly output: Theme = $derived({name: this.name, variables: this.merged_variables}); + + /** + * The value a scheme currently renders for a variable, accounting for the + * theme layer's light slots beating the base defaults' dark slots. + */ + display_value(name: string, scheme: ColorSchemeVariant): string | undefined { + const o = this.overrides.get(name); + const b = this.base_variable_by_name.get(name); + const d = default_variable_by_name.get(name); + if (scheme === 'light') return o?.light ?? b?.light ?? d?.light; + return o?.dark ?? b?.dark ?? o?.light ?? b?.light ?? d?.dark ?? d?.light; + } + + changed(name: string): boolean { + return this.overrides.has(name); + } + + set_value(name: string, value: string, scheme: ColorSchemeVariant): void { + const o = this.overrides.get(name); + const b = this.base_variable_by_name.get(name); + const d = default_variable_by_name.get(name); + const adaptive = d?.dark !== undefined || b?.dark !== undefined || o?.dark !== undefined; + const slot = adaptive ? scheme : 'light'; + this.overrides.set(name, {...o, [slot]: value}); + } + + reset(name: string): void { + this.overrides.delete(name); + } + + reset_all(): void { + this.overrides.clear(); + } + + /** + * Loads a theme as the new base: overrides clear and the editor edits on + * top of its flattened variables (flatten-on-load composition). + * + * @mutates `this` + */ + load_theme(theme: Theme): void { + if (theme.name === UNSAVED_THEME_NAME) return; + this.based_on = theme.name; + this.overrides.clear(); + this.name = theme.name === 'base' ? 'new theme' : `custom ${theme.name}`; + } + + to_snapshot(): ThemeEditorSnapshotData { + return { + name: this.name, + based_on: this.based_on, + overrides: Array.from(this.overrides.entries()).map(([name, o]) => [name, {...o}]), + }; + } + + /** + * @mutates `this` + */ + restore_snapshot(data: ThemeEditorSnapshotData): void { + this.name = data.name; + this.based_on = data.based_on; + this.overrides.clear(); + for (const [name, o] of data.overrides) { + this.overrides.set(name, {...o}); + } + } +} + +const escape_single_quotes = (s: string): string => + s.replaceAll('\\', '\\\\').replaceAll("'", "\\'"); + +/** + * Renders a theme as a copyable TypeScript module. + */ +export const render_theme_ts = (theme: Theme): string => { + const identifier = + theme.name + .toLowerCase() + .replaceAll(/[^a-z0-9]+/gu, '_') + .replaceAll(/^_+|_+$/gu, '') || 'custom'; + const variables = theme.variables + .map((v) => { + const parts = [`name: '${escape_single_quotes(v.name)}'`]; + if (v.light !== undefined) parts.push(`light: '${escape_single_quotes(v.light)}'`); + if (v.dark !== undefined) parts.push(`dark: '${escape_single_quotes(v.dark)}'`); + return `\t\t{${parts.join(', ')}},`; + }) + .join('\n'); + return `import type {Theme} from '@fuzdev/fuz_css/theme.ts'; + +export const ${identifier}_theme: Theme = { + name: '${escape_single_quotes(theme.name)}', + variables: [ +${variables} + ], +}; +`; +}; diff --git a/src/test/knobs.test.ts b/src/test/knobs.test.ts new file mode 100644 index 000000000..e77f5950f --- /dev/null +++ b/src/test/knobs.test.ts @@ -0,0 +1,70 @@ +import {test, assert, describe} from 'vitest'; + +import {theme_knobs, theme_knob_by_name, theme_knob_hook_names} from '$lib/knobs.ts'; +import {default_variables} from '$lib/variables.ts'; + +const declared_names = new Set(default_variables.map((v) => v.name)); + +describe('theme_knobs', () => { + test('knob names are unique', () => { + const names = theme_knobs.map((k) => k.name); + assert.strictEqual(new Set(names).size, names.length); + }); + + test('non-hook knobs resolve to declared variables', () => { + for (const knob of theme_knobs) { + if (knob.hook) continue; + assert.isTrue( + declared_names.has(knob.name), + `Knob "${knob.name}" is not declared in default_variables`, + ); + } + }); + + test('hook knobs are not declared variables', () => { + // a hook knob that gains a declaration should drop its stale `hook` flag + for (const name of theme_knob_hook_names) { + assert.isFalse( + declared_names.has(name), + `Hook knob "${name}" is declared in default_variables - remove its hook flag`, + ); + } + }); + + test('ranges and steps are sane', () => { + for (const knob of theme_knobs) { + if (knob.range) { + assert.isBelow(knob.range[0], knob.range[1], `Knob "${knob.name}" has an inverted range`); + } + if (knob.step !== undefined) { + assert.isAbove(knob.step, 0, `Knob "${knob.name}" has a nonpositive step`); + } + if (knob.kind === 'enum') { + assert.isAbove(knob.values?.length ?? 0, 0, `Enum knob "${knob.name}" needs values`); + } + } + }); + + test('palette tier is exactly the letter hues', () => { + const palette_names = theme_knobs.filter((k) => k.tier === 'palette').map((k) => k.name); + assert.deepEqual(palette_names, [ + 'hue_a', + 'hue_b', + 'hue_c', + 'hue_d', + 'hue_e', + 'hue_f', + 'hue_g', + 'hue_h', + 'hue_i', + 'hue_j', + ]); + }); + + test('theme_knob_by_name indexes every knob', () => { + assert.strictEqual(theme_knob_by_name.size, theme_knobs.length); + for (const knob of theme_knobs) { + assert.strictEqual(theme_knob_by_name.get(knob.name), knob); + } + }); +}); diff --git a/src/test/styles.test.ts b/src/test/styles.test.ts index 9fbafd508..b29f759cd 100644 --- a/src/test/styles.test.ts +++ b/src/test/styles.test.ts @@ -2,6 +2,7 @@ import {test, assert} from 'vitest'; import {readFileSync} from 'node:fs'; import * as exported_variables from '$lib/variables.ts'; +import {theme_knob_hook_names} from '$lib/knobs.ts'; import css_classes_text from './fixtures/css_classes_fixture.json?raw'; // vitest replaces this with an empty string because CSS isn't opted into being processed, @@ -52,8 +53,11 @@ test('variables in the CSS exist', () => { /** * These variables are known to be in the CSS but not in the exported variables. * This means they can be contextually used when defined, but otherwise have a fallback. + * Hook knobs from the catalog (e.g. `heading_font_weight`) are included + * automatically — they're defined as CSS-consumed-but-undeclared. */ const known_without_variables = new Set([ + ...theme_knob_hook_names, 'fill', // contextual variable set by button palette classes (e.g., .palette_a sets --fill: var(--palette_a_40)) 'button_fill', 'button_fill_hover', diff --git a/src/test/themes.test.ts b/src/test/themes.test.ts index c72d33d7b..f685067d2 100644 --- a/src/test/themes.test.ts +++ b/src/test/themes.test.ts @@ -3,16 +3,20 @@ import {test, assert, describe} from 'vitest'; import {default_themes, DEFAULT_THEME} from '$lib/themes.ts'; import {necromancer_theme} from '$lib/themes/necromancer.ts'; import {sunset_ember_theme} from '$lib/themes/sunset_ember.ts'; -import {brutalist_theme} from '$lib/themes/brutalist.ts'; +import {brutalish_theme} from '$lib/themes/brutalish.ts'; import {create_terminal_theme, terminal_green_theme} from '$lib/themes/terminal.ts'; import {default_variables} from '$lib/variables.ts'; import {StyleVariable} from '$lib/variable.ts'; +import {theme_knob_hook_names} from '$lib/knobs.ts'; + +// declared variables plus the hook knobs style.css consumes via fallbacks +const known_names = new Set([...default_variables.map((v) => v.name), ...theme_knob_hook_names]); /** Shipped exemplar themes that deliberately stay out of the registry. */ const exemplar_themes = [ necromancer_theme, sunset_ember_theme, - brutalist_theme, + brutalish_theme, terminal_green_theme, create_terminal_theme(70), // amber, exercises the factory ]; @@ -71,7 +75,6 @@ describe('default_themes', () => { }); test('theme variable names exist in default_variables', () => { - const known_names = new Set(default_variables.map((v) => v.name)); for (const theme of default_themes) { for (const variable of theme.variables) { assert.isTrue( @@ -92,7 +95,6 @@ describe('exemplar themes', () => { }); test('all exemplar variables validate and exist in default_variables', () => { - const known_names = new Set(default_variables.map((v) => v.name)); for (const theme of exemplar_themes) { assert.isAbove(theme.variables.length, 0); for (const variable of theme.variables) { From 990448e0702cb82cc73c60f6d09b7f6fc448f4e5 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Fri, 10 Jul 2026 14:03:01 -0400 Subject: [PATCH 05/33] wip --- .changeset/twelve-shrimps-report.md | 12 +- CLAUDE.md | 2 +- package-lock.json | 8 +- package.json | 2 +- src/lib/css_class_definitions.ts | 54 ++++---- src/lib/ramps.ts | 12 +- src/lib/variable_data.ts | 4 +- src/routes/KnobControl.svelte | 119 ++++++++++-------- src/routes/ThemeEditor.svelte | 38 ++---- src/routes/docs/borders/+page.svelte | 8 +- src/routes/docs/buttons/+page.svelte | 36 +++--- src/routes/docs/chips/+page.svelte | 12 +- src/routes/docs/classes/+page.svelte | 17 +-- src/routes/docs/colors/+page.svelte | 12 +- src/routes/docs/colors/ColorSwatch.svelte | 8 +- src/routes/docs/colors/ColorSwatchItem.svelte | 7 +- src/routes/docs/colors/HueSwatch.svelte | 8 +- src/routes/docs/shadows/+page.svelte | 24 ++-- src/routes/theme_editor_state.svelte.ts | 16 +-- .../css_bundled_resolution.variables.test.ts | 2 +- src/test/ramps.test.ts | 8 +- 21 files changed, 208 insertions(+), 201 deletions(-) diff --git a/.changeset/twelve-shrimps-report.md b/.changeset/twelve-shrimps-report.md index 9e5ff6f5f..e04918025 100644 --- a/.changeset/twelve-shrimps-report.md +++ b/.changeset/twelve-shrimps-report.md @@ -15,14 +15,17 @@ minimize the perceptual delta from the old HSL palette. Breaking changes: `outline_color_X_NN` → `outline_palette_X_NN`, `shadow_color_X_NN` → `shadow_palette_X_NN`. `bg_X_NN` classes keep their names. The `border_color_NN` alpha-ramp family (no hue letter) keeps its names. + At the TS level, the letter list in `variable_data.ts` renames with the + family: `ColorVariant`/`color_variants` → `PaletteVariant`/`palette_variants`. - **`--hue_a`…`--hue_j` values reinterpreted** as OKLCH hue angles (blue is now `250`, not HSL `210`). Any consumer CSS doing `hsl(var(--hue_x) …)` breaks — use `oklch( var(--hue_x))` or the palette/role stops. - **`--tint_hue`/`--tint_saturation` removed** → `--hue_neutral` (defaults to `var(--hue_f)`) + `--neutral_chroma` (peak chroma of the neutral scales). -- **Absolute `_light`/`_dark` variants removed** (~260 generated variables - like `--color_a_50_light`/`--shade_20_dark` and their classes, including - `shade_XX_light/dark`). Write the literal color or define one custom +- **Absolute `_light`/`_dark` variants removed**: the ~286 generated + variables (`--color_a_50_light`-style, including `--shade_XX_light/dark`) + and all their classes — `color_X_NN_light/dark`, `bg_X_NN_light/dark`, and + `shade_NN_light/dark`. Write the literal color or define one custom property instead. - **New curve knobs** (the promoted theme API): `--chroma_scale`, `--hue_shift`, `--palette_lightness_00/_100/_curve` (same trio for @@ -43,7 +46,8 @@ minimize the perceptual delta from the old HSL palette. Breaking changes: `specificity` option (the `:root:root` hack) and gained `layer?: string | null` (default `'fuz.theme'`); `generate_theme_css` lost its specificity parameter; the `theme_specificity` generator option is - removed. + removed. Custom `base_css` input is re-layered into `fuz.base` in bundled + output — its own `@layer` identities aren't preserved. - **`color-mix()` interpolation moved from `in hsl` to `in oklab`** in button fills/borders, composites, and shadow classes. - **Themes**: one module per theme under `themes/` diff --git a/CLAUDE.md b/CLAUDE.md index c86cfb348..c5b1dd4fc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -184,7 +184,7 @@ reaches any theme/base hook without a dedicated token class. ## Variable naming See [variables.ts](src/lib/variables.ts) for definitions, -[variable_data.ts](src/lib/variable_data.ts) for size/color variants. +[variable_data.ts](src/lib/variable_data.ts) for size/palette variants. **Colors (OKLCH, derived):** diff --git a/package-lock.json b/package-lock.json index 8977d1364..4b6d59c39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@changesets/changelog-git": "^0.2.1", "@fuzdev/blake3_wasm": "^0.1.1", "@fuzdev/fuz_code": "^0.47.0", - "@fuzdev/fuz_ui": "^0.206.6", + "@fuzdev/fuz_ui": "^0.206.7", "@fuzdev/fuz_util": "^0.65.2", "@fuzdev/gro": "^0.206.0", "@fuzdev/mdz": "^0.3.0", @@ -837,9 +837,9 @@ } }, "node_modules/@fuzdev/fuz_ui": { - "version": "0.206.6", - "resolved": "https://registry.npmjs.org/@fuzdev/fuz_ui/-/fuz_ui-0.206.6.tgz", - "integrity": "sha512-LJzI0GgqGYojwhpR/vjTX5CEY5XJN3Nz2Qg/akgB0foa7u8YMrwT3nXPbH3LmTkjvP6eGI6j4lELHUdEEouPnw==", + "version": "0.206.7", + "resolved": "https://registry.npmjs.org/@fuzdev/fuz_ui/-/fuz_ui-0.206.7.tgz", + "integrity": "sha512-UUO+6zVK6Ot9NmZLaKWASJ6odjjLOUDObIWOjSKtoPBnSu62UvzlGYdSGEHYk1hSR58wNj0qndhWNDzmf/WDdg==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index b9ba3a86d..c7b31b017 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@changesets/changelog-git": "^0.2.1", "@fuzdev/blake3_wasm": "^0.1.1", "@fuzdev/fuz_code": "^0.47.0", - "@fuzdev/fuz_ui": "^0.206.6", + "@fuzdev/fuz_ui": "^0.206.7", "@fuzdev/fuz_util": "^0.65.2", "@fuzdev/gro": "^0.206.0", "@fuzdev/mdz": "^0.3.0", diff --git a/src/lib/css_class_definitions.ts b/src/lib/css_class_definitions.ts index cf4259c42..cf270bb9e 100644 --- a/src/lib/css_class_definitions.ts +++ b/src/lib/css_class_definitions.ts @@ -19,7 +19,7 @@ import { import { space_variants, distance_variants, - color_variants, + palette_variants, intensity_variants, shade_variants, shade_scale_variants, @@ -96,30 +96,30 @@ export const css_class_definitions: Record ({ - name: `hue_${hue}`, - css: `--hue: var(--hue_${hue});`, + (letter: string) => ({ + name: `hue_${letter}`, + css: `--hue: var(--hue_${letter});`, }), - color_variants, + palette_variants, ), // Palette intensity classes (text color) ...generate_classes( - (hue: string, intensity: string) => ({ - name: `palette_${hue}_${intensity}`, - css: `color: var(--palette_${hue}_${intensity}); --text_color: var(--palette_${hue}_${ + (letter: string, intensity: string) => ({ + name: `palette_${letter}_${intensity}`, + css: `color: var(--palette_${letter}_${intensity}); --text_color: var(--palette_${letter}_${ intensity });`, }), - color_variants, + palette_variants, intensity_variants, ), // Palette intensity classes (background color) ...generate_classes( - (hue: string, intensity: string) => ({ - name: `bg_${hue}_${intensity}`, - css: `background-color: var(--palette_${hue}_${intensity});`, + (letter: string, intensity: string) => ({ + name: `bg_${letter}_${intensity}`, + css: `background-color: var(--palette_${letter}_${intensity});`, }), - color_variants, + palette_variants, intensity_variants, ), // Darken/lighten overlays (non-adaptive, alpha-based) @@ -164,26 +164,26 @@ export const css_class_definitions: Record ({ - name: `border_palette_${hue}_${intensity}`, - css: `border-color: var(--palette_${hue}_${intensity}); --border_color: var(--palette_${ - hue + (letter: string, intensity: string) => ({ + name: `border_palette_${letter}_${intensity}`, + css: `border-color: var(--palette_${letter}_${intensity}); --border_color: var(--palette_${ + letter }_${intensity});`, }), - color_variants, + palette_variants, intensity_variants, ), // Outline colors using shade scale ...generate_property_classes('outline-color', shade_variants, (v) => `var(--shade_${v})`), // Outline colors using palette hue + intensity (sets both property and contextual variable) ...generate_classes( - (hue: string, intensity: string) => ({ - name: `outline_palette_${hue}_${intensity}`, - css: `outline-color: var(--palette_${hue}_${intensity}); --outline_color: var(--palette_${ - hue + (letter: string, intensity: string) => ({ + name: `outline_palette_${letter}_${intensity}`, + css: `outline-color: var(--palette_${letter}_${intensity}); --outline_color: var(--palette_${ + letter }_${intensity});`, }), - color_variants, + palette_variants, intensity_variants, ), @@ -235,11 +235,11 @@ export const css_class_definitions: Record ({ - name: `shadow_palette_${hue}_${intensity}`, - css: `--shadow_color: var(--palette_${hue}_${intensity});`, + (letter: string, intensity: string) => ({ + name: `shadow_palette_${letter}_${intensity}`, + css: `--shadow_color: var(--palette_${letter}_${intensity});`, }), - color_variants, + palette_variants, intensity_variants, ), diff --git a/src/lib/ramps.ts b/src/lib/ramps.ts index b68592200..35d5deff0 100644 --- a/src/lib/ramps.ts +++ b/src/lib/ramps.ts @@ -24,7 +24,7 @@ import { numeric_scale_variants, - type ColorVariant, + type PaletteVariant, type ColorSchemeVariant, type NumericScaleVariant, } from './variable_data.ts'; @@ -69,7 +69,7 @@ export interface ChromaRampKnobs { } /** OKLCH hue angles for the 10 palette hues, fitted from the HSL palette. */ -export const PALETTE_HUES: Record = { +export const PALETTE_HUES: Record = { a: 250, // blue b: 144, // green c: 24, // red @@ -215,7 +215,7 @@ export const ramp_chroma = ( * Computes the default OKLCH color of a palette stop (`--palette_X_NN`). */ export const palette_stop_oklch = ( - letter: ColorVariant, + letter: PaletteVariant, stop: NumericScaleVariant, scheme: ColorSchemeVariant, ): Oklch => [ @@ -318,8 +318,10 @@ export const render_hue_shift_offset_css = ( * Renders the derived default of a palette color stop, e.g. `--palette_a_50`. * One definition serves both schemes — the scheme flip lives in the knobs. */ -export const render_palette_stop_css = (letter: ColorVariant, stop: NumericScaleVariant): string => - render_ramp_color_css(`var(--hue_${letter})`, stop); +export const render_palette_stop_css = ( + letter: PaletteVariant, + stop: NumericScaleVariant, +): string => render_ramp_color_css(`var(--hue_${letter})`, stop); /** * Renders a color derived from the palette ramps at a stop for an arbitrary diff --git a/src/lib/variable_data.ts b/src/lib/variable_data.ts index 2d789e5f2..8e1bb1ed4 100644 --- a/src/lib/variable_data.ts +++ b/src/lib/variable_data.ts @@ -135,8 +135,8 @@ export const icon_sizes = { icon_size_xl3: '256px', }; -export type ColorVariant = ArrayElement; -export const color_variants = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] as const; +export type PaletteVariant = ArrayElement; +export const palette_variants = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] as const; export type IntensityVariant = NumericScaleVariant; export const intensity_variants = numeric_scale_variants; diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index 907659a35..c0f2bfcb7 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -52,61 +52,67 @@
-
- --{knob.name} - {#if changed} - - {/if} -
- {#if knob.kind === 'enum'} - - {:else if knob.kind === 'hue' && numeric_value !== null} - numeric_value ?? 0, (v) => emit_numeric(String(v))} /> - {:else if scalar && numeric_value !== null} -
- - emit_numeric(e.currentTarget.value)} - /> - emit_numeric(e.currentTarget.value)} - /> -
+ {#if knob.kind === 'hue' && numeric_value !== null} + + numeric_value ?? 0, (v) => emit_numeric(String(v))}> + --{knob.name} + {:else} - onchange(e.currentTarget.value)} - /> + + {/if} + {#if changed} + + {/if}
diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte index 97f588a72..e0d3b7c39 100644 --- a/src/routes/ThemeEditor.svelte +++ b/src/routes/ThemeEditor.svelte @@ -1,5 +1,5 @@ -
-
-
+
+
+
-
+
{editor.is_palette_tier ? 'palette-tier' : 'semantic-tier'} {#if editor.dirty} - {/if} @@ -110,7 +110,7 @@
-
+
{#each quick_knobs as knob (knob.name)}

{title}

-
+
{#each knobs as knob (knob.name)} (the letter slots - moving these makes the theme palette-tier) -
+
{#each palette_knobs as knob (knob.name)} The copyable Theme object, and the CSS it renders — the theme's own footprint.

-
+
-
+
@@ -185,27 +185,11 @@
diff --git a/src/routes/RampStrip.svelte b/src/routes/RampStrip.svelte new file mode 100644 index 000000000..6132c95f9 --- /dev/null +++ b/src/routes/RampStrip.svelte @@ -0,0 +1,54 @@ + + + +
+ {label} +
+ {#each numeric_scale_variants as stop (stop)} +
+ {/each} +
+
+ + diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte index c17b25502..a8efdd913 100644 --- a/src/routes/ThemeEditor.svelte +++ b/src/routes/ThemeEditor.svelte @@ -7,14 +7,21 @@ import type {ThemeState} from '@fuzdev/fuz_ui/theme_state.svelte.ts'; import {render_theme_style} from '$lib/theme.ts'; - import {theme_knobs, type KnobAxis, type ThemeKnob} from '$lib/knobs.ts'; - import type {ColorSchemeVariant} from '$lib/variable_data.ts'; + import {theme_knobs, knob_axes, type KnobAxis, type ThemeKnob} from '$lib/knobs.ts'; + import {PALETTE_HUES} from '$lib/ramps.ts'; + import { + palette_variants, + intent_variants, + type ColorSchemeVariant, + type PaletteVariant, + } from '$lib/variable_data.ts'; import { render_theme_ts, UNSAVED_THEME_NAME, type ThemeEditorState, } from '$routes/theme_editor_state.svelte.ts'; import KnobControl from '$routes/KnobControl.svelte'; + import RampStrip from '$routes/RampStrip.svelte'; // @fuz-classes sm md lg @@ -37,21 +44,30 @@ : 'light'; }); - const axes: Array<{axis: KnobAxis; title: string}> = [ - {axis: 'color', title: 'Color'}, - {axis: 'shape', title: 'Shape'}, - {axis: 'density', title: 'Density'}, - {axis: 'depth', title: 'Depth'}, - {axis: 'typography', title: 'Typography'}, - {axis: 'motion', title: 'Motion'}, - {axis: 'decoration', title: 'Decoration'}, - ]; - const semantic_knobs = (axis: KnobAxis): Array => theme_knobs.filter((k) => k.axis === axis && k.tier === 'semantic'); const palette_knobs = theme_knobs.filter((k) => k.tier === 'palette'); - // the highest-leverage knobs duplicated compactly above the granular flow - const quick_knobs = theme_knobs.filter((k) => k.leverage === 'lg'); + // the design band: intent/neutral bindings plus the highest-leverage levers, + // duplicated compactly above the granular flow - same state, two densities + const binding_knobs = theme_knobs.filter((k) => k.bindable); + const lever_knobs = theme_knobs.filter((k) => k.leverage === 'lg' && !k.bindable); + + // the sm escape-hatch walls fold behind a disclosure per axis so the levers + // stay the visual main flow + const sm_details_titles: Partial> = { + shape: 'border width & radius tokens', + density: 'space tokens', + typography: 'line height tokens', + motion: 'motion tokens', + }; + + // resolves a letter's current angle from the same merge the renderer uses, + // so binding chips and detachment track theme overrides + const resolve_hue = (letter: PaletteVariant): number => { + const v = editor.display_value(`hue_${letter}`, editing_scheme); + const n = Number(v); + return Number.isNaN(n) ? PALETTE_HUES[letter] : n; + }; const trimmed_name = $derived(editor.name.trim()); const name_collides = $derived( @@ -62,6 +78,18 @@ const output_css = $derived(render_theme_style(editor.output)); +{#snippet knob_control(knob: ThemeKnob, compact: boolean)} + editor.set_value(knob.name, value, editing_scheme)} + onreset={() => editor.reset(knob.name)} + /> +{/snippet} +
@@ -74,7 +102,19 @@ { - const name = e.currentTarget.value; - if ( - editor.dirty && - !confirm( - `load "${name}" as the new base? ${ - editor.overrides.size - } edited knob(s) will be discarded`, - ) - ) { - e.currentTarget.value = editor.based_on; - return; - } - const theme = editor.themes.find((t) => t.name === name); - if (theme) editor.load_theme(theme); - }} - > + +
{#if editor.dirty} reset all{editor.overrides.size ? ` (${editor.overrides.size})` : ''} {/if}
@@ -143,7 +155,15 @@ {/if}
- edits write to the {editing_scheme} scheme's slots + {#if editor.stance} + single-scheme theme - edits write to the base slots and the {editor.stance} appearance renders in both color schemes + {:else} + edits write to the {editing_scheme} scheme's slots + {/if}
diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index e8537d3b0..a40bc81e6 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -158,6 +158,13 @@ lang="ts" content={`import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts';`} /> +

+ A theme can declare a single-scheme stance with scheme: 'light' | 'dark' -- the + renderer then mirrors every scheme-adaptive default the theme doesn't override, so its one + appearance renders in both color schemes, and pins + to match so form controls and scrollbars agree. The + necromancer and terminal exemplars are dark-only this way, without hand-mirrored knob values. +

@@ -168,11 +175,12 @@ Drag a knob and the whole page rethemes live -- extreme values can make the page hard to read, which is an honest signal, not a bug. Every edit updates a temporary "{UNSAVED_THEME_NAME}" theme in the picker above; it persists across navigation until you leave or reset, so copy - the Theme object below to keep it. The top band holds the semantic-tier moves: - assign each intent (accent, neutral, positive, negative, caution, info) to a palette letter -- - or a custom angle -- and pull the high-leverage levers. Below it, each axis section carries - the granular knobs, with per-token escape hatches folded away; the ramp strips repaint live as - the derived scales move. + the Theme object below to keep it. The scheme selector sets the theme's stance -- + a single-scheme theme renders its one appearance in both color schemes, so edits write the + base slots. The top band holds the semantic-tier moves: assign each intent (accent, neutral, + positive, negative, caution, info) to a palette letter -- or a custom angle -- and pull the + high-leverage levers. Below it, each axis section carries the granular knobs, with per-token + escape hatches folded away; the ramp strips repaint live as the derived scales move.

diff --git a/src/routes/docs/variables/+page.svelte b/src/routes/docs/variables/+page.svelte index 3ce723abb..0c8fa899d 100644 --- a/src/routes/docs/variables/+page.svelte +++ b/src/routes/docs/variables/+page.svelte @@ -55,6 +55,16 @@ The result is a flexible system that aligns with modern CSS to deliver high-capability UX and DX with low overhead.

+

+ Most color variables are derived: curve knobs feed ramp stops, ramp stops feed color + stops, all computed in pure CSS (calc()/pow()/oklch()). + A theme is a set of knob values, not a stylesheet -- it usually moves a handful of + high-leverage variables (intent bindings like hue_accent, levers like + chroma_scale and radius_scale, the lightness curve knobs) and + everything downstream re-derives, while any individual variable stays pinnable as the escape + hatch. See for the color system and for + theming. +

In bundled mode, only the variables your code uses are emitted, along with any they depend on. The full @@ -71,6 +81,8 @@ content={`export interface Theme { name: string; variables: StyleVariable[]; + /** Single-scheme themes render one appearance in both color schemes. */ + scheme?: 'dual' | 'light' | 'dark'; } export interface StyleVariable { diff --git a/src/routes/theme_editor_state.svelte.ts b/src/routes/theme_editor_state.svelte.ts index edd9a7862..ff6cd34bf 100644 --- a/src/routes/theme_editor_state.svelte.ts +++ b/src/routes/theme_editor_state.svelte.ts @@ -1,6 +1,6 @@ import {SvelteMap} from 'svelte/reactivity'; -import type {Theme} from '$lib/theme.ts'; +import type {Theme, ThemeScheme} from '$lib/theme.ts'; import type {StyleVariable} from '$lib/variable.ts'; import {default_variables} from '$lib/variables.ts'; import {theme_knob_by_name} from '$lib/knobs.ts'; @@ -28,6 +28,7 @@ export interface SlotOverride { export interface ThemeEditorSnapshotData { name: string; based_on: string; + scheme: ThemeScheme; overrides: Array<[string, SlotOverride]>; } @@ -44,12 +45,20 @@ export interface ThemeEditorSnapshotData { * layer order, so omitting it would silently change dark mode too. A base * theme that itself sets only light slots (e.g. dark-only mirrors) chose * those cross-scheme semantics deliberately and is left alone. + * + * A single-scheme stance (`Theme.scheme`) changes both halves: edits always + * write the light/base slot (the stance renders that one appearance in both + * color schemes, so dual slots are meaningless), and the merge skips the + * dark-slot preservation (the renderer's stance mirror handles untouched + * defaults). Switching into a stance re-slots existing overrides so the + * stanced scheme's edited values become the base slots. */ export class ThemeEditorState { readonly themes: Array = []; name: string = $state.raw('new theme'); based_on: string = $state.raw('base'); + scheme: ThemeScheme = $state.raw('dual'); readonly overrides: SvelteMap = new SvelteMap(); constructor(themes: Array) { @@ -60,11 +69,18 @@ export class ThemeEditorState { this.themes.find((t) => t.name === this.based_on) ?? this.themes[0]!, ); + readonly base_scheme: ThemeScheme = $derived(this.base_theme.scheme ?? 'dual'); + + /** The single-scheme stance, `null` for dual themes. */ + readonly stance: 'light' | 'dark' | null = $derived( + this.scheme === 'light' || this.scheme === 'dark' ? this.scheme : null, + ); + readonly base_variable_by_name: Map = $derived( new Map(this.base_theme.variables.map((v) => [v.name, v])), ); - readonly dirty: boolean = $derived(this.overrides.size > 0); + readonly dirty: boolean = $derived(this.overrides.size > 0 || this.scheme !== this.base_scheme); /** * True when the draft (or its base) moves palette-tier knobs — the letter @@ -102,8 +118,9 @@ export class ThemeEditorState { const light = o?.light ?? b?.light; let dark = o?.dark ?? b?.dark; // preserve a scheme-adaptive default's dark slot for fresh light-only - // overrides - see the class comment for the cascade-layer rationale - if (o?.light !== undefined && dark === undefined && !b) { + // overrides - see the class comment for the cascade-layer rationale; + // under a stance the renderer's mirror owns the cross-scheme story + if (o?.light !== undefined && dark === undefined && !b && !this.stance) { dark = default_variable_by_name.get(name)?.dark; } if (dark !== undefined && dark === light) dark = undefined; @@ -115,23 +132,35 @@ export class ThemeEditorState { } /** The live-applied theme, stably named so pickers key it consistently. */ - readonly draft: Theme = $derived({name: UNSAVED_THEME_NAME, variables: this.merged_variables}); + readonly draft: Theme = $derived({ + name: UNSAVED_THEME_NAME, + variables: this.merged_variables, + ...(this.stance ? {scheme: this.stance} : {}), + }); /** The copyable theme, carrying the user's chosen name. */ - readonly output: Theme = $derived({name: this.name, variables: this.merged_variables}); + readonly output: Theme = $derived({ + name: this.name, + variables: this.merged_variables, + ...(this.stance ? {scheme: this.stance} : {}), + }); /** * The value a scheme currently renders for a variable, derived from the * same merge the renderer uses so the two can't disagree — including the - * theme layer's light slots beating the base defaults' dark slots, and the + * theme layer's light slots beating the base defaults' dark slots, the * merge preserving a scheme-adaptive default's dark slot under fresh - * light-only overrides. + * light-only overrides, and a single-scheme stance mirroring untouched + * scheme-adaptive defaults so both schemes show the stanced appearance. */ display_value(name: string, scheme: ColorSchemeVariant): string | undefined { const merged = this.#merge_variable(name); const d = default_variable_by_name.get(name); - if (scheme === 'light') return merged?.light ?? d?.light; - return merged?.dark ?? merged?.light ?? d?.dark ?? d?.light; + // the renderer's stance mirror applies only to defaults the theme + // doesn't touch, re-slotted so the stanced value wins in both schemes + const mirrored = !merged && this.stance ? d?.[this.stance] : undefined; + if (scheme === 'light') return merged?.light ?? mirrored ?? d?.light; + return merged?.dark ?? mirrored ?? merged?.light ?? d?.dark ?? d?.light; } changed(name: string): boolean { @@ -143,21 +172,47 @@ export class ThemeEditorState { const b = this.base_variable_by_name.get(name); const d = default_variable_by_name.get(name); const adaptive = d?.dark !== undefined || b?.dark !== undefined || o?.dark !== undefined; - const slot = adaptive ? scheme : 'light'; + // under a stance edits always write the base slot - dual slots are + // meaningless when one appearance renders in both color schemes + const slot = !this.stance && adaptive ? scheme : 'light'; this.overrides.set(name, {...o, [slot]: value}); } + /** + * Sets the scheme stance. Entering a single-scheme stance re-slots existing + * overrides so the stanced scheme's edited values become base slots (dark + * slots would otherwise shadow later stanced edits in dark mode); a + * light-stanced theme drops dark-only overrides since that appearance never + * renders. + * + * @mutates `this` + */ + set_scheme(scheme: ThemeScheme): void { + this.scheme = scheme; + if (scheme !== 'light' && scheme !== 'dark') return; + for (const [name, o] of this.overrides) { + const value = scheme === 'dark' ? (o.dark ?? o.light) : o.light; + if (value === undefined) { + this.overrides.delete(name); + } else { + this.overrides.set(name, {light: value}); + } + } + } + reset(name: string): void { this.overrides.delete(name); } reset_all(): void { this.overrides.clear(); + this.scheme = this.base_scheme; } /** * Loads a theme as the new base: overrides clear and the editor edits on - * top of its flattened variables (flatten-on-load composition). + * top of its flattened variables (flatten-on-load composition), carrying + * the theme's scheme stance. * * @mutates `this` */ @@ -165,6 +220,7 @@ export class ThemeEditorState { if (theme.name === UNSAVED_THEME_NAME) return; this.based_on = theme.name; this.overrides.clear(); + this.scheme = theme.scheme ?? 'dual'; this.name = theme.name === 'base' ? 'new theme' : `custom ${theme.name}`; } @@ -172,6 +228,7 @@ export class ThemeEditorState { return { name: this.name, based_on: this.based_on, + scheme: this.scheme, overrides: Array.from(this.overrides.entries()).map(([name, o]) => [name, {...o}]), }; } @@ -182,6 +239,7 @@ export class ThemeEditorState { restore_snapshot(data: ThemeEditorSnapshotData): void { this.name = data.name; this.based_on = data.based_on; + this.scheme = data.scheme ?? this.base_scheme; this.overrides.clear(); for (const [name, o] of data.overrides) { this.overrides.set(name, {...o}); @@ -213,10 +271,14 @@ export const render_theme_ts = (theme: Theme): string => { const variables_ts = theme.variables.length ? `[\n${variables}\n\t],` : '[], // empty - every variable keeps its base default'; + const scheme_ts = + theme.scheme === 'light' || theme.scheme === 'dark' + ? `\n\tscheme: '${theme.scheme}', // renders this appearance in both color schemes` + : ''; return `import type {Theme} from '@fuzdev/fuz_css/theme.ts'; export const ${identifier}_theme: Theme = { - name: '${escape_single_quotes(theme.name)}', + name: '${escape_single_quotes(theme.name)}',${scheme_ts} variables: ${variables_ts} }; `; diff --git a/src/test/theme_check.test.ts b/src/test/theme_check.test.ts index 52cc144ff..4a2b0428e 100644 --- a/src/test/theme_check.test.ts +++ b/src/test/theme_check.test.ts @@ -85,6 +85,34 @@ describe('validate_theme', () => { const issues = validate_theme({name: 't', variables: [], scheme: 'dusk' as 'dark'}); assert.isTrue(issues.some((i) => i.level === 'error')); }); + + test('a dark slot under a single-scheme stance is a warning, not an error', () => { + for (const scheme of ['light', 'dark'] as const) { + const issues = validate_theme({ + name: 't', + scheme, + variables: [{name: 'neutral_chroma', light: '0.02', dark: '0.03'}], + }); + assert.isTrue(issues.some((i) => i.level === 'warning' && i.variable === 'neutral_chroma')); + assert.isFalse(issues.some((i) => i.level === 'error')); + } + // single-slot stanced and dual-slot unstanced themes stay clean + assert.deepEqual( + validate_theme({ + name: 't', + scheme: 'dark', + variables: [{name: 'neutral_chroma', light: '0.02'}], + }), + [], + ); + assert.deepEqual( + validate_theme({ + name: 't', + variables: [{name: 'neutral_chroma', light: '0.02', dark: '0.03'}], + }), + [], + ); + }); }); describe('resolution', () => { From 1b876c6c7d9df84c786347b9eda97e4f6eb97af7 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Thu, 16 Jul 2026 10:55:49 -0400 Subject: [PATCH 13/33] wip --- .changeset/dev-initial-load-prescan.md | 15 ++---- .changeset/micro-surface-hooks.md | 25 ++++----- .changeset/oklch-color-system.md | 60 +++++++++++++++++++++ .changeset/theme-knobs-and-checks.md | 63 ++++++++-------------- .changeset/theme-scheme-stance.md | 20 ++++--- .changeset/twelve-shrimps-report.md | 72 -------------------------- 6 files changed, 107 insertions(+), 148 deletions(-) create mode 100644 .changeset/oklch-color-system.md delete mode 100644 .changeset/twelve-shrimps-report.md diff --git a/.changeset/dev-initial-load-prescan.md b/.changeset/dev-initial-load-prescan.md index 4be74a2fb..45bd69c24 100644 --- a/.changeset/dev-initial-load-prescan.md +++ b/.changeset/dev-initial-load-prescan.md @@ -4,13 +4,8 @@ fix: complete utility CSS on the first dev page load -The Vite plugin now eagerly pre-scans project sources at dev-server startup -(new `prescan` option: `true` scans `src` under the Vite root, `false` -disables, or an array of directories) and resyncs clients whose HMR socket -connects after a missed CSS update. Previously the first cold-start page -load could render with incomplete utility classes until a manual refresh: -extraction state accumulated only from modules Vite had transformed so far, -and the corrective HMR update was dropped when the browser hadn't connected -yet. Bundled resources (base-CSS rule index, variable graph) also load -eagerly at dev-server startup now, overlapping their parse with the -pre-scan for faster cold starts. +The Vite plugin now pre-scans project sources at dev-server startup (new +`prescan` option: `true` scans `src` under the Vite root, `false` disables, +or an array of directories) and resyncs clients whose HMR socket connects +after a missed CSS update. Previously the first cold-start page load could +render with incomplete utility classes until a manual refresh. diff --git a/.changeset/micro-surface-hooks.md b/.changeset/micro-surface-hooks.md index 82ef0e94a..0ebc9f6c3 100644 --- a/.changeset/micro-surface-hooks.md +++ b/.changeset/micro-surface-hooks.md @@ -5,22 +5,17 @@ feat: theme scrollbars, caret, dialog backdrop, and the OS contrast preference New micro-surface defaults in `style.css`, each themable through a `var()` -fallback hook (registered in the knob catalog so `validate_theme` accepts -them): +fallback hook (all registered theme variables): - `scrollbar-color` on `:root` — thumb defaults to `var(--shade_40)` on a - transparent track, so scrollbars follow the theme's neutral; hooks - `--scrollbar_thumb_color` / `--scrollbar_track_color` -- `caret-color` on text inputs — defaults to `var(--accent_50)`, matching - selection and focus; hook `--caret_color` -- `dialog::backdrop` — defaults to `var(--darken_60)`, the same dim fuz_ui's - `Dialog` uses; hook `--backdrop_color` + transparent track; hooks `--scrollbar_thumb_color` / + `--scrollbar_track_color` +- `caret-color` on text inputs — defaults to `var(--accent_50)`; hook + `--caret_color` +- `dialog::backdrop` — defaults to `var(--darken_60)`; hook + `--backdrop_color` - `@media (prefers-contrast: more)` maps the OS preference onto the curve - knobs, mirroring the `'high contrast'` theme's moves - (`--shade_lightness_00`, `--text_lightness_curve`); theme overrides beat - it from the `fuz.theme` layer + knobs, mirroring the `'high contrast'` theme; theme overrides beat it -Also tunes the `'low contrast'` registry theme's shade compression -(`shade_lightness_00` 0.92 light / 0.245 dark) to the softest values that -pass every `check_theme` WCAG gate, so the whole registry passes its own -gates. +Also tunes the `'low contrast'` theme's shade compression to the softest +values that pass every `check_theme` contrast gate. diff --git a/.changeset/oklch-color-system.md b/.changeset/oklch-color-system.md new file mode 100644 index 000000000..625902baf --- /dev/null +++ b/.changeset/oklch-color-system.md @@ -0,0 +1,60 @@ +--- +'@fuzdev/fuz_css': minor +--- + +feat: rework the color system to derived OKLCH, add semantic intents, cascade layers, and themes + +Colors are now derived — curve knobs → ramp stops → color stops — in pure +CSS (`calc()`/`pow()`/`oklch()`), fitted to minimize the perceptual delta +from the old HSL palette. Breaking changes: + +- **`color_` renamed to `palette_`**: `--color_a_50` → `--palette_a_50`, + `.color_a_50` → `.palette_a_50`, `.color_a`–`.color_j` → + `.palette_a`–`.palette_j`. In compound families the letter alone implies + the palette: `border_color_X_NN` → `border_X_NN`, `outline_color_X_NN` → + `outline_X_NN`, `shadow_color_X_NN` → `shadow_X_NN` (`bg_X_NN` and the + letterless families — `border_color_NN`, `outline_color_NN`, + `shadow_color_umbra` — keep their names). In TS: + `ColorVariant`/`color_variants` → `PaletteVariant`/`palette_variants`. +- **`--hue_a`…`--hue_j` are now OKLCH hue angles** (blue is `250`, not HSL + `210`). Consumer CSS doing `hsl(var(--hue_x) …)` breaks — use + `oklch( var(--hue_x))` or the palette/intent stops. +- **`--tint_hue`/`--tint_saturation` removed** → `--hue_neutral` (defaults + to `var(--hue_f)`) + `--neutral_chroma`. +- **Absolute `_light`/`_dark` variants removed**: the ~286 generated + variables (`--color_a_50_light`-style, `--shade_XX_light/dark`) and all + their classes. Write the literal color or define one custom property + instead. +- **New curve knobs** (the promoted theme API): `--chroma_scale`, + `--hue_shift`, `--palette_lightness_00/_100/_curve` (same trio for + `shade_`/`text_`), `--palette_chroma_min/_max/_curve` (clamped per stop by + baked worst-hue sRGB gamut caps), plus per-stop derived variables themes + can pin individually (`--palette_lightness_NN`, `--palette_chroma_NN`, + `--chroma_shape_NN`, `--hue_shift_NN`). +- **New semantic intent knobs**: `--hue_accent`, `--hue_positive`, + `--hue_negative`, `--hue_caution`, `--hue_info`, each deriving a 13-stop + scale through the shared ramps (`--accent_00`…`--accent_100`, etc.) with + matching token classes (`.positive_50`, `.bg_caution_10`), plus + `--selection_color` and `intent_variants`/`IntentVariant` in + `variable_data.ts`. Links, focus, selection, `accent-color`, and + disabled-active feedback route through them; focus follows the element + color (via `--outline_color`) with the accent as fallback. +- **Cascade layers**: all shipped CSS is layered `fuz.base` < `fuz.theme` < + `fuz.utilities`; consumers' unlayered styles beat everything. + `render_theme_style` loses `specificity` (the `:root:root` hack) and gains + `layer?: string | null` (default `'fuz.theme'`); `generate_theme_css` + loses its specificity parameter; the `theme_specificity` generator option + is removed. The default-theme special case now keys on empty `variables` + rather than the `'base'` name. Custom `base_css` is re-layered into + `fuz.base` in bundled output. +- **`color-mix()` interpolation moved from `in hsl` to `in oklab`** in + button fills/borders, composites, and shadow classes. +- **Themes**: `themes.ts` exports the curated `default_themes` registry + (base, low contrast, high contrast — the contrast themes are rewritten as + curve-knob overrides), one module per theme under `themes/`, plus + unregistered exemplars (necromancer, sunset ember, brutalish, terminal + green + the `create_terminal_theme(hue)` factory). +- **New design-time modules**: `ramps.ts` (fitted knob constants, numeric + evaluators, CSS emitters), `oklch.ts` (OKLCH↔sRGB + gamut math), `wcag.ts` + (luminance/contrast), with tests gating every default stop for gamut, + monotonicity, and contrast. diff --git a/.changeset/theme-knobs-and-checks.md b/.changeset/theme-knobs-and-checks.md index b288ecc18..39ff32431 100644 --- a/.changeset/theme-knobs-and-checks.md +++ b/.changeset/theme-knobs-and-checks.md @@ -4,47 +4,30 @@ feat: add theme scale knobs, the knob catalog, and theme validation/gates/compile -New high-leverage theme knobs, each derived into existing token defaults so -one knob move reshapes a whole family while individual tokens stay pinnable: +New theme knobs, each derived into existing token defaults so one knob move +reshapes a whole family while individual tokens stay pinnable: +`--shadow_alpha_scale` (multiplies the `shadow_alpha_*` ramp, including +button shadows), `--radius_scale` (multiplies the `border_radius_*` tiers), +`--scale_factor` (multiplies the `space_*` scale), `--font_weight` (body), +`--heading_font_weight` (a hook with per-tier fallbacks — setting it +flattens the heading ladder), `--heading_font_family`, and +`--background_image` (decoration hook on `:root`). -- `--shadow_alpha_scale` — multiplies the `shadow_alpha_*` ramp (0 flattens - all shadows including button shadows, which reference the ramp) -- `--radius_scale` — multiplies the `border_radius_*` tiers (0 is sharp, - above 1 is rounder; per-element tiers survive) -- `--scale_factor` — multiplies the `space_*` scale (tight ↔ spacious) -- `--font_weight` — base body font weight (applied on `body`) -- `--heading_font_weight` — a hook with per-tier fallbacks (h1 300 … h5 900); - setting it flattens the heading weight ladder deliberately -- `--heading_font_family` — headings' font family (defaults to - `var(--font_family_serif)`) -- `--background_image` — decoration hook on `:root` for gradient skies, - vignettes, and textures (defaults to `none`) +New `knobs.ts`: `theme_knobs`, a typed catalog of the theme-facing knobs +(`kind`, `axis`, `leverage`, `tier`, `bindable`, ranges), which powers the +inline theme editor on the themes docs page. `variable_data.ts` gains +`palette_glosses`, the letter → color/default-intent display data. -New `knobs.ts` module: `theme_knobs`, a typed catalog of the theme-facing -knobs (`kind`, `axis`, `leverage`, `tier`, `bindable`, ranges, and the -`knob_axes` display order), which powers the inline theme editor on the -themes docs page. `variable_data.ts` gains `palette_glosses`, the letter → -color/default-intent-binding display data. +New `theme_check.ts`, resolving a theme's authored values back to numbers +(literals, `var(--hue_x)` binding chains, compiled-cap overrides): -New `theme_check.ts` module with three functions that resolve a theme's -authored values back to numbers — numeric literals, `var(--hue_x)` binding -chains, and compiled-cap overrides — with knob defaults falling through to -the numeric twin in `ramps.ts`: +- `validate_theme` — structural lint: shape and unknown-name errors, plus + advisory type/range warnings for the knob-tier variables +- `check_theme` — report-only gamut, ramp-monotonicity, and contrast gates + (the same thresholds the repo's tests assert, exported as the `GATE_*` + constants) +- `compile_theme` — recomputes per-stop worst-hue chroma caps from a theme's + own hues, lightness ramp, and hue shift, emits `palette_chroma_NN` + overrides where the baked caps no longer fit, and re-checks the result -- `validate_theme` — the structural lint: non-empty name, `StyleVariable` - shape, and known variable names as errors, plus advisory type/range - warnings for the knob-tier variables. -- `check_theme` — evaluates the gamut, ramp-monotonicity, and contrast gates - (the same thresholds the repo's tests assert for the defaults) against an - arbitrary theme. Report-only, never throws. -- `compile_theme` — recomputes each theme's per-stop worst-hue chroma caps - from its own hues, lightness ramp, and hue shift, then emits - `palette_chroma_NN` overrides where the baked caps no longer fit (a - rotated, monochrome, or dark-only theme), and re-checks the result. - -The gate thresholds are exported constants (`GATE_BODY_TEXT`, -`GATE_SUBTLE_TEXT`, `GATE_LINK`, `GATE_UI`, `GATE_FILL_TEXT`). `ramps.ts` -gains `ramp_hue_shift_offset` (the numeric twin of the hue-shift CSS -emitter) and `compute_palette_chroma_caps` (the generalized worst-hue cap -search behind the baked table and the compile step); `ramp_chroma` and -`render_chroma_stop_css` take optional knob/cap parameters. +`ramps.ts` gains `ramp_hue_shift_offset` and `compute_palette_chroma_caps`. diff --git a/.changeset/theme-scheme-stance.md b/.changeset/theme-scheme-stance.md index c4fb83f25..3a546a2f7 100644 --- a/.changeset/theme-scheme-stance.md +++ b/.changeset/theme-scheme-stance.md @@ -5,16 +5,14 @@ feat: add the theme scheme stance `Theme` gains `scheme?: 'dual' | 'light' | 'dark'` (default `'dual'`). A -single-scheme theme renders that appearance in both color schemes: +single-scheme theme renders its one appearance in both color schemes: `render_theme_style` mirrors every scheme-adaptive default the theme doesn't -override (including the `palette_chroma_NN` gamut-cap stops, so the mirrored -scheme gets correct caps) and pins `color-scheme` on the scope so form -controls and native scrollbars agree. The mirror is exported as -`scheme_stance_variables`. A stanced theme's own variables are best authored -single-slot in the light/base position. +override (exported as `scheme_stance_variables`, including the +`palette_chroma_NN` gamut-cap stops) and pins `color-scheme` on the scope so +form controls and native scrollbars agree. Author a stanced theme's own +variables single-slot in the light/base position. -`validate_theme` checks the field and warns on dark slots a single-scheme -stance makes meaningless, and `check_theme`/`compile_theme` resolve -through the same mirror so the gates and cap recomputation evaluate the -stanced reality in both schemes. The necromancer and terminal exemplars use -the stance instead of hand-mirrored ramp knobs. +`validate_theme` warns on dark slots a single-scheme stance makes +meaningless; `check_theme`/`compile_theme` resolve through the same mirror +so the gates evaluate the stanced reality in both schemes. The necromancer +and terminal exemplars use the stance instead of hand-mirrored ramp knobs. diff --git a/.changeset/twelve-shrimps-report.md b/.changeset/twelve-shrimps-report.md deleted file mode 100644 index 54672148e..000000000 --- a/.changeset/twelve-shrimps-report.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -'@fuzdev/fuz_css': minor ---- - -feat: rework the color system to derived OKLCH, add semantic intents, cascade layers, and themes - -The color system is now derived: curve knobs → ramp stops → color stops → -utility classes, computed in pure CSS (`calc()`/`pow()`/`oklch()`), fitted to -minimize the perceptual delta from the old HSL palette. Breaking changes: - -- **`color_` family renamed to `palette_`**: variables `--color_a_50` → - `--palette_a_50`, token classes `.color_a_50` → `.palette_a_50`, and semantic - component classes `.color_a`–`.color_j` → `.palette_a`–`.palette_j`. In - compound class families the letter alone implies the palette: - `border_color_X_NN` → `border_X_NN`, `outline_color_X_NN` → `outline_X_NN`, - `shadow_color_X_NN` → `shadow_X_NN`, and `bg_X_NN` keeps its name. The - letterless families are unchanged (`border_color_NN` alpha ramp, - `outline_color_NN` shade outlines, `shadow_color_umbra` semantic colors). - At the TS level, the letter list in `variable_data.ts` renames with the - family: `ColorVariant`/`color_variants` → `PaletteVariant`/`palette_variants`. -- **`--hue_a`…`--hue_j` values reinterpreted** as OKLCH hue angles (blue is - now `250`, not HSL `210`). Any consumer CSS doing `hsl(var(--hue_x) …)` - breaks — use `oklch( var(--hue_x))` or the palette/intent stops. -- **`--tint_hue`/`--tint_saturation` removed** → `--hue_neutral` (defaults to - `var(--hue_f)`) + `--neutral_chroma` (peak chroma of the neutral scales). -- **Absolute `_light`/`_dark` variants removed**: the ~286 generated - variables (`--color_a_50_light`-style, including `--shade_XX_light/dark`) - and all their classes — `color_X_NN_light/dark`, `bg_X_NN_light/dark`, and - `shade_NN_light/dark`. Write the literal color or define one custom - property instead. -- **New curve knobs** (the promoted theme API): `--chroma_scale`, - `--hue_shift`, `--palette_lightness_00/_100/_curve` (same trio for - `shade_`/`text_`), `--palette_chroma_min/_max/_curve` (clamped per stop by - baked worst-hue sRGB gamut caps), plus derived per-stop variables - (`--palette_lightness_NN`, `--palette_chroma_NN`, `--chroma_shape_NN`, - `--hue_shift_NN`) that themes can pin individually. -- **New semantic intent knobs**: `--hue_accent`, `--hue_positive`, - `--hue_negative`, `--hue_caution`, `--hue_info`, each deriving a full - 13-stop scale through the shared ramps (`--accent_00`…`--accent_100`, same - for positive/negative/caution/info) with matching lazily-generated text and - background token classes (`.positive_50`, `.bg_caution_10`), plus the - `--selection_color` site variable and the `intent_variants`/`IntentVariant` - list in `variable_data.ts`. Links, focus, selection, selected states, - `accent-color`, and disabled-active feedback all route through them; focus - now follows the element color (via `--outline_color`) with the accent as - fallback. -- **Cascade layers**: all shipped CSS is layered `fuz.base` (defaults) < - `fuz.theme` (theme overrides) < `fuz.utilities` (generated classes); - consumers' unlayered styles beat everything. `render_theme_style` lost its - `specificity` option (the `:root:root` hack) and gained - `layer?: string | null` (default `'fuz.theme'`); `generate_theme_css` lost - its specificity parameter; the `theme_specificity` generator option is - removed. `render_theme_style`'s default-theme special case now keys on - empty `variables` rather than the `'base'` name — a theme that carries - variables always renders them regardless of its name; an empty theme still - renders nothing by default and the full `default_variables` set under - `empty_default_theme: false`. Custom `base_css` input is re-layered into - `fuz.base` in bundled output — its own `@layer` identities aren't - preserved. -- **`color-mix()` interpolation moved from `in hsl` to `in oklab`** in button - fills/borders, composites, and shadow classes. -- **Themes**: one module per theme under `themes/` - (`@fuzdev/fuz_css/themes/necromancer.ts` etc.); `themes.ts` exports the - curated `default_themes` registry (base, low contrast, high contrast) - with unregistered expressive exemplars (necromancer, sunset ember, - brutalish, terminal green + the `create_terminal_theme(hue)` factory). - The contrast themes are rewritten as curve-knob overrides. -- **New design-time modules**: `ramps.ts` (fitted knob constants, numeric - evaluators, CSS emitters), `oklch.ts` (OKLCH↔sRGB + gamut math), `wcag.ts` - (luminance/contrast), with tests gating every default stop for gamut, - monotonicity, and contrast (AA/AAA thresholds the old palette partly - failed). From a3cc9e2253ce36327a8190b1fb1019dff3259233 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Thu, 16 Jul 2026 12:40:57 -0400 Subject: [PATCH 14/33] add hue shift and remove an unwanted terminal theme --- src/lib/themes/necromancer.ts | 18 +++++++++--------- src/lib/themes/terminal.ts | 21 +++++++++++++++++---- src/routes/KnobControl.svelte | 19 +++++++++---------- src/routes/docs/themes/+page.svelte | 9 +-------- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/lib/themes/necromancer.ts b/src/lib/themes/necromancer.ts index 99dcbdef9..f2fb91eb4 100644 --- a/src/lib/themes/necromancer.ts +++ b/src/lib/themes/necromancer.ts @@ -2,9 +2,9 @@ import type {Theme} from '../theme.ts'; /** * A fantasy exemplar theme built entirely from semantic bindings and levers — - * the palette letters keep their default hues. Violet surfaces come from - * binding the neutral to purple, the ectoplasm-green accent from binding the - * accent intent to green, and the lit-from-below character from hue-shifted + * the palette letters keep their default hues. Ectoplasm-green surfaces come + * from binding the neutral to green, the violet accent from binding the accent + * intent to purple, and the lit-from-below character from hue-shifted * ramps (highlights warm, shadows cool) with glow-colored depth. Dark-only * via the `scheme` stance, vivid past the gamut caps on purpose. * @@ -19,13 +19,13 @@ export const necromancer_theme: Theme = { name: 'necromancer', scheme: 'dark', variables: [ - // grave-violet surfaces and text: the neutral binds to the purple slot - {name: 'hue_neutral', light: 'var(--hue_d)'}, + // ectoplasm-green surfaces and text: the neutral binds to the green slot + {name: 'hue_neutral', light: 'var(--hue_b)'}, {name: 'neutral_chroma', light: '0.04'}, - // ectoplasm accent: links/focus/selection glow green over the violet world - {name: 'hue_accent', light: 'var(--hue_b)'}, - // painterly ramps: shadows cool toward blue-violet, highlights warm toward magenta - {name: 'hue_shift', light: '-14'}, + // violet accent: links/focus/selection glow purple over the green world + {name: 'hue_accent', light: 'var(--hue_d)'}, + // painterly ramps: shadows cool toward teal, highlights warm toward yellow-green + {name: 'hue_shift', light: '14'}, // vivid, knowingly clipping the weak hues {name: 'chroma_scale', light: '1.15'}, // modestly compact - crypt density diff --git a/src/lib/themes/terminal.ts b/src/lib/themes/terminal.ts index 4a75a887b..a963f680c 100644 --- a/src/lib/themes/terminal.ts +++ b/src/lib/themes/terminal.ts @@ -5,13 +5,23 @@ import type {Theme} from '../theme.ts'; * 145, amber at 70, cool blue at 250. Every palette slot and the neutral * collapse onto the one hue (a palette-tier move, which is why terminal * themes live outside the semantic-only registry); expressiveness comes from - * the ramps doing the work. Dark-only via the `scheme` stance, mono type, - * sharp corners, flat depth. + * the ramps doing the work — a slight hue slant blooms the bright end warm and + * the dim end cool, phosphor-style, so the monochrome reads lit rather than + * flat. Dark-only via the `scheme` stance, mono type, sharp corners, no shadows. * * Declared subversions: negative/caution/info all render in the terminal hue * — status legibility is traded for the monochrome premise. + * + * @param hue - the OKLCH hue every slot collapses onto + * @param options - `name` labels the theme; `hue_shift` tunes the phosphor + * bloom, whose ideal warm/cool slant differs by hue, `0` for a dead-flat + * monochrome. */ -export const create_terminal_theme = (hue: number, name = `terminal ${hue}`): Theme => { +export const create_terminal_theme = ( + hue: number, + options: {name?: string; hue_shift?: number} = {}, +): Theme => { + const {name = `terminal ${hue}`, hue_shift = 15} = options; const hue_value = String(hue); return { name, @@ -31,6 +41,9 @@ export const create_terminal_theme = (hue: number, name = `terminal ${hue}`): Th {name: 'hue_neutral', light: hue_value}, // tinted surfaces and text {name: 'neutral_chroma', light: '0.05'}, + // phosphor bloom: the ramps warm the bright end and cool the dim end so + // the monochrome isn't a flat fill, staying unmistakably one hue + {name: 'hue_shift', light: String(hue_shift)}, // mono type everywhere {name: 'font_family_sans', light: 'var(--font_family_mono)'}, // sharp: one knob zeroes every radius tier @@ -44,4 +57,4 @@ export const create_terminal_theme = (hue: number, name = `terminal ${hue}`): Th /** * The classic green phosphor terminal. */ -export const terminal_green_theme: Theme = create_terminal_theme(145, 'terminal green'); +export const terminal_green_theme: Theme = create_terminal_theme(145, {name: 'terminal green'}); diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index 4ed7cbef2..d5675626c 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -58,6 +58,9 @@ }; const numeric_value = $derived(resolve_numeric(value)); + // a live swatch of the current angle for the detach ("custom") button, so it + // tracks the hue the way the letter buttons track their palette slot + const custom_color = $derived(`oklch(0.65 0.14 ${numeric_value ?? 0})`); const scalar = $derived( knob.kind === 'hue' || knob.kind === 'number' || @@ -90,19 +93,22 @@ {#each palette_variants as letter (letter)} {/each} @@ -208,11 +214,4 @@ font-size: var(--font_size_sm); font-family: var(--font_family_mono); } - .letter_swatch { - display: inline-block; - width: 0.8em; - height: 0.8em; - margin-right: var(--space_xs2); - border-radius: var(--border_radius_xs2); - } diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index a40bc81e6..60a890720 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -15,7 +15,7 @@ import {necromancer_theme} from '$lib/themes/necromancer.ts'; import {sunset_ember_theme} from '$lib/themes/sunset_ember.ts'; import {brutalish_theme} from '$lib/themes/brutalish.ts'; - import {create_terminal_theme, terminal_green_theme} from '$lib/themes/terminal.ts'; + import {terminal_green_theme} from '$lib/themes/terminal.ts'; import type {Theme} from '$lib/theme.ts'; import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; import ThemeEditor from '$routes/ThemeEditor.svelte'; @@ -34,18 +34,11 @@ const theme_state = get_theme_state(); const themes = default_themes.slice(); - // amber runs modestly denser than green phosphor - plain data composition - const terminal_amber_base = create_terminal_theme(70, 'terminal amber'); - const terminal_amber_theme: Theme = { - ...terminal_amber_base, - variables: [...terminal_amber_base.variables, {name: 'scale_factor', light: '0.9'}], - }; const exemplar_themes = [ necromancer_theme, sunset_ember_theme, brutalish_theme, terminal_green_theme, - terminal_amber_theme, ]; const editor = new ThemeEditorState([...themes, ...exemplar_themes]); From 9263f076e8b23c64564a2236b886279d1b42d626 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Thu, 16 Jul 2026 13:08:09 -0400 Subject: [PATCH 15/33] wip --- CLAUDE.md | 6 ++-- src/lib/themes/terminal.ts | 51 +++++++++++++++++++++-------- src/routes/docs/themes/+page.svelte | 9 ++--- src/test/theme_check.test.ts | 10 +++--- src/test/themes.test.ts | 2 +- 5 files changed, 50 insertions(+), 28 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 15c0a3739..08b78c7b5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -353,8 +353,10 @@ typography, borders, shading, shadows, layout. See - `src/lib/themes/` - One module per theme. The registry (base, low/high contrast) is semantic-tier: intent bindings + levers only, palette hues untouched. Unregistered exemplars: necromancer, sunset ember, brutalish, - and `terminal.ts` (a `create_terminal_theme(hue)` factory; terminal green - = 145); necromancer and terminal are dark-only via `scheme: 'dark'` + and `terminal.ts` (the `terminal_theme` flagship — dark mono terminal chrome + with a green cast but functional palette — plus a `create_terminal_theme(hue)` + factory for pure single-phosphor monochrome terminals); necromancer and + terminal are dark-only via `scheme: 'dark'` - [knobs.ts](src/lib/knobs.ts) - The theme knob catalog: typed metadata (kind/axis/leverage/tier/bindable/range) for the knob-tier variables, joined against `default_variables` by name; includes hook knobs like diff --git a/src/lib/themes/terminal.ts b/src/lib/themes/terminal.ts index a963f680c..ee34fef86 100644 --- a/src/lib/themes/terminal.ts +++ b/src/lib/themes/terminal.ts @@ -1,13 +1,43 @@ import type {Theme} from '../theme.ts'; /** - * Creates a monochrome terminal theme at any OKLCH hue — green phosphor at - * 145, amber at 70, cool blue at 250. Every palette slot and the neutral - * collapse onto the one hue (a palette-tier move, which is why terminal - * themes live outside the semantic-only registry); expressiveness comes from - * the ramps doing the work — a slight hue slant blooms the bright end warm and - * the dim end cool, phosphor-style, so the monochrome reads lit rather than - * flat. Dark-only via the `scheme` stance, mono type, sharp corners, no shadows. + * A practical terminal theme: the dark, monospace, sharp-cornered, flat-depth + * terminal chrome with a green-phosphor cast in the surfaces and accent, but + * the palette slots keep their own hues so status colors still read — negative + * stays red, caution amber, info cyan. The usable twist on the pure + * single-phosphor collapse of `create_terminal_theme`. Dark-only via the + * `scheme` stance. + */ +export const terminal_theme: Theme = { + name: 'terminal', + scheme: 'dark', + variables: [ + // green-phosphor surfaces and text — the terminal cast, kept low-chroma so + // the palette colors read over it + {name: 'hue_neutral', light: 'var(--hue_b)'}, + {name: 'neutral_chroma', light: '0.05'}, + // links, focus, selection glow phosphor green + {name: 'hue_accent', light: 'var(--hue_b)'}, + // a whisper of phosphor bloom across every ramp + {name: 'hue_shift', light: '12'}, + // mono type everywhere + {name: 'font_family_sans', light: 'var(--font_family_mono)'}, + // sharp: one knob zeroes every radius tier + {name: 'radius_scale', light: '0'}, + // flat: one knob zeroes the whole alpha ramp, button shadows included + {name: 'shadow_alpha_scale', light: '0'}, + ], +}; + +/** + * Creates a pure single-phosphor terminal at any OKLCH hue — green at 145, + * amber at 70, cool blue at 250. Every palette slot and the neutral collapse + * onto the one hue (a palette-tier move, which is why terminal themes live + * outside the semantic-only registry); expressiveness comes from the ramps + * doing the work — a hue slant blooms the bright end warm and the dim end cool, + * phosphor-style, so the monochrome reads lit rather than flat. The strict + * monochrome counterpart to `terminal_theme`. Dark-only via the `scheme` + * stance, mono type, sharp corners, no shadows. * * Declared subversions: negative/caution/info all render in the terminal hue * — status legibility is traded for the monochrome premise. @@ -21,7 +51,7 @@ export const create_terminal_theme = ( hue: number, options: {name?: string; hue_shift?: number} = {}, ): Theme => { - const {name = `terminal ${hue}`, hue_shift = 15} = options; + const {name = `terminal ${hue}`, hue_shift = 45} = options; const hue_value = String(hue); return { name, @@ -53,8 +83,3 @@ export const create_terminal_theme = ( ], }; }; - -/** - * The classic green phosphor terminal. - */ -export const terminal_green_theme: Theme = create_terminal_theme(145, {name: 'terminal green'}); diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index 60a890720..58dd0d64c 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -15,7 +15,7 @@ import {necromancer_theme} from '$lib/themes/necromancer.ts'; import {sunset_ember_theme} from '$lib/themes/sunset_ember.ts'; import {brutalish_theme} from '$lib/themes/brutalish.ts'; - import {terminal_green_theme} from '$lib/themes/terminal.ts'; + import {terminal_theme} from '$lib/themes/terminal.ts'; import type {Theme} from '$lib/theme.ts'; import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; import ThemeEditor from '$routes/ThemeEditor.svelte'; @@ -34,12 +34,7 @@ const theme_state = get_theme_state(); const themes = default_themes.slice(); - const exemplar_themes = [ - necromancer_theme, - sunset_ember_theme, - brutalish_theme, - terminal_green_theme, - ]; + const exemplar_themes = [necromancer_theme, sunset_ember_theme, brutalish_theme, terminal_theme]; const editor = new ThemeEditorState([...themes, ...exemplar_themes]); diff --git a/src/test/theme_check.test.ts b/src/test/theme_check.test.ts index 4a2b0428e..e0f0c77e4 100644 --- a/src/test/theme_check.test.ts +++ b/src/test/theme_check.test.ts @@ -8,7 +8,7 @@ import {high_contrast_theme} from '$lib/themes/high_contrast.ts'; import {necromancer_theme} from '$lib/themes/necromancer.ts'; import {sunset_ember_theme} from '$lib/themes/sunset_ember.ts'; import {brutalish_theme} from '$lib/themes/brutalish.ts'; -import {terminal_green_theme, create_terminal_theme} from '$lib/themes/terminal.ts'; +import {terminal_theme, create_terminal_theme} from '$lib/themes/terminal.ts'; import { PALETTE_HUES, PALETTE_CHROMA_KNOBS, @@ -28,7 +28,7 @@ describe('validate_theme', () => { necromancer_theme, sunset_ember_theme, brutalish_theme, - terminal_green_theme, + terminal_theme, create_terminal_theme(70), // amber, exercises the factory ]; for (const theme of themes) { @@ -213,8 +213,8 @@ describe('check_theme', () => { assert.isTrue(check_theme(brutalish_theme).ok); }); - test('terminal green keeps its contrast gates', () => { - const contrast = check_theme(terminal_green_theme).entries.filter((e) => e.gate === 'contrast'); + test('terminal keeps its contrast gates', () => { + const contrast = check_theme(terminal_theme).entries.filter((e) => e.gate === 'contrast'); assert.isTrue(contrast.every((e) => e.pass)); }); @@ -287,7 +287,7 @@ describe('scheme stance', () => { }); test('the dark-stanced exemplars pass their contrast gates in both schemes', () => { - for (const theme of [necromancer_theme, terminal_green_theme]) { + for (const theme of [necromancer_theme, terminal_theme]) { const contrast = check_theme(theme).entries.filter((e) => e.gate === 'contrast'); assert.isTrue( contrast.every((e) => e.pass), diff --git a/src/test/themes.test.ts b/src/test/themes.test.ts index 2b96194c6..5e18f5839 100644 --- a/src/test/themes.test.ts +++ b/src/test/themes.test.ts @@ -100,7 +100,7 @@ describe('shipped themes', () => { assert.include(names, 'necromancer'); assert.include(names, 'sunset ember'); assert.include(names, 'brutalish'); - assert.include(names, 'terminal green'); + assert.include(names, 'terminal'); }); test('all exemplar variables validate and exist in default_variables', () => { From a5c6037190047330483f3869e95a3bef8dae2989 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Wed, 22 Jul 2026 15:01:45 -0400 Subject: [PATCH 16/33] wip --- CLAUDE.md | 2 +- package.json | 27 ++++----------------------- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 08b78c7b5..759a489fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -476,7 +476,7 @@ Integration: `vite_plugin_examples.test.ts` (skip with - TypeScript strict mode - Svelte 5 with runes API (for docs site) -- Prettier with tabs, 100 char width +- tsv with tabs, 100 char width - Node >= 24.14 - Tests in `src/test/` (not co-located) diff --git a/package.json b/package.json index 94778f841..47ad77e22 100644 --- a/package.json +++ b/package.json @@ -69,12 +69,12 @@ "devDependencies": { "@changesets/changelog-git": "^0.2.1", "@fuzdev/blake3_wasm": "^0.1.1", - "@fuzdev/fuz_code": "^0.47.0", + "@fuzdev/fuz_code": "^0.48.0", "@fuzdev/fuz_ui": "^0.206.7", "@fuzdev/fuz_util": "^0.65.2", - "@fuzdev/gro": "^0.206.0", + "@fuzdev/gro": "^0.207.0", "@fuzdev/mdz": "^0.3.0", - "@ryanatkn/eslint-config": "^0.12.1", + "@ryanatkn/eslint-config": "^0.12.2", "@sveltejs/acorn-typescript": "^1.0.9", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.66.0", @@ -88,11 +88,9 @@ "eslint-plugin-svelte": "^3.13.1", "esm-env": "^1.2.2", "magic-string": "^0.30.21", - "prettier": "^3.7.4", - "prettier-plugin-svelte": "^3.4.1", "svelte": "^5.56.3", "svelte-check": "^4.4.6", - "svelte-docinfo": "^0.5.1", + "svelte-docinfo": "^0.5.4", "svelte2tsx": "^0.7.51", "tslib": "^2.8.1", "typescript": "^5.9.3", @@ -102,23 +100,6 @@ "zimmerframe": "^1.1.4", "zod": "^4.3.6" }, - "prettier": { - "plugins": [ - "prettier-plugin-svelte" - ], - "useTabs": true, - "printWidth": 100, - "singleQuote": true, - "bracketSpacing": false, - "overrides": [ - { - "files": "package.json", - "options": { - "useTabs": false - } - } - ] - }, "sideEffects": [ "**/*.css" ], From 187375d464e5ddcab231a39de6dff731c0b8d886 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Fri, 24 Jul 2026 09:46:17 -0400 Subject: [PATCH 17/33] wip --- CLAUDE.md | 6 +- eslint.config.js | 4 +- examples/vite-preact/src/App.tsx | 57 +- examples/vite-preact/src/main.tsx | 4 +- examples/vite-preact/vite.config.ts | 10 +- examples/vite-react/src/App.tsx | 57 +- examples/vite-react/src/main.tsx | 4 +- examples/vite-react/vite.config.ts | 10 +- examples/vite-solid/src/App.tsx | 57 +- examples/vite-solid/src/main.tsx | 4 +- examples/vite-solid/vite.config.ts | 10 +- examples/vite-svelte/src/App.svelte | 8 +- examples/vite-svelte/src/main.ts | 4 +- examples/vite-svelte/vite.config.ts | 10 +- package-lock.json | 83 +- package.json | 22 +- src/app.d.ts | 2 +- src/app.html | 2 +- src/lib/bundled_resources.ts | 21 +- src/lib/class_variable_index.ts | 14 +- src/lib/css_bundled_resolution.ts | 36 +- src/lib/css_cache.ts | 30 +- src/lib/css_class_composites.ts | 48 +- src/lib/css_class_definitions.ts | 108 +- src/lib/css_class_extractor.ts | 182 +-- src/lib/css_class_generation.ts | 50 +- src/lib/css_class_generators.ts | 70 +- src/lib/css_class_interpreters.ts | 26 +- src/lib/css_class_resolution.ts | 68 +- src/lib/css_classes.ts | 8 +- src/lib/css_literal.ts | 124 +- src/lib/css_plugin_options.ts | 10 +- src/lib/css_ruleset_parser.ts | 34 +- src/lib/deps.ts | 15 +- src/lib/deps_defaults.ts | 26 +- src/lib/diagnostics.ts | 4 +- src/lib/example_class_utilities.ts | 2 +- src/lib/extract_file_cached.ts | 16 +- src/lib/gen_fuz_css.ts | 72 +- src/lib/generate_css.ts | 30 +- src/lib/knobs.ts | 219 ++-- src/lib/modifiers.ts | 164 +-- src/lib/oklch.ts | 4 +- src/lib/ramps.ts | 56 +- src/lib/style_rule_parser.ts | 79 +- src/lib/theme.gen.css.ts | 10 +- src/lib/theme.ts | 14 +- src/lib/theme_check.ts | 164 +-- src/lib/themes.ts | 10 +- src/lib/themes/base.ts | 4 +- src/lib/themes/brutalish.ts | 22 +- src/lib/themes/high_contrast.ts | 8 +- src/lib/themes/low_contrast.ts | 8 +- src/lib/themes/necromancer.ts | 36 +- src/lib/themes/sunset_ember.ts | 16 +- src/lib/themes/terminal.ts | 56 +- src/lib/variable.ts | 8 +- src/lib/variable_data.ts | 36 +- src/lib/variable_graph.ts | 32 +- src/lib/variables.ts | 1134 +++++++++-------- src/lib/vite_plugin_fuz_css.ts | 84 +- src/lib/wcag.ts | 2 +- src/routes/+layout.svelte | 14 +- src/routes/+page.svelte | 10 +- src/routes/FileLink.svelte | 17 +- src/routes/FontSizeControl.svelte | 6 +- src/routes/FontWeightControl.svelte | 4 +- src/routes/Introduction.svelte | 6 +- src/routes/KnobControl.svelte | 13 +- src/routes/RampStrip.svelte | 4 +- src/routes/SourceFileLink.svelte | 2 +- src/routes/StyleVariableDetail.svelte | 4 +- src/routes/ThemeEditor.svelte | 20 +- src/routes/about/+page.svelte | 10 +- src/routes/docs/+layout.svelte | 14 +- src/routes/docs/+page.svelte | 4 +- .../UnfinishedImplementationWarning.svelte | 4 +- src/routes/docs/api/+page.svelte | 4 +- .../docs/api/[...module_path]/+page.svelte | 2 +- src/routes/docs/borders/+page.svelte | 31 +- src/routes/docs/buttons/+page.svelte | 24 +- src/routes/docs/chips/+page.svelte | 8 +- src/routes/docs/classes/+page.svelte | 233 ++-- src/routes/docs/colors/+page.svelte | 36 +- src/routes/docs/colors/ColorSwatch.svelte | 4 +- src/routes/docs/colors/ColorSwatchItem.svelte | 10 +- src/routes/docs/colors/HueSwatch.svelte | 4 +- src/routes/docs/elements/+page.svelte | 18 +- src/routes/docs/examples/+page.svelte | 7 +- src/routes/docs/forms/+page.svelte | 18 +- src/routes/docs/introduction/+page.svelte | 17 +- src/routes/docs/layout/+page.svelte | 6 +- src/routes/docs/menuitem/todo.svelte | 4 +- src/routes/docs/semantic/+page.svelte | 26 +- src/routes/docs/shading/+page.svelte | 37 +- src/routes/docs/shadows/+page.svelte | 15 +- src/routes/docs/themes/+page.svelte | 71 +- src/routes/docs/tomes.ts | 38 +- src/routes/docs/typography/+page.svelte | 10 +- src/routes/docs/typography/IconSizes.svelte | 2 +- src/routes/docs/variables/+page.svelte | 11 +- src/routes/library.ts | 4 +- src/routes/theme_editor_state.svelte.ts | 34 +- src/test/class_variable_index.test.ts | 50 +- ...css_bundled_resolution.diagnostics.test.ts | 180 +-- src/test/css_bundled_resolution.test.ts | 298 ++--- .../css_bundled_resolution.variables.test.ts | 206 +-- src/test/css_bundled_resolution_fixtures.ts | 16 +- src/test/css_cache.test.ts | 191 +-- src/test/css_class_extractor.elements.test.ts | 30 +- .../css_class_extractor.expressions.test.ts | 44 +- src/test/css_class_extractor.jsx.test.ts | 8 +- .../css_class_extractor.locations.test.ts | 25 +- src/test/css_class_extractor.test.ts | 112 +- .../css_class_extractor.tracked_vars.test.ts | 86 +- .../css_class_extractor.typescript.test.ts | 28 +- .../css_class_extractor.utilities.test.ts | 44 +- src/test/css_class_extractor_test_helpers.ts | 24 +- src/test/css_class_generators.test.ts | 41 +- .../css_class_resolution.literals.test.ts | 62 +- src/test/css_class_resolution.test.ts | 262 ++-- src/test/css_classes.test.ts | 235 ++-- src/test/css_literal.test.ts | 174 +-- src/test/css_plugin_options.test.ts | 34 +- .../css_ruleset_parser.generation.test.ts | 66 +- src/test/css_ruleset_parser.modifiers.test.ts | 82 +- src/test/css_ruleset_parser.parse.test.ts | 4 +- src/test/css_ruleset_parser.selectors.test.ts | 8 +- src/test/css_variable_utils.test.ts | 10 +- src/test/diagnostics.test.ts | 44 +- src/test/escape_css_selector.test.ts | 8 +- src/test/explicit_classes.test.ts | 40 +- src/test/file_filter.test.ts | 4 +- .../fixtures/css_classes_fixture.gen.json.ts | 6 +- src/test/fixtures/mock_deps.ts | 22 +- src/test/fuz_comments.test.ts | 24 +- src/test/generate_bundled_css.test.ts | 24 +- src/test/generate_classes_css.test.ts | 334 ++--- src/test/generate_css.test.ts | 84 +- src/test/knobs.test.ts | 12 +- src/test/modified_class_interpreter.test.ts | 186 +-- src/test/modifiers.test.ts | 18 +- src/test/oklch.test.ts | 10 +- src/test/ramps.test.ts | 40 +- src/test/style_rule_parser.custom.test.ts | 4 +- src/test/style_rule_parser.test.ts | 30 +- src/test/styles.test.ts | 10 +- src/test/test_helpers.ts | 48 +- src/test/theme.test.ts | 46 +- src/test/theme_check.compile.test.ts | 30 +- src/test/theme_check.test.ts | 116 +- src/test/themes.test.ts | 20 +- src/test/variable.test.ts | 16 +- src/test/variable_graph.test.ts | 239 ++-- src/test/variables.test.ts | 16 +- src/test/vite_plugin_examples.test.ts | 24 +- src/test/vite_plugin_fuz_css.dev.test.ts | 26 +- svelte.config.js | 30 +- tsconfig.json | 4 +- vite.config.ts | 18 +- 160 files changed, 4170 insertions(+), 4003 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 759a489fd..11071a2de 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -263,8 +263,8 @@ your code uses: ```ts // vite.config.ts -import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; -export default defineConfig({plugins: [vite_plugin_fuz_css()]}); +import { vite_plugin_fuz_css } from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; +export default defineConfig({ plugins: [vite_plugin_fuz_css()] }); // main.ts (or your SvelteKit root layout) import 'virtual:fuz.css'; @@ -285,7 +285,7 @@ declare module 'virtual:fuz.css' { ```ts // src/routes/fuz.gen.css.ts -import {gen_fuz_css} from '@fuzdev/fuz_css/gen_fuz_css.ts'; +import { gen_fuz_css } from '@fuzdev/fuz_css/gen_fuz_css.ts'; export const gen = gen_fuz_css(); ``` diff --git a/eslint.config.js b/eslint.config.js index c59655eb8..68331fad6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,6 +1,6 @@ -import {configs, ts_config} from '@ryanatkn/eslint-config'; +import { configs, ts_config } from '@ryanatkn/eslint-config'; ts_config.rules['no-console'] = 1; // Ignore examples directory - each example has its own tsconfig -export default [{ignores: ['examples/**']}, ...configs]; +export default [{ ignores: ['examples/**'] }, ...configs]; diff --git a/examples/vite-preact/src/App.tsx b/examples/vite-preact/src/App.tsx index cf44759da..5af4d7668 100644 --- a/examples/vite-preact/src/App.tsx +++ b/examples/vite-preact/src/App.tsx @@ -1,4 +1,4 @@ -import {useState} from 'preact/hooks'; +import { useState } from 'preact/hooks'; // Import from node_modules to verify extraction works for dependencies import { @@ -22,7 +22,7 @@ import { arrayClasses, objectClasses, // Comment hint examples - fromComment, + fromComment } from '@fuzdev/fuz_css/example_class_utilities.ts'; export const App = () => { @@ -33,7 +33,11 @@ export const App = () => {

fuz_css + Preact

-

Utility classes generated on-demand via Vite plugin (docs, source)

+

+ Utility classes generated on-demand via Vite plugin ( + docs,{' '} + source) +

{/* Class types */} @@ -50,7 +54,7 @@ export const App = () => {

Composite classes

.box
-
+
.ellipsis -- this text truncates with ellipsis when it overflows
@@ -59,7 +63,9 @@ export const App = () => {

Literal classes

.opacity:60%
.color:var(--palette_j_50)
-
.box-shadow:0~4px~8px~rgb(0,0,0,0.2) (~ encodes spaces)
+
+ .box-shadow:0~4px~8px~rgb(0,0,0,0.2) (~ encodes spaces) +
@@ -71,28 +77,42 @@ export const App = () => {

Responsive

-

.column .gap_md on mobile, .md:flex-direction:row .md:gap_lg on medium+ screens

+

+ .column .gap_md on mobile, .md:flex-direction:row .md:gap_lg on medium+ screens +

Resize the window to see the layout switch from column to row

-

.min-width(543px):font_size_lg -- arbitrary breakpoint

+

+ .min-width(543px):font_size_lg -- arbitrary breakpoint +

Interactive

- - .hover:border_b_50 .hover:outline_b_50 .active:border_d_50 .active:outline_d_50 + + .hover:border_b_50 .hover:outline_b_50 .active:border_d_50 .active:outline_d_50 +
- - .hover:border_g_50 .hover:outline_g_50 .active:border_h_50 .active:outline_h_50 + + .hover:border_g_50 .hover:outline_g_50 .active:border_h_50 .active:outline_h_50 +
@@ -100,7 +120,10 @@ export const App = () => { {/* Extraction */}

Extraction

-

Classes detected via naming conventions, expressions, and comments (examples imported from node_modules to verify dependency scanning)

+

+ Classes detected via naming conventions, expressions, and comments (examples imported + from node_modules to verify dependency scanning) +

Naming patterns

@@ -144,7 +167,15 @@ export const App = () => {
diff --git a/examples/vite-preact/src/main.tsx b/examples/vite-preact/src/main.tsx index 524746d96..7638a6c24 100644 --- a/examples/vite-preact/src/main.tsx +++ b/examples/vite-preact/src/main.tsx @@ -1,6 +1,6 @@ import 'virtual:fuz.css'; -import {render} from 'preact'; -import {App} from './App.tsx'; +import { render } from 'preact'; +import { App } from './App.tsx'; render(, document.getElementById('root')!); diff --git a/examples/vite-preact/vite.config.ts b/examples/vite-preact/vite.config.ts index ada615546..438cc4200 100644 --- a/examples/vite-preact/vite.config.ts +++ b/examples/vite-preact/vite.config.ts @@ -1,14 +1,14 @@ -import {defineConfig} from 'vite'; +import { defineConfig } from 'vite'; import preact from '@preact/preset-vite'; import jsx from 'acorn-jsx'; -import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; +import { vite_plugin_fuz_css } from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; export default defineConfig({ plugins: [ // Plugin order doesn't matter for preact (fuz_css uses enforce: 'pre') preact(), vite_plugin_fuz_css({ - acorn_plugins: [jsx()], - }), - ], + acorn_plugins: [jsx()] + }) + ] }); diff --git a/examples/vite-react/src/App.tsx b/examples/vite-react/src/App.tsx index e35c8afff..25d3bc246 100644 --- a/examples/vite-react/src/App.tsx +++ b/examples/vite-react/src/App.tsx @@ -1,4 +1,4 @@ -import {useState} from 'react'; +import { useState } from 'react'; // Import from node_modules to verify extraction works for dependencies import { @@ -22,7 +22,7 @@ import { arrayClasses, objectClasses, // Comment hint examples - fromComment, + fromComment } from '@fuzdev/fuz_css/example_class_utilities.ts'; export const App = () => { @@ -33,7 +33,11 @@ export const App = () => {

fuz_css + React

-

Utility classes generated on-demand via Vite plugin (docs, source)

+

+ Utility classes generated on-demand via Vite plugin ( + docs,{' '} + source) +

{/* Class types */} @@ -50,7 +54,7 @@ export const App = () => {

Composite classes

.box
-
+
.ellipsis -- this text truncates with ellipsis when it overflows
@@ -59,7 +63,9 @@ export const App = () => {

Literal classes

.opacity:60%
.color:var(--palette_j_50)
-
.box-shadow:0~4px~8px~rgb(0,0,0,0.2) (~ encodes spaces)
+
+ .box-shadow:0~4px~8px~rgb(0,0,0,0.2) (~ encodes spaces) +
@@ -71,28 +77,42 @@ export const App = () => {

Responsive

-

.column .gap_md on mobile, .md:flex-direction:row .md:gap_lg on medium+ screens

+

+ .column .gap_md on mobile, .md:flex-direction:row .md:gap_lg on medium+ screens +

Resize the window to see the layout switch from column to row

-

.min-width(543px):font_size_lg -- arbitrary breakpoint

+

+ .min-width(543px):font_size_lg -- arbitrary breakpoint +

Interactive

- - .hover:border_b_50 .hover:outline_b_50 .active:border_d_50 .active:outline_d_50 + + .hover:border_b_50 .hover:outline_b_50 .active:border_d_50 .active:outline_d_50 +
- - .hover:border_g_50 .hover:outline_g_50 .active:border_h_50 .active:outline_h_50 + + .hover:border_g_50 .hover:outline_g_50 .active:border_h_50 .active:outline_h_50 +
@@ -100,7 +120,10 @@ export const App = () => { {/* Extraction */}

Extraction

-

Classes detected via naming conventions, expressions, and comments (examples imported from node_modules to verify dependency scanning)

+

+ Classes detected via naming conventions, expressions, and comments (examples imported + from node_modules to verify dependency scanning) +

Naming patterns

@@ -144,7 +167,15 @@ export const App = () => {
diff --git a/examples/vite-react/src/main.tsx b/examples/vite-react/src/main.tsx index 6d6b70c21..2f20aa28a 100644 --- a/examples/vite-react/src/main.tsx +++ b/examples/vite-react/src/main.tsx @@ -1,6 +1,6 @@ import 'virtual:fuz.css'; -import {createRoot} from 'react-dom/client'; -import {App} from './App.tsx'; +import { createRoot } from 'react-dom/client'; +import { App } from './App.tsx'; createRoot(document.getElementById('root')!).render(); diff --git a/examples/vite-react/vite.config.ts b/examples/vite-react/vite.config.ts index 38eddc178..276b4f3b4 100644 --- a/examples/vite-react/vite.config.ts +++ b/examples/vite-react/vite.config.ts @@ -1,14 +1,14 @@ -import {defineConfig} from 'vite'; +import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import jsx from 'acorn-jsx'; -import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; +import { vite_plugin_fuz_css } from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; export default defineConfig({ plugins: [ // Plugin order doesn't matter for react (fuz_css uses enforce: 'pre') react(), vite_plugin_fuz_css({ - acorn_plugins: [jsx()], - }), - ], + acorn_plugins: [jsx()] + }) + ] }); diff --git a/examples/vite-solid/src/App.tsx b/examples/vite-solid/src/App.tsx index 052297a21..f047d32d8 100644 --- a/examples/vite-solid/src/App.tsx +++ b/examples/vite-solid/src/App.tsx @@ -1,4 +1,4 @@ -import {createSignal} from 'solid-js'; +import { createSignal } from 'solid-js'; // Import from node_modules to verify extraction works for dependencies import { @@ -22,7 +22,7 @@ import { arrayClasses, objectClasses, // Comment hint examples - fromComment, + fromComment } from '@fuzdev/fuz_css/example_class_utilities.ts'; export const App = () => { @@ -33,7 +33,11 @@ export const App = () => {

fuz_css + Solid

-

Utility classes generated on-demand via Vite plugin (docs, source)

+

+ Utility classes generated on-demand via Vite plugin ( + docs,{' '} + source) +

{/* Class types */} @@ -50,7 +54,7 @@ export const App = () => {

Composite classes

.box
-
+
.ellipsis -- this text truncates with ellipsis when it overflows
@@ -59,7 +63,9 @@ export const App = () => {

Literal classes

.opacity:60%
.color:var(--palette_j_50)
-
.box-shadow:0~4px~8px~rgb(0,0,0,0.2) (~ encodes spaces)
+
+ .box-shadow:0~4px~8px~rgb(0,0,0,0.2) (~ encodes spaces) +
@@ -71,28 +77,42 @@ export const App = () => {

Responsive

-

.column .gap_md on mobile, .md:flex-direction:row .md:gap_lg on medium+ screens

+

+ .column .gap_md on mobile, .md:flex-direction:row .md:gap_lg on medium+ screens +

Resize the window to see the layout switch from column to row

-

.min-width(543px):font_size_lg -- arbitrary breakpoint

+

+ .min-width(543px):font_size_lg -- arbitrary breakpoint +

Interactive

- - .hover:border_b_50 .hover:outline_b_50 .active:border_d_50 .active:outline_d_50 + + .hover:border_b_50 .hover:outline_b_50 .active:border_d_50 .active:outline_d_50 +
- - .hover:border_g_50 .hover:outline_g_50 .active:border_h_50 .active:outline_h_50 + + .hover:border_g_50 .hover:outline_g_50 .active:border_h_50 .active:outline_h_50 +
@@ -100,7 +120,10 @@ export const App = () => { {/* Extraction */}

Extraction

-

Classes detected via naming conventions, expressions, and comments (examples imported from node_modules to verify dependency scanning)

+

+ Classes detected via naming conventions, expressions, and comments (examples imported + from node_modules to verify dependency scanning) +

Naming patterns

@@ -144,7 +167,15 @@ export const App = () => {
diff --git a/examples/vite-solid/src/main.tsx b/examples/vite-solid/src/main.tsx index 7b3815d60..6a2b5d30a 100644 --- a/examples/vite-solid/src/main.tsx +++ b/examples/vite-solid/src/main.tsx @@ -1,6 +1,6 @@ import 'virtual:fuz.css'; -import {render} from 'solid-js/web'; -import {App} from './App.tsx'; +import { render } from 'solid-js/web'; +import { App } from './App.tsx'; render(() => , document.getElementById('root')!); diff --git a/examples/vite-solid/vite.config.ts b/examples/vite-solid/vite.config.ts index de604732c..b36c015d6 100644 --- a/examples/vite-solid/vite.config.ts +++ b/examples/vite-solid/vite.config.ts @@ -1,14 +1,14 @@ -import {defineConfig} from 'vite'; +import { defineConfig } from 'vite'; import solid from 'vite-plugin-solid'; import jsx from 'acorn-jsx'; -import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; +import { vite_plugin_fuz_css } from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; export default defineConfig({ plugins: [ // fuz_css must be listed first for solid (enforce: 'pre' alone isn't sufficient) vite_plugin_fuz_css({ - acorn_plugins: [jsx()], + acorn_plugins: [jsx()] }), - solid(), - ], + solid() + ] }); diff --git a/examples/vite-svelte/src/App.svelte b/examples/vite-svelte/src/App.svelte index 0cab482bb..d92d472b1 100644 --- a/examples/vite-svelte/src/App.svelte +++ b/examples/vite-svelte/src/App.svelte @@ -21,7 +21,7 @@ arrayClasses, objectClasses, // Comment hint examples - fromComment, + fromComment } from '@fuzdev/fuz_css/example_class_utilities.ts'; let count = $state(0); @@ -96,8 +96,7 @@ count: {count} .hover:border_b_50 .hover:outline_b_50 .active:border_d_50 - .active:outline_d_50.hover:border_b_50 .hover:outline_b_50 .active:border_d_50 .active:outline_d_50
@@ -108,8 +107,7 @@ reset .hover:border_g_50 .hover:outline_g_50 .active:border_h_50 - .active:outline_h_50.hover:border_g_50 .hover:outline_g_50 .active:border_h_50 .active:outline_h_50
diff --git a/examples/vite-svelte/src/main.ts b/examples/vite-svelte/src/main.ts index 86ce8fd08..c5f73e540 100644 --- a/examples/vite-svelte/src/main.ts +++ b/examples/vite-svelte/src/main.ts @@ -1,6 +1,6 @@ import 'virtual:fuz.css'; -import {mount} from 'svelte'; +import { mount } from 'svelte'; import App from './App.svelte'; -mount(App, {target: document.getElementById('root')!}); +mount(App, { target: document.getElementById('root')! }); diff --git a/examples/vite-svelte/vite.config.ts b/examples/vite-svelte/vite.config.ts index 2268fd808..ee788d189 100644 --- a/examples/vite-svelte/vite.config.ts +++ b/examples/vite-svelte/vite.config.ts @@ -1,11 +1,11 @@ -import {defineConfig} from 'vite'; -import {svelte} from '@sveltejs/vite-plugin-svelte'; -import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; +import { defineConfig } from 'vite'; +import { svelte } from '@sveltejs/vite-plugin-svelte'; +import { vite_plugin_fuz_css } from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; export default defineConfig({ plugins: [ // fuz_css must be listed first for svelte (enforce: 'pre' alone isn't sufficient) vite_plugin_fuz_css(), - svelte(), - ], + svelte() + ] }); diff --git a/package-lock.json b/package-lock.json index 4b6d59c39..f51365307 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,12 @@ "devDependencies": { "@changesets/changelog-git": "^0.2.1", "@fuzdev/blake3_wasm": "^0.1.1", - "@fuzdev/fuz_code": "^0.47.0", + "@fuzdev/fuz_code": "^0.48.0", "@fuzdev/fuz_ui": "^0.206.7", "@fuzdev/fuz_util": "^0.65.2", - "@fuzdev/gro": "^0.206.0", + "@fuzdev/gro": "^0.205.1", "@fuzdev/mdz": "^0.3.0", - "@ryanatkn/eslint-config": "^0.12.1", + "@ryanatkn/eslint-config": "^0.12.2", "@sveltejs/acorn-typescript": "^1.0.9", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.66.0", @@ -30,11 +30,11 @@ "eslint-plugin-svelte": "^3.13.1", "esm-env": "^1.2.2", "magic-string": "^0.30.21", - "prettier": "^3.7.4", - "prettier-plugin-svelte": "^3.4.1", + "prettier": "^3.9.6", + "prettier-plugin-svelte": "^4.1.1", "svelte": "^5.56.3", "svelte-check": "^4.4.6", - "svelte-docinfo": "^0.5.1", + "svelte-docinfo": "^0.5.4", "svelte2tsx": "^0.7.51", "tslib": "^2.8.1", "typescript": "^5.9.3", @@ -755,9 +755,9 @@ } }, "node_modules/@fuzdev/fuz_code": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@fuzdev/fuz_code/-/fuz_code-0.47.0.tgz", - "integrity": "sha512-ZVA4gswHigMDp62F2JKIjIJsgglF14vzgAI48QBafusoj5JIUvOEGYvajXhEMQdb2cobOQjZUxbaaST7UqGKXg==", + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@fuzdev/fuz_code/-/fuz_code-0.48.0.tgz", + "integrity": "sha512-uzcAkElwu73UULfd2Vij0U9oCL0ySZEYHd8CxYGSglxLZc4JzTIxDpFkY67glPBmAVR36CgyXpdWGGCTW3bF7A==", "dev": true, "license": "MIT", "dependencies": { @@ -920,17 +920,18 @@ } }, "node_modules/@fuzdev/gro": { - "version": "0.206.0", - "resolved": "https://registry.npmjs.org/@fuzdev/gro/-/gro-0.206.0.tgz", - "integrity": "sha512-11nkTAEWDOvkgzynRChQS+bj3tc7R0NduB+eHLXzXZJqz66nc/oop8Ttvz0UkjTBPX/zzgtU8QuDDNyA7buf5A==", + "version": "0.205.1", + "resolved": "https://registry.npmjs.org/@fuzdev/gro/-/gro-0.205.1.tgz", + "integrity": "sha512-o5WBXWSH/UlN1BsDnpBNIi0WHR7lj7UFGltH/JctSP7oSB2ojvmoXD5OLCEeVdRVcDaSQKl48xcXKUwHKqVSmQ==", "dev": true, "license": "MIT", "dependencies": { - "@fuzdev/tsv_wasm": "^0.1.0", "chokidar": "^5.0.0", "dotenv": "^17.2.3", "esm-env": "^1.2.2", "oxc-parser": "^0.99.0", + "prettier": "^3.7.4", + "prettier-plugin-svelte": "^3.5.1", "ts-blank-space": "^0.6.2", "tslib": "^2.8.1" }, @@ -985,6 +986,17 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@fuzdev/gro/node_modules/prettier-plugin-svelte": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.5.2.tgz", + "integrity": "sha512-ItFouLvzSFE3ulNl4DKoWM3BGcbDCNVpIyy/Y3F2gC3aNiGLxtFUdffVqO5Z5hhYG+DFT5KULWaxmeFFpdbvaQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prettier": "^3.0.0", + "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" + } + }, "node_modules/@fuzdev/gro/node_modules/readdirp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", @@ -1031,22 +1043,6 @@ } } }, - "node_modules/@fuzdev/tsv_wasm": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@fuzdev/tsv_wasm/-/tsv_wasm-0.1.0.tgz", - "integrity": "sha512-hlZxdL1c5jiMyLnMv3NWONxNR64JChNekxlsKEJxXvgr/bmcRsgF1XuyabOTtppW9C+9HUvCpklJgJeU4aZbKw==", - "dev": true, - "license": "MIT", - "bin": { - "tsv": "cli.js" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://www.ryanatkn.com/funding" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1830,9 +1826,9 @@ ] }, "node_modules/@ryanatkn/eslint-config": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@ryanatkn/eslint-config/-/eslint-config-0.12.1.tgz", - "integrity": "sha512-rur/pPhaaGyBlFjsPs1wIjyHnlPI5UT617TibNSAq6B3fialaI/YqsaDx/DMU5yRXlyhU6esYm+aOmXnckOZOA==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@ryanatkn/eslint-config/-/eslint-config-0.12.2.tgz", + "integrity": "sha512-p5yIpW0ka5Gev3VM2a7X8kEnVqORaF8JccU79D7BxEL+erTMlsn0aFWtMaxPXaIxBwL3yqJ+42uFc4AKDIn/aA==", "dev": true, "license": "Unlicense", "dependencies": { @@ -3822,9 +3818,9 @@ } }, "node_modules/prettier": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", - "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -3838,14 +3834,17 @@ } }, "node_modules/prettier-plugin-svelte": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.5.1.tgz", - "integrity": "sha512-65+fr5+cgIKWKiqM1Doum4uX6bY8iFCdztvvp2RcF+AJoieaw9kJOFMNcJo/bkmKYsxFaM9OsVZK/gWauG/5mg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-4.1.1.tgz", + "integrity": "sha512-wXvbXMjSvb4C9ENWTHXyd+ihakKCsJ6rJhLP6/8HFNj4GkZr48jqL9PoKsl2sk7SyCZRTnJ7O2TTowUpOxP/KA==", "dev": true, "license": "MIT", + "engines": { + "node": ">=20" + }, "peerDependencies": { "prettier": "^3.0.0", - "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" + "svelte": "^5.0.0" } }, "node_modules/punycode": { @@ -4115,9 +4114,9 @@ } }, "node_modules/svelte-docinfo": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/svelte-docinfo/-/svelte-docinfo-0.5.1.tgz", - "integrity": "sha512-/+THIRDddz5QeTBBmgK69lL9jXUS5jdL08VAD8BV1JT0kYEjPK9T+wWHCVpcLX09NSyPGCX2H6jgBMF1XbspEg==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/svelte-docinfo/-/svelte-docinfo-0.5.4.tgz", + "integrity": "sha512-9XONWxu//kRmThbJBpDuNu3jSP6lSPgMy4T9G2GYHWWrHDiowJFn8ov5acvhBvAogBinBATylXk1TsvyQlI0iQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 47ad77e22..d13e250b6 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@fuzdev/fuz_code": "^0.48.0", "@fuzdev/fuz_ui": "^0.206.7", "@fuzdev/fuz_util": "^0.65.2", - "@fuzdev/gro": "^0.207.0", + "@fuzdev/gro": "^0.205.1", "@fuzdev/mdz": "^0.3.0", "@ryanatkn/eslint-config": "^0.12.2", "@sveltejs/acorn-typescript": "^1.0.9", @@ -88,6 +88,8 @@ "eslint-plugin-svelte": "^3.13.1", "esm-env": "^1.2.2", "magic-string": "^0.30.21", + "prettier": "^3.9.6", + "prettier-plugin-svelte": "^4.1.1", "svelte": "^5.56.3", "svelte-check": "^4.4.6", "svelte-docinfo": "^0.5.4", @@ -100,6 +102,24 @@ "zimmerframe": "^1.1.4", "zod": "^4.3.6" }, + "prettier": { + "plugins": [ + "prettier-plugin-svelte" + ], + "useTabs": true, + "printWidth": 100, + "singleQuote": true, + "bracketSpacing": true, + "trailingComma": "none", + "overrides": [ + { + "files": "package.json", + "options": { + "useTabs": false + } + } + ] + }, "sideEffects": [ "**/*.css" ], diff --git a/src/app.d.ts b/src/app.d.ts index a917e8990..e7cd05ab3 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -7,7 +7,7 @@ declare module 'virtual:fuz.css' { export default css; } declare module 'virtual:pkg.json' { - import type {PkgJson} from '@fuzdev/fuz_util/pkg_json.ts'; + import type { PkgJson } from '@fuzdev/fuz_util/pkg_json.ts'; const pkg_json: PkgJson; export default pkg_json; } diff --git a/src/app.html b/src/app.html index e3061bc77..06d95cd29 100644 --- a/src/app.html +++ b/src/app.html @@ -11,7 +11,7 @@ ((c) => c === 'dark' || (c !== 'light' && matchMedia('(prefers-color-scheme:dark)').matches) ? 'dark' - : 'light')(localStorage.getItem('fuz:color-scheme')), + : 'light')(localStorage.getItem('fuz:color-scheme')) ); diff --git a/src/lib/bundled_resources.ts b/src/lib/bundled_resources.ts index 3571d3d92..0af765ef2 100644 --- a/src/lib/bundled_resources.ts +++ b/src/lib/bundled_resources.ts @@ -14,13 +14,16 @@ import { type StyleRuleIndex, load_style_rule_index, create_style_rule_index, - load_default_style_css, + load_default_style_css } from './style_rule_parser.ts'; -import {type VariableDependencyGraph, build_variable_graph_from_options} from './variable_graph.ts'; -import {type CssClassVariableIndex, build_class_variable_index} from './class_variable_index.ts'; -import type {CssClassDefinition} from './css_class_generation.ts'; -import type {BaseCssOption, VariablesOption} from './css_plugin_options.ts'; -import type {CacheDeps} from './deps.ts'; +import { + type VariableDependencyGraph, + build_variable_graph_from_options +} from './variable_graph.ts'; +import { type CssClassVariableIndex, build_class_variable_index } from './class_variable_index.ts'; +import type { CssClassDefinition } from './css_class_generation.ts'; +import type { BaseCssOption, VariablesOption } from './css_plugin_options.ts'; +import type { CacheDeps } from './deps.ts'; /** * Bundled CSS resources needed to emit base styles and theme variables. @@ -49,9 +52,9 @@ export interface CreateBundledResourcesOptions { * generators' prior behavior. */ export const create_bundled_resources = async ( - options: CreateBundledResourcesOptions, + options: CreateBundledResourcesOptions ): Promise => { - const {base_css, variables, class_definitions, deps} = options; + const { base_css, variables, class_definitions, deps } = options; let style_rule_index: StyleRuleIndex; if (typeof base_css === 'string') { @@ -69,6 +72,6 @@ export const create_bundled_resources = async ( return { style_rule_index, variable_graph: build_variable_graph_from_options(variables), - class_variable_index: build_class_variable_index(class_definitions), + class_variable_index: build_class_variable_index(class_definitions) }; }; diff --git a/src/lib/class_variable_index.ts b/src/lib/class_variable_index.ts index e98054502..cac7614c5 100644 --- a/src/lib/class_variable_index.ts +++ b/src/lib/class_variable_index.ts @@ -8,8 +8,8 @@ * @module */ -import type {CssClassDefinition} from './css_class_generation.ts'; -import {extract_css_variables} from './css_variable_utils.ts'; +import type { CssClassDefinition } from './css_class_generation.ts'; +import { extract_css_variables } from './css_variable_utils.ts'; /** * Index mapping class names to their CSS variable dependencies. @@ -26,7 +26,7 @@ export interface CssClassVariableIndex { * @returns `CssClassVariableIndex` with variable lookups */ export const build_class_variable_index = ( - definitions: Record, + definitions: Record ): CssClassVariableIndex => { const by_class: Map> = new Map(); @@ -39,7 +39,7 @@ export const build_class_variable_index = ( } } - return {by_class}; + return { by_class }; }; /** @@ -80,7 +80,7 @@ const extract_variables_from_definition = (definition: CssClassDefinition): Set< */ export const get_class_variables = ( index: CssClassVariableIndex, - class_name: string, + class_name: string ): Set | null => index.by_class.get(class_name) ?? null; /** @@ -92,7 +92,7 @@ export const get_class_variables = ( */ export const collect_class_variables = ( index: CssClassVariableIndex, - class_names: Iterable, + class_names: Iterable ): Set => { const variables: Set = new Set(); @@ -117,7 +117,7 @@ export const collect_class_variables = ( */ export const get_classes_using_variable = ( index: CssClassVariableIndex, - variable_name: string, + variable_name: string ): Array => { const classes: Array = []; diff --git a/src/lib/css_bundled_resolution.ts b/src/lib/css_bundled_resolution.ts index 62da3df09..8336d4a0a 100644 --- a/src/lib/css_bundled_resolution.ts +++ b/src/lib/css_bundled_resolution.ts @@ -13,22 +13,22 @@ * @module */ -import type {GenerationDiagnostic} from './diagnostics.ts'; +import type { GenerationDiagnostic } from './diagnostics.ts'; import { type StyleRuleIndex, get_matching_rules, generate_base_css, - collect_rule_variables, + collect_rule_variables } from './style_rule_parser.ts'; import { type VariableDependencyGraph, resolve_variables_transitive, generate_theme_css, get_all_variable_names, - find_similar_variable, + find_similar_variable } from './variable_graph.ts'; -import {type CssClassVariableIndex, collect_class_variables} from './class_variable_index.ts'; -import {FUZ_LAYER_ORDER_STATEMENT} from './theme.ts'; +import { type CssClassVariableIndex, collect_class_variables } from './class_variable_index.ts'; +import { FUZ_LAYER_ORDER_STATEMENT } from './theme.ts'; /** * Threshold for string similarity to suggest typo corrections. @@ -203,7 +203,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult exclude_elements: raw_exclude_elements, exclude_variables: raw_exclude_variables, explicit_elements, - explicit_variables, + explicit_variables } = options; const diagnostics: Array = []; @@ -242,7 +242,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult included_rule_indices = get_matching_rules( style_rule_index, included_elements, - detected_classes, + detected_classes ); } @@ -258,7 +258,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult suggestion: 'Element will use browser defaults. Add to additional_elements if intentional.', identifier: element, - locations: null, + locations: null }); } } @@ -282,7 +282,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult ? `Check spelling. Similar element: ${similar}` : 'Element has no fuz_css styles. Remove from @fuz-elements or add custom styles.', identifier: element, - locations: null, + locations: null }); } } @@ -305,7 +305,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult ? `Check spelling. Similar variable: ${similar}` : 'Variable does not exist in the theme. Remove from @fuz-variables.', identifier: variable, - locations: null, + locations: null }); } } @@ -371,7 +371,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult }" is referenced by included styles but excluded via exclude_variables - references to it will be undefined`, suggestion: 'Remove it from exclude_variables, or define the variable elsewhere.', identifier: v, - locations: null, + locations: null }); } all_variables.delete(v); @@ -390,7 +390,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult message: warning, suggestion: null, identifier: 'variable_resolution', - locations: null, + locations: null }); } @@ -405,14 +405,14 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult message: `CSS variable "--${name}" not found - did you mean "--${similar}"?`, suggestion: `Check spelling. Similar theme variable: --${similar}`, identifier: `var(--${name})`, - locations: null, + locations: null }); } // Variables not similar to any theme variable are assumed to be user-defined } // Step 5: Generate theme CSS - const {light_css, dark_css} = generate_theme_css(variable_graph, resolved_variables); + const { light_css, dark_css } = generate_theme_css(variable_graph, resolved_variables); const theme_css = [light_css, dark_css].filter(Boolean).join('\n\n'); // Step 6: Generate base CSS from matched rules @@ -425,7 +425,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult elements: [...included_elements], included_rules: included_rule_indices.size, total_rules: style_rule_index.rules.length, - variable_count: resolved_variables.size, + variable_count: resolved_variables.size } : undefined; @@ -436,7 +436,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult included_rule_indices, included_elements, diagnostics, - stats, + stats }; }; @@ -463,9 +463,9 @@ export interface GenerateBundledCssOptions { export const generate_bundled_css = ( result: CssResolutionResult, utility_css: string, - options: GenerateBundledCssOptions = {}, + options: GenerateBundledCssOptions = {} ): string => { - const {include_theme = true, include_base = true, include_utilities = true} = options; + const { include_theme = true, include_base = true, include_utilities = true } = options; const parts: Array = []; diff --git a/src/lib/css_cache.ts b/src/lib/css_cache.ts index 06ce3d0fb..d3689622b 100644 --- a/src/lib/css_cache.ts +++ b/src/lib/css_cache.ts @@ -7,12 +7,12 @@ * @module */ -import {join} from 'node:path'; -import {hash_insecure} from '@fuzdev/fuz_util/hash.ts'; +import { join } from 'node:path'; +import { hash_insecure } from '@fuzdev/fuz_util/hash.ts'; -import type {SourceLocation, ExtractionDiagnostic} from './diagnostics.ts'; -import type {ExtractionData} from './css_class_extractor.ts'; -import type {CacheDeps} from './deps.ts'; +import type { SourceLocation, ExtractionDiagnostic } from './diagnostics.ts'; +import type { ExtractionData } from './css_class_extractor.ts'; +import type { CacheDeps } from './deps.ts'; /** * Default cache directory relative to project root. @@ -73,7 +73,7 @@ export interface CachedExtraction { export const get_cache_path = ( source_path: string, cache_dir: string, - project_root: string, + project_root: string ): string => { if (!source_path.startsWith(project_root)) { throw new Error(`Source path "${source_path}" is not under project root "${project_root}"`); @@ -94,7 +94,7 @@ export const get_cache_path = ( export const get_file_cache_path = ( file_id: string, cache_dir: string, - project_root: string, + project_root: string ): string => { const is_internal = file_id.startsWith(project_root); return is_internal @@ -112,10 +112,10 @@ export const get_file_cache_path = ( */ export const load_cached_extraction = async ( deps: CacheDeps, - cache_path: string, + cache_path: string ): Promise => { try { - const r = await deps.read_text({path: cache_path}); + const r = await deps.read_text({ path: cache_path }); if (!r.ok) return null; const cached = JSON.parse(r.value) as CachedExtraction; @@ -147,7 +147,7 @@ export const save_cached_extraction = async ( deps: CacheDeps, cache_path: string, content_hash: string, - extraction: ExtractionData, + extraction: ExtractionData ): Promise => { // Convert to null if empty to save allocation on load const classes_array = @@ -179,10 +179,10 @@ export const save_cached_extraction = async ( diagnostics: diagnostics_array, elements: elements_array, explicit_elements: explicit_elements_array, - explicit_variables: explicit_variables_array, + explicit_variables: explicit_variables_array }; - await deps.write_text_atomic({path: cache_path, content: JSON.stringify(data)}); + await deps.write_text_atomic({ path: cache_path, content: JSON.stringify(data) }); }; /** @@ -194,9 +194,9 @@ export const save_cached_extraction = async ( */ export const delete_cached_extraction = async ( deps: CacheDeps, - cache_path: string, + cache_path: string ): Promise => { - await deps.unlink({path: cache_path}); + await deps.unlink({ path: cache_path }); }; /** @@ -211,5 +211,5 @@ export const from_cached_extraction = (cached: CachedExtraction): ExtractionData diagnostics: cached.diagnostics, elements: cached.elements ? new Set(cached.elements) : null, explicit_elements: cached.explicit_elements ? new Set(cached.explicit_elements) : null, - explicit_variables: cached.explicit_variables ? new Set(cached.explicit_variables) : null, + explicit_variables: cached.explicit_variables ? new Set(cached.explicit_variables) : null }); diff --git a/src/lib/css_class_composites.ts b/src/lib/css_class_composites.ts index 176a92b4f..903b31e6e 100644 --- a/src/lib/css_class_composites.ts +++ b/src/lib/css_class_composites.ts @@ -1,4 +1,4 @@ -import type {CssClassDefinition} from './css_class_generation.ts'; +import type { CssClassDefinition } from './css_class_generation.ts'; export const css_class_composites: Record = { pixelated: { @@ -7,10 +7,10 @@ export const css_class_composites: Record `var(--line_height_${v})`, + (v) => `var(--line_height_${v})` ), ...generate_property_classes( 'font-size', font_size_variants, - (v) => `var(--font_size_${v}); --font_size: var(--font_size_${v})`, + (v) => `var(--font_size_${v}); --font_size: var(--font_size_${v})` ), ...generate_property_classes( 'font-size', icon_size_variants, (v) => `var(--icon_size_${v}); --font_size: var(--icon_size_${v})`, - 'icon_size', + 'icon_size' ), /* @@ -86,22 +86,22 @@ export const css_class_definitions: Record `var(--text_${v}); --text_color: var(--text_${v})`, - 'text', + 'text' ), // Shade scale (tinted backgrounds) ...generate_property_classes( 'background-color', shade_scale_variants, (v) => `var(--shade_${v})`, - 'shade', + 'shade' ), // Hue classes ...generate_classes( (letter: string) => ({ name: `hue_${letter}`, - css: `--hue: var(--hue_${letter});`, + css: `--hue: var(--hue_${letter});` }), - palette_variants, + palette_variants ), // Palette intensity classes (text color) ...generate_classes( @@ -109,63 +109,63 @@ export const css_class_definitions: Record ({ name: `bg_${letter}_${intensity}`, - css: `background-color: var(--palette_${letter}_${intensity});`, + css: `background-color: var(--palette_${letter}_${intensity});` }), palette_variants, - intensity_variants, + intensity_variants ), // Intent intensity classes (text color) - meaning-first twins of the palette classes ...generate_classes( (intent: string, intensity: string) => ({ name: `${intent}_${intensity}`, - css: `color: var(--${intent}_${intensity}); --text_color: var(--${intent}_${intensity});`, + css: `color: var(--${intent}_${intensity}); --text_color: var(--${intent}_${intensity});` }), intent_variants, - intensity_variants, + intensity_variants ), // Intent intensity classes (background color) ...generate_classes( (intent: string, intensity: string) => ({ name: `bg_${intent}_${intensity}`, - css: `background-color: var(--${intent}_${intensity});`, + css: `background-color: var(--${intent}_${intensity});` }), intent_variants, - intensity_variants, + intensity_variants ), // Darken/lighten overlays (non-adaptive, alpha-based) ...generate_property_classes( 'background-color', darken_lighten_variants, (v) => `var(--darken_${v})`, - 'darken', + 'darken' ), ...generate_property_classes( 'background-color', darken_lighten_variants, (v) => `var(--lighten_${v})`, - 'lighten', + 'lighten' ), // Adaptive alpha overlays (fg = toward foreground, bg = toward background) ...generate_property_classes( 'background-color', darken_lighten_variants, (v) => `var(--fg_${v})`, - 'fg', + 'fg' ), ...generate_property_classes( 'background-color', darken_lighten_variants, (v) => `var(--bg_${v})`, - 'bg', + 'bg' ), /* @@ -179,7 +179,7 @@ export const css_class_definitions: Record `var(--border_color_${v}); --border_color: var(--border_color_${v})`, - 'border_color', + 'border_color' ), // Border colors using palette hue + intensity (sets both property and contextual variable); // the letter alone implies the palette - the alpha-ramp family is `border_color_NN` @@ -188,10 +188,10 @@ export const css_class_definitions: Record `var(--shade_${v})`), @@ -202,29 +202,29 @@ export const css_class_definitions: Record `var(--border_width_${v})`, + (v) => `var(--border_width_${v})` ), ...generate_property_classes( 'outline-width', border_width_variants.map(String), - (v) => `var(--border_width_${v})`, + (v) => `var(--border_width_${v})` ), - outline_width_focus: {declaration: 'outline-width: var(--outline_width_focus);'}, - outline_width_active: {declaration: 'outline-width: var(--outline_width_active);'}, + outline_width_focus: { declaration: 'outline-width: var(--outline_width_focus);' }, + outline_width_active: { declaration: 'outline-width: var(--outline_width_active);' }, ...generate_property_classes( 'border-radius', border_radius_variants, - (v) => `var(--border_radius_${v})`, + (v) => `var(--border_radius_${v})` ), ...generate_border_radius_corners(border_radius_variants, (v) => `var(--border_radius_${v})`), @@ -238,31 +238,31 @@ export const css_class_definitions: Record ({ name: `shadow_color_${value}`, - css: `--shadow_color: var(--shadow_color_${value});`, + css: `--shadow_color: var(--shadow_color_${value});` }), - shadow_semantic_values, + shadow_semantic_values ), ...generate_classes( (alpha: string) => ({ name: `shadow_alpha_${alpha}`, - css: `--shadow_alpha: var(--shadow_alpha_${alpha});`, + css: `--shadow_alpha: var(--shadow_alpha_${alpha});` }), - shadow_alpha_variants, + shadow_alpha_variants ), // Shadow colors using palette hue + intensity (sets contextual variable only); // the letter alone implies the palette - semantic colors are `shadow_color_umbra` etc. ...generate_classes( (letter: string, intensity: string) => ({ name: `shadow_${letter}_${intensity}`, - css: `--shadow_color: var(--palette_${letter}_${intensity});`, + css: `--shadow_color: var(--palette_${letter}_${intensity});` }), palette_variants, - intensity_variants, + intensity_variants ), /* @@ -276,30 +276,30 @@ export const css_class_definitions: Record ({ name: `width_atmost_${v}`, - css: `width: 100%; max-width: var(--distance_${v});`, + css: `width: 100%; max-width: var(--distance_${v});` }), - distance_variants, + distance_variants ), ...generate_classes( (v: string) => ({ name: `width_atleast_${v}`, - css: `width: 100%; min-width: var(--distance_${v});`, + css: `width: 100%; min-width: var(--distance_${v});` }), - distance_variants, + distance_variants ), ...generate_classes( (v: string) => ({ name: `height_atmost_${v}`, - css: `height: 100%; max-height: var(--distance_${v});`, + css: `height: 100%; max-height: var(--distance_${v});` }), - distance_variants, + distance_variants ), ...generate_classes( (v: string) => ({ name: `height_atleast_${v}`, - css: `height: 100%; min-height: var(--distance_${v});`, + css: `height: 100%; min-height: var(--distance_${v});` }), - distance_variants, + distance_variants ), ...generate_property_classes('top', space_variants, format_spacing_value), @@ -312,7 +312,7 @@ export const css_class_definitions: Record | undefined, - include_defaults: boolean, + include_defaults: boolean ): Record => { if (!include_defaults && !user_definitions) { throw new Error('class_definitions is required when include_default_classes is false'); } return include_defaults ? user_definitions - ? {...css_class_definitions, ...user_definitions} + ? { ...css_class_definitions, ...user_definitions } : css_class_definitions : user_definitions!; }; diff --git a/src/lib/css_class_extractor.ts b/src/lib/css_class_extractor.ts index cf8da509a..3c5ff7d12 100644 --- a/src/lib/css_class_extractor.ts +++ b/src/lib/css_class_extractor.ts @@ -15,13 +15,13 @@ * @module */ -import {parse as parse_svelte, type AST} from 'svelte/compiler'; -import {walk, type Visitors} from 'zimmerframe'; -import {Parser, type Node} from 'acorn'; -import {tsPlugin} from '@sveltejs/acorn-typescript'; -import {to_error_message} from '@fuzdev/fuz_util/error.ts'; +import { parse as parse_svelte, type AST } from 'svelte/compiler'; +import { walk, type Visitors } from 'zimmerframe'; +import { Parser, type Node } from 'acorn'; +import { tsPlugin } from '@sveltejs/acorn-typescript'; +import { to_error_message } from '@fuzdev/fuz_util/error.ts'; -import {type SourceLocation, type ExtractionDiagnostic} from './diagnostics.ts'; +import { type SourceLocation, type ExtractionDiagnostic } from './diagnostics.ts'; // // Types @@ -153,7 +153,7 @@ export class SourceIndex { if (this.line_starts[mid]! <= offset) low = mid; else high = mid - 1; } - return {file, line: low + 1, column: offset - this.line_starts[low]! + 1}; + return { file, line: low + 1, column: offset - this.line_starts[low]! + 1 }; } } @@ -168,7 +168,7 @@ export class SourceIndex { const add_class_with_location = ( classes: Map>, class_name: string, - location: SourceLocation, + location: SourceLocation ): void => { const existing = classes.get(class_name); if (existing) { @@ -184,7 +184,7 @@ const CLASS_UTILITY_FUNCTIONS = new Set([ 'cn', // common alias (shadcn/ui convention) 'classNames', // classnames package 'classnames', // lowercase variant - 'cx', // emotion and other libs + 'cx' // emotion and other libs ]); // Svelte 5 runes that wrap expressions we should extract from @@ -235,7 +235,7 @@ const create_walk_state = (file: string, source_index: SourceIndex | null): Walk diagnostics: [], elements: new Set(), explicit_elements: new Set(), - explicit_variables: new Set(), + explicit_variables: new Set() }); /** @@ -248,7 +248,7 @@ const finalize_extraction_result = (state: WalkState): ExtractionResult => ({ diagnostics: state.diagnostics.length > 0 ? state.diagnostics : null, elements: state.elements.size > 0 ? state.elements : null, explicit_elements: state.explicit_elements.size > 0 ? state.explicit_elements : null, - explicit_variables: state.explicit_variables.size > 0 ? state.explicit_variables : null, + explicit_variables: state.explicit_variables.size > 0 ? state.explicit_variables : null }); /** @@ -262,7 +262,7 @@ const empty_extraction_result = (diagnostics: Array): Extr diagnostics: diagnostics.length > 0 ? diagnostics : null, elements: null, explicit_elements: null, - explicit_variables: null, + explicit_variables: null }); /** @@ -280,7 +280,7 @@ const location_from_offset = (state: WalkState, offset: number): SourceLocation return state.source_index.get_location(offset, state.file); } // Fallback for script context (should have line/column from AST) - return {file: state.file, line: 1, column: 1}; + return { file: state.file, line: 1, column: 1 }; }; /** @@ -295,7 +295,7 @@ export const extract_from_svelte = (source: string, file = ''): Extract let ast: AST.Root; try { - ast = parse_svelte(source, {modern: true}); + ast = parse_svelte(source, { modern: true }); } catch (err) { return empty_extraction_result([ { @@ -303,8 +303,8 @@ export const extract_from_svelte = (source: string, file = ''): Extract level: 'warning', message: `Failed to parse Svelte file: ${to_error_message(err, 'unknown error')}`, suggestion: 'Check for syntax errors in the file', - location: {file, line: 1, column: 1}, - }, + location: { file, line: 1, column: 1 } + } ]); } @@ -340,11 +340,11 @@ export const extract_from_svelte = (source: string, file = ''): Extract * and returns the space-separated list of names. */ const create_fuz_comment_parser = ( - tag: string, + tag: string ): (( content: string, location: SourceLocation, - diagnostics: Array, + diagnostics: Array ) => Array | null) => { const pattern = new RegExp(`^\\s*@fuz-${tag}(:?)\\s+(.+?)\\s*$`); return (content, location, diagnostics) => { @@ -356,7 +356,7 @@ const create_fuz_comment_parser = ( level: 'warning', message: `@fuz-${tag}: colon is unnecessary`, suggestion: `Use @fuz-${tag} without the colon`, - location, + location }); } return match[2]!.split(/\s+/).filter(Boolean); @@ -406,9 +406,9 @@ const process_fuz_comment = (content: string, location: SourceLocation, state: W */ const extract_fuz_comments_from_svelte = (ast: AST.Root, state: WalkState): void => { const visitors: Visitors = { - Comment(node, {state}) { + Comment(node, { state }) { process_fuz_comment(node.data, location_from_offset(state, node.start), state); - }, + } }; walk(ast.fragment as AST.SvelteNode, state, visitors); }; @@ -421,11 +421,11 @@ const extract_fuz_comments_from_svelte = (ast: AST.Root, state: WalkState): void const extract_fuz_comments_from_script = ( script: AST.Script, source: string, - state: WalkState, + state: WalkState ): void => { // Extract the script source using start/end positions // Svelte AST nodes have start/end but the TypeScript types don't include them - const content = script.content as unknown as {start: number; end: number}; + const content = script.content as unknown as { start: number; end: number }; const script_source = source.slice(content.start, content.end); // Calculate the line offset for proper location reporting @@ -436,7 +436,7 @@ const extract_fuz_comments_from_script = ( } // Collect comments via acorn's onComment callback - const comments: Array<{value: string; loc: {start: {line: number; column: number}}}> = []; + const comments: Array<{ value: string; loc: { start: { line: number; column: number } } }> = []; try { const parser = Parser.extend(tsPlugin()); @@ -449,12 +449,12 @@ const extract_fuz_comments_from_script = ( text: string, _start: number, _end: number, - startLoc?: {line: number; column: number}, + startLoc?: { line: number; column: number } ) => { if (startLoc) { - comments.push({value: text, loc: {start: startLoc}}); + comments.push({ value: text, loc: { start: startLoc } }); } - }, + } }); } catch { // If parsing fails, we can't extract comments @@ -466,7 +466,7 @@ const extract_fuz_comments_from_script = ( const location: SourceLocation = { file: state.file, line: comment.loc.start.line + line_offset, - column: comment.loc.start.column + 1, + column: comment.loc.start.column + 1 }; process_fuz_comment(comment.value, location, state); } @@ -483,10 +483,10 @@ const extract_fuz_comments_from_script = ( export const extract_from_ts = ( source: string, file = '', - acorn_plugins?: Array, + acorn_plugins?: Array ): ExtractionResult => { // Collect comments via acorn's onComment callback - const comments: Array<{value: string; loc: {start: {line: number; column: number}}}> = []; + const comments: Array<{ value: string; loc: { start: { line: number; column: number } } }> = []; let ast: Node; try { @@ -502,12 +502,12 @@ export const extract_from_ts = ( text: string, _start: number, _end: number, - startLoc?: {line: number; column: number}, + startLoc?: { line: number; column: number } ) => { if (startLoc) { - comments.push({value: text, loc: {start: startLoc}}); + comments.push({ value: text, loc: { start: startLoc } }); } - }, + } }); } catch (err) { return empty_extraction_result([ @@ -516,8 +516,8 @@ export const extract_from_ts = ( level: 'warning', message: `Failed to parse TypeScript/JS file: ${to_error_message(err, 'unknown error')}`, suggestion: 'Check for syntax errors in the file', - location: {file, line: 1, column: 1}, - }, + location: { file, line: 1, column: 1 } + } ]); } @@ -528,7 +528,7 @@ export const extract_from_ts = ( const location: SourceLocation = { file, line: comment.loc.start.line, - column: comment.loc.start.column + 1, + column: comment.loc.start.column + 1 }; process_fuz_comment(comment.value, location, state); } @@ -554,7 +554,7 @@ export const extract_from_ts = ( */ export const extract_css_classes = ( source: string, - options: ExtractCssClassesOptions = {}, + options: ExtractCssClassesOptions = {} ): Set | null => { const result = extract_css_classes_with_locations(source, options); return result.classes ? new Set(result.classes.keys()) : null; @@ -570,9 +570,9 @@ export const extract_css_classes = ( */ export const extract_css_classes_with_locations = ( source: string, - options: ExtractCssClassesOptions = {}, + options: ExtractCssClassesOptions = {} ): ExtractionResult => { - const {filename, acorn_plugins} = options; + const { filename, acorn_plugins } = options; const ext = filename ? filename.slice(filename.lastIndexOf('.')) : ''; const file = filename ?? ''; @@ -598,56 +598,56 @@ export const extract_css_classes_with_locations = ( const walk_template = (fragment: AST.Fragment, state: WalkState): void => { const visitors: Visitors = { // Handle regular elements - capture element name - RegularElement(node, {state, next}) { + RegularElement(node, { state, next }) { // Track HTML element names (not components, not svelte:* elements) state.elements.add(node.name); process_element_attributes(node.attributes, state); next(); }, // SvelteElement has dynamic tag, can't statically determine element - SvelteElement(node, {state, next}) { + SvelteElement(node, { state, next }) { process_element_attributes(node.attributes, state); next(); }, // Components are PascalCase - don't add to elements - SvelteComponent(node, {state, next}) { + SvelteComponent(node, { state, next }) { process_element_attributes(node.attributes, state); next(); }, - Component(node, {state, next}) { + Component(node, { state, next }) { process_element_attributes(node.attributes, state); next(); }, - SvelteFragment(node, {state, next}) { + SvelteFragment(node, { state, next }) { process_element_attributes(node.attributes, state); next(); }, // SvelteHead injects children into document - walk its fragment - SvelteHead(_node, {next}) { + SvelteHead(_node, { next }) { next(); }, // TitleElement is a special element type for inside svelte:head - TitleElement(node, {state, next}) { + TitleElement(node, { state, next }) { state.elements.add(node.name); process_element_attributes(node.attributes, state); next(); }, // SlotElement is <slot> - can have fallback content with elements - SlotElement(node, {state, next}) { + SlotElement(node, { state, next }) { state.elements.add(node.name); process_element_attributes(node.attributes, state); next(); }, // SvelteBody, SvelteWindow, SvelteDocument don't have element children - SvelteBody(_node, {next}) { + SvelteBody(_node, { next }) { next(); }, - SvelteWindow(_node, {next}) { + SvelteWindow(_node, { next }) { next(); }, - SvelteDocument(_node, {next}) { + SvelteDocument(_node, { next }) { next(); - }, + } }; walk(fragment as AST.SvelteNode, state, visitors); @@ -658,7 +658,7 @@ const walk_template = (fragment: AST.Fragment, state: WalkState): void => { */ const process_element_attributes = ( attributes: Array<AST.Attribute | AST.SpreadAttribute | AST.Directive | AST.AttachTag>, - state: WalkState, + state: WalkState ): void => { for (const attr of attributes) { // Handle class attribute @@ -716,20 +716,23 @@ const extract_from_attribute_value = (value: AST.Attribute['value'], state: Walk const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void => { // Get location from the expression's start offset const get_location = (): SourceLocation => { - const node = expr as unknown as {start?: number; loc?: {start: {line: number; column: number}}}; + const node = expr as unknown as { + start?: number; + loc?: { start: { line: number; column: number } }; + }; if (node.loc) { - return {file: state.file, line: node.loc.start.line, column: node.loc.start.column + 1}; + return { file: state.file, line: node.loc.start.line, column: node.loc.start.column + 1 }; } if (node.start !== undefined) { return location_from_offset(state, node.start); } - return {file: state.file, line: 1, column: 1}; + return { file: state.file, line: 1, column: 1 }; }; switch (expr.type) { case 'Literal': { // String literal - const node = expr as unknown as {value: unknown}; + const node = expr as unknown as { value: unknown }; if (typeof node.value === 'string') { const location = get_location(); for (const cls of node.value.split(/\s+/).filter(Boolean)) { @@ -744,9 +747,9 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = // Only extract tokens that are whitespace-bounded (not fragments like `icon-` from `icon-${size}`) const node = expr as unknown as { quasis: Array<{ - value: {raw: string}; + value: { raw: string }; start?: number; - loc?: {start: {line: number; column: number}}; + loc?: { start: { line: number; column: number } }; }>; expressions: Array<unknown>; }; @@ -758,7 +761,7 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = if (!quasi.value.raw) continue; const location = quasi.loc - ? {file: state.file, line: quasi.loc.start.line, column: quasi.loc.start.column + 1} + ? { file: state.file, line: quasi.loc.start.line, column: quasi.loc.start.column + 1 } : quasi.start !== undefined ? location_from_offset(state, quasi.start) : get_location(); @@ -816,7 +819,7 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = case 'ArrayExpression': { // Array: extract from each element - const node = expr as unknown as {elements: Array<unknown>}; + const node = expr as unknown as { elements: Array<unknown> }; for (const element of node.elements) { if (element) { extract_from_expression(element as AST.SvelteNode, state); @@ -833,15 +836,15 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = key: unknown; computed: boolean; start?: number; - loc?: {start: {line: number; column: number}}; + loc?: { start: { line: number; column: number } }; }>; }; for (const prop of node.properties) { if (prop.type === 'Property' && !prop.computed) { // Non-computed key - extract the key as a class name - const key = prop.key as {type: string; name?: string; value?: string}; + const key = prop.key as { type: string; name?: string; value?: string }; const location = prop.loc - ? {file: state.file, line: prop.loc.start.line, column: prop.loc.start.column + 1} + ? { file: state.file, line: prop.loc.start.line, column: prop.loc.start.column + 1 } : prop.start !== undefined ? location_from_offset(state, prop.start) : get_location(); @@ -860,7 +863,7 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = case 'ConditionalExpression': { // Ternary: extract from both branches - const node = expr as unknown as {consequent: unknown; alternate: unknown}; + const node = expr as unknown as { consequent: unknown; alternate: unknown }; extract_from_expression(node.consequent as AST.SvelteNode, state); extract_from_expression(node.alternate as AST.SvelteNode, state); break; @@ -868,7 +871,7 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = case 'LogicalExpression': { // && or ||: extract from both sides - const node = expr as unknown as {left: unknown; right: unknown}; + const node = expr as unknown as { left: unknown; right: unknown }; extract_from_expression(node.left as AST.SvelteNode, state); extract_from_expression(node.right as AST.SvelteNode, state); break; @@ -877,7 +880,12 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = case 'CallExpression': { // Function call: check if it's a class utility function or Svelte rune const node = expr as unknown as { - callee: {type: string; name?: string; object?: {name?: string}; property?: {name?: string}}; + callee: { + type: string; + name?: string; + object?: { name?: string }; + property?: { name?: string }; + }; arguments: Array<unknown>; }; @@ -905,7 +913,7 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = case 'Identifier': { // Variable reference: track it for later extraction - const node = expr as unknown as {name: string}; + const node = expr as unknown as { name: string }; if (state.in_class_context) { state.tracked_vars.add(node.name); } @@ -920,7 +928,7 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = case 'TaggedTemplateExpression': { // Tagged template literal like css`class-name` - extract from the template - const node = expr as unknown as {quasi: unknown}; + const node = expr as unknown as { quasi: unknown }; if (node.quasi) { extract_from_expression(node.quasi as AST.SvelteNode, state); } @@ -931,7 +939,7 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = case 'FunctionExpression': { // Function expression: extract from the body // Handles $derived.by(() => cond ? 'a' : 'b') - const node = expr as unknown as {body: unknown}; + const node = expr as unknown as { body: unknown }; if (node.body) { extract_from_expression(node.body as AST.SvelteNode, state); } @@ -940,7 +948,7 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = case 'BlockStatement': { // Block statement: walk all statements looking for return statements - const node = expr as unknown as {body: Array<unknown>}; + const node = expr as unknown as { body: Array<unknown> }; for (const stmt of node.body) { extract_from_expression(stmt as AST.SvelteNode, state); } @@ -949,7 +957,7 @@ const extract_from_expression = (expr: AST.SvelteNode, state: WalkState): void = case 'ReturnStatement': { // Return statement: extract from the argument - const node = expr as unknown as {argument: unknown}; + const node = expr as unknown as { argument: unknown }; if (node.argument) { extract_from_expression(node.argument as AST.SvelteNode, state); } @@ -974,14 +982,14 @@ const extract_from_jsx_attribute_value = (value: unknown, state: WalkState): voi type: string; value?: string; expression?: unknown; - loc?: {start: {line: number; column: number}}; + loc?: { start: { line: number; column: number } }; }; if (node.type === 'Literal' && typeof node.value === 'string') { // Static className="foo bar" const location: SourceLocation = node.loc - ? {file: state.file, line: node.loc.start.line, column: node.loc.start.column + 1} - : {file: state.file, line: 1, column: 1}; + ? { file: state.file, line: node.loc.start.line, column: node.loc.start.column + 1 } + : { file: state.file, line: 1, column: 1 }; for (const cls of node.value.split(/\s+/).filter(Boolean)) { add_class(state, cls, location); } @@ -1000,8 +1008,8 @@ const extract_from_jsx_attribute_value = (value: unknown, state: WalkState): voi const walk_script = (ast: unknown, state: WalkState): void => { const visitors: Visitors<Node, WalkState> = { // Variable declarations - VariableDeclarator(node, {state, next}) { - const declarator = node as unknown as {id: {type: string; name: string}; init: unknown}; + VariableDeclarator(node, { state, next }) { + const declarator = node as unknown as { id: { type: string; name: string }; init: unknown }; if (declarator.id.type === 'Identifier') { const name = declarator.id.name; // Check if variable name matches class pattern @@ -1020,9 +1028,9 @@ const walk_script = (ast: unknown, state: WalkState): void => { }, // Call expressions (for clsx/cn calls outside of class attributes) - CallExpression(node, {state, next}) { + CallExpression(node, { state, next }) { const call = node as unknown as { - callee: {type: string; name?: string}; + callee: { type: string; name?: string }; arguments: Array<unknown>; }; if (call.callee.type === 'Identifier' && CLASS_UTILITY_FUNCTIONS.has(call.callee.name!)) { @@ -1034,9 +1042,9 @@ const walk_script = (ast: unknown, state: WalkState): void => { }, // Object properties with class-related keys - Property(node, {state, next}) { + Property(node, { state, next }) { const prop = node as unknown as { - key: {type: string; name?: string; value?: string}; + key: { type: string; name?: string; value?: string }; value: unknown; computed: boolean; }; @@ -1060,13 +1068,13 @@ const walk_script = (ast: unknown, state: WalkState): void => { // JSX elements (React/Preact/Solid) - extract class-related attributes and element names // These are only present when acorn-jsx plugin is used - JSXElement(node, {state, next}) { + JSXElement(node, { state, next }) { const element = node as unknown as { openingElement: { - name?: {type: string; name?: string}; + name?: { type: string; name?: string }; attributes: Array<{ type: string; - name?: {type: string; name?: string}; + name?: { type: string; name?: string }; value?: unknown; }>; }; @@ -1092,7 +1100,7 @@ const walk_script = (ast: unknown, state: WalkState): void => { } } next(); - }, + } }; walk(ast as Node, state, visitors); @@ -1115,8 +1123,8 @@ const extract_from_tracked_vars = (ast: AST.Root, state: WalkState): void => { */ const extract_from_tracked_vars_in_script = (ast: Node, state: WalkState): void => { const find_visitors: Visitors<Node, WalkState> = { - VariableDeclarator(node, {state}) { - const declarator = node as unknown as {id: {type: string; name: string}; init: unknown}; + VariableDeclarator(node, { state }) { + const declarator = node as unknown as { id: { type: string; name: string }; init: unknown }; if ( declarator.id.type === 'Identifier' && state.tracked_vars.has(declarator.id.name) && @@ -1125,7 +1133,7 @@ const extract_from_tracked_vars_in_script = (ast: Node, state: WalkState): void ) { extract_from_expression(declarator.init as AST.SvelteNode, state); } - }, + } }; walk(ast, state, find_visitors); diff --git a/src/lib/css_class_generation.ts b/src/lib/css_class_generation.ts index cb9fb1edc..979eb3f35 100644 --- a/src/lib/css_class_generation.ts +++ b/src/lib/css_class_generation.ts @@ -7,23 +7,23 @@ * @module */ -import type {Logger} from '@fuzdev/fuz_util/log.ts'; -import {to_error_message} from '@fuzdev/fuz_util/error.ts'; +import type { Logger } from '@fuzdev/fuz_util/log.ts'; +import { to_error_message } from '@fuzdev/fuz_util/error.ts'; import { type SourceLocation, type InterpreterDiagnostic, type GenerationDiagnostic, - create_generation_diagnostic, + create_generation_diagnostic } from './diagnostics.ts'; import { parse_ruleset, is_single_selector_ruleset, - ruleset_contains_class, + ruleset_contains_class } from './css_ruleset_parser.ts'; -import {resolve_class_definition} from './css_class_resolution.ts'; -import {get_modifier} from './modifiers.ts'; -import {extract_css_variables} from './css_variable_utils.ts'; +import { resolve_class_definition } from './css_class_resolution.ts'; +import { get_modifier } from './modifiers.ts'; +import { extract_css_variables } from './css_variable_utils.ts'; // // CSS Utilities @@ -74,7 +74,7 @@ export const extract_primary_property = (declaration: string): string | null => const get_sort_index = ( class_name: string, indexes: Map<string, number>, - property_to_last_index: Map<string, number>, + property_to_last_index: Map<string, number> ): number => { const direct = indexes.get(class_name); if (direct !== undefined) return direct; @@ -145,9 +145,7 @@ export interface CssClassDefinitionRuleset extends CssClassDefinitionBase { /** Static definitions (not interpreter-based). */ export type CssClassDefinitionStatic = - | CssClassDefinitionComposition - | CssClassDefinitionDeclaration - | CssClassDefinitionRuleset; + CssClassDefinitionComposition | CssClassDefinitionDeclaration | CssClassDefinitionRuleset; /** Full union including interpreters. */ export type CssClassDefinition = CssClassDefinitionStatic | CssClassDefinitionInterpreter; @@ -206,7 +204,7 @@ export interface GenerateClassesCssOptions { } export const generate_classes_css = ( - options: GenerateClassesCssOptions, + options: GenerateClassesCssOptions ): GenerateClassesCssResult => { const { class_names, @@ -215,7 +213,7 @@ export const generate_classes_css = ( css_properties, log, class_locations, - explicit_classes, + explicit_classes } = options; const interpreter_diagnostics: Array<InterpreterDiagnostic> = []; const diagnostics: Array<GenerationDiagnostic> = []; @@ -226,7 +224,7 @@ export const generate_classes_css = ( log, diagnostics: interpreter_diagnostics, class_definitions, - css_properties, + css_properties }; // Build index maps in a single pass: @@ -258,7 +256,7 @@ export const generate_classes_css = ( const order_b = get_state_modifier_order(b); if (order_a !== order_b) return order_a - order_b; return a.localeCompare(b); // alphabetic tiebreaker for stable sort - }, + } ); let css = ''; @@ -281,10 +279,10 @@ export const generate_classes_css = ( // or just a declaration if (result.includes('{')) { // Full ruleset - use as-is - v = {ruleset: result, comment: interpreter.comment}; + v = { ruleset: result, comment: interpreter.comment }; } else { // Simple declaration - v = {declaration: result, comment: interpreter.comment}; + v = { declaration: result, comment: interpreter.comment }; } break; } @@ -302,7 +300,7 @@ export const generate_classes_css = ( const is_css_property_error = diag.level === 'error' && diag.message.startsWith('Unknown CSS property'); const level = is_css_property_error && !is_explicit ? 'warning' : diag.level; - diagnostics.push(create_generation_diagnostic({...diag, level}, locations)); + diagnostics.push(create_generation_diagnostic({ ...diag, level }, locations)); } if (!v) { @@ -316,13 +314,13 @@ export const generate_classes_css = ( message: 'No matching class definition found', identifier: c, suggestion: 'Check spelling or add a custom class definition', - locations, + locations }); } continue; } - const {comment} = v; + const { comment } = v; if (comment) { const trimmed = comment.trim(); @@ -346,7 +344,7 @@ export const generate_classes_css = ( message: resolution_result.error.message, identifier: c, suggestion: resolution_result.error.suggestion, - locations: class_locations?.get(c) ?? null, + locations: class_locations?.get(c) ?? null }); continue; } @@ -372,7 +370,7 @@ export const generate_classes_css = ( message: `Ruleset "${c}" is empty`, identifier: c, suggestion: `Add CSS rules or remove the empty ruleset definition`, - locations: class_locations?.get(c) ?? null, + locations: class_locations?.get(c) ?? null }); continue; } @@ -397,7 +395,7 @@ export const generate_classes_css = ( message: `Ruleset "${c}" has no selectors containing ".${c}"`, identifier: c, suggestion: `Ensure at least one selector uses ".${c}" so the class works when applied`, - locations: class_locations?.get(c) ?? null, + locations: class_locations?.get(c) ?? null }); } @@ -422,7 +420,7 @@ export const generate_classes_css = ( suggestion: `Convert to: { declaration: '${parsed.rules[0]?.declarations .replace(/\s+/g, ' ') .trim()}' }`, - locations: class_locations?.get(c) ?? null, + locations: class_locations?.get(c) ?? null }); } } catch (e) { @@ -434,12 +432,12 @@ export const generate_classes_css = ( message: `Failed to parse ruleset for "${c}": ${error_message}`, identifier: c, suggestion: 'Check for CSS syntax errors in the ruleset', - locations: class_locations?.get(c) ?? null, + locations: class_locations?.get(c) ?? null }); } } // Note: Interpreted types are converted to declaration above, so no else clause needed } - return {css, diagnostics, variables_used}; + return { css, diagnostics, variables_used }; }; diff --git a/src/lib/css_class_generators.ts b/src/lib/css_class_generators.ts index abc23238d..8458bdf01 100644 --- a/src/lib/css_class_generators.ts +++ b/src/lib/css_class_generators.ts @@ -1,4 +1,4 @@ -import type {CssClassDefinition} from './css_class_generation.ts'; +import type { CssClassDefinition } from './css_class_generation.ts'; export type GeneratedClassResult = { name: string; @@ -42,20 +42,20 @@ export const generate_classes = <T1 = string, T2 = string, T3 = string>( template: ClassTemplateFn<T1, T2, T3>, values: Iterable<T1>, secondary?: Iterable<T2>, - tertiary?: Iterable<T3>, + tertiary?: Iterable<T3> ): Record<string, CssClassDefinition> => { const result: Record<string, CssClassDefinition> = {}; if (!secondary) { for (const v1 of values) { const generated = (template as any)(v1); - if (generated) result[generated.name] = {declaration: generated.css}; + if (generated) result[generated.name] = { declaration: generated.css }; } } else if (!tertiary) { for (const v1 of values) { for (const v2 of secondary) { const generated = (template as any)(v1, v2); - if (generated) result[generated.name] = {declaration: generated.css}; + if (generated) result[generated.name] = { declaration: generated.css }; } } } else { @@ -63,7 +63,7 @@ export const generate_classes = <T1 = string, T2 = string, T3 = string>( for (const v2 of secondary) { for (const v3 of tertiary) { const generated = (template as any)(v1, v2, v3); - if (generated) result[generated.name] = {declaration: generated.css}; + if (generated) result[generated.name] = { declaration: generated.css }; } } } @@ -122,14 +122,14 @@ export const generate_property_classes = ( property: string, values: Iterable<string>, formatter?: (value: string) => string, - prefix: string = format_variable_name(property), + prefix: string = format_variable_name(property) ): Record<string, CssClassDefinition> => { return generate_classes( (value: string) => ({ name: `${prefix}_${format_variable_name(value)}`, - css: `${property}: ${formatter?.(value) ?? value};`, + css: `${property}: ${formatter?.(value) ?? value};` }), - values, + values ); }; @@ -144,7 +144,7 @@ export const generate_property_classes = ( export const generate_directional_classes = ( property: string, values: Iterable<string>, - formatter?: (v: string) => string, + formatter?: (v: string) => string ): Record<string, CssClassDefinition> => { const prefix = property[0]; // 'm' for margin, 'p' for padding @@ -153,38 +153,38 @@ export const generate_directional_classes = ( const formatted = formatter?.(value) ?? value; // Map variants to their configurations - const configs: Record<string, {name: string; css: string} | undefined> = { - '': {name: `${prefix}_${format_variable_name(value)}`, css: `${property}: ${formatted};`}, + const configs: Record<string, { name: string; css: string } | undefined> = { + '': { name: `${prefix}_${format_variable_name(value)}`, css: `${property}: ${formatted};` }, t: { name: `${prefix}t_${format_variable_name(value)}`, - css: `${property}-top: ${formatted};`, + css: `${property}-top: ${formatted};` }, r: { name: `${prefix}r_${format_variable_name(value)}`, - css: `${property}-right: ${formatted};`, + css: `${property}-right: ${formatted};` }, b: { name: `${prefix}b_${format_variable_name(value)}`, - css: `${property}-bottom: ${formatted};`, + css: `${property}-bottom: ${formatted};` }, l: { name: `${prefix}l_${format_variable_name(value)}`, - css: `${property}-left: ${formatted};`, + css: `${property}-left: ${formatted};` }, x: { name: `${prefix}x_${format_variable_name(value)}`, - css: `${property}-left: ${formatted};\t${property}-right: ${formatted};`, + css: `${property}-left: ${formatted};\t${property}-right: ${formatted};` }, y: { name: `${prefix}y_${format_variable_name(value)}`, - css: `${property}-top: ${formatted};\t${property}-bottom: ${formatted};`, - }, + css: `${property}-top: ${formatted};\t${property}-bottom: ${formatted};` + } }; return configs[variant] || null; }, ['', 't', 'r', 'b', 'l', 'x', 'y'], - values, + values ); }; @@ -197,22 +197,22 @@ export const generate_directional_classes = ( */ export const generate_border_radius_corners = ( values: Iterable<string>, - formatter?: (value: string) => string, + formatter?: (value: string) => string ): Record<string, CssClassDefinition> => { const corners = [ - {prop: 'border-top-left-radius', name: 'border_top_left_radius'}, - {prop: 'border-top-right-radius', name: 'border_top_right_radius'}, - {prop: 'border-bottom-left-radius', name: 'border_bottom_left_radius'}, - {prop: 'border-bottom-right-radius', name: 'border_bottom_right_radius'}, + { prop: 'border-top-left-radius', name: 'border_top_left_radius' }, + { prop: 'border-top-right-radius', name: 'border_top_right_radius' }, + { prop: 'border-bottom-left-radius', name: 'border_bottom_left_radius' }, + { prop: 'border-bottom-right-radius', name: 'border_bottom_right_radius' } ]; return generate_classes( (corner: (typeof corners)[0], value: string) => ({ name: `${corner.name}_${format_variable_name(value)}`, - css: `${corner.prop}: ${formatter?.(value) ?? value};`, + css: `${corner.prop}: ${formatter?.(value) ?? value};` }), corners, - values, + values ); }; @@ -226,15 +226,15 @@ export const generate_border_radius_corners = ( */ export const generate_shadow_classes = ( sizes: Iterable<string>, - alpha_mapping: Record<string, string>, + alpha_mapping: Record<string, string> ): Record<string, CssClassDefinition> => { const shadow_types = [ - {prefix: 'shadow', var_prefix: 'shadow'}, - {prefix: 'shadow_top', var_prefix: 'shadow_top'}, - {prefix: 'shadow_bottom', var_prefix: 'shadow_bottom'}, - {prefix: 'shadow_inset', var_prefix: 'shadow_inset'}, - {prefix: 'shadow_inset_top', var_prefix: 'shadow_inset_top'}, - {prefix: 'shadow_inset_bottom', var_prefix: 'shadow_inset_bottom'}, + { prefix: 'shadow', var_prefix: 'shadow' }, + { prefix: 'shadow_top', var_prefix: 'shadow_top' }, + { prefix: 'shadow_bottom', var_prefix: 'shadow_bottom' }, + { prefix: 'shadow_inset', var_prefix: 'shadow_inset' }, + { prefix: 'shadow_inset_top', var_prefix: 'shadow_inset_top' }, + { prefix: 'shadow_inset_bottom', var_prefix: 'shadow_inset_bottom' } ]; return generate_classes( @@ -244,9 +244,9 @@ export const generate_shadow_classes = ( size }) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_${ alpha_mapping[size] - })), transparent);`, + })), transparent);` }), shadow_types, - sizes, + sizes ); }; diff --git a/src/lib/css_class_interpreters.ts b/src/lib/css_class_interpreters.ts index f419e5802..e3fb2bb97 100644 --- a/src/lib/css_class_interpreters.ts +++ b/src/lib/css_class_interpreters.ts @@ -1,14 +1,14 @@ -import {escape_css_selector, type CssClassDefinitionInterpreter} from './css_class_generation.ts'; +import { escape_css_selector, type CssClassDefinitionInterpreter } from './css_class_generation.ts'; import { is_possible_css_literal, interpret_css_literal, generate_css_literal_simple, extract_segments, extract_and_validate_modifiers, - type CssLiteralOutput, + type CssLiteralOutput } from './css_literal.ts'; -import {generate_modified_ruleset} from './css_ruleset_parser.ts'; -import {resolve_class_definition} from './css_class_resolution.ts'; +import { generate_modified_ruleset } from './css_ruleset_parser.ts'; +import { resolve_class_definition } from './css_class_resolution.ts'; /** * Interpreter for modified token/composite classes (e.g., `hover:p_md`, `md:box`, `dark:hover:panel`). @@ -38,7 +38,7 @@ export const modified_class_interpreter: CssClassDefinitionInterpreter = { return null; } - const {modifiers, remaining} = result; + const { modifiers, remaining } = result; // All preceding segments must be valid modifiers (none left over) if (remaining.length !== 0) { @@ -76,7 +76,7 @@ export const modified_class_interpreter: CssClassDefinitionInterpreter = { base_class, base_class_name, ctx.class_definitions, - ctx.css_properties, + ctx.css_properties ); if (!resolution_result.ok) { ctx.diagnostics.push(resolution_result.error); @@ -102,7 +102,7 @@ export const modified_class_interpreter: CssClassDefinitionInterpreter = { declaration: resolution_result.declaration, selector, media_wrapper: modifiers.media?.css ?? null, - ancestor_wrapper: modifiers.ancestor?.css ?? null, + ancestor_wrapper: modifiers.ancestor?.css ?? null }; const css = generate_css_literal_simple(output); @@ -118,7 +118,7 @@ export const modified_class_interpreter: CssClassDefinitionInterpreter = { state_css, pseudo_element_css, modifiers.media?.css ?? null, - modifiers.ancestor?.css ?? null, + modifiers.ancestor?.css ?? null ); // Emit warnings for skipped modifiers @@ -131,7 +131,7 @@ export const modified_class_interpreter: CssClassDefinitionInterpreter = { message: `Rule "${skipped.selector}" already contains a pseudo-element; "${ skipped.conflicting_modifier }" modifier was not applied`, - suggestion: `The rule is included with just the class renamed`, + suggestion: `The rule is included with just the class renamed` }); } else { ctx.diagnostics.push({ @@ -140,7 +140,7 @@ export const modified_class_interpreter: CssClassDefinitionInterpreter = { message: `Rule "${skipped.selector}" already contains "${ skipped.conflicting_modifier }"; modifier was not applied to avoid redundancy`, - suggestion: `The rule is included with just the class renamed`, + suggestion: `The rule is included with just the class renamed` }); } } @@ -150,7 +150,7 @@ export const modified_class_interpreter: CssClassDefinitionInterpreter = { } return null; - }, + } }; /** @@ -186,7 +186,7 @@ export const css_literal_interpreter: CssClassDefinitionInterpreter = { // Return the CSS but strip trailing newline for consistency return css.trimEnd(); - }, + } }; /** @@ -196,5 +196,5 @@ export const css_literal_interpreter: CssClassDefinitionInterpreter = { */ export const css_class_interpreters: Array<CssClassDefinitionInterpreter> = [ modified_class_interpreter, - css_literal_interpreter, + css_literal_interpreter ]; diff --git a/src/lib/css_class_resolution.ts b/src/lib/css_class_resolution.ts index 40b044c65..0fffb2199 100644 --- a/src/lib/css_class_resolution.ts +++ b/src/lib/css_class_resolution.ts @@ -7,24 +7,24 @@ * @module */ -import {levenshtein_distance} from '@fuzdev/fuz_util/string.ts'; +import { levenshtein_distance } from '@fuzdev/fuz_util/string.ts'; -import type {InterpreterDiagnostic} from './diagnostics.ts'; -import type {CssClassDefinition, CssClassDefinitionStatic} from './css_class_generation.ts'; +import type { InterpreterDiagnostic } from './diagnostics.ts'; +import type { CssClassDefinition, CssClassDefinitionStatic } from './css_class_generation.ts'; import { try_resolve_literal, extract_segments, extract_and_validate_modifiers, has_extracted_modifiers, - suggest_modifier, + suggest_modifier } from './css_literal.ts'; /** * Result from resolving a `composes` array to combined declarations. */ export type ResolveComposesResult = - | {ok: true; declaration: string; warnings: Array<InterpreterDiagnostic> | null} - | {ok: false; error: InterpreterDiagnostic}; + | { ok: true; declaration: string; warnings: Array<InterpreterDiagnostic> | null } + | { ok: false; error: InterpreterDiagnostic }; /** * Resolves a class definition's declaration, handling `composes` composition. @@ -43,7 +43,7 @@ export const resolve_class_definition = ( def: CssClassDefinitionStatic, class_name: string, definitions: Record<string, CssClassDefinition | undefined>, - css_properties: Set<string> | null = null, + css_properties: Set<string> | null = null ): ResolveComposesResult => { let warnings: Array<InterpreterDiagnostic> | null = null; @@ -55,7 +55,7 @@ export const resolve_class_definition = ( new Set([class_name]), new Set(), class_name, - css_properties, + css_properties ); if (!result.ok) return result; @@ -72,14 +72,14 @@ export const resolve_class_definition = ( level: 'warning', identifier: class_name, message: `Class "${class_name}" has an empty declaration`, - suggestion: 'Remove the empty declaration property or add CSS', + suggestion: 'Remove the empty declaration property or add CSS' }); } } if (!warnings && result.warnings) { warnings = result.warnings; } - return {ok: true, declaration: combined, warnings}; + return { ok: true, declaration: combined, warnings }; } // Handle declaration-only definitions @@ -91,15 +91,15 @@ export const resolve_class_definition = ( level: 'warning', identifier: class_name, message: `Class "${class_name}" has an empty declaration`, - suggestion: 'Remove the empty declaration property or add CSS', - }, + suggestion: 'Remove the empty declaration property or add CSS' + } ]; } - return {ok: true, declaration: trimmed ?? '', warnings}; + return { ok: true, declaration: trimmed ?? '', warnings }; } // Ruleset definitions don't have a single declaration - return {ok: true, declaration: '', warnings: null}; + return { ok: true, declaration: '', warnings: null }; }; /** @@ -129,7 +129,7 @@ export const resolve_composes = ( resolution_stack: Set<string>, visited: Set<string>, original_class_name: string, - css_properties: Set<string> | null = null, + css_properties: Set<string> | null = null ): ResolveComposesResult => { const declarations: Array<string> = []; let warnings: Array<InterpreterDiagnostic> | null = null; @@ -147,8 +147,8 @@ export const resolve_composes = ( level: 'error', identifier: original_class_name, message: `Circular reference detected: ${cycle_path}`, - suggestion: 'Remove the circular dependency from composes arrays', - }, + suggestion: 'Remove the circular dependency from composes arrays' + } }; } @@ -161,7 +161,7 @@ export const resolve_composes = ( level: 'warning', identifier: original_class_name, message: `Class "${name}" is redundant`, - suggestion: 'Already included by another class in this definition', + suggestion: 'Already included by another class in this definition' }); } continue; @@ -188,8 +188,8 @@ export const resolve_composes = ( level: 'error', identifier: original_class_name, message: `Modified class "${name}" cannot be used in composes array`, - suggestion: 'Apply modified classes directly in markup, not in composes arrays', - }, + suggestion: 'Apply modified classes directly in markup, not in composes arrays' + } }; } else { // Looks like modifier:unknown - report the unknown base @@ -199,8 +199,8 @@ export const resolve_composes = ( level: 'error', identifier: original_class_name, message: `Unknown class "${base_name}" in composes array`, - suggestion: `Check that "${base_name}" is defined in class_definitions`, - }, + suggestion: `Check that "${base_name}" is defined in class_definitions` + } }; } } @@ -242,13 +242,13 @@ export const resolve_composes = ( message: `Unknown modifier "${failed_prop}" with class "${potential_class}"`, suggestion: `Did you mean "${ corrected - }"? Note: modified classes cannot be used in composes`, - }, + }"? Note: modified classes cannot be used in composes` + } }; } } } - return {ok: false, error: literal_result.error}; + return { ok: false, error: literal_result.error }; } // Not a literal - fall through to original "unknown class" error return { @@ -257,8 +257,8 @@ export const resolve_composes = ( level: 'error', identifier: original_class_name, message: `Unknown class "${name}" in composes array`, - suggestion: `Check that "${name}" is defined in class_definitions`, - }, + suggestion: `Check that "${name}" is defined in class_definitions` + } }; } @@ -270,8 +270,8 @@ export const resolve_composes = ( level: 'error', identifier: original_class_name, message: `Cannot reference interpreter pattern "${name}" in composes array`, - suggestion: 'Only static class definitions can be referenced', - }, + suggestion: 'Only static class definitions can be referenced' + } }; } @@ -283,8 +283,8 @@ export const resolve_composes = ( level: 'error', identifier: original_class_name, message: `Cannot reference ruleset class "${name}" in composes array`, - suggestion: 'Ruleset classes have multiple selectors and cannot be inlined', - }, + suggestion: 'Ruleset classes have multiple selectors and cannot be inlined' + } }; } @@ -300,7 +300,7 @@ export const resolve_composes = ( resolution_stack, visited, original_class_name, - css_properties, + css_properties ); resolution_stack.delete(name); if (!nested.ok) return nested; // Propagate error @@ -325,11 +325,11 @@ export const resolve_composes = ( level: 'warning', identifier: name, message: `Class "${name}" has an empty declaration`, - suggestion: 'Remove the empty declaration property or add CSS', + suggestion: 'Remove the empty declaration property or add CSS' }); } } } - return {ok: true, declaration: declarations.join(' '), warnings}; + return { ok: true, declaration: declarations.join(' '), warnings }; }; diff --git a/src/lib/css_classes.ts b/src/lib/css_classes.ts index a125f3988..f3f2f69ca 100644 --- a/src/lib/css_classes.ts +++ b/src/lib/css_classes.ts @@ -7,8 +7,8 @@ * @module */ -import type {SourceLocation, ExtractionDiagnostic} from './diagnostics.ts'; -import type {ExtractionData} from './css_class_extractor.ts'; +import type { SourceLocation, ExtractionDiagnostic } from './diagnostics.ts'; +import type { ExtractionData } from './css_class_extractor.ts'; /** * Collection of CSS classes extracted from source files. @@ -67,7 +67,7 @@ export class CssClasses { */ constructor( additional_classes: Set<string> | null = null, - exclude_classes: Set<string> | null = null, + exclude_classes: Set<string> | null = null ) { this.#additional_classes = additional_classes; this.#exclude_classes = exclude_classes; @@ -173,7 +173,7 @@ export class CssClasses { explicit_classes: this.#explicit, all_elements: this.#all_elements, explicit_elements: this.#explicit_elements, - explicit_variables: this.#explicit_variables, + explicit_variables: this.#explicit_variables }; } diff --git a/src/lib/css_literal.ts b/src/lib/css_literal.ts index 1076a4f0f..9bc532ad5 100644 --- a/src/lib/css_literal.ts +++ b/src/lib/css_literal.ts @@ -11,10 +11,10 @@ * @module */ -import {levenshtein_distance} from '@fuzdev/fuz_util/string.ts'; +import { levenshtein_distance } from '@fuzdev/fuz_util/string.ts'; -import {type InterpreterDiagnostic} from './diagnostics.ts'; -import {get_modifier, get_all_modifier_names, type ModifierDefinition} from './modifiers.ts'; +import { type InterpreterDiagnostic } from './diagnostics.ts'; +import { get_modifier, get_all_modifier_names, type ModifierDefinition } from './modifiers.ts'; // // Types @@ -52,8 +52,8 @@ export interface ParsedCssLiteral { * Callers should use a guard pattern: `if (result.diagnostics) { ... }` */ export type CssLiteralParseResult = - | {ok: true; parsed: ParsedCssLiteral; diagnostics: Array<InterpreterDiagnostic> | null} - | {ok: false; error: InterpreterDiagnostic}; + | { ok: true; parsed: ParsedCssLiteral; diagnostics: Array<InterpreterDiagnostic> | null } + | { ok: false; error: InterpreterDiagnostic }; /** * Extracted modifiers from a class name. @@ -74,8 +74,8 @@ export interface ExtractedModifiers { * Result of extracting modifiers from segments. */ export type ModifierExtractionResult = - | {ok: true; modifiers: ExtractedModifiers; remaining: Array<string>} - | {ok: false; error: InterpreterDiagnostic}; + | { ok: true; modifiers: ExtractedModifiers; remaining: Array<string> } + | { ok: false; error: InterpreterDiagnostic }; /** * Result of interpreting a CSS-literal class. @@ -84,8 +84,8 @@ export type ModifierExtractionResult = * Callers should use a guard pattern: `if (result.warnings) { ... }` */ export type InterpretCssLiteralResult = - | {ok: true; output: CssLiteralOutput; warnings: Array<InterpreterDiagnostic> | null} - | {ok: false; error: InterpreterDiagnostic}; + | { ok: true; output: CssLiteralOutput; warnings: Array<InterpreterDiagnostic> | null } + | { ok: false; error: InterpreterDiagnostic }; // // CSS Property Validation @@ -112,7 +112,7 @@ export const load_css_properties = async (): Promise<Set<string>> => { */ export const is_valid_css_property = ( property: string, - properties: Set<string> | null, + properties: Set<string> | null ): boolean => { // Custom properties are always valid if (property.startsWith('--')) return true; @@ -146,7 +146,7 @@ const find_closest_match = (typo: string, candidates: Iterable<string>): string */ export const suggest_css_property = ( typo: string, - properties: Set<string> | null, + properties: Set<string> | null ): string | null => (properties ? find_closest_match(typo, properties) : null); /** @@ -278,7 +278,7 @@ export const extract_segments = (class_name: string): Array<string> => { */ export const extract_and_validate_modifiers = ( segments: Array<string>, - class_name: string, + class_name: string ): ModifierExtractionResult => { let media: ModifierDefinition | null = null; let ancestor: ModifierDefinition | null = null; @@ -305,8 +305,8 @@ export const extract_and_validate_modifiers = ( level: 'error', message: `Multiple media modifiers not allowed`, identifier: class_name, - suggestion: null, - }, + suggestion: null + } }; } if (ancestor) { @@ -316,8 +316,8 @@ export const extract_and_validate_modifiers = ( level: 'error', message: `Media modifier must come before ancestor modifier`, identifier: class_name, - suggestion: `Move "${segment}" before "${ancestor.name}"`, - }, + suggestion: `Move "${segment}" before "${ancestor.name}"` + } }; } if (states.length > 0) { @@ -327,8 +327,8 @@ export const extract_and_validate_modifiers = ( level: 'error', message: `Media modifier must come before state modifiers`, identifier: class_name, - suggestion: `Move "${segment}" before "${states[0]!.name}"`, - }, + suggestion: `Move "${segment}" before "${states[0]!.name}"` + } }; } if (pseudo_element) { @@ -338,8 +338,8 @@ export const extract_and_validate_modifiers = ( level: 'error', message: `Media modifier must come before pseudo-element`, identifier: class_name, - suggestion: `Move "${segment}" before "${pseudo_element.name}"`, - }, + suggestion: `Move "${segment}" before "${pseudo_element.name}"` + } }; } media = modifier; @@ -354,8 +354,8 @@ export const extract_and_validate_modifiers = ( level: 'error', message: `Modifiers "${ancestor.name}" and "${segment}" are mutually exclusive`, identifier: class_name, - suggestion: null, - }, + suggestion: null + } }; } if (states.length > 0) { @@ -365,8 +365,8 @@ export const extract_and_validate_modifiers = ( level: 'error', message: `Ancestor modifier must come before state modifiers`, identifier: class_name, - suggestion: `Move "${segment}" before "${states[0]!.name}"`, - }, + suggestion: `Move "${segment}" before "${states[0]!.name}"` + } }; } if (pseudo_element) { @@ -376,8 +376,8 @@ export const extract_and_validate_modifiers = ( level: 'error', message: `Ancestor modifier must come before pseudo-element`, identifier: class_name, - suggestion: `Move "${segment}" before "${pseudo_element.name}"`, - }, + suggestion: `Move "${segment}" before "${pseudo_element.name}"` + } }; } ancestor = modifier; @@ -392,8 +392,8 @@ export const extract_and_validate_modifiers = ( level: 'error', message: `State modifiers must come before pseudo-element`, identifier: class_name, - suggestion: `Move "${segment}" before "${pseudo_element.name}"`, - }, + suggestion: `Move "${segment}" before "${pseudo_element.name}"` + } }; } // Check alphabetical order (full string comparison) @@ -408,8 +408,8 @@ export const extract_and_validate_modifiers = ( segment }" should be "${segment}:${prev.name}"`, identifier: class_name, - suggestion: `Reorder to: ...${segment}:${prev.name}:...`, - }, + suggestion: `Reorder to: ...${segment}:${prev.name}:...` + } }; } } @@ -425,8 +425,8 @@ export const extract_and_validate_modifiers = ( level: 'error', message: `Multiple pseudo-element modifiers not allowed`, identifier: class_name, - suggestion: null, - }, + suggestion: null + } }; } pseudo_element = modifier; @@ -437,8 +437,8 @@ export const extract_and_validate_modifiers = ( return { ok: true, - modifiers: {media, ancestor, states, pseudo_element}, - remaining: segments.slice(i), + modifiers: { media, ancestor, states, pseudo_element }, + remaining: segments.slice(i) }; }; @@ -452,7 +452,7 @@ export const extract_and_validate_modifiers = ( */ export const parse_css_literal = ( class_name: string, - css_properties: Set<string> | null, + css_properties: Set<string> | null ): CssLiteralParseResult => { const segments = extract_segments(class_name); @@ -463,8 +463,8 @@ export const parse_css_literal = ( level: 'error', message: `Invalid CSS-literal syntax: expected "property:value" format`, identifier: class_name, - suggestion: null, - }, + suggestion: null + } }; } @@ -480,7 +480,7 @@ export const parse_css_literal = ( const modifier_result = extract_and_validate_modifiers(modifier_segments, class_name); if (!modifier_result.ok) { - return {ok: false, error: modifier_result.error}; + return { ok: false, error: modifier_result.error }; } // All segments should have been consumed as modifiers @@ -494,12 +494,12 @@ export const parse_css_literal = ( level: 'error', message: `Unknown modifier "${unknown}"`, identifier: class_name, - suggestion: suggestion ? `Did you mean "${suggestion}"?` : null, - }, + suggestion: suggestion ? `Did you mean "${suggestion}"?` : null + } }; } - const {media, ancestor, states, pseudo_element} = modifier_result.modifiers; + const { media, ancestor, states, pseudo_element } = modifier_result.modifiers; // Validate property if (!is_valid_css_property(property, css_properties)) { @@ -510,8 +510,8 @@ export const parse_css_literal = ( level: 'error', message: `Unknown CSS property "${property}"`, identifier: class_name, - suggestion: suggestion ? `Did you mean "${suggestion}"?` : null, - }, + suggestion: suggestion ? `Did you mean "${suggestion}"?` : null + } }; } @@ -525,7 +525,7 @@ export const parse_css_literal = ( level: 'warning', message: calc_warning, identifier: class_name, - suggestion: `Use ~ for spaces in calc expressions`, + suggestion: `Use ~ for spaces in calc expressions` }); } @@ -538,9 +538,9 @@ export const parse_css_literal = ( states, pseudo_element, property, - value: formatted_value, + value: formatted_value }, - diagnostics, + diagnostics }; }; @@ -603,15 +603,15 @@ export interface CssLiteralOutput { export const interpret_css_literal = ( class_name: string, escaped_class_name: string, - css_properties: Set<string> | null, + css_properties: Set<string> | null ): InterpretCssLiteralResult => { const result = parse_css_literal(class_name, css_properties); if (!result.ok) { - return {ok: false, error: result.error}; + return { ok: false, error: result.error }; } - const {parsed, diagnostics} = result; + const { parsed, diagnostics } = result; return { ok: true, @@ -619,9 +619,9 @@ export const interpret_css_literal = ( declaration: generate_declaration(parsed), selector: generate_selector(escaped_class_name, parsed), media_wrapper: parsed.media?.css ?? null, - ancestor_wrapper: parsed.ancestor?.css ?? null, + ancestor_wrapper: parsed.ancestor?.css ?? null }, - warnings: diagnostics, + warnings: diagnostics }; }; @@ -633,8 +633,8 @@ export const interpret_css_literal = ( * Result of attempting to resolve a CSS literal for composition. */ export type LiteralResolutionResult = - | {ok: true; declaration: string; warnings: Array<InterpreterDiagnostic> | null} - | {ok: false; error: InterpreterDiagnostic | null}; + | { ok: true; declaration: string; warnings: Array<InterpreterDiagnostic> | null } + | { ok: false; error: InterpreterDiagnostic | null }; /** * Checks if a parsed CSS literal has any modifiers. @@ -671,21 +671,21 @@ export const has_extracted_modifiers = (modifiers: ExtractedModifiers): boolean export const try_resolve_literal = ( class_name: string, css_properties: Set<string> | null, - context_class_name: string, + context_class_name: string ): LiteralResolutionResult => { // Quick check - must look like a CSS literal if (!is_possible_css_literal(class_name)) { - return {ok: false, error: null}; + return { ok: false, error: null }; } // Parse the literal const result = parse_css_literal(class_name, css_properties); if (!result.ok) { - return {ok: false, error: contextualize_error(result.error, context_class_name)}; + return { ok: false, error: contextualize_error(result.error, context_class_name) }; } - const {parsed, diagnostics} = result; + const { parsed, diagnostics } = result; // Check for modifiers - modified literals cannot be composed if (has_modifiers(parsed)) { @@ -695,8 +695,8 @@ export const try_resolve_literal = ( level: 'error', identifier: context_class_name, message: `Modified class "${class_name}" cannot be used in composes array`, - suggestion: 'Apply modified classes directly in markup, not in composes arrays', - }, + suggestion: 'Apply modified classes directly in markup, not in composes arrays' + } }; } @@ -704,7 +704,7 @@ export const try_resolve_literal = ( return { ok: true, declaration: generate_declaration(parsed), - warnings: diagnostics, + warnings: diagnostics }; }; @@ -713,10 +713,10 @@ export const try_resolve_literal = ( */ const contextualize_error = ( error: InterpreterDiagnostic, - context_class_name: string, + context_class_name: string ): InterpreterDiagnostic => ({ ...error, - identifier: context_class_name, + identifier: context_class_name }); /** diff --git a/src/lib/css_plugin_options.ts b/src/lib/css_plugin_options.ts index 26de0bda8..b23de14b7 100644 --- a/src/lib/css_plugin_options.ts +++ b/src/lib/css_plugin_options.ts @@ -28,11 +28,11 @@ * @module */ -import type {FileFilter} from './file_filter.ts'; -import type {AcornPlugin} from './css_class_extractor.ts'; -import type {CssClassDefinition, CssClassDefinitionInterpreter} from './css_class_generation.ts'; -import type {StyleVariable} from './variable.ts'; -import type {CacheDeps} from './deps.ts'; +import type { FileFilter } from './file_filter.ts'; +import type { AcornPlugin } from './css_class_extractor.ts'; +import type { CssClassDefinition, CssClassDefinitionInterpreter } from './css_class_generation.ts'; +import type { StyleVariable } from './variable.ts'; +import type { CacheDeps } from './deps.ts'; /** * Options for CSS class extraction from source files. diff --git a/src/lib/css_ruleset_parser.ts b/src/lib/css_ruleset_parser.ts index bf38698dd..5621764eb 100644 --- a/src/lib/css_ruleset_parser.ts +++ b/src/lib/css_ruleset_parser.ts @@ -9,8 +9,8 @@ * @module */ -import {escape_regexp} from '@fuzdev/fuz_util/regexp.ts'; -import {parseCss, type AST} from 'svelte/compiler'; +import { escape_regexp } from '@fuzdev/fuz_util/regexp.ts'; +import { parseCss, type AST } from 'svelte/compiler'; // // Parsing @@ -55,7 +55,7 @@ export const parse_ruleset = (css: string): ParsedRuleset => { // Walk the CSS AST to find rules walk_css_children(ast, css, rules); - return {rules}; + return { rules }; }; /** @@ -65,7 +65,7 @@ export const parse_ruleset = (css: string): ParsedRuleset => { const walk_css_children = ( node: Omit<AST.CSS.StyleSheetFile, 'attributes' | 'content'> | AST.CSS.Atrule, original_css: string, - rules: Array<ParsedRule>, + rules: Array<ParsedRule> ): void => { const children = 'children' in node ? node.children : []; @@ -86,7 +86,7 @@ const walk_css_children = ( const walk_css_block = ( block: AST.CSS.Block, original_css: string, - rules: Array<ParsedRule>, + rules: Array<ParsedRule> ): void => { for (const child of block.children) { if (child.type === 'Rule') { @@ -125,7 +125,7 @@ const extract_rule = (rule: AST.CSS.Rule, original_css: string, rules: Array<Par selector_end, declarations, rule_start: rule.start, - rule_end: rule.end, + rule_end: rule.end }); }; @@ -139,7 +139,7 @@ const extract_rule = (rule: AST.CSS.Rule, original_css: string, rules: Array<Par */ export const is_single_selector_ruleset = ( rules: Array<ParsedRule>, - escaped_class_name: string, + escaped_class_name: string ): boolean => { if (rules.length !== 1) return false; @@ -162,7 +162,7 @@ export const is_single_selector_ruleset = ( */ export const ruleset_contains_class = ( rules: Array<ParsedRule>, - escaped_class_name: string, + escaped_class_name: string ): boolean => { // Match .class_name but not as part of another class (e.g., .class_name_foo) const pattern = new RegExp(`\\.${escape_regexp(escaped_class_name)}(?![\\w-])`); @@ -446,7 +446,7 @@ export const modify_single_selector = ( original_class: string, new_class_escaped: string, state_css: string, - pseudo_element_css: string, + pseudo_element_css: string ): string => { // Find the target class (must match the class name exactly, not as part of another class) const class_pattern = new RegExp(`\\.${escape_regexp(original_class)}(?![\\w-])`); @@ -486,7 +486,7 @@ export const modify_selector_group = ( original_class: string, new_class_escaped: string, states_to_add: Array<string>, - pseudo_element_css: string, + pseudo_element_css: string ): ModifiedSelectorGroupResult => { const selectors = split_selector_list(selector_group); let skipped_modifiers: Array<SkippedModifierInfo> | null = null; @@ -507,7 +507,7 @@ export const modify_selector_group = ( (skipped_modifiers ??= []).push({ selector: trimmed, reason: 'state_conflict', - conflicting_modifier: state, + conflicting_modifier: state }); } else { non_conflicting_states.push(state); @@ -519,7 +519,7 @@ export const modify_selector_group = ( (skipped_modifiers ??= []).push({ selector: trimmed, reason: 'pseudo_element_conflict', - conflicting_modifier: pseudo_element_css, + conflicting_modifier: pseudo_element_css }); } @@ -532,13 +532,13 @@ export const modify_selector_group = ( original_class, new_class_escaped, effective_state_css, - effective_pseudo_element_css, + effective_pseudo_element_css ); }); return { selector: modified_selectors.join(',\n'), - skipped_modifiers, + skipped_modifiers }; }; @@ -567,7 +567,7 @@ export const generate_modified_ruleset = ( state_css: string, pseudo_element_css: string, media_wrapper: string | null, - ancestor_wrapper: string | null, + ancestor_wrapper: string | null ): ModifiedRulesetResult => { const parsed = parse_ruleset(original_ruleset); let skipped_modifiers: Array<SkippedModifierInfo> | null = null; @@ -597,7 +597,7 @@ export const generate_modified_ruleset = ( original_class, new_class_escaped, states_to_add, - pseudo_element_css, + pseudo_element_css ); // Collect skip info from per-selector conflict detection @@ -619,5 +619,5 @@ export const generate_modified_ruleset = ( css += '}\n'; } - return {css, skipped_modifiers}; + return { css, skipped_modifiers }; }; diff --git a/src/lib/deps.ts b/src/lib/deps.ts index d49e9c7f2..55ab148ac 100644 --- a/src/lib/deps.ts +++ b/src/lib/deps.ts @@ -38,10 +38,10 @@ * @module */ -import type {Result} from '@fuzdev/fuz_util/result.ts'; -import type {FsError} from '@fuzdev/fuz_util/fs.ts'; +import type { Result } from '@fuzdev/fuz_util/result.ts'; +import type { FsError } from '@fuzdev/fuz_util/fs.ts'; -export type {FsError}; +export type { FsError }; /** * Cache-related file system deps. @@ -56,17 +56,20 @@ export interface CacheDeps { * Reads a text file. * Returns a `not_found` error if the file doesn't exist. */ - read_text: (options: {path: string}) => Promise<Result<{value: string}, FsError>>; + read_text: (options: { path: string }) => Promise<Result<{ value: string }, FsError>>; /** * Writes a text file atomically (temp file + rename for crash safety). * Creates parent directories if they don't exist. */ - write_text_atomic: (options: {path: string; content: string}) => Promise<Result<object, FsError>>; + write_text_atomic: (options: { + path: string; + content: string; + }) => Promise<Result<object, FsError>>; /** * Removes a file. Returns a `not_found` error if the file doesn't exist — * callers that want `rm -f` semantics should ignore that kind explicitly. */ - unlink: (options: {path: string}) => Promise<Result<object, FsError>>; + unlink: (options: { path: string }) => Promise<Result<object, FsError>>; } diff --git a/src/lib/deps_defaults.ts b/src/lib/deps_defaults.ts index 39cde1d29..35d56f32f 100644 --- a/src/lib/deps_defaults.ts +++ b/src/lib/deps_defaults.ts @@ -7,13 +7,13 @@ * @module */ -import {readFile, writeFile, mkdir, unlink, rename} from 'node:fs/promises'; -import {dirname} from 'node:path'; +import { readFile, writeFile, mkdir, unlink, rename } from 'node:fs/promises'; +import { dirname } from 'node:path'; -import type {Result} from '@fuzdev/fuz_util/result.ts'; -import {fs_classify_error, type FsError} from '@fuzdev/fuz_util/fs.ts'; +import type { Result } from '@fuzdev/fuz_util/result.ts'; +import { fs_classify_error, type FsError } from '@fuzdev/fuz_util/fs.ts'; -import type {CacheDeps} from './deps.ts'; +import type { CacheDeps } from './deps.ts'; /** * Wraps an async void-returning function, converting thrown errors to typed `FsError`. @@ -21,9 +21,9 @@ import type {CacheDeps} from './deps.ts'; const wrap_void = async (fn: () => Promise<unknown>): Promise<Result<object, FsError>> => { try { await fn(); - return {ok: true}; + return { ok: true }; } catch (error) { - return {ok: false, ...fs_classify_error(error)}; + return { ok: false, ...fs_classify_error(error) }; } }; @@ -31,24 +31,24 @@ const wrap_void = async (fn: () => Promise<unknown>): Promise<Result<object, FsE * Default filesystem deps using `node:fs/promises`. */ export const default_cache_deps: CacheDeps = { - read_text: async ({path}) => { + read_text: async ({ path }) => { try { - return {ok: true, value: await readFile(path, 'utf8')}; + return { ok: true, value: await readFile(path, 'utf8') }; } catch (error) { - return {ok: false, ...fs_classify_error(error)}; + return { ok: false, ...fs_classify_error(error) }; } }, - write_text_atomic: async ({path, content}) => { + write_text_atomic: async ({ path, content }) => { return wrap_void(async () => { // Atomic write: temp file + rename // Include pid + timestamp to avoid conflicts with concurrent writes - await mkdir(dirname(path), {recursive: true}); + await mkdir(dirname(path), { recursive: true }); const temp_path = path + '.tmp.' + process.pid + '.' + Date.now(); await writeFile(temp_path, content); await rename(temp_path, path); }); }, - unlink: async ({path}) => wrap_void(() => unlink(path)), + unlink: async ({ path }) => wrap_void(() => unlink(path)) }; diff --git a/src/lib/diagnostics.ts b/src/lib/diagnostics.ts index 93c784a2d..05b130083 100644 --- a/src/lib/diagnostics.ts +++ b/src/lib/diagnostics.ts @@ -87,14 +87,14 @@ export interface InterpreterDiagnostic { */ export const create_generation_diagnostic = ( diagnostic: InterpreterDiagnostic, - locations: Array<SourceLocation> | null, + locations: Array<SourceLocation> | null ): GenerationDiagnostic => ({ phase: 'generation', level: diagnostic.level, message: diagnostic.message, identifier: diagnostic.identifier, suggestion: diagnostic.suggestion ?? null, - locations, + locations }); /** diff --git a/src/lib/example_class_utilities.ts b/src/lib/example_class_utilities.ts index 2f57099bd..2f88cc512 100644 --- a/src/lib/example_class_utilities.ts +++ b/src/lib/example_class_utilities.ts @@ -79,7 +79,7 @@ export const logicalClass = true && 'mt_md'; export const arrayClasses = ['mt_lg', 'mt_xl']; /** Object - keys extracted as class names */ -export const objectClasses = {mt_xl2: 'mt_xl2', mt_xl3: 'mt_xl3'}; +export const objectClasses = { mt_xl2: 'mt_xl2', mt_xl3: 'mt_xl3' }; // // Comment hint examples - @fuz-classes directive diff --git a/src/lib/extract_file_cached.ts b/src/lib/extract_file_cached.ts index 08e5626a3..8edbd5d3a 100644 --- a/src/lib/extract_file_cached.ts +++ b/src/lib/extract_file_cached.ts @@ -13,10 +13,10 @@ import { extract_css_classes_with_locations, type ExtractionData, - type AcornPlugin, + type AcornPlugin } from './css_class_extractor.ts'; -import {load_cached_extraction, from_cached_extraction} from './css_cache.ts'; -import type {CacheDeps} from './deps.ts'; +import { load_cached_extraction, from_cached_extraction } from './css_cache.ts'; +import type { CacheDeps } from './deps.ts'; export interface ExtractFileCachedOptions { deps: CacheDeps; @@ -45,9 +45,9 @@ export interface ExtractFileCachedResult { * the caller should write. */ export const extract_file_cached = async ( - options: ExtractFileCachedOptions, + options: ExtractFileCachedOptions ): Promise<ExtractFileCachedResult> => { - const {deps, content, content_hash, cache_path, filename, acorn_plugins} = options; + const { deps, content, content_hash, cache_path, filename, acorn_plugins } = options; if (cache_path) { const cached = await load_cached_extraction(deps, cache_path); @@ -55,11 +55,11 @@ export const extract_file_cached = async ( return { extraction: from_cached_extraction(cached), from_cache: true, - cache_path_to_write: null, + cache_path_to_write: null }; } } - const extraction = extract_css_classes_with_locations(content, {filename, acorn_plugins}); - return {extraction, from_cache: false, cache_path_to_write: cache_path}; + const extraction = extract_css_classes_with_locations(content, { filename, acorn_plugins }); + return { extraction, from_cache: false, cache_path_to_write: cache_path }; }; diff --git a/src/lib/gen_fuz_css.ts b/src/lib/gen_fuz_css.ts index c7d26d65b..c620d2d71 100644 --- a/src/lib/gen_fuz_css.ts +++ b/src/lib/gen_fuz_css.ts @@ -7,30 +7,30 @@ * @module */ -import {join} from 'node:path'; -import type {Gen} from '@fuzdev/gro/gen.ts'; -import {map_concurrent, each_concurrent} from '@fuzdev/fuz_util/async.ts'; - -import {filter_file_default} from './file_filter.ts'; -import {type ExtractionData, has_extraction_data} from './css_class_extractor.ts'; -import {format_diagnostic, CssGenerationError} from './diagnostics.ts'; -import {CssClasses} from './css_classes.ts'; -import {generate_css} from './generate_css.ts'; -import {create_bundled_resources, type BundledCssResources} from './bundled_resources.ts'; -import {extract_file_cached} from './extract_file_cached.ts'; -import {merge_class_definitions} from './css_class_definitions.ts'; -import {css_class_interpreters} from './css_class_interpreters.ts'; -import {load_css_properties} from './css_literal.ts'; +import { join } from 'node:path'; +import type { Gen } from '@fuzdev/gro/gen.ts'; +import { map_concurrent, each_concurrent } from '@fuzdev/fuz_util/async.ts'; + +import { filter_file_default } from './file_filter.ts'; +import { type ExtractionData, has_extraction_data } from './css_class_extractor.ts'; +import { format_diagnostic, CssGenerationError } from './diagnostics.ts'; +import { CssClasses } from './css_classes.ts'; +import { generate_css } from './generate_css.ts'; +import { create_bundled_resources, type BundledCssResources } from './bundled_resources.ts'; +import { extract_file_cached } from './extract_file_cached.ts'; +import { merge_class_definitions } from './css_class_definitions.ts'; +import { css_class_interpreters } from './css_class_interpreters.ts'; +import { load_css_properties } from './css_literal.ts'; import { DEFAULT_CACHE_DIR, get_file_cache_path, save_cached_extraction, - delete_cached_extraction, + delete_cached_extraction } from './css_cache.ts'; -import {default_cache_deps} from './deps_defaults.ts'; -import {get_all_variable_names} from './variable_graph.ts'; -import {extract_css_variables} from './css_variable_utils.ts'; -import type {CssGeneratorBaseOptions} from './css_plugin_options.ts'; +import { default_cache_deps } from './deps_defaults.ts'; +import { get_all_variable_names } from './variable_graph.ts'; +import { extract_css_variables } from './css_variable_utils.ts'; +import type { CssGeneratorBaseOptions } from './css_plugin_options.ts'; /** * Skip cache on CI (no point writing cache that won't be reused). @@ -117,7 +117,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { additional_variables, exclude_elements, exclude_variables, - deps = default_cache_deps, + deps = default_cache_deps } = options; // Derive include flags from null check @@ -131,7 +131,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { // Merge class definitions upfront (validates that definitions exist when needed) const all_class_definitions = merge_class_definitions( user_class_definitions, - include_default_classes, + include_default_classes ); // Lazy-load expensive resources, cached per generator instance so watch-mode @@ -152,7 +152,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { base_css, variables, class_definitions: all_class_definitions, - deps, + deps }); } return bundled_resources; @@ -164,13 +164,13 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { return { // Filter dependencies to skip non-extractable files. // Returns 'all' when an extractable file changes, null otherwise. - dependencies: ({changed_file_id}) => { + dependencies: ({ changed_file_id }) => { if (!changed_file_id) return 'all'; if (filter_file(changed_file_id)) return 'all'; return null; // Ignore .json, .md, etc. }, - generate: async ({filer, log, origin_path}) => { + generate: async ({ filer, log, origin_path }) => { log.info('generating fuz_css classes...'); // Load CSS properties for validation (cached per instance) @@ -196,7 +196,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { files_with_content: 0, files_with_classes: 0, cache_hits: 0, - cache_misses: 0, + cache_misses: 0 }; // Collect nodes to process @@ -224,7 +224,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { nodes.push({ id: disknode.id, contents: disknode.contents, - content_hash: disknode.content_hash, + content_hash: disknode.content_hash }); } } @@ -239,13 +239,13 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { ? null : get_file_cache_path(node.id, resolved_cache_dir, project_root); - const {extraction, from_cache, cache_path_to_write} = await extract_file_cached({ + const { extraction, from_cache, cache_path_to_write } = await extract_file_cached({ deps, content: node.contents, content_hash: node.content_hash, cache_path, filename: node.id, - acorn_plugins, + acorn_plugins }); if (from_cache) { @@ -263,9 +263,9 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { explicit_elements: extraction.explicit_elements, explicit_variables: extraction.explicit_variables, cache_path: cache_path_to_write, - content_hash: node.content_hash, + content_hash: node.content_hash }; - }, + } ); // Add to CssClasses (skip files with all-null extraction data) @@ -280,7 +280,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { // Collect cache writes (entries that need writing) const cache_writes = extractions.filter( - (e): e is FileExtraction & {cache_path: string} => e.cache_path !== null, + (e): e is FileExtraction & { cache_path: string } => e.cache_path !== null ); // Parallel cache writes (await completion) @@ -290,7 +290,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { deps, extraction.cache_path, extraction.content_hash, - extraction, + extraction ); }).catch((err) => log.warn('Cache write error:', err)); } @@ -317,7 +317,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { explicit_classes, all_elements, explicit_elements, - explicit_variables, + explicit_variables } = css_classes.get_all(); if (include_stats) { @@ -349,7 +349,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { } } - const {css: final_css, diagnostics: all_diagnostics} = generate_css({ + const { css: final_css, diagnostics: all_diagnostics } = generate_css({ all_classes, all_classes_with_locations, explicit_classes, @@ -369,7 +369,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { exclude_elements, exclude_variables, log, - include_stats, + include_stats }); // Separate errors and warnings @@ -421,6 +421,6 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { content_parts.push(`/* ${banner} */`); return content_parts.join('\n\n'); - }, + } }; }; diff --git a/src/lib/generate_css.ts b/src/lib/generate_css.ts index 76ee02a39..c45aa891f 100644 --- a/src/lib/generate_css.ts +++ b/src/lib/generate_css.ts @@ -10,17 +10,17 @@ * @module */ -import type {Logger} from '@fuzdev/fuz_util/log.ts'; +import type { Logger } from '@fuzdev/fuz_util/log.ts'; -import type {Diagnostic, SourceLocation} from './diagnostics.ts'; +import type { Diagnostic, SourceLocation } from './diagnostics.ts'; import { generate_classes_css, type CssClassDefinition, - type CssClassDefinitionInterpreter, + type CssClassDefinitionInterpreter } from './css_class_generation.ts'; -import {resolve_css, generate_bundled_css} from './css_bundled_resolution.ts'; -import {get_all_variable_names} from './variable_graph.ts'; -import type {BundledCssResources} from './bundled_resources.ts'; +import { resolve_css, generate_bundled_css } from './css_bundled_resolution.ts'; +import { get_all_variable_names } from './variable_graph.ts'; +import type { BundledCssResources } from './bundled_resources.ts'; /** * Inputs to `generate_css`. The first group mirrors the shape returned by @@ -104,7 +104,7 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => exclude_elements, exclude_variables, log, - include_stats = false, + include_stats = false } = options; const utility_result = generate_classes_css({ @@ -114,7 +114,7 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => css_properties, log, class_locations: all_classes_with_locations, - explicit_classes, + explicit_classes }); const diagnostics: Array<Diagnostic> = [...extraction_diagnostics, ...utility_result.diagnostics]; @@ -144,19 +144,19 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => exclude_elements, exclude_variables, explicit_elements, - explicit_variables, + explicit_variables }); if (include_stats && resolution.stats && log) { log.info( `[css_resolution] Elements: ${ resolution.stats.element_count - } (${resolution.stats.elements.join(', ')})`, + } (${resolution.stats.elements.join(', ')})` ); log.info( `[css_resolution] Rules: ${resolution.stats.included_rules} of ${ resolution.stats.total_rules - }`, + }` ); log.info(`[css_resolution] Variables: ${resolution.stats.variable_count} resolved`); } @@ -170,7 +170,7 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => if (include_base && !include_theme) { const theme_var_names = get_all_variable_names(resources.variable_graph); const references_theme_var = [...resolution.resolved_variables].some((v) => - theme_var_names.has(v), + theme_var_names.has(v) ); if (references_theme_var) { diagnostics.push({ @@ -180,7 +180,7 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => 'Base styles are enabled but theme variables are disabled (variables: null); emitted styles reference theme variables that will be undefined', suggestion: 'Import theme.css separately, or set base_css: null for utility-only mode.', identifier: 'theme_variables_disabled', - locations: null, + locations: null }); } } @@ -188,12 +188,12 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => css = generate_bundled_css(resolution, utility_result.css, { include_theme, include_base, - include_utilities: true, + include_utilities: true }); } else { // utility-only mode css = utility_result.css; } - return {css, diagnostics}; + return { css, diagnostics }; }; diff --git a/src/lib/knobs.ts b/src/lib/knobs.ts index 58595fc2d..9a2842525 100644 --- a/src/lib/knobs.ts +++ b/src/lib/knobs.ts @@ -20,7 +20,7 @@ * @module */ -import type {StyleVariableName} from './variable.ts'; +import type { StyleVariableName } from './variable.ts'; /** * The value kind of a knob, determining the editor widget and how the value @@ -42,13 +42,7 @@ export type KnobKind = * The axis of the theme-space taxonomy a knob moves. */ export type KnobAxis = - | 'color' - | 'shape' - | 'density' - | 'depth' - | 'typography' - | 'motion' - | 'decoration'; + 'color' | 'shape' | 'density' | 'depth' | 'typography' | 'motion' | 'decoration'; /** * Leverage tier: how much of the system a knob reshapes, which the editor @@ -96,7 +90,7 @@ const hue = ( name: StyleVariableName, leverage: KnobLeverage, tier: KnobTier = 'semantic', - bindable = false, + bindable = false ): ThemeKnob => ({ name, kind: 'hue', @@ -105,7 +99,7 @@ const hue = ( tier, range: [0, 360], step: 1, - ...(bindable ? {bindable} : null), + ...(bindable ? { bindable } : null) }); const lightness_ramp = (family: string): Array<ThemeKnob> => [ @@ -116,7 +110,7 @@ const lightness_ramp = (family: string): Array<ThemeKnob> => [ leverage: 'md', tier: 'semantic', range: [0, 1], - step: 0.001, + step: 0.001 }, { name: `${family}_lightness_100`, @@ -125,7 +119,7 @@ const lightness_ramp = (family: string): Array<ThemeKnob> => [ leverage: 'md', tier: 'semantic', range: [0, 1], - step: 0.001, + step: 0.001 }, { name: `${family}_lightness_curve`, @@ -134,8 +128,8 @@ const lightness_ramp = (family: string): Array<ThemeKnob> => [ leverage: 'md', tier: 'semantic', range: [0.2, 4], - step: 0.01, - }, + step: 0.01 + } ]; /** @@ -152,7 +146,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'lg', tier: 'semantic', range: [0, 0.1], - step: 0.001, + step: 0.001 }, hue('hue_accent', 'lg', 'semantic', true), { @@ -162,7 +156,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'lg', tier: 'semantic', range: [0, 2], - step: 0.05, + step: 0.05 }, { name: 'hue_shift', @@ -171,7 +165,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'lg', tier: 'semantic', range: [-45, 45], - step: 1, + step: 1 }, hue('hue_positive', 'md', 'semantic', true), hue('hue_negative', 'md', 'semantic', true), @@ -187,7 +181,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'md', tier: 'semantic', range: [0, 0.06], - step: 0.001, + step: 0.001 }, { // requests up to the per-stop gamut caps; chroma_scale pushes past them @@ -197,7 +191,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'md', tier: 'semantic', range: [0, 0.125], - step: 0.001, + step: 0.001 }, { name: 'palette_chroma_curve', @@ -206,17 +200,24 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'md', tier: 'semantic', range: [0.2, 4], - step: 0.01, + step: 0.01 }, // color - micro-surface hooks consumed by style.css via var() fallbacks - {name: 'caret_color', kind: 'color', axis: 'color', leverage: 'sm', tier: 'semantic', hook: true}, + { + name: 'caret_color', + kind: 'color', + axis: 'color', + leverage: 'sm', + tier: 'semantic', + hook: true + }, { name: 'scrollbar_thumb_color', kind: 'color', axis: 'color', leverage: 'sm', tier: 'semantic', - hook: true, + hook: true }, { name: 'scrollbar_track_color', @@ -224,7 +225,7 @@ export const theme_knobs: Array<ThemeKnob> = [ axis: 'color', leverage: 'sm', tier: 'semantic', - hook: true, + hook: true }, // color - the palette tier (moving these makes a theme an exemplar) hue('hue_a', 'sm', 'palette'), @@ -245,7 +246,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'lg', tier: 'semantic', range: [0, 3], - step: 0.05, + step: 0.05 }, { name: 'border_style', @@ -253,25 +254,25 @@ export const theme_knobs: Array<ThemeKnob> = [ axis: 'shape', leverage: 'md', tier: 'semantic', - values: ['solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'none'], - }, - {name: 'border_width', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_width_1', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_width_2', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_width_3', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_width_4', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_width_5', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_width_6', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_width_7', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_width_8', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_width_9', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_radius_xs3', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_radius_xs2', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_radius_xs', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_radius_sm', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_radius_md', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_radius_lg', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, - {name: 'border_radius_xl', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic'}, + values: ['solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'none'] + }, + { name: 'border_width', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_width_1', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_width_2', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_width_3', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_width_4', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_width_5', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_width_6', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_width_7', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_width_8', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_width_9', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_radius_xs3', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_radius_xs2', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_radius_xs', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_radius_sm', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_radius_md', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_radius_lg', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, + { name: 'border_radius_xl', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, // density { name: 'scale_factor', @@ -280,31 +281,31 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'lg', tier: 'semantic', range: [0.25, 2], - step: 0.05, - }, - {name: 'space_xs5', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xs4', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xs3', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xs2', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xs', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_sm', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_md', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_lg', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl2', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl3', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl4', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl5', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl6', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl7', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl8', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl9', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl10', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl11', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl12', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl13', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl14', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, - {name: 'space_xl15', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic'}, + step: 0.05 + }, + { name: 'space_xs5', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xs4', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xs3', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xs2', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xs', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_sm', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_md', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_lg', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl2', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl3', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl4', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl5', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl6', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl7', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl8', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl9', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl10', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl11', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl12', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl13', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl14', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, + { name: 'space_xl15', kind: 'length', axis: 'density', leverage: 'sm', tier: 'semantic' }, // depth { name: 'shadow_alpha_scale', @@ -313,12 +314,18 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'lg', tier: 'semantic', range: [0, 2], - step: 0.05, + step: 0.05 + }, + { name: 'shadow_color_umbra', kind: 'color', axis: 'depth', leverage: 'md', tier: 'semantic' }, + { + name: 'shadow_color_highlight', + kind: 'color', + axis: 'depth', + leverage: 'md', + tier: 'semantic' }, - {name: 'shadow_color_umbra', kind: 'color', axis: 'depth', leverage: 'md', tier: 'semantic'}, - {name: 'shadow_color_highlight', kind: 'color', axis: 'depth', leverage: 'md', tier: 'semantic'}, - {name: 'shadow_color_glow', kind: 'color', axis: 'depth', leverage: 'md', tier: 'semantic'}, - {name: 'shadow_color_shroud', kind: 'color', axis: 'depth', leverage: 'md', tier: 'semantic'}, + { name: 'shadow_color_glow', kind: 'color', axis: 'depth', leverage: 'md', tier: 'semantic' }, + { name: 'shadow_color_shroud', kind: 'color', axis: 'depth', leverage: 'md', tier: 'semantic' }, // the dialog/fullscreen ::backdrop dim, a style.css var() fallback hook { name: 'backdrop_color', @@ -326,39 +333,39 @@ export const theme_knobs: Array<ThemeKnob> = [ axis: 'depth', leverage: 'sm', tier: 'semantic', - hook: true, + hook: true }, - {name: 'button_shadow', kind: 'shadow', axis: 'depth', leverage: 'md', tier: 'semantic'}, - {name: 'button_shadow_hover', kind: 'shadow', axis: 'depth', leverage: 'md', tier: 'semantic'}, - {name: 'button_shadow_active', kind: 'shadow', axis: 'depth', leverage: 'md', tier: 'semantic'}, + { name: 'button_shadow', kind: 'shadow', axis: 'depth', leverage: 'md', tier: 'semantic' }, + { name: 'button_shadow_hover', kind: 'shadow', axis: 'depth', leverage: 'md', tier: 'semantic' }, + { name: 'button_shadow_active', kind: 'shadow', axis: 'depth', leverage: 'md', tier: 'semantic' }, // typography { name: 'font_family_sans', kind: 'font_stack', axis: 'typography', leverage: 'md', - tier: 'semantic', + tier: 'semantic' }, { name: 'font_family_serif', kind: 'font_stack', axis: 'typography', leverage: 'md', - tier: 'semantic', + tier: 'semantic' }, { name: 'font_family_mono', kind: 'font_stack', axis: 'typography', leverage: 'md', - tier: 'semantic', + tier: 'semantic' }, { name: 'heading_font_family', kind: 'font_stack', axis: 'typography', leverage: 'md', - tier: 'semantic', + tier: 'semantic' }, { name: 'font_weight', @@ -367,7 +374,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'md', tier: 'semantic', range: [100, 900], - step: 100, + step: 100 }, { // setting this flattens the per-tier heading weight ladder deliberately @@ -378,7 +385,7 @@ export const theme_knobs: Array<ThemeKnob> = [ tier: 'semantic', range: [100, 900], step: 100, - hook: true, + hook: true }, { name: 'line_height_xs', @@ -387,7 +394,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0.8, 3], - step: 0.05, + step: 0.05 }, { name: 'line_height_sm', @@ -396,7 +403,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0.8, 3], - step: 0.05, + step: 0.05 }, { name: 'line_height_md', @@ -405,7 +412,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0.8, 3], - step: 0.05, + step: 0.05 }, { name: 'line_height_lg', @@ -414,7 +421,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0.8, 3], - step: 0.05, + step: 0.05 }, { name: 'line_height_xl', @@ -423,7 +430,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0.8, 3], - step: 0.05, + step: 0.05 }, // motion { @@ -433,7 +440,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0, 5], - step: 0.01, + step: 0.01 }, { name: 'duration_2', @@ -442,7 +449,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0, 5], - step: 0.01, + step: 0.01 }, { name: 'duration_3', @@ -451,7 +458,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0, 5], - step: 0.01, + step: 0.01 }, { name: 'duration_4', @@ -460,7 +467,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0, 5], - step: 0.01, + step: 0.01 }, { name: 'duration_5', @@ -469,7 +476,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0, 5], - step: 0.01, + step: 0.01 }, { name: 'duration_6', @@ -478,7 +485,7 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0, 5], - step: 0.01, + step: 0.01 }, { name: 'disabled_opacity', @@ -487,30 +494,30 @@ export const theme_knobs: Array<ThemeKnob> = [ leverage: 'sm', tier: 'semantic', range: [0, 100], - step: 1, + step: 1 }, // decoration - {name: 'background_image', kind: 'text', axis: 'decoration', leverage: 'md', tier: 'semantic'}, + { name: 'background_image', kind: 'text', axis: 'decoration', leverage: 'md', tier: 'semantic' } ]; /** * The theme-space axes in editor display order, with display titles. */ -export const knob_axes: Array<{axis: KnobAxis; title: string}> = [ - {axis: 'color', title: 'Color'}, - {axis: 'shape', title: 'Shape'}, - {axis: 'density', title: 'Density'}, - {axis: 'depth', title: 'Depth'}, - {axis: 'typography', title: 'Typography'}, - {axis: 'motion', title: 'Motion'}, - {axis: 'decoration', title: 'Decoration'}, +export const knob_axes: Array<{ axis: KnobAxis; title: string }> = [ + { axis: 'color', title: 'Color' }, + { axis: 'shape', title: 'Shape' }, + { axis: 'density', title: 'Density' }, + { axis: 'depth', title: 'Depth' }, + { axis: 'typography', title: 'Typography' }, + { axis: 'motion', title: 'Motion' }, + { axis: 'decoration', title: 'Decoration' } ]; /** * The catalog indexed by variable name. */ export const theme_knob_by_name: Map<string, ThemeKnob> = new Map( - theme_knobs.map((k) => [k.name, k]), + theme_knobs.map((k) => [k.name, k]) ); /** @@ -519,5 +526,5 @@ export const theme_knob_by_name: Map<string, ThemeKnob> = new Map( * variable names. */ export const theme_knob_hook_names: Set<string> = new Set( - theme_knobs.filter((k) => k.hook).map((k) => k.name), + theme_knobs.filter((k) => k.hook).map((k) => k.name) ); diff --git a/src/lib/modifiers.ts b/src/lib/modifiers.ts index 8aa358914..6e4fb1ca5 100644 --- a/src/lib/modifiers.ts +++ b/src/lib/modifiers.ts @@ -38,97 +38,97 @@ export interface ModifierDefinition { */ export const MODIFIERS: Array<ModifierDefinition> = [ // Media modifiers - viewport breakpoints (mobile-first) - {name: 'sm', type: 'media', css: '@media (width >= 40rem)', order: 1}, - {name: 'md', type: 'media', css: '@media (width >= 48rem)', order: 2}, - {name: 'lg', type: 'media', css: '@media (width >= 64rem)', order: 3}, - {name: 'xl', type: 'media', css: '@media (width >= 80rem)', order: 4}, - {name: '2xl', type: 'media', css: '@media (width >= 96rem)', order: 5}, + { name: 'sm', type: 'media', css: '@media (width >= 40rem)', order: 1 }, + { name: 'md', type: 'media', css: '@media (width >= 48rem)', order: 2 }, + { name: 'lg', type: 'media', css: '@media (width >= 64rem)', order: 3 }, + { name: 'xl', type: 'media', css: '@media (width >= 80rem)', order: 4 }, + { name: '2xl', type: 'media', css: '@media (width >= 96rem)', order: 5 }, // Max-width variants (for targeting below a breakpoint) - {name: 'max-sm', type: 'media', css: '@media (width < 40rem)', order: 11}, - {name: 'max-md', type: 'media', css: '@media (width < 48rem)', order: 12}, - {name: 'max-lg', type: 'media', css: '@media (width < 64rem)', order: 13}, - {name: 'max-xl', type: 'media', css: '@media (width < 80rem)', order: 14}, - {name: 'max-2xl', type: 'media', css: '@media (width < 96rem)', order: 15}, + { name: 'max-sm', type: 'media', css: '@media (width < 40rem)', order: 11 }, + { name: 'max-md', type: 'media', css: '@media (width < 48rem)', order: 12 }, + { name: 'max-lg', type: 'media', css: '@media (width < 64rem)', order: 13 }, + { name: 'max-xl', type: 'media', css: '@media (width < 80rem)', order: 14 }, + { name: 'max-2xl', type: 'media', css: '@media (width < 96rem)', order: 15 }, // Media modifiers - feature queries - {name: 'print', type: 'media', css: '@media print'}, - {name: 'motion-safe', type: 'media', css: '@media (prefers-reduced-motion: no-preference)'}, - {name: 'motion-reduce', type: 'media', css: '@media (prefers-reduced-motion: reduce)'}, - {name: 'contrast-more', type: 'media', css: '@media (prefers-contrast: more)'}, - {name: 'contrast-less', type: 'media', css: '@media (prefers-contrast: less)'}, - {name: 'portrait', type: 'media', css: '@media (orientation: portrait)'}, - {name: 'landscape', type: 'media', css: '@media (orientation: landscape)'}, - {name: 'forced-colors', type: 'media', css: '@media (forced-colors: active)'}, + { name: 'print', type: 'media', css: '@media print' }, + { name: 'motion-safe', type: 'media', css: '@media (prefers-reduced-motion: no-preference)' }, + { name: 'motion-reduce', type: 'media', css: '@media (prefers-reduced-motion: reduce)' }, + { name: 'contrast-more', type: 'media', css: '@media (prefers-contrast: more)' }, + { name: 'contrast-less', type: 'media', css: '@media (prefers-contrast: less)' }, + { name: 'portrait', type: 'media', css: '@media (orientation: portrait)' }, + { name: 'landscape', type: 'media', css: '@media (orientation: landscape)' }, + { name: 'forced-colors', type: 'media', css: '@media (forced-colors: active)' }, // Ancestor modifiers - color scheme - {name: 'dark', type: 'ancestor', css: ':root.dark'}, - {name: 'light', type: 'ancestor', css: ':root.light'}, + { name: 'dark', type: 'ancestor', css: ':root.dark' }, + { name: 'light', type: 'ancestor', css: ':root.light' }, // State modifiers - interaction (ordered for proper cascade: LVFHA) - {name: 'any-link', type: 'state', css: ':any-link'}, - {name: 'link', type: 'state', css: ':link'}, - {name: 'visited', type: 'state', css: ':visited', order: 1}, - {name: 'focus-within', type: 'state', css: ':focus-within', order: 2}, - {name: 'focus', type: 'state', css: ':focus', order: 3}, - {name: 'focus-visible', type: 'state', css: ':focus-visible', order: 4}, - {name: 'hover', type: 'state', css: ':hover', order: 5}, - {name: 'active', type: 'state', css: ':active', order: 6}, - {name: 'target', type: 'state', css: ':target', order: 7}, + { name: 'any-link', type: 'state', css: ':any-link' }, + { name: 'link', type: 'state', css: ':link' }, + { name: 'visited', type: 'state', css: ':visited', order: 1 }, + { name: 'focus-within', type: 'state', css: ':focus-within', order: 2 }, + { name: 'focus', type: 'state', css: ':focus', order: 3 }, + { name: 'focus-visible', type: 'state', css: ':focus-visible', order: 4 }, + { name: 'hover', type: 'state', css: ':hover', order: 5 }, + { name: 'active', type: 'state', css: ':active', order: 6 }, + { name: 'target', type: 'state', css: ':target', order: 7 }, // State modifiers - form states - {name: 'autofill', type: 'state', css: ':autofill'}, - {name: 'blank', type: 'state', css: ':blank'}, - {name: 'disabled', type: 'state', css: ':disabled'}, - {name: 'enabled', type: 'state', css: ':enabled'}, - {name: 'checked', type: 'state', css: ':checked'}, - {name: 'indeterminate', type: 'state', css: ':indeterminate'}, - {name: 'default', type: 'state', css: ':default'}, - {name: 'required', type: 'state', css: ':required'}, - {name: 'optional', type: 'state', css: ':optional'}, - {name: 'valid', type: 'state', css: ':valid'}, - {name: 'invalid', type: 'state', css: ':invalid'}, - {name: 'user-valid', type: 'state', css: ':user-valid'}, - {name: 'user-invalid', type: 'state', css: ':user-invalid'}, - {name: 'in-range', type: 'state', css: ':in-range'}, - {name: 'out-of-range', type: 'state', css: ':out-of-range'}, - {name: 'placeholder-shown', type: 'state', css: ':placeholder-shown'}, - {name: 'read-only', type: 'state', css: ':read-only'}, - {name: 'read-write', type: 'state', css: ':read-write'}, + { name: 'autofill', type: 'state', css: ':autofill' }, + { name: 'blank', type: 'state', css: ':blank' }, + { name: 'disabled', type: 'state', css: ':disabled' }, + { name: 'enabled', type: 'state', css: ':enabled' }, + { name: 'checked', type: 'state', css: ':checked' }, + { name: 'indeterminate', type: 'state', css: ':indeterminate' }, + { name: 'default', type: 'state', css: ':default' }, + { name: 'required', type: 'state', css: ':required' }, + { name: 'optional', type: 'state', css: ':optional' }, + { name: 'valid', type: 'state', css: ':valid' }, + { name: 'invalid', type: 'state', css: ':invalid' }, + { name: 'user-valid', type: 'state', css: ':user-valid' }, + { name: 'user-invalid', type: 'state', css: ':user-invalid' }, + { name: 'in-range', type: 'state', css: ':in-range' }, + { name: 'out-of-range', type: 'state', css: ':out-of-range' }, + { name: 'placeholder-shown', type: 'state', css: ':placeholder-shown' }, + { name: 'read-only', type: 'state', css: ':read-only' }, + { name: 'read-write', type: 'state', css: ':read-write' }, // State modifiers - structural - {name: 'first', type: 'state', css: ':first-child'}, - {name: 'last', type: 'state', css: ':last-child'}, - {name: 'only', type: 'state', css: ':only-child'}, - {name: 'first-of-type', type: 'state', css: ':first-of-type'}, - {name: 'last-of-type', type: 'state', css: ':last-of-type'}, - {name: 'only-of-type', type: 'state', css: ':only-of-type'}, - {name: 'odd', type: 'state', css: ':nth-child(odd)'}, - {name: 'even', type: 'state', css: ':nth-child(even)'}, - {name: 'empty', type: 'state', css: ':empty'}, + { name: 'first', type: 'state', css: ':first-child' }, + { name: 'last', type: 'state', css: ':last-child' }, + { name: 'only', type: 'state', css: ':only-child' }, + { name: 'first-of-type', type: 'state', css: ':first-of-type' }, + { name: 'last-of-type', type: 'state', css: ':last-of-type' }, + { name: 'only-of-type', type: 'state', css: ':only-of-type' }, + { name: 'odd', type: 'state', css: ':nth-child(odd)' }, + { name: 'even', type: 'state', css: ':nth-child(even)' }, + { name: 'empty', type: 'state', css: ':empty' }, // Note: nth-child(N), nth-last-child(N), nth-of-type(N), nth-last-of-type(N) are handled dynamically // State modifiers - UI states - {name: 'fullscreen', type: 'state', css: ':fullscreen'}, - {name: 'modal', type: 'state', css: ':modal'}, - {name: 'open', type: 'state', css: ':open'}, - {name: 'popover-open', type: 'state', css: ':popover-open'}, + { name: 'fullscreen', type: 'state', css: ':fullscreen' }, + { name: 'modal', type: 'state', css: ':modal' }, + { name: 'open', type: 'state', css: ':open' }, + { name: 'popover-open', type: 'state', css: ':popover-open' }, // State modifiers - media states - {name: 'paused', type: 'state', css: ':paused'}, - {name: 'playing', type: 'state', css: ':playing'}, + { name: 'paused', type: 'state', css: ':paused' }, + { name: 'playing', type: 'state', css: ':playing' }, // Pseudo-element modifiers - {name: 'before', type: 'pseudo-element', css: '::before'}, - {name: 'after', type: 'pseudo-element', css: '::after'}, - {name: 'cue', type: 'pseudo-element', css: '::cue'}, - {name: 'first-letter', type: 'pseudo-element', css: '::first-letter'}, - {name: 'first-line', type: 'pseudo-element', css: '::first-line'}, - {name: 'placeholder', type: 'pseudo-element', css: '::placeholder'}, - {name: 'selection', type: 'pseudo-element', css: '::selection'}, - {name: 'marker', type: 'pseudo-element', css: '::marker'}, - {name: 'file', type: 'pseudo-element', css: '::file-selector-button'}, - {name: 'backdrop', type: 'pseudo-element', css: '::backdrop'}, + { name: 'before', type: 'pseudo-element', css: '::before' }, + { name: 'after', type: 'pseudo-element', css: '::after' }, + { name: 'cue', type: 'pseudo-element', css: '::cue' }, + { name: 'first-letter', type: 'pseudo-element', css: '::first-letter' }, + { name: 'first-line', type: 'pseudo-element', css: '::first-line' }, + { name: 'placeholder', type: 'pseudo-element', css: '::placeholder' }, + { name: 'selection', type: 'pseudo-element', css: '::selection' }, + { name: 'marker', type: 'pseudo-element', css: '::marker' }, + { name: 'file', type: 'pseudo-element', css: '::file-selector-button' }, + { name: 'backdrop', type: 'pseudo-element', css: '::backdrop' } ]; // Generated lookup maps for efficient access @@ -269,14 +269,14 @@ export const parse_arbitrary_breakpoint = (segment: string): string | null => { * @returns object with name (including parameter) and CSS, or null if not parameterized */ export const parse_parameterized_state = ( - segment: string, -): {name: string; css: string; type: 'state'} | null => { + segment: string +): { name: string; css: string; type: 'state' } | null => { const nth_child_match = NTH_CHILD_PATTERN.exec(segment); if (nth_child_match) { return { name: segment, css: `:nth-child(${nth_child_match[1]})`, - type: 'state', + type: 'state' }; } @@ -285,7 +285,7 @@ export const parse_parameterized_state = ( return { name: segment, css: `:nth-last-child(${nth_last_child_match[1]})`, - type: 'state', + type: 'state' }; } @@ -294,7 +294,7 @@ export const parse_parameterized_state = ( return { name: segment, css: `:nth-of-type(${nth_of_type_match[1]})`, - type: 'state', + type: 'state' }; } @@ -303,7 +303,7 @@ export const parse_parameterized_state = ( return { name: segment, css: `:nth-last-of-type(${nth_last_of_type_match[1]})`, - type: 'state', + type: 'state' }; } @@ -317,8 +317,8 @@ export const parse_parameterized_state = ( * @returns the modifier definition or null if not a known modifier */ export const get_modifier = ( - segment: string, -): (ModifierDefinition & {is_arbitrary?: boolean}) | null => { + segment: string +): (ModifierDefinition & { is_arbitrary?: boolean }) | null => { // Check static modifiers first const media = MEDIA_MODIFIERS.get(segment); if (media) return media; @@ -339,7 +339,7 @@ export const get_modifier = ( name: segment, type: 'media', css: arbitrary_css, - is_arbitrary: true, + is_arbitrary: true }; } @@ -348,7 +348,7 @@ export const get_modifier = ( if (parameterized) { return { ...parameterized, - is_arbitrary: true, + is_arbitrary: true }; } diff --git a/src/lib/oklch.ts b/src/lib/oklch.ts index 19eae2800..d26a435ad 100644 --- a/src/lib/oklch.ts +++ b/src/lib/oklch.ts @@ -60,7 +60,7 @@ export const srgb_to_oklab = ([r, g, b]: RgbUnit): Oklab => { return [ 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_, 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_, - 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_, + 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_ ]; }; @@ -100,7 +100,7 @@ export const oklab_to_oklch = ([lightness, a, b]: Oklab): Oklch => { export const oklch_to_oklab = ([lightness, chroma, hue]: Oklch): Oklab => [ lightness, chroma * Math.cos(hue * DEG_TO_RAD), - chroma * Math.sin(hue * DEG_TO_RAD), + chroma * Math.sin(hue * DEG_TO_RAD) ]; /** diff --git a/src/lib/ramps.ts b/src/lib/ramps.ts index bb746fceb..cf3627f87 100644 --- a/src/lib/ramps.ts +++ b/src/lib/ramps.ts @@ -26,9 +26,9 @@ import { numeric_scale_variants, type PaletteVariant, type ColorSchemeVariant, - type NumericScaleVariant, + type NumericScaleVariant } from './variable_data.ts'; -import {oklch_max_srgb_chroma, type Oklch} from './oklch.ts'; +import { oklch_max_srgb_chroma, type Oklch } from './oklch.ts'; /** The 13 intensity stops in ramp order (00 → 100). */ export const RAMP_STOPS: ReadonlyArray<NumericScaleVariant> = numeric_scale_variants; @@ -79,25 +79,25 @@ export const PALETTE_HUES: Record<PaletteVariant, number> = { g: 358, // pink h: 41, // orange i: 204, // cyan - j: 163, // teal + j: 163 // teal }; /** Fitted lightness knobs for the palette (`palette_X_NN`) ramps. */ export const PALETTE_LIGHTNESS_KNOBS: Record<ColorSchemeVariant, LightnessRampKnobs> = { - light: {lightness_00: 0.985, lightness_100: 0.116, curve: 1.15}, - dark: {lightness_00: 0.147, lightness_100: 0.971, curve: 0.76}, + light: { lightness_00: 0.985, lightness_100: 0.116, curve: 1.15 }, + dark: { lightness_00: 0.147, lightness_100: 0.971, curve: 0.76 } }; /** Fitted lightness knobs for the shade (surface) ramps. */ export const SHADE_LIGHTNESS_KNOBS: Record<ColorSchemeVariant, LightnessRampKnobs> = { - light: {lightness_00: 0.97, lightness_100: 0.171, curve: 1.09}, - dark: {lightness_00: 0.171, lightness_100: 0.97, curve: 0.92}, + light: { lightness_00: 0.97, lightness_100: 0.171, curve: 1.09 }, + dark: { lightness_00: 0.171, lightness_100: 0.97, curve: 0.92 } }; /** Fitted lightness knobs for the text ramps. */ export const TEXT_LIGHTNESS_KNOBS: Record<ColorSchemeVariant, LightnessRampKnobs> = { - light: {lightness_00: 0.97, lightness_100: 0.146, curve: 1.19}, - dark: {lightness_00: 0.146, lightness_100: 0.97, curve: 0.87}, + light: { lightness_00: 0.97, lightness_100: 0.146, curve: 1.19 }, + dark: { lightness_00: 0.146, lightness_100: 0.97, curve: 0.87 } }; /** @@ -114,8 +114,8 @@ export const TEXT_LIGHTNESS_KNOBS: Record<ColorSchemeVariant, LightnessRampKnobs * knob). */ export const PALETTE_CHROMA_KNOBS: Record<ColorSchemeVariant, ChromaRampKnobs> = { - light: {chroma_min: 0.0132, chroma_max: 0.106, curve: 1.3}, - dark: {chroma_min: 0.0214, chroma_max: 0.1088, curve: 1.27}, + light: { chroma_min: 0.0132, chroma_max: 0.106, curve: 1.3 }, + dark: { chroma_min: 0.0214, chroma_max: 0.1088, curve: 1.27 } }; /** @@ -133,7 +133,7 @@ export const NEUTRAL_HUE = PALETTE_HUES.f; */ export const NEUTRAL_CHROMA: Record<ColorSchemeVariant, number> = { light: 0.024, - dark: 0.025, + dark: 0.025 }; /** @@ -160,7 +160,7 @@ export const PALETTE_CHROMA_CAPS: Record< '80': 0.0533, '90': 0.0366, '95': 0.0282, - '100': 0.0197, + '100': 0.0197 }, dark: { '00': 0.025, @@ -175,8 +175,8 @@ export const PALETTE_CHROMA_CAPS: Record< '80': 0.0812, '90': 0.0465, '95': 0.03, - '100': 0.0141, - }, + '100': 0.0141 + } }; /** @@ -208,7 +208,7 @@ export const ramp_chroma = ( stop: NumericScaleVariant, chroma_scale = 1, knobs: ChromaRampKnobs = PALETTE_CHROMA_KNOBS[scheme], - cap: number = PALETTE_CHROMA_CAPS[scheme][stop], + cap: number = PALETTE_CHROMA_CAPS[scheme][stop] ): number => { const requested = knobs.chroma_min + (knobs.chroma_max - knobs.chroma_min) * ramp_chroma_shape(stop, knobs.curve); @@ -224,7 +224,7 @@ export const ramp_chroma = ( export const ramp_hue_shift_offset = ( stop: NumericScaleVariant, scheme: ColorSchemeVariant, - hue_shift: number, + hue_shift: number ): number => { const centered = ramp_stop_t(stop) - 0.5; return (scheme === 'light' ? centered : -centered) * hue_shift; @@ -236,11 +236,11 @@ export const ramp_hue_shift_offset = ( export const palette_stop_oklch = ( letter: PaletteVariant, stop: NumericScaleVariant, - scheme: ColorSchemeVariant, + scheme: ColorSchemeVariant ): Oklch => [ ramp_lightness(PALETTE_LIGHTNESS_KNOBS[scheme], stop), ramp_chroma(scheme, stop), - PALETTE_HUES[letter], + PALETTE_HUES[letter] ]; /** @@ -249,7 +249,7 @@ export const palette_stop_oklch = ( export const shade_stop_oklch = (stop: NumericScaleVariant, scheme: ColorSchemeVariant): Oklch => [ ramp_lightness(SHADE_LIGHTNESS_KNOBS[scheme], stop), NEUTRAL_CHROMA[scheme] * ramp_chroma_shape(stop, PALETTE_CHROMA_KNOBS[scheme].curve), - NEUTRAL_HUE, + NEUTRAL_HUE ]; /** @@ -258,7 +258,7 @@ export const shade_stop_oklch = (stop: NumericScaleVariant, scheme: ColorSchemeV export const text_stop_oklch = (stop: NumericScaleVariant, scheme: ColorSchemeVariant): Oklch => [ ramp_lightness(TEXT_LIGHTNESS_KNOBS[scheme], stop), NEUTRAL_CHROMA[scheme] * ramp_chroma_shape(stop, PALETTE_CHROMA_KNOBS[scheme].curve), - NEUTRAL_HUE, + NEUTRAL_HUE ]; /** @@ -279,7 +279,7 @@ export const compute_palette_chroma_caps = ( hues: ReadonlyArray<number>, lightness_knobs: LightnessRampKnobs, scheme: ColorSchemeVariant, - hue_shift = 0, + hue_shift = 0 ): Record<NumericScaleVariant, number> => { const caps = {} as Record<NumericScaleVariant, number>; for (const stop of numeric_scale_variants) { @@ -315,7 +315,7 @@ const format_ramp_number = (n: number): string => String(Math.round(n * 1e6) / 1 */ export const render_lightness_stop_css = ( family: RampFamily, - stop: NumericScaleVariant, + stop: NumericScaleVariant ): string => { const p = `--${family}_lightness`; const t = format_ramp_number(ramp_stop_t(stop)); @@ -346,7 +346,7 @@ export const render_chroma_shape_css = (stop: NumericScaleVariant): string => { export const render_chroma_stop_css = ( stop: NumericScaleVariant, scheme: ColorSchemeVariant, - cap: number = PALETTE_CHROMA_CAPS[scheme][stop], + cap: number = PALETTE_CHROMA_CAPS[scheme][stop] ): string => { const cap_str = format_ramp_number(cap); return `min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_${ @@ -362,7 +362,7 @@ export const render_chroma_stop_css = ( */ export const render_hue_shift_offset_css = ( stop: NumericScaleVariant, - scheme: ColorSchemeVariant, + scheme: ColorSchemeVariant ): string => { const centered = ramp_stop_t(stop) - 0.5; const factor = Math.round((scheme === 'light' ? centered : -centered) * 1e6) / 1e6; @@ -376,7 +376,7 @@ export const render_hue_shift_offset_css = ( */ export const render_palette_stop_css = ( letter: PaletteVariant, - stop: NumericScaleVariant, + stop: NumericScaleVariant ): string => render_ramp_color_css(`var(--hue_${letter})`, stop); /** @@ -390,7 +390,7 @@ export const render_palette_stop_css = ( export const render_ramp_color_css = ( hue_reference: string, stop: NumericScaleVariant, - alpha?: string, + alpha?: string ): string => `oklch(var(--palette_lightness_${stop}) calc(var(--palette_chroma_${ stop @@ -404,7 +404,7 @@ export const render_ramp_color_css = ( */ export const render_neutral_stop_css = ( family: Exclude<RampFamily, 'palette'>, - stop: NumericScaleVariant, + stop: NumericScaleVariant ): string => `oklch(var(--${family}_lightness_${stop}) calc(var(--neutral_chroma) * var(--chroma_shape_${ stop diff --git a/src/lib/style_rule_parser.ts b/src/lib/style_rule_parser.ts index 9d7bc2915..5eab7a754 100644 --- a/src/lib/style_rule_parser.ts +++ b/src/lib/style_rule_parser.ts @@ -9,12 +9,12 @@ * @module */ -import {parseCss, type AST} from 'svelte/compiler'; -import {hash_blake3} from '@fuzdev/fuz_util/hash_blake3.ts'; +import { parseCss, type AST } from 'svelte/compiler'; +import { hash_blake3 } from '@fuzdev/fuz_util/hash_blake3.ts'; -import {extract_css_variables} from './css_variable_utils.ts'; -import type {CacheDeps} from './deps.ts'; -import type {BaseCssOption} from './css_plugin_options.ts'; +import { extract_css_variables } from './css_variable_utils.ts'; +import type { CacheDeps } from './deps.ts'; +import type { BaseCssOption } from './css_plugin_options.ts'; /** * Base fields shared by all style rules. @@ -36,13 +36,7 @@ export interface StyleRuleBase { * Reasons a rule is considered "core" and always included. */ export type CoreReason = - | 'universal' - | 'root' - | 'body' - | 'media_query' - | 'html' - | 'host' - | 'font_face'; + 'universal' | 'root' | 'body' | 'media_query' | 'html' | 'host' | 'font_face'; /** * A core style rule that is always included in output. @@ -122,7 +116,7 @@ export const parse_style_css = (css: string, content_hash: string): StyleRuleInd }; const walk_children = ( - children: Iterable<AST.CSS.Rule | AST.CSS.Atrule | AST.CSS.Node>, + children: Iterable<AST.CSS.Rule | AST.CSS.Atrule | AST.CSS.Node> ): void => { for (const child of children) { if (child.type === 'Rule') { @@ -154,7 +148,7 @@ export const parse_style_css = (css: string, content_hash: string): StyleRuleInd rules, by_element, by_class, - content_hash, + content_hash }; }; @@ -175,7 +169,7 @@ const extract_style_rule = (rule: AST.CSS.Rule, css: string, order: number): Sty const variables_used = extract_css_variables(block_css); // Determine if core rule - const {is_core, core_reason} = check_core_rule(selector_css, elements); + const { is_core, core_reason } = check_core_rule(selector_css, elements); // Type assertion needed because destructuring widens is_core to boolean return { @@ -185,7 +179,7 @@ const extract_style_rule = (rule: AST.CSS.Rule, css: string, order: number): Sty variables_used, order, is_core, - core_reason, + core_reason } as StyleRule; }; @@ -197,7 +191,7 @@ const extract_nested_rules = ( css: string, elements: Set<string>, classes: Set<string>, - variables_used: Set<string>, + variables_used: Set<string> ): void => { for (const child of block.children) { if (child.type === 'Rule') { @@ -240,7 +234,7 @@ const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): Sty variables_used, order, is_core: true, - core_reason: 'media_query', + core_reason: 'media_query' } as const; } @@ -251,7 +245,7 @@ const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): Sty variables_used, order, is_core: false, - core_reason: null, + core_reason: null } as const; } @@ -271,7 +265,7 @@ const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): Sty variables_used, order, is_core: false, - core_reason: null, + core_reason: null } as const; } @@ -290,7 +284,7 @@ const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): Sty variables_used, order, is_core: true, - core_reason: 'font_face', + core_reason: 'font_face' } as const; } @@ -310,7 +304,7 @@ const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): Sty const parse_selector_list = ( selector_css: string, elements: Set<string>, - classes: Set<string>, + classes: Set<string> ): void => { // Split on commas, respecting parentheses const selectors = split_selector_list(selector_css); @@ -369,8 +363,8 @@ const split_selector_list = (selector_group: string): Array<string> => { */ const extract_functional_content = ( selector: string, - start: number, -): {content: string; end: number} | null => { + start: number +): { content: string; end: number } | null => { let depth = 1; let i = start; @@ -385,7 +379,7 @@ const extract_functional_content = ( return { content: selector.slice(start, i - 1), - end: i, + end: i }; }; @@ -396,12 +390,12 @@ const extract_functional_content = ( const parse_single_selector = ( selector: string, elements: Set<string>, - classes: Set<string>, + classes: Set<string> ): void => { // Find all functional pseudo-classes and extract their content iteratively const functional_start_pattern = /:(?:where|is|not|has)\(/g; let match; - const ranges_to_remove: Array<{start: number; end: number}> = []; + const ranges_to_remove: Array<{ start: number; end: number }> = []; while ((match = functional_start_pattern.exec(selector)) !== null) { const content_start = match.index + match[0].length; @@ -409,7 +403,7 @@ const parse_single_selector = ( if (result) { // Recursively parse the inner content parse_selector_list(result.content, elements, classes); - ranges_to_remove.push({start: match.index, end: result.end}); + ranges_to_remove.push({ start: match.index, end: result.end }); // Update the regex lastIndex to continue after this match functional_start_pattern.lastIndex = result.end; } @@ -445,7 +439,8 @@ const parse_single_selector = ( * Result from core rule check - discriminated union for type safety. * Both variants include `core_reason` for consistent object shape. */ -type CoreRuleCheck = {is_core: true; core_reason: CoreReason} | {is_core: false; core_reason: null}; +type CoreRuleCheck = + { is_core: true; core_reason: CoreReason } | { is_core: false; core_reason: null }; /** * Checks if a rule is a "core" rule that should always be included. @@ -458,30 +453,30 @@ type CoreRuleCheck = {is_core: true; core_reason: CoreReason} | {is_core: false; const check_core_rule = (selector_css: string, elements: Set<string>): CoreRuleCheck => { // Universal selector if (selector_css.includes('*')) { - return {is_core: true, core_reason: 'universal'}; + return { is_core: true, core_reason: 'universal' }; } // :root pseudo-class if (selector_css.includes(':root')) { - return {is_core: true, core_reason: 'root'}; + return { is_core: true, core_reason: 'root' }; } // :host pseudo-class (for web components) if (selector_css.includes(':host')) { - return {is_core: true, core_reason: 'host'}; + return { is_core: true, core_reason: 'host' }; } // body element if (elements.has('body')) { - return {is_core: true, core_reason: 'body'}; + return { is_core: true, core_reason: 'body' }; } // html element if (elements.has('html')) { - return {is_core: true, core_reason: 'html'}; + return { is_core: true, core_reason: 'html' }; } - return {is_core: false, core_reason: null}; + return { is_core: false, core_reason: null }; }; /** @@ -493,10 +488,10 @@ const check_core_rule = (selector_css: string, elements: Set<string>): CoreRuleC */ export const load_style_rule_index = async ( deps: CacheDeps, - style_css_path?: string, + style_css_path?: string ): Promise<StyleRuleIndex> => { const path = style_css_path ?? new URL('./style.css', import.meta.url).pathname; - const r = await deps.read_text({path}); + const r = await deps.read_text({ path }); if (!r.ok) { throw new Error(`Failed to read style.css from ${path}: ${r.message}`); } @@ -525,10 +520,10 @@ export const create_style_rule_index = (css: string): StyleRuleIndex => { */ export const load_default_style_css = async ( deps: CacheDeps, - style_css_path?: string, + style_css_path?: string ): Promise<string> => { const path = style_css_path ?? new URL('./style.css', import.meta.url).pathname; - const r = await deps.read_text({path}); + const r = await deps.read_text({ path }); if (!r.ok) { throw new Error(`Failed to read style.css from ${path}: ${r.message}`); } @@ -545,7 +540,7 @@ export const load_default_style_css = async ( */ export const resolve_base_css_option = async ( base_css: BaseCssOption, - deps: CacheDeps, + deps: CacheDeps ): Promise<StyleRuleIndex | null> => { // null = disabled if (base_css === null) { @@ -579,7 +574,7 @@ export const resolve_base_css_option = async ( export const get_matching_rules = ( index: StyleRuleIndex, detected_elements: Set<string>, - detected_classes: Set<string>, + detected_classes: Set<string> ): Set<number> => { const included: Set<number> = new Set(); @@ -641,7 +636,7 @@ export const generate_base_css = (index: StyleRuleIndex, included_indices: Set<n */ export const collect_rule_variables = ( index: StyleRuleIndex, - included_indices: Set<number>, + included_indices: Set<number> ): Set<string> => { const variables: Set<string> = new Set(); diff --git a/src/lib/theme.gen.css.ts b/src/lib/theme.gen.css.ts index ec48a1214..a8dcd1cd1 100644 --- a/src/lib/theme.gen.css.ts +++ b/src/lib/theme.gen.css.ts @@ -1,7 +1,7 @@ -import type {Gen} from '@fuzdev/gro'; +import type { Gen } from '@fuzdev/gro'; -import {default_themes} from './themes.ts'; -import {render_theme_style} from './theme.ts'; +import { default_themes } from './themes.ts'; +import { render_theme_style } from './theme.ts'; // TODO maybe this should be `base_theme.css` or `base.css` or something, // and we could also generate and publish the other themes -- @@ -9,7 +9,7 @@ import {render_theme_style} from './theme.ts'; // currently you'd have to pass a dynamic `theme` to `ThemeRoot` /** @nodocs */ -export const gen: Gen = ({origin_path}) => { +export const gen: Gen = ({ origin_path }) => { const banner = `/** * generated by ${origin_path} -- DO NOT EDIT DIRECTLY! * @@ -29,7 +29,7 @@ export const gen: Gen = ({origin_path}) => { const theme_style = render_theme_style(theme, { comments: true, empty_default_theme: false, - layer: 'fuz.base', + layer: 'fuz.base' }); return `${banner} diff --git a/src/lib/theme.ts b/src/lib/theme.ts index 5b6d2f023..8540f1c05 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -1,5 +1,5 @@ -import {default_variables} from './variables.ts'; -import type {StyleVariable} from './variable.ts'; +import { default_variables } from './variables.ts'; +import type { StyleVariable } from './variable.ts'; /** * @see https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme @@ -66,23 +66,23 @@ export interface RenderThemeStyleOptions { */ export const scheme_stance_variables = ( scheme: 'light' | 'dark', - variables: Array<StyleVariable>, + variables: Array<StyleVariable> ): Array<StyleVariable> => { const overridden = new Set(variables.map((v) => v.name)); const mirrored: Array<StyleVariable> = []; for (const v of default_variables) { if (v.dark === undefined || overridden.has(v.name)) continue; if (scheme === 'dark') { - mirrored.push({name: v.name, light: v.dark}); + mirrored.push({ name: v.name, light: v.dark }); } else if (v.light !== undefined) { - mirrored.push({name: v.name, dark: v.light}); + mirrored.push({ name: v.name, dark: v.light }); } } return mirrored; }; export const render_theme_style = (theme: Theme, options: RenderThemeStyleOptions = {}): string => { - const {comments = false, id = null, empty_default_theme = true, layer = 'fuz.theme'} = options; + const { comments = false, id = null, empty_default_theme = true, layer = 'fuz.theme' } = options; const stance = theme.scheme === 'light' || theme.scheme === 'dark' ? theme.scheme : null; // key the default-theme special case on emptiness, not the name, so any theme // carrying variables renders them (a theme merely named 'base' still renders); @@ -126,7 +126,7 @@ ${blocks} export const render_theme_variable = ( variable: StyleVariable, dark = false, - comments = true, + comments = true ): string => { const v = dark ? variable.dark : variable.light; if (!v) return ''; diff --git a/src/lib/theme_check.ts b/src/lib/theme_check.ts index 7f8d85737..591da3652 100644 --- a/src/lib/theme_check.ts +++ b/src/lib/theme_check.ts @@ -42,10 +42,10 @@ * @module */ -import {StyleVariable} from './variable.ts'; -import {scheme_stance_variables, type Theme} from './theme.ts'; -import {default_variables} from './variables.ts'; -import {theme_knob_by_name, theme_knob_hook_names, type ThemeKnob} from './knobs.ts'; +import { StyleVariable } from './variable.ts'; +import { scheme_stance_variables, type Theme } from './theme.ts'; +import { default_variables } from './variables.ts'; +import { theme_knob_by_name, theme_knob_hook_names, type ThemeKnob } from './knobs.ts'; import { PALETTE_HUES, PALETTE_LIGHTNESS_KNOBS, @@ -60,7 +60,7 @@ import { ramp_hue_shift_offset, compute_palette_chroma_caps, render_chroma_stop_css, - type LightnessRampKnobs, + type LightnessRampKnobs } from './ramps.ts'; import { numeric_scale_variants, @@ -69,10 +69,10 @@ import { color_scheme_variants, type NumericScaleVariant, type ColorSchemeVariant, - type PaletteVariant, + type PaletteVariant } from './variable_data.ts'; -import {oklch_to_srgb, oklch_in_srgb_gamut, type Oklch, type RgbUnit} from './oklch.ts'; -import {wcag_contrast_ratio} from './wcag.ts'; +import { oklch_to_srgb, oklch_in_srgb_gamut, type Oklch, type RgbUnit } from './oklch.ts'; +import { wcag_contrast_ratio } from './wcag.ts'; // // Gate thresholds — the WCAG levels the derived palette is designed to clear. @@ -95,7 +95,7 @@ export const GATE_FILL_TEXT = 3; */ export const known_theme_variable_names: Set<string> = new Set([ ...default_variables.map((v) => v.name), - ...theme_knob_hook_names, + ...theme_knob_hook_names ]); // @@ -156,8 +156,7 @@ export interface CompiledTheme { /** A resolved numeric value, or the offending variable/value/reason on failure. */ type Resolved = - | {ok: true; value: number} - | {ok: false; variable: string; value: string; reason: string}; + { ok: true; value: number } | { ok: false; variable: string; value: string; reason: string }; /** Intent and neutral hues default to a palette-letter binding. */ const INTENT_HUE_DEFAULT_BINDING: Record<string, string> = { @@ -166,7 +165,7 @@ const INTENT_HUE_DEFAULT_BINDING: Record<string, string> = { hue_negative: 'hue_c', hue_caution: 'hue_h', hue_info: 'hue_i', - hue_neutral: 'hue_f', + hue_neutral: 'hue_f' }; const LIGHTNESS_KNOBS_BY_FAMILY: Record< @@ -175,7 +174,7 @@ const LIGHTNESS_KNOBS_BY_FAMILY: Record< > = { palette: PALETTE_LIGHTNESS_KNOBS, shade: SHADE_LIGHTNESS_KNOBS, - text: TEXT_LIGHTNESS_KNOBS, + text: TEXT_LIGHTNESS_KNOBS }; const PALETTE_LETTER_MATCHER = /^hue_([a-j])$/u; @@ -238,7 +237,12 @@ class ThemeResolver { #resolve(name: string, scheme: ColorSchemeVariant, visited: Set<string>): Resolved { if (visited.has(name)) { - return {ok: false, variable: name, value: `var(--${name})`, reason: 'cyclic var() reference'}; + return { + ok: false, + variable: name, + value: `var(--${name})`, + reason: 'cyclic var() reference' + }; } const next = new Set(visited); next.add(name); @@ -252,7 +256,7 @@ class ThemeResolver { // numeric literal if (trimmed !== '') { const n = Number(trimmed); - if (Number.isFinite(n)) return {ok: true, value: n}; + if (Number.isFinite(n)) return { ok: true, value: n }; } // exactly var(--x) — recurse through the same merge const var_match = VAR_MATCHER.exec(trimmed); @@ -260,13 +264,13 @@ class ThemeResolver { // machine-emitted compiled cap form const cap = this.#parse_compiled_cap(trimmed, scheme, visited); if (cap) return cap; - return {ok: false, variable: name, value: trimmed, reason: 'unrecognized value expression'}; + return { ok: false, variable: name, value: trimmed, reason: 'unrecognized value expression' }; } #parse_compiled_cap( value: string, scheme: ColorSchemeVariant, - visited: Set<string>, + visited: Set<string> ): Resolved | null { const m = COMPILED_CAP_MATCHER.exec(value); if (!m) return null; @@ -280,28 +284,28 @@ class ThemeResolver { if (!curve.ok) return curve; const shape = ramp_chroma_shape(stop, curve.value); const requested = chroma_min.value + (chroma_max.value - chroma_min.value) * shape; - return {ok: true, value: Math.min(requested, literal)}; + return { ok: true, value: Math.min(requested, literal) }; } #resolve_default(name: string, scheme: ColorSchemeVariant, visited: Set<string>): Resolved { // palette letters const letter_match = PALETTE_LETTER_MATCHER.exec(name); - if (letter_match) return {ok: true, value: PALETTE_HUES[letter_match[1] as PaletteVariant]}; + if (letter_match) return { ok: true, value: PALETTE_HUES[letter_match[1] as PaletteVariant] }; // intent/neutral hues default to a palette-letter binding const binding = INTENT_HUE_DEFAULT_BINDING[name]; if (binding) return this.#resolve(binding, scheme, visited); // scalar knobs - if (name === 'chroma_scale') return {ok: true, value: 1}; - if (name === 'hue_shift') return {ok: true, value: 0}; - if (name === 'neutral_chroma') return {ok: true, value: NEUTRAL_CHROMA[scheme]}; + if (name === 'chroma_scale') return { ok: true, value: 1 }; + if (name === 'hue_shift') return { ok: true, value: 0 }; + if (name === 'neutral_chroma') return { ok: true, value: NEUTRAL_CHROMA[scheme] }; if (name === 'palette_chroma_min') { - return {ok: true, value: PALETTE_CHROMA_KNOBS[scheme].chroma_min}; + return { ok: true, value: PALETTE_CHROMA_KNOBS[scheme].chroma_min }; } if (name === 'palette_chroma_max') { - return {ok: true, value: PALETTE_CHROMA_KNOBS[scheme].chroma_max}; + return { ok: true, value: PALETTE_CHROMA_KNOBS[scheme].chroma_max }; } if (name === 'palette_chroma_curve') { - return {ok: true, value: PALETTE_CHROMA_KNOBS[scheme].curve}; + return { ok: true, value: PALETTE_CHROMA_KNOBS[scheme].curve }; } // lightness endpoints and curve const knob_match = LIGHTNESS_KNOB_MATCHER.exec(name); @@ -311,7 +315,7 @@ class ThemeResolver { const field = knob_match[2]; const value = field === '00' ? knobs.lightness_00 : field === '100' ? knobs.lightness_100 : knobs.curve; - return {ok: true, value}; + return { ok: true, value }; } // derived lightness intermediates — compute from the resolved knobs const stop_match = LIGHTNESS_STOP_MATCHER.exec(name); @@ -320,7 +324,7 @@ class ThemeResolver { const stop = stop_match[2] as NumericScaleVariant; const knobs = this.#lightness_knobs(family, scheme, visited); if (!knobs.ok) return knobs.error; - return {ok: true, value: ramp_lightness(knobs.value, stop)}; + return { ok: true, value: ramp_lightness(knobs.value, stop) }; } // derived palette chroma stops — the capped knob curve const chroma_match = PALETTE_CHROMA_STOP_MATCHER.exec(name); @@ -329,48 +333,48 @@ class ThemeResolver { const knobs = this.#chroma_knobs(scheme, visited); if (!knobs.ok) return knobs.error; const value = ramp_chroma(scheme, stop, 1, knobs.value, PALETTE_CHROMA_CAPS[scheme][stop]); - return {ok: true, value}; + return { ok: true, value }; } return { ok: false, variable: name, value: '(default)', - reason: 'no numeric default for variable', + reason: 'no numeric default for variable' }; } #lightness_knobs( family: 'palette' | 'shade' | 'text', scheme: ColorSchemeVariant, - visited: Set<string>, - ): {ok: true; value: LightnessRampKnobs} | {ok: false; error: Resolved} { + visited: Set<string> + ): { ok: true; value: LightnessRampKnobs } | { ok: false; error: Resolved } { const l00 = this.#resolve(`${family}_lightness_00`, scheme, visited); - if (!l00.ok) return {ok: false, error: l00}; + if (!l00.ok) return { ok: false, error: l00 }; const l100 = this.#resolve(`${family}_lightness_100`, scheme, visited); - if (!l100.ok) return {ok: false, error: l100}; + if (!l100.ok) return { ok: false, error: l100 }; const curve = this.#resolve(`${family}_lightness_curve`, scheme, visited); - if (!curve.ok) return {ok: false, error: curve}; + if (!curve.ok) return { ok: false, error: curve }; return { ok: true, - value: {lightness_00: l00.value, lightness_100: l100.value, curve: curve.value}, + value: { lightness_00: l00.value, lightness_100: l100.value, curve: curve.value } }; } #chroma_knobs( scheme: ColorSchemeVariant, - visited: Set<string>, + visited: Set<string> ): - | {ok: true; value: {chroma_min: number; chroma_max: number; curve: number}} - | {ok: false; error: Resolved} { + | { ok: true; value: { chroma_min: number; chroma_max: number; curve: number } } + | { ok: false; error: Resolved } { const chroma_min = this.#resolve('palette_chroma_min', scheme, visited); - if (!chroma_min.ok) return {ok: false, error: chroma_min}; + if (!chroma_min.ok) return { ok: false, error: chroma_min }; const chroma_max = this.#resolve('palette_chroma_max', scheme, visited); - if (!chroma_max.ok) return {ok: false, error: chroma_max}; + if (!chroma_max.ok) return { ok: false, error: chroma_max }; const curve = this.#resolve('palette_chroma_curve', scheme, visited); - if (!curve.ok) return {ok: false, error: curve}; + if (!curve.ok) return { ok: false, error: curve }; return { ok: true, - value: {chroma_min: chroma_min.value, chroma_max: chroma_max.value, curve: curve.value}, + value: { chroma_min: chroma_min.value, chroma_max: chroma_max.value, curve: curve.value } }; } } @@ -385,7 +389,7 @@ class ThemeResolver { export const resolve_theme_knob = ( theme: Theme, name: string, - scheme: ColorSchemeVariant, + scheme: ColorSchemeVariant ): number | null => { const r = new ThemeResolver(theme).resolve(name, scheme); return r.ok ? r.value : null; @@ -399,7 +403,7 @@ const validate_knob_value = ( knob: ThemeKnob, value: string, variable: string, - slot: string, + slot: string ): Array<ThemeIssue> => { const issues: Array<ThemeIssue> = []; const trimmed = value.trim(); @@ -411,7 +415,7 @@ const validate_knob_value = ( message: `${variable} ${slot} ${trimmed} is outside the safe range [${knob.range[0]}, ${ knob.range[1] }], the design envelope (knowingly exceedable)`, - variable, + variable }); } }; @@ -422,7 +426,7 @@ const validate_knob_value = ( issues.push({ level: 'warning', message: `${variable} ${slot} "${value}" is not a numeric ${knob.kind} value`, - variable, + variable }); } else { check_range(); @@ -436,7 +440,7 @@ const validate_knob_value = ( issues.push({ level: 'warning', message: `${variable} ${slot} "${value}" is not a hue angle or var(--hue_X) binding`, - variable, + variable }); } else if (numeric) { check_range(); @@ -448,7 +452,7 @@ const validate_knob_value = ( issues.push({ level: 'warning', message: `${variable} ${slot} "${value}" is not a CSS time value like 0.2s`, - variable, + variable }); } break; @@ -458,7 +462,7 @@ const validate_knob_value = ( issues.push({ level: 'warning', message: `${variable} ${slot} "${value}" is not one of ${knob.values.join(', ')}`, - variable, + variable }); } break; @@ -479,26 +483,26 @@ const validate_knob_value = ( export const validate_theme = (theme: Theme): Array<ThemeIssue> => { const issues: Array<ThemeIssue> = []; if (!theme.name) { - issues.push({level: 'error', message: 'theme name must be non-empty'}); + issues.push({ level: 'error', message: 'theme name must be non-empty' }); } - const scheme: unknown = (theme as {scheme?: unknown}).scheme; + const scheme: unknown = (theme as { scheme?: unknown }).scheme; if (scheme !== undefined && scheme !== 'dual' && scheme !== 'light' && scheme !== 'dark') { issues.push({ level: 'error', - message: `invalid scheme ${JSON.stringify(scheme)} — expected 'dual', 'light', or 'dark'`, + message: `invalid scheme ${JSON.stringify(scheme)} — expected 'dual', 'light', or 'dark'` }); } const stance = scheme === 'light' || scheme === 'dark' ? scheme : null; for (const variable of theme.variables) { const parsed = StyleVariable.safeParse(variable); - const name: unknown = (variable as {name?: unknown}).name; + const name: unknown = (variable as { name?: unknown }).name; const name_label = typeof name === 'string' ? name : undefined; if (!parsed.success) { for (const issue of parsed.error.issues) { issues.push({ level: 'error', message: `invalid variable${name_label ? ` "${name_label}"` : ''}: ${issue.message}`, - ...(name_label ? {variable: name_label} : null), + ...(name_label ? { variable: name_label } : null) }); } continue; @@ -508,7 +512,7 @@ export const validate_theme = (theme: Theme): Array<ThemeIssue> => { issues.push({ level: 'error', message: `unknown variable "${valid.name}"`, - variable: valid.name, + variable: valid.name }); continue; } @@ -521,7 +525,7 @@ export const validate_theme = (theme: Theme): Array<ThemeIssue> => { message: `"${valid.name}" carries a dark slot under a '${ stance }' scheme stance — stanced themes render one appearance in both color schemes, so author single-slot values`, - variable: valid.name, + variable: valid.name }); } const knob = theme_knob_by_name.get(valid.name); @@ -561,11 +565,11 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { const unchecked: Array<ThemeUncheckedEntry> = []; const seen_unchecked: Set<string> = new Set(); - const record = (r: Extract<Resolved, {ok: false}>): null => { + const record = (r: Extract<Resolved, { ok: false }>): null => { const key = `${r.variable}|${r.value}|${r.reason}`; if (!seen_unchecked.has(key)) { seen_unchecked.add(key); - unchecked.push({variable: r.variable, value: r.value, reason: r.reason}); + unchecked.push({ variable: r.variable, value: r.value, reason: r.reason }); } return null; }; @@ -579,7 +583,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { const ramp_color = ( hue: number, stop: NumericScaleVariant, - scheme: ColorSchemeVariant, + scheme: ColorSchemeVariant ): Oklch | null => { const lightness = num(`palette_lightness_${stop}`, scheme); const chroma_stop = num(`palette_chroma_${stop}`, scheme); @@ -591,7 +595,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { return [ lightness, chroma_stop * chroma_scale, - hue + ramp_hue_shift_offset(stop, scheme, hue_shift), + hue + ramp_hue_shift_offset(stop, scheme, hue_shift) ]; }; @@ -599,7 +603,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { const neutral_color = ( family: 'shade' | 'text', stop: NumericScaleVariant, - scheme: ColorSchemeVariant, + scheme: ColorSchemeVariant ): Oklch | null => { const lightness = num(`${family}_lightness_${stop}`, scheme); const neutral_c = num('neutral_chroma', scheme); @@ -618,7 +622,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { return [ lightness, neutral_c * ramp_chroma_shape(stop, curve), - neutral_hue + ramp_hue_shift_offset(stop, scheme, hue_shift), + neutral_hue + ramp_hue_shift_offset(stop, scheme, hue_shift) ]; }; @@ -632,13 +636,13 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { subject, value: gamut_excess(oklch_to_srgb(color)), threshold: 1e-4, - pass: oklch_in_srgb_gamut(color, 1e-4), + pass: oklch_in_srgb_gamut(color, 1e-4) }); }; const push_monotonicity = ( family: 'palette' | 'shade' | 'text', - scheme: ColorSchemeVariant, + scheme: ColorSchemeVariant ): void => { const lightnesses: Array<number> = []; for (const stop of numeric_scale_variants) { @@ -659,7 +663,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { subject: `${family}_lightness`, value, threshold: 0, - pass: value > 0, + pass: value > 0 }); }; @@ -710,7 +714,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { subject: `text_80 on shade_${stop}`, value: ratio, threshold: GATE_BODY_TEXT, - pass: ratio >= GATE_BODY_TEXT, + pass: ratio >= GATE_BODY_TEXT }); } @@ -726,7 +730,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { subject: 'text_50 on shade_00', value: ratio, threshold: GATE_SUBTLE_TEXT, - pass: ratio >= GATE_SUBTLE_TEXT, + pass: ratio >= GATE_SUBTLE_TEXT }); } @@ -742,7 +746,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { subject: 'accent_60 on shade_00', value: ratio, threshold: GATE_LINK, - pass: ratio >= GATE_LINK, + pass: ratio >= GATE_LINK }); } } @@ -771,7 +775,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { subject: `${label}_50 vs shade_00`, value: ui, threshold: GATE_UI, - pass: ui >= GATE_UI, + pass: ui >= GATE_UI }); const on_fill = contrast(text_max, fill_rgb); entries.push({ @@ -780,14 +784,14 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { subject: `text_max on ${label}_50`, value: on_fill, threshold: GATE_FILL_TEXT, - pass: on_fill >= GATE_FILL_TEXT, + pass: on_fill >= GATE_FILL_TEXT }); } } } const ok = unchecked.length === 0 && entries.every((e) => e.pass); - return {ok, entries, unchecked}; + return { ok, entries, unchecked }; }; // @@ -818,7 +822,7 @@ const resolve_or = ( resolver: ThemeResolver, name: string, scheme: ColorSchemeVariant, - fallback: number, + fallback: number ): number => { const r = resolver.resolve(name, scheme); return r.ok ? r.value : fallback; @@ -826,7 +830,7 @@ const resolve_or = ( const resolve_lightness_knobs = ( resolver: ThemeResolver, - scheme: ColorSchemeVariant, + scheme: ColorSchemeVariant ): LightnessRampKnobs => { const l00 = resolver.resolve('palette_lightness_00', scheme); const l100 = resolver.resolve('palette_lightness_100', scheme); @@ -835,7 +839,7 @@ const resolve_lightness_knobs = ( return { lightness_00: l00.ok ? l00.value : fallback.lightness_00, lightness_100: l100.ok ? l100.value : fallback.lightness_100, - curve: curve.ok ? curve.value : fallback.curve, + curve: curve.ok ? curve.value : fallback.curve }; }; @@ -862,14 +866,14 @@ export const compile_theme = (theme: Theme): CompiledTheme => { collect_hues(resolver, 'light'), resolve_lightness_knobs(resolver, 'light'), 'light', - resolve_or(resolver, 'hue_shift', 'light', 0), + resolve_or(resolver, 'hue_shift', 'light', 0) ), dark: compute_palette_chroma_caps( collect_hues(resolver, 'dark'), resolve_lightness_knobs(resolver, 'dark'), 'dark', - resolve_or(resolver, 'hue_shift', 'dark', 0), - ), + resolve_or(resolver, 'hue_shift', 'dark', 0) + ) }; const cap_overrides: Array<StyleVariable> = []; @@ -884,12 +888,12 @@ export const compile_theme = (theme: Theme): CompiledTheme => { name: `palette_chroma_${stop}`, // always emit both slots — the dual-slot rule light: render_chroma_stop_css(stop, 'light', light_cap), - dark: render_chroma_stop_css(stop, 'dark', dark_cap), + dark: render_chroma_stop_css(stop, 'dark', dark_cap) }); } } // spread to preserve the scheme stance (and any future fields) - const compiled: Theme = {...theme, variables: [...theme.variables, ...cap_overrides]}; - return {theme: compiled, report: check_theme(compiled), issues}; + const compiled: Theme = { ...theme, variables: [...theme.variables, ...cap_overrides] }; + return { theme: compiled, report: check_theme(compiled), issues }; }; diff --git a/src/lib/themes.ts b/src/lib/themes.ts index d18c87340..a2228a8ca 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -1,7 +1,7 @@ -import type {Theme} from './theme.ts'; -import {base_theme} from './themes/base.ts'; -import {low_contrast_theme} from './themes/low_contrast.ts'; -import {high_contrast_theme} from './themes/high_contrast.ts'; +import type { Theme } from './theme.ts'; +import { base_theme } from './themes/base.ts'; +import { low_contrast_theme } from './themes/low_contrast.ts'; +import { high_contrast_theme } from './themes/high_contrast.ts'; export const DEFAULT_THEME: Theme = base_theme; @@ -23,5 +23,5 @@ export const DEFAULT_THEME: Theme = base_theme; export const default_themes: Array<Theme> = [ DEFAULT_THEME, low_contrast_theme, - high_contrast_theme, + high_contrast_theme ]; diff --git a/src/lib/themes/base.ts b/src/lib/themes/base.ts index 29504b9d2..0fa3b1d4a 100644 --- a/src/lib/themes/base.ts +++ b/src/lib/themes/base.ts @@ -1,9 +1,9 @@ -import type {Theme} from '../theme.ts'; +import type { Theme } from '../theme.ts'; /** * The default theme — inherits every base default variable. */ export const base_theme: Theme = { name: 'base', - variables: [], // inherits base + variables: [] // inherits base }; diff --git a/src/lib/themes/brutalish.ts b/src/lib/themes/brutalish.ts index 93aa6151c..c00685e9a 100644 --- a/src/lib/themes/brutalish.ts +++ b/src/lib/themes/brutalish.ts @@ -1,4 +1,4 @@ -import type {Theme} from '../theme.ts'; +import type { Theme } from '../theme.ts'; /** * An era exemplar theme riffing on brutalism — brutal-ish: raw concrete @@ -16,19 +16,19 @@ export const brutalish_theme: Theme = { variables: [ // concrete ground, saturated accents: the neutral goes raw while the // palette keeps punch - {name: 'chroma_scale', light: '0.5'}, - {name: 'neutral_chroma', light: '0.006'}, + { name: 'chroma_scale', light: '0.5' }, + { name: 'neutral_chroma', light: '0.006' }, // paper white / void black, max contrast - {name: 'shade_lightness_00', light: '1', dark: '0'}, - {name: 'text_lightness_curve', light: '0.5', dark: '0.35'}, + { name: 'shade_lightness_00', light: '1', dark: '0' }, + { name: 'text_lightness_curve', light: '0.5', dark: '0.35' }, // border-forward: heavier, opaque, high-contrast borders - {name: 'border_width', light: 'var(--border_width_2)'}, - {name: 'border_color', light: 'var(--text_60)'}, + { name: 'border_width', light: 'var(--border_width_2)' }, + { name: 'border_color', light: 'var(--text_60)' }, // sharp: one knob zeroes every radius tier - {name: 'radius_scale', light: '0'}, + { name: 'radius_scale', light: '0' }, // flat: one knob zeroes the whole alpha ramp, button shadows included - {name: 'shadow_alpha_scale', light: '0'}, + { name: 'shadow_alpha_scale', light: '0' }, // heavy display type: flatten the heading weight ladder to max - {name: 'heading_font_weight', light: '900'}, - ], + { name: 'heading_font_weight', light: '900' } + ] }; diff --git a/src/lib/themes/high_contrast.ts b/src/lib/themes/high_contrast.ts index 5bbe0461e..e4b76c436 100644 --- a/src/lib/themes/high_contrast.ts +++ b/src/lib/themes/high_contrast.ts @@ -1,4 +1,4 @@ -import type {Theme} from '../theme.ts'; +import type { Theme } from '../theme.ts'; /** * Stretched contrast: pure white/black page background and text bent toward @@ -8,8 +8,8 @@ export const high_contrast_theme: Theme = { name: 'high contrast', variables: [ // pure white/black page background; the whole shade ramp stretches to it - {name: 'shade_lightness_00', light: '1', dark: '0'}, + { name: 'shade_lightness_00', light: '1', dark: '0' }, // bend text toward the contrast end across the whole ramp - {name: 'text_lightness_curve', light: '0.45', dark: '0.31'}, - ], + { name: 'text_lightness_curve', light: '0.45', dark: '0.31' } + ] }; diff --git a/src/lib/themes/low_contrast.ts b/src/lib/themes/low_contrast.ts index 56de3fd1e..308865d7a 100644 --- a/src/lib/themes/low_contrast.ts +++ b/src/lib/themes/low_contrast.ts @@ -1,4 +1,4 @@ -import type {Theme} from '../theme.ts'; +import type { Theme } from '../theme.ts'; /** * Gentle contrast: a softer neutral tint and compressed surface range, tuned @@ -7,8 +7,8 @@ import type {Theme} from '../theme.ts'; export const low_contrast_theme: Theme = { name: 'low contrast', variables: [ - {name: 'neutral_chroma', light: '0.017', dark: '0.018'}, + { name: 'neutral_chroma', light: '0.017', dark: '0.018' }, // compress the shade ramp from the page-background end - {name: 'shade_lightness_00', light: '0.92', dark: '0.245'}, - ], + { name: 'shade_lightness_00', light: '0.92', dark: '0.245' } + ] }; diff --git a/src/lib/themes/necromancer.ts b/src/lib/themes/necromancer.ts index f2fb91eb4..faf76226e 100644 --- a/src/lib/themes/necromancer.ts +++ b/src/lib/themes/necromancer.ts @@ -1,4 +1,4 @@ -import type {Theme} from '../theme.ts'; +import type { Theme } from '../theme.ts'; /** * A fantasy exemplar theme built entirely from semantic bindings and levers — @@ -20,28 +20,28 @@ export const necromancer_theme: Theme = { scheme: 'dark', variables: [ // ectoplasm-green surfaces and text: the neutral binds to the green slot - {name: 'hue_neutral', light: 'var(--hue_b)'}, - {name: 'neutral_chroma', light: '0.04'}, + { name: 'hue_neutral', light: 'var(--hue_b)' }, + { name: 'neutral_chroma', light: '0.04' }, // violet accent: links/focus/selection glow purple over the green world - {name: 'hue_accent', light: 'var(--hue_d)'}, + { name: 'hue_accent', light: 'var(--hue_d)' }, // painterly ramps: shadows cool toward teal, highlights warm toward yellow-green - {name: 'hue_shift', light: '14'}, + { name: 'hue_shift', light: '14' }, // vivid, knowingly clipping the weak hues - {name: 'chroma_scale', light: '1.15'}, + { name: 'chroma_scale', light: '1.15' }, // modestly compact - crypt density - {name: 'scale_factor', light: '0.9'}, + { name: 'scale_factor', light: '0.9' }, // glow depth: shadows are accent-lit halos instead of neutral light - {name: 'shadow_color_umbra', light: 'oklch(0.72 0.15 var(--hue_accent))'}, - {name: 'shadow_color_glow', light: 'oklch(0.75 0.16 var(--hue_accent))'}, - {name: 'shadow_color_highlight', light: '#000'}, + { name: 'shadow_color_umbra', light: 'oklch(0.72 0.15 var(--hue_accent))' }, + { name: 'shadow_color_glow', light: 'oklch(0.75 0.16 var(--hue_accent))' }, + { name: 'shadow_color_highlight', light: '#000' }, // sharp-ish with a floor: the tier ladder compresses onto ~0.2rem, which a // uniform radius_scale can't express, so the tokens pin (sanctioned escape) - {name: 'border_radius_xs3', light: '0.2rem'}, - {name: 'border_radius_xs2', light: '0.2rem'}, - {name: 'border_radius_xs', light: '0.2rem'}, - {name: 'border_radius_sm', light: '0.3rem'}, - {name: 'border_radius_md', light: '0.4rem'}, - {name: 'border_radius_lg', light: '0.6rem'}, - {name: 'border_radius_xl', light: '0.8rem'}, - ], + { name: 'border_radius_xs3', light: '0.2rem' }, + { name: 'border_radius_xs2', light: '0.2rem' }, + { name: 'border_radius_xs', light: '0.2rem' }, + { name: 'border_radius_sm', light: '0.3rem' }, + { name: 'border_radius_md', light: '0.4rem' }, + { name: 'border_radius_lg', light: '0.6rem' }, + { name: 'border_radius_xl', light: '0.8rem' } + ] }; diff --git a/src/lib/themes/sunset_ember.ts b/src/lib/themes/sunset_ember.ts index f2ed6b2af..3e72f0f37 100644 --- a/src/lib/themes/sunset_ember.ts +++ b/src/lib/themes/sunset_ember.ts @@ -1,4 +1,4 @@ -import type {Theme} from '../theme.ts'; +import type { Theme } from '../theme.ts'; /** * A natural exemplar theme built entirely from semantic bindings and levers — @@ -18,19 +18,19 @@ export const sunset_ember_theme: Theme = { name: 'sunset ember', variables: [ // ember accent: links/focus/selection burn orange - {name: 'hue_accent', light: 'var(--hue_h)'}, + { name: 'hue_accent', light: 'var(--hue_h)' }, // warm haze over every surface (neutral already binds to warm hue_f) - {name: 'neutral_chroma', light: '0.028', dark: '0.035'}, + { name: 'neutral_chroma', light: '0.028', dark: '0.035' }, // vivid — deliberately past the caps; the browser clips, lightness holds - {name: 'chroma_scale', light: '1.3', dark: '1.2'}, + { name: 'chroma_scale', light: '1.3', dark: '1.2' }, // light ends gold, dark ends toward crimson dusk - {name: 'hue_shift', light: '16'}, + { name: 'hue_shift', light: '16' }, // gradient sky: kept near the shade_00 lightness so text contrast holds { name: 'background_image', light: 'linear-gradient(to bottom, oklch(0.96 0.05 var(--hue_e)), oklch(0.975 0.028 var(--hue_h)) 45%, oklch(0.965 0.035 var(--hue_c)))', - dark: 'linear-gradient(to bottom, oklch(0.23 0.045 var(--hue_d)), oklch(0.19 0.04 var(--hue_c)) 55%, oklch(0.16 0.045 var(--hue_c)))', - }, - ], + dark: 'linear-gradient(to bottom, oklch(0.23 0.045 var(--hue_d)), oklch(0.19 0.04 var(--hue_c)) 55%, oklch(0.16 0.045 var(--hue_c)))' + } + ] }; diff --git a/src/lib/themes/terminal.ts b/src/lib/themes/terminal.ts index ee34fef86..503e1c9e3 100644 --- a/src/lib/themes/terminal.ts +++ b/src/lib/themes/terminal.ts @@ -1,4 +1,4 @@ -import type {Theme} from '../theme.ts'; +import type { Theme } from '../theme.ts'; /** * A practical terminal theme: the dark, monospace, sharp-cornered, flat-depth @@ -14,19 +14,19 @@ export const terminal_theme: Theme = { variables: [ // green-phosphor surfaces and text — the terminal cast, kept low-chroma so // the palette colors read over it - {name: 'hue_neutral', light: 'var(--hue_b)'}, - {name: 'neutral_chroma', light: '0.05'}, + { name: 'hue_neutral', light: 'var(--hue_b)' }, + { name: 'neutral_chroma', light: '0.05' }, // links, focus, selection glow phosphor green - {name: 'hue_accent', light: 'var(--hue_b)'}, + { name: 'hue_accent', light: 'var(--hue_b)' }, // a whisper of phosphor bloom across every ramp - {name: 'hue_shift', light: '12'}, + { name: 'hue_shift', light: '12' }, // mono type everywhere - {name: 'font_family_sans', light: 'var(--font_family_mono)'}, + { name: 'font_family_sans', light: 'var(--font_family_mono)' }, // sharp: one knob zeroes every radius tier - {name: 'radius_scale', light: '0'}, + { name: 'radius_scale', light: '0' }, // flat: one knob zeroes the whole alpha ramp, button shadows included - {name: 'shadow_alpha_scale', light: '0'}, - ], + { name: 'shadow_alpha_scale', light: '0' } + ] }; /** @@ -49,37 +49,37 @@ export const terminal_theme: Theme = { */ export const create_terminal_theme = ( hue: number, - options: {name?: string; hue_shift?: number} = {}, + options: { name?: string; hue_shift?: number } = {} ): Theme => { - const {name = `terminal ${hue}`, hue_shift = 45} = options; + const { name = `terminal ${hue}`, hue_shift = 45 } = options; const hue_value = String(hue); return { name, scheme: 'dark', variables: [ // monochrome hue collapse - {name: 'hue_a', light: hue_value}, - {name: 'hue_b', light: hue_value}, - {name: 'hue_c', light: hue_value}, - {name: 'hue_d', light: hue_value}, - {name: 'hue_e', light: hue_value}, - {name: 'hue_f', light: hue_value}, - {name: 'hue_g', light: hue_value}, - {name: 'hue_h', light: hue_value}, - {name: 'hue_i', light: hue_value}, - {name: 'hue_j', light: hue_value}, - {name: 'hue_neutral', light: hue_value}, + { name: 'hue_a', light: hue_value }, + { name: 'hue_b', light: hue_value }, + { name: 'hue_c', light: hue_value }, + { name: 'hue_d', light: hue_value }, + { name: 'hue_e', light: hue_value }, + { name: 'hue_f', light: hue_value }, + { name: 'hue_g', light: hue_value }, + { name: 'hue_h', light: hue_value }, + { name: 'hue_i', light: hue_value }, + { name: 'hue_j', light: hue_value }, + { name: 'hue_neutral', light: hue_value }, // tinted surfaces and text - {name: 'neutral_chroma', light: '0.05'}, + { name: 'neutral_chroma', light: '0.05' }, // phosphor bloom: the ramps warm the bright end and cool the dim end so // the monochrome isn't a flat fill, staying unmistakably one hue - {name: 'hue_shift', light: String(hue_shift)}, + { name: 'hue_shift', light: String(hue_shift) }, // mono type everywhere - {name: 'font_family_sans', light: 'var(--font_family_mono)'}, + { name: 'font_family_sans', light: 'var(--font_family_mono)' }, // sharp: one knob zeroes every radius tier - {name: 'radius_scale', light: '0'}, + { name: 'radius_scale', light: '0' }, // flat: one knob zeroes the whole alpha ramp, button shadows included - {name: 'shadow_alpha_scale', light: '0'}, - ], + { name: 'shadow_alpha_scale', light: '0' } + ] }; }; diff --git a/src/lib/variable.ts b/src/lib/variable.ts index cb1f03cfb..e56c11f4e 100644 --- a/src/lib/variable.ts +++ b/src/lib/variable.ts @@ -1,4 +1,4 @@ -import {z} from 'zod'; +import { z } from 'zod'; // TODO maybe rename this module to `style_variable` for consistency? what about `variables`? @@ -19,13 +19,13 @@ export const StyleVariable = z name: StyleVariableName, light: z.string().optional(), dark: z.string().optional(), - summary: z.string().optional(), + summary: z.string().optional() }) .refine((v) => v.light !== undefined || v.dark !== undefined, { - message: 'must have at least one of light or dark', + message: 'must have at least one of light or dark' }) .refine((v) => !(v.light !== undefined && v.dark !== undefined && v.light === v.dark), { message: 'light and dark must differ when both specified', - path: ['dark'], + path: ['dark'] }); export type StyleVariable = z.infer<typeof StyleVariable>; diff --git a/src/lib/variable_data.ts b/src/lib/variable_data.ts index a38e6e1ce..1fed53b41 100644 --- a/src/lib/variable_data.ts +++ b/src/lib/variable_data.ts @@ -1,4 +1,4 @@ -import type {ArrayElement} from '@fuzdev/fuz_util/types.ts'; +import type { ArrayElement } from '@fuzdev/fuz_util/types.ts'; export type SizeVariant = ArrayElement<typeof font_size_variants>; export const font_size_variants = [ @@ -14,7 +14,7 @@ export const font_size_variants = [ 'xl6', 'xl7', 'xl8', - 'xl9', + 'xl9' ] as const; export const font_size_names = font_size_variants.map((s) => 'font_size_' + s); @@ -23,7 +23,7 @@ export type FontFamilyVariant = ArrayElement<typeof font_family_variants>; export const font_family_variants = [ 'font_family_sans', 'font_family_serif', - 'font_family_mono', + 'font_family_mono' ] as const; /** @@ -44,7 +44,7 @@ export const numeric_scale_variants = [ '80', '90', '95', - '100', + '100' ] as const; /** @@ -92,7 +92,7 @@ export const space_variants = [ 'xl12', 'xl13', 'xl14', - 'xl15', + 'xl15' ] as const; export type DistanceVariant = ArrayElement<typeof distance_variants>; @@ -112,7 +112,7 @@ export const shadow_variant_prefixes = [ 'shadow_bottom_', 'shadow_inset_', 'shadow_inset_top_', - 'shadow_inset_bottom_', + 'shadow_inset_bottom_' ] as const; export type ShadowSizeVariant = ArrayElement<typeof shadow_size_variants>; @@ -132,7 +132,7 @@ export const icon_sizes = { icon_size_lg: '80px', icon_size_xl: '128px', icon_size_xl2: '192px', - icon_size_xl3: '256px', + icon_size_xl3: '256px' }; export type PaletteVariant = ArrayElement<typeof palette_variants>; @@ -156,18 +156,18 @@ export const intent_variants = ['accent', 'positive', 'negative', 'caution', 'in */ export const palette_glosses: Record< PaletteVariant, - {color: string; binding?: IntentVariant | 'neutral'} + { color: string; binding?: IntentVariant | 'neutral' } > = { - a: {color: 'blue', binding: 'accent'}, - b: {color: 'green', binding: 'positive'}, - c: {color: 'red', binding: 'negative'}, - d: {color: 'purple'}, - e: {color: 'yellow'}, - f: {color: 'brown', binding: 'neutral'}, - g: {color: 'pink'}, - h: {color: 'orange', binding: 'caution'}, - i: {color: 'cyan', binding: 'info'}, - j: {color: 'teal'}, + a: { color: 'blue', binding: 'accent' }, + b: { color: 'green', binding: 'positive' }, + c: { color: 'red', binding: 'negative' }, + d: { color: 'purple' }, + e: { color: 'yellow' }, + f: { color: 'brown', binding: 'neutral' }, + g: { color: 'pink' }, + h: { color: 'orange', binding: 'caution' }, + i: { color: 'cyan', binding: 'info' }, + j: { color: 'teal' } }; export type IntensityVariant = NumericScaleVariant; diff --git a/src/lib/variable_graph.ts b/src/lib/variable_graph.ts index e33641b28..4ec086624 100644 --- a/src/lib/variable_graph.ts +++ b/src/lib/variable_graph.ts @@ -8,14 +8,14 @@ * @module */ -import {levenshtein_distance} from '@fuzdev/fuz_util/string.ts'; -import {hash_insecure} from '@fuzdev/fuz_util/hash.ts'; +import { levenshtein_distance } from '@fuzdev/fuz_util/string.ts'; +import { hash_insecure } from '@fuzdev/fuz_util/hash.ts'; -import {default_variables} from './variables.ts'; -import type {StyleVariable} from './variable.ts'; -import {extract_css_variables} from './css_variable_utils.ts'; +import { default_variables } from './variables.ts'; +import type { StyleVariable } from './variable.ts'; +import { extract_css_variables } from './css_variable_utils.ts'; -import type {VariablesOption} from './css_plugin_options.ts'; +import type { VariablesOption } from './css_plugin_options.ts'; /** * Information about a single style variable and its dependencies. @@ -52,7 +52,7 @@ export interface VariableDependencyGraph { */ export const build_variable_graph = ( variables: Array<StyleVariable>, - content_hash: string, + content_hash: string ): VariableDependencyGraph => { const graph: Map<string, StyleVariableInfo> = new Map(); @@ -65,13 +65,13 @@ export const build_variable_graph = ( light_deps, dark_deps, light_css: v.light, - dark_css: v.dark, + dark_css: v.dark }); } return { variables: graph, - content_hash, + content_hash }; }; @@ -98,7 +98,7 @@ export interface ResolveVariablesResult { */ export const resolve_variables_transitive = ( graph: VariableDependencyGraph, - initial_variables: Iterable<string>, + initial_variables: Iterable<string> ): ResolveVariablesResult => { const resolved: Set<string> = new Set(); const warnings: Array<string> = []; @@ -152,7 +152,7 @@ export const resolve_variables_transitive = ( resolve(name, new Set()); } - return {variables: resolved, warnings, missing}; + return { variables: resolved, warnings, missing }; }; /** @@ -164,8 +164,8 @@ export const resolve_variables_transitive = ( */ export const generate_theme_css = ( graph: VariableDependencyGraph, - resolved_variables: Set<string>, -): {light_css: string; dark_css: string} => { + resolved_variables: Set<string> +): { light_css: string; dark_css: string } => { const light_declarations: Array<string> = []; const dark_declarations: Array<string> = []; @@ -198,7 +198,7 @@ export const generate_theme_css = ( dark_css = `${dark_scope} {\n${dark_declarations.join('\n')}\n}`; } - return {light_css, dark_css}; + return { light_css, dark_css }; }; /** @@ -259,7 +259,7 @@ const TYPO_SIMILARITY_THRESHOLD = 0.85; */ export const find_similar_variable = ( graph: VariableDependencyGraph, - name: string, + name: string ): string | null => { let best_match: string | null = null; let best_similarity = TYPO_SIMILARITY_THRESHOLD; @@ -296,7 +296,7 @@ export const resolve_variables_option = (variables: VariablesOption): Array<Styl * @returns `VariableDependencyGraph` built from the resolved variables */ export const build_variable_graph_from_options = ( - variables: VariablesOption, + variables: VariablesOption ): VariableDependencyGraph => { const resolved = resolve_variables_option(variables); const content = resolved.map((v) => `${v.name}:${v.light ?? ''}:${v.dark ?? ''}`).join('|'); diff --git a/src/lib/variables.ts b/src/lib/variables.ts index db354e12e..93fd93488 100644 --- a/src/lib/variables.ts +++ b/src/lib/variables.ts @@ -1,5 +1,5 @@ -import type {StyleVariable} from './variable.ts'; -import {icon_sizes} from './variable_data.ts'; +import type { StyleVariable } from './variable.ts'; +import { icon_sizes } from './variable_data.ts'; import { NEUTRAL_CHROMA, PALETTE_CHROMA_KNOBS, @@ -13,7 +13,7 @@ import { render_lightness_stop_css, render_neutral_stop_css, render_palette_stop_css, - render_ramp_color_css, + render_ramp_color_css } from './ramps.ts'; /* @@ -47,48 +47,68 @@ curve knobs → ramp stops (`--palette_lightness_50`, `--palette_chroma_50`) */ // hue knobs - OKLCH hue angles; equal lightness/chroma across hues makes rotation safe -export const hue_a: StyleVariable = {name: 'hue_a', light: String(PALETTE_HUES.a), summary: 'blue'}; +export const hue_a: StyleVariable = { + name: 'hue_a', + light: String(PALETTE_HUES.a), + summary: 'blue' +}; export const hue_b: StyleVariable = { name: 'hue_b', light: String(PALETTE_HUES.b), - summary: 'green', + summary: 'green' +}; +export const hue_c: StyleVariable = { + name: 'hue_c', + light: String(PALETTE_HUES.c), + summary: 'red' }; -export const hue_c: StyleVariable = {name: 'hue_c', light: String(PALETTE_HUES.c), summary: 'red'}; export const hue_d: StyleVariable = { name: 'hue_d', light: String(PALETTE_HUES.d), - summary: 'purple', + summary: 'purple' }; export const hue_e: StyleVariable = { name: 'hue_e', light: String(PALETTE_HUES.e), - summary: 'yellow', + summary: 'yellow' }; export const hue_f: StyleVariable = { name: 'hue_f', light: String(PALETTE_HUES.f), - summary: 'brown', + summary: 'brown' +}; +export const hue_g: StyleVariable = { + name: 'hue_g', + light: String(PALETTE_HUES.g), + summary: 'pink' }; -export const hue_g: StyleVariable = {name: 'hue_g', light: String(PALETTE_HUES.g), summary: 'pink'}; export const hue_h: StyleVariable = { name: 'hue_h', light: String(PALETTE_HUES.h), - summary: 'orange', + summary: 'orange' +}; +export const hue_i: StyleVariable = { + name: 'hue_i', + light: String(PALETTE_HUES.i), + summary: 'cyan' +}; +export const hue_j: StyleVariable = { + name: 'hue_j', + light: String(PALETTE_HUES.j), + summary: 'teal' }; -export const hue_i: StyleVariable = {name: 'hue_i', light: String(PALETTE_HUES.i), summary: 'cyan'}; -export const hue_j: StyleVariable = {name: 'hue_j', light: String(PALETTE_HUES.j), summary: 'teal'}; // global color-character knobs export const chroma_scale: StyleVariable = { name: 'chroma_scale', light: '1', summary: - '0 makes the palette grayscale (the neutral scales have their own knob), above 1 is vivid and can clip past the sRGB gamut caps', + '0 makes the palette grayscale (the neutral scales have their own knob), above 1 is vivid and can clip past the sRGB gamut caps' }; export const hue_shift: StyleVariable = { name: 'hue_shift', light: '0', - summary: 'total degrees of hue rotation across each ramp, positive toward the dark end', + summary: 'total degrees of hue rotation across each ramp, positive toward the dark end' }; /* @@ -98,12 +118,12 @@ shadow (replaces the old tint_hue/tint_saturation); the neutral is an intent whose scales are shade_* and text_* rather than a neutral_00-100 family */ -export const hue_neutral: StyleVariable = {name: 'hue_neutral', light: 'var(--hue_f)'}; +export const hue_neutral: StyleVariable = { name: 'hue_neutral', light: 'var(--hue_f)' }; export const neutral_chroma: StyleVariable = { name: 'neutral_chroma', light: String(NEUTRAL_CHROMA.light), dark: String(NEUTRAL_CHROMA.dark), - summary: 'peak chroma of the neutral scales, applied through the shared chroma shape', + summary: 'peak chroma of the neutral scales, applied through the shared chroma shape' }; /* @@ -121,292 +141,292 @@ scales, and tree-shake like everything else - unused stops cost nothing. export const hue_accent: StyleVariable = { name: 'hue_accent', light: 'var(--hue_a)', - summary: 'links, focus, selection, selected states', + summary: 'links, focus, selection, selected states' }; export const hue_positive: StyleVariable = { name: 'hue_positive', light: 'var(--hue_b)', - summary: 'success affordances', + summary: 'success affordances' }; export const hue_negative: StyleVariable = { name: 'hue_negative', light: 'var(--hue_c)', - summary: 'errors, destructive actions', + summary: 'errors, destructive actions' }; export const hue_caution: StyleVariable = { name: 'hue_caution', light: 'var(--hue_h)', - summary: 'warnings', + summary: 'warnings' }; export const hue_info: StyleVariable = { name: 'hue_info', light: 'var(--hue_i)', - summary: 'informational callouts', + summary: 'informational callouts' }; // accent stops - the full scale derives through the shared ramps export const accent_00: StyleVariable = { name: 'accent_00', - light: render_ramp_color_css('var(--hue_accent)', '00'), + light: render_ramp_color_css('var(--hue_accent)', '00') }; export const accent_05: StyleVariable = { name: 'accent_05', - light: render_ramp_color_css('var(--hue_accent)', '05'), + light: render_ramp_color_css('var(--hue_accent)', '05') }; export const accent_10: StyleVariable = { name: 'accent_10', - light: render_ramp_color_css('var(--hue_accent)', '10'), + light: render_ramp_color_css('var(--hue_accent)', '10') }; export const accent_20: StyleVariable = { name: 'accent_20', - light: render_ramp_color_css('var(--hue_accent)', '20'), + light: render_ramp_color_css('var(--hue_accent)', '20') }; export const accent_30: StyleVariable = { name: 'accent_30', - light: render_ramp_color_css('var(--hue_accent)', '30'), + light: render_ramp_color_css('var(--hue_accent)', '30') }; export const accent_40: StyleVariable = { name: 'accent_40', - light: render_ramp_color_css('var(--hue_accent)', '40'), + light: render_ramp_color_css('var(--hue_accent)', '40') }; export const accent_50: StyleVariable = { name: 'accent_50', - light: render_ramp_color_css('var(--hue_accent)', '50'), + light: render_ramp_color_css('var(--hue_accent)', '50') }; export const accent_60: StyleVariable = { name: 'accent_60', - light: render_ramp_color_css('var(--hue_accent)', '60'), + light: render_ramp_color_css('var(--hue_accent)', '60') }; export const accent_70: StyleVariable = { name: 'accent_70', - light: render_ramp_color_css('var(--hue_accent)', '70'), + light: render_ramp_color_css('var(--hue_accent)', '70') }; export const accent_80: StyleVariable = { name: 'accent_80', - light: render_ramp_color_css('var(--hue_accent)', '80'), + light: render_ramp_color_css('var(--hue_accent)', '80') }; export const accent_90: StyleVariable = { name: 'accent_90', - light: render_ramp_color_css('var(--hue_accent)', '90'), + light: render_ramp_color_css('var(--hue_accent)', '90') }; export const accent_95: StyleVariable = { name: 'accent_95', - light: render_ramp_color_css('var(--hue_accent)', '95'), + light: render_ramp_color_css('var(--hue_accent)', '95') }; export const accent_100: StyleVariable = { name: 'accent_100', - light: render_ramp_color_css('var(--hue_accent)', '100'), + light: render_ramp_color_css('var(--hue_accent)', '100') }; // positive stops - the full scale derives through the shared ramps export const positive_00: StyleVariable = { name: 'positive_00', - light: render_ramp_color_css('var(--hue_positive)', '00'), + light: render_ramp_color_css('var(--hue_positive)', '00') }; export const positive_05: StyleVariable = { name: 'positive_05', - light: render_ramp_color_css('var(--hue_positive)', '05'), + light: render_ramp_color_css('var(--hue_positive)', '05') }; export const positive_10: StyleVariable = { name: 'positive_10', - light: render_ramp_color_css('var(--hue_positive)', '10'), + light: render_ramp_color_css('var(--hue_positive)', '10') }; export const positive_20: StyleVariable = { name: 'positive_20', - light: render_ramp_color_css('var(--hue_positive)', '20'), + light: render_ramp_color_css('var(--hue_positive)', '20') }; export const positive_30: StyleVariable = { name: 'positive_30', - light: render_ramp_color_css('var(--hue_positive)', '30'), + light: render_ramp_color_css('var(--hue_positive)', '30') }; export const positive_40: StyleVariable = { name: 'positive_40', - light: render_ramp_color_css('var(--hue_positive)', '40'), + light: render_ramp_color_css('var(--hue_positive)', '40') }; export const positive_50: StyleVariable = { name: 'positive_50', - light: render_ramp_color_css('var(--hue_positive)', '50'), + light: render_ramp_color_css('var(--hue_positive)', '50') }; export const positive_60: StyleVariable = { name: 'positive_60', - light: render_ramp_color_css('var(--hue_positive)', '60'), + light: render_ramp_color_css('var(--hue_positive)', '60') }; export const positive_70: StyleVariable = { name: 'positive_70', - light: render_ramp_color_css('var(--hue_positive)', '70'), + light: render_ramp_color_css('var(--hue_positive)', '70') }; export const positive_80: StyleVariable = { name: 'positive_80', - light: render_ramp_color_css('var(--hue_positive)', '80'), + light: render_ramp_color_css('var(--hue_positive)', '80') }; export const positive_90: StyleVariable = { name: 'positive_90', - light: render_ramp_color_css('var(--hue_positive)', '90'), + light: render_ramp_color_css('var(--hue_positive)', '90') }; export const positive_95: StyleVariable = { name: 'positive_95', - light: render_ramp_color_css('var(--hue_positive)', '95'), + light: render_ramp_color_css('var(--hue_positive)', '95') }; export const positive_100: StyleVariable = { name: 'positive_100', - light: render_ramp_color_css('var(--hue_positive)', '100'), + light: render_ramp_color_css('var(--hue_positive)', '100') }; // negative stops - the full scale derives through the shared ramps export const negative_00: StyleVariable = { name: 'negative_00', - light: render_ramp_color_css('var(--hue_negative)', '00'), + light: render_ramp_color_css('var(--hue_negative)', '00') }; export const negative_05: StyleVariable = { name: 'negative_05', - light: render_ramp_color_css('var(--hue_negative)', '05'), + light: render_ramp_color_css('var(--hue_negative)', '05') }; export const negative_10: StyleVariable = { name: 'negative_10', - light: render_ramp_color_css('var(--hue_negative)', '10'), + light: render_ramp_color_css('var(--hue_negative)', '10') }; export const negative_20: StyleVariable = { name: 'negative_20', - light: render_ramp_color_css('var(--hue_negative)', '20'), + light: render_ramp_color_css('var(--hue_negative)', '20') }; export const negative_30: StyleVariable = { name: 'negative_30', - light: render_ramp_color_css('var(--hue_negative)', '30'), + light: render_ramp_color_css('var(--hue_negative)', '30') }; export const negative_40: StyleVariable = { name: 'negative_40', - light: render_ramp_color_css('var(--hue_negative)', '40'), + light: render_ramp_color_css('var(--hue_negative)', '40') }; export const negative_50: StyleVariable = { name: 'negative_50', - light: render_ramp_color_css('var(--hue_negative)', '50'), + light: render_ramp_color_css('var(--hue_negative)', '50') }; export const negative_60: StyleVariable = { name: 'negative_60', - light: render_ramp_color_css('var(--hue_negative)', '60'), + light: render_ramp_color_css('var(--hue_negative)', '60') }; export const negative_70: StyleVariable = { name: 'negative_70', - light: render_ramp_color_css('var(--hue_negative)', '70'), + light: render_ramp_color_css('var(--hue_negative)', '70') }; export const negative_80: StyleVariable = { name: 'negative_80', - light: render_ramp_color_css('var(--hue_negative)', '80'), + light: render_ramp_color_css('var(--hue_negative)', '80') }; export const negative_90: StyleVariable = { name: 'negative_90', - light: render_ramp_color_css('var(--hue_negative)', '90'), + light: render_ramp_color_css('var(--hue_negative)', '90') }; export const negative_95: StyleVariable = { name: 'negative_95', - light: render_ramp_color_css('var(--hue_negative)', '95'), + light: render_ramp_color_css('var(--hue_negative)', '95') }; export const negative_100: StyleVariable = { name: 'negative_100', - light: render_ramp_color_css('var(--hue_negative)', '100'), + light: render_ramp_color_css('var(--hue_negative)', '100') }; // caution stops - the full scale derives through the shared ramps export const caution_00: StyleVariable = { name: 'caution_00', - light: render_ramp_color_css('var(--hue_caution)', '00'), + light: render_ramp_color_css('var(--hue_caution)', '00') }; export const caution_05: StyleVariable = { name: 'caution_05', - light: render_ramp_color_css('var(--hue_caution)', '05'), + light: render_ramp_color_css('var(--hue_caution)', '05') }; export const caution_10: StyleVariable = { name: 'caution_10', - light: render_ramp_color_css('var(--hue_caution)', '10'), + light: render_ramp_color_css('var(--hue_caution)', '10') }; export const caution_20: StyleVariable = { name: 'caution_20', - light: render_ramp_color_css('var(--hue_caution)', '20'), + light: render_ramp_color_css('var(--hue_caution)', '20') }; export const caution_30: StyleVariable = { name: 'caution_30', - light: render_ramp_color_css('var(--hue_caution)', '30'), + light: render_ramp_color_css('var(--hue_caution)', '30') }; export const caution_40: StyleVariable = { name: 'caution_40', - light: render_ramp_color_css('var(--hue_caution)', '40'), + light: render_ramp_color_css('var(--hue_caution)', '40') }; export const caution_50: StyleVariable = { name: 'caution_50', - light: render_ramp_color_css('var(--hue_caution)', '50'), + light: render_ramp_color_css('var(--hue_caution)', '50') }; export const caution_60: StyleVariable = { name: 'caution_60', - light: render_ramp_color_css('var(--hue_caution)', '60'), + light: render_ramp_color_css('var(--hue_caution)', '60') }; export const caution_70: StyleVariable = { name: 'caution_70', - light: render_ramp_color_css('var(--hue_caution)', '70'), + light: render_ramp_color_css('var(--hue_caution)', '70') }; export const caution_80: StyleVariable = { name: 'caution_80', - light: render_ramp_color_css('var(--hue_caution)', '80'), + light: render_ramp_color_css('var(--hue_caution)', '80') }; export const caution_90: StyleVariable = { name: 'caution_90', - light: render_ramp_color_css('var(--hue_caution)', '90'), + light: render_ramp_color_css('var(--hue_caution)', '90') }; export const caution_95: StyleVariable = { name: 'caution_95', - light: render_ramp_color_css('var(--hue_caution)', '95'), + light: render_ramp_color_css('var(--hue_caution)', '95') }; export const caution_100: StyleVariable = { name: 'caution_100', - light: render_ramp_color_css('var(--hue_caution)', '100'), + light: render_ramp_color_css('var(--hue_caution)', '100') }; // info stops - the full scale derives through the shared ramps export const info_00: StyleVariable = { name: 'info_00', - light: render_ramp_color_css('var(--hue_info)', '00'), + light: render_ramp_color_css('var(--hue_info)', '00') }; export const info_05: StyleVariable = { name: 'info_05', - light: render_ramp_color_css('var(--hue_info)', '05'), + light: render_ramp_color_css('var(--hue_info)', '05') }; export const info_10: StyleVariable = { name: 'info_10', - light: render_ramp_color_css('var(--hue_info)', '10'), + light: render_ramp_color_css('var(--hue_info)', '10') }; export const info_20: StyleVariable = { name: 'info_20', - light: render_ramp_color_css('var(--hue_info)', '20'), + light: render_ramp_color_css('var(--hue_info)', '20') }; export const info_30: StyleVariable = { name: 'info_30', - light: render_ramp_color_css('var(--hue_info)', '30'), + light: render_ramp_color_css('var(--hue_info)', '30') }; export const info_40: StyleVariable = { name: 'info_40', - light: render_ramp_color_css('var(--hue_info)', '40'), + light: render_ramp_color_css('var(--hue_info)', '40') }; export const info_50: StyleVariable = { name: 'info_50', - light: render_ramp_color_css('var(--hue_info)', '50'), + light: render_ramp_color_css('var(--hue_info)', '50') }; export const info_60: StyleVariable = { name: 'info_60', - light: render_ramp_color_css('var(--hue_info)', '60'), + light: render_ramp_color_css('var(--hue_info)', '60') }; export const info_70: StyleVariable = { name: 'info_70', - light: render_ramp_color_css('var(--hue_info)', '70'), + light: render_ramp_color_css('var(--hue_info)', '70') }; export const info_80: StyleVariable = { name: 'info_80', - light: render_ramp_color_css('var(--hue_info)', '80'), + light: render_ramp_color_css('var(--hue_info)', '80') }; export const info_90: StyleVariable = { name: 'info_90', - light: render_ramp_color_css('var(--hue_info)', '90'), + light: render_ramp_color_css('var(--hue_info)', '90') }; export const info_95: StyleVariable = { name: 'info_95', - light: render_ramp_color_css('var(--hue_info)', '95'), + light: render_ramp_color_css('var(--hue_info)', '95') }; export const info_100: StyleVariable = { name: 'info_100', - light: render_ramp_color_css('var(--hue_info)', '100'), + light: render_ramp_color_css('var(--hue_info)', '100') }; /* @@ -418,61 +438,61 @@ then the derived intermediate stops export const palette_lightness_00: StyleVariable = { name: 'palette_lightness_00', light: String(PALETTE_LIGHTNESS_KNOBS.light.lightness_00), - dark: String(PALETTE_LIGHTNESS_KNOBS.dark.lightness_00), + dark: String(PALETTE_LIGHTNESS_KNOBS.dark.lightness_00) }; export const palette_lightness_100: StyleVariable = { name: 'palette_lightness_100', light: String(PALETTE_LIGHTNESS_KNOBS.light.lightness_100), - dark: String(PALETTE_LIGHTNESS_KNOBS.dark.lightness_100), + dark: String(PALETTE_LIGHTNESS_KNOBS.dark.lightness_100) }; export const palette_lightness_curve: StyleVariable = { name: 'palette_lightness_curve', light: String(PALETTE_LIGHTNESS_KNOBS.light.curve), - dark: String(PALETTE_LIGHTNESS_KNOBS.dark.curve), + dark: String(PALETTE_LIGHTNESS_KNOBS.dark.curve) }; export const palette_lightness_05: StyleVariable = { name: 'palette_lightness_05', - light: render_lightness_stop_css('palette', '05'), + light: render_lightness_stop_css('palette', '05') }; export const palette_lightness_10: StyleVariable = { name: 'palette_lightness_10', - light: render_lightness_stop_css('palette', '10'), + light: render_lightness_stop_css('palette', '10') }; export const palette_lightness_20: StyleVariable = { name: 'palette_lightness_20', - light: render_lightness_stop_css('palette', '20'), + light: render_lightness_stop_css('palette', '20') }; export const palette_lightness_30: StyleVariable = { name: 'palette_lightness_30', - light: render_lightness_stop_css('palette', '30'), + light: render_lightness_stop_css('palette', '30') }; export const palette_lightness_40: StyleVariable = { name: 'palette_lightness_40', - light: render_lightness_stop_css('palette', '40'), + light: render_lightness_stop_css('palette', '40') }; export const palette_lightness_50: StyleVariable = { name: 'palette_lightness_50', - light: render_lightness_stop_css('palette', '50'), + light: render_lightness_stop_css('palette', '50') }; export const palette_lightness_60: StyleVariable = { name: 'palette_lightness_60', - light: render_lightness_stop_css('palette', '60'), + light: render_lightness_stop_css('palette', '60') }; export const palette_lightness_70: StyleVariable = { name: 'palette_lightness_70', - light: render_lightness_stop_css('palette', '70'), + light: render_lightness_stop_css('palette', '70') }; export const palette_lightness_80: StyleVariable = { name: 'palette_lightness_80', - light: render_lightness_stop_css('palette', '80'), + light: render_lightness_stop_css('palette', '80') }; export const palette_lightness_90: StyleVariable = { name: 'palette_lightness_90', - light: render_lightness_stop_css('palette', '90'), + light: render_lightness_stop_css('palette', '90') }; export const palette_lightness_95: StyleVariable = { name: 'palette_lightness_95', - light: render_lightness_stop_css('palette', '95'), + light: render_lightness_stop_css('palette', '95') }; /* @@ -485,140 +505,140 @@ chroma_scale multiplies outside the clamp at the color stops export const palette_chroma_min: StyleVariable = { name: 'palette_chroma_min', light: String(PALETTE_CHROMA_KNOBS.light.chroma_min), - dark: String(PALETTE_CHROMA_KNOBS.dark.chroma_min), + dark: String(PALETTE_CHROMA_KNOBS.dark.chroma_min) }; export const palette_chroma_max: StyleVariable = { name: 'palette_chroma_max', light: String(PALETTE_CHROMA_KNOBS.light.chroma_max), - dark: String(PALETTE_CHROMA_KNOBS.dark.chroma_max), + dark: String(PALETTE_CHROMA_KNOBS.dark.chroma_max) }; export const palette_chroma_curve: StyleVariable = { name: 'palette_chroma_curve', light: String(PALETTE_CHROMA_KNOBS.light.curve), dark: String(PALETTE_CHROMA_KNOBS.dark.curve), - summary: 'falloff of the chroma peak; the neutral scales ride this same shape', + summary: 'falloff of the chroma peak; the neutral scales ride this same shape' }; // normalized chroma shape per stop - 0 at the endpoints, 1 at the midpoint; // shared by the palette chroma ramp and the neutral scales export const chroma_shape_00: StyleVariable = { name: 'chroma_shape_00', - light: render_chroma_shape_css('00'), + light: render_chroma_shape_css('00') }; export const chroma_shape_05: StyleVariable = { name: 'chroma_shape_05', - light: render_chroma_shape_css('05'), + light: render_chroma_shape_css('05') }; export const chroma_shape_10: StyleVariable = { name: 'chroma_shape_10', - light: render_chroma_shape_css('10'), + light: render_chroma_shape_css('10') }; export const chroma_shape_20: StyleVariable = { name: 'chroma_shape_20', - light: render_chroma_shape_css('20'), + light: render_chroma_shape_css('20') }; export const chroma_shape_30: StyleVariable = { name: 'chroma_shape_30', - light: render_chroma_shape_css('30'), + light: render_chroma_shape_css('30') }; export const chroma_shape_40: StyleVariable = { name: 'chroma_shape_40', - light: render_chroma_shape_css('40'), + light: render_chroma_shape_css('40') }; export const chroma_shape_50: StyleVariable = { name: 'chroma_shape_50', - light: render_chroma_shape_css('50'), + light: render_chroma_shape_css('50') }; export const chroma_shape_60: StyleVariable = { name: 'chroma_shape_60', - light: render_chroma_shape_css('60'), + light: render_chroma_shape_css('60') }; export const chroma_shape_70: StyleVariable = { name: 'chroma_shape_70', - light: render_chroma_shape_css('70'), + light: render_chroma_shape_css('70') }; export const chroma_shape_80: StyleVariable = { name: 'chroma_shape_80', - light: render_chroma_shape_css('80'), + light: render_chroma_shape_css('80') }; export const chroma_shape_90: StyleVariable = { name: 'chroma_shape_90', - light: render_chroma_shape_css('90'), + light: render_chroma_shape_css('90') }; export const chroma_shape_95: StyleVariable = { name: 'chroma_shape_95', - light: render_chroma_shape_css('95'), + light: render_chroma_shape_css('95') }; export const chroma_shape_100: StyleVariable = { name: 'chroma_shape_100', - light: render_chroma_shape_css('100'), + light: render_chroma_shape_css('100') }; // capped palette chroma stops (per-scheme caps) export const palette_chroma_00: StyleVariable = { name: 'palette_chroma_00', light: render_chroma_stop_css('00', 'light'), - dark: render_chroma_stop_css('00', 'dark'), + dark: render_chroma_stop_css('00', 'dark') }; export const palette_chroma_05: StyleVariable = { name: 'palette_chroma_05', light: render_chroma_stop_css('05', 'light'), - dark: render_chroma_stop_css('05', 'dark'), + dark: render_chroma_stop_css('05', 'dark') }; export const palette_chroma_10: StyleVariable = { name: 'palette_chroma_10', light: render_chroma_stop_css('10', 'light'), - dark: render_chroma_stop_css('10', 'dark'), + dark: render_chroma_stop_css('10', 'dark') }; export const palette_chroma_20: StyleVariable = { name: 'palette_chroma_20', light: render_chroma_stop_css('20', 'light'), - dark: render_chroma_stop_css('20', 'dark'), + dark: render_chroma_stop_css('20', 'dark') }; export const palette_chroma_30: StyleVariable = { name: 'palette_chroma_30', light: render_chroma_stop_css('30', 'light'), - dark: render_chroma_stop_css('30', 'dark'), + dark: render_chroma_stop_css('30', 'dark') }; export const palette_chroma_40: StyleVariable = { name: 'palette_chroma_40', light: render_chroma_stop_css('40', 'light'), - dark: render_chroma_stop_css('40', 'dark'), + dark: render_chroma_stop_css('40', 'dark') }; export const palette_chroma_50: StyleVariable = { name: 'palette_chroma_50', light: render_chroma_stop_css('50', 'light'), - dark: render_chroma_stop_css('50', 'dark'), + dark: render_chroma_stop_css('50', 'dark') }; export const palette_chroma_60: StyleVariable = { name: 'palette_chroma_60', light: render_chroma_stop_css('60', 'light'), - dark: render_chroma_stop_css('60', 'dark'), + dark: render_chroma_stop_css('60', 'dark') }; export const palette_chroma_70: StyleVariable = { name: 'palette_chroma_70', light: render_chroma_stop_css('70', 'light'), - dark: render_chroma_stop_css('70', 'dark'), + dark: render_chroma_stop_css('70', 'dark') }; export const palette_chroma_80: StyleVariable = { name: 'palette_chroma_80', light: render_chroma_stop_css('80', 'light'), - dark: render_chroma_stop_css('80', 'dark'), + dark: render_chroma_stop_css('80', 'dark') }; export const palette_chroma_90: StyleVariable = { name: 'palette_chroma_90', light: render_chroma_stop_css('90', 'light'), - dark: render_chroma_stop_css('90', 'dark'), + dark: render_chroma_stop_css('90', 'dark') }; export const palette_chroma_95: StyleVariable = { name: 'palette_chroma_95', light: render_chroma_stop_css('95', 'light'), - dark: render_chroma_stop_css('95', 'dark'), + dark: render_chroma_stop_css('95', 'dark') }; export const palette_chroma_100: StyleVariable = { name: 'palette_chroma_100', light: render_chroma_stop_css('100', 'light'), - dark: render_chroma_stop_css('100', 'dark'), + dark: render_chroma_stop_css('100', 'dark') }; // per-stop hue-shift offsets - the scheme sign flip is baked into the slots so @@ -626,588 +646,588 @@ export const palette_chroma_100: StyleVariable = { export const hue_shift_00: StyleVariable = { name: 'hue_shift_00', light: render_hue_shift_offset_css('00', 'light'), - dark: render_hue_shift_offset_css('00', 'dark'), + dark: render_hue_shift_offset_css('00', 'dark') }; export const hue_shift_05: StyleVariable = { name: 'hue_shift_05', light: render_hue_shift_offset_css('05', 'light'), - dark: render_hue_shift_offset_css('05', 'dark'), + dark: render_hue_shift_offset_css('05', 'dark') }; export const hue_shift_10: StyleVariable = { name: 'hue_shift_10', light: render_hue_shift_offset_css('10', 'light'), - dark: render_hue_shift_offset_css('10', 'dark'), + dark: render_hue_shift_offset_css('10', 'dark') }; export const hue_shift_20: StyleVariable = { name: 'hue_shift_20', light: render_hue_shift_offset_css('20', 'light'), - dark: render_hue_shift_offset_css('20', 'dark'), + dark: render_hue_shift_offset_css('20', 'dark') }; export const hue_shift_30: StyleVariable = { name: 'hue_shift_30', light: render_hue_shift_offset_css('30', 'light'), - dark: render_hue_shift_offset_css('30', 'dark'), + dark: render_hue_shift_offset_css('30', 'dark') }; export const hue_shift_40: StyleVariable = { name: 'hue_shift_40', light: render_hue_shift_offset_css('40', 'light'), - dark: render_hue_shift_offset_css('40', 'dark'), + dark: render_hue_shift_offset_css('40', 'dark') }; export const hue_shift_50: StyleVariable = { name: 'hue_shift_50', - light: render_hue_shift_offset_css('50', 'light'), + light: render_hue_shift_offset_css('50', 'light') }; export const hue_shift_60: StyleVariable = { name: 'hue_shift_60', light: render_hue_shift_offset_css('60', 'light'), - dark: render_hue_shift_offset_css('60', 'dark'), + dark: render_hue_shift_offset_css('60', 'dark') }; export const hue_shift_70: StyleVariable = { name: 'hue_shift_70', light: render_hue_shift_offset_css('70', 'light'), - dark: render_hue_shift_offset_css('70', 'dark'), + dark: render_hue_shift_offset_css('70', 'dark') }; export const hue_shift_80: StyleVariable = { name: 'hue_shift_80', light: render_hue_shift_offset_css('80', 'light'), - dark: render_hue_shift_offset_css('80', 'dark'), + dark: render_hue_shift_offset_css('80', 'dark') }; export const hue_shift_90: StyleVariable = { name: 'hue_shift_90', light: render_hue_shift_offset_css('90', 'light'), - dark: render_hue_shift_offset_css('90', 'dark'), + dark: render_hue_shift_offset_css('90', 'dark') }; export const hue_shift_95: StyleVariable = { name: 'hue_shift_95', light: render_hue_shift_offset_css('95', 'light'), - dark: render_hue_shift_offset_css('95', 'dark'), + dark: render_hue_shift_offset_css('95', 'dark') }; export const hue_shift_100: StyleVariable = { name: 'hue_shift_100', light: render_hue_shift_offset_css('100', 'light'), - dark: render_hue_shift_offset_css('100', 'dark'), + dark: render_hue_shift_offset_css('100', 'dark') }; // palette color stops - one definition per stop; the scheme flip lives in the knobs export const palette_a_00: StyleVariable = { name: 'palette_a_00', - light: render_palette_stop_css('a', '00'), + light: render_palette_stop_css('a', '00') }; export const palette_a_05: StyleVariable = { name: 'palette_a_05', - light: render_palette_stop_css('a', '05'), + light: render_palette_stop_css('a', '05') }; export const palette_a_10: StyleVariable = { name: 'palette_a_10', - light: render_palette_stop_css('a', '10'), + light: render_palette_stop_css('a', '10') }; export const palette_a_20: StyleVariable = { name: 'palette_a_20', - light: render_palette_stop_css('a', '20'), + light: render_palette_stop_css('a', '20') }; export const palette_a_30: StyleVariable = { name: 'palette_a_30', - light: render_palette_stop_css('a', '30'), + light: render_palette_stop_css('a', '30') }; export const palette_a_40: StyleVariable = { name: 'palette_a_40', - light: render_palette_stop_css('a', '40'), + light: render_palette_stop_css('a', '40') }; export const palette_a_50: StyleVariable = { name: 'palette_a_50', - light: render_palette_stop_css('a', '50'), + light: render_palette_stop_css('a', '50') }; export const palette_a_60: StyleVariable = { name: 'palette_a_60', - light: render_palette_stop_css('a', '60'), + light: render_palette_stop_css('a', '60') }; export const palette_a_70: StyleVariable = { name: 'palette_a_70', - light: render_palette_stop_css('a', '70'), + light: render_palette_stop_css('a', '70') }; export const palette_a_80: StyleVariable = { name: 'palette_a_80', - light: render_palette_stop_css('a', '80'), + light: render_palette_stop_css('a', '80') }; export const palette_a_90: StyleVariable = { name: 'palette_a_90', - light: render_palette_stop_css('a', '90'), + light: render_palette_stop_css('a', '90') }; export const palette_a_95: StyleVariable = { name: 'palette_a_95', - light: render_palette_stop_css('a', '95'), + light: render_palette_stop_css('a', '95') }; export const palette_a_100: StyleVariable = { name: 'palette_a_100', - light: render_palette_stop_css('a', '100'), + light: render_palette_stop_css('a', '100') }; export const palette_b_00: StyleVariable = { name: 'palette_b_00', - light: render_palette_stop_css('b', '00'), + light: render_palette_stop_css('b', '00') }; export const palette_b_05: StyleVariable = { name: 'palette_b_05', - light: render_palette_stop_css('b', '05'), + light: render_palette_stop_css('b', '05') }; export const palette_b_10: StyleVariable = { name: 'palette_b_10', - light: render_palette_stop_css('b', '10'), + light: render_palette_stop_css('b', '10') }; export const palette_b_20: StyleVariable = { name: 'palette_b_20', - light: render_palette_stop_css('b', '20'), + light: render_palette_stop_css('b', '20') }; export const palette_b_30: StyleVariable = { name: 'palette_b_30', - light: render_palette_stop_css('b', '30'), + light: render_palette_stop_css('b', '30') }; export const palette_b_40: StyleVariable = { name: 'palette_b_40', - light: render_palette_stop_css('b', '40'), + light: render_palette_stop_css('b', '40') }; export const palette_b_50: StyleVariable = { name: 'palette_b_50', - light: render_palette_stop_css('b', '50'), + light: render_palette_stop_css('b', '50') }; export const palette_b_60: StyleVariable = { name: 'palette_b_60', - light: render_palette_stop_css('b', '60'), + light: render_palette_stop_css('b', '60') }; export const palette_b_70: StyleVariable = { name: 'palette_b_70', - light: render_palette_stop_css('b', '70'), + light: render_palette_stop_css('b', '70') }; export const palette_b_80: StyleVariable = { name: 'palette_b_80', - light: render_palette_stop_css('b', '80'), + light: render_palette_stop_css('b', '80') }; export const palette_b_90: StyleVariable = { name: 'palette_b_90', - light: render_palette_stop_css('b', '90'), + light: render_palette_stop_css('b', '90') }; export const palette_b_95: StyleVariable = { name: 'palette_b_95', - light: render_palette_stop_css('b', '95'), + light: render_palette_stop_css('b', '95') }; export const palette_b_100: StyleVariable = { name: 'palette_b_100', - light: render_palette_stop_css('b', '100'), + light: render_palette_stop_css('b', '100') }; export const palette_c_00: StyleVariable = { name: 'palette_c_00', - light: render_palette_stop_css('c', '00'), + light: render_palette_stop_css('c', '00') }; export const palette_c_05: StyleVariable = { name: 'palette_c_05', - light: render_palette_stop_css('c', '05'), + light: render_palette_stop_css('c', '05') }; export const palette_c_10: StyleVariable = { name: 'palette_c_10', - light: render_palette_stop_css('c', '10'), + light: render_palette_stop_css('c', '10') }; export const palette_c_20: StyleVariable = { name: 'palette_c_20', - light: render_palette_stop_css('c', '20'), + light: render_palette_stop_css('c', '20') }; export const palette_c_30: StyleVariable = { name: 'palette_c_30', - light: render_palette_stop_css('c', '30'), + light: render_palette_stop_css('c', '30') }; export const palette_c_40: StyleVariable = { name: 'palette_c_40', - light: render_palette_stop_css('c', '40'), + light: render_palette_stop_css('c', '40') }; export const palette_c_50: StyleVariable = { name: 'palette_c_50', - light: render_palette_stop_css('c', '50'), + light: render_palette_stop_css('c', '50') }; export const palette_c_60: StyleVariable = { name: 'palette_c_60', - light: render_palette_stop_css('c', '60'), + light: render_palette_stop_css('c', '60') }; export const palette_c_70: StyleVariable = { name: 'palette_c_70', - light: render_palette_stop_css('c', '70'), + light: render_palette_stop_css('c', '70') }; export const palette_c_80: StyleVariable = { name: 'palette_c_80', - light: render_palette_stop_css('c', '80'), + light: render_palette_stop_css('c', '80') }; export const palette_c_90: StyleVariable = { name: 'palette_c_90', - light: render_palette_stop_css('c', '90'), + light: render_palette_stop_css('c', '90') }; export const palette_c_95: StyleVariable = { name: 'palette_c_95', - light: render_palette_stop_css('c', '95'), + light: render_palette_stop_css('c', '95') }; export const palette_c_100: StyleVariable = { name: 'palette_c_100', - light: render_palette_stop_css('c', '100'), + light: render_palette_stop_css('c', '100') }; export const palette_d_00: StyleVariable = { name: 'palette_d_00', - light: render_palette_stop_css('d', '00'), + light: render_palette_stop_css('d', '00') }; export const palette_d_05: StyleVariable = { name: 'palette_d_05', - light: render_palette_stop_css('d', '05'), + light: render_palette_stop_css('d', '05') }; export const palette_d_10: StyleVariable = { name: 'palette_d_10', - light: render_palette_stop_css('d', '10'), + light: render_palette_stop_css('d', '10') }; export const palette_d_20: StyleVariable = { name: 'palette_d_20', - light: render_palette_stop_css('d', '20'), + light: render_palette_stop_css('d', '20') }; export const palette_d_30: StyleVariable = { name: 'palette_d_30', - light: render_palette_stop_css('d', '30'), + light: render_palette_stop_css('d', '30') }; export const palette_d_40: StyleVariable = { name: 'palette_d_40', - light: render_palette_stop_css('d', '40'), + light: render_palette_stop_css('d', '40') }; export const palette_d_50: StyleVariable = { name: 'palette_d_50', - light: render_palette_stop_css('d', '50'), + light: render_palette_stop_css('d', '50') }; export const palette_d_60: StyleVariable = { name: 'palette_d_60', - light: render_palette_stop_css('d', '60'), + light: render_palette_stop_css('d', '60') }; export const palette_d_70: StyleVariable = { name: 'palette_d_70', - light: render_palette_stop_css('d', '70'), + light: render_palette_stop_css('d', '70') }; export const palette_d_80: StyleVariable = { name: 'palette_d_80', - light: render_palette_stop_css('d', '80'), + light: render_palette_stop_css('d', '80') }; export const palette_d_90: StyleVariable = { name: 'palette_d_90', - light: render_palette_stop_css('d', '90'), + light: render_palette_stop_css('d', '90') }; export const palette_d_95: StyleVariable = { name: 'palette_d_95', - light: render_palette_stop_css('d', '95'), + light: render_palette_stop_css('d', '95') }; export const palette_d_100: StyleVariable = { name: 'palette_d_100', - light: render_palette_stop_css('d', '100'), + light: render_palette_stop_css('d', '100') }; export const palette_e_00: StyleVariable = { name: 'palette_e_00', - light: render_palette_stop_css('e', '00'), + light: render_palette_stop_css('e', '00') }; export const palette_e_05: StyleVariable = { name: 'palette_e_05', - light: render_palette_stop_css('e', '05'), + light: render_palette_stop_css('e', '05') }; export const palette_e_10: StyleVariable = { name: 'palette_e_10', - light: render_palette_stop_css('e', '10'), + light: render_palette_stop_css('e', '10') }; export const palette_e_20: StyleVariable = { name: 'palette_e_20', - light: render_palette_stop_css('e', '20'), + light: render_palette_stop_css('e', '20') }; export const palette_e_30: StyleVariable = { name: 'palette_e_30', - light: render_palette_stop_css('e', '30'), + light: render_palette_stop_css('e', '30') }; export const palette_e_40: StyleVariable = { name: 'palette_e_40', - light: render_palette_stop_css('e', '40'), + light: render_palette_stop_css('e', '40') }; export const palette_e_50: StyleVariable = { name: 'palette_e_50', - light: render_palette_stop_css('e', '50'), + light: render_palette_stop_css('e', '50') }; export const palette_e_60: StyleVariable = { name: 'palette_e_60', - light: render_palette_stop_css('e', '60'), + light: render_palette_stop_css('e', '60') }; export const palette_e_70: StyleVariable = { name: 'palette_e_70', - light: render_palette_stop_css('e', '70'), + light: render_palette_stop_css('e', '70') }; export const palette_e_80: StyleVariable = { name: 'palette_e_80', - light: render_palette_stop_css('e', '80'), + light: render_palette_stop_css('e', '80') }; export const palette_e_90: StyleVariable = { name: 'palette_e_90', - light: render_palette_stop_css('e', '90'), + light: render_palette_stop_css('e', '90') }; export const palette_e_95: StyleVariable = { name: 'palette_e_95', - light: render_palette_stop_css('e', '95'), + light: render_palette_stop_css('e', '95') }; export const palette_e_100: StyleVariable = { name: 'palette_e_100', - light: render_palette_stop_css('e', '100'), + light: render_palette_stop_css('e', '100') }; export const palette_f_00: StyleVariable = { name: 'palette_f_00', - light: render_palette_stop_css('f', '00'), + light: render_palette_stop_css('f', '00') }; export const palette_f_05: StyleVariable = { name: 'palette_f_05', - light: render_palette_stop_css('f', '05'), + light: render_palette_stop_css('f', '05') }; export const palette_f_10: StyleVariable = { name: 'palette_f_10', - light: render_palette_stop_css('f', '10'), + light: render_palette_stop_css('f', '10') }; export const palette_f_20: StyleVariable = { name: 'palette_f_20', - light: render_palette_stop_css('f', '20'), + light: render_palette_stop_css('f', '20') }; export const palette_f_30: StyleVariable = { name: 'palette_f_30', - light: render_palette_stop_css('f', '30'), + light: render_palette_stop_css('f', '30') }; export const palette_f_40: StyleVariable = { name: 'palette_f_40', - light: render_palette_stop_css('f', '40'), + light: render_palette_stop_css('f', '40') }; export const palette_f_50: StyleVariable = { name: 'palette_f_50', - light: render_palette_stop_css('f', '50'), + light: render_palette_stop_css('f', '50') }; export const palette_f_60: StyleVariable = { name: 'palette_f_60', - light: render_palette_stop_css('f', '60'), + light: render_palette_stop_css('f', '60') }; export const palette_f_70: StyleVariable = { name: 'palette_f_70', - light: render_palette_stop_css('f', '70'), + light: render_palette_stop_css('f', '70') }; export const palette_f_80: StyleVariable = { name: 'palette_f_80', - light: render_palette_stop_css('f', '80'), + light: render_palette_stop_css('f', '80') }; export const palette_f_90: StyleVariable = { name: 'palette_f_90', - light: render_palette_stop_css('f', '90'), + light: render_palette_stop_css('f', '90') }; export const palette_f_95: StyleVariable = { name: 'palette_f_95', - light: render_palette_stop_css('f', '95'), + light: render_palette_stop_css('f', '95') }; export const palette_f_100: StyleVariable = { name: 'palette_f_100', - light: render_palette_stop_css('f', '100'), + light: render_palette_stop_css('f', '100') }; export const palette_g_00: StyleVariable = { name: 'palette_g_00', - light: render_palette_stop_css('g', '00'), + light: render_palette_stop_css('g', '00') }; export const palette_g_05: StyleVariable = { name: 'palette_g_05', - light: render_palette_stop_css('g', '05'), + light: render_palette_stop_css('g', '05') }; export const palette_g_10: StyleVariable = { name: 'palette_g_10', - light: render_palette_stop_css('g', '10'), + light: render_palette_stop_css('g', '10') }; export const palette_g_20: StyleVariable = { name: 'palette_g_20', - light: render_palette_stop_css('g', '20'), + light: render_palette_stop_css('g', '20') }; export const palette_g_30: StyleVariable = { name: 'palette_g_30', - light: render_palette_stop_css('g', '30'), + light: render_palette_stop_css('g', '30') }; export const palette_g_40: StyleVariable = { name: 'palette_g_40', - light: render_palette_stop_css('g', '40'), + light: render_palette_stop_css('g', '40') }; export const palette_g_50: StyleVariable = { name: 'palette_g_50', - light: render_palette_stop_css('g', '50'), + light: render_palette_stop_css('g', '50') }; export const palette_g_60: StyleVariable = { name: 'palette_g_60', - light: render_palette_stop_css('g', '60'), + light: render_palette_stop_css('g', '60') }; export const palette_g_70: StyleVariable = { name: 'palette_g_70', - light: render_palette_stop_css('g', '70'), + light: render_palette_stop_css('g', '70') }; export const palette_g_80: StyleVariable = { name: 'palette_g_80', - light: render_palette_stop_css('g', '80'), + light: render_palette_stop_css('g', '80') }; export const palette_g_90: StyleVariable = { name: 'palette_g_90', - light: render_palette_stop_css('g', '90'), + light: render_palette_stop_css('g', '90') }; export const palette_g_95: StyleVariable = { name: 'palette_g_95', - light: render_palette_stop_css('g', '95'), + light: render_palette_stop_css('g', '95') }; export const palette_g_100: StyleVariable = { name: 'palette_g_100', - light: render_palette_stop_css('g', '100'), + light: render_palette_stop_css('g', '100') }; export const palette_h_00: StyleVariable = { name: 'palette_h_00', - light: render_palette_stop_css('h', '00'), + light: render_palette_stop_css('h', '00') }; export const palette_h_05: StyleVariable = { name: 'palette_h_05', - light: render_palette_stop_css('h', '05'), + light: render_palette_stop_css('h', '05') }; export const palette_h_10: StyleVariable = { name: 'palette_h_10', - light: render_palette_stop_css('h', '10'), + light: render_palette_stop_css('h', '10') }; export const palette_h_20: StyleVariable = { name: 'palette_h_20', - light: render_palette_stop_css('h', '20'), + light: render_palette_stop_css('h', '20') }; export const palette_h_30: StyleVariable = { name: 'palette_h_30', - light: render_palette_stop_css('h', '30'), + light: render_palette_stop_css('h', '30') }; export const palette_h_40: StyleVariable = { name: 'palette_h_40', - light: render_palette_stop_css('h', '40'), + light: render_palette_stop_css('h', '40') }; export const palette_h_50: StyleVariable = { name: 'palette_h_50', - light: render_palette_stop_css('h', '50'), + light: render_palette_stop_css('h', '50') }; export const palette_h_60: StyleVariable = { name: 'palette_h_60', - light: render_palette_stop_css('h', '60'), + light: render_palette_stop_css('h', '60') }; export const palette_h_70: StyleVariable = { name: 'palette_h_70', - light: render_palette_stop_css('h', '70'), + light: render_palette_stop_css('h', '70') }; export const palette_h_80: StyleVariable = { name: 'palette_h_80', - light: render_palette_stop_css('h', '80'), + light: render_palette_stop_css('h', '80') }; export const palette_h_90: StyleVariable = { name: 'palette_h_90', - light: render_palette_stop_css('h', '90'), + light: render_palette_stop_css('h', '90') }; export const palette_h_95: StyleVariable = { name: 'palette_h_95', - light: render_palette_stop_css('h', '95'), + light: render_palette_stop_css('h', '95') }; export const palette_h_100: StyleVariable = { name: 'palette_h_100', - light: render_palette_stop_css('h', '100'), + light: render_palette_stop_css('h', '100') }; export const palette_i_00: StyleVariable = { name: 'palette_i_00', - light: render_palette_stop_css('i', '00'), + light: render_palette_stop_css('i', '00') }; export const palette_i_05: StyleVariable = { name: 'palette_i_05', - light: render_palette_stop_css('i', '05'), + light: render_palette_stop_css('i', '05') }; export const palette_i_10: StyleVariable = { name: 'palette_i_10', - light: render_palette_stop_css('i', '10'), + light: render_palette_stop_css('i', '10') }; export const palette_i_20: StyleVariable = { name: 'palette_i_20', - light: render_palette_stop_css('i', '20'), + light: render_palette_stop_css('i', '20') }; export const palette_i_30: StyleVariable = { name: 'palette_i_30', - light: render_palette_stop_css('i', '30'), + light: render_palette_stop_css('i', '30') }; export const palette_i_40: StyleVariable = { name: 'palette_i_40', - light: render_palette_stop_css('i', '40'), + light: render_palette_stop_css('i', '40') }; export const palette_i_50: StyleVariable = { name: 'palette_i_50', - light: render_palette_stop_css('i', '50'), + light: render_palette_stop_css('i', '50') }; export const palette_i_60: StyleVariable = { name: 'palette_i_60', - light: render_palette_stop_css('i', '60'), + light: render_palette_stop_css('i', '60') }; export const palette_i_70: StyleVariable = { name: 'palette_i_70', - light: render_palette_stop_css('i', '70'), + light: render_palette_stop_css('i', '70') }; export const palette_i_80: StyleVariable = { name: 'palette_i_80', - light: render_palette_stop_css('i', '80'), + light: render_palette_stop_css('i', '80') }; export const palette_i_90: StyleVariable = { name: 'palette_i_90', - light: render_palette_stop_css('i', '90'), + light: render_palette_stop_css('i', '90') }; export const palette_i_95: StyleVariable = { name: 'palette_i_95', - light: render_palette_stop_css('i', '95'), + light: render_palette_stop_css('i', '95') }; export const palette_i_100: StyleVariable = { name: 'palette_i_100', - light: render_palette_stop_css('i', '100'), + light: render_palette_stop_css('i', '100') }; export const palette_j_00: StyleVariable = { name: 'palette_j_00', - light: render_palette_stop_css('j', '00'), + light: render_palette_stop_css('j', '00') }; export const palette_j_05: StyleVariable = { name: 'palette_j_05', - light: render_palette_stop_css('j', '05'), + light: render_palette_stop_css('j', '05') }; export const palette_j_10: StyleVariable = { name: 'palette_j_10', - light: render_palette_stop_css('j', '10'), + light: render_palette_stop_css('j', '10') }; export const palette_j_20: StyleVariable = { name: 'palette_j_20', - light: render_palette_stop_css('j', '20'), + light: render_palette_stop_css('j', '20') }; export const palette_j_30: StyleVariable = { name: 'palette_j_30', - light: render_palette_stop_css('j', '30'), + light: render_palette_stop_css('j', '30') }; export const palette_j_40: StyleVariable = { name: 'palette_j_40', - light: render_palette_stop_css('j', '40'), + light: render_palette_stop_css('j', '40') }; export const palette_j_50: StyleVariable = { name: 'palette_j_50', - light: render_palette_stop_css('j', '50'), + light: render_palette_stop_css('j', '50') }; export const palette_j_60: StyleVariable = { name: 'palette_j_60', - light: render_palette_stop_css('j', '60'), + light: render_palette_stop_css('j', '60') }; export const palette_j_70: StyleVariable = { name: 'palette_j_70', - light: render_palette_stop_css('j', '70'), + light: render_palette_stop_css('j', '70') }; export const palette_j_80: StyleVariable = { name: 'palette_j_80', - light: render_palette_stop_css('j', '80'), + light: render_palette_stop_css('j', '80') }; export const palette_j_90: StyleVariable = { name: 'palette_j_90', - light: render_palette_stop_css('j', '90'), + light: render_palette_stop_css('j', '90') }; export const palette_j_95: StyleVariable = { name: 'palette_j_95', - light: render_palette_stop_css('j', '95'), + light: render_palette_stop_css('j', '95') }; export const palette_j_100: StyleVariable = { name: 'palette_j_100', - light: render_palette_stop_css('j', '100'), + light: render_palette_stop_css('j', '100') }; /* @@ -1224,124 +1244,124 @@ reason to survive. export const shade_min: StyleVariable = { name: 'shade_min', light: '#fff', - dark: '#000', + dark: '#000' }; export const shade_max: StyleVariable = { name: 'shade_max', light: '#000', - dark: '#fff', + dark: '#fff' }; export const shade_lightness_00: StyleVariable = { name: 'shade_lightness_00', light: String(SHADE_LIGHTNESS_KNOBS.light.lightness_00), - dark: String(SHADE_LIGHTNESS_KNOBS.dark.lightness_00), + dark: String(SHADE_LIGHTNESS_KNOBS.dark.lightness_00) }; export const shade_lightness_100: StyleVariable = { name: 'shade_lightness_100', light: String(SHADE_LIGHTNESS_KNOBS.light.lightness_100), - dark: String(SHADE_LIGHTNESS_KNOBS.dark.lightness_100), + dark: String(SHADE_LIGHTNESS_KNOBS.dark.lightness_100) }; export const shade_lightness_curve: StyleVariable = { name: 'shade_lightness_curve', light: String(SHADE_LIGHTNESS_KNOBS.light.curve), - dark: String(SHADE_LIGHTNESS_KNOBS.dark.curve), + dark: String(SHADE_LIGHTNESS_KNOBS.dark.curve) }; export const shade_lightness_05: StyleVariable = { name: 'shade_lightness_05', - light: render_lightness_stop_css('shade', '05'), + light: render_lightness_stop_css('shade', '05') }; export const shade_lightness_10: StyleVariable = { name: 'shade_lightness_10', - light: render_lightness_stop_css('shade', '10'), + light: render_lightness_stop_css('shade', '10') }; export const shade_lightness_20: StyleVariable = { name: 'shade_lightness_20', - light: render_lightness_stop_css('shade', '20'), + light: render_lightness_stop_css('shade', '20') }; export const shade_lightness_30: StyleVariable = { name: 'shade_lightness_30', - light: render_lightness_stop_css('shade', '30'), + light: render_lightness_stop_css('shade', '30') }; export const shade_lightness_40: StyleVariable = { name: 'shade_lightness_40', - light: render_lightness_stop_css('shade', '40'), + light: render_lightness_stop_css('shade', '40') }; export const shade_lightness_50: StyleVariable = { name: 'shade_lightness_50', - light: render_lightness_stop_css('shade', '50'), + light: render_lightness_stop_css('shade', '50') }; export const shade_lightness_60: StyleVariable = { name: 'shade_lightness_60', - light: render_lightness_stop_css('shade', '60'), + light: render_lightness_stop_css('shade', '60') }; export const shade_lightness_70: StyleVariable = { name: 'shade_lightness_70', - light: render_lightness_stop_css('shade', '70'), + light: render_lightness_stop_css('shade', '70') }; export const shade_lightness_80: StyleVariable = { name: 'shade_lightness_80', - light: render_lightness_stop_css('shade', '80'), + light: render_lightness_stop_css('shade', '80') }; export const shade_lightness_90: StyleVariable = { name: 'shade_lightness_90', - light: render_lightness_stop_css('shade', '90'), + light: render_lightness_stop_css('shade', '90') }; export const shade_lightness_95: StyleVariable = { name: 'shade_lightness_95', - light: render_lightness_stop_css('shade', '95'), + light: render_lightness_stop_css('shade', '95') }; // Tinted shade scale (00-100) export const shade_00: StyleVariable = { name: 'shade_00', - light: render_neutral_stop_css('shade', '00'), + light: render_neutral_stop_css('shade', '00') }; export const shade_05: StyleVariable = { name: 'shade_05', - light: render_neutral_stop_css('shade', '05'), + light: render_neutral_stop_css('shade', '05') }; export const shade_10: StyleVariable = { name: 'shade_10', - light: render_neutral_stop_css('shade', '10'), + light: render_neutral_stop_css('shade', '10') }; export const shade_20: StyleVariable = { name: 'shade_20', - light: render_neutral_stop_css('shade', '20'), + light: render_neutral_stop_css('shade', '20') }; export const shade_30: StyleVariable = { name: 'shade_30', - light: render_neutral_stop_css('shade', '30'), + light: render_neutral_stop_css('shade', '30') }; export const shade_40: StyleVariable = { name: 'shade_40', - light: render_neutral_stop_css('shade', '40'), + light: render_neutral_stop_css('shade', '40') }; export const shade_50: StyleVariable = { name: 'shade_50', - light: render_neutral_stop_css('shade', '50'), + light: render_neutral_stop_css('shade', '50') }; export const shade_60: StyleVariable = { name: 'shade_60', - light: render_neutral_stop_css('shade', '60'), + light: render_neutral_stop_css('shade', '60') }; export const shade_70: StyleVariable = { name: 'shade_70', - light: render_neutral_stop_css('shade', '70'), + light: render_neutral_stop_css('shade', '70') }; export const shade_80: StyleVariable = { name: 'shade_80', - light: render_neutral_stop_css('shade', '80'), + light: render_neutral_stop_css('shade', '80') }; export const shade_90: StyleVariable = { name: 'shade_90', - light: render_neutral_stop_css('shade', '90'), + light: render_neutral_stop_css('shade', '90') }; export const shade_95: StyleVariable = { name: 'shade_95', - light: render_neutral_stop_css('shade', '95'), + light: render_neutral_stop_css('shade', '95') }; export const shade_100: StyleVariable = { name: 'shade_100', - light: render_neutral_stop_css('shade', '100'), + light: render_neutral_stop_css('shade', '100') }; /* @@ -1355,35 +1375,39 @@ consistent contrast. Unlike the adaptive shade scale, these don't flip. Perceptual curve: 3%, 6%, 12%, 21%, 32%, 45%, 65%, 80%, 89%, 96%, 98% */ -export const darken_00: StyleVariable = {name: 'darken_00', light: '#00000000', summary: '0%'}; -export const darken_05: StyleVariable = {name: 'darken_05', light: '#00000008', summary: '3%'}; -export const darken_10: StyleVariable = {name: 'darken_10', light: '#0000000f', summary: '6%'}; -export const darken_20: StyleVariable = {name: 'darken_20', light: '#0000001f', summary: '12%'}; -export const darken_30: StyleVariable = {name: 'darken_30', light: '#00000036', summary: '21%'}; -export const darken_40: StyleVariable = {name: 'darken_40', light: '#00000052', summary: '32%'}; -export const darken_50: StyleVariable = {name: 'darken_50', light: '#00000073', summary: '45%'}; -export const darken_60: StyleVariable = {name: 'darken_60', light: '#000000a6', summary: '65%'}; -export const darken_70: StyleVariable = {name: 'darken_70', light: '#000000cc', summary: '80%'}; -export const darken_80: StyleVariable = {name: 'darken_80', light: '#000000e3', summary: '89%'}; -export const darken_90: StyleVariable = {name: 'darken_90', light: '#000000f5', summary: '96%'}; -export const darken_95: StyleVariable = {name: 'darken_95', light: '#000000fa', summary: '98%'}; -export const darken_100: StyleVariable = {name: 'darken_100', light: '#000000ff', summary: '100%'}; -export const lighten_00: StyleVariable = {name: 'lighten_00', light: '#ffffff00', summary: '0%'}; -export const lighten_05: StyleVariable = {name: 'lighten_05', light: '#ffffff08', summary: '3%'}; -export const lighten_10: StyleVariable = {name: 'lighten_10', light: '#ffffff0f', summary: '6%'}; -export const lighten_20: StyleVariable = {name: 'lighten_20', light: '#ffffff1f', summary: '12%'}; -export const lighten_30: StyleVariable = {name: 'lighten_30', light: '#ffffff36', summary: '21%'}; -export const lighten_40: StyleVariable = {name: 'lighten_40', light: '#ffffff52', summary: '32%'}; -export const lighten_50: StyleVariable = {name: 'lighten_50', light: '#ffffff73', summary: '45%'}; -export const lighten_60: StyleVariable = {name: 'lighten_60', light: '#ffffffa6', summary: '65%'}; -export const lighten_70: StyleVariable = {name: 'lighten_70', light: '#ffffffcc', summary: '80%'}; -export const lighten_80: StyleVariable = {name: 'lighten_80', light: '#ffffffe3', summary: '89%'}; -export const lighten_90: StyleVariable = {name: 'lighten_90', light: '#fffffff5', summary: '96%'}; -export const lighten_95: StyleVariable = {name: 'lighten_95', light: '#fffffffa', summary: '98%'}; +export const darken_00: StyleVariable = { name: 'darken_00', light: '#00000000', summary: '0%' }; +export const darken_05: StyleVariable = { name: 'darken_05', light: '#00000008', summary: '3%' }; +export const darken_10: StyleVariable = { name: 'darken_10', light: '#0000000f', summary: '6%' }; +export const darken_20: StyleVariable = { name: 'darken_20', light: '#0000001f', summary: '12%' }; +export const darken_30: StyleVariable = { name: 'darken_30', light: '#00000036', summary: '21%' }; +export const darken_40: StyleVariable = { name: 'darken_40', light: '#00000052', summary: '32%' }; +export const darken_50: StyleVariable = { name: 'darken_50', light: '#00000073', summary: '45%' }; +export const darken_60: StyleVariable = { name: 'darken_60', light: '#000000a6', summary: '65%' }; +export const darken_70: StyleVariable = { name: 'darken_70', light: '#000000cc', summary: '80%' }; +export const darken_80: StyleVariable = { name: 'darken_80', light: '#000000e3', summary: '89%' }; +export const darken_90: StyleVariable = { name: 'darken_90', light: '#000000f5', summary: '96%' }; +export const darken_95: StyleVariable = { name: 'darken_95', light: '#000000fa', summary: '98%' }; +export const darken_100: StyleVariable = { + name: 'darken_100', + light: '#000000ff', + summary: '100%' +}; +export const lighten_00: StyleVariable = { name: 'lighten_00', light: '#ffffff00', summary: '0%' }; +export const lighten_05: StyleVariable = { name: 'lighten_05', light: '#ffffff08', summary: '3%' }; +export const lighten_10: StyleVariable = { name: 'lighten_10', light: '#ffffff0f', summary: '6%' }; +export const lighten_20: StyleVariable = { name: 'lighten_20', light: '#ffffff1f', summary: '12%' }; +export const lighten_30: StyleVariable = { name: 'lighten_30', light: '#ffffff36', summary: '21%' }; +export const lighten_40: StyleVariable = { name: 'lighten_40', light: '#ffffff52', summary: '32%' }; +export const lighten_50: StyleVariable = { name: 'lighten_50', light: '#ffffff73', summary: '45%' }; +export const lighten_60: StyleVariable = { name: 'lighten_60', light: '#ffffffa6', summary: '65%' }; +export const lighten_70: StyleVariable = { name: 'lighten_70', light: '#ffffffcc', summary: '80%' }; +export const lighten_80: StyleVariable = { name: 'lighten_80', light: '#ffffffe3', summary: '89%' }; +export const lighten_90: StyleVariable = { name: 'lighten_90', light: '#fffffff5', summary: '96%' }; +export const lighten_95: StyleVariable = { name: 'lighten_95', light: '#fffffffa', summary: '98%' }; export const lighten_100: StyleVariable = { name: 'lighten_100', light: '#ffffffff', - summary: '100%', + summary: '100%' }; /* @@ -1401,132 +1425,132 @@ These stack when nested (alpha accumulates), unlike the opaque shade scale. export const fg_00: StyleVariable = { name: 'fg_00', light: 'var(--darken_00)', - dark: 'var(--lighten_00)', + dark: 'var(--lighten_00)' }; export const fg_05: StyleVariable = { name: 'fg_05', light: 'var(--darken_05)', - dark: 'var(--lighten_05)', + dark: 'var(--lighten_05)' }; export const fg_10: StyleVariable = { name: 'fg_10', light: 'var(--darken_10)', - dark: 'var(--lighten_10)', + dark: 'var(--lighten_10)' }; export const fg_20: StyleVariable = { name: 'fg_20', light: 'var(--darken_20)', - dark: 'var(--lighten_20)', + dark: 'var(--lighten_20)' }; export const fg_30: StyleVariable = { name: 'fg_30', light: 'var(--darken_30)', - dark: 'var(--lighten_30)', + dark: 'var(--lighten_30)' }; export const fg_40: StyleVariable = { name: 'fg_40', light: 'var(--darken_40)', - dark: 'var(--lighten_40)', + dark: 'var(--lighten_40)' }; export const fg_50: StyleVariable = { name: 'fg_50', light: 'var(--darken_50)', - dark: 'var(--lighten_50)', + dark: 'var(--lighten_50)' }; export const fg_60: StyleVariable = { name: 'fg_60', light: 'var(--darken_60)', - dark: 'var(--lighten_60)', + dark: 'var(--lighten_60)' }; export const fg_70: StyleVariable = { name: 'fg_70', light: 'var(--darken_70)', - dark: 'var(--lighten_70)', + dark: 'var(--lighten_70)' }; export const fg_80: StyleVariable = { name: 'fg_80', light: 'var(--darken_80)', - dark: 'var(--lighten_80)', + dark: 'var(--lighten_80)' }; export const fg_90: StyleVariable = { name: 'fg_90', light: 'var(--darken_90)', - dark: 'var(--lighten_90)', + dark: 'var(--lighten_90)' }; export const fg_95: StyleVariable = { name: 'fg_95', light: 'var(--darken_95)', - dark: 'var(--lighten_95)', + dark: 'var(--lighten_95)' }; export const fg_100: StyleVariable = { name: 'fg_100', light: 'var(--darken_100)', - dark: 'var(--lighten_100)', + dark: 'var(--lighten_100)' }; export const bg_00: StyleVariable = { name: 'bg_00', light: 'var(--lighten_00)', - dark: 'var(--darken_00)', + dark: 'var(--darken_00)' }; export const bg_05: StyleVariable = { name: 'bg_05', light: 'var(--lighten_05)', - dark: 'var(--darken_05)', + dark: 'var(--darken_05)' }; export const bg_10: StyleVariable = { name: 'bg_10', light: 'var(--lighten_10)', - dark: 'var(--darken_10)', + dark: 'var(--darken_10)' }; export const bg_20: StyleVariable = { name: 'bg_20', light: 'var(--lighten_20)', - dark: 'var(--darken_20)', + dark: 'var(--darken_20)' }; export const bg_30: StyleVariable = { name: 'bg_30', light: 'var(--lighten_30)', - dark: 'var(--darken_30)', + dark: 'var(--darken_30)' }; export const bg_40: StyleVariable = { name: 'bg_40', light: 'var(--lighten_40)', - dark: 'var(--darken_40)', + dark: 'var(--darken_40)' }; export const bg_50: StyleVariable = { name: 'bg_50', light: 'var(--lighten_50)', - dark: 'var(--darken_50)', + dark: 'var(--darken_50)' }; export const bg_60: StyleVariable = { name: 'bg_60', light: 'var(--lighten_60)', - dark: 'var(--darken_60)', + dark: 'var(--darken_60)' }; export const bg_70: StyleVariable = { name: 'bg_70', light: 'var(--lighten_70)', - dark: 'var(--darken_70)', + dark: 'var(--darken_70)' }; export const bg_80: StyleVariable = { name: 'bg_80', light: 'var(--lighten_80)', - dark: 'var(--darken_80)', + dark: 'var(--darken_80)' }; export const bg_90: StyleVariable = { name: 'bg_90', light: 'var(--lighten_90)', - dark: 'var(--darken_90)', + dark: 'var(--darken_90)' }; export const bg_95: StyleVariable = { name: 'bg_95', light: 'var(--lighten_95)', - dark: 'var(--darken_95)', + dark: 'var(--darken_95)' }; export const bg_100: StyleVariable = { name: 'bg_100', light: 'var(--lighten_100)', - dark: 'var(--darken_100)', + dark: 'var(--darken_100)' }; /* @@ -1540,7 +1564,7 @@ Higher alpha in dark mode compensates for lower perceived contrast. */ export const border_color_00: StyleVariable = { name: 'border_color_00', - light: 'transparent', + light: 'transparent' }; // Perceptually-uniform alpha curve for borders (matches shadow_alpha curve) // - Small gaps at low end, large gaps at high end @@ -1548,62 +1572,62 @@ export const border_color_00: StyleVariable = { export const border_color_05: StyleVariable = { name: 'border_color_05', light: 'oklch(0.345 0.064 var(--hue_neutral) / 4%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 8%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 8%)' }; export const border_color_10: StyleVariable = { name: 'border_color_10', light: 'oklch(0.345 0.064 var(--hue_neutral) / 7%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 14%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 14%)' }; export const border_color_20: StyleVariable = { name: 'border_color_20', light: 'oklch(0.345 0.064 var(--hue_neutral) / 13%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 22%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 22%)' }; export const border_color_30: StyleVariable = { name: 'border_color_30', light: 'oklch(0.345 0.064 var(--hue_neutral) / 22%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 32%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 32%)' }; export const border_color_40: StyleVariable = { name: 'border_color_40', light: 'oklch(0.345 0.064 var(--hue_neutral) / 34%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 44%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 44%)' }; export const border_color_50: StyleVariable = { name: 'border_color_50', light: 'oklch(0.345 0.064 var(--hue_neutral) / 48%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 56%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 56%)' }; export const border_color_60: StyleVariable = { name: 'border_color_60', light: 'oklch(0.345 0.064 var(--hue_neutral) / 62%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 68%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 68%)' }; export const border_color_70: StyleVariable = { name: 'border_color_70', light: 'oklch(0.345 0.064 var(--hue_neutral) / 76%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 80%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 80%)' }; export const border_color_80: StyleVariable = { name: 'border_color_80', light: 'oklch(0.345 0.064 var(--hue_neutral) / 88%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 90%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 90%)' }; export const border_color_90: StyleVariable = { name: 'border_color_90', light: 'oklch(0.345 0.064 var(--hue_neutral) / 96%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 97%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 97%)' }; export const border_color_95: StyleVariable = { name: 'border_color_95', light: 'oklch(0.345 0.064 var(--hue_neutral) / 99%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 99%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 99%)' }; export const border_color_100: StyleVariable = { name: 'border_color_100', light: 'oklch(0.345 0.064 var(--hue_neutral))', - dark: 'oklch(0.857 0.053 var(--hue_neutral))', + dark: 'oklch(0.857 0.053 var(--hue_neutral))' }; /* @@ -1619,129 +1643,129 @@ Derived from the text lightness knobs (see ramps.ts). export const text_min: StyleVariable = { name: 'text_min', light: '#fff', - dark: '#000', + dark: '#000' }; export const text_max: StyleVariable = { name: 'text_max', light: '#000', - dark: '#fff', + dark: '#fff' }; -export const text_color: StyleVariable = {name: 'text_color', light: 'var(--text_80)'}; +export const text_color: StyleVariable = { name: 'text_color', light: 'var(--text_80)' }; export const text_lightness_00: StyleVariable = { name: 'text_lightness_00', light: String(TEXT_LIGHTNESS_KNOBS.light.lightness_00), - dark: String(TEXT_LIGHTNESS_KNOBS.dark.lightness_00), + dark: String(TEXT_LIGHTNESS_KNOBS.dark.lightness_00) }; export const text_lightness_100: StyleVariable = { name: 'text_lightness_100', light: String(TEXT_LIGHTNESS_KNOBS.light.lightness_100), - dark: String(TEXT_LIGHTNESS_KNOBS.dark.lightness_100), + dark: String(TEXT_LIGHTNESS_KNOBS.dark.lightness_100) }; export const text_lightness_curve: StyleVariable = { name: 'text_lightness_curve', light: String(TEXT_LIGHTNESS_KNOBS.light.curve), - dark: String(TEXT_LIGHTNESS_KNOBS.dark.curve), + dark: String(TEXT_LIGHTNESS_KNOBS.dark.curve) }; export const text_lightness_05: StyleVariable = { name: 'text_lightness_05', - light: render_lightness_stop_css('text', '05'), + light: render_lightness_stop_css('text', '05') }; export const text_lightness_10: StyleVariable = { name: 'text_lightness_10', - light: render_lightness_stop_css('text', '10'), + light: render_lightness_stop_css('text', '10') }; export const text_lightness_20: StyleVariable = { name: 'text_lightness_20', - light: render_lightness_stop_css('text', '20'), + light: render_lightness_stop_css('text', '20') }; export const text_lightness_30: StyleVariable = { name: 'text_lightness_30', - light: render_lightness_stop_css('text', '30'), + light: render_lightness_stop_css('text', '30') }; export const text_lightness_40: StyleVariable = { name: 'text_lightness_40', - light: render_lightness_stop_css('text', '40'), + light: render_lightness_stop_css('text', '40') }; export const text_lightness_50: StyleVariable = { name: 'text_lightness_50', - light: render_lightness_stop_css('text', '50'), + light: render_lightness_stop_css('text', '50') }; export const text_lightness_60: StyleVariable = { name: 'text_lightness_60', - light: render_lightness_stop_css('text', '60'), + light: render_lightness_stop_css('text', '60') }; export const text_lightness_70: StyleVariable = { name: 'text_lightness_70', - light: render_lightness_stop_css('text', '70'), + light: render_lightness_stop_css('text', '70') }; export const text_lightness_80: StyleVariable = { name: 'text_lightness_80', - light: render_lightness_stop_css('text', '80'), + light: render_lightness_stop_css('text', '80') }; export const text_lightness_90: StyleVariable = { name: 'text_lightness_90', - light: render_lightness_stop_css('text', '90'), + light: render_lightness_stop_css('text', '90') }; export const text_lightness_95: StyleVariable = { name: 'text_lightness_95', - light: render_lightness_stop_css('text', '95'), + light: render_lightness_stop_css('text', '95') }; export const text_00: StyleVariable = { name: 'text_00', - light: render_neutral_stop_css('text', '00'), + light: render_neutral_stop_css('text', '00') }; export const text_05: StyleVariable = { name: 'text_05', - light: render_neutral_stop_css('text', '05'), + light: render_neutral_stop_css('text', '05') }; export const text_10: StyleVariable = { name: 'text_10', - light: render_neutral_stop_css('text', '10'), + light: render_neutral_stop_css('text', '10') }; export const text_20: StyleVariable = { name: 'text_20', - light: render_neutral_stop_css('text', '20'), + light: render_neutral_stop_css('text', '20') }; export const text_30: StyleVariable = { name: 'text_30', - light: render_neutral_stop_css('text', '30'), + light: render_neutral_stop_css('text', '30') }; export const text_40: StyleVariable = { name: 'text_40', - light: render_neutral_stop_css('text', '40'), + light: render_neutral_stop_css('text', '40') }; export const text_50: StyleVariable = { name: 'text_50', - light: render_neutral_stop_css('text', '50'), + light: render_neutral_stop_css('text', '50') }; export const text_60: StyleVariable = { name: 'text_60', - light: render_neutral_stop_css('text', '60'), + light: render_neutral_stop_css('text', '60') }; export const text_70: StyleVariable = { name: 'text_70', - light: render_neutral_stop_css('text', '70'), + light: render_neutral_stop_css('text', '70') }; export const text_80: StyleVariable = { name: 'text_80', - light: render_neutral_stop_css('text', '80'), + light: render_neutral_stop_css('text', '80') }; export const text_90: StyleVariable = { name: 'text_90', - light: render_neutral_stop_css('text', '90'), + light: render_neutral_stop_css('text', '90') }; export const text_95: StyleVariable = { name: 'text_95', - light: render_neutral_stop_css('text', '95'), + light: render_neutral_stop_css('text', '95') }; export const text_100: StyleVariable = { name: 'text_100', - light: render_neutral_stop_css('text', '100'), + light: render_neutral_stop_css('text', '100') }; export const text_disabled: StyleVariable = { name: 'text_disabled', - light: 'var(--text_50)', + light: 'var(--text_50)' }; /* decoration */ @@ -1749,227 +1773,227 @@ export const text_disabled: StyleVariable = { // textures - the minimal decoration channel themes kept hitting walls on export const background_image: StyleVariable = { name: 'background_image', - light: 'none', + light: 'none' }; /* fonts */ export const font_family_sans: StyleVariable = { name: 'font_family_sans', light: - "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'", + "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'" }; export const font_family_serif: StyleVariable = { name: 'font_family_serif', - light: 'Georgia, serif', + light: 'Georgia, serif' }; export const font_family_mono: StyleVariable = { name: 'font_family_mono', - light: 'ui-monospace, monospace', + light: 'ui-monospace, monospace' }; export const font_weight: StyleVariable = { name: 'font_weight', light: '400', - summary: 'base body font weight', + summary: 'base body font weight' }; export const heading_font_family: StyleVariable = { name: 'heading_font_family', - light: 'var(--font_family_serif)', + light: 'var(--font_family_serif)' }; // `--heading_font_weight` is a hook consumed by `style.css` with per-tier // fallbacks (h1 300 … h5 900), not a declared variable — setting it flattens // the heading weight ladder deliberately (display-heavy themes); see `knobs.ts` /* sizes like font-size */ -export const font_size_xs: StyleVariable = {name: 'font_size_xs', light: '1rem'}; -export const font_size_sm: StyleVariable = {name: 'font_size_sm', light: '1.3rem'}; -export const font_size_md: StyleVariable = {name: 'font_size_md', light: '1.6rem'}; -export const font_size_lg: StyleVariable = {name: 'font_size_lg', light: '2.04rem'}; -export const font_size_xl: StyleVariable = {name: 'font_size_xl', light: '2.59rem'}; -export const font_size_xl2: StyleVariable = {name: 'font_size_xl2', light: '3.29rem'}; -export const font_size_xl3: StyleVariable = {name: 'font_size_xl3', light: '4.19rem'}; -export const font_size_xl4: StyleVariable = {name: 'font_size_xl4', light: '5.33rem'}; -export const font_size_xl5: StyleVariable = {name: 'font_size_xl5', light: '6.78rem'}; -export const font_size_xl6: StyleVariable = {name: 'font_size_xl6', light: '8.62rem'}; -export const font_size_xl7: StyleVariable = {name: 'font_size_xl7', light: '10.97rem'}; -export const font_size_xl8: StyleVariable = {name: 'font_size_xl8', light: '13.95rem'}; -export const font_size_xl9: StyleVariable = {name: 'font_size_xl9', light: '17.74rem'}; - -export const line_height_xs: StyleVariable = {name: 'line_height_xs', light: '1'}; -export const line_height_sm: StyleVariable = {name: 'line_height_sm', light: '1.2'}; -export const line_height_md: StyleVariable = {name: 'line_height_md', light: '1.5'}; -export const line_height_lg: StyleVariable = {name: 'line_height_lg', light: '1.8'}; -export const line_height_xl: StyleVariable = {name: 'line_height_xl', light: '2.2'}; +export const font_size_xs: StyleVariable = { name: 'font_size_xs', light: '1rem' }; +export const font_size_sm: StyleVariable = { name: 'font_size_sm', light: '1.3rem' }; +export const font_size_md: StyleVariable = { name: 'font_size_md', light: '1.6rem' }; +export const font_size_lg: StyleVariable = { name: 'font_size_lg', light: '2.04rem' }; +export const font_size_xl: StyleVariable = { name: 'font_size_xl', light: '2.59rem' }; +export const font_size_xl2: StyleVariable = { name: 'font_size_xl2', light: '3.29rem' }; +export const font_size_xl3: StyleVariable = { name: 'font_size_xl3', light: '4.19rem' }; +export const font_size_xl4: StyleVariable = { name: 'font_size_xl4', light: '5.33rem' }; +export const font_size_xl5: StyleVariable = { name: 'font_size_xl5', light: '6.78rem' }; +export const font_size_xl6: StyleVariable = { name: 'font_size_xl6', light: '8.62rem' }; +export const font_size_xl7: StyleVariable = { name: 'font_size_xl7', light: '10.97rem' }; +export const font_size_xl8: StyleVariable = { name: 'font_size_xl8', light: '13.95rem' }; +export const font_size_xl9: StyleVariable = { name: 'font_size_xl9', light: '17.74rem' }; + +export const line_height_xs: StyleVariable = { name: 'line_height_xs', light: '1' }; +export const line_height_sm: StyleVariable = { name: 'line_height_sm', light: '1.2' }; +export const line_height_md: StyleVariable = { name: 'line_height_md', light: '1.5' }; +export const line_height_lg: StyleVariable = { name: 'line_height_lg', light: '1.8' }; +export const line_height_xl: StyleVariable = { name: 'line_height_xl', light: '2.2' }; /* links */ export const link_color: StyleVariable = { name: 'link_color', - light: 'var(--accent_60)', + light: 'var(--accent_60)' }; -export const text_decoration: StyleVariable = {name: 'text_decoration', light: 'none'}; +export const text_decoration: StyleVariable = { name: 'text_decoration', light: 'none' }; export const text_decoration_hover: StyleVariable = { name: 'text_decoration_hover', - light: 'underline', + light: 'underline' }; export const text_decoration_selected: StyleVariable = { name: 'text_decoration_selected', - light: 'underline', + light: 'underline' }; export const link_color_selected: StyleVariable = { name: 'link_color_selected', - light: 'var(--text_color)', + light: 'var(--text_color)' }; // ports the old bespoke selection lightness in both schemes (light stop 20, // dark stop 80) while following the accent intent and every ramp knob export const selection_color: StyleVariable = { name: 'selection_color', light: render_ramp_color_css('var(--hue_accent)', '20', '40%'), - dark: render_ramp_color_css('var(--hue_accent)', '80', '40%'), + dark: render_ramp_color_css('var(--hue_accent)', '80', '40%') }; /* spacings, rounded to pixels for the default 16px case (at the default scale factor of 1) */ export const scale_factor: StyleVariable = { name: 'scale_factor', light: '1', - summary: 'multiplies the space scale, below 1 is tighter and above 1 is more spacious', + summary: 'multiplies the space scale, below 1 is tighter and above 1 is more spacious' }; export const space_xs5: StyleVariable = { name: 'space_xs5', - light: 'calc(0.1rem * var(--scale_factor))', + light: 'calc(0.1rem * var(--scale_factor))' }; export const space_xs4: StyleVariable = { name: 'space_xs4', - light: 'calc(0.2rem * var(--scale_factor))', + light: 'calc(0.2rem * var(--scale_factor))' }; export const space_xs3: StyleVariable = { name: 'space_xs3', - light: 'calc(0.3rem * var(--scale_factor))', + light: 'calc(0.3rem * var(--scale_factor))' }; export const space_xs2: StyleVariable = { name: 'space_xs2', - light: 'calc(0.4rem * var(--scale_factor))', + light: 'calc(0.4rem * var(--scale_factor))' }; export const space_xs: StyleVariable = { name: 'space_xs', - light: 'calc(0.6rem * var(--scale_factor))', + light: 'calc(0.6rem * var(--scale_factor))' }; export const space_sm: StyleVariable = { name: 'space_sm', - light: 'calc(0.8rem * var(--scale_factor))', + light: 'calc(0.8rem * var(--scale_factor))' }; export const space_md: StyleVariable = { name: 'space_md', - light: 'calc(1rem * var(--scale_factor))', + light: 'calc(1rem * var(--scale_factor))' }; export const space_lg: StyleVariable = { name: 'space_lg', - light: 'calc(1.3rem * var(--scale_factor))', + light: 'calc(1.3rem * var(--scale_factor))' }; export const space_xl: StyleVariable = { name: 'space_xl', - light: 'calc(1.6rem * var(--scale_factor))', + light: 'calc(1.6rem * var(--scale_factor))' }; export const space_xl2: StyleVariable = { name: 'space_xl2', - light: 'calc(2.1rem * var(--scale_factor))', + light: 'calc(2.1rem * var(--scale_factor))' }; export const space_xl3: StyleVariable = { name: 'space_xl3', - light: 'calc(2.6rem * var(--scale_factor))', + light: 'calc(2.6rem * var(--scale_factor))' }; export const space_xl4: StyleVariable = { name: 'space_xl4', - light: 'calc(3.3rem * var(--scale_factor))', + light: 'calc(3.3rem * var(--scale_factor))' }; export const space_xl5: StyleVariable = { name: 'space_xl5', - light: 'calc(4.2rem * var(--scale_factor))', + light: 'calc(4.2rem * var(--scale_factor))' }; export const space_xl6: StyleVariable = { name: 'space_xl6', - light: 'calc(5.4rem * var(--scale_factor))', + light: 'calc(5.4rem * var(--scale_factor))' }; export const space_xl7: StyleVariable = { name: 'space_xl7', - light: 'calc(6.9rem * var(--scale_factor))', + light: 'calc(6.9rem * var(--scale_factor))' }; export const space_xl8: StyleVariable = { name: 'space_xl8', - light: 'calc(8.7rem * var(--scale_factor))', + light: 'calc(8.7rem * var(--scale_factor))' }; export const space_xl9: StyleVariable = { name: 'space_xl9', - light: 'calc(11.1rem * var(--scale_factor))', + light: 'calc(11.1rem * var(--scale_factor))' }; export const space_xl10: StyleVariable = { name: 'space_xl10', - light: 'calc(14.1rem * var(--scale_factor))', + light: 'calc(14.1rem * var(--scale_factor))' }; export const space_xl11: StyleVariable = { name: 'space_xl11', - light: 'calc(17.9rem * var(--scale_factor))', + light: 'calc(17.9rem * var(--scale_factor))' }; export const space_xl12: StyleVariable = { name: 'space_xl12', - light: 'calc(22.8rem * var(--scale_factor))', + light: 'calc(22.8rem * var(--scale_factor))' }; export const space_xl13: StyleVariable = { name: 'space_xl13', - light: 'calc(29rem * var(--scale_factor))', + light: 'calc(29rem * var(--scale_factor))' }; export const space_xl14: StyleVariable = { name: 'space_xl14', - light: 'calc(36.9rem * var(--scale_factor))', + light: 'calc(36.9rem * var(--scale_factor))' }; export const space_xl15: StyleVariable = { name: 'space_xl15', - light: 'calc(47rem * var(--scale_factor))', + light: 'calc(47rem * var(--scale_factor))' }; -export const distance_xs: StyleVariable = {name: 'distance_xs', light: '200px'}; -export const distance_sm: StyleVariable = {name: 'distance_sm', light: '320px'}; -export const distance_md: StyleVariable = {name: 'distance_md', light: '800px'}; -export const distance_lg: StyleVariable = {name: 'distance_lg', light: '1200px'}; -export const distance_xl: StyleVariable = {name: 'distance_xl', light: '1600px'}; +export const distance_xs: StyleVariable = { name: 'distance_xs', light: '200px' }; +export const distance_sm: StyleVariable = { name: 'distance_sm', light: '320px' }; +export const distance_md: StyleVariable = { name: 'distance_md', light: '800px' }; +export const distance_lg: StyleVariable = { name: 'distance_lg', light: '1200px' }; +export const distance_xl: StyleVariable = { name: 'distance_xl', light: '1600px' }; /* borders and outlines */ export const border_color: StyleVariable = { name: 'border_color', - light: 'var(--shade_30)', + light: 'var(--shade_30)' }; export const border_style: StyleVariable = { name: 'border_style', - light: 'solid', + light: 'solid' }; -export const border_width: StyleVariable = {name: 'border_width', light: 'var(--border_width_1)'}; +export const border_width: StyleVariable = { name: 'border_width', light: 'var(--border_width_1)' }; // These use numbers instead of named size variants because // they more directly map to how I think about border widths. // But maye this could be expanded/rethought. -export const border_width_1: StyleVariable = {name: 'border_width_1', light: '1px'}; -export const border_width_2: StyleVariable = {name: 'border_width_2', light: '2px'}; -export const border_width_3: StyleVariable = {name: 'border_width_3', light: '3px'}; -export const border_width_4: StyleVariable = {name: 'border_width_4', light: '4px'}; -export const border_width_5: StyleVariable = {name: 'border_width_5', light: '5px'}; -export const border_width_6: StyleVariable = {name: 'border_width_6', light: '6px'}; -export const border_width_7: StyleVariable = {name: 'border_width_7', light: '7px'}; -export const border_width_8: StyleVariable = {name: 'border_width_8', light: '8px'}; -export const border_width_9: StyleVariable = {name: 'border_width_9', light: '9px'}; +export const border_width_1: StyleVariable = { name: 'border_width_1', light: '1px' }; +export const border_width_2: StyleVariable = { name: 'border_width_2', light: '2px' }; +export const border_width_3: StyleVariable = { name: 'border_width_3', light: '3px' }; +export const border_width_4: StyleVariable = { name: 'border_width_4', light: '4px' }; +export const border_width_5: StyleVariable = { name: 'border_width_5', light: '5px' }; +export const border_width_6: StyleVariable = { name: 'border_width_6', light: '6px' }; +export const border_width_7: StyleVariable = { name: 'border_width_7', light: '7px' }; +export const border_width_8: StyleVariable = { name: 'border_width_8', light: '8px' }; +export const border_width_9: StyleVariable = { name: 'border_width_9', light: '9px' }; export const outline_width: StyleVariable = { name: 'outline_width', - light: '0', + light: '0' }; export const outline_width_focus: StyleVariable = { // TODO maybe rename _2 to `focus` name: 'outline_width_focus', - light: 'var(--border_width_2)', + light: 'var(--border_width_2)' }; export const outline_width_active: StyleVariable = { // TODO maybe rename _3 to `active` name: 'outline_width_active', - light: 'var(--border_width_1)', + light: 'var(--border_width_1)' }; -export const outline_style: StyleVariable = {name: 'outline_style', light: 'solid'}; +export const outline_style: StyleVariable = { name: 'outline_style', light: 'solid' }; export const outline_color: StyleVariable = { name: 'outline_color', - light: 'var(--accent_50)', + light: 'var(--accent_50)' }; /* border radii - the tokens multiply a per-tier base by the radius scale, so @@ -1978,35 +2002,35 @@ export const outline_color: StyleVariable = { export const radius_scale: StyleVariable = { name: 'radius_scale', light: '1', - summary: '0 is sharp, below 1 is squarer, above 1 is rounder', + summary: '0 is sharp, below 1 is squarer, above 1 is rounder' }; export const border_radius_xs3: StyleVariable = { name: 'border_radius_xs3', - light: 'calc(0.3rem * var(--radius_scale))', + light: 'calc(0.3rem * var(--radius_scale))' }; export const border_radius_xs2: StyleVariable = { name: 'border_radius_xs2', - light: 'calc(0.5rem * var(--radius_scale))', + light: 'calc(0.5rem * var(--radius_scale))' }; export const border_radius_xs: StyleVariable = { name: 'border_radius_xs', - light: 'calc(0.8rem * var(--radius_scale))', + light: 'calc(0.8rem * var(--radius_scale))' }; export const border_radius_sm: StyleVariable = { name: 'border_radius_sm', - light: 'calc(1.3rem * var(--radius_scale))', + light: 'calc(1.3rem * var(--radius_scale))' }; export const border_radius_md: StyleVariable = { name: 'border_radius_md', - light: 'calc(2.1rem * var(--radius_scale))', + light: 'calc(2.1rem * var(--radius_scale))' }; export const border_radius_lg: StyleVariable = { name: 'border_radius_lg', - light: 'calc(3.4rem * var(--radius_scale))', + light: 'calc(3.4rem * var(--radius_scale))' }; export const border_radius_xl: StyleVariable = { name: 'border_radius_xl', - light: 'calc(5.5rem * var(--radius_scale))', + light: 'calc(5.5rem * var(--radius_scale))' }; /* button shadows */ @@ -2014,29 +2038,29 @@ export const button_shadow: StyleVariable = { name: 'button_shadow', light: 'var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_30), transparent), var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_30), transparent)', - dark: 'var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_30), transparent), var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_30), transparent)', + dark: 'var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_30), transparent), var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_30), transparent)' }; export const button_shadow_hover: StyleVariable = { name: 'button_shadow_hover', light: 'var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_40), transparent), var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_40), transparent)', - dark: 'var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_40), transparent), var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_40), transparent)', + dark: 'var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_40), transparent), var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_40), transparent)' }; export const button_shadow_active: StyleVariable = { name: 'button_shadow_active', light: button_shadow_hover.dark, - dark: button_shadow_hover.light, + dark: button_shadow_hover.light }; /* inputs */ -export const input_fill: StyleVariable = {name: 'input_fill', light: 'var(--bg_80)'}; -export const input_padding_y: StyleVariable = {name: 'input_padding_y', light: '0'}; -export const input_padding_x: StyleVariable = {name: 'input_padding_x', light: 'var(--space_lg)'}; -export const input_width_min: StyleVariable = {name: 'input_width_min', light: '100px'}; -export const input_height: StyleVariable = {name: 'input_height', light: 'var(--space_xl5)'}; +export const input_fill: StyleVariable = { name: 'input_fill', light: 'var(--bg_80)' }; +export const input_padding_y: StyleVariable = { name: 'input_padding_y', light: '0' }; +export const input_padding_x: StyleVariable = { name: 'input_padding_x', light: 'var(--space_lg)' }; +export const input_width_min: StyleVariable = { name: 'input_width_min', light: '100px' }; +export const input_height: StyleVariable = { name: 'input_height', light: 'var(--space_xl5)' }; export const input_height_compact: StyleVariable = { name: 'input_height_compact', - light: 'var(--space_xl4)', + light: 'var(--space_xl4)' }; /* @@ -2056,142 +2080,142 @@ shadows export const shadow_xs: StyleVariable = { name: 'shadow_xs', - light: '0 0 3px 0px', + light: '0 0 3px 0px' }; export const shadow_top_xs: StyleVariable = { name: 'shadow_top_xs', - light: '0 -1px 3px 0px', + light: '0 -1px 3px 0px' }; export const shadow_bottom_xs: StyleVariable = { name: 'shadow_bottom_xs', - light: '0 1px 3px 0px', + light: '0 1px 3px 0px' }; export const shadow_inset_xs: StyleVariable = { name: 'shadow_inset_xs', - light: 'inset 0 0 3px 0px', + light: 'inset 0 0 3px 0px' }; export const shadow_inset_top_xs: StyleVariable = { name: 'shadow_inset_top_xs', - light: 'inset 0 1px 3px 0px', + light: 'inset 0 1px 3px 0px' }; export const shadow_inset_bottom_xs: StyleVariable = { name: 'shadow_inset_bottom_xs', - light: 'inset 0 -1px 3px 0px', + light: 'inset 0 -1px 3px 0px' }; export const shadow_sm: StyleVariable = { name: 'shadow_sm', - light: '0 0 4px 0px', + light: '0 0 4px 0px' }; export const shadow_top_sm: StyleVariable = { name: 'shadow_top_sm', - light: '0 -1.5px 4px 0px', + light: '0 -1.5px 4px 0px' }; export const shadow_bottom_sm: StyleVariable = { name: 'shadow_bottom_sm', - light: '0 1.5px 4px 0px', + light: '0 1.5px 4px 0px' }; export const shadow_inset_sm: StyleVariable = { name: 'shadow_inset_sm', - light: 'inset 0 0 4px 0px', + light: 'inset 0 0 4px 0px' }; export const shadow_inset_top_sm: StyleVariable = { name: 'shadow_inset_top_sm', - light: 'inset 0 1.5px 4px 0px', + light: 'inset 0 1.5px 4px 0px' }; export const shadow_inset_bottom_sm: StyleVariable = { name: 'shadow_inset_bottom_sm', - light: 'inset 0 -1.5px 4px 0px', + light: 'inset 0 -1.5px 4px 0px' }; export const shadow_md: StyleVariable = { name: 'shadow_md', - light: '0 0 6px 0px', + light: '0 0 6px 0px' }; export const shadow_top_md: StyleVariable = { name: 'shadow_top_md', - light: '0 -2.5px 6px 0px', + light: '0 -2.5px 6px 0px' }; export const shadow_bottom_md: StyleVariable = { name: 'shadow_bottom_md', - light: '0 2.5px 6px 0px', + light: '0 2.5px 6px 0px' }; export const shadow_inset_md: StyleVariable = { name: 'shadow_inset_md', - light: 'inset 0 0 6px 0px', + light: 'inset 0 0 6px 0px' }; export const shadow_inset_top_md: StyleVariable = { name: 'shadow_inset_top_md', - light: 'inset 0 2.5px 6px 0px', + light: 'inset 0 2.5px 6px 0px' }; export const shadow_inset_bottom_md: StyleVariable = { name: 'shadow_inset_bottom_md', - light: 'inset 0 -2.5px 6px 0px', + light: 'inset 0 -2.5px 6px 0px' }; export const shadow_lg: StyleVariable = { name: 'shadow_lg', - light: '0 0 10px 0px', + light: '0 0 10px 0px' }; export const shadow_top_lg: StyleVariable = { name: 'shadow_top_lg', - light: '0 -3.5px 10px 0px', + light: '0 -3.5px 10px 0px' }; export const shadow_bottom_lg: StyleVariable = { name: 'shadow_bottom_lg', - light: '0 3.5px 10px 0px', + light: '0 3.5px 10px 0px' }; export const shadow_inset_lg: StyleVariable = { name: 'shadow_inset_lg', - light: 'inset 0 0 10px 0px', + light: 'inset 0 0 10px 0px' }; export const shadow_inset_top_lg: StyleVariable = { name: 'shadow_inset_top_lg', - light: 'inset 0 3.5px 10px 0px', + light: 'inset 0 3.5px 10px 0px' }; export const shadow_inset_bottom_lg: StyleVariable = { name: 'shadow_inset_bottom_lg', - light: 'inset 0 -3.5px 10px 0px', + light: 'inset 0 -3.5px 10px 0px' }; export const shadow_xl: StyleVariable = { name: 'shadow_xl', - light: '0 0 20px 1px', + light: '0 0 20px 1px' }; export const shadow_top_xl: StyleVariable = { name: 'shadow_top_xl', - light: '0 -5px 20px 1px', + light: '0 -5px 20px 1px' }; export const shadow_bottom_xl: StyleVariable = { name: 'shadow_bottom_xl', - light: '0 5px 20px 1px', + light: '0 5px 20px 1px' }; export const shadow_inset_xl: StyleVariable = { name: 'shadow_inset_xl', - light: 'inset 0 0 20px 1px', + light: 'inset 0 0 20px 1px' }; export const shadow_inset_top_xl: StyleVariable = { name: 'shadow_inset_top_xl', - light: 'inset 0 5px 20px 1px', + light: 'inset 0 5px 20px 1px' }; export const shadow_inset_bottom_xl: StyleVariable = { name: 'shadow_inset_bottom_xl', - light: 'inset 0 -5px 20px 1px', + light: 'inset 0 -5px 20px 1px' }; export const shadow_color_umbra: StyleVariable = { name: 'shadow_color_umbra', light: '#000', - dark: 'oklch(0.863 0.009 var(--hue_neutral))', + dark: 'oklch(0.863 0.009 var(--hue_neutral))' }; export const shadow_color_highlight: StyleVariable = { name: 'shadow_color_highlight', light: 'oklch(0.955 0.003 var(--hue_neutral))', - dark: '#000', + dark: '#000' }; export const shadow_color_glow: StyleVariable = { name: 'shadow_color_glow', light: 'oklch(0.955 0.003 var(--hue_neutral))', - dark: 'oklch(0.863 0.009 var(--hue_neutral))', + dark: 'oklch(0.863 0.009 var(--hue_neutral))' }; export const shadow_color_shroud: StyleVariable = { name: 'shadow_color_shroud', - light: '#000', + light: '#000' }; // Perceptually-uniform alpha curve for shadows: // - Small gaps at low end (subtle changes are perceptible) @@ -2203,67 +2227,67 @@ export const shadow_color_shroud: StyleVariable = { export const shadow_alpha_scale: StyleVariable = { name: 'shadow_alpha_scale', light: '1', - summary: '0 flattens all shadows including button shadows, above 1 deepens them', + summary: '0 flattens all shadows including button shadows, above 1 deepens them' }; -export const shadow_alpha_00: StyleVariable = {name: 'shadow_alpha_00', light: '0%'}; +export const shadow_alpha_00: StyleVariable = { name: 'shadow_alpha_00', light: '0%' }; export const shadow_alpha_05: StyleVariable = { name: 'shadow_alpha_05', light: 'calc(6% * var(--shadow_alpha_scale))', - dark: 'calc(13% * var(--shadow_alpha_scale))', + dark: 'calc(13% * var(--shadow_alpha_scale))' }; export const shadow_alpha_10: StyleVariable = { name: 'shadow_alpha_10', light: 'calc(10% * var(--shadow_alpha_scale))', - dark: 'calc(19% * var(--shadow_alpha_scale))', + dark: 'calc(19% * var(--shadow_alpha_scale))' }; export const shadow_alpha_20: StyleVariable = { name: 'shadow_alpha_20', light: 'calc(16% * var(--shadow_alpha_scale))', - dark: 'calc(27% * var(--shadow_alpha_scale))', + dark: 'calc(27% * var(--shadow_alpha_scale))' }; export const shadow_alpha_30: StyleVariable = { name: 'shadow_alpha_30', light: 'calc(25% * var(--shadow_alpha_scale))', - dark: 'calc(37% * var(--shadow_alpha_scale))', + dark: 'calc(37% * var(--shadow_alpha_scale))' }; export const shadow_alpha_40: StyleVariable = { name: 'shadow_alpha_40', light: 'calc(36% * var(--shadow_alpha_scale))', - dark: 'calc(47% * var(--shadow_alpha_scale))', + dark: 'calc(47% * var(--shadow_alpha_scale))' }; export const shadow_alpha_50: StyleVariable = { name: 'shadow_alpha_50', light: 'calc(50% * var(--shadow_alpha_scale))', - dark: 'calc(59% * var(--shadow_alpha_scale))', + dark: 'calc(59% * var(--shadow_alpha_scale))' }; export const shadow_alpha_60: StyleVariable = { name: 'shadow_alpha_60', light: 'calc(64% * var(--shadow_alpha_scale))', - dark: 'calc(71% * var(--shadow_alpha_scale))', + dark: 'calc(71% * var(--shadow_alpha_scale))' }; export const shadow_alpha_70: StyleVariable = { name: 'shadow_alpha_70', light: 'calc(77% * var(--shadow_alpha_scale))', - dark: 'calc(83% * var(--shadow_alpha_scale))', + dark: 'calc(83% * var(--shadow_alpha_scale))' }; export const shadow_alpha_80: StyleVariable = { name: 'shadow_alpha_80', light: 'calc(88% * var(--shadow_alpha_scale))', - dark: 'calc(91% * var(--shadow_alpha_scale))', + dark: 'calc(91% * var(--shadow_alpha_scale))' }; export const shadow_alpha_90: StyleVariable = { name: 'shadow_alpha_90', light: 'calc(96% * var(--shadow_alpha_scale))', - dark: 'calc(98% * var(--shadow_alpha_scale))', + dark: 'calc(98% * var(--shadow_alpha_scale))' }; export const shadow_alpha_95: StyleVariable = { name: 'shadow_alpha_95', light: 'calc(99% * var(--shadow_alpha_scale))', - dark: 'calc(100% * var(--shadow_alpha_scale))', + dark: 'calc(100% * var(--shadow_alpha_scale))' }; export const shadow_alpha_100: StyleVariable = { name: 'shadow_alpha_100', - light: 'calc(100% * var(--shadow_alpha_scale))', + light: 'calc(100% * var(--shadow_alpha_scale))' }; /* icons */ @@ -2271,46 +2295,46 @@ export const shadow_alpha_100: StyleVariable = { and they're insensitive to font size (`px` not `rem`) */ export const icon_size_xs: StyleVariable = { name: 'icon_size_xs', - light: icon_sizes.icon_size_xs, + light: icon_sizes.icon_size_xs }; export const icon_size_sm: StyleVariable = { name: 'icon_size_sm', - light: icon_sizes.icon_size_sm, + light: icon_sizes.icon_size_sm }; export const icon_size_md: StyleVariable = { name: 'icon_size_md', - light: icon_sizes.icon_size_md, + light: icon_sizes.icon_size_md }; export const icon_size_lg: StyleVariable = { name: 'icon_size_lg', - light: icon_sizes.icon_size_lg, + light: icon_sizes.icon_size_lg }; export const icon_size_xl: StyleVariable = { name: 'icon_size_xl', - light: icon_sizes.icon_size_xl, + light: icon_sizes.icon_size_xl }; export const icon_size_xl2: StyleVariable = { name: 'icon_size_xl2', - light: icon_sizes.icon_size_xl2, + light: icon_sizes.icon_size_xl2 }; export const icon_size_xl3: StyleVariable = { name: 'icon_size_xl3', - light: icon_sizes.icon_size_xl3, + light: icon_sizes.icon_size_xl3 }; /* durations */ // TODO maybe change the scale from xs-xl, and add an xs here around 0.04s or 0.03s (2 frames at 60fps) // TODO docs -export const duration_1: StyleVariable = {name: 'duration_1', light: '0.08s'}; -export const duration_2: StyleVariable = {name: 'duration_2', light: '0.2s'}; -export const duration_3: StyleVariable = {name: 'duration_3', light: '0.5s'}; -export const duration_4: StyleVariable = {name: 'duration_4', light: '1s'}; -export const duration_5: StyleVariable = {name: 'duration_5', light: '1.5s'}; -export const duration_6: StyleVariable = {name: 'duration_6', light: '3s'}; +export const duration_1: StyleVariable = { name: 'duration_1', light: '0.08s' }; +export const duration_2: StyleVariable = { name: 'duration_2', light: '0.2s' }; +export const duration_3: StyleVariable = { name: 'duration_3', light: '0.5s' }; +export const duration_4: StyleVariable = { name: 'duration_4', light: '1s' }; +export const duration_5: StyleVariable = { name: 'duration_5', light: '1.5s' }; +export const duration_6: StyleVariable = { name: 'duration_6', light: '3s' }; export const disabled_opacity: StyleVariable = { name: 'disabled_opacity', - light: '60%', + light: '60%' }; /** @@ -2867,5 +2891,5 @@ export const default_variables: Array<StyleVariable> = [ duration_4, duration_5, duration_6, - disabled_opacity, + disabled_opacity ]; diff --git a/src/lib/vite_plugin_fuz_css.ts b/src/lib/vite_plugin_fuz_css.ts index 30ead7d4b..db9cff8ef 100644 --- a/src/lib/vite_plugin_fuz_css.ts +++ b/src/lib/vite_plugin_fuz_css.ts @@ -29,33 +29,33 @@ * @module */ -import {normalizePath, type Logger as ViteLogger, type Plugin, type ViteDevServer} from 'vite'; -import {isAbsolute, join} from 'node:path'; -import {hash_blake3} from '@fuzdev/fuz_util/hash_blake3.ts'; -import {fs_search} from '@fuzdev/fuz_util/fs.ts'; -import {each_concurrent} from '@fuzdev/fuz_util/async.ts'; - -import {format_diagnostic, CssGenerationError} from './diagnostics.ts'; -import {generate_css} from './generate_css.ts'; -import {create_bundled_resources} from './bundled_resources.ts'; -import {extract_file_cached} from './extract_file_cached.ts'; -import {merge_class_definitions} from './css_class_definitions.ts'; -import {css_class_interpreters} from './css_class_interpreters.ts'; -import {load_css_properties} from './css_literal.ts'; +import { normalizePath, type Logger as ViteLogger, type Plugin, type ViteDevServer } from 'vite'; +import { isAbsolute, join } from 'node:path'; +import { hash_blake3 } from '@fuzdev/fuz_util/hash_blake3.ts'; +import { fs_search } from '@fuzdev/fuz_util/fs.ts'; +import { each_concurrent } from '@fuzdev/fuz_util/async.ts'; + +import { format_diagnostic, CssGenerationError } from './diagnostics.ts'; +import { generate_css } from './generate_css.ts'; +import { create_bundled_resources } from './bundled_resources.ts'; +import { extract_file_cached } from './extract_file_cached.ts'; +import { merge_class_definitions } from './css_class_definitions.ts'; +import { css_class_interpreters } from './css_class_interpreters.ts'; +import { load_css_properties } from './css_literal.ts'; import { DEFAULT_CACHE_DIR, get_file_cache_path, save_cached_extraction, - delete_cached_extraction, + delete_cached_extraction } from './css_cache.ts'; -import {default_cache_deps} from './deps_defaults.ts'; -import {filter_file_default} from './file_filter.ts'; -import {CssClasses} from './css_classes.ts'; -import type {StyleRuleIndex} from './style_rule_parser.ts'; -import {type VariableDependencyGraph, get_all_variable_names} from './variable_graph.ts'; -import {extract_css_variables} from './css_variable_utils.ts'; -import type {CssClassVariableIndex} from './class_variable_index.ts'; -import type {CssGeneratorBaseOptions} from './css_plugin_options.ts'; +import { default_cache_deps } from './deps_defaults.ts'; +import { filter_file_default } from './file_filter.ts'; +import { CssClasses } from './css_classes.ts'; +import type { StyleRuleIndex } from './style_rule_parser.ts'; +import { type VariableDependencyGraph, get_all_variable_names } from './variable_graph.ts'; +import { extract_css_variables } from './css_variable_utils.ts'; +import type { CssClassVariableIndex } from './class_variable_index.ts'; +import type { CssGeneratorBaseOptions } from './css_plugin_options.ts'; /* eslint-disable no-console */ @@ -180,7 +180,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug exclude_elements, exclude_variables, deps = default_cache_deps, - prescan = true, + prescan = true } = options; // Derive include flags from null check @@ -190,7 +190,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug // Merge class definitions (validates that definitions exist when needed) const all_class_definitions = merge_class_definitions( user_class_definitions, - include_default_classes, + include_default_classes ); // Convert to Sets for efficient lookup @@ -277,7 +277,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug base_css, variables, class_definitions: all_class_definitions, - deps, + deps }); style_rule_index = resources.style_rule_index; variable_graph = resources.variable_graph; @@ -321,7 +321,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug explicit_classes, all_elements, explicit_elements, - explicit_variables, + explicit_variables } = css_classes.get_all(); // Aggregate per-file `var(--*)` references (stored unfiltered by @@ -342,7 +342,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug } } - const {css: final_css, diagnostics: all_diagnostics} = generate_css({ + const { css: final_css, diagnostics: all_diagnostics } = generate_css({ all_classes, all_classes_with_locations, explicit_classes, @@ -359,12 +359,12 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug // Resources load lazily; null until ready falls back to utility-only. resources: style_rule_index && variable_graph && class_variable_index - ? {style_rule_index, variable_graph, class_variable_index} + ? { style_rule_index, variable_graph, class_variable_index } : null, additional_elements, additional_variables, exclude_elements, - exclude_variables, + exclude_variables }); // Separate errors and warnings @@ -424,9 +424,9 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug type: 'js-update', path: RESOLVED_VIRTUAL_ID, acceptedPath: RESOLVED_VIRTUAL_ID, - timestamp: Date.now(), - }, - ], + timestamp: Date.now() + } + ] }); } for (const vid of loaded_virtual_ids) { @@ -492,13 +492,13 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug const epoch = ++epoch_seq; transform_epochs.set(id, epoch); - const {extraction, from_cache, cache_path_to_write} = await extract_file_cached({ + const { extraction, from_cache, cache_path_to_write } = await extract_file_cached({ deps, content: code, content_hash: hash, cache_path, filename: id, - acorn_plugins, + acorn_plugins }); // Bail if the file was deleted or re-ingested during the cache read; @@ -555,9 +555,9 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug roots.map((root) => fs_search(isAbsolute(root) ? root : join(project_root!, root), { file_filter: filter_file, - sort: null, - }), - ), + sort: null + }) + ) ); // Normalize to Vite's posix-style ids so pre-scan entries share keys // with transform ingests (`hashes`, deletion handling) on every platform. @@ -565,7 +565,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug prescan_active = true; try { await each_concurrent(file_ids, PRESCAN_CONCURRENCY, async (id) => { - const r = await deps.read_text({path: id}); + const r = await deps.read_text({ path: id }); if (!r.ok) return; // deleted mid-scan or unreadable; transform covers it if it reappears await ingest_file(id, r.value); }); @@ -763,7 +763,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug if (spliced_count > 1) { log_error( `[fuz_css] placeholder marker found in ${spliced_count} CSS assets; generated CSS is` + - ` duplicated across them. Expected a single globally-loaded asset.`, + ` duplicated across them. Expected a single globally-loaded asset.` ); return; } @@ -795,7 +795,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug throw new Error( '[fuz_css] virtual:fuz.css was imported but no CSS asset exists to hold the generated' + ' styles — every page would be unstyled. Check the build config (cssCodeSplit /' + - ' CSS-in-JS inlining).', + ' CSS-in-JS inlining).' ); } @@ -805,7 +805,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug log_error( `[fuz_css] placeholder marker not found in any CSS asset (likely stripped by a CSS` + ` minifier); appending generated CSS to all ${css_asset_count} CSS assets as a` + - ` fallback. Output may be duplicated.`, + ` fallback. Output may be duplicated.` ); for (const chunk of Object.values(bundle)) { if ( @@ -827,7 +827,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug await ingest_file(id, code); return null; - }, + } // Note: handleHotUpdate not needed - transform hook handles file changes, // and configureServer's watcher.on('unlink') handles file deletion diff --git a/src/lib/wcag.ts b/src/lib/wcag.ts index 57dd45e1f..1ec58a431 100644 --- a/src/lib/wcag.ts +++ b/src/lib/wcag.ts @@ -8,7 +8,7 @@ * @module */ -import {srgb_component_to_linear, type RgbUnit} from './oklch.ts'; +import { srgb_component_to_linear, type RgbUnit } from './oklch.ts'; /** * Computes WCAG 2.x relative luminance of a gamma-encoded sRGB color. diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index f7061170f..76bd666ba 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -2,15 +2,15 @@ import 'virtual:fuz.css'; import '@fuzdev/fuz_code/theme.css'; - import {SiteState, site_context} from '@fuzdev/fuz_ui/site.svelte.ts'; - import {logo_fuz_css} from '@fuzdev/fuz_ui/logos.ts'; - import type {Snippet} from 'svelte'; + import { SiteState, site_context } from '@fuzdev/fuz_ui/site.svelte.ts'; + import { logo_fuz_css } from '@fuzdev/fuz_ui/logos.ts'; + import type { Snippet } from 'svelte'; import ThemeRoot from '@fuzdev/fuz_ui/ThemeRoot.svelte'; import Spiders from '@fuzdev/fuz_ui/Spiders.svelte'; - import {save_theme} from '@fuzdev/fuz_ui/theme_state.svelte.ts'; + import { save_theme } from '@fuzdev/fuz_ui/theme_state.svelte.ts'; import pkg_json from 'virtual:pkg.json'; - import {UNSAVED_THEME_NAME} from '$routes/theme_editor_state.svelte.ts'; + import { UNSAVED_THEME_NAME } from '$routes/theme_editor_state.svelte.ts'; // TODO re-enable this, see comment below // import ContextmenuRoot from '$lib/ContextmenuRoot.svelte'; @@ -19,12 +19,12 @@ // import {contextmenu_attachment} from '$lib/contextmenu_helpers.svelte.js'; const { - children, + children }: { children: Snippet; } = $props(); - site_context.set(new SiteState({icon: logo_fuz_css, pkg_json})); + site_context.set(new SiteState({ icon: logo_fuz_css, pkg_json })); // let show_settings = $state.raw(false); </script> diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 5c002a202..963f0c9b8 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,14 +1,14 @@ <script lang="ts"> - import {resolve} from '$app/paths'; + import { resolve } from '$app/paths'; import DocsFooter from '@fuzdev/fuz_ui/DocsFooter.svelte'; - import {site_context} from '@fuzdev/fuz_ui/site.svelte.ts'; - import {FUZ_DEV_URL} from '@fuzdev/fuz_ui/constants.ts'; + import { site_context } from '@fuzdev/fuz_ui/site.svelte.ts'; + import { FUZ_DEV_URL } from '@fuzdev/fuz_ui/constants.ts'; import Card from '@fuzdev/fuz_ui/Card.svelte'; import Svg from '@fuzdev/fuz_ui/Svg.svelte'; - import {logo_fuz_css} from '@fuzdev/fuz_ui/logos.ts'; + import { logo_fuz_css } from '@fuzdev/fuz_ui/logos.ts'; import Introduction from './Introduction.svelte'; - import {MAIN_HEADER_MARGIN_TOP} from './constants.ts'; + import { MAIN_HEADER_MARGIN_TOP } from './constants.ts'; const site = site_context.get(); </script> diff --git a/src/routes/FileLink.svelte b/src/routes/FileLink.svelte index 459fce551..c87f2ac6d 100644 --- a/src/routes/FileLink.svelte +++ b/src/routes/FileLink.svelte @@ -1,8 +1,8 @@ <script lang="ts"> - import type {Snippet} from 'svelte'; - import type {SvelteHTMLElements} from 'svelte/elements'; - import {icon_create_file} from '@fuzdev/fuz_ui/icons.ts'; - import type {SvgData} from '@fuzdev/fuz_ui/svg.ts'; + import type { Snippet } from 'svelte'; + import type { SvelteHTMLElements } from 'svelte/elements'; + import { icon_create_file } from '@fuzdev/fuz_ui/icons.ts'; + import type { SvgData } from '@fuzdev/fuz_ui/svg.ts'; import Svg from '@fuzdev/fuz_ui/Svg.svelte'; // TODO upstream? @@ -14,7 +14,7 @@ unstyled = false, attrs, icon = icon_create_file, - children, + children }: { path: string; /** @@ -35,7 +35,10 @@ <!-- eslint-disable-next-line svelte/no-navigation-without-resolve --> <a {...attrs} {href} class:chip={!unstyled} class:white-space:nowrap={true} - >{#if typeof icon === 'string'}{icon}{:else if typeof icon === - 'function'}{@render icon()}{:else}<Svg inline data={icon} size="1em" />{/if} + >{#if typeof icon === 'string'}{icon}{:else if typeof icon === 'function'}{@render icon()}{:else}<Svg + inline + data={icon} + size="1em" + />{/if} {#if children}{@render children()}{:else}{final_path}{/if}</a > diff --git a/src/routes/FontSizeControl.svelte b/src/routes/FontSizeControl.svelte index a8270dcf0..828c7d22f 100644 --- a/src/routes/FontSizeControl.svelte +++ b/src/routes/FontSizeControl.svelte @@ -1,12 +1,12 @@ <script lang="ts"> - import type {Snippet} from 'svelte'; + import type { Snippet } from 'svelte'; import StyleVariableButton from '@fuzdev/fuz_ui/StyleVariableButton.svelte'; - import {font_size_variants} from '$lib/variable_data.ts'; + import { font_size_variants } from '$lib/variable_data.ts'; let { selected_size = $bindable(2), - children, + children }: { selected_size?: number; // TODO improve API, is index for `'md'` children?: Snippet; diff --git a/src/routes/FontWeightControl.svelte b/src/routes/FontWeightControl.svelte index 971f57504..7dca58ac9 100644 --- a/src/routes/FontWeightControl.svelte +++ b/src/routes/FontWeightControl.svelte @@ -1,9 +1,9 @@ <script lang="ts"> - import type {Snippet} from 'svelte'; + import type { Snippet } from 'svelte'; let { selected_font_weight = $bindable(400), - children, + children }: { selected_font_weight?: number; children?: Snippet; diff --git a/src/routes/Introduction.svelte b/src/routes/Introduction.svelte index 3d5216341..5608c6f82 100644 --- a/src/routes/Introduction.svelte +++ b/src/routes/Introduction.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import {resolve} from '$app/paths'; + import { resolve } from '$app/paths'; import UnfinishedImplementationWarning from './docs/UnfinishedImplementationWarning.svelte'; </script> @@ -18,8 +18,8 @@ stylesheet containing the variables used in the reset, and these require no dependencies. There's also <a href={resolve('/docs/classes')}>utility classes</a> - for composition and convenience with a Vite plugin, and the library exports the full API for - complex usage. + for composition and convenience with a Vite plugin, and the library exports the full API for complex + usage. </p> <p> More at the <a href={resolve('/docs')}>docs</a> diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index d5675626c..088aba896 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -1,9 +1,9 @@ <script lang="ts"> import HueInput from '@fuzdev/fuz_ui/HueInput.svelte'; - import type {ThemeKnob} from '$lib/knobs.ts'; - import {PALETTE_HUES} from '$lib/ramps.ts'; - import {palette_variants, palette_glosses, type PaletteVariant} from '$lib/variable_data.ts'; + import type { ThemeKnob } from '$lib/knobs.ts'; + import { PALETTE_HUES } from '$lib/ramps.ts'; + import { palette_variants, palette_glosses, type PaletteVariant } from '$lib/variable_data.ts'; const { knob, @@ -12,7 +12,7 @@ onchange, onreset, compact = false, - resolve_hue = (letter) => PALETTE_HUES[letter], + resolve_hue = (letter) => PALETTE_HUES[letter] }: { knob: ThemeKnob; /** @@ -62,10 +62,7 @@ // tracks the hue the way the letter buttons track their palette slot const custom_color = $derived(`oklch(0.65 0.14 ${numeric_value ?? 0})`); const scalar = $derived( - knob.kind === 'hue' || - knob.kind === 'number' || - knob.kind === 'percent' || - knob.kind === 'time', + knob.kind === 'hue' || knob.kind === 'number' || knob.kind === 'percent' || knob.kind === 'time' ); const min = $derived(knob.range?.[0] ?? 0); const max = $derived(knob.range?.[1] ?? 100); diff --git a/src/routes/RampStrip.svelte b/src/routes/RampStrip.svelte index 6132c95f9..3640f30de 100644 --- a/src/routes/RampStrip.svelte +++ b/src/routes/RampStrip.svelte @@ -1,11 +1,11 @@ <script lang="ts"> // TODO upstream to fuz_ui - import {numeric_scale_variants} from '$lib/variable_data.ts'; + import { numeric_scale_variants } from '$lib/variable_data.ts'; const { prefix, - label = prefix, + label = prefix }: { /** The variable family prefix, e.g. `accent` renders `--accent_00`…`--accent_100`. */ prefix: string; diff --git a/src/routes/SourceFileLink.svelte b/src/routes/SourceFileLink.svelte index 3ff0227d4..dd0d8c9ed 100644 --- a/src/routes/SourceFileLink.svelte +++ b/src/routes/SourceFileLink.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import type {ComponentProps} from 'svelte'; + import type { ComponentProps } from 'svelte'; import FileLink from './FileLink.svelte'; diff --git a/src/routes/StyleVariableDetail.svelte b/src/routes/StyleVariableDetail.svelte index b247f5913..178fba900 100644 --- a/src/routes/StyleVariableDetail.svelte +++ b/src/routes/StyleVariableDetail.svelte @@ -1,8 +1,8 @@ <script lang="ts"> - import type {StyleVariable} from '$lib/variable.ts'; + import type { StyleVariable } from '$lib/variable.ts'; const { - variable, + variable }: { variable: StyleVariable | null; } = $props(); diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte index 08ce4d75c..545fa3e2c 100644 --- a/src/routes/ThemeEditor.svelte +++ b/src/routes/ThemeEditor.svelte @@ -4,21 +4,21 @@ import Code from '@fuzdev/fuz_code/Code.svelte'; import CopyToClipboard from '@fuzdev/fuz_ui/CopyToClipboard.svelte'; import ColorSchemeInput from '@fuzdev/fuz_ui/ColorSchemeInput.svelte'; - import type {ThemeState} from '@fuzdev/fuz_ui/theme_state.svelte.ts'; + import type { ThemeState } from '@fuzdev/fuz_ui/theme_state.svelte.ts'; - import {render_theme_style, type ThemeScheme} from '$lib/theme.ts'; - import {theme_knobs, knob_axes, type KnobAxis, type ThemeKnob} from '$lib/knobs.ts'; - import {PALETTE_HUES} from '$lib/ramps.ts'; + import { render_theme_style, type ThemeScheme } from '$lib/theme.ts'; + import { theme_knobs, knob_axes, type KnobAxis, type ThemeKnob } from '$lib/knobs.ts'; + import { PALETTE_HUES } from '$lib/ramps.ts'; import { palette_variants, intent_variants, type ColorSchemeVariant, - type PaletteVariant, + type PaletteVariant } from '$lib/variable_data.ts'; import { render_theme_ts, UNSAVED_THEME_NAME, - type ThemeEditorState, + type ThemeEditorState } from '$routes/theme_editor_state.svelte.ts'; import KnobControl from '$routes/KnobControl.svelte'; import RampStrip from '$routes/RampStrip.svelte'; @@ -27,7 +27,7 @@ const { editor, - theme_state, + theme_state }: { editor: ThemeEditorState; theme_state: ThemeState; @@ -58,7 +58,7 @@ shape: 'border width & radius tokens', density: 'space tokens', typography: 'line height tokens', - motion: 'motion tokens', + motion: 'motion tokens' }; // resolves a letter's current angle from the same merge the renderer uses, @@ -71,7 +71,7 @@ // switching the "based on" theme flattens it as the new base, discarding any // edits, so guard the switch behind a confirm when the draft is dirty - const on_base_change = (e: Event & {currentTarget: EventTarget & HTMLSelectElement}): void => { + const on_base_change = (e: Event & { currentTarget: EventTarget & HTMLSelectElement }): void => { const name = e.currentTarget.value; const discarded = editor.overrides.size ? `${editor.overrides.size} edited knob(s) will be discarded` @@ -90,7 +90,7 @@ const trimmed_name = $derived(editor.name.trim()); const name_collides = $derived( - trimmed_name === UNSAVED_THEME_NAME || editor.themes.some((t) => t.name === trimmed_name), + trimmed_name === UNSAVED_THEME_NAME || editor.themes.some((t) => t.name === trimmed_name) ); const output_ts = $derived(render_theme_ts(editor.output)); diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte index c75176ae5..b12113b73 100644 --- a/src/routes/about/+page.svelte +++ b/src/routes/about/+page.svelte @@ -1,14 +1,14 @@ <script lang="ts"> import DocsFooter from '@fuzdev/fuz_ui/DocsFooter.svelte'; - import {site_context} from '@fuzdev/fuz_ui/site.svelte.ts'; - import {FUZ_DEV_URL} from '@fuzdev/fuz_ui/constants.ts'; + import { site_context } from '@fuzdev/fuz_ui/site.svelte.ts'; + import { FUZ_DEV_URL } from '@fuzdev/fuz_ui/constants.ts'; import LibraryDetail from '@fuzdev/fuz_ui/LibraryDetail.svelte'; import EcosystemLinksPanel from '@fuzdev/fuz_ui/EcosystemLinksPanel.svelte'; import Breadcrumb from '@fuzdev/fuz_ui/Breadcrumb.svelte'; - import {Library, library_context} from '@fuzdev/fuz_ui/library.svelte.ts'; + import { Library, library_context } from '@fuzdev/fuz_ui/library.svelte.ts'; - import {MAIN_HEADER_MARGIN_TOP} from '$routes/constants.ts'; - import {library_json} from '$routes/library.ts'; + import { MAIN_HEADER_MARGIN_TOP } from '$routes/constants.ts'; + import { library_json } from '$routes/library.ts'; const library = new Library(library_json); library_context.set(() => library); diff --git a/src/routes/docs/+layout.svelte b/src/routes/docs/+layout.svelte index 15e3b4591..01f0e0d1f 100644 --- a/src/routes/docs/+layout.svelte +++ b/src/routes/docs/+layout.svelte @@ -1,18 +1,18 @@ <script lang="ts"> - import type {Snippet} from 'svelte'; + import type { Snippet } from 'svelte'; import Docs from '@fuzdev/fuz_ui/Docs.svelte'; import Dialog from '@fuzdev/fuz_ui/Dialog.svelte'; import DialogContent from '@fuzdev/fuz_ui/DialogContent.svelte'; - import {selected_variable_context} from '@fuzdev/fuz_ui/style_variable_helpers.svelte.ts'; - import {Library, library_context} from '@fuzdev/fuz_ui/library.svelte.ts'; + import { selected_variable_context } from '@fuzdev/fuz_ui/style_variable_helpers.svelte.ts'; + import { Library, library_context } from '@fuzdev/fuz_ui/library.svelte.ts'; - import {tomes} from './tomes.ts'; - import {library_json} from '$routes/library.ts'; + import { tomes } from './tomes.ts'; + import { library_json } from '$routes/library.ts'; import StyleVariableDetail from '$routes/StyleVariableDetail.svelte'; import UnfinishedImplementationWarning from './UnfinishedImplementationWarning.svelte'; const { - children, + children }: { children: Snippet; } = $props(); @@ -28,7 +28,7 @@ {#if selected_variable.value} <Dialog onclose={() => (selected_variable.value = null)}> <DialogContent> - {#snippet children({close})} + {#snippet children({ close })} <StyleVariableDetail variable={selected_variable.value} /> <UnfinishedImplementationWarning /> <button type="button" onclick={close}>ok</button> diff --git a/src/routes/docs/+page.svelte b/src/routes/docs/+page.svelte index dc193f7f3..3831d4b28 100644 --- a/src/routes/docs/+page.svelte +++ b/src/routes/docs/+page.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import DocsContent from '@fuzdev/fuz_ui/DocsContent.svelte'; - import {tomes_context} from '@fuzdev/fuz_ui/tome.ts'; - import {library_context} from '@fuzdev/fuz_ui/library.svelte.ts'; + import { tomes_context } from '@fuzdev/fuz_ui/tome.ts'; + import { library_context } from '@fuzdev/fuz_ui/library.svelte.ts'; const get_tomes_by_slug = tomes_context.get(); const tomes = $derived(Array.from(get_tomes_by_slug().values())); diff --git a/src/routes/docs/UnfinishedImplementationWarning.svelte b/src/routes/docs/UnfinishedImplementationWarning.svelte index a7d01c5f4..9b457edce 100644 --- a/src/routes/docs/UnfinishedImplementationWarning.svelte +++ b/src/routes/docs/UnfinishedImplementationWarning.svelte @@ -1,7 +1,7 @@ <script lang="ts"> - import type {SvelteHTMLElements} from 'svelte/elements'; + import type { SvelteHTMLElements } from 'svelte/elements'; - const {children, ...rest}: SvelteHTMLElements['aside'] = $props(); + const { children, ...rest }: SvelteHTMLElements['aside'] = $props(); </script> <aside {...rest}> diff --git a/src/routes/docs/api/+page.svelte b/src/routes/docs/api/+page.svelte index 24be1fb07..a940e94a8 100644 --- a/src/routes/docs/api/+page.svelte +++ b/src/routes/docs/api/+page.svelte @@ -1,7 +1,7 @@ <script lang="ts"> - import {page} from '$app/state'; + import { page } from '$app/state'; import ApiIndex from '@fuzdev/fuz_ui/ApiIndex.svelte'; - import {DOCS_PATH} from '@fuzdev/fuz_ui/docs_helpers.svelte.ts'; + import { DOCS_PATH } from '@fuzdev/fuz_ui/docs_helpers.svelte.ts'; // TODO this is a big strange because we use this page component in tomes, maybe it should be a separate component? const at_root = $derived(page.url.pathname === DOCS_PATH); diff --git a/src/routes/docs/api/[...module_path]/+page.svelte b/src/routes/docs/api/[...module_path]/+page.svelte index a10465561..ea718f2f0 100644 --- a/src/routes/docs/api/[...module_path]/+page.svelte +++ b/src/routes/docs/api/[...module_path]/+page.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import ApiModule from '@fuzdev/fuz_ui/ApiModule.svelte'; - const {params} = $props(); + const { params } = $props(); const module_path = $derived(params.module_path ?? ''); </script> diff --git a/src/routes/docs/borders/+page.svelte b/src/routes/docs/borders/+page.svelte index fea96a2eb..c5b066caf 100644 --- a/src/routes/docs/borders/+page.svelte +++ b/src/routes/docs/borders/+page.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import Code from '@fuzdev/fuz_code/Code.svelte'; import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; @@ -15,7 +15,7 @@ border_radius_variants, alpha_variants, intensity_variants, - type IntensityVariant, + type IntensityVariant } from '$lib/variable_data.ts'; import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; @@ -30,18 +30,18 @@ 'border-radius:0', 'border-radius:14%', 'border-radius:32%', - 'border-radius:100%', + 'border-radius:100%' ]; const border_radius_corner_classes = [ 'border-top-left-radius:26%', 'border-top-right-radius:100% border-bottom-left-radius:100%', - 'border-bottom-right-radius:77%', + 'border-bottom-right-radius:77%' ]; const border_radius_corner_size_classes = [ 'border_top_left_radius_lg border_top_right_radius_sm', - 'border_bottom_left_radius_md border_bottom_right_radius_xl', + 'border_bottom_left_radius_md border_bottom_right_radius_xl' ]; let selected_intensity: IntensityVariant = $state.raw('50'); @@ -60,9 +60,9 @@ <section> <p> - Border variables integrate with the <TomeLink slug="themes">theme</TomeLink> system and adapt - to color scheme. Alpha borders are tuned for visual balance -- dark mode uses higher alpha - because light-on-dark has lower perceived contrast. + Border variables integrate with the <TomeLink slug="themes">theme</TomeLink> system and adapt to + color scheme. Alpha borders are tuned for visual balance -- dark mode uses higher alpha because + light-on-dark has lower perceived contrast. </p> </section> <TomeSection> @@ -97,12 +97,11 @@ <TomeSection> <TomeSectionHeader text="Opaque borders with shades" tag="h4" /> <UnfinishedImplementationWarning - >We may want to add <code>border_shade_NN</code> utility classes for opaque - borders.</UnfinishedImplementationWarning + >We may want to add <code>border_shade_NN</code> utility classes for opaque borders.</UnfinishedImplementationWarning > <p> - For opaque borders, use <TomeLink slug="shading">shade</TomeLink> variables directly. This - avoids alpha transparency but requires inline styles or custom classes: + For opaque borders, use <TomeLink slug="shading">shade</TomeLink> variables directly. This avoids + alpha transparency but requires inline styles or custom classes: </p> <Code lang="css" @@ -120,8 +119,8 @@ border-color: var(--shade_30); <TomeSection> <TomeSectionHeader text="Border colors" /> <p> - Use palette variables like <code>palette_a_{selected_intensity}</code> for colored borders. - The intensity controls the color's prominence. + Use palette variables like <code>palette_a_{selected_intensity}</code> for colored borders. The + intensity controls the color's prominence. </p> <form class="intensity_selector"> <fieldset class="row mb_0"> @@ -243,8 +242,8 @@ border-color: var(--shade_30); <TomeSection> <TomeSectionHeader tag="h4" text="Custom values" /> <p> - Border <TomeLink slug="classes" hash="Literal-classes">literal classes</TomeLink> for - open-ended values: + Border <TomeLink slug="classes" hash="Literal-classes">literal classes</TomeLink> for open-ended + values: </p> <div class="border_examples border_radii"> {#each border_radius_classes as border_radius_class (border_radius_class)} diff --git a/src/routes/docs/buttons/+page.svelte b/src/routes/docs/buttons/+page.svelte index f38b89b01..016be06fc 100644 --- a/src/routes/docs/buttons/+page.svelte +++ b/src/routes/docs/buttons/+page.svelte @@ -1,14 +1,14 @@ <script lang="ts"> import Code from '@fuzdev/fuz_code/Code.svelte'; - import {slide} from 'svelte/transition'; + import { slide } from 'svelte/transition'; import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import ColorSchemeInput from '@fuzdev/fuz_ui/ColorSchemeInput.svelte'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; - import {palette_variants} from '$lib/variable_data.ts'; + import { palette_variants } from '$lib/variable_data.ts'; const LIBRARY_ITEM_NAME = 'buttons'; @@ -30,16 +30,16 @@ <section> <p> The <code><button></code> element is styled by default without adding classes. Classes - like <code>.selected</code> and <code>.plain</code> and <code>.palette_a</code> modify the - base style. + like <code>.selected</code> and <code>.plain</code> and <code>.palette_a</code> modify the base + style. </p> <p> Buttons have a <code>.selected</code> state that can be used for various UI purposes, like showing a selected item in a menu or a styling button's <code>aria-pressed</code> state. Instead of having two distinct styles of buttons with outlined and filled variants, fuz_css makes outlined buttons the default, and selected buttons are filled. There's also the - <code>.deselectable</code> modifier class for buttons that remain clickable when selected. - Themes can customize this behavior. + <code>.deselectable</code> modifier class for buttons that remain clickable when selected. Themes + can customize this behavior. </p> <div class:mb_xs> <Code content={`<button>a button</button>`} /> @@ -145,8 +145,7 @@ </section> <section> <p> - <code>.selected</code> buttons with <code>.deselectable</code> continue to be clickable when - selected: + <code>.selected</code> buttons with <code>.deselectable</code> continue to be clickable when selected: </p> <Code content={`<button class="selected deselectable">\n\t...\n</button>`} /> </section> @@ -156,8 +155,7 @@ class="width:100% deselectable" class:selected={selected_deselectable_button} onclick={() => (selected_deselectable_button = !selected_deselectable_button)} - >a - .deselectable{#if selected_deselectable_button}.selected{:else} unselected{/if} button</button + >a .deselectable{#if selected_deselectable_button}.selected{:else} unselected{/if} button</button > <button type="button" class="width:100% selected deselectable" disabled >disabled .deselectable.selected</button @@ -234,8 +232,8 @@ <TomeSectionHeader text="Size composites" /> <p> The <TomeLink slug="classes" hash="#Composite-classes">size composite classes</TomeLink> - <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code - > scale buttons up and down by overriding custom properties for font, height, and padding. + <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code> scale + buttons up and down by overriding custom properties for font, height, and padding. </p> <Code content={`<button class="xs">xs</button>\n<button class="sm">sm</button>\n<button>md</button>\n<button class="lg">lg</button>\n<button class="xl">xl</button>`} diff --git a/src/routes/docs/chips/+page.svelte b/src/routes/docs/chips/+page.svelte index fb4a11ead..882c99770 100644 --- a/src/routes/docs/chips/+page.svelte +++ b/src/routes/docs/chips/+page.svelte @@ -1,13 +1,13 @@ <script lang="ts"> import Code from '@fuzdev/fuz_code/Code.svelte'; import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import ColorSchemeInput from '@fuzdev/fuz_ui/ColorSchemeInput.svelte'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; - import {palette_variants} from '$lib/variable_data.ts'; + import { palette_variants } from '$lib/variable_data.ts'; const LIBRARY_ITEM_NAME = 'chips'; @@ -66,8 +66,8 @@ <TomeSectionHeader text="Size composites" /> <p> The <TomeLink slug="classes" hash="#Composite-classes">size composite classes</TomeLink> - <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code - > scale chips up and down, adjusting font and padding. + <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code> scale + chips up and down, adjusting font and padding. </p> <Code content={`<span class="chip xs">xs</span>\n<span class="chip sm">sm</span>\n<span class="chip">md</span>\n<span class="chip lg">lg</span>\n<span class="chip xl">xl</span>`} diff --git a/src/routes/docs/classes/+page.svelte b/src/routes/docs/classes/+page.svelte index 179557c46..3d70f184f 100644 --- a/src/routes/docs/classes/+page.svelte +++ b/src/routes/docs/classes/+page.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import Code from '@fuzdev/fuz_code/Code.svelte'; import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; @@ -26,7 +26,7 @@ shadow_alpha_variants, shadow_semantic_values, distance_variants, - darken_lighten_variants, + darken_lighten_variants } from '$lib/variable_data.ts'; const LIBRARY_ITEM_NAME = 'classes'; @@ -50,8 +50,7 @@ Utility classes complement <TomeLink slug="semantic">semantic styles</TomeLink> and <TomeLink slug="variables">style variables</TomeLink>. Use them to compose styles across component boundaries, or when you prefer classes to the <code><style></code> tag and - <code>style</code> attribute. They're optional and generated on-demand to include only what you - use. + <code>style</code> attribute. They're optional and generated on-demand to include only what you use. </p> <p> Compared to TailwindCSS and UnoCSS, fuz_css utility classes follow the grain of semantic HTML @@ -95,16 +94,16 @@ <Code lang={null} content="npm i -D @fuzdev/fuz_css" /> <p> Use the <ModuleLink module_path="vite_plugin_fuz_css.ts">Vite plugin</ModuleLink> or - <ModuleLink module_path="gen_fuz_css.ts">Gro generator</ModuleLink> to generate bundled CSS - that includes theme variables, base styles, and utility classes: + <ModuleLink module_path="gen_fuz_css.ts">Gro generator</ModuleLink> to generate bundled CSS that + includes theme variables, base styles, and utility classes: </p> <TomeSection> <TomeSectionHeader text="Vite plugin" tag="h3" /> <p> - The <ModuleLink module_path="vite_plugin_fuz_css.ts">Vite plugin</ModuleLink> 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 + The <ModuleLink module_path="vite_plugin_fuz_css.ts">Vite plugin</ModuleLink> 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 <a href="https://github.com/acornjs/acorn-jsx"><code>acorn-jsx</code></a> plugin -- see <a href="#React-and-JSX">React and JSX</a> below. </p> @@ -120,8 +119,7 @@ export default defineConfig({ });`} /> <p> - Import the virtual module in your entry file, <code>src/routes/+layout.svelte</code> for - SvelteKit: + Import the virtual module in your entry file, <code>src/routes/+layout.svelte</code> for SvelteKit: </p> <Code lang="ts" @@ -147,8 +145,8 @@ import 'virtual:fuz.css';`} /> <p> The plugin extracts classes from files as Vite processes them, including from - <code>node_modules</code> dependencies. It supports HMR -- changes to classes in your code - trigger automatic CSS updates. + <code>node_modules</code> dependencies. It supports HMR -- changes to classes in your code trigger + automatic CSS updates. </p> <h4>Plugin options</h4> <ul> @@ -156,8 +154,8 @@ import 'virtual:fuz.css';`} <code>acorn_plugins</code> - required for JSX frameworks, e.g. <code>acorn-jsx</code> </li> <li> - <code>additional_classes</code> - classes to always include (for dynamic patterns that - can't be statically extracted) + <code>additional_classes</code> - classes to always include (for dynamic patterns that can't + be statically extracted) </li> <li> <code>exclude_classes</code> - classes to exclude from output @@ -172,9 +170,9 @@ import 'virtual:fuz.css';`} <code>class_definitions</code>, excluding all default token and composite classes </li> <li> - <code>class_interpreters</code> - <a href="#Custom-interpreters">custom interpreters</a - > for dynamic class generation; replaces the default interpreters entirely if provided - (most users don't need this) + <code>class_interpreters</code> - <a href="#Custom-interpreters">custom interpreters</a> for + dynamic class generation; replaces the default interpreters entirely if provided (most users + don't need this) </li> <li> <code>filter_file</code> - custom filter for which files to process. Receives @@ -193,12 +191,12 @@ import 'virtual:fuz.css';`} <code>cache_dir</code> - cache location; defaults to <code>.fuz/cache/css</code> </li> <li> - <code>base_css</code> - customize or disable base styles; set to <code>null</code> for - utility-only mode, or provide a callback to modify defaults + <code>base_css</code> - customize or disable base styles; set to <code>null</code> for utility-only + mode, or provide a callback to modify defaults </li> <li> - <code>variables</code> - customize or disable theme variables; set to <code>null</code - > for utility-only mode, or provide a callback to modify defaults + <code>variables</code> - customize or disable theme variables; set to <code>null</code> for + utility-only mode, or provide a callback to modify defaults </li> <li> <code>additional_elements</code> - elements to always include styles for (for @@ -268,19 +266,18 @@ import './fuz.css';`} </p> <ul> <li> - <code>include_stats</code> - include file statistics in output (file counts, cache - hits/misses, class counts) + <code>include_stats</code> - include file statistics in output (file counts, cache hits/misses, + class counts) </li> <li> <code>project_root</code> - project root directory; defaults to <code>process.cwd()</code> </li> <li> - <code>concurrency</code> - max concurrent file processing for cache reads and extraction; - defaults to 8 + <code>concurrency</code> - max concurrent file processing for cache reads and extraction; defaults + to 8 </li> <li> - <code>cache_io_concurrency</code> - max concurrent cache writes and deletes; defaults to - 50 + <code>cache_io_concurrency</code> - max concurrent cache writes and deletes; defaults to 50 </li> </ul> </TomeSection> @@ -288,9 +285,8 @@ import './fuz.css';`} <TomeSection> <TomeSectionHeader text="Class detection" tag="h3" /> <p> - The <ModuleLink module_path="css_class_extractor.ts">extractor</ModuleLink> scans your - source files and extracts class names using three automatic mechanisms, plus manual hints - for edge cases: + The <ModuleLink module_path="css_class_extractor.ts">extractor</ModuleLink> scans your source + files and extracts class names using three automatic mechanisms, plus manual hints for edge cases: </p> <h4>1. Direct extraction from class attributes</h4> @@ -301,23 +297,22 @@ import './fuz.css';`} <code>{'class={[...]}'}</code> - array syntax (for clsx-compatible frameworks like Svelte) </li> <li> - <code>{'class={{...}}'}</code> - object syntax (for clsx-compatible frameworks like - Svelte) + <code>{'class={{...}}'}</code> - object syntax (for clsx-compatible frameworks like Svelte) </li> <li><code>{"class={cond ? 'a' : 'b'}"}</code> - ternary expressions</li> <li><code>{"class={(cond && 'a') || 'b'}"}</code> - logical expressions</li> <li><code>class:name</code> - class directives (Svelte)</li> <li> - <code>clsx()</code>, <code>cn()</code>, <code>cx()</code>, <code>classNames()</code> - - utility function calls + <code>clsx()</code>, <code>cn()</code>, <code>cx()</code>, <code>classNames()</code> - utility + function calls </li> </ul> <h4>2. Naming convention</h4> <p> Variables ending with <code>class</code>, <code>classes</code>, <code>className</code>, - <code>classNames</code>, <code>class_name</code>, or <code>class_names</code - > (case-insensitive) are always extracted, regardless of where they're used: + <code>classNames</code>, <code>class_name</code>, or <code>class_names</code> (case-insensitive) + are always extracted, regardless of where they're used: </p> <Code lang="ts" @@ -347,9 +342,8 @@ const turtle_class_name = 'turtle';`} /> <p> Usage tracking works for variables inside <code>clsx()</code>, arrays, ternaries, and - logical expressions within class attributes. Note that standalone <code>clsx()</code> calls - outside class attributes don't trigger tracking -- use the naming convention for those - cases. + logical expressions within class attributes. Note that standalone <code>clsx()</code> calls outside + class attributes don't trigger tracking -- use the naming convention for those cases. </p> <aside> Currently, tracking is single-file only. Cross-module analysis and more sophisticated @@ -392,9 +386,9 @@ const color = get_dynamic_color();`} {/each}`} /> <aside> - Edge values like <code>_00</code> and <code>_100</code> are especially easy to miss -- - they're generally not used directly in your code (they exist mainly for programmatic usage - ergonomics), so the class won't be generated unless you hint it. + Edge values like <code>_00</code> and <code>_100</code> are especially easy to miss -- they're + generally not used directly in your code (they exist mainly for programmatic usage ergonomics), + so the class won't be generated unless you hint it. </aside> <aside> Classes annotated with <code>@fuz-classes</code> and configured with @@ -414,9 +408,9 @@ const color = get_dynamic_color();`} });`} /> <p> - Use <DeclarationLink name="GenFuzCssOptions">exclude_classes</DeclarationLink> to filter out - false positives from extraction. This also suppresses warnings for these classes, even if - they were explicitly annotated: + Use <DeclarationLink name="GenFuzCssOptions">exclude_classes</DeclarationLink> to filter out false + positives from extraction. This also suppresses warnings for these classes, even if they were + explicitly annotated: </p> <Code lang="ts" @@ -427,8 +421,8 @@ const color = get_dynamic_color();`} <h4>Element hints</h4> <p> - Similar to <code>@fuz-classes</code>, use <code>@fuz-elements</code> to declare elements - that should be included even when they can't be statically detected: + Similar to <code>@fuz-classes</code>, use <code>@fuz-elements</code> to declare elements that + should be included even when they can't be statically detected: </p> <Code lang="ts" @@ -468,8 +462,8 @@ const el = document.createElement('dialog');`} /> <aside> Like <code>@fuz-classes</code> and <code>@fuz-elements</code>, explicit declarations via - <code>@fuz-variables</code> produce <strong>errors</strong> if they can't be resolved, - helping catch typos early. + <code>@fuz-variables</code> produce <strong>errors</strong> if they can't be resolved, helping + catch typos early. </aside> <h4>5. Build-time limitations</h4> @@ -479,8 +473,8 @@ const el = document.createElement('dialog');`} framework hydration) won't be detected. </p> <p> - Use <DeclarationLink name="GenFuzCssOptions">additional_elements</DeclarationLink> to - force-include element styles for runtime-created elements: + Use <DeclarationLink name="GenFuzCssOptions">additional_elements</DeclarationLink> to force-include + element styles for runtime-created elements: </p> <Code lang="ts" @@ -497,8 +491,8 @@ const el = document.createElement('dialog');`} Detection finds the classes, elements, and variables your source uses (see <a href="#Class-detection">class detection</a> above), and resolution turns that into CSS. Bundled mode -- the default for the <a href="#Vite-plugin">Vite plugin</a> and - <a href="#Gro-generator">Gro generator</a> -- combines three layers into a single output, each - trimmed to what your code uses: + <a href="#Gro-generator">Gro generator</a> -- combines three layers into a single output, each trimmed + to what your code uses: </p> <ol> <li> @@ -516,8 +510,8 @@ const el = document.createElement('dialog');`} <li>used utility classes (always generated on demand; there's no full version)</li> </ol> <p> - Sections are emitted in that order inside <MdnLink path="Web/CSS/@layer" /> cascade layers: - defaults in <code>fuz.base</code>, theme overrides in <code>fuz.theme</code>, and utilities in + Sections are emitted in that order inside <MdnLink path="Web/CSS/@layer" /> cascade layers: defaults + in <code>fuz.base</code>, theme overrides in <code>fuz.theme</code>, and utilities in <code>fuz.utilities</code>, so utilities beat the reset by layer order (not specificity) and your own unlayered styles beat everything. </p> @@ -539,8 +533,8 @@ const el = document.createElement('dialog');`} >additional_elements: 'all'</code >. Reach for an explicit <DeclarationLink name="CssGeneratorBaseOptions" >additional_elements</DeclarationLink - > list only when you want to keep the base CSS minimal and know exactly which extra tags - appear at runtime. + > list only when you want to keep the base CSS minimal and know exactly which extra tags appear + at runtime. </p> <Code lang="ts" @@ -568,11 +562,10 @@ vite_plugin_fuz_css({ <p> Dependencies resolve transitively, so pulling in a variable also pulls in any it's built from via - <code>var()</code>, and both <TomeLink slug="themes">light and dark</TomeLink> values are kept - together, so switching color-scheme at runtime never hits a missing variable. The complete set - ships in - <ModuleLink module_path="theme.css" /> for utility-only mode and direct imports; bundled mode - trims it to what you use. + <code>var()</code>, and both <TomeLink slug="themes">light and dark</TomeLink> values are kept together, + so switching color-scheme at runtime never hits a missing variable. The complete set ships in + <ModuleLink module_path="theme.css" /> for utility-only mode and direct imports; bundled mode trims + it to what you use. </p> <h4>Forcing and excluding</h4> @@ -583,8 +576,8 @@ vite_plugin_fuz_css({ <code>additional_variables</code> force-include (the element and variable options also accept <code>'all'</code>), while <code>exclude_classes</code>, <code>exclude_elements</code>, and <code>exclude_variables</code> drop items from the output. Excluding a variable that's still - referenced by included styles logs a warning, since the dropped <code>var(--name)</code> would - otherwise resolve to nothing. + referenced by included styles logs a warning, since the dropped <code>var(--name)</code> would otherwise + resolve to nothing. </p> </TomeSection> @@ -594,12 +587,11 @@ vite_plugin_fuz_css({ <TomeSectionHeader text="Token classes" tag="h3" /> <p> Token classes are technically <a href="#Composite-classes">composite classes</a> with a - close relationship to <TomeLink slug="variables">style variables</TomeLink> -- each maps - design tokens to CSS properties. They're generated programmatically from variant data, - making them predictable and systematic. The composites documented + close relationship to <TomeLink slug="variables">style variables</TomeLink> -- each maps design + tokens to CSS properties. They're generated programmatically from variant data, making them predictable + and systematic. The composites documented <a href="#Composite-classes">below</a> - are hand-written and typically represent higher-level semantic concepts. For raw CSS values, - use + are hand-written and typically represent higher-level semantic concepts. For raw CSS values, use <a href="#Literal-classes">literal classes</a> instead. </p> <Code content="<p class="pl_xl3 palette_g_50">some token classes</p>" /> @@ -728,8 +720,8 @@ vite_plugin_fuz_css({ <aside class="mt_lg"> Padding and margin include <code>_0</code> (and <code>_auto</code> for margin) for ergonomics: <code>pb_0</code> is much shorter than - <code>padding-bottom:0</code>. Other properties use <a href="#Literal-classes">literals</a - > for raw values. + <code>padding-bottom:0</code>. Other properties use <a href="#Literal-classes">literals</a> for + raw values. </aside> <h4>Sizing</h4> <p>See <TomeLink slug="layout" />.</p> @@ -768,7 +760,7 @@ vite_plugin_fuz_css({ <span class="code_chips" ><code >.palette_{@render variant_range(palette_variants)}_{@render variant_range( - intensity_variants, + intensity_variants )}</code > </span> @@ -777,7 +769,7 @@ vite_plugin_fuz_css({ <span class="code_chips" ><code >.bg_{@render variant_range(palette_variants)}_{@render variant_range( - intensity_variants, + intensity_variants )}</code > </span> @@ -839,8 +831,8 @@ vite_plugin_fuz_css({ <aside> The <code>text_*</code> and <code>shade_*</code> scales are separate because text and backgrounds have different contrast requirements. Use <code>text_*</code> for text colors - and <code>shade_*</code> for backgrounds. Both follow "prominence" semantics for light and - dark modes: low numbers are subtle, high numbers are strong. + and <code>shade_*</code> for backgrounds. Both follow "prominence" semantics for light and dark + modes: low numbers are subtle, high numbers are strong. </aside> <h4>Typography</h4> <p>See <TomeLink slug="typography" />.</p> @@ -879,7 +871,7 @@ vite_plugin_fuz_css({ <span class="code_chips" ><code >.border_{@render variant_range(palette_variants)}_{@render variant_range( - intensity_variants, + intensity_variants )}</code > </span> @@ -906,8 +898,7 @@ vite_plugin_fuz_css({ </li> <li class="mb_md"> <span class="code_chips" - ><code>.border_bottom_left_radius_{@render variant_range(border_radius_variants)}</code - > + ><code>.border_bottom_left_radius_{@render variant_range(border_radius_variants)}</code> </span> </li> <li class="mb_md"> @@ -936,7 +927,7 @@ vite_plugin_fuz_css({ <span class="code_chips" ><code >.outline_{@render variant_range(palette_variants)}_{@render variant_range( - intensity_variants, + intensity_variants )}</code > </span> @@ -985,7 +976,7 @@ vite_plugin_fuz_css({ <span class="code_chips" ><code >.shadow_{@render variant_range(palette_variants)}_{@render variant_range( - intensity_variants, + intensity_variants )}</code > </span> @@ -1087,8 +1078,8 @@ export const custom_composites: Record<string, CssClassDefinition> = { <h4>What <code>composes</code> can reference</h4> <p> The <code>composes</code> property resolves referenced classes and combines their - declarations. When both <code>composes</code> and <code>declaration</code> are present, the - explicit declaration comes last (winning in the cascade for duplicate properties). + declarations. When both <code>composes</code> and <code>declaration</code> are present, the explicit + declaration comes last (winning in the cascade for duplicate properties). </p> <ul> <li> @@ -1139,8 +1130,8 @@ export const custom_composites: Record<string, CssClassDefinition> = { Composites support <a href="#Modifiers">modifiers</a> like any other class. For <code>composes</code> and <code>declaration</code> - composites, declarations are combined and wrapped. For <code>ruleset</code> composites, - modifiers are applied to each selector (with smart conflict detection): + composites, declarations are combined and wrapped. For <code>ruleset</code> composites, modifiers + are applied to each selector (with smart conflict detection): </p> <Code content={`<!-- hover:foo resolves foo's \`composes\`, applies :hover --> @@ -1194,8 +1185,7 @@ export const gen = gen_fuz_css({ <li><code>.mt_flow</code> - flow-aware margin-top</li> </ul> <p> - <strong>Ruleset-based</strong> (multi-selector, cannot be used in <code>composes</code - > arrays): + <strong>Ruleset-based</strong> (multi-selector, cannot be used in <code>composes</code> arrays): </p> <ul> <li><code>.selectable</code> - selectable element styling</li> @@ -1234,8 +1224,8 @@ export const gen = gen_fuz_css({ contain spaces). Use it for multi-value properties like <code>margin:1px~auto</code>. </p> <p> - Custom properties work directly: <code>--my-var:value</code> sets the property on the - element. This is useful for scoped variables or passing values to child components. + Custom properties work directly: <code>--my-var:value</code> sets the property on the element. + This is useful for scoped variables or passing values to child components. </p> </TomeSection> </TomeSection> @@ -1441,9 +1431,9 @@ export const gen = gen_fuz_css({ <TomeSection> <TomeSectionHeader text="Builtin classes" /> <p> - fuz_css's <ModuleLink module_path="style.css">main stylesheet</ModuleLink> provides styles for - base HTML elements using <TomeLink slug="variables">style variables</TomeLink>, acting as a - modern CSS reset that adapts to dark mode. It includes CSS classes that provide common generic + fuz_css's <ModuleLink module_path="style.css">main stylesheet</ModuleLink> provides styles for base + HTML elements using <TomeLink slug="variables">style variables</TomeLink>, 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. </p> <h4><code>.unstyled</code></h4> @@ -1470,8 +1460,8 @@ export const gen = gen_fuz_css({ <li>b</li> </ul> <p> - The <code>.unstyled</code> class lets fuz_css provide solid default element styles with a - simple opt-out: + The <code>.unstyled</code> class lets fuz_css provide solid default element styles with a simple + opt-out: </p> <Code lang="css" @@ -1487,8 +1477,8 @@ export const gen = gen_fuz_css({ }`} /> <aside> - The <code>:where()</code> selector keeps specificity as low as possible to minimize - interference with your styles. It's used throughout the reset stylesheet. + The <code>:where()</code> selector keeps specificity as low as possible to minimize interference + with your styles. It's used throughout the reset stylesheet. </aside> <p>See the specific docs sections for more about <code>.unstyled</code>.</p> @@ -1564,18 +1554,17 @@ export const gen = gen_fuz_css({ <td>Vue SFC, Angular, etc.</td> <td>none</td> <td - >template syntax not parsed; use <code>clsx</code>/<code>cx</code>/<code>cn</code> in - JS/TS</td + >template syntax not parsed; use <code>clsx</code>/<code>cx</code>/<code>cn</code> in JS/TS</td > </tr> </tbody> </table> <p> - The <DeclarationLink name="GenFuzCssOptions">additional_classes</DeclarationLink> plugin - config option is an escape hatch for classes that can't be statically detected. Acorn plugins - can be added via - <DeclarationLink name="GenFuzCssOptions">acorn_plugins</DeclarationLink> for additional syntax - support like <a href="#React-and-JSX">JSX</a>. + The <DeclarationLink name="GenFuzCssOptions">additional_classes</DeclarationLink> plugin config + option is an escape hatch for classes that can't be statically detected. Acorn plugins can be added + via + <DeclarationLink name="GenFuzCssOptions">acorn_plugins</DeclarationLink> for additional syntax support + like <a href="#React-and-JSX">JSX</a>. </p> <p> Out of the box, class generation works only with TypeScript/JS, Svelte, and JSX. Angular is @@ -1586,8 +1575,8 @@ export const gen = gen_fuz_css({ <TomeSection> <TomeSectionHeader text="Svelte-first" tag="h3" /> <p> - The <ModuleLink module_path="css_class_extractor.ts">extractor</ModuleLink> parses and - analyzes the AST to understand + The <ModuleLink module_path="css_class_extractor.ts">extractor</ModuleLink> parses and analyzes + the AST to understand <a href="https://svelte.dev/docs/svelte/class">Svelte's class syntax</a>. Supported constructs: </p> @@ -1607,8 +1596,8 @@ export const gen = gen_fuz_css({ <code>additional_classes</code>) </li> <li> - <strong>Svelte 5 runes:</strong> <code>$derived()</code> and <code>$derived.by()</code - > for class variables + <strong>Svelte 5 runes:</strong> <code>$derived()</code> and <code>$derived.by()</code> for + class variables </li> <li> <strong>utility calls:</strong> <code>clsx()</code>, <code>cn()</code>, <code>cx()</code>, @@ -1672,8 +1661,7 @@ export const gen = gen_fuz_css({ </li> <li> usage tracking: variables in <code>className</code>, <code>class</code>, and - <code>classList</code> are tracked back to their definitions (has limitations, room for - improvement) + <code>classList</code> are tracked back to their definitions (has limitations, room for improvement) </li> </ul> <Code @@ -1683,8 +1671,8 @@ const styles = 'box hover:shadow_lg'; const Component = () => <div className={styles} />;`} /> <p> - The <DeclarationLink name="GenFuzCssOptions">acorn_plugins</DeclarationLink> option accepts - any Acorn-compatible plugin, so other syntax extensions can be supported the same way. + The <DeclarationLink name="GenFuzCssOptions">acorn_plugins</DeclarationLink> option accepts any + Acorn-compatible plugin, so other syntax extensions can be supported the same way. </p> </TomeSection> </TomeSection> @@ -1692,13 +1680,13 @@ const Component = () => <div className={styles} />;`} <TomeSection> <TomeSectionHeader text="Custom interpreters" /> <p> - <ModuleLink module_path="css_class_interpreters.ts">Interpreters</ModuleLink> dynamically - generate CSS for class names that aren't in the static definitions (which can be extended via + <ModuleLink module_path="css_class_interpreters.ts">Interpreters</ModuleLink> dynamically generate + CSS for class names that aren't in the static definitions (which can be extended via <code>class_definitions</code> or replaced with <code>include_default_classes: false</code>). The default <a href="#Literal-classes">CSS-literal syntax</a> and - <a href="#Modifiers">modifier support</a> are both implemented as interpreters, which you can - extend or replace. + <a href="#Modifiers">modifier support</a> are both implemented as interpreters, which you can extend + or replace. </p> <p> For advanced use cases, you can define custom interpreters that generate CSS from arbitrary @@ -1740,15 +1728,14 @@ vite_plugin_fuz_css({ /> <p> The interpreter context provides access to <code>class_definitions</code>, - <code>css_properties</code> (for validation), and <code>diagnostics</code> (for - errors/warnings). This enables full programmatic control over class-to-CSS generation. + <code>css_properties</code> (for validation), and <code>diagnostics</code> (for errors/warnings). + This enables full programmatic control over class-to-CSS generation. </p> <aside> Custom interpreters replace the defaults entirely, so include <code >...css_class_interpreters</code > - to preserve CSS-literal and modified-class support. This area is experimental and the API may - change. + to preserve CSS-literal and modified-class support. This area is experimental and the API may change. </aside> </TomeSection> @@ -1816,8 +1803,8 @@ vite_plugin_fuz_css({ <code>has-checked:</code>), arbitrary variants (<code>[&.is-dragging]:</code>), child selectors (<code>*:</code>), container queries (<code>@md:</code>), data/ARIA variants, and more. When you need these patterns, fuz_css currently expects you to use rulesets or - <code><style></code> tags, but the API is still a work in progress, and a more powerful - and potentially more TailwindCSS-aligned system is on the table. + <code><style></code> tags, but the API is still a work in progress, and a more powerful and + potentially more TailwindCSS-aligned system is on the table. </p> <p> For extensibility, all three frameworks allow custom class-to-CSS mappings. UnoCSS's dynamic @@ -1830,8 +1817,8 @@ vite_plugin_fuz_css({ 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 <code><style></code> tags, tokens when appropriate, or - composites for repeated patterns. + which nudges you toward <code><style></code> tags, tokens when appropriate, or composites + for repeated patterns. </p> </TomeSection> <hr /> diff --git a/src/routes/docs/colors/+page.svelte b/src/routes/docs/colors/+page.svelte index 93be8bc30..e4f25b256 100644 --- a/src/routes/docs/colors/+page.svelte +++ b/src/routes/docs/colors/+page.svelte @@ -1,6 +1,6 @@ <script lang="ts"> import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; import MdnLink from '@fuzdev/fuz_ui/MdnLink.svelte'; import ColorSchemeInput from '@fuzdev/fuz_ui/ColorSchemeInput.svelte'; @@ -10,7 +10,7 @@ import HueSwatch from './HueSwatch.svelte'; import ColorSwatch from './ColorSwatch.svelte'; - import {palette_variants, palette_glosses, type PaletteVariant} from '$lib/variable_data.ts'; + import { palette_variants, palette_glosses, type PaletteVariant } from '$lib/variable_data.ts'; const LIBRARY_ITEM_NAME = 'colors'; @@ -35,10 +35,10 @@ slug="variables" /> in pure CSS, in the <MdnLink path="Web/CSS/color_value/oklch" /> colorspace, adapting to the - <MdnLink path="Web/CSS/color-scheme" /> automatically. OKLCH lightness is perceptually uniform - -- equal lightness reads equally light in every hue -- so rotating a hue knob is safe: - contrast and visual weight hold, which is what makes <TomeLink slug="themes">themes</TomeLink - > a small set of knob values instead of hundreds of hand-tuned stops. + <MdnLink path="Web/CSS/color-scheme" /> automatically. OKLCH lightness is perceptually uniform -- + equal lightness reads equally light in every hue -- so rotating a hue knob is safe: contrast and + visual weight hold, which is what makes <TomeLink slug="themes">themes</TomeLink> a small set of + knob values instead of hundreds of hand-tuned stops. </p> <p> Hues use letters so themes can reassign colors without breaking semantics -- "a" is blue by @@ -57,17 +57,17 @@ <code>--hue_a</code> … <code>--hue_j</code> -- OKLCH hue angles for the 10 palette slots </li> <li> - <code>--hue_neutral</code> + <code>--neutral_chroma</code> -- the temperature and strength - of every surface, text, border, and shadow tint (the neutral intent; its scales are the - shade and text ramps) + <code>--hue_neutral</code> + <code>--neutral_chroma</code> -- the temperature and strength of + every surface, text, border, and shadow tint (the neutral intent; its scales are the shade and + text ramps) </li> <li> - <code>--chroma_scale</code> -- one multiplier from grayscale (0) through calm (1) to vivid - (above 1, knowingly clipping the weakest hues) + <code>--chroma_scale</code> -- one multiplier from grayscale (0) through calm (1) to vivid (above + 1, knowingly clipping the weakest hues) </li> <li> - <code>--hue_shift</code> -- degrees of hue rotation across each ramp for painterly - warm-light/cool-shadow character (default 0) + <code>--hue_shift</code> -- degrees of hue rotation across each ramp for painterly warm-light/cool-shadow + character (default 0) </li> <li> intent hues -- <code>--hue_accent</code>, <code>--hue_positive</code>, @@ -105,9 +105,9 @@ <TomeSection> <TomeSectionHeader text="Hue variables" /> <p> - Hue variables contain a single OKLCH <MdnLink path="Web/CSS/hue" /> angle. Because lightness - and chroma are shared across all hues at each stop, the scales are interchangeable -- setting - a hue alone is enough, no per-hue tuning required. + Hue variables contain a single OKLCH <MdnLink path="Web/CSS/hue" /> angle. Because lightness and + chroma are shared across all hues at each stop, the scales are interchangeable -- setting a hue + alone is enough, no per-hue tuning required. </p> <p> Hue variables are also useful to construct custom colors not covered by the palette. For @@ -134,8 +134,8 @@ </p> <p> Unlike the <TomeLink slug="shading">shade</TomeLink> and - <TomeLink slug="typography" hash="Text-colors">text</TomeLink> scales (which are separate), - palette variables can be used for both text and backgrounds via utility classes: + <TomeLink slug="typography" hash="Text-colors">text</TomeLink> scales (which are separate), palette + variables can be used for both text and backgrounds via utility classes: <code>.palette_a_50</code> sets text color, <code>.bg_a_50</code> sets background color. </p> <p> diff --git a/src/routes/docs/colors/ColorSwatch.svelte b/src/routes/docs/colors/ColorSwatch.svelte index 4e4242f1f..427b8c67c 100644 --- a/src/routes/docs/colors/ColorSwatch.svelte +++ b/src/routes/docs/colors/ColorSwatch.svelte @@ -1,9 +1,9 @@ <script lang="ts"> import ColorSwatchItem from './ColorSwatchItem.svelte'; - import {intensity_variants, type PaletteVariant} from '$lib/variable_data.ts'; + import { intensity_variants, type PaletteVariant } from '$lib/variable_data.ts'; const { - letter, + letter }: { letter: PaletteVariant; } = $props(); diff --git a/src/routes/docs/colors/ColorSwatchItem.svelte b/src/routes/docs/colors/ColorSwatchItem.svelte index 0ac35c69b..a4b855f15 100644 --- a/src/routes/docs/colors/ColorSwatchItem.svelte +++ b/src/routes/docs/colors/ColorSwatchItem.svelte @@ -1,13 +1,13 @@ <script lang="ts"> - import {theme_state_context} from '@fuzdev/fuz_ui/theme_state.svelte.ts'; + import { theme_state_context } from '@fuzdev/fuz_ui/theme_state.svelte.ts'; import StyleVariableButton from '@fuzdev/fuz_ui/StyleVariableButton.svelte'; - import {oklch_to_srgb, type Oklch} from '$lib/oklch.ts'; - import type {PaletteVariant} from '$lib/variable_data.ts'; + import { oklch_to_srgb, type Oklch } from '$lib/oklch.ts'; + import type { PaletteVariant } from '$lib/variable_data.ts'; const { intensity, - letter, + letter }: { intensity: string; letter: PaletteVariant; @@ -45,7 +45,7 @@ .map((c) => Math.round(Math.min(1, Math.max(0, c)) * 255) .toString(16) - .padStart(2, '0'), + .padStart(2, '0') ) .join('') ); diff --git a/src/routes/docs/colors/HueSwatch.svelte b/src/routes/docs/colors/HueSwatch.svelte index f78ca753c..b1d6678c0 100644 --- a/src/routes/docs/colors/HueSwatch.svelte +++ b/src/routes/docs/colors/HueSwatch.svelte @@ -1,14 +1,14 @@ <script lang="ts"> import StyleVariableButton from '@fuzdev/fuz_ui/StyleVariableButton.svelte'; - import type {PaletteVariant} from '$lib/variable_data.ts'; + import type { PaletteVariant } from '$lib/variable_data.ts'; const { letter, computed_styles, width = 48, height = 48, - description, + description }: { letter: PaletteVariant; computed_styles: CSSStyleDeclaration | null; diff --git a/src/routes/docs/elements/+page.svelte b/src/routes/docs/elements/+page.svelte index 8a3fd23ce..5519945c7 100644 --- a/src/routes/docs/elements/+page.svelte +++ b/src/routes/docs/elements/+page.svelte @@ -1,9 +1,9 @@ <script lang="ts"> import Code from '@fuzdev/fuz_code/Code.svelte'; - import {resolve} from '$app/paths'; + import { resolve } from '$app/paths'; import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import MdnLink from '@fuzdev/fuz_ui/MdnLink.svelte'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; @@ -27,9 +27,8 @@ <p> fuz_css applies default styles to semantic HTML elements in its <SourceFileLink path="style.css">reset stylesheet</SourceFileLink - >. The styles use <TomeLink slug="variables" /> and include appropriate spacing, so plain HTML - gets user-friendly styling and theme integration automatically. The defaults are low - specificity using + >. The styles use <TomeLink slug="variables" /> and include appropriate spacing, so plain HTML gets + user-friendly styling and theme integration automatically. The defaults are low specificity using <code>:where</code> so they're easy to override, and you can opt out by adding <code>.unstyled</code> to an element. @@ -54,11 +53,10 @@ This paragraph has no bottom margin because default spacing is omitted for the <code >:last-child</code > - of all otherwise-spaced elements, streamlining the common case. This has some unfortunate edge - cases that can usually by solved by adding <code>.mb_lg</code>. Coupling markup structure to - styles like this may be something we change, <a href="https://github.com/fuzdev/fuz_css" - >feedback</a - > is welcome. + of all otherwise-spaced elements, streamlining the common case. This has some unfortunate edge cases + that can usually by solved by adding <code>.mb_lg</code>. Coupling markup structure to styles + like this may be something we change, <a href="https://github.com/fuzdev/fuz_css">feedback</a> is + welcome. </p> </TomeSection> <TomeSection> diff --git a/src/routes/docs/examples/+page.svelte b/src/routes/docs/examples/+page.svelte index 215055fc4..974bbffdb 100644 --- a/src/routes/docs/examples/+page.svelte +++ b/src/routes/docs/examples/+page.svelte @@ -2,7 +2,7 @@ import ModuleLink from '@fuzdev/fuz_ui/ModuleLink.svelte'; import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; const tome = tome_get_by_slug('examples'); @@ -12,9 +12,8 @@ <TomeContent {tome}> <section class="column gap_md"> <p> - The <a href={GITHUB_EXAMPLES}>example repos</a> demonstrate the <TomeLink - slug="classes" - /> system using the + The <a href={GITHUB_EXAMPLES}>example repos</a> demonstrate the <TomeLink slug="classes" /> system + using the <a href="https://github.com/fuzdev/fuz_css/blob/main/src/lib/vite_plugin_fuz_css.ts" >Vite plugin</a >: diff --git a/src/routes/docs/forms/+page.svelte b/src/routes/docs/forms/+page.svelte index 803da61a8..fff44ca06 100644 --- a/src/routes/docs/forms/+page.svelte +++ b/src/routes/docs/forms/+page.svelte @@ -1,10 +1,10 @@ <script lang="ts"> import Code from '@fuzdev/fuz_code/Code.svelte'; - import {fly} from 'svelte/transition'; + import { fly } from 'svelte/transition'; import Alert from '@fuzdev/fuz_ui/Alert.svelte'; import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; import MdnLink from '@fuzdev/fuz_ui/MdnLink.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; @@ -87,8 +87,8 @@ /> </label> <p> - More info can be included in <code>{'<'}p></code> tags like this one. Here we could - include info about passwords. + More info can be included in <code>{'<'}p></code> tags like this one. Here we could include + info about passwords. </p> <label class:disabled={created_account}> <div class="title">lifestory</div> @@ -110,8 +110,8 @@ </form> {#if created_account} <form - in:fly={{y: -100, duration: ANIMATION_DURATION_FAST}} - out:fly={{y: 100, duration: ANIMATION_DURATION_FAST}} + in:fly={{ y: -100, duration: ANIMATION_DURATION_FAST }} + out:fly={{ y: 100, duration: ANIMATION_DURATION_FAST }} > <Alert status="error">cannot create account because the docs are fake</Alert> <button type="button" class="width:100%" onclick={() => (created_account = false)}> @@ -210,9 +210,9 @@ <TomeSectionHeader text="Size composites" /> <p> The <TomeLink slug="classes" hash="#Composite-classes">size composite classes</TomeLink> - <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code - > scale inputs and buttons, adjusting height and padding. Apply directly or on a container to - cascade to children. + <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code> scale + inputs and buttons, adjusting height and padding. Apply directly or on a container to cascade to + children. </p> <Code content={`<input class="xs" />\n<input class="sm" />\n<input />\n<input class="lg" />\n<input class="xl" />`} diff --git a/src/routes/docs/introduction/+page.svelte b/src/routes/docs/introduction/+page.svelte index 34026af5b..76322359e 100644 --- a/src/routes/docs/introduction/+page.svelte +++ b/src/routes/docs/introduction/+page.svelte @@ -4,7 +4,7 @@ import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import MdnLink from '@fuzdev/fuz_ui/MdnLink.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; import SourceFileLink from '$routes/SourceFileLink.svelte'; @@ -23,9 +23,9 @@ <Code lang={null} content="npm i -D @fuzdev/fuz_css" /> <p> Use the <TomeLink slug="classes" hash="Vite-plugin">Vite plugin</TomeLink> or - <TomeLink slug="classes" hash="Gro-generator">Gro generator</TomeLink> for bundled CSS that - includes theme variables, base styles, and utility classes in a single import. Add the - plugin to your Vite config: + <TomeLink slug="classes" hash="Gro-generator">Gro generator</TomeLink> for bundled CSS that includes + theme variables, base styles, and utility classes in a single import. Add the plugin to your Vite + config: </p> <Code lang="ts" @@ -64,8 +64,8 @@ import 'virtual:fuz.css'; // used utilities, \`base_css: null, variables: null\` <li>plain CSS</li> <li>minimal dependencies, all optional -- none needed if you only use the stylesheets</li> <li> - exports a reset stylesheet with <TomeLink slug="semantic" /> defaults that styles HTML - elements, and also exports the underlying data, helpers, and types for open-ended usage + exports a reset stylesheet with <TomeLink slug="semantic" /> defaults that styles HTML elements, + and also exports the underlying data, helpers, and types for open-ended usage </li> <li> supports <TomeLink slug="themes" /> with a basic theme stylesheet, <SourceFileLink @@ -76,9 +76,8 @@ import 'virtual:fuz.css'; // used utilities, \`base_css: null, variables: null\` > </li> <li> - supports optional <TomeLink slug="classes">utility classes</TomeLink> with three types - (token, composite, CSS-literal) and modifiers for responsive, state, color-scheme, and - pseudo-elements + supports optional <TomeLink slug="classes">utility classes</TomeLink> with three types (token, + composite, CSS-literal) and modifiers for responsive, state, color-scheme, and pseudo-elements </li> <li> uses its own concept of style <TomeLink slug="variables" />, a specialization of CSS diff --git a/src/routes/docs/layout/+page.svelte b/src/routes/docs/layout/+page.svelte index f58c767c1..b7f9046e2 100644 --- a/src/routes/docs/layout/+page.svelte +++ b/src/routes/docs/layout/+page.svelte @@ -1,12 +1,12 @@ <script lang="ts"> import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import StyleVariableButton from '@fuzdev/fuz_ui/StyleVariableButton.svelte'; - import {resolve} from '$app/paths'; + import { resolve } from '$app/paths'; - import {space_variants, distance_variants} from '$lib/variable_data.ts'; + import { space_variants, distance_variants } from '$lib/variable_data.ts'; const LIBRARY_ITEM_NAME = 'layout'; diff --git a/src/routes/docs/menuitem/todo.svelte b/src/routes/docs/menuitem/todo.svelte index 8e5f79417..7492ec3df 100644 --- a/src/routes/docs/menuitem/todo.svelte +++ b/src/routes/docs/menuitem/todo.svelte @@ -1,7 +1,7 @@ <script lang="ts"> - import {swallow} from '@fuzdev/fuz_util/dom.ts'; + import { swallow } from '@fuzdev/fuz_util/dom.ts'; import Code from '@fuzdev/fuz_code/Code.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; diff --git a/src/routes/docs/semantic/+page.svelte b/src/routes/docs/semantic/+page.svelte index 0cfa641c1..c531f53f6 100644 --- a/src/routes/docs/semantic/+page.svelte +++ b/src/routes/docs/semantic/+page.svelte @@ -1,9 +1,9 @@ <script lang="ts"> import Code from '@fuzdev/fuz_code/Code.svelte'; - import {resolve} from '$app/paths'; + import { resolve } from '$app/paths'; import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; @@ -28,9 +28,8 @@ <TomeSection> <TomeSectionHeader text="Low specificity" /> <p> - All opinionated styles use <code>:where()</code> selectors, giving them zero specificity - beyond the element itself. Your styles and utility classes override defaults without - specificity battles. + All opinionated styles use <code>:where()</code> selectors, giving them zero specificity beyond + the element itself. Your styles and utility classes override defaults without specificity battles. </p> <Code lang="css" @@ -51,9 +50,9 @@ <TomeSectionHeader text=".unstyled escape hatch" /> <p> Add the <code>.unstyled</code> - <TomeLink slug="classes" hash="Builtin-classes">builtin class</TomeLink> to opt out of - decorative styling while keeping reset normalizations. Works for both decorative containers - and interactive elements like links, buttons, inputs, and summary. + <TomeLink slug="classes" hash="Builtin-classes">builtin class</TomeLink> to opt out of decorative + styling while keeping reset normalizations. Works for both decorative containers and interactive + elements like links, buttons, inputs, and summary. </p> <Code lang="svelte" @@ -93,20 +92,19 @@ <p> The <code>--flow_margin</code> variable is unset by default, falling back to <code>var(--space_lg)</code>. Size composite classes like <code>.sm</code> and - <code>.lg</code> set <code>--flow_margin</code> to adjust vertical rhythm for all flow - elements and headings. + <code>.lg</code> set <code>--flow_margin</code> to adjust vertical rhythm for all flow elements + and headings. </p> <p> For elements not in the flow list, use the <code>.mb_flow</code> and <code>.mt_flow</code> - composite classes to get the same size-responsive spacing. Use <code>.mb_lg</code> when you - want a fixed value that ignores size composites. + composite classes to get the same size-responsive spacing. Use <code>.mb_lg</code> when you want + a fixed value that ignores size composites. </p> <aside> ⚠️ The <code>:not(:last-child)</code> creates unfortunate edge cases by coupling structure to style, including usage with Svelte's component-level CSS variables, because it adds a wrapper div. Perhaps the better global optimum is to omit the last child exception? This would add - unwanted margin in many cases, but perhaps that's better overall; <code>mb_0</code> removes - it. + unwanted margin in many cases, but perhaps that's better overall; <code>mb_0</code> removes it. </aside> </TomeSection> diff --git a/src/routes/docs/shading/+page.svelte b/src/routes/docs/shading/+page.svelte index 7af79aff0..3b4c3138b 100644 --- a/src/routes/docs/shading/+page.svelte +++ b/src/routes/docs/shading/+page.svelte @@ -3,14 +3,14 @@ import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; import ColorSchemeInput from '@fuzdev/fuz_ui/ColorSchemeInput.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; - import {theme_state_context} from '@fuzdev/fuz_ui/theme_state.svelte.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; + import { theme_state_context } from '@fuzdev/fuz_ui/theme_state.svelte.ts'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import MdnLink from '@fuzdev/fuz_ui/MdnLink.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import StyleVariableButton from '@fuzdev/fuz_ui/StyleVariableButton.svelte'; - import {shade_scale_variants, alpha_variants} from '$lib/variable_data.ts'; + import { shade_scale_variants, alpha_variants } from '$lib/variable_data.ts'; // @fuz-classes shade_50 const LIBRARY_ITEM_NAME = 'shading'; @@ -30,8 +30,8 @@ fuz_css offers a shading model built on <em>adaptive</em> style variables that respond to the <MdnLink path="Web/CSS/color-scheme" />. Adaptive means the underlying values change between light and dark modes to maintain consistent prominence -- low numbers stay subtle, high - numbers stay strong. Each <TomeLink slug="themes">theme</TomeLink> can implement light mode, - dark mode, or both. + numbers stay strong. Each <TomeLink slug="themes">theme</TomeLink> can implement light mode, dark + mode, or both. </p> <p> Light mode's starting point is plain white documents (like paper) where we subtract light to @@ -87,9 +87,9 @@ <ColorSchemeInput /> <aside class="mt_xl2 width_atmost_sm mx_auto"> <p> - tip: Try <button type="button" onclick={toggle_color_scheme}>toggling</button> between light - and dark to see how the shade scale adapts. Lower numbers stay near the surface, higher - numbers move toward maximum contrast. + tip: Try <button type="button" onclick={toggle_color_scheme}>toggling</button> between light and + dark to see how the shade scale adapts. Lower numbers stay near the surface, higher numbers move + toward maximum contrast. </p> </aside> </section> @@ -102,12 +102,12 @@ </p> <ul> <li> - <code>fg_NN</code> (foreground direction) - darkens in light mode, lightens in dark mode; - use for elevated surfaces like panels, cards, and hover states + <code>fg_NN</code> (foreground direction) - darkens in light mode, lightens in dark mode; use + for elevated surfaces like panels, cards, and hover states </li> <li> - <code>bg_NN</code> (background direction) - lightens in light mode, darkens in dark mode; - use for surfaces that blend toward the background + <code>bg_NN</code> (background direction) - lightens in light mode, darkens in dark mode; use + for surfaces that blend toward the background </li> </ul> <p> @@ -143,8 +143,8 @@ <TomeSection> <TomeSectionHeader text="Stacking behavior" tag="h4" /> <p> - Unlike the opaque shade scale, alpha overlays <strong>stack</strong> when nested. Each layer - adds more contrast: + Unlike the opaque shade scale, alpha overlays <strong>stack</strong> when nested. Each layer adds + more contrast: </p> <Code content={`<div class="fg_10 p_sm"> @@ -228,8 +228,7 @@ <TomeSection> <TomeSectionHeader text="When to use which" /> <p> - <strong>Use <code>fg_NN</code></strong> when you need stacking behavior or are building nested - UI: + <strong>Use <code>fg_NN</code></strong> when you need stacking behavior or are building nested UI: </p> <Code lang="css" @@ -243,8 +242,7 @@ background-color: var(--fg_10); background-color: var(--fg_20);`} /> <p class="mt_md"> - <strong>Use <code>shade_NN</code></strong> when you need explicit, predictable opaque - surfaces: + <strong>Use <code>shade_NN</code></strong> when you need explicit, predictable opaque surfaces: </p> <Code lang="css" @@ -259,8 +257,7 @@ background-color: var(--shade_min);`} /> <p class="mt_lg"> The composites (<code>.panel</code>, <code>.chip</code>, <code>.menuitem</code>) use - <code>fg_NN</code> for stacking. The page background uses <code>shade_00</code> as the opaque - base. + <code>fg_NN</code> for stacking. The page background uses <code>shade_00</code> as the opaque base. </p> </TomeSection> <TomeSection> diff --git a/src/routes/docs/shadows/+page.svelte b/src/routes/docs/shadows/+page.svelte index 6d38a2821..51269c7b7 100644 --- a/src/routes/docs/shadows/+page.svelte +++ b/src/routes/docs/shadows/+page.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; import ColorSchemeInput from '@fuzdev/fuz_ui/ColorSchemeInput.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; @@ -14,7 +14,7 @@ shadow_alpha_variants, intensity_variants, type PaletteVariant, - type IntensityVariant, + type IntensityVariant } from '$lib/variable_data.ts'; import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; @@ -35,8 +35,8 @@ <section> <p> fuz_css provides four semantic shadow types that build on the light model in the - <TomeLink slug="shading" /> docs: umbra for natural depth, highlight for rim lighting, glow - for light emphasis, and shroud for dark overlays. + <TomeLink slug="shading" /> docs: umbra for natural depth, highlight for rim lighting, glow for + light emphasis, and shroud for dark overlays. </p> </section> <TomeSection> @@ -88,9 +88,8 @@ <TomeSection> <TomeSectionHeader text="Colored shadows" /> <p> - Use <code>shadow_{'{letter}'}_{'{intensity}'}</code> classes to apply colored shadows. The - intensity controls the color's prominence -- 60 is a fine starting point for visible colored - shadows. + Use <code>shadow_{'{letter}'}_{'{intensity}'}</code> classes to apply colored shadows. The intensity + controls the color's prominence -- 60 is a fine starting point for visible colored shadows. </p> {#each palette_variants as color_variant (color_variant)} <TomeSection> @@ -103,7 +102,7 @@ {#snippet shadow_examples( color_variant: PaletteVariant | 'umbra' | 'highlight' | 'glow' | 'shroud' | null, - intensity: IntensityVariant = '60', + intensity: IntensityVariant = '60' )} {@const is_hue = color_variant && !['umbra', 'highlight', 'glow', 'shroud'].includes(color_variant)} diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index 58dd0d64c..1488be7ee 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -1,6 +1,6 @@ <script lang="ts"> import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import ColorSchemeInput from '@fuzdev/fuz_ui/ColorSchemeInput.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; @@ -9,22 +9,22 @@ import MdnLink from '@fuzdev/fuz_ui/MdnLink.svelte'; import ModuleLink from '@fuzdev/fuz_ui/ModuleLink.svelte'; import Code from '@fuzdev/fuz_code/Code.svelte'; - import {theme_state_context} from '@fuzdev/fuz_ui/theme_state.svelte.ts'; + import { theme_state_context } from '@fuzdev/fuz_ui/theme_state.svelte.ts'; - import {default_themes} from '$lib/themes.ts'; - import {necromancer_theme} from '$lib/themes/necromancer.ts'; - import {sunset_ember_theme} from '$lib/themes/sunset_ember.ts'; - import {brutalish_theme} from '$lib/themes/brutalish.ts'; - import {terminal_theme} from '$lib/themes/terminal.ts'; - import type {Theme} from '$lib/theme.ts'; + import { default_themes } from '$lib/themes.ts'; + import { necromancer_theme } from '$lib/themes/necromancer.ts'; + import { sunset_ember_theme } from '$lib/themes/sunset_ember.ts'; + import { brutalish_theme } from '$lib/themes/brutalish.ts'; + import { terminal_theme } from '$lib/themes/terminal.ts'; + import type { Theme } from '$lib/theme.ts'; import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; import ThemeEditor from '$routes/ThemeEditor.svelte'; import { ThemeEditorState, UNSAVED_THEME_NAME, - type ThemeEditorSnapshotData, + type ThemeEditorSnapshotData } from '$routes/theme_editor_state.svelte.ts'; - import type {Snapshot} from '@sveltejs/kit'; + import type { Snapshot } from '@sveltejs/kit'; const LIBRARY_ITEM_NAME = 'themes'; @@ -41,7 +41,7 @@ // the in-progress theme appears in the picker as soon as a knob moves const picker_themes: Array<Theme> = $derived([ ...themes, - ...(editor.dirty ? [editor.draft] : []), + ...(editor.dirty ? [editor.draft] : []) ]); // passed as ThemeInput's `select` (not `onselect`, which collides with the @@ -65,7 +65,7 @@ // persist the in-progress theme across navigation (history-entry-scoped) export const snapshot: Snapshot<ThemeEditorSnapshotData> = { capture: () => editor.to_snapshot(), - restore: (data) => editor.restore_snapshot(data), + restore: (data) => editor.restore_snapshot(data) }; </script> @@ -87,8 +87,8 @@ <TomeSectionHeader text="Color scheme" /> <p> fuz_css supports - <MdnLink path="Web/CSS/color-scheme" /> with dark and light modes. To apply dark mode - manually, add the <code>dark</code> class to the root <code>html</code> + <MdnLink path="Web/CSS/color-scheme" /> with dark and light modes. To apply dark mode manually, + add the <code>dark</code> class to the root <code>html</code> element. </p> <p> @@ -110,13 +110,12 @@ <TomeSection> <TomeSectionHeader text="Builtin themes" /> <UnfinishedImplementationWarning - >The builtin themes need more work, but the proof of concept is - ready!</UnfinishedImplementationWarning + >The builtin themes need more work, but the proof of concept is ready!</UnfinishedImplementationWarning > <p> - A theme is a simple JSON collection of <TomeLink slug="variables" /> that can be transformed - into CSS that set custom properties. Each variable can have values for light and/or dark color - schemes. In other words, "dark" isn't a theme, it's a mode that any theme can implement. + A theme is a simple JSON collection of <TomeLink slug="variables" /> that can be transformed into + CSS that set custom properties. Each variable can have values for light and/or dark color schemes. + In other words, "dark" isn't a theme, it's a mode that any theme can implement. </p> <p> Because the color system is derived, a theme is a set of <em>knob</em> values, not a @@ -126,9 +125,9 @@ <code>fuz.base</code> defaults, so overrides win regardless of stylesheet order. </p> <p> - These docs are a work in progress, for now see <ModuleLink - module_path="theme.ts" - /> and <ModuleLink module_path="themes.ts" />. + These docs are a work in progress, for now see <ModuleLink module_path="theme.ts" /> and <ModuleLink + module_path="themes.ts" + />. </p> <p>Selecting a theme loads its knobs into the editor below.</p> <div class="width_atmost_xs mb_lg"> @@ -139,8 +138,8 @@ <TomeSectionHeader text="Exemplar themes" /> <p> Beyond the registry, fuz_css ships expressive exemplar themes as importable modules under - <code>themes/</code> -- registry membership, not file location, is what separates builtins - from exemplars. Import one and pass it to your theme setup: + <code>themes/</code> -- registry membership, not file location, is what separates builtins from + exemplars. Import one and pass it to your theme setup: </p> <Code lang="ts" @@ -150,8 +149,8 @@ A theme can declare a single-scheme stance with <code>scheme: 'light' | 'dark'</code> -- the renderer then mirrors every scheme-adaptive default the theme doesn't override, so its one appearance renders in both color schemes, and pins - <MdnLink path="Web/CSS/color-scheme" /> to match so form controls and scrollbars agree. The - necromancer and terminal exemplars are dark-only this way, without hand-mirrored knob values. + <MdnLink path="Web/CSS/color-scheme" /> to match so form controls and scrollbars agree. The necromancer + and terminal exemplars are dark-only this way, without hand-mirrored knob values. </p> <div class="width_atmost_xs mb_lg"> <ThemeInput themes={exemplar_themes} select={select_theme} /> @@ -162,13 +161,13 @@ <p> Drag a knob and the whole page rethemes live -- extreme values can make the page hard to read, which is an honest signal, not a bug. Every edit updates a temporary "{UNSAVED_THEME_NAME}" - theme in the picker above; it persists across navigation until you leave or reset, so copy - the <code>Theme</code> object below to keep it. The scheme selector sets the theme's stance -- - a single-scheme theme renders its one appearance in both color schemes, so edits write the - base slots. The top band holds the semantic-tier moves: assign each intent (accent, neutral, - positive, negative, caution, info) to a palette letter -- or a custom angle -- and pull the - high-leverage levers. Below it, each axis section carries the granular knobs, with per-token - escape hatches folded away; the ramp strips repaint live as the derived scales move. + theme in the picker above; it persists across navigation until you leave or reset, so copy the + <code>Theme</code> object below to keep it. The scheme selector sets the theme's stance -- a single-scheme + theme renders its one appearance in both color schemes, so edits write the base slots. The top band + holds the semantic-tier moves: assign each intent (accent, neutral, positive, negative, caution, + info) to a palette letter -- or a custom angle -- and pull the high-leverage levers. Below it, each + axis section carries the granular knobs, with per-token escape hatches folded away; the ramp strips + repaint live as the derived scales move. </p> <ThemeEditor {editor} {theme_state} /> </TomeSection> @@ -179,9 +178,9 @@ <code>Theme</code> in tests or CI. </p> <p> - <code>validate_theme(theme)</code> is the structural lint: unknown variable names are errors, - while type and range mismatches on the knob-tier variables are advisory warnings. It returns - an array of issues -- empty means the theme is structurally sound. + <code>validate_theme(theme)</code> is the structural lint: unknown variable names are errors, while + type and range mismatches on the knob-tier variables are advisory warnings. It returns an array + of issues -- empty means the theme is structurally sound. </p> <p> <code>check_theme(theme)</code> runs the gamut, ramp-monotonicity, and contrast gates against diff --git a/src/routes/docs/tomes.ts b/src/routes/docs/tomes.ts index d269aaefb..9b47544f1 100644 --- a/src/routes/docs/tomes.ts +++ b/src/routes/docs/tomes.ts @@ -1,4 +1,4 @@ -import type {Tome} from '@fuzdev/fuz_ui/tome.ts'; +import type { Tome } from '@fuzdev/fuz_ui/tome.ts'; import introduction from './introduction/+page.svelte'; import api from './api/+page.svelte'; @@ -28,7 +28,7 @@ export const tomes: Array<Tome> = [ Component: introduction, related_tomes: ['api'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'api', @@ -36,7 +36,7 @@ export const tomes: Array<Tome> = [ Component: api, related_tomes: [], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'examples', @@ -44,7 +44,7 @@ export const tomes: Array<Tome> = [ Component: examples, related_tomes: ['classes'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'semantic', @@ -52,7 +52,7 @@ export const tomes: Array<Tome> = [ Component: semantic, related_tomes: ['buttons', 'elements', 'forms', 'typography'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'themes', @@ -60,7 +60,7 @@ export const tomes: Array<Tome> = [ Component: themes, related_tomes: ['variables', 'colors', 'typography'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'variables', @@ -68,7 +68,7 @@ export const tomes: Array<Tome> = [ Component: variables, related_tomes: ['themes'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'classes', @@ -76,7 +76,7 @@ export const tomes: Array<Tome> = [ Component: classes, related_tomes: ['chips', 'elements'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'colors', @@ -84,7 +84,7 @@ export const tomes: Array<Tome> = [ Component: colors, related_tomes: ['themes', 'buttons', 'chips', 'borders', 'shading', 'shadows'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'shading', @@ -92,7 +92,7 @@ export const tomes: Array<Tome> = [ Component: shading, related_tomes: ['colors', 'borders', 'shadows', 'typography'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'buttons', @@ -100,7 +100,7 @@ export const tomes: Array<Tome> = [ Component: buttons, related_tomes: ['colors', 'chips', 'elements', 'forms', 'borders'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'chips', @@ -108,7 +108,7 @@ export const tomes: Array<Tome> = [ Component: chips, related_tomes: ['colors', 'buttons', 'classes'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'elements', @@ -116,7 +116,7 @@ export const tomes: Array<Tome> = [ Component: elements, related_tomes: ['buttons', 'forms', 'classes', 'typography', 'borders', 'layout'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'forms', @@ -124,7 +124,7 @@ export const tomes: Array<Tome> = [ Component: forms, related_tomes: ['buttons', 'elements', 'borders'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'typography', @@ -132,7 +132,7 @@ export const tomes: Array<Tome> = [ Component: typography, related_tomes: ['themes', 'shading', 'elements'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'borders', @@ -140,7 +140,7 @@ export const tomes: Array<Tome> = [ Component: borders, related_tomes: ['colors', 'shading', 'buttons', 'elements', 'forms', 'shadows'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'shadows', @@ -148,7 +148,7 @@ export const tomes: Array<Tome> = [ Component: shadows, related_tomes: ['colors', 'shading', 'borders'], related_modules: [], - related_declarations: [], + related_declarations: [] }, { slug: 'layout', @@ -156,8 +156,8 @@ export const tomes: Array<Tome> = [ Component: layout, related_tomes: ['elements'], related_modules: [], - related_declarations: [], - }, + related_declarations: [] + } // TODO maybe? or do styles like this belong elsewhere? classes? problem is we'll have a classes page for the variables // { // slug: 'menuitem', diff --git a/src/routes/docs/typography/+page.svelte b/src/routes/docs/typography/+page.svelte index 328a38c87..07d905535 100644 --- a/src/routes/docs/typography/+page.svelte +++ b/src/routes/docs/typography/+page.svelte @@ -3,7 +3,7 @@ import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; import MdnLink from '@fuzdev/fuz_ui/MdnLink.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import Details from '@fuzdev/fuz_ui/Details.svelte'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; @@ -11,13 +11,13 @@ import FontWeightControl from '$routes/FontWeightControl.svelte'; import FontSizeControl from '$routes/FontSizeControl.svelte'; - import {default_variables} from '$lib/variables.ts'; + import { default_variables } from '$lib/variables.ts'; import IconSizes from './IconSizes.svelte'; import { line_height_names, font_size_names, text_scale_variants, - font_family_variants, + font_family_variants } from '$lib/variable_data.ts'; import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; @@ -241,8 +241,8 @@ </TomeSectionHeader> <p> The <code>.md</code> - <TomeLink slug="classes" hash="#Composite-classes">composite class</TomeLink> resets sizing to - the defaults. Use it inside a sized container to restore normal sizing for a subtree. + <TomeLink slug="classes" hash="#Composite-classes">composite class</TomeLink> resets sizing to the + defaults. Use it inside a sized container to restore normal sizing for a subtree. </p> <Code content={`<div class="sm">\n\t<p>small text</p>\n\t<div class="md">\n\t\t<p>back to normal</p>\n\t</div>\n</div>`} diff --git a/src/routes/docs/typography/IconSizes.svelte b/src/routes/docs/typography/IconSizes.svelte index a5eb179d0..e6cddb8c5 100644 --- a/src/routes/docs/typography/IconSizes.svelte +++ b/src/routes/docs/typography/IconSizes.svelte @@ -3,7 +3,7 @@ import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import StyleVariableButton from '@fuzdev/fuz_ui/StyleVariableButton.svelte'; - import {icon_sizes} from '$lib/variable_data.ts'; + import { icon_sizes } from '$lib/variable_data.ts'; </script> <TomeSection> diff --git a/src/routes/docs/variables/+page.svelte b/src/routes/docs/variables/+page.svelte index 0c8fa899d..7e027f054 100644 --- a/src/routes/docs/variables/+page.svelte +++ b/src/routes/docs/variables/+page.svelte @@ -3,13 +3,13 @@ import TomeContent from '@fuzdev/fuz_ui/TomeContent.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; import MdnLink from '@fuzdev/fuz_ui/MdnLink.svelte'; - import {tome_get_by_slug} from '@fuzdev/fuz_ui/tome.ts'; + import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeSectionHeader from '@fuzdev/fuz_ui/TomeSectionHeader.svelte'; import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import ModuleLink from '@fuzdev/fuz_ui/ModuleLink.svelte'; import StyleVariableButton from '@fuzdev/fuz_ui/StyleVariableButton.svelte'; - import {default_variables} from '$lib/variables.ts'; + import { default_variables } from '$lib/variables.ts'; import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; const LIBRARY_ITEM_NAME = 'variables'; @@ -62,14 +62,13 @@ high-leverage variables (intent bindings like <code>hue_accent</code>, levers like <code>chroma_scale</code> and <code>radius_scale</code>, the lightness curve knobs) and everything downstream re-derives, while any individual variable stays pinnable as the escape - hatch. See <TomeLink slug="colors" /> for the color system and <TomeLink slug="themes" /> for - theming. + hatch. See <TomeLink slug="colors" /> for the color system and <TomeLink slug="themes" /> for theming. </p> <p> In <TomeLink slug="classes" hash="What-gets-included">bundled mode</TomeLink>, only the variables your code uses are emitted, along with any they depend on. The full - <ModuleLink module_path="theme.css" /> stylesheet ships every variable, for utility-only mode - and direct imports. + <ModuleLink module_path="theme.css" /> stylesheet ships every variable, for utility-only mode and + direct imports. </p> </section> <section> diff --git a/src/routes/library.ts b/src/routes/library.ts index b2aa8dff5..b47f2089c 100644 --- a/src/routes/library.ts +++ b/src/routes/library.ts @@ -1,5 +1,5 @@ -import {library_json_from_modules} from '@fuzdev/fuz_util/library_json.ts'; -import {modules} from 'virtual:svelte-docinfo'; +import { library_json_from_modules } from '@fuzdev/fuz_util/library_json.ts'; +import { modules } from 'virtual:svelte-docinfo'; import pkg_json from 'virtual:pkg.json'; export const library_json = library_json_from_modules(pkg_json, modules); diff --git a/src/routes/theme_editor_state.svelte.ts b/src/routes/theme_editor_state.svelte.ts index ff6cd34bf..8e9f58c54 100644 --- a/src/routes/theme_editor_state.svelte.ts +++ b/src/routes/theme_editor_state.svelte.ts @@ -1,10 +1,10 @@ -import {SvelteMap} from 'svelte/reactivity'; +import { SvelteMap } from 'svelte/reactivity'; -import type {Theme, ThemeScheme} from '$lib/theme.ts'; -import type {StyleVariable} from '$lib/variable.ts'; -import {default_variables} from '$lib/variables.ts'; -import {theme_knob_by_name} from '$lib/knobs.ts'; -import type {ColorSchemeVariant} from '$lib/variable_data.ts'; +import type { Theme, ThemeScheme } from '$lib/theme.ts'; +import type { StyleVariable } from '$lib/variable.ts'; +import { default_variables } from '$lib/variables.ts'; +import { theme_knob_by_name } from '$lib/knobs.ts'; +import type { ColorSchemeVariant } from '$lib/variable_data.ts'; // TODO upstream to fuz_ui @@ -17,7 +17,7 @@ import type {ColorSchemeVariant} from '$lib/variable_data.ts'; export const UNSAVED_THEME_NAME = 'unsaved'; const default_variable_by_name: Map<string, StyleVariable> = new Map( - default_variables.map((v) => [v.name, v]), + default_variables.map((v) => [v.name, v]) ); export interface SlotOverride { @@ -66,18 +66,18 @@ export class ThemeEditorState { } readonly base_theme: Theme = $derived( - this.themes.find((t) => t.name === this.based_on) ?? this.themes[0]!, + this.themes.find((t) => t.name === this.based_on) ?? this.themes[0]! ); readonly base_scheme: ThemeScheme = $derived(this.base_theme.scheme ?? 'dual'); /** The single-scheme stance, `null` for dual themes. */ readonly stance: 'light' | 'dark' | null = $derived( - this.scheme === 'light' || this.scheme === 'dark' ? this.scheme : null, + this.scheme === 'light' || this.scheme === 'dark' ? this.scheme : null ); readonly base_variable_by_name: Map<string, StyleVariable> = $derived( - new Map(this.base_theme.variables.map((v) => [v.name, v])), + new Map(this.base_theme.variables.map((v) => [v.name, v])) ); readonly dirty: boolean = $derived(this.overrides.size > 0 || this.scheme !== this.base_scheme); @@ -125,7 +125,7 @@ export class ThemeEditorState { } if (dark !== undefined && dark === light) dark = undefined; if (light === undefined && dark === undefined) return null; - const merged: StyleVariable = {name}; + const merged: StyleVariable = { name }; if (light !== undefined) merged.light = light; if (dark !== undefined) merged.dark = dark; return merged; @@ -135,14 +135,14 @@ export class ThemeEditorState { readonly draft: Theme = $derived({ name: UNSAVED_THEME_NAME, variables: this.merged_variables, - ...(this.stance ? {scheme: this.stance} : {}), + ...(this.stance ? { scheme: this.stance } : {}) }); /** The copyable theme, carrying the user's chosen name. */ readonly output: Theme = $derived({ name: this.name, variables: this.merged_variables, - ...(this.stance ? {scheme: this.stance} : {}), + ...(this.stance ? { scheme: this.stance } : {}) }); /** @@ -175,7 +175,7 @@ export class ThemeEditorState { // under a stance edits always write the base slot - dual slots are // meaningless when one appearance renders in both color schemes const slot = !this.stance && adaptive ? scheme : 'light'; - this.overrides.set(name, {...o, [slot]: value}); + this.overrides.set(name, { ...o, [slot]: value }); } /** @@ -195,7 +195,7 @@ export class ThemeEditorState { if (value === undefined) { this.overrides.delete(name); } else { - this.overrides.set(name, {light: value}); + this.overrides.set(name, { light: value }); } } } @@ -229,7 +229,7 @@ export class ThemeEditorState { name: this.name, based_on: this.based_on, scheme: this.scheme, - overrides: Array.from(this.overrides.entries()).map(([name, o]) => [name, {...o}]), + overrides: Array.from(this.overrides.entries()).map(([name, o]) => [name, { ...o }]) }; } @@ -242,7 +242,7 @@ export class ThemeEditorState { this.scheme = data.scheme ?? this.base_scheme; this.overrides.clear(); for (const [name, o] of data.overrides) { - this.overrides.set(name, {...o}); + this.overrides.set(name, { ...o }); } } } diff --git a/src/test/class_variable_index.test.ts b/src/test/class_variable_index.test.ts index 7e6ae6252..e5fbfed38 100644 --- a/src/test/class_variable_index.test.ts +++ b/src/test/class_variable_index.test.ts @@ -1,18 +1,18 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; import { build_class_variable_index, get_class_variables, collect_class_variables, - get_classes_using_variable, + get_classes_using_variable } from '$lib/class_variable_index.ts'; -import {css_class_definitions} from '$lib/css_class_definitions.ts'; -import type {CssClassDefinition} from '$lib/css_class_generation.ts'; +import { css_class_definitions } from '$lib/css_class_definitions.ts'; +import type { CssClassDefinition } from '$lib/css_class_generation.ts'; describe('build_class_variable_index', () => { test('declaration with variable', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - text_lg: {declaration: 'font-size: var(--font_size_lg);'}, + text_lg: { declaration: 'font-size: var(--font_size_lg);' } }; const index = build_class_variable_index(definitions); @@ -24,8 +24,8 @@ describe('build_class_variable_index', () => { const definitions: Record<string, CssClassDefinition | undefined> = { button: { declaration: - 'color: var(--text_color); background: var(--bg_color); border: var(--border_width) solid;', - }, + 'color: var(--text_color); background: var(--bg_color); border: var(--border_width) solid;' + } }; const index = build_class_variable_index(definitions); @@ -38,7 +38,7 @@ describe('build_class_variable_index', () => { test('declaration without variables', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - flex: {declaration: 'display: flex;'}, + flex: { declaration: 'display: flex;' } }; const index = build_class_variable_index(definitions); @@ -50,8 +50,8 @@ describe('build_class_variable_index', () => { test('ruleset with variables', () => { const definitions: Record<string, CssClassDefinition | undefined> = { card: { - ruleset: `.card { padding: var(--space_md); border: 1px solid var(--border_color); }`, - }, + ruleset: `.card { padding: var(--space_md); border: 1px solid var(--border_color); }` + } }; const index = build_class_variable_index(definitions); @@ -63,7 +63,7 @@ describe('build_class_variable_index', () => { test('composes-only definition', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - box: {composes: ['flex', 'center']}, + box: { composes: ['flex', 'center'] } }; const index = build_class_variable_index(definitions); @@ -75,8 +75,8 @@ describe('build_class_variable_index', () => { test('undefined definition', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - exists: {declaration: 'color: var(--text);'}, - missing: undefined, + exists: { declaration: 'color: var(--text);' }, + missing: undefined }; const index = build_class_variable_index(definitions); @@ -104,7 +104,9 @@ describe('build_class_variable_index', () => { test('color classes', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - palette_a_50: {declaration: 'color: var(--palette_a_50); --text_color: var(--palette_a_50);'}, + palette_a_50: { + declaration: 'color: var(--palette_a_50); --text_color: var(--palette_a_50);' + } }; const index = build_class_variable_index(definitions); @@ -114,7 +116,7 @@ describe('build_class_variable_index', () => { test('nested var fallback', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - custom: {declaration: 'padding: var(--custom, var(--fallback));'}, + custom: { declaration: 'padding: var(--custom, var(--fallback));' } }; const index = build_class_variable_index(definitions); @@ -127,7 +129,7 @@ describe('build_class_variable_index', () => { describe('get_class_variables', () => { test('existing class', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - p_md: {declaration: 'padding: var(--space_md);'}, + p_md: { declaration: 'padding: var(--space_md);' } }; const index = build_class_variable_index(definitions); @@ -149,9 +151,9 @@ describe('get_class_variables', () => { describe('collect_class_variables', () => { test('multiple classes', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - p_md: {declaration: 'padding: var(--space_md);'}, - m_lg: {declaration: 'margin: var(--space_lg);'}, - text_sm: {declaration: 'font-size: var(--font_size_sm);'}, + p_md: { declaration: 'padding: var(--space_md);' }, + m_lg: { declaration: 'margin: var(--space_lg);' }, + text_sm: { declaration: 'font-size: var(--font_size_sm);' } }; const index = build_class_variable_index(definitions); @@ -164,7 +166,7 @@ describe('collect_class_variables', () => { test('with unknown classes', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - known: {declaration: 'color: var(--text);'}, + known: { declaration: 'color: var(--text);' } }; const index = build_class_variable_index(definitions); @@ -178,9 +180,9 @@ describe('collect_class_variables', () => { describe('get_classes_using_variable', () => { test('finds classes using a variable', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - p_md: {declaration: 'padding: var(--space_md);'}, - m_md: {declaration: 'margin: var(--space_md);'}, - text_lg: {declaration: 'font-size: var(--font_size_lg);'}, + p_md: { declaration: 'padding: var(--space_md);' }, + m_md: { declaration: 'margin: var(--space_md);' }, + text_lg: { declaration: 'font-size: var(--font_size_lg);' } }; const index = build_class_variable_index(definitions); @@ -193,7 +195,7 @@ describe('get_classes_using_variable', () => { test('unused variable returns empty array', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - p_md: {declaration: 'padding: var(--space_md);'}, + p_md: { declaration: 'padding: var(--space_md);' } }; const index = build_class_variable_index(definitions); diff --git a/src/test/css_bundled_resolution.diagnostics.test.ts b/src/test/css_bundled_resolution.diagnostics.test.ts index 7c481b11a..0ec6a2672 100644 --- a/src/test/css_bundled_resolution.diagnostics.test.ts +++ b/src/test/css_bundled_resolution.diagnostics.test.ts @@ -6,16 +6,16 @@ * @module */ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {resolve_css} from '$lib/css_bundled_resolution.ts'; -import {create_test_fixtures, empty_detection} from './css_bundled_resolution_fixtures.ts'; +import { resolve_css } from '$lib/css_bundled_resolution.ts'; +import { create_test_fixtures, empty_detection } from './css_bundled_resolution_fixtures.ts'; describe('resolve_css diagnostics', () => { describe('typo detection for variables', () => { test('emits warning for typo of known variable', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'color_primary', light: 'blue'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'color_primary', light: 'blue' } ]); const result = resolve_css({ @@ -26,7 +26,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), // 'color_primry' is a typo of 'color_primary' (missing 'a') detected_css_variables: new Set(['color_primary', 'color_primry']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.strictEqual(result.diagnostics.length, 1); @@ -39,9 +39,9 @@ describe('resolve_css diagnostics', () => { test('emits warning for typo in transitive dep', () => { // Variable references a typo that's similar to another variable - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'main_color', light: 'var(--main_colr)'}, // references typo - {name: 'main_colour', light: 'red'}, // similar - will be suggested + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'main_color', light: 'var(--main_colr)' }, // references typo + { name: 'main_colour', light: 'red' } // similar - will be suggested ]); const result = resolve_css({ @@ -51,7 +51,7 @@ describe('resolve_css diagnostics', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['main_color']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.strictEqual(result.diagnostics.length, 1); @@ -61,10 +61,10 @@ describe('resolve_css diagnostics', () => { }); test('handles multiple typos', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'background_color', light: '#fff'}, - {name: 'foreground_color', light: '#000'}, - {name: 'border_radius', light: '4px'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'background_color', light: '#fff' }, + { name: 'foreground_color', light: '#000' }, + { name: 'border_radius', light: '4px' } ]); const result = resolve_css({ @@ -77,9 +77,9 @@ describe('resolve_css diagnostics', () => { detected_css_variables: new Set([ 'backgroud_color', // typo of background_color 'forground_color', // typo of foreground_color - 'boarder_radius', // typo of border_radius + 'boarder_radius' // typo of border_radius ]), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.strictEqual(result.diagnostics.length, 3); @@ -90,9 +90,9 @@ describe('resolve_css diagnostics', () => { }); test('no warning for user-defined variables (not similar to theme vars)', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'color_primary', light: 'blue'}, - {name: 'spacing_md', light: '16px'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'color_primary', light: 'blue' }, + { name: 'spacing_md', light: '16px' } ]); const result = resolve_css({ @@ -103,7 +103,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), // These are user-defined, not similar to any theme variable detected_css_variables: new Set(['fill', 'shadow', 'icon_size', 'my_custom_var']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); // No warnings because these don't look like typos @@ -111,9 +111,9 @@ describe('resolve_css diagnostics', () => { }); test('no diagnostics when all exist', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'color', light: 'blue'}, - {name: 'space', light: '16px'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'color', light: 'blue' }, + { name: 'space', light: '16px' } ]); const result = resolve_css({ @@ -123,7 +123,7 @@ describe('resolve_css diagnostics', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['color', 'space']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.strictEqual(result.diagnostics.length, 0); @@ -132,9 +132,9 @@ describe('resolve_css diagnostics', () => { describe('unmatched elements', () => { test('no warning by default', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: red; }`, - [], + [] ); const result = resolve_css({ @@ -144,16 +144,16 @@ describe('resolve_css diagnostics', () => { detected_elements: new Set(['button', 'custom-element', 'my-widget']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.strictEqual(result.diagnostics.length, 0); }); test('warns when warn_unmatched_elements enabled', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: red; }`, - [], + [] ); const result = resolve_css({ @@ -164,7 +164,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - warn_unmatched_elements: true, + warn_unmatched_elements: true }); assert.strictEqual(result.diagnostics.length, 1); @@ -174,9 +174,9 @@ describe('resolve_css diagnostics', () => { }); test('warns for multiple unmatched', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: red; }`, - [], + [] ); const result = resolve_css({ @@ -187,7 +187,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - warn_unmatched_elements: true, + warn_unmatched_elements: true }); assert.strictEqual(result.diagnostics.length, 3); @@ -198,13 +198,13 @@ describe('resolve_css diagnostics', () => { }); test('no warning when all have rules', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: red; } input { border: 1px solid; } a { text-decoration: none; } `, - [], + [] ); const result = resolve_css({ @@ -215,7 +215,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - warn_unmatched_elements: true, + warn_unmatched_elements: true }); assert.strictEqual(result.diagnostics.length, 0); @@ -224,8 +224,8 @@ describe('resolve_css diagnostics', () => { describe('explicit variables (@fuz-variables)', () => { test('emits error for explicit variable not in theme', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a_50', light: 'blue'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a_50', light: 'blue' } ]); const result = resolve_css({ @@ -236,7 +236,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - explicit_variables: new Set(['nonexistent_var']), + explicit_variables: new Set(['nonexistent_var']) }); assert.strictEqual(result.diagnostics.length, 1); @@ -248,9 +248,9 @@ describe('resolve_css diagnostics', () => { }); test('no error when explicit variable exists in theme', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a_50', light: 'blue'}, - {name: 'shade_40', light: '#ccc'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a_50', light: 'blue' }, + { name: 'shade_40', light: '#ccc' } ]); // Callers (gen_fuz_css/vite_plugin) add explicit_variables to detected_css_variables @@ -262,7 +262,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(['palette_a_50', 'shade_40']), utility_variables_used: new Set(), - explicit_variables: new Set(['palette_a_50', 'shade_40']), + explicit_variables: new Set(['palette_a_50', 'shade_40']) }); assert.strictEqual(result.diagnostics.length, 0); @@ -272,9 +272,9 @@ describe('resolve_css diagnostics', () => { }); test('suggests similar variable for typo', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'shade_40', light: '#ccc'}, - {name: 'shade_50', light: '#999'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'shade_40', light: '#ccc' }, + { name: 'shade_50', light: '#999' } ]); const result = resolve_css({ @@ -285,7 +285,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - explicit_variables: new Set(['shade_4']), // typo + explicit_variables: new Set(['shade_4']) // typo }); assert.strictEqual(result.diagnostics.length, 1); @@ -296,9 +296,9 @@ describe('resolve_css diagnostics', () => { }); test('errors for multiple explicit variables with mix of valid and invalid', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a_50', light: 'blue'}, - {name: 'shade_40', light: '#ccc'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a_50', light: 'blue' }, + { name: 'shade_40', light: '#ccc' } ]); const result = resolve_css({ @@ -309,7 +309,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(['palette_a_50', 'shade_40']), utility_variables_used: new Set(), - explicit_variables: new Set(['palette_a_50', 'bad_var_1', 'shade_40', 'bad_var_2']), + explicit_variables: new Set(['palette_a_50', 'bad_var_1', 'shade_40', 'bad_var_2']) }); // Only the 2 invalid variables produce errors @@ -324,9 +324,9 @@ describe('resolve_css diagnostics', () => { }); test('explicit variable resolves transitive dependencies', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'base_hue', light: '210'}, - {name: 'derived_color', light: 'hsl(var(--base_hue) 50% 50%)'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'base_hue', light: '210' }, + { name: 'derived_color', light: 'hsl(var(--base_hue) 50% 50%)' } ]); const result = resolve_css({ @@ -337,7 +337,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(['derived_color']), utility_variables_used: new Set(), - explicit_variables: new Set(['derived_color']), + explicit_variables: new Set(['derived_color']) }); assert.strictEqual(result.diagnostics.length, 0); @@ -346,8 +346,8 @@ describe('resolve_css diagnostics', () => { }); test('exclude_variables suppresses explicit_variables error', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a_50', light: 'blue'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a_50', light: 'blue' } ]); const result = resolve_css({ @@ -359,7 +359,7 @@ describe('resolve_css diagnostics', () => { detected_css_variables: new Set(), utility_variables_used: new Set(), explicit_variables: new Set(['nonexistent_var', 'also_missing']), - exclude_variables: ['nonexistent_var'], + exclude_variables: ['nonexistent_var'] }); // Only 'also_missing' errors - 'nonexistent_var' is suppressed by exclude_variables @@ -371,9 +371,9 @@ describe('resolve_css diagnostics', () => { describe('explicit elements (@fuz-elements)', () => { test('emits error for explicit element with no rules', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: red; }`, - [], + [] ); const result = resolve_css({ @@ -384,7 +384,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - explicit_elements: new Set(['dialog']), + explicit_elements: new Set(['dialog']) }); assert.strictEqual(result.diagnostics.length, 1); @@ -395,12 +395,12 @@ describe('resolve_css diagnostics', () => { }); test('no error when explicit element has rules', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: red; } dialog { padding: 1rem; } `, - [], + [] ); const result = resolve_css({ @@ -411,19 +411,19 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - explicit_elements: new Set(['dialog']), + explicit_elements: new Set(['dialog']) }); assert.strictEqual(result.diagnostics.length, 0); }); test('suggests similar element for typo', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: red; } dialog { padding: 1rem; } `, - [], + [] ); const result = resolve_css({ @@ -434,7 +434,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - explicit_elements: new Set(['dilog']), // typo + explicit_elements: new Set(['dilog']) // typo }); assert.strictEqual(result.diagnostics.length, 1); @@ -445,9 +445,9 @@ describe('resolve_css diagnostics', () => { }); test('errors for multiple explicit elements without rules', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: red; }`, - [], + [] ); const result = resolve_css({ @@ -458,7 +458,7 @@ describe('resolve_css diagnostics', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - explicit_elements: new Set(['dialog', 'details', 'summary']), + explicit_elements: new Set(['dialog', 'details', 'summary']) }); assert.strictEqual(result.diagnostics.length, 3); @@ -466,9 +466,9 @@ describe('resolve_css diagnostics', () => { }); test('exclude_elements suppresses explicit_elements error', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: red; }`, - [], + [] ); const result = resolve_css({ @@ -480,7 +480,7 @@ describe('resolve_css diagnostics', () => { detected_css_variables: new Set(), utility_variables_used: new Set(), explicit_elements: new Set(['dialog', 'details']), - exclude_elements: ['dialog'], + exclude_elements: ['dialog'] }); // Only 'details' errors - 'dialog' is suppressed by exclude_elements @@ -492,9 +492,9 @@ describe('resolve_css diagnostics', () => { describe('exclude_variables footgun', () => { test('warns when an excluded variable is referenced by an included rule', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: var(--brand); }`, - [{name: 'brand', light: 'blue'}], + [{ name: 'brand', light: 'blue' }] ); const result = resolve_css({ @@ -503,7 +503,7 @@ describe('resolve_css diagnostics', () => { variable_graph, class_variable_index, detected_elements: new Set(['button']), - exclude_variables: ['brand'], + exclude_variables: ['brand'] }); assert.strictEqual(result.diagnostics.length, 1); @@ -515,8 +515,8 @@ describe('resolve_css diagnostics', () => { }); test('warns when an excluded variable is referenced directly in source', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'brand', light: 'blue'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'brand', light: 'blue' } ]); const result = resolve_css({ @@ -525,7 +525,7 @@ describe('resolve_css diagnostics', () => { variable_graph, class_variable_index, detected_css_variables: new Set(['brand']), - exclude_variables: ['brand'], + exclude_variables: ['brand'] }); assert.strictEqual(result.diagnostics.length, 1); @@ -533,12 +533,12 @@ describe('resolve_css diagnostics', () => { }); test('no warning when the excluded variable is unused', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: var(--brand); }`, [ - {name: 'brand', light: 'blue'}, - {name: 'unused', light: 'red'}, - ], + { name: 'brand', light: 'blue' }, + { name: 'unused', light: 'red' } + ] ); const result = resolve_css({ @@ -547,15 +547,15 @@ describe('resolve_css diagnostics', () => { variable_graph, class_variable_index, detected_elements: new Set(['button']), - exclude_variables: ['unused'], + exclude_variables: ['unused'] }); assert.strictEqual(result.diagnostics.length, 0); }); test('no warning when force-included via additional_variables then excluded', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'brand', light: 'blue'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'brand', light: 'blue' } ]); const result = resolve_css({ @@ -565,15 +565,15 @@ describe('resolve_css diagnostics', () => { class_variable_index, // force-included, not referenced by any shipped CSS — exclude is a clean override additional_variables: ['brand'], - exclude_variables: ['brand'], + exclude_variables: ['brand'] }); assert.strictEqual(result.diagnostics.length, 0); }); test('no warning when the excluded name is not a theme variable', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'brand', light: 'blue'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'brand', light: 'blue' } ]); const result = resolve_css({ @@ -583,7 +583,7 @@ describe('resolve_css diagnostics', () => { class_variable_index, // referenced in source but user-defined (not a known theme variable) detected_css_variables: new Set(['my_custom']), - exclude_variables: ['my_custom'], + exclude_variables: ['my_custom'] }); assert.strictEqual(result.diagnostics.length, 0); diff --git a/src/test/css_bundled_resolution.test.ts b/src/test/css_bundled_resolution.test.ts index cfb2414b4..be3bf6cb1 100644 --- a/src/test/css_bundled_resolution.test.ts +++ b/src/test/css_bundled_resolution.test.ts @@ -7,11 +7,11 @@ * @module */ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {resolve_css} from '$lib/css_bundled_resolution.ts'; -import {create_test_fixtures, empty_detection} from './css_bundled_resolution_fixtures.ts'; -import {assert_css_order} from './test_helpers.ts'; +import { resolve_css } from '$lib/css_bundled_resolution.ts'; +import { create_test_fixtures, empty_detection } from './css_bundled_resolution_fixtures.ts'; +import { assert_css_order } from './test_helpers.ts'; /** * Core rules that should always be included regardless of detected elements. @@ -21,47 +21,47 @@ const core_rules_cases = [ name: '* selector', css: '* { box-sizing: border-box; }', expected: 'box-sizing: border-box', - not_expected: null, + not_expected: null }, { name: ':root selector', css: ':root { font-size: 16px; }\nbutton { color: red; }', expected: 'font-size: 16px', - not_expected: 'color: red', + not_expected: 'color: red' }, { name: 'body selector', css: 'body { margin: 0; background: white; }\nbutton { color: blue; }', expected: 'margin: 0', - not_expected: null, + not_expected: null }, { name: '@font-face rules', css: '@font-face { font-family: "Custom"; src: url("font.woff2"); }\nbutton { color: red; }', expected: '@font-face', - not_expected: 'button', + not_expected: 'button' }, { name: '@media (prefers-reduced-motion) rules', css: '@media (prefers-reduced-motion: reduce) { *, ::before, ::after { animation-duration: 0.01ms !important; } }\nbutton { color: red; }', expected: 'prefers-reduced-motion', - not_expected: 'button', - }, + not_expected: 'button' + } ]; describe('resolve_css', () => { describe('core rules', () => { - test.each(core_rules_cases)('includes $name', ({css, expected, not_expected}) => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + test.each(core_rules_cases)('includes $name', ({ css, expected, not_expected }) => { + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( css, - [], + [] ); const result = resolve_css({ style_rule_index, variable_graph, class_variable_index, - ...empty_detection(), + ...empty_detection() }); assert.include(result.base_css, expected); @@ -73,13 +73,13 @@ describe('resolve_css', () => { describe('element matching', () => { test('includes rules for detected elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` * { margin: 0; } button { color: blue; } input { color: green; } `, - [], + [] ); const result = resolve_css({ @@ -89,7 +89,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'color: blue'); @@ -98,13 +98,13 @@ describe('resolve_css', () => { }); test('excludes rules for undetected elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { padding: 10px; } input { border: 1px solid; } a { text-decoration: none; } `, - [], + [] ); const result = resolve_css({ @@ -114,7 +114,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button', 'a']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'button { padding: 10px; }'); @@ -123,12 +123,12 @@ describe('resolve_css', () => { }); test('additional_elements option forces inclusion', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: red; } input { border: 1px solid; } `, - [], + [] ); const result = resolve_css({ @@ -136,7 +136,7 @@ describe('resolve_css', () => { variable_graph, class_variable_index, ...empty_detection(), - additional_elements: ['button'], + additional_elements: ['button'] }); assert.include(result.base_css, 'color: red'); @@ -145,14 +145,14 @@ describe('resolve_css', () => { }); test('additional_elements with multiple values', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: red; } input { border: 1px solid; } dialog { padding: 16px; } select { appearance: none; } `, - [], + [] ); const result = resolve_css({ @@ -160,7 +160,7 @@ describe('resolve_css', () => { variable_graph, class_variable_index, ...empty_detection(), - additional_elements: ['button', 'input', 'dialog'], + additional_elements: ['button', 'input', 'dialog'] }); assert.include(result.base_css, 'color: red'); @@ -174,14 +174,14 @@ describe('resolve_css', () => { }); test('additional_elements combined with detected_elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: red; } input { border: 1px solid; } a { text-decoration: none; } select { appearance: none; } `, - [], + [] ); const result = resolve_css({ @@ -192,7 +192,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - additional_elements: ['input'], + additional_elements: ['input'] }); // Both detected and additional should be included @@ -206,12 +206,12 @@ describe('resolve_css', () => { }); test('additional_elements with overlapping detected_elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: red; } input { border: 1px solid; } `, - [], + [] ); const result = resolve_css({ @@ -222,7 +222,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - additional_elements: ['button', 'input'], // button appears in both + additional_elements: ['button', 'input'] // button appears in both }); // Both should be included, no errors from overlap @@ -233,14 +233,14 @@ describe('resolve_css', () => { }); test('handles multiple elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { padding: 10px; } input { border: 1px solid; } a { text-decoration: none; } select { appearance: none; } `, - [], + [] ); const result = resolve_css({ @@ -250,7 +250,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button', 'input', 'a']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'button'); @@ -260,13 +260,13 @@ describe('resolve_css', () => { }); test('preserves cascade order', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` a { color: blue; } a:hover { color: darkblue; } a:active { color: navy; } `, - [], + [] ); const result = resolve_css({ @@ -276,7 +276,7 @@ describe('resolve_css', () => { detected_elements: new Set(['a']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert_css_order(result.base_css, 'color: blue', 'color: darkblue', 'color: navy'); @@ -285,13 +285,13 @@ describe('resolve_css', () => { describe('class matching', () => { test('includes rules for detected classes', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button.selected { background: blue; } button.disabled { opacity: 0.5; } .hidden { display: none; } `, - [], + [] ); const result = resolve_css({ @@ -301,7 +301,7 @@ describe('resolve_css', () => { detected_elements: new Set(), detected_classes: new Set(['selected', 'hidden']), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'button.selected'); @@ -310,12 +310,12 @@ describe('resolve_css', () => { }); test('excludes rules for undetected classes', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` .active { color: green; } .inactive { color: gray; } `, - [], + [] ); const result = resolve_css({ @@ -325,7 +325,7 @@ describe('resolve_css', () => { detected_elements: new Set(), detected_classes: new Set(['active']), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'color: green'); @@ -333,13 +333,13 @@ describe('resolve_css', () => { }); test('combines element and class matching', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { padding: 8px; } button.primary { background: blue; } .warning { color: orange; } `, - [], + [] ); const result = resolve_css({ @@ -349,7 +349,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(['primary', 'warning']), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'button { padding'); @@ -360,12 +360,12 @@ describe('resolve_css', () => { describe('base CSS generation', () => { test('includes matched rules', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { padding: 10px; } input { border: 1px solid; } `, - [], + [] ); const result = resolve_css({ @@ -375,7 +375,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'button { padding: 10px; }'); @@ -383,13 +383,13 @@ describe('resolve_css', () => { }); test('preserves original order', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { padding: 5px; } button:hover { background: gray; } button:focus { outline: none; } `, - [], + [] ); const result = resolve_css({ @@ -399,19 +399,19 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert_css_order(result.base_css, 'padding: 5px', 'background: gray', 'outline: none'); }); test('includes @media rules for elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { font-size: 14px; } @media (min-width: 768px) { button { font-size: 16px; } } `, - [], + [] ); const result = resolve_css({ @@ -421,7 +421,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'font-size: 14px'); @@ -430,12 +430,12 @@ describe('resolve_css', () => { }); test('includes @supports rules for elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { display: block; } @supports (display: grid) { button { display: grid; } } `, - [], + [] ); const result = resolve_css({ @@ -445,7 +445,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'display: block'); @@ -454,12 +454,12 @@ describe('resolve_css', () => { }); test('includes @container rules for elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { padding: 8px; } @container (min-width: 400px) { button { padding: 16px; } } `, - [], + [] ); const result = resolve_css({ @@ -469,7 +469,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.base_css, 'padding: 8px'); @@ -480,9 +480,9 @@ describe('resolve_css', () => { describe('statistics', () => { test('not included by default', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: red; }`, - [], + [] ); const result = resolve_css({ @@ -492,20 +492,20 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.isUndefined(result.stats); }); test('included when include_stats true', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` * { margin: 0; } button { color: blue; } input { color: green; } `, - [{name: 'color', light: 'red'}], + [{ name: 'color', light: 'red' }] ); const result = resolve_css({ @@ -516,7 +516,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(['color']), utility_variables_used: new Set(), - include_stats: true, + include_stats: true }); assert.isDefined(result.stats); @@ -528,7 +528,7 @@ describe('resolve_css', () => { }); test('reflects actual counts', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` * { box-sizing: border-box; } button { color: blue; } @@ -536,9 +536,9 @@ describe('resolve_css', () => { a { text-decoration: none; } `, [ - {name: 'palette_a', light: 'blue'}, - {name: 'palette_b', light: 'green'}, - ], + { name: 'palette_a', light: 'blue' }, + { name: 'palette_b', light: 'green' } + ] ); const result = resolve_css({ @@ -549,7 +549,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(['palette_a']), utility_variables_used: new Set(), - include_stats: true, + include_stats: true }); assert.strictEqual(result.stats!.element_count, 2); @@ -564,7 +564,7 @@ describe('resolve_css', () => { describe('empty scenarios', () => { test('projects without any HTML elements still work', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` * { box-sizing: border-box; } :root { font-size: 16px; } @@ -573,9 +573,9 @@ describe('resolve_css', () => { input { border: 1px solid; } `, [ - {name: 'text_color', light: 'black'}, - {name: 'bg_color', light: 'white'}, - ], + { name: 'text_color', light: 'black' }, + { name: 'bg_color', light: 'white' } + ] ); const result = resolve_css({ @@ -585,7 +585,7 @@ describe('resolve_css', () => { detected_elements: new Set(), detected_classes: new Set(['p_md', 'box']), detected_css_variables: new Set(['text_color']), - utility_variables_used: new Set(['bg_color']), + utility_variables_used: new Set(['bg_color']) }); // Core rules should still be included @@ -601,8 +601,8 @@ describe('resolve_css', () => { }); test('empty style_rule_index', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'color', light: 'blue'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'color', light: 'blue' } ]); const result = resolve_css({ @@ -612,7 +612,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(['color']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.strictEqual(result.base_css, ''); @@ -620,9 +620,9 @@ describe('resolve_css', () => { }); test('empty variable_graph', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: red; }`, - [], + [] ); const result = resolve_css({ @@ -632,7 +632,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.strictEqual(result.theme_css, ''); @@ -640,19 +640,19 @@ describe('resolve_css', () => { }); test('empty detected sets', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` * { margin: 0; } button { color: red; } `, - [{name: 'unused', light: 'value'}], + [{ name: 'unused', light: 'value' }] ); const result = resolve_css({ style_rule_index, variable_graph, class_variable_index, - ...empty_detection(), + ...empty_detection() }); // Should only include core rules @@ -664,10 +664,10 @@ describe('resolve_css', () => { describe('additional_variables option', () => { test('additional_variables forces inclusion', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a', light: 'blue'}, - {name: 'palette_b', light: 'green'}, - {name: 'palette_c', light: 'red'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a', light: 'blue' }, + { name: 'palette_b', light: 'green' }, + { name: 'palette_c', light: 'red' } ]); const result = resolve_css({ @@ -675,7 +675,7 @@ describe('resolve_css', () => { variable_graph, class_variable_index, ...empty_detection(), - additional_variables: ['palette_b'], + additional_variables: ['palette_b'] }); // Only additional_variables should be included (nothing detected) @@ -687,10 +687,10 @@ describe('resolve_css', () => { }); test('additional_variables combined with detected_css_variables', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a', light: 'blue'}, - {name: 'palette_b', light: 'green'}, - {name: 'palette_c', light: 'red'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a', light: 'blue' }, + { name: 'palette_b', light: 'green' }, + { name: 'palette_c', light: 'red' } ]); const result = resolve_css({ @@ -701,7 +701,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(['palette_a']), utility_variables_used: new Set(), - additional_variables: ['palette_c'], + additional_variables: ['palette_c'] }); // Both detected and additional should be included @@ -713,9 +713,9 @@ describe('resolve_css', () => { }); test('additional_variables with overlapping detected_css_variables', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a', light: 'blue'}, - {name: 'palette_b', light: 'green'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a', light: 'blue' }, + { name: 'palette_b', light: 'green' } ]); const result = resolve_css({ @@ -726,7 +726,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(['palette_a']), utility_variables_used: new Set(), - additional_variables: ['palette_a', 'palette_b'], // palette_a in both + additional_variables: ['palette_a', 'palette_b'] // palette_a in both }); // Both should be included, no errors from overlap @@ -739,16 +739,16 @@ describe('resolve_css', () => { describe('additional_elements and additional_variables combined', () => { test('both options work together', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: var(--btn_color); } input { border: 1px solid var(--input_border); } `, [ - {name: 'btn_color', light: 'blue'}, - {name: 'input_border', light: 'gray'}, - {name: 'extra_var', light: 'red'}, - ], + { name: 'btn_color', light: 'blue' }, + { name: 'input_border', light: 'gray' }, + { name: 'extra_var', light: 'red' } + ] ); const result = resolve_css({ @@ -757,7 +757,7 @@ describe('resolve_css', () => { class_variable_index, ...empty_detection(), additional_elements: ['button'], - additional_variables: ['extra_var'], + additional_variables: ['extra_var'] }); // Button rules included @@ -769,12 +769,12 @@ describe('resolve_css', () => { }); test('additional_elements brings transitive variable dependencies', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: var(--btn_color); }`, [ - {name: 'btn_color', light: 'var(--base_color)'}, - {name: 'base_color', light: 'blue'}, - ], + { name: 'btn_color', light: 'var(--base_color)' }, + { name: 'base_color', light: 'blue' } + ] ); const result = resolve_css({ @@ -782,7 +782,7 @@ describe('resolve_css', () => { variable_graph, class_variable_index, ...empty_detection(), - additional_elements: ['button'], + additional_elements: ['button'] }); // Both btn_color and its dependency base_color should be included @@ -793,13 +793,13 @@ describe('resolve_css', () => { describe('additional_elements and additional_variables "all" option', () => { test('additional_elements: "all" includes all rules', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: blue; } input { color: green; } a { color: purple; } `, - [], + [] ); const result = resolve_css({ @@ -810,7 +810,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - additional_elements: 'all', // Include all rules + additional_elements: 'all' // Include all rules }); // Should include ALL rules, not just button @@ -820,13 +820,13 @@ describe('resolve_css', () => { }); test('default behavior only includes matching rules', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: blue; } input { color: green; } a { color: purple; } `, - [], + [] ); const result = resolve_css({ @@ -836,7 +836,7 @@ describe('resolve_css', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() // no additional_elements }); @@ -847,10 +847,10 @@ describe('resolve_css', () => { }); test('additional_variables: "all" includes all variables', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a', light: 'blue'}, - {name: 'palette_b', light: 'green'}, - {name: 'palette_c', light: 'red'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a', light: 'blue' }, + { name: 'palette_b', light: 'green' }, + { name: 'palette_c', light: 'red' } ]); const result = resolve_css({ @@ -861,7 +861,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(['palette_a']), // Only detect palette_a utility_variables_used: new Set(), - additional_variables: 'all', // Include all variables + additional_variables: 'all' // Include all variables }); // Should include ALL variables, not just palette_a @@ -874,10 +874,10 @@ describe('resolve_css', () => { }); test('default behavior only includes used variables', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a', light: 'blue'}, - {name: 'palette_b', light: 'green'}, - {name: 'palette_c', light: 'red'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a', light: 'blue' }, + { name: 'palette_b', light: 'green' }, + { name: 'palette_c', light: 'red' } ]); const result = resolve_css({ @@ -887,7 +887,7 @@ describe('resolve_css', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['palette_a']), - utility_variables_used: new Set(), + utility_variables_used: new Set() // no additional_variables }); @@ -901,15 +901,15 @@ describe('resolve_css', () => { }); test('both "all" options includes everything', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: var(--btn_color); } input { border: 1px solid; } `, [ - {name: 'btn_color', light: 'blue'}, - {name: 'unused', light: 'red'}, - ], + { name: 'btn_color', light: 'blue' }, + { name: 'unused', light: 'red' } + ] ); const result = resolve_css({ @@ -918,7 +918,7 @@ describe('resolve_css', () => { class_variable_index, ...empty_detection(), additional_elements: 'all', - additional_variables: 'all', + additional_variables: 'all' }); // All rules included @@ -932,13 +932,13 @@ describe('resolve_css', () => { describe('exclude options', () => { test('exclude_elements filters from included elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: blue; } input { color: green; } a { color: purple; } `, - [], + [] ); const result = resolve_css({ @@ -949,7 +949,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(), utility_variables_used: new Set(), - exclude_elements: ['input'], + exclude_elements: ['input'] }); // button and a included, input excluded @@ -962,13 +962,13 @@ describe('resolve_css', () => { }); test('exclude_elements combined with additional_elements', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ` button { color: blue; } input { color: green; } a { color: purple; } `, - [], + [] ); const result = resolve_css({ @@ -980,7 +980,7 @@ describe('resolve_css', () => { detected_css_variables: new Set(), utility_variables_used: new Set(), additional_elements: ['input', 'a'], - exclude_elements: ['input'], // Exclude even though it's in additional + exclude_elements: ['input'] // Exclude even though it's in additional }); // button and a included, input excluded @@ -990,10 +990,10 @@ describe('resolve_css', () => { }); test('exclude_variables filters from resolved variables', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a', light: 'blue'}, - {name: 'palette_b', light: 'green'}, - {name: 'palette_c', light: 'red'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a', light: 'blue' }, + { name: 'palette_b', light: 'green' }, + { name: 'palette_c', light: 'red' } ]); const result = resolve_css({ @@ -1004,7 +1004,7 @@ describe('resolve_css', () => { detected_classes: new Set(), detected_css_variables: new Set(['palette_a', 'palette_b', 'palette_c']), utility_variables_used: new Set(), - exclude_variables: ['palette_b'], + exclude_variables: ['palette_b'] }); // palette_a and palette_c included, palette_b excluded @@ -1017,10 +1017,10 @@ describe('resolve_css', () => { }); test('exclude_variables combined with additional_variables', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a', light: 'blue'}, - {name: 'palette_b', light: 'green'}, - {name: 'palette_c', light: 'red'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a', light: 'blue' }, + { name: 'palette_b', light: 'green' }, + { name: 'palette_c', light: 'red' } ]); const result = resolve_css({ @@ -1032,7 +1032,7 @@ describe('resolve_css', () => { detected_css_variables: new Set(['palette_a']), utility_variables_used: new Set(), additional_variables: ['palette_b', 'palette_c'], - exclude_variables: ['palette_b'], // Exclude even though it's in additional + exclude_variables: ['palette_b'] // Exclude even though it's in additional }); // palette_a and palette_c included, palette_b excluded diff --git a/src/test/css_bundled_resolution.variables.test.ts b/src/test/css_bundled_resolution.variables.test.ts index bbbd6aa17..c818927df 100644 --- a/src/test/css_bundled_resolution.variables.test.ts +++ b/src/test/css_bundled_resolution.variables.test.ts @@ -7,19 +7,19 @@ * @module */ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {resolve_css} from '$lib/css_bundled_resolution.ts'; -import type {CssClassDefinition} from '$lib/css_class_generation.ts'; -import {create_test_fixtures, empty_detection} from './css_bundled_resolution_fixtures.ts'; -import {assert_css_order} from './test_helpers.ts'; +import { resolve_css } from '$lib/css_bundled_resolution.ts'; +import type { CssClassDefinition } from '$lib/css_class_generation.ts'; +import { create_test_fixtures, empty_detection } from './css_bundled_resolution_fixtures.ts'; +import { assert_css_order } from './test_helpers.ts'; describe('resolve_css variable resolution', () => { describe('source collection', () => { test('from style rules', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: var(--btn_color); }`, - [{name: 'btn_color', light: 'blue'}], + [{ name: 'btn_color', light: 'blue' }] ); const result = resolve_css({ @@ -29,7 +29,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.isTrue(result.resolved_variables.has('btn_color')); @@ -37,12 +37,12 @@ describe('resolve_css variable resolution', () => { test('from class definitions (via class_variable_index)', () => { const class_defs: Record<string, CssClassDefinition | undefined> = { - p_md: {declaration: 'padding: var(--space_md)'}, + p_md: { declaration: 'padding: var(--space_md)' } }; - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( ``, - [{name: 'space_md', light: '16px'}], - class_defs, + [{ name: 'space_md', light: '16px' }], + class_defs ); const result = resolve_css({ @@ -52,16 +52,16 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(['p_md']), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.isTrue(result.resolved_variables.has('space_md')); }); test('from utility_variables_used', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'space_md', light: '16px'}, - {name: 'unused', light: '0'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'space_md', light: '16px' }, + { name: 'unused', light: '0' } ]); const result = resolve_css({ @@ -71,7 +71,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(['space_md']), + utility_variables_used: new Set(['space_md']) }); assert.isTrue(result.resolved_variables.has('space_md')); @@ -79,8 +79,8 @@ describe('resolve_css variable resolution', () => { }); test('from detected_css_variables', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'custom', light: 'red'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'custom', light: 'red' } ]); const result = resolve_css({ @@ -90,15 +90,15 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['custom']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.isTrue(result.resolved_variables.has('custom')); }); test('from additional_variables option', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'forced', light: 'always'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'forced', light: 'always' } ]); const result = resolve_css({ @@ -106,19 +106,19 @@ describe('resolve_css variable resolution', () => { variable_graph, class_variable_index, ...empty_detection(), - additional_variables: ['forced'], + additional_variables: ['forced'] }); assert.isTrue(result.resolved_variables.has('forced')); }); test('additional_variables: "all" includes every variable', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'palette_a', light: 'blue'}, - {name: 'palette_b', light: 'green'}, - {name: 'palette_c', light: 'red'}, - {name: 'space_sm', light: '8px'}, - {name: 'space_md', light: '16px'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'palette_a', light: 'blue' }, + { name: 'palette_b', light: 'green' }, + { name: 'palette_c', light: 'red' }, + { name: 'space_sm', light: '8px' }, + { name: 'space_md', light: '16px' } ]); const result = resolve_css({ @@ -126,7 +126,7 @@ describe('resolve_css variable resolution', () => { variable_graph, class_variable_index, ...empty_detection(), - additional_variables: 'all', + additional_variables: 'all' }); // All 5 variables should be included @@ -139,9 +139,9 @@ describe('resolve_css variable resolution', () => { }); test('additional_variables: "all" includes transitive deps', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'base', light: '10'}, - {name: 'derived', light: 'calc(var(--base) * 2)'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'base', light: '10' }, + { name: 'derived', light: 'calc(var(--base) * 2)' } ]); const result = resolve_css({ @@ -149,7 +149,7 @@ describe('resolve_css variable resolution', () => { variable_graph, class_variable_index, ...empty_detection(), - additional_variables: 'all', + additional_variables: 'all' }); assert.isTrue(result.resolved_variables.has('base')); @@ -158,18 +158,18 @@ describe('resolve_css variable resolution', () => { test('combines all sources', () => { const class_defs: Record<string, CssClassDefinition | undefined> = { - gap_sm: {declaration: 'gap: var(--space_sm)'}, + gap_sm: { declaration: 'gap: var(--space_sm)' } }; - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: var(--btn_color); }`, [ - {name: 'btn_color', light: 'blue'}, - {name: 'space_md', light: '16px'}, - {name: 'space_sm', light: '8px'}, - {name: 'custom', light: 'red'}, - {name: 'forced', light: 'always'}, + { name: 'btn_color', light: 'blue' }, + { name: 'space_md', light: '16px' }, + { name: 'space_sm', light: '8px' }, + { name: 'custom', light: 'red' }, + { name: 'forced', light: 'always' } ], - class_defs, + class_defs ); const result = resolve_css({ @@ -180,7 +180,7 @@ describe('resolve_css variable resolution', () => { detected_classes: new Set(['gap_sm']), detected_css_variables: new Set(['custom']), utility_variables_used: new Set(['space_md']), - additional_variables: ['forced'], + additional_variables: ['forced'] }); // From style rules @@ -198,12 +198,12 @@ describe('resolve_css variable resolution', () => { describe('transitive resolution', () => { test('resolves direct dependencies', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: var(--color); }`, [ - {name: 'hue', light: '210'}, - {name: 'color', light: 'hsl(var(--hue) 50% 50%)'}, - ], + { name: 'hue', light: '210' }, + { name: 'color', light: 'hsl(var(--hue) 50% 50%)' } + ] ); const result = resolve_css({ @@ -213,7 +213,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.isTrue(result.resolved_variables.has('color')); @@ -221,11 +221,11 @@ describe('resolve_css variable resolution', () => { }); test('resolves deep chains (A→B→C→D)', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'base', light: '10'}, - {name: 'level_1', light: 'calc(var(--base) * 2)'}, - {name: 'level_2', light: 'calc(var(--level_1) * 2)'}, - {name: 'level_3', light: 'calc(var(--level_2) * 2)'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'base', light: '10' }, + { name: 'level_1', light: 'calc(var(--base) * 2)' }, + { name: 'level_2', light: 'calc(var(--level_1) * 2)' }, + { name: 'level_3', light: 'calc(var(--level_2) * 2)' } ]); const result = resolve_css({ @@ -235,7 +235,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['level_3']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.isTrue(result.resolved_variables.has('level_3')); @@ -245,11 +245,11 @@ describe('resolve_css variable resolution', () => { }); test('resolves diamond dependencies (A→{B,C}→D)', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'base', light: '10'}, - {name: 'branch_a', light: 'calc(var(--base) + 1)'}, - {name: 'branch_b', light: 'calc(var(--base) + 2)'}, - {name: 'combined', light: 'calc(var(--branch_a) + var(--branch_b))'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'base', light: '10' }, + { name: 'branch_a', light: 'calc(var(--base) + 1)' }, + { name: 'branch_b', light: 'calc(var(--base) + 2)' }, + { name: 'combined', light: 'calc(var(--branch_a) + var(--branch_b))' } ]); const result = resolve_css({ @@ -259,7 +259,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['combined']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.isTrue(result.resolved_variables.has('combined')); @@ -271,10 +271,10 @@ describe('resolve_css variable resolution', () => { }); test('resolves both light and dark deps', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'light_base', light: '#fff'}, - {name: 'dark_base', dark: '#000'}, - {name: 'themed', light: 'var(--light_base)', dark: 'var(--dark_base)'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'light_base', light: '#fff' }, + { name: 'dark_base', dark: '#000' }, + { name: 'themed', light: 'var(--light_base)', dark: 'var(--dark_base)' } ]); const result = resolve_css({ @@ -284,7 +284,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['themed']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.isTrue(result.resolved_variables.has('themed')); @@ -293,15 +293,15 @@ describe('resolve_css variable resolution', () => { }); test('handles empty initial set', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'unused', light: 'value'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'unused', light: 'value' } ]); const result = resolve_css({ style_rule_index, variable_graph, class_variable_index, - ...empty_detection(), + ...empty_detection() }); assert.strictEqual(result.resolved_variables.size, 0); @@ -310,9 +310,9 @@ describe('resolve_css variable resolution', () => { describe('cycle handling', () => { test('detects simple cycle (A→B→A)', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'a', light: 'var(--b)'}, - {name: 'b', light: 'var(--a)'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'a', light: 'var(--b)' }, + { name: 'b', light: 'var(--a)' } ]); const result = resolve_css({ @@ -322,7 +322,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['a']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); // Should still resolve both @@ -333,10 +333,10 @@ describe('resolve_css variable resolution', () => { }); test('detects longer cycle (A→B→C→A)', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'a', light: 'var(--b)'}, - {name: 'b', light: 'var(--c)'}, - {name: 'c', light: 'var(--a)'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'a', light: 'var(--b)' }, + { name: 'b', light: 'var(--c)' }, + { name: 'c', light: 'var(--a)' } ]); const result = resolve_css({ @@ -346,16 +346,16 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['a']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.isTrue(result.diagnostics.some((d) => d.message.includes('Circular dependency'))); }); test('propagates warnings to diagnostics', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'x', light: 'var(--y)'}, - {name: 'y', light: 'var(--x)'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'x', light: 'var(--y)' }, + { name: 'y', light: 'var(--x)' } ]); const result = resolve_css({ @@ -365,7 +365,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['x']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.strictEqual(result.diagnostics.length, 1); @@ -376,9 +376,9 @@ describe('resolve_css variable resolution', () => { describe('theme CSS generation', () => { test('light-only variables produce :root block', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'space_sm', light: '8px'}, - {name: 'space_md', light: '16px'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'space_sm', light: '8px' }, + { name: 'space_md', light: '16px' } ]); const result = resolve_css({ @@ -388,7 +388,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['space_sm', 'space_md']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.theme_css, ':root {'); @@ -398,8 +398,8 @@ describe('resolve_css variable resolution', () => { }); test('dark-only variables produce :root.dark block', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'shadow_color', dark: 'rgba(0,0,0,0.5)'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'shadow_color', dark: 'rgba(0,0,0,0.5)' } ]); const result = resolve_css({ @@ -409,7 +409,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['shadow_color']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.notMatch(result.theme_css, /^:root\s*\{/); @@ -418,9 +418,9 @@ describe('resolve_css variable resolution', () => { }); test('both produce separate blocks', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'text_color', light: 'black', dark: 'white'}, - {name: 'bg_color', light: 'white', dark: 'black'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'text_color', light: 'black', dark: 'white' }, + { name: 'bg_color', light: 'white', dark: 'black' } ]); const result = resolve_css({ @@ -430,7 +430,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['text_color', 'bg_color']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.theme_css, ':root {'); @@ -444,10 +444,10 @@ describe('resolve_css variable resolution', () => { }); test('variables sorted alphabetically', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'zebra', light: '3'}, - {name: 'alpha', light: '1'}, - {name: 'mid', light: '2'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'zebra', light: '3' }, + { name: 'alpha', light: '1' }, + { name: 'mid', light: '2' } ]); const result = resolve_css({ @@ -457,15 +457,15 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['zebra', 'alpha', 'mid']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert_css_order(result.theme_css, '--alpha', '--mid', '--zebra'); }); test('theme variables render :root and :root.dark blocks', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures(``, [ - {name: 'color', light: 'blue', dark: 'lightblue'}, + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures(``, [ + { name: 'color', light: 'blue', dark: 'lightblue' } ]); const result = resolve_css({ @@ -475,7 +475,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(), detected_classes: new Set(), detected_css_variables: new Set(['color']), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.include(result.theme_css, ':root {'); @@ -483,9 +483,9 @@ describe('resolve_css variable resolution', () => { }); test('empty variables produce empty string', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( `button { color: red; }`, - [], + [] ); const result = resolve_css({ @@ -495,7 +495,7 @@ describe('resolve_css variable resolution', () => { detected_elements: new Set(['button']), detected_classes: new Set(), detected_css_variables: new Set(), - utility_variables_used: new Set(), + utility_variables_used: new Set() }); assert.strictEqual(result.theme_css, ''); diff --git a/src/test/css_bundled_resolution_fixtures.ts b/src/test/css_bundled_resolution_fixtures.ts index f5ee27e2e..982bb0506 100644 --- a/src/test/css_bundled_resolution_fixtures.ts +++ b/src/test/css_bundled_resolution_fixtures.ts @@ -4,11 +4,11 @@ * @module */ -import {parse_style_css} from '$lib/style_rule_parser.ts'; -import {build_variable_graph} from '$lib/variable_graph.ts'; -import {build_class_variable_index} from '$lib/class_variable_index.ts'; -import type {StyleVariable} from '$lib/variable.ts'; -import type {CssClassDefinition} from '$lib/css_class_generation.ts'; +import { parse_style_css } from '$lib/style_rule_parser.ts'; +import { build_variable_graph } from '$lib/variable_graph.ts'; +import { build_class_variable_index } from '$lib/class_variable_index.ts'; +import type { StyleVariable } from '$lib/variable.ts'; +import type { CssClassDefinition } from '$lib/css_class_generation.ts'; /** * Helper to create minimal test fixtures for CSS resolution tests. @@ -16,7 +16,7 @@ import type {CssClassDefinition} from '$lib/css_class_generation.ts'; export const create_test_fixtures = ( css: string, variables: Array<StyleVariable>, - class_defs: Record<string, CssClassDefinition | undefined> = {}, + class_defs: Record<string, CssClassDefinition | undefined> = {} ): { style_rule_index: ReturnType<typeof parse_style_css>; variable_graph: ReturnType<typeof build_variable_graph>; @@ -25,7 +25,7 @@ export const create_test_fixtures = ( const style_rule_index = parse_style_css(css, 'test-hash'); const variable_graph = build_variable_graph(variables, 'test-hash'); const class_variable_index = build_class_variable_index(class_defs); - return {style_rule_index, variable_graph, class_variable_index}; + return { style_rule_index, variable_graph, class_variable_index }; }; /** @@ -41,5 +41,5 @@ export const empty_detection = (): { detected_elements: new Set<string>(), detected_classes: new Set<string>(), detected_css_variables: new Set<string>(), - utility_variables_used: new Set<string>(), + utility_variables_used: new Set<string>() }); diff --git a/src/test/css_cache.test.ts b/src/test/css_cache.test.ts index 6d9e0e348..74674627b 100644 --- a/src/test/css_cache.test.ts +++ b/src/test/css_cache.test.ts @@ -1,6 +1,6 @@ -import {test, assert, describe} from 'vitest'; -import {join} from 'node:path'; -import {mkdir, rm, writeFile, readFile} from 'node:fs/promises'; +import { test, assert, describe } from 'vitest'; +import { join } from 'node:path'; +import { mkdir, rm, writeFile, readFile } from 'node:fs/promises'; import { get_cache_path, @@ -9,16 +9,16 @@ import { delete_cached_extraction, from_cached_extraction, CSS_CACHE_VERSION, - type CachedExtraction, + type CachedExtraction } from '$lib/css_cache.ts'; -import type {SourceLocation, ExtractionDiagnostic} from '$lib/diagnostics.ts'; -import {default_cache_deps} from '$lib/deps_defaults.ts'; -import {create_mock_fs_state, create_mock_cache_deps} from './fixtures/mock_deps.ts'; +import type { SourceLocation, ExtractionDiagnostic } from '$lib/diagnostics.ts'; +import { default_cache_deps } from '$lib/deps_defaults.ts'; +import { create_mock_fs_state, create_mock_cache_deps } from './fixtures/mock_deps.ts'; import { loc, make_classes, make_extraction_diagnostic as make_diagnostic, - EMPTY_EXTRACTION, + EMPTY_EXTRACTION } from './test_helpers.ts'; const deps = default_cache_deps; @@ -40,7 +40,7 @@ const make_cached = (overrides: Partial<CachedExtraction> = {}): CachedExtractio elements: null, explicit_elements: null, explicit_variables: null, - ...overrides, + ...overrides }); // @@ -60,7 +60,7 @@ interface SaveAndLoadOptions { /** Save → load → assert valid → return typed result */ const save_and_load = async ( cache_path: string, - options: SaveAndLoadOptions = {}, + options: SaveAndLoadOptions = {} ): Promise<CachedExtraction> => { const { classes = null, @@ -69,7 +69,7 @@ const save_and_load = async ( elements = null, explicit_elements = null, explicit_variables = null, - content_hash = 'test-hash', + content_hash = 'test-hash' } = options; await save_cached_extraction(deps, cache_path, content_hash, { @@ -78,7 +78,7 @@ const save_and_load = async ( diagnostics, elements, explicit_elements, - explicit_variables, + explicit_variables }); const loaded = await load_cached_extraction(deps, cache_path); assert.isNotNull(loaded); @@ -87,8 +87,8 @@ const save_and_load = async ( }; const setup = async (): Promise<void> => { - await rm(TEST_DIR, {recursive: true, force: true}); - await mkdir(join(PROJECT_ROOT, 'src/lib'), {recursive: true}); + await rm(TEST_DIR, { recursive: true, force: true }); + await mkdir(join(PROJECT_ROOT, 'src/lib'), { recursive: true }); }; // @@ -100,28 +100,28 @@ describe('get_cache_path', () => { { name: 'mirrors source structure', source: '/tmp/fuz_css_cache_test/project/src/lib/Button.svelte', - expected: CACHE_DIR + '/src/lib/Button.svelte.json', + expected: CACHE_DIR + '/src/lib/Button.svelte.json' }, { name: 'handles nested directories', source: '/tmp/fuz_css_cache_test/project/src/routes/docs/colors/+page.svelte', - expected: CACHE_DIR + '/src/routes/docs/colors/+page.svelte.json', + expected: CACHE_DIR + '/src/routes/docs/colors/+page.svelte.json' }, { name: 'handles source path at project root', source: '/tmp/fuz_css_cache_test/project/file.ts', - expected: CACHE_DIR + '/file.ts.json', - }, + expected: CACHE_DIR + '/file.ts.json' + } ]; - test.each(cases)('$name', ({source, expected}) => { + test.each(cases)('$name', ({ source, expected }) => { assert.strictEqual(get_cache_path(source, CACHE_DIR, PROJECT_ROOT), expected); }); test('throws for paths outside project root', () => { assert.throws( () => get_cache_path('/other/file.ts', CACHE_DIR, PROJECT_ROOT), - /Source path "\/other\/file.ts" is not under project root/, + /Source path "\/other\/file.ts" is not under project root/ ); }); @@ -130,7 +130,7 @@ describe('get_cache_path', () => { const root_no_slash = '/tmp/fuz_css_cache_test/project'; assert.strictEqual( get_cache_path(source, CACHE_DIR, root_no_slash), - CACHE_DIR + '/src/lib/Button.svelte.json', + CACHE_DIR + '/src/lib/Button.svelte.json' ); }); @@ -159,7 +159,7 @@ describe('save_cached_extraction', () => { const loaded = await save_and_load(join(CACHE_DIR, 'test.json'), { classes, - content_hash: 'abc123', + content_hash: 'abc123' }); assert.strictEqual(loaded.content_hash, 'abc123'); @@ -171,10 +171,10 @@ describe('save_cached_extraction', () => { await setup(); const classes = make_classes([ ['box', [loc('test.ts', 1, 5), loc('test.ts', 10, 3)]], - ['p_md', [loc('test.ts', 5, 8)]], + ['p_md', [loc('test.ts', 5, 8)]] ]); - const loaded = await save_and_load(join(CACHE_DIR, 'multi.json'), {classes}); + const loaded = await save_and_load(join(CACHE_DIR, 'multi.json'), { classes }); const result = from_cached_extraction(loaded); assert.strictEqual(result.classes?.size, 2); @@ -188,11 +188,11 @@ describe('save_cached_extraction', () => { make_diagnostic({ message: '@fuz-classes: is deprecated', suggestion: 'Remove the colon', - location: loc('test.ts', 3, 1), - }), + location: loc('test.ts', 3, 1) + }) ]; - const loaded = await save_and_load(join(CACHE_DIR, 'diag.json'), {diagnostics}); + const loaded = await save_and_load(join(CACHE_DIR, 'diag.json'), { diagnostics }); assert.isNull(loaded.classes); assert.deepEqual(loaded.diagnostics, diagnostics); @@ -202,27 +202,27 @@ describe('save_cached_extraction', () => { await setup(); const classes = make_classes([['test', [loc('x.ts')]]]); - const loaded = await save_and_load(join(CACHE_DIR, 'deep/nested/path/file.json'), {classes}); + const loaded = await save_and_load(join(CACHE_DIR, 'deep/nested/path/file.json'), { classes }); assert.deepEqual(loaded.classes, [['test', [loc('x.ts')]]]); }); // Null/empty handling const null_handling_cases = [ - {name: 'stores empty classes Map as null', input: {classes: new Map()}, field: 'classes'}, + { name: 'stores empty classes Map as null', input: { classes: new Map() }, field: 'classes' }, { name: 'stores empty diagnostics array as null', - input: {diagnostics: [] as Array<ExtractionDiagnostic>}, - field: 'diagnostics', + input: { diagnostics: [] as Array<ExtractionDiagnostic> }, + field: 'diagnostics' }, { name: 'stores empty elements Set as null', - input: {elements: new Set<string>()}, - field: 'elements', - }, + input: { elements: new Set<string>() }, + field: 'elements' + } ] as const; - test.each(null_handling_cases)('$name', async ({input, field}) => { + test.each(null_handling_cases)('$name', async ({ input, field }) => { await setup(); const loaded = await save_and_load(join(CACHE_DIR, `${field}_empty.json`), input); assert.isNull(loaded[field]); @@ -234,11 +234,11 @@ describe('save_cached_extraction', () => { await save_and_load(cache_path, { classes: make_classes([['old', [loc('a.ts')]]]), - content_hash: 'hash1', + content_hash: 'hash1' }); const loaded = await save_and_load(cache_path, { classes: make_classes([['new', [loc('b.ts', 2, 2)]]]), - content_hash: 'hash2', + content_hash: 'hash2' }); assert.strictEqual(loaded.content_hash, 'hash2'); @@ -251,23 +251,23 @@ describe('save_cached_extraction', () => { name: 'preserves unicode in class names and file paths', classes: make_classes([ ['émoji-class', [loc('src/компонент.svelte')]], - ['日本語', [loc('src/中文.ts', 5, 10)]], + ['日本語', [loc('src/中文.ts', 5, 10)]] ]), - expected_keys: ['émoji-class', '日本語'], + expected_keys: ['émoji-class', '日本語'] }, { name: 'preserves CSS literal syntax in class names', classes: make_classes([ ['display:flex', [loc()]], - ['hover:opacity:80%', [loc('test.ts', 2)]], + ['hover:opacity:80%', [loc('test.ts', 2)]] ]), - expected_keys: ['display:flex', 'hover:opacity:80%'], - }, + expected_keys: ['display:flex', 'hover:opacity:80%'] + } ]; - test.each(special_char_cases)('$name', async ({classes, expected_keys}) => { + test.each(special_char_cases)('$name', async ({ classes, expected_keys }) => { await setup(); - const loaded = await save_and_load(join(CACHE_DIR, 'special.json'), {classes}); + const loaded = await save_and_load(join(CACHE_DIR, 'special.json'), { classes }); const result = from_cached_extraction(loaded); assert.deepEqual(new Set(result.classes?.keys()), new Set(expected_keys)); @@ -276,11 +276,11 @@ describe('save_cached_extraction', () => { test('preserves multiple diagnostics with different levels', async () => { await setup(); const diagnostics = [ - make_diagnostic({level: 'warning', message: 'first warning'}), - make_diagnostic({level: 'error', message: 'an error'}), + make_diagnostic({ level: 'warning', message: 'first warning' }), + make_diagnostic({ level: 'error', message: 'an error' }) ]; - const loaded = await save_and_load(join(CACHE_DIR, 'multi_diag.json'), {diagnostics}); + const loaded = await save_and_load(join(CACHE_DIR, 'multi_diag.json'), { diagnostics }); assert.lengthOf(loaded.diagnostics!, 2); assert.strictEqual(loaded.diagnostics![0]!.level, 'warning'); @@ -294,26 +294,29 @@ describe('save_cached_extraction', () => { describe('load_cached_extraction', () => { const invalid_cache_cases = [ - {name: 'corrupted JSON', content: 'not valid json{{{'}, - {name: 'truncated file', content: '{"v": 1, "content_hash": "abc"'}, - {name: 'empty file', content: ''}, - {name: 'JSON null', content: 'null'}, - {name: 'wrong structure', content: JSON.stringify({foo: 'bar'})}, - {name: 'version mismatch', content: JSON.stringify({v: 999, content_hash: 'x', classes: null})}, + { name: 'corrupted JSON', content: 'not valid json{{{' }, + { name: 'truncated file', content: '{"v": 1, "content_hash": "abc"' }, + { name: 'empty file', content: '' }, + { name: 'JSON null', content: 'null' }, + { name: 'wrong structure', content: JSON.stringify({ foo: 'bar' }) }, { - name: 'version as string', - content: JSON.stringify({v: '2', content_hash: 'x', classes: null}), + name: 'version mismatch', + content: JSON.stringify({ v: 999, content_hash: 'x', classes: null }) }, + { + name: 'version as string', + content: JSON.stringify({ v: '2', content_hash: 'x', classes: null }) + } ]; test('returns null for missing file', async () => { assert.isNull(await load_cached_extraction(deps, '/nonexistent/path.json')); }); - test.each(invalid_cache_cases)('returns null for $name', async ({content}) => { + test.each(invalid_cache_cases)('returns null for $name', async ({ content }) => { await setup(); const cache_path = join(CACHE_DIR, 'invalid.json'); - await mkdir(CACHE_DIR, {recursive: true}); + await mkdir(CACHE_DIR, { recursive: true }); await writeFile(cache_path, content); assert.isNull(await load_cached_extraction(deps, cache_path)); @@ -321,18 +324,18 @@ describe('load_cached_extraction', () => { // Additional corruption recovery tests - all return null gracefully const corruption_cases = [ - {name: 'binary garbage', content: '\x00\x01\x02\x03'}, - {name: 'partial JSON with valid start', content: '{"v": 2, "content_hash": "x",'}, - {name: 'array instead of object', content: '[1, 2, 3]'}, - {name: 'number instead of object', content: '42'}, - {name: 'NUL bytes in hash', content: '{"v": 2, "content_hash": "abc\x00123"}'}, - {name: 'UTF-8 BOM prefix', content: '\ufeff{"v": 2, "content_hash": "hash", "classes": null}'}, + { name: 'binary garbage', content: '\x00\x01\x02\x03' }, + { name: 'partial JSON with valid start', content: '{"v": 2, "content_hash": "x",' }, + { name: 'array instead of object', content: '[1, 2, 3]' }, + { name: 'number instead of object', content: '42' }, + { name: 'NUL bytes in hash', content: '{"v": 2, "content_hash": "abc\x00123"}' }, + { name: 'UTF-8 BOM prefix', content: '\ufeff{"v": 2, "content_hash": "hash", "classes": null}' } ]; - test.each(corruption_cases)('gracefully handles $name', async ({content}) => { + test.each(corruption_cases)('gracefully handles $name', async ({ content }) => { await setup(); const cache_path = join(CACHE_DIR, 'corrupted.json'); - await mkdir(CACHE_DIR, {recursive: true}); + await mkdir(CACHE_DIR, { recursive: true }); await writeFile(cache_path, content); // Should return null rather than throw @@ -344,7 +347,7 @@ describe('load_cached_extraction', () => { test('parses minimal valid cache (missing content_hash)', async () => { await setup(); const cache_path = join(CACHE_DIR, 'minimal.json'); - await mkdir(CACHE_DIR, {recursive: true}); + await mkdir(CACHE_DIR, { recursive: true }); await writeFile(cache_path, `{"v": ${CSS_CACHE_VERSION}}`); // Lenient parsing accepts missing content_hash @@ -362,7 +365,7 @@ describe('delete_cached_extraction', () => { test('removes file', async () => { await setup(); const cache_path = join(CACHE_DIR, 'delete.json'); - await mkdir(CACHE_DIR, {recursive: true}); + await mkdir(CACHE_DIR, { recursive: true }); await writeFile(cache_path, '{}'); assert.strictEqual(await readFile(cache_path, 'utf8'), '{}'); @@ -389,72 +392,72 @@ describe('from_cached_extraction', () => { const conversion_cases = [ { name: 'converts class tuples to Map', - input: make_cached({classes: [['box', [loc()]]]}), + input: make_cached({ classes: [['box', [loc()]]] }), check: (r: ReturnType<typeof from_cached_extraction>) => { assert.instanceOf(r.classes, Map); assert.strictEqual(r.classes.size, 1); assert.deepEqual(r.classes.get('box'), [loc()]); - }, + } }, { name: 'preserves null classes', - input: make_cached({classes: null}), + input: make_cached({ classes: null }), check: (r: ReturnType<typeof from_cached_extraction>) => { assert.isNull(r.classes); - }, + } }, { name: 'converts empty classes array to empty Map', - input: make_cached({classes: []}), + input: make_cached({ classes: [] }), check: (r: ReturnType<typeof from_cached_extraction>) => { assert.instanceOf(r.classes, Map); assert.strictEqual(r.classes.size, 0); - }, + } }, { name: 'preserves diagnostics reference', input: (() => { const diagnostics = [make_diagnostic()]; - return {cached: make_cached({diagnostics}), diagnostics}; + return { cached: make_cached({ diagnostics }), diagnostics }; })(), check: ( r: ReturnType<typeof from_cached_extraction>, - input: {diagnostics: Array<ExtractionDiagnostic>}, + input: { diagnostics: Array<ExtractionDiagnostic> } ) => { assert.strictEqual(r.diagnostics, input.diagnostics); - }, + } }, { name: 'converts elements array to Set', - input: make_cached({elements: ['button', 'div']}), + input: make_cached({ elements: ['button', 'div'] }), check: (r: ReturnType<typeof from_cached_extraction>) => { assert.deepEqual(r.elements, new Set(['button', 'div'])); - }, + } }, { name: 'preserves null elements', input: make_cached(), check: (r: ReturnType<typeof from_cached_extraction>) => { assert.isNull(r.elements); - }, + } }, { name: 'converts explicit_variables array to Set', - input: make_cached({explicit_variables: ['shade_40', 'text_50']}), + input: make_cached({ explicit_variables: ['shade_40', 'text_50'] }), check: (r: ReturnType<typeof from_cached_extraction>) => { assert.deepEqual(r.explicit_variables, new Set(['shade_40', 'text_50'])); - }, + } }, { name: 'preserves null explicit_variables', input: make_cached(), check: (r: ReturnType<typeof from_cached_extraction>) => { assert.isNull(r.explicit_variables); - }, - }, + } + } ]; - test.each(conversion_cases)('$name', ({input, check}) => { + test.each(conversion_cases)('$name', ({ input, check }) => { // Handle the special case where input contains extra data for the check const cached = 'cached' in input ? input.cached : input; const result = from_cached_extraction(cached); @@ -477,7 +480,7 @@ describe('cache fields round-trip', () => { classes, explicit_classes, elements, - content_hash: 'hash123', + content_hash: 'hash123' }); const result = from_cached_extraction(loaded); @@ -490,25 +493,25 @@ describe('cache fields round-trip', () => { const field_cases = [ { name: 'elements', - input: {elements: new Set(['button', 'input', 'svg'])}, + input: { elements: new Set(['button', 'input', 'svg']) }, check: (r: ReturnType<typeof from_cached_extraction>) => - assert.deepEqual(r.elements, new Set(['button', 'input', 'svg'])), + assert.deepEqual(r.elements, new Set(['button', 'input', 'svg'])) }, { name: 'explicit_classes', - input: {explicit_classes: new Set(['force_include'])}, + input: { explicit_classes: new Set(['force_include']) }, check: (r: ReturnType<typeof from_cached_extraction>) => - assert.deepEqual(r.explicit_classes, new Set(['force_include'])), + assert.deepEqual(r.explicit_classes, new Set(['force_include'])) }, { name: 'explicit_variables', - input: {explicit_variables: new Set(['shade_40', 'text_50'])}, + input: { explicit_variables: new Set(['shade_40', 'text_50']) }, check: (r: ReturnType<typeof from_cached_extraction>) => - assert.deepEqual(r.explicit_variables, new Set(['shade_40', 'text_50'])), - }, + assert.deepEqual(r.explicit_variables, new Set(['shade_40', 'text_50'])) + } ]; - test.each(field_cases)('saves and loads $name field', async ({name, input, check}) => { + test.each(field_cases)('saves and loads $name field', async ({ name, input, check }) => { await setup(); const loaded = await save_and_load(join(CACHE_DIR, `${name}_test.json`), input); check(from_cached_extraction(loaded)); @@ -528,7 +531,7 @@ describe('cache functions with mock deps', () => { await save_cached_extraction(mock_deps, cache_path, 'abc123', { ...EMPTY_EXTRACTION, - classes, + classes }); const loaded = await load_cached_extraction(mock_deps, cache_path); diff --git a/src/test/css_class_extractor.elements.test.ts b/src/test/css_class_extractor.elements.test.ts index fd2389784..330d65a73 100644 --- a/src/test/css_class_extractor.elements.test.ts +++ b/src/test/css_class_extractor.elements.test.ts @@ -1,8 +1,8 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {extract_from_svelte} from '$lib/css_class_extractor.ts'; +import { extract_from_svelte } from '$lib/css_class_extractor.ts'; -import {assert_elements} from './css_class_extractor_test_helpers.ts'; +import { assert_elements } from './css_class_extractor_test_helpers.ts'; const element_cases = [ { @@ -15,7 +15,7 @@ const element_cases = [ </div> `, present: ['div', 'button', 'input', 'a'], - absent: [], + absent: [] }, { name: 'svelte:element dynamic tag is not added to elements', @@ -27,7 +27,7 @@ const element_cases = [ <div class="static">Static content</div> `, present: ['div'], - absent: ['svelte:element', 'button'], + absent: ['svelte:element', 'button'] }, { name: 'svelte:body and svelte:window are not added to elements', @@ -37,7 +37,7 @@ const element_cases = [ <main>Content</main> `, present: ['main'], - absent: ['svelte:body', 'svelte:window'], + absent: ['svelte:body', 'svelte:window'] }, { name: 'svelte:head is not added but child elements are', @@ -50,7 +50,7 @@ const element_cases = [ <main>Content</main> `, present: ['main', 'title', 'meta', 'link'], - absent: ['svelte:head'], + absent: ['svelte:head'] }, { name: 'slot element and fallback content elements are detected', @@ -65,7 +65,7 @@ const element_cases = [ </slot> `, present: ['slot', 'header', 'h1', 'p'], - absent: [], + absent: [] }, { name: 'detects SVG elements', @@ -79,7 +79,7 @@ const element_cases = [ </svg> `, present: ['svg', 'circle', 'path', 'g', 'rect'], - absent: [], + absent: [] }, { name: 'detects MathML elements', @@ -96,7 +96,7 @@ const element_cases = [ </math> `, present: ['math', 'mrow', 'mi', 'mo', 'mfrac', 'mn'], - absent: [], + absent: [] }, { name: 'detects custom elements with dashes', @@ -108,7 +108,7 @@ const element_cases = [ </custom-card> `, present: ['my-button', 'custom-card', 'card-header', 'card-body'], - absent: [], + absent: [] }, { name: 'does not detect Svelte components as elements', @@ -122,7 +122,7 @@ const element_cases = [ <div>Regular element</div> `, present: ['div'], - absent: ['Button', 'Card'], + absent: ['Button', 'Card'] }, { name: 'detects elements in nested control flow', @@ -138,12 +138,12 @@ const element_cases = [ {/if} `, present: ['article', 'section', 'header'], - absent: [], - }, + absent: [] + } ]; describe('element detection', () => { - test.each(element_cases)('$name', ({source, present, absent}) => { + test.each(element_cases)('$name', ({ source, present, absent }) => { const result = extract_from_svelte(source); assert_elements(result, present, absent); }); diff --git a/src/test/css_class_extractor.expressions.test.ts b/src/test/css_class_extractor.expressions.test.ts index a9deacbc9..df22905b6 100644 --- a/src/test/css_class_extractor.expressions.test.ts +++ b/src/test/css_class_extractor.expressions.test.ts @@ -1,29 +1,29 @@ -import {test, describe} from 'vitest'; +import { test, describe } from 'vitest'; -import {extract_from_svelte} from '$lib/css_class_extractor.ts'; +import { extract_from_svelte } from '$lib/css_class_extractor.ts'; -import {class_names_equal} from './css_class_extractor_test_helpers.ts'; +import { class_names_equal } from './css_class_extractor_test_helpers.ts'; describe('ternary expressions', () => { const ternary_cases = [ { name: 'extracts both branches of ternary in class attribute', source: `<div class={large ? 'large' : 'small'}></div>`, - expected: ['large', 'small'], + expected: ['large', 'small'] }, { name: 'extracts CSS-literal classes from ternary', source: `<div class={expanded ? 'max-height:500px' : 'max-height:0'}></div>`, - expected: ['max-height:500px', 'max-height:0'], + expected: ['max-height:500px', 'max-height:0'] }, { name: 'extracts from nested ternary', source: `<div class={a ? (b ? 'ab' : 'a-only') : 'none'}></div>`, - expected: ['ab', 'a-only', 'none'], - }, + expected: ['ab', 'a-only', 'none'] + } ]; - test.each(ternary_cases)('$name', ({source, expected}) => { + test.each(ternary_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -34,21 +34,21 @@ describe('logical expressions', () => { { name: 'extracts classes from logical OR', source: `<div class={foo || 'fallback'}></div>`, - expected: ['fallback'], + expected: ['fallback'] }, { name: 'extracts classes from nullish coalescing', source: `<div class={foo ?? 'default'}></div>`, - expected: ['default'], + expected: ['default'] }, { name: 'extracts from combined logical expressions', source: `<div class={clsx(a && 'a', b || 'b-fallback', c ?? 'c-default')}></div>`, - expected: ['a', 'b-fallback', 'c-default'], - }, + expected: ['a', 'b-fallback', 'c-default'] + } ]; - test.each(logical_cases)('$name', ({source, expected}) => { + test.each(logical_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -77,16 +77,16 @@ describe('spread props with class', () => { { name: 'extracts classes from element with spread and class', source: `<Button {...props} class="explicit-class"></Button>`, - expected: ['explicit-class'], + expected: ['explicit-class'] }, { name: 'extracts classes from element with spread and dynamic class', source: `<div {...rest} class={clsx('base', extra)}></div>`, - expected: ['base'], - }, + expected: ['base'] + } ]; - test.each(spread_cases)('$name', ({source, expected}) => { + test.each(spread_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -97,21 +97,21 @@ describe('CSS-literal syntax', () => { { name: 'extracts CSS-literal with tilde space encoding', source: `<div class="margin:0~auto padding:var(--space_sm)~var(--space_lg)"></div>`, - expected: ['margin:0~auto', 'padding:var(--space_sm)~var(--space_lg)'], + expected: ['margin:0~auto', 'padding:var(--space_sm)~var(--space_lg)'] }, { name: 'extracts classes with complex modifier combinations', source: `<div class="md:dark:hover:before:opacity:80%"></div>`, - expected: ['md:dark:hover:before:opacity:80%'], + expected: ['md:dark:hover:before:opacity:80%'] }, { name: 'extracts nth-child modifier classes', source: `<div class="nth-child(2n+1):background:var(--shade_20)"></div>`, - expected: ['nth-child(2n+1):background:var(--shade_20)'], - }, + expected: ['nth-child(2n+1):background:var(--shade_20)'] + } ]; - test.each(css_literal_cases)('$name', ({source, expected}) => { + test.each(css_literal_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); diff --git a/src/test/css_class_extractor.jsx.test.ts b/src/test/css_class_extractor.jsx.test.ts index 2b5742aa9..c02b6ba69 100644 --- a/src/test/css_class_extractor.jsx.test.ts +++ b/src/test/css_class_extractor.jsx.test.ts @@ -1,8 +1,8 @@ -import {test, assert, describe, beforeAll} from 'vitest'; +import { test, assert, describe, beforeAll } from 'vitest'; -import type {ExtractionResult} from '$lib/css_class_extractor.ts'; +import type { ExtractionResult } from '$lib/css_class_extractor.ts'; -import {class_names_equal, create_jsx_extractor} from './css_class_extractor_test_helpers.ts'; +import { class_names_equal, create_jsx_extractor } from './css_class_extractor_test_helpers.ts'; /** * JSX extractor with acorn-jsx plugin pre-configured. @@ -276,7 +276,7 @@ const Component = ({ opacity }) => ( test('fails gracefully without jsx plugin on TSX files', async () => { // This test still needs direct import to test the no-plugin case - const {extract_from_ts} = await import('$lib/css_class_extractor.ts'); + const { extract_from_ts } = await import('$lib/css_class_extractor.ts'); const source = `const Button = () => <button className="btn">Click</button>;`; const result = extract_from_ts(source, 'component.tsx'); assert.isNull(result.classes); diff --git a/src/test/css_class_extractor.locations.test.ts b/src/test/css_class_extractor.locations.test.ts index 33e1821e0..2bfefc31f 100644 --- a/src/test/css_class_extractor.locations.test.ts +++ b/src/test/css_class_extractor.locations.test.ts @@ -1,23 +1,26 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {extract_from_svelte, SourceIndex} from '$lib/css_class_extractor.ts'; +import { extract_from_svelte, SourceIndex } from '$lib/css_class_extractor.ts'; -import {assert_class_at_line, assert_class_locations} from './css_class_extractor_test_helpers.ts'; -import {loc} from './test_helpers.ts'; +import { + assert_class_at_line, + assert_class_locations +} from './css_class_extractor_test_helpers.ts'; +import { loc } from './test_helpers.ts'; describe('SourceIndex', () => { describe('standard LF line endings', () => { const source = 'abc\ndef\nghi'; const lf_cases = [ - {offset: 0, expected: loc('f', 1, 1), desc: 'start of file'}, - {offset: 2, expected: loc('f', 1, 3), desc: 'within first line'}, - {offset: 4, expected: loc('f', 2, 1), desc: 'start of second line'}, - {offset: 6, expected: loc('f', 2, 3), desc: 'within second line'}, - {offset: 8, expected: loc('f', 3, 1), desc: 'start of third line'}, + { offset: 0, expected: loc('f', 1, 1), desc: 'start of file' }, + { offset: 2, expected: loc('f', 1, 3), desc: 'within first line' }, + { offset: 4, expected: loc('f', 2, 1), desc: 'start of second line' }, + { offset: 6, expected: loc('f', 2, 3), desc: 'within second line' }, + { offset: 8, expected: loc('f', 3, 1), desc: 'start of third line' } ]; - test.each(lf_cases)('$desc (offset $offset)', ({offset, expected}) => { + test.each(lf_cases)('$desc (offset $offset)', ({ offset, expected }) => { const index = new SourceIndex(source); assert.deepEqual(index.get_location(offset, 'f'), expected); }); @@ -136,7 +139,7 @@ describe('SourceIndex additional edge cases', () => { test('handles very long single line', () => { const source = 'a'.repeat(1000); const index = new SourceIndex(source); - assert.deepEqual(index.get_location(500, 'f'), {file: 'f', line: 1, column: 501}); + assert.deepEqual(index.get_location(500, 'f'), { file: 'f', line: 1, column: 501 }); }); }); diff --git a/src/test/css_class_extractor.test.ts b/src/test/css_class_extractor.test.ts index 931002e54..7feff1e1f 100644 --- a/src/test/css_class_extractor.test.ts +++ b/src/test/css_class_extractor.test.ts @@ -1,12 +1,12 @@ -import {test, describe} from 'vitest'; +import { test, describe } from 'vitest'; -import {extract_from_svelte} from '$lib/css_class_extractor.ts'; +import { extract_from_svelte } from '$lib/css_class_extractor.ts'; import { class_names_equal, assert_no_classes, assert_diagnostic, - assert_diagnostic_count, + assert_diagnostic_count } from './css_class_extractor_test_helpers.ts'; describe('basic string class attributes', () => { @@ -14,21 +14,21 @@ describe('basic string class attributes', () => { { name: 'extracts classes from class="string" attribute', source: `<div class="foo bar baz"></div>`, - expected: ['foo', 'bar', 'baz'], + expected: ['foo', 'bar', 'baz'] }, { name: 'extracts CSS-literal classes from class attribute', source: `<div class="display:flex hover:opacity:80%"></div>`, - expected: ['display:flex', 'hover:opacity:80%'], + expected: ['display:flex', 'hover:opacity:80%'] }, { name: 'extracts classes with responsive modifiers', source: `<div class="md:display:flex lg:flex-direction:row"></div>`, - expected: ['md:display:flex', 'lg:flex-direction:row'], - }, + expected: ['md:display:flex', 'lg:flex-direction:row'] + } ]; - test.each(string_attr_cases)('$name', ({source, expected}) => { + test.each(string_attr_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -39,26 +39,26 @@ describe('array-style class attributes (Svelte 5.16+)', () => { { name: 'extracts classes from class={[...]} array syntax', source: `<div class={['foo', 'bar']}></div>`, - expected: ['foo', 'bar'], + expected: ['foo', 'bar'] }, { name: 'extracts classes from conditional array syntax', source: `<div class={[cond && 'active', 'base']}></div>`, - expected: ['active', 'base'], + expected: ['active', 'base'] }, { name: 'extracts classes from complex array with CSS-literal syntax', source: `<div class={[faded && 'saturate-0 opacity-50', large && 'scale-200']}></div>`, - expected: ['saturate-0', 'opacity-50', 'scale-200'], + expected: ['saturate-0', 'opacity-50', 'scale-200'] }, { name: 'extracts CSS-literal classes from array syntax', source: `<div class={[cond && 'box', 'display:flex']}></div>`, - expected: ['box', 'display:flex'], - }, + expected: ['box', 'display:flex'] + } ]; - test.each(array_attr_cases)('$name', ({source, expected}) => { + test.each(array_attr_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -69,21 +69,21 @@ describe('object-style class attributes (Svelte 5.16+)', () => { { name: 'extracts classes from class={{...}} object syntax', source: `<div class={{ cool, lame: !cool }}></div>`, - expected: ['cool', 'lame'], + expected: ['cool', 'lame'] }, { name: 'extracts CSS-literal classes from object keys', source: `<div class={{ 'display:flex': isActive, 'hover:opacity:80%': hasHover }}></div>`, - expected: ['display:flex', 'hover:opacity:80%'], + expected: ['display:flex', 'hover:opacity:80%'] }, { name: 'extracts classes from mixed object with identifiers and strings', source: `<div class={{ active, 'hover:color:red': true }}></div>`, - expected: ['active', 'hover:color:red'], - }, + expected: ['active', 'hover:color:red'] + } ]; - test.each(object_attr_cases)('$name', ({source, expected}) => { + test.each(object_attr_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -94,21 +94,21 @@ describe('class: directive', () => { { name: 'extracts class from class:name directive', source: `<div class:active={isActive}></div>`, - expected: ['active'], + expected: ['active'] }, { name: 'extracts class from shorthand class:name directive', source: `<div class:cool></div>`, - expected: ['cool'], + expected: ['cool'] }, { name: 'extracts multiple class directives', source: `<div class:foo class:bar={cond} class:baz></div>`, - expected: ['foo', 'bar', 'baz'], - }, + expected: ['foo', 'bar', 'baz'] + } ]; - test.each(directive_cases)('$name', ({source, expected}) => { + test.each(directive_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -119,16 +119,16 @@ describe('component attributes', () => { { name: 'extracts classes from Component class prop', source: `<Button class="custom-button hover:scale:1.05"></Button>`, - expected: ['custom-button', 'hover:scale:1.05'], + expected: ['custom-button', 'hover:scale:1.05'] }, { name: 'extracts classes from Component with complex class prop', source: `<Card class={clsx('card', selected && 'border:2px~solid~blue')}></Card>`, - expected: ['card', 'border:2px~solid~blue'], - }, + expected: ['card', 'border:2px~solid~blue'] + } ]; - test.each(component_cases)('$name', ({source, expected}) => { + test.each(component_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -136,13 +136,13 @@ describe('component attributes', () => { describe('edge cases', () => { const empty_cases = [ - {name: 'handles empty class attribute', source: `<div class=""></div>`}, - {name: 'handles class attribute with only whitespace', source: `<div class=" "></div>`}, - {name: 'handles empty class array', source: `<div class={[]}></div>`}, - {name: 'handles empty class object', source: `<div class={{}}></div>`}, + { name: 'handles empty class attribute', source: `<div class=""></div>` }, + { name: 'handles class attribute with only whitespace', source: `<div class=" "></div>` }, + { name: 'handles empty class array', source: `<div class={[]}></div>` }, + { name: 'handles empty class object', source: `<div class={{}}></div>` } ]; - test.each(empty_cases)('$name', ({source}) => { + test.each(empty_cases)('$name', ({ source }) => { const result = extract_from_svelte(source); assert_no_classes(result); }); @@ -193,7 +193,7 @@ describe('no false positives', () => { <a href="mailto:someone@fuz.dev">Email</a> <div data-value="foo:bar"></div> <img src="http://fuz.dev/image.png" alt="test"> -`, +` }, { name: 'does not extract from string variables without class-like names', @@ -203,7 +203,7 @@ describe('no false positives', () => { const styles = 'display:flex'; </script> <div></div> -`, +` }, { name: 'does not extract from script string literals without class context', @@ -213,11 +213,11 @@ describe('no false positives', () => { const config = { type: 'primary', size: 'large' }; </script> <div></div> -`, - }, +` + } ]; - test.each(false_positive_cases)('$name', ({source}) => { + test.each(false_positive_cases)('$name', ({ source }) => { const result = extract_from_svelte(source); assert_no_classes(result); }); @@ -264,7 +264,7 @@ describe('Svelte control flow blocks', () => { <div class="list-item">{item.name}</div> {/each} `, - expected: ['list-item'], + expected: ['list-item'] }, { name: 'extracts classes inside {#if} blocks', @@ -275,7 +275,7 @@ describe('Svelte control flow blocks', () => { <div class="hidden"></div> {/if} `, - expected: ['shown', 'hidden'], + expected: ['shown', 'hidden'] }, { name: 'extracts classes inside {#snippet} blocks', @@ -286,7 +286,7 @@ describe('Svelte control flow blocks', () => { </tr> {/snippet} `, - expected: ['table-row', 'table-cell'], + expected: ['table-row', 'table-cell'] }, { name: 'extracts classes from {#await} blocks', @@ -299,7 +299,7 @@ describe('Svelte control flow blocks', () => { <div class="error"></div> {/await} `, - expected: ['loading', 'success', 'error'], + expected: ['loading', 'success', 'error'] }, { name: 'extracts classes from nested control flow', @@ -310,11 +310,11 @@ describe('Svelte control flow blocks', () => { {/if} {/each} `, - expected: ['nested-visible'], - }, + expected: ['nested-visible'] + } ]; - test.each(control_flow_cases)('$name', ({source, expected}) => { + test.each(control_flow_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -333,7 +333,7 @@ describe('module scripts', () => { </script> <div></div> `, - expected: ['local-class', 'module-class', 'shared'], + expected: ['local-class', 'module-class', 'shared'] }, { name: 'extracts classes from script module (Svelte 5 syntax)', @@ -346,11 +346,11 @@ describe('module scripts', () => { </script> <div></div> `, - expected: ['instance-class', 'exported-one', 'exported-two'], - }, + expected: ['instance-class', 'exported-one', 'exported-two'] + } ]; - test.each(module_script_cases)('$name', ({source, expected}) => { + test.each(module_script_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -366,7 +366,7 @@ describe('@fuz-classes comment extraction', () => { </script> <div></div> `, - expected: ['outline_width_focus', 'outline_width_active'], + expected: ['outline_width_focus', 'outline_width_active'] }, { name: 'extracts classes from multi-line @fuz-classes comment', @@ -376,7 +376,7 @@ describe('@fuz-classes comment extraction', () => { </script> <div></div> `, - expected: ['dynamic_class_1', 'dynamic_class_2'], + expected: ['dynamic_class_1', 'dynamic_class_2'] }, { name: 'extracts classes from multiple @fuz-classes comments', @@ -387,7 +387,7 @@ describe('@fuz-classes comment extraction', () => { </script> <div></div> `, - expected: ['class_a', 'class_b', 'class_c', 'class_d'], + expected: ['class_a', 'class_b', 'class_c', 'class_d'] }, { name: 'combines @fuz-classes with regular class extraction', @@ -398,7 +398,7 @@ describe('@fuz-classes comment extraction', () => { </script> <div class="attribute_class"></div> `, - expected: ['attribute_class', 'dynamic_class', 'static_class'], + expected: ['attribute_class', 'dynamic_class', 'static_class'] }, { name: 'extracts classes from HTML comment @fuz-classes', @@ -406,11 +406,11 @@ describe('@fuz-classes comment extraction', () => { <!-- @fuz-classes html-comment-class another-class --> <div></div> `, - expected: ['html-comment-class', 'another-class'], - }, + expected: ['html-comment-class', 'another-class'] + } ]; - test.each(fuz_classes_cases)('$name', ({source, expected}) => { + test.each(fuz_classes_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); diff --git a/src/test/css_class_extractor.tracked_vars.test.ts b/src/test/css_class_extractor.tracked_vars.test.ts index e0ccf40d7..c1759dcf6 100644 --- a/src/test/css_class_extractor.tracked_vars.test.ts +++ b/src/test/css_class_extractor.tracked_vars.test.ts @@ -1,12 +1,12 @@ -import {test, describe} from 'vitest'; +import { test, describe } from 'vitest'; -import {extract_from_svelte} from '$lib/css_class_extractor.ts'; +import { extract_from_svelte } from '$lib/css_class_extractor.ts'; import { class_names_equal, svelte_script, assert_no_classes, - assert_tracked_var, + assert_tracked_var } from './css_class_extractor_test_helpers.ts'; describe('variable tracking', () => { @@ -15,43 +15,43 @@ describe('variable tracking', () => { { suffix: 'Classes', script: "const buttonClasses = 'btn primary';", - expected: ['btn', 'primary'], + expected: ['btn', 'primary'] }, { suffix: 'ClassNames (with extra)', script: "const buttonClassName = 'btn-camel';\nconst cardClassNames = 'card-x card-y';", - expected: ['btn-camel', 'card-x', 'card-y'], + expected: ['btn-camel', 'card-x', 'card-y'] }, { suffix: 'class_name/class_names', script: "const button_class_name = 'btn-snake';\nconst card_class_names = 'card-a card-b';", - expected: ['btn-snake', 'card-a', 'card-b'], + expected: ['btn-snake', 'card-a', 'card-b'] }, { suffix: 'classList/class_list', script: "const buttonClassList = 'btn-list';\nconst card_class_list = 'card-list-a card-list-b';", - expected: ['btn-list', 'card-list-a', 'card-list-b'], + expected: ['btn-list', 'card-list-a', 'card-list-b'] }, { suffix: 'classLists/class_lists', script: "const buttonClassLists = ['btn-lists-a', 'btn-lists-b'];\nconst card_class_lists = 'card-lists';", - expected: ['btn-lists-a', 'btn-lists-b', 'card-lists'], - }, + expected: ['btn-lists-a', 'btn-lists-b', 'card-lists'] + } ]; test.each(naming_convention_cases)( 'extracts from variable ending in $suffix', - ({script, expected}) => { + ({ script, expected }) => { const result = extract_from_svelte(svelte_script(script)); class_names_equal(result, expected); - }, + } ); test('extracts CSS-literal classes from Classes variable', () => { const result = extract_from_svelte( - svelte_script("const cardClasses = 'display:flex gap:var(--space_md)';"), + svelte_script("const cardClasses = 'display:flex gap:var(--space_md)';") ); class_names_equal(result, ['display:flex', 'gap:var(--space_md)']); }); @@ -63,25 +63,25 @@ describe('variable tracking', () => { name: 'extracts classes from $derived rune', source: svelte_script( "let active = $state(false);\nconst buttonClasses = $derived(active ? 'btn-active' : 'btn-inactive');", - '<div class={buttonClasses}></div>', + '<div class={buttonClasses}></div>' ), - expected: ['btn-active', 'btn-inactive'], + expected: ['btn-active', 'btn-inactive'] }, { name: 'extracts classes from $derived.by rune with block body', source: svelte_script( - "const itemClasses = $derived.by(() => {\n\treturn selected ? 'item-selected' : 'item-normal';\n});", + "const itemClasses = $derived.by(() => {\n\treturn selected ? 'item-selected' : 'item-normal';\n});" ), - expected: ['item-selected', 'item-normal'], + expected: ['item-selected', 'item-normal'] }, { name: 'extracts classes from $derived.by rune with expression body', source: svelte_script("const toggleClasses = $derived.by(() => active ? 'on' : 'off');"), - expected: ['on', 'off'], - }, + expected: ['on', 'off'] + } ]; - test.each(rune_cases)('$name', ({source, expected}) => { + test.each(rune_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -93,74 +93,74 @@ describe('variable tracking', () => { name: 'extracts from arbitrarily-named variable used in class context', source: svelte_script( "const styles = 'tracked-class another';", - '<div class={styles}></div>', + '<div class={styles}></div>' ), - expected: ['tracked-class', 'another'], + expected: ['tracked-class', 'another'] }, { name: 'extracts from variable with non-class name used in class array', source: svelte_script( "const base = 'base-style';\nconst extra = 'extra-style';", - "<div class={[base, extra, 'literal']}></div>", + "<div class={[base, extra, 'literal']}></div>" ), - expected: ['literal', 'base-style', 'extra-style'], + expected: ['literal', 'base-style', 'extra-style'] }, { name: 'extracts from variable used in clsx call within class attribute', source: svelte_script( "const variant = 'primary';", - "<div class={clsx('btn', variant)}></div>", + "<div class={clsx('btn', variant)}></div>" ), - expected: ['btn', 'primary'], + expected: ['btn', 'primary'] }, { name: 'extracts from variable used in ternary class expression', source: svelte_script( "const onStyle = 'state-on';\nconst offStyle = 'state-off';", - '<div class={active ? onStyle : offStyle}></div>', + '<div class={active ? onStyle : offStyle}></div>' ), - expected: ['state-on', 'state-off'], + expected: ['state-on', 'state-off'] }, { name: 'extracts from variable used in logical AND class expression', source: svelte_script( "const conditional = 'shown-when-true';", - '<div class={isVisible && conditional}></div>', + '<div class={isVisible && conditional}></div>' ), - expected: ['shown-when-true'], + expected: ['shown-when-true'] }, { name: 'extracts from variable with array value used in class context', source: svelte_script("const items = ['item-a', 'item-b'];", '<div class={items}></div>'), - expected: ['item-a', 'item-b'], + expected: ['item-a', 'item-b'] }, { name: 'extracts from variable with ternary value used in class context', source: svelte_script( "const dynamic = condition ? 'when-true' : 'when-false';", - '<div class={dynamic}></div>', + '<div class={dynamic}></div>' ), - expected: ['when-true', 'when-false'], + expected: ['when-true', 'when-false'] }, { name: 'extracts from variable passed to component class prop', source: svelte_script( "const componentStyle = 'component-class';", - '<Button class={componentStyle}></Button>', + '<Button class={componentStyle}></Button>' ), - expected: ['component-class'], + expected: ['component-class'] }, { name: 'extracts from variable in nested clsx array within class attribute', source: svelte_script( "const nested = 'deep-class';", - "<div class={clsx(['outer', nested])}></div>", + "<div class={clsx(['outer', nested])}></div>" ), - expected: ['outer', 'deep-class'], - }, + expected: ['outer', 'deep-class'] + } ]; - test.each(context_tracking_cases)('$name', ({source, expected}) => { + test.each(context_tracking_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -170,7 +170,7 @@ describe('variable tracking', () => { "const shared = 'shared-style';", `<div class={shared}></div> <span class={[shared, 'other']}></span> -<p class={clsx(shared, 'more')}></p>`, +<p class={clsx(shared, 'more')}></p>` ); const result = extract_from_svelte(source); // shared-style appears once (deduped), plus the literals @@ -182,7 +182,7 @@ describe('variable tracking', () => { test('does not extract from variable not used in class context', () => { const source = svelte_script( "const notUsed = 'should-not-extract';\nconst alsoNotUsed = 'also-ignored';", - '<div data-value={notUsed}></div>', + '<div data-value={notUsed}></div>' ); const result = extract_from_svelte(source); assert_no_classes(result); @@ -191,7 +191,7 @@ describe('variable tracking', () => { test('extracts only from variables actually used in class contexts', () => { const source = svelte_script( "const styles = 'extracted';\nconst other = 'not-extracted';", - '<div class={styles} data-other={other}></div>', + '<div class={styles} data-other={other}></div>' ); const result = extract_from_svelte(source); class_names_equal(result, ['extracted']); @@ -199,7 +199,7 @@ describe('variable tracking', () => { test('does not track variables in standalone clsx calls outside class attributes', () => { const source = svelte_script( - "const variant = 'primary';\nconst result = clsx('btn', variant);", + "const variant = 'primary';\nconst result = clsx('btn', variant);" ); const result = extract_from_svelte(source); // Only 'btn' is extracted (literal in clsx), not 'primary' (variant not tracked) @@ -209,7 +209,7 @@ describe('variable tracking', () => { test('does not support transitive variable tracking', () => { const source = svelte_script( "const original = 'original-class';\nconst alias = original;", - '<div class={alias}></div>', + '<div class={alias}></div>' ); const result = extract_from_svelte(source); // 'alias' is tracked, but its value is another identifier, not a string literal diff --git a/src/test/css_class_extractor.typescript.test.ts b/src/test/css_class_extractor.typescript.test.ts index d8f85f7aa..e046b979b 100644 --- a/src/test/css_class_extractor.typescript.test.ts +++ b/src/test/css_class_extractor.typescript.test.ts @@ -1,11 +1,11 @@ -import {test, describe} from 'vitest'; +import { test, describe } from 'vitest'; -import {extract_from_ts, extract_css_classes} from '$lib/css_class_extractor.ts'; +import { extract_from_ts, extract_css_classes } from '$lib/css_class_extractor.ts'; import { class_names_equal, class_set_equal, - assert_diagnostic, + assert_diagnostic } from './css_class_extractor_test_helpers.ts'; describe('TypeScript extraction', () => { @@ -16,14 +16,14 @@ describe('TypeScript extraction', () => { const buttonClasses = 'btn primary hover:opacity:80%'; export const cardClass = 'card'; `, - expected: ['btn', 'primary', 'hover:opacity:80%', 'card'], + expected: ['btn', 'primary', 'hover:opacity:80%', 'card'] }, { name: 'extracts classes from clsx in TypeScript', source: ` const classes = clsx('base', active && 'active', { 'display:flex': true }); `, - expected: ['base', 'active', 'display:flex'], + expected: ['base', 'active', 'display:flex'] }, { name: 'extracts classes from object with class property', @@ -31,7 +31,7 @@ const classes = clsx('base', active && 'active', { 'display:flex': true }); const props = { class: 'foo bar' }; const config = { className: 'baz' }; `, - expected: ['foo', 'bar', 'baz'], + expected: ['foo', 'bar', 'baz'] }, { name: 'extracts classes from object with double-quoted string literal keys', @@ -43,7 +43,7 @@ const config = { "foo-classes": "dq-foo" }; `, - expected: ['dq-class', 'dq-classname', 'dq-btn', 'primary', 'dq-foo'], + expected: ['dq-class', 'dq-classname', 'dq-btn', 'primary', 'dq-foo'] }, { name: 'extracts classes from object with single-quoted string literal keys', @@ -55,7 +55,7 @@ const config = { 'bar-classes': 'sq-bar' }; `, - expected: ['sq-class', 'sq-classname', 'sq-btn', 'secondary', 'sq-bar'], + expected: ['sq-class', 'sq-classname', 'sq-btn', 'secondary', 'sq-bar'] }, { name: 'extracts classes from mixed identifier and string literal keys', @@ -67,11 +67,11 @@ const config = { "wrapper-classes": "str-wrapper" }; `, - expected: ['id-class', 'str-classname', 'id-container', 'str-wrapper'], - }, + expected: ['id-class', 'str-classname', 'id-container', 'str-wrapper'] + } ]; - test.each(ts_extraction_cases)('$name', ({source, expected}) => { + test.each(ts_extraction_cases)('$name', ({ source, expected }) => { const result = extract_from_ts(source, 'test.ts'); class_names_equal(result, expected); }); @@ -86,13 +86,13 @@ const config = { describe('unified extraction function', () => { test('extract_css_classes auto-detects Svelte files', () => { const source = `<div class="foo bar"></div>`; - const result = extract_css_classes(source, {filename: 'test.svelte'}); + const result = extract_css_classes(source, { filename: 'test.svelte' }); class_set_equal(result, ['foo', 'bar']); }); test('extract_css_classes auto-detects TypeScript files', () => { const source = `const buttonClasses = 'btn primary';`; - const result = extract_css_classes(source, {filename: 'test.ts'}); + const result = extract_css_classes(source, { filename: 'test.ts' }); class_set_equal(result, ['btn', 'primary']); }); @@ -107,7 +107,7 @@ describe('unified extraction function', () => { <!-- @fuz-classes dynamic-class --> </body> </html>`; - const result = extract_css_classes(source, {filename: 'page.html'}); + const result = extract_css_classes(source, { filename: 'page.html' }); class_set_equal(result, ['dynamic-class', 'container', 'p_lg', 'btn', 'hover:opacity:80%']); }); }); diff --git a/src/test/css_class_extractor.utilities.test.ts b/src/test/css_class_extractor.utilities.test.ts index 5ff8450d6..0c319a59a 100644 --- a/src/test/css_class_extractor.utilities.test.ts +++ b/src/test/css_class_extractor.utilities.test.ts @@ -1,39 +1,39 @@ -import {test, describe} from 'vitest'; +import { test, describe } from 'vitest'; -import {extract_from_svelte} from '$lib/css_class_extractor.ts'; +import { extract_from_svelte } from '$lib/css_class_extractor.ts'; -import {class_names_equal, assert_no_classes} from './css_class_extractor_test_helpers.ts'; +import { class_names_equal, assert_no_classes } from './css_class_extractor_test_helpers.ts'; describe('clsx/cn function calls', () => { const clsx_cases = [ { name: 'extracts classes from clsx() call', source: `<div class={clsx('foo', 'bar')}></div>`, - expected: ['foo', 'bar'], + expected: ['foo', 'bar'] }, { name: 'extracts classes from cn() call', source: `<div class={cn('base', active && 'active')}></div>`, - expected: ['base', 'active'], + expected: ['base', 'active'] }, { name: 'extracts classes from clsx() with object syntax', source: `<div class={clsx({ foo: true, bar: false })}></div>`, - expected: ['foo', 'bar'], + expected: ['foo', 'bar'] }, { name: 'extracts CSS-literal classes from clsx() call', source: `<div class={clsx('display:flex', { 'hover:opacity:80%': hasHover })}></div>`, - expected: ['display:flex', 'hover:opacity:80%'], + expected: ['display:flex', 'hover:opacity:80%'] }, { name: 'extracts classes from nested clsx arguments', source: `<div class={clsx('base', active && 'active', ['nested', condition && 'cond'])}></div>`, - expected: ['base', 'active', 'nested', 'cond'], - }, + expected: ['base', 'active', 'nested', 'cond'] + } ]; - test.each(clsx_cases)('$name', ({source, expected}) => { + test.each(clsx_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -44,21 +44,21 @@ describe('other class utility functions', () => { { name: 'extracts classes from cx() call', source: `<div class={cx('foo', 'bar')}></div>`, - expected: ['foo', 'bar'], + expected: ['foo', 'bar'] }, { name: 'extracts classes from classNames() call', source: `<div class={classNames('base', { active: isActive })}></div>`, - expected: ['base', 'active'], + expected: ['base', 'active'] }, { name: 'extracts classes from classnames() call (lowercase)', source: `<div class={classnames('one', 'two')}></div>`, - expected: ['one', 'two'], - }, + expected: ['one', 'two'] + } ]; - test.each(utility_cases)('$name', ({source, expected}) => { + test.each(utility_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); @@ -83,12 +83,12 @@ describe('template literals', () => { { name: 'extracts classes from template literal', source: `<div class={\`foo bar\`}></div>`, - expected: ['foo', 'bar'], + expected: ['foo', 'bar'] }, { name: 'extracts static parts from template literal with expressions', source: `<div class={\`base \${active ? 'active' : 'inactive'} end\`}></div>`, - expected: ['base', 'end', 'active', 'inactive'], + expected: ['base', 'end', 'active', 'inactive'] }, { name: 'extracts from multiline template literal', @@ -96,21 +96,21 @@ describe('template literals', () => { first-line second-line \`}></div>`, - expected: ['first-line', 'second-line'], + expected: ['first-line', 'second-line'] }, { name: 'extracts from tagged template literals', source: `<div class={css\`styled-class\`}></div>`, - expected: ['styled-class'], + expected: ['styled-class'] }, { name: 'extracts complete tokens surrounded by expressions', source: `<div class={\`\${a} middle \${b}\`}></div>`, - expected: ['middle'], - }, + expected: ['middle'] + } ]; - test.each(template_cases)('$name', ({source, expected}) => { + test.each(template_cases)('$name', ({ source, expected }) => { const result = extract_from_svelte(source); class_names_equal(result, expected); }); diff --git a/src/test/css_class_extractor_test_helpers.ts b/src/test/css_class_extractor_test_helpers.ts index bc104d7c5..880e12471 100644 --- a/src/test/css_class_extractor_test_helpers.ts +++ b/src/test/css_class_extractor_test_helpers.ts @@ -1,9 +1,9 @@ -import {assert} from 'vitest'; +import { assert } from 'vitest'; import { type ExtractionResult, type AcornPlugin, - extract_from_ts, + extract_from_ts } from '$lib/css_class_extractor.ts'; /** @@ -38,8 +38,8 @@ export const create_jsx_extractor = async (): Promise< * Compares as arrays to also verify extraction order. */ export const class_names_equal = ( - result: {classes: Map<string, unknown> | null}, - expected: Array<string>, + result: { classes: Map<string, unknown> | null }, + expected: Array<string> ): void => { const actual = result.classes ? [...result.classes.keys()] : []; assert.deepEqual(actual, expected); @@ -63,7 +63,7 @@ export const svelte_script = (script: string, template = '<div></div>'): string /** * Assert no classes extracted. */ -export const assert_no_classes = (result: {classes: Map<string, unknown> | null}): void => { +export const assert_no_classes = (result: { classes: Map<string, unknown> | null }): void => { assert.isNull(result.classes); }; @@ -73,7 +73,7 @@ export const assert_no_classes = (result: {classes: Map<string, unknown> | null} export const assert_elements = ( result: ExtractionResult, present: Array<string>, - absent: Array<string> = [], + absent: Array<string> = [] ): void => { for (const el of present) { assert.isTrue(result.elements?.has(el) ?? false, `Expected "${el}" present`); @@ -90,7 +90,7 @@ export const assert_diagnostic = ( result: ExtractionResult, level: 'warning' | 'error', contains: string, - file?: string, + file?: string ): void => { assert.isNotNull(result.diagnostics, 'Expected diagnostics array'); const match = result.diagnostics.find((d) => d.level === level && d.message.includes(contains)); @@ -139,7 +139,7 @@ export const assert_class_at_line = ( result: ExtractionResult, class_name: string, line: number, - file?: string, + file?: string ): void => { const locations = result.classes?.get(class_name); assert.isDefined(locations, `Expected class "${class_name}" to exist`); @@ -155,7 +155,7 @@ export const assert_class_at_line = ( export const assert_class_locations = ( result: ExtractionResult, class_name: string, - expected_lines: Array<number>, + expected_lines: Array<number> ): void => { const locations = result.classes?.get(class_name); assert.isDefined(locations, `Expected class "${class_name}" to exist`); @@ -170,7 +170,7 @@ export const assert_class_locations = ( */ export const assert_explicit_classes = ( result: ExtractionResult, - expected: Array<string>, + expected: Array<string> ): void => { assert.isNotNull(result.explicit_classes, 'Expected explicit_classes to be present'); const actual = [...result.explicit_classes].sort(); @@ -182,7 +182,7 @@ export const assert_explicit_classes = ( */ export const assert_explicit_elements = ( result: ExtractionResult, - expected: Array<string>, + expected: Array<string> ): void => { assert.isNotNull(result.explicit_elements, 'Expected explicit_elements to be present'); const actual = [...result.explicit_elements].sort(); @@ -201,7 +201,7 @@ export const assert_no_explicit_elements = (result: ExtractionResult): void => { */ export const assert_explicit_variables = ( result: ExtractionResult, - expected: Array<string>, + expected: Array<string> ): void => { assert.isNotNull(result.explicit_variables, 'Expected explicit_variables to be present'); const actual = [...result.explicit_variables].sort(); diff --git a/src/test/css_class_generators.test.ts b/src/test/css_class_generators.test.ts index ae2dc3e93..bd3d3f854 100644 --- a/src/test/css_class_generators.test.ts +++ b/src/test/css_class_generators.test.ts @@ -6,7 +6,7 @@ * @module */ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; import { generate_classes, @@ -16,9 +16,9 @@ import { generate_shadow_classes, format_spacing_value, format_dimension_value, - format_variable_name, + format_variable_name } from '$lib/css_class_generators.ts'; -import type {CssClassDefinition} from '$lib/css_class_generation.ts'; +import type { CssClassDefinition } from '$lib/css_class_generation.ts'; /* eslint-disable @typescript-eslint/dot-notation -- dynamic keys require bracket notation */ @@ -112,8 +112,8 @@ describe('generate_classes', () => { describe('single dimension', () => { test('generates from simple values', () => { const result = generate_classes( - (v: string) => ({name: `pos_${v}`, css: `position: ${v};`}), - ['static', 'relative', 'absolute'], + (v: string) => ({ name: `pos_${v}`, css: `position: ${v};` }), + ['static', 'relative', 'absolute'] ); assert.deepEqual(Object.keys(result), ['pos_static', 'pos_relative', 'pos_absolute']); @@ -123,15 +123,15 @@ describe('generate_classes', () => { test('skips null returns from template', () => { const result = generate_classes( - (v: string) => (v === 'skip' ? null : {name: `val_${v}`, css: `value: ${v};`}), - ['a', 'skip', 'b'], + (v: string) => (v === 'skip' ? null : { name: `val_${v}`, css: `value: ${v};` }), + ['a', 'skip', 'b'] ); assert.deepEqual(Object.keys(result), ['val_a', 'val_b']); }); test('handles empty iterable', () => { - const result = generate_classes((v: string) => ({name: v, css: `x: ${v};`}), []); + const result = generate_classes((v: string) => ({ name: v, css: `x: ${v};` }), []); assert.deepEqual(result, {}); }); @@ -140,9 +140,12 @@ describe('generate_classes', () => { describe('two dimensions (multiplicative)', () => { test('generates all combinations', () => { const result = generate_classes( - (dir: string, size: string) => ({name: `m${dir}_${size}`, css: `margin-${dir}: ${size};`}), + (dir: string, size: string) => ({ + name: `m${dir}_${size}`, + css: `margin-${dir}: ${size};` + }), ['t', 'b'], - ['sm', 'lg'], + ['sm', 'lg'] ); assert.deepEqual(Object.keys(result), ['mt_sm', 'mt_lg', 'mb_sm', 'mb_lg']); @@ -152,9 +155,9 @@ describe('generate_classes', () => { test('skips null returns', () => { const result = generate_classes( (a: string, b: string) => - a === 'x' && b === '1' ? null : {name: `${a}_${b}`, css: `v: ${a}${b};`}, + a === 'x' && b === '1' ? null : { name: `${a}_${b}`, css: `v: ${a}${b};` }, ['x', 'y'], - ['1', '2'], + ['1', '2'] ); // x_1 is skipped @@ -165,10 +168,10 @@ describe('generate_classes', () => { describe('three dimensions', () => { test('generates all combinations', () => { const result = generate_classes( - (a: string, b: string, c: string) => ({name: `${a}_${b}_${c}`, css: `v: ${a}${b}${c};`}), + (a: string, b: string, c: string) => ({ name: `${a}_${b}_${c}`, css: `v: ${a}${b}${c};` }), ['x', 'y'], ['1', '2'], - ['a', 'b'], + ['a', 'b'] ); assert.lengthOf(Object.keys(result), 8); // 2 * 2 * 2 @@ -278,23 +281,23 @@ describe('generate_border_radius_corners', () => { assert.strictEqual( get_declaration(result['border_top_left_radius_8px']), - 'border-top-left-radius: 8px;', + 'border-top-left-radius: 8px;' ); assert.strictEqual( get_declaration(result['border_bottom_right_radius_8px']), - 'border-bottom-right-radius: 8px;', + 'border-bottom-right-radius: 8px;' ); }); test('applies formatter', () => { const result = generate_border_radius_corners( ['lg'], - (v) => `var(--test_border_top_left_radius_${v})`, + (v) => `var(--test_border_top_left_radius_${v})` ); assert.strictEqual( get_declaration(result['border_top_left_radius_lg']), - 'border-top-left-radius: var(--test_border_top_left_radius_lg);', + 'border-top-left-radius: var(--test_border_top_left_radius_lg);' ); }); @@ -309,7 +312,7 @@ describe('generate_shadow_classes', () => { const alpha_mapping: Record<string, string> = { sm: '1', md: '2', - lg: '3', + lg: '3' }; test('generates all 6 shadow types', () => { diff --git a/src/test/css_class_resolution.literals.test.ts b/src/test/css_class_resolution.literals.test.ts index c46cad7c3..6dc6c1a14 100644 --- a/src/test/css_class_resolution.literals.test.ts +++ b/src/test/css_class_resolution.literals.test.ts @@ -1,8 +1,8 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {resolve_composes} from '$lib/css_class_resolution.ts'; -import type {CssClassDefinition} from '$lib/css_class_generation.ts'; -import {assert_resolved_declaration, assert_resolution_error} from './test_helpers.ts'; +import { resolve_composes } from '$lib/css_class_resolution.ts'; +import type { CssClassDefinition } from '$lib/css_class_generation.ts'; +import { assert_resolved_declaration, assert_resolution_error } from './test_helpers.ts'; /** * Tests for CSS literal class handling in resolve_composes. @@ -22,8 +22,8 @@ describe('resolve_composes with CSS literals', () => { [ ['text-align:center', 'display:flex', 'margin:0~auto'], 'text-align: center; display: flex; margin: 0 auto;', - 'multiple literals', - ], + 'multiple literals' + ] ]; test.each(success_cases)('resolves %j → "%s" (%s)', (composes, expectedDeclaration) => { @@ -36,7 +36,7 @@ describe('resolve_composes with CSS literals', () => { describe('mixed token classes and literals', () => { test('resolves mixed token classes and literals', () => { const definitions: Record<string, CssClassDefinition> = { - p_lg: {declaration: 'padding: var(--space_lg);'}, + p_lg: { declaration: 'padding: var(--space_lg);' } }; const result = resolve_composes( ['p_lg', 'text-align:center'], @@ -44,7 +44,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'test', - css_properties, + css_properties ); assert_resolved_declaration(result, 'padding: var(--space_lg); text-align: center;'); @@ -52,8 +52,8 @@ describe('resolve_composes with CSS literals', () => { test('resolves literal in nested composes resolution', () => { const definitions: Record<string, CssClassDefinition> = { - base: {composes: ['text-align:center']}, - card: {composes: ['base'], declaration: 'padding: 1rem;'}, + base: { composes: ['text-align:center'] }, + card: { composes: ['base'], declaration: 'padding: 1rem;' } }; const result = resolve_composes( ['card'], @@ -61,7 +61,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'test', - css_properties, + css_properties ); assert.ok(result.ok); @@ -78,7 +78,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'test', - css_properties, + css_properties ); assert_resolution_error(result, 'cannot be used in composes array'); @@ -86,7 +86,7 @@ describe('resolve_composes with CSS literals', () => { test('modified known class gives clear error', () => { const definitions: Record<string, CssClassDefinition> = { - box: {declaration: 'display: flex; flex-direction: column;'}, + box: { declaration: 'display: flex; flex-direction: column;' } }; const result = resolve_composes( ['hover:box'], @@ -94,7 +94,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'card', - css_properties, + css_properties ); assert.ok(!result.ok); @@ -104,7 +104,7 @@ describe('resolve_composes with CSS literals', () => { test('modified token class (md:p_lg) gives clear error', () => { const definitions: Record<string, CssClassDefinition> = { - p_lg: {declaration: 'padding: var(--space_lg);'}, + p_lg: { declaration: 'padding: var(--space_lg);' } }; const result = resolve_composes( ['md:p_lg'], @@ -112,7 +112,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'responsive_card', - css_properties, + css_properties ); assert.ok(!result.ok); @@ -124,7 +124,7 @@ describe('resolve_composes with CSS literals', () => { describe('modifier typo detection', () => { test('modifier with unknown base reports unknown class', () => { const definitions: Record<string, CssClassDefinition> = { - box: {declaration: 'display: flex;'}, + box: { declaration: 'display: flex;' } }; const result = resolve_composes( ['hover:bx'], // bx doesn't exist (typo for box?) @@ -132,7 +132,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'card', - css_properties, + css_properties ); assert.ok(!result.ok); @@ -142,7 +142,7 @@ describe('resolve_composes with CSS literals', () => { test('modifier typo with known class suggests correction', () => { const definitions: Record<string, CssClassDefinition> = { - box: {declaration: 'display: flex;'}, + box: { declaration: 'display: flex;' } }; const result = resolve_composes( ['hovr:box'], // typo: hovr → hover @@ -150,7 +150,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'card', - css_properties, + css_properties ); assert.ok(!result.ok); @@ -161,7 +161,7 @@ describe('resolve_composes with CSS literals', () => { test('modifier typo in middle of chain suggests correction', () => { const definitions: Record<string, CssClassDefinition> = { - box: {declaration: 'display: flex;'}, + box: { declaration: 'display: flex;' } }; const result = resolve_composes( ['md:hovr:box'], // typo in middle: md:hovr:box → md:hover:box @@ -169,7 +169,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'card', - css_properties, + css_properties ); assert.ok(!result.ok); @@ -179,7 +179,7 @@ describe('resolve_composes with CSS literals', () => { test('non-typo unknown prefix falls back to property error', () => { const definitions: Record<string, CssClassDefinition> = { - box: {declaration: 'display: flex;'}, + box: { declaration: 'display: flex;' } }; const result = resolve_composes( ['xyz:box'], // not a modifier typo @@ -187,7 +187,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'card', - css_properties, + css_properties ); assert_resolution_error(result, 'Unknown CSS property "xyz"'); @@ -202,7 +202,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'test', - css_properties, + css_properties ); assert_resolution_error(result, 'Unknown class'); @@ -215,7 +215,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'card', - css_properties, + css_properties ); assert.ok(!result.ok); @@ -232,7 +232,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'test', - css_properties, + css_properties ); assert.ok(result.ok); @@ -244,9 +244,9 @@ describe('resolve_composes with CSS literals', () => { test('diamond dependency with literals deduplicated silently', () => { // Both b and c include text-align:center (diamond), reaching it via different paths const definitions: Record<string, CssClassDefinition> = { - b: {composes: ['text-align:center'], declaration: 'padding: 10px;'}, - c: {composes: ['text-align:center'], declaration: 'margin: 10px;'}, - a: {composes: ['b', 'c']}, + b: { composes: ['text-align:center'], declaration: 'padding: 10px;' }, + c: { composes: ['text-align:center'], declaration: 'margin: 10px;' }, + a: { composes: ['b', 'c'] } }; const result = resolve_composes( ['a'], @@ -254,7 +254,7 @@ describe('resolve_composes with CSS literals', () => { new Set(), new Set(), 'test', - css_properties, + css_properties ); assert.ok(result.ok); diff --git a/src/test/css_class_resolution.test.ts b/src/test/css_class_resolution.test.ts index ccfbb8975..a290823a9 100644 --- a/src/test/css_class_resolution.test.ts +++ b/src/test/css_class_resolution.test.ts @@ -1,22 +1,22 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {resolve_composes, resolve_class_definition} from '$lib/css_class_resolution.ts'; -import type {CssClassDefinition, CssClassDefinitionStatic} from '$lib/css_class_generation.ts'; +import { resolve_composes, resolve_class_definition } from '$lib/css_class_resolution.ts'; +import type { CssClassDefinition, CssClassDefinitionStatic } from '$lib/css_class_generation.ts'; import { assert_resolved_declaration, assert_resolution_error, - count_css_occurrences, + count_css_occurrences } from './test_helpers.ts'; /** * Common definitions used across multiple tests. */ const BASE_DEFS: Record<string, CssClassDefinition> = { - p_lg: {declaration: 'padding: var(--space_lg);'}, - m_md: {declaration: 'margin: var(--space_md);'}, - rounded: {declaration: 'border-radius: var(--border_radius_md);'}, - shadow_md: {declaration: 'box-shadow: var(--shadow_md);'}, - gap_sm: {declaration: 'gap: var(--space_sm);'}, + p_lg: { declaration: 'padding: var(--space_lg);' }, + m_md: { declaration: 'margin: var(--space_md);' }, + rounded: { declaration: 'border-radius: var(--border_radius_md);' }, + shadow_md: { declaration: 'box-shadow: var(--shadow_md);' }, + gap_sm: { declaration: 'gap: var(--space_sm);' } }; describe('resolve_composes', () => { @@ -34,11 +34,11 @@ describe('resolve_composes', () => { BASE_DEFS, new Set(), new Set(), - 'test_class', + 'test_class' ); assert_resolved_declaration( result, - 'padding: var(--space_lg); margin: var(--space_md); gap: var(--space_sm);', + 'padding: var(--space_lg); margin: var(--space_md); gap: var(--space_sm);' ); }); }); @@ -46,7 +46,7 @@ describe('resolve_composes', () => { describe('composite class references', () => { test('resolves a composite with declaration', () => { const definitions: Record<string, CssClassDefinition> = { - box: {declaration: 'display: flex; flex-direction: column;'}, + box: { declaration: 'display: flex; flex-direction: column;' } }; const result = resolve_composes(['box'], definitions, new Set(), new Set(), 'test_class'); assert_resolved_declaration(result, 'display: flex; flex-direction: column;'); @@ -57,31 +57,31 @@ describe('resolve_composes', () => { test('resolves nested composes', () => { const definitions: Record<string, CssClassDefinition> = { ...BASE_DEFS, - panel_base: {composes: ['p_lg', 'rounded']}, + panel_base: { composes: ['p_lg', 'rounded'] } }; const result = resolve_composes( ['panel_base'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert_resolved_declaration( result, - 'padding: var(--space_lg); border-radius: var(--border_radius_md);', + 'padding: var(--space_lg); border-radius: var(--border_radius_md);' ); }); test('resolves deeply nested composes', () => { const definitions: Record<string, CssClassDefinition> = { ...BASE_DEFS, - panel_base: {composes: ['p_lg', 'rounded']}, - panel: {composes: ['panel_base', 'shadow_md']}, + panel_base: { composes: ['p_lg', 'rounded'] }, + panel: { composes: ['panel_base', 'shadow_md'] } }; const result = resolve_composes(['panel'], definitions, new Set(), new Set(), 'test_class'); assert_resolved_declaration( result, - 'padding: var(--space_lg); border-radius: var(--border_radius_md); box-shadow: var(--shadow_md);', + 'padding: var(--space_lg); border-radius: var(--border_radius_md); box-shadow: var(--shadow_md);' ); }); }); @@ -90,12 +90,12 @@ describe('resolve_composes', () => { test('resolves composes with additional declaration', () => { const definitions: Record<string, CssClassDefinition> = { ...BASE_DEFS, - card: {composes: ['p_lg', 'rounded'], declaration: '--card-bg: var(--shade_10);'}, + card: { composes: ['p_lg', 'rounded'], declaration: '--card-bg: var(--shade_10);' } }; const result = resolve_composes(['card'], definitions, new Set(), new Set(), 'test_class'); assert_resolved_declaration( result, - 'padding: var(--space_lg); border-radius: var(--border_radius_md); --card-bg: var(--shade_10);', + 'padding: var(--space_lg); border-radius: var(--border_radius_md); --card-bg: var(--shade_10);' ); }); }); @@ -105,22 +105,22 @@ describe('resolve_composes', () => { const cycle_cases: Array<[string, Record<string, CssClassDefinition>, string, string]> = [ [ 'direct cycle (a → b → a)', - {a: {composes: ['b']}, b: {composes: ['a']}}, + { a: { composes: ['b'] }, b: { composes: ['a'] } }, 'a → b → a', - 'detects direct cycle', + 'detects direct cycle' ], [ 'self-reference', - {self_ref: {composes: ['self_ref']}}, + { self_ref: { composes: ['self_ref'] } }, 'Circular reference detected', - 'detects self-reference', + 'detects self-reference' ], [ 'longer cycle (a → b → c → a)', - {a: {composes: ['b']}, b: {composes: ['c']}, c: {composes: ['a']}}, + { a: { composes: ['b'] }, b: { composes: ['c'] }, c: { composes: ['a'] } }, 'a → b → c → a', - 'detects longer cycle', - ], + 'detects longer cycle' + ] ]; test.each(cycle_cases)('%s', (_name, definitions, expectedMessage) => { @@ -130,7 +130,7 @@ describe('resolve_composes', () => { definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert.isFalse(result.ok); @@ -147,7 +147,7 @@ describe('resolve_composes', () => { BASE_DEFS, new Set(), new Set(), - 'test_class', + 'test_class' ); assert.isFalse(result.ok); @@ -158,7 +158,7 @@ describe('resolve_composes', () => { test('returns error for unknown nested composes', () => { const definitions: Record<string, CssClassDefinition> = { - wrapper: {composes: ['nonexistent']}, + wrapper: { composes: ['nonexistent'] } }; const result = resolve_composes(['wrapper'], definitions, new Set(), new Set(), 'test_class'); @@ -171,22 +171,22 @@ describe('resolve_composes', () => { const definitions: Record<string, CssClassDefinition> = { clickable: { ruleset: `.clickable { cursor: pointer; } -.clickable:hover { opacity: 0.8; }`, - }, +.clickable:hover { opacity: 0.8; }` + } }; const result = resolve_composes( ['clickable'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert.isFalse(result.ok); assert.strictEqual(result.error.level, 'error'); assert.include( result.error.message, - 'Cannot reference ruleset class "clickable" in composes array', + 'Cannot reference ruleset class "clickable" in composes array' ); assert.include(result.error.suggestion, 'multiple selectors'); }); @@ -197,8 +197,8 @@ describe('resolve_composes', () => { const definitions: Record<string, CssClassDefinition> = { 'hover:*': { pattern: /^hover:(.+)$/, - interpret: () => null, - }, + interpret: () => null + } }; const result = resolve_composes(['hover:*'], definitions, new Set(), new Set(), 'test_class'); @@ -220,14 +220,14 @@ describe('resolve_composes', () => { test('resolves composes-only definition', () => { const definitions: Record<string, CssClassDefinition> = { ...BASE_DEFS, - flex_center: {composes: ['p_lg']}, + flex_center: { composes: ['p_lg'] } }; const result = resolve_composes( ['flex_center'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert_resolved_declaration(result, 'padding: var(--space_lg);'); }); @@ -237,10 +237,10 @@ describe('resolve_composes', () => { test('deduplicates diamond dependency silently', () => { // A → B, A → C, B → D, C → D (D reached twice via different paths) const definitions: Record<string, CssClassDefinition> = { - d: {declaration: 'color: red;'}, - b: {composes: ['d'], declaration: 'padding: 10px;'}, - c: {composes: ['d'], declaration: 'margin: 10px;'}, - a: {composes: ['b', 'c']}, + d: { declaration: 'color: red;' }, + b: { composes: ['d'], declaration: 'padding: 10px;' }, + c: { composes: ['d'], declaration: 'margin: 10px;' }, + a: { composes: ['b', 'c'] } }; const result = resolve_composes(['a'], definitions, new Set(), new Set(), 'test_class'); @@ -251,10 +251,10 @@ describe('resolve_composes', () => { test('deduplicates deeper diamond silently', () => { const definitions: Record<string, CssClassDefinition> = { - e: {declaration: 'font-size: 16px;'}, - d: {composes: ['e'], declaration: 'color: blue;'}, - f: {composes: ['e'], declaration: 'color: green;'}, - c: {composes: ['d', 'f']}, + e: { declaration: 'font-size: 16px;' }, + d: { composes: ['e'], declaration: 'color: blue;' }, + f: { composes: ['e'], declaration: 'color: green;' }, + c: { composes: ['d', 'f'] } }; const result = resolve_composes(['c'], definitions, new Set(), new Set(), 'test_class'); @@ -266,9 +266,9 @@ describe('resolve_composes', () => { test('warns on redundant class included by sibling', () => { // b depends on d, so listing d after b is redundant const definitions: Record<string, CssClassDefinition> = { - d: {declaration: 'color: red;'}, - b: {composes: ['d'], declaration: 'padding: 10px;'}, - a: {composes: ['b', 'd']}, // d is redundant + d: { declaration: 'color: red;' }, + b: { composes: ['d'], declaration: 'padding: 10px;' }, + a: { composes: ['b', 'd'] } // d is redundant }; const result = resolve_composes(['a'], definitions, new Set(), new Set(), 'test_class'); @@ -278,15 +278,15 @@ describe('resolve_composes', () => { assert.strictEqual(result.warnings?.[0]?.message, 'Class "d" is redundant'); assert.strictEqual( result.warnings?.[0]?.suggestion, - 'Already included by another class in this definition', + 'Already included by another class in this definition' ); }); test('warns on all redundant classes', () => { const definitions: Record<string, CssClassDefinition> = { - d: {declaration: 'color: red;'}, - b: {composes: ['d'], declaration: 'padding: 10px;'}, - a: {composes: ['b', 'd', 'b']}, // both d and second b are redundant + d: { declaration: 'color: red;' }, + b: { composes: ['d'], declaration: 'padding: 10px;' }, + a: { composes: ['b', 'd', 'b'] } // both d and second b are redundant }; const result = resolve_composes(['a'], definitions, new Set(), new Set(), 'test_class'); @@ -299,8 +299,8 @@ describe('resolve_composes', () => { test('warns on explicit duplicate in composes array', () => { const definitions: Record<string, CssClassDefinition> = { - p_lg: {declaration: 'padding: var(--space_lg);'}, - dup: {composes: ['p_lg', 'p_lg']}, + p_lg: { declaration: 'padding: var(--space_lg);' }, + dup: { composes: ['p_lg', 'p_lg'] } }; const result = resolve_composes(['dup'], definitions, new Set(), new Set(), 'test_class'); @@ -312,10 +312,10 @@ describe('resolve_composes', () => { test('warns on explicit class after diamond deps include it', () => { const definitions: Record<string, CssClassDefinition> = { - d: {declaration: 'color: red;'}, - b: {composes: ['d'], declaration: 'padding: 10px;'}, - c: {composes: ['d'], declaration: 'margin: 10px;'}, - a: {composes: ['b', 'c', 'd']}, // explicit d is redundant + d: { declaration: 'color: red;' }, + b: { composes: ['d'], declaration: 'padding: 10px;' }, + c: { composes: ['d'], declaration: 'margin: 10px;' }, + a: { composes: ['b', 'c', 'd'] } // explicit d is redundant }; const result = resolve_composes(['a'], definitions, new Set(), new Set(), 'test_class'); @@ -329,14 +329,14 @@ describe('resolve_composes', () => { describe('empty nested composes array', () => { test('handles class with empty composes array', () => { const definitions: Record<string, CssClassDefinition> = { - empty_classes: {composes: []}, + empty_classes: { composes: [] } }; const result = resolve_composes( ['empty_classes'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert.isTrue(result.ok); @@ -345,14 +345,14 @@ describe('resolve_composes', () => { test('handles class with empty composes array and declaration', () => { const definitions: Record<string, CssClassDefinition> = { - with_decl: {composes: [], declaration: 'color: blue;'}, + with_decl: { composes: [], declaration: 'color: blue;' } }; const result = resolve_composes( ['with_decl'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert_resolved_declaration(result, 'color: blue;'); }); @@ -361,9 +361,9 @@ describe('resolve_composes', () => { describe('multi-level nested composes with declarations', () => { test('resolves nested composes with declarations at each level', () => { const definitions: Record<string, CssClassDefinition> = { - c: {declaration: 'color: red;'}, - b: {composes: ['c'], declaration: 'padding: 10px;'}, - a: {composes: ['b'], declaration: 'margin: 20px;'}, + c: { declaration: 'color: red;' }, + b: { composes: ['c'], declaration: 'padding: 10px;' }, + a: { composes: ['b'], declaration: 'margin: 20px;' } }; const result = resolve_composes(['a'], definitions, new Set(), new Set(), 'test_class'); assert_resolved_declaration(result, 'color: red; padding: 10px; margin: 20px;'); @@ -373,15 +373,15 @@ describe('resolve_composes', () => { describe('duplicate properties via composition', () => { test('keeps duplicate properties for CSS cascade', () => { const definitions: Record<string, CssClassDefinition> = { - base_padding: {declaration: 'padding: 10px;'}, - override_padding: {composes: ['base_padding'], declaration: 'padding: 20px;'}, + base_padding: { declaration: 'padding: 10px;' }, + override_padding: { composes: ['base_padding'], declaration: 'padding: 20px;' } }; const result = resolve_composes( ['override_padding'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert_resolved_declaration(result, 'padding: 10px; padding: 20px;'); }); @@ -390,7 +390,7 @@ describe('resolve_composes', () => { describe('whitespace handling', () => { test('trims whitespace from declarations', () => { const definitions: Record<string, CssClassDefinition> = { - spaced: {declaration: ' padding: 10px; '}, + spaced: { declaration: ' padding: 10px; ' } }; const result = resolve_composes(['spaced'], definitions, new Set(), new Set(), 'test_class'); assert_resolved_declaration(result, 'padding: 10px;'); @@ -398,8 +398,8 @@ describe('resolve_composes', () => { test('joins declarations with single space', () => { const definitions: Record<string, CssClassDefinition> = { - a: {declaration: 'color: red;'}, - b: {declaration: 'padding: 10px;'}, + a: { declaration: 'color: red;' }, + b: { declaration: 'padding: 10px;' } }; const result = resolve_composes(['a', 'b'], definitions, new Set(), new Set(), 'test_class'); assert_resolved_declaration(result, 'color: red; padding: 10px;'); @@ -407,14 +407,14 @@ describe('resolve_composes', () => { test('trims whitespace-only declarations and warns', () => { const definitions: Record<string, CssClassDefinition> = { - whitespace_only: {declaration: ' '}, + whitespace_only: { declaration: ' ' } }; const result = resolve_composes( ['whitespace_only'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert.isTrue(result.ok); @@ -427,7 +427,7 @@ describe('resolve_composes', () => { describe('pre-populated resolution stack', () => { test('detects cycle with pre-populated stack', () => { const definitions: Record<string, CssClassDefinition> = { - inner: {declaration: 'color: red;'}, + inner: { declaration: 'color: red;' } }; // Simulate being called mid-resolution where 'outer' is already in stack const result = resolve_composes( @@ -435,7 +435,7 @@ describe('resolve_composes', () => { definitions, new Set(['outer']), new Set(), - 'test_class', + 'test_class' ); assert.isFalse(result.ok); @@ -447,15 +447,15 @@ describe('resolve_composes', () => { describe('deep nested ruleset error', () => { test('propagates ruleset error from deeply nested class', () => { const definitions: Record<string, CssClassDefinition> = { - clickable: {ruleset: '.clickable { cursor: pointer; }'}, - wrapper: {composes: ['clickable']}, - outer: {composes: ['wrapper']}, + clickable: { ruleset: '.clickable { cursor: pointer; }' }, + wrapper: { composes: ['clickable'] }, + outer: { composes: ['wrapper'] } }; const result = resolve_composes(['outer'], definitions, new Set(), new Set(), 'test_class'); assert_resolution_error( result, - 'Cannot reference ruleset class "clickable" in composes array', + 'Cannot reference ruleset class "clickable" in composes array' ); }); }); @@ -463,14 +463,14 @@ describe('resolve_composes', () => { describe('empty declaration string', () => { test('warns about empty declaration string', () => { const definitions: Record<string, CssClassDefinition> = { - empty_decl: {declaration: ''}, + empty_decl: { declaration: '' } }; const result = resolve_composes( ['empty_decl'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert.isTrue(result.ok); @@ -483,15 +483,15 @@ describe('resolve_composes', () => { test('skips empty declarations when joining and warns', () => { const definitions: Record<string, CssClassDefinition> = { - empty: {declaration: ''}, - filled: {declaration: 'color: red;'}, + empty: { declaration: '' }, + filled: { declaration: 'color: red;' } }; const result = resolve_composes( ['empty', 'filled'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert.isTrue(result.ok); @@ -503,16 +503,16 @@ describe('resolve_composes', () => { test('collects multiple empty declaration warnings', () => { const definitions: Record<string, CssClassDefinition> = { - empty1: {declaration: ''}, - empty2: {declaration: ' '}, - filled: {declaration: 'color: red;'}, + empty1: { declaration: '' }, + empty2: { declaration: ' ' }, + filled: { declaration: 'color: red;' } }; const result = resolve_composes( ['empty1', 'filled', 'empty2'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert.isTrue(result.ok); @@ -526,15 +526,15 @@ describe('resolve_composes', () => { describe('fail-fast behavior', () => { test('fails on first invalid class in array', () => { const definitions: Record<string, CssClassDefinition> = { - valid: {declaration: 'color: red;'}, - ruleset_class: {ruleset: '.ruleset_class { cursor: pointer; }'}, + valid: { declaration: 'color: red;' }, + ruleset_class: { ruleset: '.ruleset_class { cursor: pointer; }' } }; const result = resolve_composes( ['valid', 'ruleset_class', 'valid'], definitions, new Set(), new Set(), - 'test_class', + 'test_class' ); assert.isFalse(result.ok); @@ -545,16 +545,16 @@ describe('resolve_composes', () => { describe('deep composition chains', () => { test('resolves 10-level deep chain', () => { const definitions: Record<string, CssClassDefinition> = { - l10: {declaration: 'color: red;'}, - l9: {composes: ['l10']}, - l8: {composes: ['l9']}, - l7: {composes: ['l8']}, - l6: {composes: ['l7']}, - l5: {composes: ['l6']}, - l4: {composes: ['l5']}, - l3: {composes: ['l4']}, - l2: {composes: ['l3']}, - l1: {composes: ['l2']}, + l10: { declaration: 'color: red;' }, + l9: { composes: ['l10'] }, + l8: { composes: ['l9'] }, + l7: { composes: ['l8'] }, + l6: { composes: ['l7'] }, + l5: { composes: ['l6'] }, + l4: { composes: ['l5'] }, + l3: { composes: ['l4'] }, + l2: { composes: ['l3'] }, + l1: { composes: ['l2'] } }; const result = resolve_composes(['l1'], definitions, new Set(), new Set(), 'test'); assert_resolved_declaration(result, 'color: red;'); @@ -562,8 +562,8 @@ describe('resolve_composes', () => { test('error on missing class mid-chain', () => { const definitions: Record<string, CssClassDefinition> = { - c: {composes: ['b']}, - a: {composes: ['c']}, + c: { composes: ['b'] }, + a: { composes: ['c'] } // 'b' is missing }; const result = resolve_composes(['a'], definitions, new Set(), new Set(), 'test'); @@ -573,12 +573,12 @@ describe('resolve_composes', () => { test('diamond with deep shared node', () => { // a → b,c → d → e → f (f is shared deeply nested) const definitions: Record<string, CssClassDefinition> = { - f: {declaration: 'font-size: 16px;'}, - e: {composes: ['f'], declaration: 'font-weight: bold;'}, - d: {composes: ['e'], declaration: 'color: blue;'}, - b: {composes: ['d'], declaration: 'padding: 10px;'}, - c: {composes: ['d'], declaration: 'margin: 10px;'}, - a: {composes: ['b', 'c']}, + f: { declaration: 'font-size: 16px;' }, + e: { composes: ['f'], declaration: 'font-weight: bold;' }, + d: { composes: ['e'], declaration: 'color: blue;' }, + b: { composes: ['d'], declaration: 'padding: 10px;' }, + c: { composes: ['d'], declaration: 'margin: 10px;' }, + a: { composes: ['b', 'c'] } }; const result = resolve_composes(['a'], definitions, new Set(), new Set(), 'test'); @@ -591,12 +591,12 @@ describe('resolve_composes', () => { test('wide diamond with many branches', () => { // 4 branches all depending on a shared base const definitions: Record<string, CssClassDefinition> = { - base: {declaration: 'color: red;'}, - branch1: {composes: ['base'], declaration: 'padding: 1px;'}, - branch2: {composes: ['base'], declaration: 'padding: 2px;'}, - branch3: {composes: ['base'], declaration: 'padding: 3px;'}, - branch4: {composes: ['base'], declaration: 'padding: 4px;'}, - top: {composes: ['branch1', 'branch2', 'branch3', 'branch4']}, + base: { declaration: 'color: red;' }, + branch1: { composes: ['base'], declaration: 'padding: 1px;' }, + branch2: { composes: ['base'], declaration: 'padding: 2px;' }, + branch3: { composes: ['base'], declaration: 'padding: 3px;' }, + branch4: { composes: ['base'], declaration: 'padding: 4px;' }, + top: { composes: ['branch1', 'branch2', 'branch3', 'branch4'] } }; const result = resolve_composes(['top'], definitions, new Set(), new Set(), 'test'); @@ -613,7 +613,7 @@ describe('resolve_composes', () => { describe('resolve_class_definition', () => { describe('declaration-only definitions', () => { test('returns declaration directly', () => { - const def: CssClassDefinitionStatic = {declaration: 'color: red;'}; + const def: CssClassDefinitionStatic = { declaration: 'color: red;' }; const result = resolve_class_definition(def, 'test', {}); assert.isTrue(result.ok); @@ -622,13 +622,13 @@ describe('resolve_class_definition', () => { }); test('trims declaration whitespace', () => { - const def: CssClassDefinitionStatic = {declaration: ' color: red; '}; + const def: CssClassDefinitionStatic = { declaration: ' color: red; ' }; const result = resolve_class_definition(def, 'test', {}); assert_resolved_declaration(result, 'color: red;'); }); test('warns about empty declaration', () => { - const def: CssClassDefinitionStatic = {declaration: ''}; + const def: CssClassDefinitionStatic = { declaration: '' }; const result = resolve_class_definition(def, 'test', {}); assert.isTrue(result.ok); @@ -641,9 +641,9 @@ describe('resolve_class_definition', () => { describe('composes-only definitions', () => { test('resolves composes array', () => { const definitions: Record<string, CssClassDefinition> = { - p_lg: {declaration: 'padding: var(--space_lg);'}, + p_lg: { declaration: 'padding: var(--space_lg);' } }; - const def: CssClassDefinitionStatic = {composes: ['p_lg']}; + const def: CssClassDefinitionStatic = { composes: ['p_lg'] }; const result = resolve_class_definition(def, 'test', definitions); assert_resolved_declaration(result, 'padding: var(--space_lg);'); }); @@ -652,11 +652,11 @@ describe('resolve_class_definition', () => { describe('composes + declaration definitions', () => { test('combines resolved composes with declaration', () => { const definitions: Record<string, CssClassDefinition> = { - p_lg: {declaration: 'padding: var(--space_lg);'}, + p_lg: { declaration: 'padding: var(--space_lg);' } }; const def: CssClassDefinitionStatic = { composes: ['p_lg'], - declaration: 'margin: 10px;', + declaration: 'margin: 10px;' }; const result = resolve_class_definition(def, 'test', definitions); assert_resolved_declaration(result, 'padding: var(--space_lg); margin: 10px;'); @@ -664,11 +664,11 @@ describe('resolve_class_definition', () => { test('trims declaration in combined output', () => { const definitions: Record<string, CssClassDefinition> = { - p_lg: {declaration: 'padding: var(--space_lg);'}, + p_lg: { declaration: 'padding: var(--space_lg);' } }; const def: CssClassDefinitionStatic = { composes: ['p_lg'], - declaration: ' margin: 10px; ', + declaration: ' margin: 10px; ' }; const result = resolve_class_definition(def, 'test', definitions); assert_resolved_declaration(result, 'padding: var(--space_lg); margin: 10px;'); @@ -676,11 +676,11 @@ describe('resolve_class_definition', () => { test('warns about empty declaration with composes', () => { const definitions: Record<string, CssClassDefinition> = { - p_lg: {declaration: 'padding: var(--space_lg);'}, + p_lg: { declaration: 'padding: var(--space_lg);' } }; const def: CssClassDefinitionStatic = { composes: ['p_lg'], - declaration: '', + declaration: '' }; const result = resolve_class_definition(def, 'test', definitions); @@ -693,7 +693,7 @@ describe('resolve_class_definition', () => { describe('ruleset definitions', () => { test('returns empty declaration for ruleset', () => { - const def: CssClassDefinitionStatic = {ruleset: '.test { color: red; }'}; + const def: CssClassDefinitionStatic = { ruleset: '.test { color: red; }' }; const result = resolve_class_definition(def, 'test', {}); assert_resolved_declaration(result, ''); }); @@ -702,9 +702,9 @@ describe('resolve_class_definition', () => { describe('self-reference prevention', () => { test('detects self-reference in composes', () => { const definitions: Record<string, CssClassDefinition> = { - self_ref: {composes: ['self_ref']}, + self_ref: { composes: ['self_ref'] } }; - const def: CssClassDefinitionStatic = {composes: ['self_ref']}; + const def: CssClassDefinitionStatic = { composes: ['self_ref'] }; const result = resolve_class_definition(def, 'self_ref', definitions); assert_resolution_error(result, 'Circular reference'); @@ -713,7 +713,7 @@ describe('resolve_class_definition', () => { describe('error propagation', () => { test('propagates unknown class error', () => { - const def: CssClassDefinitionStatic = {composes: ['nonexistent']}; + const def: CssClassDefinitionStatic = { composes: ['nonexistent'] }; const result = resolve_class_definition(def, 'test', {}); assert_resolution_error(result, 'Unknown class "nonexistent" in composes array'); diff --git a/src/test/css_classes.test.ts b/src/test/css_classes.test.ts index 13e3103e3..e69416ac5 100644 --- a/src/test/css_classes.test.ts +++ b/src/test/css_classes.test.ts @@ -1,18 +1,18 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {CssClasses} from '$lib/css_classes.ts'; -import {type ExtractionDiagnostic, type SourceLocation} from '$lib/diagnostics.ts'; -import {make_extraction_data} from './test_helpers.ts'; +import { CssClasses } from '$lib/css_classes.ts'; +import { type ExtractionDiagnostic, type SourceLocation } from '$lib/diagnostics.ts'; +import { make_extraction_data } from './test_helpers.ts'; describe('CssClasses', () => { test('add and get', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; const classes: Map<string, Array<SourceLocation>> = new Map([ ['foo', [loc]], - ['bar', [loc]], + ['bar', [loc]] ]); - css_classes.add('file1.svelte', make_extraction_data({classes})); + css_classes.add('file1.svelte', make_extraction_data({ classes })); const result = css_classes.get(); assert.isTrue(result.has('foo')); @@ -21,11 +21,11 @@ describe('CssClasses', () => { test('merges classes from multiple files', () => { const css_classes = new CssClasses(); - const loc1: SourceLocation = {file: 'file1.svelte', line: 1, column: 1}; - const loc2: SourceLocation = {file: 'file2.svelte', line: 5, column: 10}; + const loc1: SourceLocation = { file: 'file1.svelte', line: 1, column: 1 }; + const loc2: SourceLocation = { file: 'file2.svelte', line: 5, column: 10 }; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['foo', [loc1]]])})); - css_classes.add('file2.svelte', make_extraction_data({classes: new Map([['bar', [loc2]]])})); + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc1]]]) })); + css_classes.add('file2.svelte', make_extraction_data({ classes: new Map([['bar', [loc2]]]) })); const result = css_classes.get(); assert.isTrue(result.has('foo')); @@ -34,10 +34,10 @@ describe('CssClasses', () => { test('delete removes file classes', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['foo', [loc]]])})); - css_classes.add('file2.svelte', make_extraction_data({classes: new Map([['bar', [loc]]])})); + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc]]]) })); + css_classes.add('file2.svelte', make_extraction_data({ classes: new Map([['bar', [loc]]]) })); css_classes.delete('file1.svelte'); @@ -48,10 +48,10 @@ describe('CssClasses', () => { test('additional_classes always included', () => { const css_classes = new CssClasses(new Set(['always-included'])); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', - make_extraction_data({classes: new Map([['extracted', [loc]]])}), + make_extraction_data({ classes: new Map([['extracted', [loc]]]) }) ); const result = css_classes.get(); @@ -61,10 +61,10 @@ describe('CssClasses', () => { test('get_with_locations returns null for additional_classes', () => { const css_classes = new CssClasses(new Set(['included'])); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', - make_extraction_data({classes: new Map([['extracted', [loc]]])}), + make_extraction_data({ classes: new Map([['extracted', [loc]]]) }) ); const result = css_classes.get_with_locations(); @@ -74,11 +74,17 @@ describe('CssClasses', () => { test('get_with_locations merges locations from multiple files', () => { const css_classes = new CssClasses(); - const loc1: SourceLocation = {file: 'file1.svelte', line: 1, column: 1}; - const loc2: SourceLocation = {file: 'file2.svelte', line: 5, column: 10}; + const loc1: SourceLocation = { file: 'file1.svelte', line: 1, column: 1 }; + const loc2: SourceLocation = { file: 'file2.svelte', line: 5, column: 10 }; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['shared', [loc1]]])})); - css_classes.add('file2.svelte', make_extraction_data({classes: new Map([['shared', [loc2]]])})); + css_classes.add( + 'file1.svelte', + make_extraction_data({ classes: new Map([['shared', [loc1]]]) }) + ); + css_classes.add( + 'file2.svelte', + make_extraction_data({ classes: new Map([['shared', [loc2]]]) }) + ); const result = css_classes.get_with_locations(); const locations = result.get('shared'); @@ -89,18 +95,18 @@ describe('CssClasses', () => { test('get_diagnostics returns extraction diagnostics', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; const diagnostics: Array<ExtractionDiagnostic> = [ { phase: 'extraction', level: 'warning', message: 'test warning', suggestion: null, - location: loc, - }, + location: loc + } ]; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map(), diagnostics})); + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map(), diagnostics })); const result = css_classes.get_diagnostics(); assert.lengthOf(result, 1); @@ -109,22 +115,22 @@ describe('CssClasses', () => { test('dirty flag triggers recalculation', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['foo', [loc]]])})); + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc]]]) })); const result1 = css_classes.get(); assert.isTrue(result1.has('foo')); // Add more classes - css_classes.add('file2.svelte', make_extraction_data({classes: new Map([['bar', [loc]]])})); + css_classes.add('file2.svelte', make_extraction_data({ classes: new Map([['bar', [loc]]]) })); const result2 = css_classes.get(); assert.isTrue(result2.has('bar')); }); test('cache returns same object when not dirty', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['foo', [loc]]])})); + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc]]]) })); const result1 = css_classes.get(); const result2 = css_classes.get(); @@ -133,10 +139,10 @@ describe('CssClasses', () => { test('re-adding same file replaces classes', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['old', [loc]]])})); - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['new', [loc]]])})); + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map([['old', [loc]]]) })); + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map([['new', [loc]]]) })); const result = css_classes.get(); assert.isFalse(result.has('old')); @@ -145,18 +151,18 @@ describe('CssClasses', () => { test('explicit_classes tracks @fuz-classes annotations', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.ts', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.ts', line: 1, column: 1 }; const explicit = new Set(['annotated_class']); css_classes.add( 'file1.ts', make_extraction_data({ classes: new Map([['regular', [loc]]]), - explicit_classes: explicit, - }), + explicit_classes: explicit + }) ); - const {explicit_classes} = css_classes.get_all(); + const { explicit_classes } = css_classes.get_all(); assert.isNotNull(explicit_classes); assert.isTrue(explicit_classes.has('annotated_class')); assert.isFalse(explicit_classes.has('regular')); @@ -165,26 +171,26 @@ describe('CssClasses', () => { test('additional_classes are included in explicit_classes', () => { const css_classes = new CssClasses(new Set(['included_class'])); - const {explicit_classes} = css_classes.get_all(); + const { explicit_classes } = css_classes.get_all(); assert.isNotNull(explicit_classes); assert.isTrue(explicit_classes.has('included_class')); }); test('exclude_classes filters from all_classes', () => { const css_classes = new CssClasses(null, new Set(['excluded'])); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', make_extraction_data({ classes: new Map([ ['kept', [loc]], - ['excluded', [loc]], - ]), - }), + ['excluded', [loc]] + ]) + }) ); - const {all_classes} = css_classes.get_all(); + const { all_classes } = css_classes.get_all(); assert.isTrue(all_classes.has('kept')); assert.isFalse(all_classes.has('excluded')); }); @@ -194,7 +200,7 @@ describe('CssClasses', () => { const exclude = new Set(['included_explicit']); const css_classes = new CssClasses(include, exclude); - const {explicit_classes} = css_classes.get_all(); + const { explicit_classes } = css_classes.get_all(); // included_explicit is in both include and exclude, so it should be excluded assert.isNull(explicit_classes); }); @@ -206,10 +212,10 @@ describe('CssClasses', () => { css_classes.add( 'file1.ts', - make_extraction_data({classes: new Map(), explicit_classes: explicit}), + make_extraction_data({ classes: new Map(), explicit_classes: explicit }) ); - const {explicit_classes} = css_classes.get_all(); + const { explicit_classes } = css_classes.get_all(); // Should be filtered out by exclude assert.isNull(explicit_classes); }); @@ -218,24 +224,24 @@ describe('CssClasses', () => { describe('elements', () => { test('all_elements aggregates elements from files', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc]]]), - elements: new Set(['button', 'input']), - }), + elements: new Set(['button', 'input']) + }) ); css_classes.add( 'file2.svelte', make_extraction_data({ classes: new Map([['bar', [loc]]]), - elements: new Set(['a', 'button']), - }), + elements: new Set(['a', 'button']) + }) ); - const {all_elements} = css_classes.get_all(); + const { all_elements } = css_classes.get_all(); assert.isTrue(all_elements.has('button')); assert.isTrue(all_elements.has('input')); assert.isTrue(all_elements.has('a')); @@ -244,33 +250,33 @@ describe('elements', () => { test('all_elements is empty when no files have elements', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['foo', [loc]]])})); + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc]]]) })); - const {all_elements} = css_classes.get_all(); + const { all_elements } = css_classes.get_all(); assert.strictEqual(all_elements.size, 0); }); test('explicit_elements aggregates from @fuz-elements annotations', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc]]]), - explicit_elements: new Set(['button']), - }), + explicit_elements: new Set(['button']) + }) ); css_classes.add( 'file2.svelte', make_extraction_data({ classes: new Map([['bar', [loc]]]), - explicit_elements: new Set(['input']), - }), + explicit_elements: new Set(['input']) + }) ); - const {explicit_elements} = css_classes.get_all(); + const { explicit_elements } = css_classes.get_all(); assert.isNotNull(explicit_elements); assert.isTrue(explicit_elements.has('button')); assert.isTrue(explicit_elements.has('input')); @@ -278,36 +284,36 @@ describe('elements', () => { test('explicit_elements is null when no files have them', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['foo', [loc]]])})); + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc]]]) })); - const {explicit_elements} = css_classes.get_all(); + const { explicit_elements } = css_classes.get_all(); assert.isNull(explicit_elements); }); test('delete removes file elements', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc]]]), elements: new Set(['button']), - explicit_elements: new Set(['button']), - }), + explicit_elements: new Set(['button']) + }) ); css_classes.add( 'file2.svelte', make_extraction_data({ classes: new Map([['bar', [loc]]]), - elements: new Set(['input']), - }), + elements: new Set(['input']) + }) ); css_classes.delete('file1.svelte'); - const {all_elements, explicit_elements} = css_classes.get_all(); + const { all_elements, explicit_elements } = css_classes.get_all(); assert.isFalse(all_elements.has('button')); assert.isTrue(all_elements.has('input')); assert.isNull(explicit_elements); @@ -317,18 +323,18 @@ describe('elements', () => { describe('explicit_variables (@fuz-variables)', () => { test('add with explicit_variables returns them from get_all', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; const explicit_vars = new Set(['shade_40', 'text_50']); css_classes.add( 'file1.svelte', make_extraction_data({ classes: new Map([['foo', [loc]]]), - explicit_variables: explicit_vars, - }), + explicit_variables: explicit_vars + }) ); - const {explicit_variables} = css_classes.get_all(); + const { explicit_variables } = css_classes.get_all(); assert.isNotNull(explicit_variables); assert.isTrue(explicit_variables.has('shade_40')); assert.isTrue(explicit_variables.has('text_50')); @@ -336,24 +342,24 @@ describe('explicit_variables (@fuz-variables)', () => { test('multiple files contributing explicit_variables are aggregated', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', make_extraction_data({ classes: new Map([['a', [loc]]]), - explicit_variables: new Set(['shade_40']), - }), + explicit_variables: new Set(['shade_40']) + }) ); css_classes.add( 'file2.svelte', make_extraction_data({ classes: new Map([['b', [loc]]]), - explicit_variables: new Set(['text_50']), - }), + explicit_variables: new Set(['text_50']) + }) ); - const {explicit_variables} = css_classes.get_all(); + const { explicit_variables } = css_classes.get_all(); assert.isNotNull(explicit_variables); assert.isTrue(explicit_variables.has('shade_40')); assert.isTrue(explicit_variables.has('text_50')); @@ -361,26 +367,26 @@ describe('explicit_variables (@fuz-variables)', () => { test('delete removes file explicit_variables', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', make_extraction_data({ classes: new Map([['a', [loc]]]), - explicit_variables: new Set(['shade_40']), - }), + explicit_variables: new Set(['shade_40']) + }) ); css_classes.add( 'file2.svelte', make_extraction_data({ classes: new Map([['b', [loc]]]), - explicit_variables: new Set(['text_50']), - }), + explicit_variables: new Set(['text_50']) + }) ); css_classes.delete('file1.svelte'); - const {explicit_variables} = css_classes.get_all(); + const { explicit_variables } = css_classes.get_all(); assert.isNotNull(explicit_variables); assert.isFalse(explicit_variables.has('shade_40')); assert.isTrue(explicit_variables.has('text_50')); @@ -388,14 +394,14 @@ describe('explicit_variables (@fuz-variables)', () => { test('re-adding a file replaces old explicit_variables', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', make_extraction_data({ classes: new Map([['a', [loc]]]), - explicit_variables: new Set(['shade_40']), - }), + explicit_variables: new Set(['shade_40']) + }) ); // Re-add with different variables @@ -403,11 +409,11 @@ describe('explicit_variables (@fuz-variables)', () => { 'file1.svelte', make_extraction_data({ classes: new Map([['a', [loc]]]), - explicit_variables: new Set(['text_50']), - }), + explicit_variables: new Set(['text_50']) + }) ); - const {explicit_variables} = css_classes.get_all(); + const { explicit_variables } = css_classes.get_all(); assert.isNotNull(explicit_variables); assert.isFalse(explicit_variables.has('shade_40')); assert.isTrue(explicit_variables.has('text_50')); @@ -415,11 +421,11 @@ describe('explicit_variables (@fuz-variables)', () => { test('explicit_variables is null when no files have them', () => { const css_classes = new CssClasses(); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['a', [loc]]])})); + css_classes.add('file1.svelte', make_extraction_data({ classes: new Map([['a', [loc]]]) })); - const {explicit_variables} = css_classes.get_all(); + const { explicit_variables } = css_classes.get_all(); assert.isNull(explicit_variables); }); }); @@ -427,12 +433,12 @@ describe('explicit_variables (@fuz-variables)', () => { describe('additional_classes with file data (simulating cache behavior)', () => { test('additional_classes merged with file data', () => { const css_classes = new CssClasses(new Set(['additional_a', 'additional_b'])); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; // Simulate adding file data (could be from cache or fresh extraction) css_classes.add( 'file1.svelte', - make_extraction_data({classes: new Map([['extracted_class', [loc]]])}), + make_extraction_data({ classes: new Map([['extracted_class', [loc]]]) }) ); const result = css_classes.get(); @@ -446,11 +452,11 @@ describe('additional_classes with file data (simulating cache behavior)', () => const include = new Set(['keep_me', 'exclude_me']); const exclude = new Set(['exclude_me']); const css_classes = new CssClasses(include, exclude); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', - make_extraction_data({classes: new Map([['extracted', [loc]]])}), + make_extraction_data({ classes: new Map([['extracted', [loc]]]) }) ); const result = css_classes.get(); @@ -461,17 +467,17 @@ describe('additional_classes with file data (simulating cache behavior)', () => test('multiple files combined with additional_classes', () => { const css_classes = new CssClasses(new Set(['always_included'])); - const loc1: SourceLocation = {file: 'file1.svelte', line: 1, column: 1}; - const loc2: SourceLocation = {file: 'file2.svelte', line: 5, column: 10}; + const loc1: SourceLocation = { file: 'file1.svelte', line: 1, column: 1 }; + const loc2: SourceLocation = { file: 'file2.svelte', line: 5, column: 10 }; // Add multiple files (simulating cache-restored data) css_classes.add( 'file1.svelte', - make_extraction_data({classes: new Map([['from_file1', [loc1]]])}), + make_extraction_data({ classes: new Map([['from_file1', [loc1]]]) }) ); css_classes.add( 'file2.svelte', - make_extraction_data({classes: new Map([['from_file2', [loc2]]])}), + make_extraction_data({ classes: new Map([['from_file2', [loc2]]]) }) ); const result = css_classes.get(); @@ -482,11 +488,11 @@ describe('additional_classes with file data (simulating cache behavior)', () => test('empty additional_classes has no effect on file data', () => { const css_classes = new CssClasses(null); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; css_classes.add( 'file1.svelte', - make_extraction_data({classes: new Map([['extracted', [loc]]])}), + make_extraction_data({ classes: new Map([['extracted', [loc]]]) }) ); const result = css_classes.get(); @@ -496,11 +502,14 @@ describe('additional_classes with file data (simulating cache behavior)', () => test('additional_classes appear in explicit_classes for warning tracking', () => { const css_classes = new CssClasses(new Set(['additional_class'])); - const loc: SourceLocation = {file: 'test.svelte', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.svelte', line: 1, column: 1 }; - css_classes.add('file1.svelte', make_extraction_data({classes: new Map([['regular', [loc]]])})); + css_classes.add( + 'file1.svelte', + make_extraction_data({ classes: new Map([['regular', [loc]]]) }) + ); - const {explicit_classes} = css_classes.get_all(); + const { explicit_classes } = css_classes.get_all(); // additional_classes should be in explicit_classes assert.isTrue(explicit_classes!.has('additional_class')); // Regular extracted classes are not explicit unless via @fuz-classes @@ -509,18 +518,18 @@ describe('additional_classes with file data (simulating cache behavior)', () => test('file explicit classes combined with additional_classes in explicit_classes', () => { const css_classes = new CssClasses(new Set(['additional'])); - const loc: SourceLocation = {file: 'test.ts', line: 1, column: 1}; + const loc: SourceLocation = { file: 'test.ts', line: 1, column: 1 }; const file_explicit = new Set(['from_fuz_classes_annotation']); css_classes.add( 'file1.ts', make_extraction_data({ classes: new Map([['regular', [loc]]]), - explicit_classes: file_explicit, - }), + explicit_classes: file_explicit + }) ); - const {explicit_classes} = css_classes.get_all(); + const { explicit_classes } = css_classes.get_all(); assert.isTrue(explicit_classes!.has('additional')); assert.isTrue(explicit_classes!.has('from_fuz_classes_annotation')); assert.isFalse(explicit_classes!.has('regular')); diff --git a/src/test/css_literal.test.ts b/src/test/css_literal.test.ts index c49800917..b66c1701f 100644 --- a/src/test/css_literal.test.ts +++ b/src/test/css_literal.test.ts @@ -1,4 +1,4 @@ -import {test, assert, describe, beforeAll} from 'vitest'; +import { test, assert, describe, beforeAll } from 'vitest'; import { parse_css_literal, @@ -20,15 +20,15 @@ import { type CssLiteralOutput, type LiteralResolutionResult, type ExtractedModifiers, - type ModifierExtractionResult, + type ModifierExtractionResult } from '$lib/css_literal.ts'; -import {escape_css_selector} from '$lib/css_class_generation.ts'; -import {type InterpreterDiagnostic} from '$lib/diagnostics.ts'; +import { escape_css_selector } from '$lib/css_class_generation.ts'; +import { type InterpreterDiagnostic } from '$lib/diagnostics.ts'; import { get_modifier, parse_arbitrary_breakpoint, parse_parameterized_state, - extract_balanced_parens, + extract_balanced_parens } from '$lib/modifiers.ts'; // CSS properties loaded before tests run @@ -116,7 +116,7 @@ describe('is_possible_css_literal', () => { ['width:calc(100%-20px)'], ['--custom-prop:value'], ['nth-child(2n):color:red'], - ['min-width(800px):display:flex'], + ['min-width(800px):display:flex'] ])('recognizes "%s" as possible CSS-literal', (input) => { assert.isTrue(is_possible_css_literal(input)); }); @@ -129,7 +129,7 @@ describe('is_possible_css_literal', () => { ['', 'empty'], [':', 'just colon'], ['display:', 'empty value'], - [':flex', 'empty property'], + [':flex', 'empty property'] ])('rejects "%s" as not CSS-literal (%s)', (input) => { assert.isFalse(is_possible_css_literal(input)); }); @@ -144,7 +144,7 @@ describe('extract_segments', () => { ['width:calc(100%-20px)', ['width', 'calc(100%-20px)']], ['min-width(800px):display:flex', ['min-width(800px)', 'display', 'flex']], ['before:content:""', ['before', 'content', '""']], - ['width:calc(min(100%,500px))', ['width', 'calc(min(100%,500px))']], + ['width:calc(min(100%,500px))', ['width', 'calc(min(100%,500px))']] ])('extract_segments("%s") → %j', (input, expected) => { assert.deepEqual(extract_segments(input), expected); }); @@ -153,7 +153,7 @@ describe('extract_segments', () => { test.each<[string, Array<string>]>([ ['width:calc((100%', ['width', 'calc((100%']], // unclosed - keeps as-is ['width:calc(100%))', ['width', 'calc(100%))']], // extra close - keeps as-is - ['fn((a:b))', ['fn((a:b))']], // colon inside nested parens stays inside + ['fn((a:b))', ['fn((a:b))']] // colon inside nested parens stays inside ])('extract_segments("%s") handles mismatched parens → %j', (input, expected) => { assert.deepEqual(extract_segments(input), expected); }); @@ -167,7 +167,7 @@ describe('format_css_value', () => { ['1px~solid~red', '1px solid red'], ['flex!important', 'flex !important'], ['0~auto!important', '0 auto !important'], - ['calc(100%~-~20px)', 'calc(100% - 20px)'], + ['calc(100%~-~20px)', 'calc(100% - 20px)'] ] as const)('format_css_value("%s") → "%s"', (input, expected) => { assert.strictEqual(format_css_value(input), expected); }); @@ -177,7 +177,7 @@ describe('format_css_value', () => { ['~~~', ' '], // only tildes → only spaces ['0~', '0 '], // trailing tilde → trailing space ['~0', ' 0'], // leading tilde → leading space - ['~~', ' '], // consecutive tildes → consecutive spaces + ['~~', ' '] // consecutive tildes → consecutive spaces ] as const)('format_css_value("%s") → "%s" (edge cases)', (input, expected) => { assert.strictEqual(format_css_value(input), expected); }); @@ -185,7 +185,7 @@ describe('format_css_value', () => { // !important edge cases test.each([ ['flex~!important', 'flex !important'], // tilde before !important normalizes to single space - ['0~auto~!important', '0 auto !important'], // multiple tildes before !important + ['0~auto~!important', '0 auto !important'] // multiple tildes before !important ] as const)('format_css_value("%s") → "%s" (!important edge cases)', (input, expected) => { assert.strictEqual(format_css_value(input), expected); }); @@ -196,7 +196,7 @@ describe('check_calc_expression', () => { 'warns about "%s"', (input) => { assert.isNotNull(check_calc_expression(input)); - }, + } ); test.each([ @@ -204,7 +204,7 @@ describe('check_calc_expression', () => { ['calc(100%*2)', 'multiplication needs no spaces'], ['calc(100%/2)', 'division needs no spaces'], ['100%', 'not calc'], - ['flex', 'not calc'], + ['flex', 'not calc'] ])('does not warn about "%s" (%s)', (input) => { assert.isNull(check_calc_expression(input)); }); @@ -244,7 +244,7 @@ describe('get_modifier', () => { ['before', 'pseudo-element'], ['after', 'pseudo-element'], ['placeholder', 'pseudo-element'], - ['selection', 'pseudo-element'], + ['selection', 'pseudo-element'] ] as const)('get_modifier("%s") → type: %s', (name, expected_type) => { const modifier = get_modifier(name); if (!modifier) throw new Error(`Expected modifier for "${name}"`); @@ -261,7 +261,7 @@ describe('parse_arbitrary_breakpoint', () => { ['min-width(800px)', '@media (width >= 800px)'], ['max-width(600px)', '@media (width < 600px)'], ['min-width(50rem)', '@media (width >= 50rem)'], - ['max-width(100vw)', '@media (width < 100vw)'], + ['max-width(100vw)', '@media (width < 100vw)'] ] as const)('parse_arbitrary_breakpoint("%s") → %s', (input, expected) => { assert.strictEqual(parse_arbitrary_breakpoint(input), expected); }); @@ -277,7 +277,7 @@ describe('parse_parameterized_state', () => { ['nth-child(2n+1)', ':nth-child(2n+1)'], ['nth-child(odd)', ':nth-child(odd)'], ['nth-of-type(3)', ':nth-of-type(3)'], - ['nth-of-type(2n)', ':nth-of-type(2n)'], + ['nth-of-type(2n)', ':nth-of-type(2n)'] ] as const)('parse_parameterized_state("%s") → css: %s', (input, expected_css) => { const result = parse_parameterized_state(input); if (!result) throw new Error(`Expected result for "${input}"`); @@ -297,9 +297,9 @@ describe('parse_css_literal - valid cases', () => { ['color:red', 'color', 'red'], ['font-size:16px', 'font-size', '16px'], ['z-index:100', 'z-index', '100'], - ['--custom-prop:value', '--custom-prop', 'value'], + ['--custom-prop:value', '--custom-prop', 'value'] ] as const)('parse_css_literal("%s") → property: %s, value: %s', (input, property, value) => { - const {parsed} = assert_parse_ok(parse_css_literal(input, css_properties)); + const { parsed } = assert_parse_ok(parse_css_literal(input, css_properties)); assert.strictEqual(parsed.property, property); assert.strictEqual(parsed.value, value); }); @@ -307,7 +307,7 @@ describe('parse_css_literal - valid cases', () => { describe('parse_css_literal - with modifiers', () => { test('hover:opacity:80%', () => { - const {parsed} = assert_parse_ok(parse_css_literal('hover:opacity:80%', css_properties)); + const { parsed } = assert_parse_ok(parse_css_literal('hover:opacity:80%', css_properties)); assert.isNull(parsed.media); assert.isNull(parsed.ancestor); assert.lengthOf(parsed.states, 1); @@ -318,7 +318,7 @@ describe('parse_css_literal - with modifiers', () => { }); test('md:display:flex', () => { - const {parsed} = assert_parse_ok(parse_css_literal('md:display:flex', css_properties)); + const { parsed } = assert_parse_ok(parse_css_literal('md:display:flex', css_properties)); if (!parsed.media) throw new Error('Expected media'); assert.strictEqual(parsed.media.name, 'md'); assert.isNull(parsed.ancestor); @@ -327,7 +327,7 @@ describe('parse_css_literal - with modifiers', () => { }); test('dark:opacity:60%', () => { - const {parsed} = assert_parse_ok(parse_css_literal('dark:opacity:60%', css_properties)); + const { parsed } = assert_parse_ok(parse_css_literal('dark:opacity:60%', css_properties)); assert.isNull(parsed.media); if (!parsed.ancestor) throw new Error('Expected ancestor'); assert.strictEqual(parsed.ancestor.name, 'dark'); @@ -335,15 +335,15 @@ describe('parse_css_literal - with modifiers', () => { }); test('before:content:""', () => { - const {parsed} = assert_parse_ok(parse_css_literal('before:content:""', css_properties)); + const { parsed } = assert_parse_ok(parse_css_literal('before:content:""', css_properties)); if (!parsed.pseudo_element) throw new Error('Expected pseudo_element'); assert.strictEqual(parsed.pseudo_element.name, 'before'); assert.strictEqual(parsed.property, 'content'); }); test('md:dark:hover:before:opacity:80%', () => { - const {parsed} = assert_parse_ok( - parse_css_literal('md:dark:hover:before:opacity:80%', css_properties), + const { parsed } = assert_parse_ok( + parse_css_literal('md:dark:hover:before:opacity:80%', css_properties) ); if (!parsed.media) throw new Error('Expected media'); assert.strictEqual(parsed.media.name, 'md'); @@ -358,15 +358,15 @@ describe('parse_css_literal - with modifiers', () => { }); test('focus:hover:color:red (alphabetical states)', () => { - const {parsed} = assert_parse_ok(parse_css_literal('focus:hover:color:red', css_properties)); + const { parsed } = assert_parse_ok(parse_css_literal('focus:hover:color:red', css_properties)); assert.lengthOf(parsed.states, 2); assert.strictEqual(parsed.states[0]!.name, 'focus'); assert.strictEqual(parsed.states[1]!.name, 'hover'); }); test('active:focus:hover:opacity:80% (multiple states alphabetical)', () => { - const {parsed} = assert_parse_ok( - parse_css_literal('active:focus:hover:opacity:80%', css_properties), + const { parsed } = assert_parse_ok( + parse_css_literal('active:focus:hover:opacity:80%', css_properties) ); assert.lengthOf(parsed.states, 3); assert.strictEqual(parsed.states[0]!.name, 'active'); @@ -375,24 +375,24 @@ describe('parse_css_literal - with modifiers', () => { }); test('min-width(800px):display:flex (arbitrary breakpoint)', () => { - const {parsed} = assert_parse_ok( - parse_css_literal('min-width(800px):display:flex', css_properties), + const { parsed } = assert_parse_ok( + parse_css_literal('min-width(800px):display:flex', css_properties) ); if (!parsed.media) throw new Error('Expected media'); assert.strictEqual(parsed.media.css, '@media (width >= 800px)'); }); test('nth-child(2n+1):color:red (parameterized state)', () => { - const {parsed} = assert_parse_ok( - parse_css_literal('nth-child(2n+1):color:red', css_properties), + const { parsed } = assert_parse_ok( + parse_css_literal('nth-child(2n+1):color:red', css_properties) ); assert.lengthOf(parsed.states, 1); assert.strictEqual(parsed.states[0]!.css, ':nth-child(2n+1)'); }); test('hover:nth-child(2n):color:red (alphabetical with parameterized)', () => { - const {parsed} = assert_parse_ok( - parse_css_literal('hover:nth-child(2n):color:red', css_properties), + const { parsed } = assert_parse_ok( + parse_css_literal('hover:nth-child(2n):color:red', css_properties) ); assert.lengthOf(parsed.states, 2); // 'hover' < 'nth-child(2n)' alphabetically @@ -408,9 +408,9 @@ describe('parse_css_literal - error cases', () => { ['dark:md:display:none', 'Media modifier must come before'], ['hover:dark:display:none', 'Ancestor modifier must come before'], ['before:hover:opacity:100%', 'State modifiers must come before'], - ['nth-child(2n):hover:color:red', 'alphabetical order'], + ['nth-child(2n):hover:color:red', 'alphabetical order'] ])('%s → error containing "%s"', (input, expected_message) => { - const {error} = assert_parse_error(parse_css_literal(input, css_properties)); + const { error } = assert_parse_error(parse_css_literal(input, css_properties)); assert.include(error.message, expected_message); }); }); @@ -419,26 +419,26 @@ describe('parse_css_literal - error cases', () => { test.each<[string, string]>([ ['dark:light:color:red', 'mutually exclusive'], ['sm:md:display:flex', 'Multiple media modifiers'], - ['before:after:content:""', 'Multiple pseudo-element'], + ['before:after:content:""', 'Multiple pseudo-element'] ])('%s → error containing "%s"', (input, expected_message) => { - const {error} = assert_parse_error(parse_css_literal(input, css_properties)); + const { error } = assert_parse_error(parse_css_literal(input, css_properties)); assert.include(error.message, expected_message); }); }); test('unknown:color:red (unknown modifier)', () => { - const {error} = assert_parse_error(parse_css_literal('unknown:color:red', css_properties)); + const { error } = assert_parse_error(parse_css_literal('unknown:color:red', css_properties)); assert.include(error.message, 'Unknown modifier'); }); test('hoverr:color:red (typo with suggestion)', () => { - const {error} = assert_parse_error(parse_css_literal('hoverr:color:red', css_properties)); + const { error } = assert_parse_error(parse_css_literal('hoverr:color:red', css_properties)); assert.include(error.message, 'Unknown modifier'); assert.isDefined(error.suggestion); }); test('dipslay:flex (typo in property with suggestion)', () => { - const {error} = assert_parse_error(parse_css_literal('dipslay:flex', css_properties)); + const { error } = assert_parse_error(parse_css_literal('dipslay:flex', css_properties)); assert.include(error.message, 'Unknown CSS property'); assert.isDefined(error.suggestion); assert.include(error.suggestion ?? '', 'display'); @@ -447,8 +447,8 @@ describe('parse_css_literal - error cases', () => { describe('parse_css_literal - warnings', () => { test('width:calc(100%-20px) generates warning', () => { - const {diagnostics} = assert_parse_ok( - parse_css_literal('width:calc(100%-20px)', css_properties), + const { diagnostics } = assert_parse_ok( + parse_css_literal('width:calc(100%-20px)', css_properties) ); assert.ok(diagnostics); assert.lengthOf(diagnostics, 1); @@ -518,8 +518,8 @@ describe('interpret_css_literal', () => { const result = interpret_css_literal(class_name, escaped, css_properties); assert.isFalse(result.ok); assert.include( - (result as {ok: false; error: {message: string}}).error.message, - 'Unknown CSS property', + (result as { ok: false; error: { message: string } }).error.message, + 'Unknown CSS property' ); }); @@ -590,7 +590,7 @@ describe('suggest_css_property', () => { test.each([ ['dipslay', 'display'], ['opacty', 'opacity'], - ['colr', 'color'], + ['colr', 'color'] ] as const)('suggests %s for %s', (typo, expected) => { assert.strictEqual(suggest_css_property(typo, css_properties), expected); }); @@ -604,7 +604,7 @@ describe('suggest_modifier', () => { test.each([ ['hovr', 'hover'], ['focis', 'focus'], - ['actve', 'active'], + ['actve', 'active'] ] as const)('suggests "%s" → "%s"', (typo, expected) => { assert.strictEqual(suggest_modifier(typo), expected); }); @@ -620,9 +620,9 @@ describe('parse_css_literal - max breakpoints', () => { ['max-md:flex-direction:column', 'max-md', 'flex-direction', 'column'], ['max-lg:padding:1rem', 'max-lg', 'padding', '1rem'], ['max-xl:gap:0', 'max-xl', 'gap', '0'], - ['max-2xl:margin:auto', 'max-2xl', 'margin', 'auto'], + ['max-2xl:margin:auto', 'max-2xl', 'margin', 'auto'] ] as const)('%s parses correctly', (input, media_name, property, value) => { - const {parsed} = assert_parse_ok(parse_css_literal(input, css_properties)); + const { parsed } = assert_parse_ok(parse_css_literal(input, css_properties)); if (!parsed.media) throw new Error('Expected media'); assert.strictEqual(parsed.media.name, media_name); assert.include(parsed.media.css, 'width <'); @@ -634,7 +634,7 @@ describe('parse_css_literal - max breakpoints', () => { describe('generate_css_literal_simple - max breakpoints', () => { test.each([ ['max-sm:display:none', '@media (width < 40rem)', 'display: none;'], - ['max-lg:opacity:50%', '@media (width < 64rem)', 'opacity: 50%;'], + ['max-lg:opacity:50%', '@media (width < 64rem)', 'opacity: 50%;'] ] as const)('%s generates correct media query', (class_name, expected_media, expected_decl) => { const escaped = escape_css_selector(class_name); const result = interpret_css_literal(class_name, escaped, css_properties); @@ -650,9 +650,9 @@ describe('parse_css_literal - !important with modifiers', () => { ['display:flex!important', 'display', 'flex !important'], ['hover:opacity:100%!important', 'opacity', '100% !important'], ['md:dark:display:block!important', 'display', 'block !important'], - ['margin:0~auto!important', 'margin', '0 auto !important'], + ['margin:0~auto!important', 'margin', '0 auto !important'] ] as const)('%s → property: %s, value: %s', (input, property, value) => { - const {parsed} = assert_parse_ok(parse_css_literal(input, css_properties)); + const { parsed } = assert_parse_ok(parse_css_literal(input, css_properties)); assert.strictEqual(parsed.property, property); assert.strictEqual(parsed.value, value); }); @@ -661,7 +661,7 @@ describe('parse_css_literal - !important with modifiers', () => { describe('generate_css_literal_simple - !important', () => { test.each([ ['display:flex!important', 'display: flex !important;', null], - ['hover:color:red!important', 'color: red !important;', ':hover'], + ['hover:color:red!important', 'color: red !important;', ':hover'] ] as const)('%s renders correctly', (class_name, expected_decl, expected_selector) => { const escaped = escape_css_selector(class_name); const result = interpret_css_literal(class_name, escaped, css_properties); @@ -677,9 +677,9 @@ describe('parse_css_literal - Unicode values', () => { ['content:"→"', 'content', '"→"'], ['content:"✓"', 'content', '"✓"'], ['before:content:"«"', 'content', '"«"'], - ['list-style-type:"•"', 'list-style-type', '"•"'], + ['list-style-type:"•"', 'list-style-type', '"•"'] ] as const)('%s → property: %s, value: %s', (input, property, value) => { - const {parsed} = assert_parse_ok(parse_css_literal(input, css_properties)); + const { parsed } = assert_parse_ok(parse_css_literal(input, css_properties)); assert.strictEqual(parsed.property, property); assert.strictEqual(parsed.value, value); }); @@ -688,7 +688,7 @@ describe('parse_css_literal - Unicode values', () => { describe('generate_css_literal_simple - Unicode', () => { test.each([ ['content:"→"', 'content: "→";', null], - ['before:content:"✓"', 'content: "✓";', '::before'], + ['before:content:"✓"', 'content: "✓";', '::before'] ] as const)('%s renders correctly', (class_name, expected_decl, expected_selector) => { const escaped = escape_css_selector(class_name); const result = interpret_css_literal(class_name, escaped, css_properties); @@ -705,27 +705,27 @@ describe('generate_css_literal_simple - Unicode', () => { describe('has_modifiers', () => { test('returns false for unmodified literal', () => { - const {parsed} = assert_parse_ok(parse_css_literal('display:flex', null)); + const { parsed } = assert_parse_ok(parse_css_literal('display:flex', null)); assert.isFalse(has_modifiers(parsed)); }); test('returns true for media modifier', () => { - const {parsed} = assert_parse_ok(parse_css_literal('md:display:flex', null)); + const { parsed } = assert_parse_ok(parse_css_literal('md:display:flex', null)); assert.isTrue(has_modifiers(parsed)); }); test('returns true for state modifier', () => { - const {parsed} = assert_parse_ok(parse_css_literal('hover:opacity:80%', null)); + const { parsed } = assert_parse_ok(parse_css_literal('hover:opacity:80%', null)); assert.isTrue(has_modifiers(parsed)); }); test('returns true for ancestor modifier', () => { - const {parsed} = assert_parse_ok(parse_css_literal('dark:color:white', null)); + const { parsed } = assert_parse_ok(parse_css_literal('dark:color:white', null)); assert.isTrue(has_modifiers(parsed)); }); test('returns true for pseudo-element modifier', () => { - const {parsed} = assert_parse_ok(parse_css_literal('before:content:""', null)); + const { parsed } = assert_parse_ok(parse_css_literal('before:content:""', null)); assert.isTrue(has_modifiers(parsed)); }); }); @@ -733,19 +733,21 @@ describe('has_modifiers', () => { describe('has_extracted_modifiers', () => { test('returns false for empty modifiers', () => { const segments = extract_segments('box'); - const {modifiers} = assert_mod_ok(extract_and_validate_modifiers(segments, 'box')); + const { modifiers } = assert_mod_ok(extract_and_validate_modifiers(segments, 'box')); assert.isFalse(has_extracted_modifiers(modifiers)); }); test('returns true for media modifier', () => { const segments = extract_segments('md:box'); - const {modifiers} = assert_mod_ok(extract_and_validate_modifiers(segments, 'md:box')); + const { modifiers } = assert_mod_ok(extract_and_validate_modifiers(segments, 'md:box')); assert.isTrue(has_extracted_modifiers(modifiers)); }); test('returns true for multiple state modifiers', () => { const segments = extract_segments('focus:hover:box'); - const {modifiers} = assert_mod_ok(extract_and_validate_modifiers(segments, 'focus:hover:box')); + const { modifiers } = assert_mod_ok( + extract_and_validate_modifiers(segments, 'focus:hover:box') + ); assert.isTrue(has_extracted_modifiers(modifiers)); assert.lengthOf(modifiers.states, 2); }); @@ -753,22 +755,22 @@ describe('has_extracted_modifiers', () => { describe('try_resolve_literal', () => { test('resolves unmodified literal', () => { - const {declaration} = assert_literal_ok( - try_resolve_literal('text-align:center', css_properties, 'test'), + const { declaration } = assert_literal_ok( + try_resolve_literal('text-align:center', css_properties, 'test') ); assert.strictEqual(declaration, 'text-align: center;'); }); test('resolves literal with ~ space encoding', () => { - const {declaration} = assert_literal_ok( - try_resolve_literal('margin:0~auto', css_properties, 'test'), + const { declaration } = assert_literal_ok( + try_resolve_literal('margin:0~auto', css_properties, 'test') ); assert.strictEqual(declaration, 'margin: 0 auto;'); }); test('resolves custom property', () => { - const {declaration} = assert_literal_ok( - try_resolve_literal('--my-color:blue', css_properties, 'test'), + const { declaration } = assert_literal_ok( + try_resolve_literal('--my-color:blue', css_properties, 'test') ); assert.strictEqual(declaration, '--my-color: blue;'); }); @@ -779,14 +781,14 @@ describe('try_resolve_literal', () => { test('returns error for modified literal', () => { const error = assert_literal_has_error( - try_resolve_literal('hover:opacity:80%', css_properties, 'test'), + try_resolve_literal('hover:opacity:80%', css_properties, 'test') ); assert.include(error.message, 'cannot be used in composes array'); }); test('returns error for invalid property with suggestion', () => { const error = assert_literal_has_error( - try_resolve_literal('disply:flex', css_properties, 'test'), + try_resolve_literal('disply:flex', css_properties, 'test') ); assert.include(error.message, 'Unknown CSS property'); assert.isNotNull(error.suggestion); @@ -797,7 +799,7 @@ describe('try_resolve_literal', () => { // hover:shadow_lg parses as property:value, fails property validation // The "modified class" detection happens earlier in resolve_composes const error = assert_literal_has_error( - try_resolve_literal('hover:shadow_lg', css_properties, 'card'), + try_resolve_literal('hover:shadow_lg', css_properties, 'card') ); assert.include(error.message, 'Unknown CSS property'); }); @@ -819,7 +821,7 @@ describe('extract_balanced_parens', () => { 'min-width(clamp(300px, 50%, 800px))', 'min-width', 'clamp(300px, 50%, 800px)', - 'clamp function', + 'clamp function' ], ['min-width(min(100vw, 1200px))', 'min-width', 'min(100vw, 1200px)', 'min function'], ['min-width(max(300px, 20%))', 'min-width', 'max(300px, 20%)', 'max function'], @@ -827,9 +829,9 @@ describe('extract_balanced_parens', () => { 'min-width(calc(min(100vw, 1200px) - 2rem))', 'min-width', 'calc(min(100vw, 1200px) - 2rem)', - 'nested functions', + 'nested functions' ], - ['foo(bar(baz(qux)))', 'foo', 'bar(baz(qux))', 'deeply nested'], + ['foo(bar(baz(qux)))', 'foo', 'bar(baz(qux))', 'deeply nested'] ] as const)('%s with prefix "%s" → "%s" (%s)', (input, prefix, expected, _desc) => { assert.strictEqual(extract_balanced_parens(input, prefix), expected); }); @@ -844,7 +846,7 @@ describe('extract_balanced_parens', () => { ['min-width(800px)trailing', 'min-width', 'trailing characters'], ['min-width(a)(b)', 'min-width', 'multiple paren groups'], ['', 'min-width', 'empty string'], - ['min-width', 'min-width', 'no parens at all'], + ['min-width', 'min-width', 'no parens at all'] ] as const)('%s with prefix "%s" → null (%s)', (input, prefix, _desc) => { assert.isNull(extract_balanced_parens(input, prefix)); }); @@ -866,7 +868,7 @@ describe('parse_arbitrary_breakpoint - edge cases', () => { ['max-width(vh)', 'max-width without number'], ['min-width(calc(100vw)', 'unbalanced calc'], ['min-width(800px)extra', 'trailing characters'], - ['min-width(var(--breakpoint))', 'var() not supported in media queries'], + ['min-width(var(--breakpoint))', 'var() not supported in media queries'] ] as const)('%s returns null for %s', (input, _desc) => { assert.isNull(parse_arbitrary_breakpoint(input)); }); @@ -880,7 +882,7 @@ describe('parse_arbitrary_breakpoint - edge cases', () => { ['min-width(100vw)', '@media (width >= 100vw)', 'viewport units'], ['min-width(50cqw)', '@media (width >= 50cqw)', 'container query units'], ['max-width(50em)', '@media (width < 50em)', 'max-width em units'], - ['max-width(600px)', '@media (width < 600px)', 'max-width pixels'], + ['max-width(600px)', '@media (width < 600px)', 'max-width pixels'] ] as const)('%s → %s (%s)', (input, expected, _desc) => { assert.strictEqual(parse_arbitrary_breakpoint(input), expected); }); @@ -890,13 +892,13 @@ describe('parse_arbitrary_breakpoint - edge cases', () => { [ 'min-width(calc(100vw - 200px))', '@media (width >= calc(100vw - 200px))', - 'calc with subtraction', + 'calc with subtraction' ], ['min-width(calc(50% + 2rem))', '@media (width >= calc(50% + 2rem))', 'calc with addition'], [ 'min-width(clamp(300px, 50vw, 800px))', '@media (width >= clamp(300px, 50vw, 800px))', - 'clamp function', + 'clamp function' ], ['min-width(min(100vw, 1200px))', '@media (width >= min(100vw, 1200px))', 'min function'], ['min-width(max(300px, 20vw))', '@media (width >= max(300px, 20vw))', 'max function'], @@ -904,13 +906,13 @@ describe('parse_arbitrary_breakpoint - edge cases', () => { [ 'min-width(calc(min(100vw, 1200px) - 2rem))', '@media (width >= calc(min(100vw, 1200px) - 2rem))', - 'nested functions', + 'nested functions' ], [ 'min-width(env(safe-area-inset-left))', '@media (width >= env(safe-area-inset-left))', - 'env function', - ], + 'env function' + ] ] as const)('%s → %s (%s)', (input, expected, _desc) => { assert.strictEqual(parse_arbitrary_breakpoint(input), expected); }); @@ -925,7 +927,7 @@ describe('parse_parameterized_state - edge cases', () => { ['nth-of-type(odd)', ':nth-of-type(odd)', 'odd keyword'], ['nth-last-of-type(3n+1)', ':nth-last-of-type(3n+1)', 'nth-last-of-type with formula'], ['nth-child(3n-1)', ':nth-child(3n-1)', 'negative offset'], - ['nth-child(5)', ':nth-child(5)', 'simple number'], + ['nth-child(5)', ':nth-child(5)', 'simple number'] ] as const)('%s → css: %s (%s)', (input, expected_css, _desc) => { const result = parse_parameterized_state(input); assert.isNotNull(result); @@ -937,7 +939,7 @@ describe('parse_parameterized_state - edge cases', () => { ['nth-child()', 'empty formula'], ['nth-child', 'missing parens'], ['child(2n)', 'missing nth- prefix'], - ['nth(2n)', 'incomplete pattern'], + ['nth(2n)', 'incomplete pattern'] ] as const)('%s returns null for %s', (input, _desc) => { assert.isNull(parse_parameterized_state(input)); }); diff --git a/src/test/css_plugin_options.test.ts b/src/test/css_plugin_options.test.ts index e3ab3f542..be659656d 100644 --- a/src/test/css_plugin_options.test.ts +++ b/src/test/css_plugin_options.test.ts @@ -7,7 +7,7 @@ * @module */ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; import type { CssGeneratorBaseOptions, @@ -15,10 +15,10 @@ import type { CssClassOptions, CssOutputOptions, CssDiagnosticsOptions, - CssCacheOptions, + CssCacheOptions } from '$lib/css_plugin_options.ts'; -import type {GenFuzCssOptions} from '$lib/gen_fuz_css.ts'; -import type {VitePluginFuzCssOptions} from '$lib/vite_plugin_fuz_css.ts'; +import type { GenFuzCssOptions } from '$lib/gen_fuz_css.ts'; +import type { VitePluginFuzCssOptions } from '$lib/vite_plugin_fuz_css.ts'; describe('shared options interface', () => { test('CssGeneratorBaseOptions includes all expected interfaces', () => { @@ -44,7 +44,7 @@ describe('shared options interface', () => { on_error: 'log', on_warning: 'ignore', // CssCacheOptions - cache_dir: '.cache', + cache_dir: '.cache' }; assert.isDefined(base_options); @@ -62,7 +62,7 @@ describe('shared options interface', () => { include_stats: true, project_root: '/path/to/project', concurrency: 4, - cache_io_concurrency: 100, + cache_io_concurrency: 100 }; assert.isDefined(gro_options); @@ -78,7 +78,7 @@ describe('shared options interface', () => { base_css: undefined, variables: undefined, on_error: 'throw', - additional_elements: ['custom-element'], + additional_elements: ['custom-element'] }; assert.isDefined(vite_options); @@ -88,25 +88,25 @@ describe('shared options interface', () => { test('options interfaces are assignable', () => { // Verify sub-interfaces can be used independently const extraction: CssExtractionOptions = { - filter_file: (id) => id.endsWith('.svelte'), + filter_file: (id) => id.endsWith('.svelte') }; const class_opts: CssClassOptions = { - include_default_classes: true, + include_default_classes: true }; const output: CssOutputOptions = { base_css: null, // disabled - variables: null, // disabled + variables: null // disabled }; const diagnostics: CssDiagnosticsOptions = { on_error: 'throw', - on_warning: 'log', + on_warning: 'log' }; const cache: CssCacheOptions = { - cache_dir: '.custom-cache', + cache_dir: '.custom-cache' }; assert.isDefined(extraction); @@ -123,13 +123,13 @@ describe('shared options interface', () => { // Test null (disabled) const disabled_opts: CssOutputOptions = { - base_css: null, + base_css: null }; assert.isNull(disabled_opts.base_css); // Test string (custom CSS) const custom_opts: CssOutputOptions = { - base_css: 'button { color: red; }', + base_css: 'button { color: red; }' }; assert.strictEqual(typeof custom_opts.base_css, 'string'); }); @@ -141,19 +141,19 @@ describe('shared options interface', () => { // Test null (disabled) const disabled_opts: CssOutputOptions = { - variables: null, + variables: null }; assert.isNull(disabled_opts.variables); // Test array (custom variables) const custom_opts: CssOutputOptions = { - variables: [{name: 'my_var', light: 'blue', dark: 'lightblue'}], + variables: [{ name: 'my_var', light: 'blue', dark: 'lightblue' }] }; assert.isTrue(Array.isArray(custom_opts.variables)); // Test callback (modify defaults) const callback_opts: CssOutputOptions = { - variables: (defaults) => defaults.filter((v) => v.name.startsWith('color_')), + variables: (defaults) => defaults.filter((v) => v.name.startsWith('color_')) }; assert.strictEqual(typeof callback_opts.variables, 'function'); }); diff --git a/src/test/css_ruleset_parser.generation.test.ts b/src/test/css_ruleset_parser.generation.test.ts index 4a8edb028..32a83e650 100644 --- a/src/test/css_ruleset_parser.generation.test.ts +++ b/src/test/css_ruleset_parser.generation.test.ts @@ -1,7 +1,7 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {generate_modified_ruleset} from '$lib/css_ruleset_parser.ts'; -import {assert_css_contains, assert_css_not_contains} from './test_helpers.ts'; +import { generate_modified_ruleset } from '$lib/css_ruleset_parser.ts'; +import { assert_css_contains, assert_css_not_contains } from './test_helpers.ts'; /** * Tests for generate_modified_ruleset which creates CSS with modified selectors @@ -16,7 +16,7 @@ describe('generate_modified_ruleset', () => { ':hover', '', null, - null, + null ); assert_css_contains(result.css, '.hover\\:chip:hover', 'font-weight: 500;'); @@ -30,7 +30,7 @@ describe('generate_modified_ruleset', () => { '', '', '@media (width >= 48rem)', - null, + null ); assert_css_contains(result.css, '@media (width >= 48rem) {', '.md\\:chip', '}'); @@ -44,7 +44,7 @@ describe('generate_modified_ruleset', () => { '', '', null, - ':root.dark', + ':root.dark' ); assert_css_contains(result.css, ':root.dark {', '.dark\\:chip'); @@ -58,14 +58,14 @@ describe('generate_modified_ruleset', () => { '', '', '@media (width >= 48rem)', - ':root.dark', + ':root.dark' ); assert_css_contains( result.css, '@media (width >= 48rem) {', ':root.dark {', - '.md\\:dark\\:chip', + '.md\\:dark\\:chip' ); }); @@ -81,14 +81,14 @@ describe('generate_modified_ruleset', () => { '', '', '@media (width >= 48rem)', - null, + null ); assert_css_contains( result.css, '.md\\:selectable', '.md\\:selectable:hover', - '@media (width >= 48rem) {', + '@media (width >= 48rem) {' ); }); @@ -105,14 +105,14 @@ describe('generate_modified_ruleset', () => { ':focus', '', null, - null, + null ); assert_css_contains( result.css, '.focus\\:selectable:focus', '.focus\\:selectable:hover:focus', - '.focus\\:selectable.selected:focus', + '.focus\\:selectable.selected:focus' ); }); @@ -129,14 +129,14 @@ describe('generate_modified_ruleset', () => { ':hover', '', null, - null, + null ); assert_css_contains( result.css, '.hover\\:menuitem:hover', '.hover\\:menuitem:hover .content', - '.hover\\:menuitem:hover .icon', + '.hover\\:menuitem:hover .icon' ); }); @@ -152,7 +152,7 @@ describe('generate_modified_ruleset', () => { ':hover', '', null, - null, + null ); assert_css_contains(result.css, '.hover\\:chevron:hover', '.hover\\:chevron:hover::before'); @@ -170,7 +170,7 @@ describe('generate_modified_ruleset', () => { ':hover', '', null, - null, + null ); assert_css_contains(result.css, '.hover\\:chip:hover', 'a.hover\\:chip:hover'); @@ -189,7 +189,7 @@ describe('generate_modified_ruleset', () => { '', '::before', null, - null, + null ); assert_css_not_contains(result.css, ':before::before'); @@ -207,7 +207,7 @@ describe('generate_modified_ruleset', () => { '', '::after', null, - null, + null ); assert_css_not_contains(result.css, ':after::after'); @@ -223,7 +223,7 @@ describe('generate_modified_ruleset', () => { '', '::first-letter', null, - null, + null ); assert_css_not_contains(result.css, ':first-letter::first-letter'); @@ -244,14 +244,14 @@ describe('generate_modified_ruleset', () => { '', '::before', null, - null, + null ); assert_css_contains( result.css, '.before\\:chevron::before', 'position: relative', - 'border: 4px solid', + 'border: 4px solid' ); assert_css_not_contains(result.css, '::before::before'); assert.isNotNull(result.skipped_modifiers); @@ -271,7 +271,7 @@ describe('generate_modified_ruleset', () => { '', '::before', null, - null, + null ); assert_css_contains( @@ -279,7 +279,7 @@ describe('generate_modified_ruleset', () => { '.before\\:icon::before', 'display: inline', '.before\\:icon::after', - "content: '→'", + "content: '→'" ); assert_css_not_contains(result.css, '::after::before', '::before::after'); assert.isNotNull(result.skipped_modifiers); @@ -298,7 +298,7 @@ describe('generate_modified_ruleset', () => { ':hover', '', null, - null, + null ); assert_css_contains( @@ -306,7 +306,7 @@ describe('generate_modified_ruleset', () => { '.hover\\:chevron:hover', '.hover\\:chevron:hover::before', 'position: relative', - "content: ''", + "content: ''" ); assert.isNull(result.skipped_modifiers); }); @@ -321,7 +321,7 @@ describe('generate_modified_ruleset', () => { ':hover', '', null, - null, + null ); assert_css_contains(result.css, '.hover\\:plain:hover', '.hover\\:plain:active:hover'); @@ -339,7 +339,7 @@ describe('generate_modified_ruleset', () => { ':hover:focus', '', null, - null, + null ); assert_css_contains(result.css, '.hover\\:focus\\:selectable:hover:focus'); @@ -357,14 +357,14 @@ describe('generate_modified_ruleset', () => { ':hover:focus', '', null, - null, + null ); assert_css_contains( result.css, '.hover\\:focus\\:btn:hover:focus', '.hover\\:focus\\:btn:focus:hover', - '.hover\\:focus\\:btn:active:hover:focus', + '.hover\\:focus\\:btn:active:hover:focus' ); assert.isNotNull(result.skipped_modifiers); assert.strictEqual(result.skipped_modifiers.length, 2); @@ -380,7 +380,7 @@ describe('generate_modified_ruleset', () => { '', '', null, - null, + null ); assert_css_contains(result.css, '.md\\:box'); @@ -395,7 +395,7 @@ describe('generate_modified_ruleset', () => { ':focus-visible', '', null, - null, + null ); assert_css_contains(result.css, '.focus-visible\\:box:focus-visible'); @@ -409,7 +409,7 @@ describe('generate_modified_ruleset', () => { ':hover:focus:active', '', null, - null, + null ); assert_css_contains(result.css, '.hover\\:focus\\:active\\:box:hover:focus:active'); @@ -430,7 +430,7 @@ describe('generate_modified_ruleset', () => { ':hover', '', null, - null, + null ); assert.strictEqual(result.css, ''); diff --git a/src/test/css_ruleset_parser.modifiers.test.ts b/src/test/css_ruleset_parser.modifiers.test.ts index 649e8e44f..5102b0d1e 100644 --- a/src/test/css_ruleset_parser.modifiers.test.ts +++ b/src/test/css_ruleset_parser.modifiers.test.ts @@ -1,6 +1,6 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {modify_single_selector, modify_selector_group} from '$lib/css_ruleset_parser.ts'; +import { modify_single_selector, modify_selector_group } from '$lib/css_ruleset_parser.ts'; /** * Tests for selector modification: modify_single_selector and modify_selector_group. @@ -16,7 +16,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:menuitem:hover', - 'simple selector', + 'simple selector' ], [ '.menuitem .content', @@ -25,7 +25,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:menuitem:hover .content', - 'descendant', + 'descendant' ], [ '.menuitem.selected', @@ -34,7 +34,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:menuitem.selected:hover', - 'compound', + 'compound' ], [ '.selectable:active', @@ -43,7 +43,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:selectable:active:hover', - 'existing pseudo-class', + 'existing pseudo-class' ], [ '.chevron::before', @@ -52,7 +52,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:chevron:hover::before', - 'before pseudo-element', + 'before pseudo-element' ], // Pseudo-element only @@ -69,7 +69,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:parent:hover > .child', - 'child combinator', + 'child combinator' ], [ '.foo + .bar', @@ -78,7 +78,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:foo:hover + .bar', - 'adjacent sibling', + 'adjacent sibling' ], [ '.foo ~ .bar', @@ -87,7 +87,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:foo:hover ~ .bar', - 'general sibling', + 'general sibling' ], // No match cases @@ -103,7 +103,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:foo:not(:is(.a, .b)):hover', - 'nested functional', + 'nested functional' ], [ '.foo:where(.a, .b)', @@ -112,7 +112,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:foo:where(.a, .b):hover', - ':where()', + ':where()' ], [ '.parent:has(.child)', @@ -121,7 +121,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:parent:has(.child):hover', - ':has()', + ':has()' ], // With ID selectors @@ -133,7 +133,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:foo#bar:focus:hover', - 'ID + pseudo', + 'ID + pseudo' ], // Attribute selectors @@ -144,7 +144,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:btn[disabled]:hover', - 'attribute', + 'attribute' ], // Class in descendant @@ -155,7 +155,7 @@ describe('modify_single_selector', () => { ':hover', '', '.container .hover\\:box:hover', - 'class in descendant', + 'class in descendant' ], // Both state and pseudo-element @@ -166,7 +166,7 @@ describe('modify_single_selector', () => { ':hover', '::before', '.hover\\:before\\:box:hover::before', - 'state + pseudo', + 'state + pseudo' ], // Empty state/pseudo (just rename) @@ -181,7 +181,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:palette_a_50:hover', - 'underscores and numbers', + 'underscores and numbers' ], // Multiple occurrences - only first instance is modified @@ -193,7 +193,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:foo:hover .content .foo', - 'class twice - descendant', + 'class twice - descendant' ], [ '.foo.bar.foo', @@ -202,7 +202,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:foo.bar.foo:hover', - 'class twice - compound', + 'class twice - compound' ], // ::part() and ::slotted() pseudo-elements @@ -213,7 +213,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:component:hover::part(button)', - '::part()', + '::part()' ], [ '.host::slotted(.item)', @@ -222,7 +222,7 @@ describe('modify_single_selector', () => { ':focus', '', '.focus\\:host:focus::slotted(.item)', - '::slotted()', + '::slotted()' ], // CSS2 single-colon pseudo-elements @@ -233,7 +233,7 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:foo:hover:before', - 'CSS2 :before', + 'CSS2 :before' ], ['.foo:after', 'foo', 'hover\\:foo', ':hover', '', '.hover\\:foo:hover:after', 'CSS2 :after'], [ @@ -243,8 +243,8 @@ describe('modify_single_selector', () => { ':hover', '', '.hover\\:foo:hover:first-letter', - 'CSS2 :first-letter', - ], + 'CSS2 :first-letter' + ] ]; test.each(cases)( @@ -252,9 +252,9 @@ describe('modify_single_selector', () => { (selector, className, newClassName, state, pseudo, expected, _desc) => { assert.strictEqual( modify_single_selector(selector, className, newClassName, state, pseudo), - expected, + expected ); - }, + } ); }); @@ -265,12 +265,12 @@ describe('modify_selector_group', () => { 'selectable', 'hover\\:selectable', [':hover'], - '', + '' ); assert.strictEqual( result.selector, - '.hover\\:selectable.selected:hover,\n.hover\\:selectable:active:hover', + '.hover\\:selectable.selected:hover,\n.hover\\:selectable:active:hover' ); assert.isNull(result.skipped_modifiers); }); @@ -281,12 +281,12 @@ describe('modify_selector_group', () => { 'plain', 'focus\\:plain', [':focus'], - '', + '' ); assert.strictEqual( result.selector, - '.focus\\:plain:not(:hover):focus,\n.focus\\:plain:active:focus', + '.focus\\:plain:not(:hover):focus,\n.focus\\:plain:active:focus' ); assert.isNull(result.skipped_modifiers); }); @@ -298,7 +298,7 @@ describe('modify_selector_group', () => { 'plain', 'hover\\:plain', [':hover'], - '', + '' ); assert.strictEqual(result.selector, '.hover\\:plain:hover,\n.hover\\:plain:active:hover'); @@ -316,7 +316,7 @@ describe('modify_selector_group', () => { 'btn', 'focus\\:btn', [':focus'], - '', + '' ); assert.strictEqual(result.selector, '.focus\\:btn:focus-within:focus'); assert.isNull(result.skipped_modifiers); // No conflict - different states! @@ -328,7 +328,7 @@ describe('modify_selector_group', () => { 'btn', 'focus\\:btn', [':focus'], - '', + '' ); assert.strictEqual(result.selector, '.focus\\:btn:focus-visible:focus'); assert.isNull(result.skipped_modifiers); @@ -340,7 +340,7 @@ describe('modify_selector_group', () => { 'btn', 'hover\\:btn', [':hover'], - '', + '' ); assert.strictEqual(result.selector, '.hover\\:btn[data-hover="true"]:hover'); assert.isNull(result.skipped_modifiers); @@ -359,7 +359,7 @@ describe('modify_selector_group', () => { 'btn', 'focus\\:btn', [':focus'], - '', + '' ); assert.isNotNull(result.skipped_modifiers); assert.strictEqual(result.skipped_modifiers[0]!.conflicting_modifier, ':focus'); @@ -371,7 +371,7 @@ describe('modify_selector_group', () => { 'btn', 'active\\:btn', [':active'], - '', + '' ); assert.isNotNull(result.skipped_modifiers); assert.strictEqual(result.skipped_modifiers[0]!.conflicting_modifier, ':active'); @@ -383,7 +383,7 @@ describe('modify_selector_group', () => { 'btn', 'hover\\:btn', [':hover'], - '', + '' ); assert.isNotNull(result.skipped_modifiers); assert.strictEqual(result.skipped_modifiers[0]!.conflicting_modifier, ':hover'); @@ -395,7 +395,7 @@ describe('modify_selector_group', () => { 'btn', 'focus\\:btn', [':focus'], - '', + '' ); assert.isNotNull(result.skipped_modifiers); assert.strictEqual(result.skipped_modifiers[0]!.conflicting_modifier, ':focus'); @@ -407,7 +407,7 @@ describe('modify_selector_group', () => { 'selectable', 'hover\\:focus\\:selectable', [':hover', ':focus'], - '', + '' ); assert.strictEqual(result.selector, '.hover\\:focus\\:selectable:hover:focus'); @@ -422,7 +422,7 @@ describe('modify_selector_group', () => { 'chevron', 'before\\:chevron', [], - '::before', + '::before' ); assert.strictEqual(result.selector, '.before\\:chevron::before,\n.before\\:chevron::before'); diff --git a/src/test/css_ruleset_parser.parse.test.ts b/src/test/css_ruleset_parser.parse.test.ts index 6eb301db3..eda80c823 100644 --- a/src/test/css_ruleset_parser.parse.test.ts +++ b/src/test/css_ruleset_parser.parse.test.ts @@ -1,10 +1,10 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; import { parse_ruleset, is_single_selector_ruleset, ruleset_contains_class, - extract_css_comment, + extract_css_comment } from '$lib/css_ruleset_parser.ts'; /** diff --git a/src/test/css_ruleset_parser.selectors.test.ts b/src/test/css_ruleset_parser.selectors.test.ts index 69641d961..c300aa848 100644 --- a/src/test/css_ruleset_parser.selectors.test.ts +++ b/src/test/css_ruleset_parser.selectors.test.ts @@ -1,6 +1,6 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {split_selector_list, find_compound_end} from '$lib/css_ruleset_parser.ts'; +import { split_selector_list, find_compound_end } from '$lib/css_ruleset_parser.ts'; /** * Tests for selector parsing utilities: split_selector_list and find_compound_end. @@ -23,7 +23,7 @@ describe('split_selector_list', () => { [".foo[data-x='a,b'], .bar", [".foo[data-x='a,b']", '.bar']], ['.foo[data-x=value], .bar', ['.foo[data-x=value]', '.bar']], ['.foo[data-x="a\\"b,c"], .bar', ['.foo[data-x="a\\"b,c"]', '.bar']], - ['.foo[data-x="a"]:not(.b), .bar', ['.foo[data-x="a"]:not(.b)', '.bar']], + ['.foo[data-x="a"]:not(.b), .bar', ['.foo[data-x="a"]:not(.b)', '.bar']] ]; test.each(cases)('split_selector_list("%s") → %j', (input, expected) => { @@ -61,7 +61,7 @@ describe('find_compound_end', () => { ['', 0, 1, 'empty selector'], ['.btn:hover:focus', 0, 16, 'multiple pseudo-classes'], ['.chevron:before', 0, 8, 'CSS2 single-colon pseudo-element'], - ['.foo*', 0, 4, 'universal selector after class'], + ['.foo*', 0, 4, 'universal selector after class'] ]; test.each(cases)('find_compound_end("%s", %d) → %d (%s)', (selector, startPos, expected) => { diff --git a/src/test/css_variable_utils.test.ts b/src/test/css_variable_utils.test.ts index 1e07d4af8..a18de452f 100644 --- a/src/test/css_variable_utils.test.ts +++ b/src/test/css_variable_utils.test.ts @@ -1,6 +1,6 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {extract_css_variables, has_css_variables} from '$lib/css_variable_utils.ts'; +import { extract_css_variables, has_css_variables } from '$lib/css_variable_utils.ts'; describe('extract_css_variables', () => { test('returns empty set for empty string', () => { @@ -34,7 +34,7 @@ describe('extract_css_variables', () => { test('handles variable names with hyphens and numbers', () => { const result = extract_css_variables( - 'font-size: var(--font-size-md); margin: var(--spacing_2xl);', + 'font-size: var(--font-size-md); margin: var(--spacing_2xl);' ); assert.deepEqual(result, new Set(['font-size-md', 'spacing_2xl'])); }); @@ -72,7 +72,7 @@ describe('extract_css_variables', () => { test('handles complex nested fallbacks', () => { const result = extract_css_variables( - 'color: var(--primary, var(--secondary, var(--tertiary)));', + 'color: var(--primary, var(--secondary, var(--tertiary)));' ); assert.deepEqual(result, new Set(['primary', 'secondary', 'tertiary'])); }); @@ -113,7 +113,7 @@ describe('extract_css_variables', () => { assert.deepEqual(extract_css_variables('color: var( --spaced );'), new Set(['spaced'])); assert.deepEqual( extract_css_variables('color: var( --double_space );'), - new Set(['double_space']), + new Set(['double_space']) ); assert.deepEqual(extract_css_variables('color: var(\t--tabbed );'), new Set(['tabbed'])); assert.deepEqual(extract_css_variables('color: var(\n--newline );'), new Set(['newline'])); diff --git a/src/test/diagnostics.test.ts b/src/test/diagnostics.test.ts index 04e39ae82..1634f8300 100644 --- a/src/test/diagnostics.test.ts +++ b/src/test/diagnostics.test.ts @@ -1,10 +1,10 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; import { format_diagnostic, CssGenerationError, type ExtractionDiagnostic, - type GenerationDiagnostic, + type GenerationDiagnostic } from '$lib/diagnostics.ts'; describe('format_diagnostic', () => { @@ -14,7 +14,7 @@ describe('format_diagnostic', () => { level: 'warning', message: 'test warning message', suggestion: null, - location: {file: 'src/lib/Button.svelte', line: 10, column: 5}, + location: { file: 'src/lib/Button.svelte', line: 10, column: 5 } }; const result = format_diagnostic(diagnostic); @@ -27,7 +27,7 @@ describe('format_diagnostic', () => { level: 'error', message: 'deprecated syntax', suggestion: 'use the new syntax instead', - location: {file: 'app.ts', line: 1, column: 1}, + location: { file: 'app.ts', line: 1, column: 1 } }; const result = format_diagnostic(diagnostic); @@ -41,7 +41,7 @@ describe('format_diagnostic', () => { message: 'unknown CSS property', suggestion: null, identifier: 'invalid:value', - locations: [{file: 'src/App.svelte', line: 5, column: 12}], + locations: [{ file: 'src/App.svelte', line: 5, column: 12 }] }; const result = format_diagnostic(diagnostic); @@ -55,7 +55,7 @@ describe('format_diagnostic', () => { message: 'invalid class', suggestion: null, identifier: 'bad_class', - locations: null, + locations: null }; const result = format_diagnostic(diagnostic); @@ -69,13 +69,13 @@ describe('format_diagnostic', () => { message: 'unknown property', suggestion: 'did you mean "color"?', identifier: 'colour:red', - locations: [{file: 'test.ts', line: 1, column: 1}], + locations: [{ file: 'test.ts', line: 1, column: 1 }] }; const result = format_diagnostic(diagnostic); assert.strictEqual( result, - ` - test.ts:1:1: colour:red: unknown property (did you mean "color"?)`, + ` - test.ts:1:1: colour:red: unknown property (did you mean "color"?)` ); }); @@ -86,7 +86,7 @@ describe('format_diagnostic', () => { message: 'test error', suggestion: null, identifier: 'test_class', - locations: [], + locations: [] }; const result = format_diagnostic(diagnostic); @@ -103,8 +103,8 @@ describe('CssGenerationError', () => { message: 'unknown property', suggestion: null, identifier: 'bad:class', - locations: [{file: 'app.ts', line: 1, column: 1}], - }, + locations: [{ file: 'app.ts', line: 1, column: 1 }] + } ]; const error = new CssGenerationError(diagnostics); @@ -123,7 +123,7 @@ describe('CssGenerationError', () => { message: 'error one', suggestion: null, identifier: 'class_a', - locations: null, + locations: null }, { phase: 'generation', @@ -131,8 +131,8 @@ describe('CssGenerationError', () => { message: 'error two', suggestion: null, identifier: 'class_b', - locations: null, - }, + locations: null + } ]; const error = new CssGenerationError(diagnostics); @@ -150,7 +150,7 @@ describe('CssGenerationError', () => { message: 'this is an error', suggestion: null, identifier: 'error_class', - locations: null, + locations: null }, { phase: 'generation', @@ -158,8 +158,8 @@ describe('CssGenerationError', () => { message: 'this is a warning', suggestion: null, identifier: 'warning_class', - locations: null, - }, + locations: null + } ]; const error = new CssGenerationError(diagnostics); @@ -178,7 +178,7 @@ describe('CssGenerationError', () => { level: 'error' as const, message: 'extraction error', suggestion: null, - location: {file: 'test.ts', line: 1, column: 1}, + location: { file: 'test.ts', line: 1, column: 1 } }, { phase: 'generation' as const, @@ -186,8 +186,8 @@ describe('CssGenerationError', () => { message: 'generation error', suggestion: null, identifier: 'gen_class', - locations: null, - }, + locations: null + } ]; const error = new CssGenerationError(diagnostics); @@ -211,8 +211,8 @@ describe('CssGenerationError', () => { message: 'just a warning', suggestion: null, identifier: 'warn_class', - locations: null, - }, + locations: null + } ]; const error = new CssGenerationError(diagnostics); diff --git a/src/test/escape_css_selector.test.ts b/src/test/escape_css_selector.test.ts index 3c4ed899e..de3cea4ab 100644 --- a/src/test/escape_css_selector.test.ts +++ b/src/test/escape_css_selector.test.ts @@ -1,6 +1,6 @@ -import {test, describe, assert} from 'vitest'; +import { test, describe, assert } from 'vitest'; -import {escape_css_selector} from '$lib/css_class_generation.ts'; +import { escape_css_selector } from '$lib/css_class_generation.ts'; /** * Tests for CSS selector escaping. @@ -75,7 +75,7 @@ describe('escape_css_selector', () => { ['a{b', 'a\\{b'], ['a|b', 'a\\|b'], ['a}b', 'a\\}b'], - ['a~b', 'a\\~b'], + ['a~b', 'a\\~b'] ]; test.each(escape_cases)('escapes "%s" to "%s"', (input, expected) => { @@ -101,7 +101,7 @@ describe('escape_css_selector - backslash edge cases', () => { ['end\\', 'end\\\\'], // Backslash with various special characters - ['path\\to\\file.css', 'path\\\\to\\\\file\\.css'], + ['path\\to\\file.css', 'path\\\\to\\\\file\\.css'] ]; test.each(backslash_cases)('escapes "%s" to "%s"', (input, expected) => { diff --git a/src/test/explicit_classes.test.ts b/src/test/explicit_classes.test.ts index 3af04af16..8d0776f9b 100644 --- a/src/test/explicit_classes.test.ts +++ b/src/test/explicit_classes.test.ts @@ -1,8 +1,8 @@ -import {test, describe, assert} from 'vitest'; +import { test, describe, assert } from 'vitest'; -import {generate_classes_css} from '$lib/css_class_generation.ts'; -import {css_class_composites} from '$lib/css_class_composites.ts'; -import {assert_css_contains, loc} from './test_helpers.ts'; +import { generate_classes_css } from '$lib/css_class_generation.ts'; +import { css_class_composites } from '$lib/css_class_composites.ts'; +import { assert_css_contains, loc } from './test_helpers.ts'; /** * Tests for explicit_classes handling in generate_classes_css. @@ -18,7 +18,7 @@ describe('explicit_classes diagnostics', () => { class_definitions: {}, interpreters: [], css_properties: null, - explicit_classes: new Set(['unknown_class']), + explicit_classes: new Set(['unknown_class']) }); assert.strictEqual(result.css, ''); @@ -33,7 +33,7 @@ describe('explicit_classes diagnostics', () => { class_names: ['unknown_class'], class_definitions: {}, interpreters: [], - css_properties: null, + css_properties: null // Not in explicit_classes }); @@ -47,7 +47,7 @@ describe('explicit_classes diagnostics', () => { class_definitions: css_class_composites, interpreters: [], css_properties: null, - explicit_classes: new Set(['box']), + explicit_classes: new Set(['box']) }); assert_css_contains(result.css, '.box'); @@ -66,14 +66,14 @@ describe('explicit_classes diagnostics', () => { level: 'error', identifier: 'invalid-property:value', message: 'Unknown CSS property "invalid-property"', - suggestion: null, + suggestion: null }); return null; - }, - }, + } + } ], css_properties: null, - explicit_classes: new Set(['invalid-property:value']), + explicit_classes: new Set(['invalid-property:value']) }); assert.lengthOf(result.diagnostics, 1); @@ -93,13 +93,13 @@ describe('explicit_classes diagnostics', () => { level: 'error', identifier: 'invalid-property:value', message: 'Unknown CSS property "invalid-property"', - suggestion: null, + suggestion: null }); return null; - }, - }, + } + } ], - css_properties: null, + css_properties: null // Not in explicit_classes - class was implicitly extracted from code }); @@ -123,13 +123,13 @@ describe('explicit_classes diagnostics', () => { level: 'error', identifier: 'some-class:value', message: 'Circular reference detected', - suggestion: null, + suggestion: null }); return null; - }, - }, + } + } ], - css_properties: null, + css_properties: null // Not explicit }); @@ -147,7 +147,7 @@ describe('explicit_classes diagnostics', () => { interpreters: [], css_properties: null, explicit_classes: new Set(['unknown_class']), - class_locations: new Map([['unknown_class', [source_loc]]]), + class_locations: new Map([['unknown_class', [source_loc]]]) }); assert.lengthOf(result.diagnostics, 1); diff --git a/src/test/file_filter.test.ts b/src/test/file_filter.test.ts index 4d15a5ae7..225745866 100644 --- a/src/test/file_filter.test.ts +++ b/src/test/file_filter.test.ts @@ -1,6 +1,6 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {filter_file_default} from '$lib/file_filter.ts'; +import { filter_file_default } from '$lib/file_filter.ts'; describe('filter_file_default', () => { describe('includes valid extensions', () => { diff --git a/src/test/fixtures/css_classes_fixture.gen.json.ts b/src/test/fixtures/css_classes_fixture.gen.json.ts index 1a2fa6eaa..5fb8e6296 100644 --- a/src/test/fixtures/css_classes_fixture.gen.json.ts +++ b/src/test/fixtures/css_classes_fixture.gen.json.ts @@ -1,10 +1,10 @@ -import type {Gen} from '@fuzdev/gro'; +import type { Gen } from '@fuzdev/gro'; -import {css_class_definitions} from '$lib/css_class_definitions.ts'; +import { css_class_definitions } from '$lib/css_class_definitions.ts'; export const gen: Gen = { dependencies: 'all', generate: () => { return JSON.stringify(css_class_definitions); - }, + } }; diff --git a/src/test/fixtures/mock_deps.ts b/src/test/fixtures/mock_deps.ts index 6df49a997..f576921bb 100644 --- a/src/test/fixtures/mock_deps.ts +++ b/src/test/fixtures/mock_deps.ts @@ -6,7 +6,7 @@ * @module */ -import type {CacheDeps} from '$lib/deps.ts'; +import type { CacheDeps } from '$lib/deps.ts'; /** * In-memory filesystem state for mock deps. @@ -20,7 +20,7 @@ export interface MockFsState { * Creates a new empty mock filesystem state. */ export const create_mock_fs_state = (): MockFsState => ({ - files: new Map(), + files: new Map() }); /** @@ -29,22 +29,22 @@ export const create_mock_fs_state = (): MockFsState => ({ * @param state - The mock filesystem state to use */ export const create_mock_cache_deps = (state: MockFsState): CacheDeps => ({ - read_text: async ({path}) => { + read_text: async ({ path }) => { const value = state.files.get(path); - if (value === undefined) return {ok: false, kind: 'not_found', message: `not found: ${path}`}; - return {ok: true, value}; + if (value === undefined) return { ok: false, kind: 'not_found', message: `not found: ${path}` }; + return { ok: true, value }; }, - write_text_atomic: async ({path, content}) => { + write_text_atomic: async ({ path, content }) => { state.files.set(path, content); - return {ok: true}; + return { ok: true }; }, - unlink: async ({path}) => { + unlink: async ({ path }) => { if (!state.files.has(path)) { - return {ok: false, kind: 'not_found', message: `not found: ${path}`}; + return { ok: false, kind: 'not_found', message: `not found: ${path}` }; } state.files.delete(path); - return {ok: true}; - }, + return { ok: true }; + } }); diff --git a/src/test/fuz_comments.test.ts b/src/test/fuz_comments.test.ts index f027e1ce8..e56e64cfc 100644 --- a/src/test/fuz_comments.test.ts +++ b/src/test/fuz_comments.test.ts @@ -5,9 +5,9 @@ * similar to how @fuz-classes works for dynamic class names. */ -import {test, describe, beforeAll, assert} from 'vitest'; +import { test, describe, beforeAll, assert } from 'vitest'; -import {extract_from_svelte, extract_from_ts} from '$lib/css_class_extractor.ts'; +import { extract_from_svelte, extract_from_ts } from '$lib/css_class_extractor.ts'; import { assert_elements, assert_explicit_elements, @@ -16,7 +16,7 @@ import { assert_no_explicit_variables, assert_diagnostic, svelte_script, - create_jsx_extractor, + create_jsx_extractor } from './css_class_extractor_test_helpers.ts'; // @@ -39,7 +39,7 @@ describe('@fuz-elements comment extraction', () => { svelte_script(` // @fuz-elements dialog article const el = document.createElement('dialog'); -`), +`) ); assert_explicit_elements(result, ['dialog', 'article']); }); @@ -49,7 +49,7 @@ const el = document.createElement('dialog'); svelte_script(` /* @fuz-elements nav header footer */ const x = 1; -`), +`) ); assert_explicit_elements(result, ['nav', 'header', 'footer']); }); @@ -95,7 +95,7 @@ describe('@fuz-elements in TypeScript', () => { // @fuz-elements dialog details const el = document.createElement('dialog'); `, - 'test.ts', + 'test.ts' ); assert_explicit_elements(result, ['dialog', 'details']); }); @@ -106,7 +106,7 @@ const el = document.createElement('dialog'); /* @fuz-elements nav footer */ export const x = 1; `, - 'test.ts', + 'test.ts' ); assert_explicit_elements(result, ['nav', 'footer']); }); @@ -147,7 +147,7 @@ describe('@fuz-variables comment extraction', () => { svelte_script(` // @fuz-variables font_size_xl4 font_size_xl5 const x = 1; -`), +`) ); assert_explicit_variables(result, ['font_size_xl4', 'font_size_xl5']); }); @@ -157,7 +157,7 @@ const x = 1; svelte_script(` /* @fuz-variables text_min text_max */ const x = 1; -`), +`) ); assert_explicit_variables(result, ['text_min', 'text_max']); }); @@ -193,7 +193,7 @@ describe('@fuz-variables in TypeScript', () => { // @fuz-variables space_xl8 distance_lg const x = 1; `, - 'test.ts', + 'test.ts' ); assert_explicit_variables(result, ['space_xl8', 'distance_lg']); }); @@ -204,7 +204,7 @@ const x = 1; /* @fuz-variables icon_size_xl3 */ export const x = 1; `, - 'test.ts', + 'test.ts' ); assert_explicit_variables(result, ['icon_size_xl3']); }); @@ -247,7 +247,7 @@ describe('combined @fuz-* comments', () => { // @fuz-classes dynamic_class // @fuz-elements dialog const x = 1; -`), +`) ); assert.deepEqual(result.explicit_classes, new Set(['dynamic_class'])); assert_explicit_elements(result, ['dialog']); diff --git a/src/test/generate_bundled_css.test.ts b/src/test/generate_bundled_css.test.ts index 554e6bc86..bc538e025 100644 --- a/src/test/generate_bundled_css.test.ts +++ b/src/test/generate_bundled_css.test.ts @@ -7,10 +7,10 @@ * @module */ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {generate_bundled_css, type CssResolutionResult} from '$lib/css_bundled_resolution.ts'; -import {assert_css_order} from './test_helpers.ts'; +import { generate_bundled_css, type CssResolutionResult } from '$lib/css_bundled_resolution.ts'; +import { assert_css_order } from './test_helpers.ts'; /** * Creates a mock resolution result for testing generate_bundled_css. @@ -22,7 +22,7 @@ const create_mock_result = (overrides: Partial<CssResolutionResult> = {}): CssRe included_rule_indices: new Set([0]), included_elements: new Set(['button']), diagnostics: [], - ...overrides, + ...overrides }); describe('generate_bundled_css', () => { @@ -35,7 +35,7 @@ describe('generate_bundled_css', () => { combined, '/* Theme Variables */', '/* Base Styles */', - '/* Utility Classes */', + '/* Utility Classes */' ); }); @@ -52,7 +52,7 @@ describe('generate_bundled_css', () => { describe('exclusion options', () => { test('include_theme: false excludes theme', () => { const result = create_mock_result(); - const no_theme = generate_bundled_css(result, '.p_md {}', {include_theme: false}); + const no_theme = generate_bundled_css(result, '.p_md {}', { include_theme: false }); assert.notInclude(no_theme, '/* Theme Variables */'); assert.notInclude(no_theme, '--color'); @@ -62,7 +62,7 @@ describe('generate_bundled_css', () => { test('include_base: false excludes base', () => { const result = create_mock_result(); - const no_base = generate_bundled_css(result, '.p_md {}', {include_base: false}); + const no_base = generate_bundled_css(result, '.p_md {}', { include_base: false }); assert.include(no_base, '/* Theme Variables */'); assert.notInclude(no_base, '/* Base Styles */'); @@ -73,7 +73,7 @@ describe('generate_bundled_css', () => { test('include_utilities: false excludes utilities', () => { const result = create_mock_result(); const no_utility = generate_bundled_css(result, '.p_md { padding: 16px; }', { - include_utilities: false, + include_utilities: false }); assert.include(no_utility, '/* Theme Variables */'); @@ -87,7 +87,7 @@ describe('generate_bundled_css', () => { test('empty theme produces no section', () => { const result = create_mock_result({ theme_css: '', - resolved_variables: new Set<string>(), + resolved_variables: new Set<string>() }); const bundled = generate_bundled_css(result, '.p_md {}'); @@ -100,7 +100,7 @@ describe('generate_bundled_css', () => { const result = create_mock_result({ base_css: '', included_rule_indices: new Set<number>(), - included_elements: new Set<string>(), + included_elements: new Set<string>() }); const bundled = generate_bundled_css(result, '.p_md {}'); @@ -124,7 +124,7 @@ describe('generate_bundled_css', () => { base_css: '', resolved_variables: new Set<string>(), included_rule_indices: new Set<number>(), - included_elements: new Set<string>(), + included_elements: new Set<string>() }); const bundled = generate_bundled_css(result, ''); @@ -135,7 +135,7 @@ describe('generate_bundled_css', () => { const result = create_mock_result({ base_css: '', included_rule_indices: new Set<number>(), - included_elements: new Set<string>(), + included_elements: new Set<string>() }); const bundled = generate_bundled_css(result, ''); diff --git a/src/test/generate_classes_css.test.ts b/src/test/generate_classes_css.test.ts index e90c65801..f7a574197 100644 --- a/src/test/generate_classes_css.test.ts +++ b/src/test/generate_classes_css.test.ts @@ -1,34 +1,34 @@ -import {test, describe, assert} from 'vitest'; +import { test, describe, assert } from 'vitest'; import { generate_classes_css, extract_primary_property, - type CssClassDefinitionInterpreter, + type CssClassDefinitionInterpreter } from '$lib/css_class_generation.ts'; -import {css_class_composites} from '$lib/css_class_composites.ts'; +import { css_class_composites } from '$lib/css_class_composites.ts'; import { assert_css_contains, assert_css_not_contains, assert_css_order, assert_diagnostic, - assert_no_diagnostic, + assert_no_diagnostic } from './test_helpers.ts'; /** * Common class definitions used across multiple tests. */ const COMMON_DEFS = { - p_lg: {declaration: 'padding: var(--space_lg);'}, - m_md: {declaration: 'margin: var(--space_md);'}, - rounded: {declaration: 'border-radius: var(--border_radius_md);'}, - shadow_md: {declaration: 'box-shadow: var(--shadow_md);'}, + p_lg: { declaration: 'padding: var(--space_lg);' }, + m_md: { declaration: 'margin: var(--space_md);' }, + rounded: { declaration: 'border-radius: var(--border_radius_md);' }, + shadow_md: { declaration: 'box-shadow: var(--shadow_md);' } }; const BORDER_RADIUS_DEFS = { - border_radius_sm: {declaration: 'border-radius: var(--border_radius_sm);'}, + border_radius_sm: { declaration: 'border-radius: var(--border_radius_sm);' }, border_top_right_radius_sm: { - declaration: 'border-top-right-radius: var(--border_radius_sm);', - }, + declaration: 'border-top-right-radius: var(--border_radius_sm);' + } }; const SIMPLE_LITERAL_INTERPRETER: CssClassDefinitionInterpreter = { @@ -36,7 +36,7 @@ const SIMPLE_LITERAL_INTERPRETER: CssClassDefinitionInterpreter = { interpret: (matched) => { const [prop, val] = matched[0].split(':'); return `${prop}: ${val};`; - }, + } }; describe('generate_classes_css', () => { @@ -45,11 +45,11 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['display:flex', 'opacity:80%'], class_definitions: { - 'display:flex': {declaration: 'display: flex;'}, - 'opacity:80%': {declaration: 'opacity: 80%;'}, + 'display:flex': { declaration: 'display: flex;' }, + 'opacity:80%': { declaration: 'opacity: 80%;' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.display\\:flex { display: flex; }'); @@ -61,17 +61,17 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['hover:opacity:80%', 'nth-child(2n):color:red'], class_definitions: { - 'hover:opacity:80%': {declaration: 'opacity: 80%;'}, - 'nth-child(2n):color:red': {declaration: 'color: red;'}, + 'hover:opacity:80%': { declaration: 'opacity: 80%;' }, + 'nth-child(2n):color:red': { declaration: 'color: red;' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '.hover\\:opacity\\:80\\%', - '.nth-child\\(2n\\)\\:color\\:red', + '.nth-child\\(2n\\)\\:color\\:red' ); }); }); @@ -80,14 +80,14 @@ describe('generate_classes_css', () => { test('uses interpreter for unknown classes', () => { const interpreter: CssClassDefinitionInterpreter = { pattern: /^test-(\w+)$/, - interpret: (matched) => `test-prop: ${matched[1]};`, + interpret: (matched) => `test-prop: ${matched[1]};` }; const result = generate_classes_css({ class_names: ['test-value'], class_definitions: {}, interpreters: [interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.test-value', 'test-prop: value;'); @@ -97,14 +97,14 @@ describe('generate_classes_css', () => { const interpreter: CssClassDefinitionInterpreter = { pattern: /^media-(\w+)$/, interpret: (matched) => - `@media (min-width: 800px) { .media-${matched[1]} { display: ${matched[1]}; } }`, + `@media (min-width: 800px) { .media-${matched[1]} { display: ${matched[1]}; } }` }; const result = generate_classes_css({ class_names: ['media-flex'], class_definitions: {}, interpreters: [interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '@media (min-width: 800px)', 'display: flex;'); @@ -118,17 +118,17 @@ describe('generate_classes_css', () => { level: 'warning', message: 'test warning', identifier: matched[0], - suggestion: null, + suggestion: null }); return `color: ${matched[1]};`; - }, + } }; const result = generate_classes_css({ class_names: ['warn-red'], class_definitions: {}, interpreters: [interpreter], - css_properties: null, + css_properties: null }); assert.lengthOf(result.diagnostics, 1); @@ -142,10 +142,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['test-class'], class_definitions: { - 'test-class': {declaration: 'color: red;', comment: 'Single line comment'}, + 'test-class': { declaration: 'color: red;', comment: 'Single line comment' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '/* Single line comment */'); @@ -155,10 +155,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['test-class'], class_definitions: { - 'test-class': {declaration: 'color: red;', comment: 'Line 1\nLine 2'}, + 'test-class': { declaration: 'color: red;', comment: 'Line 1\nLine 2' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '/*\nLine 1\nLine 2\n*/'); @@ -169,10 +169,10 @@ describe('generate_classes_css', () => { class_names: ['card'], class_definitions: { ...COMMON_DEFS, - card: {composes: ['p_lg'], comment: 'Card component'}, + card: { composes: ['p_lg'], comment: 'Card component' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '/* Card component */', '.card {'); @@ -184,10 +184,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['simple'], class_definitions: { - simple: {ruleset: '.simple { color: red; }'}, + simple: { ruleset: '.simple { color: red; }' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.simple { color: red; }'); @@ -200,11 +200,11 @@ describe('generate_classes_css', () => { class_definitions: { multi: { ruleset: `.multi { color: red; } -.multi:hover { color: blue; }`, - }, +.multi:hover { color: blue; }` + } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.multi { color: red; }'); @@ -215,10 +215,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['clickable'], class_definitions: { - clickable: {ruleset: `.foobar { cursor: pointer; }`}, + clickable: { ruleset: `.foobar { cursor: pointer; }` } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.foobar { cursor: pointer; }'); @@ -231,11 +231,11 @@ describe('generate_classes_css', () => { class_definitions: { clickable: { ruleset: `.clickable { cursor: pointer; } -.clickable:hover { opacity: 0.8; }`, - }, +.clickable:hover { opacity: 0.8; }` + } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_no_diagnostic(result.diagnostics, 'warning', 'no selectors containing'); @@ -245,10 +245,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['clickable'], class_definitions: { - clickable: {ruleset: `.clickable_extended { cursor: pointer; }`}, + clickable: { ruleset: `.clickable_extended { cursor: pointer; }` } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_diagnostic(result.diagnostics, 'warning', 'no selectors containing'); @@ -262,11 +262,11 @@ describe('generate_classes_css', () => { ruleset: ` .other { color: red; } .another:hover { color: blue; } - `, - }, + ` + } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_diagnostic(result.diagnostics, 'warning', 'no selectors containing'); @@ -276,10 +276,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['btn'], class_definitions: { - btn: {ruleset: `.btn[disabled] { opacity: 0.5; }`}, + btn: { ruleset: `.btn[disabled] { opacity: 0.5; }` } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_no_diagnostic(result.diagnostics, 'warning', 'no selectors containing'); @@ -290,11 +290,11 @@ describe('generate_classes_css', () => { class_names: ['responsive'], class_definitions: { responsive: { - ruleset: `/* responsive wrapper */ @media (width >= 48rem) { .responsive { display: flex; } }`, - }, + ruleset: `/* responsive wrapper */ @media (width >= 48rem) { .responsive { display: flex; } }` + } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_no_diagnostic(result.diagnostics, 'warning', 'could be converted'); @@ -305,11 +305,11 @@ describe('generate_classes_css', () => { class_names: ['fancy'], class_definitions: { fancy: { - ruleset: `@supports (display: grid) { @media (width >= 48rem) { .fancy { display: grid; } } }`, - }, + ruleset: `@supports (display: grid) { @media (width >= 48rem) { .fancy { display: grid; } } }` + } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_no_diagnostic(result.diagnostics, 'warning', 'could be converted'); @@ -319,10 +319,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['empty'], class_definitions: { - empty: {ruleset: ''}, + empty: { ruleset: '' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert.strictEqual(result.css, ''); @@ -333,10 +333,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['whitespace'], class_definitions: { - whitespace: {ruleset: ' \n\t '}, + whitespace: { ruleset: ' \n\t ' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert.strictEqual(result.css, ''); @@ -347,10 +347,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['commented'], class_definitions: { - commented: {ruleset: '/* this ruleset is empty */'}, + commented: { ruleset: '/* this ruleset is empty */' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_diagnostic(result.diagnostics, 'warning', 'no selectors containing'); @@ -360,10 +360,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['my:custom'], class_definitions: { - 'my:custom': {ruleset: `.other { color: red; }`}, + 'my:custom': { ruleset: `.other { color: red; }` } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_diagnostic(result.diagnostics, 'warning', 'no selectors containing'); @@ -373,10 +373,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['my:custom'], class_definitions: { - 'my:custom': {ruleset: `.my\\:custom { color: red; }`}, + 'my:custom': { ruleset: `.my\\:custom { color: red; }` } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_no_diagnostic(result.diagnostics, 'warning', 'no selectors containing'); @@ -386,16 +386,16 @@ describe('generate_classes_css', () => { describe('sorting', () => { test('maintains definition order for known classes', () => { const class_definitions = { - aaa: {declaration: 'a: a;'}, - zzz: {declaration: 'z: z;'}, - mmm: {declaration: 'm: m;'}, + aaa: { declaration: 'a: a;' }, + zzz: { declaration: 'z: z;' }, + mmm: { declaration: 'm: m;' } }; const result = generate_classes_css({ class_names: ['mmm', 'aaa', 'zzz'], class_definitions, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_order(result.css, '.aaa', '.zzz', '.mmm'); @@ -405,10 +405,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['unknown-b', 'known', 'unknown-a'], class_definitions: { - known: {declaration: 'k: k;'}, + known: { declaration: 'k: k;' } }, interpreters: [], - css_properties: null, + css_properties: null }); // known should be present, unknown classes without interpreter produce no output @@ -419,14 +419,14 @@ describe('generate_classes_css', () => { test('sorts interpreted classes alphabetically', () => { const interpreter: CssClassDefinitionInterpreter = { pattern: /^int-(\w+)$/, - interpret: (matched) => `prop: ${matched[1]};`, + interpret: (matched) => `prop: ${matched[1]};` }; const result = generate_classes_css({ class_names: ['int-ccc', 'int-aaa', 'int-bbb'], class_definitions: {}, interpreters: [interpreter], - css_properties: null, + css_properties: null }); assert_css_order(result.css, '.int-aaa', '.int-bbb', '.int-ccc'); @@ -437,7 +437,7 @@ describe('generate_classes_css', () => { class_names: ['border_top_right_radius_sm', 'border-radius:0'], class_definitions: BORDER_RADIUS_DEFS, interpreters: [SIMPLE_LITERAL_INTERPRETER], - css_properties: null, + css_properties: null }); assert_css_order(result.css, 'border-radius\\:0', 'border_top_right_radius_sm'); @@ -448,7 +448,7 @@ describe('generate_classes_css', () => { class_names: ['border-top-right-radius:5px', 'border_radius_sm'], class_definitions: BORDER_RADIUS_DEFS, interpreters: [SIMPLE_LITERAL_INTERPRETER], - css_properties: null, + css_properties: null }); assert_css_order(result.css, 'border_radius_sm', 'border-top-right-radius\\:5px'); @@ -459,7 +459,7 @@ describe('generate_classes_css', () => { class_names: ['border-top-right-radius:5px', 'border-radius:0'], class_definitions: BORDER_RADIUS_DEFS, interpreters: [SIMPLE_LITERAL_INTERPRETER], - css_properties: null, + css_properties: null }); assert_css_order(result.css, 'border-radius\\:0', 'border-top-right-radius\\:5px'); @@ -473,14 +473,14 @@ describe('generate_classes_css', () => { const prop = parts.at(-2); const val = parts.at(-1); return `${prop}: ${val};`; - }, + } }; const result = generate_classes_css({ class_names: ['border_top_right_radius_sm', 'hover:border-radius:0'], class_definitions: BORDER_RADIUS_DEFS, interpreters: [modified_literal_interpreter], - css_properties: null, + css_properties: null }); assert_css_order(result.css, 'hover\\:border-radius\\:0', 'border_top_right_radius_sm'); @@ -492,16 +492,16 @@ describe('generate_classes_css', () => { // p_md has a finite index, so it sorts before hover:p_md. const catch_all_interpreter: CssClassDefinitionInterpreter = { pattern: /./, - interpret: (matched) => `content: "${matched[0]}";`, + interpret: (matched) => `content: "${matched[0]}";` }; const result = generate_classes_css({ class_names: ['hover:p_md', 'p_md'], class_definitions: { - p_md: {declaration: 'padding: var(--space_md);'}, + p_md: { declaration: 'padding: var(--space_md);' } }, interpreters: [catch_all_interpreter], - css_properties: null, + css_properties: null }); assert_css_order(result.css, 'p_md', 'hover\\:p_md'); @@ -514,17 +514,17 @@ describe('generate_classes_css', () => { class_names: ['card'], class_definitions: { ...COMMON_DEFS, - card: {composes: ['p_lg', 'rounded']}, + card: { composes: ['p_lg', 'rounded'] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '.card {', 'padding: var(--space_lg);', - 'border-radius: var(--border_radius_md);', + 'border-radius: var(--border_radius_md);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -534,10 +534,10 @@ describe('generate_classes_css', () => { class_names: ['card'], class_definitions: { ...COMMON_DEFS, - card: {composes: ['p_lg', 'rounded'], declaration: '--card-bg: var(--shade_10);'}, + card: { composes: ['p_lg', 'rounded'], declaration: '--card-bg: var(--shade_10);' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -545,7 +545,7 @@ describe('generate_classes_css', () => { '.card {', 'padding: var(--space_lg);', 'border-radius: var(--border_radius_md);', - '--card-bg: var(--shade_10);', + '--card-bg: var(--shade_10);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -555,11 +555,11 @@ describe('generate_classes_css', () => { class_names: ['panel'], class_definitions: { ...COMMON_DEFS, - panel_base: {composes: ['p_lg', 'rounded']}, - panel: {composes: ['panel_base', 'shadow_md']}, + panel_base: { composes: ['p_lg', 'rounded'] }, + panel: { composes: ['panel_base', 'shadow_md'] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -567,7 +567,7 @@ describe('generate_classes_css', () => { '.panel {', 'padding: var(--space_lg);', 'border-radius: var(--border_radius_md);', - 'box-shadow: var(--shadow_md);', + 'box-shadow: var(--shadow_md);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -577,17 +577,17 @@ describe('generate_classes_css', () => { class_names: ['card'], class_definitions: { ...COMMON_DEFS, - card: {composes: ['p_lg', 'unknown_class']}, + card: { composes: ['p_lg', 'unknown_class'] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, '.card'); assert_diagnostic( result.diagnostics, 'error', - 'Unknown class "unknown_class" in composes array', + 'Unknown class "unknown_class" in composes array' ); }); @@ -595,11 +595,11 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['a'], class_definitions: { - a: {composes: ['b']}, - b: {composes: ['a']}, + a: { composes: ['b'] }, + b: { composes: ['a'] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, '.a'); @@ -612,12 +612,12 @@ describe('generate_classes_css', () => { class_definitions: { clickable: { ruleset: `.clickable { cursor: pointer; } -.clickable:hover { opacity: 0.8; }`, +.clickable:hover { opacity: 0.8; }` }, - card: {composes: ['clickable']}, + card: { composes: ['clickable'] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, '.card'); @@ -628,10 +628,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['self_ref'], class_definitions: { - self_ref: {composes: ['self_ref']}, + self_ref: { composes: ['self_ref'] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, '.self_ref'); @@ -642,10 +642,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['empty_card'], class_definitions: { - empty_card: {composes: []}, + empty_card: { composes: [] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert.strictEqual(result.css, ''); @@ -656,13 +656,13 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['diamond'], class_definitions: { - base: {declaration: 'color: red;'}, - branch_a: {composes: ['base'], declaration: 'font-size: 1rem;'}, - branch_b: {composes: ['base'], declaration: 'font-weight: bold;'}, - diamond: {composes: ['branch_a', 'branch_b']}, + base: { declaration: 'color: red;' }, + branch_a: { composes: ['base'], declaration: 'font-size: 1rem;' }, + branch_b: { composes: ['base'], declaration: 'font-weight: bold;' }, + diamond: { composes: ['branch_a', 'branch_b'] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -670,7 +670,7 @@ describe('generate_classes_css', () => { '.diamond {', 'color: red;', 'font-size: 1rem;', - 'font-weight: bold;', + 'font-weight: bold;' ); assert.lengthOf(result.diagnostics, 0); }); @@ -679,12 +679,12 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['a'], class_definitions: { - a: {composes: ['b']}, - b: {composes: ['c']}, - c: {composes: ['a']}, + a: { composes: ['b'] }, + b: { composes: ['c'] }, + c: { composes: ['a'] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, '.a'); @@ -695,15 +695,15 @@ describe('generate_classes_css', () => { describe('class_locations', () => { test('diagnostics include locations when provided', () => { - const loc = {file: 'test.svelte', line: 10, column: 5}; + const loc = { file: 'test.svelte', line: 10, column: 5 }; const result = generate_classes_css({ class_names: ['card'], class_definitions: { - card: {composes: ['unknown']}, + card: { composes: ['unknown'] } }, interpreters: [], css_properties: null, - class_locations: new Map([['card', [loc]]]), + class_locations: new Map([['card', [loc]]]) }); assert.lengthOf(result.diagnostics, 1); @@ -714,10 +714,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['card'], class_definitions: { - card: {composes: ['unknown']}, + card: { composes: ['unknown'] } }, interpreters: [], - css_properties: null, + css_properties: null }); assert.lengthOf(result.diagnostics, 1); @@ -730,10 +730,10 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['p_lg'], class_definitions: { - p_lg: {declaration: 'padding: var(--space_lg);'}, + p_lg: { declaration: 'padding: var(--space_lg);' } }, interpreters: [], - css_properties: null, + css_properties: null }); assert.isTrue(result.variables_used.has('space_lg')); @@ -743,12 +743,12 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['card'], class_definitions: { - base: {declaration: 'color: var(--palette_a_5);'}, - extended: {composes: ['base'], declaration: 'margin: var(--space_md);'}, - card: {composes: ['extended']}, + base: { declaration: 'color: var(--palette_a_5);' }, + extended: { composes: ['base'], declaration: 'margin: var(--space_md);' }, + card: { composes: ['extended'] } }, interpreters: [], - css_properties: null, + css_properties: null }); // Should track variables from entire composition chain @@ -761,11 +761,11 @@ describe('generate_classes_css', () => { class_names: ['themed'], class_definitions: { themed: { - ruleset: `.themed { background: var(--bg_1); color: var(--text_color_1); }`, - }, + ruleset: `.themed { background: var(--bg_1); color: var(--text_color_1); }` + } }, interpreters: [], - css_properties: null, + css_properties: null }); assert.isTrue(result.variables_used.has('bg_1')); @@ -777,11 +777,11 @@ describe('generate_classes_css', () => { class_names: ['multi'], class_definitions: { multi: { - declaration: 'padding: var(--space_sm) var(--space_md); margin: var(--space_lg);', - }, + declaration: 'padding: var(--space_sm) var(--space_md); margin: var(--space_lg);' + } }, interpreters: [], - css_properties: null, + css_properties: null }); assert.isTrue(result.variables_used.has('space_sm')); @@ -793,11 +793,11 @@ describe('generate_classes_css', () => { const result = generate_classes_css({ class_names: ['a', 'b'], class_definitions: { - a: {declaration: 'padding: var(--space_md);'}, - b: {declaration: 'margin: var(--space_md);'}, + a: { declaration: 'padding: var(--space_md);' }, + b: { declaration: 'margin: var(--space_md);' } }, interpreters: [], - css_properties: null, + css_properties: null }); // Set naturally deduplicates @@ -812,7 +812,7 @@ describe('generate_classes_css', () => { class_names: ['xs'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -824,7 +824,7 @@ describe('generate_classes_css', () => { '--input_padding_x: var(--space_sm);', '--icon_size: var(--icon_size_xs);', '--menuitem_padding: var(--space_xs5) var(--space_xs3);', - '--flow_margin: var(--space_sm);', + '--flow_margin: var(--space_sm);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -834,7 +834,7 @@ describe('generate_classes_css', () => { class_names: ['xs'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert.isTrue(result.variables_used.has('font_size_xs')); @@ -853,7 +853,7 @@ describe('generate_classes_css', () => { class_names: ['sm'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -865,7 +865,7 @@ describe('generate_classes_css', () => { '--input_padding_x: var(--space_md);', '--icon_size: var(--icon_size_sm);', '--menuitem_padding: var(--space_xs4) var(--space_xs2);', - '--flow_margin: var(--space_md);', + '--flow_margin: var(--space_md);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -875,7 +875,7 @@ describe('generate_classes_css', () => { class_names: ['sm'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert.isTrue(result.variables_used.has('font_size_sm')); @@ -894,7 +894,7 @@ describe('generate_classes_css', () => { class_names: ['md'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -906,7 +906,7 @@ describe('generate_classes_css', () => { '--input_padding_x: var(--space_lg);', '--icon_size: var(--icon_size_md);', '--menuitem_padding: var(--space_xs3) var(--space_xs);', - '--flow_margin: var(--space_lg);', + '--flow_margin: var(--space_lg);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -916,7 +916,7 @@ describe('generate_classes_css', () => { class_names: ['md'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert.isTrue(result.variables_used.has('font_size_md')); @@ -935,7 +935,7 @@ describe('generate_classes_css', () => { class_names: ['lg'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -947,7 +947,7 @@ describe('generate_classes_css', () => { '--input_padding_x: var(--space_xl);', '--icon_size: var(--icon_size_lg);', '--menuitem_padding: var(--space_xs2) var(--space_sm);', - '--flow_margin: var(--space_xl);', + '--flow_margin: var(--space_xl);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -957,7 +957,7 @@ describe('generate_classes_css', () => { class_names: ['lg'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert.isTrue(result.variables_used.has('font_size_lg')); @@ -976,7 +976,7 @@ describe('generate_classes_css', () => { class_names: ['xl'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -988,7 +988,7 @@ describe('generate_classes_css', () => { '--input_padding_x: var(--space_xl2);', '--icon_size: var(--icon_size_xl);', '--menuitem_padding: var(--space_xs) var(--space_md);', - '--flow_margin: var(--space_xl2);', + '--flow_margin: var(--space_xl2);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -998,7 +998,7 @@ describe('generate_classes_css', () => { class_names: ['xl'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert.isTrue(result.variables_used.has('font_size_xl')); @@ -1017,13 +1017,13 @@ describe('generate_classes_css', () => { class_names: ['mb_flow'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '.mb_flow {', - 'margin-bottom: var(--flow_margin, var(--space_lg));', + 'margin-bottom: var(--flow_margin, var(--space_lg));' ); assert.lengthOf(result.diagnostics, 0); }); @@ -1033,13 +1033,13 @@ describe('generate_classes_css', () => { class_names: ['mt_flow'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '.mt_flow {', - 'margin-top: var(--flow_margin, var(--space_lg));', + 'margin-top: var(--flow_margin, var(--space_lg));' ); assert.lengthOf(result.diagnostics, 0); }); @@ -1049,7 +1049,7 @@ describe('generate_classes_css', () => { class_names: ['mb_flow'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert.isTrue(result.variables_used.has('space_lg')); @@ -1064,11 +1064,11 @@ describe('generate_classes_css', () => { class_names: [name], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, 'var(--border_radius, var(--border_radius_xs))'); - }, + } ); test('chip uses var(--font_size, inherit)', () => { @@ -1076,7 +1076,7 @@ describe('generate_classes_css', () => { class_names: ['chip'], class_definitions: css_class_composites, interpreters: [], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, 'font-size: var(--font_size, inherit)'); @@ -1087,14 +1087,14 @@ describe('generate_classes_css', () => { test('static definition takes priority over interpreter', () => { const interpreter: CssClassDefinitionInterpreter = { pattern: /^box$/, - interpret: () => 'from-interpreter: true;', + interpret: () => 'from-interpreter: true;' }; const result = generate_classes_css({ class_names: ['box'], - class_definitions: {box: {declaration: 'from-static: true;'}}, + class_definitions: { box: { declaration: 'from-static: true;' } }, interpreters: [interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, 'from-static: true;'); @@ -1104,18 +1104,18 @@ describe('generate_classes_css', () => { test('first matching interpreter wins', () => { const interpreter1: CssClassDefinitionInterpreter = { pattern: /^test-(.+)$/, - interpret: () => 'first: true;', + interpret: () => 'first: true;' }; const interpreter2: CssClassDefinitionInterpreter = { pattern: /^test-(.+)$/, - interpret: () => 'second: true;', + interpret: () => 'second: true;' }; const result = generate_classes_css({ class_names: ['test-value'], class_definitions: {}, interpreters: [interpreter1, interpreter2], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, 'first: true;'); @@ -1125,18 +1125,18 @@ describe('generate_classes_css', () => { test('interpreter returning null falls through to next', () => { const interpreter1: CssClassDefinitionInterpreter = { pattern: /^test-(.+)$/, - interpret: () => null, + interpret: () => null }; const interpreter2: CssClassDefinitionInterpreter = { pattern: /^test-(.+)$/, - interpret: () => 'second: true;', + interpret: () => 'second: true;' }; const result = generate_classes_css({ class_names: ['test-value'], class_definitions: {}, interpreters: [interpreter1, interpreter2], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, 'second: true;'); @@ -1148,7 +1148,7 @@ describe('extract_primary_property', () => { test('returns property name for single-property declaration', () => { assert.strictEqual( extract_primary_property('border-radius: var(--border_radius_sm);'), - 'border-radius', + 'border-radius' ); }); diff --git a/src/test/generate_css.test.ts b/src/test/generate_css.test.ts index 5296b6488..db2be2ede 100644 --- a/src/test/generate_css.test.ts +++ b/src/test/generate_css.test.ts @@ -5,15 +5,15 @@ * @module */ -import {test, describe, assert} from 'vitest'; +import { test, describe, assert } from 'vitest'; -import {generate_css, type GenerateCssOptions} from '$lib/generate_css.ts'; -import {create_test_fixtures} from './css_bundled_resolution_fixtures.ts'; -import type {StyleVariable} from '$lib/variable.ts'; -import {assert_css_contains, assert_css_not_contains} from './test_helpers.ts'; +import { generate_css, type GenerateCssOptions } from '$lib/generate_css.ts'; +import { create_test_fixtures } from './css_bundled_resolution_fixtures.ts'; +import type { StyleVariable } from '$lib/variable.ts'; +import { assert_css_contains, assert_css_not_contains } from './test_helpers.ts'; const CLASS_DEFS = { - p_lg: {declaration: 'padding: var(--space_lg);'}, + p_lg: { declaration: 'padding: var(--space_lg);' } }; /** Builds options with sensible defaults; override per test. */ @@ -32,30 +32,30 @@ const make_options = (overrides: Partial<GenerateCssOptions> = {}): GenerateCssO include_base: false, include_theme: false, resources: null, - ...overrides, + ...overrides }); describe('generate_css', () => { describe('utility-only mode', () => { test('emits CSS for detected token classes, no base/theme', () => { - const result = generate_css(make_options({all_classes: new Set(['p_lg'])})); + const result = generate_css(make_options({ all_classes: new Set(['p_lg']) })); assert_css_contains(result.css, '.p_lg { padding: var(--space_lg); }'); assert.equal(result.diagnostics.length, 0); }); test('ignores resources when base and theme are disabled', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( 'button { color: red; }', - [], + [] ); const result = generate_css( make_options({ all_classes: new Set(['p_lg']), all_elements: new Set(['button']), - resources: {style_rule_index, variable_graph, class_variable_index}, - }), + resources: { style_rule_index, variable_graph, class_variable_index } + }) ); assert_css_contains(result.css, '.p_lg {'); @@ -69,9 +69,9 @@ describe('generate_css', () => { message: 'test diagnostic', suggestion: null, phase: 'extraction' as const, - location: {file: 'x.svelte', line: 1, column: 0}, + location: { file: 'x.svelte', line: 1, column: 0 } }; - const result = generate_css(make_options({extraction_diagnostics: [diagnostic]})); + const result = generate_css(make_options({ extraction_diagnostics: [diagnostic] })); assert.equal(result.diagnostics.length, 1); assert.equal(result.diagnostics[0]!.message, 'test diagnostic'); @@ -80,14 +80,14 @@ describe('generate_css', () => { describe('bundled mode', () => { const VARIABLES: Array<StyleVariable> = [ - {name: 'space_lg', light: '24px'}, - {name: 'text_color', light: 'black', dark: 'white'}, + { name: 'space_lg', light: '24px' }, + { name: 'text_color', light: 'black', dark: 'white' } ]; test('includes base rules for detected elements and used theme variables', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( 'button { color: var(--text_color); }', - VARIABLES, + VARIABLES ); const result = generate_css( @@ -97,8 +97,8 @@ describe('generate_css', () => { detected_css_variables: new Set(['text_color']), include_base: true, include_theme: true, - resources: {style_rule_index, variable_graph, class_variable_index}, - }), + resources: { style_rule_index, variable_graph, class_variable_index } + }) ); // utility class @@ -110,9 +110,9 @@ describe('generate_css', () => { }); test('merges explicit_variables into the detected set', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( 'button { color: red; }', - VARIABLES, + VARIABLES ); const result = generate_css( @@ -121,17 +121,17 @@ describe('generate_css', () => { // not in detected_css_variables — only reachable via @fuz-variables explicit_variables: new Set(['text_color']), include_theme: true, - resources: {style_rule_index, variable_graph, class_variable_index}, - }), + resources: { style_rule_index, variable_graph, class_variable_index } + }) ); assert_css_contains(result.css, '--text_color'); }); test('surfaces resolution diagnostics (unresolved explicit variable)', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( 'button { color: red; }', - VARIABLES, + VARIABLES ); const result = generate_css( @@ -139,8 +139,8 @@ describe('generate_css', () => { // not in the theme — resolve_css errors on the @fuz-variables annotation explicit_variables: new Set(['nonexistent_var']), include_theme: true, - resources: {style_rule_index, variable_graph, class_variable_index}, - }), + resources: { style_rule_index, variable_graph, class_variable_index } + }) ); const error = result.diagnostics.find((d) => d.level === 'error'); @@ -149,9 +149,9 @@ describe('generate_css', () => { }); test('does not mutate the caller-supplied detected_css_variables set', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( 'button { color: red; }', - VARIABLES, + VARIABLES ); const detected = new Set(['space_lg']); @@ -160,17 +160,17 @@ describe('generate_css', () => { explicit_variables: new Set(['text_color']), include_theme: true, detected_css_variables: detected, - resources: {style_rule_index, variable_graph, class_variable_index}, - }), + resources: { style_rule_index, variable_graph, class_variable_index } + }) ); assert.deepEqual([...detected], ['space_lg']); }); test('warns when base styles are enabled but theme variables are disabled', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( 'button { color: var(--text_color); }', - VARIABLES, + VARIABLES ); const result = generate_css( @@ -178,12 +178,12 @@ describe('generate_css', () => { all_elements: new Set(['button']), include_base: true, include_theme: false, // variables: null, but base styles stay on - resources: {style_rule_index, variable_graph, class_variable_index}, - }), + resources: { style_rule_index, variable_graph, class_variable_index } + }) ); const warning = result.diagnostics.find( - (d) => d.level === 'warning' && d.message.includes('theme variables are disabled'), + (d) => d.level === 'warning' && d.message.includes('theme variables are disabled') ); assert.ok(warning, 'expected a warning about disabled theme variables'); // base rule still emitted, but the theme variables section is not @@ -192,9 +192,9 @@ describe('generate_css', () => { }); test('no theme-disabled warning when both base and theme are enabled', () => { - const {style_rule_index, variable_graph, class_variable_index} = create_test_fixtures( + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( 'button { color: var(--text_color); }', - VARIABLES, + VARIABLES ); const result = generate_css( @@ -202,12 +202,12 @@ describe('generate_css', () => { all_elements: new Set(['button']), include_base: true, include_theme: true, - resources: {style_rule_index, variable_graph, class_variable_index}, - }), + resources: { style_rule_index, variable_graph, class_variable_index } + }) ); assert.isUndefined( - result.diagnostics.find((d) => d.message.includes('theme variables are disabled')), + result.diagnostics.find((d) => d.message.includes('theme variables are disabled')) ); }); }); diff --git a/src/test/knobs.test.ts b/src/test/knobs.test.ts index e77f5950f..fb8904f44 100644 --- a/src/test/knobs.test.ts +++ b/src/test/knobs.test.ts @@ -1,7 +1,7 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {theme_knobs, theme_knob_by_name, theme_knob_hook_names} from '$lib/knobs.ts'; -import {default_variables} from '$lib/variables.ts'; +import { theme_knobs, theme_knob_by_name, theme_knob_hook_names } from '$lib/knobs.ts'; +import { default_variables } from '$lib/variables.ts'; const declared_names = new Set(default_variables.map((v) => v.name)); @@ -16,7 +16,7 @@ describe('theme_knobs', () => { if (knob.hook) continue; assert.isTrue( declared_names.has(knob.name), - `Knob "${knob.name}" is not declared in default_variables`, + `Knob "${knob.name}" is not declared in default_variables` ); } }); @@ -26,7 +26,7 @@ describe('theme_knobs', () => { for (const name of theme_knob_hook_names) { assert.isFalse( declared_names.has(name), - `Hook knob "${name}" is declared in default_variables - remove its hook flag`, + `Hook knob "${name}" is declared in default_variables - remove its hook flag` ); } }); @@ -57,7 +57,7 @@ describe('theme_knobs', () => { 'hue_g', 'hue_h', 'hue_i', - 'hue_j', + 'hue_j' ]); }); diff --git a/src/test/modified_class_interpreter.test.ts b/src/test/modified_class_interpreter.test.ts index ae4355298..5c77bc040 100644 --- a/src/test/modified_class_interpreter.test.ts +++ b/src/test/modified_class_interpreter.test.ts @@ -1,15 +1,15 @@ -import {test, describe, assert} from 'vitest'; +import { test, describe, assert } from 'vitest'; -import {generate_classes_css} from '$lib/css_class_generation.ts'; -import {modified_class_interpreter} from '$lib/css_class_interpreters.ts'; -import {css_class_definitions} from '$lib/css_class_definitions.ts'; -import {css_class_composites} from '$lib/css_class_composites.ts'; +import { generate_classes_css } from '$lib/css_class_generation.ts'; +import { modified_class_interpreter } from '$lib/css_class_interpreters.ts'; +import { css_class_definitions } from '$lib/css_class_definitions.ts'; +import { css_class_composites } from '$lib/css_class_composites.ts'; import { assert_css_contains, assert_css_not_contains, assert_css_order, assert_diagnostic, - filter_diagnostics_by_message, + filter_diagnostics_by_message } from './test_helpers.ts'; /** @@ -23,14 +23,14 @@ describe('modified_class_interpreter', () => { class_names: ['hover:box'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '.hover\\:box:hover', 'display: flex', - 'flex-direction: column', + 'flex-direction: column' ); }); @@ -39,7 +39,7 @@ describe('modified_class_interpreter', () => { class_names: ['md:box'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '@media (width >= 48rem)', '.md\\:box', 'display: flex'); @@ -50,7 +50,7 @@ describe('modified_class_interpreter', () => { class_names: ['dark:panel'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, ':root.dark', '.dark\\:panel', 'border-radius'); @@ -61,14 +61,14 @@ describe('modified_class_interpreter', () => { class_names: ['md:dark:hover:box'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '@media (width >= 48rem)', ':root.dark', - '.md\\:dark\\:hover\\:box:hover', + '.md\\:dark\\:hover\\:box:hover' ); }); @@ -77,14 +77,14 @@ describe('modified_class_interpreter', () => { class_names: ['lg:dark:focus:after:box'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '@media (width >= 64rem)', ':root.dark', - '.lg\\:dark\\:focus\\:after\\:box:focus::after', + '.lg\\:dark\\:focus\\:after\\:box:focus::after' ); }); @@ -93,7 +93,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:p_md'], class_definitions: css_class_definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.hover\\:p_md:hover', 'padding'); @@ -104,7 +104,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:unknown_class'], class_definitions: {}, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, 'hover:unknown_class'); @@ -115,7 +115,7 @@ describe('modified_class_interpreter', () => { class_names: ['box'], class_definitions: {}, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, '.box'); @@ -128,7 +128,7 @@ describe('modified_class_interpreter', () => { class_names: ['before:box'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.before\\:box::before', 'display: flex'); @@ -139,7 +139,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:before:ellipsis'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.hover\\:before\\:ellipsis:hover::before'); @@ -152,7 +152,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:row'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.hover\\:row:hover', 'display: flex', 'flex-direction: row'); @@ -165,7 +165,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:selectable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.hover\\:selectable:hover', 'cursor: pointer'); @@ -177,7 +177,7 @@ describe('modified_class_interpreter', () => { class_names: ['md:selectable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -185,7 +185,7 @@ describe('modified_class_interpreter', () => { '@media (width >= 48rem)', '.md\\:selectable', '.md\\:selectable:hover', - '.md\\:selectable.selected', + '.md\\:selectable.selected' ); }); @@ -194,7 +194,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:menuitem'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -202,7 +202,7 @@ describe('modified_class_interpreter', () => { '.hover\\:menuitem:hover', '.hover\\:menuitem:hover .content', '.hover\\:menuitem:hover .icon', - '.hover\\:menuitem:hover .title', + '.hover\\:menuitem:hover .title' ); }); @@ -211,7 +211,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:chevron'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.hover\\:chevron:hover', '.hover\\:chevron:hover::before'); @@ -222,7 +222,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:chip'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -230,7 +230,7 @@ describe('modified_class_interpreter', () => { '.hover\\:chip:hover', 'a.hover\\:chip:hover', 'font-weight: 500', - 'font-weight: 600', + 'font-weight: 600' ); }); @@ -239,14 +239,14 @@ describe('modified_class_interpreter', () => { class_names: ['md:dark:hover:selectable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '@media (width >= 48rem)', ':root.dark', - '.md\\:dark\\:hover\\:selectable:hover', + '.md\\:dark\\:hover\\:selectable:hover' ); }); @@ -255,14 +255,14 @@ describe('modified_class_interpreter', () => { class_names: ['focus:plain'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '.focus\\:plain:not(:hover):focus', '.focus\\:plain:hover:focus', - '.focus\\:plain:active:focus', + '.focus\\:plain:active:focus' ); }); @@ -271,7 +271,7 @@ describe('modified_class_interpreter', () => { class_names: ['md:clickable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -280,7 +280,7 @@ describe('modified_class_interpreter', () => { '.md\\:clickable', '.md\\:clickable:focus', '.md\\:clickable:hover', - '.md\\:clickable:active', + '.md\\:clickable:active' ); }); @@ -289,14 +289,14 @@ describe('modified_class_interpreter', () => { class_names: ['before:chevron'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '.before\\:chevron::before', 'position: relative', - 'border-left-color', + 'border-left-color' ); assert_css_not_contains(result.css, '::before::before'); }); @@ -306,7 +306,7 @@ describe('modified_class_interpreter', () => { class_names: ['before:chip'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -314,7 +314,7 @@ describe('modified_class_interpreter', () => { '.before\\:chip::before', 'a.before\\:chip::before', 'font-weight: 500', - 'font-weight: 600', + 'font-weight: 600' ); }); }); @@ -325,14 +325,14 @@ describe('modified_class_interpreter', () => { class_names: ['hover:selectable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '.hover\\:selectable:hover', 'cursor: pointer', - '.hover\\:selectable.selected:hover', + '.hover\\:selectable.selected:hover' ); assert_css_not_contains(result.css, ':hover:hover'); }); @@ -342,7 +342,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:selectable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, ':active:hover'); @@ -353,7 +353,7 @@ describe('modified_class_interpreter', () => { class_names: ['focus:clickable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.focus\\:clickable:focus', ':hover:focus', ':active:focus'); @@ -365,7 +365,7 @@ describe('modified_class_interpreter', () => { class_names: ['active:clickable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, ':active:active'); @@ -377,7 +377,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:plain'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -385,7 +385,7 @@ describe('modified_class_interpreter', () => { '.hover\\:plain', '.hover\\:plain:not(:hover)', '.hover\\:plain:hover', - '.hover\\:plain:active', + '.hover\\:plain:active' ); assert_css_not_contains(result.css, ':hover:hover'); assert.isAbove(result.diagnostics.length, 0); @@ -395,39 +395,39 @@ describe('modified_class_interpreter', () => { describe('modifier on composes-based composites', () => { test('modifier on composes-based composite', () => { const definitions = { - p_lg: {declaration: 'padding: var(--space_lg);'}, - rounded: {declaration: 'border-radius: var(--border_radius_md);'}, - card: {composes: ['p_lg', 'rounded']}, + p_lg: { declaration: 'padding: var(--space_lg);' }, + rounded: { declaration: 'border-radius: var(--border_radius_md);' }, + card: { composes: ['p_lg', 'rounded'] } }; const result = generate_classes_css({ class_names: ['hover:card'], class_definitions: definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '.hover\\:card:hover {', 'padding: var(--space_lg);', - 'border-radius: var(--border_radius_md);', + 'border-radius: var(--border_radius_md);' ); assert.lengthOf(result.diagnostics, 0); }); test('modifier on composes + declaration composite', () => { const definitions = { - p_lg: {declaration: 'padding: var(--space_lg);'}, - rounded: {declaration: 'border-radius: var(--border_radius_md);'}, - card: {composes: ['p_lg', 'rounded'], declaration: '--card-bg: blue;'}, + p_lg: { declaration: 'padding: var(--space_lg);' }, + rounded: { declaration: 'border-radius: var(--border_radius_md);' }, + card: { composes: ['p_lg', 'rounded'], declaration: '--card-bg: blue;' } }; const result = generate_classes_css({ class_names: ['hover:card'], class_definitions: definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -435,23 +435,23 @@ describe('modified_class_interpreter', () => { '.hover\\:card:hover {', 'padding: var(--space_lg);', 'border-radius: var(--border_radius_md);', - '--card-bg: blue;', + '--card-bg: blue;' ); assert.lengthOf(result.diagnostics, 0); }); test('modifier on nested composes composition', () => { const definitions = { - p_lg: {declaration: 'padding: var(--space_lg);'}, - rounded: {declaration: 'border-radius: var(--border_radius_md);'}, - panel_base: {composes: ['p_lg', 'rounded']}, + p_lg: { declaration: 'padding: var(--space_lg);' }, + rounded: { declaration: 'border-radius: var(--border_radius_md);' }, + panel_base: { composes: ['p_lg', 'rounded'] } }; const result = generate_classes_css({ class_names: ['md:panel_base'], class_definitions: definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -459,24 +459,24 @@ describe('modified_class_interpreter', () => { '@media (width >= 48rem)', '.md\\:panel_base', 'padding: var(--space_lg);', - 'border-radius: var(--border_radius_md);', + 'border-radius: var(--border_radius_md);' ); assert.lengthOf(result.diagnostics, 0); }); test('modifier on diamond dependency deduplicates silently', () => { const definitions = { - base: {declaration: 'color: red;'}, - branch_a: {composes: ['base'], declaration: 'font-size: 1rem;'}, - branch_b: {composes: ['base'], declaration: 'font-weight: bold;'}, - diamond: {composes: ['branch_a', 'branch_b']}, + base: { declaration: 'color: red;' }, + branch_a: { composes: ['base'], declaration: 'font-size: 1rem;' }, + branch_b: { composes: ['base'], declaration: 'font-weight: bold;' }, + diamond: { composes: ['branch_a', 'branch_b'] } }; const result = generate_classes_css({ class_names: ['hover:diamond'], class_definitions: definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -484,7 +484,7 @@ describe('modified_class_interpreter', () => { '.hover\\:diamond:hover', 'color: red;', 'font-size: 1rem;', - 'font-weight: bold;', + 'font-weight: bold;' ); // Should not have duplicate "color: red;" - only appears once const colorMatches = result.css.match(/color: red;/g); @@ -497,7 +497,7 @@ describe('modified_class_interpreter', () => { class_names: ['md:sm'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -506,7 +506,7 @@ describe('modified_class_interpreter', () => { '.md\\:sm', '--font_size: var(--font_size_sm);', '--input_height: var(--space_xl4);', - '--icon_size: var(--icon_size_sm);', + '--icon_size: var(--icon_size_sm);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -516,7 +516,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:sm'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.hover\\:sm:hover', '--font_size: var(--font_size_sm);'); @@ -528,7 +528,7 @@ describe('modified_class_interpreter', () => { class_names: ['hover:md'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.hover\\:md:hover', '--font_size: var(--font_size_md);'); @@ -540,14 +540,14 @@ describe('modified_class_interpreter', () => { class_names: ['lg:sm'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( result.css, '@media (width >= 64rem)', '.lg\\:sm', - '--font_size: var(--font_size_sm);', + '--font_size: var(--font_size_sm);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -557,7 +557,7 @@ describe('modified_class_interpreter', () => { class_names: ['lg:md'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains( @@ -565,7 +565,7 @@ describe('modified_class_interpreter', () => { '@media (width >= 64rem)', '.lg\\:md', '--font_size: var(--font_size_md);', - '--icon_size: var(--icon_size_md);', + '--icon_size: var(--icon_size_md);' ); assert.lengthOf(result.diagnostics, 0); }); @@ -575,7 +575,7 @@ describe('modified_class_interpreter', () => { class_names: ['sm', 'md'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_contains(result.css, '.sm {', '--font_size: var(--font_size_sm);'); @@ -587,35 +587,35 @@ describe('modified_class_interpreter', () => { describe('error propagation', () => { test('modifier on class with unknown composes array produces error', () => { const definitions = { - card: {composes: ['unknown_class']}, + card: { composes: ['unknown_class'] } }; const result = generate_classes_css({ class_names: ['hover:card'], class_definitions: definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, '.hover\\:card'); assert_diagnostic( result.diagnostics, 'error', - 'Unknown class "unknown_class" in composes array', + 'Unknown class "unknown_class" in composes array' ); }); test('modifier on class with circular reference produces error', () => { const definitions = { - a: {composes: ['b']}, - b: {composes: ['a']}, + a: { composes: ['b'] }, + b: { composes: ['a'] } }; const result = generate_classes_css({ class_names: ['hover:a'], class_definitions: definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_not_contains(result.css, '.hover\\:a'); @@ -624,14 +624,14 @@ describe('modified_class_interpreter', () => { test('modifier on empty composes array produces no output', () => { const definitions = { - empty: {composes: []}, + empty: { composes: [] } }; const result = generate_classes_css({ class_names: ['hover:empty'], class_definitions: definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert.strictEqual(result.css, ''); @@ -645,7 +645,7 @@ describe('modified_class_interpreter', () => { class_names: ['active:border_a_50', 'hover:border_b_50'], class_definitions: css_class_definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_order(result.css, '.hover\\:border_b_50', '.active\\:border_a_50'); @@ -656,7 +656,7 @@ describe('modified_class_interpreter', () => { class_names: ['active:p_xl', 'hover:p_lg', 'focus:p_md', 'visited:p_sm'], class_definitions: css_class_definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert_css_order( @@ -664,7 +664,7 @@ describe('modified_class_interpreter', () => { '.visited\\:p_sm', '.focus\\:p_md', '.hover\\:p_lg', - '.active\\:p_xl', + '.active\\:p_xl' ); }); @@ -673,7 +673,7 @@ describe('modified_class_interpreter', () => { class_names: ['odd:p_md', 'even:p_lg', 'first:p_sm'], class_definitions: css_class_definitions, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); // Alphabetical: even < first < odd @@ -687,12 +687,12 @@ describe('modified_class_interpreter', () => { class_names: ['before:chevron'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); assert.isAbove(result.diagnostics.length, 0); const warning = result.diagnostics.find( - (d) => d.identifier === 'before:chevron' && d.message.includes('pseudo-element'), + (d) => d.identifier === 'before:chevron' && d.message.includes('pseudo-element') ); assert.isDefined(warning); assert.strictEqual(warning.level, 'warning'); @@ -705,11 +705,11 @@ describe('modified_class_interpreter', () => { class_names: ['hover:selectable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); const hover_warnings = filter_diagnostics_by_message(result.diagnostics, ':hover').filter( - (d) => d.identifier === 'hover:selectable', + (d) => d.identifier === 'hover:selectable' ); assert.isAbove(hover_warnings.length, 0); @@ -724,11 +724,11 @@ describe('modified_class_interpreter', () => { class_names: ['focus:clickable'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], - css_properties: null, + css_properties: null }); const focus_warning = result.diagnostics.find( - (d) => d.identifier === 'focus:clickable' && d.message.includes(':focus'), + (d) => d.identifier === 'focus:clickable' && d.message.includes(':focus') ); assert.isDefined(focus_warning); assert.include(focus_warning.message, '.clickable:focus'); diff --git a/src/test/modifiers.test.ts b/src/test/modifiers.test.ts index 41e159eb6..8b54d081b 100644 --- a/src/test/modifiers.test.ts +++ b/src/test/modifiers.test.ts @@ -1,13 +1,13 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {MODIFIERS} from '$lib/modifiers.ts'; +import { MODIFIERS } from '$lib/modifiers.ts'; /** * Loads CSS selectors from @webref/css for validation. */ const load_webref_selectors = async (): Promise<Set<string>> => { const data = await import('@webref/css/css.json'); - const selectors: Array<{name: string}> = (data as any).default.selectors; + const selectors: Array<{ name: string }> = (data as any).default.selectors; return new Set(selectors.map((s) => s.name)); }; @@ -54,7 +54,7 @@ describe('MODIFIERS', () => { if (!webref_selectors.has(base_selector)) { failures.push( - `"${m.name}" maps to "${m.css}" (base: "${base_selector}") which is not in CSS spec`, + `"${m.name}" maps to "${m.css}" (base: "${base_selector}") which is not in CSS spec` ); } } @@ -62,7 +62,7 @@ describe('MODIFIERS', () => { assert.strictEqual( failures.length, 0, - `The following modifiers are not in the CSS spec:\n ${failures.join('\n ')}`, + `The following modifiers are not in the CSS spec:\n ${failures.join('\n ')}` ); }); }); @@ -73,7 +73,7 @@ describe('MODIFIERS', () => { if (m.type === 'media') { assert.isTrue( m.css.startsWith('@media '), - `media modifier "${m.name}" should start with "@media "`, + `media modifier "${m.name}" should start with "@media "` ); } } @@ -84,7 +84,7 @@ describe('MODIFIERS', () => { if (m.type === 'ancestor') { assert.isTrue( m.css.startsWith(':root.'), - `ancestor modifier "${m.name}" should start with ":root."`, + `ancestor modifier "${m.name}" should start with ":root."` ); } } @@ -96,7 +96,7 @@ describe('MODIFIERS', () => { assert.isTrue(m.css.startsWith(':'), `state modifier "${m.name}" should start with ":"`); assert.isFalse( m.css.startsWith('::'), - `state modifier "${m.name}" should not start with "::"`, + `state modifier "${m.name}" should not start with "::"` ); } } @@ -107,7 +107,7 @@ describe('MODIFIERS', () => { if (m.type === 'pseudo-element') { assert.isTrue( m.css.startsWith('::'), - `pseudo-element modifier "${m.name}" should start with "::"`, + `pseudo-element modifier "${m.name}" should start with "::"` ); } } diff --git a/src/test/oklch.test.ts b/src/test/oklch.test.ts index b295dd562..1d1288e6c 100644 --- a/src/test/oklch.test.ts +++ b/src/test/oklch.test.ts @@ -1,4 +1,4 @@ -import {describe, test, assert} from 'vitest'; +import { describe, test, assert } from 'vitest'; import { oklch_delta_e, @@ -7,14 +7,14 @@ import { oklch_to_srgb, srgb_to_oklch, type Oklch, - type RgbUnit, + type RgbUnit } from '../lib/oklch.ts'; -import {srgb_relative_luminance, wcag_contrast_ratio} from '../lib/wcag.ts'; +import { srgb_relative_luminance, wcag_contrast_ratio } from '../lib/wcag.ts'; const assert_close = (actual: number, expected: number, tolerance: number, message?: string) => { assert( Math.abs(actual - expected) <= tolerance, - message ?? `expected ${actual} to be within ${tolerance} of ${expected}`, + message ?? `expected ${actual} to be within ${tolerance} of ${expected}` ); }; @@ -57,7 +57,7 @@ describe('oklch_to_srgb', () => { [0.5, 0.25, 0.75], [0.1, 0.9, 0.3], [0.95, 0.95, 0.02], - [0.33, 0.33, 0.33], + [0.33, 0.33, 0.33] ]; for (const rgb of samples) { const back = oklch_to_srgb(srgb_to_oklch(rgb)); diff --git a/src/test/ramps.test.ts b/src/test/ramps.test.ts index 417e4e0fd..42fb21f59 100644 --- a/src/test/ramps.test.ts +++ b/src/test/ramps.test.ts @@ -1,4 +1,4 @@ -import {describe, test, assert} from 'vitest'; +import { describe, test, assert } from 'vitest'; import { NEUTRAL_HUE, @@ -12,17 +12,17 @@ import { palette_stop_oklch, ramp_lightness, shade_stop_oklch, - text_stop_oklch, + text_stop_oklch } from '../lib/ramps.ts'; -import {color_scheme_variants, palette_variants} from '../lib/variable_data.ts'; -import {oklch_in_srgb_gamut, oklch_to_srgb, type RgbUnit} from '../lib/oklch.ts'; -import {wcag_contrast_ratio} from '../lib/wcag.ts'; +import { color_scheme_variants, palette_variants } from '../lib/variable_data.ts'; +import { oklch_in_srgb_gamut, oklch_to_srgb, type RgbUnit } from '../lib/oklch.ts'; +import { wcag_contrast_ratio } from '../lib/wcag.ts'; import { GATE_BODY_TEXT, GATE_FILL_TEXT, GATE_LINK, GATE_SUBTLE_TEXT, - GATE_UI, + GATE_UI } from '../lib/theme_check.ts'; // Contrast gates for the default palette. OKLCH lightness is monotonic with @@ -35,7 +35,7 @@ import { const clamp_rgb = (rgb: RgbUnit): RgbUnit => [ Math.min(1, Math.max(0, rgb[0])), Math.min(1, Math.max(0, rgb[1])), - Math.min(1, Math.max(0, rgb[2])), + Math.min(1, Math.max(0, rgb[2])) ]; describe('gamut', () => { @@ -46,7 +46,7 @@ describe('gamut', () => { const lch = palette_stop_oklch(letter, stop, scheme); assert( oklch_in_srgb_gamut(lch, 1e-4), - `palette_${letter}_${stop} ${scheme} out of gamut: ${lch.join(' ')}`, + `palette_${letter}_${stop} ${scheme} out of gamut: ${lch.join(' ')}` ); } } @@ -58,7 +58,7 @@ describe('gamut', () => { for (const stop of RAMP_STOPS) { assert( oklch_in_srgb_gamut(shade_stop_oklch(stop, scheme), 1e-4), - `shade_${stop} ${scheme}`, + `shade_${stop} ${scheme}` ); assert(oklch_in_srgb_gamut(text_stop_oklch(stop, scheme), 1e-4), `text_${stop} ${scheme}`); } @@ -71,7 +71,7 @@ describe('monotonicity', () => { const families = [ ['palette', PALETTE_LIGHTNESS_KNOBS], ['shade', SHADE_LIGHTNESS_KNOBS], - ['text', TEXT_LIGHTNESS_KNOBS], + ['text', TEXT_LIGHTNESS_KNOBS] ] as const; for (const [name, knobs_by_scheme] of families) { for (const scheme of color_scheme_variants) { @@ -82,7 +82,7 @@ describe('monotonicity', () => { const l = ramp_lightness(knobs, stop); assert( Math.sign(l - prev) === direction, - `${name} ${scheme} not monotonic at stop ${stop}: ${prev} → ${l}`, + `${name} ${scheme} not monotonic at stop ${stop}: ${prev} → ${l}` ); prev = l; } @@ -99,20 +99,20 @@ describe('chroma caps', () => { const caps = compute_palette_chroma_caps( Object.values(PALETTE_HUES), PALETTE_LIGHTNESS_KNOBS[scheme], - scheme, + scheme ); for (const stop of RAMP_STOPS) { const baked = PALETTE_CHROMA_CAPS[scheme][stop]; const computed = caps[stop]; assert( baked <= computed + 1e-6, - `cap too generous at ${stop} ${scheme}: baked ${baked} > recomputed ${computed}`, + `cap too generous at ${stop} ${scheme}: baked ${baked} > recomputed ${computed}` ); assert( baked >= computed - 0.001, `cap stale/over-conservative at ${stop} ${scheme}: baked ${baked} < recomputed ${ computed - }`, + }` ); } } @@ -128,7 +128,7 @@ describe('contrast gates', () => { const ratio = wcag_contrast_ratio(text, surface); assert( ratio >= GATE_BODY_TEXT, - `text_80 on shade_${stop} ${scheme}: ${ratio.toFixed(2)} < ${GATE_BODY_TEXT}`, + `text_80 on shade_${stop} ${scheme}: ${ratio.toFixed(2)} < ${GATE_BODY_TEXT}` ); } } @@ -138,11 +138,11 @@ describe('contrast gates', () => { for (const scheme of color_scheme_variants) { const ratio = wcag_contrast_ratio( oklch_to_srgb(text_stop_oklch('50', scheme)), - oklch_to_srgb(shade_stop_oklch('00', scheme)), + oklch_to_srgb(shade_stop_oklch('00', scheme)) ); assert( ratio >= GATE_SUBTLE_TEXT, - `text_50 on shade_00 ${scheme}: ${ratio.toFixed(2)} < ${GATE_SUBTLE_TEXT}`, + `text_50 on shade_00 ${scheme}: ${ratio.toFixed(2)} < ${GATE_SUBTLE_TEXT}` ); } }); @@ -151,7 +151,7 @@ describe('contrast gates', () => { for (const scheme of color_scheme_variants) { const ratio = wcag_contrast_ratio( oklch_to_srgb(palette_stop_oklch('a', '60', scheme)), - oklch_to_srgb(shade_stop_oklch('00', scheme)), + oklch_to_srgb(shade_stop_oklch('00', scheme)) ); assert(ratio >= GATE_LINK, `link on shade_00 ${scheme}: ${ratio.toFixed(2)} < ${GATE_LINK}`); } @@ -166,12 +166,12 @@ describe('contrast gates', () => { const ui = wcag_contrast_ratio(fill, surface); assert( ui >= GATE_UI, - `palette_${letter}_50 vs shade_00 ${scheme}: ${ui.toFixed(2)} < ${GATE_UI}`, + `palette_${letter}_50 vs shade_00 ${scheme}: ${ui.toFixed(2)} < ${GATE_UI}` ); const on_fill = wcag_contrast_ratio(text_max, fill); assert( on_fill >= GATE_FILL_TEXT, - `text_max on palette_${letter}_50 ${scheme}: ${on_fill.toFixed(2)} < ${GATE_FILL_TEXT}`, + `text_max on palette_${letter}_50 ${scheme}: ${on_fill.toFixed(2)} < ${GATE_FILL_TEXT}` ); } } diff --git a/src/test/style_rule_parser.custom.test.ts b/src/test/style_rule_parser.custom.test.ts index a217ac1a6..3f978c793 100644 --- a/src/test/style_rule_parser.custom.test.ts +++ b/src/test/style_rule_parser.custom.test.ts @@ -6,12 +6,12 @@ * @module */ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; import { create_style_rule_index, get_matching_rules, - generate_base_css, + generate_base_css } from '$lib/style_rule_parser.ts'; describe('create_style_rule_index', () => { diff --git a/src/test/style_rule_parser.test.ts b/src/test/style_rule_parser.test.ts index 541bab342..f731b3c9a 100644 --- a/src/test/style_rule_parser.test.ts +++ b/src/test/style_rule_parser.test.ts @@ -1,13 +1,13 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; import { parse_style_css, get_matching_rules, generate_base_css, collect_rule_variables, - load_style_rule_index, + load_style_rule_index } from '$lib/style_rule_parser.ts'; -import {default_cache_deps} from '$lib/deps_defaults.ts'; +import { default_cache_deps } from '$lib/deps_defaults.ts'; // Alias for brevity in tests const deps = default_cache_deps; @@ -58,7 +58,7 @@ describe('parse_style_css', () => { ['body { font-size: 16px; }', 'body'], ['html { font-size: 16px; }', 'html'], [':host { display: block; }', 'host'], - ['@media (prefers-reduced-motion) { :root { --duration: 0; } }', 'media_query'], + ['@media (prefers-reduced-motion) { :root { --duration: 0; } }', 'media_query'] ] as const)('%s is core (%s)', (css, reason) => { const index = parse_style_css(css, 'test-hash'); assert.isTrue(index.rules[0]!.is_core); @@ -154,7 +154,7 @@ describe('parse_style_css', () => { test.each([ ["div::before { content: ''; }", 'div', 'before'], ["span::after { content: ''; }", 'span', 'after'], - ['a:hover { color: red; }', 'a', 'hover'], + ['a:hover { color: red; }', 'a', 'hover'] ] as const)('%s extracts element but not pseudo', (css, element, pseudo) => { const index = parse_style_css(css, 'test-hash'); assert.isTrue(index.rules[0]!.elements.has(element)); @@ -170,7 +170,7 @@ describe('parse_style_css', () => { ["input[type='text'][required] { background: pink; }", 'input'], [":where(input[type='number'], input[type='text']) { font-family: monospace; }", 'input'], ['input:not([disabled]) { cursor: pointer; }', 'input'], - ["input[placeholder='a, b, c'] { color: gray; }", 'input'], + ["input[placeholder='a, b, c'] { color: gray; }", 'input'] ])('%s extracts element', (css, element) => { const index = parse_style_css(css, 'test-hash'); assert.isTrue(index.rules[0]!.elements.has(element)); @@ -327,43 +327,43 @@ describe('parse_style_css', () => { [ 'border shorthand', 'button { border: var(--border_width) solid var(--border_color); }', - ['border_width', 'border_color'], + ['border_width', 'border_color'] ], [ 'margin shorthand', 'div { margin: var(--space_sm) var(--space_md); }', - ['space_sm', 'space_md'], + ['space_sm', 'space_md'] ], [ 'padding shorthand', 'section { padding: var(--space_xs) var(--space_sm) var(--space_md) var(--space_lg); }', - ['space_xs', 'space_sm', 'space_md', 'space_lg'], + ['space_xs', 'space_sm', 'space_md', 'space_lg'] ], [ 'box-shadow shorthand', 'div { box-shadow: var(--shadow_x) var(--shadow_y) var(--shadow_blur) var(--shadow_color); }', - ['shadow_x', 'shadow_y', 'shadow_blur', 'shadow_color'], + ['shadow_x', 'shadow_y', 'shadow_blur', 'shadow_color'] ], [ 'font shorthand', 'p { font: var(--font_weight) var(--font_size)/var(--line_height) var(--font_family); }', - ['font_weight', 'font_size', 'line_height', 'font_family'], + ['font_weight', 'font_size', 'line_height', 'font_family'] ], [ 'background shorthand', 'header { background: var(--bg_color) url(image.png) var(--bg_position) / var(--bg_size); }', - ['bg_color', 'bg_position', 'bg_size'], + ['bg_color', 'bg_position', 'bg_size'] ], [ 'transition shorthand', 'a { transition: color var(--duration) var(--easing); }', - ['duration', 'easing'], + ['duration', 'easing'] ], [ 'nested calc', 'div { width: calc(var(--base_width) + var(--extra_width) * 2); }', - ['base_width', 'extra_width'], - ], + ['base_width', 'extra_width'] + ] ])('extracts from %s', (_name, css, expected_vars) => { const index = parse_style_css(css, 'test-hash'); for (const v of expected_vars) { diff --git a/src/test/styles.test.ts b/src/test/styles.test.ts index b29f759cd..38d132080 100644 --- a/src/test/styles.test.ts +++ b/src/test/styles.test.ts @@ -1,8 +1,8 @@ -import {test, assert} from 'vitest'; -import {readFileSync} from 'node:fs'; +import { test, assert } from 'vitest'; +import { readFileSync } from 'node:fs'; import * as exported_variables from '$lib/variables.ts'; -import {theme_knob_hook_names} from '$lib/knobs.ts'; +import { theme_knob_hook_names } from '$lib/knobs.ts'; import css_classes_text from './fixtures/css_classes_fixture.json?raw'; // vitest replaces this with an empty string because CSS isn't opted into being processed, @@ -13,7 +13,7 @@ const css_files = [main_stylesheet_text, css_classes_text]; const extract_custom_properties_usage = (css: string) => Array.from(css.matchAll(/var\((?:\s|\\[nt])*--([a-z][a-z0-9_]*(?<!_))(?:[,)])/g)).map( - (m) => m[1]!, + (m) => m[1]! ); test('variables in the CSS exist', () => { @@ -91,5 +91,5 @@ const known_without_variables = new Set([ 'button_border_color_active', 'shadow', 'shadow_alpha', - 'shadow_color', + 'shadow_color' ]); diff --git a/src/test/test_helpers.ts b/src/test/test_helpers.ts index fec364988..af93de548 100644 --- a/src/test/test_helpers.ts +++ b/src/test/test_helpers.ts @@ -5,10 +5,14 @@ * to reduce duplication and improve test readability. */ -import {assert} from 'vitest'; +import { assert } from 'vitest'; -import type {SourceLocation, ExtractionDiagnostic, GenerationDiagnostic} from '$lib/diagnostics.ts'; -import type {ExtractionData} from '$lib/css_class_extractor.ts'; +import type { + SourceLocation, + ExtractionDiagnostic, + GenerationDiagnostic +} from '$lib/diagnostics.ts'; +import type { ExtractionData } from '$lib/css_class_extractor.ts'; // // Factory Helpers @@ -20,7 +24,7 @@ import type {ExtractionData} from '$lib/css_class_extractor.ts'; export const loc = (file = 'test.ts', line = 1, column = 1): SourceLocation => ({ file, line, - column, + column }); /** @@ -32,7 +36,7 @@ export const EMPTY_EXTRACTION: ExtractionData = { diagnostics: null, elements: null, explicit_elements: null, - explicit_variables: null, + explicit_variables: null }; /** @@ -40,28 +44,28 @@ export const EMPTY_EXTRACTION: ExtractionData = { */ export const make_extraction_data = (overrides: Partial<ExtractionData> = {}): ExtractionData => ({ ...EMPTY_EXTRACTION, - ...overrides, + ...overrides }); /** * Creates a Map of class names to source locations from tuple entries. */ export const make_classes = ( - entries: Array<[string, Array<SourceLocation>]>, + entries: Array<[string, Array<SourceLocation>]> ): Map<string, Array<SourceLocation>> => new Map(entries); /** * Creates an ExtractionDiagnostic with sensible defaults. */ export const make_extraction_diagnostic = ( - overrides: Partial<ExtractionDiagnostic> = {}, + overrides: Partial<ExtractionDiagnostic> = {} ): ExtractionDiagnostic => ({ phase: 'extraction', level: 'warning', message: 'test message', suggestion: null, location: loc(), - ...overrides, + ...overrides }); // @@ -95,10 +99,10 @@ export const assert_css_not_contains = (css: string, ...patterns: Array<string>) * assert_css_order(result.base_css, 'color: blue', 'color: darkblue'); */ export const assert_css_order = (css: string, ...patterns: Array<string>): void => { - const indices = patterns.map((p) => ({pattern: p, idx: css.indexOf(p)})); + const indices = patterns.map((p) => ({ pattern: p, idx: css.indexOf(p) })); // First, check all patterns exist - for (const {pattern, idx} of indices) { + for (const { pattern, idx } of indices) { assert.isAbove(idx, -1, `Expected string to contain "${pattern}"`); } @@ -109,7 +113,7 @@ export const assert_css_order = (css: string, ...patterns: Array<string>): void assert.isBelow( current.idx, next.idx, - `Expected "${current.pattern}" to appear before "${next.pattern}"`, + `Expected "${current.pattern}" to appear before "${next.pattern}"` ); } }; @@ -132,7 +136,7 @@ export const count_css_occurrences = (css: string, pattern: string): number => { export const assert_diagnostic = ( diagnostics: Array<GenerationDiagnostic> | Array<ExtractionDiagnostic> | null, level: 'error' | 'warning', - messageContains: string, + messageContains: string ): void => { assert.isNotNull(diagnostics, 'Expected diagnostics array to exist'); const match = diagnostics.find((d) => d.level === level && d.message.includes(messageContains)); @@ -145,7 +149,7 @@ export const assert_diagnostic = ( export const assert_no_diagnostic = ( diagnostics: Array<GenerationDiagnostic> | Array<ExtractionDiagnostic> | null, level: 'error' | 'warning', - messageContains: string, + messageContains: string ): void => { if (!diagnostics) return; const match = diagnostics.find((d) => d.level === level && d.message.includes(messageContains)); @@ -157,7 +161,7 @@ export const assert_no_diagnostic = ( */ export const filter_diagnostics_by_level = <T extends GenerationDiagnostic | ExtractionDiagnostic>( diagnostics: Array<T> | null, - level: 'error' | 'warning', + level: 'error' | 'warning' ): Array<T> => { if (!diagnostics) return []; return diagnostics.filter((d) => d.level === level); @@ -167,10 +171,10 @@ export const filter_diagnostics_by_level = <T extends GenerationDiagnostic | Ext * Filters diagnostics by message content. */ export const filter_diagnostics_by_message = < - T extends GenerationDiagnostic | ExtractionDiagnostic, + T extends GenerationDiagnostic | ExtractionDiagnostic >( diagnostics: Array<T> | null, - messageContains: string, + messageContains: string ): Array<T> => { if (!diagnostics) return []; return diagnostics.filter((d) => d.message.includes(messageContains)); @@ -184,8 +188,8 @@ export const filter_diagnostics_by_message = < * Asserts resolution result is ok and has expected declaration. */ export const assert_resolved_declaration = ( - result: {ok: boolean; declaration?: string}, - expected: string, + result: { ok: boolean; declaration?: string }, + expected: string ): void => { assert.isTrue(result.ok, 'Expected resolution to succeed'); assert.strictEqual(result.declaration, expected); @@ -195,14 +199,14 @@ export const assert_resolved_declaration = ( * Asserts resolution result is error with message containing text. */ export const assert_resolution_error = ( - result: {ok: boolean; error?: {message: string}}, - messageContains: string, + result: { ok: boolean; error?: { message: string } }, + messageContains: string ): void => { assert.isFalse(result.ok, 'Expected resolution to fail'); assert.isDefined(result.error); assert.include( result.error.message, messageContains, - `Expected error message to contain "${messageContains}"`, + `Expected error message to contain "${messageContains}"` ); }; diff --git a/src/test/theme.test.ts b/src/test/theme.test.ts index 00ab16c69..6a368645c 100644 --- a/src/test/theme.test.ts +++ b/src/test/theme.test.ts @@ -1,17 +1,17 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {render_theme_style, scheme_stance_variables, type Theme} from '$lib/theme.ts'; -import {default_variables} from '$lib/variables.ts'; +import { render_theme_style, scheme_stance_variables, type Theme } from '$lib/theme.ts'; +import { default_variables } from '$lib/variables.ts'; // a scheme-adaptive default (dual slots) to observe the stance mirror through const adaptive_default = default_variables.find((v) => v.name === 'shade_lightness_00')!; /** Splits rendered CSS into the `:root` (light/base) and `:root.dark` sections. */ -const split_schemes = (css: string): {light: string; dark: string} => { +const split_schemes = (css: string): { light: string; dark: string } => { const dark_start = css.indexOf(':root.dark'); return { light: dark_start === -1 ? css : css.slice(0, dark_start), - dark: dark_start === -1 ? '' : css.slice(dark_start), + dark: dark_start === -1 ? '' : css.slice(dark_start) }; }; @@ -19,19 +19,22 @@ describe('render_theme_style', () => { test('a theme named "base" with variables renders them', () => { // the special case keys on empty `variables`, not the name, so a theme // that carries variables always renders even when named 'base' - const theme: Theme = {name: 'base', variables: [{name: 'chroma_scale', light: '2'}]}; + const theme: Theme = { name: 'base', variables: [{ name: 'chroma_scale', light: '2' }] }; const css = render_theme_style(theme); assert.include(css, '--chroma_scale: 2;'); }); test('an empty-variables theme renders nothing by default', () => { - assert.strictEqual(render_theme_style({name: 'my theme', variables: []}), ''); + assert.strictEqual(render_theme_style({ name: 'my theme', variables: [] }), ''); // the emptiness, not the name, drives the special case - assert.strictEqual(render_theme_style({name: 'base', variables: []}), ''); + assert.strictEqual(render_theme_style({ name: 'base', variables: [] }), ''); }); test('an empty-variables theme with empty_default_theme false renders the full defaults', () => { - const css = render_theme_style({name: 'anything', variables: []}, {empty_default_theme: false}); + const css = render_theme_style( + { name: 'anything', variables: [] }, + { empty_default_theme: false } + ); assert.isAbove(css.length, 0); for (const v of default_variables) { if (v.light !== undefined) assert.include(css, `--${v.name}: ${v.light};`); @@ -41,8 +44,8 @@ describe('render_theme_style', () => { describe('scheme stance', () => { test('a dark stance mirrors adaptive defaults into the base scheme', () => { - const css = render_theme_style({name: 't', variables: [], scheme: 'dark'}); - const {light} = split_schemes(css); + const css = render_theme_style({ name: 't', variables: [], scheme: 'dark' }); + const { light } = split_schemes(css); assert.include(light, 'color-scheme: dark;'); // the dark slot's value renders in the :root block assert.include(light, `--${adaptive_default.name}: ${adaptive_default.dark};`); @@ -51,30 +54,35 @@ describe('scheme stance', () => { test('a dark stance skips defaults the theme overrides', () => { const theme: Theme = { name: 't', - variables: [{name: adaptive_default.name, light: '0.5'}], - scheme: 'dark', + variables: [{ name: adaptive_default.name, light: '0.5' }], + scheme: 'dark' }; - const {light} = split_schemes(render_theme_style(theme)); + const { light } = split_schemes(render_theme_style(theme)); assert.include(light, `--${adaptive_default.name}: 0.5;`); assert.notInclude(light, `--${adaptive_default.name}: ${adaptive_default.dark};`); }); test('a light stance mirrors light values into the dark scheme', () => { - const css = render_theme_style({name: 't', variables: [], scheme: 'light'}); - const {light, dark} = split_schemes(css); + const css = render_theme_style({ name: 't', variables: [], scheme: 'light' }); + const { light, dark } = split_schemes(css); assert.include(light, 'color-scheme: light;'); // the light slot's value renders in the :root.dark block, defeating base dark assert.include(dark, `--${adaptive_default.name}: ${adaptive_default.light};`); }); test('a dual or absent scheme renders no stance', () => { - assert.strictEqual(render_theme_style({name: 't', variables: [], scheme: 'dual'}), ''); - const css = render_theme_style({name: 't', variables: [{name: 'chroma_scale', light: '2'}]}); + assert.strictEqual(render_theme_style({ name: 't', variables: [], scheme: 'dual' }), ''); + const css = render_theme_style({ + name: 't', + variables: [{ name: 'chroma_scale', light: '2' }] + }); assert.notInclude(css, 'color-scheme:'); }); test('scheme_stance_variables excludes overridden names and single-slot defaults', () => { - const mirrored = scheme_stance_variables('dark', [{name: adaptive_default.name, light: '0.5'}]); + const mirrored = scheme_stance_variables('dark', [ + { name: adaptive_default.name, light: '0.5' } + ]); assert.isAbove(mirrored.length, 0); assert.isFalse(mirrored.some((v) => v.name === adaptive_default.name)); for (const v of mirrored) { diff --git a/src/test/theme_check.compile.test.ts b/src/test/theme_check.compile.test.ts index 7141f6838..22c4eab75 100644 --- a/src/test/theme_check.compile.test.ts +++ b/src/test/theme_check.compile.test.ts @@ -1,12 +1,12 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {compile_theme} from '$lib/theme_check.ts'; -import type {Theme} from '$lib/theme.ts'; -import {default_themes} from '$lib/themes.ts'; -import {necromancer_theme} from '$lib/themes/necromancer.ts'; -import {create_terminal_theme} from '$lib/themes/terminal.ts'; -import {PALETTE_CHROMA_CAPS} from '$lib/ramps.ts'; -import type {NumericScaleVariant} from '$lib/variable_data.ts'; +import { compile_theme } from '$lib/theme_check.ts'; +import type { Theme } from '$lib/theme.ts'; +import { default_themes } from '$lib/themes.ts'; +import { necromancer_theme } from '$lib/themes/necromancer.ts'; +import { create_terminal_theme } from '$lib/themes/terminal.ts'; +import { PALETTE_CHROMA_CAPS } from '$lib/ramps.ts'; +import type { NumericScaleVariant } from '$lib/variable_data.ts'; const base_theme = default_themes[0]!; @@ -22,13 +22,13 @@ const stop_of = (name: string): NumericScaleVariant => describe('compile_theme', () => { test('the base theme emits no cap overrides', () => { - const {theme} = compile_theme(base_theme); + const { theme } = compile_theme(base_theme); assert.strictEqual(theme.variables.length, base_theme.variables.length); }); test('a single-hue terminal theme emits higher mid-stop caps', () => { const input = create_terminal_theme(145); - const {theme, report} = compile_theme(input); + const { theme, report } = compile_theme(input); const overrides = theme.variables.slice(input.variables.length); assert.isAbove(overrides.length, 0); const stop_50 = overrides.find((v) => v.name === 'palette_chroma_50'); @@ -47,13 +47,13 @@ describe('compile_theme', () => { }); test('a dark stance recomputes stale light-scheme caps', () => { - const {theme} = compile_theme(necromancer_theme); + const { theme } = compile_theme(necromancer_theme); const overrides = theme.variables.slice(necromancer_theme.variables.length); assert.isAbove(overrides.length, 0); // the stance resolves the light scheme through the dark lightness ramp, so // the baked worst-hue caps are stale and must be recomputed const changed = overrides.some( - (v) => Math.abs(cap_of(v.light) - PALETTE_CHROMA_CAPS.light[stop_of(v.name)]) > 0.002, + (v) => Math.abs(cap_of(v.light) - PALETTE_CHROMA_CAPS.light[stop_of(v.name)]) > 0.002 ); assert.isTrue(changed, 'light caps differ from the baked worst-hue table'); }); @@ -63,10 +63,10 @@ describe('compile_theme', () => { name: 'terminal pinned', variables: [ ...create_terminal_theme(145).variables, - {name: 'palette_chroma_50', light: '0.05'}, - ], + { name: 'palette_chroma_50', light: '0.05' } + ] }; - const {theme} = compile_theme(input); + const { theme } = compile_theme(input); const overrides = theme.variables.slice(input.variables.length); assert.isFalse(overrides.some((v) => v.name === 'palette_chroma_50')); }); diff --git a/src/test/theme_check.test.ts b/src/test/theme_check.test.ts index e0f0c77e4..548db94ba 100644 --- a/src/test/theme_check.test.ts +++ b/src/test/theme_check.test.ts @@ -1,23 +1,23 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {validate_theme, check_theme, resolve_theme_knob, GATE_LINK} from '$lib/theme_check.ts'; -import type {Theme} from '$lib/theme.ts'; -import {default_themes} from '$lib/themes.ts'; -import {low_contrast_theme} from '$lib/themes/low_contrast.ts'; -import {high_contrast_theme} from '$lib/themes/high_contrast.ts'; -import {necromancer_theme} from '$lib/themes/necromancer.ts'; -import {sunset_ember_theme} from '$lib/themes/sunset_ember.ts'; -import {brutalish_theme} from '$lib/themes/brutalish.ts'; -import {terminal_theme, create_terminal_theme} from '$lib/themes/terminal.ts'; +import { validate_theme, check_theme, resolve_theme_knob, GATE_LINK } from '$lib/theme_check.ts'; +import type { Theme } from '$lib/theme.ts'; +import { default_themes } from '$lib/themes.ts'; +import { low_contrast_theme } from '$lib/themes/low_contrast.ts'; +import { high_contrast_theme } from '$lib/themes/high_contrast.ts'; +import { necromancer_theme } from '$lib/themes/necromancer.ts'; +import { sunset_ember_theme } from '$lib/themes/sunset_ember.ts'; +import { brutalish_theme } from '$lib/themes/brutalish.ts'; +import { terminal_theme, create_terminal_theme } from '$lib/themes/terminal.ts'; import { PALETTE_HUES, PALETTE_CHROMA_KNOBS, SHADE_LIGHTNESS_KNOBS, palette_stop_oklch, - shade_stop_oklch, + shade_stop_oklch } from '$lib/ramps.ts'; -import {oklch_to_srgb} from '$lib/oklch.ts'; -import {wcag_contrast_ratio} from '$lib/wcag.ts'; +import { oklch_to_srgb } from '$lib/oklch.ts'; +import { wcag_contrast_ratio } from '$lib/wcag.ts'; const base_theme = default_themes[0]!; @@ -29,7 +29,7 @@ describe('validate_theme', () => { sunset_ember_theme, brutalish_theme, terminal_theme, - create_terminal_theme(70), // amber, exercises the factory + create_terminal_theme(70) // amber, exercises the factory ]; for (const theme of themes) { const errors = validate_theme(theme).filter((issue) => issue.level === 'error'); @@ -38,15 +38,18 @@ describe('validate_theme', () => { }); test('an empty theme is valid', () => { - assert.deepEqual(validate_theme({name: 'base', variables: []}), []); + assert.deepEqual(validate_theme({ name: 'base', variables: [] }), []); }); test('a blank name is an error', () => { - assert.isTrue(validate_theme({name: '', variables: []}).some((i) => i.level === 'error')); + assert.isTrue(validate_theme({ name: '', variables: [] }).some((i) => i.level === 'error')); }); test('an unknown variable name is an error', () => { - const issues = validate_theme({name: 't', variables: [{name: 'not_a_real_var', light: '1'}]}); + const issues = validate_theme({ + name: 't', + variables: [{ name: 'not_a_real_var', light: '1' }] + }); assert.isTrue(issues.some((i) => i.level === 'error' && i.variable === 'not_a_real_var')); }); @@ -54,35 +57,38 @@ describe('validate_theme', () => { // light === dark trips the schema refine const issues = validate_theme({ name: 't', - variables: [{name: 'chroma_scale', light: '1', dark: '1'}], + variables: [{ name: 'chroma_scale', light: '1', dark: '1' }] }); assert.isTrue(issues.some((i) => i.level === 'error')); }); test('an out-of-range number is a warning, not an error', () => { - const issues = validate_theme({name: 't', variables: [{name: 'chroma_scale', light: '5'}]}); + const issues = validate_theme({ name: 't', variables: [{ name: 'chroma_scale', light: '5' }] }); assert.isTrue(issues.some((i) => i.level === 'warning' && i.variable === 'chroma_scale')); assert.isFalse(issues.some((i) => i.level === 'error')); }); test('a bad enum value is a warning', () => { - const issues = validate_theme({name: 't', variables: [{name: 'border_style', light: 'wavy'}]}); + const issues = validate_theme({ + name: 't', + variables: [{ name: 'border_style', light: 'wavy' }] + }); assert.isTrue(issues.some((i) => i.level === 'warning' && i.variable === 'border_style')); assert.isFalse(issues.some((i) => i.level === 'error')); }); test('a var(--hue_X) binding on a hue knob is accepted', () => { assert.deepEqual( - validate_theme({name: 't', variables: [{name: 'hue_accent', light: 'var(--hue_d)'}]}), - [], + validate_theme({ name: 't', variables: [{ name: 'hue_accent', light: 'var(--hue_d)' }] }), + [] ); }); test('scheme stance values validate', () => { for (const scheme of ['dual', 'light', 'dark'] as const) { - assert.deepEqual(validate_theme({name: 't', variables: [], scheme}), []); + assert.deepEqual(validate_theme({ name: 't', variables: [], scheme }), []); } - const issues = validate_theme({name: 't', variables: [], scheme: 'dusk' as 'dark'}); + const issues = validate_theme({ name: 't', variables: [], scheme: 'dusk' as 'dark' }); assert.isTrue(issues.some((i) => i.level === 'error')); }); @@ -91,7 +97,7 @@ describe('validate_theme', () => { const issues = validate_theme({ name: 't', scheme, - variables: [{name: 'neutral_chroma', light: '0.02', dark: '0.03'}], + variables: [{ name: 'neutral_chroma', light: '0.02', dark: '0.03' }] }); assert.isTrue(issues.some((i) => i.level === 'warning' && i.variable === 'neutral_chroma')); assert.isFalse(issues.some((i) => i.level === 'error')); @@ -101,16 +107,16 @@ describe('validate_theme', () => { validate_theme({ name: 't', scheme: 'dark', - variables: [{name: 'neutral_chroma', light: '0.02'}], + variables: [{ name: 'neutral_chroma', light: '0.02' }] }), - [], + [] ); assert.deepEqual( validate_theme({ name: 't', - variables: [{name: 'neutral_chroma', light: '0.02', dark: '0.03'}], + variables: [{ name: 'neutral_chroma', light: '0.02', dark: '0.03' }] }), - [], + [] ); }); }); @@ -125,27 +131,27 @@ describe('resolution', () => { const theme: Theme = { name: 't', variables: [ - {name: 'hue_accent', light: 'var(--hue_d)'}, - {name: 'hue_d', light: '123'}, - ], + { name: 'hue_accent', light: 'var(--hue_d)' }, + { name: 'hue_d', light: '123' } + ] }; assert.strictEqual(resolve_theme_knob(theme, 'hue_accent', 'light'), 123); }); test('an intent follows an overridden default-bound letter', () => { - const theme: Theme = {name: 't', variables: [{name: 'hue_a', light: '99'}]}; + const theme: Theme = { name: 't', variables: [{ name: 'hue_a', light: '99' }] }; assert.strictEqual(resolve_theme_knob(theme, 'hue_accent', 'light'), 99); }); test('self and mutual cycles resolve to null without hanging', () => { - const self: Theme = {name: 't', variables: [{name: 'hue_a', light: 'var(--hue_a)'}]}; + const self: Theme = { name: 't', variables: [{ name: 'hue_a', light: 'var(--hue_a)' }] }; assert.strictEqual(resolve_theme_knob(self, 'hue_a', 'light'), null); const mutual: Theme = { name: 't', variables: [ - {name: 'hue_a', light: 'var(--hue_b)'}, - {name: 'hue_b', light: 'var(--hue_a)'}, - ], + { name: 'hue_a', light: 'var(--hue_b)' }, + { name: 'hue_b', light: 'var(--hue_a)' } + ] }; assert.strictEqual(resolve_theme_knob(mutual, 'hue_a', 'light'), null); }); @@ -154,9 +160,9 @@ describe('resolution', () => { const mutual: Theme = { name: 't', variables: [ - {name: 'hue_a', light: 'var(--hue_b)'}, - {name: 'hue_b', light: 'var(--hue_a)'}, - ], + { name: 'hue_a', light: 'var(--hue_b)' }, + { name: 'hue_b', light: 'var(--hue_a)' } + ] }; const report = check_theme(mutual); assert.isFalse(report.ok); @@ -166,7 +172,7 @@ describe('resolution', () => { test('an unresolvable calc on a lightness knob leaves the affected gates unchecked', () => { const theme: Theme = { name: 't', - variables: [{name: 'text_lightness_curve', light: 'calc(1 + 2)'}], + variables: [{ name: 'text_lightness_curve', light: 'calc(1 + 2)' }] }; assert.strictEqual(resolve_theme_knob(theme, 'text_lightness_50', 'light'), null); const report = check_theme(theme); @@ -186,12 +192,12 @@ describe('check_theme', () => { test('a gate entry matches direct numeric computation', () => { const report = check_theme(base_theme); const link = report.entries.find( - (e) => e.gate === 'contrast' && e.scheme === 'light' && e.subject === 'accent_60 on shade_00', + (e) => e.gate === 'contrast' && e.scheme === 'light' && e.subject === 'accent_60 on shade_00' ); assert(link, 'link entry exists'); const expected = wcag_contrast_ratio( oklch_to_srgb(palette_stop_oklch('a', '60', 'light')), - oklch_to_srgb(shade_stop_oklch('00', 'light')), + oklch_to_srgb(shade_stop_oklch('00', 'light')) ); assert.closeTo(link.value, expected, 1e-6); assert.strictEqual(link.threshold, GATE_LINK); @@ -225,7 +231,7 @@ describe('check_theme', () => { const contrast = report.entries.filter((e) => e.gate === 'contrast'); assert.isTrue( contrast.every((e) => e.pass), - 'lightness holds through chroma clipping', + 'lightness holds through chroma clipping' ); }); @@ -236,14 +242,14 @@ describe('check_theme', () => { const contrast = report.entries.filter((e) => e.gate === 'contrast'); assert.isTrue( contrast.every((e) => e.pass), - 'lightness holds through chroma clipping', + 'lightness holds through chroma clipping' ); }); test('a mid lightness stop pinned out of order fails monotonicity', () => { - const theme: Theme = {name: 't', variables: [{name: 'shade_lightness_50', light: '0.99'}]}; + const theme: Theme = { name: 't', variables: [{ name: 'shade_lightness_50', light: '0.99' }] }; const entry = check_theme(theme).entries.find( - (e) => e.gate === 'monotonicity' && e.scheme === 'light' && e.subject === 'shade_lightness', + (e) => e.gate === 'monotonicity' && e.scheme === 'light' && e.subject === 'shade_lightness' ); assert(entry, 'monotonicity entry exists'); assert.isFalse(entry.pass); @@ -252,31 +258,31 @@ describe('check_theme', () => { describe('scheme stance', () => { test('a dark stance resolves light-scheme knobs to the dark defaults', () => { - const theme: Theme = {name: 't', variables: [], scheme: 'dark'}; + const theme: Theme = { name: 't', variables: [], scheme: 'dark' }; assert.strictEqual( resolve_theme_knob(theme, 'shade_lightness_00', 'light'), - SHADE_LIGHTNESS_KNOBS.dark.lightness_00, + SHADE_LIGHTNESS_KNOBS.dark.lightness_00 ); assert.strictEqual( resolve_theme_knob(theme, 'palette_chroma_max', 'light'), - PALETTE_CHROMA_KNOBS.dark.chroma_max, + PALETTE_CHROMA_KNOBS.dark.chroma_max ); }); test('an authored value beats the stance mirror', () => { const theme: Theme = { name: 't', - variables: [{name: 'shade_lightness_00', light: '0.5'}], - scheme: 'dark', + variables: [{ name: 'shade_lightness_00', light: '0.5' }], + scheme: 'dark' }; assert.strictEqual(resolve_theme_knob(theme, 'shade_lightness_00', 'light'), 0.5); }); test('a dark stance evaluates the same reality in both schemes', () => { - const report = check_theme({name: 't', variables: [], scheme: 'dark'}); + const report = check_theme({ name: 't', variables: [], scheme: 'dark' }); assert.isTrue(report.ok, JSON.stringify(report.entries.filter((e) => !e.pass))); const dark_by_subject = new Map( - report.entries.filter((e) => e.scheme === 'dark').map((e) => [`${e.gate}|${e.subject}`, e]), + report.entries.filter((e) => e.scheme === 'dark').map((e) => [`${e.gate}|${e.subject}`, e]) ); for (const entry of report.entries) { if (entry.scheme !== 'light') continue; @@ -291,7 +297,7 @@ describe('scheme stance', () => { const contrast = check_theme(theme).entries.filter((e) => e.gate === 'contrast'); assert.isTrue( contrast.every((e) => e.pass), - `${theme.name}: ${JSON.stringify(contrast.filter((e) => !e.pass))}`, + `${theme.name}: ${JSON.stringify(contrast.filter((e) => !e.pass))}` ); } }); diff --git a/src/test/themes.test.ts b/src/test/themes.test.ts index 5e18f5839..c3c6b87e6 100644 --- a/src/test/themes.test.ts +++ b/src/test/themes.test.ts @@ -1,14 +1,14 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import type {Theme} from '$lib/theme.ts'; -import {default_themes, DEFAULT_THEME} from '$lib/themes.ts'; -import {create_terminal_theme} from '$lib/themes/terminal.ts'; -import {StyleVariable} from '$lib/variable.ts'; -import {validate_theme} from '$lib/theme_check.ts'; +import type { Theme } from '$lib/theme.ts'; +import { default_themes, DEFAULT_THEME } from '$lib/themes.ts'; +import { create_terminal_theme } from '$lib/themes/terminal.ts'; +import { StyleVariable } from '$lib/variable.ts'; +import { validate_theme } from '$lib/theme_check.ts'; // every theme module ships from themes/, so discover them by glob — a new // module can't silently skip validation by being left off a hand-list -const theme_modules = import.meta.glob('../lib/themes/*.ts', {eager: true}); +const theme_modules = import.meta.glob('../lib/themes/*.ts', { eager: true }); const is_theme = (value: unknown): value is Theme => value !== null && @@ -19,7 +19,7 @@ const is_theme = (value: unknown): value is Theme => /** Every theme exported from a `themes/` module, registry and exemplar alike. */ const shipped_themes: Array<Theme> = Object.values(theme_modules).flatMap((mod) => - Object.values(mod as Record<string, unknown>).filter(is_theme), + Object.values(mod as Record<string, unknown>).filter(is_theme) ); const registry_names = new Set(default_themes.map((t) => t.name)); @@ -27,7 +27,7 @@ const registry_names = new Set(default_themes.map((t) => t.name)); /** Shipped exemplar themes that deliberately stay out of the registry. */ const exemplar_themes = [ ...shipped_themes.filter((t) => !registry_names.has(t.name)), - create_terminal_theme(70), // amber, exercises the factory + create_terminal_theme(70) // amber, exercises the factory ]; describe('default_themes', () => { @@ -67,7 +67,7 @@ describe('default_themes', () => { assert.isAbove( theme.variables.length, 0, - `Theme "${theme.name}" should have at least one variable`, + `Theme "${theme.name}" should have at least one variable` ); } }); diff --git a/src/test/variable.test.ts b/src/test/variable.test.ts index 98c030757..ebe40c3e0 100644 --- a/src/test/variable.test.ts +++ b/src/test/variable.test.ts @@ -1,34 +1,34 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; -import {StyleVariable, StyleVariableName} from '$lib/variable.ts'; +import { StyleVariable, StyleVariableName } from '$lib/variable.ts'; describe('StyleVariable', () => { describe('valid schemas', () => { test('validates light-only', () => { - const result = StyleVariable.safeParse({name: 'foo', light: '10px'}); + const result = StyleVariable.safeParse({ name: 'foo', light: '10px' }); assert.isTrue(result.success); }); test('validates dark-only', () => { - const result = StyleVariable.safeParse({name: 'foo', dark: '10px'}); + const result = StyleVariable.safeParse({ name: 'foo', dark: '10px' }); assert.isTrue(result.success); }); test('validates different light and dark', () => { - const result = StyleVariable.safeParse({name: 'foo', light: '10px', dark: '20px'}); + const result = StyleVariable.safeParse({ name: 'foo', light: '10px', dark: '20px' }); assert.isTrue(result.success); }); }); describe('invalid schemas', () => { test('rejects missing light and dark', () => { - const result = StyleVariable.safeParse({name: 'foo'}); + const result = StyleVariable.safeParse({ name: 'foo' }); assert.isFalse(result.success); assert.isTrue(result.error.issues.some((i) => i.message.includes('at least one'))); }); test('rejects identical light and dark', () => { - const result = StyleVariable.safeParse({name: 'foo', light: '10px', dark: '10px'}); + const result = StyleVariable.safeParse({ name: 'foo', light: '10px', dark: '10px' }); assert.isFalse(result.success); assert.isTrue(result.error.issues.some((i) => i.message.includes('must differ'))); }); @@ -47,7 +47,7 @@ describe('StyleVariableName', () => { ['123abc', 'starts with digit'], ['foo-bar', 'contains hyphen'], ['', 'empty string'], - ['foo bar', 'contains space'], + ['foo bar', 'contains space'] ])('rejects invalid name "%s" (%s)', (name) => { assert.isFalse(StyleVariableName.safeParse(name).success); }); diff --git a/src/test/variable_graph.test.ts b/src/test/variable_graph.test.ts index fb6b390b6..abb025b1f 100644 --- a/src/test/variable_graph.test.ts +++ b/src/test/variable_graph.test.ts @@ -1,4 +1,4 @@ -import {test, assert, describe} from 'vitest'; +import { test, assert, describe } from 'vitest'; import { build_variable_graph, @@ -7,15 +7,15 @@ import { generate_theme_css, get_all_variable_names, has_variable, - find_similar_variable, + find_similar_variable } from '$lib/variable_graph.ts'; -import type {StyleVariable} from '$lib/variable.ts'; +import type { StyleVariable } from '$lib/variable.ts'; describe('build_variable_graph', () => { describe('basic building', () => { test('builds graph with basic variable', () => { const variables: Array<StyleVariable> = [ - {name: 'palette_a', light: 'blue', dark: 'lightblue'}, + { name: 'palette_a', light: 'blue', dark: 'lightblue' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -28,8 +28,8 @@ describe('build_variable_graph', () => { test('extracts dependencies from var() references', () => { const variables: Array<StyleVariable> = [ - {name: 'hue_a', light: '210'}, - {name: 'palette_a', light: 'hsl(var(--hue_a) 50% 50%)'}, + { name: 'hue_a', light: '210' }, + { name: 'palette_a', light: 'hsl(var(--hue_a) 50% 50%)' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -41,7 +41,7 @@ describe('build_variable_graph', () => { describe('light/dark values', () => { test('handles light-only variable', () => { - const variables: Array<StyleVariable> = [{name: 'spacing', light: '16px'}]; + const variables: Array<StyleVariable> = [{ name: 'spacing', light: '16px' }]; const graph = build_variable_graph(variables, 'test-hash'); @@ -51,7 +51,7 @@ describe('build_variable_graph', () => { }); test('handles dark-only variable', () => { - const variables: Array<StyleVariable> = [{name: 'shadow', dark: 'none'}]; + const variables: Array<StyleVariable> = [{ name: 'shadow', dark: 'none' }]; const graph = build_variable_graph(variables, 'test-hash'); @@ -61,9 +61,9 @@ describe('build_variable_graph', () => { test('handles different light/dark dependencies', () => { const variables: Array<StyleVariable> = [ - {name: 'base_light', light: '100'}, - {name: 'base_dark', dark: '200'}, - {name: 'composite', light: 'var(--base_light)', dark: 'var(--base_dark)'}, + { name: 'base_light', light: '100' }, + { name: 'base_dark', dark: '200' }, + { name: 'composite', light: 'var(--base_light)', dark: 'var(--base_dark)' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -80,8 +80,8 @@ describe('resolve_variables_transitive', () => { describe('basic resolution', () => { test('resolves single variable', () => { const variables: Array<StyleVariable> = [ - {name: 'a', light: '1'}, - {name: 'b', light: '2'}, + { name: 'a', light: '1' }, + { name: 'b', light: '2' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -94,8 +94,8 @@ describe('resolve_variables_transitive', () => { test('includes direct dependencies', () => { const variables: Array<StyleVariable> = [ - {name: 'hue', light: '210'}, - {name: 'color', light: 'hsl(var(--hue) 50% 50%)'}, + { name: 'hue', light: '210' }, + { name: 'color', light: 'hsl(var(--hue) 50% 50%)' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -107,9 +107,9 @@ describe('resolve_variables_transitive', () => { test('resolves multiple initial variables', () => { const variables: Array<StyleVariable> = [ - {name: 'a', light: '1'}, - {name: 'b', light: '2'}, - {name: 'c', light: '3'}, + { name: 'a', light: '1' }, + { name: 'b', light: '2' }, + { name: 'c', light: '3' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -124,10 +124,10 @@ describe('resolve_variables_transitive', () => { describe('deep dependency chains', () => { test('resolves deep chain (A→B→C→D)', () => { const variables: Array<StyleVariable> = [ - {name: 'a', light: '1'}, - {name: 'b', light: 'var(--a)'}, - {name: 'c', light: 'var(--b)'}, - {name: 'd', light: 'var(--c)'}, + { name: 'a', light: '1' }, + { name: 'b', light: 'var(--a)' }, + { name: 'c', light: 'var(--b)' }, + { name: 'd', light: 'var(--c)' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -141,10 +141,10 @@ describe('resolve_variables_transitive', () => { test('resolves sibling dependencies', () => { const variables: Array<StyleVariable> = [ - {name: 'd', light: 'base-value'}, - {name: 'b', light: 'var(--d)'}, - {name: 'c', light: 'independent-value'}, - {name: 'a', light: 'calc(var(--b) + var(--c))'}, + { name: 'd', light: 'base-value' }, + { name: 'b', light: 'var(--d)' }, + { name: 'c', light: 'independent-value' }, + { name: 'a', light: 'calc(var(--b) + var(--c))' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -160,13 +160,13 @@ describe('resolve_variables_transitive', () => { test('handles complex tree with shared nodes', () => { // Diamond dependency: root → a,b → d ← (shared) const variables: Array<StyleVariable> = [ - {name: 'f', light: 'leaf-f'}, - {name: 'c', light: 'leaf-c'}, - {name: 'e', light: 'leaf-e'}, - {name: 'd', light: 'var(--f)'}, - {name: 'a', light: 'calc(var(--c) + var(--d))'}, - {name: 'b', light: 'calc(var(--d) + var(--e))'}, - {name: 'root', light: 'calc(var(--a) + var(--b))'}, + { name: 'f', light: 'leaf-f' }, + { name: 'c', light: 'leaf-c' }, + { name: 'e', light: 'leaf-e' }, + { name: 'd', light: 'var(--f)' }, + { name: 'a', light: 'calc(var(--c) + var(--d))' }, + { name: 'b', light: 'calc(var(--d) + var(--e))' }, + { name: 'root', light: 'calc(var(--a) + var(--b))' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -184,9 +184,9 @@ describe('resolve_variables_transitive', () => { test('multiple starting points share dependencies', () => { const variables: Array<StyleVariable> = [ - {name: 'shared', light: 'shared-value'}, - {name: 'x', light: 'var(--shared)'}, - {name: 'y', light: 'var(--shared)'}, + { name: 'shared', light: 'shared-value' }, + { name: 'x', light: 'var(--shared)' }, + { name: 'y', light: 'var(--shared)' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -202,9 +202,9 @@ describe('resolve_variables_transitive', () => { describe('light/dark dependency resolution', () => { test('includes both light and dark deps', () => { const variables: Array<StyleVariable> = [ - {name: 'light_base', light: 'white'}, - {name: 'dark_base', dark: 'black'}, - {name: 'combo', light: 'var(--light_base)', dark: 'var(--dark_base)'}, + { name: 'light_base', light: 'white' }, + { name: 'dark_base', dark: 'black' }, + { name: 'combo', light: 'var(--light_base)', dark: 'var(--dark_base)' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -217,11 +217,11 @@ describe('resolve_variables_transitive', () => { test('resolves mixed light/dark dependencies at multiple depths', () => { const variables: Array<StyleVariable> = [ - {name: 'light_leaf', light: '#fff'}, - {name: 'dark_leaf', dark: '#000'}, - {name: 'light_mid', light: 'var(--light_leaf)'}, - {name: 'dark_mid', dark: 'var(--dark_leaf)'}, - {name: 'themed', light: 'var(--light_mid)', dark: 'var(--dark_mid)'}, + { name: 'light_leaf', light: '#fff' }, + { name: 'dark_leaf', dark: '#000' }, + { name: 'light_mid', light: 'var(--light_leaf)' }, + { name: 'dark_mid', dark: 'var(--dark_leaf)' }, + { name: 'themed', light: 'var(--light_mid)', dark: 'var(--dark_mid)' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -239,10 +239,10 @@ describe('resolve_variables_transitive', () => { describe('var() with fallbacks', () => { test('resolves nested var() fallbacks', () => { const variables: Array<StyleVariable> = [ - {name: 'a', light: '1'}, - {name: 'b', light: '2'}, - {name: 'c', light: '3'}, - {name: 'composed', light: 'var(--a, var(--b, var(--c)))'}, + { name: 'a', light: '1' }, + { name: 'b', light: '2' }, + { name: 'c', light: '3' }, + { name: 'composed', light: 'var(--a, var(--b, var(--c)))' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -257,7 +257,7 @@ describe('resolve_variables_transitive', () => { describe('missing variables', () => { test('tracks missing variables', () => { - const variables: Array<StyleVariable> = [{name: 'known', light: '1'}]; + const variables: Array<StyleVariable> = [{ name: 'known', light: '1' }]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['known', 'unknown']); @@ -270,14 +270,14 @@ describe('resolve_variables_transitive', () => { }); test('tracks multiple missing variables', () => { - const variables: Array<StyleVariable> = [{name: 'exists', light: '1'}]; + const variables: Array<StyleVariable> = [{ name: 'exists', light: '1' }]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, [ 'exists', 'missing1', 'missing2', - 'missing3', + 'missing3' ]); assert.strictEqual(result.missing.size, 3); @@ -287,7 +287,7 @@ describe('resolve_variables_transitive', () => { }); test('tracks missing dependencies', () => { - const variables: Array<StyleVariable> = [{name: 'root', light: 'var(--missing_dep)'}]; + const variables: Array<StyleVariable> = [{ name: 'root', light: 'var(--missing_dep)' }]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['root']); @@ -300,8 +300,8 @@ describe('resolve_variables_transitive', () => { test('empty missing set when all exist', () => { const variables: Array<StyleVariable> = [ - {name: 'a', light: '1'}, - {name: 'b', light: 'var(--a)'}, + { name: 'a', light: '1' }, + { name: 'b', light: 'var(--a)' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -316,29 +316,29 @@ describe('resolve_variables_transitive', () => { [ 'simple cycle (A→B→A)', [ - {name: 'a', light: 'var(--b)'}, - {name: 'b', light: 'var(--a)'}, + { name: 'a', light: 'var(--b)' }, + { name: 'b', light: 'var(--a)' } ], - ['a', 'b'], + ['a', 'b'] ], [ 'longer cycle (A→B→C→A)', [ - {name: 'a', light: 'var(--b)'}, - {name: 'b', light: 'var(--c)'}, - {name: 'c', light: 'var(--a)'}, + { name: 'a', light: 'var(--b)' }, + { name: 'b', light: 'var(--c)' }, + { name: 'c', light: 'var(--a)' } ], - ['a', 'b', 'c'], + ['a', 'b', 'c'] ], - ['self-reference (A→A)', [{name: 'a', light: 'calc(var(--a) + 1px)'}], ['a']], + ['self-reference (A→A)', [{ name: 'a', light: 'calc(var(--a) + 1px)' }], ['a']], [ 'cycle in dark mode deps only', [ - {name: 'a', light: 'blue', dark: 'var(--b)'}, - {name: 'b', light: 'red', dark: 'var(--a)'}, + { name: 'a', light: 'blue', dark: 'var(--b)' }, + { name: 'b', light: 'red', dark: 'var(--a)' } ], - ['a', 'b'], - ], + ['a', 'b'] + ] ])('detects %s', (_name, variables, expected_vars) => { const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['a']); @@ -352,7 +352,7 @@ describe('resolve_variables_transitive', () => { test('self-reference in both light and dark triggers cycle warning', () => { const variables: Array<StyleVariable> = [ - {name: 'x', light: 'var(--x, 1)', dark: 'var(--x, 2)'}, + { name: 'x', light: 'var(--x, 1)', dark: 'var(--x, 2)' } ]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['x']); @@ -366,12 +366,12 @@ describe('resolve_variables_transitive', () => { describe('deep fallback chains', () => { test('resolves 5-level nested fallbacks', () => { const variables: Array<StyleVariable> = [ - {name: 'a', light: '1'}, - {name: 'b', light: '2'}, - {name: 'c', light: '3'}, - {name: 'd', light: '4'}, - {name: 'e', light: '5'}, - {name: 'deep', light: 'var(--a, var(--b, var(--c, var(--d, var(--e)))))'}, + { name: 'a', light: '1' }, + { name: 'b', light: '2' }, + { name: 'c', light: '3' }, + { name: 'd', light: '4' }, + { name: 'e', light: '5' }, + { name: 'deep', light: 'var(--a, var(--b, var(--c, var(--d, var(--e)))))' } ]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['deep']); @@ -384,13 +384,13 @@ describe('resolve_variables_transitive', () => { test('6-level chain with mixed light/dark', () => { const variables: Array<StyleVariable> = [ - {name: 'l1', light: 'white'}, - {name: 'l2', light: 'var(--l1)'}, - {name: 'l3', light: 'var(--l2)'}, - {name: 'd1', dark: 'black'}, - {name: 'd2', dark: 'var(--d1)'}, - {name: 'd3', dark: 'var(--d2)'}, - {name: 'themed', light: 'var(--l3)', dark: 'var(--d3)'}, + { name: 'l1', light: 'white' }, + { name: 'l2', light: 'var(--l1)' }, + { name: 'l3', light: 'var(--l2)' }, + { name: 'd1', dark: 'black' }, + { name: 'd2', dark: 'var(--d1)' }, + { name: 'd3', dark: 'var(--d2)' }, + { name: 'themed', light: 'var(--l3)', dark: 'var(--d3)' } ]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['themed']); @@ -410,8 +410,8 @@ describe('resolve_variables_transitive', () => { test('fallback with missing intermediate', () => { // a depends on b which doesn't exist, c is fallback const variables: Array<StyleVariable> = [ - {name: 'c', light: 'fallback-value'}, - {name: 'a', light: 'var(--b, var(--c))'}, + { name: 'c', light: 'fallback-value' }, + { name: 'a', light: 'var(--b, var(--c))' } ]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['a']); @@ -426,9 +426,9 @@ describe('resolve_variables_transitive', () => { describe('calc() with multiple var() references', () => { test('resolves calc with two var references', () => { const variables: Array<StyleVariable> = [ - {name: 'width', light: '100px'}, - {name: 'padding', light: '20px'}, - {name: 'content_width', light: 'calc(var(--width) - var(--padding) * 2)'}, + { name: 'width', light: '100px' }, + { name: 'padding', light: '20px' }, + { name: 'content_width', light: 'calc(var(--width) - var(--padding) * 2)' } ]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['content_width']); @@ -441,8 +441,8 @@ describe('resolve_variables_transitive', () => { test('resolves calc with mixed var and fallback', () => { const variables: Array<StyleVariable> = [ - {name: 'base', light: '10px'}, - {name: 'computed', light: 'calc(var(--base) + var(--missing, 5px))'}, + { name: 'base', light: '10px' }, + { name: 'computed', light: 'calc(var(--base) + var(--missing, 5px))' } ]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['computed']); @@ -455,10 +455,10 @@ describe('resolve_variables_transitive', () => { test('resolves nested calc expressions', () => { const variables: Array<StyleVariable> = [ - {name: 'a', light: '10'}, - {name: 'b', light: '20'}, - {name: 'c', light: '30'}, - {name: 'nested', light: 'calc(var(--a) + calc(var(--b) * var(--c)))'}, + { name: 'a', light: '10' }, + { name: 'b', light: '20' }, + { name: 'c', light: '30' }, + { name: 'nested', light: 'calc(var(--a) + calc(var(--b) * var(--c)))' } ]; const graph = build_variable_graph(variables, 'test-hash'); const result = resolve_variables_transitive(graph, ['nested']); @@ -473,10 +473,10 @@ describe('resolve_variables_transitive', () => { describe('generate_theme_css', () => { describe('basic output', () => { test('generates light and dark blocks', () => { - const variables: Array<StyleVariable> = [{name: 'color', light: 'blue', dark: 'lightblue'}]; + const variables: Array<StyleVariable> = [{ name: 'color', light: 'blue', dark: 'lightblue' }]; const graph = build_variable_graph(variables, 'test-hash'); - const {light_css, dark_css} = generate_theme_css(graph, new Set(['color'])); + const { light_css, dark_css } = generate_theme_css(graph, new Set(['color'])); assert.include(light_css, ':root'); assert.include(light_css, '--color: blue;'); @@ -485,10 +485,10 @@ describe('generate_theme_css', () => { }); test('renders single :root scopes', () => { - const variables: Array<StyleVariable> = [{name: 'color', light: 'blue', dark: 'lightblue'}]; + const variables: Array<StyleVariable> = [{ name: 'color', light: 'blue', dark: 'lightblue' }]; const graph = build_variable_graph(variables, 'test-hash'); - const {light_css, dark_css} = generate_theme_css(graph, new Set(['color'])); + const { light_css, dark_css } = generate_theme_css(graph, new Set(['color'])); assert.include(light_css, ':root {'); assert.include(dark_css, ':root.dark {'); @@ -497,20 +497,20 @@ describe('generate_theme_css', () => { describe('light/dark only variables', () => { test('light-only produces only light block', () => { - const variables: Array<StyleVariable> = [{name: 'spacing', light: '16px'}]; + const variables: Array<StyleVariable> = [{ name: 'spacing', light: '16px' }]; const graph = build_variable_graph(variables, 'test-hash'); - const {light_css, dark_css} = generate_theme_css(graph, new Set(['spacing'])); + const { light_css, dark_css } = generate_theme_css(graph, new Set(['spacing'])); assert.include(light_css, '--spacing: 16px;'); assert.strictEqual(dark_css, ''); }); test('dark-only produces only dark block', () => { - const variables: Array<StyleVariable> = [{name: 'glow', dark: 'none'}]; + const variables: Array<StyleVariable> = [{ name: 'glow', dark: 'none' }]; const graph = build_variable_graph(variables, 'test-hash'); - const {light_css, dark_css} = generate_theme_css(graph, new Set(['glow'])); + const { light_css, dark_css } = generate_theme_css(graph, new Set(['glow'])); assert.strictEqual(light_css, ''); assert.include(dark_css, '--glow: none;'); @@ -520,13 +520,13 @@ describe('generate_theme_css', () => { describe('sorting', () => { test('outputs variables in sorted order', () => { const variables: Array<StyleVariable> = [ - {name: 'zebra', light: '3'}, - {name: 'alpha', light: '1'}, - {name: 'mid', light: '2'}, + { name: 'zebra', light: '3' }, + { name: 'alpha', light: '1' }, + { name: 'mid', light: '2' } ]; const graph = build_variable_graph(variables, 'test-hash'); - const {light_css} = generate_theme_css(graph, new Set(['zebra', 'alpha', 'mid'])); + const { light_css } = generate_theme_css(graph, new Set(['zebra', 'alpha', 'mid'])); const alpha_pos = light_css.indexOf('--alpha'); const mid_pos = light_css.indexOf('--mid'); @@ -541,9 +541,9 @@ describe('generate_theme_css', () => { describe('utility functions', () => { test('get_all_variable_names returns all names', () => { const variables: Array<StyleVariable> = [ - {name: 'a', light: '1'}, - {name: 'b', light: '2'}, - {name: 'c', light: '3'}, + { name: 'a', light: '1' }, + { name: 'b', light: '2' }, + { name: 'c', light: '3' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -556,7 +556,7 @@ describe('utility functions', () => { }); test('has_variable checks existence', () => { - const variables: Array<StyleVariable> = [{name: 'exists', light: '1'}]; + const variables: Array<StyleVariable> = [{ name: 'exists', light: '1' }]; const graph = build_variable_graph(variables, 'test-hash'); assert.isTrue(has_variable(graph, 'exists')); @@ -601,9 +601,14 @@ describe('build_variable_graph_from_options', () => { describe('find_similar_variable', () => { describe('finds typos', () => { test.each<[string, string, Array<StyleVariable>, string]>([ - ['color_primry', 'color_primary', [{name: 'color_primary', light: 'blue'}], 'missing char'], - ['backuground', 'background', [{name: 'background', light: '#fff'}], 'extra char'], - ['boarder_radius', 'border_radius', [{name: 'border_radius', light: '4px'}], 'swapped chars'], + ['color_primry', 'color_primary', [{ name: 'color_primary', light: 'blue' }], 'missing char'], + ['backuground', 'background', [{ name: 'background', light: '#fff' }], 'extra char'], + [ + 'boarder_radius', + 'border_radius', + [{ name: 'border_radius', light: '4px' }], + 'swapped chars' + ] ])('%s → %s (%s)', (typo, expected, variables) => { const graph = build_variable_graph(variables, 'test-hash'); assert.strictEqual(find_similar_variable(graph, typo), expected); @@ -613,8 +618,8 @@ describe('find_similar_variable', () => { describe('returns null for dissimilar', () => { test.each([['fill'], ['shadow'], ['icon_size']])('%s has no match', (name) => { const variables: Array<StyleVariable> = [ - {name: 'color_primary', light: 'blue'}, - {name: 'spacing_md', light: '16px'}, + { name: 'color_primary', light: 'blue' }, + { name: 'spacing_md', light: '16px' } ]; const graph = build_variable_graph(variables, 'test-hash'); assert.isNull(find_similar_variable(graph, name)); @@ -628,9 +633,9 @@ describe('find_similar_variable', () => { test('finds best match among multiple similar variables', () => { const variables: Array<StyleVariable> = [ - {name: 'palette_a_1', light: '1'}, - {name: 'palette_a_2', light: '2'}, - {name: 'palette_a_3', light: '3'}, + { name: 'palette_a_1', light: '1' }, + { name: 'palette_a_2', light: '2' }, + { name: 'palette_a_3', light: '3' } ]; const graph = build_variable_graph(variables, 'test-hash'); @@ -641,8 +646,8 @@ describe('find_similar_variable', () => { test('returns null for short dissimilar strings', () => { const variables: Array<StyleVariable> = [ - {name: 'shadow_xs', light: '1px'}, - {name: 'shadow_sm', light: '2px'}, + { name: 'shadow_xs', light: '1px' }, + { name: 'shadow_sm', light: '2px' } ]; const graph = build_variable_graph(variables, 'test-hash'); assert.isNull(find_similar_variable(graph, 'shadow')); diff --git a/src/test/variables.test.ts b/src/test/variables.test.ts index 2b07973f1..771c4c1f3 100644 --- a/src/test/variables.test.ts +++ b/src/test/variables.test.ts @@ -1,15 +1,15 @@ -import {test, assert} from 'vitest'; +import { test, assert } from 'vitest'; -import {default_variables} from '$lib/variables.ts'; +import { default_variables } from '$lib/variables.ts'; import * as exported_variables from '$lib/variables.ts'; -import {StyleVariable} from '$lib/variable.ts'; +import { StyleVariable } from '$lib/variable.ts'; test('all variables pass schema validation', () => { for (const v of default_variables) { const result = StyleVariable.safeParse(v); assert.isTrue( result.success, - `variable "${v.name}" failed validation: ${JSON.stringify(result.error?.issues)}`, + `variable "${v.name}" failed validation: ${JSON.stringify(result.error?.issues)}` ); } }); @@ -19,7 +19,7 @@ test('variables have no duplicates', () => { for (const v of default_variables) { assert.isFalse( names.has(v.name), - `variable "${v.name}" is duplicated in \`default_variables\``, + `variable "${v.name}" is duplicated in \`default_variables\`` ); names.add(v.name); } @@ -29,7 +29,7 @@ test('variable names match their identifiers', () => { for (const v of default_variables) { assert.isTrue( v.name in exported_variables, - `default variable with name "${v.name}" has no matching exported identifier`, + `default variable with name "${v.name}" has no matching exported identifier` ); } }); @@ -41,11 +41,11 @@ test('variable identifiers are all included in `default_variables`', () => { assert.strictEqual( identifier, exported.name, - `variable identifier "${identifier}" does not match its name ${exported.name}`, + `variable identifier "${identifier}" does not match its name ${exported.name}` ); assert.isTrue( default_variables.some((v) => v.name === identifier), - `exported variable with identifier "${identifier}" is not included in \`default_variables\``, + `exported variable with identifier "${identifier}" is not included in \`default_variables\`` ); } }); diff --git a/src/test/vite_plugin_examples.test.ts b/src/test/vite_plugin_examples.test.ts index 8920b145c..b774846a6 100644 --- a/src/test/vite_plugin_examples.test.ts +++ b/src/test/vite_plugin_examples.test.ts @@ -13,13 +13,13 @@ * @module */ -import {test, assert, describe, beforeAll} from 'vitest'; -import {join} from 'node:path'; -import {readdir, readFile} from 'node:fs/promises'; -import {execSync} from 'node:child_process'; -import {to_error_message} from '@fuzdev/fuz_util/error.ts'; +import { test, assert, describe, beforeAll } from 'vitest'; +import { join } from 'node:path'; +import { readdir, readFile } from 'node:fs/promises'; +import { execSync } from 'node:child_process'; +import { to_error_message } from '@fuzdev/fuz_util/error.ts'; -import {FUZ_CSS_BANNER} from '$lib/vite_plugin_fuz_css.ts'; +import { FUZ_CSS_BANNER } from '$lib/vite_plugin_fuz_css.ts'; // Skip if SKIP_EXAMPLE_TESTS is set const SKIP = !!process.env.SKIP_EXAMPLE_TESTS; @@ -29,12 +29,12 @@ const EXAMPLES_DIR = join(process.cwd(), 'examples'); // Build the package before running example tests so examples get fresh dist/ if (!SKIP) { beforeAll(async () => { - execSync('npx svelte-package', {cwd: process.cwd(), stdio: 'pipe'}); + execSync('npx svelte-package', { cwd: process.cwd(), stdio: 'pipe' }); // `svelte-package` leaves relative `.ts` specifiers in `dist`; with // `rewriteRelativeImportExtensions: false` tsc no longer rewrites them, so the // gro dist-rewrite pass (which `gro build` runs after svelte-package) must run // here too — otherwise the examples can't resolve the freshly-packaged dist. - const {rewrite_dist_imports} = await import('@fuzdev/gro/dist_rewrite_imports.ts'); + const { rewrite_dist_imports } = await import('@fuzdev/gro/dist_rewrite_imports.ts'); await rewrite_dist_imports(join(process.cwd(), 'dist')); }, 60_000); // 1 minute timeout for package build } @@ -112,7 +112,7 @@ const EXPECTED_CLASSES = [ 'hover:border_g_50', 'hover:outline_g_50', 'active:border_h_50', - 'active:outline_h_50', + 'active:outline_h_50' ].sort(); /** @@ -136,8 +136,8 @@ const find_css_file = async (dist_dir: string): Promise<string | null> => { const build_example = (example_name: string): void => { const example_dir = join(EXAMPLES_DIR, example_name); try { - execSync('npm install', {cwd: example_dir, stdio: 'pipe'}); - execSync('npm run build', {cwd: example_dir, stdio: 'pipe'}); + execSync('npm install', { cwd: example_dir, stdio: 'pipe' }); + execSync('npm run build', { cwd: example_dir, stdio: 'pipe' }); } catch (err) { const message = to_error_message(err); throw new Error(`Failed to build ${example_name}: ${message}`); @@ -268,7 +268,7 @@ describe.skipIf(SKIP)('cross-example consistency', () => { assert.deepEqual( classes, first_classes, - `${example} should produce same classes as ${first_example}`, + `${example} should produce same classes as ${first_example}` ); } }); diff --git a/src/test/vite_plugin_fuz_css.dev.test.ts b/src/test/vite_plugin_fuz_css.dev.test.ts index 938eef845..f0e1f08c7 100644 --- a/src/test/vite_plugin_fuz_css.dev.test.ts +++ b/src/test/vite_plugin_fuz_css.dev.test.ts @@ -1,10 +1,10 @@ -import {describe, test, assert, afterAll} from 'vitest'; -import {createServer, type ViteDevServer} from 'vite'; -import {fileURLToPath} from 'node:url'; -import {dirname, join} from 'node:path'; -import {rm} from 'node:fs/promises'; +import { describe, test, assert, afterAll } from 'vitest'; +import { createServer, type ViteDevServer } from 'vite'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; +import { rm } from 'node:fs/promises'; -import {vite_plugin_fuz_css, type VitePluginFuzCssOptions} from '$lib/vite_plugin_fuz_css.ts'; +import { vite_plugin_fuz_css, type VitePluginFuzCssOptions } from '$lib/vite_plugin_fuz_css.ts'; const fixture_root = join(dirname(fileURLToPath(import.meta.url)), 'fixtures/vite_dev'); @@ -20,14 +20,14 @@ const create_dev_server = (options?: VitePluginFuzCssOptions): Promise<ViteDevSe // middlewareMode avoids binding an http port; `ws: false` avoids the // standalone HMR websocket server (the plugin's connection listener // registers against Vite's noop ws stub). - server: {middlewareMode: true, ws: false}, - optimizeDeps: {noDiscovery: true}, - plugins: [vite_plugin_fuz_css({filter_file: filter_fixture_file, ...options})], + server: { middlewareMode: true, ws: false }, + optimizeDeps: { noDiscovery: true }, + plugins: [vite_plugin_fuz_css({ filter_file: filter_fixture_file, ...options })] }); afterAll(async () => { // The plugin's extraction cache defaults to `.fuz/cache/css` under the Vite root. - await rm(join(fixture_root, '.fuz'), {recursive: true, force: true}); + await rm(join(fixture_root, '.fuz'), { recursive: true, force: true }); }); describe('vite_plugin_fuz_css dev pre-scan', () => { @@ -42,7 +42,7 @@ describe('vite_plugin_fuz_css dev pre-scan', () => { assert(result.code.includes('.box'), 'includes composite classes from src/page.html'); assert( result.code.includes('.gap_lg'), - 'includes classes from the never-imported src/island.html', + 'includes classes from the never-imported src/island.html' ); assert(!result.code.includes('.mt_lg'), 'extra/ is not scanned by default'); } finally { @@ -51,7 +51,7 @@ describe('vite_plugin_fuz_css dev pre-scan', () => { }); test('prescan: false leaves the first serve without utility classes', async () => { - const server = await create_dev_server({prescan: false}); + const server = await create_dev_server({ prescan: false }); try { const result = await server.transformRequest('/__fuz.css'); assert(result); @@ -63,7 +63,7 @@ describe('vite_plugin_fuz_css dev pre-scan', () => { }); test('prescan accepts custom roots', async () => { - const server = await create_dev_server({prescan: ['src', 'extra']}); + const server = await create_dev_server({ prescan: ['src', 'extra'] }); try { const result = await server.transformRequest('/__fuz.css'); assert(result); diff --git a/svelte.config.js b/svelte.config.js index 08a2fbbbf..380918655 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,27 +1,27 @@ -import {vitePreprocess} from '@sveltejs/vite-plugin-svelte'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; import adapter from '@sveltejs/adapter-static'; -import {svelte_preprocess_mdz} from '@fuzdev/mdz/svelte_preprocess_mdz.js'; -import {svelte_preprocess_fuz_code} from '@fuzdev/fuz_code/svelte_preprocess_fuz_code.js'; -import {create_csp_directives} from '@fuzdev/fuz_ui/csp.js'; -import {csp_directives_of_fuzdev} from '@fuzdev/fuz_ui/csp_of_fuzdev.js'; -import {execSync} from 'node:child_process'; +import { svelte_preprocess_mdz } from '@fuzdev/mdz/svelte_preprocess_mdz.js'; +import { svelte_preprocess_fuz_code } from '@fuzdev/fuz_code/svelte_preprocess_fuz_code.js'; +import { create_csp_directives } from '@fuzdev/fuz_ui/csp.js'; +import { csp_directives_of_fuzdev } from '@fuzdev/fuz_ui/csp_of_fuzdev.js'; +import { execSync } from 'node:child_process'; /** @type {import('@sveltejs/kit').Config} */ export default { preprocess: [svelte_preprocess_mdz(), svelte_preprocess_fuz_code(), vitePreprocess()], - compilerOptions: {runes: true}, - vitePlugin: {inspector: true}, + compilerOptions: { runes: true }, + vitePlugin: { inspector: true }, kit: { adapter: adapter(), - paths: {relative: false}, // use root-absolute paths for SSR path comparison: https://svelte.dev/docs/kit/configuration#paths - alias: {$routes: 'src/routes', '@fuzdev/fuz_css': 'src/lib'}, + paths: { relative: false }, // use root-absolute paths for SSR path comparison: https://svelte.dev/docs/kit/configuration#paths + alias: { $routes: 'src/routes', '@fuzdev/fuz_css': 'src/lib' }, csp: { directives: create_csp_directives({ - extend: [csp_directives_of_fuzdev], - }), + extend: [csp_directives_of_fuzdev] + }) }, version: { - name: execSync('git rev-parse HEAD').toString().trim(), - }, - }, + name: execSync('git rev-parse HEAD').toString().trim() + } + } }; diff --git a/tsconfig.json b/tsconfig.json index 6e255c09c..0300902f5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "./.svelte-kit/tsconfig.json", "compilerOptions": { - "types": [ - "@sveltejs/kit" - ], + "types": ["@sveltejs/kit"], "module": "nodenext", "moduleResolution": "nodenext", "strict": true, diff --git a/vite.config.ts b/vite.config.ts index 74f4d9d0a..63e0dc17f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,9 +1,9 @@ -import {defineConfig} from 'vite'; -import {sveltekit} from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; +import { sveltekit } from '@sveltejs/kit/vite'; import svelte_docinfo from 'svelte-docinfo/vite.js'; -import {vite_plugin_pkg_json} from '@fuzdev/fuz_ui/vite_plugin_pkg_json.ts'; +import { vite_plugin_pkg_json } from '@fuzdev/fuz_ui/vite_plugin_pkg_json.ts'; -import {vite_plugin_fuz_css} from './src/lib/vite_plugin_fuz_css.js'; +import { vite_plugin_fuz_css } from './src/lib/vite_plugin_fuz_css.js'; import { alpha_variants, darken_lighten_variants, @@ -13,7 +13,7 @@ import { shadow_alpha_variants, shadow_semantic_values, shadow_size_variants, - shadow_variant_prefixes, + shadow_variant_prefixes } from './src/lib/variable_data.js'; // the docs pages construct these class families dynamically from pickers, so @@ -30,7 +30,7 @@ const docs_classes: Array<string> = [ ...shadow_alpha_variants.map((v) => `shadow_alpha_${v}`), ...shadow_semantic_values.map((v) => `shadow_color_${v}`), ...palette_variants.flatMap((l) => intensity_variants.map((i) => `shadow_${l}_${i}`)), - ...palette_variants.map((l) => `palette_${l}_50`), + ...palette_variants.map((l) => `palette_${l}_50`) ]; export default defineConfig({ @@ -40,9 +40,9 @@ export default defineConfig({ vite_plugin_fuz_css({ additional_elements: 'all', additional_variables: 'all', - additional_classes: docs_classes, + additional_classes: docs_classes }), - vite_plugin_pkg_json(), + vite_plugin_pkg_json() ], - optimizeDeps: {exclude: ['@fuzdev/blake3_wasm']}, + optimizeDeps: { exclude: ['@fuzdev/blake3_wasm'] } }); From 000210c54d34efc864ea3e4607c6b9950ecb3729 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Fri, 24 Jul 2026 10:13:12 -0400 Subject: [PATCH 18/33] wip --- CLAUDE.md | 3 +- src/lib/generate_css.ts | 9 +- src/lib/oklch.ts | 11 +- src/lib/style.css | 10 +- src/lib/theme.ts | 4 +- src/lib/vite_plugin_fuz_css.ts | 5 +- src/routes/KnobControl.svelte | 3 + src/routes/docs/classes/+page.svelte | 5 + src/routes/docs/colors/ColorSwatchItem.svelte | 3 + src/routes/docs/semantic/+page.svelte | 6 +- src/test/css_plugin_options.test.ts | 2 +- src/test/fixtures/css_classes_fixture.json | 5888 +++++------------ src/test/generate_bundled_css.test.ts | 10 + src/test/generate_css.test.ts | 13 + 14 files changed, 1883 insertions(+), 4089 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 11071a2de..f15b8b83a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -343,7 +343,8 @@ typography, borders, shading, shadows, layout. See - [ramps.ts](src/lib/ramps.ts) - The derived color system: fitted knob constants, numeric evaluators, and the CSS `calc()`/`oklch()` emitters - [oklch.ts](src/lib/oklch.ts) - OKLCH↔sRGB math and gamut search - (design-time + tests only) + (design-time + tests; never needed by the shipped CSS, though display + tooling like the docs swatches may import the conversions) - [wcag.ts](src/lib/wcag.ts) - WCAG luminance/contrast (design-time + tests) - [theme.ts](src/lib/theme.ts) - Theme rendering, cascade layers, the `scheme` stance (a single-scheme theme mirrors the adaptive defaults and diff --git a/src/lib/generate_css.ts b/src/lib/generate_css.ts index c45aa891f..e01cf609e 100644 --- a/src/lib/generate_css.ts +++ b/src/lib/generate_css.ts @@ -18,6 +18,7 @@ import { type CssClassDefinition, type CssClassDefinitionInterpreter } from './css_class_generation.ts'; +import { FUZ_LAYER_ORDER_STATEMENT } from './theme.ts'; import { resolve_css, generate_bundled_css } from './css_bundled_resolution.ts'; import { get_all_variable_names } from './variable_graph.ts'; import type { BundledCssResources } from './bundled_resources.ts'; @@ -191,8 +192,12 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => include_utilities: true }); } else { - // utility-only mode - css = utility_result.css; + // utility-only mode — still layered, so the separately imported package + // style.css/theme.css slot beneath the generated classes and consumers' + // unlayered styles beat everything, same as bundled mode + css = utility_result.css + ? `${FUZ_LAYER_ORDER_STATEMENT}\n\n/* Utility Classes */\n@layer fuz.utilities {\n${utility_result.css}\n}` + : ''; } return { css, diagnostics }; diff --git a/src/lib/oklch.ts b/src/lib/oklch.ts index d26a435ad..b0757dfde 100644 --- a/src/lib/oklch.ts +++ b/src/lib/oklch.ts @@ -1,11 +1,12 @@ /** * OKLCH/OKLab ↔ sRGB conversions and gamut math. * - * Design-time and test use only — nothing here ships to browsers. The CSS - * output of fuz_css stays pure `oklch()`/`calc()`; this module exists so - * derivation scripts can fit curve knobs against real colors and so tests can - * assert that every default stop is inside the sRGB gamut and meets the - * contrast gates (see `wcag.ts`). + * The shipped CSS never needs this module — fuz_css output stays pure + * `oklch()`/`calc()`. It exists for design time and tests: derivation scripts + * fit curve knobs against real colors, and tests assert that every default + * stop is inside the sRGB gamut and meets the contrast gates (see `wcag.ts`). + * Display tooling (like the docs site's color swatches) may also import the + * conversions. * * Matrices and constants follow css-color-4 and Björn Ottosson's reference * implementation. diff --git a/src/lib/style.css b/src/lib/style.css index 84de0bdfd..0ebbd408a 100644 --- a/src/lib/style.css +++ b/src/lib/style.css @@ -14,10 +14,12 @@ See: https://css.fuz.dev/docs/classes Notes: - acts as a CSS reset and also sets minimal defaults for elements with themable variables -- uses `:where` on all selectors with more specificity than a single tag/class - to lower specificity so utility classes override them - - this also means the fuz_css stylesheet is is less likely to - interfere with the page's styles regardless of where it's imported +- wraps opinionated selectors in `:where` so they carry zero specificity - + cross-layer overrides (utilities, themes, consumer styles) are already + guaranteed by cascade-layer order, so what `:where` buys now is that + conflicts *within* this stylesheet resolve purely by source order + (the state interplay below depends on that), and that rules consumers + add into the fuz layers override these effortlessly - `.unstyled` class opts out of opinionated styling (colors, borders, decorative properties) while keeping normalizations (font inheritance, border-collapse) - applied to interactive elements (links, buttons, inputs, summary) and decorative containers, diff --git a/src/lib/theme.ts b/src/lib/theme.ts index 8540f1c05..d18d34ed3 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -95,7 +95,9 @@ export const render_theme_style = (theme: Theme, options: RenderThemeStyleOption // mirrored defaults first so the theme's own variables win by order const variables = [...(stance ? scheme_stance_variables(stance, own ?? []) : []), ...(own ?? [])]; if (!variables.length && !stance) return ''; - const rendered_light = variables.map((v) => render_theme_variable(v)).filter(Boolean); + const rendered_light = variables + .map((v) => render_theme_variable(v, false, comments)) + .filter(Boolean); if (stance) rendered_light.unshift(`color-scheme: ${stance};`); const rendered_dark = variables .map((v) => render_theme_variable(v, true, comments)) diff --git a/src/lib/vite_plugin_fuz_css.ts b/src/lib/vite_plugin_fuz_css.ts index db9cff8ef..2b38c70ba 100644 --- a/src/lib/vite_plugin_fuz_css.ts +++ b/src/lib/vite_plugin_fuz_css.ts @@ -446,8 +446,9 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug const invalidate_virtual_module = (): void => { if (!server) return; - // Skip HMR if bundled resources aren't loaded yet - // (will be loaded on first load() call, CSS regenerated then) + // Skip HMR if bundled resources aren't loaded yet — in dev they load + // eagerly at configureServer, in build lazily on the first load() call; + // either way the eventual load regenerates the CSS if ((include_base || include_theme) && style_rule_index === null) { return; } diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index 088aba896..f0534f4a4 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -92,6 +92,7 @@ type="button" class="letter_chip palette_{letter}" class:selected={bound_letter === letter} + aria-pressed={bound_letter === letter} title={gloss_title(letter)} onclick={() => onchange(`var(--hue_${letter})`)} > @@ -102,6 +103,7 @@ type="button" class="letter_chip" class:selected={bound_letter === null} + aria-pressed={bound_letter === null} style:--fill={custom_color} style:--text_color={custom_color} style:--border_color={custom_color} @@ -166,6 +168,7 @@ type="button" class="plain icon_button sm knob_reset" title="reset to base" + aria-label="reset to base" onclick={onreset}>↺</button > {/if} diff --git a/src/routes/docs/classes/+page.svelte b/src/routes/docs/classes/+page.svelte index 3d70f184f..cd481bb32 100644 --- a/src/routes/docs/classes/+page.svelte +++ b/src/routes/docs/classes/+page.svelte @@ -515,6 +515,11 @@ const el = document.createElement('dialog');`} <code>fuz.utilities</code>, so utilities beat the reset by layer order (not specificity) and your own unlayered styles beat everything. </p> + <p> + If you organize your own styles in <code>@layer</code>, declare fuz's layers first so yours + sort later and win: <code>@layer fuz.base, fuz.theme, fuz.utilities, app;</code>. Otherwise + fuz's layers are declared when its CSS loads and beat any of your earlier-declared layers. + </p> <h4>Base styles</h4> <p> diff --git a/src/routes/docs/colors/ColorSwatchItem.svelte b/src/routes/docs/colors/ColorSwatchItem.svelte index a4b855f15..bf2c9400a 100644 --- a/src/routes/docs/colors/ColorSwatchItem.svelte +++ b/src/routes/docs/colors/ColorSwatchItem.svelte @@ -31,6 +31,9 @@ }); const parsed_oklch = $derived.by((): Oklch | null => { + // expects the computed-style serialization of an opaque oklch() color; + // anything else (an alpha channel, another notation) degrades to the raw + // resolved string with no hex column const m = /^oklch\(([\d.]+) ([\d.]+) ([\d.]+)\)$/.exec(resolved); if (!m) return null; return [Number(m[1]), Number(m[2]), Number(m[3])]; diff --git a/src/routes/docs/semantic/+page.svelte b/src/routes/docs/semantic/+page.svelte index c531f53f6..ee44eb408 100644 --- a/src/routes/docs/semantic/+page.svelte +++ b/src/routes/docs/semantic/+page.svelte @@ -28,8 +28,10 @@ <TomeSection> <TomeSectionHeader text="Low specificity" /> <p> - All opinionated styles use <code>:where()</code> selectors, giving them zero specificity beyond - the element itself. Your styles and utility classes override defaults without specificity battles. + All opinionated styles live in the <code>fuz.base</code> cascade layer, so your unlayered + styles and the generated utility classes override them by layer order alone. The selectors are + additionally wrapped in <code>:where()</code>, giving them zero specificity, so overriding + stays effortless even from inside the fuz layers. </p> <Code lang="css" diff --git a/src/test/css_plugin_options.test.ts b/src/test/css_plugin_options.test.ts index be659656d..73015d659 100644 --- a/src/test/css_plugin_options.test.ts +++ b/src/test/css_plugin_options.test.ts @@ -153,7 +153,7 @@ describe('shared options interface', () => { // Test callback (modify defaults) const callback_opts: CssOutputOptions = { - variables: (defaults) => defaults.filter((v) => v.name.startsWith('color_')) + variables: (defaults) => defaults.filter((v) => v.name.startsWith('palette_')) }; assert.strictEqual(typeof callback_opts.variables, 'function'); }); diff --git a/src/test/fixtures/css_classes_fixture.json b/src/test/fixtures/css_classes_fixture.json index ecf038180..1498ae084 100644 --- a/src/test/fixtures/css_classes_fixture.json +++ b/src/test/fixtures/css_classes_fixture.json @@ -2,9 +2,7 @@ "pixelated": { "declaration": "\n\t\t\timage-rendering: -webkit-optimize-contrast; /* Safari */\n\t\t\timage-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */\n\t\t\timage-rendering: pixelated; /* in case crisp-edges isn't supported */\n\t\t\timage-rendering: crisp-edges; /* the recommended pixel art setting according to MDN */\n\t\t" }, - "circular": { - "declaration": "border-radius: 50%;" - }, + "circular": { "declaration": "border-radius: 50%;" }, "box": { "declaration": "\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t" }, @@ -78,30 +76,14 @@ "chip": { "ruleset": "\n\t\t\t.chip {\n\t\t\t\tfont-weight: 500;\n\t\t\t\tfont-size: var(--font_size, inherit);\n\t\t\t\tpadding-left: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tpadding-right: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t\t}\n\t\t\ta.chip {\n\t\t\t\tfont-weight: 600;\n\t\t\t}\n\t\t\t.chip.palette_a {\n\t\t\t\tcolor: var(--palette_a_50);\n\t\t\t\tbackground-color: var(--palette_a_10);\n\t\t\t}\n\t\t\t.chip.palette_b {\n\t\t\t\tcolor: var(--palette_b_50);\n\t\t\t\tbackground-color: var(--palette_b_10);\n\t\t\t}\n\t\t\t.chip.palette_c {\n\t\t\t\tcolor: var(--palette_c_50);\n\t\t\t\tbackground-color: var(--palette_c_10);\n\t\t\t}\n\t\t\t.chip.palette_d {\n\t\t\t\tcolor: var(--palette_d_50);\n\t\t\t\tbackground-color: var(--palette_d_10);\n\t\t\t}\n\t\t\t.chip.palette_e {\n\t\t\t\tcolor: var(--palette_e_50);\n\t\t\t\tbackground-color: var(--palette_e_10);\n\t\t\t}\n\t\t\t.chip.palette_f {\n\t\t\t\tcolor: var(--palette_f_50);\n\t\t\t\tbackground-color: var(--palette_f_10);\n\t\t\t}\n\t\t\t.chip.palette_g {\n\t\t\t\tcolor: var(--palette_g_50);\n\t\t\t\tbackground-color: var(--palette_g_10);\n\t\t\t}\n\t\t\t.chip.palette_h {\n\t\t\t\tcolor: var(--palette_h_50);\n\t\t\t\tbackground-color: var(--palette_h_10);\n\t\t\t}\n\t\t\t.chip.palette_i {\n\t\t\t\tcolor: var(--palette_i_50);\n\t\t\t\tbackground-color: var(--palette_i_10);\n\t\t\t}\n\t\t\t.chip.palette_j {\n\t\t\t\tcolor: var(--palette_j_50);\n\t\t\t\tbackground-color: var(--palette_j_10);\n\t\t\t}\n\t\t" }, - "font_family_sans": { - "declaration": "font-family: var(--font_family_sans);" - }, - "font_family_serif": { - "declaration": "font-family: var(--font_family_serif);" - }, - "font_family_mono": { - "declaration": "font-family: var(--font_family_mono);" - }, - "line_height_xs": { - "declaration": "line-height: var(--line_height_xs);" - }, - "line_height_sm": { - "declaration": "line-height: var(--line_height_sm);" - }, - "line_height_md": { - "declaration": "line-height: var(--line_height_md);" - }, - "line_height_lg": { - "declaration": "line-height: var(--line_height_lg);" - }, - "line_height_xl": { - "declaration": "line-height: var(--line_height_xl);" - }, + "font_family_sans": { "declaration": "font-family: var(--font_family_sans);" }, + "font_family_serif": { "declaration": "font-family: var(--font_family_serif);" }, + "font_family_mono": { "declaration": "font-family: var(--font_family_mono);" }, + "line_height_xs": { "declaration": "line-height: var(--line_height_xs);" }, + "line_height_sm": { "declaration": "line-height: var(--line_height_sm);" }, + "line_height_md": { "declaration": "line-height: var(--line_height_md);" }, + "line_height_lg": { "declaration": "line-height: var(--line_height_lg);" }, + "line_height_xl": { "declaration": "line-height: var(--line_height_xl);" }, "font_size_xs": { "declaration": "font-size: var(--font_size_xs); --font_size: var(--font_size_xs);" }, @@ -162,126 +144,46 @@ "icon_size_xl3": { "declaration": "font-size: var(--icon_size_xl3); --font_size: var(--icon_size_xl3);" }, - "text_min": { - "declaration": "color: var(--text_min); --text_color: var(--text_min);" - }, - "text_00": { - "declaration": "color: var(--text_00); --text_color: var(--text_00);" - }, - "text_05": { - "declaration": "color: var(--text_05); --text_color: var(--text_05);" - }, - "text_10": { - "declaration": "color: var(--text_10); --text_color: var(--text_10);" - }, - "text_20": { - "declaration": "color: var(--text_20); --text_color: var(--text_20);" - }, - "text_30": { - "declaration": "color: var(--text_30); --text_color: var(--text_30);" - }, - "text_40": { - "declaration": "color: var(--text_40); --text_color: var(--text_40);" - }, - "text_50": { - "declaration": "color: var(--text_50); --text_color: var(--text_50);" - }, - "text_60": { - "declaration": "color: var(--text_60); --text_color: var(--text_60);" - }, - "text_70": { - "declaration": "color: var(--text_70); --text_color: var(--text_70);" - }, - "text_80": { - "declaration": "color: var(--text_80); --text_color: var(--text_80);" - }, - "text_90": { - "declaration": "color: var(--text_90); --text_color: var(--text_90);" - }, - "text_95": { - "declaration": "color: var(--text_95); --text_color: var(--text_95);" - }, - "text_100": { - "declaration": "color: var(--text_100); --text_color: var(--text_100);" - }, - "text_max": { - "declaration": "color: var(--text_max); --text_color: var(--text_max);" - }, - "shade_min": { - "declaration": "background-color: var(--shade_min);" - }, - "shade_00": { - "declaration": "background-color: var(--shade_00);" - }, - "shade_05": { - "declaration": "background-color: var(--shade_05);" - }, - "shade_10": { - "declaration": "background-color: var(--shade_10);" - }, - "shade_20": { - "declaration": "background-color: var(--shade_20);" - }, - "shade_30": { - "declaration": "background-color: var(--shade_30);" - }, - "shade_40": { - "declaration": "background-color: var(--shade_40);" - }, - "shade_50": { - "declaration": "background-color: var(--shade_50);" - }, - "shade_60": { - "declaration": "background-color: var(--shade_60);" - }, - "shade_70": { - "declaration": "background-color: var(--shade_70);" - }, - "shade_80": { - "declaration": "background-color: var(--shade_80);" - }, - "shade_90": { - "declaration": "background-color: var(--shade_90);" - }, - "shade_95": { - "declaration": "background-color: var(--shade_95);" - }, - "shade_100": { - "declaration": "background-color: var(--shade_100);" - }, - "shade_max": { - "declaration": "background-color: var(--shade_max);" - }, - "hue_a": { - "declaration": "--hue: var(--hue_a);" - }, - "hue_b": { - "declaration": "--hue: var(--hue_b);" - }, - "hue_c": { - "declaration": "--hue: var(--hue_c);" - }, - "hue_d": { - "declaration": "--hue: var(--hue_d);" - }, - "hue_e": { - "declaration": "--hue: var(--hue_e);" - }, - "hue_f": { - "declaration": "--hue: var(--hue_f);" - }, - "hue_g": { - "declaration": "--hue: var(--hue_g);" - }, - "hue_h": { - "declaration": "--hue: var(--hue_h);" - }, - "hue_i": { - "declaration": "--hue: var(--hue_i);" - }, - "hue_j": { - "declaration": "--hue: var(--hue_j);" - }, + "text_min": { "declaration": "color: var(--text_min); --text_color: var(--text_min);" }, + "text_00": { "declaration": "color: var(--text_00); --text_color: var(--text_00);" }, + "text_05": { "declaration": "color: var(--text_05); --text_color: var(--text_05);" }, + "text_10": { "declaration": "color: var(--text_10); --text_color: var(--text_10);" }, + "text_20": { "declaration": "color: var(--text_20); --text_color: var(--text_20);" }, + "text_30": { "declaration": "color: var(--text_30); --text_color: var(--text_30);" }, + "text_40": { "declaration": "color: var(--text_40); --text_color: var(--text_40);" }, + "text_50": { "declaration": "color: var(--text_50); --text_color: var(--text_50);" }, + "text_60": { "declaration": "color: var(--text_60); --text_color: var(--text_60);" }, + "text_70": { "declaration": "color: var(--text_70); --text_color: var(--text_70);" }, + "text_80": { "declaration": "color: var(--text_80); --text_color: var(--text_80);" }, + "text_90": { "declaration": "color: var(--text_90); --text_color: var(--text_90);" }, + "text_95": { "declaration": "color: var(--text_95); --text_color: var(--text_95);" }, + "text_100": { "declaration": "color: var(--text_100); --text_color: var(--text_100);" }, + "text_max": { "declaration": "color: var(--text_max); --text_color: var(--text_max);" }, + "shade_min": { "declaration": "background-color: var(--shade_min);" }, + "shade_00": { "declaration": "background-color: var(--shade_00);" }, + "shade_05": { "declaration": "background-color: var(--shade_05);" }, + "shade_10": { "declaration": "background-color: var(--shade_10);" }, + "shade_20": { "declaration": "background-color: var(--shade_20);" }, + "shade_30": { "declaration": "background-color: var(--shade_30);" }, + "shade_40": { "declaration": "background-color: var(--shade_40);" }, + "shade_50": { "declaration": "background-color: var(--shade_50);" }, + "shade_60": { "declaration": "background-color: var(--shade_60);" }, + "shade_70": { "declaration": "background-color: var(--shade_70);" }, + "shade_80": { "declaration": "background-color: var(--shade_80);" }, + "shade_90": { "declaration": "background-color: var(--shade_90);" }, + "shade_95": { "declaration": "background-color: var(--shade_95);" }, + "shade_100": { "declaration": "background-color: var(--shade_100);" }, + "shade_max": { "declaration": "background-color: var(--shade_max);" }, + "hue_a": { "declaration": "--hue: var(--hue_a);" }, + "hue_b": { "declaration": "--hue: var(--hue_b);" }, + "hue_c": { "declaration": "--hue: var(--hue_c);" }, + "hue_d": { "declaration": "--hue: var(--hue_d);" }, + "hue_e": { "declaration": "--hue: var(--hue_e);" }, + "hue_f": { "declaration": "--hue: var(--hue_f);" }, + "hue_g": { "declaration": "--hue: var(--hue_g);" }, + "hue_h": { "declaration": "--hue: var(--hue_h);" }, + "hue_i": { "declaration": "--hue: var(--hue_i);" }, + "hue_j": { "declaration": "--hue: var(--hue_j);" }, "palette_a_00": { "declaration": "color: var(--palette_a_00); --text_color: var(--palette_a_00);" }, @@ -672,4273 +574,2117 @@ "palette_j_100": { "declaration": "color: var(--palette_j_100); --text_color: var(--palette_j_100);" }, - "bg_a_00": { - "declaration": "background-color: var(--palette_a_00);" - }, - "bg_a_05": { - "declaration": "background-color: var(--palette_a_05);" + "bg_a_00": { "declaration": "background-color: var(--palette_a_00);" }, + "bg_a_05": { "declaration": "background-color: var(--palette_a_05);" }, + "bg_a_10": { "declaration": "background-color: var(--palette_a_10);" }, + "bg_a_20": { "declaration": "background-color: var(--palette_a_20);" }, + "bg_a_30": { "declaration": "background-color: var(--palette_a_30);" }, + "bg_a_40": { "declaration": "background-color: var(--palette_a_40);" }, + "bg_a_50": { "declaration": "background-color: var(--palette_a_50);" }, + "bg_a_60": { "declaration": "background-color: var(--palette_a_60);" }, + "bg_a_70": { "declaration": "background-color: var(--palette_a_70);" }, + "bg_a_80": { "declaration": "background-color: var(--palette_a_80);" }, + "bg_a_90": { "declaration": "background-color: var(--palette_a_90);" }, + "bg_a_95": { "declaration": "background-color: var(--palette_a_95);" }, + "bg_a_100": { "declaration": "background-color: var(--palette_a_100);" }, + "bg_b_00": { "declaration": "background-color: var(--palette_b_00);" }, + "bg_b_05": { "declaration": "background-color: var(--palette_b_05);" }, + "bg_b_10": { "declaration": "background-color: var(--palette_b_10);" }, + "bg_b_20": { "declaration": "background-color: var(--palette_b_20);" }, + "bg_b_30": { "declaration": "background-color: var(--palette_b_30);" }, + "bg_b_40": { "declaration": "background-color: var(--palette_b_40);" }, + "bg_b_50": { "declaration": "background-color: var(--palette_b_50);" }, + "bg_b_60": { "declaration": "background-color: var(--palette_b_60);" }, + "bg_b_70": { "declaration": "background-color: var(--palette_b_70);" }, + "bg_b_80": { "declaration": "background-color: var(--palette_b_80);" }, + "bg_b_90": { "declaration": "background-color: var(--palette_b_90);" }, + "bg_b_95": { "declaration": "background-color: var(--palette_b_95);" }, + "bg_b_100": { "declaration": "background-color: var(--palette_b_100);" }, + "bg_c_00": { "declaration": "background-color: var(--palette_c_00);" }, + "bg_c_05": { "declaration": "background-color: var(--palette_c_05);" }, + "bg_c_10": { "declaration": "background-color: var(--palette_c_10);" }, + "bg_c_20": { "declaration": "background-color: var(--palette_c_20);" }, + "bg_c_30": { "declaration": "background-color: var(--palette_c_30);" }, + "bg_c_40": { "declaration": "background-color: var(--palette_c_40);" }, + "bg_c_50": { "declaration": "background-color: var(--palette_c_50);" }, + "bg_c_60": { "declaration": "background-color: var(--palette_c_60);" }, + "bg_c_70": { "declaration": "background-color: var(--palette_c_70);" }, + "bg_c_80": { "declaration": "background-color: var(--palette_c_80);" }, + "bg_c_90": { "declaration": "background-color: var(--palette_c_90);" }, + "bg_c_95": { "declaration": "background-color: var(--palette_c_95);" }, + "bg_c_100": { "declaration": "background-color: var(--palette_c_100);" }, + "bg_d_00": { "declaration": "background-color: var(--palette_d_00);" }, + "bg_d_05": { "declaration": "background-color: var(--palette_d_05);" }, + "bg_d_10": { "declaration": "background-color: var(--palette_d_10);" }, + "bg_d_20": { "declaration": "background-color: var(--palette_d_20);" }, + "bg_d_30": { "declaration": "background-color: var(--palette_d_30);" }, + "bg_d_40": { "declaration": "background-color: var(--palette_d_40);" }, + "bg_d_50": { "declaration": "background-color: var(--palette_d_50);" }, + "bg_d_60": { "declaration": "background-color: var(--palette_d_60);" }, + "bg_d_70": { "declaration": "background-color: var(--palette_d_70);" }, + "bg_d_80": { "declaration": "background-color: var(--palette_d_80);" }, + "bg_d_90": { "declaration": "background-color: var(--palette_d_90);" }, + "bg_d_95": { "declaration": "background-color: var(--palette_d_95);" }, + "bg_d_100": { "declaration": "background-color: var(--palette_d_100);" }, + "bg_e_00": { "declaration": "background-color: var(--palette_e_00);" }, + "bg_e_05": { "declaration": "background-color: var(--palette_e_05);" }, + "bg_e_10": { "declaration": "background-color: var(--palette_e_10);" }, + "bg_e_20": { "declaration": "background-color: var(--palette_e_20);" }, + "bg_e_30": { "declaration": "background-color: var(--palette_e_30);" }, + "bg_e_40": { "declaration": "background-color: var(--palette_e_40);" }, + "bg_e_50": { "declaration": "background-color: var(--palette_e_50);" }, + "bg_e_60": { "declaration": "background-color: var(--palette_e_60);" }, + "bg_e_70": { "declaration": "background-color: var(--palette_e_70);" }, + "bg_e_80": { "declaration": "background-color: var(--palette_e_80);" }, + "bg_e_90": { "declaration": "background-color: var(--palette_e_90);" }, + "bg_e_95": { "declaration": "background-color: var(--palette_e_95);" }, + "bg_e_100": { "declaration": "background-color: var(--palette_e_100);" }, + "bg_f_00": { "declaration": "background-color: var(--palette_f_00);" }, + "bg_f_05": { "declaration": "background-color: var(--palette_f_05);" }, + "bg_f_10": { "declaration": "background-color: var(--palette_f_10);" }, + "bg_f_20": { "declaration": "background-color: var(--palette_f_20);" }, + "bg_f_30": { "declaration": "background-color: var(--palette_f_30);" }, + "bg_f_40": { "declaration": "background-color: var(--palette_f_40);" }, + "bg_f_50": { "declaration": "background-color: var(--palette_f_50);" }, + "bg_f_60": { "declaration": "background-color: var(--palette_f_60);" }, + "bg_f_70": { "declaration": "background-color: var(--palette_f_70);" }, + "bg_f_80": { "declaration": "background-color: var(--palette_f_80);" }, + "bg_f_90": { "declaration": "background-color: var(--palette_f_90);" }, + "bg_f_95": { "declaration": "background-color: var(--palette_f_95);" }, + "bg_f_100": { "declaration": "background-color: var(--palette_f_100);" }, + "bg_g_00": { "declaration": "background-color: var(--palette_g_00);" }, + "bg_g_05": { "declaration": "background-color: var(--palette_g_05);" }, + "bg_g_10": { "declaration": "background-color: var(--palette_g_10);" }, + "bg_g_20": { "declaration": "background-color: var(--palette_g_20);" }, + "bg_g_30": { "declaration": "background-color: var(--palette_g_30);" }, + "bg_g_40": { "declaration": "background-color: var(--palette_g_40);" }, + "bg_g_50": { "declaration": "background-color: var(--palette_g_50);" }, + "bg_g_60": { "declaration": "background-color: var(--palette_g_60);" }, + "bg_g_70": { "declaration": "background-color: var(--palette_g_70);" }, + "bg_g_80": { "declaration": "background-color: var(--palette_g_80);" }, + "bg_g_90": { "declaration": "background-color: var(--palette_g_90);" }, + "bg_g_95": { "declaration": "background-color: var(--palette_g_95);" }, + "bg_g_100": { "declaration": "background-color: var(--palette_g_100);" }, + "bg_h_00": { "declaration": "background-color: var(--palette_h_00);" }, + "bg_h_05": { "declaration": "background-color: var(--palette_h_05);" }, + "bg_h_10": { "declaration": "background-color: var(--palette_h_10);" }, + "bg_h_20": { "declaration": "background-color: var(--palette_h_20);" }, + "bg_h_30": { "declaration": "background-color: var(--palette_h_30);" }, + "bg_h_40": { "declaration": "background-color: var(--palette_h_40);" }, + "bg_h_50": { "declaration": "background-color: var(--palette_h_50);" }, + "bg_h_60": { "declaration": "background-color: var(--palette_h_60);" }, + "bg_h_70": { "declaration": "background-color: var(--palette_h_70);" }, + "bg_h_80": { "declaration": "background-color: var(--palette_h_80);" }, + "bg_h_90": { "declaration": "background-color: var(--palette_h_90);" }, + "bg_h_95": { "declaration": "background-color: var(--palette_h_95);" }, + "bg_h_100": { "declaration": "background-color: var(--palette_h_100);" }, + "bg_i_00": { "declaration": "background-color: var(--palette_i_00);" }, + "bg_i_05": { "declaration": "background-color: var(--palette_i_05);" }, + "bg_i_10": { "declaration": "background-color: var(--palette_i_10);" }, + "bg_i_20": { "declaration": "background-color: var(--palette_i_20);" }, + "bg_i_30": { "declaration": "background-color: var(--palette_i_30);" }, + "bg_i_40": { "declaration": "background-color: var(--palette_i_40);" }, + "bg_i_50": { "declaration": "background-color: var(--palette_i_50);" }, + "bg_i_60": { "declaration": "background-color: var(--palette_i_60);" }, + "bg_i_70": { "declaration": "background-color: var(--palette_i_70);" }, + "bg_i_80": { "declaration": "background-color: var(--palette_i_80);" }, + "bg_i_90": { "declaration": "background-color: var(--palette_i_90);" }, + "bg_i_95": { "declaration": "background-color: var(--palette_i_95);" }, + "bg_i_100": { "declaration": "background-color: var(--palette_i_100);" }, + "bg_j_00": { "declaration": "background-color: var(--palette_j_00);" }, + "bg_j_05": { "declaration": "background-color: var(--palette_j_05);" }, + "bg_j_10": { "declaration": "background-color: var(--palette_j_10);" }, + "bg_j_20": { "declaration": "background-color: var(--palette_j_20);" }, + "bg_j_30": { "declaration": "background-color: var(--palette_j_30);" }, + "bg_j_40": { "declaration": "background-color: var(--palette_j_40);" }, + "bg_j_50": { "declaration": "background-color: var(--palette_j_50);" }, + "bg_j_60": { "declaration": "background-color: var(--palette_j_60);" }, + "bg_j_70": { "declaration": "background-color: var(--palette_j_70);" }, + "bg_j_80": { "declaration": "background-color: var(--palette_j_80);" }, + "bg_j_90": { "declaration": "background-color: var(--palette_j_90);" }, + "bg_j_95": { "declaration": "background-color: var(--palette_j_95);" }, + "bg_j_100": { "declaration": "background-color: var(--palette_j_100);" }, + "accent_00": { "declaration": "color: var(--accent_00); --text_color: var(--accent_00);" }, + "accent_05": { "declaration": "color: var(--accent_05); --text_color: var(--accent_05);" }, + "accent_10": { "declaration": "color: var(--accent_10); --text_color: var(--accent_10);" }, + "accent_20": { "declaration": "color: var(--accent_20); --text_color: var(--accent_20);" }, + "accent_30": { "declaration": "color: var(--accent_30); --text_color: var(--accent_30);" }, + "accent_40": { "declaration": "color: var(--accent_40); --text_color: var(--accent_40);" }, + "accent_50": { "declaration": "color: var(--accent_50); --text_color: var(--accent_50);" }, + "accent_60": { "declaration": "color: var(--accent_60); --text_color: var(--accent_60);" }, + "accent_70": { "declaration": "color: var(--accent_70); --text_color: var(--accent_70);" }, + "accent_80": { "declaration": "color: var(--accent_80); --text_color: var(--accent_80);" }, + "accent_90": { "declaration": "color: var(--accent_90); --text_color: var(--accent_90);" }, + "accent_95": { "declaration": "color: var(--accent_95); --text_color: var(--accent_95);" }, + "accent_100": { "declaration": "color: var(--accent_100); --text_color: var(--accent_100);" }, + "positive_00": { "declaration": "color: var(--positive_00); --text_color: var(--positive_00);" }, + "positive_05": { "declaration": "color: var(--positive_05); --text_color: var(--positive_05);" }, + "positive_10": { "declaration": "color: var(--positive_10); --text_color: var(--positive_10);" }, + "positive_20": { "declaration": "color: var(--positive_20); --text_color: var(--positive_20);" }, + "positive_30": { "declaration": "color: var(--positive_30); --text_color: var(--positive_30);" }, + "positive_40": { "declaration": "color: var(--positive_40); --text_color: var(--positive_40);" }, + "positive_50": { "declaration": "color: var(--positive_50); --text_color: var(--positive_50);" }, + "positive_60": { "declaration": "color: var(--positive_60); --text_color: var(--positive_60);" }, + "positive_70": { "declaration": "color: var(--positive_70); --text_color: var(--positive_70);" }, + "positive_80": { "declaration": "color: var(--positive_80); --text_color: var(--positive_80);" }, + "positive_90": { "declaration": "color: var(--positive_90); --text_color: var(--positive_90);" }, + "positive_95": { "declaration": "color: var(--positive_95); --text_color: var(--positive_95);" }, + "positive_100": { + "declaration": "color: var(--positive_100); --text_color: var(--positive_100);" }, - "bg_a_10": { - "declaration": "background-color: var(--palette_a_10);" + "negative_00": { "declaration": "color: var(--negative_00); --text_color: var(--negative_00);" }, + "negative_05": { "declaration": "color: var(--negative_05); --text_color: var(--negative_05);" }, + "negative_10": { "declaration": "color: var(--negative_10); --text_color: var(--negative_10);" }, + "negative_20": { "declaration": "color: var(--negative_20); --text_color: var(--negative_20);" }, + "negative_30": { "declaration": "color: var(--negative_30); --text_color: var(--negative_30);" }, + "negative_40": { "declaration": "color: var(--negative_40); --text_color: var(--negative_40);" }, + "negative_50": { "declaration": "color: var(--negative_50); --text_color: var(--negative_50);" }, + "negative_60": { "declaration": "color: var(--negative_60); --text_color: var(--negative_60);" }, + "negative_70": { "declaration": "color: var(--negative_70); --text_color: var(--negative_70);" }, + "negative_80": { "declaration": "color: var(--negative_80); --text_color: var(--negative_80);" }, + "negative_90": { "declaration": "color: var(--negative_90); --text_color: var(--negative_90);" }, + "negative_95": { "declaration": "color: var(--negative_95); --text_color: var(--negative_95);" }, + "negative_100": { + "declaration": "color: var(--negative_100); --text_color: var(--negative_100);" }, - "bg_a_20": { - "declaration": "background-color: var(--palette_a_20);" + "caution_00": { "declaration": "color: var(--caution_00); --text_color: var(--caution_00);" }, + "caution_05": { "declaration": "color: var(--caution_05); --text_color: var(--caution_05);" }, + "caution_10": { "declaration": "color: var(--caution_10); --text_color: var(--caution_10);" }, + "caution_20": { "declaration": "color: var(--caution_20); --text_color: var(--caution_20);" }, + "caution_30": { "declaration": "color: var(--caution_30); --text_color: var(--caution_30);" }, + "caution_40": { "declaration": "color: var(--caution_40); --text_color: var(--caution_40);" }, + "caution_50": { "declaration": "color: var(--caution_50); --text_color: var(--caution_50);" }, + "caution_60": { "declaration": "color: var(--caution_60); --text_color: var(--caution_60);" }, + "caution_70": { "declaration": "color: var(--caution_70); --text_color: var(--caution_70);" }, + "caution_80": { "declaration": "color: var(--caution_80); --text_color: var(--caution_80);" }, + "caution_90": { "declaration": "color: var(--caution_90); --text_color: var(--caution_90);" }, + "caution_95": { "declaration": "color: var(--caution_95); --text_color: var(--caution_95);" }, + "caution_100": { "declaration": "color: var(--caution_100); --text_color: var(--caution_100);" }, + "info_00": { "declaration": "color: var(--info_00); --text_color: var(--info_00);" }, + "info_05": { "declaration": "color: var(--info_05); --text_color: var(--info_05);" }, + "info_10": { "declaration": "color: var(--info_10); --text_color: var(--info_10);" }, + "info_20": { "declaration": "color: var(--info_20); --text_color: var(--info_20);" }, + "info_30": { "declaration": "color: var(--info_30); --text_color: var(--info_30);" }, + "info_40": { "declaration": "color: var(--info_40); --text_color: var(--info_40);" }, + "info_50": { "declaration": "color: var(--info_50); --text_color: var(--info_50);" }, + "info_60": { "declaration": "color: var(--info_60); --text_color: var(--info_60);" }, + "info_70": { "declaration": "color: var(--info_70); --text_color: var(--info_70);" }, + "info_80": { "declaration": "color: var(--info_80); --text_color: var(--info_80);" }, + "info_90": { "declaration": "color: var(--info_90); --text_color: var(--info_90);" }, + "info_95": { "declaration": "color: var(--info_95); --text_color: var(--info_95);" }, + "info_100": { "declaration": "color: var(--info_100); --text_color: var(--info_100);" }, + "bg_accent_00": { "declaration": "background-color: var(--accent_00);" }, + "bg_accent_05": { "declaration": "background-color: var(--accent_05);" }, + "bg_accent_10": { "declaration": "background-color: var(--accent_10);" }, + "bg_accent_20": { "declaration": "background-color: var(--accent_20);" }, + "bg_accent_30": { "declaration": "background-color: var(--accent_30);" }, + "bg_accent_40": { "declaration": "background-color: var(--accent_40);" }, + "bg_accent_50": { "declaration": "background-color: var(--accent_50);" }, + "bg_accent_60": { "declaration": "background-color: var(--accent_60);" }, + "bg_accent_70": { "declaration": "background-color: var(--accent_70);" }, + "bg_accent_80": { "declaration": "background-color: var(--accent_80);" }, + "bg_accent_90": { "declaration": "background-color: var(--accent_90);" }, + "bg_accent_95": { "declaration": "background-color: var(--accent_95);" }, + "bg_accent_100": { "declaration": "background-color: var(--accent_100);" }, + "bg_positive_00": { "declaration": "background-color: var(--positive_00);" }, + "bg_positive_05": { "declaration": "background-color: var(--positive_05);" }, + "bg_positive_10": { "declaration": "background-color: var(--positive_10);" }, + "bg_positive_20": { "declaration": "background-color: var(--positive_20);" }, + "bg_positive_30": { "declaration": "background-color: var(--positive_30);" }, + "bg_positive_40": { "declaration": "background-color: var(--positive_40);" }, + "bg_positive_50": { "declaration": "background-color: var(--positive_50);" }, + "bg_positive_60": { "declaration": "background-color: var(--positive_60);" }, + "bg_positive_70": { "declaration": "background-color: var(--positive_70);" }, + "bg_positive_80": { "declaration": "background-color: var(--positive_80);" }, + "bg_positive_90": { "declaration": "background-color: var(--positive_90);" }, + "bg_positive_95": { "declaration": "background-color: var(--positive_95);" }, + "bg_positive_100": { "declaration": "background-color: var(--positive_100);" }, + "bg_negative_00": { "declaration": "background-color: var(--negative_00);" }, + "bg_negative_05": { "declaration": "background-color: var(--negative_05);" }, + "bg_negative_10": { "declaration": "background-color: var(--negative_10);" }, + "bg_negative_20": { "declaration": "background-color: var(--negative_20);" }, + "bg_negative_30": { "declaration": "background-color: var(--negative_30);" }, + "bg_negative_40": { "declaration": "background-color: var(--negative_40);" }, + "bg_negative_50": { "declaration": "background-color: var(--negative_50);" }, + "bg_negative_60": { "declaration": "background-color: var(--negative_60);" }, + "bg_negative_70": { "declaration": "background-color: var(--negative_70);" }, + "bg_negative_80": { "declaration": "background-color: var(--negative_80);" }, + "bg_negative_90": { "declaration": "background-color: var(--negative_90);" }, + "bg_negative_95": { "declaration": "background-color: var(--negative_95);" }, + "bg_negative_100": { "declaration": "background-color: var(--negative_100);" }, + "bg_caution_00": { "declaration": "background-color: var(--caution_00);" }, + "bg_caution_05": { "declaration": "background-color: var(--caution_05);" }, + "bg_caution_10": { "declaration": "background-color: var(--caution_10);" }, + "bg_caution_20": { "declaration": "background-color: var(--caution_20);" }, + "bg_caution_30": { "declaration": "background-color: var(--caution_30);" }, + "bg_caution_40": { "declaration": "background-color: var(--caution_40);" }, + "bg_caution_50": { "declaration": "background-color: var(--caution_50);" }, + "bg_caution_60": { "declaration": "background-color: var(--caution_60);" }, + "bg_caution_70": { "declaration": "background-color: var(--caution_70);" }, + "bg_caution_80": { "declaration": "background-color: var(--caution_80);" }, + "bg_caution_90": { "declaration": "background-color: var(--caution_90);" }, + "bg_caution_95": { "declaration": "background-color: var(--caution_95);" }, + "bg_caution_100": { "declaration": "background-color: var(--caution_100);" }, + "bg_info_00": { "declaration": "background-color: var(--info_00);" }, + "bg_info_05": { "declaration": "background-color: var(--info_05);" }, + "bg_info_10": { "declaration": "background-color: var(--info_10);" }, + "bg_info_20": { "declaration": "background-color: var(--info_20);" }, + "bg_info_30": { "declaration": "background-color: var(--info_30);" }, + "bg_info_40": { "declaration": "background-color: var(--info_40);" }, + "bg_info_50": { "declaration": "background-color: var(--info_50);" }, + "bg_info_60": { "declaration": "background-color: var(--info_60);" }, + "bg_info_70": { "declaration": "background-color: var(--info_70);" }, + "bg_info_80": { "declaration": "background-color: var(--info_80);" }, + "bg_info_90": { "declaration": "background-color: var(--info_90);" }, + "bg_info_95": { "declaration": "background-color: var(--info_95);" }, + "bg_info_100": { "declaration": "background-color: var(--info_100);" }, + "darken_00": { "declaration": "background-color: var(--darken_00);" }, + "darken_05": { "declaration": "background-color: var(--darken_05);" }, + "darken_10": { "declaration": "background-color: var(--darken_10);" }, + "darken_20": { "declaration": "background-color: var(--darken_20);" }, + "darken_30": { "declaration": "background-color: var(--darken_30);" }, + "darken_40": { "declaration": "background-color: var(--darken_40);" }, + "darken_50": { "declaration": "background-color: var(--darken_50);" }, + "darken_60": { "declaration": "background-color: var(--darken_60);" }, + "darken_70": { "declaration": "background-color: var(--darken_70);" }, + "darken_80": { "declaration": "background-color: var(--darken_80);" }, + "darken_90": { "declaration": "background-color: var(--darken_90);" }, + "darken_95": { "declaration": "background-color: var(--darken_95);" }, + "darken_100": { "declaration": "background-color: var(--darken_100);" }, + "lighten_00": { "declaration": "background-color: var(--lighten_00);" }, + "lighten_05": { "declaration": "background-color: var(--lighten_05);" }, + "lighten_10": { "declaration": "background-color: var(--lighten_10);" }, + "lighten_20": { "declaration": "background-color: var(--lighten_20);" }, + "lighten_30": { "declaration": "background-color: var(--lighten_30);" }, + "lighten_40": { "declaration": "background-color: var(--lighten_40);" }, + "lighten_50": { "declaration": "background-color: var(--lighten_50);" }, + "lighten_60": { "declaration": "background-color: var(--lighten_60);" }, + "lighten_70": { "declaration": "background-color: var(--lighten_70);" }, + "lighten_80": { "declaration": "background-color: var(--lighten_80);" }, + "lighten_90": { "declaration": "background-color: var(--lighten_90);" }, + "lighten_95": { "declaration": "background-color: var(--lighten_95);" }, + "lighten_100": { "declaration": "background-color: var(--lighten_100);" }, + "fg_00": { "declaration": "background-color: var(--fg_00);" }, + "fg_05": { "declaration": "background-color: var(--fg_05);" }, + "fg_10": { "declaration": "background-color: var(--fg_10);" }, + "fg_20": { "declaration": "background-color: var(--fg_20);" }, + "fg_30": { "declaration": "background-color: var(--fg_30);" }, + "fg_40": { "declaration": "background-color: var(--fg_40);" }, + "fg_50": { "declaration": "background-color: var(--fg_50);" }, + "fg_60": { "declaration": "background-color: var(--fg_60);" }, + "fg_70": { "declaration": "background-color: var(--fg_70);" }, + "fg_80": { "declaration": "background-color: var(--fg_80);" }, + "fg_90": { "declaration": "background-color: var(--fg_90);" }, + "fg_95": { "declaration": "background-color: var(--fg_95);" }, + "fg_100": { "declaration": "background-color: var(--fg_100);" }, + "bg_00": { "declaration": "background-color: var(--bg_00);" }, + "bg_05": { "declaration": "background-color: var(--bg_05);" }, + "bg_10": { "declaration": "background-color: var(--bg_10);" }, + "bg_20": { "declaration": "background-color: var(--bg_20);" }, + "bg_30": { "declaration": "background-color: var(--bg_30);" }, + "bg_40": { "declaration": "background-color: var(--bg_40);" }, + "bg_50": { "declaration": "background-color: var(--bg_50);" }, + "bg_60": { "declaration": "background-color: var(--bg_60);" }, + "bg_70": { "declaration": "background-color: var(--bg_70);" }, + "bg_80": { "declaration": "background-color: var(--bg_80);" }, + "bg_90": { "declaration": "background-color: var(--bg_90);" }, + "bg_95": { "declaration": "background-color: var(--bg_95);" }, + "bg_100": { "declaration": "background-color: var(--bg_100);" }, + "border_color_00": { + "declaration": "border-color: var(--border_color_00); --border_color: var(--border_color_00);" }, - "bg_a_30": { - "declaration": "background-color: var(--palette_a_30);" + "border_color_05": { + "declaration": "border-color: var(--border_color_05); --border_color: var(--border_color_05);" }, - "bg_a_40": { - "declaration": "background-color: var(--palette_a_40);" + "border_color_10": { + "declaration": "border-color: var(--border_color_10); --border_color: var(--border_color_10);" }, - "bg_a_50": { - "declaration": "background-color: var(--palette_a_50);" + "border_color_20": { + "declaration": "border-color: var(--border_color_20); --border_color: var(--border_color_20);" }, - "bg_a_60": { - "declaration": "background-color: var(--palette_a_60);" + "border_color_30": { + "declaration": "border-color: var(--border_color_30); --border_color: var(--border_color_30);" }, - "bg_a_70": { - "declaration": "background-color: var(--palette_a_70);" + "border_color_40": { + "declaration": "border-color: var(--border_color_40); --border_color: var(--border_color_40);" }, - "bg_a_80": { - "declaration": "background-color: var(--palette_a_80);" + "border_color_50": { + "declaration": "border-color: var(--border_color_50); --border_color: var(--border_color_50);" }, - "bg_a_90": { - "declaration": "background-color: var(--palette_a_90);" + "border_color_60": { + "declaration": "border-color: var(--border_color_60); --border_color: var(--border_color_60);" }, - "bg_a_95": { - "declaration": "background-color: var(--palette_a_95);" + "border_color_70": { + "declaration": "border-color: var(--border_color_70); --border_color: var(--border_color_70);" }, - "bg_a_100": { - "declaration": "background-color: var(--palette_a_100);" + "border_color_80": { + "declaration": "border-color: var(--border_color_80); --border_color: var(--border_color_80);" }, - "bg_b_00": { - "declaration": "background-color: var(--palette_b_00);" + "border_color_90": { + "declaration": "border-color: var(--border_color_90); --border_color: var(--border_color_90);" }, - "bg_b_05": { - "declaration": "background-color: var(--palette_b_05);" + "border_color_95": { + "declaration": "border-color: var(--border_color_95); --border_color: var(--border_color_95);" }, - "bg_b_10": { - "declaration": "background-color: var(--palette_b_10);" + "border_color_100": { + "declaration": "border-color: var(--border_color_100); --border_color: var(--border_color_100);" }, - "bg_b_20": { - "declaration": "background-color: var(--palette_b_20);" + "border_a_00": { + "declaration": "border-color: var(--palette_a_00); --border_color: var(--palette_a_00);" }, - "bg_b_30": { - "declaration": "background-color: var(--palette_b_30);" + "border_a_05": { + "declaration": "border-color: var(--palette_a_05); --border_color: var(--palette_a_05);" }, - "bg_b_40": { - "declaration": "background-color: var(--palette_b_40);" + "border_a_10": { + "declaration": "border-color: var(--palette_a_10); --border_color: var(--palette_a_10);" }, - "bg_b_50": { - "declaration": "background-color: var(--palette_b_50);" + "border_a_20": { + "declaration": "border-color: var(--palette_a_20); --border_color: var(--palette_a_20);" }, - "bg_b_60": { - "declaration": "background-color: var(--palette_b_60);" + "border_a_30": { + "declaration": "border-color: var(--palette_a_30); --border_color: var(--palette_a_30);" }, - "bg_b_70": { - "declaration": "background-color: var(--palette_b_70);" + "border_a_40": { + "declaration": "border-color: var(--palette_a_40); --border_color: var(--palette_a_40);" }, - "bg_b_80": { - "declaration": "background-color: var(--palette_b_80);" + "border_a_50": { + "declaration": "border-color: var(--palette_a_50); --border_color: var(--palette_a_50);" }, - "bg_b_90": { - "declaration": "background-color: var(--palette_b_90);" + "border_a_60": { + "declaration": "border-color: var(--palette_a_60); --border_color: var(--palette_a_60);" }, - "bg_b_95": { - "declaration": "background-color: var(--palette_b_95);" + "border_a_70": { + "declaration": "border-color: var(--palette_a_70); --border_color: var(--palette_a_70);" }, - "bg_b_100": { - "declaration": "background-color: var(--palette_b_100);" + "border_a_80": { + "declaration": "border-color: var(--palette_a_80); --border_color: var(--palette_a_80);" }, - "bg_c_00": { - "declaration": "background-color: var(--palette_c_00);" + "border_a_90": { + "declaration": "border-color: var(--palette_a_90); --border_color: var(--palette_a_90);" }, - "bg_c_05": { - "declaration": "background-color: var(--palette_c_05);" + "border_a_95": { + "declaration": "border-color: var(--palette_a_95); --border_color: var(--palette_a_95);" }, - "bg_c_10": { - "declaration": "background-color: var(--palette_c_10);" + "border_a_100": { + "declaration": "border-color: var(--palette_a_100); --border_color: var(--palette_a_100);" }, - "bg_c_20": { - "declaration": "background-color: var(--palette_c_20);" + "border_b_00": { + "declaration": "border-color: var(--palette_b_00); --border_color: var(--palette_b_00);" }, - "bg_c_30": { - "declaration": "background-color: var(--palette_c_30);" + "border_b_05": { + "declaration": "border-color: var(--palette_b_05); --border_color: var(--palette_b_05);" }, - "bg_c_40": { - "declaration": "background-color: var(--palette_c_40);" + "border_b_10": { + "declaration": "border-color: var(--palette_b_10); --border_color: var(--palette_b_10);" }, - "bg_c_50": { - "declaration": "background-color: var(--palette_c_50);" + "border_b_20": { + "declaration": "border-color: var(--palette_b_20); --border_color: var(--palette_b_20);" }, - "bg_c_60": { - "declaration": "background-color: var(--palette_c_60);" + "border_b_30": { + "declaration": "border-color: var(--palette_b_30); --border_color: var(--palette_b_30);" }, - "bg_c_70": { - "declaration": "background-color: var(--palette_c_70);" + "border_b_40": { + "declaration": "border-color: var(--palette_b_40); --border_color: var(--palette_b_40);" }, - "bg_c_80": { - "declaration": "background-color: var(--palette_c_80);" + "border_b_50": { + "declaration": "border-color: var(--palette_b_50); --border_color: var(--palette_b_50);" }, - "bg_c_90": { - "declaration": "background-color: var(--palette_c_90);" + "border_b_60": { + "declaration": "border-color: var(--palette_b_60); --border_color: var(--palette_b_60);" }, - "bg_c_95": { - "declaration": "background-color: var(--palette_c_95);" + "border_b_70": { + "declaration": "border-color: var(--palette_b_70); --border_color: var(--palette_b_70);" }, - "bg_c_100": { - "declaration": "background-color: var(--palette_c_100);" + "border_b_80": { + "declaration": "border-color: var(--palette_b_80); --border_color: var(--palette_b_80);" }, - "bg_d_00": { - "declaration": "background-color: var(--palette_d_00);" + "border_b_90": { + "declaration": "border-color: var(--palette_b_90); --border_color: var(--palette_b_90);" }, - "bg_d_05": { - "declaration": "background-color: var(--palette_d_05);" + "border_b_95": { + "declaration": "border-color: var(--palette_b_95); --border_color: var(--palette_b_95);" }, - "bg_d_10": { - "declaration": "background-color: var(--palette_d_10);" + "border_b_100": { + "declaration": "border-color: var(--palette_b_100); --border_color: var(--palette_b_100);" }, - "bg_d_20": { - "declaration": "background-color: var(--palette_d_20);" + "border_c_00": { + "declaration": "border-color: var(--palette_c_00); --border_color: var(--palette_c_00);" }, - "bg_d_30": { - "declaration": "background-color: var(--palette_d_30);" + "border_c_05": { + "declaration": "border-color: var(--palette_c_05); --border_color: var(--palette_c_05);" }, - "bg_d_40": { - "declaration": "background-color: var(--palette_d_40);" + "border_c_10": { + "declaration": "border-color: var(--palette_c_10); --border_color: var(--palette_c_10);" }, - "bg_d_50": { - "declaration": "background-color: var(--palette_d_50);" + "border_c_20": { + "declaration": "border-color: var(--palette_c_20); --border_color: var(--palette_c_20);" }, - "bg_d_60": { - "declaration": "background-color: var(--palette_d_60);" + "border_c_30": { + "declaration": "border-color: var(--palette_c_30); --border_color: var(--palette_c_30);" }, - "bg_d_70": { - "declaration": "background-color: var(--palette_d_70);" + "border_c_40": { + "declaration": "border-color: var(--palette_c_40); --border_color: var(--palette_c_40);" }, - "bg_d_80": { - "declaration": "background-color: var(--palette_d_80);" + "border_c_50": { + "declaration": "border-color: var(--palette_c_50); --border_color: var(--palette_c_50);" }, - "bg_d_90": { - "declaration": "background-color: var(--palette_d_90);" + "border_c_60": { + "declaration": "border-color: var(--palette_c_60); --border_color: var(--palette_c_60);" }, - "bg_d_95": { - "declaration": "background-color: var(--palette_d_95);" + "border_c_70": { + "declaration": "border-color: var(--palette_c_70); --border_color: var(--palette_c_70);" }, - "bg_d_100": { - "declaration": "background-color: var(--palette_d_100);" + "border_c_80": { + "declaration": "border-color: var(--palette_c_80); --border_color: var(--palette_c_80);" }, - "bg_e_00": { - "declaration": "background-color: var(--palette_e_00);" + "border_c_90": { + "declaration": "border-color: var(--palette_c_90); --border_color: var(--palette_c_90);" }, - "bg_e_05": { - "declaration": "background-color: var(--palette_e_05);" + "border_c_95": { + "declaration": "border-color: var(--palette_c_95); --border_color: var(--palette_c_95);" }, - "bg_e_10": { - "declaration": "background-color: var(--palette_e_10);" + "border_c_100": { + "declaration": "border-color: var(--palette_c_100); --border_color: var(--palette_c_100);" }, - "bg_e_20": { - "declaration": "background-color: var(--palette_e_20);" + "border_d_00": { + "declaration": "border-color: var(--palette_d_00); --border_color: var(--palette_d_00);" }, - "bg_e_30": { - "declaration": "background-color: var(--palette_e_30);" + "border_d_05": { + "declaration": "border-color: var(--palette_d_05); --border_color: var(--palette_d_05);" }, - "bg_e_40": { - "declaration": "background-color: var(--palette_e_40);" + "border_d_10": { + "declaration": "border-color: var(--palette_d_10); --border_color: var(--palette_d_10);" }, - "bg_e_50": { - "declaration": "background-color: var(--palette_e_50);" + "border_d_20": { + "declaration": "border-color: var(--palette_d_20); --border_color: var(--palette_d_20);" }, - "bg_e_60": { - "declaration": "background-color: var(--palette_e_60);" + "border_d_30": { + "declaration": "border-color: var(--palette_d_30); --border_color: var(--palette_d_30);" }, - "bg_e_70": { - "declaration": "background-color: var(--palette_e_70);" + "border_d_40": { + "declaration": "border-color: var(--palette_d_40); --border_color: var(--palette_d_40);" }, - "bg_e_80": { - "declaration": "background-color: var(--palette_e_80);" + "border_d_50": { + "declaration": "border-color: var(--palette_d_50); --border_color: var(--palette_d_50);" }, - "bg_e_90": { - "declaration": "background-color: var(--palette_e_90);" + "border_d_60": { + "declaration": "border-color: var(--palette_d_60); --border_color: var(--palette_d_60);" }, - "bg_e_95": { - "declaration": "background-color: var(--palette_e_95);" + "border_d_70": { + "declaration": "border-color: var(--palette_d_70); --border_color: var(--palette_d_70);" }, - "bg_e_100": { - "declaration": "background-color: var(--palette_e_100);" + "border_d_80": { + "declaration": "border-color: var(--palette_d_80); --border_color: var(--palette_d_80);" }, - "bg_f_00": { - "declaration": "background-color: var(--palette_f_00);" + "border_d_90": { + "declaration": "border-color: var(--palette_d_90); --border_color: var(--palette_d_90);" }, - "bg_f_05": { - "declaration": "background-color: var(--palette_f_05);" + "border_d_95": { + "declaration": "border-color: var(--palette_d_95); --border_color: var(--palette_d_95);" }, - "bg_f_10": { - "declaration": "background-color: var(--palette_f_10);" + "border_d_100": { + "declaration": "border-color: var(--palette_d_100); --border_color: var(--palette_d_100);" }, - "bg_f_20": { - "declaration": "background-color: var(--palette_f_20);" + "border_e_00": { + "declaration": "border-color: var(--palette_e_00); --border_color: var(--palette_e_00);" }, - "bg_f_30": { - "declaration": "background-color: var(--palette_f_30);" + "border_e_05": { + "declaration": "border-color: var(--palette_e_05); --border_color: var(--palette_e_05);" }, - "bg_f_40": { - "declaration": "background-color: var(--palette_f_40);" + "border_e_10": { + "declaration": "border-color: var(--palette_e_10); --border_color: var(--palette_e_10);" }, - "bg_f_50": { - "declaration": "background-color: var(--palette_f_50);" + "border_e_20": { + "declaration": "border-color: var(--palette_e_20); --border_color: var(--palette_e_20);" }, - "bg_f_60": { - "declaration": "background-color: var(--palette_f_60);" + "border_e_30": { + "declaration": "border-color: var(--palette_e_30); --border_color: var(--palette_e_30);" }, - "bg_f_70": { - "declaration": "background-color: var(--palette_f_70);" + "border_e_40": { + "declaration": "border-color: var(--palette_e_40); --border_color: var(--palette_e_40);" }, - "bg_f_80": { - "declaration": "background-color: var(--palette_f_80);" + "border_e_50": { + "declaration": "border-color: var(--palette_e_50); --border_color: var(--palette_e_50);" }, - "bg_f_90": { - "declaration": "background-color: var(--palette_f_90);" + "border_e_60": { + "declaration": "border-color: var(--palette_e_60); --border_color: var(--palette_e_60);" }, - "bg_f_95": { - "declaration": "background-color: var(--palette_f_95);" + "border_e_70": { + "declaration": "border-color: var(--palette_e_70); --border_color: var(--palette_e_70);" }, - "bg_f_100": { - "declaration": "background-color: var(--palette_f_100);" + "border_e_80": { + "declaration": "border-color: var(--palette_e_80); --border_color: var(--palette_e_80);" }, - "bg_g_00": { - "declaration": "background-color: var(--palette_g_00);" + "border_e_90": { + "declaration": "border-color: var(--palette_e_90); --border_color: var(--palette_e_90);" }, - "bg_g_05": { - "declaration": "background-color: var(--palette_g_05);" + "border_e_95": { + "declaration": "border-color: var(--palette_e_95); --border_color: var(--palette_e_95);" }, - "bg_g_10": { - "declaration": "background-color: var(--palette_g_10);" + "border_e_100": { + "declaration": "border-color: var(--palette_e_100); --border_color: var(--palette_e_100);" }, - "bg_g_20": { - "declaration": "background-color: var(--palette_g_20);" + "border_f_00": { + "declaration": "border-color: var(--palette_f_00); --border_color: var(--palette_f_00);" }, - "bg_g_30": { - "declaration": "background-color: var(--palette_g_30);" + "border_f_05": { + "declaration": "border-color: var(--palette_f_05); --border_color: var(--palette_f_05);" }, - "bg_g_40": { - "declaration": "background-color: var(--palette_g_40);" + "border_f_10": { + "declaration": "border-color: var(--palette_f_10); --border_color: var(--palette_f_10);" }, - "bg_g_50": { - "declaration": "background-color: var(--palette_g_50);" + "border_f_20": { + "declaration": "border-color: var(--palette_f_20); --border_color: var(--palette_f_20);" }, - "bg_g_60": { - "declaration": "background-color: var(--palette_g_60);" + "border_f_30": { + "declaration": "border-color: var(--palette_f_30); --border_color: var(--palette_f_30);" }, - "bg_g_70": { - "declaration": "background-color: var(--palette_g_70);" + "border_f_40": { + "declaration": "border-color: var(--palette_f_40); --border_color: var(--palette_f_40);" }, - "bg_g_80": { - "declaration": "background-color: var(--palette_g_80);" + "border_f_50": { + "declaration": "border-color: var(--palette_f_50); --border_color: var(--palette_f_50);" }, - "bg_g_90": { - "declaration": "background-color: var(--palette_g_90);" + "border_f_60": { + "declaration": "border-color: var(--palette_f_60); --border_color: var(--palette_f_60);" }, - "bg_g_95": { - "declaration": "background-color: var(--palette_g_95);" + "border_f_70": { + "declaration": "border-color: var(--palette_f_70); --border_color: var(--palette_f_70);" }, - "bg_g_100": { - "declaration": "background-color: var(--palette_g_100);" + "border_f_80": { + "declaration": "border-color: var(--palette_f_80); --border_color: var(--palette_f_80);" }, - "bg_h_00": { - "declaration": "background-color: var(--palette_h_00);" + "border_f_90": { + "declaration": "border-color: var(--palette_f_90); --border_color: var(--palette_f_90);" }, - "bg_h_05": { - "declaration": "background-color: var(--palette_h_05);" + "border_f_95": { + "declaration": "border-color: var(--palette_f_95); --border_color: var(--palette_f_95);" }, - "bg_h_10": { - "declaration": "background-color: var(--palette_h_10);" + "border_f_100": { + "declaration": "border-color: var(--palette_f_100); --border_color: var(--palette_f_100);" }, - "bg_h_20": { - "declaration": "background-color: var(--palette_h_20);" + "border_g_00": { + "declaration": "border-color: var(--palette_g_00); --border_color: var(--palette_g_00);" }, - "bg_h_30": { - "declaration": "background-color: var(--palette_h_30);" + "border_g_05": { + "declaration": "border-color: var(--palette_g_05); --border_color: var(--palette_g_05);" }, - "bg_h_40": { - "declaration": "background-color: var(--palette_h_40);" + "border_g_10": { + "declaration": "border-color: var(--palette_g_10); --border_color: var(--palette_g_10);" }, - "bg_h_50": { - "declaration": "background-color: var(--palette_h_50);" + "border_g_20": { + "declaration": "border-color: var(--palette_g_20); --border_color: var(--palette_g_20);" }, - "bg_h_60": { - "declaration": "background-color: var(--palette_h_60);" + "border_g_30": { + "declaration": "border-color: var(--palette_g_30); --border_color: var(--palette_g_30);" }, - "bg_h_70": { - "declaration": "background-color: var(--palette_h_70);" + "border_g_40": { + "declaration": "border-color: var(--palette_g_40); --border_color: var(--palette_g_40);" }, - "bg_h_80": { - "declaration": "background-color: var(--palette_h_80);" + "border_g_50": { + "declaration": "border-color: var(--palette_g_50); --border_color: var(--palette_g_50);" }, - "bg_h_90": { - "declaration": "background-color: var(--palette_h_90);" + "border_g_60": { + "declaration": "border-color: var(--palette_g_60); --border_color: var(--palette_g_60);" }, - "bg_h_95": { - "declaration": "background-color: var(--palette_h_95);" + "border_g_70": { + "declaration": "border-color: var(--palette_g_70); --border_color: var(--palette_g_70);" }, - "bg_h_100": { - "declaration": "background-color: var(--palette_h_100);" + "border_g_80": { + "declaration": "border-color: var(--palette_g_80); --border_color: var(--palette_g_80);" }, - "bg_i_00": { - "declaration": "background-color: var(--palette_i_00);" - }, - "bg_i_05": { - "declaration": "background-color: var(--palette_i_05);" - }, - "bg_i_10": { - "declaration": "background-color: var(--palette_i_10);" - }, - "bg_i_20": { - "declaration": "background-color: var(--palette_i_20);" - }, - "bg_i_30": { - "declaration": "background-color: var(--palette_i_30);" - }, - "bg_i_40": { - "declaration": "background-color: var(--palette_i_40);" - }, - "bg_i_50": { - "declaration": "background-color: var(--palette_i_50);" - }, - "bg_i_60": { - "declaration": "background-color: var(--palette_i_60);" - }, - "bg_i_70": { - "declaration": "background-color: var(--palette_i_70);" - }, - "bg_i_80": { - "declaration": "background-color: var(--palette_i_80);" - }, - "bg_i_90": { - "declaration": "background-color: var(--palette_i_90);" - }, - "bg_i_95": { - "declaration": "background-color: var(--palette_i_95);" - }, - "bg_i_100": { - "declaration": "background-color: var(--palette_i_100);" - }, - "bg_j_00": { - "declaration": "background-color: var(--palette_j_00);" - }, - "bg_j_05": { - "declaration": "background-color: var(--palette_j_05);" - }, - "bg_j_10": { - "declaration": "background-color: var(--palette_j_10);" - }, - "bg_j_20": { - "declaration": "background-color: var(--palette_j_20);" - }, - "bg_j_30": { - "declaration": "background-color: var(--palette_j_30);" - }, - "bg_j_40": { - "declaration": "background-color: var(--palette_j_40);" - }, - "bg_j_50": { - "declaration": "background-color: var(--palette_j_50);" - }, - "bg_j_60": { - "declaration": "background-color: var(--palette_j_60);" - }, - "bg_j_70": { - "declaration": "background-color: var(--palette_j_70);" - }, - "bg_j_80": { - "declaration": "background-color: var(--palette_j_80);" - }, - "bg_j_90": { - "declaration": "background-color: var(--palette_j_90);" - }, - "bg_j_95": { - "declaration": "background-color: var(--palette_j_95);" - }, - "bg_j_100": { - "declaration": "background-color: var(--palette_j_100);" - }, - "accent_00": { - "declaration": "color: var(--accent_00); --text_color: var(--accent_00);" - }, - "accent_05": { - "declaration": "color: var(--accent_05); --text_color: var(--accent_05);" - }, - "accent_10": { - "declaration": "color: var(--accent_10); --text_color: var(--accent_10);" - }, - "accent_20": { - "declaration": "color: var(--accent_20); --text_color: var(--accent_20);" - }, - "accent_30": { - "declaration": "color: var(--accent_30); --text_color: var(--accent_30);" - }, - "accent_40": { - "declaration": "color: var(--accent_40); --text_color: var(--accent_40);" - }, - "accent_50": { - "declaration": "color: var(--accent_50); --text_color: var(--accent_50);" - }, - "accent_60": { - "declaration": "color: var(--accent_60); --text_color: var(--accent_60);" - }, - "accent_70": { - "declaration": "color: var(--accent_70); --text_color: var(--accent_70);" - }, - "accent_80": { - "declaration": "color: var(--accent_80); --text_color: var(--accent_80);" - }, - "accent_90": { - "declaration": "color: var(--accent_90); --text_color: var(--accent_90);" - }, - "accent_95": { - "declaration": "color: var(--accent_95); --text_color: var(--accent_95);" - }, - "accent_100": { - "declaration": "color: var(--accent_100); --text_color: var(--accent_100);" - }, - "positive_00": { - "declaration": "color: var(--positive_00); --text_color: var(--positive_00);" - }, - "positive_05": { - "declaration": "color: var(--positive_05); --text_color: var(--positive_05);" - }, - "positive_10": { - "declaration": "color: var(--positive_10); --text_color: var(--positive_10);" - }, - "positive_20": { - "declaration": "color: var(--positive_20); --text_color: var(--positive_20);" - }, - "positive_30": { - "declaration": "color: var(--positive_30); --text_color: var(--positive_30);" - }, - "positive_40": { - "declaration": "color: var(--positive_40); --text_color: var(--positive_40);" - }, - "positive_50": { - "declaration": "color: var(--positive_50); --text_color: var(--positive_50);" - }, - "positive_60": { - "declaration": "color: var(--positive_60); --text_color: var(--positive_60);" - }, - "positive_70": { - "declaration": "color: var(--positive_70); --text_color: var(--positive_70);" - }, - "positive_80": { - "declaration": "color: var(--positive_80); --text_color: var(--positive_80);" - }, - "positive_90": { - "declaration": "color: var(--positive_90); --text_color: var(--positive_90);" - }, - "positive_95": { - "declaration": "color: var(--positive_95); --text_color: var(--positive_95);" - }, - "positive_100": { - "declaration": "color: var(--positive_100); --text_color: var(--positive_100);" - }, - "negative_00": { - "declaration": "color: var(--negative_00); --text_color: var(--negative_00);" - }, - "negative_05": { - "declaration": "color: var(--negative_05); --text_color: var(--negative_05);" - }, - "negative_10": { - "declaration": "color: var(--negative_10); --text_color: var(--negative_10);" - }, - "negative_20": { - "declaration": "color: var(--negative_20); --text_color: var(--negative_20);" - }, - "negative_30": { - "declaration": "color: var(--negative_30); --text_color: var(--negative_30);" - }, - "negative_40": { - "declaration": "color: var(--negative_40); --text_color: var(--negative_40);" - }, - "negative_50": { - "declaration": "color: var(--negative_50); --text_color: var(--negative_50);" - }, - "negative_60": { - "declaration": "color: var(--negative_60); --text_color: var(--negative_60);" - }, - "negative_70": { - "declaration": "color: var(--negative_70); --text_color: var(--negative_70);" - }, - "negative_80": { - "declaration": "color: var(--negative_80); --text_color: var(--negative_80);" - }, - "negative_90": { - "declaration": "color: var(--negative_90); --text_color: var(--negative_90);" - }, - "negative_95": { - "declaration": "color: var(--negative_95); --text_color: var(--negative_95);" - }, - "negative_100": { - "declaration": "color: var(--negative_100); --text_color: var(--negative_100);" - }, - "caution_00": { - "declaration": "color: var(--caution_00); --text_color: var(--caution_00);" - }, - "caution_05": { - "declaration": "color: var(--caution_05); --text_color: var(--caution_05);" - }, - "caution_10": { - "declaration": "color: var(--caution_10); --text_color: var(--caution_10);" - }, - "caution_20": { - "declaration": "color: var(--caution_20); --text_color: var(--caution_20);" - }, - "caution_30": { - "declaration": "color: var(--caution_30); --text_color: var(--caution_30);" - }, - "caution_40": { - "declaration": "color: var(--caution_40); --text_color: var(--caution_40);" - }, - "caution_50": { - "declaration": "color: var(--caution_50); --text_color: var(--caution_50);" - }, - "caution_60": { - "declaration": "color: var(--caution_60); --text_color: var(--caution_60);" - }, - "caution_70": { - "declaration": "color: var(--caution_70); --text_color: var(--caution_70);" - }, - "caution_80": { - "declaration": "color: var(--caution_80); --text_color: var(--caution_80);" - }, - "caution_90": { - "declaration": "color: var(--caution_90); --text_color: var(--caution_90);" - }, - "caution_95": { - "declaration": "color: var(--caution_95); --text_color: var(--caution_95);" - }, - "caution_100": { - "declaration": "color: var(--caution_100); --text_color: var(--caution_100);" - }, - "info_00": { - "declaration": "color: var(--info_00); --text_color: var(--info_00);" - }, - "info_05": { - "declaration": "color: var(--info_05); --text_color: var(--info_05);" - }, - "info_10": { - "declaration": "color: var(--info_10); --text_color: var(--info_10);" - }, - "info_20": { - "declaration": "color: var(--info_20); --text_color: var(--info_20);" - }, - "info_30": { - "declaration": "color: var(--info_30); --text_color: var(--info_30);" - }, - "info_40": { - "declaration": "color: var(--info_40); --text_color: var(--info_40);" - }, - "info_50": { - "declaration": "color: var(--info_50); --text_color: var(--info_50);" - }, - "info_60": { - "declaration": "color: var(--info_60); --text_color: var(--info_60);" - }, - "info_70": { - "declaration": "color: var(--info_70); --text_color: var(--info_70);" - }, - "info_80": { - "declaration": "color: var(--info_80); --text_color: var(--info_80);" - }, - "info_90": { - "declaration": "color: var(--info_90); --text_color: var(--info_90);" - }, - "info_95": { - "declaration": "color: var(--info_95); --text_color: var(--info_95);" - }, - "info_100": { - "declaration": "color: var(--info_100); --text_color: var(--info_100);" - }, - "bg_accent_00": { - "declaration": "background-color: var(--accent_00);" - }, - "bg_accent_05": { - "declaration": "background-color: var(--accent_05);" - }, - "bg_accent_10": { - "declaration": "background-color: var(--accent_10);" - }, - "bg_accent_20": { - "declaration": "background-color: var(--accent_20);" - }, - "bg_accent_30": { - "declaration": "background-color: var(--accent_30);" - }, - "bg_accent_40": { - "declaration": "background-color: var(--accent_40);" - }, - "bg_accent_50": { - "declaration": "background-color: var(--accent_50);" - }, - "bg_accent_60": { - "declaration": "background-color: var(--accent_60);" - }, - "bg_accent_70": { - "declaration": "background-color: var(--accent_70);" - }, - "bg_accent_80": { - "declaration": "background-color: var(--accent_80);" - }, - "bg_accent_90": { - "declaration": "background-color: var(--accent_90);" - }, - "bg_accent_95": { - "declaration": "background-color: var(--accent_95);" - }, - "bg_accent_100": { - "declaration": "background-color: var(--accent_100);" - }, - "bg_positive_00": { - "declaration": "background-color: var(--positive_00);" - }, - "bg_positive_05": { - "declaration": "background-color: var(--positive_05);" - }, - "bg_positive_10": { - "declaration": "background-color: var(--positive_10);" - }, - "bg_positive_20": { - "declaration": "background-color: var(--positive_20);" - }, - "bg_positive_30": { - "declaration": "background-color: var(--positive_30);" - }, - "bg_positive_40": { - "declaration": "background-color: var(--positive_40);" - }, - "bg_positive_50": { - "declaration": "background-color: var(--positive_50);" - }, - "bg_positive_60": { - "declaration": "background-color: var(--positive_60);" - }, - "bg_positive_70": { - "declaration": "background-color: var(--positive_70);" - }, - "bg_positive_80": { - "declaration": "background-color: var(--positive_80);" - }, - "bg_positive_90": { - "declaration": "background-color: var(--positive_90);" - }, - "bg_positive_95": { - "declaration": "background-color: var(--positive_95);" - }, - "bg_positive_100": { - "declaration": "background-color: var(--positive_100);" - }, - "bg_negative_00": { - "declaration": "background-color: var(--negative_00);" - }, - "bg_negative_05": { - "declaration": "background-color: var(--negative_05);" - }, - "bg_negative_10": { - "declaration": "background-color: var(--negative_10);" - }, - "bg_negative_20": { - "declaration": "background-color: var(--negative_20);" - }, - "bg_negative_30": { - "declaration": "background-color: var(--negative_30);" - }, - "bg_negative_40": { - "declaration": "background-color: var(--negative_40);" - }, - "bg_negative_50": { - "declaration": "background-color: var(--negative_50);" - }, - "bg_negative_60": { - "declaration": "background-color: var(--negative_60);" - }, - "bg_negative_70": { - "declaration": "background-color: var(--negative_70);" - }, - "bg_negative_80": { - "declaration": "background-color: var(--negative_80);" - }, - "bg_negative_90": { - "declaration": "background-color: var(--negative_90);" - }, - "bg_negative_95": { - "declaration": "background-color: var(--negative_95);" - }, - "bg_negative_100": { - "declaration": "background-color: var(--negative_100);" - }, - "bg_caution_00": { - "declaration": "background-color: var(--caution_00);" - }, - "bg_caution_05": { - "declaration": "background-color: var(--caution_05);" - }, - "bg_caution_10": { - "declaration": "background-color: var(--caution_10);" - }, - "bg_caution_20": { - "declaration": "background-color: var(--caution_20);" - }, - "bg_caution_30": { - "declaration": "background-color: var(--caution_30);" - }, - "bg_caution_40": { - "declaration": "background-color: var(--caution_40);" - }, - "bg_caution_50": { - "declaration": "background-color: var(--caution_50);" - }, - "bg_caution_60": { - "declaration": "background-color: var(--caution_60);" - }, - "bg_caution_70": { - "declaration": "background-color: var(--caution_70);" - }, - "bg_caution_80": { - "declaration": "background-color: var(--caution_80);" - }, - "bg_caution_90": { - "declaration": "background-color: var(--caution_90);" - }, - "bg_caution_95": { - "declaration": "background-color: var(--caution_95);" - }, - "bg_caution_100": { - "declaration": "background-color: var(--caution_100);" - }, - "bg_info_00": { - "declaration": "background-color: var(--info_00);" - }, - "bg_info_05": { - "declaration": "background-color: var(--info_05);" - }, - "bg_info_10": { - "declaration": "background-color: var(--info_10);" - }, - "bg_info_20": { - "declaration": "background-color: var(--info_20);" - }, - "bg_info_30": { - "declaration": "background-color: var(--info_30);" - }, - "bg_info_40": { - "declaration": "background-color: var(--info_40);" - }, - "bg_info_50": { - "declaration": "background-color: var(--info_50);" - }, - "bg_info_60": { - "declaration": "background-color: var(--info_60);" - }, - "bg_info_70": { - "declaration": "background-color: var(--info_70);" - }, - "bg_info_80": { - "declaration": "background-color: var(--info_80);" - }, - "bg_info_90": { - "declaration": "background-color: var(--info_90);" - }, - "bg_info_95": { - "declaration": "background-color: var(--info_95);" - }, - "bg_info_100": { - "declaration": "background-color: var(--info_100);" - }, - "darken_00": { - "declaration": "background-color: var(--darken_00);" - }, - "darken_05": { - "declaration": "background-color: var(--darken_05);" - }, - "darken_10": { - "declaration": "background-color: var(--darken_10);" - }, - "darken_20": { - "declaration": "background-color: var(--darken_20);" - }, - "darken_30": { - "declaration": "background-color: var(--darken_30);" - }, - "darken_40": { - "declaration": "background-color: var(--darken_40);" - }, - "darken_50": { - "declaration": "background-color: var(--darken_50);" - }, - "darken_60": { - "declaration": "background-color: var(--darken_60);" - }, - "darken_70": { - "declaration": "background-color: var(--darken_70);" - }, - "darken_80": { - "declaration": "background-color: var(--darken_80);" - }, - "darken_90": { - "declaration": "background-color: var(--darken_90);" - }, - "darken_95": { - "declaration": "background-color: var(--darken_95);" - }, - "darken_100": { - "declaration": "background-color: var(--darken_100);" - }, - "lighten_00": { - "declaration": "background-color: var(--lighten_00);" - }, - "lighten_05": { - "declaration": "background-color: var(--lighten_05);" - }, - "lighten_10": { - "declaration": "background-color: var(--lighten_10);" - }, - "lighten_20": { - "declaration": "background-color: var(--lighten_20);" - }, - "lighten_30": { - "declaration": "background-color: var(--lighten_30);" - }, - "lighten_40": { - "declaration": "background-color: var(--lighten_40);" - }, - "lighten_50": { - "declaration": "background-color: var(--lighten_50);" - }, - "lighten_60": { - "declaration": "background-color: var(--lighten_60);" - }, - "lighten_70": { - "declaration": "background-color: var(--lighten_70);" - }, - "lighten_80": { - "declaration": "background-color: var(--lighten_80);" - }, - "lighten_90": { - "declaration": "background-color: var(--lighten_90);" - }, - "lighten_95": { - "declaration": "background-color: var(--lighten_95);" - }, - "lighten_100": { - "declaration": "background-color: var(--lighten_100);" - }, - "fg_00": { - "declaration": "background-color: var(--fg_00);" - }, - "fg_05": { - "declaration": "background-color: var(--fg_05);" - }, - "fg_10": { - "declaration": "background-color: var(--fg_10);" - }, - "fg_20": { - "declaration": "background-color: var(--fg_20);" - }, - "fg_30": { - "declaration": "background-color: var(--fg_30);" - }, - "fg_40": { - "declaration": "background-color: var(--fg_40);" - }, - "fg_50": { - "declaration": "background-color: var(--fg_50);" - }, - "fg_60": { - "declaration": "background-color: var(--fg_60);" - }, - "fg_70": { - "declaration": "background-color: var(--fg_70);" - }, - "fg_80": { - "declaration": "background-color: var(--fg_80);" - }, - "fg_90": { - "declaration": "background-color: var(--fg_90);" - }, - "fg_95": { - "declaration": "background-color: var(--fg_95);" - }, - "fg_100": { - "declaration": "background-color: var(--fg_100);" - }, - "bg_00": { - "declaration": "background-color: var(--bg_00);" - }, - "bg_05": { - "declaration": "background-color: var(--bg_05);" - }, - "bg_10": { - "declaration": "background-color: var(--bg_10);" - }, - "bg_20": { - "declaration": "background-color: var(--bg_20);" - }, - "bg_30": { - "declaration": "background-color: var(--bg_30);" - }, - "bg_40": { - "declaration": "background-color: var(--bg_40);" - }, - "bg_50": { - "declaration": "background-color: var(--bg_50);" - }, - "bg_60": { - "declaration": "background-color: var(--bg_60);" - }, - "bg_70": { - "declaration": "background-color: var(--bg_70);" - }, - "bg_80": { - "declaration": "background-color: var(--bg_80);" - }, - "bg_90": { - "declaration": "background-color: var(--bg_90);" - }, - "bg_95": { - "declaration": "background-color: var(--bg_95);" - }, - "bg_100": { - "declaration": "background-color: var(--bg_100);" - }, - "border_color_00": { - "declaration": "border-color: var(--border_color_00); --border_color: var(--border_color_00);" - }, - "border_color_05": { - "declaration": "border-color: var(--border_color_05); --border_color: var(--border_color_05);" - }, - "border_color_10": { - "declaration": "border-color: var(--border_color_10); --border_color: var(--border_color_10);" - }, - "border_color_20": { - "declaration": "border-color: var(--border_color_20); --border_color: var(--border_color_20);" - }, - "border_color_30": { - "declaration": "border-color: var(--border_color_30); --border_color: var(--border_color_30);" - }, - "border_color_40": { - "declaration": "border-color: var(--border_color_40); --border_color: var(--border_color_40);" - }, - "border_color_50": { - "declaration": "border-color: var(--border_color_50); --border_color: var(--border_color_50);" - }, - "border_color_60": { - "declaration": "border-color: var(--border_color_60); --border_color: var(--border_color_60);" - }, - "border_color_70": { - "declaration": "border-color: var(--border_color_70); --border_color: var(--border_color_70);" - }, - "border_color_80": { - "declaration": "border-color: var(--border_color_80); --border_color: var(--border_color_80);" - }, - "border_color_90": { - "declaration": "border-color: var(--border_color_90); --border_color: var(--border_color_90);" - }, - "border_color_95": { - "declaration": "border-color: var(--border_color_95); --border_color: var(--border_color_95);" - }, - "border_color_100": { - "declaration": "border-color: var(--border_color_100); --border_color: var(--border_color_100);" - }, - "border_a_00": { - "declaration": "border-color: var(--palette_a_00); --border_color: var(--palette_a_00);" - }, - "border_a_05": { - "declaration": "border-color: var(--palette_a_05); --border_color: var(--palette_a_05);" - }, - "border_a_10": { - "declaration": "border-color: var(--palette_a_10); --border_color: var(--palette_a_10);" - }, - "border_a_20": { - "declaration": "border-color: var(--palette_a_20); --border_color: var(--palette_a_20);" - }, - "border_a_30": { - "declaration": "border-color: var(--palette_a_30); --border_color: var(--palette_a_30);" - }, - "border_a_40": { - "declaration": "border-color: var(--palette_a_40); --border_color: var(--palette_a_40);" - }, - "border_a_50": { - "declaration": "border-color: var(--palette_a_50); --border_color: var(--palette_a_50);" - }, - "border_a_60": { - "declaration": "border-color: var(--palette_a_60); --border_color: var(--palette_a_60);" - }, - "border_a_70": { - "declaration": "border-color: var(--palette_a_70); --border_color: var(--palette_a_70);" - }, - "border_a_80": { - "declaration": "border-color: var(--palette_a_80); --border_color: var(--palette_a_80);" - }, - "border_a_90": { - "declaration": "border-color: var(--palette_a_90); --border_color: var(--palette_a_90);" - }, - "border_a_95": { - "declaration": "border-color: var(--palette_a_95); --border_color: var(--palette_a_95);" - }, - "border_a_100": { - "declaration": "border-color: var(--palette_a_100); --border_color: var(--palette_a_100);" - }, - "border_b_00": { - "declaration": "border-color: var(--palette_b_00); --border_color: var(--palette_b_00);" - }, - "border_b_05": { - "declaration": "border-color: var(--palette_b_05); --border_color: var(--palette_b_05);" - }, - "border_b_10": { - "declaration": "border-color: var(--palette_b_10); --border_color: var(--palette_b_10);" - }, - "border_b_20": { - "declaration": "border-color: var(--palette_b_20); --border_color: var(--palette_b_20);" - }, - "border_b_30": { - "declaration": "border-color: var(--palette_b_30); --border_color: var(--palette_b_30);" - }, - "border_b_40": { - "declaration": "border-color: var(--palette_b_40); --border_color: var(--palette_b_40);" - }, - "border_b_50": { - "declaration": "border-color: var(--palette_b_50); --border_color: var(--palette_b_50);" - }, - "border_b_60": { - "declaration": "border-color: var(--palette_b_60); --border_color: var(--palette_b_60);" - }, - "border_b_70": { - "declaration": "border-color: var(--palette_b_70); --border_color: var(--palette_b_70);" - }, - "border_b_80": { - "declaration": "border-color: var(--palette_b_80); --border_color: var(--palette_b_80);" - }, - "border_b_90": { - "declaration": "border-color: var(--palette_b_90); --border_color: var(--palette_b_90);" - }, - "border_b_95": { - "declaration": "border-color: var(--palette_b_95); --border_color: var(--palette_b_95);" - }, - "border_b_100": { - "declaration": "border-color: var(--palette_b_100); --border_color: var(--palette_b_100);" - }, - "border_c_00": { - "declaration": "border-color: var(--palette_c_00); --border_color: var(--palette_c_00);" - }, - "border_c_05": { - "declaration": "border-color: var(--palette_c_05); --border_color: var(--palette_c_05);" - }, - "border_c_10": { - "declaration": "border-color: var(--palette_c_10); --border_color: var(--palette_c_10);" - }, - "border_c_20": { - "declaration": "border-color: var(--palette_c_20); --border_color: var(--palette_c_20);" - }, - "border_c_30": { - "declaration": "border-color: var(--palette_c_30); --border_color: var(--palette_c_30);" - }, - "border_c_40": { - "declaration": "border-color: var(--palette_c_40); --border_color: var(--palette_c_40);" - }, - "border_c_50": { - "declaration": "border-color: var(--palette_c_50); --border_color: var(--palette_c_50);" - }, - "border_c_60": { - "declaration": "border-color: var(--palette_c_60); --border_color: var(--palette_c_60);" - }, - "border_c_70": { - "declaration": "border-color: var(--palette_c_70); --border_color: var(--palette_c_70);" - }, - "border_c_80": { - "declaration": "border-color: var(--palette_c_80); --border_color: var(--palette_c_80);" - }, - "border_c_90": { - "declaration": "border-color: var(--palette_c_90); --border_color: var(--palette_c_90);" - }, - "border_c_95": { - "declaration": "border-color: var(--palette_c_95); --border_color: var(--palette_c_95);" - }, - "border_c_100": { - "declaration": "border-color: var(--palette_c_100); --border_color: var(--palette_c_100);" - }, - "border_d_00": { - "declaration": "border-color: var(--palette_d_00); --border_color: var(--palette_d_00);" - }, - "border_d_05": { - "declaration": "border-color: var(--palette_d_05); --border_color: var(--palette_d_05);" - }, - "border_d_10": { - "declaration": "border-color: var(--palette_d_10); --border_color: var(--palette_d_10);" - }, - "border_d_20": { - "declaration": "border-color: var(--palette_d_20); --border_color: var(--palette_d_20);" - }, - "border_d_30": { - "declaration": "border-color: var(--palette_d_30); --border_color: var(--palette_d_30);" - }, - "border_d_40": { - "declaration": "border-color: var(--palette_d_40); --border_color: var(--palette_d_40);" - }, - "border_d_50": { - "declaration": "border-color: var(--palette_d_50); --border_color: var(--palette_d_50);" - }, - "border_d_60": { - "declaration": "border-color: var(--palette_d_60); --border_color: var(--palette_d_60);" - }, - "border_d_70": { - "declaration": "border-color: var(--palette_d_70); --border_color: var(--palette_d_70);" - }, - "border_d_80": { - "declaration": "border-color: var(--palette_d_80); --border_color: var(--palette_d_80);" - }, - "border_d_90": { - "declaration": "border-color: var(--palette_d_90); --border_color: var(--palette_d_90);" - }, - "border_d_95": { - "declaration": "border-color: var(--palette_d_95); --border_color: var(--palette_d_95);" - }, - "border_d_100": { - "declaration": "border-color: var(--palette_d_100); --border_color: var(--palette_d_100);" - }, - "border_e_00": { - "declaration": "border-color: var(--palette_e_00); --border_color: var(--palette_e_00);" - }, - "border_e_05": { - "declaration": "border-color: var(--palette_e_05); --border_color: var(--palette_e_05);" - }, - "border_e_10": { - "declaration": "border-color: var(--palette_e_10); --border_color: var(--palette_e_10);" - }, - "border_e_20": { - "declaration": "border-color: var(--palette_e_20); --border_color: var(--palette_e_20);" - }, - "border_e_30": { - "declaration": "border-color: var(--palette_e_30); --border_color: var(--palette_e_30);" - }, - "border_e_40": { - "declaration": "border-color: var(--palette_e_40); --border_color: var(--palette_e_40);" - }, - "border_e_50": { - "declaration": "border-color: var(--palette_e_50); --border_color: var(--palette_e_50);" - }, - "border_e_60": { - "declaration": "border-color: var(--palette_e_60); --border_color: var(--palette_e_60);" - }, - "border_e_70": { - "declaration": "border-color: var(--palette_e_70); --border_color: var(--palette_e_70);" - }, - "border_e_80": { - "declaration": "border-color: var(--palette_e_80); --border_color: var(--palette_e_80);" - }, - "border_e_90": { - "declaration": "border-color: var(--palette_e_90); --border_color: var(--palette_e_90);" - }, - "border_e_95": { - "declaration": "border-color: var(--palette_e_95); --border_color: var(--palette_e_95);" - }, - "border_e_100": { - "declaration": "border-color: var(--palette_e_100); --border_color: var(--palette_e_100);" - }, - "border_f_00": { - "declaration": "border-color: var(--palette_f_00); --border_color: var(--palette_f_00);" - }, - "border_f_05": { - "declaration": "border-color: var(--palette_f_05); --border_color: var(--palette_f_05);" - }, - "border_f_10": { - "declaration": "border-color: var(--palette_f_10); --border_color: var(--palette_f_10);" - }, - "border_f_20": { - "declaration": "border-color: var(--palette_f_20); --border_color: var(--palette_f_20);" - }, - "border_f_30": { - "declaration": "border-color: var(--palette_f_30); --border_color: var(--palette_f_30);" - }, - "border_f_40": { - "declaration": "border-color: var(--palette_f_40); --border_color: var(--palette_f_40);" - }, - "border_f_50": { - "declaration": "border-color: var(--palette_f_50); --border_color: var(--palette_f_50);" - }, - "border_f_60": { - "declaration": "border-color: var(--palette_f_60); --border_color: var(--palette_f_60);" - }, - "border_f_70": { - "declaration": "border-color: var(--palette_f_70); --border_color: var(--palette_f_70);" - }, - "border_f_80": { - "declaration": "border-color: var(--palette_f_80); --border_color: var(--palette_f_80);" - }, - "border_f_90": { - "declaration": "border-color: var(--palette_f_90); --border_color: var(--palette_f_90);" - }, - "border_f_95": { - "declaration": "border-color: var(--palette_f_95); --border_color: var(--palette_f_95);" - }, - "border_f_100": { - "declaration": "border-color: var(--palette_f_100); --border_color: var(--palette_f_100);" - }, - "border_g_00": { - "declaration": "border-color: var(--palette_g_00); --border_color: var(--palette_g_00);" - }, - "border_g_05": { - "declaration": "border-color: var(--palette_g_05); --border_color: var(--palette_g_05);" - }, - "border_g_10": { - "declaration": "border-color: var(--palette_g_10); --border_color: var(--palette_g_10);" - }, - "border_g_20": { - "declaration": "border-color: var(--palette_g_20); --border_color: var(--palette_g_20);" - }, - "border_g_30": { - "declaration": "border-color: var(--palette_g_30); --border_color: var(--palette_g_30);" - }, - "border_g_40": { - "declaration": "border-color: var(--palette_g_40); --border_color: var(--palette_g_40);" - }, - "border_g_50": { - "declaration": "border-color: var(--palette_g_50); --border_color: var(--palette_g_50);" - }, - "border_g_60": { - "declaration": "border-color: var(--palette_g_60); --border_color: var(--palette_g_60);" - }, - "border_g_70": { - "declaration": "border-color: var(--palette_g_70); --border_color: var(--palette_g_70);" - }, - "border_g_80": { - "declaration": "border-color: var(--palette_g_80); --border_color: var(--palette_g_80);" - }, - "border_g_90": { - "declaration": "border-color: var(--palette_g_90); --border_color: var(--palette_g_90);" - }, - "border_g_95": { - "declaration": "border-color: var(--palette_g_95); --border_color: var(--palette_g_95);" - }, - "border_g_100": { - "declaration": "border-color: var(--palette_g_100); --border_color: var(--palette_g_100);" - }, - "border_h_00": { - "declaration": "border-color: var(--palette_h_00); --border_color: var(--palette_h_00);" - }, - "border_h_05": { - "declaration": "border-color: var(--palette_h_05); --border_color: var(--palette_h_05);" - }, - "border_h_10": { - "declaration": "border-color: var(--palette_h_10); --border_color: var(--palette_h_10);" - }, - "border_h_20": { - "declaration": "border-color: var(--palette_h_20); --border_color: var(--palette_h_20);" - }, - "border_h_30": { - "declaration": "border-color: var(--palette_h_30); --border_color: var(--palette_h_30);" - }, - "border_h_40": { - "declaration": "border-color: var(--palette_h_40); --border_color: var(--palette_h_40);" - }, - "border_h_50": { - "declaration": "border-color: var(--palette_h_50); --border_color: var(--palette_h_50);" - }, - "border_h_60": { - "declaration": "border-color: var(--palette_h_60); --border_color: var(--palette_h_60);" - }, - "border_h_70": { - "declaration": "border-color: var(--palette_h_70); --border_color: var(--palette_h_70);" - }, - "border_h_80": { - "declaration": "border-color: var(--palette_h_80); --border_color: var(--palette_h_80);" - }, - "border_h_90": { - "declaration": "border-color: var(--palette_h_90); --border_color: var(--palette_h_90);" - }, - "border_h_95": { - "declaration": "border-color: var(--palette_h_95); --border_color: var(--palette_h_95);" - }, - "border_h_100": { - "declaration": "border-color: var(--palette_h_100); --border_color: var(--palette_h_100);" - }, - "border_i_00": { - "declaration": "border-color: var(--palette_i_00); --border_color: var(--palette_i_00);" - }, - "border_i_05": { - "declaration": "border-color: var(--palette_i_05); --border_color: var(--palette_i_05);" - }, - "border_i_10": { - "declaration": "border-color: var(--palette_i_10); --border_color: var(--palette_i_10);" - }, - "border_i_20": { - "declaration": "border-color: var(--palette_i_20); --border_color: var(--palette_i_20);" - }, - "border_i_30": { - "declaration": "border-color: var(--palette_i_30); --border_color: var(--palette_i_30);" - }, - "border_i_40": { - "declaration": "border-color: var(--palette_i_40); --border_color: var(--palette_i_40);" - }, - "border_i_50": { - "declaration": "border-color: var(--palette_i_50); --border_color: var(--palette_i_50);" - }, - "border_i_60": { - "declaration": "border-color: var(--palette_i_60); --border_color: var(--palette_i_60);" - }, - "border_i_70": { - "declaration": "border-color: var(--palette_i_70); --border_color: var(--palette_i_70);" - }, - "border_i_80": { - "declaration": "border-color: var(--palette_i_80); --border_color: var(--palette_i_80);" - }, - "border_i_90": { - "declaration": "border-color: var(--palette_i_90); --border_color: var(--palette_i_90);" - }, - "border_i_95": { - "declaration": "border-color: var(--palette_i_95); --border_color: var(--palette_i_95);" - }, - "border_i_100": { - "declaration": "border-color: var(--palette_i_100); --border_color: var(--palette_i_100);" - }, - "border_j_00": { - "declaration": "border-color: var(--palette_j_00); --border_color: var(--palette_j_00);" - }, - "border_j_05": { - "declaration": "border-color: var(--palette_j_05); --border_color: var(--palette_j_05);" - }, - "border_j_10": { - "declaration": "border-color: var(--palette_j_10); --border_color: var(--palette_j_10);" - }, - "border_j_20": { - "declaration": "border-color: var(--palette_j_20); --border_color: var(--palette_j_20);" - }, - "border_j_30": { - "declaration": "border-color: var(--palette_j_30); --border_color: var(--palette_j_30);" - }, - "border_j_40": { - "declaration": "border-color: var(--palette_j_40); --border_color: var(--palette_j_40);" - }, - "border_j_50": { - "declaration": "border-color: var(--palette_j_50); --border_color: var(--palette_j_50);" - }, - "border_j_60": { - "declaration": "border-color: var(--palette_j_60); --border_color: var(--palette_j_60);" - }, - "border_j_70": { - "declaration": "border-color: var(--palette_j_70); --border_color: var(--palette_j_70);" - }, - "border_j_80": { - "declaration": "border-color: var(--palette_j_80); --border_color: var(--palette_j_80);" - }, - "border_j_90": { - "declaration": "border-color: var(--palette_j_90); --border_color: var(--palette_j_90);" - }, - "border_j_95": { - "declaration": "border-color: var(--palette_j_95); --border_color: var(--palette_j_95);" - }, - "border_j_100": { - "declaration": "border-color: var(--palette_j_100); --border_color: var(--palette_j_100);" - }, - "outline_color_00": { - "declaration": "outline-color: var(--shade_00);" - }, - "outline_color_05": { - "declaration": "outline-color: var(--shade_05);" - }, - "outline_color_10": { - "declaration": "outline-color: var(--shade_10);" - }, - "outline_color_20": { - "declaration": "outline-color: var(--shade_20);" - }, - "outline_color_30": { - "declaration": "outline-color: var(--shade_30);" - }, - "outline_color_40": { - "declaration": "outline-color: var(--shade_40);" - }, - "outline_color_50": { - "declaration": "outline-color: var(--shade_50);" - }, - "outline_color_60": { - "declaration": "outline-color: var(--shade_60);" - }, - "outline_color_70": { - "declaration": "outline-color: var(--shade_70);" - }, - "outline_color_80": { - "declaration": "outline-color: var(--shade_80);" - }, - "outline_color_90": { - "declaration": "outline-color: var(--shade_90);" - }, - "outline_color_95": { - "declaration": "outline-color: var(--shade_95);" - }, - "outline_color_100": { - "declaration": "outline-color: var(--shade_100);" - }, - "outline_a_00": { - "declaration": "outline-color: var(--palette_a_00); --outline_color: var(--palette_a_00);" - }, - "outline_a_05": { - "declaration": "outline-color: var(--palette_a_05); --outline_color: var(--palette_a_05);" - }, - "outline_a_10": { - "declaration": "outline-color: var(--palette_a_10); --outline_color: var(--palette_a_10);" - }, - "outline_a_20": { - "declaration": "outline-color: var(--palette_a_20); --outline_color: var(--palette_a_20);" - }, - "outline_a_30": { - "declaration": "outline-color: var(--palette_a_30); --outline_color: var(--palette_a_30);" - }, - "outline_a_40": { - "declaration": "outline-color: var(--palette_a_40); --outline_color: var(--palette_a_40);" - }, - "outline_a_50": { - "declaration": "outline-color: var(--palette_a_50); --outline_color: var(--palette_a_50);" - }, - "outline_a_60": { - "declaration": "outline-color: var(--palette_a_60); --outline_color: var(--palette_a_60);" - }, - "outline_a_70": { - "declaration": "outline-color: var(--palette_a_70); --outline_color: var(--palette_a_70);" - }, - "outline_a_80": { - "declaration": "outline-color: var(--palette_a_80); --outline_color: var(--palette_a_80);" - }, - "outline_a_90": { - "declaration": "outline-color: var(--palette_a_90); --outline_color: var(--palette_a_90);" - }, - "outline_a_95": { - "declaration": "outline-color: var(--palette_a_95); --outline_color: var(--palette_a_95);" - }, - "outline_a_100": { - "declaration": "outline-color: var(--palette_a_100); --outline_color: var(--palette_a_100);" - }, - "outline_b_00": { - "declaration": "outline-color: var(--palette_b_00); --outline_color: var(--palette_b_00);" - }, - "outline_b_05": { - "declaration": "outline-color: var(--palette_b_05); --outline_color: var(--palette_b_05);" - }, - "outline_b_10": { - "declaration": "outline-color: var(--palette_b_10); --outline_color: var(--palette_b_10);" - }, - "outline_b_20": { - "declaration": "outline-color: var(--palette_b_20); --outline_color: var(--palette_b_20);" - }, - "outline_b_30": { - "declaration": "outline-color: var(--palette_b_30); --outline_color: var(--palette_b_30);" - }, - "outline_b_40": { - "declaration": "outline-color: var(--palette_b_40); --outline_color: var(--palette_b_40);" - }, - "outline_b_50": { - "declaration": "outline-color: var(--palette_b_50); --outline_color: var(--palette_b_50);" - }, - "outline_b_60": { - "declaration": "outline-color: var(--palette_b_60); --outline_color: var(--palette_b_60);" - }, - "outline_b_70": { - "declaration": "outline-color: var(--palette_b_70); --outline_color: var(--palette_b_70);" - }, - "outline_b_80": { - "declaration": "outline-color: var(--palette_b_80); --outline_color: var(--palette_b_80);" - }, - "outline_b_90": { - "declaration": "outline-color: var(--palette_b_90); --outline_color: var(--palette_b_90);" - }, - "outline_b_95": { - "declaration": "outline-color: var(--palette_b_95); --outline_color: var(--palette_b_95);" - }, - "outline_b_100": { - "declaration": "outline-color: var(--palette_b_100); --outline_color: var(--palette_b_100);" - }, - "outline_c_00": { - "declaration": "outline-color: var(--palette_c_00); --outline_color: var(--palette_c_00);" - }, - "outline_c_05": { - "declaration": "outline-color: var(--palette_c_05); --outline_color: var(--palette_c_05);" - }, - "outline_c_10": { - "declaration": "outline-color: var(--palette_c_10); --outline_color: var(--palette_c_10);" - }, - "outline_c_20": { - "declaration": "outline-color: var(--palette_c_20); --outline_color: var(--palette_c_20);" - }, - "outline_c_30": { - "declaration": "outline-color: var(--palette_c_30); --outline_color: var(--palette_c_30);" - }, - "outline_c_40": { - "declaration": "outline-color: var(--palette_c_40); --outline_color: var(--palette_c_40);" - }, - "outline_c_50": { - "declaration": "outline-color: var(--palette_c_50); --outline_color: var(--palette_c_50);" - }, - "outline_c_60": { - "declaration": "outline-color: var(--palette_c_60); --outline_color: var(--palette_c_60);" - }, - "outline_c_70": { - "declaration": "outline-color: var(--palette_c_70); --outline_color: var(--palette_c_70);" - }, - "outline_c_80": { - "declaration": "outline-color: var(--palette_c_80); --outline_color: var(--palette_c_80);" - }, - "outline_c_90": { - "declaration": "outline-color: var(--palette_c_90); --outline_color: var(--palette_c_90);" - }, - "outline_c_95": { - "declaration": "outline-color: var(--palette_c_95); --outline_color: var(--palette_c_95);" - }, - "outline_c_100": { - "declaration": "outline-color: var(--palette_c_100); --outline_color: var(--palette_c_100);" - }, - "outline_d_00": { - "declaration": "outline-color: var(--palette_d_00); --outline_color: var(--palette_d_00);" - }, - "outline_d_05": { - "declaration": "outline-color: var(--palette_d_05); --outline_color: var(--palette_d_05);" - }, - "outline_d_10": { - "declaration": "outline-color: var(--palette_d_10); --outline_color: var(--palette_d_10);" - }, - "outline_d_20": { - "declaration": "outline-color: var(--palette_d_20); --outline_color: var(--palette_d_20);" - }, - "outline_d_30": { - "declaration": "outline-color: var(--palette_d_30); --outline_color: var(--palette_d_30);" - }, - "outline_d_40": { - "declaration": "outline-color: var(--palette_d_40); --outline_color: var(--palette_d_40);" - }, - "outline_d_50": { - "declaration": "outline-color: var(--palette_d_50); --outline_color: var(--palette_d_50);" - }, - "outline_d_60": { - "declaration": "outline-color: var(--palette_d_60); --outline_color: var(--palette_d_60);" - }, - "outline_d_70": { - "declaration": "outline-color: var(--palette_d_70); --outline_color: var(--palette_d_70);" - }, - "outline_d_80": { - "declaration": "outline-color: var(--palette_d_80); --outline_color: var(--palette_d_80);" - }, - "outline_d_90": { - "declaration": "outline-color: var(--palette_d_90); --outline_color: var(--palette_d_90);" - }, - "outline_d_95": { - "declaration": "outline-color: var(--palette_d_95); --outline_color: var(--palette_d_95);" - }, - "outline_d_100": { - "declaration": "outline-color: var(--palette_d_100); --outline_color: var(--palette_d_100);" - }, - "outline_e_00": { - "declaration": "outline-color: var(--palette_e_00); --outline_color: var(--palette_e_00);" - }, - "outline_e_05": { - "declaration": "outline-color: var(--palette_e_05); --outline_color: var(--palette_e_05);" - }, - "outline_e_10": { - "declaration": "outline-color: var(--palette_e_10); --outline_color: var(--palette_e_10);" - }, - "outline_e_20": { - "declaration": "outline-color: var(--palette_e_20); --outline_color: var(--palette_e_20);" - }, - "outline_e_30": { - "declaration": "outline-color: var(--palette_e_30); --outline_color: var(--palette_e_30);" - }, - "outline_e_40": { - "declaration": "outline-color: var(--palette_e_40); --outline_color: var(--palette_e_40);" - }, - "outline_e_50": { - "declaration": "outline-color: var(--palette_e_50); --outline_color: var(--palette_e_50);" - }, - "outline_e_60": { - "declaration": "outline-color: var(--palette_e_60); --outline_color: var(--palette_e_60);" - }, - "outline_e_70": { - "declaration": "outline-color: var(--palette_e_70); --outline_color: var(--palette_e_70);" - }, - "outline_e_80": { - "declaration": "outline-color: var(--palette_e_80); --outline_color: var(--palette_e_80);" - }, - "outline_e_90": { - "declaration": "outline-color: var(--palette_e_90); --outline_color: var(--palette_e_90);" - }, - "outline_e_95": { - "declaration": "outline-color: var(--palette_e_95); --outline_color: var(--palette_e_95);" - }, - "outline_e_100": { - "declaration": "outline-color: var(--palette_e_100); --outline_color: var(--palette_e_100);" - }, - "outline_f_00": { - "declaration": "outline-color: var(--palette_f_00); --outline_color: var(--palette_f_00);" - }, - "outline_f_05": { - "declaration": "outline-color: var(--palette_f_05); --outline_color: var(--palette_f_05);" - }, - "outline_f_10": { - "declaration": "outline-color: var(--palette_f_10); --outline_color: var(--palette_f_10);" - }, - "outline_f_20": { - "declaration": "outline-color: var(--palette_f_20); --outline_color: var(--palette_f_20);" - }, - "outline_f_30": { - "declaration": "outline-color: var(--palette_f_30); --outline_color: var(--palette_f_30);" - }, - "outline_f_40": { - "declaration": "outline-color: var(--palette_f_40); --outline_color: var(--palette_f_40);" - }, - "outline_f_50": { - "declaration": "outline-color: var(--palette_f_50); --outline_color: var(--palette_f_50);" - }, - "outline_f_60": { - "declaration": "outline-color: var(--palette_f_60); --outline_color: var(--palette_f_60);" - }, - "outline_f_70": { - "declaration": "outline-color: var(--palette_f_70); --outline_color: var(--palette_f_70);" - }, - "outline_f_80": { - "declaration": "outline-color: var(--palette_f_80); --outline_color: var(--palette_f_80);" - }, - "outline_f_90": { - "declaration": "outline-color: var(--palette_f_90); --outline_color: var(--palette_f_90);" - }, - "outline_f_95": { - "declaration": "outline-color: var(--palette_f_95); --outline_color: var(--palette_f_95);" - }, - "outline_f_100": { - "declaration": "outline-color: var(--palette_f_100); --outline_color: var(--palette_f_100);" - }, - "outline_g_00": { - "declaration": "outline-color: var(--palette_g_00); --outline_color: var(--palette_g_00);" - }, - "outline_g_05": { - "declaration": "outline-color: var(--palette_g_05); --outline_color: var(--palette_g_05);" - }, - "outline_g_10": { - "declaration": "outline-color: var(--palette_g_10); --outline_color: var(--palette_g_10);" - }, - "outline_g_20": { - "declaration": "outline-color: var(--palette_g_20); --outline_color: var(--palette_g_20);" - }, - "outline_g_30": { - "declaration": "outline-color: var(--palette_g_30); --outline_color: var(--palette_g_30);" - }, - "outline_g_40": { - "declaration": "outline-color: var(--palette_g_40); --outline_color: var(--palette_g_40);" - }, - "outline_g_50": { - "declaration": "outline-color: var(--palette_g_50); --outline_color: var(--palette_g_50);" - }, - "outline_g_60": { - "declaration": "outline-color: var(--palette_g_60); --outline_color: var(--palette_g_60);" - }, - "outline_g_70": { - "declaration": "outline-color: var(--palette_g_70); --outline_color: var(--palette_g_70);" - }, - "outline_g_80": { - "declaration": "outline-color: var(--palette_g_80); --outline_color: var(--palette_g_80);" - }, - "outline_g_90": { - "declaration": "outline-color: var(--palette_g_90); --outline_color: var(--palette_g_90);" - }, - "outline_g_95": { - "declaration": "outline-color: var(--palette_g_95); --outline_color: var(--palette_g_95);" - }, - "outline_g_100": { - "declaration": "outline-color: var(--palette_g_100); --outline_color: var(--palette_g_100);" - }, - "outline_h_00": { - "declaration": "outline-color: var(--palette_h_00); --outline_color: var(--palette_h_00);" - }, - "outline_h_05": { - "declaration": "outline-color: var(--palette_h_05); --outline_color: var(--palette_h_05);" - }, - "outline_h_10": { - "declaration": "outline-color: var(--palette_h_10); --outline_color: var(--palette_h_10);" - }, - "outline_h_20": { - "declaration": "outline-color: var(--palette_h_20); --outline_color: var(--palette_h_20);" - }, - "outline_h_30": { - "declaration": "outline-color: var(--palette_h_30); --outline_color: var(--palette_h_30);" - }, - "outline_h_40": { - "declaration": "outline-color: var(--palette_h_40); --outline_color: var(--palette_h_40);" - }, - "outline_h_50": { - "declaration": "outline-color: var(--palette_h_50); --outline_color: var(--palette_h_50);" - }, - "outline_h_60": { - "declaration": "outline-color: var(--palette_h_60); --outline_color: var(--palette_h_60);" - }, - "outline_h_70": { - "declaration": "outline-color: var(--palette_h_70); --outline_color: var(--palette_h_70);" - }, - "outline_h_80": { - "declaration": "outline-color: var(--palette_h_80); --outline_color: var(--palette_h_80);" - }, - "outline_h_90": { - "declaration": "outline-color: var(--palette_h_90); --outline_color: var(--palette_h_90);" - }, - "outline_h_95": { - "declaration": "outline-color: var(--palette_h_95); --outline_color: var(--palette_h_95);" - }, - "outline_h_100": { - "declaration": "outline-color: var(--palette_h_100); --outline_color: var(--palette_h_100);" - }, - "outline_i_00": { - "declaration": "outline-color: var(--palette_i_00); --outline_color: var(--palette_i_00);" - }, - "outline_i_05": { - "declaration": "outline-color: var(--palette_i_05); --outline_color: var(--palette_i_05);" - }, - "outline_i_10": { - "declaration": "outline-color: var(--palette_i_10); --outline_color: var(--palette_i_10);" - }, - "outline_i_20": { - "declaration": "outline-color: var(--palette_i_20); --outline_color: var(--palette_i_20);" - }, - "outline_i_30": { - "declaration": "outline-color: var(--palette_i_30); --outline_color: var(--palette_i_30);" - }, - "outline_i_40": { - "declaration": "outline-color: var(--palette_i_40); --outline_color: var(--palette_i_40);" - }, - "outline_i_50": { - "declaration": "outline-color: var(--palette_i_50); --outline_color: var(--palette_i_50);" - }, - "outline_i_60": { - "declaration": "outline-color: var(--palette_i_60); --outline_color: var(--palette_i_60);" - }, - "outline_i_70": { - "declaration": "outline-color: var(--palette_i_70); --outline_color: var(--palette_i_70);" - }, - "outline_i_80": { - "declaration": "outline-color: var(--palette_i_80); --outline_color: var(--palette_i_80);" - }, - "outline_i_90": { - "declaration": "outline-color: var(--palette_i_90); --outline_color: var(--palette_i_90);" - }, - "outline_i_95": { - "declaration": "outline-color: var(--palette_i_95); --outline_color: var(--palette_i_95);" - }, - "outline_i_100": { - "declaration": "outline-color: var(--palette_i_100); --outline_color: var(--palette_i_100);" - }, - "outline_j_00": { - "declaration": "outline-color: var(--palette_j_00); --outline_color: var(--palette_j_00);" - }, - "outline_j_05": { - "declaration": "outline-color: var(--palette_j_05); --outline_color: var(--palette_j_05);" - }, - "outline_j_10": { - "declaration": "outline-color: var(--palette_j_10); --outline_color: var(--palette_j_10);" - }, - "outline_j_20": { - "declaration": "outline-color: var(--palette_j_20); --outline_color: var(--palette_j_20);" - }, - "outline_j_30": { - "declaration": "outline-color: var(--palette_j_30); --outline_color: var(--palette_j_30);" - }, - "outline_j_40": { - "declaration": "outline-color: var(--palette_j_40); --outline_color: var(--palette_j_40);" - }, - "outline_j_50": { - "declaration": "outline-color: var(--palette_j_50); --outline_color: var(--palette_j_50);" - }, - "outline_j_60": { - "declaration": "outline-color: var(--palette_j_60); --outline_color: var(--palette_j_60);" - }, - "outline_j_70": { - "declaration": "outline-color: var(--palette_j_70); --outline_color: var(--palette_j_70);" - }, - "outline_j_80": { - "declaration": "outline-color: var(--palette_j_80); --outline_color: var(--palette_j_80);" - }, - "outline_j_90": { - "declaration": "outline-color: var(--palette_j_90); --outline_color: var(--palette_j_90);" - }, - "outline_j_95": { - "declaration": "outline-color: var(--palette_j_95); --outline_color: var(--palette_j_95);" - }, - "outline_j_100": { - "declaration": "outline-color: var(--palette_j_100); --outline_color: var(--palette_j_100);" - }, - "border_width_1": { - "declaration": "border-width: var(--border_width_1);" - }, - "border_width_2": { - "declaration": "border-width: var(--border_width_2);" - }, - "border_width_3": { - "declaration": "border-width: var(--border_width_3);" - }, - "border_width_4": { - "declaration": "border-width: var(--border_width_4);" - }, - "border_width_5": { - "declaration": "border-width: var(--border_width_5);" - }, - "border_width_6": { - "declaration": "border-width: var(--border_width_6);" - }, - "border_width_7": { - "declaration": "border-width: var(--border_width_7);" - }, - "border_width_8": { - "declaration": "border-width: var(--border_width_8);" - }, - "border_width_9": { - "declaration": "border-width: var(--border_width_9);" - }, - "outline_width_1": { - "declaration": "outline-width: var(--border_width_1);" - }, - "outline_width_2": { - "declaration": "outline-width: var(--border_width_2);" - }, - "outline_width_3": { - "declaration": "outline-width: var(--border_width_3);" - }, - "outline_width_4": { - "declaration": "outline-width: var(--border_width_4);" - }, - "outline_width_5": { - "declaration": "outline-width: var(--border_width_5);" - }, - "outline_width_6": { - "declaration": "outline-width: var(--border_width_6);" - }, - "outline_width_7": { - "declaration": "outline-width: var(--border_width_7);" - }, - "outline_width_8": { - "declaration": "outline-width: var(--border_width_8);" - }, - "outline_width_9": { - "declaration": "outline-width: var(--border_width_9);" - }, - "outline_width_focus": { - "declaration": "outline-width: var(--outline_width_focus);" - }, - "outline_width_active": { - "declaration": "outline-width: var(--outline_width_active);" - }, - "border_radius_xs3": { - "declaration": "border-radius: var(--border_radius_xs3);" - }, - "border_radius_xs2": { - "declaration": "border-radius: var(--border_radius_xs2);" - }, - "border_radius_xs": { - "declaration": "border-radius: var(--border_radius_xs);" - }, - "border_radius_sm": { - "declaration": "border-radius: var(--border_radius_sm);" - }, - "border_radius_md": { - "declaration": "border-radius: var(--border_radius_md);" - }, - "border_radius_lg": { - "declaration": "border-radius: var(--border_radius_lg);" - }, - "border_radius_xl": { - "declaration": "border-radius: var(--border_radius_xl);" - }, - "border_top_left_radius_xs3": { - "declaration": "border-top-left-radius: var(--border_radius_xs3);" - }, - "border_top_left_radius_xs2": { - "declaration": "border-top-left-radius: var(--border_radius_xs2);" - }, - "border_top_left_radius_xs": { - "declaration": "border-top-left-radius: var(--border_radius_xs);" - }, - "border_top_left_radius_sm": { - "declaration": "border-top-left-radius: var(--border_radius_sm);" - }, - "border_top_left_radius_md": { - "declaration": "border-top-left-radius: var(--border_radius_md);" - }, - "border_top_left_radius_lg": { - "declaration": "border-top-left-radius: var(--border_radius_lg);" - }, - "border_top_left_radius_xl": { - "declaration": "border-top-left-radius: var(--border_radius_xl);" - }, - "border_top_right_radius_xs3": { - "declaration": "border-top-right-radius: var(--border_radius_xs3);" - }, - "border_top_right_radius_xs2": { - "declaration": "border-top-right-radius: var(--border_radius_xs2);" - }, - "border_top_right_radius_xs": { - "declaration": "border-top-right-radius: var(--border_radius_xs);" - }, - "border_top_right_radius_sm": { - "declaration": "border-top-right-radius: var(--border_radius_sm);" - }, - "border_top_right_radius_md": { - "declaration": "border-top-right-radius: var(--border_radius_md);" - }, - "border_top_right_radius_lg": { - "declaration": "border-top-right-radius: var(--border_radius_lg);" - }, - "border_top_right_radius_xl": { - "declaration": "border-top-right-radius: var(--border_radius_xl);" - }, - "border_bottom_left_radius_xs3": { - "declaration": "border-bottom-left-radius: var(--border_radius_xs3);" - }, - "border_bottom_left_radius_xs2": { - "declaration": "border-bottom-left-radius: var(--border_radius_xs2);" - }, - "border_bottom_left_radius_xs": { - "declaration": "border-bottom-left-radius: var(--border_radius_xs);" - }, - "border_bottom_left_radius_sm": { - "declaration": "border-bottom-left-radius: var(--border_radius_sm);" - }, - "border_bottom_left_radius_md": { - "declaration": "border-bottom-left-radius: var(--border_radius_md);" - }, - "border_bottom_left_radius_lg": { - "declaration": "border-bottom-left-radius: var(--border_radius_lg);" - }, - "border_bottom_left_radius_xl": { - "declaration": "border-bottom-left-radius: var(--border_radius_xl);" - }, - "border_bottom_right_radius_xs3": { - "declaration": "border-bottom-right-radius: var(--border_radius_xs3);" - }, - "border_bottom_right_radius_xs2": { - "declaration": "border-bottom-right-radius: var(--border_radius_xs2);" - }, - "border_bottom_right_radius_xs": { - "declaration": "border-bottom-right-radius: var(--border_radius_xs);" - }, - "border_bottom_right_radius_sm": { - "declaration": "border-bottom-right-radius: var(--border_radius_sm);" - }, - "border_bottom_right_radius_md": { - "declaration": "border-bottom-right-radius: var(--border_radius_md);" - }, - "border_bottom_right_radius_lg": { - "declaration": "border-bottom-right-radius: var(--border_radius_lg);" - }, - "border_bottom_right_radius_xl": { - "declaration": "border-bottom-right-radius: var(--border_radius_xl);" - }, - "shadow_xs": { - "declaration": "box-shadow: var(--shadow_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_sm": { - "declaration": "box-shadow: var(--shadow_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_md": { - "declaration": "box-shadow: var(--shadow_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_lg": { - "declaration": "box-shadow: var(--shadow_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_xl": { - "declaration": "box-shadow: var(--shadow_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_top_xs": { - "declaration": "box-shadow: var(--shadow_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_top_sm": { - "declaration": "box-shadow: var(--shadow_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_top_md": { - "declaration": "box-shadow: var(--shadow_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_top_lg": { - "declaration": "box-shadow: var(--shadow_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_top_xl": { - "declaration": "box-shadow: var(--shadow_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_bottom_xs": { - "declaration": "box-shadow: var(--shadow_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_bottom_sm": { - "declaration": "box-shadow: var(--shadow_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_bottom_md": { - "declaration": "box-shadow: var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_bottom_lg": { - "declaration": "box-shadow: var(--shadow_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_bottom_xl": { - "declaration": "box-shadow: var(--shadow_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_inset_xs": { - "declaration": "box-shadow: var(--shadow_inset_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_inset_sm": { - "declaration": "box-shadow: var(--shadow_inset_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_inset_md": { - "declaration": "box-shadow: var(--shadow_inset_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_inset_lg": { - "declaration": "box-shadow: var(--shadow_inset_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_inset_xl": { - "declaration": "box-shadow: var(--shadow_inset_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_inset_top_xs": { - "declaration": "box-shadow: var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_inset_top_sm": { - "declaration": "box-shadow: var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_inset_top_md": { - "declaration": "box-shadow: var(--shadow_inset_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_inset_top_lg": { - "declaration": "box-shadow: var(--shadow_inset_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_inset_top_xl": { - "declaration": "box-shadow: var(--shadow_inset_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_inset_bottom_xs": { - "declaration": "box-shadow: var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_inset_bottom_sm": { - "declaration": "box-shadow: var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_inset_bottom_md": { - "declaration": "box-shadow: var(--shadow_inset_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_inset_bottom_lg": { - "declaration": "box-shadow: var(--shadow_inset_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_inset_bottom_xl": { - "declaration": "box-shadow: var(--shadow_inset_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_color_umbra": { - "declaration": "--shadow_color: var(--shadow_color_umbra);" - }, - "shadow_color_highlight": { - "declaration": "--shadow_color: var(--shadow_color_highlight);" - }, - "shadow_color_glow": { - "declaration": "--shadow_color: var(--shadow_color_glow);" - }, - "shadow_color_shroud": { - "declaration": "--shadow_color: var(--shadow_color_shroud);" - }, - "shadow_alpha_00": { - "declaration": "--shadow_alpha: var(--shadow_alpha_00);" - }, - "shadow_alpha_05": { - "declaration": "--shadow_alpha: var(--shadow_alpha_05);" - }, - "shadow_alpha_10": { - "declaration": "--shadow_alpha: var(--shadow_alpha_10);" - }, - "shadow_alpha_20": { - "declaration": "--shadow_alpha: var(--shadow_alpha_20);" - }, - "shadow_alpha_30": { - "declaration": "--shadow_alpha: var(--shadow_alpha_30);" - }, - "shadow_alpha_40": { - "declaration": "--shadow_alpha: var(--shadow_alpha_40);" - }, - "shadow_alpha_50": { - "declaration": "--shadow_alpha: var(--shadow_alpha_50);" - }, - "shadow_alpha_60": { - "declaration": "--shadow_alpha: var(--shadow_alpha_60);" - }, - "shadow_alpha_70": { - "declaration": "--shadow_alpha: var(--shadow_alpha_70);" - }, - "shadow_alpha_80": { - "declaration": "--shadow_alpha: var(--shadow_alpha_80);" - }, - "shadow_alpha_90": { - "declaration": "--shadow_alpha: var(--shadow_alpha_90);" - }, - "shadow_alpha_95": { - "declaration": "--shadow_alpha: var(--shadow_alpha_95);" - }, - "shadow_alpha_100": { - "declaration": "--shadow_alpha: var(--shadow_alpha_100);" - }, - "shadow_a_00": { - "declaration": "--shadow_color: var(--palette_a_00);" - }, - "shadow_a_05": { - "declaration": "--shadow_color: var(--palette_a_05);" - }, - "shadow_a_10": { - "declaration": "--shadow_color: var(--palette_a_10);" - }, - "shadow_a_20": { - "declaration": "--shadow_color: var(--palette_a_20);" - }, - "shadow_a_30": { - "declaration": "--shadow_color: var(--palette_a_30);" - }, - "shadow_a_40": { - "declaration": "--shadow_color: var(--palette_a_40);" - }, - "shadow_a_50": { - "declaration": "--shadow_color: var(--palette_a_50);" - }, - "shadow_a_60": { - "declaration": "--shadow_color: var(--palette_a_60);" - }, - "shadow_a_70": { - "declaration": "--shadow_color: var(--palette_a_70);" - }, - "shadow_a_80": { - "declaration": "--shadow_color: var(--palette_a_80);" - }, - "shadow_a_90": { - "declaration": "--shadow_color: var(--palette_a_90);" - }, - "shadow_a_95": { - "declaration": "--shadow_color: var(--palette_a_95);" - }, - "shadow_a_100": { - "declaration": "--shadow_color: var(--palette_a_100);" - }, - "shadow_b_00": { - "declaration": "--shadow_color: var(--palette_b_00);" - }, - "shadow_b_05": { - "declaration": "--shadow_color: var(--palette_b_05);" - }, - "shadow_b_10": { - "declaration": "--shadow_color: var(--palette_b_10);" - }, - "shadow_b_20": { - "declaration": "--shadow_color: var(--palette_b_20);" - }, - "shadow_b_30": { - "declaration": "--shadow_color: var(--palette_b_30);" - }, - "shadow_b_40": { - "declaration": "--shadow_color: var(--palette_b_40);" - }, - "shadow_b_50": { - "declaration": "--shadow_color: var(--palette_b_50);" - }, - "shadow_b_60": { - "declaration": "--shadow_color: var(--palette_b_60);" - }, - "shadow_b_70": { - "declaration": "--shadow_color: var(--palette_b_70);" - }, - "shadow_b_80": { - "declaration": "--shadow_color: var(--palette_b_80);" - }, - "shadow_b_90": { - "declaration": "--shadow_color: var(--palette_b_90);" - }, - "shadow_b_95": { - "declaration": "--shadow_color: var(--palette_b_95);" - }, - "shadow_b_100": { - "declaration": "--shadow_color: var(--palette_b_100);" - }, - "shadow_c_00": { - "declaration": "--shadow_color: var(--palette_c_00);" - }, - "shadow_c_05": { - "declaration": "--shadow_color: var(--palette_c_05);" - }, - "shadow_c_10": { - "declaration": "--shadow_color: var(--palette_c_10);" - }, - "shadow_c_20": { - "declaration": "--shadow_color: var(--palette_c_20);" - }, - "shadow_c_30": { - "declaration": "--shadow_color: var(--palette_c_30);" - }, - "shadow_c_40": { - "declaration": "--shadow_color: var(--palette_c_40);" - }, - "shadow_c_50": { - "declaration": "--shadow_color: var(--palette_c_50);" - }, - "shadow_c_60": { - "declaration": "--shadow_color: var(--palette_c_60);" - }, - "shadow_c_70": { - "declaration": "--shadow_color: var(--palette_c_70);" - }, - "shadow_c_80": { - "declaration": "--shadow_color: var(--palette_c_80);" - }, - "shadow_c_90": { - "declaration": "--shadow_color: var(--palette_c_90);" - }, - "shadow_c_95": { - "declaration": "--shadow_color: var(--palette_c_95);" - }, - "shadow_c_100": { - "declaration": "--shadow_color: var(--palette_c_100);" - }, - "shadow_d_00": { - "declaration": "--shadow_color: var(--palette_d_00);" - }, - "shadow_d_05": { - "declaration": "--shadow_color: var(--palette_d_05);" - }, - "shadow_d_10": { - "declaration": "--shadow_color: var(--palette_d_10);" - }, - "shadow_d_20": { - "declaration": "--shadow_color: var(--palette_d_20);" - }, - "shadow_d_30": { - "declaration": "--shadow_color: var(--palette_d_30);" - }, - "shadow_d_40": { - "declaration": "--shadow_color: var(--palette_d_40);" - }, - "shadow_d_50": { - "declaration": "--shadow_color: var(--palette_d_50);" - }, - "shadow_d_60": { - "declaration": "--shadow_color: var(--palette_d_60);" - }, - "shadow_d_70": { - "declaration": "--shadow_color: var(--palette_d_70);" - }, - "shadow_d_80": { - "declaration": "--shadow_color: var(--palette_d_80);" - }, - "shadow_d_90": { - "declaration": "--shadow_color: var(--palette_d_90);" - }, - "shadow_d_95": { - "declaration": "--shadow_color: var(--palette_d_95);" - }, - "shadow_d_100": { - "declaration": "--shadow_color: var(--palette_d_100);" - }, - "shadow_e_00": { - "declaration": "--shadow_color: var(--palette_e_00);" - }, - "shadow_e_05": { - "declaration": "--shadow_color: var(--palette_e_05);" - }, - "shadow_e_10": { - "declaration": "--shadow_color: var(--palette_e_10);" - }, - "shadow_e_20": { - "declaration": "--shadow_color: var(--palette_e_20);" - }, - "shadow_e_30": { - "declaration": "--shadow_color: var(--palette_e_30);" - }, - "shadow_e_40": { - "declaration": "--shadow_color: var(--palette_e_40);" - }, - "shadow_e_50": { - "declaration": "--shadow_color: var(--palette_e_50);" - }, - "shadow_e_60": { - "declaration": "--shadow_color: var(--palette_e_60);" - }, - "shadow_e_70": { - "declaration": "--shadow_color: var(--palette_e_70);" - }, - "shadow_e_80": { - "declaration": "--shadow_color: var(--palette_e_80);" - }, - "shadow_e_90": { - "declaration": "--shadow_color: var(--palette_e_90);" - }, - "shadow_e_95": { - "declaration": "--shadow_color: var(--palette_e_95);" - }, - "shadow_e_100": { - "declaration": "--shadow_color: var(--palette_e_100);" - }, - "shadow_f_00": { - "declaration": "--shadow_color: var(--palette_f_00);" - }, - "shadow_f_05": { - "declaration": "--shadow_color: var(--palette_f_05);" - }, - "shadow_f_10": { - "declaration": "--shadow_color: var(--palette_f_10);" - }, - "shadow_f_20": { - "declaration": "--shadow_color: var(--palette_f_20);" - }, - "shadow_f_30": { - "declaration": "--shadow_color: var(--palette_f_30);" - }, - "shadow_f_40": { - "declaration": "--shadow_color: var(--palette_f_40);" - }, - "shadow_f_50": { - "declaration": "--shadow_color: var(--palette_f_50);" - }, - "shadow_f_60": { - "declaration": "--shadow_color: var(--palette_f_60);" - }, - "shadow_f_70": { - "declaration": "--shadow_color: var(--palette_f_70);" - }, - "shadow_f_80": { - "declaration": "--shadow_color: var(--palette_f_80);" - }, - "shadow_f_90": { - "declaration": "--shadow_color: var(--palette_f_90);" - }, - "shadow_f_95": { - "declaration": "--shadow_color: var(--palette_f_95);" - }, - "shadow_f_100": { - "declaration": "--shadow_color: var(--palette_f_100);" - }, - "shadow_g_00": { - "declaration": "--shadow_color: var(--palette_g_00);" - }, - "shadow_g_05": { - "declaration": "--shadow_color: var(--palette_g_05);" - }, - "shadow_g_10": { - "declaration": "--shadow_color: var(--palette_g_10);" - }, - "shadow_g_20": { - "declaration": "--shadow_color: var(--palette_g_20);" - }, - "shadow_g_30": { - "declaration": "--shadow_color: var(--palette_g_30);" - }, - "shadow_g_40": { - "declaration": "--shadow_color: var(--palette_g_40);" - }, - "shadow_g_50": { - "declaration": "--shadow_color: var(--palette_g_50);" - }, - "shadow_g_60": { - "declaration": "--shadow_color: var(--palette_g_60);" - }, - "shadow_g_70": { - "declaration": "--shadow_color: var(--palette_g_70);" - }, - "shadow_g_80": { - "declaration": "--shadow_color: var(--palette_g_80);" - }, - "shadow_g_90": { - "declaration": "--shadow_color: var(--palette_g_90);" - }, - "shadow_g_95": { - "declaration": "--shadow_color: var(--palette_g_95);" - }, - "shadow_g_100": { - "declaration": "--shadow_color: var(--palette_g_100);" - }, - "shadow_h_00": { - "declaration": "--shadow_color: var(--palette_h_00);" - }, - "shadow_h_05": { - "declaration": "--shadow_color: var(--palette_h_05);" - }, - "shadow_h_10": { - "declaration": "--shadow_color: var(--palette_h_10);" - }, - "shadow_h_20": { - "declaration": "--shadow_color: var(--palette_h_20);" - }, - "shadow_h_30": { - "declaration": "--shadow_color: var(--palette_h_30);" - }, - "shadow_h_40": { - "declaration": "--shadow_color: var(--palette_h_40);" - }, - "shadow_h_50": { - "declaration": "--shadow_color: var(--palette_h_50);" - }, - "shadow_h_60": { - "declaration": "--shadow_color: var(--palette_h_60);" - }, - "shadow_h_70": { - "declaration": "--shadow_color: var(--palette_h_70);" - }, - "shadow_h_80": { - "declaration": "--shadow_color: var(--palette_h_80);" - }, - "shadow_h_90": { - "declaration": "--shadow_color: var(--palette_h_90);" - }, - "shadow_h_95": { - "declaration": "--shadow_color: var(--palette_h_95);" - }, - "shadow_h_100": { - "declaration": "--shadow_color: var(--palette_h_100);" - }, - "shadow_i_00": { - "declaration": "--shadow_color: var(--palette_i_00);" - }, - "shadow_i_05": { - "declaration": "--shadow_color: var(--palette_i_05);" - }, - "shadow_i_10": { - "declaration": "--shadow_color: var(--palette_i_10);" - }, - "shadow_i_20": { - "declaration": "--shadow_color: var(--palette_i_20);" - }, - "shadow_i_30": { - "declaration": "--shadow_color: var(--palette_i_30);" - }, - "shadow_i_40": { - "declaration": "--shadow_color: var(--palette_i_40);" - }, - "shadow_i_50": { - "declaration": "--shadow_color: var(--palette_i_50);" - }, - "shadow_i_60": { - "declaration": "--shadow_color: var(--palette_i_60);" - }, - "shadow_i_70": { - "declaration": "--shadow_color: var(--palette_i_70);" - }, - "shadow_i_80": { - "declaration": "--shadow_color: var(--palette_i_80);" - }, - "shadow_i_90": { - "declaration": "--shadow_color: var(--palette_i_90);" - }, - "shadow_i_95": { - "declaration": "--shadow_color: var(--palette_i_95);" - }, - "shadow_i_100": { - "declaration": "--shadow_color: var(--palette_i_100);" - }, - "shadow_j_00": { - "declaration": "--shadow_color: var(--palette_j_00);" - }, - "shadow_j_05": { - "declaration": "--shadow_color: var(--palette_j_05);" - }, - "shadow_j_10": { - "declaration": "--shadow_color: var(--palette_j_10);" - }, - "shadow_j_20": { - "declaration": "--shadow_color: var(--palette_j_20);" - }, - "shadow_j_30": { - "declaration": "--shadow_color: var(--palette_j_30);" - }, - "shadow_j_40": { - "declaration": "--shadow_color: var(--palette_j_40);" - }, - "shadow_j_50": { - "declaration": "--shadow_color: var(--palette_j_50);" - }, - "shadow_j_60": { - "declaration": "--shadow_color: var(--palette_j_60);" - }, - "shadow_j_70": { - "declaration": "--shadow_color: var(--palette_j_70);" - }, - "shadow_j_80": { - "declaration": "--shadow_color: var(--palette_j_80);" - }, - "shadow_j_90": { - "declaration": "--shadow_color: var(--palette_j_90);" - }, - "shadow_j_95": { - "declaration": "--shadow_color: var(--palette_j_95);" - }, - "shadow_j_100": { - "declaration": "--shadow_color: var(--palette_j_100);" - }, - "width_xs5": { - "declaration": "width: var(--space_xs5);" - }, - "width_xs4": { - "declaration": "width: var(--space_xs4);" - }, - "width_xs3": { - "declaration": "width: var(--space_xs3);" - }, - "width_xs2": { - "declaration": "width: var(--space_xs2);" - }, - "width_xs": { - "declaration": "width: var(--space_xs);" - }, - "width_sm": { - "declaration": "width: var(--space_sm);" - }, - "width_md": { - "declaration": "width: var(--space_md);" - }, - "width_lg": { - "declaration": "width: var(--space_lg);" - }, - "width_xl": { - "declaration": "width: var(--space_xl);" - }, - "width_xl2": { - "declaration": "width: var(--space_xl2);" - }, - "width_xl3": { - "declaration": "width: var(--space_xl3);" - }, - "width_xl4": { - "declaration": "width: var(--space_xl4);" - }, - "width_xl5": { - "declaration": "width: var(--space_xl5);" - }, - "width_xl6": { - "declaration": "width: var(--space_xl6);" - }, - "width_xl7": { - "declaration": "width: var(--space_xl7);" - }, - "width_xl8": { - "declaration": "width: var(--space_xl8);" - }, - "width_xl9": { - "declaration": "width: var(--space_xl9);" - }, - "width_xl10": { - "declaration": "width: var(--space_xl10);" - }, - "width_xl11": { - "declaration": "width: var(--space_xl11);" - }, - "width_xl12": { - "declaration": "width: var(--space_xl12);" - }, - "width_xl13": { - "declaration": "width: var(--space_xl13);" - }, - "width_xl14": { - "declaration": "width: var(--space_xl14);" - }, - "width_xl15": { - "declaration": "width: var(--space_xl15);" - }, - "height_xs5": { - "declaration": "height: var(--space_xs5);" - }, - "height_xs4": { - "declaration": "height: var(--space_xs4);" - }, - "height_xs3": { - "declaration": "height: var(--space_xs3);" - }, - "height_xs2": { - "declaration": "height: var(--space_xs2);" - }, - "height_xs": { - "declaration": "height: var(--space_xs);" - }, - "height_sm": { - "declaration": "height: var(--space_sm);" - }, - "height_md": { - "declaration": "height: var(--space_md);" - }, - "height_lg": { - "declaration": "height: var(--space_lg);" - }, - "height_xl": { - "declaration": "height: var(--space_xl);" - }, - "height_xl2": { - "declaration": "height: var(--space_xl2);" - }, - "height_xl3": { - "declaration": "height: var(--space_xl3);" - }, - "height_xl4": { - "declaration": "height: var(--space_xl4);" - }, - "height_xl5": { - "declaration": "height: var(--space_xl5);" - }, - "height_xl6": { - "declaration": "height: var(--space_xl6);" - }, - "height_xl7": { - "declaration": "height: var(--space_xl7);" - }, - "height_xl8": { - "declaration": "height: var(--space_xl8);" - }, - "height_xl9": { - "declaration": "height: var(--space_xl9);" - }, - "height_xl10": { - "declaration": "height: var(--space_xl10);" - }, - "height_xl11": { - "declaration": "height: var(--space_xl11);" - }, - "height_xl12": { - "declaration": "height: var(--space_xl12);" - }, - "height_xl13": { - "declaration": "height: var(--space_xl13);" - }, - "height_xl14": { - "declaration": "height: var(--space_xl14);" - }, - "height_xl15": { - "declaration": "height: var(--space_xl15);" - }, - "width_atmost_xs": { - "declaration": "width: 100%; max-width: var(--distance_xs);" - }, - "width_atmost_sm": { - "declaration": "width: 100%; max-width: var(--distance_sm);" - }, - "width_atmost_md": { - "declaration": "width: 100%; max-width: var(--distance_md);" - }, - "width_atmost_lg": { - "declaration": "width: 100%; max-width: var(--distance_lg);" - }, - "width_atmost_xl": { - "declaration": "width: 100%; max-width: var(--distance_xl);" - }, - "width_atleast_xs": { - "declaration": "width: 100%; min-width: var(--distance_xs);" - }, - "width_atleast_sm": { - "declaration": "width: 100%; min-width: var(--distance_sm);" - }, - "width_atleast_md": { - "declaration": "width: 100%; min-width: var(--distance_md);" - }, - "width_atleast_lg": { - "declaration": "width: 100%; min-width: var(--distance_lg);" - }, - "width_atleast_xl": { - "declaration": "width: 100%; min-width: var(--distance_xl);" - }, - "height_atmost_xs": { - "declaration": "height: 100%; max-height: var(--distance_xs);" - }, - "height_atmost_sm": { - "declaration": "height: 100%; max-height: var(--distance_sm);" - }, - "height_atmost_md": { - "declaration": "height: 100%; max-height: var(--distance_md);" - }, - "height_atmost_lg": { - "declaration": "height: 100%; max-height: var(--distance_lg);" - }, - "height_atmost_xl": { - "declaration": "height: 100%; max-height: var(--distance_xl);" - }, - "height_atleast_xs": { - "declaration": "height: 100%; min-height: var(--distance_xs);" - }, - "height_atleast_sm": { - "declaration": "height: 100%; min-height: var(--distance_sm);" - }, - "height_atleast_md": { - "declaration": "height: 100%; min-height: var(--distance_md);" - }, - "height_atleast_lg": { - "declaration": "height: 100%; min-height: var(--distance_lg);" - }, - "height_atleast_xl": { - "declaration": "height: 100%; min-height: var(--distance_xl);" - }, - "top_xs5": { - "declaration": "top: var(--space_xs5);" - }, - "top_xs4": { - "declaration": "top: var(--space_xs4);" - }, - "top_xs3": { - "declaration": "top: var(--space_xs3);" - }, - "top_xs2": { - "declaration": "top: var(--space_xs2);" - }, - "top_xs": { - "declaration": "top: var(--space_xs);" - }, - "top_sm": { - "declaration": "top: var(--space_sm);" - }, - "top_md": { - "declaration": "top: var(--space_md);" - }, - "top_lg": { - "declaration": "top: var(--space_lg);" - }, - "top_xl": { - "declaration": "top: var(--space_xl);" - }, - "top_xl2": { - "declaration": "top: var(--space_xl2);" - }, - "top_xl3": { - "declaration": "top: var(--space_xl3);" - }, - "top_xl4": { - "declaration": "top: var(--space_xl4);" - }, - "top_xl5": { - "declaration": "top: var(--space_xl5);" - }, - "top_xl6": { - "declaration": "top: var(--space_xl6);" - }, - "top_xl7": { - "declaration": "top: var(--space_xl7);" - }, - "top_xl8": { - "declaration": "top: var(--space_xl8);" - }, - "top_xl9": { - "declaration": "top: var(--space_xl9);" - }, - "top_xl10": { - "declaration": "top: var(--space_xl10);" - }, - "top_xl11": { - "declaration": "top: var(--space_xl11);" - }, - "top_xl12": { - "declaration": "top: var(--space_xl12);" - }, - "top_xl13": { - "declaration": "top: var(--space_xl13);" - }, - "top_xl14": { - "declaration": "top: var(--space_xl14);" - }, - "top_xl15": { - "declaration": "top: var(--space_xl15);" - }, - "right_xs5": { - "declaration": "right: var(--space_xs5);" - }, - "right_xs4": { - "declaration": "right: var(--space_xs4);" - }, - "right_xs3": { - "declaration": "right: var(--space_xs3);" - }, - "right_xs2": { - "declaration": "right: var(--space_xs2);" - }, - "right_xs": { - "declaration": "right: var(--space_xs);" - }, - "right_sm": { - "declaration": "right: var(--space_sm);" - }, - "right_md": { - "declaration": "right: var(--space_md);" - }, - "right_lg": { - "declaration": "right: var(--space_lg);" - }, - "right_xl": { - "declaration": "right: var(--space_xl);" - }, - "right_xl2": { - "declaration": "right: var(--space_xl2);" - }, - "right_xl3": { - "declaration": "right: var(--space_xl3);" - }, - "right_xl4": { - "declaration": "right: var(--space_xl4);" - }, - "right_xl5": { - "declaration": "right: var(--space_xl5);" - }, - "right_xl6": { - "declaration": "right: var(--space_xl6);" - }, - "right_xl7": { - "declaration": "right: var(--space_xl7);" - }, - "right_xl8": { - "declaration": "right: var(--space_xl8);" - }, - "right_xl9": { - "declaration": "right: var(--space_xl9);" - }, - "right_xl10": { - "declaration": "right: var(--space_xl10);" - }, - "right_xl11": { - "declaration": "right: var(--space_xl11);" - }, - "right_xl12": { - "declaration": "right: var(--space_xl12);" - }, - "right_xl13": { - "declaration": "right: var(--space_xl13);" - }, - "right_xl14": { - "declaration": "right: var(--space_xl14);" - }, - "right_xl15": { - "declaration": "right: var(--space_xl15);" - }, - "bottom_xs5": { - "declaration": "bottom: var(--space_xs5);" - }, - "bottom_xs4": { - "declaration": "bottom: var(--space_xs4);" - }, - "bottom_xs3": { - "declaration": "bottom: var(--space_xs3);" - }, - "bottom_xs2": { - "declaration": "bottom: var(--space_xs2);" - }, - "bottom_xs": { - "declaration": "bottom: var(--space_xs);" - }, - "bottom_sm": { - "declaration": "bottom: var(--space_sm);" - }, - "bottom_md": { - "declaration": "bottom: var(--space_md);" - }, - "bottom_lg": { - "declaration": "bottom: var(--space_lg);" - }, - "bottom_xl": { - "declaration": "bottom: var(--space_xl);" - }, - "bottom_xl2": { - "declaration": "bottom: var(--space_xl2);" - }, - "bottom_xl3": { - "declaration": "bottom: var(--space_xl3);" - }, - "bottom_xl4": { - "declaration": "bottom: var(--space_xl4);" - }, - "bottom_xl5": { - "declaration": "bottom: var(--space_xl5);" - }, - "bottom_xl6": { - "declaration": "bottom: var(--space_xl6);" - }, - "bottom_xl7": { - "declaration": "bottom: var(--space_xl7);" - }, - "bottom_xl8": { - "declaration": "bottom: var(--space_xl8);" - }, - "bottom_xl9": { - "declaration": "bottom: var(--space_xl9);" - }, - "bottom_xl10": { - "declaration": "bottom: var(--space_xl10);" - }, - "bottom_xl11": { - "declaration": "bottom: var(--space_xl11);" - }, - "bottom_xl12": { - "declaration": "bottom: var(--space_xl12);" - }, - "bottom_xl13": { - "declaration": "bottom: var(--space_xl13);" - }, - "bottom_xl14": { - "declaration": "bottom: var(--space_xl14);" - }, - "bottom_xl15": { - "declaration": "bottom: var(--space_xl15);" - }, - "left_xs5": { - "declaration": "left: var(--space_xs5);" - }, - "left_xs4": { - "declaration": "left: var(--space_xs4);" - }, - "left_xs3": { - "declaration": "left: var(--space_xs3);" - }, - "left_xs2": { - "declaration": "left: var(--space_xs2);" - }, - "left_xs": { - "declaration": "left: var(--space_xs);" - }, - "left_sm": { - "declaration": "left: var(--space_sm);" - }, - "left_md": { - "declaration": "left: var(--space_md);" - }, - "left_lg": { - "declaration": "left: var(--space_lg);" - }, - "left_xl": { - "declaration": "left: var(--space_xl);" - }, - "left_xl2": { - "declaration": "left: var(--space_xl2);" - }, - "left_xl3": { - "declaration": "left: var(--space_xl3);" - }, - "left_xl4": { - "declaration": "left: var(--space_xl4);" - }, - "left_xl5": { - "declaration": "left: var(--space_xl5);" - }, - "left_xl6": { - "declaration": "left: var(--space_xl6);" - }, - "left_xl7": { - "declaration": "left: var(--space_xl7);" - }, - "left_xl8": { - "declaration": "left: var(--space_xl8);" - }, - "left_xl9": { - "declaration": "left: var(--space_xl9);" - }, - "left_xl10": { - "declaration": "left: var(--space_xl10);" - }, - "left_xl11": { - "declaration": "left: var(--space_xl11);" - }, - "left_xl12": { - "declaration": "left: var(--space_xl12);" - }, - "left_xl13": { - "declaration": "left: var(--space_xl13);" - }, - "left_xl14": { - "declaration": "left: var(--space_xl14);" - }, - "left_xl15": { - "declaration": "left: var(--space_xl15);" - }, - "inset_xs5": { - "declaration": "inset: var(--space_xs5);" - }, - "inset_xs4": { - "declaration": "inset: var(--space_xs4);" - }, - "inset_xs3": { - "declaration": "inset: var(--space_xs3);" - }, - "inset_xs2": { - "declaration": "inset: var(--space_xs2);" - }, - "inset_xs": { - "declaration": "inset: var(--space_xs);" - }, - "inset_sm": { - "declaration": "inset: var(--space_sm);" - }, - "inset_md": { - "declaration": "inset: var(--space_md);" - }, - "inset_lg": { - "declaration": "inset: var(--space_lg);" - }, - "inset_xl": { - "declaration": "inset: var(--space_xl);" - }, - "inset_xl2": { - "declaration": "inset: var(--space_xl2);" - }, - "inset_xl3": { - "declaration": "inset: var(--space_xl3);" - }, - "inset_xl4": { - "declaration": "inset: var(--space_xl4);" - }, - "inset_xl5": { - "declaration": "inset: var(--space_xl5);" - }, - "inset_xl6": { - "declaration": "inset: var(--space_xl6);" - }, - "inset_xl7": { - "declaration": "inset: var(--space_xl7);" - }, - "inset_xl8": { - "declaration": "inset: var(--space_xl8);" - }, - "inset_xl9": { - "declaration": "inset: var(--space_xl9);" - }, - "inset_xl10": { - "declaration": "inset: var(--space_xl10);" - }, - "inset_xl11": { - "declaration": "inset: var(--space_xl11);" - }, - "inset_xl12": { - "declaration": "inset: var(--space_xl12);" - }, - "inset_xl13": { - "declaration": "inset: var(--space_xl13);" - }, - "inset_xl14": { - "declaration": "inset: var(--space_xl14);" - }, - "inset_xl15": { - "declaration": "inset: var(--space_xl15);" - }, - "p_0": { - "declaration": "padding: 0;" - }, - "p_xs5": { - "declaration": "padding: var(--space_xs5);" - }, - "p_xs4": { - "declaration": "padding: var(--space_xs4);" - }, - "p_xs3": { - "declaration": "padding: var(--space_xs3);" - }, - "p_xs2": { - "declaration": "padding: var(--space_xs2);" - }, - "p_xs": { - "declaration": "padding: var(--space_xs);" - }, - "p_sm": { - "declaration": "padding: var(--space_sm);" - }, - "p_md": { - "declaration": "padding: var(--space_md);" - }, - "p_lg": { - "declaration": "padding: var(--space_lg);" - }, - "p_xl": { - "declaration": "padding: var(--space_xl);" - }, - "p_xl2": { - "declaration": "padding: var(--space_xl2);" - }, - "p_xl3": { - "declaration": "padding: var(--space_xl3);" - }, - "p_xl4": { - "declaration": "padding: var(--space_xl4);" - }, - "p_xl5": { - "declaration": "padding: var(--space_xl5);" - }, - "p_xl6": { - "declaration": "padding: var(--space_xl6);" - }, - "p_xl7": { - "declaration": "padding: var(--space_xl7);" - }, - "p_xl8": { - "declaration": "padding: var(--space_xl8);" - }, - "p_xl9": { - "declaration": "padding: var(--space_xl9);" - }, - "p_xl10": { - "declaration": "padding: var(--space_xl10);" - }, - "p_xl11": { - "declaration": "padding: var(--space_xl11);" - }, - "p_xl12": { - "declaration": "padding: var(--space_xl12);" - }, - "p_xl13": { - "declaration": "padding: var(--space_xl13);" - }, - "p_xl14": { - "declaration": "padding: var(--space_xl14);" - }, - "p_xl15": { - "declaration": "padding: var(--space_xl15);" - }, - "pt_0": { - "declaration": "padding-top: 0;" - }, - "pt_xs5": { - "declaration": "padding-top: var(--space_xs5);" - }, - "pt_xs4": { - "declaration": "padding-top: var(--space_xs4);" - }, - "pt_xs3": { - "declaration": "padding-top: var(--space_xs3);" - }, - "pt_xs2": { - "declaration": "padding-top: var(--space_xs2);" - }, - "pt_xs": { - "declaration": "padding-top: var(--space_xs);" - }, - "pt_sm": { - "declaration": "padding-top: var(--space_sm);" - }, - "pt_md": { - "declaration": "padding-top: var(--space_md);" - }, - "pt_lg": { - "declaration": "padding-top: var(--space_lg);" - }, - "pt_xl": { - "declaration": "padding-top: var(--space_xl);" - }, - "pt_xl2": { - "declaration": "padding-top: var(--space_xl2);" - }, - "pt_xl3": { - "declaration": "padding-top: var(--space_xl3);" - }, - "pt_xl4": { - "declaration": "padding-top: var(--space_xl4);" - }, - "pt_xl5": { - "declaration": "padding-top: var(--space_xl5);" - }, - "pt_xl6": { - "declaration": "padding-top: var(--space_xl6);" - }, - "pt_xl7": { - "declaration": "padding-top: var(--space_xl7);" - }, - "pt_xl8": { - "declaration": "padding-top: var(--space_xl8);" - }, - "pt_xl9": { - "declaration": "padding-top: var(--space_xl9);" - }, - "pt_xl10": { - "declaration": "padding-top: var(--space_xl10);" - }, - "pt_xl11": { - "declaration": "padding-top: var(--space_xl11);" - }, - "pt_xl12": { - "declaration": "padding-top: var(--space_xl12);" - }, - "pt_xl13": { - "declaration": "padding-top: var(--space_xl13);" - }, - "pt_xl14": { - "declaration": "padding-top: var(--space_xl14);" - }, - "pt_xl15": { - "declaration": "padding-top: var(--space_xl15);" - }, - "pr_0": { - "declaration": "padding-right: 0;" - }, - "pr_xs5": { - "declaration": "padding-right: var(--space_xs5);" - }, - "pr_xs4": { - "declaration": "padding-right: var(--space_xs4);" - }, - "pr_xs3": { - "declaration": "padding-right: var(--space_xs3);" - }, - "pr_xs2": { - "declaration": "padding-right: var(--space_xs2);" - }, - "pr_xs": { - "declaration": "padding-right: var(--space_xs);" - }, - "pr_sm": { - "declaration": "padding-right: var(--space_sm);" - }, - "pr_md": { - "declaration": "padding-right: var(--space_md);" - }, - "pr_lg": { - "declaration": "padding-right: var(--space_lg);" - }, - "pr_xl": { - "declaration": "padding-right: var(--space_xl);" - }, - "pr_xl2": { - "declaration": "padding-right: var(--space_xl2);" - }, - "pr_xl3": { - "declaration": "padding-right: var(--space_xl3);" - }, - "pr_xl4": { - "declaration": "padding-right: var(--space_xl4);" - }, - "pr_xl5": { - "declaration": "padding-right: var(--space_xl5);" - }, - "pr_xl6": { - "declaration": "padding-right: var(--space_xl6);" - }, - "pr_xl7": { - "declaration": "padding-right: var(--space_xl7);" - }, - "pr_xl8": { - "declaration": "padding-right: var(--space_xl8);" - }, - "pr_xl9": { - "declaration": "padding-right: var(--space_xl9);" - }, - "pr_xl10": { - "declaration": "padding-right: var(--space_xl10);" - }, - "pr_xl11": { - "declaration": "padding-right: var(--space_xl11);" - }, - "pr_xl12": { - "declaration": "padding-right: var(--space_xl12);" - }, - "pr_xl13": { - "declaration": "padding-right: var(--space_xl13);" - }, - "pr_xl14": { - "declaration": "padding-right: var(--space_xl14);" - }, - "pr_xl15": { - "declaration": "padding-right: var(--space_xl15);" - }, - "pb_0": { - "declaration": "padding-bottom: 0;" - }, - "pb_xs5": { - "declaration": "padding-bottom: var(--space_xs5);" - }, - "pb_xs4": { - "declaration": "padding-bottom: var(--space_xs4);" - }, - "pb_xs3": { - "declaration": "padding-bottom: var(--space_xs3);" - }, - "pb_xs2": { - "declaration": "padding-bottom: var(--space_xs2);" - }, - "pb_xs": { - "declaration": "padding-bottom: var(--space_xs);" - }, - "pb_sm": { - "declaration": "padding-bottom: var(--space_sm);" - }, - "pb_md": { - "declaration": "padding-bottom: var(--space_md);" - }, - "pb_lg": { - "declaration": "padding-bottom: var(--space_lg);" - }, - "pb_xl": { - "declaration": "padding-bottom: var(--space_xl);" - }, - "pb_xl2": { - "declaration": "padding-bottom: var(--space_xl2);" - }, - "pb_xl3": { - "declaration": "padding-bottom: var(--space_xl3);" - }, - "pb_xl4": { - "declaration": "padding-bottom: var(--space_xl4);" - }, - "pb_xl5": { - "declaration": "padding-bottom: var(--space_xl5);" - }, - "pb_xl6": { - "declaration": "padding-bottom: var(--space_xl6);" - }, - "pb_xl7": { - "declaration": "padding-bottom: var(--space_xl7);" - }, - "pb_xl8": { - "declaration": "padding-bottom: var(--space_xl8);" - }, - "pb_xl9": { - "declaration": "padding-bottom: var(--space_xl9);" - }, - "pb_xl10": { - "declaration": "padding-bottom: var(--space_xl10);" - }, - "pb_xl11": { - "declaration": "padding-bottom: var(--space_xl11);" - }, - "pb_xl12": { - "declaration": "padding-bottom: var(--space_xl12);" - }, - "pb_xl13": { - "declaration": "padding-bottom: var(--space_xl13);" - }, - "pb_xl14": { - "declaration": "padding-bottom: var(--space_xl14);" - }, - "pb_xl15": { - "declaration": "padding-bottom: var(--space_xl15);" - }, - "pl_0": { - "declaration": "padding-left: 0;" - }, - "pl_xs5": { - "declaration": "padding-left: var(--space_xs5);" - }, - "pl_xs4": { - "declaration": "padding-left: var(--space_xs4);" - }, - "pl_xs3": { - "declaration": "padding-left: var(--space_xs3);" - }, - "pl_xs2": { - "declaration": "padding-left: var(--space_xs2);" - }, - "pl_xs": { - "declaration": "padding-left: var(--space_xs);" - }, - "pl_sm": { - "declaration": "padding-left: var(--space_sm);" - }, - "pl_md": { - "declaration": "padding-left: var(--space_md);" - }, - "pl_lg": { - "declaration": "padding-left: var(--space_lg);" - }, - "pl_xl": { - "declaration": "padding-left: var(--space_xl);" - }, - "pl_xl2": { - "declaration": "padding-left: var(--space_xl2);" - }, - "pl_xl3": { - "declaration": "padding-left: var(--space_xl3);" - }, - "pl_xl4": { - "declaration": "padding-left: var(--space_xl4);" - }, - "pl_xl5": { - "declaration": "padding-left: var(--space_xl5);" - }, - "pl_xl6": { - "declaration": "padding-left: var(--space_xl6);" - }, - "pl_xl7": { - "declaration": "padding-left: var(--space_xl7);" - }, - "pl_xl8": { - "declaration": "padding-left: var(--space_xl8);" - }, - "pl_xl9": { - "declaration": "padding-left: var(--space_xl9);" - }, - "pl_xl10": { - "declaration": "padding-left: var(--space_xl10);" - }, - "pl_xl11": { - "declaration": "padding-left: var(--space_xl11);" - }, - "pl_xl12": { - "declaration": "padding-left: var(--space_xl12);" - }, - "pl_xl13": { - "declaration": "padding-left: var(--space_xl13);" - }, - "pl_xl14": { - "declaration": "padding-left: var(--space_xl14);" - }, - "pl_xl15": { - "declaration": "padding-left: var(--space_xl15);" - }, - "px_0": { - "declaration": "padding-left: 0;\tpadding-right: 0;" - }, - "px_xs5": { - "declaration": "padding-left: var(--space_xs5);\tpadding-right: var(--space_xs5);" - }, - "px_xs4": { - "declaration": "padding-left: var(--space_xs4);\tpadding-right: var(--space_xs4);" - }, - "px_xs3": { - "declaration": "padding-left: var(--space_xs3);\tpadding-right: var(--space_xs3);" - }, - "px_xs2": { - "declaration": "padding-left: var(--space_xs2);\tpadding-right: var(--space_xs2);" - }, - "px_xs": { - "declaration": "padding-left: var(--space_xs);\tpadding-right: var(--space_xs);" - }, - "px_sm": { - "declaration": "padding-left: var(--space_sm);\tpadding-right: var(--space_sm);" - }, - "px_md": { - "declaration": "padding-left: var(--space_md);\tpadding-right: var(--space_md);" - }, - "px_lg": { - "declaration": "padding-left: var(--space_lg);\tpadding-right: var(--space_lg);" - }, - "px_xl": { - "declaration": "padding-left: var(--space_xl);\tpadding-right: var(--space_xl);" - }, - "px_xl2": { - "declaration": "padding-left: var(--space_xl2);\tpadding-right: var(--space_xl2);" - }, - "px_xl3": { - "declaration": "padding-left: var(--space_xl3);\tpadding-right: var(--space_xl3);" - }, - "px_xl4": { - "declaration": "padding-left: var(--space_xl4);\tpadding-right: var(--space_xl4);" - }, - "px_xl5": { - "declaration": "padding-left: var(--space_xl5);\tpadding-right: var(--space_xl5);" - }, - "px_xl6": { - "declaration": "padding-left: var(--space_xl6);\tpadding-right: var(--space_xl6);" - }, - "px_xl7": { - "declaration": "padding-left: var(--space_xl7);\tpadding-right: var(--space_xl7);" - }, - "px_xl8": { - "declaration": "padding-left: var(--space_xl8);\tpadding-right: var(--space_xl8);" - }, - "px_xl9": { - "declaration": "padding-left: var(--space_xl9);\tpadding-right: var(--space_xl9);" - }, - "px_xl10": { - "declaration": "padding-left: var(--space_xl10);\tpadding-right: var(--space_xl10);" - }, - "px_xl11": { - "declaration": "padding-left: var(--space_xl11);\tpadding-right: var(--space_xl11);" - }, - "px_xl12": { - "declaration": "padding-left: var(--space_xl12);\tpadding-right: var(--space_xl12);" - }, - "px_xl13": { - "declaration": "padding-left: var(--space_xl13);\tpadding-right: var(--space_xl13);" - }, - "px_xl14": { - "declaration": "padding-left: var(--space_xl14);\tpadding-right: var(--space_xl14);" - }, - "px_xl15": { - "declaration": "padding-left: var(--space_xl15);\tpadding-right: var(--space_xl15);" - }, - "py_0": { - "declaration": "padding-top: 0;\tpadding-bottom: 0;" - }, - "py_xs5": { - "declaration": "padding-top: var(--space_xs5);\tpadding-bottom: var(--space_xs5);" - }, - "py_xs4": { - "declaration": "padding-top: var(--space_xs4);\tpadding-bottom: var(--space_xs4);" - }, - "py_xs3": { - "declaration": "padding-top: var(--space_xs3);\tpadding-bottom: var(--space_xs3);" - }, - "py_xs2": { - "declaration": "padding-top: var(--space_xs2);\tpadding-bottom: var(--space_xs2);" - }, - "py_xs": { - "declaration": "padding-top: var(--space_xs);\tpadding-bottom: var(--space_xs);" - }, - "py_sm": { - "declaration": "padding-top: var(--space_sm);\tpadding-bottom: var(--space_sm);" - }, - "py_md": { - "declaration": "padding-top: var(--space_md);\tpadding-bottom: var(--space_md);" - }, - "py_lg": { - "declaration": "padding-top: var(--space_lg);\tpadding-bottom: var(--space_lg);" - }, - "py_xl": { - "declaration": "padding-top: var(--space_xl);\tpadding-bottom: var(--space_xl);" - }, - "py_xl2": { - "declaration": "padding-top: var(--space_xl2);\tpadding-bottom: var(--space_xl2);" - }, - "py_xl3": { - "declaration": "padding-top: var(--space_xl3);\tpadding-bottom: var(--space_xl3);" - }, - "py_xl4": { - "declaration": "padding-top: var(--space_xl4);\tpadding-bottom: var(--space_xl4);" - }, - "py_xl5": { - "declaration": "padding-top: var(--space_xl5);\tpadding-bottom: var(--space_xl5);" - }, - "py_xl6": { - "declaration": "padding-top: var(--space_xl6);\tpadding-bottom: var(--space_xl6);" - }, - "py_xl7": { - "declaration": "padding-top: var(--space_xl7);\tpadding-bottom: var(--space_xl7);" - }, - "py_xl8": { - "declaration": "padding-top: var(--space_xl8);\tpadding-bottom: var(--space_xl8);" - }, - "py_xl9": { - "declaration": "padding-top: var(--space_xl9);\tpadding-bottom: var(--space_xl9);" - }, - "py_xl10": { - "declaration": "padding-top: var(--space_xl10);\tpadding-bottom: var(--space_xl10);" - }, - "py_xl11": { - "declaration": "padding-top: var(--space_xl11);\tpadding-bottom: var(--space_xl11);" - }, - "py_xl12": { - "declaration": "padding-top: var(--space_xl12);\tpadding-bottom: var(--space_xl12);" - }, - "py_xl13": { - "declaration": "padding-top: var(--space_xl13);\tpadding-bottom: var(--space_xl13);" - }, - "py_xl14": { - "declaration": "padding-top: var(--space_xl14);\tpadding-bottom: var(--space_xl14);" - }, - "py_xl15": { - "declaration": "padding-top: var(--space_xl15);\tpadding-bottom: var(--space_xl15);" - }, - "m_0": { - "declaration": "margin: 0;" - }, - "m_auto": { - "declaration": "margin: auto;" + "border_g_90": { + "declaration": "border-color: var(--palette_g_90); --border_color: var(--palette_g_90);" }, - "m_xs5": { - "declaration": "margin: var(--space_xs5);" + "border_g_95": { + "declaration": "border-color: var(--palette_g_95); --border_color: var(--palette_g_95);" }, - "m_xs4": { - "declaration": "margin: var(--space_xs4);" + "border_g_100": { + "declaration": "border-color: var(--palette_g_100); --border_color: var(--palette_g_100);" }, - "m_xs3": { - "declaration": "margin: var(--space_xs3);" + "border_h_00": { + "declaration": "border-color: var(--palette_h_00); --border_color: var(--palette_h_00);" }, - "m_xs2": { - "declaration": "margin: var(--space_xs2);" + "border_h_05": { + "declaration": "border-color: var(--palette_h_05); --border_color: var(--palette_h_05);" }, - "m_xs": { - "declaration": "margin: var(--space_xs);" + "border_h_10": { + "declaration": "border-color: var(--palette_h_10); --border_color: var(--palette_h_10);" }, - "m_sm": { - "declaration": "margin: var(--space_sm);" + "border_h_20": { + "declaration": "border-color: var(--palette_h_20); --border_color: var(--palette_h_20);" }, - "m_md": { - "declaration": "margin: var(--space_md);" + "border_h_30": { + "declaration": "border-color: var(--palette_h_30); --border_color: var(--palette_h_30);" }, - "m_lg": { - "declaration": "margin: var(--space_lg);" + "border_h_40": { + "declaration": "border-color: var(--palette_h_40); --border_color: var(--palette_h_40);" }, - "m_xl": { - "declaration": "margin: var(--space_xl);" + "border_h_50": { + "declaration": "border-color: var(--palette_h_50); --border_color: var(--palette_h_50);" }, - "m_xl2": { - "declaration": "margin: var(--space_xl2);" + "border_h_60": { + "declaration": "border-color: var(--palette_h_60); --border_color: var(--palette_h_60);" }, - "m_xl3": { - "declaration": "margin: var(--space_xl3);" + "border_h_70": { + "declaration": "border-color: var(--palette_h_70); --border_color: var(--palette_h_70);" }, - "m_xl4": { - "declaration": "margin: var(--space_xl4);" + "border_h_80": { + "declaration": "border-color: var(--palette_h_80); --border_color: var(--palette_h_80);" }, - "m_xl5": { - "declaration": "margin: var(--space_xl5);" + "border_h_90": { + "declaration": "border-color: var(--palette_h_90); --border_color: var(--palette_h_90);" }, - "m_xl6": { - "declaration": "margin: var(--space_xl6);" + "border_h_95": { + "declaration": "border-color: var(--palette_h_95); --border_color: var(--palette_h_95);" }, - "m_xl7": { - "declaration": "margin: var(--space_xl7);" + "border_h_100": { + "declaration": "border-color: var(--palette_h_100); --border_color: var(--palette_h_100);" }, - "m_xl8": { - "declaration": "margin: var(--space_xl8);" + "border_i_00": { + "declaration": "border-color: var(--palette_i_00); --border_color: var(--palette_i_00);" }, - "m_xl9": { - "declaration": "margin: var(--space_xl9);" + "border_i_05": { + "declaration": "border-color: var(--palette_i_05); --border_color: var(--palette_i_05);" }, - "m_xl10": { - "declaration": "margin: var(--space_xl10);" + "border_i_10": { + "declaration": "border-color: var(--palette_i_10); --border_color: var(--palette_i_10);" }, - "m_xl11": { - "declaration": "margin: var(--space_xl11);" + "border_i_20": { + "declaration": "border-color: var(--palette_i_20); --border_color: var(--palette_i_20);" }, - "m_xl12": { - "declaration": "margin: var(--space_xl12);" + "border_i_30": { + "declaration": "border-color: var(--palette_i_30); --border_color: var(--palette_i_30);" }, - "m_xl13": { - "declaration": "margin: var(--space_xl13);" + "border_i_40": { + "declaration": "border-color: var(--palette_i_40); --border_color: var(--palette_i_40);" }, - "m_xl14": { - "declaration": "margin: var(--space_xl14);" + "border_i_50": { + "declaration": "border-color: var(--palette_i_50); --border_color: var(--palette_i_50);" }, - "m_xl15": { - "declaration": "margin: var(--space_xl15);" + "border_i_60": { + "declaration": "border-color: var(--palette_i_60); --border_color: var(--palette_i_60);" }, - "mt_0": { - "declaration": "margin-top: 0;" + "border_i_70": { + "declaration": "border-color: var(--palette_i_70); --border_color: var(--palette_i_70);" }, - "mt_auto": { - "declaration": "margin-top: auto;" + "border_i_80": { + "declaration": "border-color: var(--palette_i_80); --border_color: var(--palette_i_80);" }, - "mt_xs5": { - "declaration": "margin-top: var(--space_xs5);" + "border_i_90": { + "declaration": "border-color: var(--palette_i_90); --border_color: var(--palette_i_90);" }, - "mt_xs4": { - "declaration": "margin-top: var(--space_xs4);" + "border_i_95": { + "declaration": "border-color: var(--palette_i_95); --border_color: var(--palette_i_95);" }, - "mt_xs3": { - "declaration": "margin-top: var(--space_xs3);" + "border_i_100": { + "declaration": "border-color: var(--palette_i_100); --border_color: var(--palette_i_100);" }, - "mt_xs2": { - "declaration": "margin-top: var(--space_xs2);" + "border_j_00": { + "declaration": "border-color: var(--palette_j_00); --border_color: var(--palette_j_00);" }, - "mt_xs": { - "declaration": "margin-top: var(--space_xs);" + "border_j_05": { + "declaration": "border-color: var(--palette_j_05); --border_color: var(--palette_j_05);" }, - "mt_sm": { - "declaration": "margin-top: var(--space_sm);" + "border_j_10": { + "declaration": "border-color: var(--palette_j_10); --border_color: var(--palette_j_10);" }, - "mt_md": { - "declaration": "margin-top: var(--space_md);" + "border_j_20": { + "declaration": "border-color: var(--palette_j_20); --border_color: var(--palette_j_20);" }, - "mt_lg": { - "declaration": "margin-top: var(--space_lg);" + "border_j_30": { + "declaration": "border-color: var(--palette_j_30); --border_color: var(--palette_j_30);" }, - "mt_xl": { - "declaration": "margin-top: var(--space_xl);" + "border_j_40": { + "declaration": "border-color: var(--palette_j_40); --border_color: var(--palette_j_40);" }, - "mt_xl2": { - "declaration": "margin-top: var(--space_xl2);" + "border_j_50": { + "declaration": "border-color: var(--palette_j_50); --border_color: var(--palette_j_50);" }, - "mt_xl3": { - "declaration": "margin-top: var(--space_xl3);" + "border_j_60": { + "declaration": "border-color: var(--palette_j_60); --border_color: var(--palette_j_60);" }, - "mt_xl4": { - "declaration": "margin-top: var(--space_xl4);" + "border_j_70": { + "declaration": "border-color: var(--palette_j_70); --border_color: var(--palette_j_70);" }, - "mt_xl5": { - "declaration": "margin-top: var(--space_xl5);" + "border_j_80": { + "declaration": "border-color: var(--palette_j_80); --border_color: var(--palette_j_80);" }, - "mt_xl6": { - "declaration": "margin-top: var(--space_xl6);" + "border_j_90": { + "declaration": "border-color: var(--palette_j_90); --border_color: var(--palette_j_90);" }, - "mt_xl7": { - "declaration": "margin-top: var(--space_xl7);" + "border_j_95": { + "declaration": "border-color: var(--palette_j_95); --border_color: var(--palette_j_95);" }, - "mt_xl8": { - "declaration": "margin-top: var(--space_xl8);" + "border_j_100": { + "declaration": "border-color: var(--palette_j_100); --border_color: var(--palette_j_100);" }, - "mt_xl9": { - "declaration": "margin-top: var(--space_xl9);" + "outline_color_00": { "declaration": "outline-color: var(--shade_00);" }, + "outline_color_05": { "declaration": "outline-color: var(--shade_05);" }, + "outline_color_10": { "declaration": "outline-color: var(--shade_10);" }, + "outline_color_20": { "declaration": "outline-color: var(--shade_20);" }, + "outline_color_30": { "declaration": "outline-color: var(--shade_30);" }, + "outline_color_40": { "declaration": "outline-color: var(--shade_40);" }, + "outline_color_50": { "declaration": "outline-color: var(--shade_50);" }, + "outline_color_60": { "declaration": "outline-color: var(--shade_60);" }, + "outline_color_70": { "declaration": "outline-color: var(--shade_70);" }, + "outline_color_80": { "declaration": "outline-color: var(--shade_80);" }, + "outline_color_90": { "declaration": "outline-color: var(--shade_90);" }, + "outline_color_95": { "declaration": "outline-color: var(--shade_95);" }, + "outline_color_100": { "declaration": "outline-color: var(--shade_100);" }, + "outline_a_00": { + "declaration": "outline-color: var(--palette_a_00); --outline_color: var(--palette_a_00);" }, - "mt_xl10": { - "declaration": "margin-top: var(--space_xl10);" + "outline_a_05": { + "declaration": "outline-color: var(--palette_a_05); --outline_color: var(--palette_a_05);" }, - "mt_xl11": { - "declaration": "margin-top: var(--space_xl11);" + "outline_a_10": { + "declaration": "outline-color: var(--palette_a_10); --outline_color: var(--palette_a_10);" }, - "mt_xl12": { - "declaration": "margin-top: var(--space_xl12);" + "outline_a_20": { + "declaration": "outline-color: var(--palette_a_20); --outline_color: var(--palette_a_20);" }, - "mt_xl13": { - "declaration": "margin-top: var(--space_xl13);" + "outline_a_30": { + "declaration": "outline-color: var(--palette_a_30); --outline_color: var(--palette_a_30);" }, - "mt_xl14": { - "declaration": "margin-top: var(--space_xl14);" + "outline_a_40": { + "declaration": "outline-color: var(--palette_a_40); --outline_color: var(--palette_a_40);" }, - "mt_xl15": { - "declaration": "margin-top: var(--space_xl15);" + "outline_a_50": { + "declaration": "outline-color: var(--palette_a_50); --outline_color: var(--palette_a_50);" }, - "mr_0": { - "declaration": "margin-right: 0;" + "outline_a_60": { + "declaration": "outline-color: var(--palette_a_60); --outline_color: var(--palette_a_60);" }, - "mr_auto": { - "declaration": "margin-right: auto;" + "outline_a_70": { + "declaration": "outline-color: var(--palette_a_70); --outline_color: var(--palette_a_70);" }, - "mr_xs5": { - "declaration": "margin-right: var(--space_xs5);" + "outline_a_80": { + "declaration": "outline-color: var(--palette_a_80); --outline_color: var(--palette_a_80);" }, - "mr_xs4": { - "declaration": "margin-right: var(--space_xs4);" + "outline_a_90": { + "declaration": "outline-color: var(--palette_a_90); --outline_color: var(--palette_a_90);" }, - "mr_xs3": { - "declaration": "margin-right: var(--space_xs3);" + "outline_a_95": { + "declaration": "outline-color: var(--palette_a_95); --outline_color: var(--palette_a_95);" }, - "mr_xs2": { - "declaration": "margin-right: var(--space_xs2);" + "outline_a_100": { + "declaration": "outline-color: var(--palette_a_100); --outline_color: var(--palette_a_100);" }, - "mr_xs": { - "declaration": "margin-right: var(--space_xs);" + "outline_b_00": { + "declaration": "outline-color: var(--palette_b_00); --outline_color: var(--palette_b_00);" }, - "mr_sm": { - "declaration": "margin-right: var(--space_sm);" + "outline_b_05": { + "declaration": "outline-color: var(--palette_b_05); --outline_color: var(--palette_b_05);" }, - "mr_md": { - "declaration": "margin-right: var(--space_md);" + "outline_b_10": { + "declaration": "outline-color: var(--palette_b_10); --outline_color: var(--palette_b_10);" }, - "mr_lg": { - "declaration": "margin-right: var(--space_lg);" + "outline_b_20": { + "declaration": "outline-color: var(--palette_b_20); --outline_color: var(--palette_b_20);" }, - "mr_xl": { - "declaration": "margin-right: var(--space_xl);" + "outline_b_30": { + "declaration": "outline-color: var(--palette_b_30); --outline_color: var(--palette_b_30);" }, - "mr_xl2": { - "declaration": "margin-right: var(--space_xl2);" + "outline_b_40": { + "declaration": "outline-color: var(--palette_b_40); --outline_color: var(--palette_b_40);" }, - "mr_xl3": { - "declaration": "margin-right: var(--space_xl3);" + "outline_b_50": { + "declaration": "outline-color: var(--palette_b_50); --outline_color: var(--palette_b_50);" }, - "mr_xl4": { - "declaration": "margin-right: var(--space_xl4);" + "outline_b_60": { + "declaration": "outline-color: var(--palette_b_60); --outline_color: var(--palette_b_60);" }, - "mr_xl5": { - "declaration": "margin-right: var(--space_xl5);" + "outline_b_70": { + "declaration": "outline-color: var(--palette_b_70); --outline_color: var(--palette_b_70);" }, - "mr_xl6": { - "declaration": "margin-right: var(--space_xl6);" + "outline_b_80": { + "declaration": "outline-color: var(--palette_b_80); --outline_color: var(--palette_b_80);" }, - "mr_xl7": { - "declaration": "margin-right: var(--space_xl7);" + "outline_b_90": { + "declaration": "outline-color: var(--palette_b_90); --outline_color: var(--palette_b_90);" }, - "mr_xl8": { - "declaration": "margin-right: var(--space_xl8);" + "outline_b_95": { + "declaration": "outline-color: var(--palette_b_95); --outline_color: var(--palette_b_95);" }, - "mr_xl9": { - "declaration": "margin-right: var(--space_xl9);" + "outline_b_100": { + "declaration": "outline-color: var(--palette_b_100); --outline_color: var(--palette_b_100);" }, - "mr_xl10": { - "declaration": "margin-right: var(--space_xl10);" + "outline_c_00": { + "declaration": "outline-color: var(--palette_c_00); --outline_color: var(--palette_c_00);" }, - "mr_xl11": { - "declaration": "margin-right: var(--space_xl11);" + "outline_c_05": { + "declaration": "outline-color: var(--palette_c_05); --outline_color: var(--palette_c_05);" }, - "mr_xl12": { - "declaration": "margin-right: var(--space_xl12);" + "outline_c_10": { + "declaration": "outline-color: var(--palette_c_10); --outline_color: var(--palette_c_10);" }, - "mr_xl13": { - "declaration": "margin-right: var(--space_xl13);" + "outline_c_20": { + "declaration": "outline-color: var(--palette_c_20); --outline_color: var(--palette_c_20);" }, - "mr_xl14": { - "declaration": "margin-right: var(--space_xl14);" + "outline_c_30": { + "declaration": "outline-color: var(--palette_c_30); --outline_color: var(--palette_c_30);" }, - "mr_xl15": { - "declaration": "margin-right: var(--space_xl15);" + "outline_c_40": { + "declaration": "outline-color: var(--palette_c_40); --outline_color: var(--palette_c_40);" }, - "mb_0": { - "declaration": "margin-bottom: 0;" + "outline_c_50": { + "declaration": "outline-color: var(--palette_c_50); --outline_color: var(--palette_c_50);" }, - "mb_auto": { - "declaration": "margin-bottom: auto;" + "outline_c_60": { + "declaration": "outline-color: var(--palette_c_60); --outline_color: var(--palette_c_60);" }, - "mb_xs5": { - "declaration": "margin-bottom: var(--space_xs5);" + "outline_c_70": { + "declaration": "outline-color: var(--palette_c_70); --outline_color: var(--palette_c_70);" }, - "mb_xs4": { - "declaration": "margin-bottom: var(--space_xs4);" + "outline_c_80": { + "declaration": "outline-color: var(--palette_c_80); --outline_color: var(--palette_c_80);" }, - "mb_xs3": { - "declaration": "margin-bottom: var(--space_xs3);" + "outline_c_90": { + "declaration": "outline-color: var(--palette_c_90); --outline_color: var(--palette_c_90);" }, - "mb_xs2": { - "declaration": "margin-bottom: var(--space_xs2);" + "outline_c_95": { + "declaration": "outline-color: var(--palette_c_95); --outline_color: var(--palette_c_95);" }, - "mb_xs": { - "declaration": "margin-bottom: var(--space_xs);" + "outline_c_100": { + "declaration": "outline-color: var(--palette_c_100); --outline_color: var(--palette_c_100);" }, - "mb_sm": { - "declaration": "margin-bottom: var(--space_sm);" + "outline_d_00": { + "declaration": "outline-color: var(--palette_d_00); --outline_color: var(--palette_d_00);" }, - "mb_md": { - "declaration": "margin-bottom: var(--space_md);" + "outline_d_05": { + "declaration": "outline-color: var(--palette_d_05); --outline_color: var(--palette_d_05);" }, - "mb_lg": { - "declaration": "margin-bottom: var(--space_lg);" + "outline_d_10": { + "declaration": "outline-color: var(--palette_d_10); --outline_color: var(--palette_d_10);" }, - "mb_xl": { - "declaration": "margin-bottom: var(--space_xl);" + "outline_d_20": { + "declaration": "outline-color: var(--palette_d_20); --outline_color: var(--palette_d_20);" }, - "mb_xl2": { - "declaration": "margin-bottom: var(--space_xl2);" + "outline_d_30": { + "declaration": "outline-color: var(--palette_d_30); --outline_color: var(--palette_d_30);" }, - "mb_xl3": { - "declaration": "margin-bottom: var(--space_xl3);" + "outline_d_40": { + "declaration": "outline-color: var(--palette_d_40); --outline_color: var(--palette_d_40);" }, - "mb_xl4": { - "declaration": "margin-bottom: var(--space_xl4);" + "outline_d_50": { + "declaration": "outline-color: var(--palette_d_50); --outline_color: var(--palette_d_50);" }, - "mb_xl5": { - "declaration": "margin-bottom: var(--space_xl5);" + "outline_d_60": { + "declaration": "outline-color: var(--palette_d_60); --outline_color: var(--palette_d_60);" }, - "mb_xl6": { - "declaration": "margin-bottom: var(--space_xl6);" + "outline_d_70": { + "declaration": "outline-color: var(--palette_d_70); --outline_color: var(--palette_d_70);" }, - "mb_xl7": { - "declaration": "margin-bottom: var(--space_xl7);" + "outline_d_80": { + "declaration": "outline-color: var(--palette_d_80); --outline_color: var(--palette_d_80);" }, - "mb_xl8": { - "declaration": "margin-bottom: var(--space_xl8);" + "outline_d_90": { + "declaration": "outline-color: var(--palette_d_90); --outline_color: var(--palette_d_90);" }, - "mb_xl9": { - "declaration": "margin-bottom: var(--space_xl9);" + "outline_d_95": { + "declaration": "outline-color: var(--palette_d_95); --outline_color: var(--palette_d_95);" }, - "mb_xl10": { - "declaration": "margin-bottom: var(--space_xl10);" + "outline_d_100": { + "declaration": "outline-color: var(--palette_d_100); --outline_color: var(--palette_d_100);" }, - "mb_xl11": { - "declaration": "margin-bottom: var(--space_xl11);" + "outline_e_00": { + "declaration": "outline-color: var(--palette_e_00); --outline_color: var(--palette_e_00);" }, - "mb_xl12": { - "declaration": "margin-bottom: var(--space_xl12);" + "outline_e_05": { + "declaration": "outline-color: var(--palette_e_05); --outline_color: var(--palette_e_05);" }, - "mb_xl13": { - "declaration": "margin-bottom: var(--space_xl13);" + "outline_e_10": { + "declaration": "outline-color: var(--palette_e_10); --outline_color: var(--palette_e_10);" }, - "mb_xl14": { - "declaration": "margin-bottom: var(--space_xl14);" + "outline_e_20": { + "declaration": "outline-color: var(--palette_e_20); --outline_color: var(--palette_e_20);" }, - "mb_xl15": { - "declaration": "margin-bottom: var(--space_xl15);" + "outline_e_30": { + "declaration": "outline-color: var(--palette_e_30); --outline_color: var(--palette_e_30);" }, - "ml_0": { - "declaration": "margin-left: 0;" + "outline_e_40": { + "declaration": "outline-color: var(--palette_e_40); --outline_color: var(--palette_e_40);" }, - "ml_auto": { - "declaration": "margin-left: auto;" + "outline_e_50": { + "declaration": "outline-color: var(--palette_e_50); --outline_color: var(--palette_e_50);" }, - "ml_xs5": { - "declaration": "margin-left: var(--space_xs5);" + "outline_e_60": { + "declaration": "outline-color: var(--palette_e_60); --outline_color: var(--palette_e_60);" }, - "ml_xs4": { - "declaration": "margin-left: var(--space_xs4);" + "outline_e_70": { + "declaration": "outline-color: var(--palette_e_70); --outline_color: var(--palette_e_70);" }, - "ml_xs3": { - "declaration": "margin-left: var(--space_xs3);" + "outline_e_80": { + "declaration": "outline-color: var(--palette_e_80); --outline_color: var(--palette_e_80);" }, - "ml_xs2": { - "declaration": "margin-left: var(--space_xs2);" + "outline_e_90": { + "declaration": "outline-color: var(--palette_e_90); --outline_color: var(--palette_e_90);" }, - "ml_xs": { - "declaration": "margin-left: var(--space_xs);" + "outline_e_95": { + "declaration": "outline-color: var(--palette_e_95); --outline_color: var(--palette_e_95);" }, - "ml_sm": { - "declaration": "margin-left: var(--space_sm);" + "outline_e_100": { + "declaration": "outline-color: var(--palette_e_100); --outline_color: var(--palette_e_100);" }, - "ml_md": { - "declaration": "margin-left: var(--space_md);" + "outline_f_00": { + "declaration": "outline-color: var(--palette_f_00); --outline_color: var(--palette_f_00);" }, - "ml_lg": { - "declaration": "margin-left: var(--space_lg);" + "outline_f_05": { + "declaration": "outline-color: var(--palette_f_05); --outline_color: var(--palette_f_05);" }, - "ml_xl": { - "declaration": "margin-left: var(--space_xl);" + "outline_f_10": { + "declaration": "outline-color: var(--palette_f_10); --outline_color: var(--palette_f_10);" }, - "ml_xl2": { - "declaration": "margin-left: var(--space_xl2);" + "outline_f_20": { + "declaration": "outline-color: var(--palette_f_20); --outline_color: var(--palette_f_20);" }, - "ml_xl3": { - "declaration": "margin-left: var(--space_xl3);" + "outline_f_30": { + "declaration": "outline-color: var(--palette_f_30); --outline_color: var(--palette_f_30);" }, - "ml_xl4": { - "declaration": "margin-left: var(--space_xl4);" + "outline_f_40": { + "declaration": "outline-color: var(--palette_f_40); --outline_color: var(--palette_f_40);" }, - "ml_xl5": { - "declaration": "margin-left: var(--space_xl5);" + "outline_f_50": { + "declaration": "outline-color: var(--palette_f_50); --outline_color: var(--palette_f_50);" }, - "ml_xl6": { - "declaration": "margin-left: var(--space_xl6);" + "outline_f_60": { + "declaration": "outline-color: var(--palette_f_60); --outline_color: var(--palette_f_60);" }, - "ml_xl7": { - "declaration": "margin-left: var(--space_xl7);" + "outline_f_70": { + "declaration": "outline-color: var(--palette_f_70); --outline_color: var(--palette_f_70);" }, - "ml_xl8": { - "declaration": "margin-left: var(--space_xl8);" + "outline_f_80": { + "declaration": "outline-color: var(--palette_f_80); --outline_color: var(--palette_f_80);" }, - "ml_xl9": { - "declaration": "margin-left: var(--space_xl9);" + "outline_f_90": { + "declaration": "outline-color: var(--palette_f_90); --outline_color: var(--palette_f_90);" }, - "ml_xl10": { - "declaration": "margin-left: var(--space_xl10);" + "outline_f_95": { + "declaration": "outline-color: var(--palette_f_95); --outline_color: var(--palette_f_95);" }, - "ml_xl11": { - "declaration": "margin-left: var(--space_xl11);" + "outline_f_100": { + "declaration": "outline-color: var(--palette_f_100); --outline_color: var(--palette_f_100);" }, - "ml_xl12": { - "declaration": "margin-left: var(--space_xl12);" + "outline_g_00": { + "declaration": "outline-color: var(--palette_g_00); --outline_color: var(--palette_g_00);" }, - "ml_xl13": { - "declaration": "margin-left: var(--space_xl13);" + "outline_g_05": { + "declaration": "outline-color: var(--palette_g_05); --outline_color: var(--palette_g_05);" }, - "ml_xl14": { - "declaration": "margin-left: var(--space_xl14);" + "outline_g_10": { + "declaration": "outline-color: var(--palette_g_10); --outline_color: var(--palette_g_10);" }, - "ml_xl15": { - "declaration": "margin-left: var(--space_xl15);" + "outline_g_20": { + "declaration": "outline-color: var(--palette_g_20); --outline_color: var(--palette_g_20);" }, - "mx_0": { - "declaration": "margin-left: 0;\tmargin-right: 0;" + "outline_g_30": { + "declaration": "outline-color: var(--palette_g_30); --outline_color: var(--palette_g_30);" }, - "mx_auto": { - "declaration": "margin-left: auto;\tmargin-right: auto;" + "outline_g_40": { + "declaration": "outline-color: var(--palette_g_40); --outline_color: var(--palette_g_40);" }, - "mx_xs5": { - "declaration": "margin-left: var(--space_xs5);\tmargin-right: var(--space_xs5);" + "outline_g_50": { + "declaration": "outline-color: var(--palette_g_50); --outline_color: var(--palette_g_50);" }, - "mx_xs4": { - "declaration": "margin-left: var(--space_xs4);\tmargin-right: var(--space_xs4);" + "outline_g_60": { + "declaration": "outline-color: var(--palette_g_60); --outline_color: var(--palette_g_60);" }, - "mx_xs3": { - "declaration": "margin-left: var(--space_xs3);\tmargin-right: var(--space_xs3);" + "outline_g_70": { + "declaration": "outline-color: var(--palette_g_70); --outline_color: var(--palette_g_70);" }, - "mx_xs2": { - "declaration": "margin-left: var(--space_xs2);\tmargin-right: var(--space_xs2);" + "outline_g_80": { + "declaration": "outline-color: var(--palette_g_80); --outline_color: var(--palette_g_80);" }, - "mx_xs": { - "declaration": "margin-left: var(--space_xs);\tmargin-right: var(--space_xs);" + "outline_g_90": { + "declaration": "outline-color: var(--palette_g_90); --outline_color: var(--palette_g_90);" }, - "mx_sm": { - "declaration": "margin-left: var(--space_sm);\tmargin-right: var(--space_sm);" + "outline_g_95": { + "declaration": "outline-color: var(--palette_g_95); --outline_color: var(--palette_g_95);" }, - "mx_md": { - "declaration": "margin-left: var(--space_md);\tmargin-right: var(--space_md);" + "outline_g_100": { + "declaration": "outline-color: var(--palette_g_100); --outline_color: var(--palette_g_100);" }, - "mx_lg": { - "declaration": "margin-left: var(--space_lg);\tmargin-right: var(--space_lg);" + "outline_h_00": { + "declaration": "outline-color: var(--palette_h_00); --outline_color: var(--palette_h_00);" }, - "mx_xl": { - "declaration": "margin-left: var(--space_xl);\tmargin-right: var(--space_xl);" + "outline_h_05": { + "declaration": "outline-color: var(--palette_h_05); --outline_color: var(--palette_h_05);" }, - "mx_xl2": { - "declaration": "margin-left: var(--space_xl2);\tmargin-right: var(--space_xl2);" + "outline_h_10": { + "declaration": "outline-color: var(--palette_h_10); --outline_color: var(--palette_h_10);" }, - "mx_xl3": { - "declaration": "margin-left: var(--space_xl3);\tmargin-right: var(--space_xl3);" + "outline_h_20": { + "declaration": "outline-color: var(--palette_h_20); --outline_color: var(--palette_h_20);" }, - "mx_xl4": { - "declaration": "margin-left: var(--space_xl4);\tmargin-right: var(--space_xl4);" + "outline_h_30": { + "declaration": "outline-color: var(--palette_h_30); --outline_color: var(--palette_h_30);" }, - "mx_xl5": { - "declaration": "margin-left: var(--space_xl5);\tmargin-right: var(--space_xl5);" + "outline_h_40": { + "declaration": "outline-color: var(--palette_h_40); --outline_color: var(--palette_h_40);" }, - "mx_xl6": { - "declaration": "margin-left: var(--space_xl6);\tmargin-right: var(--space_xl6);" + "outline_h_50": { + "declaration": "outline-color: var(--palette_h_50); --outline_color: var(--palette_h_50);" }, - "mx_xl7": { - "declaration": "margin-left: var(--space_xl7);\tmargin-right: var(--space_xl7);" + "outline_h_60": { + "declaration": "outline-color: var(--palette_h_60); --outline_color: var(--palette_h_60);" }, - "mx_xl8": { - "declaration": "margin-left: var(--space_xl8);\tmargin-right: var(--space_xl8);" + "outline_h_70": { + "declaration": "outline-color: var(--palette_h_70); --outline_color: var(--palette_h_70);" }, - "mx_xl9": { - "declaration": "margin-left: var(--space_xl9);\tmargin-right: var(--space_xl9);" + "outline_h_80": { + "declaration": "outline-color: var(--palette_h_80); --outline_color: var(--palette_h_80);" }, - "mx_xl10": { - "declaration": "margin-left: var(--space_xl10);\tmargin-right: var(--space_xl10);" + "outline_h_90": { + "declaration": "outline-color: var(--palette_h_90); --outline_color: var(--palette_h_90);" }, - "mx_xl11": { - "declaration": "margin-left: var(--space_xl11);\tmargin-right: var(--space_xl11);" + "outline_h_95": { + "declaration": "outline-color: var(--palette_h_95); --outline_color: var(--palette_h_95);" }, - "mx_xl12": { - "declaration": "margin-left: var(--space_xl12);\tmargin-right: var(--space_xl12);" + "outline_h_100": { + "declaration": "outline-color: var(--palette_h_100); --outline_color: var(--palette_h_100);" }, - "mx_xl13": { - "declaration": "margin-left: var(--space_xl13);\tmargin-right: var(--space_xl13);" + "outline_i_00": { + "declaration": "outline-color: var(--palette_i_00); --outline_color: var(--palette_i_00);" }, - "mx_xl14": { - "declaration": "margin-left: var(--space_xl14);\tmargin-right: var(--space_xl14);" + "outline_i_05": { + "declaration": "outline-color: var(--palette_i_05); --outline_color: var(--palette_i_05);" }, - "mx_xl15": { - "declaration": "margin-left: var(--space_xl15);\tmargin-right: var(--space_xl15);" + "outline_i_10": { + "declaration": "outline-color: var(--palette_i_10); --outline_color: var(--palette_i_10);" }, - "my_0": { - "declaration": "margin-top: 0;\tmargin-bottom: 0;" + "outline_i_20": { + "declaration": "outline-color: var(--palette_i_20); --outline_color: var(--palette_i_20);" }, - "my_auto": { - "declaration": "margin-top: auto;\tmargin-bottom: auto;" + "outline_i_30": { + "declaration": "outline-color: var(--palette_i_30); --outline_color: var(--palette_i_30);" }, - "my_xs5": { - "declaration": "margin-top: var(--space_xs5);\tmargin-bottom: var(--space_xs5);" + "outline_i_40": { + "declaration": "outline-color: var(--palette_i_40); --outline_color: var(--palette_i_40);" }, - "my_xs4": { - "declaration": "margin-top: var(--space_xs4);\tmargin-bottom: var(--space_xs4);" + "outline_i_50": { + "declaration": "outline-color: var(--palette_i_50); --outline_color: var(--palette_i_50);" }, - "my_xs3": { - "declaration": "margin-top: var(--space_xs3);\tmargin-bottom: var(--space_xs3);" + "outline_i_60": { + "declaration": "outline-color: var(--palette_i_60); --outline_color: var(--palette_i_60);" }, - "my_xs2": { - "declaration": "margin-top: var(--space_xs2);\tmargin-bottom: var(--space_xs2);" + "outline_i_70": { + "declaration": "outline-color: var(--palette_i_70); --outline_color: var(--palette_i_70);" }, - "my_xs": { - "declaration": "margin-top: var(--space_xs);\tmargin-bottom: var(--space_xs);" + "outline_i_80": { + "declaration": "outline-color: var(--palette_i_80); --outline_color: var(--palette_i_80);" }, - "my_sm": { - "declaration": "margin-top: var(--space_sm);\tmargin-bottom: var(--space_sm);" + "outline_i_90": { + "declaration": "outline-color: var(--palette_i_90); --outline_color: var(--palette_i_90);" }, - "my_md": { - "declaration": "margin-top: var(--space_md);\tmargin-bottom: var(--space_md);" + "outline_i_95": { + "declaration": "outline-color: var(--palette_i_95); --outline_color: var(--palette_i_95);" }, - "my_lg": { - "declaration": "margin-top: var(--space_lg);\tmargin-bottom: var(--space_lg);" + "outline_i_100": { + "declaration": "outline-color: var(--palette_i_100); --outline_color: var(--palette_i_100);" }, - "my_xl": { - "declaration": "margin-top: var(--space_xl);\tmargin-bottom: var(--space_xl);" + "outline_j_00": { + "declaration": "outline-color: var(--palette_j_00); --outline_color: var(--palette_j_00);" }, - "my_xl2": { - "declaration": "margin-top: var(--space_xl2);\tmargin-bottom: var(--space_xl2);" + "outline_j_05": { + "declaration": "outline-color: var(--palette_j_05); --outline_color: var(--palette_j_05);" }, - "my_xl3": { - "declaration": "margin-top: var(--space_xl3);\tmargin-bottom: var(--space_xl3);" + "outline_j_10": { + "declaration": "outline-color: var(--palette_j_10); --outline_color: var(--palette_j_10);" }, - "my_xl4": { - "declaration": "margin-top: var(--space_xl4);\tmargin-bottom: var(--space_xl4);" + "outline_j_20": { + "declaration": "outline-color: var(--palette_j_20); --outline_color: var(--palette_j_20);" }, - "my_xl5": { - "declaration": "margin-top: var(--space_xl5);\tmargin-bottom: var(--space_xl5);" + "outline_j_30": { + "declaration": "outline-color: var(--palette_j_30); --outline_color: var(--palette_j_30);" }, - "my_xl6": { - "declaration": "margin-top: var(--space_xl6);\tmargin-bottom: var(--space_xl6);" + "outline_j_40": { + "declaration": "outline-color: var(--palette_j_40); --outline_color: var(--palette_j_40);" }, - "my_xl7": { - "declaration": "margin-top: var(--space_xl7);\tmargin-bottom: var(--space_xl7);" + "outline_j_50": { + "declaration": "outline-color: var(--palette_j_50); --outline_color: var(--palette_j_50);" }, - "my_xl8": { - "declaration": "margin-top: var(--space_xl8);\tmargin-bottom: var(--space_xl8);" + "outline_j_60": { + "declaration": "outline-color: var(--palette_j_60); --outline_color: var(--palette_j_60);" }, - "my_xl9": { - "declaration": "margin-top: var(--space_xl9);\tmargin-bottom: var(--space_xl9);" + "outline_j_70": { + "declaration": "outline-color: var(--palette_j_70); --outline_color: var(--palette_j_70);" }, - "my_xl10": { - "declaration": "margin-top: var(--space_xl10);\tmargin-bottom: var(--space_xl10);" + "outline_j_80": { + "declaration": "outline-color: var(--palette_j_80); --outline_color: var(--palette_j_80);" }, - "my_xl11": { - "declaration": "margin-top: var(--space_xl11);\tmargin-bottom: var(--space_xl11);" + "outline_j_90": { + "declaration": "outline-color: var(--palette_j_90); --outline_color: var(--palette_j_90);" }, - "my_xl12": { - "declaration": "margin-top: var(--space_xl12);\tmargin-bottom: var(--space_xl12);" + "outline_j_95": { + "declaration": "outline-color: var(--palette_j_95); --outline_color: var(--palette_j_95);" }, - "my_xl13": { - "declaration": "margin-top: var(--space_xl13);\tmargin-bottom: var(--space_xl13);" + "outline_j_100": { + "declaration": "outline-color: var(--palette_j_100); --outline_color: var(--palette_j_100);" }, - "my_xl14": { - "declaration": "margin-top: var(--space_xl14);\tmargin-bottom: var(--space_xl14);" + "border_width_1": { "declaration": "border-width: var(--border_width_1);" }, + "border_width_2": { "declaration": "border-width: var(--border_width_2);" }, + "border_width_3": { "declaration": "border-width: var(--border_width_3);" }, + "border_width_4": { "declaration": "border-width: var(--border_width_4);" }, + "border_width_5": { "declaration": "border-width: var(--border_width_5);" }, + "border_width_6": { "declaration": "border-width: var(--border_width_6);" }, + "border_width_7": { "declaration": "border-width: var(--border_width_7);" }, + "border_width_8": { "declaration": "border-width: var(--border_width_8);" }, + "border_width_9": { "declaration": "border-width: var(--border_width_9);" }, + "outline_width_1": { "declaration": "outline-width: var(--border_width_1);" }, + "outline_width_2": { "declaration": "outline-width: var(--border_width_2);" }, + "outline_width_3": { "declaration": "outline-width: var(--border_width_3);" }, + "outline_width_4": { "declaration": "outline-width: var(--border_width_4);" }, + "outline_width_5": { "declaration": "outline-width: var(--border_width_5);" }, + "outline_width_6": { "declaration": "outline-width: var(--border_width_6);" }, + "outline_width_7": { "declaration": "outline-width: var(--border_width_7);" }, + "outline_width_8": { "declaration": "outline-width: var(--border_width_8);" }, + "outline_width_9": { "declaration": "outline-width: var(--border_width_9);" }, + "outline_width_focus": { "declaration": "outline-width: var(--outline_width_focus);" }, + "outline_width_active": { "declaration": "outline-width: var(--outline_width_active);" }, + "border_radius_xs3": { "declaration": "border-radius: var(--border_radius_xs3);" }, + "border_radius_xs2": { "declaration": "border-radius: var(--border_radius_xs2);" }, + "border_radius_xs": { "declaration": "border-radius: var(--border_radius_xs);" }, + "border_radius_sm": { "declaration": "border-radius: var(--border_radius_sm);" }, + "border_radius_md": { "declaration": "border-radius: var(--border_radius_md);" }, + "border_radius_lg": { "declaration": "border-radius: var(--border_radius_lg);" }, + "border_radius_xl": { "declaration": "border-radius: var(--border_radius_xl);" }, + "border_top_left_radius_xs3": { + "declaration": "border-top-left-radius: var(--border_radius_xs3);" }, - "my_xl15": { - "declaration": "margin-top: var(--space_xl15);\tmargin-bottom: var(--space_xl15);" + "border_top_left_radius_xs2": { + "declaration": "border-top-left-radius: var(--border_radius_xs2);" }, - "gap_xs5": { - "declaration": "gap: var(--space_xs5);" + "border_top_left_radius_xs": { + "declaration": "border-top-left-radius: var(--border_radius_xs);" }, - "gap_xs4": { - "declaration": "gap: var(--space_xs4);" + "border_top_left_radius_sm": { + "declaration": "border-top-left-radius: var(--border_radius_sm);" }, - "gap_xs3": { - "declaration": "gap: var(--space_xs3);" + "border_top_left_radius_md": { + "declaration": "border-top-left-radius: var(--border_radius_md);" }, - "gap_xs2": { - "declaration": "gap: var(--space_xs2);" + "border_top_left_radius_lg": { + "declaration": "border-top-left-radius: var(--border_radius_lg);" }, - "gap_xs": { - "declaration": "gap: var(--space_xs);" + "border_top_left_radius_xl": { + "declaration": "border-top-left-radius: var(--border_radius_xl);" }, - "gap_sm": { - "declaration": "gap: var(--space_sm);" + "border_top_right_radius_xs3": { + "declaration": "border-top-right-radius: var(--border_radius_xs3);" }, - "gap_md": { - "declaration": "gap: var(--space_md);" + "border_top_right_radius_xs2": { + "declaration": "border-top-right-radius: var(--border_radius_xs2);" }, - "gap_lg": { - "declaration": "gap: var(--space_lg);" + "border_top_right_radius_xs": { + "declaration": "border-top-right-radius: var(--border_radius_xs);" }, - "gap_xl": { - "declaration": "gap: var(--space_xl);" + "border_top_right_radius_sm": { + "declaration": "border-top-right-radius: var(--border_radius_sm);" }, - "gap_xl2": { - "declaration": "gap: var(--space_xl2);" + "border_top_right_radius_md": { + "declaration": "border-top-right-radius: var(--border_radius_md);" }, - "gap_xl3": { - "declaration": "gap: var(--space_xl3);" + "border_top_right_radius_lg": { + "declaration": "border-top-right-radius: var(--border_radius_lg);" }, - "gap_xl4": { - "declaration": "gap: var(--space_xl4);" + "border_top_right_radius_xl": { + "declaration": "border-top-right-radius: var(--border_radius_xl);" }, - "gap_xl5": { - "declaration": "gap: var(--space_xl5);" + "border_bottom_left_radius_xs3": { + "declaration": "border-bottom-left-radius: var(--border_radius_xs3);" }, - "gap_xl6": { - "declaration": "gap: var(--space_xl6);" + "border_bottom_left_radius_xs2": { + "declaration": "border-bottom-left-radius: var(--border_radius_xs2);" }, - "gap_xl7": { - "declaration": "gap: var(--space_xl7);" + "border_bottom_left_radius_xs": { + "declaration": "border-bottom-left-radius: var(--border_radius_xs);" }, - "gap_xl8": { - "declaration": "gap: var(--space_xl8);" + "border_bottom_left_radius_sm": { + "declaration": "border-bottom-left-radius: var(--border_radius_sm);" }, - "gap_xl9": { - "declaration": "gap: var(--space_xl9);" + "border_bottom_left_radius_md": { + "declaration": "border-bottom-left-radius: var(--border_radius_md);" }, - "gap_xl10": { - "declaration": "gap: var(--space_xl10);" + "border_bottom_left_radius_lg": { + "declaration": "border-bottom-left-radius: var(--border_radius_lg);" }, - "gap_xl11": { - "declaration": "gap: var(--space_xl11);" + "border_bottom_left_radius_xl": { + "declaration": "border-bottom-left-radius: var(--border_radius_xl);" }, - "gap_xl12": { - "declaration": "gap: var(--space_xl12);" + "border_bottom_right_radius_xs3": { + "declaration": "border-bottom-right-radius: var(--border_radius_xs3);" }, - "gap_xl13": { - "declaration": "gap: var(--space_xl13);" + "border_bottom_right_radius_xs2": { + "declaration": "border-bottom-right-radius: var(--border_radius_xs2);" }, - "gap_xl14": { - "declaration": "gap: var(--space_xl14);" + "border_bottom_right_radius_xs": { + "declaration": "border-bottom-right-radius: var(--border_radius_xs);" }, - "gap_xl15": { - "declaration": "gap: var(--space_xl15);" + "border_bottom_right_radius_sm": { + "declaration": "border-bottom-right-radius: var(--border_radius_sm);" }, - "column_gap_xs5": { - "declaration": "column-gap: var(--space_xs5);" + "border_bottom_right_radius_md": { + "declaration": "border-bottom-right-radius: var(--border_radius_md);" }, - "column_gap_xs4": { - "declaration": "column-gap: var(--space_xs4);" + "border_bottom_right_radius_lg": { + "declaration": "border-bottom-right-radius: var(--border_radius_lg);" }, - "column_gap_xs3": { - "declaration": "column-gap: var(--space_xs3);" + "border_bottom_right_radius_xl": { + "declaration": "border-bottom-right-radius: var(--border_radius_xl);" }, - "column_gap_xs2": { - "declaration": "column-gap: var(--space_xs2);" + "shadow_xs": { + "declaration": "box-shadow: var(--shadow_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" }, - "column_gap_xs": { - "declaration": "column-gap: var(--space_xs);" + "shadow_sm": { + "declaration": "box-shadow: var(--shadow_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" }, - "column_gap_sm": { - "declaration": "column-gap: var(--space_sm);" + "shadow_md": { + "declaration": "box-shadow: var(--shadow_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" }, - "column_gap_md": { - "declaration": "column-gap: var(--space_md);" + "shadow_lg": { + "declaration": "box-shadow: var(--shadow_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" }, - "column_gap_lg": { - "declaration": "column-gap: var(--space_lg);" + "shadow_xl": { + "declaration": "box-shadow: var(--shadow_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" }, - "column_gap_xl": { - "declaration": "column-gap: var(--space_xl);" + "shadow_top_xs": { + "declaration": "box-shadow: var(--shadow_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" }, - "column_gap_xl2": { - "declaration": "column-gap: var(--space_xl2);" + "shadow_top_sm": { + "declaration": "box-shadow: var(--shadow_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" }, - "column_gap_xl3": { - "declaration": "column-gap: var(--space_xl3);" + "shadow_top_md": { + "declaration": "box-shadow: var(--shadow_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" }, - "column_gap_xl4": { - "declaration": "column-gap: var(--space_xl4);" + "shadow_top_lg": { + "declaration": "box-shadow: var(--shadow_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" }, - "column_gap_xl5": { - "declaration": "column-gap: var(--space_xl5);" + "shadow_top_xl": { + "declaration": "box-shadow: var(--shadow_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" }, - "column_gap_xl6": { - "declaration": "column-gap: var(--space_xl6);" + "shadow_bottom_xs": { + "declaration": "box-shadow: var(--shadow_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" }, - "column_gap_xl7": { - "declaration": "column-gap: var(--space_xl7);" + "shadow_bottom_sm": { + "declaration": "box-shadow: var(--shadow_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" }, - "column_gap_xl8": { - "declaration": "column-gap: var(--space_xl8);" + "shadow_bottom_md": { + "declaration": "box-shadow: var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" }, - "column_gap_xl9": { - "declaration": "column-gap: var(--space_xl9);" + "shadow_bottom_lg": { + "declaration": "box-shadow: var(--shadow_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" }, - "column_gap_xl10": { - "declaration": "column-gap: var(--space_xl10);" + "shadow_bottom_xl": { + "declaration": "box-shadow: var(--shadow_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" }, - "column_gap_xl11": { - "declaration": "column-gap: var(--space_xl11);" + "shadow_inset_xs": { + "declaration": "box-shadow: var(--shadow_inset_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" }, - "column_gap_xl12": { - "declaration": "column-gap: var(--space_xl12);" + "shadow_inset_sm": { + "declaration": "box-shadow: var(--shadow_inset_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" }, - "column_gap_xl13": { - "declaration": "column-gap: var(--space_xl13);" + "shadow_inset_md": { + "declaration": "box-shadow: var(--shadow_inset_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" }, - "column_gap_xl14": { - "declaration": "column-gap: var(--space_xl14);" + "shadow_inset_lg": { + "declaration": "box-shadow: var(--shadow_inset_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" }, - "column_gap_xl15": { - "declaration": "column-gap: var(--space_xl15);" + "shadow_inset_xl": { + "declaration": "box-shadow: var(--shadow_inset_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" }, - "row_gap_xs5": { - "declaration": "row-gap: var(--space_xs5);" + "shadow_inset_top_xs": { + "declaration": "box-shadow: var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" }, - "row_gap_xs4": { - "declaration": "row-gap: var(--space_xs4);" + "shadow_inset_top_sm": { + "declaration": "box-shadow: var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" }, - "row_gap_xs3": { - "declaration": "row-gap: var(--space_xs3);" + "shadow_inset_top_md": { + "declaration": "box-shadow: var(--shadow_inset_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" }, - "row_gap_xs2": { - "declaration": "row-gap: var(--space_xs2);" + "shadow_inset_top_lg": { + "declaration": "box-shadow: var(--shadow_inset_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" }, - "row_gap_xs": { - "declaration": "row-gap: var(--space_xs);" + "shadow_inset_top_xl": { + "declaration": "box-shadow: var(--shadow_inset_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" }, - "row_gap_sm": { - "declaration": "row-gap: var(--space_sm);" + "shadow_inset_bottom_xs": { + "declaration": "box-shadow: var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" }, - "row_gap_md": { - "declaration": "row-gap: var(--space_md);" + "shadow_inset_bottom_sm": { + "declaration": "box-shadow: var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" }, - "row_gap_lg": { - "declaration": "row-gap: var(--space_lg);" + "shadow_inset_bottom_md": { + "declaration": "box-shadow: var(--shadow_inset_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" }, - "row_gap_xl": { - "declaration": "row-gap: var(--space_xl);" + "shadow_inset_bottom_lg": { + "declaration": "box-shadow: var(--shadow_inset_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" }, - "row_gap_xl2": { - "declaration": "row-gap: var(--space_xl2);" + "shadow_inset_bottom_xl": { + "declaration": "box-shadow: var(--shadow_inset_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" }, - "row_gap_xl3": { - "declaration": "row-gap: var(--space_xl3);" + "shadow_color_umbra": { "declaration": "--shadow_color: var(--shadow_color_umbra);" }, + "shadow_color_highlight": { "declaration": "--shadow_color: var(--shadow_color_highlight);" }, + "shadow_color_glow": { "declaration": "--shadow_color: var(--shadow_color_glow);" }, + "shadow_color_shroud": { "declaration": "--shadow_color: var(--shadow_color_shroud);" }, + "shadow_alpha_00": { "declaration": "--shadow_alpha: var(--shadow_alpha_00);" }, + "shadow_alpha_05": { "declaration": "--shadow_alpha: var(--shadow_alpha_05);" }, + "shadow_alpha_10": { "declaration": "--shadow_alpha: var(--shadow_alpha_10);" }, + "shadow_alpha_20": { "declaration": "--shadow_alpha: var(--shadow_alpha_20);" }, + "shadow_alpha_30": { "declaration": "--shadow_alpha: var(--shadow_alpha_30);" }, + "shadow_alpha_40": { "declaration": "--shadow_alpha: var(--shadow_alpha_40);" }, + "shadow_alpha_50": { "declaration": "--shadow_alpha: var(--shadow_alpha_50);" }, + "shadow_alpha_60": { "declaration": "--shadow_alpha: var(--shadow_alpha_60);" }, + "shadow_alpha_70": { "declaration": "--shadow_alpha: var(--shadow_alpha_70);" }, + "shadow_alpha_80": { "declaration": "--shadow_alpha: var(--shadow_alpha_80);" }, + "shadow_alpha_90": { "declaration": "--shadow_alpha: var(--shadow_alpha_90);" }, + "shadow_alpha_95": { "declaration": "--shadow_alpha: var(--shadow_alpha_95);" }, + "shadow_alpha_100": { "declaration": "--shadow_alpha: var(--shadow_alpha_100);" }, + "shadow_a_00": { "declaration": "--shadow_color: var(--palette_a_00);" }, + "shadow_a_05": { "declaration": "--shadow_color: var(--palette_a_05);" }, + "shadow_a_10": { "declaration": "--shadow_color: var(--palette_a_10);" }, + "shadow_a_20": { "declaration": "--shadow_color: var(--palette_a_20);" }, + "shadow_a_30": { "declaration": "--shadow_color: var(--palette_a_30);" }, + "shadow_a_40": { "declaration": "--shadow_color: var(--palette_a_40);" }, + "shadow_a_50": { "declaration": "--shadow_color: var(--palette_a_50);" }, + "shadow_a_60": { "declaration": "--shadow_color: var(--palette_a_60);" }, + "shadow_a_70": { "declaration": "--shadow_color: var(--palette_a_70);" }, + "shadow_a_80": { "declaration": "--shadow_color: var(--palette_a_80);" }, + "shadow_a_90": { "declaration": "--shadow_color: var(--palette_a_90);" }, + "shadow_a_95": { "declaration": "--shadow_color: var(--palette_a_95);" }, + "shadow_a_100": { "declaration": "--shadow_color: var(--palette_a_100);" }, + "shadow_b_00": { "declaration": "--shadow_color: var(--palette_b_00);" }, + "shadow_b_05": { "declaration": "--shadow_color: var(--palette_b_05);" }, + "shadow_b_10": { "declaration": "--shadow_color: var(--palette_b_10);" }, + "shadow_b_20": { "declaration": "--shadow_color: var(--palette_b_20);" }, + "shadow_b_30": { "declaration": "--shadow_color: var(--palette_b_30);" }, + "shadow_b_40": { "declaration": "--shadow_color: var(--palette_b_40);" }, + "shadow_b_50": { "declaration": "--shadow_color: var(--palette_b_50);" }, + "shadow_b_60": { "declaration": "--shadow_color: var(--palette_b_60);" }, + "shadow_b_70": { "declaration": "--shadow_color: var(--palette_b_70);" }, + "shadow_b_80": { "declaration": "--shadow_color: var(--palette_b_80);" }, + "shadow_b_90": { "declaration": "--shadow_color: var(--palette_b_90);" }, + "shadow_b_95": { "declaration": "--shadow_color: var(--palette_b_95);" }, + "shadow_b_100": { "declaration": "--shadow_color: var(--palette_b_100);" }, + "shadow_c_00": { "declaration": "--shadow_color: var(--palette_c_00);" }, + "shadow_c_05": { "declaration": "--shadow_color: var(--palette_c_05);" }, + "shadow_c_10": { "declaration": "--shadow_color: var(--palette_c_10);" }, + "shadow_c_20": { "declaration": "--shadow_color: var(--palette_c_20);" }, + "shadow_c_30": { "declaration": "--shadow_color: var(--palette_c_30);" }, + "shadow_c_40": { "declaration": "--shadow_color: var(--palette_c_40);" }, + "shadow_c_50": { "declaration": "--shadow_color: var(--palette_c_50);" }, + "shadow_c_60": { "declaration": "--shadow_color: var(--palette_c_60);" }, + "shadow_c_70": { "declaration": "--shadow_color: var(--palette_c_70);" }, + "shadow_c_80": { "declaration": "--shadow_color: var(--palette_c_80);" }, + "shadow_c_90": { "declaration": "--shadow_color: var(--palette_c_90);" }, + "shadow_c_95": { "declaration": "--shadow_color: var(--palette_c_95);" }, + "shadow_c_100": { "declaration": "--shadow_color: var(--palette_c_100);" }, + "shadow_d_00": { "declaration": "--shadow_color: var(--palette_d_00);" }, + "shadow_d_05": { "declaration": "--shadow_color: var(--palette_d_05);" }, + "shadow_d_10": { "declaration": "--shadow_color: var(--palette_d_10);" }, + "shadow_d_20": { "declaration": "--shadow_color: var(--palette_d_20);" }, + "shadow_d_30": { "declaration": "--shadow_color: var(--palette_d_30);" }, + "shadow_d_40": { "declaration": "--shadow_color: var(--palette_d_40);" }, + "shadow_d_50": { "declaration": "--shadow_color: var(--palette_d_50);" }, + "shadow_d_60": { "declaration": "--shadow_color: var(--palette_d_60);" }, + "shadow_d_70": { "declaration": "--shadow_color: var(--palette_d_70);" }, + "shadow_d_80": { "declaration": "--shadow_color: var(--palette_d_80);" }, + "shadow_d_90": { "declaration": "--shadow_color: var(--palette_d_90);" }, + "shadow_d_95": { "declaration": "--shadow_color: var(--palette_d_95);" }, + "shadow_d_100": { "declaration": "--shadow_color: var(--palette_d_100);" }, + "shadow_e_00": { "declaration": "--shadow_color: var(--palette_e_00);" }, + "shadow_e_05": { "declaration": "--shadow_color: var(--palette_e_05);" }, + "shadow_e_10": { "declaration": "--shadow_color: var(--palette_e_10);" }, + "shadow_e_20": { "declaration": "--shadow_color: var(--palette_e_20);" }, + "shadow_e_30": { "declaration": "--shadow_color: var(--palette_e_30);" }, + "shadow_e_40": { "declaration": "--shadow_color: var(--palette_e_40);" }, + "shadow_e_50": { "declaration": "--shadow_color: var(--palette_e_50);" }, + "shadow_e_60": { "declaration": "--shadow_color: var(--palette_e_60);" }, + "shadow_e_70": { "declaration": "--shadow_color: var(--palette_e_70);" }, + "shadow_e_80": { "declaration": "--shadow_color: var(--palette_e_80);" }, + "shadow_e_90": { "declaration": "--shadow_color: var(--palette_e_90);" }, + "shadow_e_95": { "declaration": "--shadow_color: var(--palette_e_95);" }, + "shadow_e_100": { "declaration": "--shadow_color: var(--palette_e_100);" }, + "shadow_f_00": { "declaration": "--shadow_color: var(--palette_f_00);" }, + "shadow_f_05": { "declaration": "--shadow_color: var(--palette_f_05);" }, + "shadow_f_10": { "declaration": "--shadow_color: var(--palette_f_10);" }, + "shadow_f_20": { "declaration": "--shadow_color: var(--palette_f_20);" }, + "shadow_f_30": { "declaration": "--shadow_color: var(--palette_f_30);" }, + "shadow_f_40": { "declaration": "--shadow_color: var(--palette_f_40);" }, + "shadow_f_50": { "declaration": "--shadow_color: var(--palette_f_50);" }, + "shadow_f_60": { "declaration": "--shadow_color: var(--palette_f_60);" }, + "shadow_f_70": { "declaration": "--shadow_color: var(--palette_f_70);" }, + "shadow_f_80": { "declaration": "--shadow_color: var(--palette_f_80);" }, + "shadow_f_90": { "declaration": "--shadow_color: var(--palette_f_90);" }, + "shadow_f_95": { "declaration": "--shadow_color: var(--palette_f_95);" }, + "shadow_f_100": { "declaration": "--shadow_color: var(--palette_f_100);" }, + "shadow_g_00": { "declaration": "--shadow_color: var(--palette_g_00);" }, + "shadow_g_05": { "declaration": "--shadow_color: var(--palette_g_05);" }, + "shadow_g_10": { "declaration": "--shadow_color: var(--palette_g_10);" }, + "shadow_g_20": { "declaration": "--shadow_color: var(--palette_g_20);" }, + "shadow_g_30": { "declaration": "--shadow_color: var(--palette_g_30);" }, + "shadow_g_40": { "declaration": "--shadow_color: var(--palette_g_40);" }, + "shadow_g_50": { "declaration": "--shadow_color: var(--palette_g_50);" }, + "shadow_g_60": { "declaration": "--shadow_color: var(--palette_g_60);" }, + "shadow_g_70": { "declaration": "--shadow_color: var(--palette_g_70);" }, + "shadow_g_80": { "declaration": "--shadow_color: var(--palette_g_80);" }, + "shadow_g_90": { "declaration": "--shadow_color: var(--palette_g_90);" }, + "shadow_g_95": { "declaration": "--shadow_color: var(--palette_g_95);" }, + "shadow_g_100": { "declaration": "--shadow_color: var(--palette_g_100);" }, + "shadow_h_00": { "declaration": "--shadow_color: var(--palette_h_00);" }, + "shadow_h_05": { "declaration": "--shadow_color: var(--palette_h_05);" }, + "shadow_h_10": { "declaration": "--shadow_color: var(--palette_h_10);" }, + "shadow_h_20": { "declaration": "--shadow_color: var(--palette_h_20);" }, + "shadow_h_30": { "declaration": "--shadow_color: var(--palette_h_30);" }, + "shadow_h_40": { "declaration": "--shadow_color: var(--palette_h_40);" }, + "shadow_h_50": { "declaration": "--shadow_color: var(--palette_h_50);" }, + "shadow_h_60": { "declaration": "--shadow_color: var(--palette_h_60);" }, + "shadow_h_70": { "declaration": "--shadow_color: var(--palette_h_70);" }, + "shadow_h_80": { "declaration": "--shadow_color: var(--palette_h_80);" }, + "shadow_h_90": { "declaration": "--shadow_color: var(--palette_h_90);" }, + "shadow_h_95": { "declaration": "--shadow_color: var(--palette_h_95);" }, + "shadow_h_100": { "declaration": "--shadow_color: var(--palette_h_100);" }, + "shadow_i_00": { "declaration": "--shadow_color: var(--palette_i_00);" }, + "shadow_i_05": { "declaration": "--shadow_color: var(--palette_i_05);" }, + "shadow_i_10": { "declaration": "--shadow_color: var(--palette_i_10);" }, + "shadow_i_20": { "declaration": "--shadow_color: var(--palette_i_20);" }, + "shadow_i_30": { "declaration": "--shadow_color: var(--palette_i_30);" }, + "shadow_i_40": { "declaration": "--shadow_color: var(--palette_i_40);" }, + "shadow_i_50": { "declaration": "--shadow_color: var(--palette_i_50);" }, + "shadow_i_60": { "declaration": "--shadow_color: var(--palette_i_60);" }, + "shadow_i_70": { "declaration": "--shadow_color: var(--palette_i_70);" }, + "shadow_i_80": { "declaration": "--shadow_color: var(--palette_i_80);" }, + "shadow_i_90": { "declaration": "--shadow_color: var(--palette_i_90);" }, + "shadow_i_95": { "declaration": "--shadow_color: var(--palette_i_95);" }, + "shadow_i_100": { "declaration": "--shadow_color: var(--palette_i_100);" }, + "shadow_j_00": { "declaration": "--shadow_color: var(--palette_j_00);" }, + "shadow_j_05": { "declaration": "--shadow_color: var(--palette_j_05);" }, + "shadow_j_10": { "declaration": "--shadow_color: var(--palette_j_10);" }, + "shadow_j_20": { "declaration": "--shadow_color: var(--palette_j_20);" }, + "shadow_j_30": { "declaration": "--shadow_color: var(--palette_j_30);" }, + "shadow_j_40": { "declaration": "--shadow_color: var(--palette_j_40);" }, + "shadow_j_50": { "declaration": "--shadow_color: var(--palette_j_50);" }, + "shadow_j_60": { "declaration": "--shadow_color: var(--palette_j_60);" }, + "shadow_j_70": { "declaration": "--shadow_color: var(--palette_j_70);" }, + "shadow_j_80": { "declaration": "--shadow_color: var(--palette_j_80);" }, + "shadow_j_90": { "declaration": "--shadow_color: var(--palette_j_90);" }, + "shadow_j_95": { "declaration": "--shadow_color: var(--palette_j_95);" }, + "shadow_j_100": { "declaration": "--shadow_color: var(--palette_j_100);" }, + "width_xs5": { "declaration": "width: var(--space_xs5);" }, + "width_xs4": { "declaration": "width: var(--space_xs4);" }, + "width_xs3": { "declaration": "width: var(--space_xs3);" }, + "width_xs2": { "declaration": "width: var(--space_xs2);" }, + "width_xs": { "declaration": "width: var(--space_xs);" }, + "width_sm": { "declaration": "width: var(--space_sm);" }, + "width_md": { "declaration": "width: var(--space_md);" }, + "width_lg": { "declaration": "width: var(--space_lg);" }, + "width_xl": { "declaration": "width: var(--space_xl);" }, + "width_xl2": { "declaration": "width: var(--space_xl2);" }, + "width_xl3": { "declaration": "width: var(--space_xl3);" }, + "width_xl4": { "declaration": "width: var(--space_xl4);" }, + "width_xl5": { "declaration": "width: var(--space_xl5);" }, + "width_xl6": { "declaration": "width: var(--space_xl6);" }, + "width_xl7": { "declaration": "width: var(--space_xl7);" }, + "width_xl8": { "declaration": "width: var(--space_xl8);" }, + "width_xl9": { "declaration": "width: var(--space_xl9);" }, + "width_xl10": { "declaration": "width: var(--space_xl10);" }, + "width_xl11": { "declaration": "width: var(--space_xl11);" }, + "width_xl12": { "declaration": "width: var(--space_xl12);" }, + "width_xl13": { "declaration": "width: var(--space_xl13);" }, + "width_xl14": { "declaration": "width: var(--space_xl14);" }, + "width_xl15": { "declaration": "width: var(--space_xl15);" }, + "height_xs5": { "declaration": "height: var(--space_xs5);" }, + "height_xs4": { "declaration": "height: var(--space_xs4);" }, + "height_xs3": { "declaration": "height: var(--space_xs3);" }, + "height_xs2": { "declaration": "height: var(--space_xs2);" }, + "height_xs": { "declaration": "height: var(--space_xs);" }, + "height_sm": { "declaration": "height: var(--space_sm);" }, + "height_md": { "declaration": "height: var(--space_md);" }, + "height_lg": { "declaration": "height: var(--space_lg);" }, + "height_xl": { "declaration": "height: var(--space_xl);" }, + "height_xl2": { "declaration": "height: var(--space_xl2);" }, + "height_xl3": { "declaration": "height: var(--space_xl3);" }, + "height_xl4": { "declaration": "height: var(--space_xl4);" }, + "height_xl5": { "declaration": "height: var(--space_xl5);" }, + "height_xl6": { "declaration": "height: var(--space_xl6);" }, + "height_xl7": { "declaration": "height: var(--space_xl7);" }, + "height_xl8": { "declaration": "height: var(--space_xl8);" }, + "height_xl9": { "declaration": "height: var(--space_xl9);" }, + "height_xl10": { "declaration": "height: var(--space_xl10);" }, + "height_xl11": { "declaration": "height: var(--space_xl11);" }, + "height_xl12": { "declaration": "height: var(--space_xl12);" }, + "height_xl13": { "declaration": "height: var(--space_xl13);" }, + "height_xl14": { "declaration": "height: var(--space_xl14);" }, + "height_xl15": { "declaration": "height: var(--space_xl15);" }, + "width_atmost_xs": { "declaration": "width: 100%; max-width: var(--distance_xs);" }, + "width_atmost_sm": { "declaration": "width: 100%; max-width: var(--distance_sm);" }, + "width_atmost_md": { "declaration": "width: 100%; max-width: var(--distance_md);" }, + "width_atmost_lg": { "declaration": "width: 100%; max-width: var(--distance_lg);" }, + "width_atmost_xl": { "declaration": "width: 100%; max-width: var(--distance_xl);" }, + "width_atleast_xs": { "declaration": "width: 100%; min-width: var(--distance_xs);" }, + "width_atleast_sm": { "declaration": "width: 100%; min-width: var(--distance_sm);" }, + "width_atleast_md": { "declaration": "width: 100%; min-width: var(--distance_md);" }, + "width_atleast_lg": { "declaration": "width: 100%; min-width: var(--distance_lg);" }, + "width_atleast_xl": { "declaration": "width: 100%; min-width: var(--distance_xl);" }, + "height_atmost_xs": { "declaration": "height: 100%; max-height: var(--distance_xs);" }, + "height_atmost_sm": { "declaration": "height: 100%; max-height: var(--distance_sm);" }, + "height_atmost_md": { "declaration": "height: 100%; max-height: var(--distance_md);" }, + "height_atmost_lg": { "declaration": "height: 100%; max-height: var(--distance_lg);" }, + "height_atmost_xl": { "declaration": "height: 100%; max-height: var(--distance_xl);" }, + "height_atleast_xs": { "declaration": "height: 100%; min-height: var(--distance_xs);" }, + "height_atleast_sm": { "declaration": "height: 100%; min-height: var(--distance_sm);" }, + "height_atleast_md": { "declaration": "height: 100%; min-height: var(--distance_md);" }, + "height_atleast_lg": { "declaration": "height: 100%; min-height: var(--distance_lg);" }, + "height_atleast_xl": { "declaration": "height: 100%; min-height: var(--distance_xl);" }, + "top_xs5": { "declaration": "top: var(--space_xs5);" }, + "top_xs4": { "declaration": "top: var(--space_xs4);" }, + "top_xs3": { "declaration": "top: var(--space_xs3);" }, + "top_xs2": { "declaration": "top: var(--space_xs2);" }, + "top_xs": { "declaration": "top: var(--space_xs);" }, + "top_sm": { "declaration": "top: var(--space_sm);" }, + "top_md": { "declaration": "top: var(--space_md);" }, + "top_lg": { "declaration": "top: var(--space_lg);" }, + "top_xl": { "declaration": "top: var(--space_xl);" }, + "top_xl2": { "declaration": "top: var(--space_xl2);" }, + "top_xl3": { "declaration": "top: var(--space_xl3);" }, + "top_xl4": { "declaration": "top: var(--space_xl4);" }, + "top_xl5": { "declaration": "top: var(--space_xl5);" }, + "top_xl6": { "declaration": "top: var(--space_xl6);" }, + "top_xl7": { "declaration": "top: var(--space_xl7);" }, + "top_xl8": { "declaration": "top: var(--space_xl8);" }, + "top_xl9": { "declaration": "top: var(--space_xl9);" }, + "top_xl10": { "declaration": "top: var(--space_xl10);" }, + "top_xl11": { "declaration": "top: var(--space_xl11);" }, + "top_xl12": { "declaration": "top: var(--space_xl12);" }, + "top_xl13": { "declaration": "top: var(--space_xl13);" }, + "top_xl14": { "declaration": "top: var(--space_xl14);" }, + "top_xl15": { "declaration": "top: var(--space_xl15);" }, + "right_xs5": { "declaration": "right: var(--space_xs5);" }, + "right_xs4": { "declaration": "right: var(--space_xs4);" }, + "right_xs3": { "declaration": "right: var(--space_xs3);" }, + "right_xs2": { "declaration": "right: var(--space_xs2);" }, + "right_xs": { "declaration": "right: var(--space_xs);" }, + "right_sm": { "declaration": "right: var(--space_sm);" }, + "right_md": { "declaration": "right: var(--space_md);" }, + "right_lg": { "declaration": "right: var(--space_lg);" }, + "right_xl": { "declaration": "right: var(--space_xl);" }, + "right_xl2": { "declaration": "right: var(--space_xl2);" }, + "right_xl3": { "declaration": "right: var(--space_xl3);" }, + "right_xl4": { "declaration": "right: var(--space_xl4);" }, + "right_xl5": { "declaration": "right: var(--space_xl5);" }, + "right_xl6": { "declaration": "right: var(--space_xl6);" }, + "right_xl7": { "declaration": "right: var(--space_xl7);" }, + "right_xl8": { "declaration": "right: var(--space_xl8);" }, + "right_xl9": { "declaration": "right: var(--space_xl9);" }, + "right_xl10": { "declaration": "right: var(--space_xl10);" }, + "right_xl11": { "declaration": "right: var(--space_xl11);" }, + "right_xl12": { "declaration": "right: var(--space_xl12);" }, + "right_xl13": { "declaration": "right: var(--space_xl13);" }, + "right_xl14": { "declaration": "right: var(--space_xl14);" }, + "right_xl15": { "declaration": "right: var(--space_xl15);" }, + "bottom_xs5": { "declaration": "bottom: var(--space_xs5);" }, + "bottom_xs4": { "declaration": "bottom: var(--space_xs4);" }, + "bottom_xs3": { "declaration": "bottom: var(--space_xs3);" }, + "bottom_xs2": { "declaration": "bottom: var(--space_xs2);" }, + "bottom_xs": { "declaration": "bottom: var(--space_xs);" }, + "bottom_sm": { "declaration": "bottom: var(--space_sm);" }, + "bottom_md": { "declaration": "bottom: var(--space_md);" }, + "bottom_lg": { "declaration": "bottom: var(--space_lg);" }, + "bottom_xl": { "declaration": "bottom: var(--space_xl);" }, + "bottom_xl2": { "declaration": "bottom: var(--space_xl2);" }, + "bottom_xl3": { "declaration": "bottom: var(--space_xl3);" }, + "bottom_xl4": { "declaration": "bottom: var(--space_xl4);" }, + "bottom_xl5": { "declaration": "bottom: var(--space_xl5);" }, + "bottom_xl6": { "declaration": "bottom: var(--space_xl6);" }, + "bottom_xl7": { "declaration": "bottom: var(--space_xl7);" }, + "bottom_xl8": { "declaration": "bottom: var(--space_xl8);" }, + "bottom_xl9": { "declaration": "bottom: var(--space_xl9);" }, + "bottom_xl10": { "declaration": "bottom: var(--space_xl10);" }, + "bottom_xl11": { "declaration": "bottom: var(--space_xl11);" }, + "bottom_xl12": { "declaration": "bottom: var(--space_xl12);" }, + "bottom_xl13": { "declaration": "bottom: var(--space_xl13);" }, + "bottom_xl14": { "declaration": "bottom: var(--space_xl14);" }, + "bottom_xl15": { "declaration": "bottom: var(--space_xl15);" }, + "left_xs5": { "declaration": "left: var(--space_xs5);" }, + "left_xs4": { "declaration": "left: var(--space_xs4);" }, + "left_xs3": { "declaration": "left: var(--space_xs3);" }, + "left_xs2": { "declaration": "left: var(--space_xs2);" }, + "left_xs": { "declaration": "left: var(--space_xs);" }, + "left_sm": { "declaration": "left: var(--space_sm);" }, + "left_md": { "declaration": "left: var(--space_md);" }, + "left_lg": { "declaration": "left: var(--space_lg);" }, + "left_xl": { "declaration": "left: var(--space_xl);" }, + "left_xl2": { "declaration": "left: var(--space_xl2);" }, + "left_xl3": { "declaration": "left: var(--space_xl3);" }, + "left_xl4": { "declaration": "left: var(--space_xl4);" }, + "left_xl5": { "declaration": "left: var(--space_xl5);" }, + "left_xl6": { "declaration": "left: var(--space_xl6);" }, + "left_xl7": { "declaration": "left: var(--space_xl7);" }, + "left_xl8": { "declaration": "left: var(--space_xl8);" }, + "left_xl9": { "declaration": "left: var(--space_xl9);" }, + "left_xl10": { "declaration": "left: var(--space_xl10);" }, + "left_xl11": { "declaration": "left: var(--space_xl11);" }, + "left_xl12": { "declaration": "left: var(--space_xl12);" }, + "left_xl13": { "declaration": "left: var(--space_xl13);" }, + "left_xl14": { "declaration": "left: var(--space_xl14);" }, + "left_xl15": { "declaration": "left: var(--space_xl15);" }, + "inset_xs5": { "declaration": "inset: var(--space_xs5);" }, + "inset_xs4": { "declaration": "inset: var(--space_xs4);" }, + "inset_xs3": { "declaration": "inset: var(--space_xs3);" }, + "inset_xs2": { "declaration": "inset: var(--space_xs2);" }, + "inset_xs": { "declaration": "inset: var(--space_xs);" }, + "inset_sm": { "declaration": "inset: var(--space_sm);" }, + "inset_md": { "declaration": "inset: var(--space_md);" }, + "inset_lg": { "declaration": "inset: var(--space_lg);" }, + "inset_xl": { "declaration": "inset: var(--space_xl);" }, + "inset_xl2": { "declaration": "inset: var(--space_xl2);" }, + "inset_xl3": { "declaration": "inset: var(--space_xl3);" }, + "inset_xl4": { "declaration": "inset: var(--space_xl4);" }, + "inset_xl5": { "declaration": "inset: var(--space_xl5);" }, + "inset_xl6": { "declaration": "inset: var(--space_xl6);" }, + "inset_xl7": { "declaration": "inset: var(--space_xl7);" }, + "inset_xl8": { "declaration": "inset: var(--space_xl8);" }, + "inset_xl9": { "declaration": "inset: var(--space_xl9);" }, + "inset_xl10": { "declaration": "inset: var(--space_xl10);" }, + "inset_xl11": { "declaration": "inset: var(--space_xl11);" }, + "inset_xl12": { "declaration": "inset: var(--space_xl12);" }, + "inset_xl13": { "declaration": "inset: var(--space_xl13);" }, + "inset_xl14": { "declaration": "inset: var(--space_xl14);" }, + "inset_xl15": { "declaration": "inset: var(--space_xl15);" }, + "p_0": { "declaration": "padding: 0;" }, + "p_xs5": { "declaration": "padding: var(--space_xs5);" }, + "p_xs4": { "declaration": "padding: var(--space_xs4);" }, + "p_xs3": { "declaration": "padding: var(--space_xs3);" }, + "p_xs2": { "declaration": "padding: var(--space_xs2);" }, + "p_xs": { "declaration": "padding: var(--space_xs);" }, + "p_sm": { "declaration": "padding: var(--space_sm);" }, + "p_md": { "declaration": "padding: var(--space_md);" }, + "p_lg": { "declaration": "padding: var(--space_lg);" }, + "p_xl": { "declaration": "padding: var(--space_xl);" }, + "p_xl2": { "declaration": "padding: var(--space_xl2);" }, + "p_xl3": { "declaration": "padding: var(--space_xl3);" }, + "p_xl4": { "declaration": "padding: var(--space_xl4);" }, + "p_xl5": { "declaration": "padding: var(--space_xl5);" }, + "p_xl6": { "declaration": "padding: var(--space_xl6);" }, + "p_xl7": { "declaration": "padding: var(--space_xl7);" }, + "p_xl8": { "declaration": "padding: var(--space_xl8);" }, + "p_xl9": { "declaration": "padding: var(--space_xl9);" }, + "p_xl10": { "declaration": "padding: var(--space_xl10);" }, + "p_xl11": { "declaration": "padding: var(--space_xl11);" }, + "p_xl12": { "declaration": "padding: var(--space_xl12);" }, + "p_xl13": { "declaration": "padding: var(--space_xl13);" }, + "p_xl14": { "declaration": "padding: var(--space_xl14);" }, + "p_xl15": { "declaration": "padding: var(--space_xl15);" }, + "pt_0": { "declaration": "padding-top: 0;" }, + "pt_xs5": { "declaration": "padding-top: var(--space_xs5);" }, + "pt_xs4": { "declaration": "padding-top: var(--space_xs4);" }, + "pt_xs3": { "declaration": "padding-top: var(--space_xs3);" }, + "pt_xs2": { "declaration": "padding-top: var(--space_xs2);" }, + "pt_xs": { "declaration": "padding-top: var(--space_xs);" }, + "pt_sm": { "declaration": "padding-top: var(--space_sm);" }, + "pt_md": { "declaration": "padding-top: var(--space_md);" }, + "pt_lg": { "declaration": "padding-top: var(--space_lg);" }, + "pt_xl": { "declaration": "padding-top: var(--space_xl);" }, + "pt_xl2": { "declaration": "padding-top: var(--space_xl2);" }, + "pt_xl3": { "declaration": "padding-top: var(--space_xl3);" }, + "pt_xl4": { "declaration": "padding-top: var(--space_xl4);" }, + "pt_xl5": { "declaration": "padding-top: var(--space_xl5);" }, + "pt_xl6": { "declaration": "padding-top: var(--space_xl6);" }, + "pt_xl7": { "declaration": "padding-top: var(--space_xl7);" }, + "pt_xl8": { "declaration": "padding-top: var(--space_xl8);" }, + "pt_xl9": { "declaration": "padding-top: var(--space_xl9);" }, + "pt_xl10": { "declaration": "padding-top: var(--space_xl10);" }, + "pt_xl11": { "declaration": "padding-top: var(--space_xl11);" }, + "pt_xl12": { "declaration": "padding-top: var(--space_xl12);" }, + "pt_xl13": { "declaration": "padding-top: var(--space_xl13);" }, + "pt_xl14": { "declaration": "padding-top: var(--space_xl14);" }, + "pt_xl15": { "declaration": "padding-top: var(--space_xl15);" }, + "pr_0": { "declaration": "padding-right: 0;" }, + "pr_xs5": { "declaration": "padding-right: var(--space_xs5);" }, + "pr_xs4": { "declaration": "padding-right: var(--space_xs4);" }, + "pr_xs3": { "declaration": "padding-right: var(--space_xs3);" }, + "pr_xs2": { "declaration": "padding-right: var(--space_xs2);" }, + "pr_xs": { "declaration": "padding-right: var(--space_xs);" }, + "pr_sm": { "declaration": "padding-right: var(--space_sm);" }, + "pr_md": { "declaration": "padding-right: var(--space_md);" }, + "pr_lg": { "declaration": "padding-right: var(--space_lg);" }, + "pr_xl": { "declaration": "padding-right: var(--space_xl);" }, + "pr_xl2": { "declaration": "padding-right: var(--space_xl2);" }, + "pr_xl3": { "declaration": "padding-right: var(--space_xl3);" }, + "pr_xl4": { "declaration": "padding-right: var(--space_xl4);" }, + "pr_xl5": { "declaration": "padding-right: var(--space_xl5);" }, + "pr_xl6": { "declaration": "padding-right: var(--space_xl6);" }, + "pr_xl7": { "declaration": "padding-right: var(--space_xl7);" }, + "pr_xl8": { "declaration": "padding-right: var(--space_xl8);" }, + "pr_xl9": { "declaration": "padding-right: var(--space_xl9);" }, + "pr_xl10": { "declaration": "padding-right: var(--space_xl10);" }, + "pr_xl11": { "declaration": "padding-right: var(--space_xl11);" }, + "pr_xl12": { "declaration": "padding-right: var(--space_xl12);" }, + "pr_xl13": { "declaration": "padding-right: var(--space_xl13);" }, + "pr_xl14": { "declaration": "padding-right: var(--space_xl14);" }, + "pr_xl15": { "declaration": "padding-right: var(--space_xl15);" }, + "pb_0": { "declaration": "padding-bottom: 0;" }, + "pb_xs5": { "declaration": "padding-bottom: var(--space_xs5);" }, + "pb_xs4": { "declaration": "padding-bottom: var(--space_xs4);" }, + "pb_xs3": { "declaration": "padding-bottom: var(--space_xs3);" }, + "pb_xs2": { "declaration": "padding-bottom: var(--space_xs2);" }, + "pb_xs": { "declaration": "padding-bottom: var(--space_xs);" }, + "pb_sm": { "declaration": "padding-bottom: var(--space_sm);" }, + "pb_md": { "declaration": "padding-bottom: var(--space_md);" }, + "pb_lg": { "declaration": "padding-bottom: var(--space_lg);" }, + "pb_xl": { "declaration": "padding-bottom: var(--space_xl);" }, + "pb_xl2": { "declaration": "padding-bottom: var(--space_xl2);" }, + "pb_xl3": { "declaration": "padding-bottom: var(--space_xl3);" }, + "pb_xl4": { "declaration": "padding-bottom: var(--space_xl4);" }, + "pb_xl5": { "declaration": "padding-bottom: var(--space_xl5);" }, + "pb_xl6": { "declaration": "padding-bottom: var(--space_xl6);" }, + "pb_xl7": { "declaration": "padding-bottom: var(--space_xl7);" }, + "pb_xl8": { "declaration": "padding-bottom: var(--space_xl8);" }, + "pb_xl9": { "declaration": "padding-bottom: var(--space_xl9);" }, + "pb_xl10": { "declaration": "padding-bottom: var(--space_xl10);" }, + "pb_xl11": { "declaration": "padding-bottom: var(--space_xl11);" }, + "pb_xl12": { "declaration": "padding-bottom: var(--space_xl12);" }, + "pb_xl13": { "declaration": "padding-bottom: var(--space_xl13);" }, + "pb_xl14": { "declaration": "padding-bottom: var(--space_xl14);" }, + "pb_xl15": { "declaration": "padding-bottom: var(--space_xl15);" }, + "pl_0": { "declaration": "padding-left: 0;" }, + "pl_xs5": { "declaration": "padding-left: var(--space_xs5);" }, + "pl_xs4": { "declaration": "padding-left: var(--space_xs4);" }, + "pl_xs3": { "declaration": "padding-left: var(--space_xs3);" }, + "pl_xs2": { "declaration": "padding-left: var(--space_xs2);" }, + "pl_xs": { "declaration": "padding-left: var(--space_xs);" }, + "pl_sm": { "declaration": "padding-left: var(--space_sm);" }, + "pl_md": { "declaration": "padding-left: var(--space_md);" }, + "pl_lg": { "declaration": "padding-left: var(--space_lg);" }, + "pl_xl": { "declaration": "padding-left: var(--space_xl);" }, + "pl_xl2": { "declaration": "padding-left: var(--space_xl2);" }, + "pl_xl3": { "declaration": "padding-left: var(--space_xl3);" }, + "pl_xl4": { "declaration": "padding-left: var(--space_xl4);" }, + "pl_xl5": { "declaration": "padding-left: var(--space_xl5);" }, + "pl_xl6": { "declaration": "padding-left: var(--space_xl6);" }, + "pl_xl7": { "declaration": "padding-left: var(--space_xl7);" }, + "pl_xl8": { "declaration": "padding-left: var(--space_xl8);" }, + "pl_xl9": { "declaration": "padding-left: var(--space_xl9);" }, + "pl_xl10": { "declaration": "padding-left: var(--space_xl10);" }, + "pl_xl11": { "declaration": "padding-left: var(--space_xl11);" }, + "pl_xl12": { "declaration": "padding-left: var(--space_xl12);" }, + "pl_xl13": { "declaration": "padding-left: var(--space_xl13);" }, + "pl_xl14": { "declaration": "padding-left: var(--space_xl14);" }, + "pl_xl15": { "declaration": "padding-left: var(--space_xl15);" }, + "px_0": { "declaration": "padding-left: 0;\tpadding-right: 0;" }, + "px_xs5": { "declaration": "padding-left: var(--space_xs5);\tpadding-right: var(--space_xs5);" }, + "px_xs4": { "declaration": "padding-left: var(--space_xs4);\tpadding-right: var(--space_xs4);" }, + "px_xs3": { "declaration": "padding-left: var(--space_xs3);\tpadding-right: var(--space_xs3);" }, + "px_xs2": { "declaration": "padding-left: var(--space_xs2);\tpadding-right: var(--space_xs2);" }, + "px_xs": { "declaration": "padding-left: var(--space_xs);\tpadding-right: var(--space_xs);" }, + "px_sm": { "declaration": "padding-left: var(--space_sm);\tpadding-right: var(--space_sm);" }, + "px_md": { "declaration": "padding-left: var(--space_md);\tpadding-right: var(--space_md);" }, + "px_lg": { "declaration": "padding-left: var(--space_lg);\tpadding-right: var(--space_lg);" }, + "px_xl": { "declaration": "padding-left: var(--space_xl);\tpadding-right: var(--space_xl);" }, + "px_xl2": { "declaration": "padding-left: var(--space_xl2);\tpadding-right: var(--space_xl2);" }, + "px_xl3": { "declaration": "padding-left: var(--space_xl3);\tpadding-right: var(--space_xl3);" }, + "px_xl4": { "declaration": "padding-left: var(--space_xl4);\tpadding-right: var(--space_xl4);" }, + "px_xl5": { "declaration": "padding-left: var(--space_xl5);\tpadding-right: var(--space_xl5);" }, + "px_xl6": { "declaration": "padding-left: var(--space_xl6);\tpadding-right: var(--space_xl6);" }, + "px_xl7": { "declaration": "padding-left: var(--space_xl7);\tpadding-right: var(--space_xl7);" }, + "px_xl8": { "declaration": "padding-left: var(--space_xl8);\tpadding-right: var(--space_xl8);" }, + "px_xl9": { "declaration": "padding-left: var(--space_xl9);\tpadding-right: var(--space_xl9);" }, + "px_xl10": { + "declaration": "padding-left: var(--space_xl10);\tpadding-right: var(--space_xl10);" }, - "row_gap_xl4": { - "declaration": "row-gap: var(--space_xl4);" + "px_xl11": { + "declaration": "padding-left: var(--space_xl11);\tpadding-right: var(--space_xl11);" }, - "row_gap_xl5": { - "declaration": "row-gap: var(--space_xl5);" + "px_xl12": { + "declaration": "padding-left: var(--space_xl12);\tpadding-right: var(--space_xl12);" }, - "row_gap_xl6": { - "declaration": "row-gap: var(--space_xl6);" + "px_xl13": { + "declaration": "padding-left: var(--space_xl13);\tpadding-right: var(--space_xl13);" }, - "row_gap_xl7": { - "declaration": "row-gap: var(--space_xl7);" + "px_xl14": { + "declaration": "padding-left: var(--space_xl14);\tpadding-right: var(--space_xl14);" }, - "row_gap_xl8": { - "declaration": "row-gap: var(--space_xl8);" + "px_xl15": { + "declaration": "padding-left: var(--space_xl15);\tpadding-right: var(--space_xl15);" }, - "row_gap_xl9": { - "declaration": "row-gap: var(--space_xl9);" + "py_0": { "declaration": "padding-top: 0;\tpadding-bottom: 0;" }, + "py_xs5": { "declaration": "padding-top: var(--space_xs5);\tpadding-bottom: var(--space_xs5);" }, + "py_xs4": { "declaration": "padding-top: var(--space_xs4);\tpadding-bottom: var(--space_xs4);" }, + "py_xs3": { "declaration": "padding-top: var(--space_xs3);\tpadding-bottom: var(--space_xs3);" }, + "py_xs2": { "declaration": "padding-top: var(--space_xs2);\tpadding-bottom: var(--space_xs2);" }, + "py_xs": { "declaration": "padding-top: var(--space_xs);\tpadding-bottom: var(--space_xs);" }, + "py_sm": { "declaration": "padding-top: var(--space_sm);\tpadding-bottom: var(--space_sm);" }, + "py_md": { "declaration": "padding-top: var(--space_md);\tpadding-bottom: var(--space_md);" }, + "py_lg": { "declaration": "padding-top: var(--space_lg);\tpadding-bottom: var(--space_lg);" }, + "py_xl": { "declaration": "padding-top: var(--space_xl);\tpadding-bottom: var(--space_xl);" }, + "py_xl2": { "declaration": "padding-top: var(--space_xl2);\tpadding-bottom: var(--space_xl2);" }, + "py_xl3": { "declaration": "padding-top: var(--space_xl3);\tpadding-bottom: var(--space_xl3);" }, + "py_xl4": { "declaration": "padding-top: var(--space_xl4);\tpadding-bottom: var(--space_xl4);" }, + "py_xl5": { "declaration": "padding-top: var(--space_xl5);\tpadding-bottom: var(--space_xl5);" }, + "py_xl6": { "declaration": "padding-top: var(--space_xl6);\tpadding-bottom: var(--space_xl6);" }, + "py_xl7": { "declaration": "padding-top: var(--space_xl7);\tpadding-bottom: var(--space_xl7);" }, + "py_xl8": { "declaration": "padding-top: var(--space_xl8);\tpadding-bottom: var(--space_xl8);" }, + "py_xl9": { "declaration": "padding-top: var(--space_xl9);\tpadding-bottom: var(--space_xl9);" }, + "py_xl10": { + "declaration": "padding-top: var(--space_xl10);\tpadding-bottom: var(--space_xl10);" }, - "row_gap_xl10": { - "declaration": "row-gap: var(--space_xl10);" + "py_xl11": { + "declaration": "padding-top: var(--space_xl11);\tpadding-bottom: var(--space_xl11);" }, - "row_gap_xl11": { - "declaration": "row-gap: var(--space_xl11);" + "py_xl12": { + "declaration": "padding-top: var(--space_xl12);\tpadding-bottom: var(--space_xl12);" }, - "row_gap_xl12": { - "declaration": "row-gap: var(--space_xl12);" + "py_xl13": { + "declaration": "padding-top: var(--space_xl13);\tpadding-bottom: var(--space_xl13);" }, - "row_gap_xl13": { - "declaration": "row-gap: var(--space_xl13);" + "py_xl14": { + "declaration": "padding-top: var(--space_xl14);\tpadding-bottom: var(--space_xl14);" }, - "row_gap_xl14": { - "declaration": "row-gap: var(--space_xl14);" + "py_xl15": { + "declaration": "padding-top: var(--space_xl15);\tpadding-bottom: var(--space_xl15);" }, - "row_gap_xl15": { - "declaration": "row-gap: var(--space_xl15);" - } + "m_0": { "declaration": "margin: 0;" }, + "m_auto": { "declaration": "margin: auto;" }, + "m_xs5": { "declaration": "margin: var(--space_xs5);" }, + "m_xs4": { "declaration": "margin: var(--space_xs4);" }, + "m_xs3": { "declaration": "margin: var(--space_xs3);" }, + "m_xs2": { "declaration": "margin: var(--space_xs2);" }, + "m_xs": { "declaration": "margin: var(--space_xs);" }, + "m_sm": { "declaration": "margin: var(--space_sm);" }, + "m_md": { "declaration": "margin: var(--space_md);" }, + "m_lg": { "declaration": "margin: var(--space_lg);" }, + "m_xl": { "declaration": "margin: var(--space_xl);" }, + "m_xl2": { "declaration": "margin: var(--space_xl2);" }, + "m_xl3": { "declaration": "margin: var(--space_xl3);" }, + "m_xl4": { "declaration": "margin: var(--space_xl4);" }, + "m_xl5": { "declaration": "margin: var(--space_xl5);" }, + "m_xl6": { "declaration": "margin: var(--space_xl6);" }, + "m_xl7": { "declaration": "margin: var(--space_xl7);" }, + "m_xl8": { "declaration": "margin: var(--space_xl8);" }, + "m_xl9": { "declaration": "margin: var(--space_xl9);" }, + "m_xl10": { "declaration": "margin: var(--space_xl10);" }, + "m_xl11": { "declaration": "margin: var(--space_xl11);" }, + "m_xl12": { "declaration": "margin: var(--space_xl12);" }, + "m_xl13": { "declaration": "margin: var(--space_xl13);" }, + "m_xl14": { "declaration": "margin: var(--space_xl14);" }, + "m_xl15": { "declaration": "margin: var(--space_xl15);" }, + "mt_0": { "declaration": "margin-top: 0;" }, + "mt_auto": { "declaration": "margin-top: auto;" }, + "mt_xs5": { "declaration": "margin-top: var(--space_xs5);" }, + "mt_xs4": { "declaration": "margin-top: var(--space_xs4);" }, + "mt_xs3": { "declaration": "margin-top: var(--space_xs3);" }, + "mt_xs2": { "declaration": "margin-top: var(--space_xs2);" }, + "mt_xs": { "declaration": "margin-top: var(--space_xs);" }, + "mt_sm": { "declaration": "margin-top: var(--space_sm);" }, + "mt_md": { "declaration": "margin-top: var(--space_md);" }, + "mt_lg": { "declaration": "margin-top: var(--space_lg);" }, + "mt_xl": { "declaration": "margin-top: var(--space_xl);" }, + "mt_xl2": { "declaration": "margin-top: var(--space_xl2);" }, + "mt_xl3": { "declaration": "margin-top: var(--space_xl3);" }, + "mt_xl4": { "declaration": "margin-top: var(--space_xl4);" }, + "mt_xl5": { "declaration": "margin-top: var(--space_xl5);" }, + "mt_xl6": { "declaration": "margin-top: var(--space_xl6);" }, + "mt_xl7": { "declaration": "margin-top: var(--space_xl7);" }, + "mt_xl8": { "declaration": "margin-top: var(--space_xl8);" }, + "mt_xl9": { "declaration": "margin-top: var(--space_xl9);" }, + "mt_xl10": { "declaration": "margin-top: var(--space_xl10);" }, + "mt_xl11": { "declaration": "margin-top: var(--space_xl11);" }, + "mt_xl12": { "declaration": "margin-top: var(--space_xl12);" }, + "mt_xl13": { "declaration": "margin-top: var(--space_xl13);" }, + "mt_xl14": { "declaration": "margin-top: var(--space_xl14);" }, + "mt_xl15": { "declaration": "margin-top: var(--space_xl15);" }, + "mr_0": { "declaration": "margin-right: 0;" }, + "mr_auto": { "declaration": "margin-right: auto;" }, + "mr_xs5": { "declaration": "margin-right: var(--space_xs5);" }, + "mr_xs4": { "declaration": "margin-right: var(--space_xs4);" }, + "mr_xs3": { "declaration": "margin-right: var(--space_xs3);" }, + "mr_xs2": { "declaration": "margin-right: var(--space_xs2);" }, + "mr_xs": { "declaration": "margin-right: var(--space_xs);" }, + "mr_sm": { "declaration": "margin-right: var(--space_sm);" }, + "mr_md": { "declaration": "margin-right: var(--space_md);" }, + "mr_lg": { "declaration": "margin-right: var(--space_lg);" }, + "mr_xl": { "declaration": "margin-right: var(--space_xl);" }, + "mr_xl2": { "declaration": "margin-right: var(--space_xl2);" }, + "mr_xl3": { "declaration": "margin-right: var(--space_xl3);" }, + "mr_xl4": { "declaration": "margin-right: var(--space_xl4);" }, + "mr_xl5": { "declaration": "margin-right: var(--space_xl5);" }, + "mr_xl6": { "declaration": "margin-right: var(--space_xl6);" }, + "mr_xl7": { "declaration": "margin-right: var(--space_xl7);" }, + "mr_xl8": { "declaration": "margin-right: var(--space_xl8);" }, + "mr_xl9": { "declaration": "margin-right: var(--space_xl9);" }, + "mr_xl10": { "declaration": "margin-right: var(--space_xl10);" }, + "mr_xl11": { "declaration": "margin-right: var(--space_xl11);" }, + "mr_xl12": { "declaration": "margin-right: var(--space_xl12);" }, + "mr_xl13": { "declaration": "margin-right: var(--space_xl13);" }, + "mr_xl14": { "declaration": "margin-right: var(--space_xl14);" }, + "mr_xl15": { "declaration": "margin-right: var(--space_xl15);" }, + "mb_0": { "declaration": "margin-bottom: 0;" }, + "mb_auto": { "declaration": "margin-bottom: auto;" }, + "mb_xs5": { "declaration": "margin-bottom: var(--space_xs5);" }, + "mb_xs4": { "declaration": "margin-bottom: var(--space_xs4);" }, + "mb_xs3": { "declaration": "margin-bottom: var(--space_xs3);" }, + "mb_xs2": { "declaration": "margin-bottom: var(--space_xs2);" }, + "mb_xs": { "declaration": "margin-bottom: var(--space_xs);" }, + "mb_sm": { "declaration": "margin-bottom: var(--space_sm);" }, + "mb_md": { "declaration": "margin-bottom: var(--space_md);" }, + "mb_lg": { "declaration": "margin-bottom: var(--space_lg);" }, + "mb_xl": { "declaration": "margin-bottom: var(--space_xl);" }, + "mb_xl2": { "declaration": "margin-bottom: var(--space_xl2);" }, + "mb_xl3": { "declaration": "margin-bottom: var(--space_xl3);" }, + "mb_xl4": { "declaration": "margin-bottom: var(--space_xl4);" }, + "mb_xl5": { "declaration": "margin-bottom: var(--space_xl5);" }, + "mb_xl6": { "declaration": "margin-bottom: var(--space_xl6);" }, + "mb_xl7": { "declaration": "margin-bottom: var(--space_xl7);" }, + "mb_xl8": { "declaration": "margin-bottom: var(--space_xl8);" }, + "mb_xl9": { "declaration": "margin-bottom: var(--space_xl9);" }, + "mb_xl10": { "declaration": "margin-bottom: var(--space_xl10);" }, + "mb_xl11": { "declaration": "margin-bottom: var(--space_xl11);" }, + "mb_xl12": { "declaration": "margin-bottom: var(--space_xl12);" }, + "mb_xl13": { "declaration": "margin-bottom: var(--space_xl13);" }, + "mb_xl14": { "declaration": "margin-bottom: var(--space_xl14);" }, + "mb_xl15": { "declaration": "margin-bottom: var(--space_xl15);" }, + "ml_0": { "declaration": "margin-left: 0;" }, + "ml_auto": { "declaration": "margin-left: auto;" }, + "ml_xs5": { "declaration": "margin-left: var(--space_xs5);" }, + "ml_xs4": { "declaration": "margin-left: var(--space_xs4);" }, + "ml_xs3": { "declaration": "margin-left: var(--space_xs3);" }, + "ml_xs2": { "declaration": "margin-left: var(--space_xs2);" }, + "ml_xs": { "declaration": "margin-left: var(--space_xs);" }, + "ml_sm": { "declaration": "margin-left: var(--space_sm);" }, + "ml_md": { "declaration": "margin-left: var(--space_md);" }, + "ml_lg": { "declaration": "margin-left: var(--space_lg);" }, + "ml_xl": { "declaration": "margin-left: var(--space_xl);" }, + "ml_xl2": { "declaration": "margin-left: var(--space_xl2);" }, + "ml_xl3": { "declaration": "margin-left: var(--space_xl3);" }, + "ml_xl4": { "declaration": "margin-left: var(--space_xl4);" }, + "ml_xl5": { "declaration": "margin-left: var(--space_xl5);" }, + "ml_xl6": { "declaration": "margin-left: var(--space_xl6);" }, + "ml_xl7": { "declaration": "margin-left: var(--space_xl7);" }, + "ml_xl8": { "declaration": "margin-left: var(--space_xl8);" }, + "ml_xl9": { "declaration": "margin-left: var(--space_xl9);" }, + "ml_xl10": { "declaration": "margin-left: var(--space_xl10);" }, + "ml_xl11": { "declaration": "margin-left: var(--space_xl11);" }, + "ml_xl12": { "declaration": "margin-left: var(--space_xl12);" }, + "ml_xl13": { "declaration": "margin-left: var(--space_xl13);" }, + "ml_xl14": { "declaration": "margin-left: var(--space_xl14);" }, + "ml_xl15": { "declaration": "margin-left: var(--space_xl15);" }, + "mx_0": { "declaration": "margin-left: 0;\tmargin-right: 0;" }, + "mx_auto": { "declaration": "margin-left: auto;\tmargin-right: auto;" }, + "mx_xs5": { "declaration": "margin-left: var(--space_xs5);\tmargin-right: var(--space_xs5);" }, + "mx_xs4": { "declaration": "margin-left: var(--space_xs4);\tmargin-right: var(--space_xs4);" }, + "mx_xs3": { "declaration": "margin-left: var(--space_xs3);\tmargin-right: var(--space_xs3);" }, + "mx_xs2": { "declaration": "margin-left: var(--space_xs2);\tmargin-right: var(--space_xs2);" }, + "mx_xs": { "declaration": "margin-left: var(--space_xs);\tmargin-right: var(--space_xs);" }, + "mx_sm": { "declaration": "margin-left: var(--space_sm);\tmargin-right: var(--space_sm);" }, + "mx_md": { "declaration": "margin-left: var(--space_md);\tmargin-right: var(--space_md);" }, + "mx_lg": { "declaration": "margin-left: var(--space_lg);\tmargin-right: var(--space_lg);" }, + "mx_xl": { "declaration": "margin-left: var(--space_xl);\tmargin-right: var(--space_xl);" }, + "mx_xl2": { "declaration": "margin-left: var(--space_xl2);\tmargin-right: var(--space_xl2);" }, + "mx_xl3": { "declaration": "margin-left: var(--space_xl3);\tmargin-right: var(--space_xl3);" }, + "mx_xl4": { "declaration": "margin-left: var(--space_xl4);\tmargin-right: var(--space_xl4);" }, + "mx_xl5": { "declaration": "margin-left: var(--space_xl5);\tmargin-right: var(--space_xl5);" }, + "mx_xl6": { "declaration": "margin-left: var(--space_xl6);\tmargin-right: var(--space_xl6);" }, + "mx_xl7": { "declaration": "margin-left: var(--space_xl7);\tmargin-right: var(--space_xl7);" }, + "mx_xl8": { "declaration": "margin-left: var(--space_xl8);\tmargin-right: var(--space_xl8);" }, + "mx_xl9": { "declaration": "margin-left: var(--space_xl9);\tmargin-right: var(--space_xl9);" }, + "mx_xl10": { "declaration": "margin-left: var(--space_xl10);\tmargin-right: var(--space_xl10);" }, + "mx_xl11": { "declaration": "margin-left: var(--space_xl11);\tmargin-right: var(--space_xl11);" }, + "mx_xl12": { "declaration": "margin-left: var(--space_xl12);\tmargin-right: var(--space_xl12);" }, + "mx_xl13": { "declaration": "margin-left: var(--space_xl13);\tmargin-right: var(--space_xl13);" }, + "mx_xl14": { "declaration": "margin-left: var(--space_xl14);\tmargin-right: var(--space_xl14);" }, + "mx_xl15": { "declaration": "margin-left: var(--space_xl15);\tmargin-right: var(--space_xl15);" }, + "my_0": { "declaration": "margin-top: 0;\tmargin-bottom: 0;" }, + "my_auto": { "declaration": "margin-top: auto;\tmargin-bottom: auto;" }, + "my_xs5": { "declaration": "margin-top: var(--space_xs5);\tmargin-bottom: var(--space_xs5);" }, + "my_xs4": { "declaration": "margin-top: var(--space_xs4);\tmargin-bottom: var(--space_xs4);" }, + "my_xs3": { "declaration": "margin-top: var(--space_xs3);\tmargin-bottom: var(--space_xs3);" }, + "my_xs2": { "declaration": "margin-top: var(--space_xs2);\tmargin-bottom: var(--space_xs2);" }, + "my_xs": { "declaration": "margin-top: var(--space_xs);\tmargin-bottom: var(--space_xs);" }, + "my_sm": { "declaration": "margin-top: var(--space_sm);\tmargin-bottom: var(--space_sm);" }, + "my_md": { "declaration": "margin-top: var(--space_md);\tmargin-bottom: var(--space_md);" }, + "my_lg": { "declaration": "margin-top: var(--space_lg);\tmargin-bottom: var(--space_lg);" }, + "my_xl": { "declaration": "margin-top: var(--space_xl);\tmargin-bottom: var(--space_xl);" }, + "my_xl2": { "declaration": "margin-top: var(--space_xl2);\tmargin-bottom: var(--space_xl2);" }, + "my_xl3": { "declaration": "margin-top: var(--space_xl3);\tmargin-bottom: var(--space_xl3);" }, + "my_xl4": { "declaration": "margin-top: var(--space_xl4);\tmargin-bottom: var(--space_xl4);" }, + "my_xl5": { "declaration": "margin-top: var(--space_xl5);\tmargin-bottom: var(--space_xl5);" }, + "my_xl6": { "declaration": "margin-top: var(--space_xl6);\tmargin-bottom: var(--space_xl6);" }, + "my_xl7": { "declaration": "margin-top: var(--space_xl7);\tmargin-bottom: var(--space_xl7);" }, + "my_xl8": { "declaration": "margin-top: var(--space_xl8);\tmargin-bottom: var(--space_xl8);" }, + "my_xl9": { "declaration": "margin-top: var(--space_xl9);\tmargin-bottom: var(--space_xl9);" }, + "my_xl10": { "declaration": "margin-top: var(--space_xl10);\tmargin-bottom: var(--space_xl10);" }, + "my_xl11": { "declaration": "margin-top: var(--space_xl11);\tmargin-bottom: var(--space_xl11);" }, + "my_xl12": { "declaration": "margin-top: var(--space_xl12);\tmargin-bottom: var(--space_xl12);" }, + "my_xl13": { "declaration": "margin-top: var(--space_xl13);\tmargin-bottom: var(--space_xl13);" }, + "my_xl14": { "declaration": "margin-top: var(--space_xl14);\tmargin-bottom: var(--space_xl14);" }, + "my_xl15": { "declaration": "margin-top: var(--space_xl15);\tmargin-bottom: var(--space_xl15);" }, + "gap_xs5": { "declaration": "gap: var(--space_xs5);" }, + "gap_xs4": { "declaration": "gap: var(--space_xs4);" }, + "gap_xs3": { "declaration": "gap: var(--space_xs3);" }, + "gap_xs2": { "declaration": "gap: var(--space_xs2);" }, + "gap_xs": { "declaration": "gap: var(--space_xs);" }, + "gap_sm": { "declaration": "gap: var(--space_sm);" }, + "gap_md": { "declaration": "gap: var(--space_md);" }, + "gap_lg": { "declaration": "gap: var(--space_lg);" }, + "gap_xl": { "declaration": "gap: var(--space_xl);" }, + "gap_xl2": { "declaration": "gap: var(--space_xl2);" }, + "gap_xl3": { "declaration": "gap: var(--space_xl3);" }, + "gap_xl4": { "declaration": "gap: var(--space_xl4);" }, + "gap_xl5": { "declaration": "gap: var(--space_xl5);" }, + "gap_xl6": { "declaration": "gap: var(--space_xl6);" }, + "gap_xl7": { "declaration": "gap: var(--space_xl7);" }, + "gap_xl8": { "declaration": "gap: var(--space_xl8);" }, + "gap_xl9": { "declaration": "gap: var(--space_xl9);" }, + "gap_xl10": { "declaration": "gap: var(--space_xl10);" }, + "gap_xl11": { "declaration": "gap: var(--space_xl11);" }, + "gap_xl12": { "declaration": "gap: var(--space_xl12);" }, + "gap_xl13": { "declaration": "gap: var(--space_xl13);" }, + "gap_xl14": { "declaration": "gap: var(--space_xl14);" }, + "gap_xl15": { "declaration": "gap: var(--space_xl15);" }, + "column_gap_xs5": { "declaration": "column-gap: var(--space_xs5);" }, + "column_gap_xs4": { "declaration": "column-gap: var(--space_xs4);" }, + "column_gap_xs3": { "declaration": "column-gap: var(--space_xs3);" }, + "column_gap_xs2": { "declaration": "column-gap: var(--space_xs2);" }, + "column_gap_xs": { "declaration": "column-gap: var(--space_xs);" }, + "column_gap_sm": { "declaration": "column-gap: var(--space_sm);" }, + "column_gap_md": { "declaration": "column-gap: var(--space_md);" }, + "column_gap_lg": { "declaration": "column-gap: var(--space_lg);" }, + "column_gap_xl": { "declaration": "column-gap: var(--space_xl);" }, + "column_gap_xl2": { "declaration": "column-gap: var(--space_xl2);" }, + "column_gap_xl3": { "declaration": "column-gap: var(--space_xl3);" }, + "column_gap_xl4": { "declaration": "column-gap: var(--space_xl4);" }, + "column_gap_xl5": { "declaration": "column-gap: var(--space_xl5);" }, + "column_gap_xl6": { "declaration": "column-gap: var(--space_xl6);" }, + "column_gap_xl7": { "declaration": "column-gap: var(--space_xl7);" }, + "column_gap_xl8": { "declaration": "column-gap: var(--space_xl8);" }, + "column_gap_xl9": { "declaration": "column-gap: var(--space_xl9);" }, + "column_gap_xl10": { "declaration": "column-gap: var(--space_xl10);" }, + "column_gap_xl11": { "declaration": "column-gap: var(--space_xl11);" }, + "column_gap_xl12": { "declaration": "column-gap: var(--space_xl12);" }, + "column_gap_xl13": { "declaration": "column-gap: var(--space_xl13);" }, + "column_gap_xl14": { "declaration": "column-gap: var(--space_xl14);" }, + "column_gap_xl15": { "declaration": "column-gap: var(--space_xl15);" }, + "row_gap_xs5": { "declaration": "row-gap: var(--space_xs5);" }, + "row_gap_xs4": { "declaration": "row-gap: var(--space_xs4);" }, + "row_gap_xs3": { "declaration": "row-gap: var(--space_xs3);" }, + "row_gap_xs2": { "declaration": "row-gap: var(--space_xs2);" }, + "row_gap_xs": { "declaration": "row-gap: var(--space_xs);" }, + "row_gap_sm": { "declaration": "row-gap: var(--space_sm);" }, + "row_gap_md": { "declaration": "row-gap: var(--space_md);" }, + "row_gap_lg": { "declaration": "row-gap: var(--space_lg);" }, + "row_gap_xl": { "declaration": "row-gap: var(--space_xl);" }, + "row_gap_xl2": { "declaration": "row-gap: var(--space_xl2);" }, + "row_gap_xl3": { "declaration": "row-gap: var(--space_xl3);" }, + "row_gap_xl4": { "declaration": "row-gap: var(--space_xl4);" }, + "row_gap_xl5": { "declaration": "row-gap: var(--space_xl5);" }, + "row_gap_xl6": { "declaration": "row-gap: var(--space_xl6);" }, + "row_gap_xl7": { "declaration": "row-gap: var(--space_xl7);" }, + "row_gap_xl8": { "declaration": "row-gap: var(--space_xl8);" }, + "row_gap_xl9": { "declaration": "row-gap: var(--space_xl9);" }, + "row_gap_xl10": { "declaration": "row-gap: var(--space_xl10);" }, + "row_gap_xl11": { "declaration": "row-gap: var(--space_xl11);" }, + "row_gap_xl12": { "declaration": "row-gap: var(--space_xl12);" }, + "row_gap_xl13": { "declaration": "row-gap: var(--space_xl13);" }, + "row_gap_xl14": { "declaration": "row-gap: var(--space_xl14);" }, + "row_gap_xl15": { "declaration": "row-gap: var(--space_xl15);" } } diff --git a/src/test/generate_bundled_css.test.ts b/src/test/generate_bundled_css.test.ts index bc538e025..46e71189f 100644 --- a/src/test/generate_bundled_css.test.ts +++ b/src/test/generate_bundled_css.test.ts @@ -47,6 +47,16 @@ describe('generate_bundled_css', () => { assert.include(bundled, '/* Base Styles */'); assert.include(bundled, '/* Utility Classes */'); }); + + test('emits the layer order statement and wraps each section in its layer', () => { + const result = create_mock_result(); + const bundled = generate_bundled_css(result, '.p_md { padding: 16px; }'); + + assert.match(bundled, /^@layer fuz\.base, fuz\.theme, fuz\.utilities;/); + // theme variables and base styles both live in fuz.base; utilities above + assert.equal(bundled.match(/@layer fuz\.base \{/gu)?.length, 2); + assert.include(bundled, '@layer fuz.utilities {\n.p_md { padding: 16px; }\n}'); + }); }); describe('exclusion options', () => { diff --git a/src/test/generate_css.test.ts b/src/test/generate_css.test.ts index db2be2ede..95510f9cb 100644 --- a/src/test/generate_css.test.ts +++ b/src/test/generate_css.test.ts @@ -44,6 +44,19 @@ describe('generate_css', () => { assert.equal(result.diagnostics.length, 0); }); + test('wraps output in the fuz.utilities layer with the order statement', () => { + const result = generate_css(make_options({ all_classes: new Set(['p_lg']) })); + + assert.match(result.css, /^@layer fuz\.base, fuz\.theme, fuz\.utilities;/); + assert_css_contains(result.css, '@layer fuz.utilities {'); + }); + + test('emits nothing when no classes are detected', () => { + const result = generate_css(make_options()); + + assert.equal(result.css, ''); + }); + test('ignores resources when base and theme are disabled', () => { const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( 'button { color: red; }', From e948f478dd845e97a1117f8896fd5cbdd972d5fe Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Fri, 24 Jul 2026 10:28:54 -0400 Subject: [PATCH 19/33] wip --- CLAUDE.md | 4 +--- src/lib/ramps.ts | 4 ++-- src/lib/variable_graph.ts | 7 ++----- src/lib/variables.ts | 4 ++-- src/routes/KnobControl.svelte | 1 + src/routes/docs/elements/+page.svelte | 2 +- src/test/oklch.test.ts | 4 ++-- src/test/ramps.test.ts | 10 +++++----- src/test/theme_check.test.ts | 3 +++ 9 files changed, 19 insertions(+), 20 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f15b8b83a..0d22abfa5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -322,7 +322,7 @@ Use `GenFuzCssOptions` or `VitePluginFuzCssOptions` to customize: These are the common options — see [css_plugin_options.ts](src/lib/css_plugin_options.ts) for the full set -(class definitions and interpreters, theme specificity, acorn plugins, deps). +(class definitions and interpreters, acorn plugins, deps). ## Docs @@ -466,8 +466,6 @@ Integration: `vite_plugin_examples.test.ts` (skip with - **No animation utilities** - Animation class generation not yet supported - **Button composites incomplete** - Some button variant classes are work in progress -- **CSS Cascade Layers** - `@layer` support under consideration but not yet - implemented - **Unfinished areas flagged in the docs** - builtin themes, forms (checkboxes will likely become toggles), element/table styles, the shadows system, opaque border classes, and table cell padding that doesn't yet respond to diff --git a/src/lib/ramps.ts b/src/lib/ramps.ts index cf3627f87..12084bc0d 100644 --- a/src/lib/ramps.ts +++ b/src/lib/ramps.ts @@ -120,8 +120,8 @@ export const PALETTE_CHROMA_KNOBS: Record<ColorSchemeVariant, ChromaRampKnobs> = /** * Default OKLCH hue angle of the neutral intent. In CSS the knob chains to the - * palette (`--hue_neutral: var(--hue_f)`), porting the old `tint_hue` - * behavior; this constant mirrors that default numerically. + * palette (`--hue_neutral: var(--hue_f)`); this constant mirrors that default + * numerically. */ export const NEUTRAL_HUE = PALETTE_HUES.f; diff --git a/src/lib/variable_graph.ts b/src/lib/variable_graph.ts index 4ec086624..71f2e30df 100644 --- a/src/lib/variable_graph.ts +++ b/src/lib/variable_graph.ts @@ -184,18 +184,15 @@ export const generate_theme_css = ( } } - const scope = ':root'; - const dark_scope = `${scope}.dark`; - let light_css = ''; let dark_css = ''; if (light_declarations.length > 0) { - light_css = `${scope} {\n${light_declarations.join('\n')}\n}`; + light_css = `:root {\n${light_declarations.join('\n')}\n}`; } if (dark_declarations.length > 0) { - dark_css = `${dark_scope} {\n${dark_declarations.join('\n')}\n}`; + dark_css = `:root.dark {\n${dark_declarations.join('\n')}\n}`; } return { light_css, dark_css }; diff --git a/src/lib/variables.ts b/src/lib/variables.ts index 93fd93488..e5a0187cf 100644 --- a/src/lib/variables.ts +++ b/src/lib/variables.ts @@ -114,8 +114,8 @@ export const hue_shift: StyleVariable = { /* neutral intent knobs - the temperature of every surface, text, border, and -shadow (replaces the old tint_hue/tint_saturation); the neutral is an intent -whose scales are shade_* and text_* rather than a neutral_00-100 family +shadow; the neutral is an intent whose scales are shade_* and text_* rather +than a neutral_00-100 family */ export const hue_neutral: StyleVariable = { name: 'hue_neutral', light: 'var(--hue_f)' }; diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index f0534f4a4..5e1313ab8 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -69,6 +69,7 @@ const step = $derived(knob.step ?? 1); const emit_numeric = (raw: string): void => { + if (raw.trim() === '') return; // `Number('')` is 0 — don't slam the knob mid-edit const n = Number(raw); if (Number.isNaN(n)) return; onchange(knob.kind === 'percent' ? `${n}%` : knob.kind === 'time' ? `${n}s` : String(n)); diff --git a/src/routes/docs/elements/+page.svelte b/src/routes/docs/elements/+page.svelte index 5519945c7..f6bf31f59 100644 --- a/src/routes/docs/elements/+page.svelte +++ b/src/routes/docs/elements/+page.svelte @@ -54,7 +54,7 @@ >:last-child</code > of all otherwise-spaced elements, streamlining the common case. This has some unfortunate edge cases - that can usually by solved by adding <code>.mb_lg</code>. Coupling markup structure to styles + that can usually be solved by adding <code>.mb_lg</code>. Coupling markup structure to styles like this may be something we change, <a href="https://github.com/fuzdev/fuz_css">feedback</a> is welcome. </p> diff --git a/src/test/oklch.test.ts b/src/test/oklch.test.ts index 1d1288e6c..795e9cf55 100644 --- a/src/test/oklch.test.ts +++ b/src/test/oklch.test.ts @@ -8,8 +8,8 @@ import { srgb_to_oklch, type Oklch, type RgbUnit -} from '../lib/oklch.ts'; -import { srgb_relative_luminance, wcag_contrast_ratio } from '../lib/wcag.ts'; +} from '$lib/oklch.ts'; +import { srgb_relative_luminance, wcag_contrast_ratio } from '$lib/wcag.ts'; const assert_close = (actual: number, expected: number, tolerance: number, message?: string) => { assert( diff --git a/src/test/ramps.test.ts b/src/test/ramps.test.ts index 42fb21f59..cf1c12a2a 100644 --- a/src/test/ramps.test.ts +++ b/src/test/ramps.test.ts @@ -13,17 +13,17 @@ import { ramp_lightness, shade_stop_oklch, text_stop_oklch -} from '../lib/ramps.ts'; -import { color_scheme_variants, palette_variants } from '../lib/variable_data.ts'; -import { oklch_in_srgb_gamut, oklch_to_srgb, type RgbUnit } from '../lib/oklch.ts'; -import { wcag_contrast_ratio } from '../lib/wcag.ts'; +} from '$lib/ramps.ts'; +import { color_scheme_variants, palette_variants } from '$lib/variable_data.ts'; +import { oklch_in_srgb_gamut, oklch_to_srgb, type RgbUnit } from '$lib/oklch.ts'; +import { wcag_contrast_ratio } from '$lib/wcag.ts'; import { GATE_BODY_TEXT, GATE_FILL_TEXT, GATE_LINK, GATE_SUBTLE_TEXT, GATE_UI -} from '../lib/theme_check.ts'; +} from '$lib/theme_check.ts'; // Contrast gates for the default palette. OKLCH lightness is monotonic with // luminance, so these hold for every hue at a given stop pair — but WCAG diff --git a/src/test/theme_check.test.ts b/src/test/theme_check.test.ts index 548db94ba..8d6e839d1 100644 --- a/src/test/theme_check.test.ts +++ b/src/test/theme_check.test.ts @@ -221,6 +221,7 @@ describe('check_theme', () => { test('terminal keeps its contrast gates', () => { const contrast = check_theme(terminal_theme).entries.filter((e) => e.gate === 'contrast'); + assert.isAbove(contrast.length, 0, 'contrast gates resolved'); assert.isTrue(contrast.every((e) => e.pass)); }); @@ -229,6 +230,7 @@ describe('check_theme', () => { const gamut_fails = report.entries.filter((e) => e.gate === 'gamut' && !e.pass); assert.isAbove(gamut_fails.length, 0, 'chroma_scale > 1 is expected to clip weak hues'); const contrast = report.entries.filter((e) => e.gate === 'contrast'); + assert.isAbove(contrast.length, 0, 'contrast gates resolved'); assert.isTrue( contrast.every((e) => e.pass), 'lightness holds through chroma clipping' @@ -240,6 +242,7 @@ describe('check_theme', () => { const gamut_fails = report.entries.filter((e) => e.gate === 'gamut' && !e.pass); assert.isAbove(gamut_fails.length, 0, 'chroma_scale > 1 is expected to clip weak hues'); const contrast = report.entries.filter((e) => e.gate === 'contrast'); + assert.isAbove(contrast.length, 0, 'contrast gates resolved'); assert.isTrue( contrast.every((e) => e.pass), 'lightness holds through chroma clipping' From 475db25c6d11d4eb7a50e7ddd1fa6bf78ae93966 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Fri, 24 Jul 2026 11:20:46 -0400 Subject: [PATCH 20/33] refactor --- .changeset/input-hover-accent.md | 12 +++++ .changeset/selected-button-inverse-text.md | 27 ++++++++++ src/lib/css_class_composites.ts | 49 ++++------------- src/lib/style.css | 38 +++++++------ src/lib/theme_check.ts | 53 +++++++++++++++++-- src/lib/themes/brutalish.ts | 3 +- src/lib/vite_plugin_fuz_css.ts | 9 ++++ src/routes/KnobControl.svelte | 17 +++++- src/routes/ThemeEditor.svelte | 6 +-- src/routes/docs/borders/+page.svelte | 5 +- .../docs/borders/ResolvedColorCode.svelte | 44 +++++++++++++++ src/routes/docs/colors/+page.svelte | 2 +- src/routes/docs/themes/+page.svelte | 15 +++++- src/routes/theme_editor_state.svelte.ts | 19 ++++++- src/test/ramps.test.ts | 20 +++++++ vite.config.ts | 4 +- 16 files changed, 246 insertions(+), 77 deletions(-) create mode 100644 .changeset/input-hover-accent.md create mode 100644 .changeset/selected-button-inverse-text.md create mode 100644 src/routes/docs/borders/ResolvedColorCode.svelte diff --git a/.changeset/input-hover-accent.md b/.changeset/input-hover-accent.md new file mode 100644 index 000000000..95621eaff --- /dev/null +++ b/.changeset/input-hover-accent.md @@ -0,0 +1,12 @@ +--- +'@fuzdev/fuz_css': minor +--- + +feat: input hover borders preview the focus color + +Hovering an input, textarea, or select now colors the border with +`var(--outline_color)` — the element color when classed (`.palette_*` sets +it), the accent intent otherwise — instead of fading it to the weaker +`--border_color_20` alpha. Focus keeps setting the border to the same color +and adds the outline, so hover → focus reads as one escalating highlight. +Disabled inputs no longer react to hover. diff --git a/.changeset/selected-button-inverse-text.md b/.changeset/selected-button-inverse-text.md new file mode 100644 index 000000000..6eabc4b2a --- /dev/null +++ b/.changeset/selected-button-inverse-text.md @@ -0,0 +1,27 @@ +--- +'@fuzdev/fuz_css': minor +--- + +fix: keep selected-button text readable under contrast-bent themes and colored fills + +Selected buttons now use `--text_00` (the text ramp endpoint) instead of +`--text_05`/`--text_10` for inverse text. Themes bending +`--text_lightness_curve` — the high-contrast registry theme, brutalish, and +the OS `prefers-contrast: more` mapping — dragged the near-background stops +toward the fill lightness, washing selected text out (down to ~1.2:1); the +endpoints are the knobs themselves, so the curve can never move them. + +Colored buttons (`.palette_X`) now fill with `palette_X_50` instead of +`palette_X_40`, matching the neutral `shade_50` selected fill — the stop-40 +fills left light-scheme inverse text at ~2.5:1, below the 3:1 large-text +floor (the disabled-active feedback fill moves `negative_40` → `negative_50` +for the same reason). The selected border now matches the fill, rendering +flat. Unselected tint fills mix from the same `--fill`, so they read +slightly richer. + +`check_theme` gains a matching contrast gate — `GATE_SELECTED_TEXT`: +`text_00` on `shade_50` and on every stop-50 fill must clear 3:1 — so these +pairings can't silently regress. The selected-deselectable hover swaps +`--shade_min` for its text-semantic twin `--text_min` (identical values). +The brutalish exemplar drops its `chroma_scale: 0.5` so the palette keeps +its default punch as the theme's docs describe. diff --git a/src/lib/css_class_composites.ts b/src/lib/css_class_composites.ts index 903b31e6e..299461b13 100644 --- a/src/lib/css_class_composites.ts +++ b/src/lib/css_class_composites.ts @@ -1,4 +1,5 @@ import type { CssClassDefinition } from './css_class_generation.ts'; +import { palette_variants } from './variable_data.ts'; export const css_class_composites: Record<string, CssClassDefinition | undefined> = { pixelated: { @@ -309,46 +310,14 @@ export const css_class_composites: Record<string, CssClassDefinition | undefined a.chip { font-weight: 600; } - .chip.palette_a { - color: var(--palette_a_50); - background-color: var(--palette_a_10); - } - .chip.palette_b { - color: var(--palette_b_50); - background-color: var(--palette_b_10); - } - .chip.palette_c { - color: var(--palette_c_50); - background-color: var(--palette_c_10); - } - .chip.palette_d { - color: var(--palette_d_50); - background-color: var(--palette_d_10); - } - .chip.palette_e { - color: var(--palette_e_50); - background-color: var(--palette_e_10); - } - .chip.palette_f { - color: var(--palette_f_50); - background-color: var(--palette_f_10); - } - .chip.palette_g { - color: var(--palette_g_50); - background-color: var(--palette_g_10); - } - .chip.palette_h { - color: var(--palette_h_50); - background-color: var(--palette_h_10); - } - .chip.palette_i { - color: var(--palette_i_50); - background-color: var(--palette_i_10); - } - .chip.palette_j { - color: var(--palette_j_50); - background-color: var(--palette_j_10); - } + ${palette_variants + .map( + (letter) => `.chip.palette_${letter} { + color: var(--palette_${letter}_50); + background-color: var(--palette_${letter}_10); + }` + ) + .join('\n\t\t\t')} ` } }; diff --git a/src/lib/style.css b/src/lib/style.css index 0ebbd408a..02cbb633a 100644 --- a/src/lib/style.css +++ b/src/lib/style.css @@ -368,8 +368,11 @@ currently can break based on DOM structure - https://developer.mozilla.org/en-US background-color: var(--shade_00); } - :where(:is(input, textarea, select):not(.unstyled):hover) { - --border_color: var(--border_color_20); + :where(:is(input, textarea, select):not(.unstyled):hover:not(:disabled)) { + /* hover previews the focus color — the element color when classed + (.palette_* sets --outline_color), else the accent intent; focus then + adds the outline at the same color, escalating visibility */ + --border_color: var(--outline_color); } /* these have no active states because they feel buggy in the browsers, @@ -593,61 +596,61 @@ TODO think through these patterns to get somewhere consistent :where(button:not(.unstyled).palette_a) { --text_color: var(--palette_a_50); - --fill: var(--palette_a_40); + --fill: var(--palette_a_50); --border_color: var(--palette_a_50); --outline_color: var(--palette_a_50); } :where(button:not(.unstyled).palette_b) { --text_color: var(--palette_b_50); - --fill: var(--palette_b_40); + --fill: var(--palette_b_50); --border_color: var(--palette_b_50); --outline_color: var(--palette_b_50); } :where(button:not(.unstyled).palette_c) { --text_color: var(--palette_c_50); - --fill: var(--palette_c_40); + --fill: var(--palette_c_50); --border_color: var(--palette_c_50); --outline_color: var(--palette_c_50); } :where(button:not(.unstyled).palette_d) { --text_color: var(--palette_d_50); - --fill: var(--palette_d_40); + --fill: var(--palette_d_50); --border_color: var(--palette_d_50); --outline_color: var(--palette_d_50); } :where(button:not(.unstyled).palette_e) { --text_color: var(--palette_e_50); - --fill: var(--palette_e_40); + --fill: var(--palette_e_50); --border_color: var(--palette_e_50); --outline_color: var(--palette_e_50); } :where(button:not(.unstyled).palette_f) { --text_color: var(--palette_f_50); - --fill: var(--palette_f_40); + --fill: var(--palette_f_50); --border_color: var(--palette_f_50); --outline_color: var(--palette_f_50); } :where(button:not(.unstyled).palette_g) { --text_color: var(--palette_g_50); - --fill: var(--palette_g_40); + --fill: var(--palette_g_50); --border_color: var(--palette_g_50); --outline_color: var(--palette_g_50); } :where(button:not(.unstyled).palette_h) { --text_color: var(--palette_h_50); - --fill: var(--palette_h_40); + --fill: var(--palette_h_50); --border_color: var(--palette_h_50); --outline_color: var(--palette_h_50); } :where(button:not(.unstyled).palette_i) { --text_color: var(--palette_i_50); - --fill: var(--palette_i_40); + --fill: var(--palette_i_50); --border_color: var(--palette_i_50); --outline_color: var(--palette_i_50); } :where(button:not(.unstyled).palette_j) { --text_color: var(--palette_j_50); - --fill: var(--palette_j_40); + --fill: var(--palette_j_50); --border_color: var(--palette_j_50); --outline_color: var(--palette_j_50); } @@ -663,7 +666,10 @@ TODO think through these patterns to get somewhere consistent outline: none; } :where(button:not(.unstyled).selected) { - --button_text_color: var(--text_05); + /* text_00 is the ramp endpoint: contrast themes bending + --text_lightness_curve move the near-background stops toward the fill, + but can never move an endpoint, so inverse text stays readable */ + --button_text_color: var(--text_00); --button_fill: var(--fill, var(--shade_50)); --button_fill_hover: var(--fill, var(--shade_50)); --button_fill_active: var(--fill, var(--shade_50)); @@ -686,7 +692,7 @@ TODO think through these patterns to get somewhere consistent --button_text_color: color-mix(in oklab, var(--text_color) 90%, var(--shade_max)); } :where(button:not(.unstyled).selected.deselectable:not(:disabled):hover) { - --button_text_color: var(--shade_min); + --button_text_color: var(--text_min); } :where(button:not(.unstyled):not(:disabled):active) { --button_fill: var(--button_fill_active); @@ -703,8 +709,8 @@ TODO think through these patterns to get somewhere consistent } /* TODO is there a cleaner way to override these through vars? */ :where(button:not(.unstyled).selected:disabled:active) { - --text_color: var(--text_10); - --fill: var(--negative_40); + --text_color: var(--text_00); + --fill: var(--negative_50); } :where(button:not(.unstyled):not(.selected):disabled:active) { --fill: transparent; diff --git a/src/lib/theme_check.ts b/src/lib/theme_check.ts index 591da3652..116a7501e 100644 --- a/src/lib/theme_check.ts +++ b/src/lib/theme_check.ts @@ -88,6 +88,14 @@ export const GATE_LINK = 4.5; export const GATE_UI = 3; /** Large-text floor: `text_max` on every stop-50 fill. */ export const GATE_FILL_TEXT = 3; +/** + * Selected-control inverse text: `text_00` on `shade_50` and on every stop-50 + * fill — the selected-button pairings in `style.css` (`.selected` fills with + * `shade_50`, `.palette_X.selected` with `palette_X_50`). The endpoint stop is + * immune to `text_lightness_curve` bends, so this gate moves only when a theme + * moves the endpoints, the fill ramps, or the hues. + */ +export const GATE_SELECTED_TEXT = 3; /** * The variable names a theme may set: the declared defaults plus the hook @@ -408,8 +416,8 @@ const validate_knob_value = ( const issues: Array<ThemeIssue> = []; const trimmed = value.trim(); const numeric = trimmed !== '' && Number.isFinite(Number(trimmed)); - const check_range = (): void => { - if (knob.range && (Number(trimmed) < knob.range[0] || Number(trimmed) > knob.range[1])) { + const check_range = (n: number = Number(trimmed)): void => { + if (knob.range && (n < knob.range[0] || n > knob.range[1])) { issues.push({ level: 'warning', message: `${variable} ${slot} ${trimmed} is outside the safe range [${knob.range[0]}, ${ @@ -448,12 +456,15 @@ const validate_knob_value = ( break; } case 'time': { - if (!/^-?\d*\.?\d+s$/u.test(trimmed)) { + const time_match = /^(-?\d*\.?\d+)s$/u.exec(trimmed); + if (!time_match) { issues.push({ level: 'warning', message: `${variable} ${slot} "${value}" is not a CSS time value like 0.2s`, variable }); + } else { + check_range(Number(time_match[1])); } break; } @@ -557,6 +568,11 @@ const gamut_excess = ([r, g, b]: RgbUnit): number => Math.max(0, -r, r - 1, -g, * failures land in `entries` (with `pass: false`), inputs that can't be * resolved land in `unchecked`, and `ok` is true only when every entry passes * and nothing is unchecked. Never throws. + * + * An intent hue that resolves to the same angle as a palette letter (the + * default for every intent) folds into that letter's entries rather than + * duplicating them, so reports for letter-bound themes list fewer subjects + * than the full letters × intents grid. */ export const check_theme = (theme: Theme): ThemeCheckReport => { const resolver = new ThemeResolver(theme); @@ -720,6 +736,21 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { const shade_00 = neutral_color('shade', '00', scheme); + // contrast: selected-control inverse text — text_00 on shade_50 + const text_00 = neutral_color('text', '00', scheme); + const shade_50 = neutral_color('shade', '50', scheme); + if (text_00 && shade_50) { + const ratio = contrast(oklch_to_srgb(text_00), oklch_to_srgb(shade_50)); + entries.push({ + gate: 'contrast', + scheme, + subject: 'text_00 on shade_50', + value: ratio, + threshold: GATE_SELECTED_TEXT, + pass: ratio >= GATE_SELECTED_TEXT + }); + } + // contrast: subtle text — text_50 on shade_00 const text_50 = neutral_color('text', '50', scheme); if (text_50 && shade_00) { @@ -786,6 +817,17 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { threshold: GATE_FILL_TEXT, pass: on_fill >= GATE_FILL_TEXT }); + if (text_00) { + const selected = contrast(oklch_to_srgb(text_00), fill_rgb); + entries.push({ + gate: 'contrast', + scheme, + subject: `text_00 on ${label}_50`, + value: selected, + threshold: GATE_SELECTED_TEXT, + pass: selected >= GATE_SELECTED_TEXT + }); + } } } } @@ -815,7 +857,10 @@ const collect_hues = (resolver: ThemeResolver, scheme: ColorSchemeVariant): Arra const r = resolver.resolve(`hue_${intent}`, scheme); if (r.ok && !hues.some((h) => Math.abs(h - r.value) < 1e-9)) hues.push(r.value); } - return hues; + // with no resolvable hue at all, an empty set would yield Infinity caps and + // emit garbage CSS — fall back to the default hues (the re-check still + // reports the unresolvable pins) + return hues.length ? hues : Object.values(PALETTE_HUES); }; const resolve_or = ( diff --git a/src/lib/themes/brutalish.ts b/src/lib/themes/brutalish.ts index c00685e9a..371944bdb 100644 --- a/src/lib/themes/brutalish.ts +++ b/src/lib/themes/brutalish.ts @@ -15,8 +15,7 @@ export const brutalish_theme: Theme = { name: 'brutalish', variables: [ // concrete ground, saturated accents: the neutral goes raw while the - // palette keeps punch - { name: 'chroma_scale', light: '0.5' }, + // palette keeps its full default chroma for punch { name: 'neutral_chroma', light: '0.006' }, // paper white / void black, max contrast { name: 'shade_lightness_00', light: '1', dark: '0' }, diff --git a/src/lib/vite_plugin_fuz_css.ts b/src/lib/vite_plugin_fuz_css.ts index 2b38c70ba..f499e3533 100644 --- a/src/lib/vite_plugin_fuz_css.ts +++ b/src/lib/vite_plugin_fuz_css.ts @@ -402,6 +402,10 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug * and the connect-time resync. */ const invalidate_and_push = (new_css: string): void => { + // Deliberately not updating last_served_css: only load() marks CSS as + // served, so if the pushed js-update never causes a refetch, the next + // ws connection re-diffs against what clients actually hold and pushes + // again — convergence relies on that refetch loop. last_generated_css = new_css; pending_css = new_css; // Store for reuse in load() to avoid regenerating @@ -566,6 +570,11 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug prescan_active = true; try { await each_concurrent(file_ids, PRESCAN_CONCURRENCY, async (id) => { + // Reads disk bytes while transform later receives Vite's input for + // the same id; with enforce: 'pre' those are identical, so the + // content-hash short-circuit makes the second ingest a no-op. A + // loader that rewrites content before 'pre' plugins would extract + // twice, with the transform result winning — harmless, just noted. const r = await deps.read_text({ path: id }); if (!r.ok) return; // deleted mid-scan or unreadable; transform covers it if it reappears await ingest_file(id, r.value); diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index 5e1313ab8..f69f3c710 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -59,8 +59,9 @@ const numeric_value = $derived(resolve_numeric(value)); // a live swatch of the current angle for the detach ("custom") button, so it - // tracks the hue the way the letter buttons track their palette slot - const custom_color = $derived(`oklch(0.65 0.14 ${numeric_value ?? 0})`); + // tracks the hue the way the letter buttons track their palette slot; + // same L/C as HueSwatch's gradient so hue displays read consistently + const custom_color = $derived(`oklch(0.65 0.17 ${numeric_value ?? 0})`); const scalar = $derived( knob.kind === 'hue' || knob.kind === 'number' || knob.kind === 'percent' || knob.kind === 'time' ); @@ -115,6 +116,15 @@ </div> {#if bound_letter === null && numeric_value !== null} <HueInput bind:value={() => numeric_value ?? 0, (v) => emit_numeric(String(v))} /> + {:else if bound_letter === null} + <!-- a detached value the slider can't represent (e.g. a calc()) - + raw text entry as the escape hatch --> + <input + type="text" + aria-label={knob.name} + value={value ?? ''} + onchange={(e) => onchange(e.currentTarget.value)} + /> {/if} {:else if knob.kind === 'hue' && numeric_value !== null} <!-- HueInput carries its own internal label; the name renders as its title --> @@ -126,6 +136,9 @@ <div class="title"><code class="knob_name">--{knob.name}</code></div> {#if knob.kind === 'enum'} <select value={value ?? ''} onchange={(e) => onchange(e.currentTarget.value)}> + {#if value === undefined} + <option value="" disabled>unset</option> + {/if} {#each knob.values ?? [] as v (v)} <option value={v}>{v}</option> {/each} diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte index 545fa3e2c..3c7e61661 100644 --- a/src/routes/ThemeEditor.svelte +++ b/src/routes/ThemeEditor.svelte @@ -16,6 +16,7 @@ type PaletteVariant } from '$lib/variable_data.ts'; import { + discard_confirm_message, render_theme_ts, UNSAVED_THEME_NAME, type ThemeEditorState @@ -73,13 +74,10 @@ // edits, so guard the switch behind a confirm when the draft is dirty const on_base_change = (e: Event & { currentTarget: EventTarget & HTMLSelectElement }): void => { const name = e.currentTarget.value; - const discarded = editor.overrides.size - ? `${editor.overrides.size} edited knob(s) will be discarded` - : 'the scheme change will be discarded'; if ( editor.dirty && // eslint-disable-next-line no-alert -- deliberate guard against silently discarding edits - !confirm(`load "${name}" as the new base? ${discarded}`) + !confirm(discard_confirm_message(editor, name)) ) { e.currentTarget.value = editor.based_on; return; diff --git a/src/routes/docs/borders/+page.svelte b/src/routes/docs/borders/+page.svelte index c5b066caf..6975ca529 100644 --- a/src/routes/docs/borders/+page.svelte +++ b/src/routes/docs/borders/+page.svelte @@ -18,6 +18,7 @@ type IntensityVariant } from '$lib/variable_data.ts'; import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; + import ResolvedColorCode from '$routes/docs/borders/ResolvedColorCode.svelte'; const LIBRARY_ITEM_NAME = 'borders'; @@ -160,9 +161,7 @@ border-color: var(--shade_30); {/each} </div> <div style:width="200px"> - <span class="pl_sm pr_sm">=</span><code - >{computed_styles?.getPropertyValue('--' + name)}</code - > + <span class="pl_sm pr_sm">=</span><ResolvedColorCode {name} /> </div> </div> {/each} diff --git a/src/routes/docs/borders/ResolvedColorCode.svelte b/src/routes/docs/borders/ResolvedColorCode.svelte new file mode 100644 index 000000000..5afb1c4fc --- /dev/null +++ b/src/routes/docs/borders/ResolvedColorCode.svelte @@ -0,0 +1,44 @@ +<script lang="ts"> + import { theme_state_context } from '@fuzdev/fuz_ui/theme_state.svelte.ts'; + + // the derived color stops are calc()/oklch() expressions, so reading them + // with `getPropertyValue` yields the unevaluated token stream — render a + // zero-size probe and read the browser-resolved color instead, like the + // colors page's `ColorSwatchItem` + + const { name }: { name: string } = $props(); + + const get_theme_state = theme_state_context.get(); + const theme_state = $derived(get_theme_state()); + + let probe_el: HTMLElement | undefined = $state.raw(); + let resolved = $state.raw(''); + + // read after render so the probe reflects the current name/scheme/theme + $effect(() => { + theme_state.color_scheme; + theme_state.theme; + name; + if (!probe_el) return; + resolved = window.getComputedStyle(probe_el).backgroundColor; + }); + + const formatted = $derived.by(() => { + // expects the computed-style serialization of an opaque oklch() color; + // anything else degrades to the raw resolved string + const m = /^oklch\(([\d.]+) ([\d.]+) ([\d.]+)\)$/u.exec(resolved); + if (!m) return resolved; + return `oklch(${Number(m[1]).toFixed(3)} ${Number(m[2]).toFixed(3)} ${Number(m[3]).toFixed(0)})`; + }); +</script> + +<span class="probe" bind:this={probe_el} style:background-color="var(--{name})" aria-hidden="true" +></span><code>{formatted}</code> + +<style> + .probe { + display: inline-block; + width: 0; + height: 0; + } +</style> diff --git a/src/routes/docs/colors/+page.svelte b/src/routes/docs/colors/+page.svelte index e4f25b256..574b8e963 100644 --- a/src/routes/docs/colors/+page.svelte +++ b/src/routes/docs/colors/+page.svelte @@ -113,7 +113,7 @@ Hue variables are also useful to construct custom colors not covered by the palette. For example, fuz_css's selection color derives from <code>--hue_accent</code> (try selecting some text -- - <span class="palette_a_50">same hue!</span>) + <span class="accent_50">same hue!</span>) </p> <p>Hue variables are the same in both light and dark modes (non-adaptive).</p> <ul class="palette unstyled"> diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index 1488be7ee..cabc77a13 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -20,6 +20,7 @@ import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; import ThemeEditor from '$routes/ThemeEditor.svelte'; import { + discard_confirm_message, ThemeEditorState, UNSAVED_THEME_NAME, type ThemeEditorSnapshotData @@ -46,10 +47,20 @@ // passed as ThemeInput's `select` (not `onselect`, which collides with the // DOM handler type in its menu-attribute props): applies the theme like the - // default select and loads it into the editor + // default select and loads it into the editor, with the same dirty-draft + // guard as the editor's "based on" select const select_theme = (theme: Theme): void => { + if (theme.name !== UNSAVED_THEME_NAME) { + if ( + editor.dirty && + // eslint-disable-next-line no-alert -- deliberate guard against silently discarding edits + !confirm(discard_confirm_message(editor, theme.name)) + ) { + return; + } + editor.load_theme(theme); + } theme_state.theme = theme; - if (theme.name !== UNSAVED_THEME_NAME) editor.load_theme(theme); }; // live scope is global with no pin: the draft writes to `:root` through the diff --git a/src/routes/theme_editor_state.svelte.ts b/src/routes/theme_editor_state.svelte.ts index 8e9f58c54..21bf58411 100644 --- a/src/routes/theme_editor_state.svelte.ts +++ b/src/routes/theme_editor_state.svelte.ts @@ -62,6 +62,7 @@ export class ThemeEditorState { readonly overrides: SvelteMap<string, SlotOverride> = new SvelteMap(); constructor(themes: Array<Theme>) { + if (!themes.length) throw new Error('ThemeEditorState requires at least one theme'); this.themes = themes; } @@ -238,7 +239,11 @@ export class ThemeEditorState { */ restore_snapshot(data: ThemeEditorSnapshotData): void { this.name = data.name; - this.based_on = data.based_on; + // a stale snapshot may reference a renamed/removed theme - fall back to + // the first theme rather than leaving the "based on" select unmatched + this.based_on = this.themes.some((t) => t.name === data.based_on) + ? data.based_on + : this.themes[0]!.name; this.scheme = data.scheme ?? this.base_scheme; this.overrides.clear(); for (const [name, o] of data.overrides) { @@ -247,6 +252,18 @@ export class ThemeEditorState { } } +/** + * The confirm-dialog message shown before a dirty draft is discarded by + * loading `name` as the new base — shared by every picker that can trigger + * the flatten-on-load, so the wording can't drift. + */ +export const discard_confirm_message = (editor: ThemeEditorState, name: string): string => { + const discarded = editor.overrides.size + ? `${editor.overrides.size} edited knob(s) will be discarded` + : 'the scheme change will be discarded'; + return `load "${name}" as the new base? ${discarded}`; +}; + const escape_single_quotes = (s: string): string => s.replaceAll('\\', '\\\\').replaceAll("'", "\\'"); diff --git a/src/test/ramps.test.ts b/src/test/ramps.test.ts index cf1c12a2a..6a623bff6 100644 --- a/src/test/ramps.test.ts +++ b/src/test/ramps.test.ts @@ -21,6 +21,7 @@ import { GATE_BODY_TEXT, GATE_FILL_TEXT, GATE_LINK, + GATE_SELECTED_TEXT, GATE_SUBTLE_TEXT, GATE_UI } from '$lib/theme_check.ts'; @@ -147,6 +148,19 @@ describe('contrast gates', () => { } }); + test('selected inverse text: text_00 on shade_50 clears the large-text floor', () => { + for (const scheme of color_scheme_variants) { + const ratio = wcag_contrast_ratio( + oklch_to_srgb(text_stop_oklch('00', scheme)), + oklch_to_srgb(shade_stop_oklch('50', scheme)) + ); + assert( + ratio >= GATE_SELECTED_TEXT, + `text_00 on shade_50 ${scheme}: ${ratio.toFixed(2)} < ${GATE_SELECTED_TEXT}` + ); + } + }); + test('links: palette_a_60 on shade_00 meets AA', () => { for (const scheme of color_scheme_variants) { const ratio = wcag_contrast_ratio( @@ -161,6 +175,7 @@ describe('contrast gates', () => { for (const scheme of color_scheme_variants) { const surface = oklch_to_srgb(shade_stop_oklch('00', scheme)); const text_max: RgbUnit = scheme === 'light' ? [0, 0, 0] : [1, 1, 1]; + const text_00 = oklch_to_srgb(text_stop_oklch('00', scheme)); for (const letter of palette_variants) { const fill = clamp_rgb(oklch_to_srgb(palette_stop_oklch(letter, '50', scheme))); const ui = wcag_contrast_ratio(fill, surface); @@ -173,6 +188,11 @@ describe('contrast gates', () => { on_fill >= GATE_FILL_TEXT, `text_max on palette_${letter}_50 ${scheme}: ${on_fill.toFixed(2)} < ${GATE_FILL_TEXT}` ); + const selected = wcag_contrast_ratio(text_00, fill); + assert( + selected >= GATE_SELECTED_TEXT, + `text_00 on palette_${letter}_50 ${scheme}: ${selected.toFixed(2)} < ${GATE_SELECTED_TEXT}` + ); } } }); diff --git a/vite.config.ts b/vite.config.ts index 63e0dc17f..f102c1d68 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,7 +3,7 @@ import { sveltekit } from '@sveltejs/kit/vite'; import svelte_docinfo from 'svelte-docinfo/vite.js'; import { vite_plugin_pkg_json } from '@fuzdev/fuz_ui/vite_plugin_pkg_json.ts'; -import { vite_plugin_fuz_css } from './src/lib/vite_plugin_fuz_css.js'; +import { vite_plugin_fuz_css } from './src/lib/vite_plugin_fuz_css.ts'; import { alpha_variants, darken_lighten_variants, @@ -14,7 +14,7 @@ import { shadow_semantic_values, shadow_size_variants, shadow_variant_prefixes -} from './src/lib/variable_data.js'; +} from './src/lib/variable_data.ts'; // the docs pages construct these class families dynamically from pickers, so // static extraction can't see them - declared here from the variant lists From 2258867261fee416df7dd02a9ff0666e6b11ebde Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Fri, 24 Jul 2026 11:26:49 -0400 Subject: [PATCH 21/33] wip --- .changeset/focus-ring-gap.md | 12 ++++++++++++ CLAUDE.md | 3 ++- src/lib/knobs.ts | 10 ++++++++++ src/lib/style.css | 6 ++++++ src/routes/KnobControl.svelte | 2 +- src/routes/docs/borders/+page.svelte | 2 +- 6 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 .changeset/focus-ring-gap.md diff --git a/.changeset/focus-ring-gap.md b/.changeset/focus-ring-gap.md new file mode 100644 index 000000000..51d6f494a --- /dev/null +++ b/.changeset/focus-ring-gap.md @@ -0,0 +1,12 @@ +--- +'@fuzdev/fuz_css': minor +--- + +feat: separate the focus ring from the border with a 1px gap + +Focusable elements (links, buttons, inputs, contenteditable) now draw their +outline with `outline-offset: var(--outline_offset, 1px)`. The ring and the +border share a color by design (the element color, defaulting to the +accent), so without a gap they merged into one thick band; the offset makes +them read as two shapes while keeping the ring's full contrast against the +page. `--outline_offset` is a themable hook in the knob catalog. diff --git a/CLAUDE.md b/CLAUDE.md index 0d22abfa5..940be6679 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -233,7 +233,8 @@ See [variables.ts](src/lib/variables.ts) for definitions, - Micro-surface hooks consumed by `style.css` via `var()` fallbacks: `--caret_color` (defaults to the accent), `--scrollbar_thumb_color`/ `--scrollbar_track_color` (default into the shade scale), `--backdrop_color` - (the `dialog::backdrop` dim); `prefers-contrast: more` maps onto the curve + (the `dialog::backdrop` dim), `--outline_offset` (the border-to-focus-ring + gap, default 1px); `prefers-contrast: more` maps onto the curve knobs mirroring the high-contrast theme, theme-overridable - [knobs.ts](src/lib/knobs.ts) is the typed knob catalog (`kind`, `axis`, `leverage`, `tier`, ranges) powering the themes docs page's inline editor diff --git a/src/lib/knobs.ts b/src/lib/knobs.ts index 9a2842525..a8a8b39e0 100644 --- a/src/lib/knobs.ts +++ b/src/lib/knobs.ts @@ -256,6 +256,16 @@ export const theme_knobs: Array<ThemeKnob> = [ tier: 'semantic', values: ['solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'none'] }, + { + // the gap between an element's border and its focus/active ring, a + // style.css var() fallback hook (default 1px) + name: 'outline_offset', + kind: 'length', + axis: 'shape', + leverage: 'sm', + tier: 'semantic', + hook: true + }, { name: 'border_width', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, { name: 'border_width_1', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, { name: 'border_width_2', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, diff --git a/src/lib/style.css b/src/lib/style.css index 02cbb633a..9daa2ff80 100644 --- a/src/lib/style.css +++ b/src/lib/style.css @@ -143,6 +143,9 @@ Respects `hidden="until-found"` for find-in-page support. text-decoration: var(--text_decoration); border-radius: var(--border_radius, var(--border_radius_xs3)); outline: var(--outline_width) var(--outline_style) var(--outline_color); + /* gap between border and ring so the same-color pair reads as two + shapes instead of one thick band; --outline_offset is a hook */ + outline-offset: var(--outline_offset, 1px); } /* TODO should these be customizable with utility classes? */ :where(a:not(.unstyled):hover) { @@ -347,6 +350,7 @@ currently can break based on DOM structure - https://developer.mozilla.org/en-US border-radius: var(--border_radius, var(--border_radius_sm)); /* TODO --outline_color should default to --hue when available, but can't be set above */ outline: var(--outline_width) var(--outline_style) var(--outline_color); + outline-offset: var(--outline_offset, 1px); padding: var(--input_padding_y) var(--input_padding_x); width: 100%; min-width: var(--input_width_min); @@ -512,6 +516,7 @@ see https://caniuse.com/css-has :where([contenteditable]:not(.unstyled):focus-visible) { --outline_width: var(--outline_width_focus); outline: var(--outline_width) var(--outline_style) var(--outline_color); + outline-offset: var(--outline_offset, 1px); } :where(:is(input, textarea, select, [contenteditable]):not(.unstyled):active) { --outline_width: var(--outline_width_active); @@ -583,6 +588,7 @@ TODO think through these patterns to get somewhere consistent border-color: var(--button_border_color); border-radius: var(--border_radius, var(--border_radius_sm)); outline: var(--outline_width) var(--outline_style) var(--outline_color); + outline-offset: var(--outline_offset, 1px); } :where(:is(button, input, textarea, select):not(.unstyled):focus) { diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index f69f3c710..b79ebc88b 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -169,7 +169,7 @@ <input type="text" value={value ?? ''} - placeholder={knob.hook ? 'unset (per-tier fallbacks)' : ''} + placeholder={knob.hook ? 'unset (falls back in style.css)' : ''} onchange={(e) => onchange(e.currentTarget.value)} /> {/if} diff --git a/src/routes/docs/borders/+page.svelte b/src/routes/docs/borders/+page.svelte index 6975ca529..23feaaf53 100644 --- a/src/routes/docs/borders/+page.svelte +++ b/src/routes/docs/borders/+page.svelte @@ -160,7 +160,7 @@ border-color: var(--shade_30); ></div> {/each} </div> - <div style:width="200px"> + <div style:width="250px"> <span class="pl_sm pr_sm">=</span><ResolvedColorCode {name} /> </div> </div> From bfe364dd80b1a937d1cf6d547f63d28e2236a13a Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Fri, 24 Jul 2026 11:35:17 -0400 Subject: [PATCH 22/33] wip --- .changeset/focus-ring-gap.md | 12 ---- .changeset/input-hover-accent.md | 12 ---- .changeset/interaction-and-surfaces.md | 58 +++++++++++++++++++ .changeset/micro-surface-hooks.md | 21 ------- .changeset/selected-button-inverse-text.md | 27 --------- ...knobs-and-checks.md => theme-authoring.md} | 16 ++++- .changeset/theme-scheme-stance.md | 18 ------ src/routes/docs/themes/+page.svelte | 20 +++---- 8 files changed, 83 insertions(+), 101 deletions(-) delete mode 100644 .changeset/focus-ring-gap.md delete mode 100644 .changeset/input-hover-accent.md create mode 100644 .changeset/interaction-and-surfaces.md delete mode 100644 .changeset/micro-surface-hooks.md delete mode 100644 .changeset/selected-button-inverse-text.md rename .changeset/{theme-knobs-and-checks.md => theme-authoring.md} (61%) delete mode 100644 .changeset/theme-scheme-stance.md diff --git a/.changeset/focus-ring-gap.md b/.changeset/focus-ring-gap.md deleted file mode 100644 index 51d6f494a..000000000 --- a/.changeset/focus-ring-gap.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@fuzdev/fuz_css': minor ---- - -feat: separate the focus ring from the border with a 1px gap - -Focusable elements (links, buttons, inputs, contenteditable) now draw their -outline with `outline-offset: var(--outline_offset, 1px)`. The ring and the -border share a color by design (the element color, defaulting to the -accent), so without a gap they merged into one thick band; the offset makes -them read as two shapes while keeping the ring's full contrast against the -page. `--outline_offset` is a themable hook in the knob catalog. diff --git a/.changeset/input-hover-accent.md b/.changeset/input-hover-accent.md deleted file mode 100644 index 95621eaff..000000000 --- a/.changeset/input-hover-accent.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@fuzdev/fuz_css': minor ---- - -feat: input hover borders preview the focus color - -Hovering an input, textarea, or select now colors the border with -`var(--outline_color)` — the element color when classed (`.palette_*` sets -it), the accent intent otherwise — instead of fading it to the weaker -`--border_color_20` alpha. Focus keeps setting the border to the same color -and adds the outline, so hover → focus reads as one escalating highlight. -Disabled inputs no longer react to hover. diff --git a/.changeset/interaction-and-surfaces.md b/.changeset/interaction-and-surfaces.md new file mode 100644 index 000000000..457167436 --- /dev/null +++ b/.changeset/interaction-and-surfaces.md @@ -0,0 +1,58 @@ +--- +'@fuzdev/fuz_css': minor +--- + +feat: rework interaction states and micro-surfaces — focus ring gap, hover previews focus, themable surface hooks, contrast-safe selected buttons + +Focus and hover now read as one escalating highlight: + +- Focusable elements (links, buttons, inputs, contenteditable) draw their + outline with `outline-offset: var(--outline_offset, 1px)`. The ring and + the border share a color by design (the element color, defaulting to the + accent), so without a gap they merged into one thick band; the offset + makes them read as two shapes while keeping the ring's full contrast + against the page. +- Hovering an input, textarea, or select colors the border with + `var(--outline_color)` — the element color when classed (`.palette_*` + sets it), the accent intent otherwise — instead of fading it to the + weaker `--border_color_20` alpha. Focus keeps setting the border to the + same color and adds the outline. Disabled inputs no longer react to + hover. + +New micro-surface defaults in `style.css`, each themable through a `var()` +fallback hook (all registered theme variables, in the knob catalog): + +- `scrollbar-color` on `:root` — thumb defaults to `var(--shade_40)` on a + transparent track; hooks `--scrollbar_thumb_color` / + `--scrollbar_track_color` +- `caret-color` on text inputs — defaults to `var(--accent_50)`; hook + `--caret_color` +- `dialog::backdrop` — defaults to `var(--darken_60)`; hook + `--backdrop_color` +- `--outline_offset` — the border-to-focus-ring gap above +- `@media (prefers-contrast: more)` maps the OS preference onto the curve + knobs, mirroring the `'high contrast'` theme; theme overrides beat it + +Selected-button text stays readable under contrast-bent themes and colored +fills: + +- Selected buttons use `--text_00` (the text ramp endpoint) instead of + `--text_05`/`--text_10` for inverse text. Themes bending + `--text_lightness_curve` — the high-contrast registry theme, brutalish, + and the OS `prefers-contrast: more` mapping — dragged the near-background + stops toward the fill lightness, washing selected text out (down to + ~1.2:1); the endpoints are the knobs themselves, so the curve can never + move them. +- Colored buttons (`.palette_X`) fill with `palette_X_50` instead of + `palette_X_40`, matching the neutral `shade_50` selected fill — the + stop-40 fills left light-scheme inverse text at ~2.5:1, below the 3:1 + large-text floor (the disabled-active feedback fill moves `negative_40` → + `negative_50` for the same reason). The selected border now matches the + fill, rendering flat. Unselected tint fills mix from the same `--fill`, + so they read slightly richer. +- `check_theme` gains a matching contrast gate — `GATE_SELECTED_TEXT`: + `text_00` on `shade_50` and on every stop-50 fill must clear 3:1 — so + these pairings can't silently regress. The selected-deselectable hover + swaps `--shade_min` for its text-semantic twin `--text_min` (identical + values). The brutalish exemplar drops its `chroma_scale: 0.5` so the + palette keeps its default punch as the theme's docs describe. diff --git a/.changeset/micro-surface-hooks.md b/.changeset/micro-surface-hooks.md deleted file mode 100644 index 0ebc9f6c3..000000000 --- a/.changeset/micro-surface-hooks.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'@fuzdev/fuz_css': minor ---- - -feat: theme scrollbars, caret, dialog backdrop, and the OS contrast preference - -New micro-surface defaults in `style.css`, each themable through a `var()` -fallback hook (all registered theme variables): - -- `scrollbar-color` on `:root` — thumb defaults to `var(--shade_40)` on a - transparent track; hooks `--scrollbar_thumb_color` / - `--scrollbar_track_color` -- `caret-color` on text inputs — defaults to `var(--accent_50)`; hook - `--caret_color` -- `dialog::backdrop` — defaults to `var(--darken_60)`; hook - `--backdrop_color` -- `@media (prefers-contrast: more)` maps the OS preference onto the curve - knobs, mirroring the `'high contrast'` theme; theme overrides beat it - -Also tunes the `'low contrast'` theme's shade compression to the softest -values that pass every `check_theme` contrast gate. diff --git a/.changeset/selected-button-inverse-text.md b/.changeset/selected-button-inverse-text.md deleted file mode 100644 index 6eabc4b2a..000000000 --- a/.changeset/selected-button-inverse-text.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -'@fuzdev/fuz_css': minor ---- - -fix: keep selected-button text readable under contrast-bent themes and colored fills - -Selected buttons now use `--text_00` (the text ramp endpoint) instead of -`--text_05`/`--text_10` for inverse text. Themes bending -`--text_lightness_curve` — the high-contrast registry theme, brutalish, and -the OS `prefers-contrast: more` mapping — dragged the near-background stops -toward the fill lightness, washing selected text out (down to ~1.2:1); the -endpoints are the knobs themselves, so the curve can never move them. - -Colored buttons (`.palette_X`) now fill with `palette_X_50` instead of -`palette_X_40`, matching the neutral `shade_50` selected fill — the stop-40 -fills left light-scheme inverse text at ~2.5:1, below the 3:1 large-text -floor (the disabled-active feedback fill moves `negative_40` → `negative_50` -for the same reason). The selected border now matches the fill, rendering -flat. Unselected tint fills mix from the same `--fill`, so they read -slightly richer. - -`check_theme` gains a matching contrast gate — `GATE_SELECTED_TEXT`: -`text_00` on `shade_50` and on every stop-50 fill must clear 3:1 — so these -pairings can't silently regress. The selected-deselectable hover swaps -`--shade_min` for its text-semantic twin `--text_min` (identical values). -The brutalish exemplar drops its `chroma_scale: 0.5` so the palette keeps -its default punch as the theme's docs describe. diff --git a/.changeset/theme-knobs-and-checks.md b/.changeset/theme-authoring.md similarity index 61% rename from .changeset/theme-knobs-and-checks.md rename to .changeset/theme-authoring.md index 39ff32431..65bda195e 100644 --- a/.changeset/theme-knobs-and-checks.md +++ b/.changeset/theme-authoring.md @@ -2,7 +2,7 @@ '@fuzdev/fuz_css': minor --- -feat: add theme scale knobs, the knob catalog, and theme validation/gates/compile +feat: add theme scale knobs, the knob catalog, validation/gates/compile, and the scheme stance New theme knobs, each derived into existing token defaults so one knob move reshapes a whole family while individual tokens stay pinnable: @@ -31,3 +31,17 @@ New `theme_check.ts`, resolving a theme's authored values back to numbers overrides where the baked caps no longer fit, and re-checks the result `ramps.ts` gains `ramp_hue_shift_offset` and `compute_palette_chroma_caps`. +The `'low contrast'` registry theme's shade compression is retuned to the +softest values that pass every `check_theme` contrast gate. + +New scheme stance: `Theme` gains `scheme?: 'dual' | 'light' | 'dark'` +(default `'dual'`). A single-scheme theme renders its one appearance in both +color schemes: `render_theme_style` mirrors every scheme-adaptive default +the theme doesn't override (exported as `scheme_stance_variables`, including +the `palette_chroma_NN` gamut-cap stops) and pins `color-scheme` on the +scope so form controls and native scrollbars agree. Author a stanced theme's +own variables single-slot in the light/base position. `validate_theme` warns +on dark slots a single-scheme stance makes meaningless; +`check_theme`/`compile_theme` resolve through the same mirror so the gates +evaluate the stanced reality in both schemes. The necromancer and terminal +exemplars use the stance instead of hand-mirrored ramp knobs. diff --git a/.changeset/theme-scheme-stance.md b/.changeset/theme-scheme-stance.md deleted file mode 100644 index 3a546a2f7..000000000 --- a/.changeset/theme-scheme-stance.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -'@fuzdev/fuz_css': minor ---- - -feat: add the theme scheme stance - -`Theme` gains `scheme?: 'dual' | 'light' | 'dark'` (default `'dual'`). A -single-scheme theme renders its one appearance in both color schemes: -`render_theme_style` mirrors every scheme-adaptive default the theme doesn't -override (exported as `scheme_stance_variables`, including the -`palette_chroma_NN` gamut-cap stops) and pins `color-scheme` on the scope so -form controls and native scrollbars agree. Author a stanced theme's own -variables single-slot in the light/base position. - -`validate_theme` warns on dark slots a single-scheme stance makes -meaningless; `check_theme`/`compile_theme` resolve through the same mirror -so the gates evaluate the stanced reality in both schemes. The necromancer -and terminal exemplars use the stance instead of hand-mirrored ramp knobs. diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index cabc77a13..c7cc2a4e2 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -53,7 +53,7 @@ if (theme.name !== UNSAVED_THEME_NAME) { if ( editor.dirty && - // eslint-disable-next-line no-alert -- deliberate guard against silently discarding edits + // eslint-disable-next-line no-alert - deliberate guard against silently discarding edits !confirm(discard_confirm_message(editor, theme.name)) ) { return; @@ -131,7 +131,7 @@ <p> Because the color system is derived, a theme is a set of <em>knob</em> values, not a stylesheet: a handful of high-leverage variables (hue angles, <code>chroma_scale</code>, the - lightness curve knobs -- see <TomeLink slug="colors" />) reshape everything downstream. Theme + lightness curve knobs - see <TomeLink slug="colors" />) reshape everything downstream. Theme CSS renders into the <code>fuz.theme</code> cascade layer, above the <code>fuz.base</code> defaults, so overrides win regardless of stylesheet order. </p> @@ -149,7 +149,7 @@ <TomeSectionHeader text="Exemplar themes" /> <p> Beyond the registry, fuz_css ships expressive exemplar themes as importable modules under - <code>themes/</code> -- registry membership, not file location, is what separates builtins from + <code>themes/</code> - registry membership, not file location, is what separates builtins from exemplars. Import one and pass it to your theme setup: </p> <Code @@ -157,7 +157,7 @@ content={`import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts';`} /> <p> - A theme can declare a single-scheme stance with <code>scheme: 'light' | 'dark'</code> -- the + A theme can declare a single-scheme stance with <code>scheme: 'light' | 'dark'</code> - the renderer then mirrors every scheme-adaptive default the theme doesn't override, so its one appearance renders in both color schemes, and pins <MdnLink path="Web/CSS/color-scheme" /> to match so form controls and scrollbars agree. The necromancer @@ -170,13 +170,13 @@ <TomeSection> <TomeSectionHeader text="Theme editor" /> <p> - Drag a knob and the whole page rethemes live -- extreme values can make the page hard to read, + Drag a knob and the whole page rethemes live - extreme values can make the page hard to read, which is an honest signal, not a bug. Every edit updates a temporary "{UNSAVED_THEME_NAME}" theme in the picker above; it persists across navigation until you leave or reset, so copy the - <code>Theme</code> object below to keep it. The scheme selector sets the theme's stance -- a single-scheme + <code>Theme</code> object below to keep it. The scheme selector sets the theme's stance - a single-scheme theme renders its one appearance in both color schemes, so edits write the base slots. The top band holds the semantic-tier moves: assign each intent (accent, neutral, positive, negative, caution, - info) to a palette letter -- or a custom angle -- and pull the high-leverage levers. Below it, each + info) to a palette letter - or a custom angle - and pull the high-leverage levers. Below it, each axis section carries the granular knobs, with per-token escape hatches folded away; the ramp strips repaint live as the derived scales move. </p> @@ -191,12 +191,12 @@ <p> <code>validate_theme(theme)</code> is the structural lint: unknown variable names are errors, while type and range mismatches on the knob-tier variables are advisory warnings. It returns an array - of issues -- empty means the theme is structurally sound. + of issues - empty means the theme is structurally sound. </p> <p> <code>check_theme(theme)</code> runs the gamut, ramp-monotonicity, and contrast gates against the theme's resolved values. It is report-only and never throws, returning - <code>{'{ok, entries, unchecked}'}</code> -- suited to a CI or test assertion: + <code>{'{ok, entries, unchecked}'}</code> - suited to a CI or test assertion: </p> <Code lang="ts" @@ -209,7 +209,7 @@ test('my theme clears the accessibility gates', () => { });`} /> <p> - <code>compile_theme(theme)</code> is for themes that move hues or lightness ramps -- + <code>compile_theme(theme)</code> is for themes that move hues or lightness ramps - monochrome, rotated, or dark-only. It recomputes the per-stop sRGB gamut caps from the theme's actual hues and appends the corrected <code>palette_chroma_NN</code> stop overrides, returning <code>{'{theme, report, issues}'}</code>. From 8404f165be8d5893b6664010bc90af38f7c253b7 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Fri, 24 Jul 2026 11:36:28 -0400 Subject: [PATCH 23/33] wip --- src/routes/ThemeEditor.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte index 3c7e61661..170bd7915 100644 --- a/src/routes/ThemeEditor.svelte +++ b/src/routes/ThemeEditor.svelte @@ -109,7 +109,7 @@ <div class="theme_editor width:100%"> <header class="mb_flow"> - <div class="row gap_lg flex-wrap:wrap"> + <div class="row gap_lg flex-wrap:wrap mb_lg"> <label> <div class="title">name</div> <input bind:value={editor.name} /> From 354ac4f2640a39fd9cec105c8aad45ce111bda00 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Fri, 24 Jul 2026 12:14:01 -0400 Subject: [PATCH 24/33] wip --- .changeset/interaction-and-surfaces.md | 2 +- .changeset/oklch-color-system.md | 6 +- .changeset/theme-authoring.md | 2 +- src/lib/ramps.ts | 36 ++++++------ src/lib/theme.css | 36 ++++++------ src/routes/KnobControl.svelte | 4 +- src/routes/ThemeEditor.svelte | 6 +- src/routes/docs/borders/+page.svelte | 4 +- .../docs/borders/ResolvedColorCode.svelte | 2 +- src/routes/docs/classes/+page.svelte | 55 +++++++++--------- src/routes/docs/colors/+page.svelte | 56 +++++++++---------- src/routes/docs/forms/+page.svelte | 5 +- src/routes/docs/introduction/+page.svelte | 6 +- src/routes/docs/semantic/+page.svelte | 6 +- src/routes/docs/shading/+page.svelte | 10 ++-- src/routes/docs/shadows/+page.svelte | 2 +- src/routes/docs/themes/+page.svelte | 23 +++----- src/routes/docs/variables/+page.svelte | 10 ++-- 18 files changed, 130 insertions(+), 141 deletions(-) diff --git a/.changeset/interaction-and-surfaces.md b/.changeset/interaction-and-surfaces.md index 457167436..48a69025f 100644 --- a/.changeset/interaction-and-surfaces.md +++ b/.changeset/interaction-and-surfaces.md @@ -2,7 +2,7 @@ '@fuzdev/fuz_css': minor --- -feat: rework interaction states and micro-surfaces — focus ring gap, hover previews focus, themable surface hooks, contrast-safe selected buttons +feat: rework interaction states and micro-surfaces - focus ring gap, hover previews focus, themable surface hooks, contrast-safe selected buttons Focus and hover now read as one escalating highlight: diff --git a/.changeset/oklch-color-system.md b/.changeset/oklch-color-system.md index 625902baf..44daaaacc 100644 --- a/.changeset/oklch-color-system.md +++ b/.changeset/oklch-color-system.md @@ -4,13 +4,13 @@ feat: rework the color system to derived OKLCH, add semantic intents, cascade layers, and themes -Colors are now derived — curve knobs → ramp stops → color stops — in pure +Colors are now derived - curve knobs → ramp stops → color stops - in pure CSS (`calc()`/`pow()`/`oklch()`), fitted to minimize the perceptual delta from the old HSL palette. Breaking changes: - **`color_` renamed to `palette_`**: `--color_a_50` → `--palette_a_50`, - `.color_a_50` → `.palette_a_50`, `.color_a`–`.color_j` → - `.palette_a`–`.palette_j`. In compound families the letter alone implies + `.color_a_50` → `.palette_a_50`, `.color_a`-`.color_j` → + `.palette_a`-`.palette_j`. In compound families the letter alone implies the palette: `border_color_X_NN` → `border_X_NN`, `outline_color_X_NN` → `outline_X_NN`, `shadow_color_X_NN` → `shadow_X_NN` (`bg_X_NN` and the letterless families — `border_color_NN`, `outline_color_NN`, diff --git a/.changeset/theme-authoring.md b/.changeset/theme-authoring.md index 65bda195e..e5035ffa8 100644 --- a/.changeset/theme-authoring.md +++ b/.changeset/theme-authoring.md @@ -9,7 +9,7 @@ reshapes a whole family while individual tokens stay pinnable: `--shadow_alpha_scale` (multiplies the `shadow_alpha_*` ramp, including button shadows), `--radius_scale` (multiplies the `border_radius_*` tiers), `--scale_factor` (multiplies the `space_*` scale), `--font_weight` (body), -`--heading_font_weight` (a hook with per-tier fallbacks — setting it +`--heading_font_weight` (a hook with per-tier fallbacks - setting it flattens the heading ladder), `--heading_font_family`, and `--background_image` (decoration hook on `:root`). diff --git a/src/lib/ramps.ts b/src/lib/ramps.ts index 12084bc0d..deea81408 100644 --- a/src/lib/ramps.ts +++ b/src/lib/ramps.ts @@ -78,8 +78,8 @@ export const PALETTE_HUES: Record<PaletteVariant, number> = { f: 60, // brown g: 358, // pink h: 41, // orange - i: 204, // cyan - j: 163 // teal + i: 210, // cyan + j: 180 // teal }; /** Fitted lightness knobs for the palette (`palette_X_NN`) ramps. */ @@ -153,24 +153,24 @@ export const PALETTE_CHROMA_CAPS: Record< '10': 0.0382, '20': 0.078, '30': 0.1235, - '40': 0.1162, - '50': 0.1011, - '60': 0.0856, - '70': 0.0696, - '80': 0.0533, - '90': 0.0366, - '95': 0.0282, - '100': 0.0197 + '40': 0.1179, + '50': 0.1026, + '60': 0.0868, + '70': 0.0706, + '80': 0.054, + '90': 0.0372, + '95': 0.0286, + '100': 0.02 }, dark: { - '00': 0.025, - '05': 0.0394, - '10': 0.0494, - '20': 0.0664, - '30': 0.0813, - '40': 0.095, - '50': 0.108, - '60': 0.1203, + '00': 0.0254, + '05': 0.04, + '10': 0.0501, + '20': 0.0673, + '30': 0.0824, + '40': 0.0964, + '50': 0.1095, + '60': 0.122, '70': 0.1189, '80': 0.0812, '90': 0.0465, diff --git a/src/lib/theme.css b/src/lib/theme.css index b442a08d4..96991390b 100644 --- a/src/lib/theme.css +++ b/src/lib/theme.css @@ -23,8 +23,8 @@ --hue_f: 60; /* brown */ --hue_g: 358; /* pink */ --hue_h: 41; /* orange */ - --hue_i: 204; /* cyan */ - --hue_j: 163; /* teal */ + --hue_i: 210; /* cyan */ + --hue_j: 180; /* teal */ --chroma_scale: 1; /* 0 makes the palette grayscale (the neutral scales have their own knob), above 1 is vivid and can clip past the sRGB gamut caps */ --hue_shift: 0; /* total degrees of hue rotation across each ramp, positive toward the dark end */ --hue_neutral: var(--hue_f); @@ -397,56 +397,56 @@ var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_40) ), - 0.1162 + 0.1179 ); --palette_chroma_50: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_50) ), - 0.1011 + 0.1026 ); --palette_chroma_60: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_60) ), - 0.0856 + 0.0868 ); --palette_chroma_70: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_70) ), - 0.0696 + 0.0706 ); --palette_chroma_80: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_80) ), - 0.0533 + 0.054 ); --palette_chroma_90: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_90) ), - 0.0366 + 0.0372 ); --palette_chroma_95: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_95) ), - 0.0282 + 0.0286 ); --palette_chroma_100: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_100) ), - 0.0197 + 0.02 ); --hue_shift_00: calc(var(--hue_shift) * -0.5); --hue_shift_05: calc(var(--hue_shift) * -0.45); @@ -1448,56 +1448,56 @@ var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_00) ), - 0.025 + 0.0254 ); --palette_chroma_05: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_05) ), - 0.0394 + 0.04 ); --palette_chroma_10: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_10) ), - 0.0494 + 0.0501 ); --palette_chroma_20: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_20) ), - 0.0664 + 0.0673 ); --palette_chroma_30: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_30) ), - 0.0813 + 0.0824 ); --palette_chroma_40: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_40) ), - 0.095 + 0.0964 ); --palette_chroma_50: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_50) ), - 0.108 + 0.1095 ); --palette_chroma_60: min( calc( var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_60) ), - 0.1203 + 0.122 ); --palette_chroma_70: min( calc( diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index b79ebc88b..24886bccf 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -70,7 +70,7 @@ const step = $derived(knob.step ?? 1); const emit_numeric = (raw: string): void => { - if (raw.trim() === '') return; // `Number('')` is 0 — don't slam the knob mid-edit + if (raw.trim() === '') return; // `Number('')` is 0 - don't slam the knob mid-edit const n = Number(raw); if (Number.isNaN(n)) return; onchange(knob.kind === 'percent' ? `${n}%` : knob.kind === 'time' ? `${n}s` : String(n)); @@ -78,7 +78,7 @@ const gloss_title = (letter: PaletteVariant): string => { const gloss = palette_glosses[letter]; - return `${letter} — ${gloss.color}${gloss.binding ? ` · default ${gloss.binding}` : ''}`; + return `${letter} - ${gloss.color}${gloss.binding ? ` · default ${gloss.binding}` : ''}`; }; </script> diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte index 170bd7915..37ff6d190 100644 --- a/src/routes/ThemeEditor.svelte +++ b/src/routes/ThemeEditor.svelte @@ -249,7 +249,7 @@ <section> <h3>Output</h3> <p> - The copyable <code>Theme</code> object, and the CSS it renders — the theme's own footprint. + The copyable <code>Theme</code> object, and the CSS it renders (only the variables the theme sets). </p> <div class="rendered mb_lg"> <div class="copy"> @@ -266,8 +266,8 @@ </div> {:else} <p> - The theme is empty -- every variable keeps its base default, so it renders no CSS. Move a - knob to see its output. + The theme is empty: every variable keeps its base default, so it renders no CSS. Move a knob + to see its output. </p> {/if} </section> diff --git a/src/routes/docs/borders/+page.svelte b/src/routes/docs/borders/+page.svelte index 23feaaf53..cd963d270 100644 --- a/src/routes/docs/borders/+page.svelte +++ b/src/routes/docs/borders/+page.svelte @@ -62,8 +62,8 @@ <section> <p> Border variables integrate with the <TomeLink slug="themes">theme</TomeLink> system and adapt to - color scheme. Alpha borders are tuned for visual balance -- dark mode uses higher alpha because - light-on-dark has lower perceived contrast. + color scheme. Alpha borders are tuned for visual balance: dark mode uses higher alpha because light-on-dark + has lower perceived contrast. </p> </section> <TomeSection> diff --git a/src/routes/docs/borders/ResolvedColorCode.svelte b/src/routes/docs/borders/ResolvedColorCode.svelte index 5afb1c4fc..b3ffa312a 100644 --- a/src/routes/docs/borders/ResolvedColorCode.svelte +++ b/src/routes/docs/borders/ResolvedColorCode.svelte @@ -2,7 +2,7 @@ import { theme_state_context } from '@fuzdev/fuz_ui/theme_state.svelte.ts'; // the derived color stops are calc()/oklch() expressions, so reading them - // with `getPropertyValue` yields the unevaluated token stream — render a + // with `getPropertyValue` yields the unevaluated token stream - render a // zero-size probe and read the browser-resolved color instead, like the // colors page's `ColorSwatchItem` diff --git a/src/routes/docs/classes/+page.svelte b/src/routes/docs/classes/+page.svelte index cd481bb32..335c3d458 100644 --- a/src/routes/docs/classes/+page.svelte +++ b/src/routes/docs/classes/+page.svelte @@ -55,7 +55,7 @@ <p> 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 + interpreters providing a programmatic escape hatch - see the <a href="#Compared-to-alternatives">comparison</a> below. </p> <p>Compared to the <code><style></code> tag, classes:</p> @@ -104,7 +104,7 @@ The <ModuleLink module_path="vite_plugin_fuz_css.ts">Vite plugin</ModuleLink> 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 - <a href="https://github.com/acornjs/acorn-jsx"><code>acorn-jsx</code></a> plugin -- see + <a href="https://github.com/acornjs/acorn-jsx"><code>acorn-jsx</code></a> plugin - see <a href="#React-and-JSX">React and JSX</a> below. </p> <Code @@ -145,7 +145,7 @@ import 'virtual:fuz.css';`} /> <p> The plugin extracts classes from files as Vite processes them, including from - <code>node_modules</code> dependencies. It supports HMR -- changes to classes in your code trigger + <code>node_modules</code> dependencies. It supports HMR: changes to classes in your code trigger automatic CSS updates. </p> <h4>Plugin options</h4> @@ -343,7 +343,7 @@ const turtle_class_name = 'turtle';`} <p> Usage tracking works for variables inside <code>clsx()</code>, arrays, ternaries, and logical expressions within class attributes. Note that standalone <code>clsx()</code> calls outside - class attributes don't trigger tracking -- use the naming convention for those cases. + class attributes don't trigger tracking - use the naming convention for those cases. </p> <aside> Currently, tracking is single-file only. Cross-module analysis and more sophisticated @@ -386,7 +386,7 @@ const color = get_dynamic_color();`} {/each}`} /> <aside> - Edge values like <code>_00</code> and <code>_100</code> are especially easy to miss -- they're + Edge values like <code>_00</code> and <code>_100</code> are especially easy to miss - they're generally not used directly in your code (they exist mainly for programmatic usage ergonomics), so the class won't be generated unless you hint it. </aside> @@ -490,9 +490,9 @@ const el = document.createElement('dialog');`} <p> Detection finds the classes, elements, and variables your source uses (see <a href="#Class-detection">class detection</a> above), and resolution turns that into CSS. - Bundled mode -- the default for the <a href="#Vite-plugin">Vite plugin</a> and - <a href="#Gro-generator">Gro generator</a> -- combines three layers into a single output, each trimmed - to what your code uses: + Bundled mode (the default for the <a href="#Vite-plugin">Vite plugin</a> and + <a href="#Gro-generator">Gro generator</a>) combines three layers into a single output, each + trimmed to what your code uses: </p> <ol> <li> @@ -517,8 +517,9 @@ const el = document.createElement('dialog');`} </p> <p> If you organize your own styles in <code>@layer</code>, declare fuz's layers first so yours - sort later and win: <code>@layer fuz.base, fuz.theme, fuz.utilities, app;</code>. Otherwise - fuz's layers are declared when its CSS loads and beat any of your earlier-declared layers. + sort later and win: <code>@layer fuz.base, fuz.theme, fuz.utilities, app;</code>. + Later-declared layers win, so without this line fuz's layers are declared when its CSS loads + and beat any layers you declared before it. </p> <h4>Base styles</h4> @@ -592,9 +593,9 @@ vite_plugin_fuz_css({ <TomeSectionHeader text="Token classes" tag="h3" /> <p> Token classes are technically <a href="#Composite-classes">composite classes</a> with a - close relationship to <TomeLink slug="variables">style variables</TomeLink> -- each maps design - tokens to CSS properties. They're generated programmatically from variant data, making them predictable - and systematic. The composites documented + close relationship to <TomeLink slug="variables">style variables</TomeLink>: each maps + design tokens to CSS properties. They're generated programmatically from variant data, + making them predictable and systematic. The composites documented <a href="#Composite-classes">below</a> are hand-written and typically represent higher-level semantic concepts. For raw CSS values, use <a href="#Literal-classes">literal classes</a> instead. @@ -795,9 +796,9 @@ vite_plugin_fuz_css({ </li> </ul> <aside> - A bare scale class applies its family's dominant use — <code>.palette_a_50</code>, + A bare scale class applies its family's dominant use: <code>.palette_a_50</code>, <code>.positive_50</code>, and <code>.text_70</code> set the text color while - <code>.shade_50</code> sets the background — and the <code>bg_</code> prefix selects the + <code>.shade_50</code> sets the background. The <code>bg_</code> prefix selects the background twin (<code>.bg_a_50</code>, <code>.bg_positive_50</code>). In compound families a letter alone implies the palette: <code>.border_a_50</code> is the palette family, <code>.border_color_50</code> the alpha ramp. @@ -1238,9 +1239,9 @@ export const gen = gen_fuz_css({ <TomeSection> <TomeSectionHeader text="Modifiers" /> <p> - 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. + 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. </p> <h4>Responsive modifiers</h4> @@ -1390,7 +1391,7 @@ export const gen = gen_fuz_css({ <p> Combined modifiers follow a canonical order enforced with errors that guide you. Multiple states must be alphabetical (<code>focus:hover:</code> not <code>hover:focus:</code>) - because both generate equivalent CSS -- canonical ordering prevents duplicates. + because both generate equivalent CSS; canonical ordering prevents duplicates. </p> <Code content="[media:][ancestor:][...state:][pseudo-element:]class" /> <ol> @@ -1439,7 +1440,7 @@ export const gen = gen_fuz_css({ fuz_css's <ModuleLink module_path="style.css">main stylesheet</ModuleLink> provides styles for base HTML elements using <TomeLink slug="variables">style variables</TomeLink>, 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. + functionality - these are called builtin classes. </p> <h4><code>.unstyled</code></h4> <p>Default list (styled):</p> @@ -1593,7 +1594,7 @@ export const gen = gen_fuz_css({ </li> <li> <strong>expressions:</strong> logical (<code>&&</code>, - <code>||</code>, <code>??</code>), ternaries, template literals (complete tokens only -- + <code>||</code>, <code>??</code>), ternaries, template literals (complete tokens only - <code>`palette_a_50 ${base}`</code> extracts <code>palette_a_50</code>, but <code>`palette_${hue}_50`</code> cannot be extracted; use <code>@fuz-classes</code> @@ -1715,12 +1716,10 @@ const grid_cols_interpreter: CssClassDefinitionInterpreter = { };`} /> <p> - This generates <code>grid-cols-1</code> through <code>grid-cols-24</code> 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 <ModuleLink - module_path="css_class_definitions.ts" - />. + This generates <code>grid-cols-1</code> through <code>grid-cols-24</code> 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 <ModuleLink module_path="css_class_definitions.ts" />. </p> <p>Register with the Vite plugin or Gro generator:</p> <Code @@ -1815,7 +1814,7 @@ vite_plugin_fuz_css({ 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 -- + fuz_css offers comparable power with interpreters but it's still evolving - <a href="https://github.com/fuzdev/fuz_css/discussions">feedback</a> is welcome! </p> <p> diff --git a/src/routes/docs/colors/+page.svelte b/src/routes/docs/colors/+page.svelte index 574b8e963..614f4fa4e 100644 --- a/src/routes/docs/colors/+page.svelte +++ b/src/routes/docs/colors/+page.svelte @@ -35,62 +35,63 @@ slug="variables" /> in pure CSS, in the <MdnLink path="Web/CSS/color_value/oklch" /> colorspace, adapting to the - <MdnLink path="Web/CSS/color-scheme" /> automatically. OKLCH lightness is perceptually uniform -- - equal lightness reads equally light in every hue -- so rotating a hue knob is safe: contrast and - visual weight hold, which is what makes <TomeLink slug="themes">themes</TomeLink> a small set of - knob values instead of hundreds of hand-tuned stops. + <MdnLink path="Web/CSS/color-scheme" /> automatically. OKLCH lightness is perceptually uniform (equal + lightness reads equally light in every hue), so rotating a hue knob is safe: contrast and visual + weight hold. </p> <p> - Hues use letters so themes can reassign colors without breaking semantics -- "a" is blue by - default but could be any color. Meaning attaches through the intent knobs layered on top: - <code>--hue_accent</code> (links, focus, selection, selected states -- what other systems call - the "primary" color, named here for what it communicates) defaults to + Hues use letters so themes can reassign colors without breaking semantics ("a" is blue by + default but could be any color). Meaning attaches through the intent knobs layered on top: + <code>--hue_accent</code> (links, focus, selection, selected states - what other systems call + "primary") defaults to <code>--hue_a</code>, <code>--hue_negative</code> to <code>--hue_c</code>, and so on. Retarget an intent to move just that meaning; rotate a letter to move the palette. </p> </section> <TomeSection> <TomeSectionHeader text="Curve knobs" /> - <p>The knobs are the theme API, from highest leverage down:</p> + <p> + The knobs are the <TomeLink slug="themes">theme</TomeLink> API, from highest leverage down: + </p> <ul> <li> - <code>--hue_a</code> … <code>--hue_j</code> -- OKLCH hue angles for the 10 palette slots + <code>--hue_a</code> … <code>--hue_j</code> - OKLCH hue angles for the 10 palette slots </li> <li> - <code>--hue_neutral</code> + <code>--neutral_chroma</code> -- the temperature and strength of - every surface, text, border, and shadow tint (the neutral intent; its scales are the shade and - text ramps) + <code>--hue_neutral</code> + <code>--neutral_chroma</code> - the temperature and strength of every + surface, text, border, and shadow tint (the neutral intent; its scales are the shade and text + ramps) </li> <li> - <code>--chroma_scale</code> -- one multiplier from grayscale (0) through calm (1) to vivid (above - 1, knowingly clipping the weakest hues) + <code>--chroma_scale</code> - one multiplier from grayscale (0) through calm (1) to vivid (above + 1, deliberately clipping the weakest hues) </li> <li> - <code>--hue_shift</code> -- degrees of hue rotation across each ramp for painterly warm-light/cool-shadow + <code>--hue_shift</code> - degrees of hue rotation across each ramp for painterly warm-light/cool-shadow character (default 0) </li> <li> - intent hues -- <code>--hue_accent</code>, <code>--hue_positive</code>, - <code>--hue_negative</code>, <code>--hue_caution</code>, <code>--hue_info</code> -- each + intent hues - <code>--hue_accent</code>, <code>--hue_positive</code>, + <code>--hue_negative</code>, <code>--hue_caution</code>, <code>--hue_info</code> - each deriving a full 13-stop scale (<code>--accent_00</code> … <code>--accent_100</code>) with matching text and background classes (<code>.positive_50</code>, <code>.bg_caution_10</code>) </li> <li> - lightness ramps -- <code>--palette_lightness_00</code>/<code>_100</code>/<code>_curve</code> + lightness ramps - <code>--palette_lightness_00</code>/<code>_100</code>/<code>_curve</code> (and the same trio for <code>shade_</code> and <code>text_</code>): the endpoint stops plus a curve exponent bending the ramp between them, per color scheme </li> <li> - chroma curve -- <code>--palette_chroma_min</code>/<code>_max</code>/<code>_curve</code>: a + chroma curve - <code>--palette_chroma_min</code>/<code>_max</code>/<code>_curve</code>: a mid-peaked curve, clamped per stop by gamut caps computed from the worst hue </li> </ul> <p> Every intermediate value these produce is also its own variable (<code >--palette_lightness_30</code - >, <code>--palette_chroma_50</code>, …), so a theme can pin any individual stop as a surgical - escape hatch -- but the knobs come first. + >, <code>--palette_chroma_50</code>, …), so a theme can pin any individual stop as an escape + hatch. </p> <Code lang="css" @@ -106,13 +107,13 @@ <TomeSectionHeader text="Hue variables" /> <p> Hue variables contain a single OKLCH <MdnLink path="Web/CSS/hue" /> angle. Because lightness and - chroma are shared across all hues at each stop, the scales are interchangeable -- setting a hue - alone is enough, no per-hue tuning required. + chroma are shared across all hues at each stop, the scales are interchangeable: setting a hue alone + is enough, no per-hue tuning required. </p> <p> Hue variables are also useful to construct custom colors not covered by the palette. For example, fuz_css's selection color derives from <code>--hue_accent</code> (try selecting some - text -- + text - <span class="accent_50">same hue!</span>) </p> <p>Hue variables are the same in both light and dark modes (non-adaptive).</p> @@ -140,9 +141,8 @@ </p> <p> Palette stops are adaptive: they switch between light and dark ramps based on color scheme. - For a color that doesn't adapt, write the literal color or define one custom property -- the - old generated absolute variants (<code>color_a_50_light</code>-style) were removed with the - OKLCH migration. + There are no absolute variants; for a color that doesn't adapt, write the literal color or + define one custom property. </p> <ul class="palette unstyled pt_xl2"> {#each palette_variants as letter (letter)} diff --git a/src/routes/docs/forms/+page.svelte b/src/routes/docs/forms/+page.svelte index fff44ca06..31271f411 100644 --- a/src/routes/docs/forms/+page.svelte +++ b/src/routes/docs/forms/+page.svelte @@ -145,7 +145,7 @@ <UnfinishedImplementationWarning >This will change, probably to toggles.</UnfinishedImplementationWarning > - <!-- TODO make this a form, but figure out the checkbox problem -- maybe a last-child exception? --> + <!-- TODO make this a form, but figure out the checkbox problem - maybe a last-child exception? --> <form> <fieldset> <label class="row"> @@ -246,8 +246,7 @@ </div> <UnfinishedImplementationWarning class="mt_lg"> Table cell padding doesn't yet respond to size composites. A size-composite region containing - a table will scale inputs and buttons but leave cells at their default padding. Planned in the - semantic variable chains migration. + a table will scale inputs and buttons but leave cells at their default padding. </UnfinishedImplementationWarning> </TomeSection> </TomeContent> diff --git a/src/routes/docs/introduction/+page.svelte b/src/routes/docs/introduction/+page.svelte index 76322359e..23c18bbfd 100644 --- a/src/routes/docs/introduction/+page.svelte +++ b/src/routes/docs/introduction/+page.svelte @@ -62,7 +62,7 @@ import 'virtual:fuz.css'; // used utilities, \`base_css: null, variables: null\` <TomeSectionHeader text="Details" /> <ul> <li>plain CSS</li> - <li>minimal dependencies, all optional -- none needed if you only use the stylesheets</li> + <li>minimal dependencies, all optional (none needed if you only use the stylesheets)</li> <li> exports a reset stylesheet with <TomeLink slug="semantic" /> defaults that styles HTML elements, and also exports the underlying data, helpers, and types for open-ended usage @@ -70,7 +70,7 @@ import 'virtual:fuz.css'; // used utilities, \`base_css: null, variables: null\` <li> supports <TomeLink slug="themes" /> with a basic theme stylesheet, <SourceFileLink path="theme.css" - />, that can be replaced with your own -- dark mode is a first-class concept, not a theme; + />, that can be replaced with your own - dark mode is a first-class concept, not a theme; instead, each theme can support light and/or dark <MdnLink path="Web/CSS/color-scheme" >color-schemes</MdnLink > @@ -86,7 +86,7 @@ import 'virtual:fuz.css'; // used utilities, \`base_css: null, variables: null\` </li> <li> the stylesheets work with any framework and plain HTML; utility class generation supports - HTML/JS/TS, Svelte, and JSX -- see the utility class + HTML/JS/TS, Svelte, and JSX - see the utility class <TomeLink slug="classes" hash="Framework-support">framework support</TomeLink>, and for the companion Svelte integration see <a href="https://ui.fuz.dev/docs/ThemeRoot"><code>ThemeRoot</code></a> in diff --git a/src/routes/docs/semantic/+page.svelte b/src/routes/docs/semantic/+page.svelte index ee44eb408..3d6f904c1 100644 --- a/src/routes/docs/semantic/+page.svelte +++ b/src/routes/docs/semantic/+page.svelte @@ -19,14 +19,14 @@ <p> fuz_css styles HTML elements in its <SourceFileLink path="style.css" >reset stylesheet</SourceFileLink - >, so semantic markup gets themed and color-scheme-aware styling automatically -- utility + >, so semantic markup gets themed and color-scheme-aware styling automatically - utility classes optional. The goal is to be accessible and attractive out of the box, minimal yet extensible. </p> </section> <TomeSection> - <TomeSectionHeader text="Low specificity" /> + <TomeSectionHeader text="Layers and specificity" /> <p> All opinionated styles live in the <code>fuz.base</code> cascade layer, so your unlayered styles and the generated utility classes override them by layer order alone. The selectors are @@ -114,7 +114,7 @@ <TomeSectionHeader text="Flex containers reset flow margins" /> <p> The <code>.row</code> layout composite resets margins on its direct children. Flow margins - make less sense in horizontal flex layout -- for spacing prefer gap utilities like + make less sense in horizontal flex layout - for spacing prefer gap utilities like <code>.gap_md</code> and <code>var(--gap_sm)</code> instead. </p> <Code diff --git a/src/routes/docs/shading/+page.svelte b/src/routes/docs/shading/+page.svelte index 3b4c3138b..4b02046c2 100644 --- a/src/routes/docs/shading/+page.svelte +++ b/src/routes/docs/shading/+page.svelte @@ -29,9 +29,9 @@ <p> fuz_css offers a shading model built on <em>adaptive</em> style variables that respond to the <MdnLink path="Web/CSS/color-scheme" />. Adaptive means the underlying values change between - light and dark modes to maintain consistent prominence -- low numbers stay subtle, high - numbers stay strong. Each <TomeLink slug="themes">theme</TomeLink> can implement light mode, dark - mode, or both. + light and dark modes to maintain consistent prominence: low numbers stay subtle, high numbers + stay strong. Each <TomeLink slug="themes">theme</TomeLink> can implement light mode, dark mode, + or both. </p> <p> Light mode's starting point is plain white documents (like paper) where we subtract light to @@ -67,8 +67,8 @@ <TomeSectionHeader text="Key values" tag="h4" /> <ul> <li> - <code>shade_min</code>: untinted surface-side extreme -- white in light mode, black in - dark mode; used for input backgrounds + <code>shade_min</code>: untinted surface-side extreme (white in light mode, black in dark + mode); used for input backgrounds </li> <li><code>shade_00</code>: the base background</li> <li><code>shade_05</code>: very subtle: hover states on surface, code backgrounds</li> diff --git a/src/routes/docs/shadows/+page.svelte b/src/routes/docs/shadows/+page.svelte index 51269c7b7..2bfbeb783 100644 --- a/src/routes/docs/shadows/+page.svelte +++ b/src/routes/docs/shadows/+page.svelte @@ -89,7 +89,7 @@ <TomeSectionHeader text="Colored shadows" /> <p> Use <code>shadow_{'{letter}'}_{'{intensity}'}</code> classes to apply colored shadows. The intensity - controls the color's prominence -- 60 is a fine starting point for visible colored shadows. + controls the color's prominence; 60 is a fine starting point for visible colored shadows. </p> {#each palette_variants as color_variant (color_variant)} <TomeSection> diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index c7cc2a4e2..865da604f 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -53,7 +53,7 @@ if (theme.name !== UNSAVED_THEME_NAME) { if ( editor.dirty && - // eslint-disable-next-line no-alert - deliberate guard against silently discarding edits + // eslint-disable-next-line no-alert -- deliberate guard against silently discarding edits !confirm(discard_confirm_message(editor, theme.name)) ) { return; @@ -125,7 +125,7 @@ > <p> A theme is a simple JSON collection of <TomeLink slug="variables" /> that can be transformed into - CSS that set custom properties. Each variable can have values for light and/or dark color schemes. + CSS that sets custom properties. Each variable can have values for light and/or dark color schemes. In other words, "dark" isn't a theme, it's a mode that any theme can implement. </p> <p> @@ -136,9 +136,7 @@ <code>fuz.base</code> defaults, so overrides win regardless of stylesheet order. </p> <p> - These docs are a work in progress, for now see <ModuleLink module_path="theme.ts" /> and <ModuleLink - module_path="themes.ts" - />. + See <ModuleLink module_path="theme.ts" /> and <ModuleLink module_path="themes.ts" /> for the API. </p> <p>Selecting a theme loads its knobs into the editor below.</p> <div class="width_atmost_xs mb_lg"> @@ -149,8 +147,8 @@ <TomeSectionHeader text="Exemplar themes" /> <p> Beyond the registry, fuz_css ships expressive exemplar themes as importable modules under - <code>themes/</code> - registry membership, not file location, is what separates builtins from - exemplars. Import one and pass it to your theme setup: + <code>themes/</code> - registry membership, not file location, is what separates builtins from exemplars. + Import one and pass it to your theme setup: </p> <Code lang="ts" @@ -172,13 +170,8 @@ <p> Drag a knob and the whole page rethemes live - extreme values can make the page hard to read, which is an honest signal, not a bug. Every edit updates a temporary "{UNSAVED_THEME_NAME}" - theme in the picker above; it persists across navigation until you leave or reset, so copy the - <code>Theme</code> object below to keep it. The scheme selector sets the theme's stance - a single-scheme - theme renders its one appearance in both color schemes, so edits write the base slots. The top band - holds the semantic-tier moves: assign each intent (accent, neutral, positive, negative, caution, - info) to a palette letter - or a custom angle - and pull the high-leverage levers. Below it, each - axis section carries the granular knobs, with per-token escape hatches folded away; the ramp strips - repaint live as the derived scales move. + theme in the picker above; it survives navigating away and back, but copy the + <code>Theme</code> object below to keep it. </p> <ThemeEditor {editor} {theme_state} /> </TomeSection> @@ -186,7 +179,7 @@ <TomeSectionHeader text="Validating and compiling themes" /> <p> <ModuleLink module_path="theme_check.ts" /> provides three pure functions for checking a - <code>Theme</code> in tests or CI. + <code>Theme</code>. </p> <p> <code>validate_theme(theme)</code> is the structural lint: unknown variable names are errors, while diff --git a/src/routes/docs/variables/+page.svelte b/src/routes/docs/variables/+page.svelte index 7e027f054..99567af78 100644 --- a/src/routes/docs/variables/+page.svelte +++ b/src/routes/docs/variables/+page.svelte @@ -18,7 +18,7 @@ const variables = default_variables.slice().sort((a, b) => a.name.localeCompare(b.name)); - // TODO maybe FAQ? need a standardized pattern -- first add the "on this page" menu functionality + // TODO maybe FAQ? need a standardized pattern - first add the "on this page" menu functionality </script> <TomeContent {tome}> @@ -58,11 +58,9 @@ <p> Most color variables are <em>derived</em>: curve knobs feed ramp stops, ramp stops feed color stops, all computed in pure CSS (<code>calc()</code>/<code>pow()</code>/<code>oklch()</code>). - A theme is a set of knob values, not a stylesheet -- it usually moves a handful of - high-leverage variables (intent bindings like <code>hue_accent</code>, levers like - <code>chroma_scale</code> and <code>radius_scale</code>, the lightness curve knobs) and - everything downstream re-derives, while any individual variable stays pinnable as the escape - hatch. See <TomeLink slug="colors" /> for the color system and <TomeLink slug="themes" /> for theming. + Scale knobs like <code>radius_scale</code> derive other families the same way, and any + individual variable stays pinnable as the escape hatch. See <TomeLink slug="colors" /> for the color + system and <TomeLink slug="themes" /> for theming. </p> <p> In <TomeLink slug="classes" hash="What-gets-included">bundled mode</TomeLink>, only the From e690ed490228c96cf9c37ee87a904eeb739e71a4 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Fri, 24 Jul 2026 12:18:57 -0400 Subject: [PATCH 25/33] wip --- src/lib/ramps.ts | 4 ++-- src/lib/theme.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/ramps.ts b/src/lib/ramps.ts index deea81408..20bf8522a 100644 --- a/src/lib/ramps.ts +++ b/src/lib/ramps.ts @@ -72,7 +72,7 @@ export interface ChromaRampKnobs { export const PALETTE_HUES: Record<PaletteVariant, number> = { a: 250, // blue b: 144, // green - c: 24, // red + c: 29, // red - the OKLCH hue of #ff0000 (29.23) rounded d: 296, // purple e: 98, // yellow f: 60, // brown @@ -175,7 +175,7 @@ export const PALETTE_CHROMA_CAPS: Record< '80': 0.0812, '90': 0.0465, '95': 0.03, - '100': 0.0141 + '100': 0.0142 } }; diff --git a/src/lib/theme.css b/src/lib/theme.css index 96991390b..be60f58ac 100644 --- a/src/lib/theme.css +++ b/src/lib/theme.css @@ -17,7 +17,7 @@ :root { --hue_a: 250; /* blue */ --hue_b: 144; /* green */ - --hue_c: 24; /* red */ + --hue_c: 29; /* red */ --hue_d: 296; /* purple */ --hue_e: 98; /* yellow */ --hue_f: 60; /* brown */ @@ -1532,7 +1532,7 @@ var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_100) ), - 0.0141 + 0.0142 ); --hue_shift_00: calc(var(--hue_shift) * 0.5); --hue_shift_05: calc(var(--hue_shift) * 0.45); From 659b73aa29cae2152735aebef323ba0f8ca76e25 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Fri, 24 Jul 2026 13:04:59 -0400 Subject: [PATCH 26/33] wip --- .changeset/chroma-character.md | 27 ++ CLAUDE.md | 5 + src/lib/knobs.ts | 29 ++ src/lib/ramps.ts | 60 ++- src/lib/theme.css | 606 +++++++++++++++++++--------- src/lib/theme_check.ts | 119 +++++- src/lib/variables.ts | 100 +++++ src/routes/docs/colors/+page.svelte | 12 +- src/test/knobs.test.ts | 14 +- src/test/ramps.test.ts | 18 + src/test/theme_check.test.ts | 90 +++++ 11 files changed, 853 insertions(+), 227 deletions(-) create mode 100644 .changeset/chroma-character.md diff --git a/.changeset/chroma-character.md b/.changeset/chroma-character.md new file mode 100644 index 000000000..2f80407d4 --- /dev/null +++ b/.changeset/chroma-character.md @@ -0,0 +1,27 @@ +--- +'@fuzdev/fuz_css': minor +--- + +feat: add per-slot chroma multipliers and mute the brown slot + +Every palette letter and intent gains a chroma-character knob: +`--palette_a_chroma_scale` … `--palette_j_chroma_scale` and the intent twins +`--accent_chroma_scale` (same for positive/negative/caution/info), default +`1`. Each multiplies its slot's chroma under the global `--chroma_scale`, so +the slot's character holds at any global setting - grayscale stays grayscale +and vivid scales proportionally. Values at or below 1 stay inside the sRGB +gamut caps by construction; above 1 knowingly clips, like the global knob. +The neutral is unchanged - its character remains `--neutral_chroma`. + +`--palette_f_chroma_scale` ships at `0.55`: brown is low-chroma dark orange, +so no hue angle alone can render it - under uniform chroma the brown slot +read as a second orange beside `--hue_h`. + +An intent hue bound to a palette letter (`--hue_accent: var(--hue_f)`) +shares only the angle. `validate_theme` now warns when the bound letter's +multiplier differs from the intent's `*_chroma_scale` twin, so a slot's +chroma character isn't silently dropped across a binding. `check_theme` runs +its gamut and contrast gates through the multipliers, and an intent folds +into a letter's report entries only when hue and multiplier both match. All +15 knobs are registered variables in the catalog (`knobs.ts`), editable in +the themes docs page's inline editor. diff --git a/CLAUDE.md b/CLAUDE.md index 940be6679..3a91b2b8d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -221,6 +221,11 @@ See [variables.ts](src/lib/variables.ts) for definitions, and `text_`), and the chroma curve (`--palette_chroma_min`/`_max`/`_curve`) clamped per stop by baked worst-hue sRGB gamut caps +- Per-slot chroma character: `--palette_a_chroma_scale`…`_j_` and intent + twins (`--accent_chroma_scale`, …) multiply one slot's chroma under + `--chroma_scale`; the brown slot `f` ships muted at 0.55 (brown is + low-chroma orange). A hue binding shares only the angle — `validate_theme` + warns when a bound letter's multiplier differs from the intent's twin - 13 intensity stops: `palette_a_00` (nearest the background) through `palette_a_100`, with `_50` as the base (steps: 00, 05, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 100) diff --git a/src/lib/knobs.ts b/src/lib/knobs.ts index a8a8b39e0..5ac18265b 100644 --- a/src/lib/knobs.ts +++ b/src/lib/knobs.ts @@ -102,6 +102,18 @@ const hue = ( ...(bindable ? { bindable } : null) }); +// a slot chroma multiplier: [0, 1] is the safe envelope (1 = at the gamut +// caps); direct entry above 1 knowingly clips, like the global chroma_scale +const chroma_multiplier = (name: StyleVariableName, tier: KnobTier): ThemeKnob => ({ + name, + kind: 'number', + axis: 'color', + leverage: 'sm', + tier, + range: [0, 1], + step: 0.01 +}); + const lightness_ramp = (family: string): Array<ThemeKnob> => [ { name: `${family}_lightness_00`, @@ -171,6 +183,12 @@ export const theme_knobs: Array<ThemeKnob> = [ hue('hue_negative', 'md', 'semantic', true), hue('hue_caution', 'md', 'semantic', true), hue('hue_info', 'md', 'semantic', true), + // intent chroma-character twins - pair with a binding to a muted slot + chroma_multiplier('accent_chroma_scale', 'semantic'), + chroma_multiplier('positive_chroma_scale', 'semantic'), + chroma_multiplier('negative_chroma_scale', 'semantic'), + chroma_multiplier('caution_chroma_scale', 'semantic'), + chroma_multiplier('info_chroma_scale', 'semantic'), ...lightness_ramp('palette'), ...lightness_ramp('shade'), ...lightness_ramp('text'), @@ -238,6 +256,17 @@ export const theme_knobs: Array<ThemeKnob> = [ hue('hue_h', 'sm', 'palette'), hue('hue_i', 'sm', 'palette'), hue('hue_j', 'sm', 'palette'), + // per-slot chroma multipliers - a slot's chroma character under chroma_scale + chroma_multiplier('palette_a_chroma_scale', 'palette'), + chroma_multiplier('palette_b_chroma_scale', 'palette'), + chroma_multiplier('palette_c_chroma_scale', 'palette'), + chroma_multiplier('palette_d_chroma_scale', 'palette'), + chroma_multiplier('palette_e_chroma_scale', 'palette'), + chroma_multiplier('palette_f_chroma_scale', 'palette'), + chroma_multiplier('palette_g_chroma_scale', 'palette'), + chroma_multiplier('palette_h_chroma_scale', 'palette'), + chroma_multiplier('palette_i_chroma_scale', 'palette'), + chroma_multiplier('palette_j_chroma_scale', 'palette'), // shape { name: 'radius_scale', diff --git a/src/lib/ramps.ts b/src/lib/ramps.ts index 20bf8522a..2be88c94c 100644 --- a/src/lib/ramps.ts +++ b/src/lib/ramps.ts @@ -24,6 +24,8 @@ import { numeric_scale_variants, + palette_variants, + intent_variants, type PaletteVariant, type ColorSchemeVariant, type NumericScaleVariant @@ -82,6 +84,28 @@ export const PALETTE_HUES: Record<PaletteVariant, number> = { j: 180 // teal }; +/** + * Per-slot chroma multipliers (`--palette_X_chroma_scale`), the palette's + * chroma-character knobs. Each multiplies its slot's chroma under the global + * `--chroma_scale` (multiplicative, so the slot's character is preserved at + * any global setting); values at or below 1 stay inside the worst-hue gamut + * caps by construction, above 1 knowingly clips like the global knob. The + * brown slot ships muted because brown is low-chroma dark orange — no hue + * angle renders it at full palette chroma. + */ +export const PALETTE_CHROMA_MULTIPLIERS: Record<PaletteVariant, number> = { + a: 1, + b: 1, + c: 1, + d: 1, + e: 1, + f: 0.55, // brown - low-chroma orange by definition + g: 1, + h: 1, + i: 1, + j: 1 +}; + /** Fitted lightness knobs for the palette (`palette_X_NN`) ramps. */ export const PALETTE_LIGHTNESS_KNOBS: Record<ColorSchemeVariant, LightnessRampKnobs> = { light: { lightness_00: 0.985, lightness_100: 0.116, curve: 1.15 }, @@ -239,7 +263,7 @@ export const palette_stop_oklch = ( scheme: ColorSchemeVariant ): Oklch => [ ramp_lightness(PALETTE_LIGHTNESS_KNOBS[scheme], stop), - ramp_chroma(scheme, stop), + ramp_chroma(scheme, stop) * PALETTE_CHROMA_MULTIPLIERS[letter], PALETTE_HUES[letter] ]; @@ -379,10 +403,30 @@ export const render_palette_stop_css = ( stop: NumericScaleVariant ): string => render_ramp_color_css(`var(--hue_${letter})`, stop); +// the character twin of a hue slot: palette letters and intents carry a +// chroma multiplier that composes with the global `--chroma_scale`; other +// hue references (e.g. the neutral, whose character is `--neutral_chroma`) +// have none +const slot_chroma_scale_reference = (hue_reference: string): string | null => { + const m = /^var\(--hue_([a-z]+)\)$/u.exec(hue_reference); + if (!m) return null; + const slot = m[1]!; + if ((palette_variants as ReadonlyArray<string>).includes(slot)) { + return `var(--palette_${slot}_chroma_scale, 1)`; + } + if ((intent_variants as ReadonlyArray<string>).includes(slot)) { + return `var(--${slot}_chroma_scale, 1)`; + } + return null; +}; + /** * Renders a color derived from the palette ramps at a stop for an arbitrary * hue reference — the shared template behind palette stops and intent stops - * (`--accent_50` renders with `var(--hue_accent)`). + * (`--accent_50` renders with `var(--hue_accent)`). A palette-letter or + * intent hue reference also picks up its slot's chroma multiplier + * (`--palette_X_chroma_scale`/`--<intent>_chroma_scale`), so the hue slot and + * its chroma character stay paired in the emitted CSS. * * @param hue_reference - a CSS expression for the hue, e.g. `var(--hue_accent)` * @param alpha - optional CSS alpha (e.g. `40%`) appended inside the `oklch()` @@ -391,12 +435,14 @@ export const render_ramp_color_css = ( hue_reference: string, stop: NumericScaleVariant, alpha?: string -): string => - `oklch(var(--palette_lightness_${stop}) calc(var(--palette_chroma_${ +): string => { + const slot_scale = slot_chroma_scale_reference(hue_reference); + return `oklch(var(--palette_lightness_${stop}) calc(var(--palette_chroma_${ stop - }) * var(--chroma_scale)) calc(${hue_reference} + var(--hue_shift_${stop}))${ - alpha ? ` / ${alpha}` : '' - })`; + }) * var(--chroma_scale)${slot_scale ? ` * ${slot_scale}` : ''}) calc(${ + hue_reference + } + var(--hue_shift_${stop}))${alpha ? ` / ${alpha}` : ''})`; +}; /** * Renders the derived default of a neutral (shade/text) stop. The neutral diff --git a/src/lib/theme.css b/src/lib/theme.css index be60f58ac..d2e2e444a 100644 --- a/src/lib/theme.css +++ b/src/lib/theme.css @@ -25,6 +25,16 @@ --hue_h: 41; /* orange */ --hue_i: 210; /* cyan */ --hue_j: 180; /* teal */ + --palette_a_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ + --palette_b_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ + --palette_c_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ + --palette_d_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ + --palette_e_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ + --palette_f_chroma_scale: 0.55; /* mutes the brown slot - brown is low-chroma orange, unreachable by hue alone */ + --palette_g_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ + --palette_h_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ + --palette_i_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ + --palette_j_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ --chroma_scale: 1; /* 0 makes the palette grayscale (the neutral scales have their own knob), above 1 is vivid and can clip past the sRGB gamut caps */ --hue_shift: 0; /* total degrees of hue rotation across each ramp, positive toward the dark end */ --hue_neutral: var(--hue_f); @@ -34,264 +44,334 @@ --hue_negative: var(--hue_c); /* errors, destructive actions */ --hue_caution: var(--hue_h); /* warnings */ --hue_info: var(--hue_i); /* informational callouts */ + --accent_chroma_scale: 1; /* the intent's chroma multiplier; set it when binding the intent to a muted palette slot */ + --positive_chroma_scale: 1; /* the intent's chroma multiplier; set it when binding the intent to a muted palette slot */ + --negative_chroma_scale: 1; /* the intent's chroma multiplier; set it when binding the intent to a muted palette slot */ + --caution_chroma_scale: 1; /* the intent's chroma multiplier; set it when binding the intent to a muted palette slot */ + --info_chroma_scale: 1; /* the intent's chroma multiplier; set it when binding the intent to a muted palette slot */ --accent_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_00)) ); --accent_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_05)) ); --accent_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_10)) ); --accent_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_20)) ); --accent_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_30)) ); --accent_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_40)) ); --accent_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_50)) ); --accent_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_60)) ); --accent_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_70)) ); --accent_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_80)) ); --accent_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_90)) ); --accent_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_95)) ); --accent_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_100)) ); --positive_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_00)) ); --positive_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_05)) ); --positive_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_10)) ); --positive_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_20)) ); --positive_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_30)) ); --positive_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_40)) ); --positive_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_50)) ); --positive_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_60)) ); --positive_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_70)) ); --positive_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_80)) ); --positive_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_90)) ); --positive_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_95)) ); --positive_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) calc(var(--hue_positive) + var(--hue_shift_100)) ); --negative_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_00)) ); --negative_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_05)) ); --negative_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_10)) ); --negative_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_20)) ); --negative_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_30)) ); --negative_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_40)) ); --negative_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_50)) ); --negative_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_60)) ); --negative_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_70)) ); --negative_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_80)) ); --negative_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_90)) ); --negative_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_95)) ); --negative_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) calc(var(--hue_negative) + var(--hue_shift_100)) ); --caution_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_00)) ); --caution_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_05)) ); --caution_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_10)) ); --caution_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_20)) ); --caution_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_30)) ); --caution_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_40)) ); --caution_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_50)) ); --caution_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_60)) ); --caution_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_70)) ); --caution_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_80)) ); --caution_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_90)) ); --caution_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_95)) ); --caution_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) calc(var(--hue_caution) + var(--hue_shift_100)) ); --info_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_00)) ); --info_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_05)) ); --info_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_10)) ); --info_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_20)) ); --info_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_30)) ); --info_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_40)) ); --info_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_50)) ); --info_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_60)) ); --info_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_70)) ); --info_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_80)) ); --info_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_90)) ); --info_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_95)) ); --info_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--info_chroma_scale, 1)) calc(var(--hue_info) + var(--hue_shift_100)) ); --palette_lightness_00: 0.985; @@ -462,523 +542,653 @@ --hue_shift_95: calc(var(--hue_shift) * 0.45); --hue_shift_100: calc(var(--hue_shift) * 0.5); --palette_a_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_00)) ); --palette_a_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_05)) ); --palette_a_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_10)) ); --palette_a_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_20)) ); --palette_a_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_30)) ); --palette_a_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_40)) ); --palette_a_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_50)) ); --palette_a_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_60)) ); --palette_a_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_70)) ); --palette_a_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_80)) ); --palette_a_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_90)) ); --palette_a_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_95)) ); --palette_a_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) calc(var(--hue_a) + var(--hue_shift_100)) ); --palette_b_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_00)) ); --palette_b_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_05)) ); --palette_b_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_10)) ); --palette_b_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_20)) ); --palette_b_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_30)) ); --palette_b_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_40)) ); --palette_b_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_50)) ); --palette_b_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_60)) ); --palette_b_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_70)) ); --palette_b_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_80)) ); --palette_b_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_90)) ); --palette_b_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_95)) ); --palette_b_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) calc(var(--hue_b) + var(--hue_shift_100)) ); --palette_c_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_00)) ); --palette_c_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_05)) ); --palette_c_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_10)) ); --palette_c_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_20)) ); --palette_c_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_30)) ); --palette_c_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_40)) ); --palette_c_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_50)) ); --palette_c_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_60)) ); --palette_c_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_70)) ); --palette_c_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_80)) ); --palette_c_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_90)) ); --palette_c_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_95)) ); --palette_c_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) calc(var(--hue_c) + var(--hue_shift_100)) ); --palette_d_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_00)) ); --palette_d_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_05)) ); --palette_d_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_10)) ); --palette_d_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_20)) ); --palette_d_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_30)) ); --palette_d_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_40)) ); --palette_d_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_50)) ); --palette_d_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_60)) ); --palette_d_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_70)) ); --palette_d_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_80)) ); --palette_d_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_90)) ); --palette_d_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_95)) ); --palette_d_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) calc(var(--hue_d) + var(--hue_shift_100)) ); --palette_e_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_00)) ); --palette_e_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_05)) ); --palette_e_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_10)) ); --palette_e_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_20)) ); --palette_e_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_30)) ); --palette_e_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_40)) ); --palette_e_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_50)) ); --palette_e_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_60)) ); --palette_e_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_70)) ); --palette_e_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_80)) ); --palette_e_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_90)) ); --palette_e_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_95)) ); --palette_e_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) calc(var(--hue_e) + var(--hue_shift_100)) ); --palette_f_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_00)) ); --palette_f_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_05)) ); --palette_f_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_10)) ); --palette_f_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_20)) ); --palette_f_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_30)) ); --palette_f_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_40)) ); --palette_f_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_50)) ); --palette_f_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_60)) ); --palette_f_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_70)) ); --palette_f_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_80)) ); --palette_f_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_90)) ); --palette_f_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_95)) ); --palette_f_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) calc(var(--hue_f) + var(--hue_shift_100)) ); --palette_g_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_00)) ); --palette_g_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_05)) ); --palette_g_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_10)) ); --palette_g_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_20)) ); --palette_g_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_30)) ); --palette_g_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_40)) ); --palette_g_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_50)) ); --palette_g_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_60)) ); --palette_g_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_70)) ); --palette_g_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_80)) ); --palette_g_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_90)) ); --palette_g_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_95)) ); --palette_g_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) calc(var(--hue_g) + var(--hue_shift_100)) ); --palette_h_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_00)) ); --palette_h_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_05)) ); --palette_h_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_10)) ); --palette_h_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_20)) ); --palette_h_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_30)) ); --palette_h_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_40)) ); --palette_h_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_50)) ); --palette_h_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_60)) ); --palette_h_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_70)) ); --palette_h_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_80)) ); --palette_h_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_90)) ); --palette_h_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_95)) ); --palette_h_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) calc(var(--hue_h) + var(--hue_shift_100)) ); --palette_i_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_00)) ); --palette_i_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_05)) ); --palette_i_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_10)) ); --palette_i_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_20)) ); --palette_i_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_30)) ); --palette_i_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_40)) ); --palette_i_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_50)) ); --palette_i_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_60)) ); --palette_i_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_70)) ); --palette_i_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_80)) ); --palette_i_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_90)) ); --palette_i_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_95)) ); --palette_i_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) calc(var(--hue_i) + var(--hue_shift_100)) ); --palette_j_00: oklch( - var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale)) + var(--palette_lightness_00) + calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_00)) ); --palette_j_05: oklch( - var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale)) + var(--palette_lightness_05) + calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_05)) ); --palette_j_10: oklch( - var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale)) + var(--palette_lightness_10) + calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_10)) ); --palette_j_20: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_20)) ); --palette_j_30: oklch( - var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale)) + var(--palette_lightness_30) + calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_30)) ); --palette_j_40: oklch( - var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale)) + var(--palette_lightness_40) + calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_40)) ); --palette_j_50: oklch( - var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale)) + var(--palette_lightness_50) + calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_50)) ); --palette_j_60: oklch( - var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale)) + var(--palette_lightness_60) + calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_60)) ); --palette_j_70: oklch( - var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale)) + var(--palette_lightness_70) + calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_70)) ); --palette_j_80: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_80)) ); --palette_j_90: oklch( - var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale)) + var(--palette_lightness_90) + calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_90)) ); --palette_j_95: oklch( - var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale)) + var(--palette_lightness_95) + calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_95)) ); --palette_j_100: oklch( - var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale)) + var(--palette_lightness_100) + calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) calc(var(--hue_j) + var(--hue_shift_100)) ); --shade_min: #fff; @@ -1282,7 +1492,8 @@ --text_decoration_selected: underline; --link_color_selected: var(--text_color); --selection_color: oklch( - var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale)) + var(--palette_lightness_20) + calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_20)) / 40% ); --scale_factor: 1; /* multiplies the space scale, below 1 is tighter and above 1 is more spacious */ @@ -1595,7 +1806,8 @@ --text_lightness_100: 0.97; --text_lightness_curve: 0.87; --selection_color: oklch( - var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale)) + var(--palette_lightness_80) + calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) calc(var(--hue_accent) + var(--hue_shift_80)) / 40% ); --button_shadow: diff --git a/src/lib/theme_check.ts b/src/lib/theme_check.ts index 116a7501e..ee5105099 100644 --- a/src/lib/theme_check.ts +++ b/src/lib/theme_check.ts @@ -48,6 +48,7 @@ import { default_variables } from './variables.ts'; import { theme_knob_by_name, theme_knob_hook_names, type ThemeKnob } from './knobs.ts'; import { PALETTE_HUES, + PALETTE_CHROMA_MULTIPLIERS, PALETTE_LIGHTNESS_KNOBS, SHADE_LIGHTNESS_KNOBS, TEXT_LIGHTNESS_KNOBS, @@ -186,6 +187,9 @@ const LIGHTNESS_KNOBS_BY_FAMILY: Record< }; const PALETTE_LETTER_MATCHER = /^hue_([a-j])$/u; +const PALETTE_MULTIPLIER_MATCHER = /^palette_([a-j])_chroma_scale$/u; +const INTENT_MULTIPLIER_MATCHER = /^(accent|positive|negative|caution|info)_chroma_scale$/u; +const INTENT_HUE_BINDING_MATCHER = /^var\(--hue_([a-j])\)$/u; const LIGHTNESS_KNOB_MATCHER = /^(palette|shade|text)_lightness_(00|100|curve)$/u; const LIGHTNESS_STOP_MATCHER = /^(palette|shade|text)_lightness_(05|10|20|30|40|50|60|70|80|90|95)$/u; @@ -302,6 +306,15 @@ class ThemeResolver { // intent/neutral hues default to a palette-letter binding const binding = INTENT_HUE_DEFAULT_BINDING[name]; if (binding) return this.#resolve(binding, scheme, visited); + // per-slot chroma multipliers + const multiplier_match = PALETTE_MULTIPLIER_MATCHER.exec(name); + if (multiplier_match) { + return { + ok: true, + value: PALETTE_CHROMA_MULTIPLIERS[multiplier_match[1] as PaletteVariant] + }; + } + if (INTENT_MULTIPLIER_MATCHER.test(name)) return { ok: true, value: 1 }; // scalar knobs if (name === 'chroma_scale') return { ok: true, value: 1 }; if (name === 'hue_shift') return { ok: true, value: 0 }; @@ -488,8 +501,12 @@ const validate_knob_value = ( /** * Lints a theme's structure: a non-empty name, valid `StyleVariable` shape and * known name per variable (errors), and advisory type/range warnings for the - * knob-tier variables. Value validation is advisory and never an error. An - * empty array means the theme is structurally valid. + * knob-tier variables — including a pairing warning when an intent hue binds + * a palette letter whose chroma multiplier differs from the intent's own + * `*_chroma_scale` twin (a binding shares only the hue angle, so the slot's + * chroma character is otherwise silently dropped). Value validation is + * advisory and never an error. An empty array means the theme is structurally + * valid. */ export const validate_theme = (theme: Theme): Array<ThemeIssue> => { const issues: Array<ThemeIssue> = []; @@ -548,6 +565,54 @@ export const validate_theme = (theme: Theme): Array<ThemeIssue> => { issues.push(...validate_knob_value(knob, valid.dark, valid.name, 'dark')); } } + issues.push(...validate_binding_pairing(theme)); + return issues; +}; + +// the pairing lint: an intent hue bound to a palette letter (authored +// `var(--hue_X)` or the default binding) shares only the angle, so warn when +// the letter's chroma multiplier and the intent's twin disagree — the theme +// probably meant the character to follow the binding (the neutral is exempt: +// its character is `--neutral_chroma` by design) +const validate_binding_pairing = (theme: Theme): Array<ThemeIssue> => { + const issues: Array<ThemeIssue> = []; + const resolver = new ThemeResolver(theme); + for (const intent of intent_variants) { + const hue_name = `hue_${intent}`; + const authored = theme.variables.find((v) => v.name === hue_name); + let letter: string | null = null; + if (authored) { + for (const slot of [authored.light, authored.dark]) { + const m = slot === undefined ? null : INTENT_HUE_BINDING_MATCHER.exec(slot.trim()); + if (m) letter = m[1]!; + } + } else { + letter = INTENT_HUE_DEFAULT_BINDING[hue_name]!.slice('hue_'.length); + } + if (!letter) continue; + for (const scheme of color_scheme_variants) { + const letter_multiplier = resolver.resolve(`palette_${letter}_chroma_scale`, scheme); + const intent_multiplier = resolver.resolve(`${intent}_chroma_scale`, scheme); + if ( + letter_multiplier.ok && + intent_multiplier.ok && + Math.abs(letter_multiplier.value - intent_multiplier.value) > 1e-9 + ) { + issues.push({ + level: 'warning', + message: `${hue_name} binds palette letter ${letter} (chroma multiplier ${ + letter_multiplier.value + }) but ${intent}_chroma_scale is ${ + intent_multiplier.value + } — a binding shares only the hue angle, so set ${ + intent + }_chroma_scale to carry the slot's chroma character`, + variable: hue_name + }); + break; + } + } + } return issues; }; @@ -595,11 +660,13 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { return r.ok ? r.value : record(r); }; - // a palette/intent ramp color at a stop for a given hue angle + // a palette/intent ramp color at a stop for a given hue angle and the + // slot's chroma multiplier const ramp_color = ( hue: number, stop: NumericScaleVariant, - scheme: ColorSchemeVariant + scheme: ColorSchemeVariant, + multiplier = 1 ): Oklch | null => { const lightness = num(`palette_lightness_${stop}`, scheme); const chroma_stop = num(`palette_chroma_${stop}`, scheme); @@ -610,7 +677,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { } return [ lightness, - chroma_stop * chroma_scale, + chroma_stop * chroma_scale * multiplier, hue + ramp_hue_shift_offset(stop, scheme, hue_shift) ]; }; @@ -685,23 +752,30 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { for (const scheme of color_scheme_variants) { // gamut: palette letters × 13 stops - const letter_hues: Array<number> = []; + const letter_slots: Array<[hue: number, multiplier: number]> = []; for (const letter of palette_variants) { const hue = num(`hue_${letter}`, scheme); - if (hue === null) continue; - letter_hues.push(hue); + const multiplier = num(`palette_${letter}_chroma_scale`, scheme); + if (hue === null || multiplier === null) continue; + letter_slots.push([hue, multiplier]); for (const stop of numeric_scale_variants) { - const color = ramp_color(hue, stop, scheme); + const color = ramp_color(hue, stop, scheme, multiplier); if (color) push_gamut(`palette_${letter}_${stop}`, color, scheme); } } - // gamut: each intent hue that resolves to a literal angle distinct from all letters + // gamut: each intent that renders differently from every letter — an + // intent folds into a letter's entries only when hue AND multiplier match for (const intent of intent_variants) { const hue = num(`hue_${intent}`, scheme); - if (hue === null) continue; - if (letter_hues.some((h) => Math.abs(h - hue) < 1e-9)) continue; + const multiplier = num(`${intent}_chroma_scale`, scheme); + if (hue === null || multiplier === null) continue; + if ( + letter_slots.some(([h, m]) => Math.abs(h - hue) < 1e-9 && Math.abs(m - multiplier) < 1e-9) + ) { + continue; + } for (const stop of numeric_scale_variants) { - const color = ramp_color(hue, stop, scheme); + const color = ramp_color(hue, stop, scheme, multiplier); if (color) push_gamut(`${intent}_${stop}`, color, scheme); } } @@ -767,8 +841,9 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { // contrast: link — the accent hue at stop 60 on shade_00 (resolved through bindings) const accent_hue = num('hue_accent', scheme); - if (accent_hue !== null && shade_00) { - const link = ramp_color(accent_hue, '60', scheme); + const accent_multiplier = num('accent_chroma_scale', scheme); + if (accent_hue !== null && accent_multiplier !== null && shade_00) { + const link = ramp_color(accent_hue, '60', scheme, accent_multiplier); if (link) { const ratio = contrast(oklch_to_srgb(link), oklch_to_srgb(shade_00)); entries.push({ @@ -785,18 +860,22 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { // contrast: UI affordances — every letter and intent fill at stop 50; // a stance renders its scheme's appearance in both, so text_max follows it const text_max: RgbUnit = (stance ?? scheme) === 'light' ? [0, 0, 0] : [1, 1, 1]; - const fills: Array<[string, number]> = []; + const fills: Array<[string, number, number]> = []; for (const letter of palette_variants) { const hue = num(`hue_${letter}`, scheme); - if (hue !== null) fills.push([`palette_${letter}`, hue]); + const multiplier = num(`palette_${letter}_chroma_scale`, scheme); + if (hue !== null && multiplier !== null) { + fills.push([`palette_${letter}`, hue, multiplier]); + } } for (const intent of intent_variants) { const hue = num(`hue_${intent}`, scheme); - if (hue !== null) fills.push([intent, hue]); + const multiplier = num(`${intent}_chroma_scale`, scheme); + if (hue !== null && multiplier !== null) fills.push([intent, hue, multiplier]); } if (shade_00) { - for (const [label, hue] of fills) { - const fill = ramp_color(hue, '50', scheme); + for (const [label, hue, multiplier] of fills) { + const fill = ramp_color(hue, '50', scheme, multiplier); if (!fill) continue; const fill_rgb = oklch_to_srgb(fill); const ui = contrast(fill_rgb, oklch_to_srgb(shade_00)); diff --git a/src/lib/variables.ts b/src/lib/variables.ts index e5a0187cf..77f0fb73a 100644 --- a/src/lib/variables.ts +++ b/src/lib/variables.ts @@ -3,6 +3,7 @@ import { icon_sizes } from './variable_data.ts'; import { NEUTRAL_CHROMA, PALETTE_CHROMA_KNOBS, + PALETTE_CHROMA_MULTIPLIERS, PALETTE_HUES, PALETTE_LIGHTNESS_KNOBS, SHADE_LIGHTNESS_KNOBS, @@ -98,6 +99,61 @@ export const hue_j: StyleVariable = { summary: 'teal' }; +// per-slot chroma-character knobs - each multiplies its slot's chroma under +// the global chroma_scale, so the slot's character holds at any global +// setting; at or below 1 stays inside the gamut caps, above 1 knowingly clips +const slot_chroma_summary = "the slot's chroma multiplier under the global chroma_scale"; +export const palette_a_chroma_scale: StyleVariable = { + name: 'palette_a_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.a), + summary: slot_chroma_summary +}; +export const palette_b_chroma_scale: StyleVariable = { + name: 'palette_b_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.b), + summary: slot_chroma_summary +}; +export const palette_c_chroma_scale: StyleVariable = { + name: 'palette_c_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.c), + summary: slot_chroma_summary +}; +export const palette_d_chroma_scale: StyleVariable = { + name: 'palette_d_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.d), + summary: slot_chroma_summary +}; +export const palette_e_chroma_scale: StyleVariable = { + name: 'palette_e_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.e), + summary: slot_chroma_summary +}; +export const palette_f_chroma_scale: StyleVariable = { + name: 'palette_f_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.f), + summary: 'mutes the brown slot - brown is low-chroma orange, unreachable by hue alone' +}; +export const palette_g_chroma_scale: StyleVariable = { + name: 'palette_g_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.g), + summary: slot_chroma_summary +}; +export const palette_h_chroma_scale: StyleVariable = { + name: 'palette_h_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.h), + summary: slot_chroma_summary +}; +export const palette_i_chroma_scale: StyleVariable = { + name: 'palette_i_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.i), + summary: slot_chroma_summary +}; +export const palette_j_chroma_scale: StyleVariable = { + name: 'palette_j_chroma_scale', + light: String(PALETTE_CHROMA_MULTIPLIERS.j), + summary: slot_chroma_summary +}; + // global color-character knobs export const chroma_scale: StyleVariable = { name: 'chroma_scale', @@ -163,6 +219,35 @@ export const hue_info: StyleVariable = { light: 'var(--hue_i)', summary: 'informational callouts' }; +// intent chroma-character twins - an intent hue binding shares only the +// angle, so binding an intent to a muted palette slot needs the twin set too +const intent_chroma_summary = + "the intent's chroma multiplier; set it when binding the intent to a muted palette slot"; +export const accent_chroma_scale: StyleVariable = { + name: 'accent_chroma_scale', + light: '1', + summary: intent_chroma_summary +}; +export const positive_chroma_scale: StyleVariable = { + name: 'positive_chroma_scale', + light: '1', + summary: intent_chroma_summary +}; +export const negative_chroma_scale: StyleVariable = { + name: 'negative_chroma_scale', + light: '1', + summary: intent_chroma_summary +}; +export const caution_chroma_scale: StyleVariable = { + name: 'caution_chroma_scale', + light: '1', + summary: intent_chroma_summary +}; +export const info_chroma_scale: StyleVariable = { + name: 'info_chroma_scale', + light: '1', + summary: intent_chroma_summary +}; // accent stops - the full scale derives through the shared ramps export const accent_00: StyleVariable = { name: 'accent_00', @@ -2352,6 +2437,16 @@ export const default_variables: Array<StyleVariable> = [ hue_h, hue_i, hue_j, + palette_a_chroma_scale, + palette_b_chroma_scale, + palette_c_chroma_scale, + palette_d_chroma_scale, + palette_e_chroma_scale, + palette_f_chroma_scale, + palette_g_chroma_scale, + palette_h_chroma_scale, + palette_i_chroma_scale, + palette_j_chroma_scale, chroma_scale, hue_shift, hue_neutral, @@ -2361,6 +2456,11 @@ export const default_variables: Array<StyleVariable> = [ hue_negative, hue_caution, hue_info, + accent_chroma_scale, + positive_chroma_scale, + negative_chroma_scale, + caution_chroma_scale, + info_chroma_scale, accent_00, accent_05, accent_10, diff --git a/src/routes/docs/colors/+page.svelte b/src/routes/docs/colors/+page.svelte index 614f4fa4e..6de43be61 100644 --- a/src/routes/docs/colors/+page.svelte +++ b/src/routes/docs/colors/+page.svelte @@ -86,6 +86,15 @@ chroma curve - <code>--palette_chroma_min</code>/<code>_max</code>/<code>_curve</code>: a mid-peaked curve, clamped per stop by gamut caps computed from the worst hue </li> + <li> + per-slot chroma multipliers - <code>--palette_a_chroma_scale</code> … + <code>--palette_j_chroma_scale</code> and intent twins (<code>--accent_chroma_scale</code>, + …), each multiplying one slot's chroma under the global <code>--chroma_scale</code>. The + brown slot ships muted (<code>--palette_f_chroma_scale: 0.55</code>) because brown is + low-chroma orange, unreachable by hue alone. An intent bound to a muted slot needs its twin + set too - bindings share only the hue angle, and <code>validate_theme</code> warns when the character + would be dropped + </li> </ul> <p> Every intermediate value these produce is also its own variable (<code @@ -108,7 +117,8 @@ <p> Hue variables contain a single OKLCH <MdnLink path="Web/CSS/hue" /> angle. Because lightness and chroma are shared across all hues at each stop, the scales are interchangeable: setting a hue alone - is enough, no per-hue tuning required. + is enough, no per-hue tuning required. The one deliberate exception is the per-slot chroma multiplier + - the brown slot ships muted because no hue angle renders brown at full palette chroma. </p> <p> Hue variables are also useful to construct custom colors not covered by the palette. For diff --git a/src/test/knobs.test.ts b/src/test/knobs.test.ts index fb8904f44..c495b4715 100644 --- a/src/test/knobs.test.ts +++ b/src/test/knobs.test.ts @@ -45,7 +45,7 @@ describe('theme_knobs', () => { } }); - test('palette tier is exactly the letter hues', () => { + test('palette tier is exactly the letter hues and their chroma multipliers', () => { const palette_names = theme_knobs.filter((k) => k.tier === 'palette').map((k) => k.name); assert.deepEqual(palette_names, [ 'hue_a', @@ -57,7 +57,17 @@ describe('theme_knobs', () => { 'hue_g', 'hue_h', 'hue_i', - 'hue_j' + 'hue_j', + 'palette_a_chroma_scale', + 'palette_b_chroma_scale', + 'palette_c_chroma_scale', + 'palette_d_chroma_scale', + 'palette_e_chroma_scale', + 'palette_f_chroma_scale', + 'palette_g_chroma_scale', + 'palette_h_chroma_scale', + 'palette_i_chroma_scale', + 'palette_j_chroma_scale' ]); }); diff --git a/src/test/ramps.test.ts b/src/test/ramps.test.ts index 6a623bff6..651e916de 100644 --- a/src/test/ramps.test.ts +++ b/src/test/ramps.test.ts @@ -3,6 +3,7 @@ import { describe, test, assert } from 'vitest'; import { NEUTRAL_HUE, PALETTE_CHROMA_CAPS, + PALETTE_CHROMA_MULTIPLIERS, PALETTE_HUES, PALETTE_LIGHTNESS_KNOBS, RAMP_STOPS, @@ -10,6 +11,7 @@ import { TEXT_LIGHTNESS_KNOBS, compute_palette_chroma_caps, palette_stop_oklch, + ramp_chroma, ramp_lightness, shade_stop_oklch, text_stop_oklch @@ -92,6 +94,22 @@ describe('monotonicity', () => { }); }); +describe('chroma multipliers', () => { + test('defaults are in (0, 1] and palette_stop_oklch applies them', () => { + for (const letter of palette_variants) { + const multiplier = PALETTE_CHROMA_MULTIPLIERS[letter]; + assert.isAbove(multiplier, 0, `${letter} multiplier must be positive`); + assert.isAtMost(multiplier, 1, `${letter} multiplier above 1 clips past the gamut caps`); + } + // the brown slot ships muted - brown is low-chroma orange + assert.isBelow(PALETTE_CHROMA_MULTIPLIERS.f, 1); + for (const scheme of color_scheme_variants) { + const [, chroma] = palette_stop_oklch('f', '50', scheme); + assert.approximately(chroma, ramp_chroma(scheme, '50') * PALETTE_CHROMA_MULTIPLIERS.f, 1e-12); + } + }); +}); + describe('chroma caps', () => { test('baked caps match the worst-hue gamut math (drift check)', () => { // if the default hues or lightness knobs change without recomputing the diff --git a/src/test/theme_check.test.ts b/src/test/theme_check.test.ts index 8d6e839d1..3c169e4f6 100644 --- a/src/test/theme_check.test.ts +++ b/src/test/theme_check.test.ts @@ -12,6 +12,7 @@ import { terminal_theme, create_terminal_theme } from '$lib/themes/terminal.ts'; import { PALETTE_HUES, PALETTE_CHROMA_KNOBS, + PALETTE_CHROMA_MULTIPLIERS, SHADE_LIGHTNESS_KNOBS, palette_stop_oklch, shade_stop_oklch @@ -119,6 +120,45 @@ describe('validate_theme', () => { [] ); }); + + test('binding an intent to the muted brown slot warns about the dropped character', () => { + const issues = validate_theme({ + name: 't', + variables: [{ name: 'hue_accent', light: 'var(--hue_f)' }] + }); + assert.isTrue(issues.some((i) => i.level === 'warning' && i.variable === 'hue_accent')); + }); + + test('setting the intent chroma twin to match the bound slot silences the pairing warning', () => { + const issues = validate_theme({ + name: 't', + variables: [ + { name: 'hue_accent', light: 'var(--hue_f)' }, + { name: 'accent_chroma_scale', light: String(PALETTE_CHROMA_MULTIPLIERS.f) } + ] + }); + assert.deepEqual(issues, []); + }); + + test('muting a letter warns through the default binding that points at it', () => { + // hue_negative defaults to var(--hue_c) + const issues = validate_theme({ + name: 't', + variables: [{ name: 'palette_c_chroma_scale', light: '0.5' }] + }); + assert.isTrue(issues.some((i) => i.level === 'warning' && i.variable === 'hue_negative')); + }); + + test('a literal intent hue never triggers the pairing warning', () => { + const issues = validate_theme({ + name: 't', + variables: [ + { name: 'hue_negative', light: '20' }, + { name: 'palette_c_chroma_scale', light: '0.5' } + ] + }); + assert.deepEqual(issues, []); + }); }); describe('resolution', () => { @@ -143,6 +183,21 @@ describe('resolution', () => { assert.strictEqual(resolve_theme_knob(theme, 'hue_accent', 'light'), 99); }); + test('chroma multipliers resolve to their defaults and honor pins', () => { + const empty: Theme = { name: 't', variables: [] }; + assert.strictEqual( + resolve_theme_knob(empty, 'palette_f_chroma_scale', 'light'), + PALETTE_CHROMA_MULTIPLIERS.f + ); + assert.strictEqual(resolve_theme_knob(empty, 'palette_a_chroma_scale', 'light'), 1); + assert.strictEqual(resolve_theme_knob(empty, 'accent_chroma_scale', 'light'), 1); + const pinned: Theme = { + name: 't', + variables: [{ name: 'palette_f_chroma_scale', light: '1' }] + }; + assert.strictEqual(resolve_theme_knob(pinned, 'palette_f_chroma_scale', 'light'), 1); + }); + test('self and mutual cycles resolve to null without hanging', () => { const self: Theme = { name: 't', variables: [{ name: 'hue_a', light: 'var(--hue_a)' }] }; assert.strictEqual(resolve_theme_knob(self, 'hue_a', 'light'), null); @@ -257,6 +312,41 @@ describe('check_theme', () => { assert(entry, 'monotonicity entry exists'); assert.isFalse(entry.pass); }); + + test('a chroma multiplier above 1 clips gamut on a low-headroom slot', () => { + // the cyan slot binds the worst-hue caps, so 1.4x pushes past sRGB + const theme: Theme = { + name: 't', + variables: [{ name: 'palette_i_chroma_scale', light: '1.4' }] + }; + const failing = check_theme(theme).entries.filter( + (e) => e.gate === 'gamut' && !e.pass && e.subject.startsWith('palette_i_') + ); + assert.isAbove(failing.length, 0); + }); + + test('an intent folds into a letter only when hue and multiplier both match', () => { + // bound to the muted brown slot with the default twin of 1: same hue, + // different chroma, so the accent gets its own gamut entries + const unpaired: Theme = { + name: 't', + variables: [{ name: 'hue_accent', light: 'var(--hue_f)' }] + }; + assert.isTrue( + check_theme(unpaired).entries.some((e) => e.gate === 'gamut' && e.subject === 'accent_50') + ); + // with the twin carried, the accent renders identically to the letter and folds + const paired: Theme = { + name: 't', + variables: [ + { name: 'hue_accent', light: 'var(--hue_f)' }, + { name: 'accent_chroma_scale', light: String(PALETTE_CHROMA_MULTIPLIERS.f) } + ] + }; + assert.isFalse( + check_theme(paired).entries.some((e) => e.gate === 'gamut' && e.subject === 'accent_50') + ); + }); }); describe('scheme stance', () => { From 9236e194b22586aa4356af94107179e459a9a806 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Wed, 29 Jul 2026 09:10:49 -0400 Subject: [PATCH 27/33] rework --- .changeset/interaction-and-surfaces.md | 2 +- .changeset/oklch-color-system.md | 13 +- .changeset/theme-authoring.md | 20 +- CLAUDE.md | 22 +- src/lib/knobs.ts | 9 - src/lib/ramps.ts | 54 +-- src/lib/theme.css | 472 ++++++++++++------------- src/lib/theme.ts | 37 ++ src/lib/theme_check.ts | 54 +-- src/lib/themes.ts | 33 +- src/lib/themes/necromancer.ts | 8 +- src/lib/themes/sunset_ember.ts | 5 +- src/lib/themes/terminal.ts | 85 ----- src/lib/themes/terminalien.ts | 36 ++ src/lib/variables.ts | 87 ----- src/routes/docs/colors/+page.svelte | 4 - src/routes/docs/themes/+page.svelte | 191 +++++----- src/test/ramps.test.ts | 3 +- src/test/test_helpers.ts | 29 ++ src/test/theme.test.ts | 58 ++- src/test/theme_check.compile.test.ts | 14 +- src/test/theme_check.test.ts | 62 +++- src/test/themes.test.ts | 28 +- 23 files changed, 649 insertions(+), 677 deletions(-) delete mode 100644 src/lib/themes/terminal.ts create mode 100644 src/lib/themes/terminalien.ts diff --git a/.changeset/interaction-and-surfaces.md b/.changeset/interaction-and-surfaces.md index 48a69025f..24f33570c 100644 --- a/.changeset/interaction-and-surfaces.md +++ b/.changeset/interaction-and-surfaces.md @@ -38,7 +38,7 @@ fills: - Selected buttons use `--text_00` (the text ramp endpoint) instead of `--text_05`/`--text_10` for inverse text. Themes bending - `--text_lightness_curve` — the high-contrast registry theme, brutalish, + `--text_lightness_curve` — the high-contrast modifier, brutalish, and the OS `prefers-contrast: more` mapping — dragged the near-background stops toward the fill lightness, washing selected text out (down to ~1.2:1); the endpoints are the knobs themselves, so the curve can never diff --git a/.changeset/oklch-color-system.md b/.changeset/oklch-color-system.md index 44daaaacc..2882ea801 100644 --- a/.changeset/oklch-color-system.md +++ b/.changeset/oklch-color-system.md @@ -26,11 +26,11 @@ from the old HSL palette. Breaking changes: their classes. Write the literal color or define one custom property instead. - **New curve knobs** (the promoted theme API): `--chroma_scale`, - `--hue_shift`, `--palette_lightness_00/_100/_curve` (same trio for + `--palette_lightness_00/_100/_curve` (same trio for `shade_`/`text_`), `--palette_chroma_min/_max/_curve` (clamped per stop by baked worst-hue sRGB gamut caps), plus per-stop derived variables themes can pin individually (`--palette_lightness_NN`, `--palette_chroma_NN`, - `--chroma_shape_NN`, `--hue_shift_NN`). + `--chroma_shape_NN`). - **New semantic intent knobs**: `--hue_accent`, `--hue_positive`, `--hue_negative`, `--hue_caution`, `--hue_info`, each deriving a 13-stop scale through the shared ramps (`--accent_00`…`--accent_100`, etc.) with @@ -50,10 +50,11 @@ from the old HSL palette. Breaking changes: - **`color-mix()` interpolation moved from `in hsl` to `in oklab`** in button fills/borders, composites, and shadow classes. - **Themes**: `themes.ts` exports the curated `default_themes` registry - (base, low contrast, high contrast — the contrast themes are rewritten as - curve-knob overrides), one module per theme under `themes/`, plus - unregistered exemplars (necromancer, sunset ember, brutalish, terminal - green + the `create_terminal_theme(hue)` factory). + (base) and `contrast_modifiers` (low/high contrast, rewritten as + curve-knob overrides — modifiers composed over any theme with + `compose_themes`, not themes in the list), one module per theme under + `themes/`, plus unregistered exemplars (necromancer, sunset ember, + brutalish, terminalien). - **New design-time modules**: `ramps.ts` (fitted knob constants, numeric evaluators, CSS emitters), `oklch.ts` (OKLCH↔sRGB + gamut math), `wcag.ts` (luminance/contrast), with tests gating every default stop for gamut, diff --git a/.changeset/theme-authoring.md b/.changeset/theme-authoring.md index e5035ffa8..531c05a93 100644 --- a/.changeset/theme-authoring.md +++ b/.changeset/theme-authoring.md @@ -27,13 +27,20 @@ New `theme_check.ts`, resolving a theme's authored values back to numbers (the same thresholds the repo's tests assert, exported as the `GATE_*` constants) - `compile_theme` — recomputes per-stop worst-hue chroma caps from a theme's - own hues, lightness ramp, and hue shift, emits `palette_chroma_NN` + own hues and lightness ramp, emits `palette_chroma_NN` overrides where the baked caps no longer fit, and re-checks the result -`ramps.ts` gains `ramp_hue_shift_offset` and `compute_palette_chroma_caps`. -The `'low contrast'` registry theme's shade compression is retuned to the +`ramps.ts` gains `compute_palette_chroma_caps`. +The `'low contrast'` theme's shade compression is retuned to the softest values that pass every `check_theme` contrast gate. +Contrast becomes a modifier, not a theme: `default_themes` shrinks to the +base theme, the contrast pair move to the new `contrast_modifiers` export, +and the new `compose_themes(base, ...overlays)` composes fragments by +flatten + last-wins (a single-scheme base re-slots dual-slot overlay +variables to its stance). The test suite gates every shipped theme × +contrast-modifier combination. + New scheme stance: `Theme` gains `scheme?: 'dual' | 'light' | 'dark'` (default `'dual'`). A single-scheme theme renders its one appearance in both color schemes: `render_theme_style` mirrors every scheme-adaptive default @@ -43,5 +50,8 @@ scope so form controls and native scrollbars agree. Author a stanced theme's own variables single-slot in the light/base position. `validate_theme` warns on dark slots a single-scheme stance makes meaningless; `check_theme`/`compile_theme` resolve through the same mirror so the gates -evaluate the stanced reality in both schemes. The necromancer and terminal -exemplars use the stance instead of hand-mirrored ramp knobs. +evaluate the stanced reality in both schemes. The necromancer and +terminalien exemplars use the stance instead of hand-mirrored ramp knobs. +(Terminalien is the terminal exemplar renamed and densified — compact +`scale_factor` plus tightened line-height pins; the +`create_terminal_theme(hue)` factory is deleted.) diff --git a/CLAUDE.md b/CLAUDE.md index 3a91b2b8d..1b92efca8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -216,7 +216,7 @@ See [variables.ts](src/lib/variables.ts) for definitions, others) with matching text/background token classes (`.positive_50`, `.bg_caution_10`) - Curve knobs drive everything: `--chroma_scale` (0 grayscale → >1 vivid), - `--hue_shift` (degrees of rotation across a ramp), per-scheme lightness + per-scheme lightness ramps (`--palette_lightness_00`/`_100`/`_curve`, same trio for `shade_` and `text_`), and the chroma curve (`--palette_chroma_min`/`_max`/`_curve`) clamped per stop by baked @@ -355,15 +355,19 @@ typography, borders, shading, shadows, layout. See - [theme.ts](src/lib/theme.ts) - Theme rendering, cascade layers, the `scheme` stance (a single-scheme theme mirrors the adaptive defaults and pins `color-scheme`, rendering its one appearance in both schemes), - `ColorScheme` type + `compose_themes` (flatten + last-wins fragment composition — the + hand-flatten precursor to `extends`), `ColorScheme` type - [themes.ts](src/lib/themes.ts) - The curated theme registry -- `src/lib/themes/` - One module per theme. The registry (base, low/high - contrast) is semantic-tier: intent bindings + levers only, palette hues - untouched. Unregistered exemplars: necromancer, sunset ember, brutalish, - and `terminal.ts` (the `terminal_theme` flagship — dark mono terminal chrome - with a green cast but functional palette — plus a `create_terminal_theme(hue)` - factory for pure single-phosphor monochrome terminals); necromancer and - terminal are dark-only via `scheme: 'dark'` + (`default_themes`, semantic-tier policy) plus `contrast_modifiers`: + low/high contrast are modifiers composed over any theme via + `compose_themes`, not themes themselves — users see one flat "themes" + list +- `src/lib/themes/` - One module per theme. Registered: base. Shipped + exemplars: necromancer, sunset ember, brutalish, and `terminalien.ts` + (dense dark mono terminal chrome with a green-phosphor cast but + functional palette — compact `scale_factor` plus tightened line-height + pins); necromancer and terminalien are dark-only via `scheme: 'dark'`. + The contrast pair live here too as the modifier modules - [knobs.ts](src/lib/knobs.ts) - The theme knob catalog: typed metadata (kind/axis/leverage/tier/bindable/range) for the knob-tier variables, joined against `default_variables` by name; includes hook knobs like diff --git a/src/lib/knobs.ts b/src/lib/knobs.ts index 5ac18265b..44c709f30 100644 --- a/src/lib/knobs.ts +++ b/src/lib/knobs.ts @@ -170,15 +170,6 @@ export const theme_knobs: Array<ThemeKnob> = [ range: [0, 2], step: 0.05 }, - { - name: 'hue_shift', - kind: 'number', - axis: 'color', - leverage: 'lg', - tier: 'semantic', - range: [-45, 45], - step: 1 - }, hue('hue_positive', 'md', 'semantic', true), hue('hue_negative', 'md', 'semantic', true), hue('hue_caution', 'md', 'semantic', true), diff --git a/src/lib/ramps.ts b/src/lib/ramps.ts index 2be88c94c..1ff3ef3c0 100644 --- a/src/lib/ramps.ts +++ b/src/lib/ramps.ts @@ -239,21 +239,6 @@ export const ramp_chroma = ( return Math.min(requested, cap) * chroma_scale; }; -/** - * Evaluates a stop's hue-shift offset in degrees — the numeric twin of - * `render_hue_shift_offset_css`. `hue_shift` is the total rotation across a - * ramp, anchored at stop 50; the scheme flip is baked in so positive values - * always rotate hue upward toward the dark end. - */ -export const ramp_hue_shift_offset = ( - stop: NumericScaleVariant, - scheme: ColorSchemeVariant, - hue_shift: number -): number => { - const centered = ramp_stop_t(stop) - 0.5; - return (scheme === 'light' ? centered : -centered) * hue_shift; -}; - /** * Computes the default OKLCH color of a palette stop (`--palette_X_NN`). */ @@ -288,30 +273,25 @@ export const text_stop_oklch = (stop: NumericScaleVariant, scheme: ColorSchemeVa /** * Recomputes the worst-hue safe chroma caps per stop for an arbitrary hue set * and lightness ramp — the generalization of the baked `PALETTE_CHROMA_CAPS`. - * For each stop the lightness comes from `ramp_lightness`, each hue is offset - * by that stop's `ramp_hue_shift_offset`, and the cap is the minimum - * `oklch_max_srgb_chroma` across the hues, floored to 4 decimals to stay - * conservative (the browser clips anything past it). A theme's compile step - * feeds its own hues, lightness knobs, and hue shift to detect where the baked + * For each stop the lightness comes from `ramp_lightness` and the cap is the + * minimum `oklch_max_srgb_chroma` across the hues, floored to 4 decimals to + * stay conservative (the browser clips anything past it). A theme's compile + * step feeds its own hues and lightness knobs to detect where the baked * worst-hue envelope no longer fits. * * @param hues - OKLCH hue angles the ramp must stay in gamut for * @param lightness_knobs - the palette lightness ramp knobs for this scheme - * @param hue_shift - total ramp hue rotation in degrees; defaults to 0 */ export const compute_palette_chroma_caps = ( hues: ReadonlyArray<number>, - lightness_knobs: LightnessRampKnobs, - scheme: ColorSchemeVariant, - hue_shift = 0 + lightness_knobs: LightnessRampKnobs ): Record<NumericScaleVariant, number> => { const caps = {} as Record<NumericScaleVariant, number>; for (const stop of numeric_scale_variants) { const lightness = ramp_lightness(lightness_knobs, stop); let cap = Infinity; for (const hue of hues) { - const effective_hue = hue + ramp_hue_shift_offset(stop, scheme, hue_shift); - cap = Math.min(cap, oklch_max_srgb_chroma(lightness, effective_hue)); + cap = Math.min(cap, oklch_max_srgb_chroma(lightness, hue)); } caps[stop] = Math.floor(cap * 1e4) / 1e4; } @@ -378,22 +358,6 @@ export const render_chroma_stop_css = ( })), ${cap_str})`; }; -/** - * Renders a stop's hue-shift offset. `--hue_shift` is the total rotation in - * degrees across a ramp, anchored at stop 50; the scheme sign flip is baked - * into these per-scheme slots so positive values always rotate hue upward - * toward the dark end. - */ -export const render_hue_shift_offset_css = ( - stop: NumericScaleVariant, - scheme: ColorSchemeVariant -): string => { - const centered = ramp_stop_t(stop) - 0.5; - const factor = Math.round((scheme === 'light' ? centered : -centered) * 1e6) / 1e6; - if (factor === 0) return '0'; - return `calc(var(--hue_shift) * ${format_ramp_number(factor)})`; -}; - /** * Renders the derived default of a palette color stop, e.g. `--palette_a_50`. * One definition serves both schemes — the scheme flip lives in the knobs. @@ -439,9 +403,9 @@ export const render_ramp_color_css = ( const slot_scale = slot_chroma_scale_reference(hue_reference); return `oklch(var(--palette_lightness_${stop}) calc(var(--palette_chroma_${ stop - }) * var(--chroma_scale)${slot_scale ? ` * ${slot_scale}` : ''}) calc(${ + }) * var(--chroma_scale)${slot_scale ? ` * ${slot_scale}` : ''}) ${ hue_reference - } + var(--hue_shift_${stop}))${alpha ? ` / ${alpha}` : ''})`; + }${alpha ? ` / ${alpha}` : ''})`; }; /** @@ -454,4 +418,4 @@ export const render_neutral_stop_css = ( ): string => `oklch(var(--${family}_lightness_${stop}) calc(var(--neutral_chroma) * var(--chroma_shape_${ stop - })) calc(var(--hue_neutral) + var(--hue_shift_${stop})))`; + })) var(--hue_neutral))`; diff --git a/src/lib/theme.css b/src/lib/theme.css index d2e2e444a..615d00837 100644 --- a/src/lib/theme.css +++ b/src/lib/theme.css @@ -36,7 +36,6 @@ --palette_i_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ --palette_j_chroma_scale: 1; /* the slot's chroma multiplier under the global chroma_scale */ --chroma_scale: 1; /* 0 makes the palette grayscale (the neutral scales have their own knob), above 1 is vivid and can clip past the sRGB gamut caps */ - --hue_shift: 0; /* total degrees of hue rotation across each ramp, positive toward the dark end */ --hue_neutral: var(--hue_f); --neutral_chroma: 0.024; /* peak chroma of the neutral scales, applied through the shared chroma shape */ --hue_accent: var(--hue_a); /* links, focus, selection, selected states */ @@ -52,327 +51,327 @@ --accent_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_00)) + var(--hue_accent) ); --accent_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_05)) + var(--hue_accent) ); --accent_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_10)) + var(--hue_accent) ); --accent_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_20)) + var(--hue_accent) ); --accent_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_30)) + var(--hue_accent) ); --accent_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_40)) + var(--hue_accent) ); --accent_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_50)) + var(--hue_accent) ); --accent_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_60)) + var(--hue_accent) ); --accent_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_70)) + var(--hue_accent) ); --accent_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_80)) + var(--hue_accent) ); --accent_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_90)) + var(--hue_accent) ); --accent_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_95)) + var(--hue_accent) ); --accent_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_100)) + var(--hue_accent) ); --positive_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_00)) + var(--hue_positive) ); --positive_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_05)) + var(--hue_positive) ); --positive_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_10)) + var(--hue_positive) ); --positive_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_20)) + var(--hue_positive) ); --positive_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_30)) + var(--hue_positive) ); --positive_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_40)) + var(--hue_positive) ); --positive_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_50)) + var(--hue_positive) ); --positive_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_60)) + var(--hue_positive) ); --positive_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_70)) + var(--hue_positive) ); --positive_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_80)) + var(--hue_positive) ); --positive_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_90)) + var(--hue_positive) ); --positive_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_95)) + var(--hue_positive) ); --positive_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--positive_chroma_scale, 1)) - calc(var(--hue_positive) + var(--hue_shift_100)) + var(--hue_positive) ); --negative_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_00)) + var(--hue_negative) ); --negative_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_05)) + var(--hue_negative) ); --negative_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_10)) + var(--hue_negative) ); --negative_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_20)) + var(--hue_negative) ); --negative_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_30)) + var(--hue_negative) ); --negative_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_40)) + var(--hue_negative) ); --negative_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_50)) + var(--hue_negative) ); --negative_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_60)) + var(--hue_negative) ); --negative_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_70)) + var(--hue_negative) ); --negative_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_80)) + var(--hue_negative) ); --negative_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_90)) + var(--hue_negative) ); --negative_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_95)) + var(--hue_negative) ); --negative_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--negative_chroma_scale, 1)) - calc(var(--hue_negative) + var(--hue_shift_100)) + var(--hue_negative) ); --caution_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_00)) + var(--hue_caution) ); --caution_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_05)) + var(--hue_caution) ); --caution_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_10)) + var(--hue_caution) ); --caution_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_20)) + var(--hue_caution) ); --caution_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_30)) + var(--hue_caution) ); --caution_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_40)) + var(--hue_caution) ); --caution_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_50)) + var(--hue_caution) ); --caution_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_60)) + var(--hue_caution) ); --caution_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_70)) + var(--hue_caution) ); --caution_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_80)) + var(--hue_caution) ); --caution_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_90)) + var(--hue_caution) ); --caution_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_95)) + var(--hue_caution) ); --caution_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--caution_chroma_scale, 1)) - calc(var(--hue_caution) + var(--hue_shift_100)) + var(--hue_caution) ); --info_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_00)) + var(--hue_info) ); --info_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_05)) + var(--hue_info) ); --info_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_10)) + var(--hue_info) ); --info_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_20)) + var(--hue_info) ); --info_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_30)) + var(--hue_info) ); --info_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_40)) + var(--hue_info) ); --info_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_50)) + var(--hue_info) ); --info_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_60)) + var(--hue_info) ); --info_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_70)) + var(--hue_info) ); --info_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_80)) + var(--hue_info) ); --info_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_90)) + var(--hue_info) ); --info_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_95)) + var(--hue_info) ); --info_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--info_chroma_scale, 1)) - calc(var(--hue_info) + var(--hue_shift_100)) + var(--hue_info) ); --palette_lightness_00: 0.985; --palette_lightness_100: 0.116; @@ -528,668 +527,655 @@ ), 0.02 ); - --hue_shift_00: calc(var(--hue_shift) * -0.5); - --hue_shift_05: calc(var(--hue_shift) * -0.45); - --hue_shift_10: calc(var(--hue_shift) * -0.4); - --hue_shift_20: calc(var(--hue_shift) * -0.3); - --hue_shift_30: calc(var(--hue_shift) * -0.2); - --hue_shift_40: calc(var(--hue_shift) * -0.1); - --hue_shift_50: 0; - --hue_shift_60: calc(var(--hue_shift) * 0.1); - --hue_shift_70: calc(var(--hue_shift) * 0.2); - --hue_shift_80: calc(var(--hue_shift) * 0.3); - --hue_shift_90: calc(var(--hue_shift) * 0.4); - --hue_shift_95: calc(var(--hue_shift) * 0.45); - --hue_shift_100: calc(var(--hue_shift) * 0.5); --palette_a_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_00)) + var(--hue_a) ); --palette_a_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_05)) + var(--hue_a) ); --palette_a_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_10)) + var(--hue_a) ); --palette_a_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_20)) + var(--hue_a) ); --palette_a_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_30)) + var(--hue_a) ); --palette_a_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_40)) + var(--hue_a) ); --palette_a_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_50)) + var(--hue_a) ); --palette_a_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_60)) + var(--hue_a) ); --palette_a_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_70)) + var(--hue_a) ); --palette_a_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_80)) + var(--hue_a) ); --palette_a_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_90)) + var(--hue_a) ); --palette_a_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_95)) + var(--hue_a) ); --palette_a_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_a_chroma_scale, 1)) - calc(var(--hue_a) + var(--hue_shift_100)) + var(--hue_a) ); --palette_b_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_00)) + var(--hue_b) ); --palette_b_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_05)) + var(--hue_b) ); --palette_b_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_10)) + var(--hue_b) ); --palette_b_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_20)) + var(--hue_b) ); --palette_b_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_30)) + var(--hue_b) ); --palette_b_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_40)) + var(--hue_b) ); --palette_b_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_50)) + var(--hue_b) ); --palette_b_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_60)) + var(--hue_b) ); --palette_b_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_70)) + var(--hue_b) ); --palette_b_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_80)) + var(--hue_b) ); --palette_b_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_90)) + var(--hue_b) ); --palette_b_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_95)) + var(--hue_b) ); --palette_b_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_b_chroma_scale, 1)) - calc(var(--hue_b) + var(--hue_shift_100)) + var(--hue_b) ); --palette_c_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_00)) + var(--hue_c) ); --palette_c_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_05)) + var(--hue_c) ); --palette_c_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_10)) + var(--hue_c) ); --palette_c_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_20)) + var(--hue_c) ); --palette_c_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_30)) + var(--hue_c) ); --palette_c_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_40)) + var(--hue_c) ); --palette_c_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_50)) + var(--hue_c) ); --palette_c_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_60)) + var(--hue_c) ); --palette_c_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_70)) + var(--hue_c) ); --palette_c_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_80)) + var(--hue_c) ); --palette_c_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_90)) + var(--hue_c) ); --palette_c_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_95)) + var(--hue_c) ); --palette_c_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_c_chroma_scale, 1)) - calc(var(--hue_c) + var(--hue_shift_100)) + var(--hue_c) ); --palette_d_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_00)) + var(--hue_d) ); --palette_d_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_05)) + var(--hue_d) ); --palette_d_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_10)) + var(--hue_d) ); --palette_d_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_20)) + var(--hue_d) ); --palette_d_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_30)) + var(--hue_d) ); --palette_d_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_40)) + var(--hue_d) ); --palette_d_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_50)) + var(--hue_d) ); --palette_d_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_60)) + var(--hue_d) ); --palette_d_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_70)) + var(--hue_d) ); --palette_d_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_80)) + var(--hue_d) ); --palette_d_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_90)) + var(--hue_d) ); --palette_d_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_95)) + var(--hue_d) ); --palette_d_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_d_chroma_scale, 1)) - calc(var(--hue_d) + var(--hue_shift_100)) + var(--hue_d) ); --palette_e_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_00)) + var(--hue_e) ); --palette_e_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_05)) + var(--hue_e) ); --palette_e_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_10)) + var(--hue_e) ); --palette_e_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_20)) + var(--hue_e) ); --palette_e_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_30)) + var(--hue_e) ); --palette_e_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_40)) + var(--hue_e) ); --palette_e_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_50)) + var(--hue_e) ); --palette_e_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_60)) + var(--hue_e) ); --palette_e_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_70)) + var(--hue_e) ); --palette_e_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_80)) + var(--hue_e) ); --palette_e_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_90)) + var(--hue_e) ); --palette_e_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_95)) + var(--hue_e) ); --palette_e_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_e_chroma_scale, 1)) - calc(var(--hue_e) + var(--hue_shift_100)) + var(--hue_e) ); --palette_f_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_00)) + var(--hue_f) ); --palette_f_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_05)) + var(--hue_f) ); --palette_f_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_10)) + var(--hue_f) ); --palette_f_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_20)) + var(--hue_f) ); --palette_f_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_30)) + var(--hue_f) ); --palette_f_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_40)) + var(--hue_f) ); --palette_f_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_50)) + var(--hue_f) ); --palette_f_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_60)) + var(--hue_f) ); --palette_f_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_70)) + var(--hue_f) ); --palette_f_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_80)) + var(--hue_f) ); --palette_f_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_90)) + var(--hue_f) ); --palette_f_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_95)) + var(--hue_f) ); --palette_f_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_f_chroma_scale, 1)) - calc(var(--hue_f) + var(--hue_shift_100)) + var(--hue_f) ); --palette_g_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_00)) + var(--hue_g) ); --palette_g_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_05)) + var(--hue_g) ); --palette_g_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_10)) + var(--hue_g) ); --palette_g_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_20)) + var(--hue_g) ); --palette_g_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_30)) + var(--hue_g) ); --palette_g_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_40)) + var(--hue_g) ); --palette_g_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_50)) + var(--hue_g) ); --palette_g_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_60)) + var(--hue_g) ); --palette_g_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_70)) + var(--hue_g) ); --palette_g_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_80)) + var(--hue_g) ); --palette_g_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_90)) + var(--hue_g) ); --palette_g_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_95)) + var(--hue_g) ); --palette_g_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_g_chroma_scale, 1)) - calc(var(--hue_g) + var(--hue_shift_100)) + var(--hue_g) ); --palette_h_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_00)) + var(--hue_h) ); --palette_h_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_05)) + var(--hue_h) ); --palette_h_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_10)) + var(--hue_h) ); --palette_h_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_20)) + var(--hue_h) ); --palette_h_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_30)) + var(--hue_h) ); --palette_h_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_40)) + var(--hue_h) ); --palette_h_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_50)) + var(--hue_h) ); --palette_h_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_60)) + var(--hue_h) ); --palette_h_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_70)) + var(--hue_h) ); --palette_h_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_80)) + var(--hue_h) ); --palette_h_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_90)) + var(--hue_h) ); --palette_h_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_95)) + var(--hue_h) ); --palette_h_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_h_chroma_scale, 1)) - calc(var(--hue_h) + var(--hue_shift_100)) + var(--hue_h) ); --palette_i_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_00)) + var(--hue_i) ); --palette_i_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_05)) + var(--hue_i) ); --palette_i_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_10)) + var(--hue_i) ); --palette_i_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_20)) + var(--hue_i) ); --palette_i_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_30)) + var(--hue_i) ); --palette_i_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_40)) + var(--hue_i) ); --palette_i_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_50)) + var(--hue_i) ); --palette_i_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_60)) + var(--hue_i) ); --palette_i_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_70)) + var(--hue_i) ); --palette_i_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_80)) + var(--hue_i) ); --palette_i_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_90)) + var(--hue_i) ); --palette_i_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_95)) + var(--hue_i) ); --palette_i_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_i_chroma_scale, 1)) - calc(var(--hue_i) + var(--hue_shift_100)) + var(--hue_i) ); --palette_j_00: oklch( var(--palette_lightness_00) calc(var(--palette_chroma_00) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_00)) + var(--hue_j) ); --palette_j_05: oklch( var(--palette_lightness_05) calc(var(--palette_chroma_05) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_05)) + var(--hue_j) ); --palette_j_10: oklch( var(--palette_lightness_10) calc(var(--palette_chroma_10) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_10)) + var(--hue_j) ); --palette_j_20: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_20)) + var(--hue_j) ); --palette_j_30: oklch( var(--palette_lightness_30) calc(var(--palette_chroma_30) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_30)) + var(--hue_j) ); --palette_j_40: oklch( var(--palette_lightness_40) calc(var(--palette_chroma_40) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_40)) + var(--hue_j) ); --palette_j_50: oklch( var(--palette_lightness_50) calc(var(--palette_chroma_50) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_50)) + var(--hue_j) ); --palette_j_60: oklch( var(--palette_lightness_60) calc(var(--palette_chroma_60) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_60)) + var(--hue_j) ); --palette_j_70: oklch( var(--palette_lightness_70) calc(var(--palette_chroma_70) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_70)) + var(--hue_j) ); --palette_j_80: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_80)) + var(--hue_j) ); --palette_j_90: oklch( var(--palette_lightness_90) calc(var(--palette_chroma_90) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_90)) + var(--hue_j) ); --palette_j_95: oklch( var(--palette_lightness_95) calc(var(--palette_chroma_95) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_95)) + var(--hue_j) ); --palette_j_100: oklch( var(--palette_lightness_100) calc(var(--palette_chroma_100) * var(--chroma_scale) * var(--palette_j_chroma_scale, 1)) - calc(var(--hue_j) + var(--hue_shift_100)) + var(--hue_j) ); --shade_min: #fff; --shade_max: #000; @@ -1242,55 +1228,55 @@ ); --shade_00: oklch( var(--shade_lightness_00) calc(var(--neutral_chroma) * var(--chroma_shape_00)) - calc(var(--hue_neutral) + var(--hue_shift_00)) + var(--hue_neutral) ); --shade_05: oklch( var(--shade_lightness_05) calc(var(--neutral_chroma) * var(--chroma_shape_05)) - calc(var(--hue_neutral) + var(--hue_shift_05)) + var(--hue_neutral) ); --shade_10: oklch( var(--shade_lightness_10) calc(var(--neutral_chroma) * var(--chroma_shape_10)) - calc(var(--hue_neutral) + var(--hue_shift_10)) + var(--hue_neutral) ); --shade_20: oklch( var(--shade_lightness_20) calc(var(--neutral_chroma) * var(--chroma_shape_20)) - calc(var(--hue_neutral) + var(--hue_shift_20)) + var(--hue_neutral) ); --shade_30: oklch( var(--shade_lightness_30) calc(var(--neutral_chroma) * var(--chroma_shape_30)) - calc(var(--hue_neutral) + var(--hue_shift_30)) + var(--hue_neutral) ); --shade_40: oklch( var(--shade_lightness_40) calc(var(--neutral_chroma) * var(--chroma_shape_40)) - calc(var(--hue_neutral) + var(--hue_shift_40)) + var(--hue_neutral) ); --shade_50: oklch( var(--shade_lightness_50) calc(var(--neutral_chroma) * var(--chroma_shape_50)) - calc(var(--hue_neutral) + var(--hue_shift_50)) + var(--hue_neutral) ); --shade_60: oklch( var(--shade_lightness_60) calc(var(--neutral_chroma) * var(--chroma_shape_60)) - calc(var(--hue_neutral) + var(--hue_shift_60)) + var(--hue_neutral) ); --shade_70: oklch( var(--shade_lightness_70) calc(var(--neutral_chroma) * var(--chroma_shape_70)) - calc(var(--hue_neutral) + var(--hue_shift_70)) + var(--hue_neutral) ); --shade_80: oklch( var(--shade_lightness_80) calc(var(--neutral_chroma) * var(--chroma_shape_80)) - calc(var(--hue_neutral) + var(--hue_shift_80)) + var(--hue_neutral) ); --shade_90: oklch( var(--shade_lightness_90) calc(var(--neutral_chroma) * var(--chroma_shape_90)) - calc(var(--hue_neutral) + var(--hue_shift_90)) + var(--hue_neutral) ); --shade_95: oklch( var(--shade_lightness_95) calc(var(--neutral_chroma) * var(--chroma_shape_95)) - calc(var(--hue_neutral) + var(--hue_shift_95)) + var(--hue_neutral) ); --shade_100: oklch( var(--shade_lightness_100) calc(var(--neutral_chroma) * var(--chroma_shape_100)) - calc(var(--hue_neutral) + var(--hue_shift_100)) + var(--hue_neutral) ); --darken_00: #00000000; /* 0% */ --darken_05: #00000008; /* 3% */ @@ -1409,55 +1395,55 @@ ); --text_00: oklch( var(--text_lightness_00) calc(var(--neutral_chroma) * var(--chroma_shape_00)) - calc(var(--hue_neutral) + var(--hue_shift_00)) + var(--hue_neutral) ); --text_05: oklch( var(--text_lightness_05) calc(var(--neutral_chroma) * var(--chroma_shape_05)) - calc(var(--hue_neutral) + var(--hue_shift_05)) + var(--hue_neutral) ); --text_10: oklch( var(--text_lightness_10) calc(var(--neutral_chroma) * var(--chroma_shape_10)) - calc(var(--hue_neutral) + var(--hue_shift_10)) + var(--hue_neutral) ); --text_20: oklch( var(--text_lightness_20) calc(var(--neutral_chroma) * var(--chroma_shape_20)) - calc(var(--hue_neutral) + var(--hue_shift_20)) + var(--hue_neutral) ); --text_30: oklch( var(--text_lightness_30) calc(var(--neutral_chroma) * var(--chroma_shape_30)) - calc(var(--hue_neutral) + var(--hue_shift_30)) + var(--hue_neutral) ); --text_40: oklch( var(--text_lightness_40) calc(var(--neutral_chroma) * var(--chroma_shape_40)) - calc(var(--hue_neutral) + var(--hue_shift_40)) + var(--hue_neutral) ); --text_50: oklch( var(--text_lightness_50) calc(var(--neutral_chroma) * var(--chroma_shape_50)) - calc(var(--hue_neutral) + var(--hue_shift_50)) + var(--hue_neutral) ); --text_60: oklch( var(--text_lightness_60) calc(var(--neutral_chroma) * var(--chroma_shape_60)) - calc(var(--hue_neutral) + var(--hue_shift_60)) + var(--hue_neutral) ); --text_70: oklch( var(--text_lightness_70) calc(var(--neutral_chroma) * var(--chroma_shape_70)) - calc(var(--hue_neutral) + var(--hue_shift_70)) + var(--hue_neutral) ); --text_80: oklch( var(--text_lightness_80) calc(var(--neutral_chroma) * var(--chroma_shape_80)) - calc(var(--hue_neutral) + var(--hue_shift_80)) + var(--hue_neutral) ); --text_90: oklch( var(--text_lightness_90) calc(var(--neutral_chroma) * var(--chroma_shape_90)) - calc(var(--hue_neutral) + var(--hue_shift_90)) + var(--hue_neutral) ); --text_95: oklch( var(--text_lightness_95) calc(var(--neutral_chroma) * var(--chroma_shape_95)) - calc(var(--hue_neutral) + var(--hue_shift_95)) + var(--hue_neutral) ); --text_100: oklch( var(--text_lightness_100) calc(var(--neutral_chroma) * var(--chroma_shape_100)) - calc(var(--hue_neutral) + var(--hue_shift_100)) + var(--hue_neutral) ); --text_disabled: var(--text_50); --background_image: none; @@ -1494,7 +1480,7 @@ --selection_color: oklch( var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_20)) / 40% + var(--hue_accent) / 40% ); --scale_factor: 1; /* multiplies the space scale, below 1 is tighter and above 1 is more spacious */ --space_xs5: calc(0.1rem * var(--scale_factor)); @@ -1745,18 +1731,6 @@ ), 0.0142 ); - --hue_shift_00: calc(var(--hue_shift) * 0.5); - --hue_shift_05: calc(var(--hue_shift) * 0.45); - --hue_shift_10: calc(var(--hue_shift) * 0.4); - --hue_shift_20: calc(var(--hue_shift) * 0.3); - --hue_shift_30: calc(var(--hue_shift) * 0.2); - --hue_shift_40: calc(var(--hue_shift) * 0.1); - --hue_shift_60: calc(var(--hue_shift) * -0.1); - --hue_shift_70: calc(var(--hue_shift) * -0.2); - --hue_shift_80: calc(var(--hue_shift) * -0.3); - --hue_shift_90: calc(var(--hue_shift) * -0.4); - --hue_shift_95: calc(var(--hue_shift) * -0.45); - --hue_shift_100: calc(var(--hue_shift) * -0.5); --shade_min: #000; --shade_max: #fff; --shade_lightness_00: 0.171; @@ -1808,7 +1782,7 @@ --selection_color: oklch( var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) - calc(var(--hue_accent) + var(--hue_shift_80)) / 40% + var(--hue_accent) / 40% ); --button_shadow: var(--shadow_inset_top_xs) diff --git a/src/lib/theme.ts b/src/lib/theme.ts index d18d34ed3..9ecabc924 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -57,6 +57,43 @@ export interface RenderThemeStyleOptions { layer?: string | null; } +/** + * Composes a base theme with overlay fragments by flatten + last-wins: later + * variables replace same-named earlier ones wholesale (both slots). Any + * knob-only theme is already a valid fragment — the contrast modifiers in + * `contrast_modifiers` are the canonical overlays. This is the hand-flatten + * precursor to a first-class `extends`, with the same merge semantics. + * + * The base's `scheme` stance wins; when the base is single-scheme, each + * overlay variable is re-slotted to the stanced scheme's value so a + * dual-slot fragment can't leak the other scheme's appearance past the + * stance. The composed name appends the overlay names so name-keyed pickers + * and renderers treat the composition as its own theme. + */ +export const compose_themes = (base: Theme, ...overlays: Array<Theme>): Theme => { + if (!overlays.length) return base; + const stance = base.scheme === 'light' || base.scheme === 'dark' ? base.scheme : null; + const by_name = new Map<string, StyleVariable>(); + for (const v of base.variables) by_name.set(v.name, v); + for (const overlay of overlays) { + for (const v of overlay.variables) { + if (stance) { + // single-slot in the base position, like stanced themes author their own + const value = stance === 'dark' ? (v.dark ?? v.light) : v.light; + if (value === undefined) continue; + by_name.set(v.name, { name: v.name, light: value }); + } else { + by_name.set(v.name, v); + } + } + } + return { + name: `${base.name} (${overlays.map((o) => o.name).join(', ')})`, + ...(base.scheme !== undefined && { scheme: base.scheme }), + variables: [...by_name.values()] + }; +}; + /** * Computes the mirror a single-scheme stance implies: every scheme-adaptive * default (dual-slot entry in `default_variables`) not overridden by diff --git a/src/lib/theme_check.ts b/src/lib/theme_check.ts index ee5105099..dec1bb9aa 100644 --- a/src/lib/theme_check.ts +++ b/src/lib/theme_check.ts @@ -58,7 +58,6 @@ import { ramp_lightness, ramp_chroma, ramp_chroma_shape, - ramp_hue_shift_offset, compute_palette_chroma_caps, render_chroma_stop_css, type LightnessRampKnobs @@ -317,7 +316,6 @@ class ThemeResolver { if (INTENT_MULTIPLIER_MATCHER.test(name)) return { ok: true, value: 1 }; // scalar knobs if (name === 'chroma_scale') return { ok: true, value: 1 }; - if (name === 'hue_shift') return { ok: true, value: 0 }; if (name === 'neutral_chroma') return { ok: true, value: NEUTRAL_CHROMA[scheme] }; if (name === 'palette_chroma_min') { return { ok: true, value: PALETTE_CHROMA_KNOBS[scheme].chroma_min }; @@ -671,15 +669,10 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { const lightness = num(`palette_lightness_${stop}`, scheme); const chroma_stop = num(`palette_chroma_${stop}`, scheme); const chroma_scale = num('chroma_scale', scheme); - const hue_shift = num('hue_shift', scheme); - if (lightness === null || chroma_stop === null || chroma_scale === null || hue_shift === null) { + if (lightness === null || chroma_stop === null || chroma_scale === null) { return null; } - return [ - lightness, - chroma_stop * chroma_scale * multiplier, - hue + ramp_hue_shift_offset(stop, scheme, hue_shift) - ]; + return [lightness, chroma_stop * chroma_scale * multiplier, hue]; }; // a neutral (shade/text) ramp color at a stop @@ -692,21 +685,10 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { const neutral_c = num('neutral_chroma', scheme); const curve = num('palette_chroma_curve', scheme); const neutral_hue = num('hue_neutral', scheme); - const hue_shift = num('hue_shift', scheme); - if ( - lightness === null || - neutral_c === null || - curve === null || - neutral_hue === null || - hue_shift === null - ) { + if (lightness === null || neutral_c === null || curve === null || neutral_hue === null) { return null; } - return [ - lightness, - neutral_c * ramp_chroma_shape(stop, curve), - neutral_hue + ramp_hue_shift_offset(stop, scheme, hue_shift) - ]; + return [lightness, neutral_c * ramp_chroma_shape(stop, curve), neutral_hue]; }; const contrast = (a: RgbUnit, b: RgbUnit): number => @@ -942,16 +924,6 @@ const collect_hues = (resolver: ThemeResolver, scheme: ColorSchemeVariant): Arra return hues.length ? hues : Object.values(PALETTE_HUES); }; -const resolve_or = ( - resolver: ThemeResolver, - name: string, - scheme: ColorSchemeVariant, - fallback: number -): number => { - const r = resolver.resolve(name, scheme); - return r.ok ? r.value : fallback; -}; - const resolve_lightness_knobs = ( resolver: ThemeResolver, scheme: ColorSchemeVariant @@ -968,11 +940,11 @@ const resolve_lightness_knobs = ( }; /** - * Recomputes a theme's per-stop worst-hue chroma caps from its own hues, - * palette lightness ramp, and hue shift, then emits `palette_chroma_NN` - * overrides wherever the baked caps no longer fit — the fix for a theme - * (rotated hues, a monochrome collapse, a dark-only mirror) whose gamut - * headroom the baked worst-hue table misjudges. + * Recomputes a theme's per-stop worst-hue chroma caps from its own hues and + * palette lightness ramp, then emits `palette_chroma_NN` overrides wherever + * the baked caps no longer fit — the fix for a theme (rotated hues, a + * monochrome collapse, a dark-only mirror) whose gamut headroom the baked + * worst-hue table misjudges. * * A stop is emitted only when either scheme's recomputed cap drifts from the * baked value by more than the emit epsilon and the theme doesn't already pin @@ -988,15 +960,11 @@ export const compile_theme = (theme: Theme): CompiledTheme => { const recomputed: Record<ColorSchemeVariant, Record<NumericScaleVariant, number>> = { light: compute_palette_chroma_caps( collect_hues(resolver, 'light'), - resolve_lightness_knobs(resolver, 'light'), - 'light', - resolve_or(resolver, 'hue_shift', 'light', 0) + resolve_lightness_knobs(resolver, 'light') ), dark: compute_palette_chroma_caps( collect_hues(resolver, 'dark'), - resolve_lightness_knobs(resolver, 'dark'), - 'dark', - resolve_or(resolver, 'hue_shift', 'dark', 0) + resolve_lightness_knobs(resolver, 'dark') ) }; diff --git a/src/lib/themes.ts b/src/lib/themes.ts index a2228a8ca..85a69cf2c 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -9,19 +9,28 @@ export const DEFAULT_THEME: Theme = base_theme; * The curated theme registry that theme pickers enumerate. * * Registry themes are semantic-tier by policy: they move intent bindings - * (which palette letter a meaning points at) and levers (chroma scale, hue - * shift, curves, form knobs) but never the palette hues themselves, so the - * letters stay a stable vocabulary across every registered theme. + * (which palette letter a meaning points at) and levers (chroma scale, + * curves, form knobs) but never the palette hues themselves, so the letters + * stay a stable vocabulary across every registered theme. * * Themes live one module per theme under `themes/` and every module ships as * an importable export — registry membership, not file location, is what - * separates builtins from shipped exemplars. Palette-tier themes (rotations, - * duotone/monochrome collapses like `themes/terminal.ts`) and the expressive - * exemplars (`themes/necromancer.ts`, `themes/sunset_ember.ts`, - * `themes/brutalish.ts`) deliberately stay out of this list. + * separates registered themes from shipped exemplars. Palette-tier themes + * (rotations, duotone/monochrome collapses like `themes/terminalien.ts`) and + * the expressive exemplars (`themes/necromancer.ts`, + * `themes/sunset_ember.ts`, `themes/brutalish.ts`) deliberately stay out of + * this list. + * + * Contrast is not a theme: the low/high contrast pair are modifiers + * (`contrast_modifiers`) composed over any theme with `compose_themes`. + */ +export const default_themes: Array<Theme> = [DEFAULT_THEME]; + +/** + * The contrast modifiers: axis fragments (2 variables each) composed over + * any theme via `compose_themes` rather than picked as themes themselves. + * Composition is flatten + last-wins, so a modifier's variables replace the + * base theme's same-named ones — a base that moves the same knobs + * (`shade_lightness_00`, `text_lightness_curve`) cedes them to the modifier. */ -export const default_themes: Array<Theme> = [ - DEFAULT_THEME, - low_contrast_theme, - high_contrast_theme -]; +export const contrast_modifiers: Array<Theme> = [low_contrast_theme, high_contrast_theme]; diff --git a/src/lib/themes/necromancer.ts b/src/lib/themes/necromancer.ts index faf76226e..0e8c9f3e1 100644 --- a/src/lib/themes/necromancer.ts +++ b/src/lib/themes/necromancer.ts @@ -4,9 +4,9 @@ import type { Theme } from '../theme.ts'; * A fantasy exemplar theme built entirely from semantic bindings and levers — * the palette letters keep their default hues. Ectoplasm-green surfaces come * from binding the neutral to green, the violet accent from binding the accent - * intent to purple, and the lit-from-below character from hue-shifted - * ramps (highlights warm, shadows cool) with glow-colored depth. Dark-only - * via the `scheme` stance, vivid past the gamut caps on purpose. + * intent to purple, and the lit-from-below character from glow-colored + * depth. Dark-only via the `scheme` stance, vivid past the gamut caps on + * purpose. * * Declared subversions: none — negative stays red. * @@ -24,8 +24,6 @@ export const necromancer_theme: Theme = { { name: 'neutral_chroma', light: '0.04' }, // violet accent: links/focus/selection glow purple over the green world { name: 'hue_accent', light: 'var(--hue_d)' }, - // painterly ramps: shadows cool toward teal, highlights warm toward yellow-green - { name: 'hue_shift', light: '14' }, // vivid, knowingly clipping the weak hues { name: 'chroma_scale', light: '1.15' }, // modestly compact - crypt density diff --git a/src/lib/themes/sunset_ember.ts b/src/lib/themes/sunset_ember.ts index 3e72f0f37..b69e4fb18 100644 --- a/src/lib/themes/sunset_ember.ts +++ b/src/lib/themes/sunset_ember.ts @@ -7,8 +7,7 @@ import type { Theme } from '../theme.ts'; * already sits at dusk-warm 60°), the ember accent from binding the accent * intent to orange, and the sunset character from vivid chroma pushed past the * conservative caps on purpose (this theme exists partly to stress weak-hue - * gamut clipping) with hue-shifted ramps so light ends glow gold and dark - * ends sink toward crimson. The gradient sky rides the `background_image` + * gamut clipping). The gradient sky rides the `background_image` * decoration hook: gold high to crimson low by day, indigo sinking to * crimson dusk by night. Dual-scheme. * @@ -23,8 +22,6 @@ export const sunset_ember_theme: Theme = { { name: 'neutral_chroma', light: '0.028', dark: '0.035' }, // vivid — deliberately past the caps; the browser clips, lightness holds { name: 'chroma_scale', light: '1.3', dark: '1.2' }, - // light ends gold, dark ends toward crimson dusk - { name: 'hue_shift', light: '16' }, // gradient sky: kept near the shade_00 lightness so text contrast holds { name: 'background_image', diff --git a/src/lib/themes/terminal.ts b/src/lib/themes/terminal.ts deleted file mode 100644 index 503e1c9e3..000000000 --- a/src/lib/themes/terminal.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { Theme } from '../theme.ts'; - -/** - * A practical terminal theme: the dark, monospace, sharp-cornered, flat-depth - * terminal chrome with a green-phosphor cast in the surfaces and accent, but - * the palette slots keep their own hues so status colors still read — negative - * stays red, caution amber, info cyan. The usable twist on the pure - * single-phosphor collapse of `create_terminal_theme`. Dark-only via the - * `scheme` stance. - */ -export const terminal_theme: Theme = { - name: 'terminal', - scheme: 'dark', - variables: [ - // green-phosphor surfaces and text — the terminal cast, kept low-chroma so - // the palette colors read over it - { name: 'hue_neutral', light: 'var(--hue_b)' }, - { name: 'neutral_chroma', light: '0.05' }, - // links, focus, selection glow phosphor green - { name: 'hue_accent', light: 'var(--hue_b)' }, - // a whisper of phosphor bloom across every ramp - { name: 'hue_shift', light: '12' }, - // mono type everywhere - { name: 'font_family_sans', light: 'var(--font_family_mono)' }, - // sharp: one knob zeroes every radius tier - { name: 'radius_scale', light: '0' }, - // flat: one knob zeroes the whole alpha ramp, button shadows included - { name: 'shadow_alpha_scale', light: '0' } - ] -}; - -/** - * Creates a pure single-phosphor terminal at any OKLCH hue — green at 145, - * amber at 70, cool blue at 250. Every palette slot and the neutral collapse - * onto the one hue (a palette-tier move, which is why terminal themes live - * outside the semantic-only registry); expressiveness comes from the ramps - * doing the work — a hue slant blooms the bright end warm and the dim end cool, - * phosphor-style, so the monochrome reads lit rather than flat. The strict - * monochrome counterpart to `terminal_theme`. Dark-only via the `scheme` - * stance, mono type, sharp corners, no shadows. - * - * Declared subversions: negative/caution/info all render in the terminal hue - * — status legibility is traded for the monochrome premise. - * - * @param hue - the OKLCH hue every slot collapses onto - * @param options - `name` labels the theme; `hue_shift` tunes the phosphor - * bloom, whose ideal warm/cool slant differs by hue, `0` for a dead-flat - * monochrome. - */ -export const create_terminal_theme = ( - hue: number, - options: { name?: string; hue_shift?: number } = {} -): Theme => { - const { name = `terminal ${hue}`, hue_shift = 45 } = options; - const hue_value = String(hue); - return { - name, - scheme: 'dark', - variables: [ - // monochrome hue collapse - { name: 'hue_a', light: hue_value }, - { name: 'hue_b', light: hue_value }, - { name: 'hue_c', light: hue_value }, - { name: 'hue_d', light: hue_value }, - { name: 'hue_e', light: hue_value }, - { name: 'hue_f', light: hue_value }, - { name: 'hue_g', light: hue_value }, - { name: 'hue_h', light: hue_value }, - { name: 'hue_i', light: hue_value }, - { name: 'hue_j', light: hue_value }, - { name: 'hue_neutral', light: hue_value }, - // tinted surfaces and text - { name: 'neutral_chroma', light: '0.05' }, - // phosphor bloom: the ramps warm the bright end and cool the dim end so - // the monochrome isn't a flat fill, staying unmistakably one hue - { name: 'hue_shift', light: String(hue_shift) }, - // mono type everywhere - { name: 'font_family_sans', light: 'var(--font_family_mono)' }, - // sharp: one knob zeroes every radius tier - { name: 'radius_scale', light: '0' }, - // flat: one knob zeroes the whole alpha ramp, button shadows included - { name: 'shadow_alpha_scale', light: '0' } - ] - }; -}; diff --git a/src/lib/themes/terminalien.ts b/src/lib/themes/terminalien.ts new file mode 100644 index 000000000..9b3892051 --- /dev/null +++ b/src/lib/themes/terminalien.ts @@ -0,0 +1,36 @@ +import type { Theme } from '../theme.ts'; + +/** + * A dense terminal theme with an alien cast: dark, monospace, sharp-cornered, + * flat-depth terminal chrome carrying a green-phosphor tint in the surfaces + * and accent, but the palette slots keep their own hues so status colors + * still read — negative stays red, caution amber, info cyan. Compact spacing + * and tightened leading give it terminal density. Dark-only via the `scheme` + * stance. + * + * Declared subversions: none. + */ +export const terminalien_theme: Theme = { + name: 'terminalien', + scheme: 'dark', + variables: [ + // green-phosphor surfaces and text — the terminal cast, kept low-chroma so + // the palette colors read over it + { name: 'hue_neutral', light: 'var(--hue_b)' }, + { name: 'neutral_chroma', light: '0.05' }, + // links, focus, selection glow phosphor green + { name: 'hue_accent', light: 'var(--hue_b)' }, + // mono type everywhere + { name: 'font_family_sans', light: 'var(--font_family_mono)' }, + // sharp: one knob zeroes every radius tier + { name: 'radius_scale', light: '0' }, + // flat: one knob zeroes the whole alpha ramp, button shadows included + { name: 'shadow_alpha_scale', light: '0' }, + // terminal density: compact spacing plus tightened leading (leading is + // deliberately decoupled from scale_factor — these pins are the theme's own) + { name: 'scale_factor', light: '0.85' }, + { name: 'line_height_md', light: '1.4' }, + { name: 'line_height_lg', light: '1.6' }, + { name: 'line_height_xl', light: '2' } + ] +}; diff --git a/src/lib/variables.ts b/src/lib/variables.ts index 77f0fb73a..8f3b82f5b 100644 --- a/src/lib/variables.ts +++ b/src/lib/variables.ts @@ -10,7 +10,6 @@ import { TEXT_LIGHTNESS_KNOBS, render_chroma_shape_css, render_chroma_stop_css, - render_hue_shift_offset_css, render_lightness_stop_css, render_neutral_stop_css, render_palette_stop_css, @@ -161,11 +160,6 @@ export const chroma_scale: StyleVariable = { summary: '0 makes the palette grayscale (the neutral scales have their own knob), above 1 is vivid and can clip past the sRGB gamut caps' }; -export const hue_shift: StyleVariable = { - name: 'hue_shift', - light: '0', - summary: 'total degrees of hue rotation across each ramp, positive toward the dark end' -}; /* @@ -726,73 +720,6 @@ export const palette_chroma_100: StyleVariable = { dark: render_chroma_stop_css('100', 'dark') }; -// per-stop hue-shift offsets - the scheme sign flip is baked into the slots so -// positive --hue_shift always rotates hue upward toward the dark end -export const hue_shift_00: StyleVariable = { - name: 'hue_shift_00', - light: render_hue_shift_offset_css('00', 'light'), - dark: render_hue_shift_offset_css('00', 'dark') -}; -export const hue_shift_05: StyleVariable = { - name: 'hue_shift_05', - light: render_hue_shift_offset_css('05', 'light'), - dark: render_hue_shift_offset_css('05', 'dark') -}; -export const hue_shift_10: StyleVariable = { - name: 'hue_shift_10', - light: render_hue_shift_offset_css('10', 'light'), - dark: render_hue_shift_offset_css('10', 'dark') -}; -export const hue_shift_20: StyleVariable = { - name: 'hue_shift_20', - light: render_hue_shift_offset_css('20', 'light'), - dark: render_hue_shift_offset_css('20', 'dark') -}; -export const hue_shift_30: StyleVariable = { - name: 'hue_shift_30', - light: render_hue_shift_offset_css('30', 'light'), - dark: render_hue_shift_offset_css('30', 'dark') -}; -export const hue_shift_40: StyleVariable = { - name: 'hue_shift_40', - light: render_hue_shift_offset_css('40', 'light'), - dark: render_hue_shift_offset_css('40', 'dark') -}; -export const hue_shift_50: StyleVariable = { - name: 'hue_shift_50', - light: render_hue_shift_offset_css('50', 'light') -}; -export const hue_shift_60: StyleVariable = { - name: 'hue_shift_60', - light: render_hue_shift_offset_css('60', 'light'), - dark: render_hue_shift_offset_css('60', 'dark') -}; -export const hue_shift_70: StyleVariable = { - name: 'hue_shift_70', - light: render_hue_shift_offset_css('70', 'light'), - dark: render_hue_shift_offset_css('70', 'dark') -}; -export const hue_shift_80: StyleVariable = { - name: 'hue_shift_80', - light: render_hue_shift_offset_css('80', 'light'), - dark: render_hue_shift_offset_css('80', 'dark') -}; -export const hue_shift_90: StyleVariable = { - name: 'hue_shift_90', - light: render_hue_shift_offset_css('90', 'light'), - dark: render_hue_shift_offset_css('90', 'dark') -}; -export const hue_shift_95: StyleVariable = { - name: 'hue_shift_95', - light: render_hue_shift_offset_css('95', 'light'), - dark: render_hue_shift_offset_css('95', 'dark') -}; -export const hue_shift_100: StyleVariable = { - name: 'hue_shift_100', - light: render_hue_shift_offset_css('100', 'light'), - dark: render_hue_shift_offset_css('100', 'dark') -}; - // palette color stops - one definition per stop; the scheme flip lives in the knobs export const palette_a_00: StyleVariable = { name: 'palette_a_00', @@ -2448,7 +2375,6 @@ export const default_variables: Array<StyleVariable> = [ palette_i_chroma_scale, palette_j_chroma_scale, chroma_scale, - hue_shift, hue_neutral, neutral_chroma, hue_accent, @@ -2569,19 +2495,6 @@ export const default_variables: Array<StyleVariable> = [ palette_chroma_90, palette_chroma_95, palette_chroma_100, - hue_shift_00, - hue_shift_05, - hue_shift_10, - hue_shift_20, - hue_shift_30, - hue_shift_40, - hue_shift_50, - hue_shift_60, - hue_shift_70, - hue_shift_80, - hue_shift_90, - hue_shift_95, - hue_shift_100, palette_a_00, palette_a_05, palette_a_10, diff --git a/src/routes/docs/colors/+page.svelte b/src/routes/docs/colors/+page.svelte index 6de43be61..c568e86c6 100644 --- a/src/routes/docs/colors/+page.svelte +++ b/src/routes/docs/colors/+page.svelte @@ -66,10 +66,6 @@ <code>--chroma_scale</code> - one multiplier from grayscale (0) through calm (1) to vivid (above 1, deliberately clipping the weakest hues) </li> - <li> - <code>--hue_shift</code> - degrees of hue rotation across each ramp for painterly warm-light/cool-shadow - character (default 0) - </li> <li> intent hues - <code>--hue_accent</code>, <code>--hue_positive</code>, <code>--hue_negative</code>, <code>--hue_caution</code>, <code>--hue_info</code> - each diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index 865da604f..a89e06af0 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -11,12 +11,12 @@ import Code from '@fuzdev/fuz_code/Code.svelte'; import { theme_state_context } from '@fuzdev/fuz_ui/theme_state.svelte.ts'; - import { default_themes } from '$lib/themes.ts'; + import { default_themes, contrast_modifiers } from '$lib/themes.ts'; import { necromancer_theme } from '$lib/themes/necromancer.ts'; import { sunset_ember_theme } from '$lib/themes/sunset_ember.ts'; import { brutalish_theme } from '$lib/themes/brutalish.ts'; - import { terminal_theme } from '$lib/themes/terminal.ts'; - import type { Theme } from '$lib/theme.ts'; + import { terminalien_theme } from '$lib/themes/terminalien.ts'; + import { compose_themes, type Theme } from '$lib/theme.ts'; import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; import ThemeEditor from '$routes/ThemeEditor.svelte'; import { @@ -34,10 +34,22 @@ const get_theme_state = theme_state_context.get(); const theme_state = get_theme_state(); - const themes = default_themes.slice(); - const exemplar_themes = [necromancer_theme, sunset_ember_theme, brutalish_theme, terminal_theme]; + // one gallery: the registry and the shipped exemplars are a single list to + // users — registry membership is policy for consumer pickers, not UX + const themes = [ + ...default_themes, + necromancer_theme, + sunset_ember_theme, + brutalish_theme, + terminalien_theme + ]; - const editor = new ThemeEditorState([...themes, ...exemplar_themes]); + const editor = new ThemeEditorState(themes); + + // the picked base theme, tracked apart from the applied theme so the picker + // highlight survives contrast composition (compositions rename themselves) + let selected_base: Theme = $state.raw(theme_state.theme); + let contrast_modifier: Theme | null = $state.raw(null); // the in-progress theme appears in the picker as soon as a knob moves const picker_themes: Array<Theme> = $derived([ @@ -45,22 +57,36 @@ ...(editor.dirty ? [editor.draft] : []) ]); + const apply_theme = (): void => { + theme_state.theme = contrast_modifier + ? compose_themes(selected_base, contrast_modifier) + : selected_base; + }; + // passed as ThemeInput's `select` (not `onselect`, which collides with the - // DOM handler type in its menu-attribute props): applies the theme like the - // default select and loads it into the editor, with the same dirty-draft - // guard as the editor's "based on" select + // DOM handler type in its menu-attribute props): applies the theme composed + // with the active contrast modifier and loads it into the editor, with the + // same dirty-draft guard as the editor's "based on" select const select_theme = (theme: Theme): void => { - if (theme.name !== UNSAVED_THEME_NAME) { - if ( - editor.dirty && - // eslint-disable-next-line no-alert -- deliberate guard against silently discarding edits - !confirm(discard_confirm_message(editor, theme.name)) - ) { - return; - } - editor.load_theme(theme); + if (theme.name === UNSAVED_THEME_NAME) { + theme_state.theme = theme; + return; } - theme_state.theme = theme; + if ( + editor.dirty && + // eslint-disable-next-line no-alert -- deliberate guard against silently discarding edits + !confirm(discard_confirm_message(editor, theme.name)) + ) { + return; + } + editor.load_theme(theme); + selected_base = theme; + apply_theme(); + }; + + const select_contrast = (name: string): void => { + contrast_modifier = contrast_modifiers.find((m) => m.name === name) ?? null; + apply_theme(); }; // live scope is global with no pin: the draft writes to `:root` through the @@ -81,90 +107,80 @@ </script> <TomeContent {tome}> - <section> + <TomeSection> + <TomeSectionHeader text="Themes" /> + <UnfinishedImplementationWarning + >The theme set is still growing, but the proof of concept is ready!</UnfinishedImplementationWarning + > <p> - fuz_css supports both the browser's - <MdnLink path="Web/CSS/color-scheme" /> - and custom themes based on <TomeLink slug="variables" />, which use - <MdnLink path="Web/CSS/--*">CSS custom properties</MdnLink>. + A theme is a set of <em>knob</em> values, not a stylesheet: a JSON collection of + <TomeLink slug="variables" /> where a handful of high-leverage variables (hue angles, + <code>chroma_scale</code>, the lightness curve knobs - see <TomeLink slug="colors" />) reshape + everything downstream. Each variable can have values for light and/or dark color schemes, so + "dark" isn't a theme, it's a mode that any theme can implement. Selecting a theme applies it + to this whole website and loads its knobs into the editor below. + </p> + <div class="width_atmost_xs mb_lg"> + <ThemeInput + themes={picker_themes} + selected_theme={{ theme: selected_base }} + select={select_theme} + /> + </div> + <label class="width_atmost_xs mb_lg"> + <span class="title">Contrast</span> + <select + value={contrast_modifier?.name ?? ''} + onchange={(e) => select_contrast(e.currentTarget.value)} + > + <option value="">default</option> + {#each contrast_modifiers as modifier (modifier.name)} + <option value={modifier.name}>{modifier.name}</option> + {/each} + </select> + </label> + <p> + Contrast is a <em>modifier</em>, not a theme: the low/high contrast pair compose over any + selected theme with <code>compose_themes</code> (flatten + last-wins), so every theme has low and + high contrast variants for free. + </p> + <p> + Every theme ships as an importable module under <code>themes/</code>, and theme CSS renders + into the <code>fuz.theme</code> cascade layer, above the <code>fuz.base</code> defaults, so + overrides win regardless of stylesheet order. See <ModuleLink module_path="theme.ts" /> and + <ModuleLink module_path="themes.ts" /> for the API: </p> + <Code + lang="ts" + content={`import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts';`} + /> <p> - fuz_css works with any JS framework, but it provides only stylesheets, not integrations. This - website uses the companion Svelte UI library <a href="https://ui.fuz.dev/">fuz_ui</a> - to provide the UI below to control the fuz_css color scheme and themes. + A theme can declare a single-scheme stance with <code>scheme: 'light' | 'dark'</code> - its + one appearance then renders in both color schemes and + <MdnLink path="Web/CSS/color-scheme" /> is pinned to match. The necromancer and terminalien themes + are dark-only this way. </p> - </section> + </TomeSection> <TomeSection> <TomeSectionHeader text="Color scheme" /> <p> fuz_css supports - <MdnLink path="Web/CSS/color-scheme" /> with dark and light modes. To apply dark mode manually, - add the <code>dark</code> class to the root <code>html</code> - element. - </p> - <p> - The Fuz integration detects the default with - <MdnLink path="Web/CSS/@media/prefers-color-scheme" />, and users can also set it directly - with a component like + <MdnLink path="Web/CSS/color-scheme" /> with dark and light modes, detected from + <MdnLink path="Web/CSS/@media/prefers-color-scheme" /> by default. To apply dark mode manually, + add the <code>dark</code> class to the root <code>html</code> element, or use a component like <a href="https://github.com/fuzdev/fuz_ui/blob/main/src/lib/ColorSchemeInput.svelte" >this one</a - >: + > + from the companion Svelte library <a href="https://ui.fuz.dev/">fuz_ui</a>: </p> <div class="display:flex mb_lg"> <ColorSchemeInput /> </div> <p> - The builtin themes support both dark and light color schemes. Custom themes may support one or - both color schemes. + fuz_css itself works with any JS framework - it provides only stylesheets, and themes are + plain data any integration can render. </p> </TomeSection> - <TomeSection> - <TomeSectionHeader text="Builtin themes" /> - <UnfinishedImplementationWarning - >The builtin themes need more work, but the proof of concept is ready!</UnfinishedImplementationWarning - > - <p> - A theme is a simple JSON collection of <TomeLink slug="variables" /> that can be transformed into - CSS that sets custom properties. Each variable can have values for light and/or dark color schemes. - In other words, "dark" isn't a theme, it's a mode that any theme can implement. - </p> - <p> - Because the color system is derived, a theme is a set of <em>knob</em> values, not a - stylesheet: a handful of high-leverage variables (hue angles, <code>chroma_scale</code>, the - lightness curve knobs - see <TomeLink slug="colors" />) reshape everything downstream. Theme - CSS renders into the <code>fuz.theme</code> cascade layer, above the - <code>fuz.base</code> defaults, so overrides win regardless of stylesheet order. - </p> - <p> - See <ModuleLink module_path="theme.ts" /> and <ModuleLink module_path="themes.ts" /> for the API. - </p> - <p>Selecting a theme loads its knobs into the editor below.</p> - <div class="width_atmost_xs mb_lg"> - <ThemeInput themes={picker_themes} select={select_theme} /> - </div> - </TomeSection> - <TomeSection> - <TomeSectionHeader text="Exemplar themes" /> - <p> - Beyond the registry, fuz_css ships expressive exemplar themes as importable modules under - <code>themes/</code> - registry membership, not file location, is what separates builtins from exemplars. - Import one and pass it to your theme setup: - </p> - <Code - lang="ts" - content={`import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts';`} - /> - <p> - A theme can declare a single-scheme stance with <code>scheme: 'light' | 'dark'</code> - the - renderer then mirrors every scheme-adaptive default the theme doesn't override, so its one - appearance renders in both color schemes, and pins - <MdnLink path="Web/CSS/color-scheme" /> to match so form controls and scrollbars agree. The necromancer - and terminal exemplars are dark-only this way, without hand-mirrored knob values. - </p> - <div class="width_atmost_xs mb_lg"> - <ThemeInput themes={exemplar_themes} select={select_theme} /> - </div> - </TomeSection> <TomeSection> <TomeSectionHeader text="Theme editor" /> <p> @@ -208,7 +224,8 @@ test('my theme clears the accessibility gates', () => { <code>{'{theme, report, issues}'}</code>. </p> <p> - fuz_css gates its own registry and exemplar themes with these functions in its test suite. + fuz_css gates its own themes - including every theme × contrast-modifier composition - with + these functions in its test suite. </p> </TomeSection> </TomeContent> diff --git a/src/test/ramps.test.ts b/src/test/ramps.test.ts index 651e916de..c7ec07bc9 100644 --- a/src/test/ramps.test.ts +++ b/src/test/ramps.test.ts @@ -117,8 +117,7 @@ describe('chroma caps', () => { for (const scheme of color_scheme_variants) { const caps = compute_palette_chroma_caps( Object.values(PALETTE_HUES), - PALETTE_LIGHTNESS_KNOBS[scheme], - scheme + PALETTE_LIGHTNESS_KNOBS[scheme] ); for (const stop of RAMP_STOPS) { const baked = PALETTE_CHROMA_CAPS[scheme][stop]; diff --git a/src/test/test_helpers.ts b/src/test/test_helpers.ts index af93de548..f0fb80cc9 100644 --- a/src/test/test_helpers.ts +++ b/src/test/test_helpers.ts @@ -13,6 +13,7 @@ import type { GenerationDiagnostic } from '$lib/diagnostics.ts'; import type { ExtractionData } from '$lib/css_class_extractor.ts'; +import type { Theme } from '$lib/theme.ts'; // // Factory Helpers @@ -27,6 +28,34 @@ export const loc = (file = 'test.ts', line = 1, column = 1): SourceLocation => ( column }); +/** + * Creates a pure single-hue monochrome theme: every palette slot and the + * neutral collapse onto one OKLCH hue, dark-only. The palette-tier stress + * fixture for the resolution/gate/compile paths (rotated hues, dark-only + * stance, recomputed worst-hue caps). + */ +export const create_monochrome_theme = (hue: number): Theme => { + const hue_value = String(hue); + return { + name: `monochrome ${hue}`, + scheme: 'dark', + variables: [ + { name: 'hue_a', light: hue_value }, + { name: 'hue_b', light: hue_value }, + { name: 'hue_c', light: hue_value }, + { name: 'hue_d', light: hue_value }, + { name: 'hue_e', light: hue_value }, + { name: 'hue_f', light: hue_value }, + { name: 'hue_g', light: hue_value }, + { name: 'hue_h', light: hue_value }, + { name: 'hue_i', light: hue_value }, + { name: 'hue_j', light: hue_value }, + { name: 'hue_neutral', light: hue_value }, + { name: 'neutral_chroma', light: '0.05' } + ] + }; +}; + /** * An `ExtractionData` with all fields set to `null`. */ diff --git a/src/test/theme.test.ts b/src/test/theme.test.ts index 6a368645c..f46f553c6 100644 --- a/src/test/theme.test.ts +++ b/src/test/theme.test.ts @@ -1,6 +1,11 @@ import { test, assert, describe } from 'vitest'; -import { render_theme_style, scheme_stance_variables, type Theme } from '$lib/theme.ts'; +import { + compose_themes, + render_theme_style, + scheme_stance_variables, + type Theme +} from '$lib/theme.ts'; import { default_variables } from '$lib/variables.ts'; // a scheme-adaptive default (dual slots) to observe the stance mirror through @@ -93,3 +98,54 @@ describe('scheme stance', () => { } }); }); + +describe('compose_themes', () => { + const base: Theme = { + name: 'my base', + variables: [ + { name: 'chroma_scale', light: '1.2' }, + { name: 'shade_lightness_00', light: '0.95', dark: '0.2' } + ] + }; + const overlay: Theme = { + name: 'my overlay', + variables: [ + { name: 'shade_lightness_00', light: '1', dark: '0' }, + { name: 'text_lightness_curve', light: '0.5' } + ] + }; + + test('no overlays returns the base unchanged', () => { + assert.strictEqual(compose_themes(base), base); + }); + + test('flatten + last-wins: overlay variables replace same-named ones wholesale', () => { + const composed = compose_themes(base, overlay); + const shade = composed.variables.find((v) => v.name === 'shade_lightness_00'); + assert.deepEqual(shade, { name: 'shade_lightness_00', light: '1', dark: '0' }); + // untouched base variables survive + assert.isDefined(composed.variables.find((v) => v.name === 'chroma_scale')); + // overlay-only variables append + assert.isDefined(composed.variables.find((v) => v.name === 'text_lightness_curve')); + assert.strictEqual(composed.variables.length, 3); + }); + + test('the composed name appends the overlay names', () => { + assert.strictEqual(compose_themes(base, overlay).name, 'my base (my overlay)'); + }); + + test('a single-scheme base re-slots dual-slot overlay variables to the stance', () => { + const stanced: Theme = { ...base, scheme: 'dark' }; + const composed = compose_themes(stanced, overlay); + assert.strictEqual(composed.scheme, 'dark'); + // the overlay's dark value lands in the base slot, single-slot + const shade = composed.variables.find((v) => v.name === 'shade_lightness_00'); + assert.deepEqual(shade, { name: 'shade_lightness_00', light: '0' }); + }); + + test('rendering a composition applies the overlay over the base', () => { + const css = render_theme_style(compose_themes(base, overlay)); + assert.include(css, '--shade_lightness_00: 1;'); + assert.include(css, '--chroma_scale: 1.2;'); + }); +}); diff --git a/src/test/theme_check.compile.test.ts b/src/test/theme_check.compile.test.ts index 22c4eab75..a954ee2c9 100644 --- a/src/test/theme_check.compile.test.ts +++ b/src/test/theme_check.compile.test.ts @@ -4,7 +4,7 @@ import { compile_theme } from '$lib/theme_check.ts'; import type { Theme } from '$lib/theme.ts'; import { default_themes } from '$lib/themes.ts'; import { necromancer_theme } from '$lib/themes/necromancer.ts'; -import { create_terminal_theme } from '$lib/themes/terminal.ts'; +import { create_monochrome_theme } from './test_helpers.ts'; import { PALETTE_CHROMA_CAPS } from '$lib/ramps.ts'; import type { NumericScaleVariant } from '$lib/variable_data.ts'; @@ -26,8 +26,8 @@ describe('compile_theme', () => { assert.strictEqual(theme.variables.length, base_theme.variables.length); }); - test('a single-hue terminal theme emits higher mid-stop caps', () => { - const input = create_terminal_theme(145); + test('a single-hue monochrome theme emits higher mid-stop caps', () => { + const input = create_monochrome_theme(145); const { theme, report } = compile_theme(input); const overrides = theme.variables.slice(input.variables.length); assert.isAbove(overrides.length, 0); @@ -40,7 +40,7 @@ describe('compile_theme', () => { }); test('compile does not mutate the input theme', () => { - const input = create_terminal_theme(145); + const input = create_monochrome_theme(145); const before = input.variables.length; compile_theme(input); assert.strictEqual(input.variables.length, before); @@ -60,9 +60,9 @@ describe('compile_theme', () => { test('a pinned palette_chroma_NN is respected — no emission for that stop', () => { const input: Theme = { - name: 'terminal pinned', + name: 'monochrome pinned', variables: [ - ...create_terminal_theme(145).variables, + ...create_monochrome_theme(145).variables, { name: 'palette_chroma_50', light: '0.05' } ] }; @@ -72,6 +72,6 @@ describe('compile_theme', () => { }); test('the compiled theme is fully checkable — nothing unchecked', () => { - assert.strictEqual(compile_theme(create_terminal_theme(145)).report.unchecked.length, 0); + assert.strictEqual(compile_theme(create_monochrome_theme(145)).report.unchecked.length, 0); }); }); diff --git a/src/test/theme_check.test.ts b/src/test/theme_check.test.ts index 3c169e4f6..f5b945ad8 100644 --- a/src/test/theme_check.test.ts +++ b/src/test/theme_check.test.ts @@ -1,14 +1,15 @@ import { test, assert, describe } from 'vitest'; import { validate_theme, check_theme, resolve_theme_knob, GATE_LINK } from '$lib/theme_check.ts'; -import type { Theme } from '$lib/theme.ts'; -import { default_themes } from '$lib/themes.ts'; +import { compose_themes, type Theme } from '$lib/theme.ts'; +import { default_themes, contrast_modifiers } from '$lib/themes.ts'; import { low_contrast_theme } from '$lib/themes/low_contrast.ts'; import { high_contrast_theme } from '$lib/themes/high_contrast.ts'; import { necromancer_theme } from '$lib/themes/necromancer.ts'; import { sunset_ember_theme } from '$lib/themes/sunset_ember.ts'; import { brutalish_theme } from '$lib/themes/brutalish.ts'; -import { terminal_theme, create_terminal_theme } from '$lib/themes/terminal.ts'; +import { terminalien_theme } from '$lib/themes/terminalien.ts'; +import { create_monochrome_theme } from './test_helpers.ts'; import { PALETTE_HUES, PALETTE_CHROMA_KNOBS, @@ -29,8 +30,8 @@ describe('validate_theme', () => { necromancer_theme, sunset_ember_theme, brutalish_theme, - terminal_theme, - create_terminal_theme(70) // amber, exercises the factory + terminalien_theme, + create_monochrome_theme(70) // amber, exercises the palette-tier collapse ]; for (const theme of themes) { const errors = validate_theme(theme).filter((issue) => issue.level === 'error'); @@ -274,8 +275,8 @@ describe('check_theme', () => { assert.isTrue(check_theme(brutalish_theme).ok); }); - test('terminal keeps its contrast gates', () => { - const contrast = check_theme(terminal_theme).entries.filter((e) => e.gate === 'contrast'); + test('terminalien keeps its contrast gates', () => { + const contrast = check_theme(terminalien_theme).entries.filter((e) => e.gate === 'contrast'); assert.isAbove(contrast.length, 0, 'contrast gates resolved'); assert.isTrue(contrast.every((e) => e.pass)); }); @@ -386,7 +387,7 @@ describe('scheme stance', () => { }); test('the dark-stanced exemplars pass their contrast gates in both schemes', () => { - for (const theme of [necromancer_theme, terminal_theme]) { + for (const theme of [necromancer_theme, terminalien_theme]) { const contrast = check_theme(theme).entries.filter((e) => e.gate === 'contrast'); assert.isTrue( contrast.every((e) => e.pass), @@ -395,3 +396,48 @@ describe('scheme stance', () => { } }); }); + +describe('contrast modifier compositions', () => { + const bases = [ + default_themes[0]!, + necromancer_theme, + sunset_ember_theme, + brutalish_theme, + terminalien_theme + ]; + + test('every base × modifier validates with no errors', () => { + for (const base of bases) { + for (const modifier of contrast_modifiers) { + const composed = compose_themes(base, modifier); + const errors = validate_theme(composed).filter((issue) => issue.level === 'error'); + assert.deepEqual(errors, [], `${composed.name}: ${JSON.stringify(errors)}`); + } + } + }); + + // declared exception: sunset ember's past-cap cyan/teal UI fills sit just + // under the 3:1 fill gate against low contrast's raised background floor + // (~2.89–2.91) — a marginal, known combination cost, not a regression + const known_failing = new Set(['sunset ember (low contrast)']); + + test('every base × modifier keeps its contrast gates, minus declared exceptions', () => { + for (const base of bases) { + for (const modifier of contrast_modifiers) { + const composed = compose_themes(base, modifier); + const contrast = check_theme(composed).entries.filter((e) => e.gate === 'contrast'); + assert.isAbove(contrast.length, 0, `${composed.name}: contrast gates resolved`); + const failing = contrast.filter((e) => !e.pass); + if (known_failing.has(composed.name)) { + // the exception stays marginal: only near-threshold fill gates fail + assert.isAbove(failing.length, 0, `${composed.name}: exception no longer needed`); + for (const e of failing) { + assert.isAbove(e.value, e.threshold * 0.9, `${composed.name}: ${e.subject}`); + } + } else { + assert.deepEqual(failing, [], `${composed.name}: ${JSON.stringify(failing)}`); + } + } + } + }); +}); diff --git a/src/test/themes.test.ts b/src/test/themes.test.ts index c3c6b87e6..7cc622efe 100644 --- a/src/test/themes.test.ts +++ b/src/test/themes.test.ts @@ -1,8 +1,7 @@ import { test, assert, describe } from 'vitest'; import type { Theme } from '$lib/theme.ts'; -import { default_themes, DEFAULT_THEME } from '$lib/themes.ts'; -import { create_terminal_theme } from '$lib/themes/terminal.ts'; +import { default_themes, DEFAULT_THEME, contrast_modifiers } from '$lib/themes.ts'; import { StyleVariable } from '$lib/variable.ts'; import { validate_theme } from '$lib/theme_check.ts'; @@ -23,12 +22,12 @@ const shipped_themes: Array<Theme> = Object.values(theme_modules).flatMap((mod) ); const registry_names = new Set(default_themes.map((t) => t.name)); +const modifier_names = new Set(contrast_modifiers.map((t) => t.name)); -/** Shipped exemplar themes that deliberately stay out of the registry. */ -const exemplar_themes = [ - ...shipped_themes.filter((t) => !registry_names.has(t.name)), - create_terminal_theme(70) // amber, exercises the factory -]; +/** Shipped exemplar themes: outside the registry and not contrast modifiers. */ +const exemplar_themes = shipped_themes.filter( + (t) => !registry_names.has(t.name) && !modifier_names.has(t.name) +); describe('default_themes', () => { test('all themes have valid name', () => { @@ -75,8 +74,21 @@ describe('default_themes', () => { test('default_themes contains expected themes', () => { const names = default_themes.map((t) => t.name); assert.include(names, 'base'); + }); + + test('contrast is a modifier, not a registry theme', () => { + const names = contrast_modifiers.map((t) => t.name); assert.include(names, 'low contrast'); assert.include(names, 'high contrast'); + const registry = new Set(default_themes.map((t) => t.name)); + for (const name of names) assert.isFalse(registry.has(name)); + }); + + test('contrast modifiers validate with no errors', () => { + for (const modifier of contrast_modifiers) { + const errors = validate_theme(modifier).filter((issue) => issue.level === 'error'); + assert.deepEqual(errors, [], `Modifier "${modifier.name}" has validation errors`); + } }); test('DEFAULT_THEME has name "base"', () => { @@ -100,7 +112,7 @@ describe('shipped themes', () => { assert.include(names, 'necromancer'); assert.include(names, 'sunset ember'); assert.include(names, 'brutalish'); - assert.include(names, 'terminal'); + assert.include(names, 'terminalien'); }); test('all exemplar variables validate and exist in default_variables', () => { From 566a670d4b9243353ac23d9f5a1ae9f363c620b6 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Wed, 29 Jul 2026 10:40:33 -0400 Subject: [PATCH 28/33] more rework --- .changeset/theme-authoring.md | 15 +- .changeset/theme-plugin-option.md | 26 ++ .changeset/theme-renderer-pure.md | 30 +++ CLAUDE.md | 29 ++- src/lib/bundled_resources.ts | 7 +- src/lib/css_plugin_options.ts | 21 ++ src/lib/gen_fuz_css.ts | 2 + src/lib/scheme_adaptive_variables.gen.ts | 43 ++++ src/lib/scheme_adaptive_variables.ts | 282 ++++++++++++++++++++++ src/lib/theme.gen.css.ts | 19 +- src/lib/theme.ts | 92 ++++--- src/lib/theme_check.ts | 7 +- src/lib/theme_stance.ts | 64 +++++ src/lib/themes/necromancer.ts | 9 +- src/lib/themes/terminalien.ts | 9 +- src/lib/variable_graph.ts | 31 ++- src/lib/vite_plugin_fuz_css.ts | 2 + src/routes/docs/classes/+page.svelte | 4 + src/routes/docs/introduction/+page.svelte | 21 +- src/routes/docs/themes/+page.svelte | 33 +++ src/test/theme.test.ts | 68 ++++-- src/test/variable_graph.test.ts | 72 ++++++ 22 files changed, 780 insertions(+), 106 deletions(-) create mode 100644 .changeset/theme-plugin-option.md create mode 100644 .changeset/theme-renderer-pure.md create mode 100644 src/lib/scheme_adaptive_variables.gen.ts create mode 100644 src/lib/scheme_adaptive_variables.ts create mode 100644 src/lib/theme_stance.ts diff --git a/.changeset/theme-authoring.md b/.changeset/theme-authoring.md index 531c05a93..373a1d3cf 100644 --- a/.changeset/theme-authoring.md +++ b/.changeset/theme-authoring.md @@ -43,14 +43,13 @@ contrast-modifier combination. New scheme stance: `Theme` gains `scheme?: 'dual' | 'light' | 'dark'` (default `'dual'`). A single-scheme theme renders its one appearance in both -color schemes: `render_theme_style` mirrors every scheme-adaptive default -the theme doesn't override (exported as `scheme_stance_variables`, including -the `palette_chroma_NN` gamut-cap stops) and pins `color-scheme` on the -scope so form controls and native scrollbars agree. Author a stanced theme's -own variables single-slot in the light/base position. `validate_theme` warns -on dark slots a single-scheme stance makes meaningless; -`check_theme`/`compile_theme` resolve through the same mirror so the gates -evaluate the stanced reality in both schemes. The necromancer and +color schemes by mirroring every scheme-adaptive default it doesn't override +(including the `palette_chroma_NN` gamut-cap stops) and pinning +`color-scheme` on the scope so form controls and native scrollbars agree. +Author a stanced theme's own variables single-slot in the light/base +position. `validate_theme` warns on dark slots a single-scheme stance makes +meaningless; `check_theme`/`compile_theme` resolve through the same mirror so +the gates evaluate the stanced reality in both schemes. The necromancer and terminalien exemplars use the stance instead of hand-mirrored ramp knobs. (Terminalien is the terminal exemplar renamed and densified — compact `scale_factor` plus tightened line-height pins; the diff --git a/.changeset/theme-plugin-option.md b/.changeset/theme-plugin-option.md new file mode 100644 index 000000000..ae36dd34b --- /dev/null +++ b/.changeset/theme-plugin-option.md @@ -0,0 +1,26 @@ +--- +'@fuzdev/fuz_css': minor +--- + +Add a `theme` option to the Vite plugin and Gro generator for picking a theme +at build time: + +```ts +import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts'; + +vite_plugin_fuz_css({theme: necromancer_theme}); +``` + +The theme overlays the resolved `variables` last-wins by name, so its values +flow through the dependency graph like any other - the variables a theme +references are pulled in transitively and the output stays tree-shaken. A +single-scheme theme's `scheme_mirror` applies first, matching the renderer's +order. + +This is the static counterpart to fuz_ui's `ThemeRoot`: no theme rendering at +runtime and no JavaScript to ship. The two compose, with a runtime theme +overriding the build-time one by cascade layer. Pinning `color-scheme` stays +separate - the `dark`/`light` class on the root element drives it. + +Also exposes `apply_theme_variables` from `variable_graph.ts`, and +`build_variable_graph_from_options` takes an optional theme. diff --git a/.changeset/theme-renderer-pure.md b/.changeset/theme-renderer-pure.md new file mode 100644 index 000000000..f2e7c9748 --- /dev/null +++ b/.changeset/theme-renderer-pure.md @@ -0,0 +1,30 @@ +--- +'@fuzdev/fuz_css': minor +--- + +`theme.ts` is now a pure renderer with no dependency on `variables.ts`, so +mounting a theme no longer pulls the full derived variable set into the +bundle. Bundled minified, `theme.ts` drops from ~38KB to ~1.3KB (~9KB to +~0.7KB gzipped) — the variable data was previously paid by every consumer on +the theme path, including apps on the default theme that never call +`render_theme_style` at all. + +Two breaking changes carry the split: + +- `RenderThemeStyleOptions.empty_default_theme` is removed. It existed to let + an empty theme render the full defaults, which meant the renderer had to + hold them. Pass them instead: + `render_theme_style({name: 'base', variables: default_variables})`. +- `render_theme_style` no longer computes the scheme-stance mirror, and + `scheme_stance_variables` moves from `theme.ts` to the new + `theme_stance.ts`. A single-scheme theme is resolved before rendering with + `resolve_theme_stance`, which stores the mirror on the theme's new + `scheme_mirror` field. The renderer still pins `color-scheme` on its own. + +The shipped stanced exemplars (`themes/necromancer.ts`, +`themes/terminalien.ts`) resolve themselves at module scope, so consumers +importing them get a ready-to-render theme and the mirror data lands in that +theme's chunk. Only hand-rolled stanced themes need the new call. + +`default_variables` is unchanged, so the class-extraction pipeline and +`theme.css` output are unaffected. diff --git a/CLAUDE.md b/CLAUDE.md index 1b92efca8..589794e0a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -110,6 +110,10 @@ combined and only used content is included. In utility-only mode, import - [`render_theme_style()`](src/lib/theme.ts) generates CSS into the `fuz.theme` cascade layer (defaults live in `fuz.base`, generated utility classes in `fuz.utilities`; consumers' unlayered styles beat everything) +- A theme applies either at build time (the generators' `theme` option, baked + into the bundled CSS, no JS shipped) or at runtime (fuz_ui's `ThemeRoot` + renders it to a `<style>` element). They compose — the runtime theme wins + by cascade layer - Color values are derived: curve knobs → ramp stops → color stops, computed in pure CSS (`calc()`/`pow()`/`oklch()`); the fitted knob constants and CSS emitters live in [ramps.ts](src/lib/ramps.ts) with design-time gamut and @@ -309,6 +313,10 @@ Use `GenFuzCssOptions` or `VitePluginFuzCssOptions` to customize: - `base_css` - Custom base styles or callback to modify defaults - `variables` - Custom theme variables or callback to modify defaults +- `theme` - A `Theme` baked into the generated CSS, overlaid onto `variables` + last-wins by name. The static counterpart to fuz_ui's `ThemeRoot`: no + runtime theme rendering, and the output stays tree-shaken because the + overlay happens before the dependency graph is built - `additional_classes` - Classes to always include (for dynamic names) - `additional_elements` - Elements to always include, or `'all'` for all base styles - `additional_variables` - Variables to always include, or `'all'` for all theme vars @@ -352,11 +360,22 @@ typography, borders, shading, shadows, layout. See (design-time + tests; never needed by the shipped CSS, though display tooling like the docs swatches may import the conversions) - [wcag.ts](src/lib/wcag.ts) - WCAG luminance/contrast (design-time + tests) -- [theme.ts](src/lib/theme.ts) - Theme rendering, cascade layers, the - `scheme` stance (a single-scheme theme mirrors the adaptive defaults and - pins `color-scheme`, rendering its one appearance in both schemes), +- [theme.ts](src/lib/theme.ts) - Theme rendering, cascade layers, `compose_themes` (flatten + last-wins fragment composition — the - hand-flatten precursor to `extends`), `ColorScheme` type + hand-flatten precursor to `extends`), `ColorScheme` type. A pure renderer: + it holds no variable data, so mounting a theme costs ~1.3KB minified + instead of ~38KB. It renders what the theme carries and pins + `color-scheme` for a `scheme` stance +- [theme_stance.ts](src/lib/theme_stance.ts) - `resolve_theme_stance`, which + computes a single-scheme theme's `scheme_mirror` (the scheme-adaptive + defaults re-slotted so its one appearance holds in both schemes). Kept out + of `theme.ts` so only consumers of a stanced theme pay for the data; the + shipped stanced exemplars resolve at their own module scope +- [scheme_adaptive_variables.ts](src/lib/scheme_adaptive_variables.ts) - + generated literal twin of the dual-slot subset of `default_variables`, + emitted so the mirror carries no dependency on `variables.ts` (whose + module-init emitter calls defeat tree-shaking — importing one variable + costs ~20KB) - [themes.ts](src/lib/themes.ts) - The curated theme registry (`default_themes`, semantic-tier policy) plus `contrast_modifiers`: low/high contrast are modifiers composed over any theme via @@ -378,6 +397,8 @@ typography, borders, shading, shadows, layout. See a shared string→number resolution core - [theme.gen.css.ts](src/lib/theme.gen.css.ts) - Gro generator that produces `theme.css` +- [scheme_adaptive_variables.gen.ts](src/lib/scheme_adaptive_variables.gen.ts) - + Gro generator that produces `scheme_adaptive_variables.ts` **CSS extraction:** diff --git a/src/lib/bundled_resources.ts b/src/lib/bundled_resources.ts index 0af765ef2..6edf2e3c7 100644 --- a/src/lib/bundled_resources.ts +++ b/src/lib/bundled_resources.ts @@ -23,6 +23,7 @@ import { import { type CssClassVariableIndex, build_class_variable_index } from './class_variable_index.ts'; import type { CssClassDefinition } from './css_class_generation.ts'; import type { BaseCssOption, VariablesOption } from './css_plugin_options.ts'; +import type { Theme } from './theme.ts'; import type { CacheDeps } from './deps.ts'; /** @@ -40,6 +41,8 @@ export interface CreateBundledResourcesOptions { base_css: BaseCssOption; /** Theme variables source. */ variables: VariablesOption; + /** Optional theme baked into the variables, overlaid last-wins by name. */ + theme?: Theme | null; /** Merged class definitions, indexed to their referenced variables. */ class_definitions: Record<string, CssClassDefinition | undefined>; /** Filesystem deps for loading the default `style.css`. */ @@ -54,7 +57,7 @@ export interface CreateBundledResourcesOptions { export const create_bundled_resources = async ( options: CreateBundledResourcesOptions ): Promise<BundledCssResources> => { - const { base_css, variables, class_definitions, deps } = options; + const { base_css, variables, theme, class_definitions, deps } = options; let style_rule_index: StyleRuleIndex; if (typeof base_css === 'string') { @@ -71,7 +74,7 @@ export const create_bundled_resources = async ( return { style_rule_index, - variable_graph: build_variable_graph_from_options(variables), + variable_graph: build_variable_graph_from_options(variables, theme), class_variable_index: build_class_variable_index(class_definitions) }; }; diff --git a/src/lib/css_plugin_options.ts b/src/lib/css_plugin_options.ts index b23de14b7..694d051c7 100644 --- a/src/lib/css_plugin_options.ts +++ b/src/lib/css_plugin_options.ts @@ -32,6 +32,7 @@ import type { FileFilter } from './file_filter.ts'; import type { AcornPlugin } from './css_class_extractor.ts'; import type { CssClassDefinition, CssClassDefinitionInterpreter } from './css_class_generation.ts'; import type { StyleVariable } from './variable.ts'; +import type { Theme } from './theme.ts'; import type { CacheDeps } from './deps.ts'; /** @@ -155,6 +156,26 @@ export interface CssOutputOptions { * ``` */ variables?: VariablesOption; + /** + * A theme to bake into the generated CSS, overlaid onto `variables` + * last-wins by name. This is how a project picks a theme statically: no JS + * theme rendering at runtime, and the output stays tree-shaken because the + * overlay happens before the dependency graph is built, so a theme's + * referenced variables are pulled in transitively. + * + * For runtime switching use fuz_ui's `ThemeRoot`; the two compose, with the + * runtime theme winning by cascade layer. A single-scheme theme needs + * `resolve_theme_stance` first so its `scheme_mirror` applies, and pinning + * `color-scheme` is separate — the `dark`/`light` class on the `html` + * element drives it. + * + * @example + * ```ts + * import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts'; + * vite_plugin_fuz_css({theme: necromancer_theme}); + * ``` + */ + theme?: Theme | null; /** * Classes to always include in the output, regardless of detection. * Useful for dynamically generated class names that can't be statically extracted. diff --git a/src/lib/gen_fuz_css.ts b/src/lib/gen_fuz_css.ts index c620d2d71..0581316c7 100644 --- a/src/lib/gen_fuz_css.ts +++ b/src/lib/gen_fuz_css.ts @@ -113,6 +113,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { acorn_plugins, base_css, variables, + theme, additional_elements, additional_variables, exclude_elements, @@ -151,6 +152,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { bundled_resources = await create_bundled_resources({ base_css, variables, + theme, class_definitions: all_class_definitions, deps }); diff --git a/src/lib/scheme_adaptive_variables.gen.ts b/src/lib/scheme_adaptive_variables.gen.ts new file mode 100644 index 000000000..a13b1729c --- /dev/null +++ b/src/lib/scheme_adaptive_variables.gen.ts @@ -0,0 +1,43 @@ +import type { Gen } from '@fuzdev/gro'; + +import { default_variables } from './variables.ts'; + +/** + * Generates `scheme_adaptive_variables.ts`, the literal twin of the + * scheme-adaptive (dual-slot) subset of `default_variables`. + * + * The subset is emitted as literals rather than imported so `theme_stance.ts` + * carries no dependency on `variables.ts` — the emitter calls there defeat + * tree-shaking, so importing any part of it pulls the whole derived variable + * set into the consumer's bundle. + * + * @nodocs + */ +export const gen: Gen = ({ origin_path }) => { + const adaptive = default_variables.filter((v) => v.dark !== undefined); + + const entries = adaptive + .map( + (v) => + `\t{ name: ${JSON.stringify(v.name)}, light: ${JSON.stringify(v.light)}, dark: ${JSON.stringify(v.dark)} }` + ) + .join(',\n'); + + return `// generated by ${origin_path} -- DO NOT EDIT DIRECTLY! + +import type { StyleVariable } from './variable.ts'; + +/** + * The scheme-adaptive subset of \`default_variables\`: every default that + * carries both a light and a dark value, and therefore changes appearance + * with the color scheme. \`resolve_theme_stance\` mirrors these so a + * single-scheme theme shows its one appearance in both schemes. + * + * Literal twin of \`variables.ts\`, generated so consumers of the stance + * mirror don't pull the full derived variable set. + */ +export const scheme_adaptive_variables: Array<StyleVariable> = [ +${entries} +]; +`; +}; diff --git a/src/lib/scheme_adaptive_variables.ts b/src/lib/scheme_adaptive_variables.ts new file mode 100644 index 000000000..8710eb7fa --- /dev/null +++ b/src/lib/scheme_adaptive_variables.ts @@ -0,0 +1,282 @@ +// generated by src/lib/scheme_adaptive_variables.gen.ts -- DO NOT EDIT DIRECTLY! + +import type { StyleVariable } from './variable.ts'; + +/** + * The scheme-adaptive subset of `default_variables`: every default that + * carries both a light and a dark value, and therefore changes appearance + * with the color scheme. `resolve_theme_stance` mirrors these so a + * single-scheme theme shows its one appearance in both schemes. + * + * Literal twin of `variables.ts`, generated so consumers of the stance + * mirror don't pull the full derived variable set. + */ +export const scheme_adaptive_variables: Array<StyleVariable> = [ + { name: 'neutral_chroma', light: '0.024', dark: '0.025' }, + { name: 'palette_lightness_00', light: '0.985', dark: '0.147' }, + { name: 'palette_lightness_100', light: '0.116', dark: '0.971' }, + { name: 'palette_lightness_curve', light: '1.15', dark: '0.76' }, + { name: 'palette_chroma_min', light: '0.0132', dark: '0.0214' }, + { name: 'palette_chroma_max', light: '0.106', dark: '0.1088' }, + { name: 'palette_chroma_curve', light: '1.3', dark: '1.27' }, + { + name: 'palette_chroma_00', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_00)), 0.0072)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_00)), 0.0254)' + }, + { + name: 'palette_chroma_05', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_05)), 0.021)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_05)), 0.04)' + }, + { + name: 'palette_chroma_10', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_10)), 0.0382)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_10)), 0.0501)' + }, + { + name: 'palette_chroma_20', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_20)), 0.078)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_20)), 0.0673)' + }, + { + name: 'palette_chroma_30', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_30)), 0.1235)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_30)), 0.0824)' + }, + { + name: 'palette_chroma_40', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_40)), 0.1179)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_40)), 0.0964)' + }, + { + name: 'palette_chroma_50', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_50)), 0.1026)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_50)), 0.1095)' + }, + { + name: 'palette_chroma_60', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_60)), 0.0868)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_60)), 0.122)' + }, + { + name: 'palette_chroma_70', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_70)), 0.0706)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_70)), 0.1189)' + }, + { + name: 'palette_chroma_80', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_80)), 0.054)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_80)), 0.0812)' + }, + { + name: 'palette_chroma_90', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_90)), 0.0372)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_90)), 0.0465)' + }, + { + name: 'palette_chroma_95', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_95)), 0.0286)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_95)), 0.03)' + }, + { + name: 'palette_chroma_100', + light: + 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_100)), 0.02)', + dark: 'min(calc(var(--palette_chroma_min) + (var(--palette_chroma_max) - var(--palette_chroma_min)) * var(--chroma_shape_100)), 0.0142)' + }, + { name: 'shade_min', light: '#fff', dark: '#000' }, + { name: 'shade_max', light: '#000', dark: '#fff' }, + { name: 'shade_lightness_00', light: '0.97', dark: '0.171' }, + { name: 'shade_lightness_100', light: '0.171', dark: '0.97' }, + { name: 'shade_lightness_curve', light: '1.09', dark: '0.92' }, + { name: 'fg_00', light: 'var(--darken_00)', dark: 'var(--lighten_00)' }, + { name: 'fg_05', light: 'var(--darken_05)', dark: 'var(--lighten_05)' }, + { name: 'fg_10', light: 'var(--darken_10)', dark: 'var(--lighten_10)' }, + { name: 'fg_20', light: 'var(--darken_20)', dark: 'var(--lighten_20)' }, + { name: 'fg_30', light: 'var(--darken_30)', dark: 'var(--lighten_30)' }, + { name: 'fg_40', light: 'var(--darken_40)', dark: 'var(--lighten_40)' }, + { name: 'fg_50', light: 'var(--darken_50)', dark: 'var(--lighten_50)' }, + { name: 'fg_60', light: 'var(--darken_60)', dark: 'var(--lighten_60)' }, + { name: 'fg_70', light: 'var(--darken_70)', dark: 'var(--lighten_70)' }, + { name: 'fg_80', light: 'var(--darken_80)', dark: 'var(--lighten_80)' }, + { name: 'fg_90', light: 'var(--darken_90)', dark: 'var(--lighten_90)' }, + { name: 'fg_95', light: 'var(--darken_95)', dark: 'var(--lighten_95)' }, + { name: 'fg_100', light: 'var(--darken_100)', dark: 'var(--lighten_100)' }, + { name: 'bg_00', light: 'var(--lighten_00)', dark: 'var(--darken_00)' }, + { name: 'bg_05', light: 'var(--lighten_05)', dark: 'var(--darken_05)' }, + { name: 'bg_10', light: 'var(--lighten_10)', dark: 'var(--darken_10)' }, + { name: 'bg_20', light: 'var(--lighten_20)', dark: 'var(--darken_20)' }, + { name: 'bg_30', light: 'var(--lighten_30)', dark: 'var(--darken_30)' }, + { name: 'bg_40', light: 'var(--lighten_40)', dark: 'var(--darken_40)' }, + { name: 'bg_50', light: 'var(--lighten_50)', dark: 'var(--darken_50)' }, + { name: 'bg_60', light: 'var(--lighten_60)', dark: 'var(--darken_60)' }, + { name: 'bg_70', light: 'var(--lighten_70)', dark: 'var(--darken_70)' }, + { name: 'bg_80', light: 'var(--lighten_80)', dark: 'var(--darken_80)' }, + { name: 'bg_90', light: 'var(--lighten_90)', dark: 'var(--darken_90)' }, + { name: 'bg_95', light: 'var(--lighten_95)', dark: 'var(--darken_95)' }, + { name: 'bg_100', light: 'var(--lighten_100)', dark: 'var(--darken_100)' }, + { + name: 'border_color_05', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 4%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 8%)' + }, + { + name: 'border_color_10', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 7%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 14%)' + }, + { + name: 'border_color_20', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 13%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 22%)' + }, + { + name: 'border_color_30', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 22%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 32%)' + }, + { + name: 'border_color_40', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 34%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 44%)' + }, + { + name: 'border_color_50', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 48%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 56%)' + }, + { + name: 'border_color_60', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 62%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 68%)' + }, + { + name: 'border_color_70', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 76%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 80%)' + }, + { + name: 'border_color_80', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 88%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 90%)' + }, + { + name: 'border_color_90', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 96%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 97%)' + }, + { + name: 'border_color_95', + light: 'oklch(0.345 0.064 var(--hue_neutral) / 99%)', + dark: 'oklch(0.857 0.053 var(--hue_neutral) / 99%)' + }, + { + name: 'border_color_100', + light: 'oklch(0.345 0.064 var(--hue_neutral))', + dark: 'oklch(0.857 0.053 var(--hue_neutral))' + }, + { name: 'text_min', light: '#fff', dark: '#000' }, + { name: 'text_max', light: '#000', dark: '#fff' }, + { name: 'text_lightness_00', light: '0.97', dark: '0.146' }, + { name: 'text_lightness_100', light: '0.146', dark: '0.97' }, + { name: 'text_lightness_curve', light: '1.19', dark: '0.87' }, + { + name: 'selection_color', + light: + 'oklch(var(--palette_lightness_20) calc(var(--palette_chroma_20) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) var(--hue_accent) / 40%)', + dark: 'oklch(var(--palette_lightness_80) calc(var(--palette_chroma_80) * var(--chroma_scale) * var(--accent_chroma_scale, 1)) var(--hue_accent) / 40%)' + }, + { + name: 'button_shadow', + light: + 'var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_30), transparent), var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_30), transparent)', + dark: 'var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_30), transparent), var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_30), transparent)' + }, + { + name: 'button_shadow_hover', + light: + 'var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_40), transparent), var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_40), transparent)', + dark: 'var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_40), transparent), var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_40), transparent)' + }, + { + name: 'button_shadow_active', + light: + 'var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_40), transparent), var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_40), transparent)', + dark: 'var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_40), transparent), var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color_highlight) var(--shadow_alpha_40), transparent)' + }, + { name: 'shadow_color_umbra', light: '#000', dark: 'oklch(0.863 0.009 var(--hue_neutral))' }, + { name: 'shadow_color_highlight', light: 'oklch(0.955 0.003 var(--hue_neutral))', dark: '#000' }, + { + name: 'shadow_color_glow', + light: 'oklch(0.955 0.003 var(--hue_neutral))', + dark: 'oklch(0.863 0.009 var(--hue_neutral))' + }, + { + name: 'shadow_alpha_05', + light: 'calc(6% * var(--shadow_alpha_scale))', + dark: 'calc(13% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_10', + light: 'calc(10% * var(--shadow_alpha_scale))', + dark: 'calc(19% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_20', + light: 'calc(16% * var(--shadow_alpha_scale))', + dark: 'calc(27% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_30', + light: 'calc(25% * var(--shadow_alpha_scale))', + dark: 'calc(37% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_40', + light: 'calc(36% * var(--shadow_alpha_scale))', + dark: 'calc(47% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_50', + light: 'calc(50% * var(--shadow_alpha_scale))', + dark: 'calc(59% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_60', + light: 'calc(64% * var(--shadow_alpha_scale))', + dark: 'calc(71% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_70', + light: 'calc(77% * var(--shadow_alpha_scale))', + dark: 'calc(83% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_80', + light: 'calc(88% * var(--shadow_alpha_scale))', + dark: 'calc(91% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_90', + light: 'calc(96% * var(--shadow_alpha_scale))', + dark: 'calc(98% * var(--shadow_alpha_scale))' + }, + { + name: 'shadow_alpha_95', + light: 'calc(99% * var(--shadow_alpha_scale))', + dark: 'calc(100% * var(--shadow_alpha_scale))' + } +]; diff --git a/src/lib/theme.gen.css.ts b/src/lib/theme.gen.css.ts index a8dcd1cd1..e109cbe43 100644 --- a/src/lib/theme.gen.css.ts +++ b/src/lib/theme.gen.css.ts @@ -2,11 +2,9 @@ import type { Gen } from '@fuzdev/gro'; import { default_themes } from './themes.ts'; import { render_theme_style } from './theme.ts'; +import { default_variables } from './variables.ts'; -// TODO maybe this should be `base_theme.css` or `base.css` or something, -// and we could also generate and publish the other themes -- -// that would allow them to be staticly used, -// currently you'd have to pass a dynamic `theme` to `ThemeRoot` +// TODO maybe this should be `base_theme.css` or `base.css` or something /** @nodocs */ export const gen: Gen = ({ origin_path }) => { @@ -25,12 +23,13 @@ export const gen: Gen = ({ origin_path }) => { const theme = default_themes[0]!; // the default theme's variables are the system's defaults, so they render - // into the base layer; runtime theme overrides beat them from fuz.theme - const theme_style = render_theme_style(theme, { - comments: true, - empty_default_theme: false, - layer: 'fuz.base' - }); + // into the base layer; runtime theme overrides beat them from fuz.theme. + // the theme itself is empty (it inherits the defaults), so the full set is + // passed explicitly — the renderer holds no variable data of its own + const theme_style = render_theme_style( + { ...theme, variables: default_variables }, + { comments: true, layer: 'fuz.base' } + ); return `${banner} diff --git a/src/lib/theme.ts b/src/lib/theme.ts index 9ecabc924..dae3b96f1 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -1,4 +1,3 @@ -import { default_variables } from './variables.ts'; import type { StyleVariable } from './variable.ts'; /** @@ -19,14 +18,29 @@ export interface Theme { name: string; variables: Array<StyleVariable>; /** - * The scheme stance, defaulting to `'dual'`. A single-scheme stance mirrors - * every scheme-adaptive default the theme doesn't override so the stanced - * scheme's values apply in both color schemes (see - * `scheme_stance_variables`), and pins `color-scheme` on the scope so form - * controls and native scrollbars agree. A stanced theme's own variables - * are best authored single-slot in the light/base position. + * The scheme stance, defaulting to `'dual'`. A single-scheme stance pins + * `color-scheme` on the scope so form controls and native scrollbars agree. + * A stanced theme's own variables are best authored single-slot in the + * light/base position. + * + * Rendering a stanced theme faithfully also needs the scheme-adaptive + * defaults mirrored into the stanced scheme; that is + * `resolve_theme_stance` in `theme_stance.ts`, applied before rendering. + * The renderer only pins `color-scheme` — it holds no variable data of its + * own, so the mirror stays off the theme path of consumers who don't use a + * stanced theme. */ scheme?: ThemeScheme; + /** + * The stance mirror computed by `resolve_theme_stance`: scheme-adaptive + * defaults re-slotted so a single-scheme theme's appearance holds in both + * color schemes. Rendered before `variables`, so the theme's own values + * win by order. + * + * Kept apart from `variables` so the authored knobs stay distinguishable + * from the derived ones. + */ + scheme_mirror?: Array<StyleVariable>; } /** @@ -41,14 +55,6 @@ export const FUZ_LAYER_ORDER_STATEMENT = '@layer fuz.base, fuz.theme, fuz.utilit export interface RenderThemeStyleOptions { comments?: boolean; id?: string | null; - /** - * How to treat a theme whose `variables` are empty. When `true` (the - * default) it renders nothing, inheriting the `fuz.base` defaults; when - * `false` it renders the full `default_variables` set (how - * `theme.gen.css.ts` emits the standalone `theme.css`). A theme that - * carries variables always renders them and ignores this option. - */ - empty_default_theme?: boolean; /** * The cascade layer wrapping the rendered variables. Theme overrides * default to `fuz.theme` so they beat the `fuz.base` defaults by layer @@ -67,8 +73,10 @@ export interface RenderThemeStyleOptions { * The base's `scheme` stance wins; when the base is single-scheme, each * overlay variable is re-slotted to the stanced scheme's value so a * dual-slot fragment can't leak the other scheme's appearance past the - * stance. The composed name appends the overlay names so name-keyed pickers - * and renderers treat the composition as its own theme. + * stance. The base's `scheme_mirror` carries through unchanged — it renders + * before `variables`, so overlay values still win. The composed name appends + * the overlay names so name-keyed pickers and renderers treat the composition + * as its own theme. */ export const compose_themes = (base: Theme, ...overlays: Array<Theme>): Theme => { if (!overlays.length) return base; @@ -90,47 +98,33 @@ export const compose_themes = (base: Theme, ...overlays: Array<Theme>): Theme => return { name: `${base.name} (${overlays.map((o) => o.name).join(', ')})`, ...(base.scheme !== undefined && { scheme: base.scheme }), + // the mirror renders before `variables`, so overlay values still win + ...(base.scheme_mirror !== undefined && { scheme_mirror: base.scheme_mirror }), variables: [...by_name.values()] }; }; /** - * Computes the mirror a single-scheme stance implies: every scheme-adaptive - * default (dual-slot entry in `default_variables`) not overridden by - * `variables`, re-slotted so the stanced scheme's value applies in both color - * schemes — into the base slot for a `'dark'` stance, into the dark slot for - * a `'light'` stance (defeating the base dark values by cascade-layer order). + * Renders a theme's variables as CSS, wrapped in the `fuz.theme` cascade + * layer by default. + * + * Renders exactly what the theme carries — an empty `variables` array renders + * nothing (inheriting the `fuz.base` defaults) unless a `scheme` stance needs + * pinning. To render the full default set, pass it: `render_theme_style({name: + * 'base', variables: default_variables})`. To render a single-scheme theme + * faithfully, resolve it through `resolve_theme_stance` first. + * + * @param theme - the theme to render + * @param options - see `RenderThemeStyleOptions` + * @returns the theme CSS, or an empty string when there's nothing to render */ -export const scheme_stance_variables = ( - scheme: 'light' | 'dark', - variables: Array<StyleVariable> -): Array<StyleVariable> => { - const overridden = new Set(variables.map((v) => v.name)); - const mirrored: Array<StyleVariable> = []; - for (const v of default_variables) { - if (v.dark === undefined || overridden.has(v.name)) continue; - if (scheme === 'dark') { - mirrored.push({ name: v.name, light: v.dark }); - } else if (v.light !== undefined) { - mirrored.push({ name: v.name, dark: v.light }); - } - } - return mirrored; -}; - export const render_theme_style = (theme: Theme, options: RenderThemeStyleOptions = {}): string => { - const { comments = false, id = null, empty_default_theme = true, layer = 'fuz.theme' } = options; + const { comments = false, id = null, layer = 'fuz.theme' } = options; const stance = theme.scheme === 'light' || theme.scheme === 'dark' ? theme.scheme : null; - // key the default-theme special case on emptiness, not the name, so any theme - // carrying variables renders them (a theme merely named 'base' still renders); - // a stanced theme always renders its stance mirror, even with no variables - const own = theme.variables.length - ? theme.variables - : empty_default_theme - ? null - : default_variables; // mirrored defaults first so the theme's own variables win by order - const variables = [...(stance ? scheme_stance_variables(stance, own ?? []) : []), ...(own ?? [])]; + const variables = theme.scheme_mirror?.length + ? [...theme.scheme_mirror, ...theme.variables] + : theme.variables; if (!variables.length && !stance) return ''; const rendered_light = variables .map((v) => render_theme_variable(v, false, comments)) diff --git a/src/lib/theme_check.ts b/src/lib/theme_check.ts index dec1bb9aa..e4d3180ad 100644 --- a/src/lib/theme_check.ts +++ b/src/lib/theme_check.ts @@ -36,14 +36,15 @@ * `display_value`): light = `theme.light`; dark = `theme.dark ?? theme.light`, * falling back to the numeric-twin default for the scheme. A single-scheme * stance (`Theme.scheme`) resolves through the same `scheme_stance_variables` - * mirror the renderer emits, so the gates evaluate the stanced reality in - * both schemes. + * mirror `resolve_theme_stance` computes, so the gates evaluate the stanced + * reality in both schemes whether or not the theme arrives resolved. * * @module */ import { StyleVariable } from './variable.ts'; -import { scheme_stance_variables, type Theme } from './theme.ts'; +import type { Theme } from './theme.ts'; +import { scheme_stance_variables } from './theme_stance.ts'; import { default_variables } from './variables.ts'; import { theme_knob_by_name, theme_knob_hook_names, type ThemeKnob } from './knobs.ts'; import { diff --git a/src/lib/theme_stance.ts b/src/lib/theme_stance.ts new file mode 100644 index 000000000..3a76485b6 --- /dev/null +++ b/src/lib/theme_stance.ts @@ -0,0 +1,64 @@ +/** + * The scheme stance mirror, which makes a single-scheme theme render its one + * appearance in both color schemes. + * + * This lives apart from `theme.ts` so the renderer stays free of the variable + * set. Only a stanced theme needs the mirror, and most consumers use a + * dual-scheme theme, so keeping the dependency here means only the code that + * renders a stanced theme pays for the data. + * + * @module + */ + +import { scheme_adaptive_variables } from './scheme_adaptive_variables.ts'; +import type { Theme } from './theme.ts'; +import type { StyleVariable } from './variable.ts'; + +/** + * Computes the mirror a single-scheme stance implies: every scheme-adaptive + * default not overridden by `variables`, re-slotted so the stanced scheme's + * value applies in both color schemes — into the base slot for a `'dark'` + * stance, into the dark slot for a `'light'` stance (defeating the base dark + * values by cascade-layer order). + * + * @param scheme - the stance to mirror for + * @param variables - the theme's own variables, whose names are left alone + * @returns the mirrored variables, to render before the theme's own + */ +export const scheme_stance_variables = ( + scheme: 'light' | 'dark', + variables: Array<StyleVariable> +): Array<StyleVariable> => { + const overridden = new Set(variables.map((v) => v.name)); + const mirrored: Array<StyleVariable> = []; + for (const v of scheme_adaptive_variables) { + if (overridden.has(v.name)) continue; + if (scheme === 'dark') { + if (v.dark !== undefined) mirrored.push({ name: v.name, light: v.dark }); + } else if (v.light !== undefined) { + mirrored.push({ name: v.name, dark: v.light }); + } + } + return mirrored; +}; + +/** + * Resolves a single-scheme theme by computing its stance mirror into + * `scheme_mirror`, so `render_theme_style` needs no knowledge of the defaults. + * A dual-scheme theme is returned unchanged. + * + * The mirror lands in its own field rather than merged into `variables` so the + * authored knobs stay distinguishable from the derived ones — `compile_theme` + * reads `variables` to detect author pins, and theme editors show it as the + * theme's own surface. + * + * The shipped stanced themes apply this at their own module scope, so a + * hand-rolled stanced theme is the only kind that needs the call. + * + * @param theme - the theme to resolve + * @returns the theme carrying its stance mirror + */ +export const resolve_theme_stance = (theme: Theme): Theme => { + if (theme.scheme !== 'light' && theme.scheme !== 'dark') return theme; + return { ...theme, scheme_mirror: scheme_stance_variables(theme.scheme, theme.variables) }; +}; diff --git a/src/lib/themes/necromancer.ts b/src/lib/themes/necromancer.ts index 0e8c9f3e1..7037f420f 100644 --- a/src/lib/themes/necromancer.ts +++ b/src/lib/themes/necromancer.ts @@ -1,4 +1,5 @@ import type { Theme } from '../theme.ts'; +import { resolve_theme_stance } from '../theme_stance.ts'; /** * A fantasy exemplar theme built entirely from semantic bindings and levers — @@ -15,7 +16,7 @@ import type { Theme } from '../theme.ts'; * so this knob-only version ships at ~90% volume. The walls it hit are * recorded as probe evidence. */ -export const necromancer_theme: Theme = { +const authored: Theme = { name: 'necromancer', scheme: 'dark', variables: [ @@ -43,3 +44,9 @@ export const necromancer_theme: Theme = { { name: 'border_radius_xl', light: '0.8rem' } ] }; + +/** + * Resolved at module scope so the stance mirror rides this module's chunk + * rather than every consumer's theme path — see `theme_stance.ts`. + */ +export const necromancer_theme: Theme = resolve_theme_stance(authored); diff --git a/src/lib/themes/terminalien.ts b/src/lib/themes/terminalien.ts index 9b3892051..1bccfae69 100644 --- a/src/lib/themes/terminalien.ts +++ b/src/lib/themes/terminalien.ts @@ -1,4 +1,5 @@ import type { Theme } from '../theme.ts'; +import { resolve_theme_stance } from '../theme_stance.ts'; /** * A dense terminal theme with an alien cast: dark, monospace, sharp-cornered, @@ -10,7 +11,7 @@ import type { Theme } from '../theme.ts'; * * Declared subversions: none. */ -export const terminalien_theme: Theme = { +const authored: Theme = { name: 'terminalien', scheme: 'dark', variables: [ @@ -34,3 +35,9 @@ export const terminalien_theme: Theme = { { name: 'line_height_xl', light: '2' } ] }; + +/** + * Resolved at module scope so the stance mirror rides this module's chunk + * rather than every consumer's theme path — see `theme_stance.ts`. + */ +export const terminalien_theme: Theme = resolve_theme_stance(authored); diff --git a/src/lib/variable_graph.ts b/src/lib/variable_graph.ts index 71f2e30df..68575d563 100644 --- a/src/lib/variable_graph.ts +++ b/src/lib/variable_graph.ts @@ -12,6 +12,7 @@ import { levenshtein_distance } from '@fuzdev/fuz_util/string.ts'; import { hash_insecure } from '@fuzdev/fuz_util/hash.ts'; import { default_variables } from './variables.ts'; +import type { Theme } from './theme.ts'; import type { StyleVariable } from './variable.ts'; import { extract_css_variables } from './css_variable_utils.ts'; @@ -285,17 +286,43 @@ export const resolve_variables_option = (variables: VariablesOption): Array<Styl : (variables ?? default_variables); }; +/** + * Overlays a theme's variables onto a resolved variable set, last-wins by name. + * + * This is how a theme is baked in at build time: the overlaid values flow + * through the dependency graph like any other, so the variables a theme + * references are pulled in transitively and the output stays tree-shaken. A + * stanced theme's `scheme_mirror` applies first, matching the renderer's order. + * + * @param variables - the resolved default or custom variables + * @param theme - the theme to overlay, or null/undefined for none + * @returns the composed variables, or `variables` unchanged when there's no theme + */ +export const apply_theme_variables = ( + variables: Array<StyleVariable>, + theme: Theme | null | undefined +): Array<StyleVariable> => { + if (!theme) return variables; + const by_name = new Map(variables.map((v) => [v.name, v])); + // mirror first, then the theme's own, so authored values win + for (const v of theme.scheme_mirror ?? []) by_name.set(v.name, v); + for (const v of theme.variables) by_name.set(v.name, v); + return [...by_name.values()]; +}; + /** * Builds a variable dependency graph from a variables option. * Handles all option forms: undefined (defaults), null (disabled), array, or callback. * * @param variables - the variables option from generator config + * @param theme - an optional theme to overlay onto the resolved variables * @returns `VariableDependencyGraph` built from the resolved variables */ export const build_variable_graph_from_options = ( - variables: VariablesOption + variables: VariablesOption, + theme?: Theme | null ): VariableDependencyGraph => { - const resolved = resolve_variables_option(variables); + const resolved = apply_theme_variables(resolve_variables_option(variables), theme); const content = resolved.map((v) => `${v.name}:${v.light ?? ''}:${v.dark ?? ''}`).join('|'); return build_variable_graph(resolved, hash_insecure(content)); }; diff --git a/src/lib/vite_plugin_fuz_css.ts b/src/lib/vite_plugin_fuz_css.ts index f499e3533..dec7d3e7c 100644 --- a/src/lib/vite_plugin_fuz_css.ts +++ b/src/lib/vite_plugin_fuz_css.ts @@ -175,6 +175,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug cache_dir = DEFAULT_CACHE_DIR, base_css, variables, + theme, additional_elements, additional_variables, exclude_elements, @@ -276,6 +277,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug const resources = await create_bundled_resources({ base_css, variables, + theme, class_definitions: all_class_definitions, deps }); diff --git a/src/routes/docs/classes/+page.svelte b/src/routes/docs/classes/+page.svelte index 335c3d458..455ec94e1 100644 --- a/src/routes/docs/classes/+page.svelte +++ b/src/routes/docs/classes/+page.svelte @@ -198,6 +198,10 @@ import 'virtual:fuz.css';`} <code>variables</code> - customize or disable theme variables; set to <code>null</code> for utility-only mode, or provide a callback to modify defaults </li> + <li> + <code>theme</code> - a <TomeLink slug="themes" /> to bake into the generated CSS, overlaid onto + <code>variables</code> last-wins by name + </li> <li> <code>additional_elements</code> - elements to always include styles for (for runtime-created elements), or <code>'all'</code> to include all base styles diff --git a/src/routes/docs/introduction/+page.svelte b/src/routes/docs/introduction/+page.svelte index 23c18bbfd..156da98bf 100644 --- a/src/routes/docs/introduction/+page.svelte +++ b/src/routes/docs/introduction/+page.svelte @@ -7,7 +7,6 @@ import { tome_get_by_slug } from '@fuzdev/fuz_ui/tome.ts'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; - import SourceFileLink from '$routes/SourceFileLink.svelte'; import Introduction from '$routes/Introduction.svelte'; const LIBRARY_ITEM_NAME = 'introduction'; @@ -44,15 +43,17 @@ import 'virtual:fuz.css'; // Vite plugin import './fuz.css'; // Gro generator, written to the filesystem`} /> <p> - For projects managing their own theme or base styles, use utility-only mode with separate - imports: + That single import is all most projects need - it already contains the base styles and theme + variables, so importing the full stylesheets alongside it ships both layers twice. Those + stylesheets are the escape hatch, for projects managing their own theme or base styles; + using them means turning the generated ones off: </p> <Code lang="ts" - content={`// utility-only mode - full package CSS, only used utilities + content={`// utility-only mode - set \`base_css: null, variables: null\` on the plugin import '@fuzdev/fuz_css/style.css'; // all base styles import '@fuzdev/fuz_css/theme.css'; // all variables -import 'virtual:fuz.css'; // used utilities, \`base_css: null, variables: null\``} +import 'virtual:fuz.css'; // used utilities only`} /> <p> See the <TomeLink slug="classes" /> reference for setup details and configuration options. @@ -68,12 +69,10 @@ import 'virtual:fuz.css'; // used utilities, \`base_css: null, variables: null\` and also exports the underlying data, helpers, and types for open-ended usage </li> <li> - supports <TomeLink slug="themes" /> with a basic theme stylesheet, <SourceFileLink - path="theme.css" - />, that can be replaced with your own - dark mode is a first-class concept, not a theme; - instead, each theme can support light and/or dark <MdnLink path="Web/CSS/color-scheme" - >color-schemes</MdnLink - > + supports <TomeLink slug="themes" /> as sets of style variables, applied at build time through + the generators' <code>theme</code> option or swapped at runtime - dark mode is a + first-class concept, not a theme; instead, each theme can support light and/or dark + <MdnLink path="Web/CSS/color-scheme">color-schemes</MdnLink> </li> <li> supports optional <TomeLink slug="classes">utility classes</TomeLink> with three types (token, diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index a89e06af0..39d95f3e4 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -161,6 +161,39 @@ are dark-only this way. </p> </TomeSection> + <TomeSection> + <TomeSectionHeader text="Applying a theme" /> + <p> + A theme is plain data - an array of style variables - so it can be applied at build time or at + runtime. Most projects want build time. + </p> + <p> + Pass a theme to the <TomeLink slug="classes" hash="Vite-plugin">Vite plugin</TomeLink> or + <TomeLink slug="classes" hash="Gro-generator">Gro generator</TomeLink> and its values bake into + the generated CSS - no runtime rendering, no JavaScript shipped, and the output stays tree-shaken: + </p> + <Code + lang="ts" + content={`// vite.config.ts +import {vite_plugin_fuz_css} from '@fuzdev/fuz_css/vite_plugin_fuz_css.ts'; +import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts'; + +export default defineConfig({plugins: [vite_plugin_fuz_css({theme: necromancer_theme})]});`} + /> + <p> + It overlays the default variables last-wins by name, so it composes with the + <code>variables</code> option. Unlike the runtime path it doesn't pin + <MdnLink path="Web/CSS/color-scheme" /> - for a single-scheme theme, add the + <code>dark</code> or <code>light</code> class to the root <code>html</code> element, as in the next + section. + </p> + <p> + For runtime switching - a picker, or a theme loaded per user - use <code>ThemeRoot</code> from + <a href="https://ui.fuz.dev/">fuz_ui</a>, which renders the theme to a + <code>style</code> element. The two compose: the build-time theme is the starting point, and a runtime + theme overrides it by cascade layer. + </p> + </TomeSection> <TomeSection> <TomeSectionHeader text="Color scheme" /> <p> diff --git a/src/test/theme.test.ts b/src/test/theme.test.ts index f46f553c6..b942bc7ba 100644 --- a/src/test/theme.test.ts +++ b/src/test/theme.test.ts @@ -1,11 +1,8 @@ import { test, assert, describe } from 'vitest'; -import { - compose_themes, - render_theme_style, - scheme_stance_variables, - type Theme -} from '$lib/theme.ts'; +import { compose_themes, render_theme_style, type Theme } from '$lib/theme.ts'; +import { resolve_theme_stance, scheme_stance_variables } from '$lib/theme_stance.ts'; +import { scheme_adaptive_variables } from '$lib/scheme_adaptive_variables.ts'; import { default_variables } from '$lib/variables.ts'; // a scheme-adaptive default (dual slots) to observe the stance mirror through @@ -35,11 +32,10 @@ describe('render_theme_style', () => { assert.strictEqual(render_theme_style({ name: 'base', variables: [] }), ''); }); - test('an empty-variables theme with empty_default_theme false renders the full defaults', () => { - const css = render_theme_style( - { name: 'anything', variables: [] }, - { empty_default_theme: false } - ); + test('the full defaults render when passed explicitly', () => { + // the renderer holds no variable data of its own, so rendering the full + // default set is an ordinary call with the defaults as the theme + const css = render_theme_style({ name: 'anything', variables: default_variables }); assert.isAbove(css.length, 0); for (const v of default_variables) { if (v.light !== undefined) assert.include(css, `--${v.name}: ${v.light};`); @@ -49,7 +45,9 @@ describe('render_theme_style', () => { describe('scheme stance', () => { test('a dark stance mirrors adaptive defaults into the base scheme', () => { - const css = render_theme_style({ name: 't', variables: [], scheme: 'dark' }); + const css = render_theme_style( + resolve_theme_stance({ name: 't', variables: [], scheme: 'dark' }) + ); const { light } = split_schemes(css); assert.include(light, 'color-scheme: dark;'); // the dark slot's value renders in the :root block @@ -57,24 +55,50 @@ describe('scheme stance', () => { }); test('a dark stance skips defaults the theme overrides', () => { - const theme: Theme = { + const theme = resolve_theme_stance({ name: 't', variables: [{ name: adaptive_default.name, light: '0.5' }], scheme: 'dark' - }; + }); const { light } = split_schemes(render_theme_style(theme)); assert.include(light, `--${adaptive_default.name}: 0.5;`); assert.notInclude(light, `--${adaptive_default.name}: ${adaptive_default.dark};`); }); test('a light stance mirrors light values into the dark scheme', () => { - const css = render_theme_style({ name: 't', variables: [], scheme: 'light' }); + const css = render_theme_style( + resolve_theme_stance({ name: 't', variables: [], scheme: 'light' }) + ); const { light, dark } = split_schemes(css); assert.include(light, 'color-scheme: light;'); // the light slot's value renders in the :root.dark block, defeating base dark assert.include(dark, `--${adaptive_default.name}: ${adaptive_default.light};`); }); + test('resolve_theme_stance keeps the mirror out of the authored variables', () => { + const theme = resolve_theme_stance({ + name: 't', + variables: [{ name: 'chroma_scale', light: '2' }], + scheme: 'dark' + }); + // authored knobs stay distinguishable from the derived mirror + assert.lengthOf(theme.variables, 1); + assert.isAbove(theme.scheme_mirror!.length, 0); + }); + + test('resolve_theme_stance leaves a dual-scheme theme unchanged', () => { + const theme: Theme = { name: 't', variables: [], scheme: 'dual' }; + assert.strictEqual(resolve_theme_stance(theme), theme); + const bare: Theme = { name: 't', variables: [] }; + assert.strictEqual(resolve_theme_stance(bare), bare); + }); + + test('an unresolved stanced theme still pins color-scheme', () => { + // the renderer pins the stance on its own; only the mirror needs resolving + const css = render_theme_style({ name: 't', variables: [], scheme: 'dark' }); + assert.include(css, 'color-scheme: dark;'); + }); + test('a dual or absent scheme renders no stance', () => { assert.strictEqual(render_theme_style({ name: 't', variables: [], scheme: 'dual' }), ''); const css = render_theme_style({ @@ -84,6 +108,20 @@ describe('scheme stance', () => { assert.notInclude(css, 'color-scheme:'); }); + test('the generated mirror twin matches the dual-slot defaults', () => { + // `gro gen --check` guards drift too; this states the invariant + const adaptive = default_variables.filter((v) => v.dark !== undefined); + assert.deepEqual( + scheme_adaptive_variables.map((v) => v.name), + adaptive.map((v) => v.name) + ); + for (const v of scheme_adaptive_variables) { + const source = adaptive.find((d) => d.name === v.name)!; + assert.strictEqual(v.light, source.light); + assert.strictEqual(v.dark, source.dark); + } + }); + test('scheme_stance_variables excludes overridden names and single-slot defaults', () => { const mirrored = scheme_stance_variables('dark', [ { name: adaptive_default.name, light: '0.5' } diff --git a/src/test/variable_graph.test.ts b/src/test/variable_graph.test.ts index abb025b1f..fee3a2314 100644 --- a/src/test/variable_graph.test.ts +++ b/src/test/variable_graph.test.ts @@ -1,6 +1,7 @@ import { test, assert, describe } from 'vitest'; import { + apply_theme_variables, build_variable_graph, build_variable_graph_from_options, resolve_variables_transitive, @@ -10,6 +11,7 @@ import { find_similar_variable } from '$lib/variable_graph.ts'; import type { StyleVariable } from '$lib/variable.ts'; +import type { Theme } from '$lib/theme.ts'; describe('build_variable_graph', () => { describe('basic building', () => { @@ -653,3 +655,73 @@ describe('find_similar_variable', () => { assert.isNull(find_similar_variable(graph, 'shadow')); }); }); + +describe('apply_theme_variables', () => { + const defaults: Array<StyleVariable> = [ + { name: 'hue_a', light: '210' }, + { name: 'hue_b', light: '120' }, + { name: 'shade_00', light: '#fff', dark: '#000' } + ]; + + test('returns the input unchanged with no theme', () => { + assert.strictEqual(apply_theme_variables(defaults, null), defaults); + assert.strictEqual(apply_theme_variables(defaults, undefined), defaults); + }); + + test("overlays the theme's variables last-wins by name", () => { + const theme: Theme = { name: 't', variables: [{ name: 'hue_a', light: '30' }] }; + const result = apply_theme_variables(defaults, theme); + assert.deepEqual( + result.find((v) => v.name === 'hue_a'), + { name: 'hue_a', light: '30' } + ); + // untouched names survive, and the set doesn't grow + assert.lengthOf(result, 3); + assert.deepEqual( + result.find((v) => v.name === 'hue_b'), + { name: 'hue_b', light: '120' } + ); + }); + + test('appends variables the defaults lack', () => { + const theme: Theme = { name: 't', variables: [{ name: 'my_brand', light: '#f60' }] }; + const result = apply_theme_variables(defaults, theme); + assert.lengthOf(result, 4); + assert.isDefined(result.find((v) => v.name === 'my_brand')); + }); + + test("applies scheme_mirror before the theme's own variables", () => { + const theme: Theme = { + name: 't', + scheme: 'dark', + scheme_mirror: [ + { name: 'shade_00', light: '#000' }, + { name: 'hue_b', light: '99' } + ], + variables: [{ name: 'shade_00', light: 'authored' }] + }; + const result = apply_theme_variables(defaults, theme); + // the theme's own value wins over its mirror + assert.deepEqual( + result.find((v) => v.name === 'shade_00'), + { name: 'shade_00', light: 'authored' } + ); + // mirror entries the theme doesn't author still apply, replacing both slots + assert.deepEqual( + result.find((v) => v.name === 'hue_b'), + { name: 'hue_b', light: '99' } + ); + }); + + test('build_variable_graph_from_options bakes the theme into the graph', () => { + const theme: Theme = { name: 't', variables: [{ name: 'hue_a', light: '30' }] }; + const graph = build_variable_graph_from_options(defaults, theme); + assert.strictEqual(graph.variables.get('hue_a')!.light_css, '30'); + // a different theme must not collide in the graph's content hash + const other = build_variable_graph_from_options(defaults, { + name: 't2', + variables: [{ name: 'hue_a', light: '40' }] + }); + assert.notStrictEqual(graph.content_hash, other.content_hash); + }); +}); From 2f842ea21971287f3dc93628ae5eadfad98a74e5 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Wed, 29 Jul 2026 11:11:18 -0400 Subject: [PATCH 29/33] changesets --- .changeset/chroma-character.md | 27 ----- .changeset/interaction-and-surfaces.md | 44 ++++----- .changeset/oklch-color-system.md | 71 ++++++++------ .changeset/theme-authoring.md | 131 ++++++++++++++++--------- .changeset/theme-plugin-option.md | 26 ----- .changeset/theme-renderer-pure.md | 30 ------ 6 files changed, 145 insertions(+), 184 deletions(-) delete mode 100644 .changeset/chroma-character.md delete mode 100644 .changeset/theme-plugin-option.md delete mode 100644 .changeset/theme-renderer-pure.md diff --git a/.changeset/chroma-character.md b/.changeset/chroma-character.md deleted file mode 100644 index 2f80407d4..000000000 --- a/.changeset/chroma-character.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -'@fuzdev/fuz_css': minor ---- - -feat: add per-slot chroma multipliers and mute the brown slot - -Every palette letter and intent gains a chroma-character knob: -`--palette_a_chroma_scale` … `--palette_j_chroma_scale` and the intent twins -`--accent_chroma_scale` (same for positive/negative/caution/info), default -`1`. Each multiplies its slot's chroma under the global `--chroma_scale`, so -the slot's character holds at any global setting - grayscale stays grayscale -and vivid scales proportionally. Values at or below 1 stay inside the sRGB -gamut caps by construction; above 1 knowingly clips, like the global knob. -The neutral is unchanged - its character remains `--neutral_chroma`. - -`--palette_f_chroma_scale` ships at `0.55`: brown is low-chroma dark orange, -so no hue angle alone can render it - under uniform chroma the brown slot -read as a second orange beside `--hue_h`. - -An intent hue bound to a palette letter (`--hue_accent: var(--hue_f)`) -shares only the angle. `validate_theme` now warns when the bound letter's -multiplier differs from the intent's `*_chroma_scale` twin, so a slot's -chroma character isn't silently dropped across a binding. `check_theme` runs -its gamut and contrast gates through the multipliers, and an intent folds -into a letter's report entries only when hue and multiplier both match. All -15 knobs are registered variables in the catalog (`knobs.ts`), editable in -the themes docs page's inline editor. diff --git a/.changeset/interaction-and-surfaces.md b/.changeset/interaction-and-surfaces.md index 24f33570c..4c82a4e90 100644 --- a/.changeset/interaction-and-surfaces.md +++ b/.changeset/interaction-and-surfaces.md @@ -13,14 +13,14 @@ Focus and hover now read as one escalating highlight: makes them read as two shapes while keeping the ring's full contrast against the page. - Hovering an input, textarea, or select colors the border with - `var(--outline_color)` — the element color when classed (`.palette_*` - sets it), the accent intent otherwise — instead of fading it to the - weaker `--border_color_20` alpha. Focus keeps setting the border to the - same color and adds the outline. Disabled inputs no longer react to - hover. + `var(--outline_color)` — the element color when one is set (an + `outline_*` class, or `.palette_*` on buttons), the accent intent + otherwise — instead of fading it to the weaker `--border_color_20` + alpha. Focus keeps setting the border to the same color and adds the + outline. Disabled inputs no longer react to hover. New micro-surface defaults in `style.css`, each themable through a `var()` -fallback hook (all registered theme variables, in the knob catalog): +fallback hook (all in the knob catalog, so theme validation accepts them): - `scrollbar-color` on `:root` — thumb defaults to `var(--shade_40)` on a transparent track; hooks `--scrollbar_thumb_color` / @@ -38,21 +38,19 @@ fills: - Selected buttons use `--text_00` (the text ramp endpoint) instead of `--text_05`/`--text_10` for inverse text. Themes bending - `--text_lightness_curve` — the high-contrast modifier, brutalish, - and the OS `prefers-contrast: more` mapping — dragged the near-background - stops toward the fill lightness, washing selected text out (down to - ~1.2:1); the endpoints are the knobs themselves, so the curve can never - move them. + `--text_lightness_curve` — the high-contrast modifier, the OS + `prefers-contrast: more` mapping — drag the near-background stops toward + the fill lightness, washing selected text out (down to ~1.2:1); the + endpoints are the knobs themselves, so the curve can never move them. - Colored buttons (`.palette_X`) fill with `palette_X_50` instead of - `palette_X_40`, matching the neutral `shade_50` selected fill — the - stop-40 fills left light-scheme inverse text at ~2.5:1, below the 3:1 - large-text floor (the disabled-active feedback fill moves `negative_40` → - `negative_50` for the same reason). The selected border now matches the - fill, rendering flat. Unselected tint fills mix from the same `--fill`, - so they read slightly richer. -- `check_theme` gains a matching contrast gate — `GATE_SELECTED_TEXT`: - `text_00` on `shade_50` and on every stop-50 fill must clear 3:1 — so - these pairings can't silently regress. The selected-deselectable hover - swaps `--shade_min` for its text-semantic twin `--text_min` (identical - values). The brutalish exemplar drops its `chroma_scale: 0.5` so the - palette keeps its default punch as the theme's docs describe. + `palette_X_40`, matching the neutral `shade_50` selected fill — stop-40 + fills leave light-scheme inverse text at ~2.5:1, below the 3:1 + large-text floor (disabled-active feedback fills with `negative_50` for + the same reason). The selected border now matches the fill, rendering + flat. Unselected tint fills mix from the same `--fill`, so they read + slightly richer. +- A matching contrast gate in `check_theme` — `GATE_SELECTED_TEXT`: + `text_00` on `shade_50` and on every stop-50 fill must clear 3:1 — keeps + these pairings from silently regressing. The selected-deselectable hover + uses `--text_min`, the text-semantic twin of `--shade_min` (identical + values). diff --git a/.changeset/oklch-color-system.md b/.changeset/oklch-color-system.md index 2882ea801..7e30bbe29 100644 --- a/.changeset/oklch-color-system.md +++ b/.changeset/oklch-color-system.md @@ -2,11 +2,13 @@ '@fuzdev/fuz_css': minor --- -feat: rework the color system to derived OKLCH, add semantic intents, cascade layers, and themes +feat: rework the color system to derived OKLCH with semantic intents and cascade layers Colors are now derived - curve knobs → ramp stops → color stops - in pure CSS (`calc()`/`pow()`/`oklch()`), fitted to minimize the perceptual delta -from the old HSL palette. Breaking changes: +from the old HSL palette. + +Breaking changes: - **`color_` renamed to `palette_`**: `--color_a_50` → `--palette_a_50`, `.color_a_50` → `.palette_a_50`, `.color_a`-`.color_j` → @@ -25,37 +27,42 @@ from the old HSL palette. Breaking changes: variables (`--color_a_50_light`-style, `--shade_XX_light/dark`) and all their classes. Write the literal color or define one custom property instead. -- **New curve knobs** (the promoted theme API): `--chroma_scale`, - `--palette_lightness_00/_100/_curve` (same trio for - `shade_`/`text_`), `--palette_chroma_min/_max/_curve` (clamped per stop by - baked worst-hue sRGB gamut caps), plus per-stop derived variables themes - can pin individually (`--palette_lightness_NN`, `--palette_chroma_NN`, +- **`color-mix()` interpolation moved from `in hsl` to `in oklab`** in + button fills/borders, composites, and shadow classes. +- **Cascade layers**: all shipped CSS is layered `fuz.base` < `fuz.theme` < + `fuz.utilities`, so consumers' unlayered styles beat everything. Custom + `base_css` is re-layered into `fuz.base` in bundled output. + +New: + +- **Curve knobs** (the promoted theme API): `--chroma_scale`, + `--palette_lightness_00/_100/_curve` (same trio for `shade_`/`text_`), + and `--palette_chroma_min/_max/_curve` clamped per stop by baked worst-hue + sRGB gamut caps, plus per-stop derived variables themes can pin + individually (`--palette_lightness_NN`, `--palette_chroma_NN`, `--chroma_shape_NN`). -- **New semantic intent knobs**: `--hue_accent`, `--hue_positive`, - `--hue_negative`, `--hue_caution`, `--hue_info`, each deriving a 13-stop - scale through the shared ramps (`--accent_00`…`--accent_100`, etc.) with - matching token classes (`.positive_50`, `.bg_caution_10`), plus - `--selection_color` and `intent_variants`/`IntentVariant` in +- **Semantic intent knobs**: `--hue_accent`, `--hue_positive`, + `--hue_negative`, `--hue_caution`, `--hue_info`, each deriving a full + 13-stop scale through the shared ramps (`--accent_00`…`--accent_100`, + etc.) with matching token classes (`.positive_50`, `.bg_caution_10`), + plus `--selection_color` and `intent_variants`/`IntentVariant` in `variable_data.ts`. Links, focus, selection, `accent-color`, and disabled-active feedback route through them; focus follows the element color (via `--outline_color`) with the accent as fallback. -- **Cascade layers**: all shipped CSS is layered `fuz.base` < `fuz.theme` < - `fuz.utilities`; consumers' unlayered styles beat everything. - `render_theme_style` loses `specificity` (the `:root:root` hack) and gains - `layer?: string | null` (default `'fuz.theme'`); `generate_theme_css` - loses its specificity parameter; the `theme_specificity` generator option - is removed. The default-theme special case now keys on empty `variables` - rather than the `'base'` name. Custom `base_css` is re-layered into - `fuz.base` in bundled output. -- **`color-mix()` interpolation moved from `in hsl` to `in oklab`** in - button fills/borders, composites, and shadow classes. -- **Themes**: `themes.ts` exports the curated `default_themes` registry - (base) and `contrast_modifiers` (low/high contrast, rewritten as - curve-knob overrides — modifiers composed over any theme with - `compose_themes`, not themes in the list), one module per theme under - `themes/`, plus unregistered exemplars (necromancer, sunset ember, - brutalish, terminalien). -- **New design-time modules**: `ramps.ts` (fitted knob constants, numeric - evaluators, CSS emitters), `oklch.ts` (OKLCH↔sRGB + gamut math), `wcag.ts` - (luminance/contrast), with tests gating every default stop for gamut, - monotonicity, and contrast. +- **Per-slot chroma character**: `--palette_a_chroma_scale` … + `--palette_j_chroma_scale` and intent twins (`--accent_chroma_scale`, + same for positive/negative/caution/info), default `1`, each multiplying + its slot's chroma under the global `--chroma_scale` so the slot's + character holds at any global setting — grayscale stays grayscale and + vivid scales proportionally. Values at or below 1 stay inside the sRGB + gamut caps by construction; above 1 knowingly clips, like the global + knob. The brown slot `f` ships at `0.55`: brown is low-chroma dark + orange, so under uniform chroma it read as a second orange beside + `--hue_h`. An intent hue bound to a palette letter shares only the angle + — `validate_theme` warns when the bound letter's multiplier differs from + the intent's twin, and `check_theme` runs its gates through the + multipliers. +- **Design-time modules**: `ramps.ts` (fitted knob constants, numeric + evaluators, CSS emitters), `oklch.ts` (OKLCH↔sRGB + gamut math), and + `wcag.ts` (luminance/contrast), with tests gating every default stop for + gamut, monotonicity, and contrast. diff --git a/.changeset/theme-authoring.md b/.changeset/theme-authoring.md index 373a1d3cf..cf6941a59 100644 --- a/.changeset/theme-authoring.md +++ b/.changeset/theme-authoring.md @@ -2,55 +2,94 @@ '@fuzdev/fuz_css': minor --- -feat: add theme scale knobs, the knob catalog, validation/gates/compile, and the scheme stance +feat: rework themes - registry and contrast modifiers, scheme stance, knob catalog, validation/gates/compile, pure renderer, build-time `theme` option -New theme knobs, each derived into existing token defaults so one knob move -reshapes a whole family while individual tokens stay pinnable: -`--shadow_alpha_scale` (multiplies the `shadow_alpha_*` ramp, including -button shadows), `--radius_scale` (multiplies the `border_radius_*` tiers), -`--scale_factor` (multiplies the `space_*` scale), `--font_weight` (body), -`--heading_font_weight` (a hook with per-tier fallbacks - setting it -flattens the heading ladder), `--heading_font_family`, and -`--background_image` (decoration hook on `:root`). +Themes are now knob-first: a theme moves the derived color system's curve +and scale knobs, composes with modifiers, validates and gates like the +shipped defaults, and applies at runtime or build time. + +**Registry and modifiers.** `themes.ts` exports the curated +`default_themes` registry (just base) and `contrast_modifiers`: low/high +contrast are curve-knob fragments composed over any theme with the new +`compose_themes(base, ...overlays)` (flatten + last-wins; a single-scheme +base re-slots dual-slot overlay variables to its stance), not themes in the +list. Themes live one module per theme under `themes/`, with unregistered +exemplars: necromancer, sunset ember, brutalish, and terminalien. Low +contrast is tuned to the softest compression that passes every +`check_theme` contrast gate. + +**Scheme stance.** `Theme` gains `scheme?: 'dual' | 'light' | 'dark'` +(default `'dual'`). A single-scheme theme renders its one appearance in +both color schemes by mirroring every scheme-adaptive default it doesn't +override and pinning `color-scheme` on the scope so form controls and +native scrollbars agree. Author a stanced theme's own variables single-slot +in the light/base position. `validate_theme` warns on dark slots a stance +makes meaningless; `check_theme`/`compile_theme` resolve through the same +mirror so the gates evaluate the stanced reality in both schemes. The +necromancer and terminalien exemplars are dark-only via the stance. + +**Pure renderer.** `theme.ts` no longer depends on `variables.ts`, so +mounting a theme stops pulling the full derived variable set into the +bundle: minified, `theme.ts` drops from ~38KB to ~1.3KB (~9KB to ~0.7KB +gzipped). Breaking: + +- `RenderThemeStyleOptions.empty_default_theme` is removed — to render the + full defaults, pass them: + `render_theme_style({name: 'base', variables: default_variables})`. The + default-theme special case now keys on empty `variables` rather than the + `'base'` name. +- `render_theme_style` loses `specificity` (the `:root:root` hack) and + gains `layer?: string | null` (default `'fuz.theme'`); + `generate_theme_css` loses its specificity parameter; the + `theme_specificity` generator option is removed. +- The stance mirror is computed before rendering, not by the renderer: + `resolve_theme_stance` (new `theme_stance.ts`, replacing `theme.ts`'s + `scheme_stance_variables`) stores it on the theme's `scheme_mirror` + field. The shipped stanced exemplars resolve at module scope, so only + hand-rolled stanced themes need the call. -New `knobs.ts`: `theme_knobs`, a typed catalog of the theme-facing knobs -(`kind`, `axis`, `leverage`, `tier`, `bindable`, ranges), which powers the -inline theme editor on the themes docs page. `variable_data.ts` gains -`palette_glosses`, the letter → color/default-intent display data. +**Theme knobs and the catalog.** New scale knobs derive into existing token +defaults so one knob move reshapes a whole family while individual tokens +stay pinnable: `--shadow_alpha_scale` (the `shadow_alpha_*` ramp, button +shadows included), `--radius_scale` (the `border_radius_*` tiers), +`--scale_factor` (the `space_*` scale), `--font_weight` (body), +`--heading_font_weight` (a hook with per-tier fallbacks - setting it +flattens the heading ladder), `--heading_font_family`, and the +`--background_image` decoration hook on `:root`. New `knobs.ts` catalogs +the theme-facing knobs with typed metadata (`kind`, `axis`, `leverage`, +`tier`, `bindable`, ranges), powering the inline theme editor on the themes +docs page; `variable_data.ts` gains `palette_glosses`, the letter → +color/default-intent display data. -New `theme_check.ts`, resolving a theme's authored values back to numbers -(literals, `var(--hue_x)` binding chains, compiled-cap overrides): +**Validation, gates, compile.** New `theme_check.ts` resolves a theme's +authored values back to numbers (literals, `var(--hue_x)` binding chains, +compiled-cap overrides): - `validate_theme` — structural lint: shape and unknown-name errors, plus advisory type/range warnings for the knob-tier variables -- `check_theme` — report-only gamut, ramp-monotonicity, and contrast gates - (the same thresholds the repo's tests assert, exported as the `GATE_*` - constants) -- `compile_theme` — recomputes per-stop worst-hue chroma caps from a theme's - own hues and lightness ramp, emits `palette_chroma_NN` - overrides where the baked caps no longer fit, and re-checks the result - -`ramps.ts` gains `compute_palette_chroma_caps`. -The `'low contrast'` theme's shade compression is retuned to the -softest values that pass every `check_theme` contrast gate. - -Contrast becomes a modifier, not a theme: `default_themes` shrinks to the -base theme, the contrast pair move to the new `contrast_modifiers` export, -and the new `compose_themes(base, ...overlays)` composes fragments by -flatten + last-wins (a single-scheme base re-slots dual-slot overlay -variables to its stance). The test suite gates every shipped theme × -contrast-modifier combination. - -New scheme stance: `Theme` gains `scheme?: 'dual' | 'light' | 'dark'` -(default `'dual'`). A single-scheme theme renders its one appearance in both -color schemes by mirroring every scheme-adaptive default it doesn't override -(including the `palette_chroma_NN` gamut-cap stops) and pinning -`color-scheme` on the scope so form controls and native scrollbars agree. -Author a stanced theme's own variables single-slot in the light/base -position. `validate_theme` warns on dark slots a single-scheme stance makes -meaningless; `check_theme`/`compile_theme` resolve through the same mirror so -the gates evaluate the stanced reality in both schemes. The necromancer and -terminalien exemplars use the stance instead of hand-mirrored ramp knobs. -(Terminalien is the terminal exemplar renamed and densified — compact -`scale_factor` plus tightened line-height pins; the -`create_terminal_theme(hue)` factory is deleted.) +- `check_theme` — report-only gamut, ramp-monotonicity, and contrast + gates, with the thresholds exported as the `GATE_*` constants +- `compile_theme` — recomputes per-stop worst-hue chroma caps from a + theme's own hues and lightness ramp, emits `palette_chroma_NN` overrides + where the baked caps no longer fit, and re-checks the result + +**Build-time `theme` option.** The Vite plugin and Gro generator take a +`theme` baked into the generated CSS: + +```ts +import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts'; + +vite_plugin_fuz_css({theme: necromancer_theme}); +``` + +The theme overlays the resolved `variables` last-wins by name, so its +values flow through the dependency graph like any other - the variables a +theme references are pulled in transitively and the output stays +tree-shaken. A single-scheme theme's `scheme_mirror` applies first, +matching the renderer's order. This is the static counterpart to fuz_ui's +`ThemeRoot`: no runtime theme rendering, no JavaScript shipped; the two +compose, the runtime theme winning by cascade layer. Pinning +`color-scheme` stays separate - the `dark`/`light` class on the root +element drives it. Also exposes `apply_theme_variables` from +`variable_graph.ts`; `build_variable_graph_from_options` takes an optional +theme. diff --git a/.changeset/theme-plugin-option.md b/.changeset/theme-plugin-option.md deleted file mode 100644 index ae36dd34b..000000000 --- a/.changeset/theme-plugin-option.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -'@fuzdev/fuz_css': minor ---- - -Add a `theme` option to the Vite plugin and Gro generator for picking a theme -at build time: - -```ts -import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts'; - -vite_plugin_fuz_css({theme: necromancer_theme}); -``` - -The theme overlays the resolved `variables` last-wins by name, so its values -flow through the dependency graph like any other - the variables a theme -references are pulled in transitively and the output stays tree-shaken. A -single-scheme theme's `scheme_mirror` applies first, matching the renderer's -order. - -This is the static counterpart to fuz_ui's `ThemeRoot`: no theme rendering at -runtime and no JavaScript to ship. The two compose, with a runtime theme -overriding the build-time one by cascade layer. Pinning `color-scheme` stays -separate - the `dark`/`light` class on the root element drives it. - -Also exposes `apply_theme_variables` from `variable_graph.ts`, and -`build_variable_graph_from_options` takes an optional theme. diff --git a/.changeset/theme-renderer-pure.md b/.changeset/theme-renderer-pure.md deleted file mode 100644 index f2e7c9748..000000000 --- a/.changeset/theme-renderer-pure.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -'@fuzdev/fuz_css': minor ---- - -`theme.ts` is now a pure renderer with no dependency on `variables.ts`, so -mounting a theme no longer pulls the full derived variable set into the -bundle. Bundled minified, `theme.ts` drops from ~38KB to ~1.3KB (~9KB to -~0.7KB gzipped) — the variable data was previously paid by every consumer on -the theme path, including apps on the default theme that never call -`render_theme_style` at all. - -Two breaking changes carry the split: - -- `RenderThemeStyleOptions.empty_default_theme` is removed. It existed to let - an empty theme render the full defaults, which meant the renderer had to - hold them. Pass them instead: - `render_theme_style({name: 'base', variables: default_variables})`. -- `render_theme_style` no longer computes the scheme-stance mirror, and - `scheme_stance_variables` moves from `theme.ts` to the new - `theme_stance.ts`. A single-scheme theme is resolved before rendering with - `resolve_theme_stance`, which stores the mirror on the theme's new - `scheme_mirror` field. The renderer still pins `color-scheme` on its own. - -The shipped stanced exemplars (`themes/necromancer.ts`, -`themes/terminalien.ts`) resolve themselves at module scope, so consumers -importing them get a ready-to-render theme and the mirror data lands in that -theme's chunk. Only hand-rolled stanced themes need the new call. - -`default_variables` is unchanged, so the class-extraction pipeline and -`theme.css` output are unaffected. From 239325b58b380ac2ead9a7bb8b5f8c0336ebddbd Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Wed, 29 Jul 2026 11:11:44 -0400 Subject: [PATCH 30/33] wip --- package-lock.json | 38 +- package.json | 2 +- src/lib/style.css | 34 +- src/routes/FileLink.svelte | 18 +- src/routes/FontSizeControl.svelte | 10 +- src/routes/FontWeightControl.svelte | 4 +- src/routes/Introduction.svelte | 10 +- src/routes/KnobControl.svelte | 8 +- src/routes/ThemeEditor.svelte | 40 +- src/routes/docs/borders/+page.svelte | 48 +- src/routes/docs/buttons/+page.svelte | 82 +-- src/routes/docs/chips/+page.svelte | 4 +- src/routes/docs/classes/+page.svelte | 690 +++++++++++----------- src/routes/docs/colors/+page.svelte | 46 +- src/routes/docs/elements/+page.svelte | 43 +- src/routes/docs/examples/+page.svelte | 10 +- src/routes/docs/forms/+page.svelte | 41 +- src/routes/docs/introduction/+page.svelte | 26 +- src/routes/docs/menuitem/todo.svelte | 24 +- src/routes/docs/semantic/+page.svelte | 27 +- src/routes/docs/shading/+page.svelte | 36 +- src/routes/docs/shadows/+page.svelte | 34 +- src/routes/docs/themes/+page.svelte | 54 +- src/routes/docs/typography/+page.svelte | 36 +- src/routes/docs/variables/+page.svelte | 21 +- 25 files changed, 729 insertions(+), 657 deletions(-) diff --git a/package-lock.json b/package-lock.json index f51365307..2f29f74ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@fuzdev/fuz_code": "^0.48.0", "@fuzdev/fuz_ui": "^0.206.7", "@fuzdev/fuz_util": "^0.65.2", - "@fuzdev/gro": "^0.205.1", + "@fuzdev/gro": "^0.209.0", "@fuzdev/mdz": "^0.3.0", "@ryanatkn/eslint-config": "^0.12.2", "@sveltejs/acorn-typescript": "^1.0.9", @@ -920,18 +920,17 @@ } }, "node_modules/@fuzdev/gro": { - "version": "0.205.1", - "resolved": "https://registry.npmjs.org/@fuzdev/gro/-/gro-0.205.1.tgz", - "integrity": "sha512-o5WBXWSH/UlN1BsDnpBNIi0WHR7lj7UFGltH/JctSP7oSB2ojvmoXD5OLCEeVdRVcDaSQKl48xcXKUwHKqVSmQ==", + "version": "0.209.0", + "resolved": "https://registry.npmjs.org/@fuzdev/gro/-/gro-0.209.0.tgz", + "integrity": "sha512-i2eViCE4aRoJgcaVEf2Xiq6iqHmrKmYpoyHGDDoFVSs4WeKs34ZacqBbMe8vkmaeUQauFIVUnFcFbudArngAtw==", "dev": true, "license": "MIT", "dependencies": { + "@fuzdev/tsv_wasm": "^0.2.0", "chokidar": "^5.0.0", "dotenv": "^17.2.3", "esm-env": "^1.2.2", "oxc-parser": "^0.99.0", - "prettier": "^3.7.4", - "prettier-plugin-svelte": "^3.5.1", "ts-blank-space": "^0.6.2", "tslib": "^2.8.1" }, @@ -986,17 +985,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@fuzdev/gro/node_modules/prettier-plugin-svelte": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.5.2.tgz", - "integrity": "sha512-ItFouLvzSFE3ulNl4DKoWM3BGcbDCNVpIyy/Y3F2gC3aNiGLxtFUdffVqO5Z5hhYG+DFT5KULWaxmeFFpdbvaQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "prettier": "^3.0.0", - "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" - } - }, "node_modules/@fuzdev/gro/node_modules/readdirp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", @@ -1043,6 +1031,22 @@ } } }, + "node_modules/@fuzdev/tsv_wasm": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fuzdev/tsv_wasm/-/tsv_wasm-0.2.0.tgz", + "integrity": "sha512-mbnFSWnAG8otz0SPY1baF0E6mbyJHhuxPxhJ607aVZ8N7vm6xb2rV9M/+PStgzt1p1KX2fLuQ2WKZcj1S0CZdg==", + "dev": true, + "license": "MIT", + "bin": { + "tsv": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://www.ryanatkn.com/funding" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", diff --git a/package.json b/package.json index d13e250b6..6970eab5c 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@fuzdev/fuz_code": "^0.48.0", "@fuzdev/fuz_ui": "^0.206.7", "@fuzdev/fuz_util": "^0.65.2", - "@fuzdev/gro": "^0.205.1", + "@fuzdev/gro": "^0.209.0", "@fuzdev/mdz": "^0.3.0", "@ryanatkn/eslint-config": "^0.12.2", "@sveltejs/acorn-typescript": "^1.0.9", diff --git a/src/lib/style.css b/src/lib/style.css index 9daa2ff80..3ed35264a 100644 --- a/src/lib/style.css +++ b/src/lib/style.css @@ -238,23 +238,23 @@ currently can break based on DOM structure - https://developer.mozilla.org/en-US /* checkboxes and radios are expected to be nested inside a `label`, thus no bottom margin */ :where( :is( - p, - ul, - ol, - menu, - form, - fieldset, - table, - details, - textarea, - select, - label, - pre, - blockquote, - aside, - nav, - legend - ):not(:last-child):not(.unstyled) + p, + ul, + ol, + menu, + form, + fieldset, + table, + details, + textarea, + select, + label, + pre, + blockquote, + aside, + nav, + legend + ):not(:last-child):not(.unstyled) ) { margin-bottom: var(--flow_margin, var(--space_lg)); } diff --git a/src/routes/FileLink.svelte b/src/routes/FileLink.svelte index c87f2ac6d..7e21658fe 100644 --- a/src/routes/FileLink.svelte +++ b/src/routes/FileLink.svelte @@ -34,11 +34,13 @@ </script> <!-- eslint-disable-next-line svelte/no-navigation-without-resolve --> -<a {...attrs} {href} class:chip={!unstyled} class:white-space:nowrap={true} - >{#if typeof icon === 'string'}{icon}{:else if typeof icon === 'function'}{@render icon()}{:else}<Svg - inline - data={icon} - size="1em" - />{/if} - {#if children}{@render children()}{:else}{final_path}{/if}</a -> +<a {...attrs} {href} class:chip={!unstyled} class:white-space:nowrap={true}> + {#if typeof icon === 'string'} + {icon} + {:else if typeof icon === 'function'} + {@render icon()} + {:else} + <Svg inline data={icon} size="1em" /> + {/if} + {#if children}{@render children()}{:else}{final_path}{/if} +</a> diff --git a/src/routes/FontSizeControl.svelte b/src/routes/FontSizeControl.svelte index 828c7d22f..c06b2d368 100644 --- a/src/routes/FontSizeControl.svelte +++ b/src/routes/FontSizeControl.svelte @@ -22,8 +22,8 @@ const selected_name = $derived(font_size_variants[selected_size - 1]); </script> -<label - ><div class="title row width:100% flex-wrap:wrap"> +<label> + <div class="title row width:100% flex-wrap:wrap"> {#if children} {@render children()} {:else} @@ -39,9 +39,9 @@ step={1} {max} /> - = var(<StyleVariableButton name="font_size_{selected_name}" - >--font_size_{selected_name}</StyleVariableButton - >) + = var(<StyleVariableButton name="font_size_{selected_name}"> + --font_size_{selected_name} + </StyleVariableButton>) </div> <input type="range" bind:value={selected_size} {min} step={1} {max} /> </label> diff --git a/src/routes/FontWeightControl.svelte b/src/routes/FontWeightControl.svelte index 7dca58ac9..0dc0a6565 100644 --- a/src/routes/FontWeightControl.svelte +++ b/src/routes/FontWeightControl.svelte @@ -12,8 +12,8 @@ // TODO @many publish in $lib when ready, the 950 thing has me wack </script> -<label - ><div class="title row width:100% flex-wrap:wrap"> +<label> + <div class="title row width:100% flex-wrap:wrap"> {#if children} {@render children()} {:else} diff --git a/src/routes/Introduction.svelte b/src/routes/Introduction.svelte index 5608c6f82..d8a317dbf 100644 --- a/src/routes/Introduction.svelte +++ b/src/routes/Introduction.svelte @@ -18,8 +18,8 @@ stylesheet containing the variables used in the reset, and these require no dependencies. There's also <a href={resolve('/docs/classes')}>utility classes</a> - for composition and convenience with a Vite plugin, and the library exports the full API for complex - usage. + for composition and convenience with a Vite plugin, and the library exports the full API for + complex usage. </p> <p> More at the <a href={resolve('/docs')}>docs</a> @@ -28,7 +28,7 @@ <UnfinishedImplementationWarning> fuz_css is in early alpha with breaking changes ahead. Your input and feedback is appreciated in - the GitHub issues for bugs and <a href="https://github.com/fuzdev/fuz_css/discussions" - >discussions</a - > for everything else. + the GitHub issues for bugs and + <a href="https://github.com/fuzdev/fuz_css/discussions">discussions</a> + for everything else. </UnfinishedImplementationWarning> diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index 24886bccf..28bc300f0 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -111,8 +111,10 @@ style:--border_color={custom_color} style:--outline_color={custom_color} title="detach from the palette and set a literal angle" - onclick={() => onchange(String(numeric_value ?? 0))}>custom</button + onclick={() => onchange(String(numeric_value ?? 0))} > + custom + </button> </div> {#if bound_letter === null && numeric_value !== null} <HueInput bind:value={() => numeric_value ?? 0, (v) => emit_numeric(String(v))} /> @@ -183,8 +185,10 @@ class="plain icon_button sm knob_reset" title="reset to base" aria-label="reset to base" - onclick={onreset}>↺</button + onclick={onreset} > + ↺ + </button> {/if} </div> diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte index 37ff6d190..b6fe8a1fe 100644 --- a/src/routes/ThemeEditor.svelte +++ b/src/routes/ThemeEditor.svelte @@ -139,12 +139,13 @@ title={editor.is_palette_tier ? 'moves palette letter hues - exemplar tier, stays out of registries' : 'moves only intent bindings and levers - safe for theme registries'} - >{editor.is_palette_tier ? 'palette-tier' : 'semantic-tier'}</span > + {editor.is_palette_tier ? 'palette-tier' : 'semantic-tier'} + </span> {#if editor.dirty} - <button type="button" class="sm" onclick={() => editor.reset_all()} - >reset all{editor.overrides.size ? ` (${editor.overrides.size})` : ''}</button - > + <button type="button" class="sm" onclick={() => editor.reset_all()}> + reset all{editor.overrides.size ? ` (${editor.overrides.size})` : ''} + </button> {/if} </div> </div> @@ -154,11 +155,11 @@ <div class="row gap_lg"> <ColorSchemeInput /> {#if editor.stance} - <small - >single-scheme theme - edits write to the base slots and the <strong - >{editor.stance}</strong - > appearance renders in both color schemes</small - > + <small> + single-scheme theme - edits write to the base slots and the + <strong>{editor.stance}</strong> + appearance renders in both color schemes + </small> {:else} <small>edits write to the <strong>{editing_scheme}</strong> scheme's slots</small> {/if} @@ -212,10 +213,10 @@ {/if} {#if sm_knobs.length} <details class="mt_lg"> - <summary - >{sm_details_titles[axis] ?? 'granular tokens'} - <small>(escape hatches - pin individual tokens)</small></summary - > + <summary> + {sm_details_titles[axis] ?? 'granular tokens'} + <small>(escape hatches - pin individual tokens)</small> + </summary> <div class="knobs row flex-wrap:wrap gap_lg align-items:flex-end mt_md"> {#each sm_knobs as knob (knob.name)} {@render knob_control(knob, false)} @@ -225,11 +226,11 @@ {/if} {#if axis === 'color'} <details class="mt_lg"> - <summary - >palette hues <small - >(the letter slots - moving these makes the theme palette-tier)</small - ></summary - > + <summary> + palette hues <small> + (the letter slots - moving these makes the theme palette-tier) + </small> + </summary> <div class="ramp_strips mt_md mb_lg"> {#each palette_variants as letter (letter)} <RampStrip prefix="palette_{letter}" label="palette_{letter}" /> @@ -249,7 +250,8 @@ <section> <h3>Output</h3> <p> - The copyable <code>Theme</code> object, and the CSS it renders (only the variables the theme sets). + The copyable <code>Theme</code> object, and the CSS it renders (only the variables the theme + sets). </p> <div class="rendered mb_lg"> <div class="copy"> diff --git a/src/routes/docs/borders/+page.svelte b/src/routes/docs/borders/+page.svelte index cd963d270..ec8730ee3 100644 --- a/src/routes/docs/borders/+page.svelte +++ b/src/routes/docs/borders/+page.svelte @@ -61,9 +61,9 @@ <section> <p> - Border variables integrate with the <TomeLink slug="themes">theme</TomeLink> system and adapt to - color scheme. Alpha borders are tuned for visual balance: dark mode uses higher alpha because light-on-dark - has lower perceived contrast. + Border variables integrate with the <TomeLink slug="themes">theme</TomeLink> system and adapt + to color scheme. Alpha borders are tuned for visual balance: dark mode uses higher alpha + because light-on-dark has lower perceived contrast. </p> </section> <TomeSection> @@ -88,21 +88,21 @@ {/each} </div> <div style:width="250px"> - <span class="pl_sm pr_sm">=</span><code - >{computed_styles?.getPropertyValue('--' + name)}</code - > + <span class="pl_sm pr_sm">=</span><code> + {computed_styles?.getPropertyValue('--' + name)} + </code> </div> </div> {/each} </div> <TomeSection> <TomeSectionHeader text="Opaque borders with shades" tag="h4" /> - <UnfinishedImplementationWarning - >We may want to add <code>border_shade_NN</code> utility classes for opaque borders.</UnfinishedImplementationWarning - > + <UnfinishedImplementationWarning> + We may want to add <code>border_shade_NN</code> utility classes for opaque borders. + </UnfinishedImplementationWarning> <p> - For opaque borders, use <TomeLink slug="shading">shade</TomeLink> variables directly. This avoids - alpha transparency but requires inline styles or custom classes: + For opaque borders, use <TomeLink slug="shading">shade</TomeLink> variables directly. This + avoids alpha transparency but requires inline styles or custom classes: </p> <Code lang="css" @@ -120,8 +120,8 @@ border-color: var(--shade_30); <TomeSection> <TomeSectionHeader text="Border colors" /> <p> - Use palette variables like <code>palette_a_{selected_intensity}</code> for colored borders. The - intensity controls the color's prominence. + Use palette variables like <code>palette_a_{selected_intensity}</code> for colored borders. + The intensity controls the color's prominence. </p> <form class="intensity_selector"> <fieldset class="row mb_0"> @@ -177,9 +177,9 @@ border-color: var(--shade_30); <div class="border_example border_width" style:border-width="var(--{name})"> <StyleVariableButton {name} /> </div> - <span class="pl_sm pr_sm">=</span><code - >{computed_styles?.getPropertyValue('--' + name)}</code - > + <span class="pl_sm pr_sm">=</span><code> + {computed_styles?.getPropertyValue('--' + name)} + </code> </div> {/each} </div> @@ -198,9 +198,9 @@ border-color: var(--shade_30); <div class="border_example {name} outline-style:solid outline_color_30"> <StyleVariableButton {name} /> </div> - <span class="pl_sm pr_sm">=</span><code - >{computed_styles?.getPropertyValue('--' + name)}</code - > + <span class="pl_sm pr_sm">=</span><code> + {computed_styles?.getPropertyValue('--' + name)} + </code> </div> {/each} </div> @@ -217,9 +217,9 @@ border-color: var(--shade_30); <div class="border_example border_radius" style:border-radius="var(--{name})"> <StyleVariableButton {name} /> </div> - <span class="pl_sm pr_sm">=</span><code - >{computed_styles?.getPropertyValue('--' + name)}</code - > + <span class="pl_sm pr_sm">=</span><code> + {computed_styles?.getPropertyValue('--' + name)} + </code> </div> {/each} </div> @@ -241,8 +241,8 @@ border-color: var(--shade_30); <TomeSection> <TomeSectionHeader tag="h4" text="Custom values" /> <p> - Border <TomeLink slug="classes" hash="Literal-classes">literal classes</TomeLink> for open-ended - values: + Border <TomeLink slug="classes" hash="Literal-classes">literal classes</TomeLink> for + open-ended values: </p> <div class="border_examples border_radii"> {#each border_radius_classes as border_radius_class (border_radius_class)} diff --git a/src/routes/docs/buttons/+page.svelte b/src/routes/docs/buttons/+page.svelte index 016be06fc..63b177993 100644 --- a/src/routes/docs/buttons/+page.svelte +++ b/src/routes/docs/buttons/+page.svelte @@ -30,16 +30,17 @@ <section> <p> The <code><button></code> element is styled by default without adding classes. Classes - like <code>.selected</code> and <code>.plain</code> and <code>.palette_a</code> modify the base - style. + like <code>.selected</code> and <code>.plain</code> and <code>.palette_a</code> modify the + base style. </p> <p> Buttons have a <code>.selected</code> state that can be used for various UI purposes, like showing a selected item in a menu or a styling button's <code>aria-pressed</code> state. Instead of having two distinct styles of buttons with outlined and filled variants, fuz_css makes outlined buttons the default, and selected buttons are filled. There's also the - <code>.deselectable</code> modifier class for buttons that remain clickable when selected. Themes - can customize this behavior. + <code>.deselectable</code> + modifier class for buttons that remain clickable when selected. Themes can customize this + behavior. </p> <div class:mb_xs> <Code content={`<button>a button</button>`} /> @@ -53,12 +54,12 @@ <button type="button" class="plain" disabled class:mb_xs>button.plain:disabled</button> <button type="button" class="selected" class:mb_xs>button.selected</button> <button type="button" class="selected" disabled class:mb_xs>button.selected:disabled</button> - <button type="button" class="selected deselectable" class:mb_xs - >button.selected.deselectable</button - > - <button type="button" class="selected deselectable" disabled class:mb_xs - >button.selected.deselectable:disabled</button - > + <button type="button" class="selected deselectable" class:mb_xs> + button.selected.deselectable + </button> + <button type="button" class="selected deselectable" disabled class:mb_xs> + button.selected.deselectable:disabled + </button> <button type="button" class="unstyled">with .unstyled</button> </section> @@ -75,26 +76,27 @@ <section> <Code content={`<button class="${palette_name}">`} /> <button type="button" class={palette_name} class:mb_xs>.{palette_name}</button> - <button type="button" class={palette_name} disabled class:mb_xs - >.{palette_name}:disabled</button - > - <button type="button" class="{palette_name} plain" class:mb_xs>.{palette_name}.plain</button - > - <button type="button" class="{palette_name} plain" disabled class:mb_xs - >.{palette_name}.plain:disabled</button - > - <button type="button" class="{palette_name} selected" class:mb_xs - >.{palette_name}.selected</button - > - <button type="button" class="{palette_name} selected" disabled class:mb_xs - >.{palette_name}.selected:disabled</button - > - <button type="button" class="{palette_name} selected deselectable" class:mb_xs - >.{palette_name}.selected.deselectable</button - > - <button type="button" class="{palette_name} selected deselectable" disabled class:mb_xs - >.{palette_name}.selected.deselectable:disabled</button - > + <button type="button" class={palette_name} disabled class:mb_xs> + .{palette_name}:disabled + </button> + <button type="button" class="{palette_name} plain" class:mb_xs> + .{palette_name}.plain + </button> + <button type="button" class="{palette_name} plain" disabled class:mb_xs> + .{palette_name}.plain:disabled + </button> + <button type="button" class="{palette_name} selected" class:mb_xs> + .{palette_name}.selected + </button> + <button type="button" class="{palette_name} selected" disabled class:mb_xs> + .{palette_name}.selected:disabled + </button> + <button type="button" class="{palette_name} selected deselectable" class:mb_xs> + .{palette_name}.selected.deselectable + </button> + <button type="button" class="{palette_name} selected deselectable" disabled class:mb_xs> + .{palette_name}.selected.deselectable:disabled + </button> </section> {/each} <div class="box width:100% mb_lg"> @@ -145,7 +147,8 @@ </section> <section> <p> - <code>.selected</code> buttons with <code>.deselectable</code> continue to be clickable when selected: + <code>.selected</code> buttons with <code>.deselectable</code> continue to be clickable when + selected: </p> <Code content={`<button class="selected deselectable">\n\t...\n</button>`} /> </section> @@ -155,11 +158,16 @@ class="width:100% deselectable" class:selected={selected_deselectable_button} onclick={() => (selected_deselectable_button = !selected_deselectable_button)} - >a .deselectable{#if selected_deselectable_button}.selected{:else} unselected{/if} button</button - > - <button type="button" class="width:100% selected deselectable" disabled - >disabled .deselectable.selected</button > + a .deselectable{#if selected_deselectable_button} + .selected + {:else} +  unselected + {/if} button + </button> + <button type="button" class="width:100% selected deselectable" disabled> + disabled .deselectable.selected + </button> </section> </TomeSection> @@ -232,8 +240,8 @@ <TomeSectionHeader text="Size composites" /> <p> The <TomeLink slug="classes" hash="#Composite-classes">size composite classes</TomeLink> - <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code> scale - buttons up and down by overriding custom properties for font, height, and padding. + <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code> + scale buttons up and down by overriding custom properties for font, height, and padding. </p> <Code content={`<button class="xs">xs</button>\n<button class="sm">sm</button>\n<button>md</button>\n<button class="lg">lg</button>\n<button class="xl">xl</button>`} diff --git a/src/routes/docs/chips/+page.svelte b/src/routes/docs/chips/+page.svelte index 882c99770..7f774b0a1 100644 --- a/src/routes/docs/chips/+page.svelte +++ b/src/routes/docs/chips/+page.svelte @@ -66,8 +66,8 @@ <TomeSectionHeader text="Size composites" /> <p> The <TomeLink slug="classes" hash="#Composite-classes">size composite classes</TomeLink> - <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code> scale - chips up and down, adjusting font and padding. + <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code> + scale chips up and down, adjusting font and padding. </p> <Code content={`<span class="chip xs">xs</span>\n<span class="chip sm">sm</span>\n<span class="chip">md</span>\n<span class="chip lg">lg</span>\n<span class="chip xl">xl</span>`} diff --git a/src/routes/docs/classes/+page.svelte b/src/routes/docs/classes/+page.svelte index 455ec94e1..da1ad4b2c 100644 --- a/src/routes/docs/classes/+page.svelte +++ b/src/routes/docs/classes/+page.svelte @@ -36,8 +36,10 @@ {#snippet variant_range<T>(arr: ReadonlyArray<T>)} <span class="text_50">{'{'}</span>{arr[0]}<span class="text_50">-</span>{arr.at(-1)}<span - class="text_50">}</span + class="text_50" > + } + </span> {/snippet} <TomeContent {tome}> @@ -50,13 +52,15 @@ Utility classes complement <TomeLink slug="semantic">semantic styles</TomeLink> and <TomeLink slug="variables">style variables</TomeLink>. Use them to compose styles across component boundaries, or when you prefer classes to the <code><style></code> tag and - <code>style</code> attribute. They're optional and generated on-demand to include only what you use. + <code>style</code> + attribute. They're optional and generated on-demand to include only what you use. </p> <p> 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 - <a href="#Compared-to-alternatives">comparison</a> below. + <a href="#Compared-to-alternatives">comparison</a> + below. </p> <p>Compared to the <code><style></code> tag, classes:</p> <ul> @@ -94,18 +98,19 @@ <Code lang={null} content="npm i -D @fuzdev/fuz_css" /> <p> Use the <ModuleLink module_path="vite_plugin_fuz_css.ts">Vite plugin</ModuleLink> or - <ModuleLink module_path="gen_fuz_css.ts">Gro generator</ModuleLink> to generate bundled CSS that - includes theme variables, base styles, and utility classes: + <ModuleLink module_path="gen_fuz_css.ts">Gro generator</ModuleLink> + to generate bundled CSS that includes theme variables, base styles, and utility classes: </p> <TomeSection> <TomeSectionHeader text="Vite plugin" tag="h3" /> <p> - The <ModuleLink module_path="vite_plugin_fuz_css.ts">Vite plugin</ModuleLink> 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 + The <ModuleLink module_path="vite_plugin_fuz_css.ts">Vite plugin</ModuleLink> 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 <a href="https://github.com/acornjs/acorn-jsx"><code>acorn-jsx</code></a> plugin - see - <a href="#React-and-JSX">React and JSX</a> below. + <a href="#React-and-JSX">React and JSX</a> + below. </p> <Code lang="ts" @@ -119,7 +124,8 @@ export default defineConfig({ });`} /> <p> - Import the virtual module in your entry file, <code>src/routes/+layout.svelte</code> for SvelteKit: + Import the virtual module in your entry file, <code>src/routes/+layout.svelte</code> for + SvelteKit: </p> <Code lang="ts" @@ -145,8 +151,9 @@ import 'virtual:fuz.css';`} /> <p> The plugin extracts classes from files as Vite processes them, including from - <code>node_modules</code> dependencies. It supports HMR: changes to classes in your code trigger - automatic CSS updates. + <code>node_modules</code> + dependencies. It supports HMR: changes to classes in your code trigger automatic CSS + updates. </p> <h4>Plugin options</h4> <ul> @@ -154,8 +161,8 @@ import 'virtual:fuz.css';`} <code>acorn_plugins</code> - required for JSX frameworks, e.g. <code>acorn-jsx</code> </li> <li> - <code>additional_classes</code> - classes to always include (for dynamic patterns that can't - be statically extracted) + <code>additional_classes</code> - classes to always include (for dynamic patterns that + can't be statically extracted) </li> <li> <code>exclude_classes</code> - classes to exclude from output @@ -170,9 +177,9 @@ import 'virtual:fuz.css';`} <code>class_definitions</code>, excluding all default token and composite classes </li> <li> - <code>class_interpreters</code> - <a href="#Custom-interpreters">custom interpreters</a> for - dynamic class generation; replaces the default interpreters entirely if provided (most users - don't need this) + <code>class_interpreters</code> - <a href="#Custom-interpreters">custom interpreters</a> + for dynamic class generation; replaces the default interpreters entirely if provided (most + users don't need this) </li> <li> <code>filter_file</code> - custom filter for which files to process. Receives @@ -191,16 +198,18 @@ import 'virtual:fuz.css';`} <code>cache_dir</code> - cache location; defaults to <code>.fuz/cache/css</code> </li> <li> - <code>base_css</code> - customize or disable base styles; set to <code>null</code> for utility-only - mode, or provide a callback to modify defaults + <code>base_css</code> - customize or disable base styles; set to <code>null</code> for + utility-only mode, or provide a callback to modify defaults </li> <li> - <code>variables</code> - customize or disable theme variables; set to <code>null</code> for - utility-only mode, or provide a callback to modify defaults + <code>variables</code> - customize or disable theme variables; set to <code>null</code> + for utility-only mode, or provide a callback to modify defaults </li> <li> - <code>theme</code> - a <TomeLink slug="themes" /> to bake into the generated CSS, overlaid onto - <code>variables</code> last-wins by name + <code>theme</code> - a <TomeLink slug="themes" /> to bake into the generated CSS, overlaid + onto + <code>variables</code> + last-wins by name </li> <li> <code>additional_elements</code> - elements to always include styles for (for @@ -208,7 +217,8 @@ import 'virtual:fuz.css';`} </li> <li> <code>additional_variables</code> - variables to always include in theme output, or - <code>'all'</code> to include all theme variables + <code>'all'</code> + to include all theme variables </li> </ul> <h4>TypeScript setup</h4> @@ -227,9 +237,9 @@ declare module 'virtual:fuz.css' { <TomeSection> <TomeSectionHeader text="Gro generator" tag="h3" /> <p> - For projects using <a href="https://github.com/fuzdev/gro">Gro</a>, the <ModuleLink - module_path="gen_fuz_css.ts" - /> generator creates a <code>*.gen.css.ts</code> file anywhere in <code>src/</code>: + For projects using <a href="https://github.com/fuzdev/gro">Gro</a>, the + <ModuleLink module_path="gen_fuz_css.ts" /> generator creates a + <code>*.gen.css.ts</code> file anywhere in <code>src/</code>: </p> <Code lang="ts" @@ -270,18 +280,19 @@ import './fuz.css';`} </p> <ul> <li> - <code>include_stats</code> - include file statistics in output (file counts, cache hits/misses, - class counts) + <code>include_stats</code> - include file statistics in output (file counts, cache + hits/misses, class counts) </li> <li> <code>project_root</code> - project root directory; defaults to <code>process.cwd()</code> </li> <li> - <code>concurrency</code> - max concurrent file processing for cache reads and extraction; defaults - to 8 + <code>concurrency</code> - max concurrent file processing for cache reads and extraction; + defaults to 8 </li> <li> - <code>cache_io_concurrency</code> - max concurrent cache writes and deletes; defaults to 50 + <code>cache_io_concurrency</code> - max concurrent cache writes and deletes; defaults to + 50 </li> </ul> </TomeSection> @@ -289,8 +300,9 @@ import './fuz.css';`} <TomeSection> <TomeSectionHeader text="Class detection" tag="h3" /> <p> - The <ModuleLink module_path="css_class_extractor.ts">extractor</ModuleLink> scans your source - files and extracts class names using three automatic mechanisms, plus manual hints for edge cases: + The <ModuleLink module_path="css_class_extractor.ts">extractor</ModuleLink> scans your + source files and extracts class names using three automatic mechanisms, plus manual hints + for edge cases: </p> <h4>1. Direct extraction from class attributes</h4> @@ -301,22 +313,23 @@ import './fuz.css';`} <code>{'class={[...]}'}</code> - array syntax (for clsx-compatible frameworks like Svelte) </li> <li> - <code>{'class={{...}}'}</code> - object syntax (for clsx-compatible frameworks like Svelte) + <code>{'class={{...}}'}</code> - object syntax (for clsx-compatible frameworks like + Svelte) </li> <li><code>{"class={cond ? 'a' : 'b'}"}</code> - ternary expressions</li> <li><code>{"class={(cond && 'a') || 'b'}"}</code> - logical expressions</li> <li><code>class:name</code> - class directives (Svelte)</li> <li> - <code>clsx()</code>, <code>cn()</code>, <code>cx()</code>, <code>classNames()</code> - utility - function calls + <code>clsx()</code>, <code>cn()</code>, <code>cx()</code>, <code>classNames()</code> - + utility function calls </li> </ul> <h4>2. Naming convention</h4> <p> Variables ending with <code>class</code>, <code>classes</code>, <code>className</code>, - <code>classNames</code>, <code>class_name</code>, or <code>class_names</code> (case-insensitive) - are always extracted, regardless of where they're used: + <code>classNames</code>, <code>class_name</code>, or <code>class_names</code> + (case-insensitive) are always extracted, regardless of where they're used: </p> <Code lang="ts" @@ -346,21 +359,21 @@ const turtle_class_name = 'turtle';`} /> <p> Usage tracking works for variables inside <code>clsx()</code>, arrays, ternaries, and - logical expressions within class attributes. Note that standalone <code>clsx()</code> calls outside - class attributes don't trigger tracking - use the naming convention for those cases. + logical expressions within class attributes. Note that standalone <code>clsx()</code> calls + outside class attributes don't trigger tracking - use the naming convention for those cases. </p> <aside> Currently, tracking is single-file only. Cross-module analysis and more sophisticated - inference are potential future improvements. <a - href="https://github.com/fuzdev/fuz_css/discussions">Discussion</a - > is appreciated here. + inference are potential future improvements. + <a href="https://github.com/fuzdev/fuz_css/discussions">Discussion</a> + is appreciated here. </aside> <h4>4. Manual hints</h4> <p> - For dynamically constructed classes that can't be statically analyzed, use the <code - >@fuz-classes</code - > comment: + For dynamically constructed classes that can't be statically analyzed, use the + <code>@fuz-classes</code> + comment: </p> <Code lang="ts" @@ -390,9 +403,9 @@ const color = get_dynamic_color();`} {/each}`} /> <aside> - Edge values like <code>_00</code> and <code>_100</code> are especially easy to miss - they're - generally not used directly in your code (they exist mainly for programmatic usage ergonomics), - so the class won't be generated unless you hint it. + Edge values like <code>_00</code> and <code>_100</code> are especially easy to miss - + they're generally not used directly in your code (they exist mainly for programmatic usage + ergonomics), so the class won't be generated unless you hint it. </aside> <aside> Classes annotated with <code>@fuz-classes</code> and configured with @@ -401,9 +414,9 @@ const color = get_dynamic_color();`} <code>@fuz-classes palette_a_55</code> instead of <code>palette_a_50</code>. </aside> <p> - Alternatively, use the <DeclarationLink name="GenFuzCssOptions" - >additional_classes</DeclarationLink - > option in your config to the Vite plugin or Gro generator: + Alternatively, use the + <DeclarationLink name="GenFuzCssOptions">additional_classes</DeclarationLink> + option in your config to the Vite plugin or Gro generator: </p> <Code lang="ts" @@ -412,9 +425,9 @@ const color = get_dynamic_color();`} });`} /> <p> - Use <DeclarationLink name="GenFuzCssOptions">exclude_classes</DeclarationLink> to filter out false - positives from extraction. This also suppresses warnings for these classes, even if they were - explicitly annotated: + Use <DeclarationLink name="GenFuzCssOptions">exclude_classes</DeclarationLink> to filter out + false positives from extraction. This also suppresses warnings for these classes, even if + they were explicitly annotated: </p> <Code lang="ts" @@ -425,8 +438,8 @@ const color = get_dynamic_color();`} <h4>Element hints</h4> <p> - Similar to <code>@fuz-classes</code>, use <code>@fuz-elements</code> to declare elements that - should be included even when they can't be statically detected: + Similar to <code>@fuz-classes</code>, use <code>@fuz-elements</code> to declare elements + that should be included even when they can't be statically detected: </p> <Code lang="ts" @@ -444,12 +457,13 @@ const el = document.createElement('dialog');`} source files. Only theme variables are included; unknown variables are silently ignored. This approach catches usage in component props like <!-- eslint-disable-next-line svelte/no-useless-mustaches --> - <code>{'size="var(--icon_size_xs)"'}</code> that AST-based extraction would miss. + <code>{'size="var(--icon_size_xs)"'}</code> + that AST-based extraction would miss. </p> <p> - When variable names are constructed at runtime (e.g. with template literals), use <code - >@fuz-variables</code - > to explicitly include them: + When variable names are constructed at runtime (e.g. with template literals), use + <code>@fuz-variables</code> + to explicitly include them: </p> <Code lang="svelte" @@ -466,8 +480,8 @@ const el = document.createElement('dialog');`} /> <aside> Like <code>@fuz-classes</code> and <code>@fuz-elements</code>, explicit declarations via - <code>@fuz-variables</code> produce <strong>errors</strong> if they can't be resolved, helping - catch typos early. + <code>@fuz-variables</code> produce <strong>errors</strong> if they can't be resolved, + helping catch typos early. </aside> <h4>5. Build-time limitations</h4> @@ -477,8 +491,8 @@ const el = document.createElement('dialog');`} framework hydration) won't be detected. </p> <p> - Use <DeclarationLink name="GenFuzCssOptions">additional_elements</DeclarationLink> to force-include - element styles for runtime-created elements: + Use <DeclarationLink name="GenFuzCssOptions">additional_elements</DeclarationLink> to + force-include element styles for runtime-created elements: </p> <Code lang="ts" @@ -501,21 +515,19 @@ const el = document.createElement('dialog');`} <ol> <li> <strong><TomeLink slug="variables">style variables</TomeLink></strong> as - <code>:root</code> custom properties (the full version of which is <ModuleLink - module_path="theme.css" - />) + <code>:root</code> custom properties (the full version of which is + <ModuleLink module_path="theme.css" />) </li> <li> the reset stylesheet's <strong><TomeLink slug="semantic">base styles</TomeLink></strong> for - the HTML elements you use (the full version of which is <ModuleLink - module_path="style.css" - />) + the HTML elements you use (the full version of which is + <ModuleLink module_path="style.css" />) </li> <li>used utility classes (always generated on demand; there's no full version)</li> </ol> <p> - Sections are emitted in that order inside <MdnLink path="Web/CSS/@layer" /> cascade layers: defaults - in <code>fuz.base</code>, theme overrides in <code>fuz.theme</code>, and utilities in + Sections are emitted in that order inside <MdnLink path="Web/CSS/@layer" /> cascade layers: + defaults in <code>fuz.base</code>, theme overrides in <code>fuz.theme</code>, and utilities in <code>fuz.utilities</code>, so utilities beat the reset by layer order (not specificity) and your own unlayered styles beat everything. </p> @@ -539,11 +551,11 @@ const el = document.createElement('dialog');`} </p> <p> For apps that use dynamic HTML patterns, element detection may have false negatives, omitting - styles that you actually need. The reliable fix is to ship the full reset with <code - >additional_elements: 'all'</code - >. Reach for an explicit <DeclarationLink name="CssGeneratorBaseOptions" - >additional_elements</DeclarationLink - > list only when you want to keep the base CSS minimal and know exactly which extra tags appear + styles that you actually need. The reliable fix is to ship the full reset with <code> + additional_elements: 'all' + </code>. Reach for an explicit + <DeclarationLink name="CssGeneratorBaseOptions">additional_elements</DeclarationLink> + list only when you want to keep the base CSS minimal and know exactly which extra tags appear at runtime. </p> <Code @@ -564,18 +576,19 @@ vite_plugin_fuz_css({ attribute or <code><style></code> block, or </li> <li> - it's force-included via <DeclarationLink name="CssGeneratorBaseOptions" - >additional_variables</DeclarationLink - >. + it's force-included via <DeclarationLink name="CssGeneratorBaseOptions"> + additional_variables + </DeclarationLink>. </li> </ul> <p> Dependencies resolve transitively, so pulling in a variable also pulls in any it's built from via - <code>var()</code>, and both <TomeLink slug="themes">light and dark</TomeLink> values are kept together, - so switching color-scheme at runtime never hits a missing variable. The complete set ships in - <ModuleLink module_path="theme.css" /> for utility-only mode and direct imports; bundled mode trims - it to what you use. + <code>var()</code>, and both <TomeLink slug="themes">light and dark</TomeLink> values are kept + together, so switching color-scheme at runtime never hits a missing variable. The complete set + ships in + <ModuleLink module_path="theme.css" /> + for utility-only mode and direct imports; bundled mode trims it to what you use. </p> <h4>Forcing and excluding</h4> @@ -586,8 +599,8 @@ vite_plugin_fuz_css({ <code>additional_variables</code> force-include (the element and variable options also accept <code>'all'</code>), while <code>exclude_classes</code>, <code>exclude_elements</code>, and <code>exclude_variables</code> drop items from the output. Excluding a variable that's still - referenced by included styles logs a warning, since the dropped <code>var(--name)</code> would otherwise - resolve to nothing. + referenced by included styles logs a warning, since the dropped <code>var(--name)</code> would + otherwise resolve to nothing. </p> </TomeSection> @@ -601,8 +614,10 @@ vite_plugin_fuz_css({ design tokens to CSS properties. They're generated programmatically from variant data, making them predictable and systematic. The composites documented <a href="#Composite-classes">below</a> - are hand-written and typically represent higher-level semantic concepts. For raw CSS values, use - <a href="#Literal-classes">literal classes</a> instead. + are hand-written and typically represent higher-level semantic concepts. For raw CSS values, + use + <a href="#Literal-classes">literal classes</a> + instead. </p> <Code content="<p class="pl_xl3 palette_g_50">some token classes</p>" /> <p class="pl_xl3 palette_g_50">some token classes</p> @@ -618,79 +633,79 @@ vite_plugin_fuz_css({ <p>See <TomeLink slug="layout" />.</p> <ul class="unstyled"> <li class="mb_md"> - <span class="code_chips" - ><code>.p_{@render variant_range(space_variants)}</code> <code>.p_0</code> + <span class="code_chips"> + <code>.p_{@render variant_range(space_variants)}</code> <code>.p_0</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.pt_{@render variant_range(space_variants)}</code> <code>.pt_0</code> + <span class="code_chips"> + <code>.pt_{@render variant_range(space_variants)}</code> <code>.pt_0</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.pr_{@render variant_range(space_variants)}</code> <code>.pr_0</code> + <span class="code_chips"> + <code>.pr_{@render variant_range(space_variants)}</code> <code>.pr_0</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.pb_{@render variant_range(space_variants)}</code> <code>.pb_0</code> + <span class="code_chips"> + <code>.pb_{@render variant_range(space_variants)}</code> <code>.pb_0</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.pl_{@render variant_range(space_variants)}</code> <code>.pl_0</code> + <span class="code_chips"> + <code>.pl_{@render variant_range(space_variants)}</code> <code>.pl_0</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.px_{@render variant_range(space_variants)}</code> <code>.px_0</code> + <span class="code_chips"> + <code>.px_{@render variant_range(space_variants)}</code> <code>.px_0</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.py_{@render variant_range(space_variants)}</code> <code>.py_0</code> + <span class="code_chips"> + <code>.py_{@render variant_range(space_variants)}</code> <code>.py_0</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.m_{@render variant_range(space_variants)}</code> <code>.m_0</code> + <span class="code_chips"> + <code>.m_{@render variant_range(space_variants)}</code> <code>.m_0</code> <code>.m_auto</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.mt_{@render variant_range(space_variants)}</code> <code>.mt_0</code> + <span class="code_chips"> + <code>.mt_{@render variant_range(space_variants)}</code> <code>.mt_0</code> <code>.mt_auto</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.mr_{@render variant_range(space_variants)}</code> <code>.mr_0</code> + <span class="code_chips"> + <code>.mr_{@render variant_range(space_variants)}</code> <code>.mr_0</code> <code>.mr_auto</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.mb_{@render variant_range(space_variants)}</code> <code>.mb_0</code> + <span class="code_chips"> + <code>.mb_{@render variant_range(space_variants)}</code> <code>.mb_0</code> <code>.mb_auto</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.ml_{@render variant_range(space_variants)}</code> <code>.ml_0</code> + <span class="code_chips"> + <code>.ml_{@render variant_range(space_variants)}</code> <code>.ml_0</code> <code>.ml_auto</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.mx_{@render variant_range(space_variants)}</code> <code>.mx_0</code> + <span class="code_chips"> + <code>.mx_{@render variant_range(space_variants)}</code> <code>.mx_0</code> <code>.mx_auto</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.my_{@render variant_range(space_variants)}</code> <code>.my_0</code> + <span class="code_chips"> + <code>.my_{@render variant_range(space_variants)}</code> <code>.my_0</code> <code>.my_auto</code> </span> </li> @@ -698,104 +713,105 @@ vite_plugin_fuz_css({ <span class="code_chips"><code>.gap_{@render variant_range(space_variants)}</code></span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.column_gap_{@render variant_range(space_variants)}</code></span - > + <span class="code_chips"> + <code>.column_gap_{@render variant_range(space_variants)}</code> + </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.row_gap_{@render variant_range(space_variants)}</code></span - > + <span class="code_chips"> + <code>.row_gap_{@render variant_range(space_variants)}</code> + </span> </li> <li class="mb_md"> <span class="code_chips"><code>.top_{@render variant_range(space_variants)}</code></span> </li> <li class="mb_md"> - <span class="code_chips"><code>.right_{@render variant_range(space_variants)}</code></span - > + <span class="code_chips"> + <code>.right_{@render variant_range(space_variants)}</code> + </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.bottom_{@render variant_range(space_variants)}</code></span - > + <span class="code_chips"> + <code>.bottom_{@render variant_range(space_variants)}</code> + </span> </li> <li class="mb_md"> <span class="code_chips"><code>.left_{@render variant_range(space_variants)}</code></span> </li> <li class="mb_md"> - <span class="code_chips"><code>.inset_{@render variant_range(space_variants)}</code></span - > + <span class="code_chips"> + <code>.inset_{@render variant_range(space_variants)}</code> + </span> </li> </ul> <aside class="mt_lg"> Padding and margin include <code>_0</code> (and <code>_auto</code> for margin) for ergonomics: <code>pb_0</code> is much shorter than - <code>padding-bottom:0</code>. Other properties use <a href="#Literal-classes">literals</a> for - raw values. + <code>padding-bottom:0</code>. Other properties use <a href="#Literal-classes">literals</a> + for raw values. </aside> <h4>Sizing</h4> <p>See <TomeLink slug="layout" />.</p> <ul class="unstyled"> <li class="mb_md"> - <span class="code_chips"><code>.width_{@render variant_range(space_variants)}</code></span - > + <span class="code_chips"> + <code>.width_{@render variant_range(space_variants)}</code> + </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.height_{@render variant_range(space_variants)}</code></span - > + <span class="code_chips"> + <code>.height_{@render variant_range(space_variants)}</code> + </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.width_atmost_{@render variant_range(distance_variants)}</code> + <span class="code_chips"> + <code>.width_atmost_{@render variant_range(distance_variants)}</code> <code>.width_atleast_{@render variant_range(distance_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.height_atmost_{@render variant_range(distance_variants)}</code> + <span class="code_chips"> + <code>.height_atmost_{@render variant_range(distance_variants)}</code> <code>.height_atleast_{@render variant_range(distance_variants)}</code> </span> </li> </ul> <h4>Colors</h4> <p> - See <TomeLink slug="colors" />, <TomeLink slug="shading" />, and <TomeLink - slug="typography" - hash="Text-colors" - />. + See <TomeLink slug="colors" />, <TomeLink slug="shading" />, and + <TomeLink slug="typography" hash="Text-colors" />. </p> <ul class="unstyled"> <li class="mb_md"> - <span class="code_chips" - ><code - >.palette_{@render variant_range(palette_variants)}_{@render variant_range( + <span class="code_chips"> + <code> + .palette_{@render variant_range(palette_variants)}_{@render variant_range( intensity_variants - )}</code - > + )} + </code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code - >.bg_{@render variant_range(palette_variants)}_{@render variant_range( + <span class="code_chips"> + <code> + .bg_{@render variant_range(palette_variants)}_{@render variant_range( intensity_variants - )}</code - > + )} + </code> </span> </li> <li class="mb_md"> - <span class="code_chips" - >{#each intent_variants as intent (intent)}<code - >.{intent}_{@render variant_range(intensity_variants)}</code - >{/each} + <span class="code_chips"> + {#each intent_variants as intent (intent)} + <code>.{intent}_{@render variant_range(intensity_variants)}</code> + {/each} </span> </li> <li class="mb_md"> - <span class="code_chips" - >{#each intent_variants as intent (intent)}<code - >.bg_{intent}_{@render variant_range(intensity_variants)}</code - >{/each} + <span class="code_chips"> + {#each intent_variants as intent (intent)} + <code>.bg_{intent}_{@render variant_range(intensity_variants)}</code> + {/each} </span> </li> </ul> @@ -805,35 +821,38 @@ vite_plugin_fuz_css({ <code>.shade_50</code> sets the background. The <code>bg_</code> prefix selects the background twin (<code>.bg_a_50</code>, <code>.bg_positive_50</code>). In compound families a letter alone implies the palette: <code>.border_a_50</code> is the palette family, - <code>.border_color_50</code> the alpha ramp. + <code>.border_color_50</code> + the alpha ramp. </aside> <aside> Color and text classes (<code>.palette_a_50</code>, <code>.text_70</code>, etc.) also set <code>--text_color</code>, so nested elements like <code><code></code> that use - <code>color: var(--text_color)</code> inherit the color properly. + <code>color: var(--text_color)</code> + inherit the color properly. </aside> <ul class="unstyled"> <li class="mb_md"> - <span class="code_chips" - ><code>.text_min</code> + <span class="code_chips"> + <code>.text_min</code> <code>.text_max</code> <code>.text_{@render variant_range(text_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.shade_min</code> + <span class="code_chips"> + <code>.shade_min</code> <code>.shade_max</code> <code>.shade_{@render variant_range(shade_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips"><code>.hue_{@render variant_range(palette_variants)}</code></span - > + <span class="code_chips"> + <code>.hue_{@render variant_range(palette_variants)}</code> + </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.darken_{@render variant_range(darken_lighten_variants)}</code> + <span class="code_chips"> + <code>.darken_{@render variant_range(darken_lighten_variants)}</code> <code>.lighten_{@render variant_range(darken_lighten_variants)}</code> </span> </li> @@ -841,31 +860,32 @@ vite_plugin_fuz_css({ <aside> The <code>text_*</code> and <code>shade_*</code> scales are separate because text and backgrounds have different contrast requirements. Use <code>text_*</code> for text colors - and <code>shade_*</code> for backgrounds. Both follow "prominence" semantics for light and dark - modes: low numbers are subtle, high numbers are strong. + and <code>shade_*</code> for backgrounds. Both follow "prominence" semantics for light and + dark modes: low numbers are subtle, high numbers are strong. </aside> <h4>Typography</h4> <p>See <TomeLink slug="typography" />.</p> <ul class="unstyled"> <li class="mb_md"> - <span class="code_chips" - >{#each font_family_variants as font_family (font_family)}<code>.{font_family}</code - >{/each} + <span class="code_chips"> + {#each font_family_variants as font_family (font_family)} + <code>.{font_family}</code> + {/each} </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.font_size_{@render variant_range(font_size_variants)}</code> + <span class="code_chips"> + <code>.font_size_{@render variant_range(font_size_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.line_height_{@render variant_range(line_height_variants)}</code> + <span class="code_chips"> + <code>.line_height_{@render variant_range(line_height_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.icon_size_{@render variant_range(icon_size_variants)}</code> + <span class="code_chips"> + <code>.icon_size_{@render variant_range(icon_size_variants)}</code> </span> </li> </ul> @@ -873,73 +893,73 @@ vite_plugin_fuz_css({ <p>See <TomeLink slug="borders" />.</p> <ul class="unstyled"> <li class="mb_md"> - <span class="code_chips" - ><code>.border_color_{@render variant_range(shade_variants)}</code> + <span class="code_chips"> + <code>.border_color_{@render variant_range(shade_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code - >.border_{@render variant_range(palette_variants)}_{@render variant_range( + <span class="code_chips"> + <code> + .border_{@render variant_range(palette_variants)}_{@render variant_range( intensity_variants - )}</code - > + )} + </code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.border_width_{@render variant_range(border_width_variants)}</code> + <span class="code_chips"> + <code>.border_width_{@render variant_range(border_width_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.border_radius_{@render variant_range(border_radius_variants)}</code> + <span class="code_chips"> + <code>.border_radius_{@render variant_range(border_radius_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.border_top_left_radius_{@render variant_range(border_radius_variants)}</code> + <span class="code_chips"> + <code>.border_top_left_radius_{@render variant_range(border_radius_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.border_top_right_radius_{@render variant_range(border_radius_variants)}</code> + <span class="code_chips"> + <code>.border_top_right_radius_{@render variant_range(border_radius_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.border_bottom_left_radius_{@render variant_range(border_radius_variants)}</code> + <span class="code_chips"> + <code>.border_bottom_left_radius_{@render variant_range(border_radius_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.border_bottom_right_radius_{@render variant_range(border_radius_variants)}</code - > + <span class="code_chips"> + <code>.border_bottom_right_radius_{@render variant_range(border_radius_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.outline_width_{@render variant_range(border_width_variants)}</code> + <span class="code_chips"> + <code>.outline_width_{@render variant_range(border_width_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - >{#each outline_width_variants as variant (variant)}<code>.outline_width_{variant}</code - >{/each} + <span class="code_chips"> + {#each outline_width_variants as variant (variant)} + <code>.outline_width_{variant}</code> + {/each} </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.outline_color_{@render variant_range(shade_variants)}</code> + <span class="code_chips"> + <code>.outline_color_{@render variant_range(shade_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code - >.outline_{@render variant_range(palette_variants)}_{@render variant_range( + <span class="code_chips"> + <code> + .outline_{@render variant_range(palette_variants)}_{@render variant_range( intensity_variants - )}</code - > + )} + </code> </span> </li> </ul> @@ -947,53 +967,52 @@ vite_plugin_fuz_css({ <p>See <TomeLink slug="shadows" />.</p> <ul class="unstyled"> <li class="mb_md"> - <span class="code_chips" - ><code>.shadow_{@render variant_range(shadow_size_variants)}</code> + <span class="code_chips"> + <code>.shadow_{@render variant_range(shadow_size_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.shadow_top_{@render variant_range(shadow_size_variants)}</code> + <span class="code_chips"> + <code>.shadow_top_{@render variant_range(shadow_size_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.shadow_bottom_{@render variant_range(shadow_size_variants)}</code> + <span class="code_chips"> + <code>.shadow_bottom_{@render variant_range(shadow_size_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.shadow_inset_{@render variant_range(shadow_size_variants)}</code> + <span class="code_chips"> + <code>.shadow_inset_{@render variant_range(shadow_size_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.shadow_inset_top_{@render variant_range(shadow_size_variants)}</code> + <span class="code_chips"> + <code>.shadow_inset_top_{@render variant_range(shadow_size_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.shadow_inset_bottom_{@render variant_range(shadow_size_variants)}</code> + <span class="code_chips"> + <code>.shadow_inset_bottom_{@render variant_range(shadow_size_variants)}</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - >{#each shadow_semantic_values as value (value)}<code>.shadow_color_{value}</code - >{/each} + <span class="code_chips"> + {#each shadow_semantic_values as value (value)}<code>.shadow_color_{value}</code>{/each} </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code - >.shadow_{@render variant_range(palette_variants)}_{@render variant_range( + <span class="code_chips"> + <code> + .shadow_{@render variant_range(palette_variants)}_{@render variant_range( intensity_variants - )}</code - > + )} + </code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><code>.shadow_alpha_{@render variant_range(shadow_alpha_variants)}</code> + <span class="code_chips"> + <code>.shadow_alpha_{@render variant_range(shadow_alpha_variants)}</code> </span> </li> </ul> @@ -1082,14 +1101,15 @@ export const custom_composites: Record<string, CssClassDefinition> = { <p> Composites can compose other composites, enabling layered abstractions. Resolution is depth-first: nested composes are fully resolved before the parent's - <code>declaration</code> is appended. Circular references are detected and produce an error. + <code>declaration</code> + is appended. Circular references are detected and produce an error. </p> <h4>What <code>composes</code> can reference</h4> <p> The <code>composes</code> property resolves referenced classes and combines their - declarations. When both <code>composes</code> and <code>declaration</code> are present, the explicit - declaration comes last (winning in the cascade for duplicate properties). + declarations. When both <code>composes</code> and <code>declaration</code> are present, the + explicit declaration comes last (winning in the cascade for duplicate properties). </p> <ul> <li> @@ -1140,8 +1160,8 @@ export const custom_composites: Record<string, CssClassDefinition> = { Composites support <a href="#Modifiers">modifiers</a> like any other class. For <code>composes</code> and <code>declaration</code> - composites, declarations are combined and wrapped. For <code>ruleset</code> composites, modifiers - are applied to each selector (with smart conflict detection): + composites, declarations are combined and wrapped. For <code>ruleset</code> composites, + modifiers are applied to each selector (with smart conflict detection): </p> <Code content={`<!-- hover:foo resolves foo's \`composes\`, applies :hover --> @@ -1195,7 +1215,8 @@ export const gen = gen_fuz_css({ <li><code>.mt_flow</code> - flow-aware margin-top</li> </ul> <p> - <strong>Ruleset-based</strong> (multi-selector, cannot be used in <code>composes</code> arrays): + <strong>Ruleset-based</strong> (multi-selector, cannot be used in <code>composes</code> + arrays): </p> <ul> <li><code>.selectable</code> - selectable element styling</li> @@ -1234,8 +1255,8 @@ export const gen = gen_fuz_css({ contain spaces). Use it for multi-value properties like <code>margin:1px~auto</code>. </p> <p> - Custom properties work directly: <code>--my-var:value</code> sets the property on the element. - This is useful for scoped variables or passing values to child components. + Custom properties work directly: <code>--my-var:value</code> sets the property on the + element. This is useful for scoped variables or passing values to child components. </p> </TomeSection> </TomeSection> @@ -1259,31 +1280,31 @@ export const gen = gen_fuz_css({ </tr> </thead> <tbody> - <tr - ><td><code>sm:</code></td><td>40rem (640px)</td><td - ><code>@media (width >= 40rem)</code></td - ></tr - > - <tr - ><td><code>md:</code></td><td>48rem (768px)</td><td - ><code>@media (width >= 48rem)</code></td - ></tr - > - <tr - ><td><code>lg:</code></td><td>64rem (1024px)</td><td - ><code>@media (width >= 64rem)</code></td - ></tr - > - <tr - ><td><code>xl:</code></td><td>80rem (1280px)</td><td - ><code>@media (width >= 80rem)</code></td - ></tr - > - <tr - ><td><code>2xl:</code></td><td>96rem (1536px)</td><td - ><code>@media (width >= 96rem)</code></td - ></tr - > + <tr> + <td><code>sm:</code></td><td>40rem (640px)</td><td> + <code>@media (width >= 40rem)</code> + </td> + </tr> + <tr> + <td><code>md:</code></td><td>48rem (768px)</td><td> + <code>@media (width >= 48rem)</code> + </td> + </tr> + <tr> + <td><code>lg:</code></td><td>64rem (1024px)</td><td> + <code>@media (width >= 64rem)</code> + </td> + </tr> + <tr> + <td><code>xl:</code></td><td>80rem (1280px)</td><td> + <code>@media (width >= 80rem)</code> + </td> + </tr> + <tr> + <td><code>2xl:</code></td><td>96rem (1536px)</td><td> + <code>@media (width >= 96rem)</code> + </td> + </tr> </tbody> </table> <Code @@ -1310,16 +1331,16 @@ export const gen = gen_fuz_css({ <p>Available state modifiers include:</p> <ul> <li class="mb_md"> - <span class="code_chips" - ><strong>interaction:</strong> <code>hover:</code> <code>focus:</code> + <span class="code_chips"> + <strong>interaction:</strong> <code>hover:</code> <code>focus:</code> <code>focus-visible:</code> <code>focus-within:</code> <code>active:</code> <code>link:</code> <code>visited:</code> <code>any-link:</code> <code>target:</code> </span> </li> <li class="mb_md"> - <span class="code_chips" - ><strong>form:</strong> <code>autofill:</code> <code>blank:</code> <code>disabled:</code> + <span class="code_chips"> + <strong>form:</strong> <code>autofill:</code> <code>blank:</code> <code>disabled:</code> <code>enabled:</code> <code>checked:</code> <code>indeterminate:</code> <code>required:</code> <code>optional:</code> <code>valid:</code> <code>invalid:</code> <code>user-valid:</code> <code>user-invalid:</code> <code>in-range:</code> @@ -1328,8 +1349,8 @@ export const gen = gen_fuz_css({ </span> </li> <li class="mb_md"> - <span class="code_chips" - ><strong>structural:</strong> <code>first:</code> <code>last:</code> <code>only:</code> + <span class="code_chips"> + <strong>structural:</strong> <code>first:</code> <code>last:</code> <code>only:</code> <code>first-of-type:</code> <code>last-of-type:</code> <code>only-of-type:</code> <code>odd:</code> <code>even:</code> <code>empty:</code> <code>nth-child(N):</code> <code>nth-last-child(N):</code> <code>nth-of-type(N):</code> @@ -1337,14 +1358,14 @@ export const gen = gen_fuz_css({ </span> </li> <li class="mb_md"> - <span class="code_chips" - ><strong>UI states:</strong> <code>fullscreen:</code> <code>modal:</code> + <span class="code_chips"> + <strong>UI states:</strong> <code>fullscreen:</code> <code>modal:</code> <code>open:</code> <code>popover-open:</code> </span> </li> <li class="mb_md"> - <span class="code_chips">media: <code>playing:</code> <code>paused:</code> </span> + <span class="code_chips">media: <code>playing:</code> <code>paused:</code></span> </li> </ul> @@ -1441,9 +1462,9 @@ export const gen = gen_fuz_css({ <TomeSection> <TomeSectionHeader text="Builtin classes" /> <p> - fuz_css's <ModuleLink module_path="style.css">main stylesheet</ModuleLink> provides styles for base - HTML elements using <TomeLink slug="variables">style variables</TomeLink>, acting as a modern - CSS reset that adapts to dark mode. It includes CSS classes that provide common generic + fuz_css's <ModuleLink module_path="style.css">main stylesheet</ModuleLink> provides styles for + base HTML elements using <TomeLink slug="variables">style variables</TomeLink>, 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. </p> <h4><code>.unstyled</code></h4> @@ -1470,8 +1491,8 @@ export const gen = gen_fuz_css({ <li>b</li> </ul> <p> - The <code>.unstyled</code> class lets fuz_css provide solid default element styles with a simple - opt-out: + The <code>.unstyled</code> class lets fuz_css provide solid default element styles with a + simple opt-out: </p> <Code lang="css" @@ -1487,8 +1508,8 @@ export const gen = gen_fuz_css({ }`} /> <aside> - The <code>:where()</code> selector keeps specificity as low as possible to minimize interference - with your styles. It's used throughout the reset stylesheet. + The <code>:where()</code> selector keeps specificity as low as possible to minimize + interference with your styles. It's used throughout the reset stylesheet. </aside> <p>See the specific docs sections for more about <code>.unstyled</code>.</p> @@ -1506,9 +1527,8 @@ export const gen = gen_fuz_css({ <TomeLink slug="colors" /> </li> <li> - <code>.dark</code>, <code>.light</code> on <code>:root</code> - see <TomeLink - slug="themes" - /> + <code>.dark</code>, <code>.light</code> on <code>:root</code> - see + <TomeLink slug="themes" /> </li> </ul> </TomeSection> @@ -1519,7 +1539,8 @@ export const gen = gen_fuz_css({ fuz_css ships two plain CSS files, the base <ModuleLink module_path="style.css" /> and <ModuleLink module_path="theme.css" />, that work with any framework and plain HTML. The utility class generator's - <a href="#Class-detection">detection</a> support varies by framework: + <a href="#Class-detection">detection</a> + support varies by framework: </p> <table> <thead> @@ -1563,18 +1584,19 @@ export const gen = gen_fuz_css({ <tr> <td>Vue SFC, Angular, etc.</td> <td>none</td> - <td - >template syntax not parsed; use <code>clsx</code>/<code>cx</code>/<code>cn</code> in JS/TS</td - > + <td> + template syntax not parsed; use <code>clsx</code>/<code>cx</code>/<code>cn</code> in + JS/TS + </td> </tr> </tbody> </table> <p> - The <DeclarationLink name="GenFuzCssOptions">additional_classes</DeclarationLink> plugin config - option is an escape hatch for classes that can't be statically detected. Acorn plugins can be added - via - <DeclarationLink name="GenFuzCssOptions">acorn_plugins</DeclarationLink> for additional syntax support - like <a href="#React-and-JSX">JSX</a>. + The <DeclarationLink name="GenFuzCssOptions">additional_classes</DeclarationLink> plugin + config option is an escape hatch for classes that can't be statically detected. Acorn plugins + can be added via + <DeclarationLink name="GenFuzCssOptions">acorn_plugins</DeclarationLink> for additional syntax + support like <a href="#React-and-JSX">JSX</a>. </p> <p> Out of the box, class generation works only with TypeScript/JS, Svelte, and JSX. Angular is @@ -1585,8 +1607,8 @@ export const gen = gen_fuz_css({ <TomeSection> <TomeSectionHeader text="Svelte-first" tag="h3" /> <p> - The <ModuleLink module_path="css_class_extractor.ts">extractor</ModuleLink> parses and analyzes - the AST to understand + The <ModuleLink module_path="css_class_extractor.ts">extractor</ModuleLink> parses and + analyzes the AST to understand <a href="https://svelte.dev/docs/svelte/class">Svelte's class syntax</a>. Supported constructs: </p> @@ -1606,12 +1628,13 @@ export const gen = gen_fuz_css({ <code>additional_classes</code>) </li> <li> - <strong>Svelte 5 runes:</strong> <code>$derived()</code> and <code>$derived.by()</code> for - class variables + <strong>Svelte 5 runes:</strong> <code>$derived()</code> and <code>$derived.by()</code> + for class variables </li> <li> <strong>utility calls:</strong> <code>clsx()</code>, <code>cn()</code>, <code>cx()</code>, - <code>classNames()</code> with nested arrays, objects, and utility calls + <code>classNames()</code> + with nested arrays, objects, and utility calls </li> <li> <strong>scripts:</strong> both <code><script></code> and @@ -1623,9 +1646,9 @@ export const gen = gen_fuz_css({ <TomeSection> <TomeSectionHeader text="React and JSX" tag="h3" /> <p> - To enable JSX support for React, Preact, Solid, etc, install <a - href="https://github.com/acornjs/acorn-jsx"><code>acorn-jsx</code></a - > and pass it to the plugin or generator: + To enable JSX support for React, Preact, Solid, etc, install + <a href="https://github.com/acornjs/acorn-jsx"><code>acorn-jsx</code></a> + and pass it to the plugin or generator: </p> <Code lang={null} content="npm i -D acorn-jsx" /> <h4>Vite plugin</h4> @@ -1671,7 +1694,8 @@ export const gen = gen_fuz_css({ </li> <li> usage tracking: variables in <code>className</code>, <code>class</code>, and - <code>classList</code> are tracked back to their definitions (has limitations, room for improvement) + <code>classList</code> + are tracked back to their definitions (has limitations, room for improvement) </li> </ul> <Code @@ -1681,8 +1705,8 @@ const styles = 'box hover:shadow_lg'; const Component = () => <div className={styles} />;`} /> <p> - The <DeclarationLink name="GenFuzCssOptions">acorn_plugins</DeclarationLink> option accepts any - Acorn-compatible plugin, so other syntax extensions can be supported the same way. + The <DeclarationLink name="GenFuzCssOptions">acorn_plugins</DeclarationLink> option accepts + any Acorn-compatible plugin, so other syntax extensions can be supported the same way. </p> </TomeSection> </TomeSection> @@ -1690,13 +1714,13 @@ const Component = () => <div className={styles} />;`} <TomeSection> <TomeSectionHeader text="Custom interpreters" /> <p> - <ModuleLink module_path="css_class_interpreters.ts">Interpreters</ModuleLink> dynamically generate - CSS for class names that aren't in the static definitions (which can be extended via + <ModuleLink module_path="css_class_interpreters.ts">Interpreters</ModuleLink> dynamically + generate CSS for class names that aren't in the static definitions (which can be extended via <code>class_definitions</code> or replaced with <code>include_default_classes: false</code>). The default <a href="#Literal-classes">CSS-literal syntax</a> and - <a href="#Modifiers">modifier support</a> are both implemented as interpreters, which you can extend - or replace. + <a href="#Modifiers">modifier support</a> + are both implemented as interpreters, which you can extend or replace. </p> <p> For advanced use cases, you can define custom interpreters that generate CSS from arbitrary @@ -1736,14 +1760,15 @@ vite_plugin_fuz_css({ /> <p> The interpreter context provides access to <code>class_definitions</code>, - <code>css_properties</code> (for validation), and <code>diagnostics</code> (for errors/warnings). - This enables full programmatic control over class-to-CSS generation. + <code>css_properties</code> (for validation), and <code>diagnostics</code> (for + errors/warnings). This enables full programmatic control over class-to-CSS generation. </p> <aside> - Custom interpreters replace the defaults entirely, so include <code - >...css_class_interpreters</code - > - to preserve CSS-literal and modified-class support. This area is experimental and the API may change. + Custom interpreters replace the defaults entirely, so include <code> + ...css_class_interpreters + </code> + to preserve CSS-literal and modified-class support. This area is experimental and the API may + change. </aside> </TomeSection> @@ -1768,10 +1793,10 @@ vite_plugin_fuz_css({ <td>primary syntax</td> <td>DSL-first</td> <td>config-first</td> - <td - ><a href="#Token-classes">token DSL</a> + - <a href="#Literal-classes">CSS literals</a></td - > + <td> + <a href="#Token-classes">token DSL</a> + + <a href="#Literal-classes">CSS literals</a> + </td> </tr> <tr> <td>multi-property</td> @@ -1811,22 +1836,24 @@ vite_plugin_fuz_css({ <code>has-checked:</code>), arbitrary variants (<code>[&.is-dragging]:</code>), child selectors (<code>*:</code>), container queries (<code>@md:</code>), data/ARIA variants, and more. When you need these patterns, fuz_css currently expects you to use rulesets or - <code><style></code> tags, but the API is still a work in progress, and a more powerful and - potentially more TailwindCSS-aligned system is on the table. + <code><style></code> + tags, but the API is still a work in progress, and a more powerful and potentially more + TailwindCSS-aligned system is on the table. </p> <p> 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 - - <a href="https://github.com/fuzdev/fuz_css/discussions">feedback</a> is welcome! + <a href="https://github.com/fuzdev/fuz_css/discussions">feedback</a> + is welcome! </p> <p> 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 <code><style></code> tags, tokens when appropriate, or composites - for repeated patterns. + which nudges you toward <code><style></code> tags, tokens when appropriate, or + composites for repeated patterns. </p> </TomeSection> <hr /> @@ -1834,7 +1861,8 @@ vite_plugin_fuz_css({ <p> <code>fuz_css</code> is still early in development. Your input and feedback is appreciated in the GitHub issues for bugs and - <a href="https://github.com/fuzdev/fuz_css/discussions">discussions</a> for everything else. + <a href="https://github.com/fuzdev/fuz_css/discussions">discussions</a> + for everything else. </p> </section> </TomeContent> diff --git a/src/routes/docs/colors/+page.svelte b/src/routes/docs/colors/+page.svelte index c568e86c6..1b872a7e9 100644 --- a/src/routes/docs/colors/+page.svelte +++ b/src/routes/docs/colors/+page.svelte @@ -31,13 +31,12 @@ <TomeContent {tome}> <section> <p> - fuz_css's colors are <em>derived</em>: around a dozen knobs produce every color <TomeLink - slug="variables" - /> in pure CSS, in the + fuz_css's colors are <em>derived</em>: around a dozen knobs produce every color + <TomeLink slug="variables" /> in pure CSS, in the <MdnLink path="Web/CSS/color_value/oklch" /> colorspace, adapting to the - <MdnLink path="Web/CSS/color-scheme" /> automatically. OKLCH lightness is perceptually uniform (equal - lightness reads equally light in every hue), so rotating a hue knob is safe: contrast and visual - weight hold. + <MdnLink path="Web/CSS/color-scheme" /> + automatically. OKLCH lightness is perceptually uniform (equal lightness reads equally light in + every hue), so rotating a hue knob is safe: contrast and visual weight hold. </p> <p> Hues use letters so themes can reassign colors without breaking semantics ("a" is blue by @@ -58,13 +57,13 @@ <code>--hue_a</code> … <code>--hue_j</code> - OKLCH hue angles for the 10 palette slots </li> <li> - <code>--hue_neutral</code> + <code>--neutral_chroma</code> - the temperature and strength of every - surface, text, border, and shadow tint (the neutral intent; its scales are the shade and text - ramps) + <code>--hue_neutral</code> + <code>--neutral_chroma</code> - the temperature and strength of + every surface, text, border, and shadow tint (the neutral intent; its scales are the shade + and text ramps) </li> <li> - <code>--chroma_scale</code> - one multiplier from grayscale (0) through calm (1) to vivid (above - 1, deliberately clipping the weakest hues) + <code>--chroma_scale</code> - one multiplier from grayscale (0) through calm (1) to vivid + (above 1, deliberately clipping the weakest hues) </li> <li> intent hues - <code>--hue_accent</code>, <code>--hue_positive</code>, @@ -88,15 +87,15 @@ …), each multiplying one slot's chroma under the global <code>--chroma_scale</code>. The brown slot ships muted (<code>--palette_f_chroma_scale: 0.55</code>) because brown is low-chroma orange, unreachable by hue alone. An intent bound to a muted slot needs its twin - set too - bindings share only the hue angle, and <code>validate_theme</code> warns when the character - would be dropped + set too - bindings share only the hue angle, and <code>validate_theme</code> warns when the + character would be dropped </li> </ul> <p> - Every intermediate value these produce is also its own variable (<code - >--palette_lightness_30</code - >, <code>--palette_chroma_50</code>, …), so a theme can pin any individual stop as an escape - hatch. + Every intermediate value these produce is also its own variable (<code> + --palette_lightness_30 + </code>, <code>--palette_chroma_50</code>, …), so a theme can pin any individual stop as an + escape hatch. </p> <Code lang="css" @@ -111,10 +110,11 @@ <TomeSection> <TomeSectionHeader text="Hue variables" /> <p> - Hue variables contain a single OKLCH <MdnLink path="Web/CSS/hue" /> angle. Because lightness and - chroma are shared across all hues at each stop, the scales are interchangeable: setting a hue alone - is enough, no per-hue tuning required. The one deliberate exception is the per-slot chroma multiplier - - the brown slot ships muted because no hue angle renders brown at full palette chroma. + Hue variables contain a single OKLCH <MdnLink path="Web/CSS/hue" /> angle. Because lightness + and chroma are shared across all hues at each stop, the scales are interchangeable: setting a + hue alone is enough, no per-hue tuning required. The one deliberate exception is the per-slot + chroma multiplier - the brown slot ships muted because no hue angle renders brown at full + palette chroma. </p> <p> Hue variables are also useful to construct custom colors not covered by the palette. For @@ -141,8 +141,8 @@ </p> <p> Unlike the <TomeLink slug="shading">shade</TomeLink> and - <TomeLink slug="typography" hash="Text-colors">text</TomeLink> scales (which are separate), palette - variables can be used for both text and backgrounds via utility classes: + <TomeLink slug="typography" hash="Text-colors">text</TomeLink> scales (which are separate), + palette variables can be used for both text and backgrounds via utility classes: <code>.palette_a_50</code> sets text color, <code>.bg_a_50</code> sets background color. </p> <p> diff --git a/src/routes/docs/elements/+page.svelte b/src/routes/docs/elements/+page.svelte index f6bf31f59..fc2ea5bc9 100644 --- a/src/routes/docs/elements/+page.svelte +++ b/src/routes/docs/elements/+page.svelte @@ -26,19 +26,23 @@ <section> <p> fuz_css applies default styles to semantic HTML elements in its <SourceFileLink - path="style.css">reset stylesheet</SourceFileLink - >. The styles use <TomeLink slug="variables" /> and include appropriate spacing, so plain HTML gets - user-friendly styling and theme integration automatically. The defaults are low specificity using + path="style.css" + > + reset stylesheet + </SourceFileLink>. The styles use <TomeLink slug="variables" /> and include appropriate + spacing, so plain HTML gets user-friendly styling and theme integration automatically. The + defaults are low specificity using <code>:where</code> so they're easy to override, and you can opt out by adding - <code>.unstyled</code> to an element. + <code>.unstyled</code> + to an element. </p> </section> - <UnfinishedImplementationWarning - >Element styles are incomplete. Any <a href="https://github.com/fuzdev/fuz_css/issues" - >requests</a - >?</UnfinishedImplementationWarning - > + <UnfinishedImplementationWarning> + Element styles are incomplete. Any <a href="https://github.com/fuzdev/fuz_css/issues"> + requests + </a>? + </UnfinishedImplementationWarning> <TomeSection> <TomeSectionHeader text="p"> <MdnLink path="Web/HTML/Element/p" /> @@ -50,13 +54,14 @@ <p>p</p> <p>p</p> <p> - This paragraph has no bottom margin because default spacing is omitted for the <code - >:last-child</code - > - of all otherwise-spaced elements, streamlining the common case. This has some unfortunate edge cases - that can usually be solved by adding <code>.mb_lg</code>. Coupling markup structure to styles - like this may be something we change, <a href="https://github.com/fuzdev/fuz_css">feedback</a> is - welcome. + This paragraph has no bottom margin because default spacing is omitted for the <code> + :last-child + </code> + of all otherwise-spaced elements, streamlining the common case. This has some unfortunate edge + cases that can usually be solved by adding <code>.mb_lg</code>. Coupling markup structure to + styles like this may be something we change, + <a href="https://github.com/fuzdev/fuz_css">feedback</a> + is welcome. </p> </TomeSection> <TomeSection> @@ -103,9 +108,9 @@ /> </details> <details class="unstyled"> - <summary class="unstyled" - ><code>details</code> and <code>summary</code> with <code>.unstyled</code></summary - > + <summary class="unstyled"> + <code>details</code> and <code>summary</code> with <code>.unstyled</code> + </summary> unstyled details content </details> </TomeSection> diff --git a/src/routes/docs/examples/+page.svelte b/src/routes/docs/examples/+page.svelte index 974bbffdb..f1cb91752 100644 --- a/src/routes/docs/examples/+page.svelte +++ b/src/routes/docs/examples/+page.svelte @@ -12,11 +12,11 @@ <TomeContent {tome}> <section class="column gap_md"> <p> - The <a href={GITHUB_EXAMPLES}>example repos</a> demonstrate the <TomeLink slug="classes" /> system - using the - <a href="https://github.com/fuzdev/fuz_css/blob/main/src/lib/vite_plugin_fuz_css.ts" - >Vite plugin</a - >: + The <a href={GITHUB_EXAMPLES}>example repos</a> demonstrate the + <TomeLink slug="classes" /> system using the + <a href="https://github.com/fuzdev/fuz_css/blob/main/src/lib/vite_plugin_fuz_css.ts"> + Vite plugin + </a>: </p> <ul> <li><a href="{GITHUB_EXAMPLES}/vite-svelte">vite-svelte</a> - Svelte 5</li> diff --git a/src/routes/docs/forms/+page.svelte b/src/routes/docs/forms/+page.svelte index 31271f411..d6e23bbe6 100644 --- a/src/routes/docs/forms/+page.svelte +++ b/src/routes/docs/forms/+page.svelte @@ -87,14 +87,13 @@ /> </label> <p> - More info can be included in <code>{'<'}p></code> tags like this one. Here we could include - info about passwords. + More info can be included in <code>{'<'}p></code> tags like this one. Here we could + include info about passwords. </p> <label class:disabled={created_account}> <div class="title">lifestory</div> - <textarea bind:value={lifestory} disabled={created_account} placeholder="👀" - ></textarea></label - > + <textarea bind:value={lifestory} disabled={created_account} placeholder="👀"></textarea> + </label> <label class:disabled={created_account}> <div class="title">select</div> <select class="text-align:center font_size_xl5" disabled={created_account}> @@ -142,9 +141,9 @@ <TomeSectionHeader text="form with checkboxes"> <code>form</code> with checkboxes </TomeSectionHeader> - <UnfinishedImplementationWarning - >This will change, probably to toggles.</UnfinishedImplementationWarning - > + <UnfinishedImplementationWarning> + This will change, probably to toggles. + </UnfinishedImplementationWarning> <!-- TODO make this a form, but figure out the checkbox problem - maybe a last-child exception? --> <form> <fieldset> @@ -158,20 +157,20 @@ </label> <label class="row disabled"> <input type="checkbox" disabled style:margin-right="var(--space_lg)" /> - <Code content={`<input type="checkbox" disabled />`} /> (disabled) + <Code content={`<input type="checkbox" disabled />`} /> + (disabled) </label> <label class="row disabled"> <input type="checkbox" checked disabled /> - <Code content={`<input type="checkbox" checked disabled />`} /> (disabled) + <Code content={`<input type="checkbox" checked disabled />`} /> + (disabled) </label> </fieldset> </form> <aside> The above are wrapped with: <Code content={`<label class="row">`} inline /> - with <code>.disabled</code> as needed: <Code - content={`<label class="row disabled">`} - inline - /> + with <code>.disabled</code> as needed: + <Code content={`<label class="row disabled">`} inline /> </aside> </TomeSection> @@ -183,14 +182,14 @@ <fieldset> {#each radio_items as radio_item (radio_item)} {@const selected = radio_item === selected_radio_item} - <label class="row" class:selected - ><input type="radio" bind:group={selected_radio_item} value={radio_item} /> + <label class="row" class:selected> + <input type="radio" bind:group={selected_radio_item} value={radio_item} /> <Code content={`<label class="row${selected ? ' selected' : ''}">\n\t<input type="radio" ${ selected ? 'checked' : '' }/>\n</label>`} - /></label - > + /> + </label> {/each} <label class="row disabled"> <input type="radio" disabled /> @@ -210,9 +209,9 @@ <TomeSectionHeader text="Size composites" /> <p> The <TomeLink slug="classes" hash="#Composite-classes">size composite classes</TomeLink> - <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code> scale - inputs and buttons, adjusting height and padding. Apply directly or on a container to cascade to - children. + <code>.xs</code>, <code>.sm</code>, <code>.md</code>, <code>.lg</code>, and <code>.xl</code> + scale inputs and buttons, adjusting height and padding. Apply directly or on a container to + cascade to children. </p> <Code content={`<input class="xs" />\n<input class="sm" />\n<input />\n<input class="lg" />\n<input class="xl" />`} diff --git a/src/routes/docs/introduction/+page.svelte b/src/routes/docs/introduction/+page.svelte index 156da98bf..899f78dd4 100644 --- a/src/routes/docs/introduction/+page.svelte +++ b/src/routes/docs/introduction/+page.svelte @@ -22,9 +22,9 @@ <Code lang={null} content="npm i -D @fuzdev/fuz_css" /> <p> Use the <TomeLink slug="classes" hash="Vite-plugin">Vite plugin</TomeLink> or - <TomeLink slug="classes" hash="Gro-generator">Gro generator</TomeLink> for bundled CSS that includes - theme variables, base styles, and utility classes in a single import. Add the plugin to your Vite - config: + <TomeLink slug="classes" hash="Gro-generator">Gro generator</TomeLink> + for bundled CSS that includes theme variables, base styles, and utility classes in a single + import. Add the plugin to your Vite config: </p> <Code lang="ts" @@ -65,18 +65,19 @@ import 'virtual:fuz.css'; // used utilities only`} <li>plain CSS</li> <li>minimal dependencies, all optional (none needed if you only use the stylesheets)</li> <li> - exports a reset stylesheet with <TomeLink slug="semantic" /> defaults that styles HTML elements, - and also exports the underlying data, helpers, and types for open-ended usage + exports a reset stylesheet with <TomeLink slug="semantic" /> defaults that styles HTML + elements, and also exports the underlying data, helpers, and types for open-ended usage </li> <li> - supports <TomeLink slug="themes" /> as sets of style variables, applied at build time through - the generators' <code>theme</code> option or swapped at runtime - dark mode is a + supports <TomeLink slug="themes" /> as sets of style variables, applied at build time + through the generators' <code>theme</code> option or swapped at runtime - dark mode is a first-class concept, not a theme; instead, each theme can support light and/or dark <MdnLink path="Web/CSS/color-scheme">color-schemes</MdnLink> </li> <li> - supports optional <TomeLink slug="classes">utility classes</TomeLink> with three types (token, - composite, CSS-literal) and modifiers for responsive, state, color-scheme, and pseudo-elements + supports optional <TomeLink slug="classes">utility classes</TomeLink> with three types + (token, composite, CSS-literal) and modifiers for responsive, state, color-scheme, and + pseudo-elements </li> <li> uses its own concept of style <TomeLink slug="variables" />, a specialization of CSS @@ -92,9 +93,10 @@ import 'virtual:fuz.css'; // used utilities only`} <a href="https://ui.fuz.dev/">fuz_ui</a> </li> <li> - see the <TomeLink slug="classes" hash="Compared-to-alternatives" - >comparison to alternatives</TomeLink - > to understand fuz_css relative to TailwindCSS and UnoCSS + see the <TomeLink slug="classes" hash="Compared-to-alternatives"> + comparison to alternatives + </TomeLink> + to understand fuz_css relative to TailwindCSS and UnoCSS </li> </ul> </TomeSection> diff --git a/src/routes/docs/menuitem/todo.svelte b/src/routes/docs/menuitem/todo.svelte index 7492ec3df..0ec1a4b9d 100644 --- a/src/routes/docs/menuitem/todo.svelte +++ b/src/routes/docs/menuitem/todo.svelte @@ -82,13 +82,13 @@ <div class="content"> <div class="icon">a</div> <div class="title"> - <span class="text" - ><Code + <span class="text"> + <Code content={`<div class="menuitem selectable${ clicked === 'a' ? ' selected' : '' }">`} - /></span - > + /> + </span> </div> </div> </div> @@ -105,13 +105,13 @@ <div class="content"> <div class="icon">b</div> <div class="title"> - <span class="text" - ><Code + <span class="text"> + <Code content={`<div class="menuitem selectable${ clicked === 'b' ? ' selected' : '' }">`} - /></span - > + /> + </span> </div> </div> </div> @@ -128,13 +128,13 @@ <div class="content"> <div class="icon">c</div> <div class="title"> - <span class="text" - ><Code + <span class="text"> + <Code content={`<div class="menuitem selectable${ clicked === 'c' ? ' selected' : '' }">`} - /></span - > + /> + </span> </div> </div> </div> diff --git a/src/routes/docs/semantic/+page.svelte b/src/routes/docs/semantic/+page.svelte index 3d6f904c1..c2efdad9e 100644 --- a/src/routes/docs/semantic/+page.svelte +++ b/src/routes/docs/semantic/+page.svelte @@ -17,11 +17,11 @@ <TomeContent {tome}> <section> <p> - fuz_css styles HTML elements in its <SourceFileLink path="style.css" - >reset stylesheet</SourceFileLink - >, so semantic markup gets themed and color-scheme-aware styling automatically - utility - classes optional. The goal is to be accessible and attractive out of the box, minimal yet - extensible. + fuz_css styles HTML elements in its <SourceFileLink path="style.css"> + reset stylesheet + </SourceFileLink>, so semantic markup gets themed and color-scheme-aware styling automatically + - utility classes optional. The goal is to be accessible and attractive out of the box, + minimal yet extensible. </p> </section> @@ -52,9 +52,9 @@ <TomeSectionHeader text=".unstyled escape hatch" /> <p> Add the <code>.unstyled</code> - <TomeLink slug="classes" hash="Builtin-classes">builtin class</TomeLink> to opt out of decorative - styling while keeping reset normalizations. Works for both decorative containers and interactive - elements like links, buttons, inputs, and summary. + <TomeLink slug="classes" hash="Builtin-classes">builtin class</TomeLink> + to opt out of decorative styling while keeping reset normalizations. Works for both decorative + containers and interactive elements like links, buttons, inputs, and summary. </p> <Code lang="svelte" @@ -94,19 +94,20 @@ <p> The <code>--flow_margin</code> variable is unset by default, falling back to <code>var(--space_lg)</code>. Size composite classes like <code>.sm</code> and - <code>.lg</code> set <code>--flow_margin</code> to adjust vertical rhythm for all flow elements - and headings. + <code>.lg</code> set <code>--flow_margin</code> to adjust vertical rhythm for all flow + elements and headings. </p> <p> For elements not in the flow list, use the <code>.mb_flow</code> and <code>.mt_flow</code> - composite classes to get the same size-responsive spacing. Use <code>.mb_lg</code> when you want - a fixed value that ignores size composites. + composite classes to get the same size-responsive spacing. Use <code>.mb_lg</code> when you + want a fixed value that ignores size composites. </p> <aside> ⚠️ The <code>:not(:last-child)</code> creates unfortunate edge cases by coupling structure to style, including usage with Svelte's component-level CSS variables, because it adds a wrapper div. Perhaps the better global optimum is to omit the last child exception? This would add - unwanted margin in many cases, but perhaps that's better overall; <code>mb_0</code> removes it. + unwanted margin in many cases, but perhaps that's better overall; <code>mb_0</code> removes + it. </aside> </TomeSection> diff --git a/src/routes/docs/shading/+page.svelte b/src/routes/docs/shading/+page.svelte index 4b02046c2..9e87d639b 100644 --- a/src/routes/docs/shading/+page.svelte +++ b/src/routes/docs/shading/+page.svelte @@ -30,8 +30,8 @@ fuz_css offers a shading model built on <em>adaptive</em> style variables that respond to the <MdnLink path="Web/CSS/color-scheme" />. Adaptive means the underlying values change between light and dark modes to maintain consistent prominence: low numbers stay subtle, high numbers - stay strong. Each <TomeLink slug="themes">theme</TomeLink> can implement light mode, dark mode, - or both. + stay strong. Each <TomeLink slug="themes">theme</TomeLink> can implement light mode, dark + mode, or both. </p> <p> Light mode's starting point is plain white documents (like paper) where we subtract light to @@ -46,9 +46,8 @@ <TomeSection> <TomeSectionHeader text="The shade scale" /> <p> - The shade scale is the primary system for backgrounds and surfaces. All numbered shades (<code - >shade_00</code - > + The shade scale is the primary system for backgrounds and surfaces. All numbered shades + (<code>shade_00</code> through <code>shade_100</code>) are tinted using the theme's <code>hue_neutral</code> and <code>neutral_chroma</code> for visual cohesion. The scale also includes two untinted extremes (<code>shade_min</code> and <code>shade_max</code>) for maximum @@ -87,9 +86,9 @@ <ColorSchemeInput /> <aside class="mt_xl2 width_atmost_sm mx_auto"> <p> - tip: Try <button type="button" onclick={toggle_color_scheme}>toggling</button> between light and - dark to see how the shade scale adapts. Lower numbers stay near the surface, higher numbers move - toward maximum contrast. + tip: Try <button type="button" onclick={toggle_color_scheme}>toggling</button> between light + and dark to see how the shade scale adapts. Lower numbers stay near the surface, higher + numbers move toward maximum contrast. </p> </aside> </section> @@ -102,12 +101,12 @@ </p> <ul> <li> - <code>fg_NN</code> (foreground direction) - darkens in light mode, lightens in dark mode; use - for elevated surfaces like panels, cards, and hover states + <code>fg_NN</code> (foreground direction) - darkens in light mode, lightens in dark mode; + use for elevated surfaces like panels, cards, and hover states </li> <li> - <code>bg_NN</code> (background direction) - lightens in light mode, darkens in dark mode; use - for surfaces that blend toward the background + <code>bg_NN</code> (background direction) - lightens in light mode, darkens in dark mode; + use for surfaces that blend toward the background </li> </ul> <p> @@ -143,8 +142,8 @@ <TomeSection> <TomeSectionHeader text="Stacking behavior" tag="h4" /> <p> - Unlike the opaque shade scale, alpha overlays <strong>stack</strong> when nested. Each layer adds - more contrast: + Unlike the opaque shade scale, alpha overlays <strong>stack</strong> when nested. Each layer + adds more contrast: </p> <Code content={`<div class="fg_10 p_sm"> @@ -228,7 +227,8 @@ <TomeSection> <TomeSectionHeader text="When to use which" /> <p> - <strong>Use <code>fg_NN</code></strong> when you need stacking behavior or are building nested UI: + <strong>Use <code>fg_NN</code></strong> when you need stacking behavior or are building nested + UI: </p> <Code lang="css" @@ -242,7 +242,8 @@ background-color: var(--fg_10); background-color: var(--fg_20);`} /> <p class="mt_md"> - <strong>Use <code>shade_NN</code></strong> when you need explicit, predictable opaque surfaces: + <strong>Use <code>shade_NN</code></strong> when you need explicit, predictable opaque + surfaces: </p> <Code lang="css" @@ -257,7 +258,8 @@ background-color: var(--shade_min);`} /> <p class="mt_lg"> The composites (<code>.panel</code>, <code>.chip</code>, <code>.menuitem</code>) use - <code>fg_NN</code> for stacking. The page background uses <code>shade_00</code> as the opaque base. + <code>fg_NN</code> for stacking. The page background uses <code>shade_00</code> as the opaque + base. </p> </TomeSection> <TomeSection> diff --git a/src/routes/docs/shadows/+page.svelte b/src/routes/docs/shadows/+page.svelte index 2bfbeb783..b24cdd4e5 100644 --- a/src/routes/docs/shadows/+page.svelte +++ b/src/routes/docs/shadows/+page.svelte @@ -28,15 +28,16 @@ </script> <TomeContent {tome}> - <UnfinishedImplementationWarning - >This is unfinished and will change. It feels simultaneously limiting in usage and bloated in - the implementation.</UnfinishedImplementationWarning - > + <UnfinishedImplementationWarning> + This is unfinished and will change. It feels simultaneously limiting in usage and bloated in the + implementation. + </UnfinishedImplementationWarning> <section> <p> fuz_css provides four semantic shadow types that build on the light model in the - <TomeLink slug="shading" /> docs: umbra for natural depth, highlight for rim lighting, glow for - light emphasis, and shroud for dark overlays. + <TomeLink slug="shading" /> + docs: umbra for natural depth, highlight for rim lighting, glow for light emphasis, and shroud + for dark overlays. </p> </section> <TomeSection> @@ -88,8 +89,9 @@ <TomeSection> <TomeSectionHeader text="Colored shadows" /> <p> - Use <code>shadow_{'{letter}'}_{'{intensity}'}</code> classes to apply colored shadows. The intensity - controls the color's prominence; 60 is a fine starting point for visible colored shadows. + Use <code>shadow_{'{letter}'}_{'{intensity}'}</code> classes to apply colored shadows. The + intensity controls the color's prominence; 60 is a fine starting point for visible colored + shadows. </p> {#each palette_variants as color_variant (color_variant)} <TomeSection> @@ -122,12 +124,12 @@ {#if is_hue} <code class="p_xs {classes}">.{shadow_color_name}</code> {:else} - <StyleVariableButton name={shadow_color_name} class={classes} - ><span> + <StyleVariableButton name={shadow_color_name} class={classes}> + <span> {shadow_color_name} - {#if color_variant === 'umbra'} <small>(default)</small>{/if}</span - ></StyleVariableButton - > + {#if color_variant === 'umbra'} <small>(default)</small>{/if} + </span> + </StyleVariableButton> {/if} </div> {@render shadow_variant_examples(shadow_color_name, shadow_size_name)} @@ -161,9 +163,9 @@ {/snippet} {#snippet intensity_selector(color_variant: PaletteVariant)} - <TomeSectionHeader text="shadow_{color_variant}" tag="h3" - >shadow_{color_variant}_{selected_intensity}</TomeSectionHeader - > + <TomeSectionHeader text="shadow_{color_variant}" tag="h3"> + shadow_{color_variant}_{selected_intensity} + </TomeSectionHeader> <form class="intensity_selector"> <fieldset class="row mb_0"> {#each intensity_variants as intensity (intensity)} diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index 39d95f3e4..33ef21198 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -109,9 +109,9 @@ <TomeContent {tome}> <TomeSection> <TomeSectionHeader text="Themes" /> - <UnfinishedImplementationWarning - >The theme set is still growing, but the proof of concept is ready!</UnfinishedImplementationWarning - > + <UnfinishedImplementationWarning> + The theme set is still growing, but the proof of concept is ready! + </UnfinishedImplementationWarning> <p> A theme is a set of <em>knob</em> values, not a stylesheet: a JSON collection of <TomeLink slug="variables" /> where a handful of high-leverage variables (hue angles, @@ -141,14 +141,15 @@ </label> <p> Contrast is a <em>modifier</em>, not a theme: the low/high contrast pair compose over any - selected theme with <code>compose_themes</code> (flatten + last-wins), so every theme has low and - high contrast variants for free. + selected theme with <code>compose_themes</code> (flatten + last-wins), so every theme has low + and high contrast variants for free. </p> <p> Every theme ships as an importable module under <code>themes/</code>, and theme CSS renders into the <code>fuz.theme</code> cascade layer, above the <code>fuz.base</code> defaults, so overrides win regardless of stylesheet order. See <ModuleLink module_path="theme.ts" /> and - <ModuleLink module_path="themes.ts" /> for the API: + <ModuleLink module_path="themes.ts" /> + for the API: </p> <Code lang="ts" @@ -157,8 +158,8 @@ <p> A theme can declare a single-scheme stance with <code>scheme: 'light' | 'dark'</code> - its one appearance then renders in both color schemes and - <MdnLink path="Web/CSS/color-scheme" /> is pinned to match. The necromancer and terminalien themes - are dark-only this way. + <MdnLink path="Web/CSS/color-scheme" /> + is pinned to match. The necromancer and terminalien themes are dark-only this way. </p> </TomeSection> <TomeSection> @@ -169,8 +170,9 @@ </p> <p> Pass a theme to the <TomeLink slug="classes" hash="Vite-plugin">Vite plugin</TomeLink> or - <TomeLink slug="classes" hash="Gro-generator">Gro generator</TomeLink> and its values bake into - the generated CSS - no runtime rendering, no JavaScript shipped, and the output stays tree-shaken: + <TomeLink slug="classes" hash="Gro-generator">Gro generator</TomeLink> + and its values bake into the generated CSS - no runtime rendering, no JavaScript shipped, and + the output stays tree-shaken: </p> <Code lang="ts" @@ -184,14 +186,15 @@ export default defineConfig({plugins: [vite_plugin_fuz_css({theme: necromancer_t It overlays the default variables last-wins by name, so it composes with the <code>variables</code> option. Unlike the runtime path it doesn't pin <MdnLink path="Web/CSS/color-scheme" /> - for a single-scheme theme, add the - <code>dark</code> or <code>light</code> class to the root <code>html</code> element, as in the next - section. + <code>dark</code> or <code>light</code> class to the root <code>html</code> element, as in the + next section. </p> <p> For runtime switching - a picker, or a theme loaded per user - use <code>ThemeRoot</code> from <a href="https://ui.fuz.dev/">fuz_ui</a>, which renders the theme to a - <code>style</code> element. The two compose: the build-time theme is the starting point, and a runtime - theme overrides it by cascade layer. + <code>style</code> + element. The two compose: the build-time theme is the starting point, and a runtime theme + overrides it by cascade layer. </p> </TomeSection> <TomeSection> @@ -199,11 +202,12 @@ export default defineConfig({plugins: [vite_plugin_fuz_css({theme: necromancer_t <p> fuz_css supports <MdnLink path="Web/CSS/color-scheme" /> with dark and light modes, detected from - <MdnLink path="Web/CSS/@media/prefers-color-scheme" /> by default. To apply dark mode manually, - add the <code>dark</code> class to the root <code>html</code> element, or use a component like - <a href="https://github.com/fuzdev/fuz_ui/blob/main/src/lib/ColorSchemeInput.svelte" - >this one</a - > + <MdnLink path="Web/CSS/@media/prefers-color-scheme" /> by default. To apply dark mode + manually, add the <code>dark</code> class to the root <code>html</code> element, or use a + component like + <a href="https://github.com/fuzdev/fuz_ui/blob/main/src/lib/ColorSchemeInput.svelte"> + this one + </a> from the companion Svelte library <a href="https://ui.fuz.dev/">fuz_ui</a>: </p> <div class="display:flex mb_lg"> @@ -220,7 +224,8 @@ export default defineConfig({plugins: [vite_plugin_fuz_css({theme: necromancer_t Drag a knob and the whole page rethemes live - extreme values can make the page hard to read, which is an honest signal, not a bug. Every edit updates a temporary "{UNSAVED_THEME_NAME}" theme in the picker above; it survives navigating away and back, but copy the - <code>Theme</code> object below to keep it. + <code>Theme</code> + object below to keep it. </p> <ThemeEditor {editor} {theme_state} /> </TomeSection> @@ -231,14 +236,15 @@ export default defineConfig({plugins: [vite_plugin_fuz_css({theme: necromancer_t <code>Theme</code>. </p> <p> - <code>validate_theme(theme)</code> is the structural lint: unknown variable names are errors, while - type and range mismatches on the knob-tier variables are advisory warnings. It returns an array - of issues - empty means the theme is structurally sound. + <code>validate_theme(theme)</code> is the structural lint: unknown variable names are errors, + while type and range mismatches on the knob-tier variables are advisory warnings. It returns + an array of issues - empty means the theme is structurally sound. </p> <p> <code>check_theme(theme)</code> runs the gamut, ramp-monotonicity, and contrast gates against the theme's resolved values. It is report-only and never throws, returning - <code>{'{ok, entries, unchecked}'}</code> - suited to a CI or test assertion: + <code>{'{ok, entries, unchecked}'}</code> + - suited to a CI or test assertion: </p> <Code lang="ts" diff --git a/src/routes/docs/typography/+page.svelte b/src/routes/docs/typography/+page.svelte index 07d905535..ff096d3bd 100644 --- a/src/routes/docs/typography/+page.svelte +++ b/src/routes/docs/typography/+page.svelte @@ -109,13 +109,15 @@ </form> {#each font_size_variants as size (size.name)} <div class="row flex-wrap:wrap"> - <StyleVariableButton title={size.light} name={size.name} - ><span + <StyleVariableButton title={size.light} name={size.name}> + <span style:font-size="var(--{size.name})" style:font-weight={selected_font_weight} - class="font_family_sans">{size.name}</span - ></StyleVariableButton - > + class="font_family_sans" + > + {size.name} + </span> + </StyleVariableButton> <div class="row"> <span class="pr_sm">=</span> <code>{computed_styles?.getPropertyValue('--' + size.name)}</code> @@ -151,7 +153,8 @@ The text scale (<code>text_00</code> through <code>text_100</code>) provides tinted neutral colors optimized for text legibility. The scale uses "prominence" semantics for light and dark modes: low numbers are subtle, high numbers are strong. This matches the - <TomeLink slug="shading">shade scale</TomeLink> pattern. + <TomeLink slug="shading">shade scale</TomeLink> + pattern. </p> <ul> <li><code>text_00</code> - surface-side endpoint: essentially invisible on surface</li> @@ -174,11 +177,11 @@ {#each text_scale_variants as variant (variant)} {@const name = 'text_' + variant} <div class="row"> - <StyleVariableButton {name} - ><span class="font_family_mono" style:color="var(--{name})"> + <StyleVariableButton {name}> + <span class="font_family_mono" style:color="var(--{name})"> {name} - </span></StyleVariableButton - > = <code>{computed_styles?.getPropertyValue('--' + name)}</code> + </span> + </StyleVariableButton> = <code>{computed_styles?.getPropertyValue('--' + name)}</code> </div> {/each} </div> @@ -190,16 +193,16 @@ <div> {#each line_height_names as name (name)} <div> - <StyleVariableButton {name} - ><div style:line-height="var(--{name})" class="button_contents font_family_mono"> + <StyleVariableButton {name}> + <div style:line-height="var(--{name})" class="button_contents font_family_mono"> <div> {name} = <code>{computed_styles?.getPropertyValue('--' + name)}</code> </div> <div>{name}</div> <div>{name}</div> - </div></StyleVariableButton - > + </div> + </StyleVariableButton> </div> {/each} </div> @@ -241,8 +244,9 @@ </TomeSectionHeader> <p> The <code>.md</code> - <TomeLink slug="classes" hash="#Composite-classes">composite class</TomeLink> resets sizing to the - defaults. Use it inside a sized container to restore normal sizing for a subtree. + <TomeLink slug="classes" hash="#Composite-classes">composite class</TomeLink> + resets sizing to the defaults. Use it inside a sized container to restore normal sizing for a + subtree. </p> <Code content={`<div class="sm">\n\t<p>small text</p>\n\t<div class="md">\n\t\t<p>back to normal</p>\n\t</div>\n</div>`} diff --git a/src/routes/docs/variables/+page.svelte b/src/routes/docs/variables/+page.svelte index 99567af78..fc1b2d62a 100644 --- a/src/routes/docs/variables/+page.svelte +++ b/src/routes/docs/variables/+page.svelte @@ -25,8 +25,11 @@ <section> <p> Style variables, or just "variables" in fuz_css, are <MdnLink - path="https://developer.mozilla.org/en-US/docs/Web/CSS/--*">CSS custom properties</MdnLink - > that can be grouped into a <TomeLink slug="themes">theme</TomeLink>. Each variable can have + path="https://developer.mozilla.org/en-US/docs/Web/CSS/--*" + > + CSS custom properties + </MdnLink> + that can be grouped into a <TomeLink slug="themes">theme</TomeLink>. Each variable can have values for light and/or dark <MdnLink path="Web/CSS/color-scheme">color-schemes</MdnLink>. They're design tokens with an API. </p> @@ -37,9 +40,9 @@ <ul> <li>by CSS classes, both utility and component</li> <li> - by other variables, both in calculations and to add useful semantics (e.g. <code - >button_fill_hover</code - > + by other variables, both in calculations and to add useful semantics (e.g. <code> + button_fill_hover + </code> uses <code>shade_50</code> but can be themed independently) </li> <li> @@ -59,14 +62,14 @@ Most color variables are <em>derived</em>: curve knobs feed ramp stops, ramp stops feed color stops, all computed in pure CSS (<code>calc()</code>/<code>pow()</code>/<code>oklch()</code>). Scale knobs like <code>radius_scale</code> derive other families the same way, and any - individual variable stays pinnable as the escape hatch. See <TomeLink slug="colors" /> for the color - system and <TomeLink slug="themes" /> for theming. + individual variable stays pinnable as the escape hatch. See <TomeLink slug="colors" /> for the + color system and <TomeLink slug="themes" /> for theming. </p> <p> In <TomeLink slug="classes" hash="What-gets-included">bundled mode</TomeLink>, only the variables your code uses are emitted, along with any they depend on. The full - <ModuleLink module_path="theme.css" /> stylesheet ships every variable, for utility-only mode and - direct imports. + <ModuleLink module_path="theme.css" /> + stylesheet ships every variable, for utility-only mode and direct imports. </p> </section> <section> From e948bcc150acc762eaefecd80c95e2375a151103 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Wed, 29 Jul 2026 12:11:40 -0400 Subject: [PATCH 31/33] more --- .changeset/interaction-and-surfaces.md | 6 +- .changeset/oklch-color-system.md | 16 +- CLAUDE.md | 6 +- src/lib/bundled_resources.ts | 4 +- src/lib/css_bundled_resolution.ts | 24 +- src/lib/css_class_generation.ts | 6 +- src/lib/css_class_interpreters.ts | 2 +- src/lib/css_plugin_options.ts | 8 +- src/lib/deps.ts | 2 +- src/lib/extract_file_cached.ts | 2 +- src/lib/gen_fuz_css.ts | 1 + src/lib/generate_css.ts | 32 +- src/lib/knobs.ts | 31 +- src/lib/oklch.ts | 4 +- src/lib/ramps.ts | 120 +- src/lib/scheme_adaptive_variables.gen.ts | 2 +- src/lib/scheme_adaptive_variables.ts | 66 +- src/lib/style.css | 82 +- src/lib/style_rule_parser.ts | 146 +- src/lib/theme.css | 96 +- src/lib/theme.gen.css.ts | 2 +- src/lib/theme.ts | 47 +- src/lib/theme_check.ts | 164 +- src/lib/theme_stance.ts | 20 +- src/lib/themes.ts | 4 +- src/lib/themes/base.ts | 2 +- src/lib/themes/brutalish.ts | 6 +- src/lib/themes/high_contrast.ts | 2 +- src/lib/themes/necromancer.ts | 23 +- src/lib/themes/sunset_ember.ts | 6 +- src/lib/themes/terminalien.ts | 10 +- src/lib/variable_data.ts | 4 +- src/lib/variable_graph.ts | 32 +- src/lib/variables.ts | 82 +- src/lib/vite_plugin_fuz_css.ts | 72 +- src/lib/wcag.ts | 2 +- src/routes/KnobControl.svelte | 11 +- src/routes/ThemeEditor.svelte | 55 +- src/routes/docs/classes/+page.svelte | 14 +- src/routes/docs/colors/ColorSwatchItem.svelte | 13 +- src/routes/docs/colors/HueSwatch.svelte | 8 +- src/routes/docs/themes/+page.svelte | 25 +- src/routes/theme_editor_state.svelte.ts | 70 +- ...css_bundled_resolution.diagnostics.test.ts | 2 +- src/test/css_bundled_resolution_fixtures.ts | 4 +- src/test/fixtures/css_classes_fixture.json | 2691 +---------------- .../fixtures/vite_dev/extra/late_module.ts | 3 + src/test/generate_bundled_css.test.ts | 19 +- src/test/generate_classes_css.test.ts | 2 +- src/test/generate_css.test.ts | 59 +- src/test/ramps.emitters.test.ts | 373 +++ src/test/ramps.test.ts | 4 +- src/test/style_rule_parser.custom.test.ts | 4 +- src/test/style_rule_parser.test.ts | 85 +- src/test/styles.test.ts | 36 +- src/test/theme.test.ts | 56 +- src/test/theme_check.compile.test.ts | 39 +- src/test/theme_check.test.ts | 134 +- src/test/theme_editor_state.test.ts | 240 ++ src/test/themes.test.ts | 2 +- src/test/variable_graph.test.ts | 124 +- src/test/variables.test.ts | 20 + src/test/vite_plugin_examples.test.ts | 2 +- src/test/vite_plugin_fuz_css.dev.test.ts | 4 +- src/test/vite_plugin_fuz_css.ws.test.ts | 129 + 65 files changed, 2114 insertions(+), 3248 deletions(-) create mode 100644 src/test/fixtures/vite_dev/extra/late_module.ts create mode 100644 src/test/ramps.emitters.test.ts create mode 100644 src/test/theme_editor_state.test.ts create mode 100644 src/test/vite_plugin_fuz_css.ws.test.ts diff --git a/.changeset/interaction-and-surfaces.md b/.changeset/interaction-and-surfaces.md index 4c82a4e90..ee635950d 100644 --- a/.changeset/interaction-and-surfaces.md +++ b/.changeset/interaction-and-surfaces.md @@ -31,7 +31,11 @@ fallback hook (all in the knob catalog, so theme validation accepts them): `--backdrop_color` - `--outline_offset` — the border-to-focus-ring gap above - `@media (prefers-contrast: more)` maps the OS preference onto the curve - knobs, mirroring the `'high contrast'` theme; theme overrides beat it + knobs, mirroring the `'high contrast'` theme. It ships in the + `fuz.preferences` cascade layer (as does the `prefers-reduced-motion` + duration suppression), above the `fuz.base` defaults and below + `fuz.theme`, so it applies regardless of stylesheet order and theme + overrides beat it Selected-button text stays readable under contrast-bent themes and colored fills: diff --git a/.changeset/oklch-color-system.md b/.changeset/oklch-color-system.md index 7e30bbe29..44a24934a 100644 --- a/.changeset/oklch-color-system.md +++ b/.changeset/oklch-color-system.md @@ -29,9 +29,14 @@ Breaking changes: instead. - **`color-mix()` interpolation moved from `in hsl` to `in oklab`** in button fills/borders, composites, and shadow classes. -- **Cascade layers**: all shipped CSS is layered `fuz.base` < `fuz.theme` < - `fuz.utilities`, so consumers' unlayered styles beat everything. Custom - `base_css` is re-layered into `fuz.base` in bundled output. +- **Cascade layers**: all shipped CSS is layered `fuz.base` < + `fuz.preferences` < `fuz.theme` < `fuz.utilities`, so consumers' unlayered + styles beat everything. The OS user-preference mappings + (`prefers-contrast`, `prefers-reduced-motion`) live in `fuz.preferences`, + above the defaults and below themes, so they apply in every consumption + mode and explicit theme overrides still win. Custom `base_css` is + re-layered into `fuz.base` in bundled output (only the `fuz.preferences` + identity is preserved). New: @@ -62,6 +67,11 @@ New: — `validate_theme` warns when the bound letter's multiplier differs from the intent's twin, and `check_theme` runs its gates through the multipliers. +- **Derived border colors**: the `border_color_*` alpha ramp colors through + the neutral intent — new `--border_color_lightness` and + `--border_color_chroma` knobs, the chroma derived from `--neutral_chroma` + — so grayscale and retinted themes reshape borders in the same move as + surfaces and text. - **Design-time modules**: `ramps.ts` (fitted knob constants, numeric evaluators, CSS emitters), `oklch.ts` (OKLCH↔sRGB + gamut math), and `wcag.ts` (luminance/contrast), with tests gating every default stop for diff --git a/CLAUDE.md b/CLAUDE.md index 589794e0a..54e0868aa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -108,8 +108,10 @@ combined and only used content is included. In utility-only mode, import - Each variable can have `light` and/or `dark` values - Light/dark are color-schemes _within_ a theme, not separate themes - [`render_theme_style()`](src/lib/theme.ts) generates CSS into the - `fuz.theme` cascade layer (defaults live in `fuz.base`, generated utility - classes in `fuz.utilities`; consumers' unlayered styles beat everything) + `fuz.theme` cascade layer (defaults live in `fuz.base`, OS user-preference + mappings like `prefers-contrast` in `fuz.preferences` above them, generated + utility classes in `fuz.utilities`; consumers' unlayered styles beat + everything) - A theme applies either at build time (the generators' `theme` option, baked into the bundled CSS, no JS shipped) or at runtime (fuz_ui's `ThemeRoot` renders it to a `<style>` element). They compose — the runtime theme wins diff --git a/src/lib/bundled_resources.ts b/src/lib/bundled_resources.ts index 6edf2e3c7..b4795e583 100644 --- a/src/lib/bundled_resources.ts +++ b/src/lib/bundled_resources.ts @@ -2,9 +2,9 @@ * Construction of the bundled CSS resources (style-rule index, variable graph, * class→variable index) shared by the Gro generator and the Vite plugin. * - * The two generators consume these differently — the Gro generator caches one + * The two generators consume these differently - the Gro generator caches one * bundle per instance, the Vite plugin loads lazily on first virtual-module - * access — but build them identically from the same options. This keeps that + * access - but build them identically from the same options. This keeps that * construction in one place. * * @module diff --git a/src/lib/css_bundled_resolution.ts b/src/lib/css_bundled_resolution.ts index 8336d4a0a..81f5259a4 100644 --- a/src/lib/css_bundled_resolution.ts +++ b/src/lib/css_bundled_resolution.ts @@ -115,8 +115,10 @@ export interface CssResolutionStats { export interface CssResolutionResult { /** CSS for theme variables (light and dark) */ theme_css: string; - /** CSS for base styles (from `style.css`) */ + /** CSS for base styles (from `style.css`), destined for `fuz.base` */ base_css: string; + /** CSS for the OS user-preference mappings, destined for `fuz.preferences` */ + preferences_css: string; /** All resolved variable names (including transitive deps) */ resolved_variables: Set<string>; /** Indices of rules included from the style index */ @@ -357,7 +359,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult } // g) Remove excluded variables. Warn when an excluded variable is actually referenced by - // shipped CSS — its `var()` would resolve to nothing. Force-included-only variables and + // shipped CSS - its `var()` would resolve to nothing. Force-included-only variables and // unknown (non-theme) names are dropped silently, since neither leaves a dangling reference. if (exclude_variables) { const known_var_names = get_all_variable_names(variable_graph); @@ -415,8 +417,13 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult const { light_css, dark_css } = generate_theme_css(variable_graph, resolved_variables); const theme_css = [light_css, dark_css].filter(Boolean).join('\n\n'); - // Step 6: Generate base CSS from matched rules - const base_css = generate_base_css(style_rule_index, included_rule_indices); + // Step 6: Generate base CSS from matched rules, split by destination layer + const base_css = generate_base_css(style_rule_index, included_rule_indices, 'fuz.base'); + const preferences_css = generate_base_css( + style_rule_index, + included_rule_indices, + 'fuz.preferences' + ); // Build stats if requested const stats = include_stats @@ -432,6 +439,7 @@ export const resolve_css = (options: CssResolutionOptions): CssResolutionResult return { theme_css, base_css, + preferences_css, resolved_variables, included_rule_indices, included_elements, @@ -469,7 +477,7 @@ export const generate_bundled_css = ( const parts: Array<string> = []; - // Theme section — default variables belong to the base layer; + // Theme section - default variables belong to the base layer; // `render_theme_style` overrides them from the higher `fuz.theme` layer if (include_theme && result.theme_css) { parts.push('/* Theme Variables */'); @@ -482,6 +490,12 @@ export const generate_bundled_css = ( parts.push(`@layer fuz.base {\n${result.base_css}\n}`); } + // OS user-preference mappings, above the defaults and below themes + if (include_base && result.preferences_css) { + parts.push('/* User-Preference Mappings */'); + parts.push(`@layer fuz.preferences {\n${result.preferences_css}\n}`); + } + // Utility classes section if (include_utilities && utility_css) { parts.push('/* Utility Classes */'); diff --git a/src/lib/css_class_generation.ts b/src/lib/css_class_generation.ts index 979eb3f35..491159a42 100644 --- a/src/lib/css_class_generation.ts +++ b/src/lib/css_class_generation.ts @@ -66,7 +66,7 @@ export const extract_primary_property = (declaration: string): string | null => /** * Resolves a sort index for a class name relative to the static definition order. * Literal classes (e.g., `border-radius:0`) map to `property_to_last_index + 0.5` - * so they slot after their token family but before the next family — ensuring + * so they slot after their token family but before the next family - ensuring * CSS shorthands appear before longhands. Modified token classes (`hover:p_md`) * return `MAX_VALUE`; modified literals (`hover:border-radius:0`) get a property * index because the loop skips the modifier prefix to find the property. @@ -85,14 +85,14 @@ const get_sort_index = ( // - `continue` skips known modifier prefixes (e.g. `hover`, `sm`) // - `break` bails once the first non-modifier segment is reached, since // that segment is the CSS property (if known) or an unrecognized prefix - // that can't be sorted by property — no point scanning further. + // that can't be sorted by property - no point scanning further. const segments = class_name.split(':'); for (let i = 0; i < segments.length - 1; i++) { const segment = segments[i]!; if (get_modifier(segment)) continue; // skip modifier prefixes like `hover:`, `sm:` const property_index = property_to_last_index.get(segment); if (property_index !== undefined) return property_index + 0.5; - break; // unrecognized non-modifier prefix — can't determine property sort order + break; // unrecognized non-modifier prefix - can't determine property sort order } return Number.MAX_VALUE; diff --git a/src/lib/css_class_interpreters.ts b/src/lib/css_class_interpreters.ts index e3fb2bb97..2ed7aaae4 100644 --- a/src/lib/css_class_interpreters.ts +++ b/src/lib/css_class_interpreters.ts @@ -23,7 +23,7 @@ export const modified_class_interpreter: CssClassDefinitionInterpreter = { const class_name = matched[0]; const segments = extract_segments(class_name); - // The last segment is always the base class name — only preceding segments + // The last segment is always the base class name - only preceding segments // can be modifiers. This avoids ambiguity when a class name collides with // a modifier name (e.g., `sm` and `md` are both breakpoint modifiers and // size composite classes). diff --git a/src/lib/css_plugin_options.ts b/src/lib/css_plugin_options.ts index 694d051c7..3b92c0124 100644 --- a/src/lib/css_plugin_options.ts +++ b/src/lib/css_plugin_options.ts @@ -166,9 +166,15 @@ export interface CssOutputOptions { * For runtime switching use fuz_ui's `ThemeRoot`; the two compose, with the * runtime theme winning by cascade layer. A single-scheme theme needs * `resolve_theme_stance` first so its `scheme_mirror` applies, and pinning - * `color-scheme` is separate — the `dark`/`light` class on the `html` + * `color-scheme` is separate - the `dark`/`light` class on the `html` * element drives it. * + * The baked values become the output's defaults, so a runtime theme can't + * revert to the pre-bake appearance by being empty - the base theme + * renders nothing. To offer "back to fuz defaults" at runtime, render the + * defaults explicitly: + * `render_theme_style({name: 'base', variables: default_variables})`. + * * @example * ```ts * import {necromancer_theme} from '@fuzdev/fuz_css/themes/necromancer.ts'; diff --git a/src/lib/deps.ts b/src/lib/deps.ts index 55ab148ac..17a1c7bea 100644 --- a/src/lib/deps.ts +++ b/src/lib/deps.ts @@ -68,7 +68,7 @@ export interface CacheDeps { }) => Promise<Result<object, FsError>>; /** - * Removes a file. Returns a `not_found` error if the file doesn't exist — + * Removes a file. Returns a `not_found` error if the file doesn't exist - * callers that want `rm -f` semantics should ignore that kind explicitly. */ unlink: (options: { path: string }) => Promise<Result<object, FsError>>; diff --git a/src/lib/extract_file_cached.ts b/src/lib/extract_file_cached.ts index 8edbd5d3a..0940bc631 100644 --- a/src/lib/extract_file_cached.ts +++ b/src/lib/extract_file_cached.ts @@ -3,7 +3,7 @@ * Vite plugin. Returns extraction sourced from cache when the content hash * matches, else freshly parsed, plus the cache path to write on a miss. * - * Callers own everything around this — in-memory hash short-circuits, the Vite + * Callers own everything around this - in-memory hash short-circuits, the Vite * deletion-race guard, diagnostic logging, stats counting, and how/when to * write the cache (fire-and-forget vs. batched). * diff --git a/src/lib/gen_fuz_css.ts b/src/lib/gen_fuz_css.ts index 0581316c7..0737cbe9d 100644 --- a/src/lib/gen_fuz_css.ts +++ b/src/lib/gen_fuz_css.ts @@ -365,6 +365,7 @@ export const gen_fuz_css = (options: GenFuzCssOptions = {}): Gen => { css_properties: cached_css_properties, include_base, include_theme, + has_theme: theme != null, resources, additional_elements, additional_variables, diff --git a/src/lib/generate_css.ts b/src/lib/generate_css.ts index e01cf609e..99c7fd5e3 100644 --- a/src/lib/generate_css.ts +++ b/src/lib/generate_css.ts @@ -56,6 +56,12 @@ export interface GenerateCssOptions { include_base: boolean; include_theme: boolean; + /** + * Whether a `theme` option was configured, independent of whether theme + * output is enabled - lets the footgun guard flag a theme silently + * discarded by `variables: null`. + */ + has_theme?: boolean; /** Bundled resources, or null for utility-only mode. */ resources: BundledCssResources | null; @@ -71,7 +77,7 @@ export interface GenerateCssOptions { } export interface GenerateCssResult { - /** Final CSS without banner comments — callers add their own. */ + /** Final CSS without banner comments - callers add their own. */ css: string; /** Extraction + generation + resolution diagnostics, unfiltered. */ diagnostics: Array<Diagnostic>; @@ -79,8 +85,8 @@ export interface GenerateCssResult { /** * Runs the full CSS-generation pipeline: utility classes via - * `generate_classes_css`, then — when base or theme output is enabled and - * bundled `resources` are available — base styles and theme variables via + * `generate_classes_css`, then - when base or theme output is enabled and + * bundled `resources` are available - base styles and theme variables via * `resolve_css` + `generate_bundled_css`. Returns the combined CSS and every * diagnostic produced along the way. */ @@ -99,6 +105,7 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => css_properties, include_base, include_theme, + has_theme = false, resources, additional_elements, additional_variables, @@ -120,6 +127,21 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => const diagnostics: Array<Diagnostic> = [...extraction_diagnostics, ...utility_result.diagnostics]; + // Footgun guard: a configured `theme` with theme output disabled + // (`variables: null`) is silently discarded - the theme flows into the + // variable graph but the graph never renders. + if (has_theme && !include_theme) { + diagnostics.push({ + phase: 'generation', + level: 'warning', + message: + 'A theme is configured but theme variables are disabled (variables: null); the theme will not be emitted', + suggestion: 'Remove the theme option, or enable variables so the theme can render.', + identifier: 'theme_discarded', + locations: null + }); + } + let css: string; if ((include_base || include_theme) && resources) { // `@fuz-variables` are included in output and checked for typos by resolve_css. @@ -166,7 +188,7 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => // Footgun guard: base styles on, theme off (`variables: null`). The kept base rules and // utility classes still reference fuz_css theme variables, but the disabled theme output - // won't define them — every such `var()` dangles. Utility-only mode (both off) never reaches + // won't define them - every such `var()` dangles. Utility-only mode (both off) never reaches // this branch; the legitimate escape is importing `theme.css` separately. if (include_base && !include_theme) { const theme_var_names = get_all_variable_names(resources.variable_graph); @@ -192,7 +214,7 @@ export const generate_css = (options: GenerateCssOptions): GenerateCssResult => include_utilities: true }); } else { - // utility-only mode — still layered, so the separately imported package + // utility-only mode - still layered, so the separately imported package // style.css/theme.css slot beneath the generated classes and consumers' // unlayered styles beat everything, same as bundled mode css = utility_result.css diff --git a/src/lib/knobs.ts b/src/lib/knobs.ts index 44c709f30..33da5c6ec 100644 --- a/src/lib/knobs.ts +++ b/src/lib/knobs.ts @@ -2,18 +2,18 @@ * The theme knob catalog: typed metadata for the style variables that themes * are expected to turn, keyed by variable name. * - * This is a side catalog over `variables.ts` — `StyleVariable` stays the + * This is a side catalog over `variables.ts` - `StyleVariable` stays the * authored and rendered value form, and the catalog adds the metadata that * value form can't carry: what kind of value a knob takes (which widget edits * it), which axis of the theme space it moves, how much leverage it has (its * control size in the editor), and which tier of the two-tier theme policy it - * belongs to. Defaults and light/dark slots are not duplicated here — join + * belongs to. Defaults and light/dark slots are not duplicated here - join * against `default_variables` by name. * * The catalog deliberately covers only the knob tier, not all ~490 variables: * derived ramp stops, color stops, and most site hooks are the escape-hatch * tier and stay out. A few catalog entries are hooks (`hook: true`) consumed - * by `style.css` with fallbacks rather than declared variables — they exist + * by `style.css` with fallbacks rather than declared variables - they exist * here so themes can set them without tripping the no-invented-variables * check. * @@ -64,7 +64,7 @@ export interface ThemeKnob { leverage: KnobLeverage; tier: KnobTier; /** - * The safe envelope for scalar kinds — editor sliders clamp to it, while + * The safe envelope for scalar kinds - editor sliders clamp to it, while * direct numeric entry may exceed it knowingly. */ range?: [number, number]; @@ -79,7 +79,7 @@ export interface ThemeKnob { hook?: boolean; /** * True for hue knobs whose default is a palette-letter binding - * (`var(--hue_X)`) — the intent and neutral hues. Editors render these as + * (`var(--hue_X)`) - the intent and neutral hues. Editors render these as * a letter picker with a custom-angle escape, and edits may write either a * binding or a literal angle. */ @@ -236,6 +236,16 @@ export const theme_knobs: Array<ThemeKnob> = [ tier: 'semantic', hook: true }, + { + // how muted disabled UI reads - a perceptual/color move, not motion + name: 'disabled_opacity', + kind: 'percent', + axis: 'color', + leverage: 'sm', + tier: 'semantic', + range: [0, 100], + step: 1 + }, // color - the palette tier (moving these makes a theme an exemplar) hue('hue_a', 'sm', 'palette'), hue('hue_b', 'sm', 'palette'), @@ -517,15 +527,6 @@ export const theme_knobs: Array<ThemeKnob> = [ range: [0, 5], step: 0.01 }, - { - name: 'disabled_opacity', - kind: 'percent', - axis: 'motion', - leverage: 'sm', - tier: 'semantic', - range: [0, 100], - step: 1 - }, // decoration { name: 'background_image', kind: 'text', axis: 'decoration', leverage: 'md', tier: 'semantic' } ]; @@ -551,7 +552,7 @@ export const theme_knob_by_name: Map<string, ThemeKnob> = new Map( ); /** - * Names of hook knobs — consumed by `style.css` via fallbacks, not declared + * Names of hook knobs - consumed by `style.css` via fallbacks, not declared * in `default_variables`. Theme validation unions these with the declared * variable names. */ diff --git a/src/lib/oklch.ts b/src/lib/oklch.ts index b0757dfde..a1f90cffc 100644 --- a/src/lib/oklch.ts +++ b/src/lib/oklch.ts @@ -1,7 +1,7 @@ /** * OKLCH/OKLab ↔ sRGB conversions and gamut math. * - * The shipped CSS never needs this module — fuz_css output stays pure + * The shipped CSS never needs this module - fuz_css output stays pure * `oklch()`/`calc()`. It exists for design time and tests: derivation scripts * fit curve knobs against real colors, and tests assert that every default * stop is inside the sRGB gamut and meets the contrast gates (see `wcag.ts`). @@ -168,7 +168,7 @@ export const oklch_max_srgb_chroma = (lightness: number, hue: number, precision }; /** - * Computes ΔEOK between two OKLCH colors — the Euclidean distance in OKLab. + * Computes ΔEOK between two OKLCH colors - the Euclidean distance in OKLab. * Just-noticeable differences sit around 0.02; css-color-4's gamut mapping * uses 0.02 as its "local MINDE" threshold. */ diff --git a/src/lib/ramps.ts b/src/lib/ramps.ts index 1ff3ef3c0..aa7380d3b 100644 --- a/src/lib/ramps.ts +++ b/src/lib/ramps.ts @@ -5,14 +5,14 @@ * constants and formulas that `variables.ts` renders as pure-CSS * `calc()`/`pow()`/`oklch()` defaults, evaluated here in TypeScript so * design-time tests can gate the defaults (gamut, ramp monotonicity, contrast - * — see `src/test/ramps.test.ts`) and derivation scripts can reason about the + * - see `src/test/ramps.test.ts`) and derivation scripts can reason about the * palette without a browser. * * The knob values were fitted against the pre-OKLCH HSL palette to minimize * the perceptual delta of the port. Model shapes: * * - lightness: `L(t) = L00 + (L100 - L00) * pow(t, curve)` with `t = stop/100` - * — the endpoint knobs literally are stops 00/100, so the dark scheme flips + * - the endpoint knobs literally are stops 00/100, so the dark scheme flips * direction purely through knob values, and pinning an endpoint stop and * turning a knob are the same act * - chroma: `C(t) = min + (max - min) * pow(4t(1-t), curve)`, a symmetric @@ -43,7 +43,7 @@ export const ramp_stop_t = (stop: NumericScaleVariant): number => Number(stop) / /** * Knobs for a pow-curve lightness ramp. The endpoints are the ramp's own * 00/100 stops, so light and dark schemes differ only in values, never in - * formula — no direction flag needed. + * formula - no direction flag needed. */ export interface LightnessRampKnobs { /** OKLCH lightness at stop 00, in [0, 1]. */ @@ -90,7 +90,7 @@ export const PALETTE_HUES: Record<PaletteVariant, number> = { * `--chroma_scale` (multiplicative, so the slot's character is preserved at * any global setting); values at or below 1 stay inside the worst-hue gamut * caps by construction, above 1 knowingly clips like the global knob. The - * brown slot ships muted because brown is low-chroma dark orange — no hue + * brown slot ships muted because brown is low-chroma dark orange - no hue * angle renders it at full palette chroma. */ export const PALETTE_CHROMA_MULTIPLIERS: Record<PaletteVariant, number> = { @@ -127,15 +127,14 @@ export const TEXT_LIGHTNESS_KNOBS: Record<ColorSchemeVariant, LightnessRampKnobs /** * Fitted chroma-curve knobs for the palette ramps. * - * Re-fitted ("honest chroma" retune) so the requested curve hugs the - * effective post-clamp chroma instead of sitting above the caps through the - * mid-range — turning the knobs down now responds immediately. Turning - * `chroma_max` up remains cap-clamped by design; `--chroma_scale` is the - * push-past-the-gamut knob. Residual vs the original port is small (mean - * ΔEOK ≈ 0.005 light / 0.003 dark) except light stop 30 (−0.028 C): the - * symmetric mid-peaked model can't reach the light cap envelope's peak at - * stop 30 — recovering it would take an asymmetric shape (peak-position - * knob). + * Fitted so the requested curve hugs the effective post-clamp chroma instead + * of sitting above the caps through the mid-range - turning the knobs down + * responds immediately. Turning `chroma_max` up remains cap-clamped by + * design; `--chroma_scale` is the push-past-the-gamut knob. The residual vs + * the HSL palette this fit ports is small (mean ΔEOK ≈ 0.005 light / 0.003 + * dark) except light stop 30 (−0.028 C): the symmetric mid-peaked model + * can't reach the light cap envelope's peak there - recovering it would + * take an asymmetric shape (a peak-position knob). */ export const PALETTE_CHROMA_KNOBS: Record<ColorSchemeVariant, ChromaRampKnobs> = { light: { chroma_min: 0.0132, chroma_max: 0.106, curve: 1.3 }, @@ -160,6 +159,74 @@ export const NEUTRAL_CHROMA: Record<ColorSchemeVariant, number> = { dark: 0.025 }; +/** + * OKLCH lightness of the border color family (`--border_color_lightness`), + * fitted from the HSL palette. Sits mid-ramp so borders read against both + * the page background and fills. + */ +export const BORDER_COLOR_LIGHTNESS: Record<ColorSchemeVariant, number> = { + light: 0.345, + dark: 0.857 +}; + +/** + * The border family's chroma as a multiple of `--neutral_chroma` - borders + * carry a stronger tint than surfaces so they read at low alpha. In CSS this + * is `--border_color_chroma: calc(var(--neutral_chroma) * <multiple>)`. + */ +export const BORDER_CHROMA_MULTIPLIER: Record<ColorSchemeVariant, number> = { + light: 2.6667, + dark: 2.12 +}; + +/** + * Alpha (in %) of each border color stop. A perceptually-uniform curve - + * small gaps at the low end, large at the high end - boosted at the low end + * in dark mode where borders are less visible against dark backgrounds. + * Stop 00 renders `transparent` and stop 100 opaque. + */ +export const BORDER_COLOR_ALPHAS: Record<ColorSchemeVariant, Record<NumericScaleVariant, number>> = { + light: { + '00': 0, + '05': 4, + '10': 7, + '20': 13, + '30': 22, + '40': 34, + '50': 48, + '60': 62, + '70': 76, + '80': 88, + '90': 96, + '95': 99, + '100': 100 + }, + dark: { + '00': 0, + '05': 8, + '10': 14, + '20': 22, + '30': 32, + '40': 44, + '50': 56, + '60': 68, + '70': 80, + '80': 90, + '90': 97, + '95': 99, + '100': 100 + } +}; + +/** + * Computes the default OKLCH color of the border family (without alpha). + */ +export const border_color_oklch = (scheme: ColorSchemeVariant): Oklch => [ + BORDER_COLOR_LIGHTNESS[scheme], + NEUTRAL_CHROMA[scheme] * BORDER_CHROMA_MULTIPLIER[scheme], + NEUTRAL_HUE +]; + /** * Worst-hue safe chroma caps per stop: the largest chroma at that stop's * default lightness that stays inside sRGB for every one of the 10 default @@ -212,7 +279,7 @@ export const ramp_lightness = (knobs: LightnessRampKnobs, stop: NumericScaleVari }; /** - * Evaluates the normalized chroma shape `pow(4t(1-t), curve)` at a stop — + * Evaluates the normalized chroma shape `pow(4t(1-t), curve)` at a stop - * 0 at the endpoints, 1 at the midpoint. */ export const ramp_chroma_shape = (stop: NumericScaleVariant, curve: number): number => { @@ -272,7 +339,7 @@ export const text_stop_oklch = (stop: NumericScaleVariant, scheme: ColorSchemeVa /** * Recomputes the worst-hue safe chroma caps per stop for an arbitrary hue set - * and lightness ramp — the generalization of the baked `PALETTE_CHROMA_CAPS`. + * and lightness ramp - the generalization of the baked `PALETTE_CHROMA_CAPS`. * For each stop the lightness comes from `ramp_lightness` and the cap is the * minimum `oklch_max_srgb_chroma` across the hues, floored to 4 decimals to * stay conservative (the browser clips anything past it). A theme's compile @@ -300,7 +367,7 @@ export const compute_palette_chroma_caps = ( /* -CSS emitters — the string twins of the numeric evaluators above. These render +CSS emitters - the string twins of the numeric evaluators above. These render the pure-CSS `calc()`/`pow()`/`min()`/`oklch()` default values that `variables.ts` ships, pulling every literal (stop positions, shape constants, caps) from the same source the tests evaluate. @@ -360,7 +427,7 @@ export const render_chroma_stop_css = ( /** * Renders the derived default of a palette color stop, e.g. `--palette_a_50`. - * One definition serves both schemes — the scheme flip lives in the knobs. + * One definition serves both schemes - the scheme flip lives in the knobs. */ export const render_palette_stop_css = ( letter: PaletteVariant, @@ -386,7 +453,7 @@ const slot_chroma_scale_reference = (hue_reference: string): string | null => { /** * Renders a color derived from the palette ramps at a stop for an arbitrary - * hue reference — the shared template behind palette stops and intent stops + * hue reference - the shared template behind palette stops and intent stops * (`--accent_50` renders with `var(--hue_accent)`). A palette-letter or * intent hue reference also picks up its slot's chroma multiplier * (`--palette_X_chroma_scale`/`--<intent>_chroma_scale`), so the hue slot and @@ -419,3 +486,20 @@ export const render_neutral_stop_css = ( `oklch(var(--${family}_lightness_${stop}) calc(var(--neutral_chroma) * var(--chroma_shape_${ stop })) var(--hue_neutral))`; + +/** + * Renders the derived default of a border color stop (`--border_color_NN`): + * the border family's color at the stop's alpha from `BORDER_COLOR_ALPHAS`. + * The color part is scheme-agnostic - the flip lives in the referenced knobs + * - so stops whose alpha matches across schemes render identically and + * `variables.ts` declares them single-slot. + */ +export const render_border_color_stop_css = ( + stop: NumericScaleVariant, + scheme: ColorSchemeVariant +): string => { + const alpha = BORDER_COLOR_ALPHAS[scheme][stop]; + if (alpha === 0) return 'transparent'; + const color = 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral)'; + return alpha === 100 ? `${color})` : `${color} / ${alpha}%)`; +}; diff --git a/src/lib/scheme_adaptive_variables.gen.ts b/src/lib/scheme_adaptive_variables.gen.ts index a13b1729c..a934d8a0f 100644 --- a/src/lib/scheme_adaptive_variables.gen.ts +++ b/src/lib/scheme_adaptive_variables.gen.ts @@ -7,7 +7,7 @@ import { default_variables } from './variables.ts'; * scheme-adaptive (dual-slot) subset of `default_variables`. * * The subset is emitted as literals rather than imported so `theme_stance.ts` - * carries no dependency on `variables.ts` — the emitter calls there defeat + * carries no dependency on `variables.ts` - the emitter calls there defeat * tree-shaking, so importing any part of it pulls the whole derived variable * set into the consumer's bundle. * diff --git a/src/lib/scheme_adaptive_variables.ts b/src/lib/scheme_adaptive_variables.ts index 8710eb7fa..eaf0fff72 100644 --- a/src/lib/scheme_adaptive_variables.ts +++ b/src/lib/scheme_adaptive_variables.ts @@ -128,65 +128,71 @@ export const scheme_adaptive_variables: Array<StyleVariable> = [ { name: 'bg_90', light: 'var(--lighten_90)', dark: 'var(--darken_90)' }, { name: 'bg_95', light: 'var(--lighten_95)', dark: 'var(--darken_95)' }, { name: 'bg_100', light: 'var(--lighten_100)', dark: 'var(--darken_100)' }, + { name: 'border_color_lightness', light: '0.345', dark: '0.857' }, + { + name: 'border_color_chroma', + light: 'calc(var(--neutral_chroma) * 2.6667)', + dark: 'calc(var(--neutral_chroma) * 2.12)' + }, { name: 'border_color_05', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 4%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 8%)' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 4%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 8%)' }, { name: 'border_color_10', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 7%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 14%)' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 7%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 14%)' }, { name: 'border_color_20', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 13%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 22%)' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 13%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 22%)' }, { name: 'border_color_30', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 22%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 32%)' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 22%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 32%)' }, { name: 'border_color_40', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 34%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 44%)' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 34%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 44%)' }, { name: 'border_color_50', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 48%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 56%)' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 48%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 56%)' }, { name: 'border_color_60', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 62%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 68%)' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 62%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 68%)' }, { name: 'border_color_70', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 76%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 80%)' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 76%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 80%)' }, { name: 'border_color_80', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 88%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 90%)' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 88%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 90%)' }, { name: 'border_color_90', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 96%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 97%)' - }, - { - name: 'border_color_95', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 99%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 99%)' - }, - { - name: 'border_color_100', - light: 'oklch(0.345 0.064 var(--hue_neutral))', - dark: 'oklch(0.857 0.053 var(--hue_neutral))' + light: + 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 96%)', + dark: 'oklch(var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 97%)' }, { name: 'text_min', light: '#fff', dark: '#000' }, { name: 'text_max', light: '#000', dark: '#fff' }, diff --git a/src/lib/style.css b/src/lib/style.css index 3ed35264a..76675d311 100644 --- a/src/lib/style.css +++ b/src/lib/style.css @@ -28,12 +28,43 @@ Notes: */ -@layer fuz.base, fuz.theme, fuz.utilities; +@layer fuz.base, fuz.preferences, fuz.theme, fuz.utilities; -/* All rules live in the fuz.base cascade layer: theme overrides beat them from - fuz.theme, generated utility classes from fuz.utilities, and consumers' - unlayered styles beat everything. The bundled generators unwrap this block - and re-layer only the rules a project uses. */ +/* OS user-preference mappings live in fuz.preferences, above the fuz.base + defaults (so they beat them regardless of import order, including theme.css + in utility-only mode) and below fuz.theme (so theme overrides beat them). */ +@layer fuz.preferences { + @media (prefers-reduced-motion) { + :root { + /* setting to `initial` disables animations but allows for fallbacks (see PendingAnimation) */ + --duration_6: initial; + --duration_5: initial; + --duration_4: initial; + --duration_3: initial; + --duration_2: initial; + --duration_1: initial; + } + } + + /* map the OS contrast preference onto the curve knobs, mirroring the + 'high contrast' theme */ + @media (prefers-contrast: more) { + :root { + --shade_lightness_00: 1; + --text_lightness_curve: 0.45; + } + :root.dark { + --shade_lightness_00: 0; + --text_lightness_curve: 0.31; + } + } +} + +/* All other rules live in the fuz.base cascade layer: the preference mappings + beat them from fuz.preferences, theme overrides from fuz.theme, generated + utility classes from fuz.utilities, and consumers' unlayered styles beat + everything. The bundled generators unwrap these blocks and re-layer only + the rules a project uses. */ @layer fuz.base { *, ::before, @@ -91,7 +122,7 @@ Notes: cursor: pointer; } - dialog::backdrop { + :where(dialog:not(.unstyled))::backdrop { /* same default dim as fuz_ui's Dialog, themable through one hook */ background-color: var(--backdrop_color, var(--darken_60)); } @@ -109,31 +140,6 @@ Respects `hidden="until-found"` for find-in-page support. display: none !important; } - @media (prefers-reduced-motion) { - :root { - /* setting to `initial` disables animations but allows for fallbacks (see PendingAnimation) */ - --duration_6: initial; - --duration_5: initial; - --duration_4: initial; - --duration_3: initial; - --duration_2: initial; - --duration_1: initial; - } - } - - /* map the OS contrast preference onto the curve knobs, mirroring the - 'high contrast' theme; theme overrides beat this from fuz.theme */ - @media (prefers-contrast: more) { - :root { - --shade_lightness_00: 1; - --text_lightness_curve: 0.45; - } - :root.dark { - --shade_lightness_00: 0; - --text_lightness_curve: 0.31; - } - } - :where(a:not(.unstyled)) { cursor: pointer; /* TODO should the generic be --link_color or --color? */ @@ -162,9 +168,6 @@ Respects `hidden="until-found"` for find-in-page support. --outline_color: var(--border_color); text-decoration: var(--text_decoration_selected); } - :where(a:not(.unstyled).selected:active) { - --outline_color: var(--border_color); - } :where(:is(ul, ol, menu):not(.unstyled)) { padding-left: var(--space_xl4); @@ -373,9 +376,9 @@ currently can break based on DOM structure - https://developer.mozilla.org/en-US } :where(:is(input, textarea, select):not(.unstyled):hover:not(:disabled)) { - /* hover previews the focus color — the element color when classed - (.palette_* sets --outline_color), else the accent intent; focus then - adds the outline at the same color, escalating visibility */ + /* hover previews the focus color - the element color when set (an + outline_* class; on buttons, .palette_*), else the accent intent; focus + then adds the outline at the same color, escalating visibility */ --border_color: var(--outline_color); } @@ -592,7 +595,8 @@ TODO think through these patterns to get somewhere consistent } :where(:is(button, input, textarea, select):not(.unstyled):focus) { - /* follows the element color (.palette_* sets --outline_color), else the accent intent */ + /* follows the element color when set (an outline_* class; on buttons, + .palette_*), else the accent intent */ --border_color: var(--outline_color); --outline_width: var(--outline_width_focus); } @@ -693,8 +697,6 @@ TODO think through these patterns to get somewhere consistent --shadow: var(--button_shadow_hover); } :where(button:not(.unstyled):not(.selected):not(:disabled):hover) { - /* TODO or set a hover var? make consistent with fill */ - --button_border_color: var(--button_border_color_hover); --button_text_color: color-mix(in oklab, var(--text_color) 90%, var(--shade_max)); } :where(button:not(.unstyled).selected.deselectable:not(:disabled):hover) { diff --git a/src/lib/style_rule_parser.ts b/src/lib/style_rule_parser.ts index 5eab7a754..0db474d17 100644 --- a/src/lib/style_rule_parser.ts +++ b/src/lib/style_rule_parser.ts @@ -10,12 +10,19 @@ */ import { parseCss, type AST } from 'svelte/compiler'; -import { hash_blake3 } from '@fuzdev/fuz_util/hash_blake3.ts'; import { extract_css_variables } from './css_variable_utils.ts'; import type { CacheDeps } from './deps.ts'; import type { BaseCssOption } from './css_plugin_options.ts'; +/** + * The cascade layer a rule is emitted into in bundled output. Only the + * `fuz.preferences` identity survives parsing - the OS user-preference + * mappings must stay above the `fuz.base` defaults - while every other + * layer (including custom `base_css` layers) flattens to `fuz.base`. + */ +export type RuleLayer = 'fuz.base' | 'fuz.preferences'; + /** * Base fields shared by all style rules. */ @@ -30,13 +37,27 @@ export interface StyleRuleBase { variables_used: Set<string>; /** Original order in `style.css` (for preserving cascade) */ order: number; + /** The cascade layer this rule is emitted into in bundled output */ + layer: RuleLayer; } /** - * Reasons a rule is considered "core" and always included. + * Reasons a rule is considered "core" and always included. `conditional_core` + * marks a conditional group rule (`@media`, `@supports`, `@container`) whose + * inner rule is itself core. `untargetable` + * marks rules whose selector names no element or class at all (pseudo-element + * or attribute selectors like `::selection` and `[hidden]`) - detection can + * never match them, so tree-shaking would drop them unconditionally. */ export type CoreReason = - 'universal' | 'root' | 'body' | 'media_query' | 'html' | 'host' | 'font_face'; + | 'universal' + | 'root' + | 'body' + | 'conditional_core' + | 'html' + | 'host' + | 'font_face' + | 'untargetable'; /** * A core style rule that is always included in output. @@ -71,18 +92,15 @@ export interface StyleRuleIndex { by_element: Map<string, Array<number>>; /** Rules indexed by class name */ by_class: Map<string, Array<number>>; - /** Content hash for cache invalidation */ - content_hash: string; } /** * Parses a CSS stylesheet into a `StyleRuleIndex`. * * @param css - raw CSS string (e.g., contents of `style.css`) - * @param content_hash - hash of the CSS for cache invalidation * @returns `StyleRuleIndex` with rules and lookup maps */ -export const parse_style_css = (css: string, content_hash: string): StyleRuleIndex => { +export const parse_style_css = (css: string): StyleRuleIndex => { const ast = parseCss(css); const rules: Array<StyleRule> = []; const by_element: Map<string, Array<number>> = new Map(); @@ -116,24 +134,29 @@ export const parse_style_css = (css: string, content_hash: string): StyleRuleInd }; const walk_children = ( - children: Iterable<AST.CSS.Rule | AST.CSS.Atrule | AST.CSS.Node> + children: Iterable<AST.CSS.Rule | AST.CSS.Atrule | AST.CSS.Node>, + layer: RuleLayer ): void => { for (const child of children) { if (child.type === 'Rule') { - index_rule(extract_style_rule(child, css, order++)); + index_rule(extract_style_rule(child, css, order++, layer)); } else if (child.type === 'Atrule') { // A top-level `@layer <name> { ... }` block is unwrapped so its - // contents tree-shake per rule — `style.css` wraps everything in - // `fuz.base`, and bundled output re-layers the selected rules - // (custom `base_css` layer identities are likewise not preserved). - // A blockless `@layer a, b;` order statement is skipped for the - // same reason: the bundle emits its own. + // contents tree-shake per rule, and bundled output re-layers the + // selected rules. Only the `fuz.preferences` identity is kept - + // everything else (including custom `base_css` layers) flattens + // to `fuz.base`. A blockless `@layer a, b;` order statement is + // skipped for the same reason: the bundle emits its own. if (child.name === 'layer') { - if (child.block) walk_children(child.block.children); + if (child.block) { + const child_layer = + child.prelude.trim() === 'fuz.preferences' ? 'fuz.preferences' : layer; + walk_children(child.block.children, child_layer); + } continue; } // Handle @media and other at-rules - const rule = extract_atrule(child, css, order++); + const rule = extract_atrule(child, css, order++, layer); if (rule) { index_rule(rule); } @@ -142,20 +165,24 @@ export const parse_style_css = (css: string, content_hash: string): StyleRuleInd }; // Walk the CSS AST - walk_children(ast.children); + walk_children(ast.children, 'fuz.base'); return { rules, by_element, - by_class, - content_hash + by_class }; }; /** * Extracts a StyleRule from a CSS Rule AST node. */ -const extract_style_rule = (rule: AST.CSS.Rule, css: string, order: number): StyleRule => { +const extract_style_rule = ( + rule: AST.CSS.Rule, + css: string, + order: number, + layer: RuleLayer +): StyleRule => { const rule_css = css.slice(rule.start, rule.end); const elements: Set<string> = new Set(); const classes: Set<string> = new Set(); @@ -168,8 +195,13 @@ const extract_style_rule = (rule: AST.CSS.Rule, css: string, order: number): Sty const block_css = css.slice(rule.block.start, rule.block.end); const variables_used = extract_css_variables(block_css); - // Determine if core rule - const { is_core, core_reason } = check_core_rule(selector_css, elements); + // Determine if core rule; a rule with no element or class hooks at all + // can never be matched by detection, so it must always ship + let { is_core, core_reason } = check_core_rule(selector_css, elements); + if (!is_core && elements.size === 0 && classes.size === 0) { + is_core = true; + core_reason = 'untargetable'; + } // Type assertion needed because destructuring widens is_core to boolean return { @@ -178,13 +210,16 @@ const extract_style_rule = (rule: AST.CSS.Rule, css: string, order: number): Sty classes, variables_used, order, + layer, is_core, core_reason } as StyleRule; }; /** - * Walks nested rules in an at-rule block to extract elements, classes, and variables. + * Walks nested rules in an at-rule block to extract elements, classes, and + * variables, and reports whether any nested rule is itself core (e.g. a + * `:root` block inside a media query). */ const extract_nested_rules = ( block: AST.CSS.Block, @@ -192,11 +227,17 @@ const extract_nested_rules = ( elements: Set<string>, classes: Set<string>, variables_used: Set<string> -): void => { +): boolean => { + let has_core_rule = false; for (const child of block.children) { if (child.type === 'Rule') { const selector_css = css.slice(child.prelude.start, child.prelude.end); - parse_selector_list(selector_css, elements, classes); + const rule_elements: Set<string> = new Set(); + parse_selector_list(selector_css, rule_elements, classes); + for (const e of rule_elements) elements.add(e); + if (check_core_rule(selector_css, rule_elements).is_core) { + has_core_rule = true; + } const block_css = css.slice(child.block.start, child.block.end); for (const v of extract_css_variables(block_css)) { @@ -204,12 +245,19 @@ const extract_nested_rules = ( } } } + return has_core_rule; }; /** - * Extracts a StyleRule from an at-rule (e.g., @media, @supports, @container, @layer). + * Extracts a StyleRule from an at-rule (e.g., @media, @supports, @container). + * `@layer` never reaches here - `walk_children` unwraps it. */ -const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): StyleRule | null => { +const extract_atrule = ( + atrule: AST.CSS.Atrule, + css: string, + order: number, + layer: RuleLayer +): StyleRule | null => { const rule_css = css.slice(atrule.start, atrule.end); const elements: Set<string> = new Set(); const classes: Set<string> = new Set(); @@ -217,24 +265,30 @@ const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): Sty // Handle conditional group rules that contain nested rules if ( - (atrule.name === 'media' || - atrule.name === 'supports' || - atrule.name === 'container' || - atrule.name === 'layer') && + (atrule.name === 'media' || atrule.name === 'supports' || atrule.name === 'container') && atrule.block ) { - extract_nested_rules(atrule.block, css, elements, classes, variables_used); + // a conditional group is core when any rule inside it is - the OS + // user-preference mappings (`prefers-contrast`, `prefers-reduced-motion`) + // target `:root`, so they ride the same rule as bare `:root` blocks + const has_core_rule = extract_nested_rules( + atrule.block, + css, + elements, + classes, + variables_used + ); - // prefers-reduced-motion media queries are core (accessibility) - if (atrule.name === 'media' && atrule.prelude.includes('prefers-reduced-motion')) { + if (has_core_rule) { return { css: rule_css, elements, classes, variables_used, order, + layer, is_core: true, - core_reason: 'media_query' + core_reason: 'conditional_core' } as const; } @@ -244,6 +298,7 @@ const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): Sty classes, variables_used, order, + layer, is_core: false, core_reason: null } as const; @@ -264,6 +319,7 @@ const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): Sty classes, // Empty - keyframes don't target classes variables_used, order, + layer, is_core: false, core_reason: null } as const; @@ -283,6 +339,7 @@ const extract_atrule = (atrule: AST.CSS.Atrule, css: string, order: number): Sty classes, // Empty - font-face doesn't target classes variables_used, order, + layer, is_core: true, core_reason: 'font_face' } as const; @@ -495,8 +552,7 @@ export const load_style_rule_index = async ( if (!r.ok) { throw new Error(`Failed to read style.css from ${path}: ${r.message}`); } - const content_hash = hash_blake3(r.value); - return parse_style_css(r.value, content_hash); + return parse_style_css(r.value); }; /** @@ -506,10 +562,7 @@ export const load_style_rule_index = async ( * @param css - raw CSS string to parse * @returns `StyleRuleIndex` */ -export const create_style_rule_index = (css: string): StyleRuleIndex => { - const content_hash = hash_blake3(css); - return parse_style_css(css, content_hash); -}; +export const create_style_rule_index = (css: string): StyleRuleIndex => parse_style_css(css); /** * Loads the raw default `style.css` content. @@ -613,15 +666,22 @@ export const get_matching_rules = ( * * @param index - the `StyleRuleIndex` * @param included_indices - set of rule indices to include + * @param layer - when given, include only rules destined for this layer * @returns CSS string with only included rules, in original order */ -export const generate_base_css = (index: StyleRuleIndex, included_indices: Set<number>): string => { +export const generate_base_css = ( + index: StyleRuleIndex, + included_indices: Set<number>, + layer?: RuleLayer +): string => { // Sort by order to preserve cascade const sorted_indices = Array.from(included_indices).sort((a, b) => a - b); const parts: Array<string> = []; for (const idx of sorted_indices) { - parts.push(index.rules[idx]!.css); + const rule = index.rules[idx]!; + if (layer !== undefined && rule.layer !== layer) continue; + parts.push(rule.css); } return parts.join('\n\n'); diff --git a/src/lib/theme.css b/src/lib/theme.css index 615d00837..c75ad92ab 100644 --- a/src/lib/theme.css +++ b/src/lib/theme.css @@ -12,7 +12,7 @@ */ /* theme: base */ -@layer fuz.base, fuz.theme, fuz.utilities; +@layer fuz.base, fuz.preferences, fuz.theme, fuz.utilities; @layer fuz.base { :root { --hue_a: 250; /* blue */ @@ -1330,19 +1330,45 @@ --bg_90: var(--lighten_90); --bg_95: var(--lighten_95); --bg_100: var(--lighten_100); + --border_color_lightness: 0.345; /* OKLCH lightness of the border color family */ + --border_color_chroma: calc(var(--neutral_chroma) * 2.6667); /* the border family's chroma, derived from the neutral's */ --border_color_00: transparent; - --border_color_05: oklch(0.345 0.064 var(--hue_neutral) / 4%); - --border_color_10: oklch(0.345 0.064 var(--hue_neutral) / 7%); - --border_color_20: oklch(0.345 0.064 var(--hue_neutral) / 13%); - --border_color_30: oklch(0.345 0.064 var(--hue_neutral) / 22%); - --border_color_40: oklch(0.345 0.064 var(--hue_neutral) / 34%); - --border_color_50: oklch(0.345 0.064 var(--hue_neutral) / 48%); - --border_color_60: oklch(0.345 0.064 var(--hue_neutral) / 62%); - --border_color_70: oklch(0.345 0.064 var(--hue_neutral) / 76%); - --border_color_80: oklch(0.345 0.064 var(--hue_neutral) / 88%); - --border_color_90: oklch(0.345 0.064 var(--hue_neutral) / 96%); - --border_color_95: oklch(0.345 0.064 var(--hue_neutral) / 99%); - --border_color_100: oklch(0.345 0.064 var(--hue_neutral)); + --border_color_05: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 4% + ); + --border_color_10: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 7% + ); + --border_color_20: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 13% + ); + --border_color_30: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 22% + ); + --border_color_40: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 34% + ); + --border_color_50: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 48% + ); + --border_color_60: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 62% + ); + --border_color_70: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 76% + ); + --border_color_80: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 88% + ); + --border_color_90: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 96% + ); + --border_color_95: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 99% + ); + --border_color_100: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) + ); --text_min: #fff; --text_max: #000; --text_color: var(--text_80); @@ -1762,18 +1788,38 @@ --bg_90: var(--darken_90); --bg_95: var(--darken_95); --bg_100: var(--darken_100); - --border_color_05: oklch(0.857 0.053 var(--hue_neutral) / 8%); - --border_color_10: oklch(0.857 0.053 var(--hue_neutral) / 14%); - --border_color_20: oklch(0.857 0.053 var(--hue_neutral) / 22%); - --border_color_30: oklch(0.857 0.053 var(--hue_neutral) / 32%); - --border_color_40: oklch(0.857 0.053 var(--hue_neutral) / 44%); - --border_color_50: oklch(0.857 0.053 var(--hue_neutral) / 56%); - --border_color_60: oklch(0.857 0.053 var(--hue_neutral) / 68%); - --border_color_70: oklch(0.857 0.053 var(--hue_neutral) / 80%); - --border_color_80: oklch(0.857 0.053 var(--hue_neutral) / 90%); - --border_color_90: oklch(0.857 0.053 var(--hue_neutral) / 97%); - --border_color_95: oklch(0.857 0.053 var(--hue_neutral) / 99%); - --border_color_100: oklch(0.857 0.053 var(--hue_neutral)); + --border_color_lightness: 0.857; /* OKLCH lightness of the border color family */ + --border_color_chroma: calc(var(--neutral_chroma) * 2.12); /* the border family's chroma, derived from the neutral's */ + --border_color_05: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 8% + ); + --border_color_10: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 14% + ); + --border_color_20: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 22% + ); + --border_color_30: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 32% + ); + --border_color_40: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 44% + ); + --border_color_50: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 56% + ); + --border_color_60: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 68% + ); + --border_color_70: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 80% + ); + --border_color_80: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 90% + ); + --border_color_90: oklch( + var(--border_color_lightness) var(--border_color_chroma) var(--hue_neutral) / 97% + ); --text_min: #000; --text_max: #fff; --text_lightness_00: 0.146; diff --git a/src/lib/theme.gen.css.ts b/src/lib/theme.gen.css.ts index e109cbe43..8270ca863 100644 --- a/src/lib/theme.gen.css.ts +++ b/src/lib/theme.gen.css.ts @@ -25,7 +25,7 @@ export const gen: Gen = ({ origin_path }) => { // the default theme's variables are the system's defaults, so they render // into the base layer; runtime theme overrides beat them from fuz.theme. // the theme itself is empty (it inherits the defaults), so the full set is - // passed explicitly — the renderer holds no variable data of its own + // passed explicitly - the renderer holds no variable data of its own const theme_style = render_theme_style( { ...theme, variables: default_variables }, { comments: true, layer: 'fuz.base' } diff --git a/src/lib/theme.ts b/src/lib/theme.ts index dae3b96f1..37910002c 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -26,7 +26,7 @@ export interface Theme { * Rendering a stanced theme faithfully also needs the scheme-adaptive * defaults mirrored into the stanced scheme; that is * `resolve_theme_stance` in `theme_stance.ts`, applied before rendering. - * The renderer only pins `color-scheme` — it holds no variable data of its + * The renderer only pins `color-scheme` - it holds no variable data of its * own, so the mirror stays off the theme path of consumers who don't use a * stanced theme. */ @@ -45,15 +45,24 @@ export interface Theme { /** * The fuz_css cascade layer order: defaults (variables + element styles) in - * `fuz.base`, theme overrides in `fuz.theme`, generated utility classes in - * `fuz.utilities`. Layer order beats specificity, so theme overrides win over - * the statically imported defaults regardless of head insertion order, and - * consumers' unlayered styles beat everything. + * `fuz.base`, OS user-preference mappings (`prefers-contrast`, + * `prefers-reduced-motion`) in `fuz.preferences`, theme overrides in + * `fuz.theme`, generated utility classes in `fuz.utilities`. Layer order + * beats specificity and source order, so the preference mappings win over + * the defaults, theme overrides win over both regardless of head insertion + * order, and consumers' unlayered styles beat everything. */ -export const FUZ_LAYER_ORDER_STATEMENT = '@layer fuz.base, fuz.theme, fuz.utilities;'; +export const FUZ_LAYER_ORDER_STATEMENT = + '@layer fuz.base, fuz.preferences, fuz.theme, fuz.utilities;'; export interface RenderThemeStyleOptions { comments?: boolean; + /** + * Scopes the rendered variables to `#id` instead of `:root`. Dark slots + * render for both placements of the scheme class - on the scope element + * itself (`#id.dark`) and on the root (`:root.dark #id`), the ecosystem + * convention - so a scoped theme's dark appearance follows the page's. + */ id?: string | null; /** * The cascade layer wrapping the rendered variables. Theme overrides @@ -66,17 +75,19 @@ export interface RenderThemeStyleOptions { /** * Composes a base theme with overlay fragments by flatten + last-wins: later * variables replace same-named earlier ones wholesale (both slots). Any - * knob-only theme is already a valid fragment — the contrast modifiers in + * knob-only theme is already a valid fragment - the contrast modifiers in * `contrast_modifiers` are the canonical overlays. This is the hand-flatten * precursor to a first-class `extends`, with the same merge semantics. * * The base's `scheme` stance wins; when the base is single-scheme, each * overlay variable is re-slotted to the stanced scheme's value so a * dual-slot fragment can't leak the other scheme's appearance past the - * stance. The base's `scheme_mirror` carries through unchanged — it renders - * before `variables`, so overlay values still win. The composed name appends - * the overlay names so name-keyed pickers and renderers treat the composition - * as its own theme. + * stance. The base's `scheme_mirror` carries through minus any names the + * overlays set - the mirror was computed against the base's own variables, + * so entries for newly composed names would shadow nothing but still render + * - and overlay values win over the remaining mirror by source order. + * The composed name appends the overlay names so name-keyed pickers and + * renderers treat the composition as its own theme. */ export const compose_themes = (base: Theme, ...overlays: Array<Theme>): Theme => { if (!overlays.length) return base; @@ -98,8 +109,11 @@ export const compose_themes = (base: Theme, ...overlays: Array<Theme>): Theme => return { name: `${base.name} (${overlays.map((o) => o.name).join(', ')})`, ...(base.scheme !== undefined && { scheme: base.scheme }), - // the mirror renders before `variables`, so overlay values still win - ...(base.scheme_mirror !== undefined && { scheme_mirror: base.scheme_mirror }), + // drop mirror entries the overlays now author; the rest renders before + // `variables`, so overlay values win by order + ...(base.scheme_mirror !== undefined && { + scheme_mirror: base.scheme_mirror.filter((v) => !by_name.has(v.name)) + }), variables: [...by_name.values()] }; }; @@ -108,7 +122,7 @@ export const compose_themes = (base: Theme, ...overlays: Array<Theme>): Theme => * Renders a theme's variables as CSS, wrapped in the `fuz.theme` cascade * layer by default. * - * Renders exactly what the theme carries — an empty `variables` array renders + * Renders exactly what the theme carries - an empty `variables` array renders * nothing (inheriting the `fuz.base` defaults) unless a `scheme` stance needs * pinning. To render the full default set, pass it: `render_theme_style({name: * 'base', variables: default_variables})`. To render a single-scheme theme @@ -134,6 +148,9 @@ export const render_theme_style = (theme: Theme, options: RenderThemeStyleOption .map((v) => render_theme_variable(v, true, comments)) .filter(Boolean); const scope = id ? '#' + id : ':root'; + // the scheme class conventionally lives on the root element, so a scoped + // theme's dark block matches both that and a class on the scope itself + const dark_scope = id ? `${scope}.dark, :root.dark ${scope}` : ':root.dark'; const blocks = `${ rendered_light.length ? `${scope} { @@ -143,7 +160,7 @@ export const render_theme_style = (theme: Theme, options: RenderThemeStyleOption } ${ rendered_dark.length - ? `${scope}.dark { + ? `${dark_scope} { ${rendered_dark.join('\n\t')} }` : '' diff --git a/src/lib/theme_check.ts b/src/lib/theme_check.ts index e4d3180ad..c298f03bb 100644 --- a/src/lib/theme_check.ts +++ b/src/lib/theme_check.ts @@ -4,7 +4,7 @@ * * Three functions sit over a shared numeric resolution core: * - * - `validate_theme` is the structural lint — non-empty name, `StyleVariable` + * - `validate_theme` is the structural lint - non-empty name, `StyleVariable` * shape, known variable names, and advisory type/range warnings for the * knob-tier variables. * - `check_theme` evaluates the gamut, ramp-monotonicity, and contrast gates @@ -56,6 +56,9 @@ import { PALETTE_CHROMA_KNOBS, PALETTE_CHROMA_CAPS, NEUTRAL_CHROMA, + BORDER_COLOR_LIGHTNESS, + BORDER_CHROMA_MULTIPLIER, + BORDER_COLOR_ALPHAS, ramp_lightness, ramp_chroma, ramp_chroma_shape, @@ -76,7 +79,7 @@ import { oklch_to_srgb, oklch_in_srgb_gamut, type Oklch, type RgbUnit } from './ import { wcag_contrast_ratio } from './wcag.ts'; // -// Gate thresholds — the WCAG levels the derived palette is designed to clear. +// Gate thresholds - the WCAG levels the derived palette is designed to clear. // /** AAA body text: `text_80` on `shade_00`/`05`/`10`. */ @@ -91,12 +94,25 @@ export const GATE_UI = 3; export const GATE_FILL_TEXT = 3; /** * Selected-control inverse text: `text_00` on `shade_50` and on every stop-50 - * fill — the selected-button pairings in `style.css` (`.selected` fills with + * fill - the selected-button pairings in `style.css` (`.selected` fills with * `shade_50`, `.palette_X.selected` with `palette_X_50`). The endpoint stop is * immune to `text_lightness_curve` bends, so this gate moves only when a theme * moves the endpoints, the fill ramps, or the hues. */ export const GATE_SELECTED_TEXT = 3; +/** + * Control borders: `shade_30` (the `--border_color` default) vs `shade_00`. + * A regression floor for the shipped design, not a WCAG level - 1.4.11's 3:1 + * applies to required component boundaries, and fuz borders sit deliberately + * softer - so a theme can't silently wash control borders out. + */ +export const GATE_BORDER = 1.5; +/** + * Divider borders: the `border_color_30` alpha color composited over + * `shade_00` (the `hr` pairing in `style.css`). A regression floor like + * `GATE_BORDER`. + */ +export const GATE_BORDER_DIVIDER = 1.3; /** * The variable names a theme may set: the declared defaults plus the hook @@ -195,6 +211,10 @@ const LIGHTNESS_STOP_MATCHER = /^(palette|shade|text)_lightness_(05|10|20|30|40|50|60|70|80|90|95)$/u; const PALETTE_CHROMA_STOP_MATCHER = /^palette_chroma_(00|05|10|20|30|40|50|60|70|80|90|95|100)$/u; const VAR_MATCHER = /^var\(\s*--([a-z][a-z0-9_]*)\s*\)$/u; +// the scaled-reference form emitted for `border_color_chroma` +// (`calc(var(--neutral_chroma) * 2.12)`) and useful for authored multipliers +const SCALED_VAR_MATCHER = + /^calc\(\s*var\(\s*--([a-z][a-z0-9_]*)\s*\)\s*\*\s*(-?\d*\.?\d+)\s*\)$/u; /** * The compiled worst-hue cap form emitted by `render_chroma_stop_css`: @@ -270,9 +290,16 @@ class ThemeResolver { const n = Number(trimmed); if (Number.isFinite(n)) return { ok: true, value: n }; } - // exactly var(--x) — recurse through the same merge + // exactly var(--x) - recurse through the same merge const var_match = VAR_MATCHER.exec(trimmed); if (var_match) return this.#resolve(var_match[1]!, scheme, visited); + // calc(var(--x) * k) - a scaled reference, resolved then multiplied + const scaled_match = SCALED_VAR_MATCHER.exec(trimmed); + if (scaled_match) { + const inner = this.#resolve(scaled_match[1]!, scheme, visited); + if (!inner.ok) return inner; + return { ok: true, value: inner.value * Number(scaled_match[2]) }; + } // machine-emitted compiled cap form const cap = this.#parse_compiled_cap(trimmed, scheme, visited); if (cap) return cap; @@ -318,6 +345,15 @@ class ThemeResolver { // scalar knobs if (name === 'chroma_scale') return { ok: true, value: 1 }; if (name === 'neutral_chroma') return { ok: true, value: NEUTRAL_CHROMA[scheme] }; + if (name === 'border_color_lightness') { + return { ok: true, value: BORDER_COLOR_LIGHTNESS[scheme] }; + } + if (name === 'border_color_chroma') { + // derives from the neutral so a retinted theme flows through + const neutral = this.#resolve('neutral_chroma', scheme, visited); + if (!neutral.ok) return neutral; + return { ok: true, value: neutral.value * BORDER_CHROMA_MULTIPLIER[scheme] }; + } if (name === 'palette_chroma_min') { return { ok: true, value: PALETTE_CHROMA_KNOBS[scheme].chroma_min }; } @@ -337,7 +373,7 @@ class ThemeResolver { field === '00' ? knobs.lightness_00 : field === '100' ? knobs.lightness_100 : knobs.curve; return { ok: true, value }; } - // derived lightness intermediates — compute from the resolved knobs + // derived lightness intermediates - compute from the resolved knobs const stop_match = LIGHTNESS_STOP_MATCHER.exec(name); if (stop_match) { const family = stop_match[1] as 'palette' | 'shade' | 'text'; @@ -346,7 +382,7 @@ class ThemeResolver { if (!knobs.ok) return knobs.error; return { ok: true, value: ramp_lightness(knobs.value, stop) }; } - // derived palette chroma stops — the capped knob curve + // derived palette chroma stops - the capped knob curve const chroma_match = PALETTE_CHROMA_STOP_MATCHER.exec(name); if (chroma_match) { const stop = chroma_match[1] as NumericScaleVariant; @@ -416,7 +452,7 @@ export const resolve_theme_knob = ( }; // -// validate_theme — the structural lint. +// validate_theme - the structural lint. // const validate_knob_value = ( @@ -491,7 +527,7 @@ const validate_knob_value = ( break; } default: - // length, color, font_stack, shadow, text — freeform, advisory only + // length, color, font_stack, shadow, text - freeform, advisory only break; } return issues; @@ -500,7 +536,7 @@ const validate_knob_value = ( /** * Lints a theme's structure: a non-empty name, valid `StyleVariable` shape and * known name per variable (errors), and advisory type/range warnings for the - * knob-tier variables — including a pairing warning when an intent hue binds + * knob-tier variables - including a pairing warning when an intent hue binds * a palette letter whose chroma multiplier differs from the intent's own * `*_chroma_scale` twin (a binding shares only the hue angle, so the slot's * chroma character is otherwise silently dropped). Value validation is @@ -516,10 +552,19 @@ export const validate_theme = (theme: Theme): Array<ThemeIssue> => { if (scheme !== undefined && scheme !== 'dual' && scheme !== 'light' && scheme !== 'dark') { issues.push({ level: 'error', - message: `invalid scheme ${JSON.stringify(scheme)} — expected 'dual', 'light', or 'dark'` + message: `invalid scheme ${JSON.stringify(scheme)} - expected 'dual', 'light', or 'dark'` }); } const stance = scheme === 'light' || scheme === 'dark' ? scheme : null; + // a stanced theme renders correctly only with its mirror computed - the + // gates here resolve through the mirror either way, so without this warning + // a hand-rolled stanced theme checks clean but renders unmirrored + if (stance && theme.scheme_mirror === undefined) { + issues.push({ + level: 'warning', + message: `'${stance}' scheme stance with no scheme_mirror - resolve the theme with resolve_theme_stance before rendering so its one appearance holds in both color schemes` + }); + } for (const variable of theme.variables) { const parsed = StyleVariable.safeParse(variable); const name: unknown = (variable as { name?: unknown }).name; @@ -551,7 +596,7 @@ export const validate_theme = (theme: Theme): Array<ThemeIssue> => { level: 'warning', message: `"${valid.name}" carries a dark slot under a '${ stance - }' scheme stance — stanced themes render one appearance in both color schemes, so author single-slot values`, + }' scheme stance - stanced themes render one appearance in both color schemes, so author single-slot values`, variable: valid.name }); } @@ -570,7 +615,7 @@ export const validate_theme = (theme: Theme): Array<ThemeIssue> => { // the pairing lint: an intent hue bound to a palette letter (authored // `var(--hue_X)` or the default binding) shares only the angle, so warn when -// the letter's chroma multiplier and the intent's twin disagree — the theme +// the letter's chroma multiplier and the intent's twin disagree - the theme // probably meant the character to follow the binding (the neutral is exempt: // its character is `--neutral_chroma` by design) const validate_binding_pairing = (theme: Theme): Array<ThemeIssue> => { @@ -603,7 +648,7 @@ const validate_binding_pairing = (theme: Theme): Array<ThemeIssue> => { letter_multiplier.value }) but ${intent}_chroma_scale is ${ intent_multiplier.value - } — a binding shares only the hue angle, so set ${ + } - a binding shares only the hue angle, so set ${ intent }_chroma_scale to carry the slot's chroma character`, variable: hue_name @@ -616,14 +661,14 @@ const validate_binding_pairing = (theme: Theme): Array<ThemeIssue> => { }; // -// check_theme — the numeric-twin accessibility gates. +// check_theme - the numeric-twin accessibility gates. // const clamp01 = (n: number): number => Math.min(1, Math.max(0, n)); const clamp_rgb = (rgb: RgbUnit): RgbUnit => [clamp01(rgb[0]), clamp01(rgb[1]), clamp01(rgb[2])]; -// max sRGB channel excess outside [0, 1] — 0 when in gamut +// max sRGB channel excess outside [0, 1] - 0 when in gamut const gamut_excess = ([r, g, b]: RgbUnit): number => Math.max(0, -r, r - 1, -g, g - 1, -b, b - 1); /** @@ -746,7 +791,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { if (color) push_gamut(`palette_${letter}_${stop}`, color, scheme); } } - // gamut: each intent that renders differently from every letter — an + // gamut: each intent that renders differently from every letter - an // intent folds into a letter's entries only when hue AND multiplier match for (const intent of intent_variants) { const hue = num(`hue_${intent}`, scheme); @@ -775,7 +820,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { push_monotonicity(family, scheme); } - // contrast: body text — text_80 on shade_00/05/10 + // contrast: body text - text_80 on shade_00/05/10 const text_80 = neutral_color('text', '80', scheme); for (const stop of ['00', '05', '10'] as const) { const surface = neutral_color('shade', stop, scheme); @@ -793,7 +838,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { const shade_00 = neutral_color('shade', '00', scheme); - // contrast: selected-control inverse text — text_00 on shade_50 + // contrast: selected-control inverse text - text_00 on shade_50 const text_00 = neutral_color('text', '00', scheme); const shade_50 = neutral_color('shade', '50', scheme); if (text_00 && shade_50) { @@ -808,7 +853,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { }); } - // contrast: subtle text — text_50 on shade_00 + // contrast: subtle text - text_50 on shade_00 const text_50 = neutral_color('text', '50', scheme); if (text_50 && shade_00) { const ratio = contrast(oklch_to_srgb(text_50), oklch_to_srgb(shade_00)); @@ -822,7 +867,48 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { }); } - // contrast: link — the accent hue at stop 60 on shade_00 (resolved through bindings) + // contrast: control borders - shade_30 (the --border_color default) vs shade_00 + const shade_30 = neutral_color('shade', '30', scheme); + if (shade_30 && shade_00) { + const ratio = contrast(oklch_to_srgb(shade_30), oklch_to_srgb(shade_00)); + entries.push({ + gate: 'contrast', + scheme, + subject: 'shade_30 vs shade_00', + value: ratio, + threshold: GATE_BORDER, + pass: ratio >= GATE_BORDER + }); + } + + // contrast: divider borders - border_color_30 alpha-composited over shade_00 + const border_l = num('border_color_lightness', scheme); + const border_c = num('border_color_chroma', scheme); + const border_h = num('hue_neutral', scheme); + if (border_l !== null && border_c !== null && border_h !== null && shade_00) { + const border_rgb = clamp_rgb(oklch_to_srgb([border_l, border_c, border_h])); + const bg_rgb = clamp_rgb(oklch_to_srgb(shade_00)); + // the alpha is baked into border_color_30's rendered CSS, which a + // stance mirrors into both schemes - so it follows the stance + const alpha = BORDER_COLOR_ALPHAS[stance ?? scheme]['30'] / 100; + // gamma-space compositing, matching how browsers stack backgrounds + const composited: RgbUnit = [ + alpha * border_rgb[0] + (1 - alpha) * bg_rgb[0], + alpha * border_rgb[1] + (1 - alpha) * bg_rgb[1], + alpha * border_rgb[2] + (1 - alpha) * bg_rgb[2] + ]; + const ratio = wcag_contrast_ratio(composited, bg_rgb); + entries.push({ + gate: 'contrast', + scheme, + subject: 'border_color_30 over shade_00', + value: ratio, + threshold: GATE_BORDER_DIVIDER, + pass: ratio >= GATE_BORDER_DIVIDER + }); + } + + // contrast: link - the accent hue at stop 60 on shade_00 (resolved through bindings) const accent_hue = num('hue_accent', scheme); const accent_multiplier = num('accent_chroma_scale', scheme); if (accent_hue !== null && accent_multiplier !== null && shade_00) { @@ -840,7 +926,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { } } - // contrast: UI affordances — every letter and intent fill at stop 50; + // contrast: UI affordances - every letter and intent fill at stop 50; // a stance renders its scheme's appearance in both, so text_max follows it const text_max: RgbUnit = (stance ?? scheme) === 'light' ? [0, 0, 0] : [1, 1, 1]; const fills: Array<[string, number, number]> = []; @@ -899,7 +985,7 @@ export const check_theme = (theme: Theme): ThemeCheckReport => { }; // -// compile_theme — recompute worst-hue caps for the theme's own hues. +// compile_theme - recompute worst-hue caps for the theme's own hues. // // the difference at which a recomputed cap is worth emitting; below this the @@ -920,7 +1006,7 @@ const collect_hues = (resolver: ThemeResolver, scheme: ColorSchemeVariant): Arra if (r.ok && !hues.some((h) => Math.abs(h - r.value) < 1e-9)) hues.push(r.value); } // with no resolvable hue at all, an empty set would yield Infinity caps and - // emit garbage CSS — fall back to the default hues (the re-check still + // emit garbage CSS - fall back to the default hues (the re-check still // reports the unresolvable pins) return hues.length ? hues : Object.values(PALETTE_HUES); }; @@ -943,20 +1029,26 @@ const resolve_lightness_knobs = ( /** * Recomputes a theme's per-stop worst-hue chroma caps from its own hues and * palette lightness ramp, then emits `palette_chroma_NN` overrides wherever - * the baked caps no longer fit — the fix for a theme (rotated hues, a + * the baked caps no longer fit - the fix for a theme (rotated hues, a * monochrome collapse, a dark-only mirror) whose gamut headroom the baked * worst-hue table misjudges. * * A stop is emitted only when either scheme's recomputed cap drifts from the * baked value by more than the emit epsilon and the theme doesn't already pin - * that stop. Both slots are always emitted together so a one-scheme override - * can't silently kill the base default's other slot by cascade-layer order. - * The input theme is never mutated. The report re-checks the emitted theme, - * whose compiled-cap overrides the resolution core recognizes. + * that stop. A dual theme's overrides emit both slots together so a + * one-scheme override can't silently kill the base default's other slot by + * cascade-layer order; a stanced theme's emit single-slot in the base + * position (its two schemes resolve identically through the mirror), as do + * slots whose rendered values coincide. A stanced theme's `scheme_mirror` is + * recomputed over the emitted variables, so the output is render-ready even + * when the input wasn't resolved. The input theme is never mutated. The + * report re-checks the emitted theme, whose compiled-cap overrides the + * resolution core recognizes. */ export const compile_theme = (theme: Theme): CompiledTheme => { const issues = validate_theme(theme); const resolver = new ThemeResolver(theme); + const stance = theme.scheme === 'light' || theme.scheme === 'dark' ? theme.scheme : null; const recomputed: Record<ColorSchemeVariant, Record<NumericScaleVariant, number>> = { light: compute_palette_chroma_caps( @@ -977,16 +1069,20 @@ export const compile_theme = (theme: Theme): CompiledTheme => { const light_drift = Math.abs(light_cap - PALETTE_CHROMA_CAPS.light[stop]); const dark_drift = Math.abs(dark_cap - PALETTE_CHROMA_CAPS.dark[stop]); if (light_drift > CAP_EMIT_EPSILON || dark_drift > CAP_EMIT_EPSILON) { - cap_overrides.push({ - name: `palette_chroma_${stop}`, - // always emit both slots — the dual-slot rule - light: render_chroma_stop_css(stop, 'light', light_cap), - dark: render_chroma_stop_css(stop, 'dark', dark_cap) - }); + const light_value = render_chroma_stop_css(stop, 'light', light_cap); + const dark_value = render_chroma_stop_css(stop, 'dark', dark_cap); + cap_overrides.push( + stance || light_value === dark_value + ? { name: `palette_chroma_${stop}`, light: light_value } + : { name: `palette_chroma_${stop}`, light: light_value, dark: dark_value } + ); } } // spread to preserve the scheme stance (and any future fields) const compiled: Theme = { ...theme, variables: [...theme.variables, ...cap_overrides] }; + // the emitted variables shadow mirror entries, so recompute the mirror over + // them; this also resolves a stanced input that skipped resolve_theme_stance + if (stance) compiled.scheme_mirror = scheme_stance_variables(stance, compiled.variables); return { theme: compiled, report: check_theme(compiled), issues }; }; diff --git a/src/lib/theme_stance.ts b/src/lib/theme_stance.ts index 3a76485b6..6a7101a93 100644 --- a/src/lib/theme_stance.ts +++ b/src/lib/theme_stance.ts @@ -16,10 +16,13 @@ import type { StyleVariable } from './variable.ts'; /** * Computes the mirror a single-scheme stance implies: every scheme-adaptive - * default not overridden by `variables`, re-slotted so the stanced scheme's - * value applies in both color schemes — into the base slot for a `'dark'` - * stance, into the dark slot for a `'light'` stance (defeating the base dark - * values by cascade-layer order). + * default not overridden by `variables`, re-slotted with the stanced scheme's + * value in the base (light) slot. A base-slot declaration in `fuz.theme` + * applies in both color schemes - cascade-layer order beats the higher + * specificity of the `fuz.base` `:root.dark` defaults - and keeping the + * mirror out of the dark slot means later same-block declarations (the + * theme's own variables, composed overlays, compiled cap overrides) always + * win by source order. * * @param scheme - the stance to mirror for * @param variables - the theme's own variables, whose names are left alone @@ -33,11 +36,8 @@ export const scheme_stance_variables = ( const mirrored: Array<StyleVariable> = []; for (const v of scheme_adaptive_variables) { if (overridden.has(v.name)) continue; - if (scheme === 'dark') { - if (v.dark !== undefined) mirrored.push({ name: v.name, light: v.dark }); - } else if (v.light !== undefined) { - mirrored.push({ name: v.name, dark: v.light }); - } + const value = scheme === 'dark' ? v.dark : v.light; + if (value !== undefined) mirrored.push({ name: v.name, light: value }); } return mirrored; }; @@ -48,7 +48,7 @@ export const scheme_stance_variables = ( * A dual-scheme theme is returned unchanged. * * The mirror lands in its own field rather than merged into `variables` so the - * authored knobs stay distinguishable from the derived ones — `compile_theme` + * authored knobs stay distinguishable from the derived ones - `compile_theme` * reads `variables` to detect author pins, and theme editors show it as the * theme's own surface. * diff --git a/src/lib/themes.ts b/src/lib/themes.ts index 85a69cf2c..840fc21f9 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -14,7 +14,7 @@ export const DEFAULT_THEME: Theme = base_theme; * stay a stable vocabulary across every registered theme. * * Themes live one module per theme under `themes/` and every module ships as - * an importable export — registry membership, not file location, is what + * an importable export - registry membership, not file location, is what * separates registered themes from shipped exemplars. Palette-tier themes * (rotations, duotone/monochrome collapses like `themes/terminalien.ts`) and * the expressive exemplars (`themes/necromancer.ts`, @@ -30,7 +30,7 @@ export const default_themes: Array<Theme> = [DEFAULT_THEME]; * The contrast modifiers: axis fragments (2 variables each) composed over * any theme via `compose_themes` rather than picked as themes themselves. * Composition is flatten + last-wins, so a modifier's variables replace the - * base theme's same-named ones — a base that moves the same knobs + * base theme's same-named ones - a base that moves the same knobs * (`shade_lightness_00`, `text_lightness_curve`) cedes them to the modifier. */ export const contrast_modifiers: Array<Theme> = [low_contrast_theme, high_contrast_theme]; diff --git a/src/lib/themes/base.ts b/src/lib/themes/base.ts index 0fa3b1d4a..be47b4efe 100644 --- a/src/lib/themes/base.ts +++ b/src/lib/themes/base.ts @@ -1,7 +1,7 @@ import type { Theme } from '../theme.ts'; /** - * The default theme — inherits every base default variable. + * The default theme - inherits every base default variable. */ export const base_theme: Theme = { name: 'base', diff --git a/src/lib/themes/brutalish.ts b/src/lib/themes/brutalish.ts index 371944bdb..3ff5006eb 100644 --- a/src/lib/themes/brutalish.ts +++ b/src/lib/themes/brutalish.ts @@ -1,15 +1,13 @@ import type { Theme } from '../theme.ts'; /** - * An era exemplar theme riffing on brutalism — brutal-ish: raw concrete - * surfaces with punchy saturated accents, sharp, flat, border-forward — + * An era exemplar theme riffing on brutalism - brutal-ish: raw concrete + * surfaces with punchy saturated accents, sharp, flat, border-forward - * built from levers only, the palette letters keep their default hues. The * neutral collapses to a concrete whisper while the palette keeps real * color, so statuses and accents punch against the raw ground; shape and * depth flatten to single scale knobs; borders carry the structure; * headings go display-heavy through the flattened weight ladder. - * - * Declared subversions: none. */ export const brutalish_theme: Theme = { name: 'brutalish', diff --git a/src/lib/themes/high_contrast.ts b/src/lib/themes/high_contrast.ts index e4b76c436..068671f15 100644 --- a/src/lib/themes/high_contrast.ts +++ b/src/lib/themes/high_contrast.ts @@ -2,7 +2,7 @@ import type { Theme } from '../theme.ts'; /** * Stretched contrast: pure white/black page background and text bent toward - * the extremes across the whole ramp — curve-knob moves, not stop surgery. + * the extremes across the whole ramp - curve-knob moves, not stop surgery. */ export const high_contrast_theme: Theme = { name: 'high contrast', diff --git a/src/lib/themes/necromancer.ts b/src/lib/themes/necromancer.ts index 7037f420f..4befb5e6d 100644 --- a/src/lib/themes/necromancer.ts +++ b/src/lib/themes/necromancer.ts @@ -2,19 +2,16 @@ import type { Theme } from '../theme.ts'; import { resolve_theme_stance } from '../theme_stance.ts'; /** - * A fantasy exemplar theme built entirely from semantic bindings and levers — - * the palette letters keep their default hues. Ectoplasm-green surfaces come - * from binding the neutral to green, the violet accent from binding the accent - * intent to purple, and the lit-from-below character from glow-colored - * depth. Dark-only via the `scheme` stance, vivid past the gamut caps on - * purpose. + * A fantasy exemplar theme built entirely from semantic bindings and levers - + * the palette letters keep their default hues, and negative stays red. + * Ectoplasm-green surfaces come from binding the neutral to green, the violet + * accent from binding the accent intent to purple, and the lit-from-below + * character from glow-colored depth. Dark-only via the `scheme` stance, vivid + * past the gamut caps on purpose. * - * Declared subversions: none — negative stays red. - * - * This theme carries the decoration brief (ornamental borders, glow textures, - * vignette backgrounds); those need the theme CSS block or decoration hooks, - * so this knob-only version ships at ~90% volume. The walls it hit are - * recorded as probe evidence. + * Ornamental borders, glow textures, and vignette backgrounds would complete + * the look, but need decoration hooks that don't exist yet - this is the + * knob-only version. */ const authored: Theme = { name: 'necromancer', @@ -47,6 +44,6 @@ const authored: Theme = { /** * Resolved at module scope so the stance mirror rides this module's chunk - * rather than every consumer's theme path — see `theme_stance.ts`. + * rather than every consumer's theme path - see `theme_stance.ts`. */ export const necromancer_theme: Theme = resolve_theme_stance(authored); diff --git a/src/lib/themes/sunset_ember.ts b/src/lib/themes/sunset_ember.ts index b69e4fb18..47fe962a3 100644 --- a/src/lib/themes/sunset_ember.ts +++ b/src/lib/themes/sunset_ember.ts @@ -1,7 +1,7 @@ import type { Theme } from '../theme.ts'; /** - * A natural exemplar theme built entirely from semantic bindings and levers — + * A natural exemplar theme built entirely from semantic bindings and levers - * the palette letters keep their default hues. The warm haze comes from a * strengthened neutral tint (the neutral's default binding to brown `hue_f` * already sits at dusk-warm 60°), the ember accent from binding the accent @@ -10,8 +10,6 @@ import type { Theme } from '../theme.ts'; * gamut clipping). The gradient sky rides the `background_image` * decoration hook: gold high to crimson low by day, indigo sinking to * crimson dusk by night. Dual-scheme. - * - * Declared subversions: none. */ export const sunset_ember_theme: Theme = { name: 'sunset ember', @@ -20,7 +18,7 @@ export const sunset_ember_theme: Theme = { { name: 'hue_accent', light: 'var(--hue_h)' }, // warm haze over every surface (neutral already binds to warm hue_f) { name: 'neutral_chroma', light: '0.028', dark: '0.035' }, - // vivid — deliberately past the caps; the browser clips, lightness holds + // vivid - deliberately past the caps; the browser clips, lightness holds { name: 'chroma_scale', light: '1.3', dark: '1.2' }, // gradient sky: kept near the shade_00 lightness so text contrast holds { diff --git a/src/lib/themes/terminalien.ts b/src/lib/themes/terminalien.ts index 1bccfae69..fbb3084d2 100644 --- a/src/lib/themes/terminalien.ts +++ b/src/lib/themes/terminalien.ts @@ -5,17 +5,15 @@ import { resolve_theme_stance } from '../theme_stance.ts'; * A dense terminal theme with an alien cast: dark, monospace, sharp-cornered, * flat-depth terminal chrome carrying a green-phosphor tint in the surfaces * and accent, but the palette slots keep their own hues so status colors - * still read — negative stays red, caution amber, info cyan. Compact spacing + * still read - negative stays red, caution amber, info cyan. Compact spacing * and tightened leading give it terminal density. Dark-only via the `scheme` * stance. - * - * Declared subversions: none. */ const authored: Theme = { name: 'terminalien', scheme: 'dark', variables: [ - // green-phosphor surfaces and text — the terminal cast, kept low-chroma so + // green-phosphor surfaces and text - the terminal cast, kept low-chroma so // the palette colors read over it { name: 'hue_neutral', light: 'var(--hue_b)' }, { name: 'neutral_chroma', light: '0.05' }, @@ -28,7 +26,7 @@ const authored: Theme = { // flat: one knob zeroes the whole alpha ramp, button shadows included { name: 'shadow_alpha_scale', light: '0' }, // terminal density: compact spacing plus tightened leading (leading is - // deliberately decoupled from scale_factor — these pins are the theme's own) + // deliberately decoupled from scale_factor - these pins are the theme's own) { name: 'scale_factor', light: '0.85' }, { name: 'line_height_md', light: '1.4' }, { name: 'line_height_lg', light: '1.6' }, @@ -38,6 +36,6 @@ const authored: Theme = { /** * Resolved at module scope so the stance mirror rides this module's chunk - * rather than every consumer's theme path — see `theme_stance.ts`. + * rather than every consumer's theme path - see `theme_stance.ts`. */ export const terminalien_theme: Theme = resolve_theme_stance(authored); diff --git a/src/lib/variable_data.ts b/src/lib/variable_data.ts index 1fed53b41..b97d6a828 100644 --- a/src/lib/variable_data.ts +++ b/src/lib/variable_data.ts @@ -142,14 +142,14 @@ export const palette_variants = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j * The semantic color intents that derive full scales through the shared * ramps. The neutral is also an intent (`--hue_neutral`) but is absent here * because its scales are `shade_*` and `text_*` rather than a - * `neutral_00`–`neutral_100` family. + * `neutral_00` through `neutral_100` family. */ export type IntentVariant = ArrayElement<typeof intent_variants>; export const intent_variants = ['accent', 'positive', 'negative', 'caution', 'info'] as const; /** * Letter glosses: each palette slot's default color plus its default intent - * binding where one exists. Display data for docs and the theme editor — + * binding where one exists. Display data for docs and the theme editor - * themes recolor the slots, so these describe the defaults, not promises. * The neutral binds like an intent (`--hue_neutral: var(--hue_f)`) despite * being absent from `intent_variants`. diff --git a/src/lib/variable_graph.ts b/src/lib/variable_graph.ts index 68575d563..8e92d9758 100644 --- a/src/lib/variable_graph.ts +++ b/src/lib/variable_graph.ts @@ -9,7 +9,6 @@ */ import { levenshtein_distance } from '@fuzdev/fuz_util/string.ts'; -import { hash_insecure } from '@fuzdev/fuz_util/hash.ts'; import { default_variables } from './variables.ts'; import type { Theme } from './theme.ts'; @@ -40,21 +39,15 @@ export interface StyleVariableInfo { export interface VariableDependencyGraph { /** Map from variable name to its info */ variables: Map<string, StyleVariableInfo>; - /** Content hash for cache invalidation */ - content_hash: string; } /** * Builds a dependency graph from an array of style variables. * * @param variables - array of `StyleVariable` objects - * @param content_hash - hash of the source for cache invalidation * @returns `VariableDependencyGraph` */ -export const build_variable_graph = ( - variables: Array<StyleVariable>, - content_hash: string -): VariableDependencyGraph => { +export const build_variable_graph = (variables: Array<StyleVariable>): VariableDependencyGraph => { const graph: Map<string, StyleVariableInfo> = new Map(); for (const v of variables) { @@ -71,8 +64,7 @@ export const build_variable_graph = ( } return { - variables: graph, - content_hash + variables: graph }; }; @@ -304,9 +296,22 @@ export const apply_theme_variables = ( ): Array<StyleVariable> => { if (!theme) return variables; const by_name = new Map(variables.map((v) => [v.name, v])); + // Replacement mirrors the runtime cascade: a light-slot theme value beats + // the base default's dark slot by layer order, so it replaces wholesale. + // A dark-only theme value only shadows the default under `.dark`, so the + // default's light slot must survive into the baked entry - dropping it + // would leave the variable undefined in the light scheme. + const overlay = (v: StyleVariable): void => { + const existing = by_name.get(v.name); + if (v.light === undefined && v.dark !== undefined && existing?.light !== undefined) { + by_name.set(v.name, { ...v, light: existing.light }); + } else { + by_name.set(v.name, v); + } + }; // mirror first, then the theme's own, so authored values win - for (const v of theme.scheme_mirror ?? []) by_name.set(v.name, v); - for (const v of theme.variables) by_name.set(v.name, v); + for (const v of theme.scheme_mirror ?? []) overlay(v); + for (const v of theme.variables) overlay(v); return [...by_name.values()]; }; @@ -323,6 +328,5 @@ export const build_variable_graph_from_options = ( theme?: Theme | null ): VariableDependencyGraph => { const resolved = apply_theme_variables(resolve_variables_option(variables), theme); - const content = resolved.map((v) => `${v.name}:${v.light ?? ''}:${v.dark ?? ''}`).join('|'); - return build_variable_graph(resolved, hash_insecure(content)); + return build_variable_graph(resolved); }; diff --git a/src/lib/variables.ts b/src/lib/variables.ts index 8f3b82f5b..daf7f2505 100644 --- a/src/lib/variables.ts +++ b/src/lib/variables.ts @@ -2,12 +2,15 @@ import type { StyleVariable } from './variable.ts'; import { icon_sizes } from './variable_data.ts'; import { NEUTRAL_CHROMA, + BORDER_CHROMA_MULTIPLIER, + BORDER_COLOR_LIGHTNESS, PALETTE_CHROMA_KNOBS, PALETTE_CHROMA_MULTIPLIERS, PALETTE_HUES, PALETTE_LIGHTNESS_KNOBS, SHADE_LIGHTNESS_KNOBS, TEXT_LIGHTNESS_KNOBS, + render_border_color_stop_css, render_chroma_shape_css, render_chroma_stop_css, render_lightness_stop_css, @@ -1569,77 +1572,90 @@ export const bg_100: StyleVariable = { border_color alpha - tinted alpha borders for accessibility -Theme-integrated borders with alpha transparency, tinted by the neutral hue -for cohesion (the base colors port the old hsl(tint 60% 20%/80%) exactly). -Higher alpha in dark mode compensates for lower perceived contrast. +Theme-integrated borders with alpha transparency, colored by the neutral +intent: the hue is `--hue_neutral` and the chroma derives from +`--neutral_chroma` (borders carry a stronger tint than surfaces, hence the +multiplier), so grayscale and retinted themes reshape borders in the same +move as everything else. The lightness sits mid-ramp as its own pinnable +knob. Higher alpha in dark mode compensates for lower perceived contrast. */ +export const border_color_lightness: StyleVariable = { + name: 'border_color_lightness', + light: String(BORDER_COLOR_LIGHTNESS.light), + dark: String(BORDER_COLOR_LIGHTNESS.dark), + summary: 'OKLCH lightness of the border color family' +}; +export const border_color_chroma: StyleVariable = { + name: 'border_color_chroma', + // stronger tint than the surfaces so borders read at low alpha + light: `calc(var(--neutral_chroma) * ${BORDER_CHROMA_MULTIPLIER.light})`, + dark: `calc(var(--neutral_chroma) * ${BORDER_CHROMA_MULTIPLIER.dark})`, + summary: "the border family's chroma, derived from the neutral's" +}; export const border_color_00: StyleVariable = { name: 'border_color_00', - light: 'transparent' + light: render_border_color_stop_css('00', 'light') }; -// Perceptually-uniform alpha curve for borders (matches shadow_alpha curve) -// - Small gaps at low end, large gaps at high end -// - Dark mode boosted at low end (borders less visible against dark backgrounds) export const border_color_05: StyleVariable = { name: 'border_color_05', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 4%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 8%)' + light: render_border_color_stop_css('05', 'light'), + dark: render_border_color_stop_css('05', 'dark') }; export const border_color_10: StyleVariable = { name: 'border_color_10', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 7%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 14%)' + light: render_border_color_stop_css('10', 'light'), + dark: render_border_color_stop_css('10', 'dark') }; export const border_color_20: StyleVariable = { name: 'border_color_20', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 13%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 22%)' + light: render_border_color_stop_css('20', 'light'), + dark: render_border_color_stop_css('20', 'dark') }; export const border_color_30: StyleVariable = { name: 'border_color_30', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 22%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 32%)' + light: render_border_color_stop_css('30', 'light'), + dark: render_border_color_stop_css('30', 'dark') }; export const border_color_40: StyleVariable = { name: 'border_color_40', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 34%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 44%)' + light: render_border_color_stop_css('40', 'light'), + dark: render_border_color_stop_css('40', 'dark') }; export const border_color_50: StyleVariable = { name: 'border_color_50', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 48%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 56%)' + light: render_border_color_stop_css('50', 'light'), + dark: render_border_color_stop_css('50', 'dark') }; export const border_color_60: StyleVariable = { name: 'border_color_60', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 62%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 68%)' + light: render_border_color_stop_css('60', 'light'), + dark: render_border_color_stop_css('60', 'dark') }; export const border_color_70: StyleVariable = { name: 'border_color_70', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 76%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 80%)' + light: render_border_color_stop_css('70', 'light'), + dark: render_border_color_stop_css('70', 'dark') }; export const border_color_80: StyleVariable = { name: 'border_color_80', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 88%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 90%)' + light: render_border_color_stop_css('80', 'light'), + dark: render_border_color_stop_css('80', 'dark') }; export const border_color_90: StyleVariable = { name: 'border_color_90', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 96%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 97%)' + light: render_border_color_stop_css('90', 'light'), + dark: render_border_color_stop_css('90', 'dark') }; +// the color part is scheme-agnostic (the flip lives in the knobs), so stops +// whose alpha matches across schemes define a single slot export const border_color_95: StyleVariable = { name: 'border_color_95', - light: 'oklch(0.345 0.064 var(--hue_neutral) / 99%)', - dark: 'oklch(0.857 0.053 var(--hue_neutral) / 99%)' + light: render_border_color_stop_css('95', 'light') }; export const border_color_100: StyleVariable = { name: 'border_color_100', - light: 'oklch(0.345 0.064 var(--hue_neutral))', - dark: 'oklch(0.857 0.053 var(--hue_neutral))' + light: render_border_color_stop_css('100', 'light') }; /* @@ -1812,7 +1828,7 @@ export const heading_font_family: StyleVariable = { light: 'var(--font_family_serif)' }; // `--heading_font_weight` is a hook consumed by `style.css` with per-tier -// fallbacks (h1 300 … h5 900), not a declared variable — setting it flattens +// fallbacks (h1 300 … h5 900), not a declared variable - setting it flattens // the heading weight ladder deliberately (display-heavy themes); see `knobs.ts` /* sizes like font-size */ @@ -2706,6 +2722,8 @@ export const default_variables: Array<StyleVariable> = [ bg_90, bg_95, bg_100, + border_color_lightness, + border_color_chroma, border_color_00, border_color_05, border_color_10, diff --git a/src/lib/vite_plugin_fuz_css.ts b/src/lib/vite_plugin_fuz_css.ts index dec7d3e7c..9a6ed1836 100644 --- a/src/lib/vite_plugin_fuz_css.ts +++ b/src/lib/vite_plugin_fuz_css.ts @@ -66,7 +66,7 @@ export const FUZ_CSS_BANNER = '/* generated by vite_plugin_fuz_css */'; * Marker custom property emitted by the build-mode virtual module. Unlike a CSS * comment (stripped by minification) this survives into the output, so * `generateBundle` can locate the asset where Vite placed the virtual module's - * CSS — the importer's globally-loaded stylesheet — and splice the generated + * CSS - the importer's globally-loaded stylesheet - and splice the generated * CSS in there instead of an arbitrary code-split chunk. */ const FUZ_CSS_PLACEHOLDER = '--fuz-css-placeholder'; @@ -82,7 +82,7 @@ const FUZ_CSS_PLACEHOLDER_EMPTY_ROOT_RE = /:root\s*\{\s*\}/; const VIRTUAL_ID = 'virtual:fuz.css'; /** - * Resolved id of the virtual module, in both dev and build — a leading-slash + * Resolved id of the virtual module, in both dev and build - a leading-slash * path ending in `.css`, deliberately *not* a `\0`-prefixed virtual id. * * Two properties matter: @@ -93,7 +93,7 @@ const VIRTUAL_ID = 'virtual:fuz.css'; * - The plain URL (no `\0`) is what makes that inlining actually happen. A * `\0` id is encoded as `/@id/__x00__virtual:fuz.css` in the import graph, * and SvelteKit looks deps up by URL via `moduleGraph.getModuleByUrl()`, - * which can't resolve the `__x00__`-encoded form back to its node — so a `\0` + * which can't resolve the `__x00__`-encoded form back to its node - so a `\0` * id is silently skipped and the page flashes unstyled on every refresh. * * This mirrors UnoCSS's `/__uno.css`. `resolveId` claims both the @@ -119,7 +119,7 @@ const HMR_DEBOUNCE_MS = 10; /** * Concurrency for the dev pre-scan (file read + cache read + extract). - * Matches the Gro generator's default — the value controls I/O interleaving, + * Matches the Gro generator's default - the value controls I/O interleaving, * not CPU parallelism (AST parsing is synchronous on the main thread). */ const PRESCAN_CONCURRENCY = 8; @@ -137,7 +137,7 @@ export interface VitePluginFuzCssOptions extends CssGeneratorBaseOptions { * * Extraction state accumulates from Vite's transform hook, so without a * pre-scan the first CSS served on a cold start includes only the files - * the module graph happened to visit first — classes from modules the SSR + * the module graph happened to visit first - classes from modules the SSR * walk never reaches (client-only branches, dynamic imports) are missing * until a corrective HMR update, which is dropped when the browser hasn't * connected yet. The pre-scan extracts every file passing `filter_file` @@ -147,7 +147,7 @@ export interface VitePluginFuzCssOptions extends CssGeneratorBaseOptions { * * `true` scans `src` under the Vite root; `false` disables; an array gives * custom directories (resolved against the Vite root unless absolute). - * node_modules deps are never pre-scanned — they stream in via transform + * node_modules deps are never pre-scanned - they stream in via transform * and are covered by the connect-time resync. * * @default true @@ -205,7 +205,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug const detected_variables_by_file: Map<string, Set<string>> = new Map(); let virtual_module_loaded = false; /** - * Every resolved virtual-module id `load()` has served — the bare + * Every resolved virtual-module id `load()` has served - the bare * `/__fuz.css` plus each query variant (`?inline`/`?direct`/`?used`). HMR must * invalidate *all* of them: SvelteKit's dev SSR FOUC-inlining reads * `/__fuz.css?inline`, a module Vite caches separately from the bare id, so @@ -241,7 +241,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug * `render_css` narrows to theme variables once `variable_graph` is loaded. * * Filtering here (the old behavior) silently dropped detections from any file - * transformed before the graph loads — and the graph loads asynchronously + * transformed before the graph loads - and the graph loads asynchronously * (eagerly at `configureServer` in dev, on the first `load()` in build). * In SvelteKit dev, route nodes (e.g. `+page.svelte`) are resolved, and * thus transformed, during SSR *before* the layout's `virtual:fuz.css` import @@ -327,7 +327,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug } = css_classes.get_all(); // Aggregate per-file `var(--*)` references (stored unfiltered by - // `update_detected_variables`) and narrow to theme variables — non-theme refs + // `update_detected_variables`) and narrow to theme variables - non-theme refs // may be user-defined and have no definition to emit. The graph is loaded at // every render-time call site (`load()`/HMR/`generateBundle` all run after // `ensure_bundled_resources`); when it isn't (utility-only mode), variables @@ -358,6 +358,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug css_properties, include_base, include_theme, + has_theme: theme != null, // Resources load lazily; null until ready falls back to utility-only. resources: style_rule_index && variable_graph && class_variable_index @@ -407,15 +408,15 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug // Deliberately not updating last_served_css: only load() marks CSS as // served, so if the pushed js-update never causes a refetch, the next // ws connection re-diffs against what clients actually hold and pushes - // again — convergence relies on that refetch loop. + // again - convergence relies on that refetch loop. last_generated_css = new_css; pending_css = new_css; // Store for reuse in load() to avoid regenerating // Invalidate every served variant, not just the bare id. The bare // `/__fuz.css` backs the client `<style>` and gets a `js-update` so Vite // re-runs `updateStyle` with fresh content live. The `?inline`/`?direct`/ - // `?used` variants have no client HMR boundary — they're read fresh by the - // next SSR render — so invalidating their cached module is enough; without + // `?used` variants have no client HMR boundary - they're read fresh by the + // next SSR render - so invalidating their cached module is enough; without // it, SvelteKit keeps inlining stale `<head>` CSS on every reload. const bare = server!.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ID); if (bare) { @@ -452,7 +453,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug const invalidate_virtual_module = (): void => { if (!server) return; - // Skip HMR if bundled resources aren't loaded yet — in dev they load + // Skip HMR if bundled resources aren't loaded yet - in dev they load // eagerly at configureServer, in build lazily on the first load() call; // either way the eventual load regenerates the CSS if ((include_base || include_theme) && style_rule_index === null) { @@ -552,7 +553,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug /** * Dev-only eager scan: extracts every matching file under the configured * roots before the virtual module is first served, so the initial CSS is - * complete — including classes from modules the SSR walk never reaches. + * complete - including classes from modules the SSR walk never reaches. * `load()` awaits the returned promise. See the `prescan` option. */ const run_prescan = async (): Promise<void> => { @@ -576,7 +577,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug // the same id; with enforce: 'pre' those are identical, so the // content-hash short-circuit makes the second ingest a no-op. A // loader that rewrites content before 'pre' plugins would extract - // twice, with the transform result winning — harmless, just noted. + // twice, with the transform result winning - harmless, just noted. const r = await deps.read_text({ path: id }); if (!r.ok) return; // deleted mid-scan or unreadable; transform covers it if it reappears await ingest_file(id, r.value); @@ -609,7 +610,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug configureServer(dev_server) { server = dev_server; - // Start bundled-resource loading now instead of at the first load() — + // Start bundled-resource loading now instead of at the first load() - // it overlaps with the pre-scan and the framework's own startup, // taking the base-CSS parse off the first request's critical path. // Errors are swallowed here: load() awaits the same cached promise @@ -630,7 +631,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug // Connect-time resync: a client that loaded the page while extraction // was still discovering files (late dep transforms, dynamic imports) - // may have missed the corrective HMR update — updates are dropped when + // may have missed the corrective HMR update - updates are dropped when // no socket is connected. When its socket opens, re-check and push if // the CSS moved past what load() served. dev_server.ws.on('connection', () => { @@ -638,7 +639,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug if ((include_base || include_theme) && style_rule_index === null) return; // Fast path: any state change schedules the debounce timer, so no // pending timer plus generated == served proves there's nothing to - // push — skip the render (the common case for every new tab). + // push - skip the render (the common case for every new tab). if (hmr_timeout === null && last_generated_css === last_served_css) return; const css = render_css(); if (css !== last_served_css) { @@ -647,7 +648,10 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug }); // Handle file deletion - watcher 'unlink' event - dev_server.watcher.on('unlink', (file) => { + dev_server.watcher.on('unlink', (raw_file) => { + // chokidar reports platform paths - normalize so the key matches the + // transform/prescan ingests (`hashes`, `transform_epochs`) + const file = normalizePath(raw_file); // Invalidate any in-flight transform for this file so its post-await // re-add can't resurrect the deleted entry. transform_epochs.delete(file); @@ -670,7 +674,8 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug } }); - // Clean up pending timeout on server close + // Clean up pending timeout on server close; `httpServer` is null in + // middleware mode, where buildEnd() below covers it dev_server.httpServer?.on('close', () => { if (hmr_timeout) { clearTimeout(hmr_timeout); @@ -680,20 +685,29 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug }, async buildStart() { - // Load CSS properties for validation (needed for transform()) + // Load CSS properties for literal-class validation during render_css() css_properties = await load_css_properties(); // Note: Bundled CSS resources (style_rule_index, variable_graph, class_variable_index) - // load via ensure_bundled_resources() — started eagerly in configureServer + // load via ensure_bundled_resources() - started eagerly in configureServer // (dev), or lazily on the first load() call (build) }, + buildEnd() { + // Vite calls this when the dev server closes too - the reliable cleanup + // in middleware mode, where there's no owned httpServer to listen to + if (hmr_timeout) { + clearTimeout(hmr_timeout); + hmr_timeout = null; + } + }, + resolveId(id) { if (id === VIRTUAL_ID) { return RESOLVED_VIRTUAL_ID; } // Claim the resolved id (and its `?inline`/`?direct`/`?used` query // variants) so re-resolution stays ours instead of hitting the - // filesystem — SvelteKit's dev SSR inlining loads `/__fuz.css?inline`. + // filesystem - SvelteKit's dev SSR inlining loads `/__fuz.css?inline`. if (id.split('?', 1)[0] === RESOLVED_VIRTUAL_ID) { return id; } @@ -702,7 +716,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug async load(id) { // Match the base id and any query variant (`?inline`, `?direct`, `?used`) - // Vite or SvelteKit appends — SvelteKit's dev SSR inlining loads `?inline`. + // Vite or SvelteKit appends - SvelteKit's dev SSR inlining loads `?inline`. if (id.split('?', 1)[0] !== RESOLVED_VIRTUAL_ID) { return undefined; } @@ -732,7 +746,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug last_served_css = css; // Track for connect-time resync return css; } - // Build: emit a marker rule (not a comment — comments are minified away) + // Build: emit a marker rule (not a comment - comments are minified away) // so generateBundle can find the importer's CSS asset (loaded on every // page) and splice the full generated CSS in there. return `:root{${FUZ_CSS_PLACEHOLDER}:1}`; @@ -770,7 +784,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug // Normal path: exactly one marked asset, loaded on every page. if (spliced_count === 1) return; - // Marker landed in more than one asset — the full CSS is now duplicated + // Marker landed in more than one asset - the full CSS is now duplicated // across them. Functional but bloated; surface it rather than ship silently. if (spliced_count > 1) { log_error( @@ -781,7 +795,7 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug } // spliced_count === 0 below: the marker is gone. Only act when the virtual - // module was actually imported and there's real CSS to deliver — otherwise + // module was actually imported and there's real CSS to deliver - otherwise // there's nothing to place (e.g. plugin installed but `virtual:fuz.css` // never imported, or a fully empty generation that's only banners). if (!virtual_module_loaded) return; @@ -799,14 +813,14 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug } } - // `virtual:fuz.css` was imported but there's nowhere to place its output — + // `virtual:fuz.css` was imported but there's nowhere to place its output - // every page would render completely unstyled. Fail the build rather than // ship a broken artifact (e.g. a config that inlines CSS into JS chunks, // where the marker rides in a chunk these asset checks skip). if (css_asset_count === 0) { throw new Error( '[fuz_css] virtual:fuz.css was imported but no CSS asset exists to hold the generated' + - ' styles — every page would be unstyled. Check the build config (cssCodeSplit /' + + ' styles - every page would be unstyled. Check the build config (cssCodeSplit /' + ' CSS-in-JS inlining).' ); } diff --git a/src/lib/wcag.ts b/src/lib/wcag.ts index 1ec58a431..46fb98c1f 100644 --- a/src/lib/wcag.ts +++ b/src/lib/wcag.ts @@ -1,7 +1,7 @@ /** * WCAG 2.x relative luminance and contrast ratio. * - * Design-time and test use only, like `oklch.ts` — these power the contrast + * Design-time and test use only, like `oklch.ts` - these power the contrast * gates that assert readable default pairings (body text on surfaces, links, * focus outlines) across the derived palette. * diff --git a/src/routes/KnobControl.svelte b/src/routes/KnobControl.svelte index 28bc300f0..4d42f0fce 100644 --- a/src/routes/KnobControl.svelte +++ b/src/routes/KnobControl.svelte @@ -194,13 +194,16 @@ <style> .knob { + /* control-column sizing; no distance token sits near these */ + --knob_basis: 260px; + --knob_max_width: 420px; position: relative; /* for the .knob_reset button */ - flex: 1 1 260px; - max-width: 420px; + flex: 1 1 var(--knob_basis); + max-width: var(--knob_max_width); } .knob.compact { - flex: 1 1 190px; - max-width: 300px; + --knob_basis: 190px; + --knob_max_width: 300px; } .title { /* keep long names clear of the reset button (an sm icon_button, diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte index b6fe8a1fe..26d15bba1 100644 --- a/src/routes/ThemeEditor.svelte +++ b/src/routes/ThemeEditor.svelte @@ -6,7 +6,7 @@ import ColorSchemeInput from '@fuzdev/fuz_ui/ColorSchemeInput.svelte'; import type { ThemeState } from '@fuzdev/fuz_ui/theme_state.svelte.ts'; - import { render_theme_style, type ThemeScheme } from '$lib/theme.ts'; + import { render_theme_style, type Theme, type ThemeScheme } from '$lib/theme.ts'; import { theme_knobs, knob_axes, type KnobAxis, type ThemeKnob } from '$lib/knobs.ts'; import { PALETTE_HUES } from '$lib/ramps.ts'; import { @@ -28,10 +28,16 @@ const { editor, - theme_state + theme_state, + onload_theme }: { editor: ThemeEditorState; theme_state: ThemeState; + /** + * Called after the "based on" select loads a theme as the new base, so + * the page can apply it and sync its own picker selection. + */ + onload_theme?: (theme: Theme) => void; } = $props(); // the scheme whose slots edits write to; OS changes while 'auto' aren't @@ -53,13 +59,21 @@ const binding_knobs = theme_knobs.filter((k) => k.bindable); const lever_knobs = theme_knobs.filter((k) => k.leverage === 'lg' && !k.bindable); - // the sm escape-hatch walls fold behind a disclosure per axis so the levers - // stay the visual main flow - const sm_details_titles: Partial<Record<KnobAxis, string>> = { - shape: 'border width & radius tokens', - density: 'space tokens', - typography: 'line height tokens', - motion: 'motion tokens' + // the sm knobs fold behind a disclosure per axis so the levers stay the + // visual main flow; each axis labels what its disclosure actually holds + const sm_details: Partial<Record<KnobAxis, { title: string; hint: string }>> = { + color: { + title: 'chroma character & surface hooks', + hint: 'per-slot multipliers and micro-surface colors' + }, + shape: { + title: 'border width & radius tokens', + hint: 'escape hatches - pin individual tokens' + }, + density: { title: 'space tokens', hint: 'escape hatches - pin individual tokens' }, + depth: { title: 'backdrop hook', hint: 'the dialog and fullscreen backdrop dim' }, + typography: { title: 'line height tokens', hint: 'escape hatches - pin individual tokens' }, + motion: { title: 'duration tokens', hint: 'escape hatches - pin individual tokens' } }; // resolves a letter's current angle from the same merge the renderer uses, @@ -83,7 +97,10 @@ return; } const theme = editor.themes.find((t) => t.name === name); - if (theme) editor.load_theme(theme); + if (theme) { + editor.load_theme(theme); + onload_theme?.(theme); + } }; const trimmed_name = $derived(editor.name.trim()); @@ -179,7 +196,7 @@ </div> <div> <small>levers - the highest-leverage knobs</small> - <div class="knobs row flex-wrap:wrap gap_md"> + <div class="knobs compact row flex-wrap:wrap"> {#each lever_knobs as knob (knob.name)} {@render knob_control(knob, true)} {/each} @@ -212,10 +229,11 @@ </div> {/if} {#if sm_knobs.length} + {@const sm_meta = sm_details[axis]} <details class="mt_lg"> <summary> - {sm_details_titles[axis] ?? 'granular tokens'} - <small>(escape hatches - pin individual tokens)</small> + {sm_meta?.title ?? 'granular tokens'} + <small>({sm_meta?.hint ?? 'escape hatches - pin individual tokens'})</small> </summary> <div class="knobs row flex-wrap:wrap gap_lg align-items:flex-end mt_md"> {#each sm_knobs as knob (knob.name)} @@ -250,8 +268,8 @@ <section> <h3>Output</h3> <p> - The copyable <code>Theme</code> object, and the CSS it renders (only the variables the theme - sets). + The copyable <code>Theme</code> object, and the CSS it renders - the variables the theme sets, + plus the scheme-stance mirror when the theme is single-scheme. </p> <div class="rendered mb_lg"> <div class="copy"> @@ -259,7 +277,7 @@ </div> <Code content={output_ts} lang="ts" /> </div> - {#if editor.output.variables.length} + {#if editor.output.variables.length || editor.output.scheme_mirror?.length} <div class="rendered mb_lg"> <div class="copy"> <CopyToClipboard text={output_css} /> @@ -276,8 +294,9 @@ </div> <style> - .knobs { - row-gap: var(--space_lg); + /* dense horizontally, normal rhythm between wrapped rows */ + .knobs.compact { + gap: var(--space_lg) var(--space_md); } .ramp_strips { display: grid; diff --git a/src/routes/docs/classes/+page.svelte b/src/routes/docs/classes/+page.svelte index da1ad4b2c..1ba75c091 100644 --- a/src/routes/docs/classes/+page.svelte +++ b/src/routes/docs/classes/+page.svelte @@ -527,15 +527,17 @@ const el = document.createElement('dialog');`} </ol> <p> Sections are emitted in that order inside <MdnLink path="Web/CSS/@layer" /> cascade layers: - defaults in <code>fuz.base</code>, theme overrides in <code>fuz.theme</code>, and utilities in - <code>fuz.utilities</code>, so utilities beat the reset by layer order (not specificity) and - your own unlayered styles beat everything. + defaults in <code>fuz.base</code>, OS user-preference mappings (like + <code>prefers-contrast</code>) in <code>fuz.preferences</code>, theme overrides in + <code>fuz.theme</code>, and utilities in <code>fuz.utilities</code>, so utilities beat the + reset by layer order (not specificity) and your own unlayered styles beat everything. </p> <p> If you organize your own styles in <code>@layer</code>, declare fuz's layers first so yours - sort later and win: <code>@layer fuz.base, fuz.theme, fuz.utilities, app;</code>. - Later-declared layers win, so without this line fuz's layers are declared when its CSS loads - and beat any layers you declared before it. + sort later and win: + <code>@layer fuz.base, fuz.preferences, fuz.theme, fuz.utilities, app;</code>. Later-declared + layers win, so without this line fuz's layers are declared when its CSS loads and beat any + layers you declared before it. </p> <h4>Base styles</h4> diff --git a/src/routes/docs/colors/ColorSwatchItem.svelte b/src/routes/docs/colors/ColorSwatchItem.svelte index bf2c9400a..c0076ea63 100644 --- a/src/routes/docs/colors/ColorSwatchItem.svelte +++ b/src/routes/docs/colors/ColorSwatchItem.svelte @@ -19,15 +19,18 @@ const name = $derived(`palette_${letter}_${intensity}`); let color_el: HTMLElement | undefined = $state.raw(); + let resolved = $state.raw(''); // the stop's value is a derived calc()/oklch() expression, so read the - // browser-resolved color off the rendered swatch element instead - const resolved = $derived.by(() => { - // re-read when the user switches color scheme or theme + // browser-resolved color off the rendered swatch element instead; an + // effect (not a derived) so the read happens after the scheme class + // toggles on the root - same shape as the borders page's ResolvedColorCode + $effect(() => { theme_state.color_scheme; theme_state.theme; - if (!color_el) return ''; - return window.getComputedStyle(color_el).backgroundColor; + name; + if (!color_el) return; + resolved = window.getComputedStyle(color_el).backgroundColor; }); const parsed_oklch = $derived.by((): Oklch | null => { diff --git a/src/routes/docs/colors/HueSwatch.svelte b/src/routes/docs/colors/HueSwatch.svelte index b1d6678c0..c0dcc6f03 100644 --- a/src/routes/docs/colors/HueSwatch.svelte +++ b/src/routes/docs/colors/HueSwatch.svelte @@ -2,6 +2,7 @@ import StyleVariableButton from '@fuzdev/fuz_ui/StyleVariableButton.svelte'; import type { PaletteVariant } from '$lib/variable_data.ts'; + import { PALETTE_HUES } from '$lib/ramps.ts'; const { letter, @@ -20,7 +21,12 @@ const get_color_hue_string = (name: string) => computed_styles?.getPropertyValue('--' + name); const variable_name = $derived(`hue_${letter}`); - const hue = $derived(Number(get_color_hue_string(variable_name))); + // `computed_styles` is null during SSR/prerender - fall back to the default + // angle so the static HTML doesn't ship NaN + const hue = $derived.by(() => { + const n = Number(get_color_hue_string(variable_name)); + return Number.isNaN(n) ? PALETTE_HUES[letter] : n; + }); </script> <li style:--hue="var(--{variable_name})"> diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index 33ef21198..aeab8dcfa 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -35,7 +35,7 @@ const theme_state = get_theme_state(); // one gallery: the registry and the shipped exemplars are a single list to - // users — registry membership is policy for consumer pickers, not UX + // users - registry membership is policy for consumer pickers, not UX const themes = [ ...default_themes, necromancer_theme, @@ -47,8 +47,11 @@ const editor = new ThemeEditorState(themes); // the picked base theme, tracked apart from the applied theme so the picker - // highlight survives contrast composition (compositions rename themselves) - let selected_base: Theme = $state.raw(theme_state.theme); + // highlight survives contrast composition (compositions rename themselves); + // a persisted unsaved draft isn't a base, so fall back to the editor's + let selected_base: Theme = $state.raw( + theme_state.theme.name === UNSAVED_THEME_NAME ? editor.base_theme : theme_state.theme + ); let contrast_modifier: Theme | null = $state.raw(null); // the in-progress theme appears in the picker as soon as a knob moves @@ -99,10 +102,22 @@ } }); + // applies a theme the editor's "based on" select loaded as the new base + const on_editor_load_theme = (theme: Theme): void => { + selected_base = theme; + apply_theme(); + }; + // persist the in-progress theme across navigation (history-entry-scoped) export const snapshot: Snapshot<ThemeEditorSnapshotData> = { capture: () => editor.to_snapshot(), - restore: (data) => editor.restore_snapshot(data) + restore: (data) => { + editor.restore_snapshot(data); + // re-sync the picker highlight and applied theme with the restored + // base; a dirty draft re-applies itself through the effect below + selected_base = editor.base_theme; + if (!editor.dirty) apply_theme(); + } }; </script> @@ -227,7 +242,7 @@ export default defineConfig({plugins: [vite_plugin_fuz_css({theme: necromancer_t <code>Theme</code> object below to keep it. </p> - <ThemeEditor {editor} {theme_state} /> + <ThemeEditor {editor} {theme_state} onload_theme={on_editor_load_theme} /> </TomeSection> <TomeSection> <TomeSectionHeader text="Validating and compiling themes" /> diff --git a/src/routes/theme_editor_state.svelte.ts b/src/routes/theme_editor_state.svelte.ts index 21bf58411..718ccce9f 100644 --- a/src/routes/theme_editor_state.svelte.ts +++ b/src/routes/theme_editor_state.svelte.ts @@ -1,6 +1,7 @@ import { SvelteMap } from 'svelte/reactivity'; import type { Theme, ThemeScheme } from '$lib/theme.ts'; +import { resolve_theme_stance } from '$lib/theme_stance.ts'; import type { StyleVariable } from '$lib/variable.ts'; import { default_variables } from '$lib/variables.ts'; import { theme_knob_by_name } from '$lib/knobs.ts'; @@ -40,7 +41,7 @@ export interface ThemeEditorSnapshotData { * (dual-slot) edit the slot of the scheme being viewed; single-slot variables * always edit the light (base) slot so the change applies to both schemes. * When a fresh light-slot override lands on a variable whose default is - * dual-slot, the merge preserves the default's dark slot explicitly — a + * dual-slot, the merge preserves the default's dark slot explicitly - a * theme's `:root` block beats the base defaults' `:root.dark` by cascade * layer order, so omitting it would silently change dark mode too. A base * theme that itself sets only light slots (e.g. dark-only mirrors) chose @@ -84,8 +85,8 @@ export class ThemeEditorState { readonly dirty: boolean = $derived(this.overrides.size > 0 || this.scheme !== this.base_scheme); /** - * True when the draft (or its base) moves palette-tier knobs — the letter - * hues — making it an exemplar-tier theme per the two-tier policy. + * True when the draft (or its base) moves palette-tier knobs - the letter + * hues - making it an exemplar-tier theme per the two-tier policy. */ readonly is_palette_tier: boolean = $derived.by(() => { for (const name of this.overrides.keys()) { @@ -132,23 +133,32 @@ export class ThemeEditorState { return merged; } - /** The live-applied theme, stably named so pickers key it consistently. */ - readonly draft: Theme = $derived({ - name: UNSAVED_THEME_NAME, - variables: this.merged_variables, - ...(this.stance ? { scheme: this.stance } : {}) - }); + /** + * The live-applied theme, stably named so pickers key it consistently. + * Resolved through `resolve_theme_stance` so a single-scheme draft carries + * its mirror - without it the renderer would pin `color-scheme` but show + * the other scheme's defaults. + */ + readonly draft: Theme = $derived( + resolve_theme_stance({ + name: UNSAVED_THEME_NAME, + variables: this.merged_variables, + ...(this.stance ? { scheme: this.stance } : {}) + }) + ); - /** The copyable theme, carrying the user's chosen name. */ - readonly output: Theme = $derived({ - name: this.name, - variables: this.merged_variables, - ...(this.stance ? { scheme: this.stance } : {}) - }); + /** The copyable theme, carrying the user's chosen name, stance-resolved. */ + readonly output: Theme = $derived( + resolve_theme_stance({ + name: this.name, + variables: this.merged_variables, + ...(this.stance ? { scheme: this.stance } : {}) + }) + ); /** * The value a scheme currently renders for a variable, derived from the - * same merge the renderer uses so the two can't disagree — including the + * same merge the renderer uses so the two can't disagree - including the * theme layer's light slots beating the base defaults' dark slots, the * merge preserving a scheme-adaptive default's dark slot under fresh * light-only overrides, and a single-scheme stance mirroring untouched @@ -254,7 +264,7 @@ export class ThemeEditorState { /** * The confirm-dialog message shown before a dirty draft is discarded by - * loading `name` as the new base — shared by every picker that can trigger + * loading `name` as the new base - shared by every picker that can trigger * the flatten-on-load, so the wording can't drift. */ export const discard_confirm_message = (editor: ThemeEditorState, name: string): string => { @@ -268,7 +278,10 @@ const escape_single_quotes = (s: string): string => s.replaceAll('\\', '\\\\').replaceAll("'", "\\'"); /** - * Renders a theme as a copyable TypeScript module. + * Renders a theme as a copyable TypeScript module. A single-scheme theme + * emits the same resolve-at-module-scope shape as the shipped stanced + * exemplars, so the copied module is render-ready - the authored variables + * stay legible and the stance mirror computes where the theme is defined. */ export const render_theme_ts = (theme: Theme): string => { const identifier = @@ -288,14 +301,25 @@ export const render_theme_ts = (theme: Theme): string => { const variables_ts = theme.variables.length ? `[\n${variables}\n\t],` : '[], // empty - every variable keeps its base default'; - const scheme_ts = - theme.scheme === 'light' || theme.scheme === 'dark' - ? `\n\tscheme: '${theme.scheme}', // renders this appearance in both color schemes` - : ''; + const stanced = theme.scheme === 'light' || theme.scheme === 'dark'; + if (stanced) { + return `import type {Theme} from '@fuzdev/fuz_css/theme.ts'; +import {resolve_theme_stance} from '@fuzdev/fuz_css/theme_stance.ts'; + +const authored: Theme = { + name: '${escape_single_quotes(theme.name)}', + scheme: '${theme.scheme}', // renders this appearance in both color schemes + variables: ${variables_ts} +}; + +/** Resolved at module scope so the theme is render-ready when imported. */ +export const ${identifier}_theme: Theme = resolve_theme_stance(authored); +`; + } return `import type {Theme} from '@fuzdev/fuz_css/theme.ts'; export const ${identifier}_theme: Theme = { - name: '${escape_single_quotes(theme.name)}',${scheme_ts} + name: '${escape_single_quotes(theme.name)}', variables: ${variables_ts} }; `; diff --git a/src/test/css_bundled_resolution.diagnostics.test.ts b/src/test/css_bundled_resolution.diagnostics.test.ts index 0ec6a2672..c95ce4d42 100644 --- a/src/test/css_bundled_resolution.diagnostics.test.ts +++ b/src/test/css_bundled_resolution.diagnostics.test.ts @@ -563,7 +563,7 @@ describe('resolve_css diagnostics', () => { style_rule_index, variable_graph, class_variable_index, - // force-included, not referenced by any shipped CSS — exclude is a clean override + // force-included, not referenced by any shipped CSS - exclude is a clean override additional_variables: ['brand'], exclude_variables: ['brand'] }); diff --git a/src/test/css_bundled_resolution_fixtures.ts b/src/test/css_bundled_resolution_fixtures.ts index 982bb0506..07a18e63d 100644 --- a/src/test/css_bundled_resolution_fixtures.ts +++ b/src/test/css_bundled_resolution_fixtures.ts @@ -22,8 +22,8 @@ export const create_test_fixtures = ( variable_graph: ReturnType<typeof build_variable_graph>; class_variable_index: ReturnType<typeof build_class_variable_index>; } => { - const style_rule_index = parse_style_css(css, 'test-hash'); - const variable_graph = build_variable_graph(variables, 'test-hash'); + const style_rule_index = parse_style_css(css); + const variable_graph = build_variable_graph(variables); const class_variable_index = build_class_variable_index(class_defs); return { style_rule_index, variable_graph, class_variable_index }; }; diff --git a/src/test/fixtures/css_classes_fixture.json b/src/test/fixtures/css_classes_fixture.json index 1498ae084..3a6a49650 100644 --- a/src/test/fixtures/css_classes_fixture.json +++ b/src/test/fixtures/css_classes_fixture.json @@ -1,2690 +1 @@ -{ - "pixelated": { - "declaration": "\n\t\t\timage-rendering: -webkit-optimize-contrast; /* Safari */\n\t\t\timage-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */\n\t\t\timage-rendering: pixelated; /* in case crisp-edges isn't supported */\n\t\t\timage-rendering: crisp-edges; /* the recommended pixel art setting according to MDN */\n\t\t" - }, - "circular": { "declaration": "border-radius: 50%;" }, - "box": { - "declaration": "\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t" - }, - "column": { - "comment": "like `.box` but uncentered", - "declaration": "\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t" - }, - "row": { - "comment": "can be used to override the direction of a `.box`", - "declaration": "\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: row;\n\t\t\talign-items: center;\n\t\t" - }, - "ellipsis": { - "declaration": "\n\t\t\tdisplay: block;\n\t\t\twhite-space: nowrap;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t" - }, - "selectable": { - "ruleset": "\n\t\t\t.selectable {\n\t\t\t\t--button_fill: color-mix(in oklab, var(--shade_50) 8%, transparent);\n\t\t\t\t--button_fill_hover: color-mix(in oklab, var(--shade_50) 16%, transparent);\n\t\t\t\t--button_fill_active: color-mix(in oklab, var(--shade_50) 24%, transparent);\n\t\t\t\tcursor: pointer;\n\t\t\t\tbackground-color: var(--button_fill);\n\t\t\t\tborder-color: var(--border_color_30);\n\t\t\t\tborder-style: var(--border_style);\n\t\t\t\tborder-width: var(--border_width);\n\t\t\t}\n\t\t\t.selectable:hover {\n\t\t\t\tbackground-color: var(--button_fill_hover);\n\t\t\t\tborder-color: var(--border_color_20);\n\t\t\t}\n\t\t\t.selectable.selected,\n\t\t\t.selectable:active {\n\t\t\t\tbackground-color: var(--button_fill_active);\n\t\t\t\tborder-color: var(--accent_50);\n\t\t\t}\n\t\t\t.selectable.selected {\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t\t.selectable.selected.deselectable:not(:disabled) {\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\t\t" - }, - "clickable": { - "ruleset": "\n\t\t\t.clickable {\n\t\t\t\tcursor: pointer;\n\t\t\t\ttransform: var(--clickable_transform, scale3d(1, 1, 1));\n\t\t\t\ttransform-origin: var(--clickable_transform_origin);\n\t\t\t\ttransition-duration: var(--clickable_transition_duration); /* default to instant, chunky/lofi */\n\t\t\t}\n\t\t\t.clickable:focus {\n\t\t\t\ttransform: var(--clickable_transform_focus, scale3d(1.07, 1.07, 1.07));\n\t\t\t}\n\t\t\t.clickable:hover {\n\t\t\t\ttransform: var(--clickable_transform_hover, scale3d(1.1, 1.1, 1.1));\n\t\t\t}\n\t\t\t.clickable:active,\n\t\t\t.clickable.active {\n\t\t\t\ttransform: var(--clickable_transform_active, scale3d(1.2, 1.2, 1.2));\n\t\t\t}\n\t\t" - }, - "pane": { - "comment": "A pane is a box floating over the page, like for dialogs. By default it's opaque, resetting the background to the initial depth.", - "declaration": "\n\t\t\tbackground-color: var(--shade_00);\n\t\t\tbox-shadow: var(--pane_shadow, var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_50), transparent));\n\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t" - }, - "panel": { - "comment": "A panel is a box embedded into the page, useful for visually isolating content.", - "declaration": "\n\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t\tbackground-color: var(--fg_10);\n\t\t" - }, - "xs": { - "comment": "Smallest sizing, two steps down from the default, cascading to children. Works on individual elements or containers.", - "declaration": "\n\t\t\t--font_size: var(--font_size_xs);\n\t\t\t--input_height: var(--space_xl3);\n\t\t\t--input_height_compact: var(--space_xl2);\n\t\t\t--input_padding_x: var(--space_sm);\n\t\t\t--chip_padding_x: var(--space_xs3);\n\t\t\t--icon_size: var(--icon_size_xs);\n\t\t\t--menuitem_padding: var(--space_xs5) var(--space_xs3);\n\t\t\t--flow_margin: var(--space_sm);\n\t\t" - }, - "sm": { - "comment": "Smaller sizing, one step down from the default, cascading to children. Works on individual elements or containers.", - "declaration": "\n\t\t\t--font_size: var(--font_size_sm);\n\t\t\t--input_height: var(--space_xl4);\n\t\t\t--input_height_compact: var(--space_xl3);\n\t\t\t--input_padding_x: var(--space_md);\n\t\t\t--chip_padding_x: var(--space_xs2);\n\t\t\t--icon_size: var(--icon_size_sm);\n\t\t\t--menuitem_padding: var(--space_xs4) var(--space_xs2);\n\t\t\t--flow_margin: var(--space_md);\n\t\t" - }, - "md": { - "comment": "Default sizing restated explicitly, useful as a cascade reset within a sized parent.", - "declaration": "\n\t\t\t--font_size: var(--font_size_md);\n\t\t\t--input_height: var(--space_xl5);\n\t\t\t--input_height_compact: var(--space_xl4);\n\t\t\t--input_padding_x: var(--space_lg);\n\t\t\t--chip_padding_x: var(--space_xs);\n\t\t\t--icon_size: var(--icon_size_md);\n\t\t\t--menuitem_padding: var(--space_xs3) var(--space_xs);\n\t\t\t--flow_margin: var(--space_lg);\n\t\t" - }, - "lg": { - "comment": "Larger sizing, one step up from the default, cascading to children. Works on individual elements or containers.", - "declaration": "\n\t\t\t--font_size: var(--font_size_lg);\n\t\t\t--input_height: var(--space_xl6);\n\t\t\t--input_height_compact: var(--space_xl5);\n\t\t\t--input_padding_x: var(--space_xl);\n\t\t\t--chip_padding_x: var(--space_sm);\n\t\t\t--icon_size: var(--icon_size_lg);\n\t\t\t--menuitem_padding: var(--space_xs2) var(--space_sm);\n\t\t\t--flow_margin: var(--space_xl);\n\t\t" - }, - "xl": { - "comment": "Largest sizing, two steps up from the default, cascading to children. Works on individual elements or containers.", - "declaration": "\n\t\t\t--font_size: var(--font_size_xl);\n\t\t\t--input_height: var(--space_xl7);\n\t\t\t--input_height_compact: var(--space_xl6);\n\t\t\t--input_padding_x: var(--space_xl2);\n\t\t\t--chip_padding_x: var(--space_md);\n\t\t\t--icon_size: var(--icon_size_xl);\n\t\t\t--menuitem_padding: var(--space_xs) var(--space_md);\n\t\t\t--flow_margin: var(--space_xl2);\n\t\t" - }, - "mb_flow": { - "comment": "Flow-aware margin-bottom that responds to --flow_margin overrides from size composites.", - "declaration": "margin-bottom: var(--flow_margin, var(--space_lg));" - }, - "mt_flow": { - "comment": "Flow-aware margin-top that responds to --flow_margin overrides from size composites.", - "declaration": "margin-top: var(--flow_margin, var(--space_lg));" - }, - "icon_button": { - "comment": "\n\t\t\tTODO other button variants?\n\t\t\tTODO this is slightly strange that it doesn't use --icon_size.\n\t\t\tThese are used as modifiers to buttons.\n\t\t", - "declaration": "\n\t\t\twidth: var(--input_height);\n\t\t\theight: var(--input_height);\n\t\t\tmin-width: var(--input_height);\n\t\t\tmin-height: var(--input_height);\n\t\t\tflex-shrink: 0;\n\t\t\tline-height: 1;\n\t\t\tfont-weight: 900;\n\t\t\tpadding: 0;\n\t\t" - }, - "plain": { - "comment": "TODO maybe this belongs with the reset, like `selected`? or does `selected` belong here?", - "ruleset": "\n\t\t\t.plain:not(:hover) {\n\t\t\t\t--border_color: transparent;\n\t\t\t\tbox-shadow: none;\n\t\t\t\t--button_fill: transparent;\n\t\t\t}\n\t\t\t.plain:hover, .plain:active {\n\t\t\t\t--border_color: transparent;\n\t\t\t}\n\t\t" - }, - "menuitem": { - "ruleset": "\n\t\t\t.menuitem {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tborder-radius: 0;\n\t\t\t\tposition: relative;\n\t\t\t\tcursor: pointer;\n\t\t\t\twidth: 100%;\n\t\t\t\tmin-height: var(--menuitem_min_height, var(--input_height_compact));\n\t\t\t\tdisplay: flex;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\talign-items: center;\n\t\t\t\tpadding: var(--menuitem_padding, var(--space_xs3) var(--space_xs));\n\t\t\t}\n\t\t\t.menuitem.selected {\n\t\t\t\t/* TODO different patterns for border and surface? */\n\t\t\t\t--border_color: var(--accent_50);\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t\t.menuitem.selected.deselectable:not(:disabled) {\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\t\t\t.menuitem:hover {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t}\n\t\t\t.menuitem:active,\n\t\t\t.menuitem.selected:hover {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tbackground-color: var(--fg_20);\n\t\t\t}\n\t\t\t.menuitem.plain {\n\t\t\t\tborder: none;\n\t\t\t}\n\t\t\t.menuitem .content {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tflex: 1;\n\t\t\t\t/* allows the flex children to shrink */\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\t\t\t.menuitem .icon {\n\t\t\t\twidth: var(--icon_size, var(--icon_size_md));\n\t\t\t\tmargin-right: var(--space_sm);\n\t\t\t\tflex-shrink: 0;\n\t\t\t\ttext-align: center;\n\t\t\t\tfont-weight: 900;\n\t\t\t}\n\t\t\t.menuitem .title {\n\t\t\t\tmargin-right: var(--space_lg);\n\t\t\t\tflex-shrink: 1;\n\t\t\t\toverflow: hidden;\n\t\t\t\twhite-space: nowrap;\n\t\t\t\ttext-overflow: ellipsis;\n\t\t\t\tline-height: var(--line_height_lg); /* prevents the bottom of g's and others from being cut off */\n\t\t\t}\n\t\t" - }, - "chevron": { - "ruleset": "\n\t\t\t.chevron {\n\t\t\t\tposition: relative;\n\t\t\t\theight: 8px;\n\t\t\t}\n\t\t\t.chevron::before {\n\t\t\t\tdisplay: block;\n\t\t\t\tcontent: '';\n\t\t\t\tborder: 4px solid transparent;\n\t\t\t\tborder-left-color: var(--text_70);\n\t\t\t}\n\t\t" - }, - "chip": { - "ruleset": "\n\t\t\t.chip {\n\t\t\t\tfont-weight: 500;\n\t\t\t\tfont-size: var(--font_size, inherit);\n\t\t\t\tpadding-left: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tpadding-right: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t\t}\n\t\t\ta.chip {\n\t\t\t\tfont-weight: 600;\n\t\t\t}\n\t\t\t.chip.palette_a {\n\t\t\t\tcolor: var(--palette_a_50);\n\t\t\t\tbackground-color: var(--palette_a_10);\n\t\t\t}\n\t\t\t.chip.palette_b {\n\t\t\t\tcolor: var(--palette_b_50);\n\t\t\t\tbackground-color: var(--palette_b_10);\n\t\t\t}\n\t\t\t.chip.palette_c {\n\t\t\t\tcolor: var(--palette_c_50);\n\t\t\t\tbackground-color: var(--palette_c_10);\n\t\t\t}\n\t\t\t.chip.palette_d {\n\t\t\t\tcolor: var(--palette_d_50);\n\t\t\t\tbackground-color: var(--palette_d_10);\n\t\t\t}\n\t\t\t.chip.palette_e {\n\t\t\t\tcolor: var(--palette_e_50);\n\t\t\t\tbackground-color: var(--palette_e_10);\n\t\t\t}\n\t\t\t.chip.palette_f {\n\t\t\t\tcolor: var(--palette_f_50);\n\t\t\t\tbackground-color: var(--palette_f_10);\n\t\t\t}\n\t\t\t.chip.palette_g {\n\t\t\t\tcolor: var(--palette_g_50);\n\t\t\t\tbackground-color: var(--palette_g_10);\n\t\t\t}\n\t\t\t.chip.palette_h {\n\t\t\t\tcolor: var(--palette_h_50);\n\t\t\t\tbackground-color: var(--palette_h_10);\n\t\t\t}\n\t\t\t.chip.palette_i {\n\t\t\t\tcolor: var(--palette_i_50);\n\t\t\t\tbackground-color: var(--palette_i_10);\n\t\t\t}\n\t\t\t.chip.palette_j {\n\t\t\t\tcolor: var(--palette_j_50);\n\t\t\t\tbackground-color: var(--palette_j_10);\n\t\t\t}\n\t\t" - }, - "font_family_sans": { "declaration": "font-family: var(--font_family_sans);" }, - "font_family_serif": { "declaration": "font-family: var(--font_family_serif);" }, - "font_family_mono": { "declaration": "font-family: var(--font_family_mono);" }, - "line_height_xs": { "declaration": "line-height: var(--line_height_xs);" }, - "line_height_sm": { "declaration": "line-height: var(--line_height_sm);" }, - "line_height_md": { "declaration": "line-height: var(--line_height_md);" }, - "line_height_lg": { "declaration": "line-height: var(--line_height_lg);" }, - "line_height_xl": { "declaration": "line-height: var(--line_height_xl);" }, - "font_size_xs": { - "declaration": "font-size: var(--font_size_xs); --font_size: var(--font_size_xs);" - }, - "font_size_sm": { - "declaration": "font-size: var(--font_size_sm); --font_size: var(--font_size_sm);" - }, - "font_size_md": { - "declaration": "font-size: var(--font_size_md); --font_size: var(--font_size_md);" - }, - "font_size_lg": { - "declaration": "font-size: var(--font_size_lg); --font_size: var(--font_size_lg);" - }, - "font_size_xl": { - "declaration": "font-size: var(--font_size_xl); --font_size: var(--font_size_xl);" - }, - "font_size_xl2": { - "declaration": "font-size: var(--font_size_xl2); --font_size: var(--font_size_xl2);" - }, - "font_size_xl3": { - "declaration": "font-size: var(--font_size_xl3); --font_size: var(--font_size_xl3);" - }, - "font_size_xl4": { - "declaration": "font-size: var(--font_size_xl4); --font_size: var(--font_size_xl4);" - }, - "font_size_xl5": { - "declaration": "font-size: var(--font_size_xl5); --font_size: var(--font_size_xl5);" - }, - "font_size_xl6": { - "declaration": "font-size: var(--font_size_xl6); --font_size: var(--font_size_xl6);" - }, - "font_size_xl7": { - "declaration": "font-size: var(--font_size_xl7); --font_size: var(--font_size_xl7);" - }, - "font_size_xl8": { - "declaration": "font-size: var(--font_size_xl8); --font_size: var(--font_size_xl8);" - }, - "font_size_xl9": { - "declaration": "font-size: var(--font_size_xl9); --font_size: var(--font_size_xl9);" - }, - "icon_size_xs": { - "declaration": "font-size: var(--icon_size_xs); --font_size: var(--icon_size_xs);" - }, - "icon_size_sm": { - "declaration": "font-size: var(--icon_size_sm); --font_size: var(--icon_size_sm);" - }, - "icon_size_md": { - "declaration": "font-size: var(--icon_size_md); --font_size: var(--icon_size_md);" - }, - "icon_size_lg": { - "declaration": "font-size: var(--icon_size_lg); --font_size: var(--icon_size_lg);" - }, - "icon_size_xl": { - "declaration": "font-size: var(--icon_size_xl); --font_size: var(--icon_size_xl);" - }, - "icon_size_xl2": { - "declaration": "font-size: var(--icon_size_xl2); --font_size: var(--icon_size_xl2);" - }, - "icon_size_xl3": { - "declaration": "font-size: var(--icon_size_xl3); --font_size: var(--icon_size_xl3);" - }, - "text_min": { "declaration": "color: var(--text_min); --text_color: var(--text_min);" }, - "text_00": { "declaration": "color: var(--text_00); --text_color: var(--text_00);" }, - "text_05": { "declaration": "color: var(--text_05); --text_color: var(--text_05);" }, - "text_10": { "declaration": "color: var(--text_10); --text_color: var(--text_10);" }, - "text_20": { "declaration": "color: var(--text_20); --text_color: var(--text_20);" }, - "text_30": { "declaration": "color: var(--text_30); --text_color: var(--text_30);" }, - "text_40": { "declaration": "color: var(--text_40); --text_color: var(--text_40);" }, - "text_50": { "declaration": "color: var(--text_50); --text_color: var(--text_50);" }, - "text_60": { "declaration": "color: var(--text_60); --text_color: var(--text_60);" }, - "text_70": { "declaration": "color: var(--text_70); --text_color: var(--text_70);" }, - "text_80": { "declaration": "color: var(--text_80); --text_color: var(--text_80);" }, - "text_90": { "declaration": "color: var(--text_90); --text_color: var(--text_90);" }, - "text_95": { "declaration": "color: var(--text_95); --text_color: var(--text_95);" }, - "text_100": { "declaration": "color: var(--text_100); --text_color: var(--text_100);" }, - "text_max": { "declaration": "color: var(--text_max); --text_color: var(--text_max);" }, - "shade_min": { "declaration": "background-color: var(--shade_min);" }, - "shade_00": { "declaration": "background-color: var(--shade_00);" }, - "shade_05": { "declaration": "background-color: var(--shade_05);" }, - "shade_10": { "declaration": "background-color: var(--shade_10);" }, - "shade_20": { "declaration": "background-color: var(--shade_20);" }, - "shade_30": { "declaration": "background-color: var(--shade_30);" }, - "shade_40": { "declaration": "background-color: var(--shade_40);" }, - "shade_50": { "declaration": "background-color: var(--shade_50);" }, - "shade_60": { "declaration": "background-color: var(--shade_60);" }, - "shade_70": { "declaration": "background-color: var(--shade_70);" }, - "shade_80": { "declaration": "background-color: var(--shade_80);" }, - "shade_90": { "declaration": "background-color: var(--shade_90);" }, - "shade_95": { "declaration": "background-color: var(--shade_95);" }, - "shade_100": { "declaration": "background-color: var(--shade_100);" }, - "shade_max": { "declaration": "background-color: var(--shade_max);" }, - "hue_a": { "declaration": "--hue: var(--hue_a);" }, - "hue_b": { "declaration": "--hue: var(--hue_b);" }, - "hue_c": { "declaration": "--hue: var(--hue_c);" }, - "hue_d": { "declaration": "--hue: var(--hue_d);" }, - "hue_e": { "declaration": "--hue: var(--hue_e);" }, - "hue_f": { "declaration": "--hue: var(--hue_f);" }, - "hue_g": { "declaration": "--hue: var(--hue_g);" }, - "hue_h": { "declaration": "--hue: var(--hue_h);" }, - "hue_i": { "declaration": "--hue: var(--hue_i);" }, - "hue_j": { "declaration": "--hue: var(--hue_j);" }, - "palette_a_00": { - "declaration": "color: var(--palette_a_00); --text_color: var(--palette_a_00);" - }, - "palette_a_05": { - "declaration": "color: var(--palette_a_05); --text_color: var(--palette_a_05);" - }, - "palette_a_10": { - "declaration": "color: var(--palette_a_10); --text_color: var(--palette_a_10);" - }, - "palette_a_20": { - "declaration": "color: var(--palette_a_20); --text_color: var(--palette_a_20);" - }, - "palette_a_30": { - "declaration": "color: var(--palette_a_30); --text_color: var(--palette_a_30);" - }, - "palette_a_40": { - "declaration": "color: var(--palette_a_40); --text_color: var(--palette_a_40);" - }, - "palette_a_50": { - "declaration": "color: var(--palette_a_50); --text_color: var(--palette_a_50);" - }, - "palette_a_60": { - "declaration": "color: var(--palette_a_60); --text_color: var(--palette_a_60);" - }, - "palette_a_70": { - "declaration": "color: var(--palette_a_70); --text_color: var(--palette_a_70);" - }, - "palette_a_80": { - "declaration": "color: var(--palette_a_80); --text_color: var(--palette_a_80);" - }, - "palette_a_90": { - "declaration": "color: var(--palette_a_90); --text_color: var(--palette_a_90);" - }, - "palette_a_95": { - "declaration": "color: var(--palette_a_95); --text_color: var(--palette_a_95);" - }, - "palette_a_100": { - "declaration": "color: var(--palette_a_100); --text_color: var(--palette_a_100);" - }, - "palette_b_00": { - "declaration": "color: var(--palette_b_00); --text_color: var(--palette_b_00);" - }, - "palette_b_05": { - "declaration": "color: var(--palette_b_05); --text_color: var(--palette_b_05);" - }, - "palette_b_10": { - "declaration": "color: var(--palette_b_10); --text_color: var(--palette_b_10);" - }, - "palette_b_20": { - "declaration": "color: var(--palette_b_20); --text_color: var(--palette_b_20);" - }, - "palette_b_30": { - "declaration": "color: var(--palette_b_30); --text_color: var(--palette_b_30);" - }, - "palette_b_40": { - "declaration": "color: var(--palette_b_40); --text_color: var(--palette_b_40);" - }, - "palette_b_50": { - "declaration": "color: var(--palette_b_50); --text_color: var(--palette_b_50);" - }, - "palette_b_60": { - "declaration": "color: var(--palette_b_60); --text_color: var(--palette_b_60);" - }, - "palette_b_70": { - "declaration": "color: var(--palette_b_70); --text_color: var(--palette_b_70);" - }, - "palette_b_80": { - "declaration": "color: var(--palette_b_80); --text_color: var(--palette_b_80);" - }, - "palette_b_90": { - "declaration": "color: var(--palette_b_90); --text_color: var(--palette_b_90);" - }, - "palette_b_95": { - "declaration": "color: var(--palette_b_95); --text_color: var(--palette_b_95);" - }, - "palette_b_100": { - "declaration": "color: var(--palette_b_100); --text_color: var(--palette_b_100);" - }, - "palette_c_00": { - "declaration": "color: var(--palette_c_00); --text_color: var(--palette_c_00);" - }, - "palette_c_05": { - "declaration": "color: var(--palette_c_05); --text_color: var(--palette_c_05);" - }, - "palette_c_10": { - "declaration": "color: var(--palette_c_10); --text_color: var(--palette_c_10);" - }, - "palette_c_20": { - "declaration": "color: var(--palette_c_20); --text_color: var(--palette_c_20);" - }, - "palette_c_30": { - "declaration": "color: var(--palette_c_30); --text_color: var(--palette_c_30);" - }, - "palette_c_40": { - "declaration": "color: var(--palette_c_40); --text_color: var(--palette_c_40);" - }, - "palette_c_50": { - "declaration": "color: var(--palette_c_50); --text_color: var(--palette_c_50);" - }, - "palette_c_60": { - "declaration": "color: var(--palette_c_60); --text_color: var(--palette_c_60);" - }, - "palette_c_70": { - "declaration": "color: var(--palette_c_70); --text_color: var(--palette_c_70);" - }, - "palette_c_80": { - "declaration": "color: var(--palette_c_80); --text_color: var(--palette_c_80);" - }, - "palette_c_90": { - "declaration": "color: var(--palette_c_90); --text_color: var(--palette_c_90);" - }, - "palette_c_95": { - "declaration": "color: var(--palette_c_95); --text_color: var(--palette_c_95);" - }, - "palette_c_100": { - "declaration": "color: var(--palette_c_100); --text_color: var(--palette_c_100);" - }, - "palette_d_00": { - "declaration": "color: var(--palette_d_00); --text_color: var(--palette_d_00);" - }, - "palette_d_05": { - "declaration": "color: var(--palette_d_05); --text_color: var(--palette_d_05);" - }, - "palette_d_10": { - "declaration": "color: var(--palette_d_10); --text_color: var(--palette_d_10);" - }, - "palette_d_20": { - "declaration": "color: var(--palette_d_20); --text_color: var(--palette_d_20);" - }, - "palette_d_30": { - "declaration": "color: var(--palette_d_30); --text_color: var(--palette_d_30);" - }, - "palette_d_40": { - "declaration": "color: var(--palette_d_40); --text_color: var(--palette_d_40);" - }, - "palette_d_50": { - "declaration": "color: var(--palette_d_50); --text_color: var(--palette_d_50);" - }, - "palette_d_60": { - "declaration": "color: var(--palette_d_60); --text_color: var(--palette_d_60);" - }, - "palette_d_70": { - "declaration": "color: var(--palette_d_70); --text_color: var(--palette_d_70);" - }, - "palette_d_80": { - "declaration": "color: var(--palette_d_80); --text_color: var(--palette_d_80);" - }, - "palette_d_90": { - "declaration": "color: var(--palette_d_90); --text_color: var(--palette_d_90);" - }, - "palette_d_95": { - "declaration": "color: var(--palette_d_95); --text_color: var(--palette_d_95);" - }, - "palette_d_100": { - "declaration": "color: var(--palette_d_100); --text_color: var(--palette_d_100);" - }, - "palette_e_00": { - "declaration": "color: var(--palette_e_00); --text_color: var(--palette_e_00);" - }, - "palette_e_05": { - "declaration": "color: var(--palette_e_05); --text_color: var(--palette_e_05);" - }, - "palette_e_10": { - "declaration": "color: var(--palette_e_10); --text_color: var(--palette_e_10);" - }, - "palette_e_20": { - "declaration": "color: var(--palette_e_20); --text_color: var(--palette_e_20);" - }, - "palette_e_30": { - "declaration": "color: var(--palette_e_30); --text_color: var(--palette_e_30);" - }, - "palette_e_40": { - "declaration": "color: var(--palette_e_40); --text_color: var(--palette_e_40);" - }, - "palette_e_50": { - "declaration": "color: var(--palette_e_50); --text_color: var(--palette_e_50);" - }, - "palette_e_60": { - "declaration": "color: var(--palette_e_60); --text_color: var(--palette_e_60);" - }, - "palette_e_70": { - "declaration": "color: var(--palette_e_70); --text_color: var(--palette_e_70);" - }, - "palette_e_80": { - "declaration": "color: var(--palette_e_80); --text_color: var(--palette_e_80);" - }, - "palette_e_90": { - "declaration": "color: var(--palette_e_90); --text_color: var(--palette_e_90);" - }, - "palette_e_95": { - "declaration": "color: var(--palette_e_95); --text_color: var(--palette_e_95);" - }, - "palette_e_100": { - "declaration": "color: var(--palette_e_100); --text_color: var(--palette_e_100);" - }, - "palette_f_00": { - "declaration": "color: var(--palette_f_00); --text_color: var(--palette_f_00);" - }, - "palette_f_05": { - "declaration": "color: var(--palette_f_05); --text_color: var(--palette_f_05);" - }, - "palette_f_10": { - "declaration": "color: var(--palette_f_10); --text_color: var(--palette_f_10);" - }, - "palette_f_20": { - "declaration": "color: var(--palette_f_20); --text_color: var(--palette_f_20);" - }, - "palette_f_30": { - "declaration": "color: var(--palette_f_30); --text_color: var(--palette_f_30);" - }, - "palette_f_40": { - "declaration": "color: var(--palette_f_40); --text_color: var(--palette_f_40);" - }, - "palette_f_50": { - "declaration": "color: var(--palette_f_50); --text_color: var(--palette_f_50);" - }, - "palette_f_60": { - "declaration": "color: var(--palette_f_60); --text_color: var(--palette_f_60);" - }, - "palette_f_70": { - "declaration": "color: var(--palette_f_70); --text_color: var(--palette_f_70);" - }, - "palette_f_80": { - "declaration": "color: var(--palette_f_80); --text_color: var(--palette_f_80);" - }, - "palette_f_90": { - "declaration": "color: var(--palette_f_90); --text_color: var(--palette_f_90);" - }, - "palette_f_95": { - "declaration": "color: var(--palette_f_95); --text_color: var(--palette_f_95);" - }, - "palette_f_100": { - "declaration": "color: var(--palette_f_100); --text_color: var(--palette_f_100);" - }, - "palette_g_00": { - "declaration": "color: var(--palette_g_00); --text_color: var(--palette_g_00);" - }, - "palette_g_05": { - "declaration": "color: var(--palette_g_05); --text_color: var(--palette_g_05);" - }, - "palette_g_10": { - "declaration": "color: var(--palette_g_10); --text_color: var(--palette_g_10);" - }, - "palette_g_20": { - "declaration": "color: var(--palette_g_20); --text_color: var(--palette_g_20);" - }, - "palette_g_30": { - "declaration": "color: var(--palette_g_30); --text_color: var(--palette_g_30);" - }, - "palette_g_40": { - "declaration": "color: var(--palette_g_40); --text_color: var(--palette_g_40);" - }, - "palette_g_50": { - "declaration": "color: var(--palette_g_50); --text_color: var(--palette_g_50);" - }, - "palette_g_60": { - "declaration": "color: var(--palette_g_60); --text_color: var(--palette_g_60);" - }, - "palette_g_70": { - "declaration": "color: var(--palette_g_70); --text_color: var(--palette_g_70);" - }, - "palette_g_80": { - "declaration": "color: var(--palette_g_80); --text_color: var(--palette_g_80);" - }, - "palette_g_90": { - "declaration": "color: var(--palette_g_90); --text_color: var(--palette_g_90);" - }, - "palette_g_95": { - "declaration": "color: var(--palette_g_95); --text_color: var(--palette_g_95);" - }, - "palette_g_100": { - "declaration": "color: var(--palette_g_100); --text_color: var(--palette_g_100);" - }, - "palette_h_00": { - "declaration": "color: var(--palette_h_00); --text_color: var(--palette_h_00);" - }, - "palette_h_05": { - "declaration": "color: var(--palette_h_05); --text_color: var(--palette_h_05);" - }, - "palette_h_10": { - "declaration": "color: var(--palette_h_10); --text_color: var(--palette_h_10);" - }, - "palette_h_20": { - "declaration": "color: var(--palette_h_20); --text_color: var(--palette_h_20);" - }, - "palette_h_30": { - "declaration": "color: var(--palette_h_30); --text_color: var(--palette_h_30);" - }, - "palette_h_40": { - "declaration": "color: var(--palette_h_40); --text_color: var(--palette_h_40);" - }, - "palette_h_50": { - "declaration": "color: var(--palette_h_50); --text_color: var(--palette_h_50);" - }, - "palette_h_60": { - "declaration": "color: var(--palette_h_60); --text_color: var(--palette_h_60);" - }, - "palette_h_70": { - "declaration": "color: var(--palette_h_70); --text_color: var(--palette_h_70);" - }, - "palette_h_80": { - "declaration": "color: var(--palette_h_80); --text_color: var(--palette_h_80);" - }, - "palette_h_90": { - "declaration": "color: var(--palette_h_90); --text_color: var(--palette_h_90);" - }, - "palette_h_95": { - "declaration": "color: var(--palette_h_95); --text_color: var(--palette_h_95);" - }, - "palette_h_100": { - "declaration": "color: var(--palette_h_100); --text_color: var(--palette_h_100);" - }, - "palette_i_00": { - "declaration": "color: var(--palette_i_00); --text_color: var(--palette_i_00);" - }, - "palette_i_05": { - "declaration": "color: var(--palette_i_05); --text_color: var(--palette_i_05);" - }, - "palette_i_10": { - "declaration": "color: var(--palette_i_10); --text_color: var(--palette_i_10);" - }, - "palette_i_20": { - "declaration": "color: var(--palette_i_20); --text_color: var(--palette_i_20);" - }, - "palette_i_30": { - "declaration": "color: var(--palette_i_30); --text_color: var(--palette_i_30);" - }, - "palette_i_40": { - "declaration": "color: var(--palette_i_40); --text_color: var(--palette_i_40);" - }, - "palette_i_50": { - "declaration": "color: var(--palette_i_50); --text_color: var(--palette_i_50);" - }, - "palette_i_60": { - "declaration": "color: var(--palette_i_60); --text_color: var(--palette_i_60);" - }, - "palette_i_70": { - "declaration": "color: var(--palette_i_70); --text_color: var(--palette_i_70);" - }, - "palette_i_80": { - "declaration": "color: var(--palette_i_80); --text_color: var(--palette_i_80);" - }, - "palette_i_90": { - "declaration": "color: var(--palette_i_90); --text_color: var(--palette_i_90);" - }, - "palette_i_95": { - "declaration": "color: var(--palette_i_95); --text_color: var(--palette_i_95);" - }, - "palette_i_100": { - "declaration": "color: var(--palette_i_100); --text_color: var(--palette_i_100);" - }, - "palette_j_00": { - "declaration": "color: var(--palette_j_00); --text_color: var(--palette_j_00);" - }, - "palette_j_05": { - "declaration": "color: var(--palette_j_05); --text_color: var(--palette_j_05);" - }, - "palette_j_10": { - "declaration": "color: var(--palette_j_10); --text_color: var(--palette_j_10);" - }, - "palette_j_20": { - "declaration": "color: var(--palette_j_20); --text_color: var(--palette_j_20);" - }, - "palette_j_30": { - "declaration": "color: var(--palette_j_30); --text_color: var(--palette_j_30);" - }, - "palette_j_40": { - "declaration": "color: var(--palette_j_40); --text_color: var(--palette_j_40);" - }, - "palette_j_50": { - "declaration": "color: var(--palette_j_50); --text_color: var(--palette_j_50);" - }, - "palette_j_60": { - "declaration": "color: var(--palette_j_60); --text_color: var(--palette_j_60);" - }, - "palette_j_70": { - "declaration": "color: var(--palette_j_70); --text_color: var(--palette_j_70);" - }, - "palette_j_80": { - "declaration": "color: var(--palette_j_80); --text_color: var(--palette_j_80);" - }, - "palette_j_90": { - "declaration": "color: var(--palette_j_90); --text_color: var(--palette_j_90);" - }, - "palette_j_95": { - "declaration": "color: var(--palette_j_95); --text_color: var(--palette_j_95);" - }, - "palette_j_100": { - "declaration": "color: var(--palette_j_100); --text_color: var(--palette_j_100);" - }, - "bg_a_00": { "declaration": "background-color: var(--palette_a_00);" }, - "bg_a_05": { "declaration": "background-color: var(--palette_a_05);" }, - "bg_a_10": { "declaration": "background-color: var(--palette_a_10);" }, - "bg_a_20": { "declaration": "background-color: var(--palette_a_20);" }, - "bg_a_30": { "declaration": "background-color: var(--palette_a_30);" }, - "bg_a_40": { "declaration": "background-color: var(--palette_a_40);" }, - "bg_a_50": { "declaration": "background-color: var(--palette_a_50);" }, - "bg_a_60": { "declaration": "background-color: var(--palette_a_60);" }, - "bg_a_70": { "declaration": "background-color: var(--palette_a_70);" }, - "bg_a_80": { "declaration": "background-color: var(--palette_a_80);" }, - "bg_a_90": { "declaration": "background-color: var(--palette_a_90);" }, - "bg_a_95": { "declaration": "background-color: var(--palette_a_95);" }, - "bg_a_100": { "declaration": "background-color: var(--palette_a_100);" }, - "bg_b_00": { "declaration": "background-color: var(--palette_b_00);" }, - "bg_b_05": { "declaration": "background-color: var(--palette_b_05);" }, - "bg_b_10": { "declaration": "background-color: var(--palette_b_10);" }, - "bg_b_20": { "declaration": "background-color: var(--palette_b_20);" }, - "bg_b_30": { "declaration": "background-color: var(--palette_b_30);" }, - "bg_b_40": { "declaration": "background-color: var(--palette_b_40);" }, - "bg_b_50": { "declaration": "background-color: var(--palette_b_50);" }, - "bg_b_60": { "declaration": "background-color: var(--palette_b_60);" }, - "bg_b_70": { "declaration": "background-color: var(--palette_b_70);" }, - "bg_b_80": { "declaration": "background-color: var(--palette_b_80);" }, - "bg_b_90": { "declaration": "background-color: var(--palette_b_90);" }, - "bg_b_95": { "declaration": "background-color: var(--palette_b_95);" }, - "bg_b_100": { "declaration": "background-color: var(--palette_b_100);" }, - "bg_c_00": { "declaration": "background-color: var(--palette_c_00);" }, - "bg_c_05": { "declaration": "background-color: var(--palette_c_05);" }, - "bg_c_10": { "declaration": "background-color: var(--palette_c_10);" }, - "bg_c_20": { "declaration": "background-color: var(--palette_c_20);" }, - "bg_c_30": { "declaration": "background-color: var(--palette_c_30);" }, - "bg_c_40": { "declaration": "background-color: var(--palette_c_40);" }, - "bg_c_50": { "declaration": "background-color: var(--palette_c_50);" }, - "bg_c_60": { "declaration": "background-color: var(--palette_c_60);" }, - "bg_c_70": { "declaration": "background-color: var(--palette_c_70);" }, - "bg_c_80": { "declaration": "background-color: var(--palette_c_80);" }, - "bg_c_90": { "declaration": "background-color: var(--palette_c_90);" }, - "bg_c_95": { "declaration": "background-color: var(--palette_c_95);" }, - "bg_c_100": { "declaration": "background-color: var(--palette_c_100);" }, - "bg_d_00": { "declaration": "background-color: var(--palette_d_00);" }, - "bg_d_05": { "declaration": "background-color: var(--palette_d_05);" }, - "bg_d_10": { "declaration": "background-color: var(--palette_d_10);" }, - "bg_d_20": { "declaration": "background-color: var(--palette_d_20);" }, - "bg_d_30": { "declaration": "background-color: var(--palette_d_30);" }, - "bg_d_40": { "declaration": "background-color: var(--palette_d_40);" }, - "bg_d_50": { "declaration": "background-color: var(--palette_d_50);" }, - "bg_d_60": { "declaration": "background-color: var(--palette_d_60);" }, - "bg_d_70": { "declaration": "background-color: var(--palette_d_70);" }, - "bg_d_80": { "declaration": "background-color: var(--palette_d_80);" }, - "bg_d_90": { "declaration": "background-color: var(--palette_d_90);" }, - "bg_d_95": { "declaration": "background-color: var(--palette_d_95);" }, - "bg_d_100": { "declaration": "background-color: var(--palette_d_100);" }, - "bg_e_00": { "declaration": "background-color: var(--palette_e_00);" }, - "bg_e_05": { "declaration": "background-color: var(--palette_e_05);" }, - "bg_e_10": { "declaration": "background-color: var(--palette_e_10);" }, - "bg_e_20": { "declaration": "background-color: var(--palette_e_20);" }, - "bg_e_30": { "declaration": "background-color: var(--palette_e_30);" }, - "bg_e_40": { "declaration": "background-color: var(--palette_e_40);" }, - "bg_e_50": { "declaration": "background-color: var(--palette_e_50);" }, - "bg_e_60": { "declaration": "background-color: var(--palette_e_60);" }, - "bg_e_70": { "declaration": "background-color: var(--palette_e_70);" }, - "bg_e_80": { "declaration": "background-color: var(--palette_e_80);" }, - "bg_e_90": { "declaration": "background-color: var(--palette_e_90);" }, - "bg_e_95": { "declaration": "background-color: var(--palette_e_95);" }, - "bg_e_100": { "declaration": "background-color: var(--palette_e_100);" }, - "bg_f_00": { "declaration": "background-color: var(--palette_f_00);" }, - "bg_f_05": { "declaration": "background-color: var(--palette_f_05);" }, - "bg_f_10": { "declaration": "background-color: var(--palette_f_10);" }, - "bg_f_20": { "declaration": "background-color: var(--palette_f_20);" }, - "bg_f_30": { "declaration": "background-color: var(--palette_f_30);" }, - "bg_f_40": { "declaration": "background-color: var(--palette_f_40);" }, - "bg_f_50": { "declaration": "background-color: var(--palette_f_50);" }, - "bg_f_60": { "declaration": "background-color: var(--palette_f_60);" }, - "bg_f_70": { "declaration": "background-color: var(--palette_f_70);" }, - "bg_f_80": { "declaration": "background-color: var(--palette_f_80);" }, - "bg_f_90": { "declaration": "background-color: var(--palette_f_90);" }, - "bg_f_95": { "declaration": "background-color: var(--palette_f_95);" }, - "bg_f_100": { "declaration": "background-color: var(--palette_f_100);" }, - "bg_g_00": { "declaration": "background-color: var(--palette_g_00);" }, - "bg_g_05": { "declaration": "background-color: var(--palette_g_05);" }, - "bg_g_10": { "declaration": "background-color: var(--palette_g_10);" }, - "bg_g_20": { "declaration": "background-color: var(--palette_g_20);" }, - "bg_g_30": { "declaration": "background-color: var(--palette_g_30);" }, - "bg_g_40": { "declaration": "background-color: var(--palette_g_40);" }, - "bg_g_50": { "declaration": "background-color: var(--palette_g_50);" }, - "bg_g_60": { "declaration": "background-color: var(--palette_g_60);" }, - "bg_g_70": { "declaration": "background-color: var(--palette_g_70);" }, - "bg_g_80": { "declaration": "background-color: var(--palette_g_80);" }, - "bg_g_90": { "declaration": "background-color: var(--palette_g_90);" }, - "bg_g_95": { "declaration": "background-color: var(--palette_g_95);" }, - "bg_g_100": { "declaration": "background-color: var(--palette_g_100);" }, - "bg_h_00": { "declaration": "background-color: var(--palette_h_00);" }, - "bg_h_05": { "declaration": "background-color: var(--palette_h_05);" }, - "bg_h_10": { "declaration": "background-color: var(--palette_h_10);" }, - "bg_h_20": { "declaration": "background-color: var(--palette_h_20);" }, - "bg_h_30": { "declaration": "background-color: var(--palette_h_30);" }, - "bg_h_40": { "declaration": "background-color: var(--palette_h_40);" }, - "bg_h_50": { "declaration": "background-color: var(--palette_h_50);" }, - "bg_h_60": { "declaration": "background-color: var(--palette_h_60);" }, - "bg_h_70": { "declaration": "background-color: var(--palette_h_70);" }, - "bg_h_80": { "declaration": "background-color: var(--palette_h_80);" }, - "bg_h_90": { "declaration": "background-color: var(--palette_h_90);" }, - "bg_h_95": { "declaration": "background-color: var(--palette_h_95);" }, - "bg_h_100": { "declaration": "background-color: var(--palette_h_100);" }, - "bg_i_00": { "declaration": "background-color: var(--palette_i_00);" }, - "bg_i_05": { "declaration": "background-color: var(--palette_i_05);" }, - "bg_i_10": { "declaration": "background-color: var(--palette_i_10);" }, - "bg_i_20": { "declaration": "background-color: var(--palette_i_20);" }, - "bg_i_30": { "declaration": "background-color: var(--palette_i_30);" }, - "bg_i_40": { "declaration": "background-color: var(--palette_i_40);" }, - "bg_i_50": { "declaration": "background-color: var(--palette_i_50);" }, - "bg_i_60": { "declaration": "background-color: var(--palette_i_60);" }, - "bg_i_70": { "declaration": "background-color: var(--palette_i_70);" }, - "bg_i_80": { "declaration": "background-color: var(--palette_i_80);" }, - "bg_i_90": { "declaration": "background-color: var(--palette_i_90);" }, - "bg_i_95": { "declaration": "background-color: var(--palette_i_95);" }, - "bg_i_100": { "declaration": "background-color: var(--palette_i_100);" }, - "bg_j_00": { "declaration": "background-color: var(--palette_j_00);" }, - "bg_j_05": { "declaration": "background-color: var(--palette_j_05);" }, - "bg_j_10": { "declaration": "background-color: var(--palette_j_10);" }, - "bg_j_20": { "declaration": "background-color: var(--palette_j_20);" }, - "bg_j_30": { "declaration": "background-color: var(--palette_j_30);" }, - "bg_j_40": { "declaration": "background-color: var(--palette_j_40);" }, - "bg_j_50": { "declaration": "background-color: var(--palette_j_50);" }, - "bg_j_60": { "declaration": "background-color: var(--palette_j_60);" }, - "bg_j_70": { "declaration": "background-color: var(--palette_j_70);" }, - "bg_j_80": { "declaration": "background-color: var(--palette_j_80);" }, - "bg_j_90": { "declaration": "background-color: var(--palette_j_90);" }, - "bg_j_95": { "declaration": "background-color: var(--palette_j_95);" }, - "bg_j_100": { "declaration": "background-color: var(--palette_j_100);" }, - "accent_00": { "declaration": "color: var(--accent_00); --text_color: var(--accent_00);" }, - "accent_05": { "declaration": "color: var(--accent_05); --text_color: var(--accent_05);" }, - "accent_10": { "declaration": "color: var(--accent_10); --text_color: var(--accent_10);" }, - "accent_20": { "declaration": "color: var(--accent_20); --text_color: var(--accent_20);" }, - "accent_30": { "declaration": "color: var(--accent_30); --text_color: var(--accent_30);" }, - "accent_40": { "declaration": "color: var(--accent_40); --text_color: var(--accent_40);" }, - "accent_50": { "declaration": "color: var(--accent_50); --text_color: var(--accent_50);" }, - "accent_60": { "declaration": "color: var(--accent_60); --text_color: var(--accent_60);" }, - "accent_70": { "declaration": "color: var(--accent_70); --text_color: var(--accent_70);" }, - "accent_80": { "declaration": "color: var(--accent_80); --text_color: var(--accent_80);" }, - "accent_90": { "declaration": "color: var(--accent_90); --text_color: var(--accent_90);" }, - "accent_95": { "declaration": "color: var(--accent_95); --text_color: var(--accent_95);" }, - "accent_100": { "declaration": "color: var(--accent_100); --text_color: var(--accent_100);" }, - "positive_00": { "declaration": "color: var(--positive_00); --text_color: var(--positive_00);" }, - "positive_05": { "declaration": "color: var(--positive_05); --text_color: var(--positive_05);" }, - "positive_10": { "declaration": "color: var(--positive_10); --text_color: var(--positive_10);" }, - "positive_20": { "declaration": "color: var(--positive_20); --text_color: var(--positive_20);" }, - "positive_30": { "declaration": "color: var(--positive_30); --text_color: var(--positive_30);" }, - "positive_40": { "declaration": "color: var(--positive_40); --text_color: var(--positive_40);" }, - "positive_50": { "declaration": "color: var(--positive_50); --text_color: var(--positive_50);" }, - "positive_60": { "declaration": "color: var(--positive_60); --text_color: var(--positive_60);" }, - "positive_70": { "declaration": "color: var(--positive_70); --text_color: var(--positive_70);" }, - "positive_80": { "declaration": "color: var(--positive_80); --text_color: var(--positive_80);" }, - "positive_90": { "declaration": "color: var(--positive_90); --text_color: var(--positive_90);" }, - "positive_95": { "declaration": "color: var(--positive_95); --text_color: var(--positive_95);" }, - "positive_100": { - "declaration": "color: var(--positive_100); --text_color: var(--positive_100);" - }, - "negative_00": { "declaration": "color: var(--negative_00); --text_color: var(--negative_00);" }, - "negative_05": { "declaration": "color: var(--negative_05); --text_color: var(--negative_05);" }, - "negative_10": { "declaration": "color: var(--negative_10); --text_color: var(--negative_10);" }, - "negative_20": { "declaration": "color: var(--negative_20); --text_color: var(--negative_20);" }, - "negative_30": { "declaration": "color: var(--negative_30); --text_color: var(--negative_30);" }, - "negative_40": { "declaration": "color: var(--negative_40); --text_color: var(--negative_40);" }, - "negative_50": { "declaration": "color: var(--negative_50); --text_color: var(--negative_50);" }, - "negative_60": { "declaration": "color: var(--negative_60); --text_color: var(--negative_60);" }, - "negative_70": { "declaration": "color: var(--negative_70); --text_color: var(--negative_70);" }, - "negative_80": { "declaration": "color: var(--negative_80); --text_color: var(--negative_80);" }, - "negative_90": { "declaration": "color: var(--negative_90); --text_color: var(--negative_90);" }, - "negative_95": { "declaration": "color: var(--negative_95); --text_color: var(--negative_95);" }, - "negative_100": { - "declaration": "color: var(--negative_100); --text_color: var(--negative_100);" - }, - "caution_00": { "declaration": "color: var(--caution_00); --text_color: var(--caution_00);" }, - "caution_05": { "declaration": "color: var(--caution_05); --text_color: var(--caution_05);" }, - "caution_10": { "declaration": "color: var(--caution_10); --text_color: var(--caution_10);" }, - "caution_20": { "declaration": "color: var(--caution_20); --text_color: var(--caution_20);" }, - "caution_30": { "declaration": "color: var(--caution_30); --text_color: var(--caution_30);" }, - "caution_40": { "declaration": "color: var(--caution_40); --text_color: var(--caution_40);" }, - "caution_50": { "declaration": "color: var(--caution_50); --text_color: var(--caution_50);" }, - "caution_60": { "declaration": "color: var(--caution_60); --text_color: var(--caution_60);" }, - "caution_70": { "declaration": "color: var(--caution_70); --text_color: var(--caution_70);" }, - "caution_80": { "declaration": "color: var(--caution_80); --text_color: var(--caution_80);" }, - "caution_90": { "declaration": "color: var(--caution_90); --text_color: var(--caution_90);" }, - "caution_95": { "declaration": "color: var(--caution_95); --text_color: var(--caution_95);" }, - "caution_100": { "declaration": "color: var(--caution_100); --text_color: var(--caution_100);" }, - "info_00": { "declaration": "color: var(--info_00); --text_color: var(--info_00);" }, - "info_05": { "declaration": "color: var(--info_05); --text_color: var(--info_05);" }, - "info_10": { "declaration": "color: var(--info_10); --text_color: var(--info_10);" }, - "info_20": { "declaration": "color: var(--info_20); --text_color: var(--info_20);" }, - "info_30": { "declaration": "color: var(--info_30); --text_color: var(--info_30);" }, - "info_40": { "declaration": "color: var(--info_40); --text_color: var(--info_40);" }, - "info_50": { "declaration": "color: var(--info_50); --text_color: var(--info_50);" }, - "info_60": { "declaration": "color: var(--info_60); --text_color: var(--info_60);" }, - "info_70": { "declaration": "color: var(--info_70); --text_color: var(--info_70);" }, - "info_80": { "declaration": "color: var(--info_80); --text_color: var(--info_80);" }, - "info_90": { "declaration": "color: var(--info_90); --text_color: var(--info_90);" }, - "info_95": { "declaration": "color: var(--info_95); --text_color: var(--info_95);" }, - "info_100": { "declaration": "color: var(--info_100); --text_color: var(--info_100);" }, - "bg_accent_00": { "declaration": "background-color: var(--accent_00);" }, - "bg_accent_05": { "declaration": "background-color: var(--accent_05);" }, - "bg_accent_10": { "declaration": "background-color: var(--accent_10);" }, - "bg_accent_20": { "declaration": "background-color: var(--accent_20);" }, - "bg_accent_30": { "declaration": "background-color: var(--accent_30);" }, - "bg_accent_40": { "declaration": "background-color: var(--accent_40);" }, - "bg_accent_50": { "declaration": "background-color: var(--accent_50);" }, - "bg_accent_60": { "declaration": "background-color: var(--accent_60);" }, - "bg_accent_70": { "declaration": "background-color: var(--accent_70);" }, - "bg_accent_80": { "declaration": "background-color: var(--accent_80);" }, - "bg_accent_90": { "declaration": "background-color: var(--accent_90);" }, - "bg_accent_95": { "declaration": "background-color: var(--accent_95);" }, - "bg_accent_100": { "declaration": "background-color: var(--accent_100);" }, - "bg_positive_00": { "declaration": "background-color: var(--positive_00);" }, - "bg_positive_05": { "declaration": "background-color: var(--positive_05);" }, - "bg_positive_10": { "declaration": "background-color: var(--positive_10);" }, - "bg_positive_20": { "declaration": "background-color: var(--positive_20);" }, - "bg_positive_30": { "declaration": "background-color: var(--positive_30);" }, - "bg_positive_40": { "declaration": "background-color: var(--positive_40);" }, - "bg_positive_50": { "declaration": "background-color: var(--positive_50);" }, - "bg_positive_60": { "declaration": "background-color: var(--positive_60);" }, - "bg_positive_70": { "declaration": "background-color: var(--positive_70);" }, - "bg_positive_80": { "declaration": "background-color: var(--positive_80);" }, - "bg_positive_90": { "declaration": "background-color: var(--positive_90);" }, - "bg_positive_95": { "declaration": "background-color: var(--positive_95);" }, - "bg_positive_100": { "declaration": "background-color: var(--positive_100);" }, - "bg_negative_00": { "declaration": "background-color: var(--negative_00);" }, - "bg_negative_05": { "declaration": "background-color: var(--negative_05);" }, - "bg_negative_10": { "declaration": "background-color: var(--negative_10);" }, - "bg_negative_20": { "declaration": "background-color: var(--negative_20);" }, - "bg_negative_30": { "declaration": "background-color: var(--negative_30);" }, - "bg_negative_40": { "declaration": "background-color: var(--negative_40);" }, - "bg_negative_50": { "declaration": "background-color: var(--negative_50);" }, - "bg_negative_60": { "declaration": "background-color: var(--negative_60);" }, - "bg_negative_70": { "declaration": "background-color: var(--negative_70);" }, - "bg_negative_80": { "declaration": "background-color: var(--negative_80);" }, - "bg_negative_90": { "declaration": "background-color: var(--negative_90);" }, - "bg_negative_95": { "declaration": "background-color: var(--negative_95);" }, - "bg_negative_100": { "declaration": "background-color: var(--negative_100);" }, - "bg_caution_00": { "declaration": "background-color: var(--caution_00);" }, - "bg_caution_05": { "declaration": "background-color: var(--caution_05);" }, - "bg_caution_10": { "declaration": "background-color: var(--caution_10);" }, - "bg_caution_20": { "declaration": "background-color: var(--caution_20);" }, - "bg_caution_30": { "declaration": "background-color: var(--caution_30);" }, - "bg_caution_40": { "declaration": "background-color: var(--caution_40);" }, - "bg_caution_50": { "declaration": "background-color: var(--caution_50);" }, - "bg_caution_60": { "declaration": "background-color: var(--caution_60);" }, - "bg_caution_70": { "declaration": "background-color: var(--caution_70);" }, - "bg_caution_80": { "declaration": "background-color: var(--caution_80);" }, - "bg_caution_90": { "declaration": "background-color: var(--caution_90);" }, - "bg_caution_95": { "declaration": "background-color: var(--caution_95);" }, - "bg_caution_100": { "declaration": "background-color: var(--caution_100);" }, - "bg_info_00": { "declaration": "background-color: var(--info_00);" }, - "bg_info_05": { "declaration": "background-color: var(--info_05);" }, - "bg_info_10": { "declaration": "background-color: var(--info_10);" }, - "bg_info_20": { "declaration": "background-color: var(--info_20);" }, - "bg_info_30": { "declaration": "background-color: var(--info_30);" }, - "bg_info_40": { "declaration": "background-color: var(--info_40);" }, - "bg_info_50": { "declaration": "background-color: var(--info_50);" }, - "bg_info_60": { "declaration": "background-color: var(--info_60);" }, - "bg_info_70": { "declaration": "background-color: var(--info_70);" }, - "bg_info_80": { "declaration": "background-color: var(--info_80);" }, - "bg_info_90": { "declaration": "background-color: var(--info_90);" }, - "bg_info_95": { "declaration": "background-color: var(--info_95);" }, - "bg_info_100": { "declaration": "background-color: var(--info_100);" }, - "darken_00": { "declaration": "background-color: var(--darken_00);" }, - "darken_05": { "declaration": "background-color: var(--darken_05);" }, - "darken_10": { "declaration": "background-color: var(--darken_10);" }, - "darken_20": { "declaration": "background-color: var(--darken_20);" }, - "darken_30": { "declaration": "background-color: var(--darken_30);" }, - "darken_40": { "declaration": "background-color: var(--darken_40);" }, - "darken_50": { "declaration": "background-color: var(--darken_50);" }, - "darken_60": { "declaration": "background-color: var(--darken_60);" }, - "darken_70": { "declaration": "background-color: var(--darken_70);" }, - "darken_80": { "declaration": "background-color: var(--darken_80);" }, - "darken_90": { "declaration": "background-color: var(--darken_90);" }, - "darken_95": { "declaration": "background-color: var(--darken_95);" }, - "darken_100": { "declaration": "background-color: var(--darken_100);" }, - "lighten_00": { "declaration": "background-color: var(--lighten_00);" }, - "lighten_05": { "declaration": "background-color: var(--lighten_05);" }, - "lighten_10": { "declaration": "background-color: var(--lighten_10);" }, - "lighten_20": { "declaration": "background-color: var(--lighten_20);" }, - "lighten_30": { "declaration": "background-color: var(--lighten_30);" }, - "lighten_40": { "declaration": "background-color: var(--lighten_40);" }, - "lighten_50": { "declaration": "background-color: var(--lighten_50);" }, - "lighten_60": { "declaration": "background-color: var(--lighten_60);" }, - "lighten_70": { "declaration": "background-color: var(--lighten_70);" }, - "lighten_80": { "declaration": "background-color: var(--lighten_80);" }, - "lighten_90": { "declaration": "background-color: var(--lighten_90);" }, - "lighten_95": { "declaration": "background-color: var(--lighten_95);" }, - "lighten_100": { "declaration": "background-color: var(--lighten_100);" }, - "fg_00": { "declaration": "background-color: var(--fg_00);" }, - "fg_05": { "declaration": "background-color: var(--fg_05);" }, - "fg_10": { "declaration": "background-color: var(--fg_10);" }, - "fg_20": { "declaration": "background-color: var(--fg_20);" }, - "fg_30": { "declaration": "background-color: var(--fg_30);" }, - "fg_40": { "declaration": "background-color: var(--fg_40);" }, - "fg_50": { "declaration": "background-color: var(--fg_50);" }, - "fg_60": { "declaration": "background-color: var(--fg_60);" }, - "fg_70": { "declaration": "background-color: var(--fg_70);" }, - "fg_80": { "declaration": "background-color: var(--fg_80);" }, - "fg_90": { "declaration": "background-color: var(--fg_90);" }, - "fg_95": { "declaration": "background-color: var(--fg_95);" }, - "fg_100": { "declaration": "background-color: var(--fg_100);" }, - "bg_00": { "declaration": "background-color: var(--bg_00);" }, - "bg_05": { "declaration": "background-color: var(--bg_05);" }, - "bg_10": { "declaration": "background-color: var(--bg_10);" }, - "bg_20": { "declaration": "background-color: var(--bg_20);" }, - "bg_30": { "declaration": "background-color: var(--bg_30);" }, - "bg_40": { "declaration": "background-color: var(--bg_40);" }, - "bg_50": { "declaration": "background-color: var(--bg_50);" }, - "bg_60": { "declaration": "background-color: var(--bg_60);" }, - "bg_70": { "declaration": "background-color: var(--bg_70);" }, - "bg_80": { "declaration": "background-color: var(--bg_80);" }, - "bg_90": { "declaration": "background-color: var(--bg_90);" }, - "bg_95": { "declaration": "background-color: var(--bg_95);" }, - "bg_100": { "declaration": "background-color: var(--bg_100);" }, - "border_color_00": { - "declaration": "border-color: var(--border_color_00); --border_color: var(--border_color_00);" - }, - "border_color_05": { - "declaration": "border-color: var(--border_color_05); --border_color: var(--border_color_05);" - }, - "border_color_10": { - "declaration": "border-color: var(--border_color_10); --border_color: var(--border_color_10);" - }, - "border_color_20": { - "declaration": "border-color: var(--border_color_20); --border_color: var(--border_color_20);" - }, - "border_color_30": { - "declaration": "border-color: var(--border_color_30); --border_color: var(--border_color_30);" - }, - "border_color_40": { - "declaration": "border-color: var(--border_color_40); --border_color: var(--border_color_40);" - }, - "border_color_50": { - "declaration": "border-color: var(--border_color_50); --border_color: var(--border_color_50);" - }, - "border_color_60": { - "declaration": "border-color: var(--border_color_60); --border_color: var(--border_color_60);" - }, - "border_color_70": { - "declaration": "border-color: var(--border_color_70); --border_color: var(--border_color_70);" - }, - "border_color_80": { - "declaration": "border-color: var(--border_color_80); --border_color: var(--border_color_80);" - }, - "border_color_90": { - "declaration": "border-color: var(--border_color_90); --border_color: var(--border_color_90);" - }, - "border_color_95": { - "declaration": "border-color: var(--border_color_95); --border_color: var(--border_color_95);" - }, - "border_color_100": { - "declaration": "border-color: var(--border_color_100); --border_color: var(--border_color_100);" - }, - "border_a_00": { - "declaration": "border-color: var(--palette_a_00); --border_color: var(--palette_a_00);" - }, - "border_a_05": { - "declaration": "border-color: var(--palette_a_05); --border_color: var(--palette_a_05);" - }, - "border_a_10": { - "declaration": "border-color: var(--palette_a_10); --border_color: var(--palette_a_10);" - }, - "border_a_20": { - "declaration": "border-color: var(--palette_a_20); --border_color: var(--palette_a_20);" - }, - "border_a_30": { - "declaration": "border-color: var(--palette_a_30); --border_color: var(--palette_a_30);" - }, - "border_a_40": { - "declaration": "border-color: var(--palette_a_40); --border_color: var(--palette_a_40);" - }, - "border_a_50": { - "declaration": "border-color: var(--palette_a_50); --border_color: var(--palette_a_50);" - }, - "border_a_60": { - "declaration": "border-color: var(--palette_a_60); --border_color: var(--palette_a_60);" - }, - "border_a_70": { - "declaration": "border-color: var(--palette_a_70); --border_color: var(--palette_a_70);" - }, - "border_a_80": { - "declaration": "border-color: var(--palette_a_80); --border_color: var(--palette_a_80);" - }, - "border_a_90": { - "declaration": "border-color: var(--palette_a_90); --border_color: var(--palette_a_90);" - }, - "border_a_95": { - "declaration": "border-color: var(--palette_a_95); --border_color: var(--palette_a_95);" - }, - "border_a_100": { - "declaration": "border-color: var(--palette_a_100); --border_color: var(--palette_a_100);" - }, - "border_b_00": { - "declaration": "border-color: var(--palette_b_00); --border_color: var(--palette_b_00);" - }, - "border_b_05": { - "declaration": "border-color: var(--palette_b_05); --border_color: var(--palette_b_05);" - }, - "border_b_10": { - "declaration": "border-color: var(--palette_b_10); --border_color: var(--palette_b_10);" - }, - "border_b_20": { - "declaration": "border-color: var(--palette_b_20); --border_color: var(--palette_b_20);" - }, - "border_b_30": { - "declaration": "border-color: var(--palette_b_30); --border_color: var(--palette_b_30);" - }, - "border_b_40": { - "declaration": "border-color: var(--palette_b_40); --border_color: var(--palette_b_40);" - }, - "border_b_50": { - "declaration": "border-color: var(--palette_b_50); --border_color: var(--palette_b_50);" - }, - "border_b_60": { - "declaration": "border-color: var(--palette_b_60); --border_color: var(--palette_b_60);" - }, - "border_b_70": { - "declaration": "border-color: var(--palette_b_70); --border_color: var(--palette_b_70);" - }, - "border_b_80": { - "declaration": "border-color: var(--palette_b_80); --border_color: var(--palette_b_80);" - }, - "border_b_90": { - "declaration": "border-color: var(--palette_b_90); --border_color: var(--palette_b_90);" - }, - "border_b_95": { - "declaration": "border-color: var(--palette_b_95); --border_color: var(--palette_b_95);" - }, - "border_b_100": { - "declaration": "border-color: var(--palette_b_100); --border_color: var(--palette_b_100);" - }, - "border_c_00": { - "declaration": "border-color: var(--palette_c_00); --border_color: var(--palette_c_00);" - }, - "border_c_05": { - "declaration": "border-color: var(--palette_c_05); --border_color: var(--palette_c_05);" - }, - "border_c_10": { - "declaration": "border-color: var(--palette_c_10); --border_color: var(--palette_c_10);" - }, - "border_c_20": { - "declaration": "border-color: var(--palette_c_20); --border_color: var(--palette_c_20);" - }, - "border_c_30": { - "declaration": "border-color: var(--palette_c_30); --border_color: var(--palette_c_30);" - }, - "border_c_40": { - "declaration": "border-color: var(--palette_c_40); --border_color: var(--palette_c_40);" - }, - "border_c_50": { - "declaration": "border-color: var(--palette_c_50); --border_color: var(--palette_c_50);" - }, - "border_c_60": { - "declaration": "border-color: var(--palette_c_60); --border_color: var(--palette_c_60);" - }, - "border_c_70": { - "declaration": "border-color: var(--palette_c_70); --border_color: var(--palette_c_70);" - }, - "border_c_80": { - "declaration": "border-color: var(--palette_c_80); --border_color: var(--palette_c_80);" - }, - "border_c_90": { - "declaration": "border-color: var(--palette_c_90); --border_color: var(--palette_c_90);" - }, - "border_c_95": { - "declaration": "border-color: var(--palette_c_95); --border_color: var(--palette_c_95);" - }, - "border_c_100": { - "declaration": "border-color: var(--palette_c_100); --border_color: var(--palette_c_100);" - }, - "border_d_00": { - "declaration": "border-color: var(--palette_d_00); --border_color: var(--palette_d_00);" - }, - "border_d_05": { - "declaration": "border-color: var(--palette_d_05); --border_color: var(--palette_d_05);" - }, - "border_d_10": { - "declaration": "border-color: var(--palette_d_10); --border_color: var(--palette_d_10);" - }, - "border_d_20": { - "declaration": "border-color: var(--palette_d_20); --border_color: var(--palette_d_20);" - }, - "border_d_30": { - "declaration": "border-color: var(--palette_d_30); --border_color: var(--palette_d_30);" - }, - "border_d_40": { - "declaration": "border-color: var(--palette_d_40); --border_color: var(--palette_d_40);" - }, - "border_d_50": { - "declaration": "border-color: var(--palette_d_50); --border_color: var(--palette_d_50);" - }, - "border_d_60": { - "declaration": "border-color: var(--palette_d_60); --border_color: var(--palette_d_60);" - }, - "border_d_70": { - "declaration": "border-color: var(--palette_d_70); --border_color: var(--palette_d_70);" - }, - "border_d_80": { - "declaration": "border-color: var(--palette_d_80); --border_color: var(--palette_d_80);" - }, - "border_d_90": { - "declaration": "border-color: var(--palette_d_90); --border_color: var(--palette_d_90);" - }, - "border_d_95": { - "declaration": "border-color: var(--palette_d_95); --border_color: var(--palette_d_95);" - }, - "border_d_100": { - "declaration": "border-color: var(--palette_d_100); --border_color: var(--palette_d_100);" - }, - "border_e_00": { - "declaration": "border-color: var(--palette_e_00); --border_color: var(--palette_e_00);" - }, - "border_e_05": { - "declaration": "border-color: var(--palette_e_05); --border_color: var(--palette_e_05);" - }, - "border_e_10": { - "declaration": "border-color: var(--palette_e_10); --border_color: var(--palette_e_10);" - }, - "border_e_20": { - "declaration": "border-color: var(--palette_e_20); --border_color: var(--palette_e_20);" - }, - "border_e_30": { - "declaration": "border-color: var(--palette_e_30); --border_color: var(--palette_e_30);" - }, - "border_e_40": { - "declaration": "border-color: var(--palette_e_40); --border_color: var(--palette_e_40);" - }, - "border_e_50": { - "declaration": "border-color: var(--palette_e_50); --border_color: var(--palette_e_50);" - }, - "border_e_60": { - "declaration": "border-color: var(--palette_e_60); --border_color: var(--palette_e_60);" - }, - "border_e_70": { - "declaration": "border-color: var(--palette_e_70); --border_color: var(--palette_e_70);" - }, - "border_e_80": { - "declaration": "border-color: var(--palette_e_80); --border_color: var(--palette_e_80);" - }, - "border_e_90": { - "declaration": "border-color: var(--palette_e_90); --border_color: var(--palette_e_90);" - }, - "border_e_95": { - "declaration": "border-color: var(--palette_e_95); --border_color: var(--palette_e_95);" - }, - "border_e_100": { - "declaration": "border-color: var(--palette_e_100); --border_color: var(--palette_e_100);" - }, - "border_f_00": { - "declaration": "border-color: var(--palette_f_00); --border_color: var(--palette_f_00);" - }, - "border_f_05": { - "declaration": "border-color: var(--palette_f_05); --border_color: var(--palette_f_05);" - }, - "border_f_10": { - "declaration": "border-color: var(--palette_f_10); --border_color: var(--palette_f_10);" - }, - "border_f_20": { - "declaration": "border-color: var(--palette_f_20); --border_color: var(--palette_f_20);" - }, - "border_f_30": { - "declaration": "border-color: var(--palette_f_30); --border_color: var(--palette_f_30);" - }, - "border_f_40": { - "declaration": "border-color: var(--palette_f_40); --border_color: var(--palette_f_40);" - }, - "border_f_50": { - "declaration": "border-color: var(--palette_f_50); --border_color: var(--palette_f_50);" - }, - "border_f_60": { - "declaration": "border-color: var(--palette_f_60); --border_color: var(--palette_f_60);" - }, - "border_f_70": { - "declaration": "border-color: var(--palette_f_70); --border_color: var(--palette_f_70);" - }, - "border_f_80": { - "declaration": "border-color: var(--palette_f_80); --border_color: var(--palette_f_80);" - }, - "border_f_90": { - "declaration": "border-color: var(--palette_f_90); --border_color: var(--palette_f_90);" - }, - "border_f_95": { - "declaration": "border-color: var(--palette_f_95); --border_color: var(--palette_f_95);" - }, - "border_f_100": { - "declaration": "border-color: var(--palette_f_100); --border_color: var(--palette_f_100);" - }, - "border_g_00": { - "declaration": "border-color: var(--palette_g_00); --border_color: var(--palette_g_00);" - }, - "border_g_05": { - "declaration": "border-color: var(--palette_g_05); --border_color: var(--palette_g_05);" - }, - "border_g_10": { - "declaration": "border-color: var(--palette_g_10); --border_color: var(--palette_g_10);" - }, - "border_g_20": { - "declaration": "border-color: var(--palette_g_20); --border_color: var(--palette_g_20);" - }, - "border_g_30": { - "declaration": "border-color: var(--palette_g_30); --border_color: var(--palette_g_30);" - }, - "border_g_40": { - "declaration": "border-color: var(--palette_g_40); --border_color: var(--palette_g_40);" - }, - "border_g_50": { - "declaration": "border-color: var(--palette_g_50); --border_color: var(--palette_g_50);" - }, - "border_g_60": { - "declaration": "border-color: var(--palette_g_60); --border_color: var(--palette_g_60);" - }, - "border_g_70": { - "declaration": "border-color: var(--palette_g_70); --border_color: var(--palette_g_70);" - }, - "border_g_80": { - "declaration": "border-color: var(--palette_g_80); --border_color: var(--palette_g_80);" - }, - "border_g_90": { - "declaration": "border-color: var(--palette_g_90); --border_color: var(--palette_g_90);" - }, - "border_g_95": { - "declaration": "border-color: var(--palette_g_95); --border_color: var(--palette_g_95);" - }, - "border_g_100": { - "declaration": "border-color: var(--palette_g_100); --border_color: var(--palette_g_100);" - }, - "border_h_00": { - "declaration": "border-color: var(--palette_h_00); --border_color: var(--palette_h_00);" - }, - "border_h_05": { - "declaration": "border-color: var(--palette_h_05); --border_color: var(--palette_h_05);" - }, - "border_h_10": { - "declaration": "border-color: var(--palette_h_10); --border_color: var(--palette_h_10);" - }, - "border_h_20": { - "declaration": "border-color: var(--palette_h_20); --border_color: var(--palette_h_20);" - }, - "border_h_30": { - "declaration": "border-color: var(--palette_h_30); --border_color: var(--palette_h_30);" - }, - "border_h_40": { - "declaration": "border-color: var(--palette_h_40); --border_color: var(--palette_h_40);" - }, - "border_h_50": { - "declaration": "border-color: var(--palette_h_50); --border_color: var(--palette_h_50);" - }, - "border_h_60": { - "declaration": "border-color: var(--palette_h_60); --border_color: var(--palette_h_60);" - }, - "border_h_70": { - "declaration": "border-color: var(--palette_h_70); --border_color: var(--palette_h_70);" - }, - "border_h_80": { - "declaration": "border-color: var(--palette_h_80); --border_color: var(--palette_h_80);" - }, - "border_h_90": { - "declaration": "border-color: var(--palette_h_90); --border_color: var(--palette_h_90);" - }, - "border_h_95": { - "declaration": "border-color: var(--palette_h_95); --border_color: var(--palette_h_95);" - }, - "border_h_100": { - "declaration": "border-color: var(--palette_h_100); --border_color: var(--palette_h_100);" - }, - "border_i_00": { - "declaration": "border-color: var(--palette_i_00); --border_color: var(--palette_i_00);" - }, - "border_i_05": { - "declaration": "border-color: var(--palette_i_05); --border_color: var(--palette_i_05);" - }, - "border_i_10": { - "declaration": "border-color: var(--palette_i_10); --border_color: var(--palette_i_10);" - }, - "border_i_20": { - "declaration": "border-color: var(--palette_i_20); --border_color: var(--palette_i_20);" - }, - "border_i_30": { - "declaration": "border-color: var(--palette_i_30); --border_color: var(--palette_i_30);" - }, - "border_i_40": { - "declaration": "border-color: var(--palette_i_40); --border_color: var(--palette_i_40);" - }, - "border_i_50": { - "declaration": "border-color: var(--palette_i_50); --border_color: var(--palette_i_50);" - }, - "border_i_60": { - "declaration": "border-color: var(--palette_i_60); --border_color: var(--palette_i_60);" - }, - "border_i_70": { - "declaration": "border-color: var(--palette_i_70); --border_color: var(--palette_i_70);" - }, - "border_i_80": { - "declaration": "border-color: var(--palette_i_80); --border_color: var(--palette_i_80);" - }, - "border_i_90": { - "declaration": "border-color: var(--palette_i_90); --border_color: var(--palette_i_90);" - }, - "border_i_95": { - "declaration": "border-color: var(--palette_i_95); --border_color: var(--palette_i_95);" - }, - "border_i_100": { - "declaration": "border-color: var(--palette_i_100); --border_color: var(--palette_i_100);" - }, - "border_j_00": { - "declaration": "border-color: var(--palette_j_00); --border_color: var(--palette_j_00);" - }, - "border_j_05": { - "declaration": "border-color: var(--palette_j_05); --border_color: var(--palette_j_05);" - }, - "border_j_10": { - "declaration": "border-color: var(--palette_j_10); --border_color: var(--palette_j_10);" - }, - "border_j_20": { - "declaration": "border-color: var(--palette_j_20); --border_color: var(--palette_j_20);" - }, - "border_j_30": { - "declaration": "border-color: var(--palette_j_30); --border_color: var(--palette_j_30);" - }, - "border_j_40": { - "declaration": "border-color: var(--palette_j_40); --border_color: var(--palette_j_40);" - }, - "border_j_50": { - "declaration": "border-color: var(--palette_j_50); --border_color: var(--palette_j_50);" - }, - "border_j_60": { - "declaration": "border-color: var(--palette_j_60); --border_color: var(--palette_j_60);" - }, - "border_j_70": { - "declaration": "border-color: var(--palette_j_70); --border_color: var(--palette_j_70);" - }, - "border_j_80": { - "declaration": "border-color: var(--palette_j_80); --border_color: var(--palette_j_80);" - }, - "border_j_90": { - "declaration": "border-color: var(--palette_j_90); --border_color: var(--palette_j_90);" - }, - "border_j_95": { - "declaration": "border-color: var(--palette_j_95); --border_color: var(--palette_j_95);" - }, - "border_j_100": { - "declaration": "border-color: var(--palette_j_100); --border_color: var(--palette_j_100);" - }, - "outline_color_00": { "declaration": "outline-color: var(--shade_00);" }, - "outline_color_05": { "declaration": "outline-color: var(--shade_05);" }, - "outline_color_10": { "declaration": "outline-color: var(--shade_10);" }, - "outline_color_20": { "declaration": "outline-color: var(--shade_20);" }, - "outline_color_30": { "declaration": "outline-color: var(--shade_30);" }, - "outline_color_40": { "declaration": "outline-color: var(--shade_40);" }, - "outline_color_50": { "declaration": "outline-color: var(--shade_50);" }, - "outline_color_60": { "declaration": "outline-color: var(--shade_60);" }, - "outline_color_70": { "declaration": "outline-color: var(--shade_70);" }, - "outline_color_80": { "declaration": "outline-color: var(--shade_80);" }, - "outline_color_90": { "declaration": "outline-color: var(--shade_90);" }, - "outline_color_95": { "declaration": "outline-color: var(--shade_95);" }, - "outline_color_100": { "declaration": "outline-color: var(--shade_100);" }, - "outline_a_00": { - "declaration": "outline-color: var(--palette_a_00); --outline_color: var(--palette_a_00);" - }, - "outline_a_05": { - "declaration": "outline-color: var(--palette_a_05); --outline_color: var(--palette_a_05);" - }, - "outline_a_10": { - "declaration": "outline-color: var(--palette_a_10); --outline_color: var(--palette_a_10);" - }, - "outline_a_20": { - "declaration": "outline-color: var(--palette_a_20); --outline_color: var(--palette_a_20);" - }, - "outline_a_30": { - "declaration": "outline-color: var(--palette_a_30); --outline_color: var(--palette_a_30);" - }, - "outline_a_40": { - "declaration": "outline-color: var(--palette_a_40); --outline_color: var(--palette_a_40);" - }, - "outline_a_50": { - "declaration": "outline-color: var(--palette_a_50); --outline_color: var(--palette_a_50);" - }, - "outline_a_60": { - "declaration": "outline-color: var(--palette_a_60); --outline_color: var(--palette_a_60);" - }, - "outline_a_70": { - "declaration": "outline-color: var(--palette_a_70); --outline_color: var(--palette_a_70);" - }, - "outline_a_80": { - "declaration": "outline-color: var(--palette_a_80); --outline_color: var(--palette_a_80);" - }, - "outline_a_90": { - "declaration": "outline-color: var(--palette_a_90); --outline_color: var(--palette_a_90);" - }, - "outline_a_95": { - "declaration": "outline-color: var(--palette_a_95); --outline_color: var(--palette_a_95);" - }, - "outline_a_100": { - "declaration": "outline-color: var(--palette_a_100); --outline_color: var(--palette_a_100);" - }, - "outline_b_00": { - "declaration": "outline-color: var(--palette_b_00); --outline_color: var(--palette_b_00);" - }, - "outline_b_05": { - "declaration": "outline-color: var(--palette_b_05); --outline_color: var(--palette_b_05);" - }, - "outline_b_10": { - "declaration": "outline-color: var(--palette_b_10); --outline_color: var(--palette_b_10);" - }, - "outline_b_20": { - "declaration": "outline-color: var(--palette_b_20); --outline_color: var(--palette_b_20);" - }, - "outline_b_30": { - "declaration": "outline-color: var(--palette_b_30); --outline_color: var(--palette_b_30);" - }, - "outline_b_40": { - "declaration": "outline-color: var(--palette_b_40); --outline_color: var(--palette_b_40);" - }, - "outline_b_50": { - "declaration": "outline-color: var(--palette_b_50); --outline_color: var(--palette_b_50);" - }, - "outline_b_60": { - "declaration": "outline-color: var(--palette_b_60); --outline_color: var(--palette_b_60);" - }, - "outline_b_70": { - "declaration": "outline-color: var(--palette_b_70); --outline_color: var(--palette_b_70);" - }, - "outline_b_80": { - "declaration": "outline-color: var(--palette_b_80); --outline_color: var(--palette_b_80);" - }, - "outline_b_90": { - "declaration": "outline-color: var(--palette_b_90); --outline_color: var(--palette_b_90);" - }, - "outline_b_95": { - "declaration": "outline-color: var(--palette_b_95); --outline_color: var(--palette_b_95);" - }, - "outline_b_100": { - "declaration": "outline-color: var(--palette_b_100); --outline_color: var(--palette_b_100);" - }, - "outline_c_00": { - "declaration": "outline-color: var(--palette_c_00); --outline_color: var(--palette_c_00);" - }, - "outline_c_05": { - "declaration": "outline-color: var(--palette_c_05); --outline_color: var(--palette_c_05);" - }, - "outline_c_10": { - "declaration": "outline-color: var(--palette_c_10); --outline_color: var(--palette_c_10);" - }, - "outline_c_20": { - "declaration": "outline-color: var(--palette_c_20); --outline_color: var(--palette_c_20);" - }, - "outline_c_30": { - "declaration": "outline-color: var(--palette_c_30); --outline_color: var(--palette_c_30);" - }, - "outline_c_40": { - "declaration": "outline-color: var(--palette_c_40); --outline_color: var(--palette_c_40);" - }, - "outline_c_50": { - "declaration": "outline-color: var(--palette_c_50); --outline_color: var(--palette_c_50);" - }, - "outline_c_60": { - "declaration": "outline-color: var(--palette_c_60); --outline_color: var(--palette_c_60);" - }, - "outline_c_70": { - "declaration": "outline-color: var(--palette_c_70); --outline_color: var(--palette_c_70);" - }, - "outline_c_80": { - "declaration": "outline-color: var(--palette_c_80); --outline_color: var(--palette_c_80);" - }, - "outline_c_90": { - "declaration": "outline-color: var(--palette_c_90); --outline_color: var(--palette_c_90);" - }, - "outline_c_95": { - "declaration": "outline-color: var(--palette_c_95); --outline_color: var(--palette_c_95);" - }, - "outline_c_100": { - "declaration": "outline-color: var(--palette_c_100); --outline_color: var(--palette_c_100);" - }, - "outline_d_00": { - "declaration": "outline-color: var(--palette_d_00); --outline_color: var(--palette_d_00);" - }, - "outline_d_05": { - "declaration": "outline-color: var(--palette_d_05); --outline_color: var(--palette_d_05);" - }, - "outline_d_10": { - "declaration": "outline-color: var(--palette_d_10); --outline_color: var(--palette_d_10);" - }, - "outline_d_20": { - "declaration": "outline-color: var(--palette_d_20); --outline_color: var(--palette_d_20);" - }, - "outline_d_30": { - "declaration": "outline-color: var(--palette_d_30); --outline_color: var(--palette_d_30);" - }, - "outline_d_40": { - "declaration": "outline-color: var(--palette_d_40); --outline_color: var(--palette_d_40);" - }, - "outline_d_50": { - "declaration": "outline-color: var(--palette_d_50); --outline_color: var(--palette_d_50);" - }, - "outline_d_60": { - "declaration": "outline-color: var(--palette_d_60); --outline_color: var(--palette_d_60);" - }, - "outline_d_70": { - "declaration": "outline-color: var(--palette_d_70); --outline_color: var(--palette_d_70);" - }, - "outline_d_80": { - "declaration": "outline-color: var(--palette_d_80); --outline_color: var(--palette_d_80);" - }, - "outline_d_90": { - "declaration": "outline-color: var(--palette_d_90); --outline_color: var(--palette_d_90);" - }, - "outline_d_95": { - "declaration": "outline-color: var(--palette_d_95); --outline_color: var(--palette_d_95);" - }, - "outline_d_100": { - "declaration": "outline-color: var(--palette_d_100); --outline_color: var(--palette_d_100);" - }, - "outline_e_00": { - "declaration": "outline-color: var(--palette_e_00); --outline_color: var(--palette_e_00);" - }, - "outline_e_05": { - "declaration": "outline-color: var(--palette_e_05); --outline_color: var(--palette_e_05);" - }, - "outline_e_10": { - "declaration": "outline-color: var(--palette_e_10); --outline_color: var(--palette_e_10);" - }, - "outline_e_20": { - "declaration": "outline-color: var(--palette_e_20); --outline_color: var(--palette_e_20);" - }, - "outline_e_30": { - "declaration": "outline-color: var(--palette_e_30); --outline_color: var(--palette_e_30);" - }, - "outline_e_40": { - "declaration": "outline-color: var(--palette_e_40); --outline_color: var(--palette_e_40);" - }, - "outline_e_50": { - "declaration": "outline-color: var(--palette_e_50); --outline_color: var(--palette_e_50);" - }, - "outline_e_60": { - "declaration": "outline-color: var(--palette_e_60); --outline_color: var(--palette_e_60);" - }, - "outline_e_70": { - "declaration": "outline-color: var(--palette_e_70); --outline_color: var(--palette_e_70);" - }, - "outline_e_80": { - "declaration": "outline-color: var(--palette_e_80); --outline_color: var(--palette_e_80);" - }, - "outline_e_90": { - "declaration": "outline-color: var(--palette_e_90); --outline_color: var(--palette_e_90);" - }, - "outline_e_95": { - "declaration": "outline-color: var(--palette_e_95); --outline_color: var(--palette_e_95);" - }, - "outline_e_100": { - "declaration": "outline-color: var(--palette_e_100); --outline_color: var(--palette_e_100);" - }, - "outline_f_00": { - "declaration": "outline-color: var(--palette_f_00); --outline_color: var(--palette_f_00);" - }, - "outline_f_05": { - "declaration": "outline-color: var(--palette_f_05); --outline_color: var(--palette_f_05);" - }, - "outline_f_10": { - "declaration": "outline-color: var(--palette_f_10); --outline_color: var(--palette_f_10);" - }, - "outline_f_20": { - "declaration": "outline-color: var(--palette_f_20); --outline_color: var(--palette_f_20);" - }, - "outline_f_30": { - "declaration": "outline-color: var(--palette_f_30); --outline_color: var(--palette_f_30);" - }, - "outline_f_40": { - "declaration": "outline-color: var(--palette_f_40); --outline_color: var(--palette_f_40);" - }, - "outline_f_50": { - "declaration": "outline-color: var(--palette_f_50); --outline_color: var(--palette_f_50);" - }, - "outline_f_60": { - "declaration": "outline-color: var(--palette_f_60); --outline_color: var(--palette_f_60);" - }, - "outline_f_70": { - "declaration": "outline-color: var(--palette_f_70); --outline_color: var(--palette_f_70);" - }, - "outline_f_80": { - "declaration": "outline-color: var(--palette_f_80); --outline_color: var(--palette_f_80);" - }, - "outline_f_90": { - "declaration": "outline-color: var(--palette_f_90); --outline_color: var(--palette_f_90);" - }, - "outline_f_95": { - "declaration": "outline-color: var(--palette_f_95); --outline_color: var(--palette_f_95);" - }, - "outline_f_100": { - "declaration": "outline-color: var(--palette_f_100); --outline_color: var(--palette_f_100);" - }, - "outline_g_00": { - "declaration": "outline-color: var(--palette_g_00); --outline_color: var(--palette_g_00);" - }, - "outline_g_05": { - "declaration": "outline-color: var(--palette_g_05); --outline_color: var(--palette_g_05);" - }, - "outline_g_10": { - "declaration": "outline-color: var(--palette_g_10); --outline_color: var(--palette_g_10);" - }, - "outline_g_20": { - "declaration": "outline-color: var(--palette_g_20); --outline_color: var(--palette_g_20);" - }, - "outline_g_30": { - "declaration": "outline-color: var(--palette_g_30); --outline_color: var(--palette_g_30);" - }, - "outline_g_40": { - "declaration": "outline-color: var(--palette_g_40); --outline_color: var(--palette_g_40);" - }, - "outline_g_50": { - "declaration": "outline-color: var(--palette_g_50); --outline_color: var(--palette_g_50);" - }, - "outline_g_60": { - "declaration": "outline-color: var(--palette_g_60); --outline_color: var(--palette_g_60);" - }, - "outline_g_70": { - "declaration": "outline-color: var(--palette_g_70); --outline_color: var(--palette_g_70);" - }, - "outline_g_80": { - "declaration": "outline-color: var(--palette_g_80); --outline_color: var(--palette_g_80);" - }, - "outline_g_90": { - "declaration": "outline-color: var(--palette_g_90); --outline_color: var(--palette_g_90);" - }, - "outline_g_95": { - "declaration": "outline-color: var(--palette_g_95); --outline_color: var(--palette_g_95);" - }, - "outline_g_100": { - "declaration": "outline-color: var(--palette_g_100); --outline_color: var(--palette_g_100);" - }, - "outline_h_00": { - "declaration": "outline-color: var(--palette_h_00); --outline_color: var(--palette_h_00);" - }, - "outline_h_05": { - "declaration": "outline-color: var(--palette_h_05); --outline_color: var(--palette_h_05);" - }, - "outline_h_10": { - "declaration": "outline-color: var(--palette_h_10); --outline_color: var(--palette_h_10);" - }, - "outline_h_20": { - "declaration": "outline-color: var(--palette_h_20); --outline_color: var(--palette_h_20);" - }, - "outline_h_30": { - "declaration": "outline-color: var(--palette_h_30); --outline_color: var(--palette_h_30);" - }, - "outline_h_40": { - "declaration": "outline-color: var(--palette_h_40); --outline_color: var(--palette_h_40);" - }, - "outline_h_50": { - "declaration": "outline-color: var(--palette_h_50); --outline_color: var(--palette_h_50);" - }, - "outline_h_60": { - "declaration": "outline-color: var(--palette_h_60); --outline_color: var(--palette_h_60);" - }, - "outline_h_70": { - "declaration": "outline-color: var(--palette_h_70); --outline_color: var(--palette_h_70);" - }, - "outline_h_80": { - "declaration": "outline-color: var(--palette_h_80); --outline_color: var(--palette_h_80);" - }, - "outline_h_90": { - "declaration": "outline-color: var(--palette_h_90); --outline_color: var(--palette_h_90);" - }, - "outline_h_95": { - "declaration": "outline-color: var(--palette_h_95); --outline_color: var(--palette_h_95);" - }, - "outline_h_100": { - "declaration": "outline-color: var(--palette_h_100); --outline_color: var(--palette_h_100);" - }, - "outline_i_00": { - "declaration": "outline-color: var(--palette_i_00); --outline_color: var(--palette_i_00);" - }, - "outline_i_05": { - "declaration": "outline-color: var(--palette_i_05); --outline_color: var(--palette_i_05);" - }, - "outline_i_10": { - "declaration": "outline-color: var(--palette_i_10); --outline_color: var(--palette_i_10);" - }, - "outline_i_20": { - "declaration": "outline-color: var(--palette_i_20); --outline_color: var(--palette_i_20);" - }, - "outline_i_30": { - "declaration": "outline-color: var(--palette_i_30); --outline_color: var(--palette_i_30);" - }, - "outline_i_40": { - "declaration": "outline-color: var(--palette_i_40); --outline_color: var(--palette_i_40);" - }, - "outline_i_50": { - "declaration": "outline-color: var(--palette_i_50); --outline_color: var(--palette_i_50);" - }, - "outline_i_60": { - "declaration": "outline-color: var(--palette_i_60); --outline_color: var(--palette_i_60);" - }, - "outline_i_70": { - "declaration": "outline-color: var(--palette_i_70); --outline_color: var(--palette_i_70);" - }, - "outline_i_80": { - "declaration": "outline-color: var(--palette_i_80); --outline_color: var(--palette_i_80);" - }, - "outline_i_90": { - "declaration": "outline-color: var(--palette_i_90); --outline_color: var(--palette_i_90);" - }, - "outline_i_95": { - "declaration": "outline-color: var(--palette_i_95); --outline_color: var(--palette_i_95);" - }, - "outline_i_100": { - "declaration": "outline-color: var(--palette_i_100); --outline_color: var(--palette_i_100);" - }, - "outline_j_00": { - "declaration": "outline-color: var(--palette_j_00); --outline_color: var(--palette_j_00);" - }, - "outline_j_05": { - "declaration": "outline-color: var(--palette_j_05); --outline_color: var(--palette_j_05);" - }, - "outline_j_10": { - "declaration": "outline-color: var(--palette_j_10); --outline_color: var(--palette_j_10);" - }, - "outline_j_20": { - "declaration": "outline-color: var(--palette_j_20); --outline_color: var(--palette_j_20);" - }, - "outline_j_30": { - "declaration": "outline-color: var(--palette_j_30); --outline_color: var(--palette_j_30);" - }, - "outline_j_40": { - "declaration": "outline-color: var(--palette_j_40); --outline_color: var(--palette_j_40);" - }, - "outline_j_50": { - "declaration": "outline-color: var(--palette_j_50); --outline_color: var(--palette_j_50);" - }, - "outline_j_60": { - "declaration": "outline-color: var(--palette_j_60); --outline_color: var(--palette_j_60);" - }, - "outline_j_70": { - "declaration": "outline-color: var(--palette_j_70); --outline_color: var(--palette_j_70);" - }, - "outline_j_80": { - "declaration": "outline-color: var(--palette_j_80); --outline_color: var(--palette_j_80);" - }, - "outline_j_90": { - "declaration": "outline-color: var(--palette_j_90); --outline_color: var(--palette_j_90);" - }, - "outline_j_95": { - "declaration": "outline-color: var(--palette_j_95); --outline_color: var(--palette_j_95);" - }, - "outline_j_100": { - "declaration": "outline-color: var(--palette_j_100); --outline_color: var(--palette_j_100);" - }, - "border_width_1": { "declaration": "border-width: var(--border_width_1);" }, - "border_width_2": { "declaration": "border-width: var(--border_width_2);" }, - "border_width_3": { "declaration": "border-width: var(--border_width_3);" }, - "border_width_4": { "declaration": "border-width: var(--border_width_4);" }, - "border_width_5": { "declaration": "border-width: var(--border_width_5);" }, - "border_width_6": { "declaration": "border-width: var(--border_width_6);" }, - "border_width_7": { "declaration": "border-width: var(--border_width_7);" }, - "border_width_8": { "declaration": "border-width: var(--border_width_8);" }, - "border_width_9": { "declaration": "border-width: var(--border_width_9);" }, - "outline_width_1": { "declaration": "outline-width: var(--border_width_1);" }, - "outline_width_2": { "declaration": "outline-width: var(--border_width_2);" }, - "outline_width_3": { "declaration": "outline-width: var(--border_width_3);" }, - "outline_width_4": { "declaration": "outline-width: var(--border_width_4);" }, - "outline_width_5": { "declaration": "outline-width: var(--border_width_5);" }, - "outline_width_6": { "declaration": "outline-width: var(--border_width_6);" }, - "outline_width_7": { "declaration": "outline-width: var(--border_width_7);" }, - "outline_width_8": { "declaration": "outline-width: var(--border_width_8);" }, - "outline_width_9": { "declaration": "outline-width: var(--border_width_9);" }, - "outline_width_focus": { "declaration": "outline-width: var(--outline_width_focus);" }, - "outline_width_active": { "declaration": "outline-width: var(--outline_width_active);" }, - "border_radius_xs3": { "declaration": "border-radius: var(--border_radius_xs3);" }, - "border_radius_xs2": { "declaration": "border-radius: var(--border_radius_xs2);" }, - "border_radius_xs": { "declaration": "border-radius: var(--border_radius_xs);" }, - "border_radius_sm": { "declaration": "border-radius: var(--border_radius_sm);" }, - "border_radius_md": { "declaration": "border-radius: var(--border_radius_md);" }, - "border_radius_lg": { "declaration": "border-radius: var(--border_radius_lg);" }, - "border_radius_xl": { "declaration": "border-radius: var(--border_radius_xl);" }, - "border_top_left_radius_xs3": { - "declaration": "border-top-left-radius: var(--border_radius_xs3);" - }, - "border_top_left_radius_xs2": { - "declaration": "border-top-left-radius: var(--border_radius_xs2);" - }, - "border_top_left_radius_xs": { - "declaration": "border-top-left-radius: var(--border_radius_xs);" - }, - "border_top_left_radius_sm": { - "declaration": "border-top-left-radius: var(--border_radius_sm);" - }, - "border_top_left_radius_md": { - "declaration": "border-top-left-radius: var(--border_radius_md);" - }, - "border_top_left_radius_lg": { - "declaration": "border-top-left-radius: var(--border_radius_lg);" - }, - "border_top_left_radius_xl": { - "declaration": "border-top-left-radius: var(--border_radius_xl);" - }, - "border_top_right_radius_xs3": { - "declaration": "border-top-right-radius: var(--border_radius_xs3);" - }, - "border_top_right_radius_xs2": { - "declaration": "border-top-right-radius: var(--border_radius_xs2);" - }, - "border_top_right_radius_xs": { - "declaration": "border-top-right-radius: var(--border_radius_xs);" - }, - "border_top_right_radius_sm": { - "declaration": "border-top-right-radius: var(--border_radius_sm);" - }, - "border_top_right_radius_md": { - "declaration": "border-top-right-radius: var(--border_radius_md);" - }, - "border_top_right_radius_lg": { - "declaration": "border-top-right-radius: var(--border_radius_lg);" - }, - "border_top_right_radius_xl": { - "declaration": "border-top-right-radius: var(--border_radius_xl);" - }, - "border_bottom_left_radius_xs3": { - "declaration": "border-bottom-left-radius: var(--border_radius_xs3);" - }, - "border_bottom_left_radius_xs2": { - "declaration": "border-bottom-left-radius: var(--border_radius_xs2);" - }, - "border_bottom_left_radius_xs": { - "declaration": "border-bottom-left-radius: var(--border_radius_xs);" - }, - "border_bottom_left_radius_sm": { - "declaration": "border-bottom-left-radius: var(--border_radius_sm);" - }, - "border_bottom_left_radius_md": { - "declaration": "border-bottom-left-radius: var(--border_radius_md);" - }, - "border_bottom_left_radius_lg": { - "declaration": "border-bottom-left-radius: var(--border_radius_lg);" - }, - "border_bottom_left_radius_xl": { - "declaration": "border-bottom-left-radius: var(--border_radius_xl);" - }, - "border_bottom_right_radius_xs3": { - "declaration": "border-bottom-right-radius: var(--border_radius_xs3);" - }, - "border_bottom_right_radius_xs2": { - "declaration": "border-bottom-right-radius: var(--border_radius_xs2);" - }, - "border_bottom_right_radius_xs": { - "declaration": "border-bottom-right-radius: var(--border_radius_xs);" - }, - "border_bottom_right_radius_sm": { - "declaration": "border-bottom-right-radius: var(--border_radius_sm);" - }, - "border_bottom_right_radius_md": { - "declaration": "border-bottom-right-radius: var(--border_radius_md);" - }, - "border_bottom_right_radius_lg": { - "declaration": "border-bottom-right-radius: var(--border_radius_lg);" - }, - "border_bottom_right_radius_xl": { - "declaration": "border-bottom-right-radius: var(--border_radius_xl);" - }, - "shadow_xs": { - "declaration": "box-shadow: var(--shadow_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_sm": { - "declaration": "box-shadow: var(--shadow_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_md": { - "declaration": "box-shadow: var(--shadow_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_lg": { - "declaration": "box-shadow: var(--shadow_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_xl": { - "declaration": "box-shadow: var(--shadow_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_top_xs": { - "declaration": "box-shadow: var(--shadow_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_top_sm": { - "declaration": "box-shadow: var(--shadow_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_top_md": { - "declaration": "box-shadow: var(--shadow_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_top_lg": { - "declaration": "box-shadow: var(--shadow_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_top_xl": { - "declaration": "box-shadow: var(--shadow_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_bottom_xs": { - "declaration": "box-shadow: var(--shadow_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_bottom_sm": { - "declaration": "box-shadow: var(--shadow_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_bottom_md": { - "declaration": "box-shadow: var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_bottom_lg": { - "declaration": "box-shadow: var(--shadow_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_bottom_xl": { - "declaration": "box-shadow: var(--shadow_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_inset_xs": { - "declaration": "box-shadow: var(--shadow_inset_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_inset_sm": { - "declaration": "box-shadow: var(--shadow_inset_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_inset_md": { - "declaration": "box-shadow: var(--shadow_inset_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_inset_lg": { - "declaration": "box-shadow: var(--shadow_inset_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_inset_xl": { - "declaration": "box-shadow: var(--shadow_inset_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_inset_top_xs": { - "declaration": "box-shadow: var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_inset_top_sm": { - "declaration": "box-shadow: var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_inset_top_md": { - "declaration": "box-shadow: var(--shadow_inset_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_inset_top_lg": { - "declaration": "box-shadow: var(--shadow_inset_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_inset_top_xl": { - "declaration": "box-shadow: var(--shadow_inset_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_inset_bottom_xs": { - "declaration": "box-shadow: var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);" - }, - "shadow_inset_bottom_sm": { - "declaration": "box-shadow: var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);" - }, - "shadow_inset_bottom_md": { - "declaration": "box-shadow: var(--shadow_inset_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);" - }, - "shadow_inset_bottom_lg": { - "declaration": "box-shadow: var(--shadow_inset_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);" - }, - "shadow_inset_bottom_xl": { - "declaration": "box-shadow: var(--shadow_inset_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);" - }, - "shadow_color_umbra": { "declaration": "--shadow_color: var(--shadow_color_umbra);" }, - "shadow_color_highlight": { "declaration": "--shadow_color: var(--shadow_color_highlight);" }, - "shadow_color_glow": { "declaration": "--shadow_color: var(--shadow_color_glow);" }, - "shadow_color_shroud": { "declaration": "--shadow_color: var(--shadow_color_shroud);" }, - "shadow_alpha_00": { "declaration": "--shadow_alpha: var(--shadow_alpha_00);" }, - "shadow_alpha_05": { "declaration": "--shadow_alpha: var(--shadow_alpha_05);" }, - "shadow_alpha_10": { "declaration": "--shadow_alpha: var(--shadow_alpha_10);" }, - "shadow_alpha_20": { "declaration": "--shadow_alpha: var(--shadow_alpha_20);" }, - "shadow_alpha_30": { "declaration": "--shadow_alpha: var(--shadow_alpha_30);" }, - "shadow_alpha_40": { "declaration": "--shadow_alpha: var(--shadow_alpha_40);" }, - "shadow_alpha_50": { "declaration": "--shadow_alpha: var(--shadow_alpha_50);" }, - "shadow_alpha_60": { "declaration": "--shadow_alpha: var(--shadow_alpha_60);" }, - "shadow_alpha_70": { "declaration": "--shadow_alpha: var(--shadow_alpha_70);" }, - "shadow_alpha_80": { "declaration": "--shadow_alpha: var(--shadow_alpha_80);" }, - "shadow_alpha_90": { "declaration": "--shadow_alpha: var(--shadow_alpha_90);" }, - "shadow_alpha_95": { "declaration": "--shadow_alpha: var(--shadow_alpha_95);" }, - "shadow_alpha_100": { "declaration": "--shadow_alpha: var(--shadow_alpha_100);" }, - "shadow_a_00": { "declaration": "--shadow_color: var(--palette_a_00);" }, - "shadow_a_05": { "declaration": "--shadow_color: var(--palette_a_05);" }, - "shadow_a_10": { "declaration": "--shadow_color: var(--palette_a_10);" }, - "shadow_a_20": { "declaration": "--shadow_color: var(--palette_a_20);" }, - "shadow_a_30": { "declaration": "--shadow_color: var(--palette_a_30);" }, - "shadow_a_40": { "declaration": "--shadow_color: var(--palette_a_40);" }, - "shadow_a_50": { "declaration": "--shadow_color: var(--palette_a_50);" }, - "shadow_a_60": { "declaration": "--shadow_color: var(--palette_a_60);" }, - "shadow_a_70": { "declaration": "--shadow_color: var(--palette_a_70);" }, - "shadow_a_80": { "declaration": "--shadow_color: var(--palette_a_80);" }, - "shadow_a_90": { "declaration": "--shadow_color: var(--palette_a_90);" }, - "shadow_a_95": { "declaration": "--shadow_color: var(--palette_a_95);" }, - "shadow_a_100": { "declaration": "--shadow_color: var(--palette_a_100);" }, - "shadow_b_00": { "declaration": "--shadow_color: var(--palette_b_00);" }, - "shadow_b_05": { "declaration": "--shadow_color: var(--palette_b_05);" }, - "shadow_b_10": { "declaration": "--shadow_color: var(--palette_b_10);" }, - "shadow_b_20": { "declaration": "--shadow_color: var(--palette_b_20);" }, - "shadow_b_30": { "declaration": "--shadow_color: var(--palette_b_30);" }, - "shadow_b_40": { "declaration": "--shadow_color: var(--palette_b_40);" }, - "shadow_b_50": { "declaration": "--shadow_color: var(--palette_b_50);" }, - "shadow_b_60": { "declaration": "--shadow_color: var(--palette_b_60);" }, - "shadow_b_70": { "declaration": "--shadow_color: var(--palette_b_70);" }, - "shadow_b_80": { "declaration": "--shadow_color: var(--palette_b_80);" }, - "shadow_b_90": { "declaration": "--shadow_color: var(--palette_b_90);" }, - "shadow_b_95": { "declaration": "--shadow_color: var(--palette_b_95);" }, - "shadow_b_100": { "declaration": "--shadow_color: var(--palette_b_100);" }, - "shadow_c_00": { "declaration": "--shadow_color: var(--palette_c_00);" }, - "shadow_c_05": { "declaration": "--shadow_color: var(--palette_c_05);" }, - "shadow_c_10": { "declaration": "--shadow_color: var(--palette_c_10);" }, - "shadow_c_20": { "declaration": "--shadow_color: var(--palette_c_20);" }, - "shadow_c_30": { "declaration": "--shadow_color: var(--palette_c_30);" }, - "shadow_c_40": { "declaration": "--shadow_color: var(--palette_c_40);" }, - "shadow_c_50": { "declaration": "--shadow_color: var(--palette_c_50);" }, - "shadow_c_60": { "declaration": "--shadow_color: var(--palette_c_60);" }, - "shadow_c_70": { "declaration": "--shadow_color: var(--palette_c_70);" }, - "shadow_c_80": { "declaration": "--shadow_color: var(--palette_c_80);" }, - "shadow_c_90": { "declaration": "--shadow_color: var(--palette_c_90);" }, - "shadow_c_95": { "declaration": "--shadow_color: var(--palette_c_95);" }, - "shadow_c_100": { "declaration": "--shadow_color: var(--palette_c_100);" }, - "shadow_d_00": { "declaration": "--shadow_color: var(--palette_d_00);" }, - "shadow_d_05": { "declaration": "--shadow_color: var(--palette_d_05);" }, - "shadow_d_10": { "declaration": "--shadow_color: var(--palette_d_10);" }, - "shadow_d_20": { "declaration": "--shadow_color: var(--palette_d_20);" }, - "shadow_d_30": { "declaration": "--shadow_color: var(--palette_d_30);" }, - "shadow_d_40": { "declaration": "--shadow_color: var(--palette_d_40);" }, - "shadow_d_50": { "declaration": "--shadow_color: var(--palette_d_50);" }, - "shadow_d_60": { "declaration": "--shadow_color: var(--palette_d_60);" }, - "shadow_d_70": { "declaration": "--shadow_color: var(--palette_d_70);" }, - "shadow_d_80": { "declaration": "--shadow_color: var(--palette_d_80);" }, - "shadow_d_90": { "declaration": "--shadow_color: var(--palette_d_90);" }, - "shadow_d_95": { "declaration": "--shadow_color: var(--palette_d_95);" }, - "shadow_d_100": { "declaration": "--shadow_color: var(--palette_d_100);" }, - "shadow_e_00": { "declaration": "--shadow_color: var(--palette_e_00);" }, - "shadow_e_05": { "declaration": "--shadow_color: var(--palette_e_05);" }, - "shadow_e_10": { "declaration": "--shadow_color: var(--palette_e_10);" }, - "shadow_e_20": { "declaration": "--shadow_color: var(--palette_e_20);" }, - "shadow_e_30": { "declaration": "--shadow_color: var(--palette_e_30);" }, - "shadow_e_40": { "declaration": "--shadow_color: var(--palette_e_40);" }, - "shadow_e_50": { "declaration": "--shadow_color: var(--palette_e_50);" }, - "shadow_e_60": { "declaration": "--shadow_color: var(--palette_e_60);" }, - "shadow_e_70": { "declaration": "--shadow_color: var(--palette_e_70);" }, - "shadow_e_80": { "declaration": "--shadow_color: var(--palette_e_80);" }, - "shadow_e_90": { "declaration": "--shadow_color: var(--palette_e_90);" }, - "shadow_e_95": { "declaration": "--shadow_color: var(--palette_e_95);" }, - "shadow_e_100": { "declaration": "--shadow_color: var(--palette_e_100);" }, - "shadow_f_00": { "declaration": "--shadow_color: var(--palette_f_00);" }, - "shadow_f_05": { "declaration": "--shadow_color: var(--palette_f_05);" }, - "shadow_f_10": { "declaration": "--shadow_color: var(--palette_f_10);" }, - "shadow_f_20": { "declaration": "--shadow_color: var(--palette_f_20);" }, - "shadow_f_30": { "declaration": "--shadow_color: var(--palette_f_30);" }, - "shadow_f_40": { "declaration": "--shadow_color: var(--palette_f_40);" }, - "shadow_f_50": { "declaration": "--shadow_color: var(--palette_f_50);" }, - "shadow_f_60": { "declaration": "--shadow_color: var(--palette_f_60);" }, - "shadow_f_70": { "declaration": "--shadow_color: var(--palette_f_70);" }, - "shadow_f_80": { "declaration": "--shadow_color: var(--palette_f_80);" }, - "shadow_f_90": { "declaration": "--shadow_color: var(--palette_f_90);" }, - "shadow_f_95": { "declaration": "--shadow_color: var(--palette_f_95);" }, - "shadow_f_100": { "declaration": "--shadow_color: var(--palette_f_100);" }, - "shadow_g_00": { "declaration": "--shadow_color: var(--palette_g_00);" }, - "shadow_g_05": { "declaration": "--shadow_color: var(--palette_g_05);" }, - "shadow_g_10": { "declaration": "--shadow_color: var(--palette_g_10);" }, - "shadow_g_20": { "declaration": "--shadow_color: var(--palette_g_20);" }, - "shadow_g_30": { "declaration": "--shadow_color: var(--palette_g_30);" }, - "shadow_g_40": { "declaration": "--shadow_color: var(--palette_g_40);" }, - "shadow_g_50": { "declaration": "--shadow_color: var(--palette_g_50);" }, - "shadow_g_60": { "declaration": "--shadow_color: var(--palette_g_60);" }, - "shadow_g_70": { "declaration": "--shadow_color: var(--palette_g_70);" }, - "shadow_g_80": { "declaration": "--shadow_color: var(--palette_g_80);" }, - "shadow_g_90": { "declaration": "--shadow_color: var(--palette_g_90);" }, - "shadow_g_95": { "declaration": "--shadow_color: var(--palette_g_95);" }, - "shadow_g_100": { "declaration": "--shadow_color: var(--palette_g_100);" }, - "shadow_h_00": { "declaration": "--shadow_color: var(--palette_h_00);" }, - "shadow_h_05": { "declaration": "--shadow_color: var(--palette_h_05);" }, - "shadow_h_10": { "declaration": "--shadow_color: var(--palette_h_10);" }, - "shadow_h_20": { "declaration": "--shadow_color: var(--palette_h_20);" }, - "shadow_h_30": { "declaration": "--shadow_color: var(--palette_h_30);" }, - "shadow_h_40": { "declaration": "--shadow_color: var(--palette_h_40);" }, - "shadow_h_50": { "declaration": "--shadow_color: var(--palette_h_50);" }, - "shadow_h_60": { "declaration": "--shadow_color: var(--palette_h_60);" }, - "shadow_h_70": { "declaration": "--shadow_color: var(--palette_h_70);" }, - "shadow_h_80": { "declaration": "--shadow_color: var(--palette_h_80);" }, - "shadow_h_90": { "declaration": "--shadow_color: var(--palette_h_90);" }, - "shadow_h_95": { "declaration": "--shadow_color: var(--palette_h_95);" }, - "shadow_h_100": { "declaration": "--shadow_color: var(--palette_h_100);" }, - "shadow_i_00": { "declaration": "--shadow_color: var(--palette_i_00);" }, - "shadow_i_05": { "declaration": "--shadow_color: var(--palette_i_05);" }, - "shadow_i_10": { "declaration": "--shadow_color: var(--palette_i_10);" }, - "shadow_i_20": { "declaration": "--shadow_color: var(--palette_i_20);" }, - "shadow_i_30": { "declaration": "--shadow_color: var(--palette_i_30);" }, - "shadow_i_40": { "declaration": "--shadow_color: var(--palette_i_40);" }, - "shadow_i_50": { "declaration": "--shadow_color: var(--palette_i_50);" }, - "shadow_i_60": { "declaration": "--shadow_color: var(--palette_i_60);" }, - "shadow_i_70": { "declaration": "--shadow_color: var(--palette_i_70);" }, - "shadow_i_80": { "declaration": "--shadow_color: var(--palette_i_80);" }, - "shadow_i_90": { "declaration": "--shadow_color: var(--palette_i_90);" }, - "shadow_i_95": { "declaration": "--shadow_color: var(--palette_i_95);" }, - "shadow_i_100": { "declaration": "--shadow_color: var(--palette_i_100);" }, - "shadow_j_00": { "declaration": "--shadow_color: var(--palette_j_00);" }, - "shadow_j_05": { "declaration": "--shadow_color: var(--palette_j_05);" }, - "shadow_j_10": { "declaration": "--shadow_color: var(--palette_j_10);" }, - "shadow_j_20": { "declaration": "--shadow_color: var(--palette_j_20);" }, - "shadow_j_30": { "declaration": "--shadow_color: var(--palette_j_30);" }, - "shadow_j_40": { "declaration": "--shadow_color: var(--palette_j_40);" }, - "shadow_j_50": { "declaration": "--shadow_color: var(--palette_j_50);" }, - "shadow_j_60": { "declaration": "--shadow_color: var(--palette_j_60);" }, - "shadow_j_70": { "declaration": "--shadow_color: var(--palette_j_70);" }, - "shadow_j_80": { "declaration": "--shadow_color: var(--palette_j_80);" }, - "shadow_j_90": { "declaration": "--shadow_color: var(--palette_j_90);" }, - "shadow_j_95": { "declaration": "--shadow_color: var(--palette_j_95);" }, - "shadow_j_100": { "declaration": "--shadow_color: var(--palette_j_100);" }, - "width_xs5": { "declaration": "width: var(--space_xs5);" }, - "width_xs4": { "declaration": "width: var(--space_xs4);" }, - "width_xs3": { "declaration": "width: var(--space_xs3);" }, - "width_xs2": { "declaration": "width: var(--space_xs2);" }, - "width_xs": { "declaration": "width: var(--space_xs);" }, - "width_sm": { "declaration": "width: var(--space_sm);" }, - "width_md": { "declaration": "width: var(--space_md);" }, - "width_lg": { "declaration": "width: var(--space_lg);" }, - "width_xl": { "declaration": "width: var(--space_xl);" }, - "width_xl2": { "declaration": "width: var(--space_xl2);" }, - "width_xl3": { "declaration": "width: var(--space_xl3);" }, - "width_xl4": { "declaration": "width: var(--space_xl4);" }, - "width_xl5": { "declaration": "width: var(--space_xl5);" }, - "width_xl6": { "declaration": "width: var(--space_xl6);" }, - "width_xl7": { "declaration": "width: var(--space_xl7);" }, - "width_xl8": { "declaration": "width: var(--space_xl8);" }, - "width_xl9": { "declaration": "width: var(--space_xl9);" }, - "width_xl10": { "declaration": "width: var(--space_xl10);" }, - "width_xl11": { "declaration": "width: var(--space_xl11);" }, - "width_xl12": { "declaration": "width: var(--space_xl12);" }, - "width_xl13": { "declaration": "width: var(--space_xl13);" }, - "width_xl14": { "declaration": "width: var(--space_xl14);" }, - "width_xl15": { "declaration": "width: var(--space_xl15);" }, - "height_xs5": { "declaration": "height: var(--space_xs5);" }, - "height_xs4": { "declaration": "height: var(--space_xs4);" }, - "height_xs3": { "declaration": "height: var(--space_xs3);" }, - "height_xs2": { "declaration": "height: var(--space_xs2);" }, - "height_xs": { "declaration": "height: var(--space_xs);" }, - "height_sm": { "declaration": "height: var(--space_sm);" }, - "height_md": { "declaration": "height: var(--space_md);" }, - "height_lg": { "declaration": "height: var(--space_lg);" }, - "height_xl": { "declaration": "height: var(--space_xl);" }, - "height_xl2": { "declaration": "height: var(--space_xl2);" }, - "height_xl3": { "declaration": "height: var(--space_xl3);" }, - "height_xl4": { "declaration": "height: var(--space_xl4);" }, - "height_xl5": { "declaration": "height: var(--space_xl5);" }, - "height_xl6": { "declaration": "height: var(--space_xl6);" }, - "height_xl7": { "declaration": "height: var(--space_xl7);" }, - "height_xl8": { "declaration": "height: var(--space_xl8);" }, - "height_xl9": { "declaration": "height: var(--space_xl9);" }, - "height_xl10": { "declaration": "height: var(--space_xl10);" }, - "height_xl11": { "declaration": "height: var(--space_xl11);" }, - "height_xl12": { "declaration": "height: var(--space_xl12);" }, - "height_xl13": { "declaration": "height: var(--space_xl13);" }, - "height_xl14": { "declaration": "height: var(--space_xl14);" }, - "height_xl15": { "declaration": "height: var(--space_xl15);" }, - "width_atmost_xs": { "declaration": "width: 100%; max-width: var(--distance_xs);" }, - "width_atmost_sm": { "declaration": "width: 100%; max-width: var(--distance_sm);" }, - "width_atmost_md": { "declaration": "width: 100%; max-width: var(--distance_md);" }, - "width_atmost_lg": { "declaration": "width: 100%; max-width: var(--distance_lg);" }, - "width_atmost_xl": { "declaration": "width: 100%; max-width: var(--distance_xl);" }, - "width_atleast_xs": { "declaration": "width: 100%; min-width: var(--distance_xs);" }, - "width_atleast_sm": { "declaration": "width: 100%; min-width: var(--distance_sm);" }, - "width_atleast_md": { "declaration": "width: 100%; min-width: var(--distance_md);" }, - "width_atleast_lg": { "declaration": "width: 100%; min-width: var(--distance_lg);" }, - "width_atleast_xl": { "declaration": "width: 100%; min-width: var(--distance_xl);" }, - "height_atmost_xs": { "declaration": "height: 100%; max-height: var(--distance_xs);" }, - "height_atmost_sm": { "declaration": "height: 100%; max-height: var(--distance_sm);" }, - "height_atmost_md": { "declaration": "height: 100%; max-height: var(--distance_md);" }, - "height_atmost_lg": { "declaration": "height: 100%; max-height: var(--distance_lg);" }, - "height_atmost_xl": { "declaration": "height: 100%; max-height: var(--distance_xl);" }, - "height_atleast_xs": { "declaration": "height: 100%; min-height: var(--distance_xs);" }, - "height_atleast_sm": { "declaration": "height: 100%; min-height: var(--distance_sm);" }, - "height_atleast_md": { "declaration": "height: 100%; min-height: var(--distance_md);" }, - "height_atleast_lg": { "declaration": "height: 100%; min-height: var(--distance_lg);" }, - "height_atleast_xl": { "declaration": "height: 100%; min-height: var(--distance_xl);" }, - "top_xs5": { "declaration": "top: var(--space_xs5);" }, - "top_xs4": { "declaration": "top: var(--space_xs4);" }, - "top_xs3": { "declaration": "top: var(--space_xs3);" }, - "top_xs2": { "declaration": "top: var(--space_xs2);" }, - "top_xs": { "declaration": "top: var(--space_xs);" }, - "top_sm": { "declaration": "top: var(--space_sm);" }, - "top_md": { "declaration": "top: var(--space_md);" }, - "top_lg": { "declaration": "top: var(--space_lg);" }, - "top_xl": { "declaration": "top: var(--space_xl);" }, - "top_xl2": { "declaration": "top: var(--space_xl2);" }, - "top_xl3": { "declaration": "top: var(--space_xl3);" }, - "top_xl4": { "declaration": "top: var(--space_xl4);" }, - "top_xl5": { "declaration": "top: var(--space_xl5);" }, - "top_xl6": { "declaration": "top: var(--space_xl6);" }, - "top_xl7": { "declaration": "top: var(--space_xl7);" }, - "top_xl8": { "declaration": "top: var(--space_xl8);" }, - "top_xl9": { "declaration": "top: var(--space_xl9);" }, - "top_xl10": { "declaration": "top: var(--space_xl10);" }, - "top_xl11": { "declaration": "top: var(--space_xl11);" }, - "top_xl12": { "declaration": "top: var(--space_xl12);" }, - "top_xl13": { "declaration": "top: var(--space_xl13);" }, - "top_xl14": { "declaration": "top: var(--space_xl14);" }, - "top_xl15": { "declaration": "top: var(--space_xl15);" }, - "right_xs5": { "declaration": "right: var(--space_xs5);" }, - "right_xs4": { "declaration": "right: var(--space_xs4);" }, - "right_xs3": { "declaration": "right: var(--space_xs3);" }, - "right_xs2": { "declaration": "right: var(--space_xs2);" }, - "right_xs": { "declaration": "right: var(--space_xs);" }, - "right_sm": { "declaration": "right: var(--space_sm);" }, - "right_md": { "declaration": "right: var(--space_md);" }, - "right_lg": { "declaration": "right: var(--space_lg);" }, - "right_xl": { "declaration": "right: var(--space_xl);" }, - "right_xl2": { "declaration": "right: var(--space_xl2);" }, - "right_xl3": { "declaration": "right: var(--space_xl3);" }, - "right_xl4": { "declaration": "right: var(--space_xl4);" }, - "right_xl5": { "declaration": "right: var(--space_xl5);" }, - "right_xl6": { "declaration": "right: var(--space_xl6);" }, - "right_xl7": { "declaration": "right: var(--space_xl7);" }, - "right_xl8": { "declaration": "right: var(--space_xl8);" }, - "right_xl9": { "declaration": "right: var(--space_xl9);" }, - "right_xl10": { "declaration": "right: var(--space_xl10);" }, - "right_xl11": { "declaration": "right: var(--space_xl11);" }, - "right_xl12": { "declaration": "right: var(--space_xl12);" }, - "right_xl13": { "declaration": "right: var(--space_xl13);" }, - "right_xl14": { "declaration": "right: var(--space_xl14);" }, - "right_xl15": { "declaration": "right: var(--space_xl15);" }, - "bottom_xs5": { "declaration": "bottom: var(--space_xs5);" }, - "bottom_xs4": { "declaration": "bottom: var(--space_xs4);" }, - "bottom_xs3": { "declaration": "bottom: var(--space_xs3);" }, - "bottom_xs2": { "declaration": "bottom: var(--space_xs2);" }, - "bottom_xs": { "declaration": "bottom: var(--space_xs);" }, - "bottom_sm": { "declaration": "bottom: var(--space_sm);" }, - "bottom_md": { "declaration": "bottom: var(--space_md);" }, - "bottom_lg": { "declaration": "bottom: var(--space_lg);" }, - "bottom_xl": { "declaration": "bottom: var(--space_xl);" }, - "bottom_xl2": { "declaration": "bottom: var(--space_xl2);" }, - "bottom_xl3": { "declaration": "bottom: var(--space_xl3);" }, - "bottom_xl4": { "declaration": "bottom: var(--space_xl4);" }, - "bottom_xl5": { "declaration": "bottom: var(--space_xl5);" }, - "bottom_xl6": { "declaration": "bottom: var(--space_xl6);" }, - "bottom_xl7": { "declaration": "bottom: var(--space_xl7);" }, - "bottom_xl8": { "declaration": "bottom: var(--space_xl8);" }, - "bottom_xl9": { "declaration": "bottom: var(--space_xl9);" }, - "bottom_xl10": { "declaration": "bottom: var(--space_xl10);" }, - "bottom_xl11": { "declaration": "bottom: var(--space_xl11);" }, - "bottom_xl12": { "declaration": "bottom: var(--space_xl12);" }, - "bottom_xl13": { "declaration": "bottom: var(--space_xl13);" }, - "bottom_xl14": { "declaration": "bottom: var(--space_xl14);" }, - "bottom_xl15": { "declaration": "bottom: var(--space_xl15);" }, - "left_xs5": { "declaration": "left: var(--space_xs5);" }, - "left_xs4": { "declaration": "left: var(--space_xs4);" }, - "left_xs3": { "declaration": "left: var(--space_xs3);" }, - "left_xs2": { "declaration": "left: var(--space_xs2);" }, - "left_xs": { "declaration": "left: var(--space_xs);" }, - "left_sm": { "declaration": "left: var(--space_sm);" }, - "left_md": { "declaration": "left: var(--space_md);" }, - "left_lg": { "declaration": "left: var(--space_lg);" }, - "left_xl": { "declaration": "left: var(--space_xl);" }, - "left_xl2": { "declaration": "left: var(--space_xl2);" }, - "left_xl3": { "declaration": "left: var(--space_xl3);" }, - "left_xl4": { "declaration": "left: var(--space_xl4);" }, - "left_xl5": { "declaration": "left: var(--space_xl5);" }, - "left_xl6": { "declaration": "left: var(--space_xl6);" }, - "left_xl7": { "declaration": "left: var(--space_xl7);" }, - "left_xl8": { "declaration": "left: var(--space_xl8);" }, - "left_xl9": { "declaration": "left: var(--space_xl9);" }, - "left_xl10": { "declaration": "left: var(--space_xl10);" }, - "left_xl11": { "declaration": "left: var(--space_xl11);" }, - "left_xl12": { "declaration": "left: var(--space_xl12);" }, - "left_xl13": { "declaration": "left: var(--space_xl13);" }, - "left_xl14": { "declaration": "left: var(--space_xl14);" }, - "left_xl15": { "declaration": "left: var(--space_xl15);" }, - "inset_xs5": { "declaration": "inset: var(--space_xs5);" }, - "inset_xs4": { "declaration": "inset: var(--space_xs4);" }, - "inset_xs3": { "declaration": "inset: var(--space_xs3);" }, - "inset_xs2": { "declaration": "inset: var(--space_xs2);" }, - "inset_xs": { "declaration": "inset: var(--space_xs);" }, - "inset_sm": { "declaration": "inset: var(--space_sm);" }, - "inset_md": { "declaration": "inset: var(--space_md);" }, - "inset_lg": { "declaration": "inset: var(--space_lg);" }, - "inset_xl": { "declaration": "inset: var(--space_xl);" }, - "inset_xl2": { "declaration": "inset: var(--space_xl2);" }, - "inset_xl3": { "declaration": "inset: var(--space_xl3);" }, - "inset_xl4": { "declaration": "inset: var(--space_xl4);" }, - "inset_xl5": { "declaration": "inset: var(--space_xl5);" }, - "inset_xl6": { "declaration": "inset: var(--space_xl6);" }, - "inset_xl7": { "declaration": "inset: var(--space_xl7);" }, - "inset_xl8": { "declaration": "inset: var(--space_xl8);" }, - "inset_xl9": { "declaration": "inset: var(--space_xl9);" }, - "inset_xl10": { "declaration": "inset: var(--space_xl10);" }, - "inset_xl11": { "declaration": "inset: var(--space_xl11);" }, - "inset_xl12": { "declaration": "inset: var(--space_xl12);" }, - "inset_xl13": { "declaration": "inset: var(--space_xl13);" }, - "inset_xl14": { "declaration": "inset: var(--space_xl14);" }, - "inset_xl15": { "declaration": "inset: var(--space_xl15);" }, - "p_0": { "declaration": "padding: 0;" }, - "p_xs5": { "declaration": "padding: var(--space_xs5);" }, - "p_xs4": { "declaration": "padding: var(--space_xs4);" }, - "p_xs3": { "declaration": "padding: var(--space_xs3);" }, - "p_xs2": { "declaration": "padding: var(--space_xs2);" }, - "p_xs": { "declaration": "padding: var(--space_xs);" }, - "p_sm": { "declaration": "padding: var(--space_sm);" }, - "p_md": { "declaration": "padding: var(--space_md);" }, - "p_lg": { "declaration": "padding: var(--space_lg);" }, - "p_xl": { "declaration": "padding: var(--space_xl);" }, - "p_xl2": { "declaration": "padding: var(--space_xl2);" }, - "p_xl3": { "declaration": "padding: var(--space_xl3);" }, - "p_xl4": { "declaration": "padding: var(--space_xl4);" }, - "p_xl5": { "declaration": "padding: var(--space_xl5);" }, - "p_xl6": { "declaration": "padding: var(--space_xl6);" }, - "p_xl7": { "declaration": "padding: var(--space_xl7);" }, - "p_xl8": { "declaration": "padding: var(--space_xl8);" }, - "p_xl9": { "declaration": "padding: var(--space_xl9);" }, - "p_xl10": { "declaration": "padding: var(--space_xl10);" }, - "p_xl11": { "declaration": "padding: var(--space_xl11);" }, - "p_xl12": { "declaration": "padding: var(--space_xl12);" }, - "p_xl13": { "declaration": "padding: var(--space_xl13);" }, - "p_xl14": { "declaration": "padding: var(--space_xl14);" }, - "p_xl15": { "declaration": "padding: var(--space_xl15);" }, - "pt_0": { "declaration": "padding-top: 0;" }, - "pt_xs5": { "declaration": "padding-top: var(--space_xs5);" }, - "pt_xs4": { "declaration": "padding-top: var(--space_xs4);" }, - "pt_xs3": { "declaration": "padding-top: var(--space_xs3);" }, - "pt_xs2": { "declaration": "padding-top: var(--space_xs2);" }, - "pt_xs": { "declaration": "padding-top: var(--space_xs);" }, - "pt_sm": { "declaration": "padding-top: var(--space_sm);" }, - "pt_md": { "declaration": "padding-top: var(--space_md);" }, - "pt_lg": { "declaration": "padding-top: var(--space_lg);" }, - "pt_xl": { "declaration": "padding-top: var(--space_xl);" }, - "pt_xl2": { "declaration": "padding-top: var(--space_xl2);" }, - "pt_xl3": { "declaration": "padding-top: var(--space_xl3);" }, - "pt_xl4": { "declaration": "padding-top: var(--space_xl4);" }, - "pt_xl5": { "declaration": "padding-top: var(--space_xl5);" }, - "pt_xl6": { "declaration": "padding-top: var(--space_xl6);" }, - "pt_xl7": { "declaration": "padding-top: var(--space_xl7);" }, - "pt_xl8": { "declaration": "padding-top: var(--space_xl8);" }, - "pt_xl9": { "declaration": "padding-top: var(--space_xl9);" }, - "pt_xl10": { "declaration": "padding-top: var(--space_xl10);" }, - "pt_xl11": { "declaration": "padding-top: var(--space_xl11);" }, - "pt_xl12": { "declaration": "padding-top: var(--space_xl12);" }, - "pt_xl13": { "declaration": "padding-top: var(--space_xl13);" }, - "pt_xl14": { "declaration": "padding-top: var(--space_xl14);" }, - "pt_xl15": { "declaration": "padding-top: var(--space_xl15);" }, - "pr_0": { "declaration": "padding-right: 0;" }, - "pr_xs5": { "declaration": "padding-right: var(--space_xs5);" }, - "pr_xs4": { "declaration": "padding-right: var(--space_xs4);" }, - "pr_xs3": { "declaration": "padding-right: var(--space_xs3);" }, - "pr_xs2": { "declaration": "padding-right: var(--space_xs2);" }, - "pr_xs": { "declaration": "padding-right: var(--space_xs);" }, - "pr_sm": { "declaration": "padding-right: var(--space_sm);" }, - "pr_md": { "declaration": "padding-right: var(--space_md);" }, - "pr_lg": { "declaration": "padding-right: var(--space_lg);" }, - "pr_xl": { "declaration": "padding-right: var(--space_xl);" }, - "pr_xl2": { "declaration": "padding-right: var(--space_xl2);" }, - "pr_xl3": { "declaration": "padding-right: var(--space_xl3);" }, - "pr_xl4": { "declaration": "padding-right: var(--space_xl4);" }, - "pr_xl5": { "declaration": "padding-right: var(--space_xl5);" }, - "pr_xl6": { "declaration": "padding-right: var(--space_xl6);" }, - "pr_xl7": { "declaration": "padding-right: var(--space_xl7);" }, - "pr_xl8": { "declaration": "padding-right: var(--space_xl8);" }, - "pr_xl9": { "declaration": "padding-right: var(--space_xl9);" }, - "pr_xl10": { "declaration": "padding-right: var(--space_xl10);" }, - "pr_xl11": { "declaration": "padding-right: var(--space_xl11);" }, - "pr_xl12": { "declaration": "padding-right: var(--space_xl12);" }, - "pr_xl13": { "declaration": "padding-right: var(--space_xl13);" }, - "pr_xl14": { "declaration": "padding-right: var(--space_xl14);" }, - "pr_xl15": { "declaration": "padding-right: var(--space_xl15);" }, - "pb_0": { "declaration": "padding-bottom: 0;" }, - "pb_xs5": { "declaration": "padding-bottom: var(--space_xs5);" }, - "pb_xs4": { "declaration": "padding-bottom: var(--space_xs4);" }, - "pb_xs3": { "declaration": "padding-bottom: var(--space_xs3);" }, - "pb_xs2": { "declaration": "padding-bottom: var(--space_xs2);" }, - "pb_xs": { "declaration": "padding-bottom: var(--space_xs);" }, - "pb_sm": { "declaration": "padding-bottom: var(--space_sm);" }, - "pb_md": { "declaration": "padding-bottom: var(--space_md);" }, - "pb_lg": { "declaration": "padding-bottom: var(--space_lg);" }, - "pb_xl": { "declaration": "padding-bottom: var(--space_xl);" }, - "pb_xl2": { "declaration": "padding-bottom: var(--space_xl2);" }, - "pb_xl3": { "declaration": "padding-bottom: var(--space_xl3);" }, - "pb_xl4": { "declaration": "padding-bottom: var(--space_xl4);" }, - "pb_xl5": { "declaration": "padding-bottom: var(--space_xl5);" }, - "pb_xl6": { "declaration": "padding-bottom: var(--space_xl6);" }, - "pb_xl7": { "declaration": "padding-bottom: var(--space_xl7);" }, - "pb_xl8": { "declaration": "padding-bottom: var(--space_xl8);" }, - "pb_xl9": { "declaration": "padding-bottom: var(--space_xl9);" }, - "pb_xl10": { "declaration": "padding-bottom: var(--space_xl10);" }, - "pb_xl11": { "declaration": "padding-bottom: var(--space_xl11);" }, - "pb_xl12": { "declaration": "padding-bottom: var(--space_xl12);" }, - "pb_xl13": { "declaration": "padding-bottom: var(--space_xl13);" }, - "pb_xl14": { "declaration": "padding-bottom: var(--space_xl14);" }, - "pb_xl15": { "declaration": "padding-bottom: var(--space_xl15);" }, - "pl_0": { "declaration": "padding-left: 0;" }, - "pl_xs5": { "declaration": "padding-left: var(--space_xs5);" }, - "pl_xs4": { "declaration": "padding-left: var(--space_xs4);" }, - "pl_xs3": { "declaration": "padding-left: var(--space_xs3);" }, - "pl_xs2": { "declaration": "padding-left: var(--space_xs2);" }, - "pl_xs": { "declaration": "padding-left: var(--space_xs);" }, - "pl_sm": { "declaration": "padding-left: var(--space_sm);" }, - "pl_md": { "declaration": "padding-left: var(--space_md);" }, - "pl_lg": { "declaration": "padding-left: var(--space_lg);" }, - "pl_xl": { "declaration": "padding-left: var(--space_xl);" }, - "pl_xl2": { "declaration": "padding-left: var(--space_xl2);" }, - "pl_xl3": { "declaration": "padding-left: var(--space_xl3);" }, - "pl_xl4": { "declaration": "padding-left: var(--space_xl4);" }, - "pl_xl5": { "declaration": "padding-left: var(--space_xl5);" }, - "pl_xl6": { "declaration": "padding-left: var(--space_xl6);" }, - "pl_xl7": { "declaration": "padding-left: var(--space_xl7);" }, - "pl_xl8": { "declaration": "padding-left: var(--space_xl8);" }, - "pl_xl9": { "declaration": "padding-left: var(--space_xl9);" }, - "pl_xl10": { "declaration": "padding-left: var(--space_xl10);" }, - "pl_xl11": { "declaration": "padding-left: var(--space_xl11);" }, - "pl_xl12": { "declaration": "padding-left: var(--space_xl12);" }, - "pl_xl13": { "declaration": "padding-left: var(--space_xl13);" }, - "pl_xl14": { "declaration": "padding-left: var(--space_xl14);" }, - "pl_xl15": { "declaration": "padding-left: var(--space_xl15);" }, - "px_0": { "declaration": "padding-left: 0;\tpadding-right: 0;" }, - "px_xs5": { "declaration": "padding-left: var(--space_xs5);\tpadding-right: var(--space_xs5);" }, - "px_xs4": { "declaration": "padding-left: var(--space_xs4);\tpadding-right: var(--space_xs4);" }, - "px_xs3": { "declaration": "padding-left: var(--space_xs3);\tpadding-right: var(--space_xs3);" }, - "px_xs2": { "declaration": "padding-left: var(--space_xs2);\tpadding-right: var(--space_xs2);" }, - "px_xs": { "declaration": "padding-left: var(--space_xs);\tpadding-right: var(--space_xs);" }, - "px_sm": { "declaration": "padding-left: var(--space_sm);\tpadding-right: var(--space_sm);" }, - "px_md": { "declaration": "padding-left: var(--space_md);\tpadding-right: var(--space_md);" }, - "px_lg": { "declaration": "padding-left: var(--space_lg);\tpadding-right: var(--space_lg);" }, - "px_xl": { "declaration": "padding-left: var(--space_xl);\tpadding-right: var(--space_xl);" }, - "px_xl2": { "declaration": "padding-left: var(--space_xl2);\tpadding-right: var(--space_xl2);" }, - "px_xl3": { "declaration": "padding-left: var(--space_xl3);\tpadding-right: var(--space_xl3);" }, - "px_xl4": { "declaration": "padding-left: var(--space_xl4);\tpadding-right: var(--space_xl4);" }, - "px_xl5": { "declaration": "padding-left: var(--space_xl5);\tpadding-right: var(--space_xl5);" }, - "px_xl6": { "declaration": "padding-left: var(--space_xl6);\tpadding-right: var(--space_xl6);" }, - "px_xl7": { "declaration": "padding-left: var(--space_xl7);\tpadding-right: var(--space_xl7);" }, - "px_xl8": { "declaration": "padding-left: var(--space_xl8);\tpadding-right: var(--space_xl8);" }, - "px_xl9": { "declaration": "padding-left: var(--space_xl9);\tpadding-right: var(--space_xl9);" }, - "px_xl10": { - "declaration": "padding-left: var(--space_xl10);\tpadding-right: var(--space_xl10);" - }, - "px_xl11": { - "declaration": "padding-left: var(--space_xl11);\tpadding-right: var(--space_xl11);" - }, - "px_xl12": { - "declaration": "padding-left: var(--space_xl12);\tpadding-right: var(--space_xl12);" - }, - "px_xl13": { - "declaration": "padding-left: var(--space_xl13);\tpadding-right: var(--space_xl13);" - }, - "px_xl14": { - "declaration": "padding-left: var(--space_xl14);\tpadding-right: var(--space_xl14);" - }, - "px_xl15": { - "declaration": "padding-left: var(--space_xl15);\tpadding-right: var(--space_xl15);" - }, - "py_0": { "declaration": "padding-top: 0;\tpadding-bottom: 0;" }, - "py_xs5": { "declaration": "padding-top: var(--space_xs5);\tpadding-bottom: var(--space_xs5);" }, - "py_xs4": { "declaration": "padding-top: var(--space_xs4);\tpadding-bottom: var(--space_xs4);" }, - "py_xs3": { "declaration": "padding-top: var(--space_xs3);\tpadding-bottom: var(--space_xs3);" }, - "py_xs2": { "declaration": "padding-top: var(--space_xs2);\tpadding-bottom: var(--space_xs2);" }, - "py_xs": { "declaration": "padding-top: var(--space_xs);\tpadding-bottom: var(--space_xs);" }, - "py_sm": { "declaration": "padding-top: var(--space_sm);\tpadding-bottom: var(--space_sm);" }, - "py_md": { "declaration": "padding-top: var(--space_md);\tpadding-bottom: var(--space_md);" }, - "py_lg": { "declaration": "padding-top: var(--space_lg);\tpadding-bottom: var(--space_lg);" }, - "py_xl": { "declaration": "padding-top: var(--space_xl);\tpadding-bottom: var(--space_xl);" }, - "py_xl2": { "declaration": "padding-top: var(--space_xl2);\tpadding-bottom: var(--space_xl2);" }, - "py_xl3": { "declaration": "padding-top: var(--space_xl3);\tpadding-bottom: var(--space_xl3);" }, - "py_xl4": { "declaration": "padding-top: var(--space_xl4);\tpadding-bottom: var(--space_xl4);" }, - "py_xl5": { "declaration": "padding-top: var(--space_xl5);\tpadding-bottom: var(--space_xl5);" }, - "py_xl6": { "declaration": "padding-top: var(--space_xl6);\tpadding-bottom: var(--space_xl6);" }, - "py_xl7": { "declaration": "padding-top: var(--space_xl7);\tpadding-bottom: var(--space_xl7);" }, - "py_xl8": { "declaration": "padding-top: var(--space_xl8);\tpadding-bottom: var(--space_xl8);" }, - "py_xl9": { "declaration": "padding-top: var(--space_xl9);\tpadding-bottom: var(--space_xl9);" }, - "py_xl10": { - "declaration": "padding-top: var(--space_xl10);\tpadding-bottom: var(--space_xl10);" - }, - "py_xl11": { - "declaration": "padding-top: var(--space_xl11);\tpadding-bottom: var(--space_xl11);" - }, - "py_xl12": { - "declaration": "padding-top: var(--space_xl12);\tpadding-bottom: var(--space_xl12);" - }, - "py_xl13": { - "declaration": "padding-top: var(--space_xl13);\tpadding-bottom: var(--space_xl13);" - }, - "py_xl14": { - "declaration": "padding-top: var(--space_xl14);\tpadding-bottom: var(--space_xl14);" - }, - "py_xl15": { - "declaration": "padding-top: var(--space_xl15);\tpadding-bottom: var(--space_xl15);" - }, - "m_0": { "declaration": "margin: 0;" }, - "m_auto": { "declaration": "margin: auto;" }, - "m_xs5": { "declaration": "margin: var(--space_xs5);" }, - "m_xs4": { "declaration": "margin: var(--space_xs4);" }, - "m_xs3": { "declaration": "margin: var(--space_xs3);" }, - "m_xs2": { "declaration": "margin: var(--space_xs2);" }, - "m_xs": { "declaration": "margin: var(--space_xs);" }, - "m_sm": { "declaration": "margin: var(--space_sm);" }, - "m_md": { "declaration": "margin: var(--space_md);" }, - "m_lg": { "declaration": "margin: var(--space_lg);" }, - "m_xl": { "declaration": "margin: var(--space_xl);" }, - "m_xl2": { "declaration": "margin: var(--space_xl2);" }, - "m_xl3": { "declaration": "margin: var(--space_xl3);" }, - "m_xl4": { "declaration": "margin: var(--space_xl4);" }, - "m_xl5": { "declaration": "margin: var(--space_xl5);" }, - "m_xl6": { "declaration": "margin: var(--space_xl6);" }, - "m_xl7": { "declaration": "margin: var(--space_xl7);" }, - "m_xl8": { "declaration": "margin: var(--space_xl8);" }, - "m_xl9": { "declaration": "margin: var(--space_xl9);" }, - "m_xl10": { "declaration": "margin: var(--space_xl10);" }, - "m_xl11": { "declaration": "margin: var(--space_xl11);" }, - "m_xl12": { "declaration": "margin: var(--space_xl12);" }, - "m_xl13": { "declaration": "margin: var(--space_xl13);" }, - "m_xl14": { "declaration": "margin: var(--space_xl14);" }, - "m_xl15": { "declaration": "margin: var(--space_xl15);" }, - "mt_0": { "declaration": "margin-top: 0;" }, - "mt_auto": { "declaration": "margin-top: auto;" }, - "mt_xs5": { "declaration": "margin-top: var(--space_xs5);" }, - "mt_xs4": { "declaration": "margin-top: var(--space_xs4);" }, - "mt_xs3": { "declaration": "margin-top: var(--space_xs3);" }, - "mt_xs2": { "declaration": "margin-top: var(--space_xs2);" }, - "mt_xs": { "declaration": "margin-top: var(--space_xs);" }, - "mt_sm": { "declaration": "margin-top: var(--space_sm);" }, - "mt_md": { "declaration": "margin-top: var(--space_md);" }, - "mt_lg": { "declaration": "margin-top: var(--space_lg);" }, - "mt_xl": { "declaration": "margin-top: var(--space_xl);" }, - "mt_xl2": { "declaration": "margin-top: var(--space_xl2);" }, - "mt_xl3": { "declaration": "margin-top: var(--space_xl3);" }, - "mt_xl4": { "declaration": "margin-top: var(--space_xl4);" }, - "mt_xl5": { "declaration": "margin-top: var(--space_xl5);" }, - "mt_xl6": { "declaration": "margin-top: var(--space_xl6);" }, - "mt_xl7": { "declaration": "margin-top: var(--space_xl7);" }, - "mt_xl8": { "declaration": "margin-top: var(--space_xl8);" }, - "mt_xl9": { "declaration": "margin-top: var(--space_xl9);" }, - "mt_xl10": { "declaration": "margin-top: var(--space_xl10);" }, - "mt_xl11": { "declaration": "margin-top: var(--space_xl11);" }, - "mt_xl12": { "declaration": "margin-top: var(--space_xl12);" }, - "mt_xl13": { "declaration": "margin-top: var(--space_xl13);" }, - "mt_xl14": { "declaration": "margin-top: var(--space_xl14);" }, - "mt_xl15": { "declaration": "margin-top: var(--space_xl15);" }, - "mr_0": { "declaration": "margin-right: 0;" }, - "mr_auto": { "declaration": "margin-right: auto;" }, - "mr_xs5": { "declaration": "margin-right: var(--space_xs5);" }, - "mr_xs4": { "declaration": "margin-right: var(--space_xs4);" }, - "mr_xs3": { "declaration": "margin-right: var(--space_xs3);" }, - "mr_xs2": { "declaration": "margin-right: var(--space_xs2);" }, - "mr_xs": { "declaration": "margin-right: var(--space_xs);" }, - "mr_sm": { "declaration": "margin-right: var(--space_sm);" }, - "mr_md": { "declaration": "margin-right: var(--space_md);" }, - "mr_lg": { "declaration": "margin-right: var(--space_lg);" }, - "mr_xl": { "declaration": "margin-right: var(--space_xl);" }, - "mr_xl2": { "declaration": "margin-right: var(--space_xl2);" }, - "mr_xl3": { "declaration": "margin-right: var(--space_xl3);" }, - "mr_xl4": { "declaration": "margin-right: var(--space_xl4);" }, - "mr_xl5": { "declaration": "margin-right: var(--space_xl5);" }, - "mr_xl6": { "declaration": "margin-right: var(--space_xl6);" }, - "mr_xl7": { "declaration": "margin-right: var(--space_xl7);" }, - "mr_xl8": { "declaration": "margin-right: var(--space_xl8);" }, - "mr_xl9": { "declaration": "margin-right: var(--space_xl9);" }, - "mr_xl10": { "declaration": "margin-right: var(--space_xl10);" }, - "mr_xl11": { "declaration": "margin-right: var(--space_xl11);" }, - "mr_xl12": { "declaration": "margin-right: var(--space_xl12);" }, - "mr_xl13": { "declaration": "margin-right: var(--space_xl13);" }, - "mr_xl14": { "declaration": "margin-right: var(--space_xl14);" }, - "mr_xl15": { "declaration": "margin-right: var(--space_xl15);" }, - "mb_0": { "declaration": "margin-bottom: 0;" }, - "mb_auto": { "declaration": "margin-bottom: auto;" }, - "mb_xs5": { "declaration": "margin-bottom: var(--space_xs5);" }, - "mb_xs4": { "declaration": "margin-bottom: var(--space_xs4);" }, - "mb_xs3": { "declaration": "margin-bottom: var(--space_xs3);" }, - "mb_xs2": { "declaration": "margin-bottom: var(--space_xs2);" }, - "mb_xs": { "declaration": "margin-bottom: var(--space_xs);" }, - "mb_sm": { "declaration": "margin-bottom: var(--space_sm);" }, - "mb_md": { "declaration": "margin-bottom: var(--space_md);" }, - "mb_lg": { "declaration": "margin-bottom: var(--space_lg);" }, - "mb_xl": { "declaration": "margin-bottom: var(--space_xl);" }, - "mb_xl2": { "declaration": "margin-bottom: var(--space_xl2);" }, - "mb_xl3": { "declaration": "margin-bottom: var(--space_xl3);" }, - "mb_xl4": { "declaration": "margin-bottom: var(--space_xl4);" }, - "mb_xl5": { "declaration": "margin-bottom: var(--space_xl5);" }, - "mb_xl6": { "declaration": "margin-bottom: var(--space_xl6);" }, - "mb_xl7": { "declaration": "margin-bottom: var(--space_xl7);" }, - "mb_xl8": { "declaration": "margin-bottom: var(--space_xl8);" }, - "mb_xl9": { "declaration": "margin-bottom: var(--space_xl9);" }, - "mb_xl10": { "declaration": "margin-bottom: var(--space_xl10);" }, - "mb_xl11": { "declaration": "margin-bottom: var(--space_xl11);" }, - "mb_xl12": { "declaration": "margin-bottom: var(--space_xl12);" }, - "mb_xl13": { "declaration": "margin-bottom: var(--space_xl13);" }, - "mb_xl14": { "declaration": "margin-bottom: var(--space_xl14);" }, - "mb_xl15": { "declaration": "margin-bottom: var(--space_xl15);" }, - "ml_0": { "declaration": "margin-left: 0;" }, - "ml_auto": { "declaration": "margin-left: auto;" }, - "ml_xs5": { "declaration": "margin-left: var(--space_xs5);" }, - "ml_xs4": { "declaration": "margin-left: var(--space_xs4);" }, - "ml_xs3": { "declaration": "margin-left: var(--space_xs3);" }, - "ml_xs2": { "declaration": "margin-left: var(--space_xs2);" }, - "ml_xs": { "declaration": "margin-left: var(--space_xs);" }, - "ml_sm": { "declaration": "margin-left: var(--space_sm);" }, - "ml_md": { "declaration": "margin-left: var(--space_md);" }, - "ml_lg": { "declaration": "margin-left: var(--space_lg);" }, - "ml_xl": { "declaration": "margin-left: var(--space_xl);" }, - "ml_xl2": { "declaration": "margin-left: var(--space_xl2);" }, - "ml_xl3": { "declaration": "margin-left: var(--space_xl3);" }, - "ml_xl4": { "declaration": "margin-left: var(--space_xl4);" }, - "ml_xl5": { "declaration": "margin-left: var(--space_xl5);" }, - "ml_xl6": { "declaration": "margin-left: var(--space_xl6);" }, - "ml_xl7": { "declaration": "margin-left: var(--space_xl7);" }, - "ml_xl8": { "declaration": "margin-left: var(--space_xl8);" }, - "ml_xl9": { "declaration": "margin-left: var(--space_xl9);" }, - "ml_xl10": { "declaration": "margin-left: var(--space_xl10);" }, - "ml_xl11": { "declaration": "margin-left: var(--space_xl11);" }, - "ml_xl12": { "declaration": "margin-left: var(--space_xl12);" }, - "ml_xl13": { "declaration": "margin-left: var(--space_xl13);" }, - "ml_xl14": { "declaration": "margin-left: var(--space_xl14);" }, - "ml_xl15": { "declaration": "margin-left: var(--space_xl15);" }, - "mx_0": { "declaration": "margin-left: 0;\tmargin-right: 0;" }, - "mx_auto": { "declaration": "margin-left: auto;\tmargin-right: auto;" }, - "mx_xs5": { "declaration": "margin-left: var(--space_xs5);\tmargin-right: var(--space_xs5);" }, - "mx_xs4": { "declaration": "margin-left: var(--space_xs4);\tmargin-right: var(--space_xs4);" }, - "mx_xs3": { "declaration": "margin-left: var(--space_xs3);\tmargin-right: var(--space_xs3);" }, - "mx_xs2": { "declaration": "margin-left: var(--space_xs2);\tmargin-right: var(--space_xs2);" }, - "mx_xs": { "declaration": "margin-left: var(--space_xs);\tmargin-right: var(--space_xs);" }, - "mx_sm": { "declaration": "margin-left: var(--space_sm);\tmargin-right: var(--space_sm);" }, - "mx_md": { "declaration": "margin-left: var(--space_md);\tmargin-right: var(--space_md);" }, - "mx_lg": { "declaration": "margin-left: var(--space_lg);\tmargin-right: var(--space_lg);" }, - "mx_xl": { "declaration": "margin-left: var(--space_xl);\tmargin-right: var(--space_xl);" }, - "mx_xl2": { "declaration": "margin-left: var(--space_xl2);\tmargin-right: var(--space_xl2);" }, - "mx_xl3": { "declaration": "margin-left: var(--space_xl3);\tmargin-right: var(--space_xl3);" }, - "mx_xl4": { "declaration": "margin-left: var(--space_xl4);\tmargin-right: var(--space_xl4);" }, - "mx_xl5": { "declaration": "margin-left: var(--space_xl5);\tmargin-right: var(--space_xl5);" }, - "mx_xl6": { "declaration": "margin-left: var(--space_xl6);\tmargin-right: var(--space_xl6);" }, - "mx_xl7": { "declaration": "margin-left: var(--space_xl7);\tmargin-right: var(--space_xl7);" }, - "mx_xl8": { "declaration": "margin-left: var(--space_xl8);\tmargin-right: var(--space_xl8);" }, - "mx_xl9": { "declaration": "margin-left: var(--space_xl9);\tmargin-right: var(--space_xl9);" }, - "mx_xl10": { "declaration": "margin-left: var(--space_xl10);\tmargin-right: var(--space_xl10);" }, - "mx_xl11": { "declaration": "margin-left: var(--space_xl11);\tmargin-right: var(--space_xl11);" }, - "mx_xl12": { "declaration": "margin-left: var(--space_xl12);\tmargin-right: var(--space_xl12);" }, - "mx_xl13": { "declaration": "margin-left: var(--space_xl13);\tmargin-right: var(--space_xl13);" }, - "mx_xl14": { "declaration": "margin-left: var(--space_xl14);\tmargin-right: var(--space_xl14);" }, - "mx_xl15": { "declaration": "margin-left: var(--space_xl15);\tmargin-right: var(--space_xl15);" }, - "my_0": { "declaration": "margin-top: 0;\tmargin-bottom: 0;" }, - "my_auto": { "declaration": "margin-top: auto;\tmargin-bottom: auto;" }, - "my_xs5": { "declaration": "margin-top: var(--space_xs5);\tmargin-bottom: var(--space_xs5);" }, - "my_xs4": { "declaration": "margin-top: var(--space_xs4);\tmargin-bottom: var(--space_xs4);" }, - "my_xs3": { "declaration": "margin-top: var(--space_xs3);\tmargin-bottom: var(--space_xs3);" }, - "my_xs2": { "declaration": "margin-top: var(--space_xs2);\tmargin-bottom: var(--space_xs2);" }, - "my_xs": { "declaration": "margin-top: var(--space_xs);\tmargin-bottom: var(--space_xs);" }, - "my_sm": { "declaration": "margin-top: var(--space_sm);\tmargin-bottom: var(--space_sm);" }, - "my_md": { "declaration": "margin-top: var(--space_md);\tmargin-bottom: var(--space_md);" }, - "my_lg": { "declaration": "margin-top: var(--space_lg);\tmargin-bottom: var(--space_lg);" }, - "my_xl": { "declaration": "margin-top: var(--space_xl);\tmargin-bottom: var(--space_xl);" }, - "my_xl2": { "declaration": "margin-top: var(--space_xl2);\tmargin-bottom: var(--space_xl2);" }, - "my_xl3": { "declaration": "margin-top: var(--space_xl3);\tmargin-bottom: var(--space_xl3);" }, - "my_xl4": { "declaration": "margin-top: var(--space_xl4);\tmargin-bottom: var(--space_xl4);" }, - "my_xl5": { "declaration": "margin-top: var(--space_xl5);\tmargin-bottom: var(--space_xl5);" }, - "my_xl6": { "declaration": "margin-top: var(--space_xl6);\tmargin-bottom: var(--space_xl6);" }, - "my_xl7": { "declaration": "margin-top: var(--space_xl7);\tmargin-bottom: var(--space_xl7);" }, - "my_xl8": { "declaration": "margin-top: var(--space_xl8);\tmargin-bottom: var(--space_xl8);" }, - "my_xl9": { "declaration": "margin-top: var(--space_xl9);\tmargin-bottom: var(--space_xl9);" }, - "my_xl10": { "declaration": "margin-top: var(--space_xl10);\tmargin-bottom: var(--space_xl10);" }, - "my_xl11": { "declaration": "margin-top: var(--space_xl11);\tmargin-bottom: var(--space_xl11);" }, - "my_xl12": { "declaration": "margin-top: var(--space_xl12);\tmargin-bottom: var(--space_xl12);" }, - "my_xl13": { "declaration": "margin-top: var(--space_xl13);\tmargin-bottom: var(--space_xl13);" }, - "my_xl14": { "declaration": "margin-top: var(--space_xl14);\tmargin-bottom: var(--space_xl14);" }, - "my_xl15": { "declaration": "margin-top: var(--space_xl15);\tmargin-bottom: var(--space_xl15);" }, - "gap_xs5": { "declaration": "gap: var(--space_xs5);" }, - "gap_xs4": { "declaration": "gap: var(--space_xs4);" }, - "gap_xs3": { "declaration": "gap: var(--space_xs3);" }, - "gap_xs2": { "declaration": "gap: var(--space_xs2);" }, - "gap_xs": { "declaration": "gap: var(--space_xs);" }, - "gap_sm": { "declaration": "gap: var(--space_sm);" }, - "gap_md": { "declaration": "gap: var(--space_md);" }, - "gap_lg": { "declaration": "gap: var(--space_lg);" }, - "gap_xl": { "declaration": "gap: var(--space_xl);" }, - "gap_xl2": { "declaration": "gap: var(--space_xl2);" }, - "gap_xl3": { "declaration": "gap: var(--space_xl3);" }, - "gap_xl4": { "declaration": "gap: var(--space_xl4);" }, - "gap_xl5": { "declaration": "gap: var(--space_xl5);" }, - "gap_xl6": { "declaration": "gap: var(--space_xl6);" }, - "gap_xl7": { "declaration": "gap: var(--space_xl7);" }, - "gap_xl8": { "declaration": "gap: var(--space_xl8);" }, - "gap_xl9": { "declaration": "gap: var(--space_xl9);" }, - "gap_xl10": { "declaration": "gap: var(--space_xl10);" }, - "gap_xl11": { "declaration": "gap: var(--space_xl11);" }, - "gap_xl12": { "declaration": "gap: var(--space_xl12);" }, - "gap_xl13": { "declaration": "gap: var(--space_xl13);" }, - "gap_xl14": { "declaration": "gap: var(--space_xl14);" }, - "gap_xl15": { "declaration": "gap: var(--space_xl15);" }, - "column_gap_xs5": { "declaration": "column-gap: var(--space_xs5);" }, - "column_gap_xs4": { "declaration": "column-gap: var(--space_xs4);" }, - "column_gap_xs3": { "declaration": "column-gap: var(--space_xs3);" }, - "column_gap_xs2": { "declaration": "column-gap: var(--space_xs2);" }, - "column_gap_xs": { "declaration": "column-gap: var(--space_xs);" }, - "column_gap_sm": { "declaration": "column-gap: var(--space_sm);" }, - "column_gap_md": { "declaration": "column-gap: var(--space_md);" }, - "column_gap_lg": { "declaration": "column-gap: var(--space_lg);" }, - "column_gap_xl": { "declaration": "column-gap: var(--space_xl);" }, - "column_gap_xl2": { "declaration": "column-gap: var(--space_xl2);" }, - "column_gap_xl3": { "declaration": "column-gap: var(--space_xl3);" }, - "column_gap_xl4": { "declaration": "column-gap: var(--space_xl4);" }, - "column_gap_xl5": { "declaration": "column-gap: var(--space_xl5);" }, - "column_gap_xl6": { "declaration": "column-gap: var(--space_xl6);" }, - "column_gap_xl7": { "declaration": "column-gap: var(--space_xl7);" }, - "column_gap_xl8": { "declaration": "column-gap: var(--space_xl8);" }, - "column_gap_xl9": { "declaration": "column-gap: var(--space_xl9);" }, - "column_gap_xl10": { "declaration": "column-gap: var(--space_xl10);" }, - "column_gap_xl11": { "declaration": "column-gap: var(--space_xl11);" }, - "column_gap_xl12": { "declaration": "column-gap: var(--space_xl12);" }, - "column_gap_xl13": { "declaration": "column-gap: var(--space_xl13);" }, - "column_gap_xl14": { "declaration": "column-gap: var(--space_xl14);" }, - "column_gap_xl15": { "declaration": "column-gap: var(--space_xl15);" }, - "row_gap_xs5": { "declaration": "row-gap: var(--space_xs5);" }, - "row_gap_xs4": { "declaration": "row-gap: var(--space_xs4);" }, - "row_gap_xs3": { "declaration": "row-gap: var(--space_xs3);" }, - "row_gap_xs2": { "declaration": "row-gap: var(--space_xs2);" }, - "row_gap_xs": { "declaration": "row-gap: var(--space_xs);" }, - "row_gap_sm": { "declaration": "row-gap: var(--space_sm);" }, - "row_gap_md": { "declaration": "row-gap: var(--space_md);" }, - "row_gap_lg": { "declaration": "row-gap: var(--space_lg);" }, - "row_gap_xl": { "declaration": "row-gap: var(--space_xl);" }, - "row_gap_xl2": { "declaration": "row-gap: var(--space_xl2);" }, - "row_gap_xl3": { "declaration": "row-gap: var(--space_xl3);" }, - "row_gap_xl4": { "declaration": "row-gap: var(--space_xl4);" }, - "row_gap_xl5": { "declaration": "row-gap: var(--space_xl5);" }, - "row_gap_xl6": { "declaration": "row-gap: var(--space_xl6);" }, - "row_gap_xl7": { "declaration": "row-gap: var(--space_xl7);" }, - "row_gap_xl8": { "declaration": "row-gap: var(--space_xl8);" }, - "row_gap_xl9": { "declaration": "row-gap: var(--space_xl9);" }, - "row_gap_xl10": { "declaration": "row-gap: var(--space_xl10);" }, - "row_gap_xl11": { "declaration": "row-gap: var(--space_xl11);" }, - "row_gap_xl12": { "declaration": "row-gap: var(--space_xl12);" }, - "row_gap_xl13": { "declaration": "row-gap: var(--space_xl13);" }, - "row_gap_xl14": { "declaration": "row-gap: var(--space_xl14);" }, - "row_gap_xl15": { "declaration": "row-gap: var(--space_xl15);" } -} +{"pixelated":{"declaration":"\n\t\t\timage-rendering: -webkit-optimize-contrast; /* Safari */\n\t\t\timage-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */\n\t\t\timage-rendering: pixelated; /* in case crisp-edges isn't supported */\n\t\t\timage-rendering: crisp-edges; /* the recommended pixel art setting according to MDN */\n\t\t"},"circular":{"declaration":"border-radius: 50%;"},"box":{"declaration":"\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t"},"column":{"comment":"like `.box` but uncentered","declaration":"\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t"},"row":{"comment":"can be used to override the direction of a `.box`","declaration":"\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: row;\n\t\t\talign-items: center;\n\t\t"},"ellipsis":{"declaration":"\n\t\t\tdisplay: block;\n\t\t\twhite-space: nowrap;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t"},"selectable":{"ruleset":"\n\t\t\t.selectable {\n\t\t\t\t--button_fill: color-mix(in oklab, var(--shade_50) 8%, transparent);\n\t\t\t\t--button_fill_hover: color-mix(in oklab, var(--shade_50) 16%, transparent);\n\t\t\t\t--button_fill_active: color-mix(in oklab, var(--shade_50) 24%, transparent);\n\t\t\t\tcursor: pointer;\n\t\t\t\tbackground-color: var(--button_fill);\n\t\t\t\tborder-color: var(--border_color_30);\n\t\t\t\tborder-style: var(--border_style);\n\t\t\t\tborder-width: var(--border_width);\n\t\t\t}\n\t\t\t.selectable:hover {\n\t\t\t\tbackground-color: var(--button_fill_hover);\n\t\t\t\tborder-color: var(--border_color_20);\n\t\t\t}\n\t\t\t.selectable.selected,\n\t\t\t.selectable:active {\n\t\t\t\tbackground-color: var(--button_fill_active);\n\t\t\t\tborder-color: var(--accent_50);\n\t\t\t}\n\t\t\t.selectable.selected {\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t\t.selectable.selected.deselectable:not(:disabled) {\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\t\t"},"clickable":{"ruleset":"\n\t\t\t.clickable {\n\t\t\t\tcursor: pointer;\n\t\t\t\ttransform: var(--clickable_transform, scale3d(1, 1, 1));\n\t\t\t\ttransform-origin: var(--clickable_transform_origin);\n\t\t\t\ttransition-duration: var(--clickable_transition_duration); /* default to instant, chunky/lofi */\n\t\t\t}\n\t\t\t.clickable:focus {\n\t\t\t\ttransform: var(--clickable_transform_focus, scale3d(1.07, 1.07, 1.07));\n\t\t\t}\n\t\t\t.clickable:hover {\n\t\t\t\ttransform: var(--clickable_transform_hover, scale3d(1.1, 1.1, 1.1));\n\t\t\t}\n\t\t\t.clickable:active,\n\t\t\t.clickable.active {\n\t\t\t\ttransform: var(--clickable_transform_active, scale3d(1.2, 1.2, 1.2));\n\t\t\t}\n\t\t"},"pane":{"comment":"A pane is a box floating over the page, like for dialogs. By default it's opaque, resetting the background to the initial depth.","declaration":"\n\t\t\tbackground-color: var(--shade_00);\n\t\t\tbox-shadow: var(--pane_shadow, var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_50), transparent));\n\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t"},"panel":{"comment":"A panel is a box embedded into the page, useful for visually isolating content.","declaration":"\n\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t\tbackground-color: var(--fg_10);\n\t\t"},"xs":{"comment":"Smallest sizing, two steps down from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_xs);\n\t\t\t--input_height: var(--space_xl3);\n\t\t\t--input_height_compact: var(--space_xl2);\n\t\t\t--input_padding_x: var(--space_sm);\n\t\t\t--chip_padding_x: var(--space_xs3);\n\t\t\t--icon_size: var(--icon_size_xs);\n\t\t\t--menuitem_padding: var(--space_xs5) var(--space_xs3);\n\t\t\t--flow_margin: var(--space_sm);\n\t\t"},"sm":{"comment":"Smaller sizing, one step down from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_sm);\n\t\t\t--input_height: var(--space_xl4);\n\t\t\t--input_height_compact: var(--space_xl3);\n\t\t\t--input_padding_x: var(--space_md);\n\t\t\t--chip_padding_x: var(--space_xs2);\n\t\t\t--icon_size: var(--icon_size_sm);\n\t\t\t--menuitem_padding: var(--space_xs4) var(--space_xs2);\n\t\t\t--flow_margin: var(--space_md);\n\t\t"},"md":{"comment":"Default sizing restated explicitly, useful as a cascade reset within a sized parent.","declaration":"\n\t\t\t--font_size: var(--font_size_md);\n\t\t\t--input_height: var(--space_xl5);\n\t\t\t--input_height_compact: var(--space_xl4);\n\t\t\t--input_padding_x: var(--space_lg);\n\t\t\t--chip_padding_x: var(--space_xs);\n\t\t\t--icon_size: var(--icon_size_md);\n\t\t\t--menuitem_padding: var(--space_xs3) var(--space_xs);\n\t\t\t--flow_margin: var(--space_lg);\n\t\t"},"lg":{"comment":"Larger sizing, one step up from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_lg);\n\t\t\t--input_height: var(--space_xl6);\n\t\t\t--input_height_compact: var(--space_xl5);\n\t\t\t--input_padding_x: var(--space_xl);\n\t\t\t--chip_padding_x: var(--space_sm);\n\t\t\t--icon_size: var(--icon_size_lg);\n\t\t\t--menuitem_padding: var(--space_xs2) var(--space_sm);\n\t\t\t--flow_margin: var(--space_xl);\n\t\t"},"xl":{"comment":"Largest sizing, two steps up from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_xl);\n\t\t\t--input_height: var(--space_xl7);\n\t\t\t--input_height_compact: var(--space_xl6);\n\t\t\t--input_padding_x: var(--space_xl2);\n\t\t\t--chip_padding_x: var(--space_md);\n\t\t\t--icon_size: var(--icon_size_xl);\n\t\t\t--menuitem_padding: var(--space_xs) var(--space_md);\n\t\t\t--flow_margin: var(--space_xl2);\n\t\t"},"mb_flow":{"comment":"Flow-aware margin-bottom that responds to --flow_margin overrides from size composites.","declaration":"margin-bottom: var(--flow_margin, var(--space_lg));"},"mt_flow":{"comment":"Flow-aware margin-top that responds to --flow_margin overrides from size composites.","declaration":"margin-top: var(--flow_margin, var(--space_lg));"},"icon_button":{"comment":"\n\t\t\tTODO other button variants?\n\t\t\tTODO this is slightly strange that it doesn't use --icon_size.\n\t\t\tThese are used as modifiers to buttons.\n\t\t","declaration":"\n\t\t\twidth: var(--input_height);\n\t\t\theight: var(--input_height);\n\t\t\tmin-width: var(--input_height);\n\t\t\tmin-height: var(--input_height);\n\t\t\tflex-shrink: 0;\n\t\t\tline-height: 1;\n\t\t\tfont-weight: 900;\n\t\t\tpadding: 0;\n\t\t"},"plain":{"comment":"TODO maybe this belongs with the reset, like `selected`? or does `selected` belong here?","ruleset":"\n\t\t\t.plain:not(:hover) {\n\t\t\t\t--border_color: transparent;\n\t\t\t\tbox-shadow: none;\n\t\t\t\t--button_fill: transparent;\n\t\t\t}\n\t\t\t.plain:hover, .plain:active {\n\t\t\t\t--border_color: transparent;\n\t\t\t}\n\t\t"},"menuitem":{"ruleset":"\n\t\t\t.menuitem {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tborder-radius: 0;\n\t\t\t\tposition: relative;\n\t\t\t\tcursor: pointer;\n\t\t\t\twidth: 100%;\n\t\t\t\tmin-height: var(--menuitem_min_height, var(--input_height_compact));\n\t\t\t\tdisplay: flex;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\talign-items: center;\n\t\t\t\tpadding: var(--menuitem_padding, var(--space_xs3) var(--space_xs));\n\t\t\t}\n\t\t\t.menuitem.selected {\n\t\t\t\t/* TODO different patterns for border and surface? */\n\t\t\t\t--border_color: var(--accent_50);\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t\t.menuitem.selected.deselectable:not(:disabled) {\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\t\t\t.menuitem:hover {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t}\n\t\t\t.menuitem:active,\n\t\t\t.menuitem.selected:hover {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tbackground-color: var(--fg_20);\n\t\t\t}\n\t\t\t.menuitem.plain {\n\t\t\t\tborder: none;\n\t\t\t}\n\t\t\t.menuitem .content {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tflex: 1;\n\t\t\t\t/* allows the flex children to shrink */\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\t\t\t.menuitem .icon {\n\t\t\t\twidth: var(--icon_size, var(--icon_size_md));\n\t\t\t\tmargin-right: var(--space_sm);\n\t\t\t\tflex-shrink: 0;\n\t\t\t\ttext-align: center;\n\t\t\t\tfont-weight: 900;\n\t\t\t}\n\t\t\t.menuitem .title {\n\t\t\t\tmargin-right: var(--space_lg);\n\t\t\t\tflex-shrink: 1;\n\t\t\t\toverflow: hidden;\n\t\t\t\twhite-space: nowrap;\n\t\t\t\ttext-overflow: ellipsis;\n\t\t\t\tline-height: var(--line_height_lg); /* prevents the bottom of g's and others from being cut off */\n\t\t\t}\n\t\t"},"chevron":{"ruleset":"\n\t\t\t.chevron {\n\t\t\t\tposition: relative;\n\t\t\t\theight: 8px;\n\t\t\t}\n\t\t\t.chevron::before {\n\t\t\t\tdisplay: block;\n\t\t\t\tcontent: '';\n\t\t\t\tborder: 4px solid transparent;\n\t\t\t\tborder-left-color: var(--text_70);\n\t\t\t}\n\t\t"},"chip":{"ruleset":"\n\t\t\t.chip {\n\t\t\t\tfont-weight: 500;\n\t\t\t\tfont-size: var(--font_size, inherit);\n\t\t\t\tpadding-left: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tpadding-right: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t\t}\n\t\t\ta.chip {\n\t\t\t\tfont-weight: 600;\n\t\t\t}\n\t\t\t.chip.palette_a {\n\t\t\t\tcolor: var(--palette_a_50);\n\t\t\t\tbackground-color: var(--palette_a_10);\n\t\t\t}\n\t\t\t.chip.palette_b {\n\t\t\t\tcolor: var(--palette_b_50);\n\t\t\t\tbackground-color: var(--palette_b_10);\n\t\t\t}\n\t\t\t.chip.palette_c {\n\t\t\t\tcolor: var(--palette_c_50);\n\t\t\t\tbackground-color: var(--palette_c_10);\n\t\t\t}\n\t\t\t.chip.palette_d {\n\t\t\t\tcolor: var(--palette_d_50);\n\t\t\t\tbackground-color: var(--palette_d_10);\n\t\t\t}\n\t\t\t.chip.palette_e {\n\t\t\t\tcolor: var(--palette_e_50);\n\t\t\t\tbackground-color: var(--palette_e_10);\n\t\t\t}\n\t\t\t.chip.palette_f {\n\t\t\t\tcolor: var(--palette_f_50);\n\t\t\t\tbackground-color: var(--palette_f_10);\n\t\t\t}\n\t\t\t.chip.palette_g {\n\t\t\t\tcolor: var(--palette_g_50);\n\t\t\t\tbackground-color: var(--palette_g_10);\n\t\t\t}\n\t\t\t.chip.palette_h {\n\t\t\t\tcolor: var(--palette_h_50);\n\t\t\t\tbackground-color: var(--palette_h_10);\n\t\t\t}\n\t\t\t.chip.palette_i {\n\t\t\t\tcolor: var(--palette_i_50);\n\t\t\t\tbackground-color: var(--palette_i_10);\n\t\t\t}\n\t\t\t.chip.palette_j {\n\t\t\t\tcolor: var(--palette_j_50);\n\t\t\t\tbackground-color: var(--palette_j_10);\n\t\t\t}\n\t\t"},"font_family_sans":{"declaration":"font-family: var(--font_family_sans);"},"font_family_serif":{"declaration":"font-family: var(--font_family_serif);"},"font_family_mono":{"declaration":"font-family: var(--font_family_mono);"},"line_height_xs":{"declaration":"line-height: var(--line_height_xs);"},"line_height_sm":{"declaration":"line-height: var(--line_height_sm);"},"line_height_md":{"declaration":"line-height: var(--line_height_md);"},"line_height_lg":{"declaration":"line-height: var(--line_height_lg);"},"line_height_xl":{"declaration":"line-height: var(--line_height_xl);"},"font_size_xs":{"declaration":"font-size: var(--font_size_xs); --font_size: var(--font_size_xs);"},"font_size_sm":{"declaration":"font-size: var(--font_size_sm); --font_size: var(--font_size_sm);"},"font_size_md":{"declaration":"font-size: var(--font_size_md); --font_size: var(--font_size_md);"},"font_size_lg":{"declaration":"font-size: var(--font_size_lg); --font_size: var(--font_size_lg);"},"font_size_xl":{"declaration":"font-size: var(--font_size_xl); --font_size: var(--font_size_xl);"},"font_size_xl2":{"declaration":"font-size: var(--font_size_xl2); --font_size: var(--font_size_xl2);"},"font_size_xl3":{"declaration":"font-size: var(--font_size_xl3); --font_size: var(--font_size_xl3);"},"font_size_xl4":{"declaration":"font-size: var(--font_size_xl4); --font_size: var(--font_size_xl4);"},"font_size_xl5":{"declaration":"font-size: var(--font_size_xl5); --font_size: var(--font_size_xl5);"},"font_size_xl6":{"declaration":"font-size: var(--font_size_xl6); --font_size: var(--font_size_xl6);"},"font_size_xl7":{"declaration":"font-size: var(--font_size_xl7); --font_size: var(--font_size_xl7);"},"font_size_xl8":{"declaration":"font-size: var(--font_size_xl8); --font_size: var(--font_size_xl8);"},"font_size_xl9":{"declaration":"font-size: var(--font_size_xl9); --font_size: var(--font_size_xl9);"},"icon_size_xs":{"declaration":"font-size: var(--icon_size_xs); --font_size: var(--icon_size_xs);"},"icon_size_sm":{"declaration":"font-size: var(--icon_size_sm); --font_size: var(--icon_size_sm);"},"icon_size_md":{"declaration":"font-size: var(--icon_size_md); --font_size: var(--icon_size_md);"},"icon_size_lg":{"declaration":"font-size: var(--icon_size_lg); --font_size: var(--icon_size_lg);"},"icon_size_xl":{"declaration":"font-size: var(--icon_size_xl); --font_size: var(--icon_size_xl);"},"icon_size_xl2":{"declaration":"font-size: var(--icon_size_xl2); --font_size: var(--icon_size_xl2);"},"icon_size_xl3":{"declaration":"font-size: var(--icon_size_xl3); --font_size: var(--icon_size_xl3);"},"text_min":{"declaration":"color: var(--text_min); --text_color: var(--text_min);"},"text_00":{"declaration":"color: var(--text_00); --text_color: var(--text_00);"},"text_05":{"declaration":"color: var(--text_05); --text_color: var(--text_05);"},"text_10":{"declaration":"color: var(--text_10); --text_color: var(--text_10);"},"text_20":{"declaration":"color: var(--text_20); --text_color: var(--text_20);"},"text_30":{"declaration":"color: var(--text_30); --text_color: var(--text_30);"},"text_40":{"declaration":"color: var(--text_40); --text_color: var(--text_40);"},"text_50":{"declaration":"color: var(--text_50); --text_color: var(--text_50);"},"text_60":{"declaration":"color: var(--text_60); --text_color: var(--text_60);"},"text_70":{"declaration":"color: var(--text_70); --text_color: var(--text_70);"},"text_80":{"declaration":"color: var(--text_80); --text_color: var(--text_80);"},"text_90":{"declaration":"color: var(--text_90); --text_color: var(--text_90);"},"text_95":{"declaration":"color: var(--text_95); --text_color: var(--text_95);"},"text_100":{"declaration":"color: var(--text_100); --text_color: var(--text_100);"},"text_max":{"declaration":"color: var(--text_max); --text_color: var(--text_max);"},"shade_min":{"declaration":"background-color: var(--shade_min);"},"shade_00":{"declaration":"background-color: var(--shade_00);"},"shade_05":{"declaration":"background-color: var(--shade_05);"},"shade_10":{"declaration":"background-color: var(--shade_10);"},"shade_20":{"declaration":"background-color: var(--shade_20);"},"shade_30":{"declaration":"background-color: var(--shade_30);"},"shade_40":{"declaration":"background-color: var(--shade_40);"},"shade_50":{"declaration":"background-color: var(--shade_50);"},"shade_60":{"declaration":"background-color: var(--shade_60);"},"shade_70":{"declaration":"background-color: var(--shade_70);"},"shade_80":{"declaration":"background-color: var(--shade_80);"},"shade_90":{"declaration":"background-color: var(--shade_90);"},"shade_95":{"declaration":"background-color: var(--shade_95);"},"shade_100":{"declaration":"background-color: var(--shade_100);"},"shade_max":{"declaration":"background-color: var(--shade_max);"},"hue_a":{"declaration":"--hue: var(--hue_a);"},"hue_b":{"declaration":"--hue: var(--hue_b);"},"hue_c":{"declaration":"--hue: var(--hue_c);"},"hue_d":{"declaration":"--hue: var(--hue_d);"},"hue_e":{"declaration":"--hue: var(--hue_e);"},"hue_f":{"declaration":"--hue: var(--hue_f);"},"hue_g":{"declaration":"--hue: var(--hue_g);"},"hue_h":{"declaration":"--hue: var(--hue_h);"},"hue_i":{"declaration":"--hue: var(--hue_i);"},"hue_j":{"declaration":"--hue: var(--hue_j);"},"palette_a_00":{"declaration":"color: var(--palette_a_00); --text_color: var(--palette_a_00);"},"palette_a_05":{"declaration":"color: var(--palette_a_05); --text_color: var(--palette_a_05);"},"palette_a_10":{"declaration":"color: var(--palette_a_10); --text_color: var(--palette_a_10);"},"palette_a_20":{"declaration":"color: var(--palette_a_20); --text_color: var(--palette_a_20);"},"palette_a_30":{"declaration":"color: var(--palette_a_30); --text_color: var(--palette_a_30);"},"palette_a_40":{"declaration":"color: var(--palette_a_40); --text_color: var(--palette_a_40);"},"palette_a_50":{"declaration":"color: var(--palette_a_50); --text_color: var(--palette_a_50);"},"palette_a_60":{"declaration":"color: var(--palette_a_60); --text_color: var(--palette_a_60);"},"palette_a_70":{"declaration":"color: var(--palette_a_70); --text_color: var(--palette_a_70);"},"palette_a_80":{"declaration":"color: var(--palette_a_80); --text_color: var(--palette_a_80);"},"palette_a_90":{"declaration":"color: var(--palette_a_90); --text_color: var(--palette_a_90);"},"palette_a_95":{"declaration":"color: var(--palette_a_95); --text_color: var(--palette_a_95);"},"palette_a_100":{"declaration":"color: var(--palette_a_100); --text_color: var(--palette_a_100);"},"palette_b_00":{"declaration":"color: var(--palette_b_00); --text_color: var(--palette_b_00);"},"palette_b_05":{"declaration":"color: var(--palette_b_05); --text_color: var(--palette_b_05);"},"palette_b_10":{"declaration":"color: var(--palette_b_10); --text_color: var(--palette_b_10);"},"palette_b_20":{"declaration":"color: var(--palette_b_20); --text_color: var(--palette_b_20);"},"palette_b_30":{"declaration":"color: var(--palette_b_30); --text_color: var(--palette_b_30);"},"palette_b_40":{"declaration":"color: var(--palette_b_40); --text_color: var(--palette_b_40);"},"palette_b_50":{"declaration":"color: var(--palette_b_50); --text_color: var(--palette_b_50);"},"palette_b_60":{"declaration":"color: var(--palette_b_60); --text_color: var(--palette_b_60);"},"palette_b_70":{"declaration":"color: var(--palette_b_70); --text_color: var(--palette_b_70);"},"palette_b_80":{"declaration":"color: var(--palette_b_80); --text_color: var(--palette_b_80);"},"palette_b_90":{"declaration":"color: var(--palette_b_90); --text_color: var(--palette_b_90);"},"palette_b_95":{"declaration":"color: var(--palette_b_95); --text_color: var(--palette_b_95);"},"palette_b_100":{"declaration":"color: var(--palette_b_100); --text_color: var(--palette_b_100);"},"palette_c_00":{"declaration":"color: var(--palette_c_00); --text_color: var(--palette_c_00);"},"palette_c_05":{"declaration":"color: var(--palette_c_05); --text_color: var(--palette_c_05);"},"palette_c_10":{"declaration":"color: var(--palette_c_10); --text_color: var(--palette_c_10);"},"palette_c_20":{"declaration":"color: var(--palette_c_20); --text_color: var(--palette_c_20);"},"palette_c_30":{"declaration":"color: var(--palette_c_30); --text_color: var(--palette_c_30);"},"palette_c_40":{"declaration":"color: var(--palette_c_40); --text_color: var(--palette_c_40);"},"palette_c_50":{"declaration":"color: var(--palette_c_50); --text_color: var(--palette_c_50);"},"palette_c_60":{"declaration":"color: var(--palette_c_60); --text_color: var(--palette_c_60);"},"palette_c_70":{"declaration":"color: var(--palette_c_70); --text_color: var(--palette_c_70);"},"palette_c_80":{"declaration":"color: var(--palette_c_80); --text_color: var(--palette_c_80);"},"palette_c_90":{"declaration":"color: var(--palette_c_90); --text_color: var(--palette_c_90);"},"palette_c_95":{"declaration":"color: var(--palette_c_95); --text_color: var(--palette_c_95);"},"palette_c_100":{"declaration":"color: var(--palette_c_100); --text_color: var(--palette_c_100);"},"palette_d_00":{"declaration":"color: var(--palette_d_00); --text_color: var(--palette_d_00);"},"palette_d_05":{"declaration":"color: var(--palette_d_05); --text_color: var(--palette_d_05);"},"palette_d_10":{"declaration":"color: var(--palette_d_10); --text_color: var(--palette_d_10);"},"palette_d_20":{"declaration":"color: var(--palette_d_20); --text_color: var(--palette_d_20);"},"palette_d_30":{"declaration":"color: var(--palette_d_30); --text_color: var(--palette_d_30);"},"palette_d_40":{"declaration":"color: var(--palette_d_40); --text_color: var(--palette_d_40);"},"palette_d_50":{"declaration":"color: var(--palette_d_50); --text_color: var(--palette_d_50);"},"palette_d_60":{"declaration":"color: var(--palette_d_60); --text_color: var(--palette_d_60);"},"palette_d_70":{"declaration":"color: var(--palette_d_70); --text_color: var(--palette_d_70);"},"palette_d_80":{"declaration":"color: var(--palette_d_80); --text_color: var(--palette_d_80);"},"palette_d_90":{"declaration":"color: var(--palette_d_90); --text_color: var(--palette_d_90);"},"palette_d_95":{"declaration":"color: var(--palette_d_95); --text_color: var(--palette_d_95);"},"palette_d_100":{"declaration":"color: var(--palette_d_100); --text_color: var(--palette_d_100);"},"palette_e_00":{"declaration":"color: var(--palette_e_00); --text_color: var(--palette_e_00);"},"palette_e_05":{"declaration":"color: var(--palette_e_05); --text_color: var(--palette_e_05);"},"palette_e_10":{"declaration":"color: var(--palette_e_10); --text_color: var(--palette_e_10);"},"palette_e_20":{"declaration":"color: var(--palette_e_20); --text_color: var(--palette_e_20);"},"palette_e_30":{"declaration":"color: var(--palette_e_30); --text_color: var(--palette_e_30);"},"palette_e_40":{"declaration":"color: var(--palette_e_40); --text_color: var(--palette_e_40);"},"palette_e_50":{"declaration":"color: var(--palette_e_50); --text_color: var(--palette_e_50);"},"palette_e_60":{"declaration":"color: var(--palette_e_60); --text_color: var(--palette_e_60);"},"palette_e_70":{"declaration":"color: var(--palette_e_70); --text_color: var(--palette_e_70);"},"palette_e_80":{"declaration":"color: var(--palette_e_80); --text_color: var(--palette_e_80);"},"palette_e_90":{"declaration":"color: var(--palette_e_90); --text_color: var(--palette_e_90);"},"palette_e_95":{"declaration":"color: var(--palette_e_95); --text_color: var(--palette_e_95);"},"palette_e_100":{"declaration":"color: var(--palette_e_100); --text_color: var(--palette_e_100);"},"palette_f_00":{"declaration":"color: var(--palette_f_00); --text_color: var(--palette_f_00);"},"palette_f_05":{"declaration":"color: var(--palette_f_05); --text_color: var(--palette_f_05);"},"palette_f_10":{"declaration":"color: var(--palette_f_10); --text_color: var(--palette_f_10);"},"palette_f_20":{"declaration":"color: var(--palette_f_20); --text_color: var(--palette_f_20);"},"palette_f_30":{"declaration":"color: var(--palette_f_30); --text_color: var(--palette_f_30);"},"palette_f_40":{"declaration":"color: var(--palette_f_40); --text_color: var(--palette_f_40);"},"palette_f_50":{"declaration":"color: var(--palette_f_50); --text_color: var(--palette_f_50);"},"palette_f_60":{"declaration":"color: var(--palette_f_60); --text_color: var(--palette_f_60);"},"palette_f_70":{"declaration":"color: var(--palette_f_70); --text_color: var(--palette_f_70);"},"palette_f_80":{"declaration":"color: var(--palette_f_80); --text_color: var(--palette_f_80);"},"palette_f_90":{"declaration":"color: var(--palette_f_90); --text_color: var(--palette_f_90);"},"palette_f_95":{"declaration":"color: var(--palette_f_95); --text_color: var(--palette_f_95);"},"palette_f_100":{"declaration":"color: var(--palette_f_100); --text_color: var(--palette_f_100);"},"palette_g_00":{"declaration":"color: var(--palette_g_00); --text_color: var(--palette_g_00);"},"palette_g_05":{"declaration":"color: var(--palette_g_05); --text_color: var(--palette_g_05);"},"palette_g_10":{"declaration":"color: var(--palette_g_10); --text_color: var(--palette_g_10);"},"palette_g_20":{"declaration":"color: var(--palette_g_20); --text_color: var(--palette_g_20);"},"palette_g_30":{"declaration":"color: var(--palette_g_30); --text_color: var(--palette_g_30);"},"palette_g_40":{"declaration":"color: var(--palette_g_40); --text_color: var(--palette_g_40);"},"palette_g_50":{"declaration":"color: var(--palette_g_50); --text_color: var(--palette_g_50);"},"palette_g_60":{"declaration":"color: var(--palette_g_60); --text_color: var(--palette_g_60);"},"palette_g_70":{"declaration":"color: var(--palette_g_70); --text_color: var(--palette_g_70);"},"palette_g_80":{"declaration":"color: var(--palette_g_80); --text_color: var(--palette_g_80);"},"palette_g_90":{"declaration":"color: var(--palette_g_90); --text_color: var(--palette_g_90);"},"palette_g_95":{"declaration":"color: var(--palette_g_95); --text_color: var(--palette_g_95);"},"palette_g_100":{"declaration":"color: var(--palette_g_100); --text_color: var(--palette_g_100);"},"palette_h_00":{"declaration":"color: var(--palette_h_00); --text_color: var(--palette_h_00);"},"palette_h_05":{"declaration":"color: var(--palette_h_05); --text_color: var(--palette_h_05);"},"palette_h_10":{"declaration":"color: var(--palette_h_10); --text_color: var(--palette_h_10);"},"palette_h_20":{"declaration":"color: var(--palette_h_20); --text_color: var(--palette_h_20);"},"palette_h_30":{"declaration":"color: var(--palette_h_30); --text_color: var(--palette_h_30);"},"palette_h_40":{"declaration":"color: var(--palette_h_40); --text_color: var(--palette_h_40);"},"palette_h_50":{"declaration":"color: var(--palette_h_50); --text_color: var(--palette_h_50);"},"palette_h_60":{"declaration":"color: var(--palette_h_60); --text_color: var(--palette_h_60);"},"palette_h_70":{"declaration":"color: var(--palette_h_70); --text_color: var(--palette_h_70);"},"palette_h_80":{"declaration":"color: var(--palette_h_80); --text_color: var(--palette_h_80);"},"palette_h_90":{"declaration":"color: var(--palette_h_90); --text_color: var(--palette_h_90);"},"palette_h_95":{"declaration":"color: var(--palette_h_95); --text_color: var(--palette_h_95);"},"palette_h_100":{"declaration":"color: var(--palette_h_100); --text_color: var(--palette_h_100);"},"palette_i_00":{"declaration":"color: var(--palette_i_00); --text_color: var(--palette_i_00);"},"palette_i_05":{"declaration":"color: var(--palette_i_05); --text_color: var(--palette_i_05);"},"palette_i_10":{"declaration":"color: var(--palette_i_10); --text_color: var(--palette_i_10);"},"palette_i_20":{"declaration":"color: var(--palette_i_20); --text_color: var(--palette_i_20);"},"palette_i_30":{"declaration":"color: var(--palette_i_30); --text_color: var(--palette_i_30);"},"palette_i_40":{"declaration":"color: var(--palette_i_40); --text_color: var(--palette_i_40);"},"palette_i_50":{"declaration":"color: var(--palette_i_50); --text_color: var(--palette_i_50);"},"palette_i_60":{"declaration":"color: var(--palette_i_60); --text_color: var(--palette_i_60);"},"palette_i_70":{"declaration":"color: var(--palette_i_70); --text_color: var(--palette_i_70);"},"palette_i_80":{"declaration":"color: var(--palette_i_80); --text_color: var(--palette_i_80);"},"palette_i_90":{"declaration":"color: var(--palette_i_90); --text_color: var(--palette_i_90);"},"palette_i_95":{"declaration":"color: var(--palette_i_95); --text_color: var(--palette_i_95);"},"palette_i_100":{"declaration":"color: var(--palette_i_100); --text_color: var(--palette_i_100);"},"palette_j_00":{"declaration":"color: var(--palette_j_00); --text_color: var(--palette_j_00);"},"palette_j_05":{"declaration":"color: var(--palette_j_05); --text_color: var(--palette_j_05);"},"palette_j_10":{"declaration":"color: var(--palette_j_10); --text_color: var(--palette_j_10);"},"palette_j_20":{"declaration":"color: var(--palette_j_20); --text_color: var(--palette_j_20);"},"palette_j_30":{"declaration":"color: var(--palette_j_30); --text_color: var(--palette_j_30);"},"palette_j_40":{"declaration":"color: var(--palette_j_40); --text_color: var(--palette_j_40);"},"palette_j_50":{"declaration":"color: var(--palette_j_50); --text_color: var(--palette_j_50);"},"palette_j_60":{"declaration":"color: var(--palette_j_60); --text_color: var(--palette_j_60);"},"palette_j_70":{"declaration":"color: var(--palette_j_70); --text_color: var(--palette_j_70);"},"palette_j_80":{"declaration":"color: var(--palette_j_80); --text_color: var(--palette_j_80);"},"palette_j_90":{"declaration":"color: var(--palette_j_90); --text_color: var(--palette_j_90);"},"palette_j_95":{"declaration":"color: var(--palette_j_95); --text_color: var(--palette_j_95);"},"palette_j_100":{"declaration":"color: var(--palette_j_100); --text_color: var(--palette_j_100);"},"bg_a_00":{"declaration":"background-color: var(--palette_a_00);"},"bg_a_05":{"declaration":"background-color: var(--palette_a_05);"},"bg_a_10":{"declaration":"background-color: var(--palette_a_10);"},"bg_a_20":{"declaration":"background-color: var(--palette_a_20);"},"bg_a_30":{"declaration":"background-color: var(--palette_a_30);"},"bg_a_40":{"declaration":"background-color: var(--palette_a_40);"},"bg_a_50":{"declaration":"background-color: var(--palette_a_50);"},"bg_a_60":{"declaration":"background-color: var(--palette_a_60);"},"bg_a_70":{"declaration":"background-color: var(--palette_a_70);"},"bg_a_80":{"declaration":"background-color: var(--palette_a_80);"},"bg_a_90":{"declaration":"background-color: var(--palette_a_90);"},"bg_a_95":{"declaration":"background-color: var(--palette_a_95);"},"bg_a_100":{"declaration":"background-color: var(--palette_a_100);"},"bg_b_00":{"declaration":"background-color: var(--palette_b_00);"},"bg_b_05":{"declaration":"background-color: var(--palette_b_05);"},"bg_b_10":{"declaration":"background-color: var(--palette_b_10);"},"bg_b_20":{"declaration":"background-color: var(--palette_b_20);"},"bg_b_30":{"declaration":"background-color: var(--palette_b_30);"},"bg_b_40":{"declaration":"background-color: var(--palette_b_40);"},"bg_b_50":{"declaration":"background-color: var(--palette_b_50);"},"bg_b_60":{"declaration":"background-color: var(--palette_b_60);"},"bg_b_70":{"declaration":"background-color: var(--palette_b_70);"},"bg_b_80":{"declaration":"background-color: var(--palette_b_80);"},"bg_b_90":{"declaration":"background-color: var(--palette_b_90);"},"bg_b_95":{"declaration":"background-color: var(--palette_b_95);"},"bg_b_100":{"declaration":"background-color: var(--palette_b_100);"},"bg_c_00":{"declaration":"background-color: var(--palette_c_00);"},"bg_c_05":{"declaration":"background-color: var(--palette_c_05);"},"bg_c_10":{"declaration":"background-color: var(--palette_c_10);"},"bg_c_20":{"declaration":"background-color: var(--palette_c_20);"},"bg_c_30":{"declaration":"background-color: var(--palette_c_30);"},"bg_c_40":{"declaration":"background-color: var(--palette_c_40);"},"bg_c_50":{"declaration":"background-color: var(--palette_c_50);"},"bg_c_60":{"declaration":"background-color: var(--palette_c_60);"},"bg_c_70":{"declaration":"background-color: var(--palette_c_70);"},"bg_c_80":{"declaration":"background-color: var(--palette_c_80);"},"bg_c_90":{"declaration":"background-color: var(--palette_c_90);"},"bg_c_95":{"declaration":"background-color: var(--palette_c_95);"},"bg_c_100":{"declaration":"background-color: var(--palette_c_100);"},"bg_d_00":{"declaration":"background-color: var(--palette_d_00);"},"bg_d_05":{"declaration":"background-color: var(--palette_d_05);"},"bg_d_10":{"declaration":"background-color: var(--palette_d_10);"},"bg_d_20":{"declaration":"background-color: var(--palette_d_20);"},"bg_d_30":{"declaration":"background-color: var(--palette_d_30);"},"bg_d_40":{"declaration":"background-color: var(--palette_d_40);"},"bg_d_50":{"declaration":"background-color: var(--palette_d_50);"},"bg_d_60":{"declaration":"background-color: var(--palette_d_60);"},"bg_d_70":{"declaration":"background-color: var(--palette_d_70);"},"bg_d_80":{"declaration":"background-color: var(--palette_d_80);"},"bg_d_90":{"declaration":"background-color: var(--palette_d_90);"},"bg_d_95":{"declaration":"background-color: var(--palette_d_95);"},"bg_d_100":{"declaration":"background-color: var(--palette_d_100);"},"bg_e_00":{"declaration":"background-color: var(--palette_e_00);"},"bg_e_05":{"declaration":"background-color: var(--palette_e_05);"},"bg_e_10":{"declaration":"background-color: var(--palette_e_10);"},"bg_e_20":{"declaration":"background-color: var(--palette_e_20);"},"bg_e_30":{"declaration":"background-color: var(--palette_e_30);"},"bg_e_40":{"declaration":"background-color: var(--palette_e_40);"},"bg_e_50":{"declaration":"background-color: var(--palette_e_50);"},"bg_e_60":{"declaration":"background-color: var(--palette_e_60);"},"bg_e_70":{"declaration":"background-color: var(--palette_e_70);"},"bg_e_80":{"declaration":"background-color: var(--palette_e_80);"},"bg_e_90":{"declaration":"background-color: var(--palette_e_90);"},"bg_e_95":{"declaration":"background-color: var(--palette_e_95);"},"bg_e_100":{"declaration":"background-color: var(--palette_e_100);"},"bg_f_00":{"declaration":"background-color: var(--palette_f_00);"},"bg_f_05":{"declaration":"background-color: var(--palette_f_05);"},"bg_f_10":{"declaration":"background-color: var(--palette_f_10);"},"bg_f_20":{"declaration":"background-color: var(--palette_f_20);"},"bg_f_30":{"declaration":"background-color: var(--palette_f_30);"},"bg_f_40":{"declaration":"background-color: var(--palette_f_40);"},"bg_f_50":{"declaration":"background-color: var(--palette_f_50);"},"bg_f_60":{"declaration":"background-color: var(--palette_f_60);"},"bg_f_70":{"declaration":"background-color: var(--palette_f_70);"},"bg_f_80":{"declaration":"background-color: var(--palette_f_80);"},"bg_f_90":{"declaration":"background-color: var(--palette_f_90);"},"bg_f_95":{"declaration":"background-color: var(--palette_f_95);"},"bg_f_100":{"declaration":"background-color: var(--palette_f_100);"},"bg_g_00":{"declaration":"background-color: var(--palette_g_00);"},"bg_g_05":{"declaration":"background-color: var(--palette_g_05);"},"bg_g_10":{"declaration":"background-color: var(--palette_g_10);"},"bg_g_20":{"declaration":"background-color: var(--palette_g_20);"},"bg_g_30":{"declaration":"background-color: var(--palette_g_30);"},"bg_g_40":{"declaration":"background-color: var(--palette_g_40);"},"bg_g_50":{"declaration":"background-color: var(--palette_g_50);"},"bg_g_60":{"declaration":"background-color: var(--palette_g_60);"},"bg_g_70":{"declaration":"background-color: var(--palette_g_70);"},"bg_g_80":{"declaration":"background-color: var(--palette_g_80);"},"bg_g_90":{"declaration":"background-color: var(--palette_g_90);"},"bg_g_95":{"declaration":"background-color: var(--palette_g_95);"},"bg_g_100":{"declaration":"background-color: var(--palette_g_100);"},"bg_h_00":{"declaration":"background-color: var(--palette_h_00);"},"bg_h_05":{"declaration":"background-color: var(--palette_h_05);"},"bg_h_10":{"declaration":"background-color: var(--palette_h_10);"},"bg_h_20":{"declaration":"background-color: var(--palette_h_20);"},"bg_h_30":{"declaration":"background-color: var(--palette_h_30);"},"bg_h_40":{"declaration":"background-color: var(--palette_h_40);"},"bg_h_50":{"declaration":"background-color: var(--palette_h_50);"},"bg_h_60":{"declaration":"background-color: var(--palette_h_60);"},"bg_h_70":{"declaration":"background-color: var(--palette_h_70);"},"bg_h_80":{"declaration":"background-color: var(--palette_h_80);"},"bg_h_90":{"declaration":"background-color: var(--palette_h_90);"},"bg_h_95":{"declaration":"background-color: var(--palette_h_95);"},"bg_h_100":{"declaration":"background-color: var(--palette_h_100);"},"bg_i_00":{"declaration":"background-color: var(--palette_i_00);"},"bg_i_05":{"declaration":"background-color: var(--palette_i_05);"},"bg_i_10":{"declaration":"background-color: var(--palette_i_10);"},"bg_i_20":{"declaration":"background-color: var(--palette_i_20);"},"bg_i_30":{"declaration":"background-color: var(--palette_i_30);"},"bg_i_40":{"declaration":"background-color: var(--palette_i_40);"},"bg_i_50":{"declaration":"background-color: var(--palette_i_50);"},"bg_i_60":{"declaration":"background-color: var(--palette_i_60);"},"bg_i_70":{"declaration":"background-color: var(--palette_i_70);"},"bg_i_80":{"declaration":"background-color: var(--palette_i_80);"},"bg_i_90":{"declaration":"background-color: var(--palette_i_90);"},"bg_i_95":{"declaration":"background-color: var(--palette_i_95);"},"bg_i_100":{"declaration":"background-color: var(--palette_i_100);"},"bg_j_00":{"declaration":"background-color: var(--palette_j_00);"},"bg_j_05":{"declaration":"background-color: var(--palette_j_05);"},"bg_j_10":{"declaration":"background-color: var(--palette_j_10);"},"bg_j_20":{"declaration":"background-color: var(--palette_j_20);"},"bg_j_30":{"declaration":"background-color: var(--palette_j_30);"},"bg_j_40":{"declaration":"background-color: var(--palette_j_40);"},"bg_j_50":{"declaration":"background-color: var(--palette_j_50);"},"bg_j_60":{"declaration":"background-color: var(--palette_j_60);"},"bg_j_70":{"declaration":"background-color: var(--palette_j_70);"},"bg_j_80":{"declaration":"background-color: var(--palette_j_80);"},"bg_j_90":{"declaration":"background-color: var(--palette_j_90);"},"bg_j_95":{"declaration":"background-color: var(--palette_j_95);"},"bg_j_100":{"declaration":"background-color: var(--palette_j_100);"},"accent_00":{"declaration":"color: var(--accent_00); --text_color: var(--accent_00);"},"accent_05":{"declaration":"color: var(--accent_05); --text_color: var(--accent_05);"},"accent_10":{"declaration":"color: var(--accent_10); --text_color: var(--accent_10);"},"accent_20":{"declaration":"color: var(--accent_20); --text_color: var(--accent_20);"},"accent_30":{"declaration":"color: var(--accent_30); --text_color: var(--accent_30);"},"accent_40":{"declaration":"color: var(--accent_40); --text_color: var(--accent_40);"},"accent_50":{"declaration":"color: var(--accent_50); --text_color: var(--accent_50);"},"accent_60":{"declaration":"color: var(--accent_60); --text_color: var(--accent_60);"},"accent_70":{"declaration":"color: var(--accent_70); --text_color: var(--accent_70);"},"accent_80":{"declaration":"color: var(--accent_80); --text_color: var(--accent_80);"},"accent_90":{"declaration":"color: var(--accent_90); --text_color: var(--accent_90);"},"accent_95":{"declaration":"color: var(--accent_95); --text_color: var(--accent_95);"},"accent_100":{"declaration":"color: var(--accent_100); --text_color: var(--accent_100);"},"positive_00":{"declaration":"color: var(--positive_00); --text_color: var(--positive_00);"},"positive_05":{"declaration":"color: var(--positive_05); --text_color: var(--positive_05);"},"positive_10":{"declaration":"color: var(--positive_10); --text_color: var(--positive_10);"},"positive_20":{"declaration":"color: var(--positive_20); --text_color: var(--positive_20);"},"positive_30":{"declaration":"color: var(--positive_30); --text_color: var(--positive_30);"},"positive_40":{"declaration":"color: var(--positive_40); --text_color: var(--positive_40);"},"positive_50":{"declaration":"color: var(--positive_50); --text_color: var(--positive_50);"},"positive_60":{"declaration":"color: var(--positive_60); --text_color: var(--positive_60);"},"positive_70":{"declaration":"color: var(--positive_70); --text_color: var(--positive_70);"},"positive_80":{"declaration":"color: var(--positive_80); --text_color: var(--positive_80);"},"positive_90":{"declaration":"color: var(--positive_90); --text_color: var(--positive_90);"},"positive_95":{"declaration":"color: var(--positive_95); --text_color: var(--positive_95);"},"positive_100":{"declaration":"color: var(--positive_100); --text_color: var(--positive_100);"},"negative_00":{"declaration":"color: var(--negative_00); --text_color: var(--negative_00);"},"negative_05":{"declaration":"color: var(--negative_05); --text_color: var(--negative_05);"},"negative_10":{"declaration":"color: var(--negative_10); --text_color: var(--negative_10);"},"negative_20":{"declaration":"color: var(--negative_20); --text_color: var(--negative_20);"},"negative_30":{"declaration":"color: var(--negative_30); --text_color: var(--negative_30);"},"negative_40":{"declaration":"color: var(--negative_40); --text_color: var(--negative_40);"},"negative_50":{"declaration":"color: var(--negative_50); --text_color: var(--negative_50);"},"negative_60":{"declaration":"color: var(--negative_60); --text_color: var(--negative_60);"},"negative_70":{"declaration":"color: var(--negative_70); --text_color: var(--negative_70);"},"negative_80":{"declaration":"color: var(--negative_80); --text_color: var(--negative_80);"},"negative_90":{"declaration":"color: var(--negative_90); --text_color: var(--negative_90);"},"negative_95":{"declaration":"color: var(--negative_95); --text_color: var(--negative_95);"},"negative_100":{"declaration":"color: var(--negative_100); --text_color: var(--negative_100);"},"caution_00":{"declaration":"color: var(--caution_00); --text_color: var(--caution_00);"},"caution_05":{"declaration":"color: var(--caution_05); --text_color: var(--caution_05);"},"caution_10":{"declaration":"color: var(--caution_10); --text_color: var(--caution_10);"},"caution_20":{"declaration":"color: var(--caution_20); --text_color: var(--caution_20);"},"caution_30":{"declaration":"color: var(--caution_30); --text_color: var(--caution_30);"},"caution_40":{"declaration":"color: var(--caution_40); --text_color: var(--caution_40);"},"caution_50":{"declaration":"color: var(--caution_50); --text_color: var(--caution_50);"},"caution_60":{"declaration":"color: var(--caution_60); --text_color: var(--caution_60);"},"caution_70":{"declaration":"color: var(--caution_70); --text_color: var(--caution_70);"},"caution_80":{"declaration":"color: var(--caution_80); --text_color: var(--caution_80);"},"caution_90":{"declaration":"color: var(--caution_90); --text_color: var(--caution_90);"},"caution_95":{"declaration":"color: var(--caution_95); --text_color: var(--caution_95);"},"caution_100":{"declaration":"color: var(--caution_100); --text_color: var(--caution_100);"},"info_00":{"declaration":"color: var(--info_00); --text_color: var(--info_00);"},"info_05":{"declaration":"color: var(--info_05); --text_color: var(--info_05);"},"info_10":{"declaration":"color: var(--info_10); --text_color: var(--info_10);"},"info_20":{"declaration":"color: var(--info_20); --text_color: var(--info_20);"},"info_30":{"declaration":"color: var(--info_30); --text_color: var(--info_30);"},"info_40":{"declaration":"color: var(--info_40); --text_color: var(--info_40);"},"info_50":{"declaration":"color: var(--info_50); --text_color: var(--info_50);"},"info_60":{"declaration":"color: var(--info_60); --text_color: var(--info_60);"},"info_70":{"declaration":"color: var(--info_70); --text_color: var(--info_70);"},"info_80":{"declaration":"color: var(--info_80); --text_color: var(--info_80);"},"info_90":{"declaration":"color: var(--info_90); --text_color: var(--info_90);"},"info_95":{"declaration":"color: var(--info_95); --text_color: var(--info_95);"},"info_100":{"declaration":"color: var(--info_100); --text_color: var(--info_100);"},"bg_accent_00":{"declaration":"background-color: var(--accent_00);"},"bg_accent_05":{"declaration":"background-color: var(--accent_05);"},"bg_accent_10":{"declaration":"background-color: var(--accent_10);"},"bg_accent_20":{"declaration":"background-color: var(--accent_20);"},"bg_accent_30":{"declaration":"background-color: var(--accent_30);"},"bg_accent_40":{"declaration":"background-color: var(--accent_40);"},"bg_accent_50":{"declaration":"background-color: var(--accent_50);"},"bg_accent_60":{"declaration":"background-color: var(--accent_60);"},"bg_accent_70":{"declaration":"background-color: var(--accent_70);"},"bg_accent_80":{"declaration":"background-color: var(--accent_80);"},"bg_accent_90":{"declaration":"background-color: var(--accent_90);"},"bg_accent_95":{"declaration":"background-color: var(--accent_95);"},"bg_accent_100":{"declaration":"background-color: var(--accent_100);"},"bg_positive_00":{"declaration":"background-color: var(--positive_00);"},"bg_positive_05":{"declaration":"background-color: var(--positive_05);"},"bg_positive_10":{"declaration":"background-color: var(--positive_10);"},"bg_positive_20":{"declaration":"background-color: var(--positive_20);"},"bg_positive_30":{"declaration":"background-color: var(--positive_30);"},"bg_positive_40":{"declaration":"background-color: var(--positive_40);"},"bg_positive_50":{"declaration":"background-color: var(--positive_50);"},"bg_positive_60":{"declaration":"background-color: var(--positive_60);"},"bg_positive_70":{"declaration":"background-color: var(--positive_70);"},"bg_positive_80":{"declaration":"background-color: var(--positive_80);"},"bg_positive_90":{"declaration":"background-color: var(--positive_90);"},"bg_positive_95":{"declaration":"background-color: var(--positive_95);"},"bg_positive_100":{"declaration":"background-color: var(--positive_100);"},"bg_negative_00":{"declaration":"background-color: var(--negative_00);"},"bg_negative_05":{"declaration":"background-color: var(--negative_05);"},"bg_negative_10":{"declaration":"background-color: var(--negative_10);"},"bg_negative_20":{"declaration":"background-color: var(--negative_20);"},"bg_negative_30":{"declaration":"background-color: var(--negative_30);"},"bg_negative_40":{"declaration":"background-color: var(--negative_40);"},"bg_negative_50":{"declaration":"background-color: var(--negative_50);"},"bg_negative_60":{"declaration":"background-color: var(--negative_60);"},"bg_negative_70":{"declaration":"background-color: var(--negative_70);"},"bg_negative_80":{"declaration":"background-color: var(--negative_80);"},"bg_negative_90":{"declaration":"background-color: var(--negative_90);"},"bg_negative_95":{"declaration":"background-color: var(--negative_95);"},"bg_negative_100":{"declaration":"background-color: var(--negative_100);"},"bg_caution_00":{"declaration":"background-color: var(--caution_00);"},"bg_caution_05":{"declaration":"background-color: var(--caution_05);"},"bg_caution_10":{"declaration":"background-color: var(--caution_10);"},"bg_caution_20":{"declaration":"background-color: var(--caution_20);"},"bg_caution_30":{"declaration":"background-color: var(--caution_30);"},"bg_caution_40":{"declaration":"background-color: var(--caution_40);"},"bg_caution_50":{"declaration":"background-color: var(--caution_50);"},"bg_caution_60":{"declaration":"background-color: var(--caution_60);"},"bg_caution_70":{"declaration":"background-color: var(--caution_70);"},"bg_caution_80":{"declaration":"background-color: var(--caution_80);"},"bg_caution_90":{"declaration":"background-color: var(--caution_90);"},"bg_caution_95":{"declaration":"background-color: var(--caution_95);"},"bg_caution_100":{"declaration":"background-color: var(--caution_100);"},"bg_info_00":{"declaration":"background-color: var(--info_00);"},"bg_info_05":{"declaration":"background-color: var(--info_05);"},"bg_info_10":{"declaration":"background-color: var(--info_10);"},"bg_info_20":{"declaration":"background-color: var(--info_20);"},"bg_info_30":{"declaration":"background-color: var(--info_30);"},"bg_info_40":{"declaration":"background-color: var(--info_40);"},"bg_info_50":{"declaration":"background-color: var(--info_50);"},"bg_info_60":{"declaration":"background-color: var(--info_60);"},"bg_info_70":{"declaration":"background-color: var(--info_70);"},"bg_info_80":{"declaration":"background-color: var(--info_80);"},"bg_info_90":{"declaration":"background-color: var(--info_90);"},"bg_info_95":{"declaration":"background-color: var(--info_95);"},"bg_info_100":{"declaration":"background-color: var(--info_100);"},"darken_00":{"declaration":"background-color: var(--darken_00);"},"darken_05":{"declaration":"background-color: var(--darken_05);"},"darken_10":{"declaration":"background-color: var(--darken_10);"},"darken_20":{"declaration":"background-color: var(--darken_20);"},"darken_30":{"declaration":"background-color: var(--darken_30);"},"darken_40":{"declaration":"background-color: var(--darken_40);"},"darken_50":{"declaration":"background-color: var(--darken_50);"},"darken_60":{"declaration":"background-color: var(--darken_60);"},"darken_70":{"declaration":"background-color: var(--darken_70);"},"darken_80":{"declaration":"background-color: var(--darken_80);"},"darken_90":{"declaration":"background-color: var(--darken_90);"},"darken_95":{"declaration":"background-color: var(--darken_95);"},"darken_100":{"declaration":"background-color: var(--darken_100);"},"lighten_00":{"declaration":"background-color: var(--lighten_00);"},"lighten_05":{"declaration":"background-color: var(--lighten_05);"},"lighten_10":{"declaration":"background-color: var(--lighten_10);"},"lighten_20":{"declaration":"background-color: var(--lighten_20);"},"lighten_30":{"declaration":"background-color: var(--lighten_30);"},"lighten_40":{"declaration":"background-color: var(--lighten_40);"},"lighten_50":{"declaration":"background-color: var(--lighten_50);"},"lighten_60":{"declaration":"background-color: var(--lighten_60);"},"lighten_70":{"declaration":"background-color: var(--lighten_70);"},"lighten_80":{"declaration":"background-color: var(--lighten_80);"},"lighten_90":{"declaration":"background-color: var(--lighten_90);"},"lighten_95":{"declaration":"background-color: var(--lighten_95);"},"lighten_100":{"declaration":"background-color: var(--lighten_100);"},"fg_00":{"declaration":"background-color: var(--fg_00);"},"fg_05":{"declaration":"background-color: var(--fg_05);"},"fg_10":{"declaration":"background-color: var(--fg_10);"},"fg_20":{"declaration":"background-color: var(--fg_20);"},"fg_30":{"declaration":"background-color: var(--fg_30);"},"fg_40":{"declaration":"background-color: var(--fg_40);"},"fg_50":{"declaration":"background-color: var(--fg_50);"},"fg_60":{"declaration":"background-color: var(--fg_60);"},"fg_70":{"declaration":"background-color: var(--fg_70);"},"fg_80":{"declaration":"background-color: var(--fg_80);"},"fg_90":{"declaration":"background-color: var(--fg_90);"},"fg_95":{"declaration":"background-color: var(--fg_95);"},"fg_100":{"declaration":"background-color: var(--fg_100);"},"bg_00":{"declaration":"background-color: var(--bg_00);"},"bg_05":{"declaration":"background-color: var(--bg_05);"},"bg_10":{"declaration":"background-color: var(--bg_10);"},"bg_20":{"declaration":"background-color: var(--bg_20);"},"bg_30":{"declaration":"background-color: var(--bg_30);"},"bg_40":{"declaration":"background-color: var(--bg_40);"},"bg_50":{"declaration":"background-color: var(--bg_50);"},"bg_60":{"declaration":"background-color: var(--bg_60);"},"bg_70":{"declaration":"background-color: var(--bg_70);"},"bg_80":{"declaration":"background-color: var(--bg_80);"},"bg_90":{"declaration":"background-color: var(--bg_90);"},"bg_95":{"declaration":"background-color: var(--bg_95);"},"bg_100":{"declaration":"background-color: var(--bg_100);"},"border_color_00":{"declaration":"border-color: var(--border_color_00); --border_color: var(--border_color_00);"},"border_color_05":{"declaration":"border-color: var(--border_color_05); --border_color: var(--border_color_05);"},"border_color_10":{"declaration":"border-color: var(--border_color_10); --border_color: var(--border_color_10);"},"border_color_20":{"declaration":"border-color: var(--border_color_20); --border_color: var(--border_color_20);"},"border_color_30":{"declaration":"border-color: var(--border_color_30); --border_color: var(--border_color_30);"},"border_color_40":{"declaration":"border-color: var(--border_color_40); --border_color: var(--border_color_40);"},"border_color_50":{"declaration":"border-color: var(--border_color_50); --border_color: var(--border_color_50);"},"border_color_60":{"declaration":"border-color: var(--border_color_60); --border_color: var(--border_color_60);"},"border_color_70":{"declaration":"border-color: var(--border_color_70); --border_color: var(--border_color_70);"},"border_color_80":{"declaration":"border-color: var(--border_color_80); --border_color: var(--border_color_80);"},"border_color_90":{"declaration":"border-color: var(--border_color_90); --border_color: var(--border_color_90);"},"border_color_95":{"declaration":"border-color: var(--border_color_95); --border_color: var(--border_color_95);"},"border_color_100":{"declaration":"border-color: var(--border_color_100); --border_color: var(--border_color_100);"},"border_a_00":{"declaration":"border-color: var(--palette_a_00); --border_color: var(--palette_a_00);"},"border_a_05":{"declaration":"border-color: var(--palette_a_05); --border_color: var(--palette_a_05);"},"border_a_10":{"declaration":"border-color: var(--palette_a_10); --border_color: var(--palette_a_10);"},"border_a_20":{"declaration":"border-color: var(--palette_a_20); --border_color: var(--palette_a_20);"},"border_a_30":{"declaration":"border-color: var(--palette_a_30); --border_color: var(--palette_a_30);"},"border_a_40":{"declaration":"border-color: var(--palette_a_40); --border_color: var(--palette_a_40);"},"border_a_50":{"declaration":"border-color: var(--palette_a_50); --border_color: var(--palette_a_50);"},"border_a_60":{"declaration":"border-color: var(--palette_a_60); --border_color: var(--palette_a_60);"},"border_a_70":{"declaration":"border-color: var(--palette_a_70); --border_color: var(--palette_a_70);"},"border_a_80":{"declaration":"border-color: var(--palette_a_80); --border_color: var(--palette_a_80);"},"border_a_90":{"declaration":"border-color: var(--palette_a_90); --border_color: var(--palette_a_90);"},"border_a_95":{"declaration":"border-color: var(--palette_a_95); --border_color: var(--palette_a_95);"},"border_a_100":{"declaration":"border-color: var(--palette_a_100); --border_color: var(--palette_a_100);"},"border_b_00":{"declaration":"border-color: var(--palette_b_00); --border_color: var(--palette_b_00);"},"border_b_05":{"declaration":"border-color: var(--palette_b_05); --border_color: var(--palette_b_05);"},"border_b_10":{"declaration":"border-color: var(--palette_b_10); --border_color: var(--palette_b_10);"},"border_b_20":{"declaration":"border-color: var(--palette_b_20); --border_color: var(--palette_b_20);"},"border_b_30":{"declaration":"border-color: var(--palette_b_30); --border_color: var(--palette_b_30);"},"border_b_40":{"declaration":"border-color: var(--palette_b_40); --border_color: var(--palette_b_40);"},"border_b_50":{"declaration":"border-color: var(--palette_b_50); --border_color: var(--palette_b_50);"},"border_b_60":{"declaration":"border-color: var(--palette_b_60); --border_color: var(--palette_b_60);"},"border_b_70":{"declaration":"border-color: var(--palette_b_70); --border_color: var(--palette_b_70);"},"border_b_80":{"declaration":"border-color: var(--palette_b_80); --border_color: var(--palette_b_80);"},"border_b_90":{"declaration":"border-color: var(--palette_b_90); --border_color: var(--palette_b_90);"},"border_b_95":{"declaration":"border-color: var(--palette_b_95); --border_color: var(--palette_b_95);"},"border_b_100":{"declaration":"border-color: var(--palette_b_100); --border_color: var(--palette_b_100);"},"border_c_00":{"declaration":"border-color: var(--palette_c_00); --border_color: var(--palette_c_00);"},"border_c_05":{"declaration":"border-color: var(--palette_c_05); --border_color: var(--palette_c_05);"},"border_c_10":{"declaration":"border-color: var(--palette_c_10); --border_color: var(--palette_c_10);"},"border_c_20":{"declaration":"border-color: var(--palette_c_20); --border_color: var(--palette_c_20);"},"border_c_30":{"declaration":"border-color: var(--palette_c_30); --border_color: var(--palette_c_30);"},"border_c_40":{"declaration":"border-color: var(--palette_c_40); --border_color: var(--palette_c_40);"},"border_c_50":{"declaration":"border-color: var(--palette_c_50); --border_color: var(--palette_c_50);"},"border_c_60":{"declaration":"border-color: var(--palette_c_60); --border_color: var(--palette_c_60);"},"border_c_70":{"declaration":"border-color: var(--palette_c_70); --border_color: var(--palette_c_70);"},"border_c_80":{"declaration":"border-color: var(--palette_c_80); --border_color: var(--palette_c_80);"},"border_c_90":{"declaration":"border-color: var(--palette_c_90); --border_color: var(--palette_c_90);"},"border_c_95":{"declaration":"border-color: var(--palette_c_95); --border_color: var(--palette_c_95);"},"border_c_100":{"declaration":"border-color: var(--palette_c_100); --border_color: var(--palette_c_100);"},"border_d_00":{"declaration":"border-color: var(--palette_d_00); --border_color: var(--palette_d_00);"},"border_d_05":{"declaration":"border-color: var(--palette_d_05); --border_color: var(--palette_d_05);"},"border_d_10":{"declaration":"border-color: var(--palette_d_10); --border_color: var(--palette_d_10);"},"border_d_20":{"declaration":"border-color: var(--palette_d_20); --border_color: var(--palette_d_20);"},"border_d_30":{"declaration":"border-color: var(--palette_d_30); --border_color: var(--palette_d_30);"},"border_d_40":{"declaration":"border-color: var(--palette_d_40); --border_color: var(--palette_d_40);"},"border_d_50":{"declaration":"border-color: var(--palette_d_50); --border_color: var(--palette_d_50);"},"border_d_60":{"declaration":"border-color: var(--palette_d_60); --border_color: var(--palette_d_60);"},"border_d_70":{"declaration":"border-color: var(--palette_d_70); --border_color: var(--palette_d_70);"},"border_d_80":{"declaration":"border-color: var(--palette_d_80); --border_color: var(--palette_d_80);"},"border_d_90":{"declaration":"border-color: var(--palette_d_90); --border_color: var(--palette_d_90);"},"border_d_95":{"declaration":"border-color: var(--palette_d_95); --border_color: var(--palette_d_95);"},"border_d_100":{"declaration":"border-color: var(--palette_d_100); --border_color: var(--palette_d_100);"},"border_e_00":{"declaration":"border-color: var(--palette_e_00); --border_color: var(--palette_e_00);"},"border_e_05":{"declaration":"border-color: var(--palette_e_05); --border_color: var(--palette_e_05);"},"border_e_10":{"declaration":"border-color: var(--palette_e_10); --border_color: var(--palette_e_10);"},"border_e_20":{"declaration":"border-color: var(--palette_e_20); --border_color: var(--palette_e_20);"},"border_e_30":{"declaration":"border-color: var(--palette_e_30); --border_color: var(--palette_e_30);"},"border_e_40":{"declaration":"border-color: var(--palette_e_40); --border_color: var(--palette_e_40);"},"border_e_50":{"declaration":"border-color: var(--palette_e_50); --border_color: var(--palette_e_50);"},"border_e_60":{"declaration":"border-color: var(--palette_e_60); --border_color: var(--palette_e_60);"},"border_e_70":{"declaration":"border-color: var(--palette_e_70); --border_color: var(--palette_e_70);"},"border_e_80":{"declaration":"border-color: var(--palette_e_80); --border_color: var(--palette_e_80);"},"border_e_90":{"declaration":"border-color: var(--palette_e_90); --border_color: var(--palette_e_90);"},"border_e_95":{"declaration":"border-color: var(--palette_e_95); --border_color: var(--palette_e_95);"},"border_e_100":{"declaration":"border-color: var(--palette_e_100); --border_color: var(--palette_e_100);"},"border_f_00":{"declaration":"border-color: var(--palette_f_00); --border_color: var(--palette_f_00);"},"border_f_05":{"declaration":"border-color: var(--palette_f_05); --border_color: var(--palette_f_05);"},"border_f_10":{"declaration":"border-color: var(--palette_f_10); --border_color: var(--palette_f_10);"},"border_f_20":{"declaration":"border-color: var(--palette_f_20); --border_color: var(--palette_f_20);"},"border_f_30":{"declaration":"border-color: var(--palette_f_30); --border_color: var(--palette_f_30);"},"border_f_40":{"declaration":"border-color: var(--palette_f_40); --border_color: var(--palette_f_40);"},"border_f_50":{"declaration":"border-color: var(--palette_f_50); --border_color: var(--palette_f_50);"},"border_f_60":{"declaration":"border-color: var(--palette_f_60); --border_color: var(--palette_f_60);"},"border_f_70":{"declaration":"border-color: var(--palette_f_70); --border_color: var(--palette_f_70);"},"border_f_80":{"declaration":"border-color: var(--palette_f_80); --border_color: var(--palette_f_80);"},"border_f_90":{"declaration":"border-color: var(--palette_f_90); --border_color: var(--palette_f_90);"},"border_f_95":{"declaration":"border-color: var(--palette_f_95); --border_color: var(--palette_f_95);"},"border_f_100":{"declaration":"border-color: var(--palette_f_100); --border_color: var(--palette_f_100);"},"border_g_00":{"declaration":"border-color: var(--palette_g_00); --border_color: var(--palette_g_00);"},"border_g_05":{"declaration":"border-color: var(--palette_g_05); --border_color: var(--palette_g_05);"},"border_g_10":{"declaration":"border-color: var(--palette_g_10); --border_color: var(--palette_g_10);"},"border_g_20":{"declaration":"border-color: var(--palette_g_20); --border_color: var(--palette_g_20);"},"border_g_30":{"declaration":"border-color: var(--palette_g_30); --border_color: var(--palette_g_30);"},"border_g_40":{"declaration":"border-color: var(--palette_g_40); --border_color: var(--palette_g_40);"},"border_g_50":{"declaration":"border-color: var(--palette_g_50); --border_color: var(--palette_g_50);"},"border_g_60":{"declaration":"border-color: var(--palette_g_60); --border_color: var(--palette_g_60);"},"border_g_70":{"declaration":"border-color: var(--palette_g_70); --border_color: var(--palette_g_70);"},"border_g_80":{"declaration":"border-color: var(--palette_g_80); --border_color: var(--palette_g_80);"},"border_g_90":{"declaration":"border-color: var(--palette_g_90); --border_color: var(--palette_g_90);"},"border_g_95":{"declaration":"border-color: var(--palette_g_95); --border_color: var(--palette_g_95);"},"border_g_100":{"declaration":"border-color: var(--palette_g_100); --border_color: var(--palette_g_100);"},"border_h_00":{"declaration":"border-color: var(--palette_h_00); --border_color: var(--palette_h_00);"},"border_h_05":{"declaration":"border-color: var(--palette_h_05); --border_color: var(--palette_h_05);"},"border_h_10":{"declaration":"border-color: var(--palette_h_10); --border_color: var(--palette_h_10);"},"border_h_20":{"declaration":"border-color: var(--palette_h_20); --border_color: var(--palette_h_20);"},"border_h_30":{"declaration":"border-color: var(--palette_h_30); --border_color: var(--palette_h_30);"},"border_h_40":{"declaration":"border-color: var(--palette_h_40); --border_color: var(--palette_h_40);"},"border_h_50":{"declaration":"border-color: var(--palette_h_50); --border_color: var(--palette_h_50);"},"border_h_60":{"declaration":"border-color: var(--palette_h_60); --border_color: var(--palette_h_60);"},"border_h_70":{"declaration":"border-color: var(--palette_h_70); --border_color: var(--palette_h_70);"},"border_h_80":{"declaration":"border-color: var(--palette_h_80); --border_color: var(--palette_h_80);"},"border_h_90":{"declaration":"border-color: var(--palette_h_90); --border_color: var(--palette_h_90);"},"border_h_95":{"declaration":"border-color: var(--palette_h_95); --border_color: var(--palette_h_95);"},"border_h_100":{"declaration":"border-color: var(--palette_h_100); --border_color: var(--palette_h_100);"},"border_i_00":{"declaration":"border-color: var(--palette_i_00); --border_color: var(--palette_i_00);"},"border_i_05":{"declaration":"border-color: var(--palette_i_05); --border_color: var(--palette_i_05);"},"border_i_10":{"declaration":"border-color: var(--palette_i_10); --border_color: var(--palette_i_10);"},"border_i_20":{"declaration":"border-color: var(--palette_i_20); --border_color: var(--palette_i_20);"},"border_i_30":{"declaration":"border-color: var(--palette_i_30); --border_color: var(--palette_i_30);"},"border_i_40":{"declaration":"border-color: var(--palette_i_40); --border_color: var(--palette_i_40);"},"border_i_50":{"declaration":"border-color: var(--palette_i_50); --border_color: var(--palette_i_50);"},"border_i_60":{"declaration":"border-color: var(--palette_i_60); --border_color: var(--palette_i_60);"},"border_i_70":{"declaration":"border-color: var(--palette_i_70); --border_color: var(--palette_i_70);"},"border_i_80":{"declaration":"border-color: var(--palette_i_80); --border_color: var(--palette_i_80);"},"border_i_90":{"declaration":"border-color: var(--palette_i_90); --border_color: var(--palette_i_90);"},"border_i_95":{"declaration":"border-color: var(--palette_i_95); --border_color: var(--palette_i_95);"},"border_i_100":{"declaration":"border-color: var(--palette_i_100); --border_color: var(--palette_i_100);"},"border_j_00":{"declaration":"border-color: var(--palette_j_00); --border_color: var(--palette_j_00);"},"border_j_05":{"declaration":"border-color: var(--palette_j_05); --border_color: var(--palette_j_05);"},"border_j_10":{"declaration":"border-color: var(--palette_j_10); --border_color: var(--palette_j_10);"},"border_j_20":{"declaration":"border-color: var(--palette_j_20); --border_color: var(--palette_j_20);"},"border_j_30":{"declaration":"border-color: var(--palette_j_30); --border_color: var(--palette_j_30);"},"border_j_40":{"declaration":"border-color: var(--palette_j_40); --border_color: var(--palette_j_40);"},"border_j_50":{"declaration":"border-color: var(--palette_j_50); --border_color: var(--palette_j_50);"},"border_j_60":{"declaration":"border-color: var(--palette_j_60); --border_color: var(--palette_j_60);"},"border_j_70":{"declaration":"border-color: var(--palette_j_70); --border_color: var(--palette_j_70);"},"border_j_80":{"declaration":"border-color: var(--palette_j_80); --border_color: var(--palette_j_80);"},"border_j_90":{"declaration":"border-color: var(--palette_j_90); --border_color: var(--palette_j_90);"},"border_j_95":{"declaration":"border-color: var(--palette_j_95); --border_color: var(--palette_j_95);"},"border_j_100":{"declaration":"border-color: var(--palette_j_100); --border_color: var(--palette_j_100);"},"outline_color_00":{"declaration":"outline-color: var(--shade_00);"},"outline_color_05":{"declaration":"outline-color: var(--shade_05);"},"outline_color_10":{"declaration":"outline-color: var(--shade_10);"},"outline_color_20":{"declaration":"outline-color: var(--shade_20);"},"outline_color_30":{"declaration":"outline-color: var(--shade_30);"},"outline_color_40":{"declaration":"outline-color: var(--shade_40);"},"outline_color_50":{"declaration":"outline-color: var(--shade_50);"},"outline_color_60":{"declaration":"outline-color: var(--shade_60);"},"outline_color_70":{"declaration":"outline-color: var(--shade_70);"},"outline_color_80":{"declaration":"outline-color: var(--shade_80);"},"outline_color_90":{"declaration":"outline-color: var(--shade_90);"},"outline_color_95":{"declaration":"outline-color: var(--shade_95);"},"outline_color_100":{"declaration":"outline-color: var(--shade_100);"},"outline_a_00":{"declaration":"outline-color: var(--palette_a_00); --outline_color: var(--palette_a_00);"},"outline_a_05":{"declaration":"outline-color: var(--palette_a_05); --outline_color: var(--palette_a_05);"},"outline_a_10":{"declaration":"outline-color: var(--palette_a_10); --outline_color: var(--palette_a_10);"},"outline_a_20":{"declaration":"outline-color: var(--palette_a_20); --outline_color: var(--palette_a_20);"},"outline_a_30":{"declaration":"outline-color: var(--palette_a_30); --outline_color: var(--palette_a_30);"},"outline_a_40":{"declaration":"outline-color: var(--palette_a_40); --outline_color: var(--palette_a_40);"},"outline_a_50":{"declaration":"outline-color: var(--palette_a_50); --outline_color: var(--palette_a_50);"},"outline_a_60":{"declaration":"outline-color: var(--palette_a_60); --outline_color: var(--palette_a_60);"},"outline_a_70":{"declaration":"outline-color: var(--palette_a_70); --outline_color: var(--palette_a_70);"},"outline_a_80":{"declaration":"outline-color: var(--palette_a_80); --outline_color: var(--palette_a_80);"},"outline_a_90":{"declaration":"outline-color: var(--palette_a_90); --outline_color: var(--palette_a_90);"},"outline_a_95":{"declaration":"outline-color: var(--palette_a_95); --outline_color: var(--palette_a_95);"},"outline_a_100":{"declaration":"outline-color: var(--palette_a_100); --outline_color: var(--palette_a_100);"},"outline_b_00":{"declaration":"outline-color: var(--palette_b_00); --outline_color: var(--palette_b_00);"},"outline_b_05":{"declaration":"outline-color: var(--palette_b_05); --outline_color: var(--palette_b_05);"},"outline_b_10":{"declaration":"outline-color: var(--palette_b_10); --outline_color: var(--palette_b_10);"},"outline_b_20":{"declaration":"outline-color: var(--palette_b_20); --outline_color: var(--palette_b_20);"},"outline_b_30":{"declaration":"outline-color: var(--palette_b_30); --outline_color: var(--palette_b_30);"},"outline_b_40":{"declaration":"outline-color: var(--palette_b_40); --outline_color: var(--palette_b_40);"},"outline_b_50":{"declaration":"outline-color: var(--palette_b_50); --outline_color: var(--palette_b_50);"},"outline_b_60":{"declaration":"outline-color: var(--palette_b_60); --outline_color: var(--palette_b_60);"},"outline_b_70":{"declaration":"outline-color: var(--palette_b_70); --outline_color: var(--palette_b_70);"},"outline_b_80":{"declaration":"outline-color: var(--palette_b_80); --outline_color: var(--palette_b_80);"},"outline_b_90":{"declaration":"outline-color: var(--palette_b_90); --outline_color: var(--palette_b_90);"},"outline_b_95":{"declaration":"outline-color: var(--palette_b_95); --outline_color: var(--palette_b_95);"},"outline_b_100":{"declaration":"outline-color: var(--palette_b_100); --outline_color: var(--palette_b_100);"},"outline_c_00":{"declaration":"outline-color: var(--palette_c_00); --outline_color: var(--palette_c_00);"},"outline_c_05":{"declaration":"outline-color: var(--palette_c_05); --outline_color: var(--palette_c_05);"},"outline_c_10":{"declaration":"outline-color: var(--palette_c_10); --outline_color: var(--palette_c_10);"},"outline_c_20":{"declaration":"outline-color: var(--palette_c_20); --outline_color: var(--palette_c_20);"},"outline_c_30":{"declaration":"outline-color: var(--palette_c_30); --outline_color: var(--palette_c_30);"},"outline_c_40":{"declaration":"outline-color: var(--palette_c_40); --outline_color: var(--palette_c_40);"},"outline_c_50":{"declaration":"outline-color: var(--palette_c_50); --outline_color: var(--palette_c_50);"},"outline_c_60":{"declaration":"outline-color: var(--palette_c_60); --outline_color: var(--palette_c_60);"},"outline_c_70":{"declaration":"outline-color: var(--palette_c_70); --outline_color: var(--palette_c_70);"},"outline_c_80":{"declaration":"outline-color: var(--palette_c_80); --outline_color: var(--palette_c_80);"},"outline_c_90":{"declaration":"outline-color: var(--palette_c_90); --outline_color: var(--palette_c_90);"},"outline_c_95":{"declaration":"outline-color: var(--palette_c_95); --outline_color: var(--palette_c_95);"},"outline_c_100":{"declaration":"outline-color: var(--palette_c_100); --outline_color: var(--palette_c_100);"},"outline_d_00":{"declaration":"outline-color: var(--palette_d_00); --outline_color: var(--palette_d_00);"},"outline_d_05":{"declaration":"outline-color: var(--palette_d_05); --outline_color: var(--palette_d_05);"},"outline_d_10":{"declaration":"outline-color: var(--palette_d_10); --outline_color: var(--palette_d_10);"},"outline_d_20":{"declaration":"outline-color: var(--palette_d_20); --outline_color: var(--palette_d_20);"},"outline_d_30":{"declaration":"outline-color: var(--palette_d_30); --outline_color: var(--palette_d_30);"},"outline_d_40":{"declaration":"outline-color: var(--palette_d_40); --outline_color: var(--palette_d_40);"},"outline_d_50":{"declaration":"outline-color: var(--palette_d_50); --outline_color: var(--palette_d_50);"},"outline_d_60":{"declaration":"outline-color: var(--palette_d_60); --outline_color: var(--palette_d_60);"},"outline_d_70":{"declaration":"outline-color: var(--palette_d_70); --outline_color: var(--palette_d_70);"},"outline_d_80":{"declaration":"outline-color: var(--palette_d_80); --outline_color: var(--palette_d_80);"},"outline_d_90":{"declaration":"outline-color: var(--palette_d_90); --outline_color: var(--palette_d_90);"},"outline_d_95":{"declaration":"outline-color: var(--palette_d_95); --outline_color: var(--palette_d_95);"},"outline_d_100":{"declaration":"outline-color: var(--palette_d_100); --outline_color: var(--palette_d_100);"},"outline_e_00":{"declaration":"outline-color: var(--palette_e_00); --outline_color: var(--palette_e_00);"},"outline_e_05":{"declaration":"outline-color: var(--palette_e_05); --outline_color: var(--palette_e_05);"},"outline_e_10":{"declaration":"outline-color: var(--palette_e_10); --outline_color: var(--palette_e_10);"},"outline_e_20":{"declaration":"outline-color: var(--palette_e_20); --outline_color: var(--palette_e_20);"},"outline_e_30":{"declaration":"outline-color: var(--palette_e_30); --outline_color: var(--palette_e_30);"},"outline_e_40":{"declaration":"outline-color: var(--palette_e_40); --outline_color: var(--palette_e_40);"},"outline_e_50":{"declaration":"outline-color: var(--palette_e_50); --outline_color: var(--palette_e_50);"},"outline_e_60":{"declaration":"outline-color: var(--palette_e_60); --outline_color: var(--palette_e_60);"},"outline_e_70":{"declaration":"outline-color: var(--palette_e_70); --outline_color: var(--palette_e_70);"},"outline_e_80":{"declaration":"outline-color: var(--palette_e_80); --outline_color: var(--palette_e_80);"},"outline_e_90":{"declaration":"outline-color: var(--palette_e_90); --outline_color: var(--palette_e_90);"},"outline_e_95":{"declaration":"outline-color: var(--palette_e_95); --outline_color: var(--palette_e_95);"},"outline_e_100":{"declaration":"outline-color: var(--palette_e_100); --outline_color: var(--palette_e_100);"},"outline_f_00":{"declaration":"outline-color: var(--palette_f_00); --outline_color: var(--palette_f_00);"},"outline_f_05":{"declaration":"outline-color: var(--palette_f_05); --outline_color: var(--palette_f_05);"},"outline_f_10":{"declaration":"outline-color: var(--palette_f_10); --outline_color: var(--palette_f_10);"},"outline_f_20":{"declaration":"outline-color: var(--palette_f_20); --outline_color: var(--palette_f_20);"},"outline_f_30":{"declaration":"outline-color: var(--palette_f_30); --outline_color: var(--palette_f_30);"},"outline_f_40":{"declaration":"outline-color: var(--palette_f_40); --outline_color: var(--palette_f_40);"},"outline_f_50":{"declaration":"outline-color: var(--palette_f_50); --outline_color: var(--palette_f_50);"},"outline_f_60":{"declaration":"outline-color: var(--palette_f_60); --outline_color: var(--palette_f_60);"},"outline_f_70":{"declaration":"outline-color: var(--palette_f_70); --outline_color: var(--palette_f_70);"},"outline_f_80":{"declaration":"outline-color: var(--palette_f_80); --outline_color: var(--palette_f_80);"},"outline_f_90":{"declaration":"outline-color: var(--palette_f_90); --outline_color: var(--palette_f_90);"},"outline_f_95":{"declaration":"outline-color: var(--palette_f_95); --outline_color: var(--palette_f_95);"},"outline_f_100":{"declaration":"outline-color: var(--palette_f_100); --outline_color: var(--palette_f_100);"},"outline_g_00":{"declaration":"outline-color: var(--palette_g_00); --outline_color: var(--palette_g_00);"},"outline_g_05":{"declaration":"outline-color: var(--palette_g_05); --outline_color: var(--palette_g_05);"},"outline_g_10":{"declaration":"outline-color: var(--palette_g_10); --outline_color: var(--palette_g_10);"},"outline_g_20":{"declaration":"outline-color: var(--palette_g_20); --outline_color: var(--palette_g_20);"},"outline_g_30":{"declaration":"outline-color: var(--palette_g_30); --outline_color: var(--palette_g_30);"},"outline_g_40":{"declaration":"outline-color: var(--palette_g_40); --outline_color: var(--palette_g_40);"},"outline_g_50":{"declaration":"outline-color: var(--palette_g_50); --outline_color: var(--palette_g_50);"},"outline_g_60":{"declaration":"outline-color: var(--palette_g_60); --outline_color: var(--palette_g_60);"},"outline_g_70":{"declaration":"outline-color: var(--palette_g_70); --outline_color: var(--palette_g_70);"},"outline_g_80":{"declaration":"outline-color: var(--palette_g_80); --outline_color: var(--palette_g_80);"},"outline_g_90":{"declaration":"outline-color: var(--palette_g_90); --outline_color: var(--palette_g_90);"},"outline_g_95":{"declaration":"outline-color: var(--palette_g_95); --outline_color: var(--palette_g_95);"},"outline_g_100":{"declaration":"outline-color: var(--palette_g_100); --outline_color: var(--palette_g_100);"},"outline_h_00":{"declaration":"outline-color: var(--palette_h_00); --outline_color: var(--palette_h_00);"},"outline_h_05":{"declaration":"outline-color: var(--palette_h_05); --outline_color: var(--palette_h_05);"},"outline_h_10":{"declaration":"outline-color: var(--palette_h_10); --outline_color: var(--palette_h_10);"},"outline_h_20":{"declaration":"outline-color: var(--palette_h_20); --outline_color: var(--palette_h_20);"},"outline_h_30":{"declaration":"outline-color: var(--palette_h_30); --outline_color: var(--palette_h_30);"},"outline_h_40":{"declaration":"outline-color: var(--palette_h_40); --outline_color: var(--palette_h_40);"},"outline_h_50":{"declaration":"outline-color: var(--palette_h_50); --outline_color: var(--palette_h_50);"},"outline_h_60":{"declaration":"outline-color: var(--palette_h_60); --outline_color: var(--palette_h_60);"},"outline_h_70":{"declaration":"outline-color: var(--palette_h_70); --outline_color: var(--palette_h_70);"},"outline_h_80":{"declaration":"outline-color: var(--palette_h_80); --outline_color: var(--palette_h_80);"},"outline_h_90":{"declaration":"outline-color: var(--palette_h_90); --outline_color: var(--palette_h_90);"},"outline_h_95":{"declaration":"outline-color: var(--palette_h_95); --outline_color: var(--palette_h_95);"},"outline_h_100":{"declaration":"outline-color: var(--palette_h_100); --outline_color: var(--palette_h_100);"},"outline_i_00":{"declaration":"outline-color: var(--palette_i_00); --outline_color: var(--palette_i_00);"},"outline_i_05":{"declaration":"outline-color: var(--palette_i_05); --outline_color: var(--palette_i_05);"},"outline_i_10":{"declaration":"outline-color: var(--palette_i_10); --outline_color: var(--palette_i_10);"},"outline_i_20":{"declaration":"outline-color: var(--palette_i_20); --outline_color: var(--palette_i_20);"},"outline_i_30":{"declaration":"outline-color: var(--palette_i_30); --outline_color: var(--palette_i_30);"},"outline_i_40":{"declaration":"outline-color: var(--palette_i_40); --outline_color: var(--palette_i_40);"},"outline_i_50":{"declaration":"outline-color: var(--palette_i_50); --outline_color: var(--palette_i_50);"},"outline_i_60":{"declaration":"outline-color: var(--palette_i_60); --outline_color: var(--palette_i_60);"},"outline_i_70":{"declaration":"outline-color: var(--palette_i_70); --outline_color: var(--palette_i_70);"},"outline_i_80":{"declaration":"outline-color: var(--palette_i_80); --outline_color: var(--palette_i_80);"},"outline_i_90":{"declaration":"outline-color: var(--palette_i_90); --outline_color: var(--palette_i_90);"},"outline_i_95":{"declaration":"outline-color: var(--palette_i_95); --outline_color: var(--palette_i_95);"},"outline_i_100":{"declaration":"outline-color: var(--palette_i_100); --outline_color: var(--palette_i_100);"},"outline_j_00":{"declaration":"outline-color: var(--palette_j_00); --outline_color: var(--palette_j_00);"},"outline_j_05":{"declaration":"outline-color: var(--palette_j_05); --outline_color: var(--palette_j_05);"},"outline_j_10":{"declaration":"outline-color: var(--palette_j_10); --outline_color: var(--palette_j_10);"},"outline_j_20":{"declaration":"outline-color: var(--palette_j_20); --outline_color: var(--palette_j_20);"},"outline_j_30":{"declaration":"outline-color: var(--palette_j_30); --outline_color: var(--palette_j_30);"},"outline_j_40":{"declaration":"outline-color: var(--palette_j_40); --outline_color: var(--palette_j_40);"},"outline_j_50":{"declaration":"outline-color: var(--palette_j_50); --outline_color: var(--palette_j_50);"},"outline_j_60":{"declaration":"outline-color: var(--palette_j_60); --outline_color: var(--palette_j_60);"},"outline_j_70":{"declaration":"outline-color: var(--palette_j_70); --outline_color: var(--palette_j_70);"},"outline_j_80":{"declaration":"outline-color: var(--palette_j_80); --outline_color: var(--palette_j_80);"},"outline_j_90":{"declaration":"outline-color: var(--palette_j_90); --outline_color: var(--palette_j_90);"},"outline_j_95":{"declaration":"outline-color: var(--palette_j_95); --outline_color: var(--palette_j_95);"},"outline_j_100":{"declaration":"outline-color: var(--palette_j_100); --outline_color: var(--palette_j_100);"},"border_width_1":{"declaration":"border-width: var(--border_width_1);"},"border_width_2":{"declaration":"border-width: var(--border_width_2);"},"border_width_3":{"declaration":"border-width: var(--border_width_3);"},"border_width_4":{"declaration":"border-width: var(--border_width_4);"},"border_width_5":{"declaration":"border-width: var(--border_width_5);"},"border_width_6":{"declaration":"border-width: var(--border_width_6);"},"border_width_7":{"declaration":"border-width: var(--border_width_7);"},"border_width_8":{"declaration":"border-width: var(--border_width_8);"},"border_width_9":{"declaration":"border-width: var(--border_width_9);"},"outline_width_1":{"declaration":"outline-width: var(--border_width_1);"},"outline_width_2":{"declaration":"outline-width: var(--border_width_2);"},"outline_width_3":{"declaration":"outline-width: var(--border_width_3);"},"outline_width_4":{"declaration":"outline-width: var(--border_width_4);"},"outline_width_5":{"declaration":"outline-width: var(--border_width_5);"},"outline_width_6":{"declaration":"outline-width: var(--border_width_6);"},"outline_width_7":{"declaration":"outline-width: var(--border_width_7);"},"outline_width_8":{"declaration":"outline-width: var(--border_width_8);"},"outline_width_9":{"declaration":"outline-width: var(--border_width_9);"},"outline_width_focus":{"declaration":"outline-width: var(--outline_width_focus);"},"outline_width_active":{"declaration":"outline-width: var(--outline_width_active);"},"border_radius_xs3":{"declaration":"border-radius: var(--border_radius_xs3);"},"border_radius_xs2":{"declaration":"border-radius: var(--border_radius_xs2);"},"border_radius_xs":{"declaration":"border-radius: var(--border_radius_xs);"},"border_radius_sm":{"declaration":"border-radius: var(--border_radius_sm);"},"border_radius_md":{"declaration":"border-radius: var(--border_radius_md);"},"border_radius_lg":{"declaration":"border-radius: var(--border_radius_lg);"},"border_radius_xl":{"declaration":"border-radius: var(--border_radius_xl);"},"border_top_left_radius_xs3":{"declaration":"border-top-left-radius: var(--border_radius_xs3);"},"border_top_left_radius_xs2":{"declaration":"border-top-left-radius: var(--border_radius_xs2);"},"border_top_left_radius_xs":{"declaration":"border-top-left-radius: var(--border_radius_xs);"},"border_top_left_radius_sm":{"declaration":"border-top-left-radius: var(--border_radius_sm);"},"border_top_left_radius_md":{"declaration":"border-top-left-radius: var(--border_radius_md);"},"border_top_left_radius_lg":{"declaration":"border-top-left-radius: var(--border_radius_lg);"},"border_top_left_radius_xl":{"declaration":"border-top-left-radius: var(--border_radius_xl);"},"border_top_right_radius_xs3":{"declaration":"border-top-right-radius: var(--border_radius_xs3);"},"border_top_right_radius_xs2":{"declaration":"border-top-right-radius: var(--border_radius_xs2);"},"border_top_right_radius_xs":{"declaration":"border-top-right-radius: var(--border_radius_xs);"},"border_top_right_radius_sm":{"declaration":"border-top-right-radius: var(--border_radius_sm);"},"border_top_right_radius_md":{"declaration":"border-top-right-radius: var(--border_radius_md);"},"border_top_right_radius_lg":{"declaration":"border-top-right-radius: var(--border_radius_lg);"},"border_top_right_radius_xl":{"declaration":"border-top-right-radius: var(--border_radius_xl);"},"border_bottom_left_radius_xs3":{"declaration":"border-bottom-left-radius: var(--border_radius_xs3);"},"border_bottom_left_radius_xs2":{"declaration":"border-bottom-left-radius: var(--border_radius_xs2);"},"border_bottom_left_radius_xs":{"declaration":"border-bottom-left-radius: var(--border_radius_xs);"},"border_bottom_left_radius_sm":{"declaration":"border-bottom-left-radius: var(--border_radius_sm);"},"border_bottom_left_radius_md":{"declaration":"border-bottom-left-radius: var(--border_radius_md);"},"border_bottom_left_radius_lg":{"declaration":"border-bottom-left-radius: var(--border_radius_lg);"},"border_bottom_left_radius_xl":{"declaration":"border-bottom-left-radius: var(--border_radius_xl);"},"border_bottom_right_radius_xs3":{"declaration":"border-bottom-right-radius: var(--border_radius_xs3);"},"border_bottom_right_radius_xs2":{"declaration":"border-bottom-right-radius: var(--border_radius_xs2);"},"border_bottom_right_radius_xs":{"declaration":"border-bottom-right-radius: var(--border_radius_xs);"},"border_bottom_right_radius_sm":{"declaration":"border-bottom-right-radius: var(--border_radius_sm);"},"border_bottom_right_radius_md":{"declaration":"border-bottom-right-radius: var(--border_radius_md);"},"border_bottom_right_radius_lg":{"declaration":"border-bottom-right-radius: var(--border_radius_lg);"},"border_bottom_right_radius_xl":{"declaration":"border-bottom-right-radius: var(--border_radius_xl);"},"shadow_xs":{"declaration":"box-shadow: var(--shadow_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_sm":{"declaration":"box-shadow: var(--shadow_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_md":{"declaration":"box-shadow: var(--shadow_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_lg":{"declaration":"box-shadow: var(--shadow_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_xl":{"declaration":"box-shadow: var(--shadow_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_top_xs":{"declaration":"box-shadow: var(--shadow_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_top_sm":{"declaration":"box-shadow: var(--shadow_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_top_md":{"declaration":"box-shadow: var(--shadow_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_top_lg":{"declaration":"box-shadow: var(--shadow_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_top_xl":{"declaration":"box-shadow: var(--shadow_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_bottom_xs":{"declaration":"box-shadow: var(--shadow_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_bottom_sm":{"declaration":"box-shadow: var(--shadow_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_bottom_md":{"declaration":"box-shadow: var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_bottom_lg":{"declaration":"box-shadow: var(--shadow_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_bottom_xl":{"declaration":"box-shadow: var(--shadow_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_inset_xs":{"declaration":"box-shadow: var(--shadow_inset_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_inset_sm":{"declaration":"box-shadow: var(--shadow_inset_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_inset_md":{"declaration":"box-shadow: var(--shadow_inset_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_inset_lg":{"declaration":"box-shadow: var(--shadow_inset_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_inset_xl":{"declaration":"box-shadow: var(--shadow_inset_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_inset_top_xs":{"declaration":"box-shadow: var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_inset_top_sm":{"declaration":"box-shadow: var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_inset_top_md":{"declaration":"box-shadow: var(--shadow_inset_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_inset_top_lg":{"declaration":"box-shadow: var(--shadow_inset_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_inset_top_xl":{"declaration":"box-shadow: var(--shadow_inset_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_inset_bottom_xs":{"declaration":"box-shadow: var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_inset_bottom_sm":{"declaration":"box-shadow: var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_inset_bottom_md":{"declaration":"box-shadow: var(--shadow_inset_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_inset_bottom_lg":{"declaration":"box-shadow: var(--shadow_inset_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_inset_bottom_xl":{"declaration":"box-shadow: var(--shadow_inset_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_color_umbra":{"declaration":"--shadow_color: var(--shadow_color_umbra);"},"shadow_color_highlight":{"declaration":"--shadow_color: var(--shadow_color_highlight);"},"shadow_color_glow":{"declaration":"--shadow_color: var(--shadow_color_glow);"},"shadow_color_shroud":{"declaration":"--shadow_color: var(--shadow_color_shroud);"},"shadow_alpha_00":{"declaration":"--shadow_alpha: var(--shadow_alpha_00);"},"shadow_alpha_05":{"declaration":"--shadow_alpha: var(--shadow_alpha_05);"},"shadow_alpha_10":{"declaration":"--shadow_alpha: var(--shadow_alpha_10);"},"shadow_alpha_20":{"declaration":"--shadow_alpha: var(--shadow_alpha_20);"},"shadow_alpha_30":{"declaration":"--shadow_alpha: var(--shadow_alpha_30);"},"shadow_alpha_40":{"declaration":"--shadow_alpha: var(--shadow_alpha_40);"},"shadow_alpha_50":{"declaration":"--shadow_alpha: var(--shadow_alpha_50);"},"shadow_alpha_60":{"declaration":"--shadow_alpha: var(--shadow_alpha_60);"},"shadow_alpha_70":{"declaration":"--shadow_alpha: var(--shadow_alpha_70);"},"shadow_alpha_80":{"declaration":"--shadow_alpha: var(--shadow_alpha_80);"},"shadow_alpha_90":{"declaration":"--shadow_alpha: var(--shadow_alpha_90);"},"shadow_alpha_95":{"declaration":"--shadow_alpha: var(--shadow_alpha_95);"},"shadow_alpha_100":{"declaration":"--shadow_alpha: var(--shadow_alpha_100);"},"shadow_a_00":{"declaration":"--shadow_color: var(--palette_a_00);"},"shadow_a_05":{"declaration":"--shadow_color: var(--palette_a_05);"},"shadow_a_10":{"declaration":"--shadow_color: var(--palette_a_10);"},"shadow_a_20":{"declaration":"--shadow_color: var(--palette_a_20);"},"shadow_a_30":{"declaration":"--shadow_color: var(--palette_a_30);"},"shadow_a_40":{"declaration":"--shadow_color: var(--palette_a_40);"},"shadow_a_50":{"declaration":"--shadow_color: var(--palette_a_50);"},"shadow_a_60":{"declaration":"--shadow_color: var(--palette_a_60);"},"shadow_a_70":{"declaration":"--shadow_color: var(--palette_a_70);"},"shadow_a_80":{"declaration":"--shadow_color: var(--palette_a_80);"},"shadow_a_90":{"declaration":"--shadow_color: var(--palette_a_90);"},"shadow_a_95":{"declaration":"--shadow_color: var(--palette_a_95);"},"shadow_a_100":{"declaration":"--shadow_color: var(--palette_a_100);"},"shadow_b_00":{"declaration":"--shadow_color: var(--palette_b_00);"},"shadow_b_05":{"declaration":"--shadow_color: var(--palette_b_05);"},"shadow_b_10":{"declaration":"--shadow_color: var(--palette_b_10);"},"shadow_b_20":{"declaration":"--shadow_color: var(--palette_b_20);"},"shadow_b_30":{"declaration":"--shadow_color: var(--palette_b_30);"},"shadow_b_40":{"declaration":"--shadow_color: var(--palette_b_40);"},"shadow_b_50":{"declaration":"--shadow_color: var(--palette_b_50);"},"shadow_b_60":{"declaration":"--shadow_color: var(--palette_b_60);"},"shadow_b_70":{"declaration":"--shadow_color: var(--palette_b_70);"},"shadow_b_80":{"declaration":"--shadow_color: var(--palette_b_80);"},"shadow_b_90":{"declaration":"--shadow_color: var(--palette_b_90);"},"shadow_b_95":{"declaration":"--shadow_color: var(--palette_b_95);"},"shadow_b_100":{"declaration":"--shadow_color: var(--palette_b_100);"},"shadow_c_00":{"declaration":"--shadow_color: var(--palette_c_00);"},"shadow_c_05":{"declaration":"--shadow_color: var(--palette_c_05);"},"shadow_c_10":{"declaration":"--shadow_color: var(--palette_c_10);"},"shadow_c_20":{"declaration":"--shadow_color: var(--palette_c_20);"},"shadow_c_30":{"declaration":"--shadow_color: var(--palette_c_30);"},"shadow_c_40":{"declaration":"--shadow_color: var(--palette_c_40);"},"shadow_c_50":{"declaration":"--shadow_color: var(--palette_c_50);"},"shadow_c_60":{"declaration":"--shadow_color: var(--palette_c_60);"},"shadow_c_70":{"declaration":"--shadow_color: var(--palette_c_70);"},"shadow_c_80":{"declaration":"--shadow_color: var(--palette_c_80);"},"shadow_c_90":{"declaration":"--shadow_color: var(--palette_c_90);"},"shadow_c_95":{"declaration":"--shadow_color: var(--palette_c_95);"},"shadow_c_100":{"declaration":"--shadow_color: var(--palette_c_100);"},"shadow_d_00":{"declaration":"--shadow_color: var(--palette_d_00);"},"shadow_d_05":{"declaration":"--shadow_color: var(--palette_d_05);"},"shadow_d_10":{"declaration":"--shadow_color: var(--palette_d_10);"},"shadow_d_20":{"declaration":"--shadow_color: var(--palette_d_20);"},"shadow_d_30":{"declaration":"--shadow_color: var(--palette_d_30);"},"shadow_d_40":{"declaration":"--shadow_color: var(--palette_d_40);"},"shadow_d_50":{"declaration":"--shadow_color: var(--palette_d_50);"},"shadow_d_60":{"declaration":"--shadow_color: var(--palette_d_60);"},"shadow_d_70":{"declaration":"--shadow_color: var(--palette_d_70);"},"shadow_d_80":{"declaration":"--shadow_color: var(--palette_d_80);"},"shadow_d_90":{"declaration":"--shadow_color: var(--palette_d_90);"},"shadow_d_95":{"declaration":"--shadow_color: var(--palette_d_95);"},"shadow_d_100":{"declaration":"--shadow_color: var(--palette_d_100);"},"shadow_e_00":{"declaration":"--shadow_color: var(--palette_e_00);"},"shadow_e_05":{"declaration":"--shadow_color: var(--palette_e_05);"},"shadow_e_10":{"declaration":"--shadow_color: var(--palette_e_10);"},"shadow_e_20":{"declaration":"--shadow_color: var(--palette_e_20);"},"shadow_e_30":{"declaration":"--shadow_color: var(--palette_e_30);"},"shadow_e_40":{"declaration":"--shadow_color: var(--palette_e_40);"},"shadow_e_50":{"declaration":"--shadow_color: var(--palette_e_50);"},"shadow_e_60":{"declaration":"--shadow_color: var(--palette_e_60);"},"shadow_e_70":{"declaration":"--shadow_color: var(--palette_e_70);"},"shadow_e_80":{"declaration":"--shadow_color: var(--palette_e_80);"},"shadow_e_90":{"declaration":"--shadow_color: var(--palette_e_90);"},"shadow_e_95":{"declaration":"--shadow_color: var(--palette_e_95);"},"shadow_e_100":{"declaration":"--shadow_color: var(--palette_e_100);"},"shadow_f_00":{"declaration":"--shadow_color: var(--palette_f_00);"},"shadow_f_05":{"declaration":"--shadow_color: var(--palette_f_05);"},"shadow_f_10":{"declaration":"--shadow_color: var(--palette_f_10);"},"shadow_f_20":{"declaration":"--shadow_color: var(--palette_f_20);"},"shadow_f_30":{"declaration":"--shadow_color: var(--palette_f_30);"},"shadow_f_40":{"declaration":"--shadow_color: var(--palette_f_40);"},"shadow_f_50":{"declaration":"--shadow_color: var(--palette_f_50);"},"shadow_f_60":{"declaration":"--shadow_color: var(--palette_f_60);"},"shadow_f_70":{"declaration":"--shadow_color: var(--palette_f_70);"},"shadow_f_80":{"declaration":"--shadow_color: var(--palette_f_80);"},"shadow_f_90":{"declaration":"--shadow_color: var(--palette_f_90);"},"shadow_f_95":{"declaration":"--shadow_color: var(--palette_f_95);"},"shadow_f_100":{"declaration":"--shadow_color: var(--palette_f_100);"},"shadow_g_00":{"declaration":"--shadow_color: var(--palette_g_00);"},"shadow_g_05":{"declaration":"--shadow_color: var(--palette_g_05);"},"shadow_g_10":{"declaration":"--shadow_color: var(--palette_g_10);"},"shadow_g_20":{"declaration":"--shadow_color: var(--palette_g_20);"},"shadow_g_30":{"declaration":"--shadow_color: var(--palette_g_30);"},"shadow_g_40":{"declaration":"--shadow_color: var(--palette_g_40);"},"shadow_g_50":{"declaration":"--shadow_color: var(--palette_g_50);"},"shadow_g_60":{"declaration":"--shadow_color: var(--palette_g_60);"},"shadow_g_70":{"declaration":"--shadow_color: var(--palette_g_70);"},"shadow_g_80":{"declaration":"--shadow_color: var(--palette_g_80);"},"shadow_g_90":{"declaration":"--shadow_color: var(--palette_g_90);"},"shadow_g_95":{"declaration":"--shadow_color: var(--palette_g_95);"},"shadow_g_100":{"declaration":"--shadow_color: var(--palette_g_100);"},"shadow_h_00":{"declaration":"--shadow_color: var(--palette_h_00);"},"shadow_h_05":{"declaration":"--shadow_color: var(--palette_h_05);"},"shadow_h_10":{"declaration":"--shadow_color: var(--palette_h_10);"},"shadow_h_20":{"declaration":"--shadow_color: var(--palette_h_20);"},"shadow_h_30":{"declaration":"--shadow_color: var(--palette_h_30);"},"shadow_h_40":{"declaration":"--shadow_color: var(--palette_h_40);"},"shadow_h_50":{"declaration":"--shadow_color: var(--palette_h_50);"},"shadow_h_60":{"declaration":"--shadow_color: var(--palette_h_60);"},"shadow_h_70":{"declaration":"--shadow_color: var(--palette_h_70);"},"shadow_h_80":{"declaration":"--shadow_color: var(--palette_h_80);"},"shadow_h_90":{"declaration":"--shadow_color: var(--palette_h_90);"},"shadow_h_95":{"declaration":"--shadow_color: var(--palette_h_95);"},"shadow_h_100":{"declaration":"--shadow_color: var(--palette_h_100);"},"shadow_i_00":{"declaration":"--shadow_color: var(--palette_i_00);"},"shadow_i_05":{"declaration":"--shadow_color: var(--palette_i_05);"},"shadow_i_10":{"declaration":"--shadow_color: var(--palette_i_10);"},"shadow_i_20":{"declaration":"--shadow_color: var(--palette_i_20);"},"shadow_i_30":{"declaration":"--shadow_color: var(--palette_i_30);"},"shadow_i_40":{"declaration":"--shadow_color: var(--palette_i_40);"},"shadow_i_50":{"declaration":"--shadow_color: var(--palette_i_50);"},"shadow_i_60":{"declaration":"--shadow_color: var(--palette_i_60);"},"shadow_i_70":{"declaration":"--shadow_color: var(--palette_i_70);"},"shadow_i_80":{"declaration":"--shadow_color: var(--palette_i_80);"},"shadow_i_90":{"declaration":"--shadow_color: var(--palette_i_90);"},"shadow_i_95":{"declaration":"--shadow_color: var(--palette_i_95);"},"shadow_i_100":{"declaration":"--shadow_color: var(--palette_i_100);"},"shadow_j_00":{"declaration":"--shadow_color: var(--palette_j_00);"},"shadow_j_05":{"declaration":"--shadow_color: var(--palette_j_05);"},"shadow_j_10":{"declaration":"--shadow_color: var(--palette_j_10);"},"shadow_j_20":{"declaration":"--shadow_color: var(--palette_j_20);"},"shadow_j_30":{"declaration":"--shadow_color: var(--palette_j_30);"},"shadow_j_40":{"declaration":"--shadow_color: var(--palette_j_40);"},"shadow_j_50":{"declaration":"--shadow_color: var(--palette_j_50);"},"shadow_j_60":{"declaration":"--shadow_color: var(--palette_j_60);"},"shadow_j_70":{"declaration":"--shadow_color: var(--palette_j_70);"},"shadow_j_80":{"declaration":"--shadow_color: var(--palette_j_80);"},"shadow_j_90":{"declaration":"--shadow_color: var(--palette_j_90);"},"shadow_j_95":{"declaration":"--shadow_color: var(--palette_j_95);"},"shadow_j_100":{"declaration":"--shadow_color: var(--palette_j_100);"},"width_xs5":{"declaration":"width: var(--space_xs5);"},"width_xs4":{"declaration":"width: var(--space_xs4);"},"width_xs3":{"declaration":"width: var(--space_xs3);"},"width_xs2":{"declaration":"width: var(--space_xs2);"},"width_xs":{"declaration":"width: var(--space_xs);"},"width_sm":{"declaration":"width: var(--space_sm);"},"width_md":{"declaration":"width: var(--space_md);"},"width_lg":{"declaration":"width: var(--space_lg);"},"width_xl":{"declaration":"width: var(--space_xl);"},"width_xl2":{"declaration":"width: var(--space_xl2);"},"width_xl3":{"declaration":"width: var(--space_xl3);"},"width_xl4":{"declaration":"width: var(--space_xl4);"},"width_xl5":{"declaration":"width: var(--space_xl5);"},"width_xl6":{"declaration":"width: var(--space_xl6);"},"width_xl7":{"declaration":"width: var(--space_xl7);"},"width_xl8":{"declaration":"width: var(--space_xl8);"},"width_xl9":{"declaration":"width: var(--space_xl9);"},"width_xl10":{"declaration":"width: var(--space_xl10);"},"width_xl11":{"declaration":"width: var(--space_xl11);"},"width_xl12":{"declaration":"width: var(--space_xl12);"},"width_xl13":{"declaration":"width: var(--space_xl13);"},"width_xl14":{"declaration":"width: var(--space_xl14);"},"width_xl15":{"declaration":"width: var(--space_xl15);"},"height_xs5":{"declaration":"height: var(--space_xs5);"},"height_xs4":{"declaration":"height: var(--space_xs4);"},"height_xs3":{"declaration":"height: var(--space_xs3);"},"height_xs2":{"declaration":"height: var(--space_xs2);"},"height_xs":{"declaration":"height: var(--space_xs);"},"height_sm":{"declaration":"height: var(--space_sm);"},"height_md":{"declaration":"height: var(--space_md);"},"height_lg":{"declaration":"height: var(--space_lg);"},"height_xl":{"declaration":"height: var(--space_xl);"},"height_xl2":{"declaration":"height: var(--space_xl2);"},"height_xl3":{"declaration":"height: var(--space_xl3);"},"height_xl4":{"declaration":"height: var(--space_xl4);"},"height_xl5":{"declaration":"height: var(--space_xl5);"},"height_xl6":{"declaration":"height: var(--space_xl6);"},"height_xl7":{"declaration":"height: var(--space_xl7);"},"height_xl8":{"declaration":"height: var(--space_xl8);"},"height_xl9":{"declaration":"height: var(--space_xl9);"},"height_xl10":{"declaration":"height: var(--space_xl10);"},"height_xl11":{"declaration":"height: var(--space_xl11);"},"height_xl12":{"declaration":"height: var(--space_xl12);"},"height_xl13":{"declaration":"height: var(--space_xl13);"},"height_xl14":{"declaration":"height: var(--space_xl14);"},"height_xl15":{"declaration":"height: var(--space_xl15);"},"width_atmost_xs":{"declaration":"width: 100%; max-width: var(--distance_xs);"},"width_atmost_sm":{"declaration":"width: 100%; max-width: var(--distance_sm);"},"width_atmost_md":{"declaration":"width: 100%; max-width: var(--distance_md);"},"width_atmost_lg":{"declaration":"width: 100%; max-width: var(--distance_lg);"},"width_atmost_xl":{"declaration":"width: 100%; max-width: var(--distance_xl);"},"width_atleast_xs":{"declaration":"width: 100%; min-width: var(--distance_xs);"},"width_atleast_sm":{"declaration":"width: 100%; min-width: var(--distance_sm);"},"width_atleast_md":{"declaration":"width: 100%; min-width: var(--distance_md);"},"width_atleast_lg":{"declaration":"width: 100%; min-width: var(--distance_lg);"},"width_atleast_xl":{"declaration":"width: 100%; min-width: var(--distance_xl);"},"height_atmost_xs":{"declaration":"height: 100%; max-height: var(--distance_xs);"},"height_atmost_sm":{"declaration":"height: 100%; max-height: var(--distance_sm);"},"height_atmost_md":{"declaration":"height: 100%; max-height: var(--distance_md);"},"height_atmost_lg":{"declaration":"height: 100%; max-height: var(--distance_lg);"},"height_atmost_xl":{"declaration":"height: 100%; max-height: var(--distance_xl);"},"height_atleast_xs":{"declaration":"height: 100%; min-height: var(--distance_xs);"},"height_atleast_sm":{"declaration":"height: 100%; min-height: var(--distance_sm);"},"height_atleast_md":{"declaration":"height: 100%; min-height: var(--distance_md);"},"height_atleast_lg":{"declaration":"height: 100%; min-height: var(--distance_lg);"},"height_atleast_xl":{"declaration":"height: 100%; min-height: var(--distance_xl);"},"top_xs5":{"declaration":"top: var(--space_xs5);"},"top_xs4":{"declaration":"top: var(--space_xs4);"},"top_xs3":{"declaration":"top: var(--space_xs3);"},"top_xs2":{"declaration":"top: var(--space_xs2);"},"top_xs":{"declaration":"top: var(--space_xs);"},"top_sm":{"declaration":"top: var(--space_sm);"},"top_md":{"declaration":"top: var(--space_md);"},"top_lg":{"declaration":"top: var(--space_lg);"},"top_xl":{"declaration":"top: var(--space_xl);"},"top_xl2":{"declaration":"top: var(--space_xl2);"},"top_xl3":{"declaration":"top: var(--space_xl3);"},"top_xl4":{"declaration":"top: var(--space_xl4);"},"top_xl5":{"declaration":"top: var(--space_xl5);"},"top_xl6":{"declaration":"top: var(--space_xl6);"},"top_xl7":{"declaration":"top: var(--space_xl7);"},"top_xl8":{"declaration":"top: var(--space_xl8);"},"top_xl9":{"declaration":"top: var(--space_xl9);"},"top_xl10":{"declaration":"top: var(--space_xl10);"},"top_xl11":{"declaration":"top: var(--space_xl11);"},"top_xl12":{"declaration":"top: var(--space_xl12);"},"top_xl13":{"declaration":"top: var(--space_xl13);"},"top_xl14":{"declaration":"top: var(--space_xl14);"},"top_xl15":{"declaration":"top: var(--space_xl15);"},"right_xs5":{"declaration":"right: var(--space_xs5);"},"right_xs4":{"declaration":"right: var(--space_xs4);"},"right_xs3":{"declaration":"right: var(--space_xs3);"},"right_xs2":{"declaration":"right: var(--space_xs2);"},"right_xs":{"declaration":"right: var(--space_xs);"},"right_sm":{"declaration":"right: var(--space_sm);"},"right_md":{"declaration":"right: var(--space_md);"},"right_lg":{"declaration":"right: var(--space_lg);"},"right_xl":{"declaration":"right: var(--space_xl);"},"right_xl2":{"declaration":"right: var(--space_xl2);"},"right_xl3":{"declaration":"right: var(--space_xl3);"},"right_xl4":{"declaration":"right: var(--space_xl4);"},"right_xl5":{"declaration":"right: var(--space_xl5);"},"right_xl6":{"declaration":"right: var(--space_xl6);"},"right_xl7":{"declaration":"right: var(--space_xl7);"},"right_xl8":{"declaration":"right: var(--space_xl8);"},"right_xl9":{"declaration":"right: var(--space_xl9);"},"right_xl10":{"declaration":"right: var(--space_xl10);"},"right_xl11":{"declaration":"right: var(--space_xl11);"},"right_xl12":{"declaration":"right: var(--space_xl12);"},"right_xl13":{"declaration":"right: var(--space_xl13);"},"right_xl14":{"declaration":"right: var(--space_xl14);"},"right_xl15":{"declaration":"right: var(--space_xl15);"},"bottom_xs5":{"declaration":"bottom: var(--space_xs5);"},"bottom_xs4":{"declaration":"bottom: var(--space_xs4);"},"bottom_xs3":{"declaration":"bottom: var(--space_xs3);"},"bottom_xs2":{"declaration":"bottom: var(--space_xs2);"},"bottom_xs":{"declaration":"bottom: var(--space_xs);"},"bottom_sm":{"declaration":"bottom: var(--space_sm);"},"bottom_md":{"declaration":"bottom: var(--space_md);"},"bottom_lg":{"declaration":"bottom: var(--space_lg);"},"bottom_xl":{"declaration":"bottom: var(--space_xl);"},"bottom_xl2":{"declaration":"bottom: var(--space_xl2);"},"bottom_xl3":{"declaration":"bottom: var(--space_xl3);"},"bottom_xl4":{"declaration":"bottom: var(--space_xl4);"},"bottom_xl5":{"declaration":"bottom: var(--space_xl5);"},"bottom_xl6":{"declaration":"bottom: var(--space_xl6);"},"bottom_xl7":{"declaration":"bottom: var(--space_xl7);"},"bottom_xl8":{"declaration":"bottom: var(--space_xl8);"},"bottom_xl9":{"declaration":"bottom: var(--space_xl9);"},"bottom_xl10":{"declaration":"bottom: var(--space_xl10);"},"bottom_xl11":{"declaration":"bottom: var(--space_xl11);"},"bottom_xl12":{"declaration":"bottom: var(--space_xl12);"},"bottom_xl13":{"declaration":"bottom: var(--space_xl13);"},"bottom_xl14":{"declaration":"bottom: var(--space_xl14);"},"bottom_xl15":{"declaration":"bottom: var(--space_xl15);"},"left_xs5":{"declaration":"left: var(--space_xs5);"},"left_xs4":{"declaration":"left: var(--space_xs4);"},"left_xs3":{"declaration":"left: var(--space_xs3);"},"left_xs2":{"declaration":"left: var(--space_xs2);"},"left_xs":{"declaration":"left: var(--space_xs);"},"left_sm":{"declaration":"left: var(--space_sm);"},"left_md":{"declaration":"left: var(--space_md);"},"left_lg":{"declaration":"left: var(--space_lg);"},"left_xl":{"declaration":"left: var(--space_xl);"},"left_xl2":{"declaration":"left: var(--space_xl2);"},"left_xl3":{"declaration":"left: var(--space_xl3);"},"left_xl4":{"declaration":"left: var(--space_xl4);"},"left_xl5":{"declaration":"left: var(--space_xl5);"},"left_xl6":{"declaration":"left: var(--space_xl6);"},"left_xl7":{"declaration":"left: var(--space_xl7);"},"left_xl8":{"declaration":"left: var(--space_xl8);"},"left_xl9":{"declaration":"left: var(--space_xl9);"},"left_xl10":{"declaration":"left: var(--space_xl10);"},"left_xl11":{"declaration":"left: var(--space_xl11);"},"left_xl12":{"declaration":"left: var(--space_xl12);"},"left_xl13":{"declaration":"left: var(--space_xl13);"},"left_xl14":{"declaration":"left: var(--space_xl14);"},"left_xl15":{"declaration":"left: var(--space_xl15);"},"inset_xs5":{"declaration":"inset: var(--space_xs5);"},"inset_xs4":{"declaration":"inset: var(--space_xs4);"},"inset_xs3":{"declaration":"inset: var(--space_xs3);"},"inset_xs2":{"declaration":"inset: var(--space_xs2);"},"inset_xs":{"declaration":"inset: var(--space_xs);"},"inset_sm":{"declaration":"inset: var(--space_sm);"},"inset_md":{"declaration":"inset: var(--space_md);"},"inset_lg":{"declaration":"inset: var(--space_lg);"},"inset_xl":{"declaration":"inset: var(--space_xl);"},"inset_xl2":{"declaration":"inset: var(--space_xl2);"},"inset_xl3":{"declaration":"inset: var(--space_xl3);"},"inset_xl4":{"declaration":"inset: var(--space_xl4);"},"inset_xl5":{"declaration":"inset: var(--space_xl5);"},"inset_xl6":{"declaration":"inset: var(--space_xl6);"},"inset_xl7":{"declaration":"inset: var(--space_xl7);"},"inset_xl8":{"declaration":"inset: var(--space_xl8);"},"inset_xl9":{"declaration":"inset: var(--space_xl9);"},"inset_xl10":{"declaration":"inset: var(--space_xl10);"},"inset_xl11":{"declaration":"inset: var(--space_xl11);"},"inset_xl12":{"declaration":"inset: var(--space_xl12);"},"inset_xl13":{"declaration":"inset: var(--space_xl13);"},"inset_xl14":{"declaration":"inset: var(--space_xl14);"},"inset_xl15":{"declaration":"inset: var(--space_xl15);"},"p_0":{"declaration":"padding: 0;"},"p_xs5":{"declaration":"padding: var(--space_xs5);"},"p_xs4":{"declaration":"padding: var(--space_xs4);"},"p_xs3":{"declaration":"padding: var(--space_xs3);"},"p_xs2":{"declaration":"padding: var(--space_xs2);"},"p_xs":{"declaration":"padding: var(--space_xs);"},"p_sm":{"declaration":"padding: var(--space_sm);"},"p_md":{"declaration":"padding: var(--space_md);"},"p_lg":{"declaration":"padding: var(--space_lg);"},"p_xl":{"declaration":"padding: var(--space_xl);"},"p_xl2":{"declaration":"padding: var(--space_xl2);"},"p_xl3":{"declaration":"padding: var(--space_xl3);"},"p_xl4":{"declaration":"padding: var(--space_xl4);"},"p_xl5":{"declaration":"padding: var(--space_xl5);"},"p_xl6":{"declaration":"padding: var(--space_xl6);"},"p_xl7":{"declaration":"padding: var(--space_xl7);"},"p_xl8":{"declaration":"padding: var(--space_xl8);"},"p_xl9":{"declaration":"padding: var(--space_xl9);"},"p_xl10":{"declaration":"padding: var(--space_xl10);"},"p_xl11":{"declaration":"padding: var(--space_xl11);"},"p_xl12":{"declaration":"padding: var(--space_xl12);"},"p_xl13":{"declaration":"padding: var(--space_xl13);"},"p_xl14":{"declaration":"padding: var(--space_xl14);"},"p_xl15":{"declaration":"padding: var(--space_xl15);"},"pt_0":{"declaration":"padding-top: 0;"},"pt_xs5":{"declaration":"padding-top: var(--space_xs5);"},"pt_xs4":{"declaration":"padding-top: var(--space_xs4);"},"pt_xs3":{"declaration":"padding-top: var(--space_xs3);"},"pt_xs2":{"declaration":"padding-top: var(--space_xs2);"},"pt_xs":{"declaration":"padding-top: var(--space_xs);"},"pt_sm":{"declaration":"padding-top: var(--space_sm);"},"pt_md":{"declaration":"padding-top: var(--space_md);"},"pt_lg":{"declaration":"padding-top: var(--space_lg);"},"pt_xl":{"declaration":"padding-top: var(--space_xl);"},"pt_xl2":{"declaration":"padding-top: var(--space_xl2);"},"pt_xl3":{"declaration":"padding-top: var(--space_xl3);"},"pt_xl4":{"declaration":"padding-top: var(--space_xl4);"},"pt_xl5":{"declaration":"padding-top: var(--space_xl5);"},"pt_xl6":{"declaration":"padding-top: var(--space_xl6);"},"pt_xl7":{"declaration":"padding-top: var(--space_xl7);"},"pt_xl8":{"declaration":"padding-top: var(--space_xl8);"},"pt_xl9":{"declaration":"padding-top: var(--space_xl9);"},"pt_xl10":{"declaration":"padding-top: var(--space_xl10);"},"pt_xl11":{"declaration":"padding-top: var(--space_xl11);"},"pt_xl12":{"declaration":"padding-top: var(--space_xl12);"},"pt_xl13":{"declaration":"padding-top: var(--space_xl13);"},"pt_xl14":{"declaration":"padding-top: var(--space_xl14);"},"pt_xl15":{"declaration":"padding-top: var(--space_xl15);"},"pr_0":{"declaration":"padding-right: 0;"},"pr_xs5":{"declaration":"padding-right: var(--space_xs5);"},"pr_xs4":{"declaration":"padding-right: var(--space_xs4);"},"pr_xs3":{"declaration":"padding-right: var(--space_xs3);"},"pr_xs2":{"declaration":"padding-right: var(--space_xs2);"},"pr_xs":{"declaration":"padding-right: var(--space_xs);"},"pr_sm":{"declaration":"padding-right: var(--space_sm);"},"pr_md":{"declaration":"padding-right: var(--space_md);"},"pr_lg":{"declaration":"padding-right: var(--space_lg);"},"pr_xl":{"declaration":"padding-right: var(--space_xl);"},"pr_xl2":{"declaration":"padding-right: var(--space_xl2);"},"pr_xl3":{"declaration":"padding-right: var(--space_xl3);"},"pr_xl4":{"declaration":"padding-right: var(--space_xl4);"},"pr_xl5":{"declaration":"padding-right: var(--space_xl5);"},"pr_xl6":{"declaration":"padding-right: var(--space_xl6);"},"pr_xl7":{"declaration":"padding-right: var(--space_xl7);"},"pr_xl8":{"declaration":"padding-right: var(--space_xl8);"},"pr_xl9":{"declaration":"padding-right: var(--space_xl9);"},"pr_xl10":{"declaration":"padding-right: var(--space_xl10);"},"pr_xl11":{"declaration":"padding-right: var(--space_xl11);"},"pr_xl12":{"declaration":"padding-right: var(--space_xl12);"},"pr_xl13":{"declaration":"padding-right: var(--space_xl13);"},"pr_xl14":{"declaration":"padding-right: var(--space_xl14);"},"pr_xl15":{"declaration":"padding-right: var(--space_xl15);"},"pb_0":{"declaration":"padding-bottom: 0;"},"pb_xs5":{"declaration":"padding-bottom: var(--space_xs5);"},"pb_xs4":{"declaration":"padding-bottom: var(--space_xs4);"},"pb_xs3":{"declaration":"padding-bottom: var(--space_xs3);"},"pb_xs2":{"declaration":"padding-bottom: var(--space_xs2);"},"pb_xs":{"declaration":"padding-bottom: var(--space_xs);"},"pb_sm":{"declaration":"padding-bottom: var(--space_sm);"},"pb_md":{"declaration":"padding-bottom: var(--space_md);"},"pb_lg":{"declaration":"padding-bottom: var(--space_lg);"},"pb_xl":{"declaration":"padding-bottom: var(--space_xl);"},"pb_xl2":{"declaration":"padding-bottom: var(--space_xl2);"},"pb_xl3":{"declaration":"padding-bottom: var(--space_xl3);"},"pb_xl4":{"declaration":"padding-bottom: var(--space_xl4);"},"pb_xl5":{"declaration":"padding-bottom: var(--space_xl5);"},"pb_xl6":{"declaration":"padding-bottom: var(--space_xl6);"},"pb_xl7":{"declaration":"padding-bottom: var(--space_xl7);"},"pb_xl8":{"declaration":"padding-bottom: var(--space_xl8);"},"pb_xl9":{"declaration":"padding-bottom: var(--space_xl9);"},"pb_xl10":{"declaration":"padding-bottom: var(--space_xl10);"},"pb_xl11":{"declaration":"padding-bottom: var(--space_xl11);"},"pb_xl12":{"declaration":"padding-bottom: var(--space_xl12);"},"pb_xl13":{"declaration":"padding-bottom: var(--space_xl13);"},"pb_xl14":{"declaration":"padding-bottom: var(--space_xl14);"},"pb_xl15":{"declaration":"padding-bottom: var(--space_xl15);"},"pl_0":{"declaration":"padding-left: 0;"},"pl_xs5":{"declaration":"padding-left: var(--space_xs5);"},"pl_xs4":{"declaration":"padding-left: var(--space_xs4);"},"pl_xs3":{"declaration":"padding-left: var(--space_xs3);"},"pl_xs2":{"declaration":"padding-left: var(--space_xs2);"},"pl_xs":{"declaration":"padding-left: var(--space_xs);"},"pl_sm":{"declaration":"padding-left: var(--space_sm);"},"pl_md":{"declaration":"padding-left: var(--space_md);"},"pl_lg":{"declaration":"padding-left: var(--space_lg);"},"pl_xl":{"declaration":"padding-left: var(--space_xl);"},"pl_xl2":{"declaration":"padding-left: var(--space_xl2);"},"pl_xl3":{"declaration":"padding-left: var(--space_xl3);"},"pl_xl4":{"declaration":"padding-left: var(--space_xl4);"},"pl_xl5":{"declaration":"padding-left: var(--space_xl5);"},"pl_xl6":{"declaration":"padding-left: var(--space_xl6);"},"pl_xl7":{"declaration":"padding-left: var(--space_xl7);"},"pl_xl8":{"declaration":"padding-left: var(--space_xl8);"},"pl_xl9":{"declaration":"padding-left: var(--space_xl9);"},"pl_xl10":{"declaration":"padding-left: var(--space_xl10);"},"pl_xl11":{"declaration":"padding-left: var(--space_xl11);"},"pl_xl12":{"declaration":"padding-left: var(--space_xl12);"},"pl_xl13":{"declaration":"padding-left: var(--space_xl13);"},"pl_xl14":{"declaration":"padding-left: var(--space_xl14);"},"pl_xl15":{"declaration":"padding-left: var(--space_xl15);"},"px_0":{"declaration":"padding-left: 0;\tpadding-right: 0;"},"px_xs5":{"declaration":"padding-left: var(--space_xs5);\tpadding-right: var(--space_xs5);"},"px_xs4":{"declaration":"padding-left: var(--space_xs4);\tpadding-right: var(--space_xs4);"},"px_xs3":{"declaration":"padding-left: var(--space_xs3);\tpadding-right: var(--space_xs3);"},"px_xs2":{"declaration":"padding-left: var(--space_xs2);\tpadding-right: var(--space_xs2);"},"px_xs":{"declaration":"padding-left: var(--space_xs);\tpadding-right: var(--space_xs);"},"px_sm":{"declaration":"padding-left: var(--space_sm);\tpadding-right: var(--space_sm);"},"px_md":{"declaration":"padding-left: var(--space_md);\tpadding-right: var(--space_md);"},"px_lg":{"declaration":"padding-left: var(--space_lg);\tpadding-right: var(--space_lg);"},"px_xl":{"declaration":"padding-left: var(--space_xl);\tpadding-right: var(--space_xl);"},"px_xl2":{"declaration":"padding-left: var(--space_xl2);\tpadding-right: var(--space_xl2);"},"px_xl3":{"declaration":"padding-left: var(--space_xl3);\tpadding-right: var(--space_xl3);"},"px_xl4":{"declaration":"padding-left: var(--space_xl4);\tpadding-right: var(--space_xl4);"},"px_xl5":{"declaration":"padding-left: var(--space_xl5);\tpadding-right: var(--space_xl5);"},"px_xl6":{"declaration":"padding-left: var(--space_xl6);\tpadding-right: var(--space_xl6);"},"px_xl7":{"declaration":"padding-left: var(--space_xl7);\tpadding-right: var(--space_xl7);"},"px_xl8":{"declaration":"padding-left: var(--space_xl8);\tpadding-right: var(--space_xl8);"},"px_xl9":{"declaration":"padding-left: var(--space_xl9);\tpadding-right: var(--space_xl9);"},"px_xl10":{"declaration":"padding-left: var(--space_xl10);\tpadding-right: var(--space_xl10);"},"px_xl11":{"declaration":"padding-left: var(--space_xl11);\tpadding-right: var(--space_xl11);"},"px_xl12":{"declaration":"padding-left: var(--space_xl12);\tpadding-right: var(--space_xl12);"},"px_xl13":{"declaration":"padding-left: var(--space_xl13);\tpadding-right: var(--space_xl13);"},"px_xl14":{"declaration":"padding-left: var(--space_xl14);\tpadding-right: var(--space_xl14);"},"px_xl15":{"declaration":"padding-left: var(--space_xl15);\tpadding-right: var(--space_xl15);"},"py_0":{"declaration":"padding-top: 0;\tpadding-bottom: 0;"},"py_xs5":{"declaration":"padding-top: var(--space_xs5);\tpadding-bottom: var(--space_xs5);"},"py_xs4":{"declaration":"padding-top: var(--space_xs4);\tpadding-bottom: var(--space_xs4);"},"py_xs3":{"declaration":"padding-top: var(--space_xs3);\tpadding-bottom: var(--space_xs3);"},"py_xs2":{"declaration":"padding-top: var(--space_xs2);\tpadding-bottom: var(--space_xs2);"},"py_xs":{"declaration":"padding-top: var(--space_xs);\tpadding-bottom: var(--space_xs);"},"py_sm":{"declaration":"padding-top: var(--space_sm);\tpadding-bottom: var(--space_sm);"},"py_md":{"declaration":"padding-top: var(--space_md);\tpadding-bottom: var(--space_md);"},"py_lg":{"declaration":"padding-top: var(--space_lg);\tpadding-bottom: var(--space_lg);"},"py_xl":{"declaration":"padding-top: var(--space_xl);\tpadding-bottom: var(--space_xl);"},"py_xl2":{"declaration":"padding-top: var(--space_xl2);\tpadding-bottom: var(--space_xl2);"},"py_xl3":{"declaration":"padding-top: var(--space_xl3);\tpadding-bottom: var(--space_xl3);"},"py_xl4":{"declaration":"padding-top: var(--space_xl4);\tpadding-bottom: var(--space_xl4);"},"py_xl5":{"declaration":"padding-top: var(--space_xl5);\tpadding-bottom: var(--space_xl5);"},"py_xl6":{"declaration":"padding-top: var(--space_xl6);\tpadding-bottom: var(--space_xl6);"},"py_xl7":{"declaration":"padding-top: var(--space_xl7);\tpadding-bottom: var(--space_xl7);"},"py_xl8":{"declaration":"padding-top: var(--space_xl8);\tpadding-bottom: var(--space_xl8);"},"py_xl9":{"declaration":"padding-top: var(--space_xl9);\tpadding-bottom: var(--space_xl9);"},"py_xl10":{"declaration":"padding-top: var(--space_xl10);\tpadding-bottom: var(--space_xl10);"},"py_xl11":{"declaration":"padding-top: var(--space_xl11);\tpadding-bottom: var(--space_xl11);"},"py_xl12":{"declaration":"padding-top: var(--space_xl12);\tpadding-bottom: var(--space_xl12);"},"py_xl13":{"declaration":"padding-top: var(--space_xl13);\tpadding-bottom: var(--space_xl13);"},"py_xl14":{"declaration":"padding-top: var(--space_xl14);\tpadding-bottom: var(--space_xl14);"},"py_xl15":{"declaration":"padding-top: var(--space_xl15);\tpadding-bottom: var(--space_xl15);"},"m_0":{"declaration":"margin: 0;"},"m_auto":{"declaration":"margin: auto;"},"m_xs5":{"declaration":"margin: var(--space_xs5);"},"m_xs4":{"declaration":"margin: var(--space_xs4);"},"m_xs3":{"declaration":"margin: var(--space_xs3);"},"m_xs2":{"declaration":"margin: var(--space_xs2);"},"m_xs":{"declaration":"margin: var(--space_xs);"},"m_sm":{"declaration":"margin: var(--space_sm);"},"m_md":{"declaration":"margin: var(--space_md);"},"m_lg":{"declaration":"margin: var(--space_lg);"},"m_xl":{"declaration":"margin: var(--space_xl);"},"m_xl2":{"declaration":"margin: var(--space_xl2);"},"m_xl3":{"declaration":"margin: var(--space_xl3);"},"m_xl4":{"declaration":"margin: var(--space_xl4);"},"m_xl5":{"declaration":"margin: var(--space_xl5);"},"m_xl6":{"declaration":"margin: var(--space_xl6);"},"m_xl7":{"declaration":"margin: var(--space_xl7);"},"m_xl8":{"declaration":"margin: var(--space_xl8);"},"m_xl9":{"declaration":"margin: var(--space_xl9);"},"m_xl10":{"declaration":"margin: var(--space_xl10);"},"m_xl11":{"declaration":"margin: var(--space_xl11);"},"m_xl12":{"declaration":"margin: var(--space_xl12);"},"m_xl13":{"declaration":"margin: var(--space_xl13);"},"m_xl14":{"declaration":"margin: var(--space_xl14);"},"m_xl15":{"declaration":"margin: var(--space_xl15);"},"mt_0":{"declaration":"margin-top: 0;"},"mt_auto":{"declaration":"margin-top: auto;"},"mt_xs5":{"declaration":"margin-top: var(--space_xs5);"},"mt_xs4":{"declaration":"margin-top: var(--space_xs4);"},"mt_xs3":{"declaration":"margin-top: var(--space_xs3);"},"mt_xs2":{"declaration":"margin-top: var(--space_xs2);"},"mt_xs":{"declaration":"margin-top: var(--space_xs);"},"mt_sm":{"declaration":"margin-top: var(--space_sm);"},"mt_md":{"declaration":"margin-top: var(--space_md);"},"mt_lg":{"declaration":"margin-top: var(--space_lg);"},"mt_xl":{"declaration":"margin-top: var(--space_xl);"},"mt_xl2":{"declaration":"margin-top: var(--space_xl2);"},"mt_xl3":{"declaration":"margin-top: var(--space_xl3);"},"mt_xl4":{"declaration":"margin-top: var(--space_xl4);"},"mt_xl5":{"declaration":"margin-top: var(--space_xl5);"},"mt_xl6":{"declaration":"margin-top: var(--space_xl6);"},"mt_xl7":{"declaration":"margin-top: var(--space_xl7);"},"mt_xl8":{"declaration":"margin-top: var(--space_xl8);"},"mt_xl9":{"declaration":"margin-top: var(--space_xl9);"},"mt_xl10":{"declaration":"margin-top: var(--space_xl10);"},"mt_xl11":{"declaration":"margin-top: var(--space_xl11);"},"mt_xl12":{"declaration":"margin-top: var(--space_xl12);"},"mt_xl13":{"declaration":"margin-top: var(--space_xl13);"},"mt_xl14":{"declaration":"margin-top: var(--space_xl14);"},"mt_xl15":{"declaration":"margin-top: var(--space_xl15);"},"mr_0":{"declaration":"margin-right: 0;"},"mr_auto":{"declaration":"margin-right: auto;"},"mr_xs5":{"declaration":"margin-right: var(--space_xs5);"},"mr_xs4":{"declaration":"margin-right: var(--space_xs4);"},"mr_xs3":{"declaration":"margin-right: var(--space_xs3);"},"mr_xs2":{"declaration":"margin-right: var(--space_xs2);"},"mr_xs":{"declaration":"margin-right: var(--space_xs);"},"mr_sm":{"declaration":"margin-right: var(--space_sm);"},"mr_md":{"declaration":"margin-right: var(--space_md);"},"mr_lg":{"declaration":"margin-right: var(--space_lg);"},"mr_xl":{"declaration":"margin-right: var(--space_xl);"},"mr_xl2":{"declaration":"margin-right: var(--space_xl2);"},"mr_xl3":{"declaration":"margin-right: var(--space_xl3);"},"mr_xl4":{"declaration":"margin-right: var(--space_xl4);"},"mr_xl5":{"declaration":"margin-right: var(--space_xl5);"},"mr_xl6":{"declaration":"margin-right: var(--space_xl6);"},"mr_xl7":{"declaration":"margin-right: var(--space_xl7);"},"mr_xl8":{"declaration":"margin-right: var(--space_xl8);"},"mr_xl9":{"declaration":"margin-right: var(--space_xl9);"},"mr_xl10":{"declaration":"margin-right: var(--space_xl10);"},"mr_xl11":{"declaration":"margin-right: var(--space_xl11);"},"mr_xl12":{"declaration":"margin-right: var(--space_xl12);"},"mr_xl13":{"declaration":"margin-right: var(--space_xl13);"},"mr_xl14":{"declaration":"margin-right: var(--space_xl14);"},"mr_xl15":{"declaration":"margin-right: var(--space_xl15);"},"mb_0":{"declaration":"margin-bottom: 0;"},"mb_auto":{"declaration":"margin-bottom: auto;"},"mb_xs5":{"declaration":"margin-bottom: var(--space_xs5);"},"mb_xs4":{"declaration":"margin-bottom: var(--space_xs4);"},"mb_xs3":{"declaration":"margin-bottom: var(--space_xs3);"},"mb_xs2":{"declaration":"margin-bottom: var(--space_xs2);"},"mb_xs":{"declaration":"margin-bottom: var(--space_xs);"},"mb_sm":{"declaration":"margin-bottom: var(--space_sm);"},"mb_md":{"declaration":"margin-bottom: var(--space_md);"},"mb_lg":{"declaration":"margin-bottom: var(--space_lg);"},"mb_xl":{"declaration":"margin-bottom: var(--space_xl);"},"mb_xl2":{"declaration":"margin-bottom: var(--space_xl2);"},"mb_xl3":{"declaration":"margin-bottom: var(--space_xl3);"},"mb_xl4":{"declaration":"margin-bottom: var(--space_xl4);"},"mb_xl5":{"declaration":"margin-bottom: var(--space_xl5);"},"mb_xl6":{"declaration":"margin-bottom: var(--space_xl6);"},"mb_xl7":{"declaration":"margin-bottom: var(--space_xl7);"},"mb_xl8":{"declaration":"margin-bottom: var(--space_xl8);"},"mb_xl9":{"declaration":"margin-bottom: var(--space_xl9);"},"mb_xl10":{"declaration":"margin-bottom: var(--space_xl10);"},"mb_xl11":{"declaration":"margin-bottom: var(--space_xl11);"},"mb_xl12":{"declaration":"margin-bottom: var(--space_xl12);"},"mb_xl13":{"declaration":"margin-bottom: var(--space_xl13);"},"mb_xl14":{"declaration":"margin-bottom: var(--space_xl14);"},"mb_xl15":{"declaration":"margin-bottom: var(--space_xl15);"},"ml_0":{"declaration":"margin-left: 0;"},"ml_auto":{"declaration":"margin-left: auto;"},"ml_xs5":{"declaration":"margin-left: var(--space_xs5);"},"ml_xs4":{"declaration":"margin-left: var(--space_xs4);"},"ml_xs3":{"declaration":"margin-left: var(--space_xs3);"},"ml_xs2":{"declaration":"margin-left: var(--space_xs2);"},"ml_xs":{"declaration":"margin-left: var(--space_xs);"},"ml_sm":{"declaration":"margin-left: var(--space_sm);"},"ml_md":{"declaration":"margin-left: var(--space_md);"},"ml_lg":{"declaration":"margin-left: var(--space_lg);"},"ml_xl":{"declaration":"margin-left: var(--space_xl);"},"ml_xl2":{"declaration":"margin-left: var(--space_xl2);"},"ml_xl3":{"declaration":"margin-left: var(--space_xl3);"},"ml_xl4":{"declaration":"margin-left: var(--space_xl4);"},"ml_xl5":{"declaration":"margin-left: var(--space_xl5);"},"ml_xl6":{"declaration":"margin-left: var(--space_xl6);"},"ml_xl7":{"declaration":"margin-left: var(--space_xl7);"},"ml_xl8":{"declaration":"margin-left: var(--space_xl8);"},"ml_xl9":{"declaration":"margin-left: var(--space_xl9);"},"ml_xl10":{"declaration":"margin-left: var(--space_xl10);"},"ml_xl11":{"declaration":"margin-left: var(--space_xl11);"},"ml_xl12":{"declaration":"margin-left: var(--space_xl12);"},"ml_xl13":{"declaration":"margin-left: var(--space_xl13);"},"ml_xl14":{"declaration":"margin-left: var(--space_xl14);"},"ml_xl15":{"declaration":"margin-left: var(--space_xl15);"},"mx_0":{"declaration":"margin-left: 0;\tmargin-right: 0;"},"mx_auto":{"declaration":"margin-left: auto;\tmargin-right: auto;"},"mx_xs5":{"declaration":"margin-left: var(--space_xs5);\tmargin-right: var(--space_xs5);"},"mx_xs4":{"declaration":"margin-left: var(--space_xs4);\tmargin-right: var(--space_xs4);"},"mx_xs3":{"declaration":"margin-left: var(--space_xs3);\tmargin-right: var(--space_xs3);"},"mx_xs2":{"declaration":"margin-left: var(--space_xs2);\tmargin-right: var(--space_xs2);"},"mx_xs":{"declaration":"margin-left: var(--space_xs);\tmargin-right: var(--space_xs);"},"mx_sm":{"declaration":"margin-left: var(--space_sm);\tmargin-right: var(--space_sm);"},"mx_md":{"declaration":"margin-left: var(--space_md);\tmargin-right: var(--space_md);"},"mx_lg":{"declaration":"margin-left: var(--space_lg);\tmargin-right: var(--space_lg);"},"mx_xl":{"declaration":"margin-left: var(--space_xl);\tmargin-right: var(--space_xl);"},"mx_xl2":{"declaration":"margin-left: var(--space_xl2);\tmargin-right: var(--space_xl2);"},"mx_xl3":{"declaration":"margin-left: var(--space_xl3);\tmargin-right: var(--space_xl3);"},"mx_xl4":{"declaration":"margin-left: var(--space_xl4);\tmargin-right: var(--space_xl4);"},"mx_xl5":{"declaration":"margin-left: var(--space_xl5);\tmargin-right: var(--space_xl5);"},"mx_xl6":{"declaration":"margin-left: var(--space_xl6);\tmargin-right: var(--space_xl6);"},"mx_xl7":{"declaration":"margin-left: var(--space_xl7);\tmargin-right: var(--space_xl7);"},"mx_xl8":{"declaration":"margin-left: var(--space_xl8);\tmargin-right: var(--space_xl8);"},"mx_xl9":{"declaration":"margin-left: var(--space_xl9);\tmargin-right: var(--space_xl9);"},"mx_xl10":{"declaration":"margin-left: var(--space_xl10);\tmargin-right: var(--space_xl10);"},"mx_xl11":{"declaration":"margin-left: var(--space_xl11);\tmargin-right: var(--space_xl11);"},"mx_xl12":{"declaration":"margin-left: var(--space_xl12);\tmargin-right: var(--space_xl12);"},"mx_xl13":{"declaration":"margin-left: var(--space_xl13);\tmargin-right: var(--space_xl13);"},"mx_xl14":{"declaration":"margin-left: var(--space_xl14);\tmargin-right: var(--space_xl14);"},"mx_xl15":{"declaration":"margin-left: var(--space_xl15);\tmargin-right: var(--space_xl15);"},"my_0":{"declaration":"margin-top: 0;\tmargin-bottom: 0;"},"my_auto":{"declaration":"margin-top: auto;\tmargin-bottom: auto;"},"my_xs5":{"declaration":"margin-top: var(--space_xs5);\tmargin-bottom: var(--space_xs5);"},"my_xs4":{"declaration":"margin-top: var(--space_xs4);\tmargin-bottom: var(--space_xs4);"},"my_xs3":{"declaration":"margin-top: var(--space_xs3);\tmargin-bottom: var(--space_xs3);"},"my_xs2":{"declaration":"margin-top: var(--space_xs2);\tmargin-bottom: var(--space_xs2);"},"my_xs":{"declaration":"margin-top: var(--space_xs);\tmargin-bottom: var(--space_xs);"},"my_sm":{"declaration":"margin-top: var(--space_sm);\tmargin-bottom: var(--space_sm);"},"my_md":{"declaration":"margin-top: var(--space_md);\tmargin-bottom: var(--space_md);"},"my_lg":{"declaration":"margin-top: var(--space_lg);\tmargin-bottom: var(--space_lg);"},"my_xl":{"declaration":"margin-top: var(--space_xl);\tmargin-bottom: var(--space_xl);"},"my_xl2":{"declaration":"margin-top: var(--space_xl2);\tmargin-bottom: var(--space_xl2);"},"my_xl3":{"declaration":"margin-top: var(--space_xl3);\tmargin-bottom: var(--space_xl3);"},"my_xl4":{"declaration":"margin-top: var(--space_xl4);\tmargin-bottom: var(--space_xl4);"},"my_xl5":{"declaration":"margin-top: var(--space_xl5);\tmargin-bottom: var(--space_xl5);"},"my_xl6":{"declaration":"margin-top: var(--space_xl6);\tmargin-bottom: var(--space_xl6);"},"my_xl7":{"declaration":"margin-top: var(--space_xl7);\tmargin-bottom: var(--space_xl7);"},"my_xl8":{"declaration":"margin-top: var(--space_xl8);\tmargin-bottom: var(--space_xl8);"},"my_xl9":{"declaration":"margin-top: var(--space_xl9);\tmargin-bottom: var(--space_xl9);"},"my_xl10":{"declaration":"margin-top: var(--space_xl10);\tmargin-bottom: var(--space_xl10);"},"my_xl11":{"declaration":"margin-top: var(--space_xl11);\tmargin-bottom: var(--space_xl11);"},"my_xl12":{"declaration":"margin-top: var(--space_xl12);\tmargin-bottom: var(--space_xl12);"},"my_xl13":{"declaration":"margin-top: var(--space_xl13);\tmargin-bottom: var(--space_xl13);"},"my_xl14":{"declaration":"margin-top: var(--space_xl14);\tmargin-bottom: var(--space_xl14);"},"my_xl15":{"declaration":"margin-top: var(--space_xl15);\tmargin-bottom: var(--space_xl15);"},"gap_xs5":{"declaration":"gap: var(--space_xs5);"},"gap_xs4":{"declaration":"gap: var(--space_xs4);"},"gap_xs3":{"declaration":"gap: var(--space_xs3);"},"gap_xs2":{"declaration":"gap: var(--space_xs2);"},"gap_xs":{"declaration":"gap: var(--space_xs);"},"gap_sm":{"declaration":"gap: var(--space_sm);"},"gap_md":{"declaration":"gap: var(--space_md);"},"gap_lg":{"declaration":"gap: var(--space_lg);"},"gap_xl":{"declaration":"gap: var(--space_xl);"},"gap_xl2":{"declaration":"gap: var(--space_xl2);"},"gap_xl3":{"declaration":"gap: var(--space_xl3);"},"gap_xl4":{"declaration":"gap: var(--space_xl4);"},"gap_xl5":{"declaration":"gap: var(--space_xl5);"},"gap_xl6":{"declaration":"gap: var(--space_xl6);"},"gap_xl7":{"declaration":"gap: var(--space_xl7);"},"gap_xl8":{"declaration":"gap: var(--space_xl8);"},"gap_xl9":{"declaration":"gap: var(--space_xl9);"},"gap_xl10":{"declaration":"gap: var(--space_xl10);"},"gap_xl11":{"declaration":"gap: var(--space_xl11);"},"gap_xl12":{"declaration":"gap: var(--space_xl12);"},"gap_xl13":{"declaration":"gap: var(--space_xl13);"},"gap_xl14":{"declaration":"gap: var(--space_xl14);"},"gap_xl15":{"declaration":"gap: var(--space_xl15);"},"column_gap_xs5":{"declaration":"column-gap: var(--space_xs5);"},"column_gap_xs4":{"declaration":"column-gap: var(--space_xs4);"},"column_gap_xs3":{"declaration":"column-gap: var(--space_xs3);"},"column_gap_xs2":{"declaration":"column-gap: var(--space_xs2);"},"column_gap_xs":{"declaration":"column-gap: var(--space_xs);"},"column_gap_sm":{"declaration":"column-gap: var(--space_sm);"},"column_gap_md":{"declaration":"column-gap: var(--space_md);"},"column_gap_lg":{"declaration":"column-gap: var(--space_lg);"},"column_gap_xl":{"declaration":"column-gap: var(--space_xl);"},"column_gap_xl2":{"declaration":"column-gap: var(--space_xl2);"},"column_gap_xl3":{"declaration":"column-gap: var(--space_xl3);"},"column_gap_xl4":{"declaration":"column-gap: var(--space_xl4);"},"column_gap_xl5":{"declaration":"column-gap: var(--space_xl5);"},"column_gap_xl6":{"declaration":"column-gap: var(--space_xl6);"},"column_gap_xl7":{"declaration":"column-gap: var(--space_xl7);"},"column_gap_xl8":{"declaration":"column-gap: var(--space_xl8);"},"column_gap_xl9":{"declaration":"column-gap: var(--space_xl9);"},"column_gap_xl10":{"declaration":"column-gap: var(--space_xl10);"},"column_gap_xl11":{"declaration":"column-gap: var(--space_xl11);"},"column_gap_xl12":{"declaration":"column-gap: var(--space_xl12);"},"column_gap_xl13":{"declaration":"column-gap: var(--space_xl13);"},"column_gap_xl14":{"declaration":"column-gap: var(--space_xl14);"},"column_gap_xl15":{"declaration":"column-gap: var(--space_xl15);"},"row_gap_xs5":{"declaration":"row-gap: var(--space_xs5);"},"row_gap_xs4":{"declaration":"row-gap: var(--space_xs4);"},"row_gap_xs3":{"declaration":"row-gap: var(--space_xs3);"},"row_gap_xs2":{"declaration":"row-gap: var(--space_xs2);"},"row_gap_xs":{"declaration":"row-gap: var(--space_xs);"},"row_gap_sm":{"declaration":"row-gap: var(--space_sm);"},"row_gap_md":{"declaration":"row-gap: var(--space_md);"},"row_gap_lg":{"declaration":"row-gap: var(--space_lg);"},"row_gap_xl":{"declaration":"row-gap: var(--space_xl);"},"row_gap_xl2":{"declaration":"row-gap: var(--space_xl2);"},"row_gap_xl3":{"declaration":"row-gap: var(--space_xl3);"},"row_gap_xl4":{"declaration":"row-gap: var(--space_xl4);"},"row_gap_xl5":{"declaration":"row-gap: var(--space_xl5);"},"row_gap_xl6":{"declaration":"row-gap: var(--space_xl6);"},"row_gap_xl7":{"declaration":"row-gap: var(--space_xl7);"},"row_gap_xl8":{"declaration":"row-gap: var(--space_xl8);"},"row_gap_xl9":{"declaration":"row-gap: var(--space_xl9);"},"row_gap_xl10":{"declaration":"row-gap: var(--space_xl10);"},"row_gap_xl11":{"declaration":"row-gap: var(--space_xl11);"},"row_gap_xl12":{"declaration":"row-gap: var(--space_xl12);"},"row_gap_xl13":{"declaration":"row-gap: var(--space_xl13);"},"row_gap_xl14":{"declaration":"row-gap: var(--space_xl14);"},"row_gap_xl15":{"declaration":"row-gap: var(--space_xl15);"}} \ No newline at end of file diff --git a/src/test/fixtures/vite_dev/extra/late_module.ts b/src/test/fixtures/vite_dev/extra/late_module.ts new file mode 100644 index 000000000..e4f6b8dc8 --- /dev/null +++ b/src/test/fixtures/vite_dev/extra/late_module.ts @@ -0,0 +1,3 @@ +// a module outside the prescan roots, transformed on demand by the ws test +// to create extraction state the first CSS serve has never seen +export const late_class = 'mb_xl3'; diff --git a/src/test/generate_bundled_css.test.ts b/src/test/generate_bundled_css.test.ts index 46e71189f..a039f4cfd 100644 --- a/src/test/generate_bundled_css.test.ts +++ b/src/test/generate_bundled_css.test.ts @@ -18,6 +18,7 @@ import { assert_css_order } from './test_helpers.ts'; const create_mock_result = (overrides: Partial<CssResolutionResult> = {}): CssResolutionResult => ({ theme_css: ':root { --color: blue; }', base_css: 'button { color: red; }', + preferences_css: '', resolved_variables: new Set(['color']), included_rule_indices: new Set([0]), included_elements: new Set(['button']), @@ -52,11 +53,27 @@ describe('generate_bundled_css', () => { const result = create_mock_result(); const bundled = generate_bundled_css(result, '.p_md { padding: 16px; }'); - assert.match(bundled, /^@layer fuz\.base, fuz\.theme, fuz\.utilities;/); + assert.match(bundled, /^@layer fuz\.base, fuz\.preferences, fuz\.theme, fuz\.utilities;/); // theme variables and base styles both live in fuz.base; utilities above assert.equal(bundled.match(/@layer fuz\.base \{/gu)?.length, 2); assert.include(bundled, '@layer fuz.utilities {\n.p_md { padding: 16px; }\n}'); }); + + test('preference rules are wrapped in fuz.preferences', () => { + const result = create_mock_result({ + preferences_css: '@media (prefers-contrast: more) {\n:root { --x: 1; }\n}' + }); + const bundled = generate_bundled_css(result, ''); + + assert.include(bundled, '/* User-Preference Mappings */'); + assert.include( + bundled, + '@layer fuz.preferences {\n@media (prefers-contrast: more) {\n:root { --x: 1; }\n}\n}' + ); + // gated by include_base alongside the base styles they come from + const no_base = generate_bundled_css(result, '', { include_base: false }); + assert.notInclude(no_base, 'fuz.preferences {'); + }); }); describe('exclusion options', () => { diff --git a/src/test/generate_classes_css.test.ts b/src/test/generate_classes_css.test.ts index f7a574197..05ce7ac3c 100644 --- a/src/test/generate_classes_css.test.ts +++ b/src/test/generate_classes_css.test.ts @@ -487,7 +487,7 @@ describe('generate_classes_css', () => { }); test('modified token class sorts alphabetically, not by property', () => { - // hover:p_md: segments ['hover', 'p_md'] — the loop runs i < 1 (i.e. only i=0), + // hover:p_md: segments ['hover', 'p_md'] - the loop runs i < 1 (i.e. only i=0), // 'hover' is skipped as a modifier, loop ends, returns MAX_VALUE. // p_md has a finite index, so it sorts before hover:p_md. const catch_all_interpreter: CssClassDefinitionInterpreter = { diff --git a/src/test/generate_css.test.ts b/src/test/generate_css.test.ts index 95510f9cb..508b07339 100644 --- a/src/test/generate_css.test.ts +++ b/src/test/generate_css.test.ts @@ -47,7 +47,7 @@ describe('generate_css', () => { test('wraps output in the fuz.utilities layer with the order statement', () => { const result = generate_css(make_options({ all_classes: new Set(['p_lg']) })); - assert.match(result.css, /^@layer fuz\.base, fuz\.theme, fuz\.utilities;/); + assert.match(result.css, /^@layer fuz\.base, fuz\.preferences, fuz\.theme, fuz\.utilities;/); assert_css_contains(result.css, '@layer fuz.utilities {'); }); @@ -131,7 +131,7 @@ describe('generate_css', () => { const result = generate_css( make_options({ all_elements: new Set(['button']), - // not in detected_css_variables — only reachable via @fuz-variables + // not in detected_css_variables - only reachable via @fuz-variables explicit_variables: new Set(['text_color']), include_theme: true, resources: { style_rule_index, variable_graph, class_variable_index } @@ -149,7 +149,7 @@ describe('generate_css', () => { const result = generate_css( make_options({ - // not in the theme — resolve_css errors on the @fuz-variables annotation + // not in the theme - resolve_css errors on the @fuz-variables annotation explicit_variables: new Set(['nonexistent_var']), include_theme: true, resources: { style_rule_index, variable_graph, class_variable_index } @@ -223,5 +223,58 @@ describe('generate_css', () => { result.diagnostics.find((d) => d.message.includes('theme variables are disabled')) ); }); + + test('a configured theme discarded by variables: null warns', () => { + const result = generate_css(make_options({ has_theme: true, include_theme: false })); + const warning = result.diagnostics.find( + (d) => d.level === 'warning' && 'identifier' in d && d.identifier === 'theme_discarded' + ); + assert.ok(warning, 'expected a theme_discarded warning'); + // no warning when the theme can actually render + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( + 'button { color: red; }', + VARIABLES + ); + const ok_result = generate_css( + make_options({ + has_theme: true, + include_theme: true, + resources: { style_rule_index, variable_graph, class_variable_index } + }) + ); + assert.isUndefined( + ok_result.diagnostics.find((d) => 'identifier' in d && d.identifier === 'theme_discarded') + ); + }); + + test('preference rules survive tree-shaking into the fuz.preferences layer', () => { + // no detected elements or classes at all - the media block still ships + // because its inner :root rule is core + const base_css = `@layer fuz.preferences { + @media (prefers-contrast: more) { + :root { --text_color: black; } + } +} +@layer fuz.base { + button { color: var(--text_color); } +}`; + const { style_rule_index, variable_graph, class_variable_index } = create_test_fixtures( + base_css, + VARIABLES + ); + + const result = generate_css( + make_options({ + include_base: true, + include_theme: true, + resources: { style_rule_index, variable_graph, class_variable_index } + }) + ); + + assert_css_contains(result.css, '@layer fuz.preferences {'); + assert_css_contains(result.css, 'prefers-contrast: more'); + // the unused button rule is still shaken out + assert_css_not_contains(result.css, 'button'); + }); }); }); diff --git a/src/test/ramps.emitters.test.ts b/src/test/ramps.emitters.test.ts new file mode 100644 index 000000000..ac3cb442e --- /dev/null +++ b/src/test/ramps.emitters.test.ts @@ -0,0 +1,373 @@ +/** + * Emitter ↔ numeric-twin equivalence: the CSS strings the `render_*_css` + * emitters produce, evaluated numerically with the knob variables + * substituted, must equal the `ramps.ts` evaluators the gates assert + * against. Without this the gamut/monotonicity/contrast gates prove + * properties of the JS model while the shipped `calc()`/`oklch()` CSS could + * drift (an operator-precedence slip, a missing clamp, a dropped + * multiplier). + * + * @module + */ + +import { test, assert, describe } from 'vitest'; + +import { + PALETTE_HUES, + PALETTE_CHROMA_MULTIPLIERS, + PALETTE_LIGHTNESS_KNOBS, + SHADE_LIGHTNESS_KNOBS, + TEXT_LIGHTNESS_KNOBS, + PALETTE_CHROMA_KNOBS, + NEUTRAL_CHROMA, + RAMP_STOPS, + ramp_lightness, + ramp_chroma, + ramp_chroma_shape, + palette_stop_oklch, + shade_stop_oklch, + text_stop_oklch, + render_lightness_stop_css, + render_chroma_shape_css, + render_chroma_stop_css, + render_palette_stop_css, + render_neutral_stop_css, + render_ramp_color_css, + render_border_color_stop_css, + border_color_oklch, + BORDER_COLOR_ALPHAS, + type RampFamily +} from '$lib/ramps.ts'; +import { + palette_variants, + color_scheme_variants, + type ColorSchemeVariant, + type NumericScaleVariant +} from '$lib/variable_data.ts'; + +// the emitters round ramp positions to 1e-6, so allow a hair over that +const TOLERANCE = 1e-5; + +/** + * Evaluates the emitted CSS value grammar to a number: numeric literals, + * `var(--x[, fallback])`, `calc(expr)`, `min(a, b)`, `pow(a, b)`, `+ - * /`, + * and parentheses. Throws on anything outside the grammar so an emitter + * change that this evaluator can't follow fails loudly instead of silently + * passing. + */ +const evaluate_css_number = (expr: string, vars: Record<string, number>): number => { + let i = 0; + const s = expr; + + const skip_ws = (): void => { + while (i < s.length && /\s/.test(s[i]!)) i++; + }; + + const parse_expr = (): number => { + let value = parse_term(); + for (;;) { + skip_ws(); + const c = s[i]; + if (c === '+' || c === '-') { + i++; + const rhs = parse_term(); + value = c === '+' ? value + rhs : value - rhs; + } else { + return value; + } + } + }; + + const parse_term = (): number => { + let value = parse_factor(); + for (;;) { + skip_ws(); + const c = s[i]; + if (c === '*' || c === '/') { + i++; + const rhs = parse_factor(); + value = c === '*' ? value * rhs : value / rhs; + } else { + return value; + } + } + }; + + const expect = (char: string): void => { + skip_ws(); + if (s[i] !== char) { + throw new Error(`expected "${char}" at ${i} in ${expr}`); + } + i++; + }; + + const parse_factor = (): number => { + skip_ws(); + if (s[i] === '(') { + i++; + const value = parse_expr(); + expect(')'); + return value; + } + const fn = /^(calc|min|pow|var)\(/.exec(s.slice(i)); + if (fn) { + const name = fn[1]!; + i += fn[0].length; + if (name === 'calc') { + const value = parse_expr(); + expect(')'); + return value; + } + if (name === 'var') { + skip_ws(); + const m = /^--([a-z][a-z0-9_]*)/.exec(s.slice(i)); + if (!m) throw new Error(`expected a custom property name at ${i} in ${expr}`); + i += m[0].length; + skip_ws(); + let fallback: number | null = null; + if (s[i] === ',') { + i++; + fallback = parse_expr(); + } + expect(')'); + const value = vars[m[1]!] ?? fallback; + if (value === null || value === undefined) { + throw new Error(`unbound variable --${m[1]} with no fallback in ${expr}`); + } + return value; + } + // min/pow: two comma-separated arguments + const a = parse_expr(); + expect(','); + const b = parse_expr(); + expect(')'); + return name === 'min' ? Math.min(a, b) : a ** b; + } + const num = /^-?\d*\.?\d+(e-?\d+)?/i.exec(s.slice(i)); + if (!num) throw new Error(`expected a number at ${i} ("${s.slice(i, i + 12)}") in ${expr}`); + i += num[0].length; + return Number(num[0]); + }; + + const value = parse_expr(); + skip_ws(); + if (i !== s.length) throw new Error(`trailing content at ${i} in ${expr}`); + return value; +}; + +/** Splits `oklch(L C H)` into its three top-level space-separated arguments. */ +const parse_oklch_args = (css: string): [string, string, string] => { + const m = /^oklch\((.*)\)$/su.exec(css); + assert(m, `not an oklch() value: ${css}`); + const inner = m[1]!; + const args: Array<string> = []; + let depth = 0; + let current = ''; + for (const char of inner) { + if (char === '(') depth++; + else if (char === ')') depth--; + if (char === ' ' && depth === 0) { + if (current) args.push(current); + current = ''; + } else { + current += char; + } + } + if (current) args.push(current); + assert.strictEqual(args.length, 3, `expected 3 oklch args in ${css}`); + return args as [string, string, string]; +}; + +const LIGHTNESS_KNOBS = { + palette: PALETTE_LIGHTNESS_KNOBS, + shade: SHADE_LIGHTNESS_KNOBS, + text: TEXT_LIGHTNESS_KNOBS +} as const; + +/** All knob variables an emitted color stop can reference, per scheme. */ +const knob_vars = (scheme: ColorSchemeVariant): Record<string, number> => { + const vars: Record<string, number> = { + chroma_scale: 1, + neutral_chroma: NEUTRAL_CHROMA[scheme], + border_color_lightness: border_color_oklch(scheme)[0], + border_color_chroma: border_color_oklch(scheme)[1], + hue_neutral: PALETTE_HUES.f, + hue_accent: PALETTE_HUES.a, + accent_chroma_scale: 1, + palette_chroma_min: PALETTE_CHROMA_KNOBS[scheme].chroma_min, + palette_chroma_max: PALETTE_CHROMA_KNOBS[scheme].chroma_max, + palette_chroma_curve: PALETTE_CHROMA_KNOBS[scheme].curve + }; + for (const family of ['palette', 'shade', 'text'] as const) { + const knobs = LIGHTNESS_KNOBS[family][scheme]; + vars[`${family}_lightness_00`] = knobs.lightness_00; + vars[`${family}_lightness_100`] = knobs.lightness_100; + vars[`${family}_lightness_curve`] = knobs.curve; + } + for (const letter of palette_variants) { + vars[`hue_${letter}`] = PALETTE_HUES[letter]; + vars[`palette_${letter}_chroma_scale`] = PALETTE_CHROMA_MULTIPLIERS[letter]; + } + for (const stop of RAMP_STOPS) { + vars[`chroma_shape_${stop}`] = ramp_chroma_shape(stop, PALETTE_CHROMA_KNOBS[scheme].curve); + vars[`palette_chroma_${stop}`] = ramp_chroma(scheme, stop); + for (const family of ['palette', 'shade', 'text'] as const) { + vars[`${family}_lightness_${stop}`] = ramp_lightness(LIGHTNESS_KNOBS[family][scheme], stop); + } + } + return vars; +}; + +const DERIVED_STOPS = RAMP_STOPS.filter( + (stop) => stop !== '00' && stop !== '100' +) as Array<NumericScaleVariant>; + +describe('lightness stop emitter', () => { + test('matches ramp_lightness for every family, scheme, and derived stop', () => { + for (const family of ['palette', 'shade', 'text'] as Array<RampFamily>) { + for (const scheme of color_scheme_variants) { + const vars = knob_vars(scheme); + for (const stop of DERIVED_STOPS) { + const css = render_lightness_stop_css(family, stop); + assert.closeTo( + evaluate_css_number(css, vars), + ramp_lightness(LIGHTNESS_KNOBS[family][scheme], stop), + TOLERANCE, + `${family} ${scheme} ${stop}` + ); + } + } + } + }); +}); + +describe('chroma shape emitter', () => { + test('matches ramp_chroma_shape at every stop, exact 0/1 at the extremes', () => { + for (const scheme of color_scheme_variants) { + const curve = PALETTE_CHROMA_KNOBS[scheme].curve; + const vars = knob_vars(scheme); + for (const stop of RAMP_STOPS) { + const css = render_chroma_shape_css(stop); + assert.closeTo( + evaluate_css_number(css, vars), + ramp_chroma_shape(stop, curve), + TOLERANCE, + `${scheme} ${stop}` + ); + } + } + assert.strictEqual(render_chroma_shape_css('00'), '0'); + assert.strictEqual(render_chroma_shape_css('100'), '0'); + assert.strictEqual(render_chroma_shape_css('50'), '1'); + }); +}); + +describe('chroma stop emitter', () => { + test('matches ramp_chroma - the capped knob curve - at every stop', () => { + for (const scheme of color_scheme_variants) { + const vars = knob_vars(scheme); + for (const stop of RAMP_STOPS) { + const css = render_chroma_stop_css(stop, scheme); + assert.closeTo( + evaluate_css_number(css, vars), + ramp_chroma(scheme, stop), + TOLERANCE, + `${scheme} ${stop}` + ); + } + } + }); + + test('an explicit cap overrides the baked one', () => { + const vars = knob_vars('light'); + const css = render_chroma_stop_css('50', 'light', 0.01); + assert.closeTo(evaluate_css_number(css, vars), 0.01, TOLERANCE); + }); +}); + +describe('palette stop emitter', () => { + test('matches palette_stop_oklch for every letter, scheme, and stop', () => { + for (const scheme of color_scheme_variants) { + const vars = knob_vars(scheme); + for (const letter of palette_variants) { + for (const stop of RAMP_STOPS) { + const [l_css, c_css, h_css] = parse_oklch_args(render_palette_stop_css(letter, stop)); + const [l, c, h] = palette_stop_oklch(letter, stop, scheme); + assert.closeTo(evaluate_css_number(l_css, vars), l, TOLERANCE, `${letter} ${stop} L`); + assert.closeTo(evaluate_css_number(c_css, vars), c, TOLERANCE, `${letter} ${stop} C`); + assert.closeTo(evaluate_css_number(h_css, vars), h, TOLERANCE, `${letter} ${stop} H`); + } + } + } + }); + + test("the slot multiplier rides the emitted chroma - brown's mute is in the CSS", () => { + const vars = knob_vars('light'); + const [, c_f] = parse_oklch_args(render_palette_stop_css('f', '50')); + const [, c_h] = parse_oklch_args(render_palette_stop_css('h', '50')); + const ratio = evaluate_css_number(c_f, vars) / evaluate_css_number(c_h, vars); + assert.closeTo(ratio, PALETTE_CHROMA_MULTIPLIERS.f, TOLERANCE); + }); +}); + +describe('neutral stop emitter', () => { + test('matches shade_stop_oklch and text_stop_oklch at every stop', () => { + for (const scheme of color_scheme_variants) { + const vars = knob_vars(scheme); + for (const stop of RAMP_STOPS) { + for (const [family, twin] of [ + ['shade', shade_stop_oklch], + ['text', text_stop_oklch] + ] as const) { + const [l_css, c_css, h_css] = parse_oklch_args(render_neutral_stop_css(family, stop)); + const [l, c, h] = twin(stop, scheme); + assert.closeTo(evaluate_css_number(l_css, vars), l, TOLERANCE, `${family} ${stop} L`); + assert.closeTo(evaluate_css_number(c_css, vars), c, TOLERANCE, `${family} ${stop} C`); + assert.closeTo(evaluate_css_number(h_css, vars), h, TOLERANCE, `${family} ${stop} H`); + } + } + } + }); +}); + +describe('ramp color emitter', () => { + test('an intent hue reference picks up its chroma multiplier twin', () => { + const vars = { ...knob_vars('light'), accent_chroma_scale: 0.7 }; + const [, c_css] = parse_oklch_args(render_ramp_color_css('var(--hue_accent)', '50')); + assert.closeTo(evaluate_css_number(c_css, vars), ramp_chroma('light', '50') * 0.7, TOLERANCE); + }); + + test('a non-slot hue reference carries no multiplier', () => { + const css = render_ramp_color_css('var(--hue_neutral)', '50'); + assert.notInclude(css, 'chroma_scale,'); + assert.notInclude(css, 'neutral_chroma_scale'); + }); + + test('the alpha channel renders into the oklch()', () => { + const css = render_ramp_color_css('var(--hue_a)', '40', '40%'); + assert.isTrue(css.endsWith('/ 40%)'), css); + }); +}); + +describe('border color stop emitter', () => { + test('matches border_color_oklch with the stop alpha appended', () => { + for (const scheme of color_scheme_variants) { + const vars = knob_vars(scheme); + const [l, c, h] = border_color_oklch(scheme); + for (const stop of RAMP_STOPS) { + const alpha = BORDER_COLOR_ALPHAS[scheme][stop]; + const css = render_border_color_stop_css(stop, scheme); + if (alpha === 0) { + assert.strictEqual(css, 'transparent'); + continue; + } + const color = alpha === 100 ? css : css.replace(` / ${alpha}%)`, ')'); + assert.notStrictEqual(color, css === color ? '' : css, 'alpha stripped'); + const [l_css, c_css, h_css] = parse_oklch_args(color); + assert.closeTo(evaluate_css_number(l_css, vars), l, TOLERANCE, `${scheme} ${stop} L`); + assert.closeTo(evaluate_css_number(c_css, vars), c, TOLERANCE, `${scheme} ${stop} C`); + assert.closeTo(evaluate_css_number(h_css, vars), h, TOLERANCE, `${scheme} ${stop} H`); + } + } + }); +}); diff --git a/src/test/ramps.test.ts b/src/test/ramps.test.ts index c7ec07bc9..03eb6c42c 100644 --- a/src/test/ramps.test.ts +++ b/src/test/ramps.test.ts @@ -29,7 +29,7 @@ import { } from '$lib/theme_check.ts'; // Contrast gates for the default palette. OKLCH lightness is monotonic with -// luminance, so these hold for every hue at a given stop pair — but WCAG +// luminance, so these hold for every hue at a given stop pair - but WCAG // ratios are computed from real luminance here, not approximated from ΔL. // Thresholds are anchored to WCAG levels the old palette met (or, for the // per-hue UI gates, failed and the derived palette now meets); promoted to @@ -113,7 +113,7 @@ describe('chroma multipliers', () => { describe('chroma caps', () => { test('baked caps match the worst-hue gamut math (drift check)', () => { // if the default hues or lightness knobs change without recomputing the - // caps, this fails — the caps are design-time constants, not free values + // caps, this fails - the caps are design-time constants, not free values for (const scheme of color_scheme_variants) { const caps = compute_palette_chroma_caps( Object.values(PALETTE_HUES), diff --git a/src/test/style_rule_parser.custom.test.ts b/src/test/style_rule_parser.custom.test.ts index 3f978c793..37524477f 100644 --- a/src/test/style_rule_parser.custom.test.ts +++ b/src/test/style_rule_parser.custom.test.ts @@ -242,8 +242,8 @@ describe('create_style_rule_index', () => { const index1 = create_style_rule_index(css1); const index2 = create_style_rule_index(css2); - // Different content should produce different hashes - assert.notStrictEqual(index1.content_hash, index2.content_hash); + // Different content parses to different rule sets + assert.notStrictEqual(index1.rules[0]!.css, index2.rules[0]!.css); }); }); diff --git a/src/test/style_rule_parser.test.ts b/src/test/style_rule_parser.test.ts index f731b3c9a..0b0d113bd 100644 --- a/src/test/style_rule_parser.test.ts +++ b/src/test/style_rule_parser.test.ts @@ -16,7 +16,7 @@ describe('parse_style_css', () => { describe('basic parsing', () => { test('parses basic rule', () => { const css = `button { color: red; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.isTrue(index.rules[0]!.elements.has('button')); @@ -26,7 +26,7 @@ describe('parse_style_css', () => { test('parses rule with class', () => { const css = `button.selected { color: blue; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.isTrue(index.rules[0]!.elements.has('button')); @@ -35,20 +35,13 @@ describe('parse_style_css', () => { test('parses multiple selectors', () => { const css = `h1, h2, h3 { font-weight: bold; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.isTrue(index.rules[0]!.elements.has('h1')); assert.isTrue(index.rules[0]!.elements.has('h2')); assert.isTrue(index.rules[0]!.elements.has('h3')); }); - - test('stores content hash', () => { - const css = `button { color: red; }`; - const index = parse_style_css(css, 'my-hash'); - - assert.strictEqual(index.content_hash, 'my-hash'); - }); }); describe('core rules', () => { @@ -58,16 +51,16 @@ describe('parse_style_css', () => { ['body { font-size: 16px; }', 'body'], ['html { font-size: 16px; }', 'html'], [':host { display: block; }', 'host'], - ['@media (prefers-reduced-motion) { :root { --duration: 0; } }', 'media_query'] + ['@media (prefers-reduced-motion) { :root { --duration: 0; } }', 'conditional_core'] ] as const)('%s is core (%s)', (css, reason) => { - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.is_core); assert.strictEqual(index.rules[0]!.core_reason, reason); }); test('@media not prefers-reduced-motion is not core', () => { const css = `@media (min-width: 768px) { button { font-size: 18px; } }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isFalse(index.rules[0]!.is_core); assert.isTrue(index.rules[0]!.elements.has('button')); @@ -77,7 +70,7 @@ describe('parse_style_css', () => { describe('functional pseudo-classes', () => { test(':where selector', () => { const css = `:where(button:not(.unstyled)) { color: var(--text_color); }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.isTrue(index.rules[0]!.elements.has('button')); @@ -87,7 +80,7 @@ describe('parse_style_css', () => { test('complex :is selector', () => { const css = `:where(:is(input, textarea, select):not(.unstyled)) { display: block; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.elements.has('input')); assert.isTrue(index.rules[0]!.elements.has('textarea')); @@ -97,7 +90,7 @@ describe('parse_style_css', () => { test('nested :is in :where', () => { const css = `:where(:is(h1, h2, h3, h4, h5, h6, .heading):not(.unstyled)) { font-family: serif; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.elements.has('h1')); assert.isTrue(index.rules[0]!.elements.has('h6')); @@ -107,7 +100,7 @@ describe('parse_style_css', () => { test('deeply nested :where(:not(:has(...)))', () => { const css = `:where(:not(:has(button.disabled))) { opacity: 1; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.elements.has('button')); assert.isTrue(index.rules[0]!.classes.has('disabled')); @@ -115,7 +108,7 @@ describe('parse_style_css', () => { test('triple nested functional pseudo-classes', () => { const css = `:where(:is(:not(.hidden):has(span.icon))) { display: flex; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.elements.has('span')); assert.isTrue(index.rules[0]!.classes.has('hidden')); @@ -126,7 +119,7 @@ describe('parse_style_css', () => { describe('combinators', () => { test('child combinator', () => { const css = `ul > li { list-style: none; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.elements.has('ul')); assert.isTrue(index.rules[0]!.elements.has('li')); @@ -134,7 +127,7 @@ describe('parse_style_css', () => { test('sibling combinators', () => { const css = `h1 + p, h2 ~ p { margin-top: 0; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.elements.has('h1')); assert.isTrue(index.rules[0]!.elements.has('h2')); @@ -145,7 +138,7 @@ describe('parse_style_css', () => { describe('pseudo-elements and pseudo-classes', () => { test('does not extract pseudo-elements as elements', () => { const css = `::selection { background: blue; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.strictEqual(index.rules[0]!.elements.size, 0); @@ -156,7 +149,7 @@ describe('parse_style_css', () => { ["span::after { content: ''; }", 'span', 'after'], ['a:hover { color: red; }', 'a', 'hover'] ] as const)('%s extracts element but not pseudo', (css, element, pseudo) => { - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.elements.has(element)); assert.isFalse(index.rules[0]!.elements.has(pseudo)); }); @@ -172,13 +165,13 @@ describe('parse_style_css', () => { ['input:not([disabled]) { cursor: pointer; }', 'input'], ["input[placeholder='a, b, c'] { color: gray; }", 'input'] ])('%s extracts element', (css, element) => { - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.elements.has(element)); }); test('attribute selector with class does not extract class from attribute', () => { const css = `[class~="unstyled"] { all: unset; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isFalse(index.rules[0]!.classes.has('unstyled')); assert.strictEqual(index.rules[0]!.elements.size, 0); @@ -186,7 +179,7 @@ describe('parse_style_css', () => { test('class selector alongside attribute selector', () => { const css = `input.error[type='text'] { border: 2px solid red; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.elements.has('input')); assert.isTrue(index.rules[0]!.classes.has('error')); @@ -194,7 +187,7 @@ describe('parse_style_css', () => { test('data attribute selectors', () => { const css = `[data-theme='dark'] { background: black; }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules[0]!.elements.size, 0); assert.strictEqual(index.rules[0]!.classes.size, 0); @@ -204,7 +197,7 @@ describe('parse_style_css', () => { describe('at-rules', () => { test('@supports rule', () => { const css = `@supports (display: grid) { .grid { display: grid; } }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.isTrue(index.rules[0]!.classes.has('grid')); @@ -213,7 +206,7 @@ describe('parse_style_css', () => { test('@container rule', () => { const css = `@container (min-width: 400px) { .card { padding: var(--space_lg); } }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.isTrue(index.rules[0]!.classes.has('card')); @@ -222,7 +215,7 @@ describe('parse_style_css', () => { test('@layer rule', () => { const css = `@layer base { button { color: blue; } }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.isTrue(index.rules[0]!.elements.has('button')); @@ -233,7 +226,7 @@ describe('parse_style_css', () => { from { opacity: 0; } to { opacity: 1; } }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.strictEqual(index.rules[0]!.elements.size, 0); @@ -246,7 +239,7 @@ describe('parse_style_css', () => { 0% { transform: scale(var(--scale_min)); } 100% { transform: scale(var(--scale_max)); } }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.variables_used.has('scale_min')); assert.isTrue(index.rules[0]!.variables_used.has('scale_max')); @@ -256,7 +249,7 @@ describe('parse_style_css', () => { const css = `@supports (display: flex) { .flex { display: flex; } }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.isTrue(index.rules[0]!.classes.has('flex')); @@ -269,7 +262,7 @@ describe('parse_style_css', () => { font-family: 'CustomFont'; src: url('/fonts/custom.woff2') format('woff2'); }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 1); assert.isTrue(index.rules[0]!.is_core); @@ -281,7 +274,7 @@ describe('parse_style_css', () => { font-family: 'CustomFont'; src: url('/fonts/custom.woff2') format('woff2'); }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules[0]!.elements.size, 0); assert.strictEqual(index.rules[0]!.classes.size, 0); @@ -293,7 +286,7 @@ describe('parse_style_css', () => { src: url(var(--font_path)); font-display: var(--font_display); }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.variables_used.has('font_family_name')); assert.isTrue(index.rules[0]!.variables_used.has('font_path')); @@ -312,7 +305,7 @@ describe('parse_style_css', () => { font-weight: bold; } `; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.strictEqual(index.rules.length, 2); assert.isTrue(index.rules[0]!.is_core); @@ -365,7 +358,7 @@ describe('parse_style_css', () => { ['base_width', 'extra_width'] ] ])('extracts from %s', (_name, css, expected_vars) => { - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); for (const v of expected_vars) { assert.isTrue(index.rules[0]!.variables_used.has(v), `Expected "${v}" in variables`); } @@ -377,7 +370,7 @@ describe('parse_style_css', () => { background: var(--bg_color); border: var(--border_width) solid var(--border_color); }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.isTrue(index.rules[0]!.variables_used.has('text_color')); assert.isTrue(index.rules[0]!.variables_used.has('bg_color')); @@ -393,7 +386,7 @@ describe('parse_style_css', () => { input { color: blue; } button.selected { background: green; } `; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); assert.deepEqual(index.by_element.get('button'), [0, 2]); assert.deepEqual(index.by_element.get('input'), [1]); @@ -408,7 +401,7 @@ describe('get_matching_rules', () => { *, ::before { box-sizing: border-box; } button { color: red; } `; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); const included = get_matching_rules(index, new Set(), new Set()); assert.isTrue(included.has(0)); @@ -420,7 +413,7 @@ describe('get_matching_rules', () => { button { color: red; } input { color: blue; } `; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); const included = get_matching_rules(index, new Set(['button']), new Set()); assert.isTrue(included.has(0)); @@ -432,7 +425,7 @@ describe('get_matching_rules', () => { .foo { color: red; } .bar { color: blue; } `; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); const included = get_matching_rules(index, new Set(), new Set(['foo'])); assert.isTrue(included.has(0)); @@ -447,7 +440,7 @@ describe('get_matching_rules', () => { } button { color: red; } `; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); const included = get_matching_rules(index, new Set(), new Set()); @@ -463,7 +456,7 @@ a { color: red; } b { color: blue; } c { color: green; } `; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); const result = generate_base_css(index, new Set([2, 0])); @@ -478,7 +471,7 @@ describe('collect_rule_variables', () => { a { color: var(--palette_a); } b { color: var(--palette_b); background: var(--shade_00); } `; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); const vars = collect_rule_variables(index, new Set([1])); assert.isFalse(vars.has('palette_a')); @@ -488,7 +481,7 @@ describe('collect_rule_variables', () => { test('returns empty set for empty included_rules', () => { const css = `a { color: var(--palette_a); }`; - const index = parse_style_css(css, 'test-hash'); + const index = parse_style_css(css); const vars = collect_rule_variables(index, new Set()); assert.strictEqual(vars.size, 0); }); diff --git a/src/test/styles.test.ts b/src/test/styles.test.ts index 38d132080..fd0f35d1e 100644 --- a/src/test/styles.test.ts +++ b/src/test/styles.test.ts @@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs'; import * as exported_variables from '$lib/variables.ts'; import { theme_knob_hook_names } from '$lib/knobs.ts'; +import { parse_style_css } from '$lib/style_rule_parser.ts'; import css_classes_text from './fixtures/css_classes_fixture.json?raw'; // vitest replaces this with an empty string because CSS isn't opted into being processed, @@ -54,7 +55,7 @@ test('variables in the CSS exist', () => { * These variables are known to be in the CSS but not in the exported variables. * This means they can be contextually used when defined, but otherwise have a fallback. * Hook knobs from the catalog (e.g. `heading_font_weight`) are included - * automatically — they're defined as CSS-consumed-but-undeclared. + * automatically - they're defined as CSS-consumed-but-undeclared. */ const known_without_variables = new Set([ ...theme_knob_hook_names, @@ -93,3 +94,36 @@ const known_without_variables = new Set([ 'shadow_alpha', 'shadow_color' ]); + +test('the OS user-preference mappings parse core and layer into fuz.preferences', () => { + // bundled output must always carry these - they target :root, so they ride + // the conditional-group-with-core-inner rule - and must re-layer into + // fuz.preferences so they beat the fuz.base defaults in every mode + const index = parse_style_css(main_stylesheet_text); + for (const feature of ['prefers-contrast: more', 'prefers-reduced-motion']) { + const rule = index.rules.find((r) => r.css.includes(feature)); + assert(rule, `style.css carries a ${feature} block`); + assert.isTrue(rule.is_core, `${feature} is core`); + assert.strictEqual(rule.layer, 'fuz.preferences', feature); + } + // everything else stays in fuz.base + const misplaced = index.rules.filter( + (r) => r.layer === 'fuz.preferences' && !r.css.includes('prefers-') + ); + assert.deepEqual(misplaced, []); +}); + +test('untargetable base rules are core, so bundled output always ships them', () => { + // pseudo-element and attribute selectors name no element or class, so + // detection can never match them - without the core marking they'd be + // tree-shaken out of every bundle (and --selection_color could never apply) + const index = parse_style_css(main_stylesheet_text); + for (const selector of ['::selection', '::placeholder', '::file-selector-button', '[hidden]']) { + const rule = index.rules.find((r) => r.css.startsWith(selector)); + assert(rule, `style.css carries a ${selector} rule`); + assert.isTrue(rule.is_core, `${selector} is core`); + assert.strictEqual(rule.core_reason, 'untargetable', selector); + } + const selection = index.rules.find((r) => r.css.startsWith('::selection')); + assert.isTrue(selection!.variables_used.has('selection_color')); +}); diff --git a/src/test/theme.test.ts b/src/test/theme.test.ts index b942bc7ba..19376316b 100644 --- a/src/test/theme.test.ts +++ b/src/test/theme.test.ts @@ -41,6 +41,18 @@ describe('render_theme_style', () => { if (v.light !== undefined) assert.include(css, `--${v.name}: ${v.light};`); } }); + + test('an id scope renders dark slots for both scheme-class placements', () => { + // the scheme class conventionally lives on the root element, so the dark + // block must match :root.dark descendants, not only a class on the scope + const css = render_theme_style( + { name: 't', variables: [{ name: 'shade_lightness_00', light: '0.9', dark: '0.2' }] }, + { id: 'my_scope' } + ); + assert.include(css, '#my_scope {'); + assert.include(css, '#my_scope.dark, :root.dark #my_scope {'); + assert.notInclude(css, ':root {'); + }); }); describe('scheme stance', () => { @@ -65,14 +77,18 @@ describe('scheme stance', () => { assert.notInclude(light, `--${adaptive_default.name}: ${adaptive_default.dark};`); }); - test('a light stance mirrors light values into the dark scheme', () => { + test('a light stance mirrors light values into the base slot', () => { const css = render_theme_style( resolve_theme_stance({ name: 't', variables: [], scheme: 'light' }) ); const { light, dark } = split_schemes(css); assert.include(light, 'color-scheme: light;'); - // the light slot's value renders in the :root.dark block, defeating base dark - assert.include(dark, `--${adaptive_default.name}: ${adaptive_default.light};`); + // the light value renders in the :root block, which applies in both + // schemes - fuz.theme beats the fuz.base :root.dark defaults by layer + // order, and staying out of :root.dark keeps later same-block + // declarations (overlays, compiled caps) winning by source order + assert.include(light, `--${adaptive_default.name}: ${adaptive_default.light};`); + assert.notInclude(dark, `--${adaptive_default.name}:`); }); test('resolve_theme_stance keeps the mirror out of the authored variables', () => { @@ -181,6 +197,40 @@ describe('compose_themes', () => { assert.deepEqual(shade, { name: 'shade_lightness_00', light: '0' }); }); + test('a light-stanced base takes overlay light values, dropping dark-only ones', () => { + const stanced: Theme = { ...base, scheme: 'light' }; + const composed = compose_themes(stanced, { + name: 'o', + variables: [ + { name: 'shade_lightness_00', light: '1', dark: '0' }, + { name: 'text_lightness_curve', dark: '0.5' } // dark-only never renders under a light stance + ] + }); + assert.deepEqual( + composed.variables.find((v) => v.name === 'shade_lightness_00'), + { name: 'shade_lightness_00', light: '1' } + ); + assert.isUndefined(composed.variables.find((v) => v.name === 'text_lightness_curve')); + }); + + test('composing over a resolved stanced base drops overlaid names from the mirror', () => { + // resolve first, like the shipped stanced exemplars arrive + const stanced = resolve_theme_stance({ name: 's', variables: [], scheme: 'light' }); + assert(stanced.scheme_mirror); + assert.isTrue(stanced.scheme_mirror.some((v) => v.name === adaptive_default.name)); + const composed = compose_themes(stanced, { + name: 'o', + variables: [{ name: adaptive_default.name, light: '0.123' }] + }); + // the overlay now authors the variable, so the mirror cedes it - the + // overlay's :root declaration must be the only one for the name + assert.isFalse(composed.scheme_mirror!.some((v) => v.name === adaptive_default.name)); + const css = render_theme_style(composed); + const { light, dark } = split_schemes(css); + assert.include(light, `--${adaptive_default.name}: 0.123;`); + assert.notInclude(dark, `--${adaptive_default.name}:`); + }); + test('rendering a composition applies the overlay over the base', () => { const css = render_theme_style(compose_themes(base, overlay)); assert.include(css, '--shade_lightness_00: 1;'); diff --git a/src/test/theme_check.compile.test.ts b/src/test/theme_check.compile.test.ts index a954ee2c9..847f0e3fc 100644 --- a/src/test/theme_check.compile.test.ts +++ b/src/test/theme_check.compile.test.ts @@ -1,6 +1,6 @@ import { test, assert, describe } from 'vitest'; -import { compile_theme } from '$lib/theme_check.ts'; +import { compile_theme, check_theme } from '$lib/theme_check.ts'; import type { Theme } from '$lib/theme.ts'; import { default_themes } from '$lib/themes.ts'; import { necromancer_theme } from '$lib/themes/necromancer.ts'; @@ -35,7 +35,9 @@ describe('compile_theme', () => { assert(stop_50, 'stop 50 emitted'); // one green hue has more gamut headroom than the worst-hue envelope assert.isAbove(cap_of(stop_50.light), PALETTE_CHROMA_CAPS.light['50']); - assert.isAbove(cap_of(stop_50.dark), PALETTE_CHROMA_CAPS.dark['50']); + // a stanced theme emits single-slot in the base position - both schemes + // resolve identically through the mirror + assert.isUndefined(stop_50.dark); assert.strictEqual(report.unchecked.length, 0); }); @@ -58,7 +60,7 @@ describe('compile_theme', () => { assert.isTrue(changed, 'light caps differ from the baked worst-hue table'); }); - test('a pinned palette_chroma_NN is respected — no emission for that stop', () => { + test('a pinned palette_chroma_NN is respected - no emission for that stop', () => { const input: Theme = { name: 'monochrome pinned', variables: [ @@ -71,7 +73,36 @@ describe('compile_theme', () => { assert.isFalse(overrides.some((v) => v.name === 'palette_chroma_50')); }); - test('the compiled theme is fully checkable — nothing unchecked', () => { + test('the compiled theme is fully checkable - nothing unchecked', () => { assert.strictEqual(compile_theme(create_monochrome_theme(145)).report.unchecked.length, 0); }); + + test('compiling fixes the gamut failures its input has - the differential', () => { + // an extremely light palette ramp: the baked worst-hue caps assume the + // default lightness, so the requested chroma is far out of gamut at the + // new lightness until compile recomputes the caps + const input: Theme = { + name: 'washed out', + variables: [ + { name: 'palette_lightness_00', light: '0.999', dark: '0.99' }, + { name: 'palette_lightness_100', light: '0.9', dark: '0.92' } + ] + }; + const before = check_theme(input).entries.filter((e) => e.gate === 'gamut' && !e.pass); + assert.isAbove(before.length, 0, 'the input fails gamut against the baked caps'); + const { report } = compile_theme(input); + const after = report.entries.filter((e) => e.gate === 'gamut' && !e.pass); + assert.deepEqual(after, [], 'compiled caps bring every stop back into gamut'); + assert.strictEqual(report.unchecked.length, 0); + }); + + test('a stanced compiled theme recomputes its scheme_mirror over the emitted variables', () => { + const input = create_monochrome_theme(145); // dark-stanced, unresolved + const { theme } = compile_theme(input); + assert(theme.scheme_mirror, 'compile resolves the stance'); + const mirror_names = new Set(theme.scheme_mirror.map((v) => v.name)); + for (const v of theme.variables) { + assert.isFalse(mirror_names.has(v.name), `mirror excludes emitted ${v.name}`); + } + }); }); diff --git a/src/test/theme_check.test.ts b/src/test/theme_check.test.ts index f5b945ad8..333f7e81a 100644 --- a/src/test/theme_check.test.ts +++ b/src/test/theme_check.test.ts @@ -1,6 +1,19 @@ import { test, assert, describe } from 'vitest'; -import { validate_theme, check_theme, resolve_theme_knob, GATE_LINK } from '$lib/theme_check.ts'; +import { + validate_theme, + check_theme, + resolve_theme_knob, + GATE_BODY_TEXT, + GATE_SUBTLE_TEXT, + GATE_LINK, + GATE_UI, + GATE_FILL_TEXT, + GATE_SELECTED_TEXT, + GATE_BORDER, + GATE_BORDER_DIVIDER +} from '$lib/theme_check.ts'; +import { resolve_theme_stance } from '$lib/theme_stance.ts'; import { compose_themes, type Theme } from '$lib/theme.ts'; import { default_themes, contrast_modifiers } from '$lib/themes.ts'; import { low_contrast_theme } from '$lib/themes/low_contrast.ts'; @@ -87,13 +100,32 @@ describe('validate_theme', () => { }); test('scheme stance values validate', () => { - for (const scheme of ['dual', 'light', 'dark'] as const) { - assert.deepEqual(validate_theme({ name: 't', variables: [], scheme }), []); + assert.deepEqual(validate_theme({ name: 't', variables: [], scheme: 'dual' }), []); + for (const scheme of ['light', 'dark'] as const) { + assert.deepEqual( + validate_theme(resolve_theme_stance({ name: 't', variables: [], scheme })), + [] + ); } const issues = validate_theme({ name: 't', variables: [], scheme: 'dusk' as 'dark' }); assert.isTrue(issues.some((i) => i.level === 'error')); }); + test('a single-scheme stance without a resolved mirror is a warning', () => { + for (const scheme of ['light', 'dark'] as const) { + const issues = validate_theme({ name: 't', variables: [], scheme }); + assert.isTrue( + issues.some((i) => i.level === 'warning' && i.message.includes('resolve_theme_stance')) + ); + assert.isFalse(issues.some((i) => i.level === 'error')); + // resolving clears it + assert.deepEqual( + validate_theme(resolve_theme_stance({ name: 't', variables: [], scheme })), + [] + ); + } + }); + test('a dark slot under a single-scheme stance is a warning, not an error', () => { for (const scheme of ['light', 'dark'] as const) { const issues = validate_theme({ @@ -106,11 +138,13 @@ describe('validate_theme', () => { } // single-slot stanced and dual-slot unstanced themes stay clean assert.deepEqual( - validate_theme({ - name: 't', - scheme: 'dark', - variables: [{ name: 'neutral_chroma', light: '0.02' }] - }), + validate_theme( + resolve_theme_stance({ + name: 't', + scheme: 'dark', + variables: [{ name: 'neutral_chroma', light: '0.02' }] + }) + ), [] ); assert.deepEqual( @@ -386,9 +420,42 @@ describe('scheme stance', () => { } }); + test('every contrast gate emits its pinned subjects at its exported threshold', () => { + // pins gate *emission*: deleting a gate's block from check_theme fails + // here even though every all-pass assertion elsewhere stays green + const report = check_theme({ name: 't', variables: [] }); + const by_subject = new Map<string, Array<(typeof report.entries)[number]>>(); + for (const e of report.entries) { + if (e.gate !== 'contrast') continue; + const list = by_subject.get(e.subject) ?? []; + list.push(e); + by_subject.set(e.subject, list); + } + const expect_subject = (subject: string, threshold: number): void => { + const entries = by_subject.get(subject); + assert(entries, `emits ${subject}`); + assert.strictEqual(entries.length, 2, `${subject} in both schemes`); + for (const e of entries) assert.strictEqual(e.threshold, threshold, subject); + }; + expect_subject('text_80 on shade_00', GATE_BODY_TEXT); + expect_subject('text_80 on shade_05', GATE_BODY_TEXT); + expect_subject('text_80 on shade_10', GATE_BODY_TEXT); + expect_subject('text_00 on shade_50', GATE_SELECTED_TEXT); + expect_subject('text_50 on shade_00', GATE_SUBTLE_TEXT); + expect_subject('shade_30 vs shade_00', GATE_BORDER); + expect_subject('border_color_30 over shade_00', GATE_BORDER_DIVIDER); + expect_subject('accent_60 on shade_00', GATE_LINK); + for (const letter of ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']) { + expect_subject(`palette_${letter}_50 vs shade_00`, GATE_UI); + expect_subject(`text_max on palette_${letter}_50`, GATE_FILL_TEXT); + expect_subject(`text_00 on palette_${letter}_50`, GATE_SELECTED_TEXT); + } + }); + test('the dark-stanced exemplars pass their contrast gates in both schemes', () => { for (const theme of [necromancer_theme, terminalien_theme]) { const contrast = check_theme(theme).entries.filter((e) => e.gate === 'contrast'); + assert.isAbove(contrast.length, 0, `${theme.name}: contrast gates resolved`); assert.isTrue( contrast.every((e) => e.pass), `${theme.name}: ${JSON.stringify(contrast.filter((e) => !e.pass))}` @@ -398,13 +465,32 @@ describe('scheme stance', () => { }); describe('contrast modifier compositions', () => { - const bases = [ - default_themes[0]!, - necromancer_theme, - sunset_ember_theme, - brutalish_theme, - terminalien_theme - ]; + // discover the shipped themes by glob like themes.test.ts, so a new + // exemplar module can't be silently left out of the composition matrix + const theme_modules = import.meta.glob('../lib/themes/*.ts', { eager: true }); + const is_theme = (value: unknown): value is Theme => + value !== null && + typeof value === 'object' && + 'name' in value && + 'variables' in value && + Array.isArray((value as Theme).variables); + const modifier_names = new Set(contrast_modifiers.map((t) => t.name)); + const bases: Array<Theme> = Object.values(theme_modules) + .flatMap((mod) => Object.values(mod as Record<string, unknown>).filter(is_theme)) + .filter((t) => !modifier_names.has(t.name)); + + test('the glob discovers every hand-known base', () => { + const names = new Set(bases.map((t) => t.name)); + for (const expected of [ + default_themes[0]!.name, + necromancer_theme.name, + sunset_ember_theme.name, + brutalish_theme.name, + terminalien_theme.name + ]) { + assert.isTrue(names.has(expected), expected); + } + }); test('every base × modifier validates with no errors', () => { for (const base of bases) { @@ -418,9 +504,25 @@ describe('contrast modifier compositions', () => { // declared exception: sunset ember's past-cap cyan/teal UI fills sit just // under the 3:1 fill gate against low contrast's raised background floor - // (~2.89–2.91) — a marginal, known combination cost, not a regression + // (~2.89 to 2.91) - a marginal, known combination cost, not a regression const known_failing = new Set(['sunset ember (low contrast)']); + test('every base × modifier resolves fully and keeps its lightness ramps monotonic', () => { + for (const base of bases) { + for (const modifier of contrast_modifiers) { + const composed = compose_themes(base, modifier); + const report = check_theme(composed); + // nothing unresolvable: an unresolved knob silently removes gate + // entries, so this is what keeps the contrast assertions honest + assert.deepEqual(report.unchecked, [], composed.name); + const monotonicity = report.entries.filter((e) => e.gate === 'monotonicity'); + assert.isAbove(monotonicity.length, 0, `${composed.name}: monotonicity resolved`); + const failing_monotonicity = monotonicity.filter((e) => !e.pass); + assert.deepEqual(failing_monotonicity, [], composed.name); + } + } + }); + test('every base × modifier keeps its contrast gates, minus declared exceptions', () => { for (const base of bases) { for (const modifier of contrast_modifiers) { diff --git a/src/test/theme_editor_state.test.ts b/src/test/theme_editor_state.test.ts new file mode 100644 index 000000000..b73ca45a0 --- /dev/null +++ b/src/test/theme_editor_state.test.ts @@ -0,0 +1,240 @@ +/** + * Tests for the docs site's theme editor state: the slot-merge semantics, + * scheme-stance re-slotting, display values, snapshots, and the copyable + * TypeScript output. + * + * @module + */ + +import { test, assert, describe } from 'vitest'; + +import { + ThemeEditorState, + render_theme_ts, + discard_confirm_message, + UNSAVED_THEME_NAME +} from '$routes/theme_editor_state.svelte.ts'; +import type { Theme } from '$lib/theme.ts'; +import { base_theme } from '$lib/themes/base.ts'; +import { necromancer_theme } from '$lib/themes/necromancer.ts'; +import { default_variables } from '$lib/variables.ts'; + +const adaptive_default = default_variables.find((v) => v.name === 'shade_lightness_00')!; +const single_slot_default = default_variables.find((v) => v.name === 'chroma_scale')!; + +const create_editor = (): ThemeEditorState => + new ThemeEditorState([base_theme, necromancer_theme]); + +describe('set_value slot semantics', () => { + test('a scheme-adaptive variable edits the viewed scheme, preserving the other slot', () => { + const editor = create_editor(); + editor.set_value(adaptive_default.name, '0.9', 'light'); + const merged = editor.merged_variables.find((v) => v.name === adaptive_default.name); + // the default's dark slot is preserved explicitly - a theme's :root block + // beats the base defaults' :root.dark by layer order, so omitting it + // would silently change dark mode too + assert.deepEqual(merged, { + name: adaptive_default.name, + light: '0.9', + dark: adaptive_default.dark + }); + }); + + test('editing the dark slot leaves the light slot to the default', () => { + const editor = create_editor(); + editor.set_value(adaptive_default.name, '0.3', 'dark'); + const merged = editor.merged_variables.find((v) => v.name === adaptive_default.name); + assert.deepEqual(merged, { name: adaptive_default.name, dark: '0.3' }); + }); + + test('a single-slot variable always edits the base slot', () => { + const editor = create_editor(); + editor.set_value(single_slot_default.name, '1.4', 'dark'); + const merged = editor.merged_variables.find((v) => v.name === single_slot_default.name); + assert.deepEqual(merged, { name: single_slot_default.name, light: '1.4' }); + }); + + test('identical light and dark values collapse to a single slot', () => { + const editor = create_editor(); + editor.set_value(adaptive_default.name, '0.5', 'light'); + editor.set_value(adaptive_default.name, '0.5', 'dark'); + const merged = editor.merged_variables.find((v) => v.name === adaptive_default.name); + assert.deepEqual(merged, { name: adaptive_default.name, light: '0.5' }); + }); +}); + +describe('scheme stance', () => { + test('entering a stance re-slots existing overrides to the base slot', () => { + const editor = create_editor(); + editor.set_value(adaptive_default.name, '0.25', 'dark'); + editor.set_scheme('dark'); + const merged = editor.merged_variables.find((v) => v.name === adaptive_default.name); + assert.deepEqual(merged, { name: adaptive_default.name, light: '0.25' }); + }); + + test('a light stance drops dark-only overrides - that appearance never renders', () => { + const editor = create_editor(); + editor.set_value(adaptive_default.name, '0.25', 'dark'); + editor.set_scheme('light'); + assert.isFalse(editor.overrides.has(adaptive_default.name)); + }); + + test('edits under a stance write the base slot and skip dark preservation', () => { + const editor = create_editor(); + editor.set_scheme('dark'); + editor.set_value(adaptive_default.name, '0.25', 'dark'); + const merged = editor.merged_variables.find((v) => v.name === adaptive_default.name); + // no preserved dark slot - the stance mirror owns untouched defaults + assert.deepEqual(merged, { name: adaptive_default.name, light: '0.25' }); + }); + + test('the draft and output of a stanced editor carry a resolved scheme_mirror', () => { + const editor = create_editor(); + editor.set_scheme('dark'); + assert.strictEqual(editor.draft.scheme, 'dark'); + assert.isAbove(editor.draft.scheme_mirror!.length, 0); + assert.isAbove(editor.output.scheme_mirror!.length, 0); + // a dual editor's draft has no mirror + const dual = create_editor(); + assert.isUndefined(dual.draft.scheme_mirror); + }); +}); + +describe('display_value', () => { + test('falls back to the default per scheme', () => { + const editor = create_editor(); + assert.strictEqual( + editor.display_value(adaptive_default.name, 'light'), + adaptive_default.light + ); + assert.strictEqual(editor.display_value(adaptive_default.name, 'dark'), adaptive_default.dark); + }); + + test('a light-only merged value applies to both schemes', () => { + const editor = create_editor(); + editor.set_value(single_slot_default.name, '1.7', 'light'); + assert.strictEqual(editor.display_value(single_slot_default.name, 'light'), '1.7'); + assert.strictEqual(editor.display_value(single_slot_default.name, 'dark'), '1.7'); + }); + + test('a stance mirrors untouched scheme-adaptive defaults into both schemes', () => { + const editor = create_editor(); + editor.set_scheme('dark'); + assert.strictEqual( + editor.display_value(adaptive_default.name, 'light'), + adaptive_default.dark + ); + assert.strictEqual(editor.display_value(adaptive_default.name, 'dark'), adaptive_default.dark); + }); +}); + +describe('load_theme and dirty', () => { + test('a fresh editor is clean; edits and scheme changes dirty it', () => { + const editor = create_editor(); + assert.isFalse(editor.dirty); + editor.set_value(single_slot_default.name, '2', 'light'); + assert.isTrue(editor.dirty); + editor.reset(single_slot_default.name); + assert.isFalse(editor.dirty); + editor.set_scheme('dark'); + assert.isTrue(editor.dirty); + editor.reset_all(); + assert.isFalse(editor.dirty); + }); + + test('loading a theme flattens it as the base and carries its stance', () => { + const editor = create_editor(); + editor.set_value(single_slot_default.name, '2', 'light'); + editor.load_theme(necromancer_theme); + assert.strictEqual(editor.based_on, necromancer_theme.name); + assert.strictEqual(editor.overrides.size, 0); + assert.strictEqual(editor.scheme, 'dark'); + assert.isFalse(editor.dirty); + // the base theme's own variables flow into the merge + const merged_names = new Set(editor.merged_variables.map((v) => v.name)); + for (const v of necromancer_theme.variables) { + assert.isTrue(merged_names.has(v.name), v.name); + } + }); + + test('the unsaved draft itself never loads as a base', () => { + const editor = create_editor(); + editor.load_theme({ name: UNSAVED_THEME_NAME, variables: [] }); + assert.strictEqual(editor.based_on, 'base'); + }); +}); + +describe('snapshots', () => { + test('round-trips name, base, scheme, and overrides', () => { + const editor = create_editor(); + editor.name = 'my theme'; + editor.load_theme(necromancer_theme); + editor.set_value(single_slot_default.name, '1.4', 'light'); + const snapshot = editor.to_snapshot(); + + const restored = create_editor(); + restored.restore_snapshot(snapshot); + assert.strictEqual(restored.based_on, necromancer_theme.name); + assert.strictEqual(restored.scheme, 'dark'); + assert.deepEqual(restored.overrides.get(single_slot_default.name), { light: '1.4' }); + }); + + test('a stale snapshot referencing a removed theme falls back to the first', () => { + const editor = create_editor(); + editor.restore_snapshot({ + name: 'x', + based_on: 'deleted theme', + scheme: 'dual', + overrides: [] + }); + assert.strictEqual(editor.based_on, base_theme.name); + }); +}); + +describe('render_theme_ts', () => { + test('a dual theme renders a plain module', () => { + const ts = render_theme_ts({ + name: 'my theme', + variables: [{ name: 'chroma_scale', light: '1.4' }] + }); + assert.include(ts, 'export const my_theme_theme: Theme = {'); + assert.include(ts, "{name: 'chroma_scale', light: '1.4'},"); + assert.notInclude(ts, 'resolve_theme_stance'); + }); + + test('a stanced theme renders the resolve-at-module-scope shape', () => { + const ts = render_theme_ts({ + name: 'darkling', + scheme: 'dark', + variables: [{ name: 'neutral_chroma', light: '0.04' }] + }); + assert.include(ts, "import {resolve_theme_stance} from '@fuzdev/fuz_css/theme_stance.ts';"); + assert.include(ts, "scheme: 'dark',"); + assert.include(ts, 'const authored: Theme = {'); + assert.include(ts, 'export const darkling_theme: Theme = resolve_theme_stance(authored);'); + }); + + test('single quotes in values escape', () => { + const ts = render_theme_ts({ + name: "it's", + variables: [{ name: 'background_image', light: "url('x.png')" }] + }); + assert.include(ts, "name: 'it\\'s'"); + assert.include(ts, "light: 'url(\\'x.png\\')'"); + }); +}); + +describe('discard_confirm_message', () => { + test('names the discarded work', () => { + const editor = create_editor(); + editor.set_value(single_slot_default.name, '2', 'light'); + assert.include(discard_confirm_message(editor, 'necromancer'), '1 edited knob(s)'); + editor.reset_all(); + editor.set_scheme('dark'); + assert.include(discard_confirm_message(editor, 'necromancer'), 'scheme change'); + }); +}); + +// keep the type import "used" for the linter across fixture literals +const _theme_type_check: Theme = base_theme; +void _theme_type_check; diff --git a/src/test/themes.test.ts b/src/test/themes.test.ts index 7cc622efe..6187d6d44 100644 --- a/src/test/themes.test.ts +++ b/src/test/themes.test.ts @@ -5,7 +5,7 @@ import { default_themes, DEFAULT_THEME, contrast_modifiers } from '$lib/themes.t import { StyleVariable } from '$lib/variable.ts'; import { validate_theme } from '$lib/theme_check.ts'; -// every theme module ships from themes/, so discover them by glob — a new +// every theme module ships from themes/, so discover them by glob - a new // module can't silently skip validation by being left off a hand-list const theme_modules = import.meta.glob('../lib/themes/*.ts', { eager: true }); diff --git a/src/test/variable_graph.test.ts b/src/test/variable_graph.test.ts index fee3a2314..dae3c3cea 100644 --- a/src/test/variable_graph.test.ts +++ b/src/test/variable_graph.test.ts @@ -20,12 +20,11 @@ describe('build_variable_graph', () => { { name: 'palette_a', light: 'blue', dark: 'lightblue' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); assert.strictEqual(graph.variables.size, 1); assert.strictEqual(graph.variables.get('palette_a')!.light_css, 'blue'); assert.strictEqual(graph.variables.get('palette_a')!.dark_css, 'lightblue'); - assert.strictEqual(graph.content_hash, 'test-hash'); }); test('extracts dependencies from var() references', () => { @@ -34,7 +33,7 @@ describe('build_variable_graph', () => { { name: 'palette_a', light: 'hsl(var(--hue_a) 50% 50%)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); assert.isTrue(graph.variables.get('palette_a')!.light_deps.has('hue_a')); assert.strictEqual(graph.variables.get('hue_a')!.light_deps.size, 0); @@ -45,7 +44,7 @@ describe('build_variable_graph', () => { test('handles light-only variable', () => { const variables: Array<StyleVariable> = [{ name: 'spacing', light: '16px' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); assert.strictEqual(graph.variables.get('spacing')!.light_css, '16px'); assert.isUndefined(graph.variables.get('spacing')!.dark_css); @@ -55,7 +54,7 @@ describe('build_variable_graph', () => { test('handles dark-only variable', () => { const variables: Array<StyleVariable> = [{ name: 'shadow', dark: 'none' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); assert.isUndefined(graph.variables.get('shadow')!.light_css); assert.strictEqual(graph.variables.get('shadow')!.dark_css, 'none'); @@ -68,7 +67,7 @@ describe('build_variable_graph', () => { { name: 'composite', light: 'var(--base_light)', dark: 'var(--base_dark)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); assert.isTrue(graph.variables.get('composite')!.light_deps.has('base_light')); assert.isFalse(graph.variables.get('composite')!.light_deps.has('base_dark')); @@ -85,7 +84,7 @@ describe('resolve_variables_transitive', () => { { name: 'a', light: '1' }, { name: 'b', light: '2' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['a']); @@ -99,7 +98,7 @@ describe('resolve_variables_transitive', () => { { name: 'hue', light: '210' }, { name: 'color', light: 'hsl(var(--hue) 50% 50%)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['color']); @@ -113,7 +112,7 @@ describe('resolve_variables_transitive', () => { { name: 'b', light: '2' }, { name: 'c', light: '3' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['a', 'c']); @@ -131,7 +130,7 @@ describe('resolve_variables_transitive', () => { { name: 'c', light: 'var(--b)' }, { name: 'd', light: 'var(--c)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['d']); @@ -148,7 +147,7 @@ describe('resolve_variables_transitive', () => { { name: 'c', light: 'independent-value' }, { name: 'a', light: 'calc(var(--b) + var(--c))' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['a']); @@ -170,7 +169,7 @@ describe('resolve_variables_transitive', () => { { name: 'b', light: 'calc(var(--d) + var(--e))' }, { name: 'root', light: 'calc(var(--a) + var(--b))' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['root']); @@ -190,7 +189,7 @@ describe('resolve_variables_transitive', () => { { name: 'x', light: 'var(--shared)' }, { name: 'y', light: 'var(--shared)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['x', 'y']); @@ -208,7 +207,7 @@ describe('resolve_variables_transitive', () => { { name: 'dark_base', dark: 'black' }, { name: 'combo', light: 'var(--light_base)', dark: 'var(--dark_base)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['combo']); @@ -225,7 +224,7 @@ describe('resolve_variables_transitive', () => { { name: 'dark_mid', dark: 'var(--dark_leaf)' }, { name: 'themed', light: 'var(--light_mid)', dark: 'var(--dark_mid)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['themed']); @@ -246,7 +245,7 @@ describe('resolve_variables_transitive', () => { { name: 'c', light: '3' }, { name: 'composed', light: 'var(--a, var(--b, var(--c)))' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['composed']); @@ -260,7 +259,7 @@ describe('resolve_variables_transitive', () => { describe('missing variables', () => { test('tracks missing variables', () => { const variables: Array<StyleVariable> = [{ name: 'known', light: '1' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['known', 'unknown']); @@ -273,7 +272,7 @@ describe('resolve_variables_transitive', () => { test('tracks multiple missing variables', () => { const variables: Array<StyleVariable> = [{ name: 'exists', light: '1' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, [ 'exists', @@ -290,7 +289,7 @@ describe('resolve_variables_transitive', () => { test('tracks missing dependencies', () => { const variables: Array<StyleVariable> = [{ name: 'root', light: 'var(--missing_dep)' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['root']); @@ -305,7 +304,7 @@ describe('resolve_variables_transitive', () => { { name: 'a', light: '1' }, { name: 'b', light: 'var(--a)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['b']); @@ -342,7 +341,7 @@ describe('resolve_variables_transitive', () => { ['a', 'b'] ] ])('detects %s', (_name, variables, expected_vars) => { - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['a']); for (const v of expected_vars) { @@ -356,7 +355,7 @@ describe('resolve_variables_transitive', () => { const variables: Array<StyleVariable> = [ { name: 'x', light: 'var(--x, 1)', dark: 'var(--x, 2)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['x']); assert.isTrue(result.variables.has('x')); @@ -375,7 +374,7 @@ describe('resolve_variables_transitive', () => { { name: 'e', light: '5' }, { name: 'deep', light: 'var(--a, var(--b, var(--c, var(--d, var(--e)))))' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['deep']); for (const v of ['a', 'b', 'c', 'd', 'e', 'deep']) { @@ -394,7 +393,7 @@ describe('resolve_variables_transitive', () => { { name: 'd3', dark: 'var(--d2)' }, { name: 'themed', light: 'var(--l3)', dark: 'var(--d3)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['themed']); assert.strictEqual(result.variables.size, 7); @@ -415,7 +414,7 @@ describe('resolve_variables_transitive', () => { { name: 'c', light: 'fallback-value' }, { name: 'a', light: 'var(--b, var(--c))' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['a']); assert.isTrue(result.variables.has('a')); @@ -432,7 +431,7 @@ describe('resolve_variables_transitive', () => { { name: 'padding', light: '20px' }, { name: 'content_width', light: 'calc(var(--width) - var(--padding) * 2)' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['content_width']); assert.isTrue(result.variables.has('content_width')); @@ -446,7 +445,7 @@ describe('resolve_variables_transitive', () => { { name: 'base', light: '10px' }, { name: 'computed', light: 'calc(var(--base) + var(--missing, 5px))' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['computed']); assert.isTrue(result.variables.has('computed')); @@ -462,7 +461,7 @@ describe('resolve_variables_transitive', () => { { name: 'c', light: '30' }, { name: 'nested', light: 'calc(var(--a) + calc(var(--b) * var(--c)))' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = resolve_variables_transitive(graph, ['nested']); for (const v of ['a', 'b', 'c', 'nested']) { @@ -476,7 +475,7 @@ describe('generate_theme_css', () => { describe('basic output', () => { test('generates light and dark blocks', () => { const variables: Array<StyleVariable> = [{ name: 'color', light: 'blue', dark: 'lightblue' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const { light_css, dark_css } = generate_theme_css(graph, new Set(['color'])); @@ -488,7 +487,7 @@ describe('generate_theme_css', () => { test('renders single :root scopes', () => { const variables: Array<StyleVariable> = [{ name: 'color', light: 'blue', dark: 'lightblue' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const { light_css, dark_css } = generate_theme_css(graph, new Set(['color'])); @@ -500,7 +499,7 @@ describe('generate_theme_css', () => { describe('light/dark only variables', () => { test('light-only produces only light block', () => { const variables: Array<StyleVariable> = [{ name: 'spacing', light: '16px' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const { light_css, dark_css } = generate_theme_css(graph, new Set(['spacing'])); @@ -510,7 +509,7 @@ describe('generate_theme_css', () => { test('dark-only produces only dark block', () => { const variables: Array<StyleVariable> = [{ name: 'glow', dark: 'none' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const { light_css, dark_css } = generate_theme_css(graph, new Set(['glow'])); @@ -526,7 +525,7 @@ describe('generate_theme_css', () => { { name: 'alpha', light: '1' }, { name: 'mid', light: '2' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const { light_css } = generate_theme_css(graph, new Set(['zebra', 'alpha', 'mid'])); @@ -547,7 +546,7 @@ describe('utility functions', () => { { name: 'b', light: '2' }, { name: 'c', light: '3' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const names = get_all_variable_names(graph); @@ -559,7 +558,7 @@ describe('utility functions', () => { test('has_variable checks existence', () => { const variables: Array<StyleVariable> = [{ name: 'exists', light: '1' }]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); assert.isTrue(has_variable(graph, 'exists')); assert.isFalse(has_variable(graph, 'missing')); @@ -612,7 +611,7 @@ describe('find_similar_variable', () => { 'swapped chars' ] ])('%s → %s (%s)', (typo, expected, variables) => { - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); assert.strictEqual(find_similar_variable(graph, typo), expected); }); }); @@ -623,13 +622,13 @@ describe('find_similar_variable', () => { { name: 'color_primary', light: 'blue' }, { name: 'spacing_md', light: '16px' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); assert.isNull(find_similar_variable(graph, name)); }); }); test('returns null for empty graph', () => { - const graph = build_variable_graph([], 'test-hash'); + const graph = build_variable_graph([]); assert.isNull(find_similar_variable(graph, 'anything')); }); @@ -639,7 +638,7 @@ describe('find_similar_variable', () => { { name: 'palette_a_2', light: '2' }, { name: 'palette_a_3', light: '3' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); const result = find_similar_variable(graph, 'palette_a_'); assert.isNotNull(result); @@ -651,7 +650,7 @@ describe('find_similar_variable', () => { { name: 'shadow_xs', light: '1px' }, { name: 'shadow_sm', light: '2px' } ]; - const graph = build_variable_graph(variables, 'test-hash'); + const graph = build_variable_graph(variables); assert.isNull(find_similar_variable(graph, 'shadow')); }); }); @@ -717,11 +716,52 @@ describe('apply_theme_variables', () => { const theme: Theme = { name: 't', variables: [{ name: 'hue_a', light: '30' }] }; const graph = build_variable_graph_from_options(defaults, theme); assert.strictEqual(graph.variables.get('hue_a')!.light_css, '30'); - // a different theme must not collide in the graph's content hash + // a different theme produces a different graph const other = build_variable_graph_from_options(defaults, { name: 't2', variables: [{ name: 'hue_a', light: '40' }] }); - assert.notStrictEqual(graph.content_hash, other.content_hash); + assert.strictEqual(other.variables.get('hue_a')!.light_css, '40'); + }); +}); + +describe('apply_theme_variables slot semantics', () => { + const defaults: Array<StyleVariable> = [{ name: 'shade_00', light: '#fff', dark: '#000' }]; + + test('a light-only theme value replaces both slots, matching the runtime cascade', () => { + // at runtime a fuz.theme :root declaration beats the fuz.base :root.dark + // default by layer order, so the baked entry drops the dark slot + const result = apply_theme_variables(defaults, { + name: 't', + variables: [{ name: 'shade_00', light: '#eee' }] + }); + assert.deepEqual(result, [{ name: 'shade_00', light: '#eee' }]); + }); + + test("a dark-only theme value keeps the default's light slot", () => { + // at runtime the theme's :root.dark only shadows under `.dark`, so the + // light scheme still resolves the default - the baked entry must too + const result = apply_theme_variables(defaults, { + name: 't', + variables: [{ name: 'shade_00', dark: '#111' }] + }); + assert.deepEqual(result, [{ name: 'shade_00', light: '#fff', dark: '#111' }]); + }); +}); + +describe('build-time theme against the real defaults', () => { + test("a theme's var() reference pulls the variable in transitively", () => { + const graph = build_variable_graph_from_options(undefined, { + name: 't', + variables: [{ name: 'hue_accent', light: 'var(--hue_d)' }] + }); + const { variables, missing } = resolve_variables_transitive(graph, ['accent_50']); + assert.strictEqual(missing.size, 0); + assert.isTrue(variables.has('hue_accent')); + // the rebinding's target rides along even though nothing else uses it + assert.isTrue(variables.has('hue_d')); + const { light_css } = generate_theme_css(graph, variables); + assert.include(light_css, '--hue_accent: var(--hue_d);'); + assert.include(light_css, '--hue_d:'); }); }); diff --git a/src/test/variables.test.ts b/src/test/variables.test.ts index 771c4c1f3..4fec21fcd 100644 --- a/src/test/variables.test.ts +++ b/src/test/variables.test.ts @@ -3,6 +3,8 @@ import { test, assert } from 'vitest'; import { default_variables } from '$lib/variables.ts'; import * as exported_variables from '$lib/variables.ts'; import { StyleVariable } from '$lib/variable.ts'; +import { PALETTE_CHROMA_MULTIPLIERS } from '$lib/ramps.ts'; +import { palette_variants, intent_variants } from '$lib/variable_data.ts'; test('all variables pass schema validation', () => { for (const v of default_variables) { @@ -51,3 +53,21 @@ test('variable identifiers are all included in `default_variables`', () => { }); const is_style_variable = (v: unknown): v is StyleVariable => StyleVariable.safeParse(v).success; + +test('per-slot chroma multiplier defaults pin the numeric twin', () => { + // the emitted CSS falls back to 1 when the variable is absent, so the + // declared defaults are what keep the CSS and the numeric-twin gates + // agreeing - brown's 0.55 mute in particular + const by_name = new Map(default_variables.map((v) => [v.name, v])); + for (const letter of palette_variants) { + const v = by_name.get(`palette_${letter}_chroma_scale`); + assert(v, `palette_${letter}_chroma_scale is declared`); + assert.strictEqual(v.light, String(PALETTE_CHROMA_MULTIPLIERS[letter]), v.name); + assert.isUndefined(v.dark, `${v.name} is scheme-agnostic`); + } + for (const intent of intent_variants) { + const v = by_name.get(`${intent}_chroma_scale`); + assert(v, `${intent}_chroma_scale is declared`); + assert.strictEqual(v.light, '1', v.name); + } +}); diff --git a/src/test/vite_plugin_examples.test.ts b/src/test/vite_plugin_examples.test.ts index b774846a6..04ccad97c 100644 --- a/src/test/vite_plugin_examples.test.ts +++ b/src/test/vite_plugin_examples.test.ts @@ -33,7 +33,7 @@ if (!SKIP) { // `svelte-package` leaves relative `.ts` specifiers in `dist`; with // `rewriteRelativeImportExtensions: false` tsc no longer rewrites them, so the // gro dist-rewrite pass (which `gro build` runs after svelte-package) must run - // here too — otherwise the examples can't resolve the freshly-packaged dist. + // here too - otherwise the examples can't resolve the freshly-packaged dist. const { rewrite_dist_imports } = await import('@fuzdev/gro/dist_rewrite_imports.ts'); await rewrite_dist_imports(join(process.cwd(), 'dist')); }, 60_000); // 1 minute timeout for package build diff --git a/src/test/vite_plugin_fuz_css.dev.test.ts b/src/test/vite_plugin_fuz_css.dev.test.ts index f0e1f08c7..2a3164731 100644 --- a/src/test/vite_plugin_fuz_css.dev.test.ts +++ b/src/test/vite_plugin_fuz_css.dev.test.ts @@ -9,7 +9,7 @@ import { vite_plugin_fuz_css, type VitePluginFuzCssOptions } from '$lib/vite_plu const fixture_root = join(dirname(fileURLToPath(import.meta.url)), 'fixtures/vite_dev'); // The fixture lives under `src/test/`, which the default filter excludes by -// path — scope extraction to the fixture's html files instead. +// path - scope extraction to the fixture's html files instead. const filter_fixture_file = (path: string): boolean => path.endsWith('.html'); const create_dev_server = (options?: VitePluginFuzCssOptions): Promise<ViteDevServer> => @@ -34,7 +34,7 @@ describe('vite_plugin_fuz_css dev pre-scan', () => { test('first CSS serve includes classes from files no module ever imported', async () => { const server = await create_dev_server(); try { - // Request the virtual CSS before transforming anything — the cold-start + // Request the virtual CSS before transforming anything - the cold-start // shape where extraction state would otherwise be empty. const result = await server.transformRequest('/__fuz.css'); assert(result); diff --git a/src/test/vite_plugin_fuz_css.ws.test.ts b/src/test/vite_plugin_fuz_css.ws.test.ts new file mode 100644 index 000000000..bc1ddeb94 --- /dev/null +++ b/src/test/vite_plugin_fuz_css.ws.test.ts @@ -0,0 +1,129 @@ +/** + * Connect-time HMR resync over a real websocket: a client whose socket opens + * after a missed CSS update must be pushed the fresh virtual module. The + * middleware-mode dev tests can't reach this path (the plugin's connection + * listener registers against Vite's noop ws stub), so this suite runs a + * listening dev server and speaks the `vite-hmr` protocol with Node's global + * `WebSocket`. + * + * @module + */ + +import { describe, test, assert, afterAll } from 'vitest'; +import { createServer, type ViteDevServer } from 'vite'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; +import { rm } from 'node:fs/promises'; +import type { AddressInfo } from 'node:net'; + +import { vite_plugin_fuz_css } from '$lib/vite_plugin_fuz_css.ts'; + +const fixture_root = join(dirname(fileURLToPath(import.meta.url)), 'fixtures/vite_dev'); + +// scope extraction to the fixture's files (the default filter excludes +// src/test/ paths); .ts included so late_module.ts ingests on transform +const filter_fixture_file = (path: string): boolean => + path.endsWith('.html') || path.endsWith('late_module.ts'); + +afterAll(async () => { + await rm(join(fixture_root, '.fuz'), { recursive: true, force: true }); +}); + +interface WsSession { + socket: WebSocket; + messages: Array<{ type: string; [key: string]: unknown }>; + close: () => void; +} + +/** Opens a `vite-hmr` websocket and collects parsed messages. */ +const connect_hmr = (port: number): Promise<WsSession> => + new Promise((resolve, reject) => { + const socket = new WebSocket(`ws://127.0.0.1:${port}`, 'vite-hmr'); + const messages: Array<{ type: string; [key: string]: unknown }> = []; + socket.addEventListener('message', (e) => { + messages.push(JSON.parse(String(e.data))); + }); + socket.addEventListener('open', () => { + resolve({ socket, messages, close: () => socket.close() }); + }); + socket.addEventListener('error', () => { + reject(new Error('websocket failed to connect')); + }); + }); + +/** Polls until `predicate` or times out; resolves the predicate's result. */ +const wait_for = async <T>( + predicate: () => T | undefined, + timeout = 3000, + interval = 25 +): Promise<T> => { + const deadline = Date.now() + timeout; + for (;;) { + const result = predicate(); + if (result !== undefined) return result; + if (Date.now() > deadline) throw new Error('timed out waiting'); + await new Promise((r) => setTimeout(r, interval)); + } +}; + +describe('vite_plugin_fuz_css connect-time resync', () => { + test('a socket opening after a missed update is pushed the fresh CSS', async () => { + let server: ViteDevServer | null = null; + const sessions: Array<WsSession> = []; + try { + server = await createServer({ + root: fixture_root, + configFile: false, + logLevel: 'silent', + server: { host: '127.0.0.1', port: 0 }, + optimizeDeps: { noDiscovery: true }, + plugins: [vite_plugin_fuz_css({ filter_file: filter_fixture_file })] + }); + await server.listen(); + const port = (server.httpServer!.address() as AddressInfo).port; + + // first serve: the state every already-connected client holds + const first = await server.transformRequest('/__fuz.css'); + assert(first); + assert(first.code.includes('.p_md'), 'prescan classes served'); + assert(!first.code.includes('.mb_xl3'), 'late module not yet ingested'); + + // a client connected while the CSS is current gets no update push + const idle = await connect_hmr(port); + sessions.push(idle); + await wait_for(() => (idle.messages.some((m) => m.type === 'connected') ? true : undefined)); + await new Promise((r) => setTimeout(r, 150)); + assert.isFalse( + idle.messages.some((m) => m.type === 'update'), + 'no update for an in-sync client' + ); + idle.close(); + + // diverge with no client connected: transforming the late module + // ingests new classes and schedules the debounced push, which is + // dropped because no socket is open + await server.transformRequest('/extra/late_module.ts'); + await new Promise((r) => setTimeout(r, 100)); // let the debounce fire unheard + + // the resync: a socket opening now gets pushed the virtual module + const late = await connect_hmr(port); + sessions.push(late); + const update = await wait_for(() => + late.messages.find( + (m) => + m.type === 'update' && + JSON.stringify((m as { updates?: unknown }).updates ?? '').includes('__fuz.css') + ) + ); + assert(update, 'the late client received the virtual CSS update'); + + // the refetch the update triggers serves the diverged CSS + const refetched = await server.transformRequest('/__fuz.css'); + assert(refetched); + assert(refetched.code.includes('.mb_xl3'), 'refetch serves the late class'); + } finally { + for (const s of sessions) s.close(); + await server?.close(); + } + }, 20_000); +}); From c1c19a20755a228aea6522c16dbf99f90174f7b1 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Wed, 29 Jul 2026 12:17:12 -0400 Subject: [PATCH 32/33] cleanup --- src/lib/ramps.ts | 5 ++++- src/lib/theme_check.ts | 3 +-- src/test/ramps.test.ts | 13 +++---------- src/test/theme_editor_state.test.ts | 8 ++------ 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/lib/ramps.ts b/src/lib/ramps.ts index aa7380d3b..1a05c1ade 100644 --- a/src/lib/ramps.ts +++ b/src/lib/ramps.ts @@ -185,7 +185,10 @@ export const BORDER_CHROMA_MULTIPLIER: Record<ColorSchemeVariant, number> = { * in dark mode where borders are less visible against dark backgrounds. * Stop 00 renders `transparent` and stop 100 opaque. */ -export const BORDER_COLOR_ALPHAS: Record<ColorSchemeVariant, Record<NumericScaleVariant, number>> = { +export const BORDER_COLOR_ALPHAS: Record< + ColorSchemeVariant, + Record<NumericScaleVariant, number> +> = { light: { '00': 0, '05': 4, diff --git a/src/lib/theme_check.ts b/src/lib/theme_check.ts index c298f03bb..a99c051b7 100644 --- a/src/lib/theme_check.ts +++ b/src/lib/theme_check.ts @@ -213,8 +213,7 @@ const PALETTE_CHROMA_STOP_MATCHER = /^palette_chroma_(00|05|10|20|30|40|50|60|70 const VAR_MATCHER = /^var\(\s*--([a-z][a-z0-9_]*)\s*\)$/u; // the scaled-reference form emitted for `border_color_chroma` // (`calc(var(--neutral_chroma) * 2.12)`) and useful for authored multipliers -const SCALED_VAR_MATCHER = - /^calc\(\s*var\(\s*--([a-z][a-z0-9_]*)\s*\)\s*\*\s*(-?\d*\.?\d+)\s*\)$/u; +const SCALED_VAR_MATCHER = /^calc\(\s*var\(\s*--([a-z][a-z0-9_]*)\s*\)\s*\*\s*(-?\d*\.?\d+)\s*\)$/u; /** * The compiled worst-hue cap form emitted by `render_chroma_stop_css`: diff --git a/src/test/ramps.test.ts b/src/test/ramps.test.ts index 03eb6c42c..f75e6449c 100644 --- a/src/test/ramps.test.ts +++ b/src/test/ramps.test.ts @@ -1,7 +1,6 @@ import { describe, test, assert } from 'vitest'; import { - NEUTRAL_HUE, PALETTE_CHROMA_CAPS, PALETTE_CHROMA_MULTIPLIERS, PALETTE_HUES, @@ -11,7 +10,6 @@ import { TEXT_LIGHTNESS_KNOBS, compute_palette_chroma_caps, palette_stop_oklch, - ramp_chroma, ramp_lightness, shade_stop_oklch, text_stop_oklch @@ -95,7 +93,7 @@ describe('monotonicity', () => { }); describe('chroma multipliers', () => { - test('defaults are in (0, 1] and palette_stop_oklch applies them', () => { + test('defaults are in (0, 1]', () => { for (const letter of palette_variants) { const multiplier = PALETTE_CHROMA_MULTIPLIERS[letter]; assert.isAbove(multiplier, 0, `${letter} multiplier must be positive`); @@ -103,10 +101,6 @@ describe('chroma multipliers', () => { } // the brown slot ships muted - brown is low-chroma orange assert.isBelow(PALETTE_CHROMA_MULTIPLIERS.f, 1); - for (const scheme of color_scheme_variants) { - const [, chroma] = palette_stop_oklch('f', '50', scheme); - assert.approximately(chroma, ramp_chroma(scheme, '50') * PALETTE_CHROMA_MULTIPLIERS.f, 1e-12); - } }); }); @@ -216,12 +210,11 @@ describe('contrast gates', () => { }); describe('neutral', () => { - test('shade and text scales stay near the neutral hue with subtle chroma', () => { + test('the shade scale keeps its chroma subtle', () => { for (const scheme of color_scheme_variants) { for (const stop of RAMP_STOPS) { - const [, chroma, hue] = shade_stop_oklch(stop, scheme); + const [, chroma] = shade_stop_oklch(stop, scheme); assert(chroma <= 0.03, `shade_${stop} ${scheme} chroma ${chroma} too strong`); - assert(hue === NEUTRAL_HUE); } } }); diff --git a/src/test/theme_editor_state.test.ts b/src/test/theme_editor_state.test.ts index b73ca45a0..6325766fe 100644 --- a/src/test/theme_editor_state.test.ts +++ b/src/test/theme_editor_state.test.ts @@ -22,8 +22,7 @@ import { default_variables } from '$lib/variables.ts'; const adaptive_default = default_variables.find((v) => v.name === 'shade_lightness_00')!; const single_slot_default = default_variables.find((v) => v.name === 'chroma_scale')!; -const create_editor = (): ThemeEditorState => - new ThemeEditorState([base_theme, necromancer_theme]); +const create_editor = (): ThemeEditorState => new ThemeEditorState([base_theme, necromancer_theme]); describe('set_value slot semantics', () => { test('a scheme-adaptive variable edits the viewed scheme, preserving the other slot', () => { @@ -120,10 +119,7 @@ describe('display_value', () => { test('a stance mirrors untouched scheme-adaptive defaults into both schemes', () => { const editor = create_editor(); editor.set_scheme('dark'); - assert.strictEqual( - editor.display_value(adaptive_default.name, 'light'), - adaptive_default.dark - ); + assert.strictEqual(editor.display_value(adaptive_default.name, 'light'), adaptive_default.dark); assert.strictEqual(editor.display_value(adaptive_default.name, 'dark'), adaptive_default.dark); }); }); From bb8757a4e87656e821530dc7e98048df2289d57a Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Wed, 29 Jul 2026 14:02:28 -0400 Subject: [PATCH 33/33] wip --- .changeset/dev-initial-load-prescan.md | 4 +- .changeset/interaction-and-surfaces.md | 31 +++++---- .changeset/oklch-color-system.md | 23 +++++-- .changeset/theme-authoring.md | 4 ++ CLAUDE.md | 26 ++++--- src/lib/css_class_definitions.ts | 27 ++++---- src/lib/knobs.ts | 33 ++++----- src/lib/style.css | 19 +++-- src/lib/style_rule_parser.ts | 4 ++ src/lib/theme.css | 5 ++ src/lib/variables.ts | 29 ++++++++ src/lib/vite_plugin_fuz_css.ts | 24 ++++--- src/routes/ThemeEditor.svelte | 69 ++++++++++++++++++- src/routes/docs/classes/+page.svelte | 34 +++++---- src/routes/docs/colors/+page.svelte | 2 +- src/routes/docs/shading/+page.svelte | 50 ++++++++------ src/routes/docs/shadows/+page.svelte | 2 +- src/routes/docs/themes/+page.svelte | 3 +- src/routes/docs/typography/+page.svelte | 2 +- src/routes/theme_editor_state.svelte.ts | 16 +++++ src/test/class_variable_index.test.ts | 4 +- src/test/css_literal.test.ts | 2 +- src/test/css_ruleset_parser.modifiers.test.ts | 8 +-- src/test/css_ruleset_parser.parse.test.ts | 6 +- src/test/fixtures/css_classes_fixture.json | 2 +- src/test/styles.test.ts | 2 +- src/test/theme_editor_state.test.ts | 32 +++++++++ src/test/vite_plugin_fuz_css.dev.test.ts | 53 ++++++++++++++ vite.config.ts | 7 +- 29 files changed, 379 insertions(+), 144 deletions(-) diff --git a/.changeset/dev-initial-load-prescan.md b/.changeset/dev-initial-load-prescan.md index 45bd69c24..9a2815a1b 100644 --- a/.changeset/dev-initial-load-prescan.md +++ b/.changeset/dev-initial-load-prescan.md @@ -8,4 +8,6 @@ The Vite plugin now pre-scans project sources at dev-server startup (new `prescan` option: `true` scans `src` under the Vite root, `false` disables, or an array of directories) and resyncs clients whose HMR socket connects after a missed CSS update. Previously the first cold-start page load could -render with incomplete utility classes until a manual refresh. +render with incomplete utility classes until a manual refresh. Files are +isolated during the scan: one file that fails to read or extract logs and +is skipped rather than aborting the rest of the scan. diff --git a/.changeset/interaction-and-surfaces.md b/.changeset/interaction-and-surfaces.md index ee635950d..c017cf192 100644 --- a/.changeset/interaction-and-surfaces.md +++ b/.changeset/interaction-and-surfaces.md @@ -2,16 +2,16 @@ '@fuzdev/fuz_css': minor --- -feat: rework interaction states and micro-surfaces - focus ring gap, hover previews focus, themable surface hooks, contrast-safe selected buttons +feat: rework interaction states and micro-surfaces - focus ring gap, hover previews focus, themable surface variables, contrast-safe selected buttons Focus and hover now read as one escalating highlight: - Focusable elements (links, buttons, inputs, contenteditable) draw their - outline with `outline-offset: var(--outline_offset, 1px)`. The ring and - the border share a color by design (the element color, defaulting to the - accent), so without a gap they merged into one thick band; the offset - makes them read as two shapes while keeping the ring's full contrast - against the page. + outline with `outline-offset: var(--outline_offset)` (default `1px`). The + ring and the border share a color by design (the element color, defaulting + to the accent), so without a gap they merged into one thick band; the + offset makes them read as two shapes while keeping the ring's full + contrast against the page. - Hovering an input, textarea, or select colors the border with `var(--outline_color)` — the element color when one is set (an `outline_*` class, or `.palette_*` on buttons), the accent intent @@ -19,16 +19,13 @@ Focus and hover now read as one escalating highlight: alpha. Focus keeps setting the border to the same color and adds the outline. Disabled inputs no longer react to hover. -New micro-surface defaults in `style.css`, each themable through a `var()` -fallback hook (all in the knob catalog, so theme validation accepts them): +New micro-surface defaults in `style.css`, each themable through an +ordinary declared variable (all in the knob catalog): -- `scrollbar-color` on `:root` — thumb defaults to `var(--shade_40)` on a - transparent track; hooks `--scrollbar_thumb_color` / - `--scrollbar_track_color` -- `caret-color` on text inputs — defaults to `var(--accent_50)`; hook - `--caret_color` -- `dialog::backdrop` — defaults to `var(--darken_60)`; hook - `--backdrop_color` +- `scrollbar-color` on `:root` — `--scrollbar_thumb_color` (default + `var(--shade_40)`) on `--scrollbar_track_color` (default transparent) +- `caret-color` on text inputs — `--caret_color`, default `var(--accent_50)` +- `dialog::backdrop` — `--backdrop_color`, default `var(--darken_60)` - `--outline_offset` — the border-to-focus-ring gap above - `@media (prefers-contrast: more)` maps the OS preference onto the curve knobs, mirroring the `'high contrast'` theme. It ships in the @@ -37,6 +34,10 @@ fallback hook (all in the knob catalog, so theme validation accepts them): `fuz.theme`, so it applies regardless of stylesheet order and theme overrides beat it +(`--heading_font_weight` is the lone `var()`-fallback hook — its per-tier +fallbacks mean no single declared default exists, and setting it flattens +the heading weight ladder deliberately.) + Selected-button text stays readable under contrast-bent themes and colored fills: diff --git a/.changeset/oklch-color-system.md b/.changeset/oklch-color-system.md index 44a24934a..ca4349cec 100644 --- a/.changeset/oklch-color-system.md +++ b/.changeset/oklch-color-system.md @@ -10,14 +10,25 @@ from the old HSL palette. Breaking changes: -- **`color_` renamed to `palette_`**: `--color_a_50` → `--palette_a_50`, - `.color_a_50` → `.palette_a_50`, `.color_a`-`.color_j` → - `.palette_a`-`.palette_j`. In compound families the letter alone implies - the palette: `border_color_X_NN` → `border_X_NN`, `outline_color_X_NN` → +- **`--color_*` variables renamed to `--palette_*`**: `--color_a_50` → + `--palette_a_50` for all 10 letters × 13 stops, with the TS mirrors + `ColorVariant`/`color_variants` → `PaletteVariant`/`palette_variants`. + Classes are property-first and the letter alone implies the palette: the + text-color stop classes keep their names (`.color_a_50`, now applying + `--palette_a_50`), while the compound families shorten - + `border_color_X_NN` → `border_X_NN`, `outline_color_X_NN` → `outline_X_NN`, `shadow_color_X_NN` → `shadow_X_NN` (`bg_X_NN` and the letterless families — `border_color_NN`, `outline_color_NN`, - `shadow_color_umbra` — keep their names). In TS: - `ColorVariant`/`color_variants` → `PaletteVariant`/`palette_variants`. + `shadow_color_umbra` — keep their names). The bare component conventions + `.color_a`-`.color_j` → `.palette_a`-`.palette_j` (they recolor buttons + and chips as a unit, not one property). +- **`.fg_NN`/`.bg_NN` token classes removed**: the adaptive alpha overlays + stay as variables (`--fg_*`/`--bg_*`) but no longer generate bare token + classes — `bg_` is the opaque background class prefix (`.bg_a_50`, + `.bg_positive_50`), and a translucent `.bg_50` beside those was the one + collision in the naming family. Use literals instead: + `background-color:var(--fg_10)`. The `.darken_NN`/`.lighten_NN` classes + are unchanged. - **`--hue_a`…`--hue_j` are now OKLCH hue angles** (blue is `250`, not HSL `210`). Consumer CSS doing `hsl(var(--hue_x) …)` breaks — use `oklch(<l> <c> var(--hue_x))` or the palette/intent stops. diff --git a/.changeset/theme-authoring.md b/.changeset/theme-authoring.md index cf6941a59..15ef1b66c 100644 --- a/.changeset/theme-authoring.md +++ b/.changeset/theme-authoring.md @@ -73,6 +73,10 @@ compiled-cap overrides): theme's own hues and lightness ramp, emits `palette_chroma_NN` overrides where the baked caps no longer fit, and re-checks the result +The shipped themes pass these in CI - including every theme × +contrast-modifier composition - and the docs page's inline editor runs the +same lint and gates live on every edit. + **Build-time `theme` option.** The Vite plugin and Gro generator take a `theme` baked into the generated CSS: diff --git a/CLAUDE.md b/CLAUDE.md index 54e0868aa..5e77a2d84 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -165,12 +165,16 @@ See `GenFuzCssOptions` and `VitePluginFuzCssOptions` types for configuration. ### Three class types -- **Token classes** - Map to style variables: `p_md`, `palette_a_50`, - `positive_50`, `gap_lg`. A bare scale class applies its family's dominant - use (`palette_a_50`/`positive_50`/`text_70` set text color, `shade_50` sets - background) with `bg_` twins (`bg_a_50`, `bg_positive_50`); in compound - families a letter alone implies the palette (`border_a_50` vs the - `border_color_50` alpha ramp) +- **Token classes** - Map to style variables: `p_md`, `color_a_50`, + `positive_50`, `gap_lg`. Palette-letter classes are property-first and the + letter alone implies the palette: `color_a_50`, `bg_a_50`, `border_a_50`, + `outline_a_50`, and `shadow_a_50` apply the `--palette_a_NN` stops to their + named property (`border_color_50` is the letterless alpha ramp). A bare + intent or neutral scale class applies its family's dominant use + (`positive_50`/`text_70` set text color, `shade_50` sets background) with + `bg_` twins (`bg_positive_50`). The adaptive alpha overlays + (`--fg_*`/`--bg_*`) are variables only, reached via literals + (`background-color:var(--fg_10)`) - `bg_` classes are always opaque - **Composite classes** - Multi-property shortcuts: `box`, `column`, `row`, `ellipsis`, `pixelated`, `circular`, `selectable`, `clickable`, `pane`, `panel`, the size composites `xs`/`sm`/`md`/`lg`/`xl` (uniform step offsets @@ -241,12 +245,14 @@ See [variables.ts](src/lib/variables.ts) for definitions, `--font_weight`, `--heading_font_weight` (a hook with per-tier fallbacks — setting it flattens the heading ladder), `--heading_font_family`, and the `--background_image` decoration hook on `:root` -- Micro-surface hooks consumed by `style.css` via `var()` fallbacks: - `--caret_color` (defaults to the accent), `--scrollbar_thumb_color`/ +- Micro-surface variables declared in `default_variables`: `--caret_color` + (defaults to the accent), `--scrollbar_thumb_color`/ `--scrollbar_track_color` (default into the shade scale), `--backdrop_color` (the `dialog::backdrop` dim), `--outline_offset` (the border-to-focus-ring - gap, default 1px); `prefers-contrast: more` maps onto the curve - knobs mirroring the high-contrast theme, theme-overridable + gap, default 1px); `--heading_font_weight` is the lone `var()`-fallback + hook (per-tier fallbacks, so no single default exists); `prefers-contrast: + more` maps onto the curve knobs mirroring the high-contrast theme, + theme-overridable - [knobs.ts](src/lib/knobs.ts) is the typed knob catalog (`kind`, `axis`, `leverage`, `tier`, ranges) powering the themes docs page's inline editor - `bg_*`/`fg_*` - color-scheme-aware (swap in dark mode, use alpha for stacking) diff --git a/src/lib/css_class_definitions.ts b/src/lib/css_class_definitions.ts index 48742c016..f1ef2b2ae 100644 --- a/src/lib/css_class_definitions.ts +++ b/src/lib/css_class_definitions.ts @@ -96,6 +96,9 @@ export const css_class_definitions: Record<string, CssClassDefinition | undefine 'shade' ), // Hue classes + // TODO nothing in the shipped CSS consumes `--hue` yet (see the + // `--outline_color` TODO in style.css) - these are a consumer hook for + // now; wire something to the variable or drop them ...generate_classes( (letter: string) => ({ name: `hue_${letter}`, @@ -103,10 +106,12 @@ export const css_class_definitions: Record<string, CssClassDefinition | undefine }), palette_variants ), - // Palette intensity classes (text color) + // Palette intensity classes (text color); property-first like the other + // applications of the palette variables (`bg_`, `border_`, `outline_`, + // `shadow_`) - the letter alone implies the palette ...generate_classes( (letter: string, intensity: string) => ({ - name: `palette_${letter}_${intensity}`, + name: `color_${letter}_${intensity}`, css: `color: var(--palette_${letter}_${intensity}); --text_color: var(--palette_${letter}_${ intensity });` @@ -154,19 +159,11 @@ export const css_class_definitions: Record<string, CssClassDefinition | undefine (v) => `var(--lighten_${v})`, 'lighten' ), - // Adaptive alpha overlays (fg = toward foreground, bg = toward background) - ...generate_property_classes( - 'background-color', - darken_lighten_variants, - (v) => `var(--fg_${v})`, - 'fg' - ), - ...generate_property_classes( - 'background-color', - darken_lighten_variants, - (v) => `var(--bg_${v})`, - 'bg' - ), + // The adaptive alpha overlay variables (`--fg_*` toward foreground, + // `--bg_*` toward background) have no bare token classes: `bg_` is the + // opaque background prefix (`bg_a_50`, `bg_positive_50`), and a bare + // `bg_50` alpha wash beside those was the one collision in the family. + // Reach the overlays with literals - `background-color:var(--fg_10)`. /* borders diff --git a/src/lib/knobs.ts b/src/lib/knobs.ts index 33da5c6ec..b9bc93f47 100644 --- a/src/lib/knobs.ts +++ b/src/lib/knobs.ts @@ -12,9 +12,9 @@ * * The catalog deliberately covers only the knob tier, not all ~490 variables: * derived ramp stops, color stops, and most site hooks are the escape-hatch - * tier and stay out. A few catalog entries are hooks (`hook: true`) consumed - * by `style.css` with fallbacks rather than declared variables - they exist - * here so themes can set them without tripping the no-invented-variables + * tier and stay out. A catalog entry may be a hook (`hook: true`) consumed by + * `style.css` with per-site fallbacks rather than a declared variable - it + * exists here so themes can set it without tripping the no-invented-variables * check. * * @module @@ -73,8 +73,9 @@ export interface ThemeKnob { values?: Array<string>; /** * True for knobs consumed by `style.css` via `var()` fallbacks instead of - * being declared in `default_variables` (e.g. `heading_font_weight`, whose - * per-tier fallbacks flatten when set). + * being declared in `default_variables`. Only for knobs whose fallbacks + * differ per site, so no single declared default exists - + * `heading_font_weight`, whose per-tier fallbacks flatten when set. */ hook?: boolean; /** @@ -211,30 +212,27 @@ export const theme_knobs: Array<ThemeKnob> = [ range: [0.2, 4], step: 0.01 }, - // color - micro-surface hooks consumed by style.css via var() fallbacks + // color - micro-surface variables consumed by style.css { name: 'caret_color', kind: 'color', axis: 'color', leverage: 'sm', - tier: 'semantic', - hook: true + tier: 'semantic' }, { name: 'scrollbar_thumb_color', kind: 'color', axis: 'color', leverage: 'sm', - tier: 'semantic', - hook: true + tier: 'semantic' }, { name: 'scrollbar_track_color', kind: 'color', axis: 'color', leverage: 'sm', - tier: 'semantic', - hook: true + tier: 'semantic' }, { // how muted disabled UI reads - a perceptual/color move, not motion @@ -287,14 +285,12 @@ export const theme_knobs: Array<ThemeKnob> = [ values: ['solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'none'] }, { - // the gap between an element's border and its focus/active ring, a - // style.css var() fallback hook (default 1px) + // the gap between an element's border and its focus/active ring name: 'outline_offset', kind: 'length', axis: 'shape', leverage: 'sm', - tier: 'semantic', - hook: true + tier: 'semantic' }, { name: 'border_width', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, { name: 'border_width_1', kind: 'length', axis: 'shape', leverage: 'sm', tier: 'semantic' }, @@ -366,14 +362,13 @@ export const theme_knobs: Array<ThemeKnob> = [ }, { name: 'shadow_color_glow', kind: 'color', axis: 'depth', leverage: 'md', tier: 'semantic' }, { name: 'shadow_color_shroud', kind: 'color', axis: 'depth', leverage: 'md', tier: 'semantic' }, - // the dialog/fullscreen ::backdrop dim, a style.css var() fallback hook + // the dialog/fullscreen ::backdrop dim { name: 'backdrop_color', kind: 'color', axis: 'depth', leverage: 'sm', - tier: 'semantic', - hook: true + tier: 'semantic' }, { name: 'button_shadow', kind: 'shadow', axis: 'depth', leverage: 'md', tier: 'semantic' }, { name: 'button_shadow_hover', kind: 'shadow', axis: 'depth', leverage: 'md', tier: 'semantic' }, diff --git a/src/lib/style.css b/src/lib/style.css index 76675d311..049df2eb9 100644 --- a/src/lib/style.css +++ b/src/lib/style.css @@ -83,8 +83,7 @@ Notes: background-image: var(--background_image); color: var(--text_color); accent-color: var(--accent_50); - scrollbar-color: var(--scrollbar_thumb_color, var(--shade_40)) - var(--scrollbar_track_color, transparent); + scrollbar-color: var(--scrollbar_thumb_color) var(--scrollbar_track_color); line-height: var(--line_height_md); font-size: 62.5%; /* see `body.font-size`, using `rem` font size units with a base 16px */ /* @source https://kilianvalkhof.com/2022/css-html/your-css-reset-needs-text-size-adjust-probably/ */ @@ -123,8 +122,8 @@ Notes: } :where(dialog:not(.unstyled))::backdrop { - /* same default dim as fuz_ui's Dialog, themable through one hook */ - background-color: var(--backdrop_color, var(--darken_60)); + /* same default dim as fuz_ui's Dialog, themable through one variable */ + background-color: var(--backdrop_color); } /* @@ -150,8 +149,8 @@ Respects `hidden="until-found"` for find-in-page support. border-radius: var(--border_radius, var(--border_radius_xs3)); outline: var(--outline_width) var(--outline_style) var(--outline_color); /* gap between border and ring so the same-color pair reads as two - shapes instead of one thick band; --outline_offset is a hook */ - outline-offset: var(--outline_offset, 1px); + shapes instead of one thick band */ + outline-offset: var(--outline_offset); } /* TODO should these be customizable with utility classes? */ :where(a:not(.unstyled):hover) { @@ -345,7 +344,7 @@ currently can break based on DOM structure - https://developer.mozilla.org/en-US line-height: normal; display: block; color: var(--text_color); - caret-color: var(--caret_color, var(--accent_50)); + caret-color: var(--caret_color); background-color: var(--input_fill); border-color: var(--border_color); border-style: var(--border_style); @@ -353,7 +352,7 @@ currently can break based on DOM structure - https://developer.mozilla.org/en-US border-radius: var(--border_radius, var(--border_radius_sm)); /* TODO --outline_color should default to --hue when available, but can't be set above */ outline: var(--outline_width) var(--outline_style) var(--outline_color); - outline-offset: var(--outline_offset, 1px); + outline-offset: var(--outline_offset); padding: var(--input_padding_y) var(--input_padding_x); width: 100%; min-width: var(--input_width_min); @@ -519,7 +518,7 @@ see https://caniuse.com/css-has :where([contenteditable]:not(.unstyled):focus-visible) { --outline_width: var(--outline_width_focus); outline: var(--outline_width) var(--outline_style) var(--outline_color); - outline-offset: var(--outline_offset, 1px); + outline-offset: var(--outline_offset); } :where(:is(input, textarea, select, [contenteditable]):not(.unstyled):active) { --outline_width: var(--outline_width_active); @@ -591,7 +590,7 @@ TODO think through these patterns to get somewhere consistent border-color: var(--button_border_color); border-radius: var(--border_radius, var(--border_radius_sm)); outline: var(--outline_width) var(--outline_style) var(--outline_color); - outline-offset: var(--outline_offset, 1px); + outline-offset: var(--outline_offset); } :where(:is(button, input, textarea, select):not(.unstyled):focus) { diff --git a/src/lib/style_rule_parser.ts b/src/lib/style_rule_parser.ts index 0db474d17..2a74f6579 100644 --- a/src/lib/style_rule_parser.ts +++ b/src/lib/style_rule_parser.ts @@ -221,6 +221,10 @@ const extract_style_rule = ( * variables, and reports whether any nested rule is itself core (e.g. a * `:root` block inside a media query). */ +// TODO the top-level `untargetable` fallback in `extract_style_rule` (no +// element/class hooks → force core) isn't applied here, so a conditional +// group containing only untargetable rules (e.g. `@media { ::selection {} }`) +// would be silently unreachable; no shipped rule hits this today const extract_nested_rules = ( block: AST.CSS.Block, css: string, diff --git a/src/lib/theme.css b/src/lib/theme.css index c75ad92ab..36677fea2 100644 --- a/src/lib/theme.css +++ b/src/lib/theme.css @@ -1554,6 +1554,7 @@ --outline_width_active: var(--border_width_1); --outline_style: solid; --outline_color: var(--accent_50); + --outline_offset: 1px; /* the gap between an element border and its focus ring */ --radius_scale: 1; /* 0 is sharp, below 1 is squarer, above 1 is rounder */ --border_radius_xs3: calc(0.3rem * var(--radius_scale)); --border_radius_xs2: calc(0.5rem * var(--radius_scale)); @@ -1595,6 +1596,10 @@ --input_width_min: 100px; --input_height: var(--space_xl5); --input_height_compact: var(--space_xl4); + --caret_color: var(--accent_50); + --scrollbar_thumb_color: var(--shade_40); + --scrollbar_track_color: transparent; + --backdrop_color: var(--darken_60); /* the dim behind an open dialog */ --shadow_xs: 0 0 3px 0px; --shadow_top_xs: 0 -1px 3px 0px; --shadow_bottom_xs: 0 1px 3px 0px; diff --git a/src/lib/variables.ts b/src/lib/variables.ts index daf7f2505..7b4c74e5e 100644 --- a/src/lib/variables.ts +++ b/src/lib/variables.ts @@ -2023,6 +2023,11 @@ export const outline_color: StyleVariable = { name: 'outline_color', light: 'var(--accent_50)' }; +export const outline_offset: StyleVariable = { + name: 'outline_offset', + light: '1px', + summary: 'the gap between an element border and its focus ring' +}; /* border radii - the tokens multiply a per-tier base by the radius scale, so "sharp"/"soft"/"pill" is one knob move while per-element tiers survive; @@ -2091,6 +2096,25 @@ export const input_height_compact: StyleVariable = { light: 'var(--space_xl4)' }; +/* micro-surfaces */ +export const caret_color: StyleVariable = { + name: 'caret_color', + light: 'var(--accent_50)' +}; +export const scrollbar_thumb_color: StyleVariable = { + name: 'scrollbar_thumb_color', + light: 'var(--shade_40)' +}; +export const scrollbar_track_color: StyleVariable = { + name: 'scrollbar_track_color', + light: 'transparent' +}; +export const backdrop_color: StyleVariable = { + name: 'backdrop_color', + light: 'var(--darken_60)', + summary: 'the dim behind an open dialog' +}; + /* shadows @@ -2844,6 +2868,7 @@ export const default_variables: Array<StyleVariable> = [ outline_width_active, outline_style, outline_color, + outline_offset, radius_scale, border_radius_xs3, border_radius_xs2, @@ -2861,6 +2886,10 @@ export const default_variables: Array<StyleVariable> = [ input_width_min, input_height, input_height_compact, + caret_color, + scrollbar_thumb_color, + scrollbar_track_color, + backdrop_color, shadow_xs, shadow_top_xs, shadow_bottom_xs, diff --git a/src/lib/vite_plugin_fuz_css.ts b/src/lib/vite_plugin_fuz_css.ts index 9a6ed1836..b08bc602d 100644 --- a/src/lib/vite_plugin_fuz_css.ts +++ b/src/lib/vite_plugin_fuz_css.ts @@ -573,14 +573,22 @@ export const vite_plugin_fuz_css = (options: VitePluginFuzCssOptions = {}): Plug prescan_active = true; try { await each_concurrent(file_ids, PRESCAN_CONCURRENCY, async (id) => { - // Reads disk bytes while transform later receives Vite's input for - // the same id; with enforce: 'pre' those are identical, so the - // content-hash short-circuit makes the second ingest a no-op. A - // loader that rewrites content before 'pre' plugins would extract - // twice, with the transform result winning - harmless, just noted. - const r = await deps.read_text({ path: id }); - if (!r.ok) return; // deleted mid-scan or unreadable; transform covers it if it reappears - await ingest_file(id, r.value); + // Per-file isolation: `each_concurrent` is fail-fast, so an uncaught + // throw (a custom deps or acorn plugin, not parse errors - those + // become diagnostics) would silently skip every remaining file; + // transform() ingests independently and this matches that. + try { + // Reads disk bytes while transform later receives Vite's input for + // the same id; with enforce: 'pre' those are identical, so the + // content-hash short-circuit makes the second ingest a no-op. A + // loader that rewrites content before 'pre' plugins would extract + // twice, with the transform result winning - harmless, just noted. + const r = await deps.read_text({ path: id }); + if (!r.ok) return; // deleted mid-scan or unreadable; transform covers it if it reappears + await ingest_file(id, r.value); + } catch (error) { + log_error(`[fuz_css] pre-scan failed to extract ${id}: ${error}`); + } }); } finally { prescan_active = false; diff --git a/src/routes/ThemeEditor.svelte b/src/routes/ThemeEditor.svelte index 26d15bba1..bed7a43a8 100644 --- a/src/routes/ThemeEditor.svelte +++ b/src/routes/ThemeEditor.svelte @@ -8,6 +8,7 @@ import { render_theme_style, type Theme, type ThemeScheme } from '$lib/theme.ts'; import { theme_knobs, knob_axes, type KnobAxis, type ThemeKnob } from '$lib/knobs.ts'; + import type { ThemeGateEntry } from '$lib/theme_check.ts'; import { PALETTE_HUES } from '$lib/ramps.ts'; import { palette_variants, @@ -51,6 +52,21 @@ : 'light'; }); + const failing_gates: Array<ThemeGateEntry> = $derived( + editor.check_report.entries.filter((e) => !e.pass) + ); + + const format_gate_value = (entry: ThemeGateEntry): string => { + switch (entry.gate) { + case 'contrast': + return `${entry.value.toFixed(2)}:1 (needs ${entry.threshold}:1)`; + case 'gamut': + return `${entry.value.toFixed(4)} past the sRGB gamut`; + case 'monotonicity': + return `min ramp step ${entry.value.toFixed(4)} (needs > ${entry.threshold})`; + } + }; + const semantic_knobs = (axis: KnobAxis): Array<ThemeKnob> => theme_knobs.filter((k) => k.axis === axis && k.tier === 'semantic'); const palette_knobs = theme_knobs.filter((k) => k.tier === 'palette'); @@ -63,7 +79,7 @@ // visual main flow; each axis labels what its disclosure actually holds const sm_details: Partial<Record<KnobAxis, { title: string; hint: string }>> = { color: { - title: 'chroma character & surface hooks', + title: 'chroma character & surface colors', hint: 'per-slot multipliers and micro-surface colors' }, shape: { @@ -71,7 +87,7 @@ hint: 'escape hatches - pin individual tokens' }, density: { title: 'space tokens', hint: 'escape hatches - pin individual tokens' }, - depth: { title: 'backdrop hook', hint: 'the dialog and fullscreen backdrop dim' }, + depth: { title: 'backdrop', hint: 'the dialog and fullscreen backdrop dim' }, typography: { title: 'line height tokens', hint: 'escape hatches - pin individual tokens' }, motion: { title: 'duration tokens', hint: 'escape hatches - pin individual tokens' } }; @@ -265,6 +281,55 @@ {/if} {/each} + <section> + <h3>Gates</h3> + <p> + <code>validate_theme</code> and <code>check_theme</code> run live against the draft - the same + lint and gamut/monotonicity/contrast gates the shipped themes pass in CI. + </p> + {#if editor.issues.length === 0 && + failing_gates.length === 0 && + editor.check_report.unchecked.length === 0 + } + <p class="positive_50"> + all gates pass <small>({editor.check_report.entries.length} checks)</small> + </p> + {:else} + {#if editor.issues.length} + <ul class="unstyled"> + {#each editor.issues as issue, i (i)} + <li class={issue.level === 'error' ? 'negative_50' : 'caution_50'}> + {issue.level}: {issue.message} + </li> + {/each} + </ul> + {/if} + {#if failing_gates.length} + <ul class="unstyled"> + {#each failing_gates as entry, i (i)} + <li class="negative_50"> + {entry.gate} · {entry.scheme} · {entry.subject}: {format_gate_value(entry)} + </li> + {/each} + </ul> + {/if} + {#if editor.check_report.unchecked.length} + <details> + <summary> + {editor.check_report.unchecked.length} unchecked <small> + (values the numeric gates can't resolve) + </small> + </summary> + <ul class="unstyled"> + {#each editor.check_report.unchecked as u (u.variable + u.value)} + <li><code>{u.variable}</code>: {u.value} <small>({u.reason})</small></li> + {/each} + </ul> + </details> + {/if} + {/if} + </section> + <section> <h3>Output</h3> <p> diff --git a/src/routes/docs/classes/+page.svelte b/src/routes/docs/classes/+page.svelte index 1ba75c091..5223fc780 100644 --- a/src/routes/docs/classes/+page.svelte +++ b/src/routes/docs/classes/+page.svelte @@ -380,7 +380,7 @@ const turtle_class_name = 'turtle';`} content={`// @fuz-classes opacity:50% opacity:75% opacity:100% const opacity_classes = [50, 75, 100].map((n) => \`opacity:\${n}%\`); -/* @fuz-classes palette_a_50 palette_b_50 palette_c_50 */ +/* @fuz-classes color_a_50 color_b_50 color_c_50 */ const color = get_dynamic_color();`} /> <p> @@ -411,7 +411,7 @@ const color = get_dynamic_color();`} Classes annotated with <code>@fuz-classes</code> and configured with <code>additional_classes</code> produce errors if they can't be resolved. This helps catch typos like - <code>@fuz-classes palette_a_55</code> instead of <code>palette_a_50</code>. + <code>@fuz-classes color_a_55</code> instead of <code>color_a_50</code>. </aside> <p> Alternatively, use the @@ -621,8 +621,8 @@ vite_plugin_fuz_css({ <a href="#Literal-classes">literal classes</a> instead. </p> - <Code content="<p class="pl_xl3 palette_g_50">some token classes</p>" /> - <p class="pl_xl3 palette_g_50">some token classes</p> + <Code content="<p class="pl_xl3 color_g_50">some token classes</p>" /> + <p class="pl_xl3 color_g_50">some token classes</p> <p> Token classes use <code>snake_case</code> because style variables are designed for optional use in JS (imported from <ModuleLink module_path="variables.ts" />, but costing nothing @@ -787,7 +787,7 @@ vite_plugin_fuz_css({ <li class="mb_md"> <span class="code_chips"> <code> - .palette_{@render variant_range(palette_variants)}_{@render variant_range( + .color_{@render variant_range(palette_variants)}_{@render variant_range( intensity_variants )} </code> @@ -818,16 +818,20 @@ vite_plugin_fuz_css({ </li> </ul> <aside> - A bare scale class applies its family's dominant use: <code>.palette_a_50</code>, - <code>.positive_50</code>, and <code>.text_70</code> set the text color while - <code>.shade_50</code> sets the background. The <code>bg_</code> prefix selects the - background twin (<code>.bg_a_50</code>, <code>.bg_positive_50</code>). In compound families - a letter alone implies the palette: <code>.border_a_50</code> is the palette family, - <code>.border_color_50</code> - the alpha ramp. + Palette-letter classes are property-first, and the letter alone implies the palette: + <code>.color_a_50</code>, <code>.bg_a_50</code>, <code>.border_a_50</code>, and + <code>.outline_a_50</code> apply <code>--palette_a_50</code> to their named property (<code> + .border_color_50 + </code> + is the letterless alpha ramp). A bare intent or neutral scale class applies its family's + dominant use: <code>.positive_50</code> and + <code>.text_70</code> set the text color while <code>.shade_50</code> sets the background, + with the <code>bg_</code> prefix selecting the background twin (<code> + .bg_positive_50 + </code>). </aside> <aside> - Color and text classes (<code>.palette_a_50</code>, <code>.text_70</code>, etc.) also set + Color and text classes (<code>.color_a_50</code>, <code>.text_70</code>, etc.) also set <code>--text_color</code>, so nested elements like <code><code></code> that use <code>color: var(--text_color)</code> inherit the color properly. @@ -1115,7 +1119,7 @@ export const custom_composites: Record<string, CssClassDefinition> = { </p> <ul> <li> - token classes (<code>p_lg</code>, <code>palette_a_50</code>) - resolved to their + token classes (<code>p_lg</code>, <code>color_a_50</code>) - resolved to their declarations </li> <li> @@ -1623,7 +1627,7 @@ export const gen = gen_fuz_css({ <li> <strong>expressions:</strong> logical (<code>&&</code>, <code>||</code>, <code>??</code>), ternaries, template literals (complete tokens only - - <code>`palette_a_50 ${base}`</code> extracts <code>palette_a_50</code>, but + <code>`color_a_50 ${base}`</code> extracts <code>color_a_50</code>, but <code>`palette_${hue}_50`</code> cannot be extracted; use <code>@fuz-classes</code> or diff --git a/src/routes/docs/colors/+page.svelte b/src/routes/docs/colors/+page.svelte index 1b872a7e9..c1d5331eb 100644 --- a/src/routes/docs/colors/+page.svelte +++ b/src/routes/docs/colors/+page.svelte @@ -143,7 +143,7 @@ Unlike the <TomeLink slug="shading">shade</TomeLink> and <TomeLink slug="typography" hash="Text-colors">text</TomeLink> scales (which are separate), palette variables can be used for both text and backgrounds via utility classes: - <code>.palette_a_50</code> sets text color, <code>.bg_a_50</code> sets background color. + <code>.color_a_50</code> sets text color, <code>.bg_a_50</code> sets background color. </p> <p> Palette stops are adaptive: they switch between light and dark ramps based on color scheme. diff --git a/src/routes/docs/shading/+page.svelte b/src/routes/docs/shading/+page.svelte index 9e87d639b..f6ce53923 100644 --- a/src/routes/docs/shading/+page.svelte +++ b/src/routes/docs/shading/+page.svelte @@ -101,11 +101,11 @@ </p> <ul> <li> - <code>fg_NN</code> (foreground direction) - darkens in light mode, lightens in dark mode; + <code>--fg_NN</code> (foreground direction) - darkens in light mode, lightens in dark mode; use for elevated surfaces like panels, cards, and hover states </li> <li> - <code>bg_NN</code> (background direction) - lightens in light mode, darkens in dark mode; + <code>--bg_NN</code> (background direction) - lightens in light mode, darkens in dark mode; use for surfaces that blend toward the background </li> </ul> @@ -113,6 +113,13 @@ In light mode, <code>fg</code> is the same as <code>darken</code> and <code>bg</code> is the same as <code>lighten</code>. In dark mode, they're swapped. </p> + <p> + The overlays are variables, not token classes - <code>bg_</code> is the opaque background + class prefix (<code>.bg_a_50</code>, <code>.bg_positive_50</code>), so reach the overlays with + a literal class (<code>background-color:var(--fg_10)</code>) or a + <code><style></code> + block. + </p> <TomeSection> <TomeSectionHeader text="fg (toward foreground)" tag="h4" /> <p>Adaptive alpha overlays that add contrast with the surface.</p> @@ -120,7 +127,7 @@ {#each alpha_variants as v (v)} {@const name = 'fg_' + v} <div> - <div class="overlay_color fg_{v}"></div> + <div class="overlay_color" style:background-color="var(--{name})"></div> <small><StyleVariableButton {name} /></small> </div> {/each} @@ -133,7 +140,7 @@ {#each alpha_variants as v (v)} {@const name = 'bg_' + v} <div> - <div class="overlay_color bg_{v}"></div> + <div class="overlay_color" style:background-color="var(--{name})"></div> <small><StyleVariableButton {name} /></small> </div> {/each} @@ -146,10 +153,10 @@ adds more contrast: </p> <Code - content={`<div class="fg_10 p_sm"> - <div class="fg_10 p_sm"> - <div class="fg_10 p_sm"> - <div class="fg_10 p_sm"> + content={`<div class="background-color:var(--fg_10) p_sm"> + <div class="background-color:var(--fg_10) p_sm"> + <div class="background-color:var(--fg_10) p_sm"> + <div class="background-color:var(--fg_10) p_sm"> ... </div> </div> @@ -157,13 +164,13 @@ </div>`} /> <div class="stacking_demo"> - <div class="stacking_layer fg_10"> - <span>fg_10</span> - <div class="stacking_layer fg_10"> - <span>nested fg_10</span> - <div class="stacking_layer fg_10"> + <div class="stacking_layer"> + <span>var(--fg_10)</span> + <div class="stacking_layer"> + <span>nested var(--fg_10)</span> + <div class="stacking_layer"> <span>triple nested</span> - <div class="stacking_layer fg_10"> + <div class="stacking_layer"> <span>quadruple nested</span> </div> </div> @@ -173,7 +180,7 @@ <p class="mt_md"> This is useful for nested UI elements like cards within cards, or hover states inside elevated containers. Composites like <code>.panel</code>, <code>.chip</code>, and - <code>.menuitem</code> use <code>fg_10</code> for this stacking behavior. + <code>.menuitem</code> use <code>var(--fg_10)</code> for this stacking behavior. </p> </TomeSection> </TomeSection> @@ -194,7 +201,7 @@ {#each alpha_variants as v (v)} {@const name = 'darken_' + v} <div> - <div class="overlay_color darken_{v}"></div> + <div class="overlay_color" style:background-color="var(--{name})"></div> <small><StyleVariableButton {name} /></small> </div> {/each} @@ -207,7 +214,7 @@ {#each alpha_variants as v (v)} {@const name = 'lighten_' + v} <div> - <div class="overlay_color lighten_{v}"></div> + <div class="overlay_color" style:background-color="var(--{name})"></div> <small><StyleVariableButton {name} /></small> </div> {/each} @@ -227,8 +234,8 @@ <TomeSection> <TomeSectionHeader text="When to use which" /> <p> - <strong>Use <code>fg_NN</code></strong> when you need stacking behavior or are building nested - UI: + <strong>Use <code>--fg_NN</code></strong> when you need stacking behavior or are building + nested UI: </p> <Code lang="css" @@ -258,8 +265,8 @@ background-color: var(--shade_min);`} /> <p class="mt_lg"> The composites (<code>.panel</code>, <code>.chip</code>, <code>.menuitem</code>) use - <code>fg_NN</code> for stacking. The page background uses <code>shade_00</code> as the opaque - base. + <code>--fg_NN</code> for stacking. The page background uses <code>shade_00</code> as the + opaque base. </p> </TomeSection> <TomeSection> @@ -309,6 +316,7 @@ background-color: var(--shade_min);`} padding: var(--space_md); border-radius: var(--border_radius_xs); margin-top: var(--space_sm); + background-color: var(--fg_10); } .stacking_layer span { font-family: var(--font_family_mono); diff --git a/src/routes/docs/shadows/+page.svelte b/src/routes/docs/shadows/+page.svelte index b24cdd4e5..6885339ae 100644 --- a/src/routes/docs/shadows/+page.svelte +++ b/src/routes/docs/shadows/+page.svelte @@ -59,7 +59,7 @@ black). </p> <form><ColorSchemeInput /></form> - <div class="panel fg_30 p_md"> + <div class="panel background-color:var(--fg_30) p_md"> {@render shadow_examples('highlight')} </div> </TomeSection> diff --git a/src/routes/docs/themes/+page.svelte b/src/routes/docs/themes/+page.svelte index aeab8dcfa..c16967672 100644 --- a/src/routes/docs/themes/+page.svelte +++ b/src/routes/docs/themes/+page.svelte @@ -279,7 +279,8 @@ test('my theme clears the accessibility gates', () => { </p> <p> fuz_css gates its own themes - including every theme × contrast-modifier composition - with - these functions in its test suite. + these functions in its test suite, and the editor above runs the same lint and gates live on + every edit. </p> </TomeSection> </TomeContent> diff --git a/src/routes/docs/typography/+page.svelte b/src/routes/docs/typography/+page.svelte index ff096d3bd..5d5e03afc 100644 --- a/src/routes/docs/typography/+page.svelte +++ b/src/routes/docs/typography/+page.svelte @@ -171,7 +171,7 @@ <p> The text scale is separate from the shade scale because text and backgrounds have different contrast requirements. Use <code>text_*</code> for text colors and <code>shade_*</code> for - backgrounds. For colored text, use <code>palette_a_50</code> etc. + backgrounds. For colored text, use <code>color_a_50</code> etc. </p> <div class="panel"> {#each text_scale_variants as variant (variant)} diff --git a/src/routes/theme_editor_state.svelte.ts b/src/routes/theme_editor_state.svelte.ts index 718ccce9f..13387046f 100644 --- a/src/routes/theme_editor_state.svelte.ts +++ b/src/routes/theme_editor_state.svelte.ts @@ -5,6 +5,12 @@ import { resolve_theme_stance } from '$lib/theme_stance.ts'; import type { StyleVariable } from '$lib/variable.ts'; import { default_variables } from '$lib/variables.ts'; import { theme_knob_by_name } from '$lib/knobs.ts'; +import { + validate_theme, + check_theme, + type ThemeIssue, + type ThemeCheckReport +} from '$lib/theme_check.ts'; import type { ColorSchemeVariant } from '$lib/variable_data.ts'; // TODO upstream to fuz_ui @@ -156,6 +162,16 @@ export class ThemeEditorState { }) ); + /** Structural lint findings for the draft, from `validate_theme`. */ + readonly issues: Array<ThemeIssue> = $derived(validate_theme(this.output)); + + /** + * The gamut/monotonicity/contrast gate report for the draft, from + * `check_theme` - the same gates the shipped themes pass in CI, re-run on + * every edit. + */ + readonly check_report: ThemeCheckReport = $derived(check_theme(this.output)); + /** * The value a scheme currently renders for a variable, derived from the * same merge the renderer uses so the two can't disagree - including the diff --git a/src/test/class_variable_index.test.ts b/src/test/class_variable_index.test.ts index e5fbfed38..74b4be35c 100644 --- a/src/test/class_variable_index.test.ts +++ b/src/test/class_variable_index.test.ts @@ -104,13 +104,13 @@ describe('build_class_variable_index', () => { test('color classes', () => { const definitions: Record<string, CssClassDefinition | undefined> = { - palette_a_50: { + color_a_50: { declaration: 'color: var(--palette_a_50); --text_color: var(--palette_a_50);' } }; const index = build_class_variable_index(definitions); - const vars = index.by_class.get('palette_a_50')!; + const vars = index.by_class.get('color_a_50')!; assert.isTrue(vars.has('palette_a_50')); }); diff --git a/src/test/css_literal.test.ts b/src/test/css_literal.test.ts index b66c1701f..7fd0fe254 100644 --- a/src/test/css_literal.test.ts +++ b/src/test/css_literal.test.ts @@ -123,7 +123,7 @@ describe('is_possible_css_literal', () => { test.each([ ['opacity_50', 'underscore pattern'], - ['palette_a_50', 'underscore pattern'], + ['color_a_50', 'underscore pattern'], ['box', 'no colon'], ['p_md', 'underscore pattern'], ['', 'empty'], diff --git a/src/test/css_ruleset_parser.modifiers.test.ts b/src/test/css_ruleset_parser.modifiers.test.ts index 5102b0d1e..faa89a8d7 100644 --- a/src/test/css_ruleset_parser.modifiers.test.ts +++ b/src/test/css_ruleset_parser.modifiers.test.ts @@ -175,12 +175,12 @@ describe('modify_single_selector', () => { // Class with numbers/underscores ['.p_md', 'p_md', 'hover\\:p_md', ':hover', '', '.hover\\:p_md:hover', 'numbers'], [ - '.palette_a_50', - 'palette_a_50', - 'hover\\:palette_a_50', + '.color_a_50', + 'color_a_50', + 'hover\\:color_a_50', ':hover', '', - '.hover\\:palette_a_50:hover', + '.hover\\:color_a_50:hover', 'underscores and numbers' ], diff --git a/src/test/css_ruleset_parser.parse.test.ts b/src/test/css_ruleset_parser.parse.test.ts index eda80c823..bb965ddf4 100644 --- a/src/test/css_ruleset_parser.parse.test.ts +++ b/src/test/css_ruleset_parser.parse.test.ts @@ -351,9 +351,9 @@ describe('ruleset_contains_class', () => { }); test('handles class name with numbers', () => { - const result = parse_ruleset('.palette_a_50 { color: var(--palette_a_50); }'); - assert.isTrue(ruleset_contains_class(result.rules, 'palette_a_50')); - assert.isFalse(ruleset_contains_class(result.rules, 'palette_a')); + const result = parse_ruleset('.color_a_50 { color: var(--palette_a_50); }'); + assert.isTrue(ruleset_contains_class(result.rules, 'color_a_50')); + assert.isFalse(ruleset_contains_class(result.rules, 'color_a')); }); test('handles class name with hyphens', () => { diff --git a/src/test/fixtures/css_classes_fixture.json b/src/test/fixtures/css_classes_fixture.json index 3a6a49650..038a6eeb9 100644 --- a/src/test/fixtures/css_classes_fixture.json +++ b/src/test/fixtures/css_classes_fixture.json @@ -1 +1 @@ -{"pixelated":{"declaration":"\n\t\t\timage-rendering: -webkit-optimize-contrast; /* Safari */\n\t\t\timage-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */\n\t\t\timage-rendering: pixelated; /* in case crisp-edges isn't supported */\n\t\t\timage-rendering: crisp-edges; /* the recommended pixel art setting according to MDN */\n\t\t"},"circular":{"declaration":"border-radius: 50%;"},"box":{"declaration":"\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t"},"column":{"comment":"like `.box` but uncentered","declaration":"\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t"},"row":{"comment":"can be used to override the direction of a `.box`","declaration":"\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: row;\n\t\t\talign-items: center;\n\t\t"},"ellipsis":{"declaration":"\n\t\t\tdisplay: block;\n\t\t\twhite-space: nowrap;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t"},"selectable":{"ruleset":"\n\t\t\t.selectable {\n\t\t\t\t--button_fill: color-mix(in oklab, var(--shade_50) 8%, transparent);\n\t\t\t\t--button_fill_hover: color-mix(in oklab, var(--shade_50) 16%, transparent);\n\t\t\t\t--button_fill_active: color-mix(in oklab, var(--shade_50) 24%, transparent);\n\t\t\t\tcursor: pointer;\n\t\t\t\tbackground-color: var(--button_fill);\n\t\t\t\tborder-color: var(--border_color_30);\n\t\t\t\tborder-style: var(--border_style);\n\t\t\t\tborder-width: var(--border_width);\n\t\t\t}\n\t\t\t.selectable:hover {\n\t\t\t\tbackground-color: var(--button_fill_hover);\n\t\t\t\tborder-color: var(--border_color_20);\n\t\t\t}\n\t\t\t.selectable.selected,\n\t\t\t.selectable:active {\n\t\t\t\tbackground-color: var(--button_fill_active);\n\t\t\t\tborder-color: var(--accent_50);\n\t\t\t}\n\t\t\t.selectable.selected {\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t\t.selectable.selected.deselectable:not(:disabled) {\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\t\t"},"clickable":{"ruleset":"\n\t\t\t.clickable {\n\t\t\t\tcursor: pointer;\n\t\t\t\ttransform: var(--clickable_transform, scale3d(1, 1, 1));\n\t\t\t\ttransform-origin: var(--clickable_transform_origin);\n\t\t\t\ttransition-duration: var(--clickable_transition_duration); /* default to instant, chunky/lofi */\n\t\t\t}\n\t\t\t.clickable:focus {\n\t\t\t\ttransform: var(--clickable_transform_focus, scale3d(1.07, 1.07, 1.07));\n\t\t\t}\n\t\t\t.clickable:hover {\n\t\t\t\ttransform: var(--clickable_transform_hover, scale3d(1.1, 1.1, 1.1));\n\t\t\t}\n\t\t\t.clickable:active,\n\t\t\t.clickable.active {\n\t\t\t\ttransform: var(--clickable_transform_active, scale3d(1.2, 1.2, 1.2));\n\t\t\t}\n\t\t"},"pane":{"comment":"A pane is a box floating over the page, like for dialogs. By default it's opaque, resetting the background to the initial depth.","declaration":"\n\t\t\tbackground-color: var(--shade_00);\n\t\t\tbox-shadow: var(--pane_shadow, var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_50), transparent));\n\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t"},"panel":{"comment":"A panel is a box embedded into the page, useful for visually isolating content.","declaration":"\n\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t\tbackground-color: var(--fg_10);\n\t\t"},"xs":{"comment":"Smallest sizing, two steps down from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_xs);\n\t\t\t--input_height: var(--space_xl3);\n\t\t\t--input_height_compact: var(--space_xl2);\n\t\t\t--input_padding_x: var(--space_sm);\n\t\t\t--chip_padding_x: var(--space_xs3);\n\t\t\t--icon_size: var(--icon_size_xs);\n\t\t\t--menuitem_padding: var(--space_xs5) var(--space_xs3);\n\t\t\t--flow_margin: var(--space_sm);\n\t\t"},"sm":{"comment":"Smaller sizing, one step down from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_sm);\n\t\t\t--input_height: var(--space_xl4);\n\t\t\t--input_height_compact: var(--space_xl3);\n\t\t\t--input_padding_x: var(--space_md);\n\t\t\t--chip_padding_x: var(--space_xs2);\n\t\t\t--icon_size: var(--icon_size_sm);\n\t\t\t--menuitem_padding: var(--space_xs4) var(--space_xs2);\n\t\t\t--flow_margin: var(--space_md);\n\t\t"},"md":{"comment":"Default sizing restated explicitly, useful as a cascade reset within a sized parent.","declaration":"\n\t\t\t--font_size: var(--font_size_md);\n\t\t\t--input_height: var(--space_xl5);\n\t\t\t--input_height_compact: var(--space_xl4);\n\t\t\t--input_padding_x: var(--space_lg);\n\t\t\t--chip_padding_x: var(--space_xs);\n\t\t\t--icon_size: var(--icon_size_md);\n\t\t\t--menuitem_padding: var(--space_xs3) var(--space_xs);\n\t\t\t--flow_margin: var(--space_lg);\n\t\t"},"lg":{"comment":"Larger sizing, one step up from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_lg);\n\t\t\t--input_height: var(--space_xl6);\n\t\t\t--input_height_compact: var(--space_xl5);\n\t\t\t--input_padding_x: var(--space_xl);\n\t\t\t--chip_padding_x: var(--space_sm);\n\t\t\t--icon_size: var(--icon_size_lg);\n\t\t\t--menuitem_padding: var(--space_xs2) var(--space_sm);\n\t\t\t--flow_margin: var(--space_xl);\n\t\t"},"xl":{"comment":"Largest sizing, two steps up from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_xl);\n\t\t\t--input_height: var(--space_xl7);\n\t\t\t--input_height_compact: var(--space_xl6);\n\t\t\t--input_padding_x: var(--space_xl2);\n\t\t\t--chip_padding_x: var(--space_md);\n\t\t\t--icon_size: var(--icon_size_xl);\n\t\t\t--menuitem_padding: var(--space_xs) var(--space_md);\n\t\t\t--flow_margin: var(--space_xl2);\n\t\t"},"mb_flow":{"comment":"Flow-aware margin-bottom that responds to --flow_margin overrides from size composites.","declaration":"margin-bottom: var(--flow_margin, var(--space_lg));"},"mt_flow":{"comment":"Flow-aware margin-top that responds to --flow_margin overrides from size composites.","declaration":"margin-top: var(--flow_margin, var(--space_lg));"},"icon_button":{"comment":"\n\t\t\tTODO other button variants?\n\t\t\tTODO this is slightly strange that it doesn't use --icon_size.\n\t\t\tThese are used as modifiers to buttons.\n\t\t","declaration":"\n\t\t\twidth: var(--input_height);\n\t\t\theight: var(--input_height);\n\t\t\tmin-width: var(--input_height);\n\t\t\tmin-height: var(--input_height);\n\t\t\tflex-shrink: 0;\n\t\t\tline-height: 1;\n\t\t\tfont-weight: 900;\n\t\t\tpadding: 0;\n\t\t"},"plain":{"comment":"TODO maybe this belongs with the reset, like `selected`? or does `selected` belong here?","ruleset":"\n\t\t\t.plain:not(:hover) {\n\t\t\t\t--border_color: transparent;\n\t\t\t\tbox-shadow: none;\n\t\t\t\t--button_fill: transparent;\n\t\t\t}\n\t\t\t.plain:hover, .plain:active {\n\t\t\t\t--border_color: transparent;\n\t\t\t}\n\t\t"},"menuitem":{"ruleset":"\n\t\t\t.menuitem {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tborder-radius: 0;\n\t\t\t\tposition: relative;\n\t\t\t\tcursor: pointer;\n\t\t\t\twidth: 100%;\n\t\t\t\tmin-height: var(--menuitem_min_height, var(--input_height_compact));\n\t\t\t\tdisplay: flex;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\talign-items: center;\n\t\t\t\tpadding: var(--menuitem_padding, var(--space_xs3) var(--space_xs));\n\t\t\t}\n\t\t\t.menuitem.selected {\n\t\t\t\t/* TODO different patterns for border and surface? */\n\t\t\t\t--border_color: var(--accent_50);\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t\t.menuitem.selected.deselectable:not(:disabled) {\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\t\t\t.menuitem:hover {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t}\n\t\t\t.menuitem:active,\n\t\t\t.menuitem.selected:hover {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tbackground-color: var(--fg_20);\n\t\t\t}\n\t\t\t.menuitem.plain {\n\t\t\t\tborder: none;\n\t\t\t}\n\t\t\t.menuitem .content {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tflex: 1;\n\t\t\t\t/* allows the flex children to shrink */\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\t\t\t.menuitem .icon {\n\t\t\t\twidth: var(--icon_size, var(--icon_size_md));\n\t\t\t\tmargin-right: var(--space_sm);\n\t\t\t\tflex-shrink: 0;\n\t\t\t\ttext-align: center;\n\t\t\t\tfont-weight: 900;\n\t\t\t}\n\t\t\t.menuitem .title {\n\t\t\t\tmargin-right: var(--space_lg);\n\t\t\t\tflex-shrink: 1;\n\t\t\t\toverflow: hidden;\n\t\t\t\twhite-space: nowrap;\n\t\t\t\ttext-overflow: ellipsis;\n\t\t\t\tline-height: var(--line_height_lg); /* prevents the bottom of g's and others from being cut off */\n\t\t\t}\n\t\t"},"chevron":{"ruleset":"\n\t\t\t.chevron {\n\t\t\t\tposition: relative;\n\t\t\t\theight: 8px;\n\t\t\t}\n\t\t\t.chevron::before {\n\t\t\t\tdisplay: block;\n\t\t\t\tcontent: '';\n\t\t\t\tborder: 4px solid transparent;\n\t\t\t\tborder-left-color: var(--text_70);\n\t\t\t}\n\t\t"},"chip":{"ruleset":"\n\t\t\t.chip {\n\t\t\t\tfont-weight: 500;\n\t\t\t\tfont-size: var(--font_size, inherit);\n\t\t\t\tpadding-left: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tpadding-right: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t\t}\n\t\t\ta.chip {\n\t\t\t\tfont-weight: 600;\n\t\t\t}\n\t\t\t.chip.palette_a {\n\t\t\t\tcolor: var(--palette_a_50);\n\t\t\t\tbackground-color: var(--palette_a_10);\n\t\t\t}\n\t\t\t.chip.palette_b {\n\t\t\t\tcolor: var(--palette_b_50);\n\t\t\t\tbackground-color: var(--palette_b_10);\n\t\t\t}\n\t\t\t.chip.palette_c {\n\t\t\t\tcolor: var(--palette_c_50);\n\t\t\t\tbackground-color: var(--palette_c_10);\n\t\t\t}\n\t\t\t.chip.palette_d {\n\t\t\t\tcolor: var(--palette_d_50);\n\t\t\t\tbackground-color: var(--palette_d_10);\n\t\t\t}\n\t\t\t.chip.palette_e {\n\t\t\t\tcolor: var(--palette_e_50);\n\t\t\t\tbackground-color: var(--palette_e_10);\n\t\t\t}\n\t\t\t.chip.palette_f {\n\t\t\t\tcolor: var(--palette_f_50);\n\t\t\t\tbackground-color: var(--palette_f_10);\n\t\t\t}\n\t\t\t.chip.palette_g {\n\t\t\t\tcolor: var(--palette_g_50);\n\t\t\t\tbackground-color: var(--palette_g_10);\n\t\t\t}\n\t\t\t.chip.palette_h {\n\t\t\t\tcolor: var(--palette_h_50);\n\t\t\t\tbackground-color: var(--palette_h_10);\n\t\t\t}\n\t\t\t.chip.palette_i {\n\t\t\t\tcolor: var(--palette_i_50);\n\t\t\t\tbackground-color: var(--palette_i_10);\n\t\t\t}\n\t\t\t.chip.palette_j {\n\t\t\t\tcolor: var(--palette_j_50);\n\t\t\t\tbackground-color: var(--palette_j_10);\n\t\t\t}\n\t\t"},"font_family_sans":{"declaration":"font-family: var(--font_family_sans);"},"font_family_serif":{"declaration":"font-family: var(--font_family_serif);"},"font_family_mono":{"declaration":"font-family: var(--font_family_mono);"},"line_height_xs":{"declaration":"line-height: var(--line_height_xs);"},"line_height_sm":{"declaration":"line-height: var(--line_height_sm);"},"line_height_md":{"declaration":"line-height: var(--line_height_md);"},"line_height_lg":{"declaration":"line-height: var(--line_height_lg);"},"line_height_xl":{"declaration":"line-height: var(--line_height_xl);"},"font_size_xs":{"declaration":"font-size: var(--font_size_xs); --font_size: var(--font_size_xs);"},"font_size_sm":{"declaration":"font-size: var(--font_size_sm); --font_size: var(--font_size_sm);"},"font_size_md":{"declaration":"font-size: var(--font_size_md); --font_size: var(--font_size_md);"},"font_size_lg":{"declaration":"font-size: var(--font_size_lg); --font_size: var(--font_size_lg);"},"font_size_xl":{"declaration":"font-size: var(--font_size_xl); --font_size: var(--font_size_xl);"},"font_size_xl2":{"declaration":"font-size: var(--font_size_xl2); --font_size: var(--font_size_xl2);"},"font_size_xl3":{"declaration":"font-size: var(--font_size_xl3); --font_size: var(--font_size_xl3);"},"font_size_xl4":{"declaration":"font-size: var(--font_size_xl4); --font_size: var(--font_size_xl4);"},"font_size_xl5":{"declaration":"font-size: var(--font_size_xl5); --font_size: var(--font_size_xl5);"},"font_size_xl6":{"declaration":"font-size: var(--font_size_xl6); --font_size: var(--font_size_xl6);"},"font_size_xl7":{"declaration":"font-size: var(--font_size_xl7); --font_size: var(--font_size_xl7);"},"font_size_xl8":{"declaration":"font-size: var(--font_size_xl8); --font_size: var(--font_size_xl8);"},"font_size_xl9":{"declaration":"font-size: var(--font_size_xl9); --font_size: var(--font_size_xl9);"},"icon_size_xs":{"declaration":"font-size: var(--icon_size_xs); --font_size: var(--icon_size_xs);"},"icon_size_sm":{"declaration":"font-size: var(--icon_size_sm); --font_size: var(--icon_size_sm);"},"icon_size_md":{"declaration":"font-size: var(--icon_size_md); --font_size: var(--icon_size_md);"},"icon_size_lg":{"declaration":"font-size: var(--icon_size_lg); --font_size: var(--icon_size_lg);"},"icon_size_xl":{"declaration":"font-size: var(--icon_size_xl); --font_size: var(--icon_size_xl);"},"icon_size_xl2":{"declaration":"font-size: var(--icon_size_xl2); --font_size: var(--icon_size_xl2);"},"icon_size_xl3":{"declaration":"font-size: var(--icon_size_xl3); --font_size: var(--icon_size_xl3);"},"text_min":{"declaration":"color: var(--text_min); --text_color: var(--text_min);"},"text_00":{"declaration":"color: var(--text_00); --text_color: var(--text_00);"},"text_05":{"declaration":"color: var(--text_05); --text_color: var(--text_05);"},"text_10":{"declaration":"color: var(--text_10); --text_color: var(--text_10);"},"text_20":{"declaration":"color: var(--text_20); --text_color: var(--text_20);"},"text_30":{"declaration":"color: var(--text_30); --text_color: var(--text_30);"},"text_40":{"declaration":"color: var(--text_40); --text_color: var(--text_40);"},"text_50":{"declaration":"color: var(--text_50); --text_color: var(--text_50);"},"text_60":{"declaration":"color: var(--text_60); --text_color: var(--text_60);"},"text_70":{"declaration":"color: var(--text_70); --text_color: var(--text_70);"},"text_80":{"declaration":"color: var(--text_80); --text_color: var(--text_80);"},"text_90":{"declaration":"color: var(--text_90); --text_color: var(--text_90);"},"text_95":{"declaration":"color: var(--text_95); --text_color: var(--text_95);"},"text_100":{"declaration":"color: var(--text_100); --text_color: var(--text_100);"},"text_max":{"declaration":"color: var(--text_max); --text_color: var(--text_max);"},"shade_min":{"declaration":"background-color: var(--shade_min);"},"shade_00":{"declaration":"background-color: var(--shade_00);"},"shade_05":{"declaration":"background-color: var(--shade_05);"},"shade_10":{"declaration":"background-color: var(--shade_10);"},"shade_20":{"declaration":"background-color: var(--shade_20);"},"shade_30":{"declaration":"background-color: var(--shade_30);"},"shade_40":{"declaration":"background-color: var(--shade_40);"},"shade_50":{"declaration":"background-color: var(--shade_50);"},"shade_60":{"declaration":"background-color: var(--shade_60);"},"shade_70":{"declaration":"background-color: var(--shade_70);"},"shade_80":{"declaration":"background-color: var(--shade_80);"},"shade_90":{"declaration":"background-color: var(--shade_90);"},"shade_95":{"declaration":"background-color: var(--shade_95);"},"shade_100":{"declaration":"background-color: var(--shade_100);"},"shade_max":{"declaration":"background-color: var(--shade_max);"},"hue_a":{"declaration":"--hue: var(--hue_a);"},"hue_b":{"declaration":"--hue: var(--hue_b);"},"hue_c":{"declaration":"--hue: var(--hue_c);"},"hue_d":{"declaration":"--hue: var(--hue_d);"},"hue_e":{"declaration":"--hue: var(--hue_e);"},"hue_f":{"declaration":"--hue: var(--hue_f);"},"hue_g":{"declaration":"--hue: var(--hue_g);"},"hue_h":{"declaration":"--hue: var(--hue_h);"},"hue_i":{"declaration":"--hue: var(--hue_i);"},"hue_j":{"declaration":"--hue: var(--hue_j);"},"palette_a_00":{"declaration":"color: var(--palette_a_00); --text_color: var(--palette_a_00);"},"palette_a_05":{"declaration":"color: var(--palette_a_05); --text_color: var(--palette_a_05);"},"palette_a_10":{"declaration":"color: var(--palette_a_10); --text_color: var(--palette_a_10);"},"palette_a_20":{"declaration":"color: var(--palette_a_20); --text_color: var(--palette_a_20);"},"palette_a_30":{"declaration":"color: var(--palette_a_30); --text_color: var(--palette_a_30);"},"palette_a_40":{"declaration":"color: var(--palette_a_40); --text_color: var(--palette_a_40);"},"palette_a_50":{"declaration":"color: var(--palette_a_50); --text_color: var(--palette_a_50);"},"palette_a_60":{"declaration":"color: var(--palette_a_60); --text_color: var(--palette_a_60);"},"palette_a_70":{"declaration":"color: var(--palette_a_70); --text_color: var(--palette_a_70);"},"palette_a_80":{"declaration":"color: var(--palette_a_80); --text_color: var(--palette_a_80);"},"palette_a_90":{"declaration":"color: var(--palette_a_90); --text_color: var(--palette_a_90);"},"palette_a_95":{"declaration":"color: var(--palette_a_95); --text_color: var(--palette_a_95);"},"palette_a_100":{"declaration":"color: var(--palette_a_100); --text_color: var(--palette_a_100);"},"palette_b_00":{"declaration":"color: var(--palette_b_00); --text_color: var(--palette_b_00);"},"palette_b_05":{"declaration":"color: var(--palette_b_05); --text_color: var(--palette_b_05);"},"palette_b_10":{"declaration":"color: var(--palette_b_10); --text_color: var(--palette_b_10);"},"palette_b_20":{"declaration":"color: var(--palette_b_20); --text_color: var(--palette_b_20);"},"palette_b_30":{"declaration":"color: var(--palette_b_30); --text_color: var(--palette_b_30);"},"palette_b_40":{"declaration":"color: var(--palette_b_40); --text_color: var(--palette_b_40);"},"palette_b_50":{"declaration":"color: var(--palette_b_50); --text_color: var(--palette_b_50);"},"palette_b_60":{"declaration":"color: var(--palette_b_60); --text_color: var(--palette_b_60);"},"palette_b_70":{"declaration":"color: var(--palette_b_70); --text_color: var(--palette_b_70);"},"palette_b_80":{"declaration":"color: var(--palette_b_80); --text_color: var(--palette_b_80);"},"palette_b_90":{"declaration":"color: var(--palette_b_90); --text_color: var(--palette_b_90);"},"palette_b_95":{"declaration":"color: var(--palette_b_95); --text_color: var(--palette_b_95);"},"palette_b_100":{"declaration":"color: var(--palette_b_100); --text_color: var(--palette_b_100);"},"palette_c_00":{"declaration":"color: var(--palette_c_00); --text_color: var(--palette_c_00);"},"palette_c_05":{"declaration":"color: var(--palette_c_05); --text_color: var(--palette_c_05);"},"palette_c_10":{"declaration":"color: var(--palette_c_10); --text_color: var(--palette_c_10);"},"palette_c_20":{"declaration":"color: var(--palette_c_20); --text_color: var(--palette_c_20);"},"palette_c_30":{"declaration":"color: var(--palette_c_30); --text_color: var(--palette_c_30);"},"palette_c_40":{"declaration":"color: var(--palette_c_40); --text_color: var(--palette_c_40);"},"palette_c_50":{"declaration":"color: var(--palette_c_50); --text_color: var(--palette_c_50);"},"palette_c_60":{"declaration":"color: var(--palette_c_60); --text_color: var(--palette_c_60);"},"palette_c_70":{"declaration":"color: var(--palette_c_70); --text_color: var(--palette_c_70);"},"palette_c_80":{"declaration":"color: var(--palette_c_80); --text_color: var(--palette_c_80);"},"palette_c_90":{"declaration":"color: var(--palette_c_90); --text_color: var(--palette_c_90);"},"palette_c_95":{"declaration":"color: var(--palette_c_95); --text_color: var(--palette_c_95);"},"palette_c_100":{"declaration":"color: var(--palette_c_100); --text_color: var(--palette_c_100);"},"palette_d_00":{"declaration":"color: var(--palette_d_00); --text_color: var(--palette_d_00);"},"palette_d_05":{"declaration":"color: var(--palette_d_05); --text_color: var(--palette_d_05);"},"palette_d_10":{"declaration":"color: var(--palette_d_10); --text_color: var(--palette_d_10);"},"palette_d_20":{"declaration":"color: var(--palette_d_20); --text_color: var(--palette_d_20);"},"palette_d_30":{"declaration":"color: var(--palette_d_30); --text_color: var(--palette_d_30);"},"palette_d_40":{"declaration":"color: var(--palette_d_40); --text_color: var(--palette_d_40);"},"palette_d_50":{"declaration":"color: var(--palette_d_50); --text_color: var(--palette_d_50);"},"palette_d_60":{"declaration":"color: var(--palette_d_60); --text_color: var(--palette_d_60);"},"palette_d_70":{"declaration":"color: var(--palette_d_70); --text_color: var(--palette_d_70);"},"palette_d_80":{"declaration":"color: var(--palette_d_80); --text_color: var(--palette_d_80);"},"palette_d_90":{"declaration":"color: var(--palette_d_90); --text_color: var(--palette_d_90);"},"palette_d_95":{"declaration":"color: var(--palette_d_95); --text_color: var(--palette_d_95);"},"palette_d_100":{"declaration":"color: var(--palette_d_100); --text_color: var(--palette_d_100);"},"palette_e_00":{"declaration":"color: var(--palette_e_00); --text_color: var(--palette_e_00);"},"palette_e_05":{"declaration":"color: var(--palette_e_05); --text_color: var(--palette_e_05);"},"palette_e_10":{"declaration":"color: var(--palette_e_10); --text_color: var(--palette_e_10);"},"palette_e_20":{"declaration":"color: var(--palette_e_20); --text_color: var(--palette_e_20);"},"palette_e_30":{"declaration":"color: var(--palette_e_30); --text_color: var(--palette_e_30);"},"palette_e_40":{"declaration":"color: var(--palette_e_40); --text_color: var(--palette_e_40);"},"palette_e_50":{"declaration":"color: var(--palette_e_50); --text_color: var(--palette_e_50);"},"palette_e_60":{"declaration":"color: var(--palette_e_60); --text_color: var(--palette_e_60);"},"palette_e_70":{"declaration":"color: var(--palette_e_70); --text_color: var(--palette_e_70);"},"palette_e_80":{"declaration":"color: var(--palette_e_80); --text_color: var(--palette_e_80);"},"palette_e_90":{"declaration":"color: var(--palette_e_90); --text_color: var(--palette_e_90);"},"palette_e_95":{"declaration":"color: var(--palette_e_95); --text_color: var(--palette_e_95);"},"palette_e_100":{"declaration":"color: var(--palette_e_100); --text_color: var(--palette_e_100);"},"palette_f_00":{"declaration":"color: var(--palette_f_00); --text_color: var(--palette_f_00);"},"palette_f_05":{"declaration":"color: var(--palette_f_05); --text_color: var(--palette_f_05);"},"palette_f_10":{"declaration":"color: var(--palette_f_10); --text_color: var(--palette_f_10);"},"palette_f_20":{"declaration":"color: var(--palette_f_20); --text_color: var(--palette_f_20);"},"palette_f_30":{"declaration":"color: var(--palette_f_30); --text_color: var(--palette_f_30);"},"palette_f_40":{"declaration":"color: var(--palette_f_40); --text_color: var(--palette_f_40);"},"palette_f_50":{"declaration":"color: var(--palette_f_50); --text_color: var(--palette_f_50);"},"palette_f_60":{"declaration":"color: var(--palette_f_60); --text_color: var(--palette_f_60);"},"palette_f_70":{"declaration":"color: var(--palette_f_70); --text_color: var(--palette_f_70);"},"palette_f_80":{"declaration":"color: var(--palette_f_80); --text_color: var(--palette_f_80);"},"palette_f_90":{"declaration":"color: var(--palette_f_90); --text_color: var(--palette_f_90);"},"palette_f_95":{"declaration":"color: var(--palette_f_95); --text_color: var(--palette_f_95);"},"palette_f_100":{"declaration":"color: var(--palette_f_100); --text_color: var(--palette_f_100);"},"palette_g_00":{"declaration":"color: var(--palette_g_00); --text_color: var(--palette_g_00);"},"palette_g_05":{"declaration":"color: var(--palette_g_05); --text_color: var(--palette_g_05);"},"palette_g_10":{"declaration":"color: var(--palette_g_10); --text_color: var(--palette_g_10);"},"palette_g_20":{"declaration":"color: var(--palette_g_20); --text_color: var(--palette_g_20);"},"palette_g_30":{"declaration":"color: var(--palette_g_30); --text_color: var(--palette_g_30);"},"palette_g_40":{"declaration":"color: var(--palette_g_40); --text_color: var(--palette_g_40);"},"palette_g_50":{"declaration":"color: var(--palette_g_50); --text_color: var(--palette_g_50);"},"palette_g_60":{"declaration":"color: var(--palette_g_60); --text_color: var(--palette_g_60);"},"palette_g_70":{"declaration":"color: var(--palette_g_70); --text_color: var(--palette_g_70);"},"palette_g_80":{"declaration":"color: var(--palette_g_80); --text_color: var(--palette_g_80);"},"palette_g_90":{"declaration":"color: var(--palette_g_90); --text_color: var(--palette_g_90);"},"palette_g_95":{"declaration":"color: var(--palette_g_95); --text_color: var(--palette_g_95);"},"palette_g_100":{"declaration":"color: var(--palette_g_100); --text_color: var(--palette_g_100);"},"palette_h_00":{"declaration":"color: var(--palette_h_00); --text_color: var(--palette_h_00);"},"palette_h_05":{"declaration":"color: var(--palette_h_05); --text_color: var(--palette_h_05);"},"palette_h_10":{"declaration":"color: var(--palette_h_10); --text_color: var(--palette_h_10);"},"palette_h_20":{"declaration":"color: var(--palette_h_20); --text_color: var(--palette_h_20);"},"palette_h_30":{"declaration":"color: var(--palette_h_30); --text_color: var(--palette_h_30);"},"palette_h_40":{"declaration":"color: var(--palette_h_40); --text_color: var(--palette_h_40);"},"palette_h_50":{"declaration":"color: var(--palette_h_50); --text_color: var(--palette_h_50);"},"palette_h_60":{"declaration":"color: var(--palette_h_60); --text_color: var(--palette_h_60);"},"palette_h_70":{"declaration":"color: var(--palette_h_70); --text_color: var(--palette_h_70);"},"palette_h_80":{"declaration":"color: var(--palette_h_80); --text_color: var(--palette_h_80);"},"palette_h_90":{"declaration":"color: var(--palette_h_90); --text_color: var(--palette_h_90);"},"palette_h_95":{"declaration":"color: var(--palette_h_95); --text_color: var(--palette_h_95);"},"palette_h_100":{"declaration":"color: var(--palette_h_100); --text_color: var(--palette_h_100);"},"palette_i_00":{"declaration":"color: var(--palette_i_00); --text_color: var(--palette_i_00);"},"palette_i_05":{"declaration":"color: var(--palette_i_05); --text_color: var(--palette_i_05);"},"palette_i_10":{"declaration":"color: var(--palette_i_10); --text_color: var(--palette_i_10);"},"palette_i_20":{"declaration":"color: var(--palette_i_20); --text_color: var(--palette_i_20);"},"palette_i_30":{"declaration":"color: var(--palette_i_30); --text_color: var(--palette_i_30);"},"palette_i_40":{"declaration":"color: var(--palette_i_40); --text_color: var(--palette_i_40);"},"palette_i_50":{"declaration":"color: var(--palette_i_50); --text_color: var(--palette_i_50);"},"palette_i_60":{"declaration":"color: var(--palette_i_60); --text_color: var(--palette_i_60);"},"palette_i_70":{"declaration":"color: var(--palette_i_70); --text_color: var(--palette_i_70);"},"palette_i_80":{"declaration":"color: var(--palette_i_80); --text_color: var(--palette_i_80);"},"palette_i_90":{"declaration":"color: var(--palette_i_90); --text_color: var(--palette_i_90);"},"palette_i_95":{"declaration":"color: var(--palette_i_95); --text_color: var(--palette_i_95);"},"palette_i_100":{"declaration":"color: var(--palette_i_100); --text_color: var(--palette_i_100);"},"palette_j_00":{"declaration":"color: var(--palette_j_00); --text_color: var(--palette_j_00);"},"palette_j_05":{"declaration":"color: var(--palette_j_05); --text_color: var(--palette_j_05);"},"palette_j_10":{"declaration":"color: var(--palette_j_10); --text_color: var(--palette_j_10);"},"palette_j_20":{"declaration":"color: var(--palette_j_20); --text_color: var(--palette_j_20);"},"palette_j_30":{"declaration":"color: var(--palette_j_30); --text_color: var(--palette_j_30);"},"palette_j_40":{"declaration":"color: var(--palette_j_40); --text_color: var(--palette_j_40);"},"palette_j_50":{"declaration":"color: var(--palette_j_50); --text_color: var(--palette_j_50);"},"palette_j_60":{"declaration":"color: var(--palette_j_60); --text_color: var(--palette_j_60);"},"palette_j_70":{"declaration":"color: var(--palette_j_70); --text_color: var(--palette_j_70);"},"palette_j_80":{"declaration":"color: var(--palette_j_80); --text_color: var(--palette_j_80);"},"palette_j_90":{"declaration":"color: var(--palette_j_90); --text_color: var(--palette_j_90);"},"palette_j_95":{"declaration":"color: var(--palette_j_95); --text_color: var(--palette_j_95);"},"palette_j_100":{"declaration":"color: var(--palette_j_100); --text_color: var(--palette_j_100);"},"bg_a_00":{"declaration":"background-color: var(--palette_a_00);"},"bg_a_05":{"declaration":"background-color: var(--palette_a_05);"},"bg_a_10":{"declaration":"background-color: var(--palette_a_10);"},"bg_a_20":{"declaration":"background-color: var(--palette_a_20);"},"bg_a_30":{"declaration":"background-color: var(--palette_a_30);"},"bg_a_40":{"declaration":"background-color: var(--palette_a_40);"},"bg_a_50":{"declaration":"background-color: var(--palette_a_50);"},"bg_a_60":{"declaration":"background-color: var(--palette_a_60);"},"bg_a_70":{"declaration":"background-color: var(--palette_a_70);"},"bg_a_80":{"declaration":"background-color: var(--palette_a_80);"},"bg_a_90":{"declaration":"background-color: var(--palette_a_90);"},"bg_a_95":{"declaration":"background-color: var(--palette_a_95);"},"bg_a_100":{"declaration":"background-color: var(--palette_a_100);"},"bg_b_00":{"declaration":"background-color: var(--palette_b_00);"},"bg_b_05":{"declaration":"background-color: var(--palette_b_05);"},"bg_b_10":{"declaration":"background-color: var(--palette_b_10);"},"bg_b_20":{"declaration":"background-color: var(--palette_b_20);"},"bg_b_30":{"declaration":"background-color: var(--palette_b_30);"},"bg_b_40":{"declaration":"background-color: var(--palette_b_40);"},"bg_b_50":{"declaration":"background-color: var(--palette_b_50);"},"bg_b_60":{"declaration":"background-color: var(--palette_b_60);"},"bg_b_70":{"declaration":"background-color: var(--palette_b_70);"},"bg_b_80":{"declaration":"background-color: var(--palette_b_80);"},"bg_b_90":{"declaration":"background-color: var(--palette_b_90);"},"bg_b_95":{"declaration":"background-color: var(--palette_b_95);"},"bg_b_100":{"declaration":"background-color: var(--palette_b_100);"},"bg_c_00":{"declaration":"background-color: var(--palette_c_00);"},"bg_c_05":{"declaration":"background-color: var(--palette_c_05);"},"bg_c_10":{"declaration":"background-color: var(--palette_c_10);"},"bg_c_20":{"declaration":"background-color: var(--palette_c_20);"},"bg_c_30":{"declaration":"background-color: var(--palette_c_30);"},"bg_c_40":{"declaration":"background-color: var(--palette_c_40);"},"bg_c_50":{"declaration":"background-color: var(--palette_c_50);"},"bg_c_60":{"declaration":"background-color: var(--palette_c_60);"},"bg_c_70":{"declaration":"background-color: var(--palette_c_70);"},"bg_c_80":{"declaration":"background-color: var(--palette_c_80);"},"bg_c_90":{"declaration":"background-color: var(--palette_c_90);"},"bg_c_95":{"declaration":"background-color: var(--palette_c_95);"},"bg_c_100":{"declaration":"background-color: var(--palette_c_100);"},"bg_d_00":{"declaration":"background-color: var(--palette_d_00);"},"bg_d_05":{"declaration":"background-color: var(--palette_d_05);"},"bg_d_10":{"declaration":"background-color: var(--palette_d_10);"},"bg_d_20":{"declaration":"background-color: var(--palette_d_20);"},"bg_d_30":{"declaration":"background-color: var(--palette_d_30);"},"bg_d_40":{"declaration":"background-color: var(--palette_d_40);"},"bg_d_50":{"declaration":"background-color: var(--palette_d_50);"},"bg_d_60":{"declaration":"background-color: var(--palette_d_60);"},"bg_d_70":{"declaration":"background-color: var(--palette_d_70);"},"bg_d_80":{"declaration":"background-color: var(--palette_d_80);"},"bg_d_90":{"declaration":"background-color: var(--palette_d_90);"},"bg_d_95":{"declaration":"background-color: var(--palette_d_95);"},"bg_d_100":{"declaration":"background-color: var(--palette_d_100);"},"bg_e_00":{"declaration":"background-color: var(--palette_e_00);"},"bg_e_05":{"declaration":"background-color: var(--palette_e_05);"},"bg_e_10":{"declaration":"background-color: var(--palette_e_10);"},"bg_e_20":{"declaration":"background-color: var(--palette_e_20);"},"bg_e_30":{"declaration":"background-color: var(--palette_e_30);"},"bg_e_40":{"declaration":"background-color: var(--palette_e_40);"},"bg_e_50":{"declaration":"background-color: var(--palette_e_50);"},"bg_e_60":{"declaration":"background-color: var(--palette_e_60);"},"bg_e_70":{"declaration":"background-color: var(--palette_e_70);"},"bg_e_80":{"declaration":"background-color: var(--palette_e_80);"},"bg_e_90":{"declaration":"background-color: var(--palette_e_90);"},"bg_e_95":{"declaration":"background-color: var(--palette_e_95);"},"bg_e_100":{"declaration":"background-color: var(--palette_e_100);"},"bg_f_00":{"declaration":"background-color: var(--palette_f_00);"},"bg_f_05":{"declaration":"background-color: var(--palette_f_05);"},"bg_f_10":{"declaration":"background-color: var(--palette_f_10);"},"bg_f_20":{"declaration":"background-color: var(--palette_f_20);"},"bg_f_30":{"declaration":"background-color: var(--palette_f_30);"},"bg_f_40":{"declaration":"background-color: var(--palette_f_40);"},"bg_f_50":{"declaration":"background-color: var(--palette_f_50);"},"bg_f_60":{"declaration":"background-color: var(--palette_f_60);"},"bg_f_70":{"declaration":"background-color: var(--palette_f_70);"},"bg_f_80":{"declaration":"background-color: var(--palette_f_80);"},"bg_f_90":{"declaration":"background-color: var(--palette_f_90);"},"bg_f_95":{"declaration":"background-color: var(--palette_f_95);"},"bg_f_100":{"declaration":"background-color: var(--palette_f_100);"},"bg_g_00":{"declaration":"background-color: var(--palette_g_00);"},"bg_g_05":{"declaration":"background-color: var(--palette_g_05);"},"bg_g_10":{"declaration":"background-color: var(--palette_g_10);"},"bg_g_20":{"declaration":"background-color: var(--palette_g_20);"},"bg_g_30":{"declaration":"background-color: var(--palette_g_30);"},"bg_g_40":{"declaration":"background-color: var(--palette_g_40);"},"bg_g_50":{"declaration":"background-color: var(--palette_g_50);"},"bg_g_60":{"declaration":"background-color: var(--palette_g_60);"},"bg_g_70":{"declaration":"background-color: var(--palette_g_70);"},"bg_g_80":{"declaration":"background-color: var(--palette_g_80);"},"bg_g_90":{"declaration":"background-color: var(--palette_g_90);"},"bg_g_95":{"declaration":"background-color: var(--palette_g_95);"},"bg_g_100":{"declaration":"background-color: var(--palette_g_100);"},"bg_h_00":{"declaration":"background-color: var(--palette_h_00);"},"bg_h_05":{"declaration":"background-color: var(--palette_h_05);"},"bg_h_10":{"declaration":"background-color: var(--palette_h_10);"},"bg_h_20":{"declaration":"background-color: var(--palette_h_20);"},"bg_h_30":{"declaration":"background-color: var(--palette_h_30);"},"bg_h_40":{"declaration":"background-color: var(--palette_h_40);"},"bg_h_50":{"declaration":"background-color: var(--palette_h_50);"},"bg_h_60":{"declaration":"background-color: var(--palette_h_60);"},"bg_h_70":{"declaration":"background-color: var(--palette_h_70);"},"bg_h_80":{"declaration":"background-color: var(--palette_h_80);"},"bg_h_90":{"declaration":"background-color: var(--palette_h_90);"},"bg_h_95":{"declaration":"background-color: var(--palette_h_95);"},"bg_h_100":{"declaration":"background-color: var(--palette_h_100);"},"bg_i_00":{"declaration":"background-color: var(--palette_i_00);"},"bg_i_05":{"declaration":"background-color: var(--palette_i_05);"},"bg_i_10":{"declaration":"background-color: var(--palette_i_10);"},"bg_i_20":{"declaration":"background-color: var(--palette_i_20);"},"bg_i_30":{"declaration":"background-color: var(--palette_i_30);"},"bg_i_40":{"declaration":"background-color: var(--palette_i_40);"},"bg_i_50":{"declaration":"background-color: var(--palette_i_50);"},"bg_i_60":{"declaration":"background-color: var(--palette_i_60);"},"bg_i_70":{"declaration":"background-color: var(--palette_i_70);"},"bg_i_80":{"declaration":"background-color: var(--palette_i_80);"},"bg_i_90":{"declaration":"background-color: var(--palette_i_90);"},"bg_i_95":{"declaration":"background-color: var(--palette_i_95);"},"bg_i_100":{"declaration":"background-color: var(--palette_i_100);"},"bg_j_00":{"declaration":"background-color: var(--palette_j_00);"},"bg_j_05":{"declaration":"background-color: var(--palette_j_05);"},"bg_j_10":{"declaration":"background-color: var(--palette_j_10);"},"bg_j_20":{"declaration":"background-color: var(--palette_j_20);"},"bg_j_30":{"declaration":"background-color: var(--palette_j_30);"},"bg_j_40":{"declaration":"background-color: var(--palette_j_40);"},"bg_j_50":{"declaration":"background-color: var(--palette_j_50);"},"bg_j_60":{"declaration":"background-color: var(--palette_j_60);"},"bg_j_70":{"declaration":"background-color: var(--palette_j_70);"},"bg_j_80":{"declaration":"background-color: var(--palette_j_80);"},"bg_j_90":{"declaration":"background-color: var(--palette_j_90);"},"bg_j_95":{"declaration":"background-color: var(--palette_j_95);"},"bg_j_100":{"declaration":"background-color: var(--palette_j_100);"},"accent_00":{"declaration":"color: var(--accent_00); --text_color: var(--accent_00);"},"accent_05":{"declaration":"color: var(--accent_05); --text_color: var(--accent_05);"},"accent_10":{"declaration":"color: var(--accent_10); --text_color: var(--accent_10);"},"accent_20":{"declaration":"color: var(--accent_20); --text_color: var(--accent_20);"},"accent_30":{"declaration":"color: var(--accent_30); --text_color: var(--accent_30);"},"accent_40":{"declaration":"color: var(--accent_40); --text_color: var(--accent_40);"},"accent_50":{"declaration":"color: var(--accent_50); --text_color: var(--accent_50);"},"accent_60":{"declaration":"color: var(--accent_60); --text_color: var(--accent_60);"},"accent_70":{"declaration":"color: var(--accent_70); --text_color: var(--accent_70);"},"accent_80":{"declaration":"color: var(--accent_80); --text_color: var(--accent_80);"},"accent_90":{"declaration":"color: var(--accent_90); --text_color: var(--accent_90);"},"accent_95":{"declaration":"color: var(--accent_95); --text_color: var(--accent_95);"},"accent_100":{"declaration":"color: var(--accent_100); --text_color: var(--accent_100);"},"positive_00":{"declaration":"color: var(--positive_00); --text_color: var(--positive_00);"},"positive_05":{"declaration":"color: var(--positive_05); --text_color: var(--positive_05);"},"positive_10":{"declaration":"color: var(--positive_10); --text_color: var(--positive_10);"},"positive_20":{"declaration":"color: var(--positive_20); --text_color: var(--positive_20);"},"positive_30":{"declaration":"color: var(--positive_30); --text_color: var(--positive_30);"},"positive_40":{"declaration":"color: var(--positive_40); --text_color: var(--positive_40);"},"positive_50":{"declaration":"color: var(--positive_50); --text_color: var(--positive_50);"},"positive_60":{"declaration":"color: var(--positive_60); --text_color: var(--positive_60);"},"positive_70":{"declaration":"color: var(--positive_70); --text_color: var(--positive_70);"},"positive_80":{"declaration":"color: var(--positive_80); --text_color: var(--positive_80);"},"positive_90":{"declaration":"color: var(--positive_90); --text_color: var(--positive_90);"},"positive_95":{"declaration":"color: var(--positive_95); --text_color: var(--positive_95);"},"positive_100":{"declaration":"color: var(--positive_100); --text_color: var(--positive_100);"},"negative_00":{"declaration":"color: var(--negative_00); --text_color: var(--negative_00);"},"negative_05":{"declaration":"color: var(--negative_05); --text_color: var(--negative_05);"},"negative_10":{"declaration":"color: var(--negative_10); --text_color: var(--negative_10);"},"negative_20":{"declaration":"color: var(--negative_20); --text_color: var(--negative_20);"},"negative_30":{"declaration":"color: var(--negative_30); --text_color: var(--negative_30);"},"negative_40":{"declaration":"color: var(--negative_40); --text_color: var(--negative_40);"},"negative_50":{"declaration":"color: var(--negative_50); --text_color: var(--negative_50);"},"negative_60":{"declaration":"color: var(--negative_60); --text_color: var(--negative_60);"},"negative_70":{"declaration":"color: var(--negative_70); --text_color: var(--negative_70);"},"negative_80":{"declaration":"color: var(--negative_80); --text_color: var(--negative_80);"},"negative_90":{"declaration":"color: var(--negative_90); --text_color: var(--negative_90);"},"negative_95":{"declaration":"color: var(--negative_95); --text_color: var(--negative_95);"},"negative_100":{"declaration":"color: var(--negative_100); --text_color: var(--negative_100);"},"caution_00":{"declaration":"color: var(--caution_00); --text_color: var(--caution_00);"},"caution_05":{"declaration":"color: var(--caution_05); --text_color: var(--caution_05);"},"caution_10":{"declaration":"color: var(--caution_10); --text_color: var(--caution_10);"},"caution_20":{"declaration":"color: var(--caution_20); --text_color: var(--caution_20);"},"caution_30":{"declaration":"color: var(--caution_30); --text_color: var(--caution_30);"},"caution_40":{"declaration":"color: var(--caution_40); --text_color: var(--caution_40);"},"caution_50":{"declaration":"color: var(--caution_50); --text_color: var(--caution_50);"},"caution_60":{"declaration":"color: var(--caution_60); --text_color: var(--caution_60);"},"caution_70":{"declaration":"color: var(--caution_70); --text_color: var(--caution_70);"},"caution_80":{"declaration":"color: var(--caution_80); --text_color: var(--caution_80);"},"caution_90":{"declaration":"color: var(--caution_90); --text_color: var(--caution_90);"},"caution_95":{"declaration":"color: var(--caution_95); --text_color: var(--caution_95);"},"caution_100":{"declaration":"color: var(--caution_100); --text_color: var(--caution_100);"},"info_00":{"declaration":"color: var(--info_00); --text_color: var(--info_00);"},"info_05":{"declaration":"color: var(--info_05); --text_color: var(--info_05);"},"info_10":{"declaration":"color: var(--info_10); --text_color: var(--info_10);"},"info_20":{"declaration":"color: var(--info_20); --text_color: var(--info_20);"},"info_30":{"declaration":"color: var(--info_30); --text_color: var(--info_30);"},"info_40":{"declaration":"color: var(--info_40); --text_color: var(--info_40);"},"info_50":{"declaration":"color: var(--info_50); --text_color: var(--info_50);"},"info_60":{"declaration":"color: var(--info_60); --text_color: var(--info_60);"},"info_70":{"declaration":"color: var(--info_70); --text_color: var(--info_70);"},"info_80":{"declaration":"color: var(--info_80); --text_color: var(--info_80);"},"info_90":{"declaration":"color: var(--info_90); --text_color: var(--info_90);"},"info_95":{"declaration":"color: var(--info_95); --text_color: var(--info_95);"},"info_100":{"declaration":"color: var(--info_100); --text_color: var(--info_100);"},"bg_accent_00":{"declaration":"background-color: var(--accent_00);"},"bg_accent_05":{"declaration":"background-color: var(--accent_05);"},"bg_accent_10":{"declaration":"background-color: var(--accent_10);"},"bg_accent_20":{"declaration":"background-color: var(--accent_20);"},"bg_accent_30":{"declaration":"background-color: var(--accent_30);"},"bg_accent_40":{"declaration":"background-color: var(--accent_40);"},"bg_accent_50":{"declaration":"background-color: var(--accent_50);"},"bg_accent_60":{"declaration":"background-color: var(--accent_60);"},"bg_accent_70":{"declaration":"background-color: var(--accent_70);"},"bg_accent_80":{"declaration":"background-color: var(--accent_80);"},"bg_accent_90":{"declaration":"background-color: var(--accent_90);"},"bg_accent_95":{"declaration":"background-color: var(--accent_95);"},"bg_accent_100":{"declaration":"background-color: var(--accent_100);"},"bg_positive_00":{"declaration":"background-color: var(--positive_00);"},"bg_positive_05":{"declaration":"background-color: var(--positive_05);"},"bg_positive_10":{"declaration":"background-color: var(--positive_10);"},"bg_positive_20":{"declaration":"background-color: var(--positive_20);"},"bg_positive_30":{"declaration":"background-color: var(--positive_30);"},"bg_positive_40":{"declaration":"background-color: var(--positive_40);"},"bg_positive_50":{"declaration":"background-color: var(--positive_50);"},"bg_positive_60":{"declaration":"background-color: var(--positive_60);"},"bg_positive_70":{"declaration":"background-color: var(--positive_70);"},"bg_positive_80":{"declaration":"background-color: var(--positive_80);"},"bg_positive_90":{"declaration":"background-color: var(--positive_90);"},"bg_positive_95":{"declaration":"background-color: var(--positive_95);"},"bg_positive_100":{"declaration":"background-color: var(--positive_100);"},"bg_negative_00":{"declaration":"background-color: var(--negative_00);"},"bg_negative_05":{"declaration":"background-color: var(--negative_05);"},"bg_negative_10":{"declaration":"background-color: var(--negative_10);"},"bg_negative_20":{"declaration":"background-color: var(--negative_20);"},"bg_negative_30":{"declaration":"background-color: var(--negative_30);"},"bg_negative_40":{"declaration":"background-color: var(--negative_40);"},"bg_negative_50":{"declaration":"background-color: var(--negative_50);"},"bg_negative_60":{"declaration":"background-color: var(--negative_60);"},"bg_negative_70":{"declaration":"background-color: var(--negative_70);"},"bg_negative_80":{"declaration":"background-color: var(--negative_80);"},"bg_negative_90":{"declaration":"background-color: var(--negative_90);"},"bg_negative_95":{"declaration":"background-color: var(--negative_95);"},"bg_negative_100":{"declaration":"background-color: var(--negative_100);"},"bg_caution_00":{"declaration":"background-color: var(--caution_00);"},"bg_caution_05":{"declaration":"background-color: var(--caution_05);"},"bg_caution_10":{"declaration":"background-color: var(--caution_10);"},"bg_caution_20":{"declaration":"background-color: var(--caution_20);"},"bg_caution_30":{"declaration":"background-color: var(--caution_30);"},"bg_caution_40":{"declaration":"background-color: var(--caution_40);"},"bg_caution_50":{"declaration":"background-color: var(--caution_50);"},"bg_caution_60":{"declaration":"background-color: var(--caution_60);"},"bg_caution_70":{"declaration":"background-color: var(--caution_70);"},"bg_caution_80":{"declaration":"background-color: var(--caution_80);"},"bg_caution_90":{"declaration":"background-color: var(--caution_90);"},"bg_caution_95":{"declaration":"background-color: var(--caution_95);"},"bg_caution_100":{"declaration":"background-color: var(--caution_100);"},"bg_info_00":{"declaration":"background-color: var(--info_00);"},"bg_info_05":{"declaration":"background-color: var(--info_05);"},"bg_info_10":{"declaration":"background-color: var(--info_10);"},"bg_info_20":{"declaration":"background-color: var(--info_20);"},"bg_info_30":{"declaration":"background-color: var(--info_30);"},"bg_info_40":{"declaration":"background-color: var(--info_40);"},"bg_info_50":{"declaration":"background-color: var(--info_50);"},"bg_info_60":{"declaration":"background-color: var(--info_60);"},"bg_info_70":{"declaration":"background-color: var(--info_70);"},"bg_info_80":{"declaration":"background-color: var(--info_80);"},"bg_info_90":{"declaration":"background-color: var(--info_90);"},"bg_info_95":{"declaration":"background-color: var(--info_95);"},"bg_info_100":{"declaration":"background-color: var(--info_100);"},"darken_00":{"declaration":"background-color: var(--darken_00);"},"darken_05":{"declaration":"background-color: var(--darken_05);"},"darken_10":{"declaration":"background-color: var(--darken_10);"},"darken_20":{"declaration":"background-color: var(--darken_20);"},"darken_30":{"declaration":"background-color: var(--darken_30);"},"darken_40":{"declaration":"background-color: var(--darken_40);"},"darken_50":{"declaration":"background-color: var(--darken_50);"},"darken_60":{"declaration":"background-color: var(--darken_60);"},"darken_70":{"declaration":"background-color: var(--darken_70);"},"darken_80":{"declaration":"background-color: var(--darken_80);"},"darken_90":{"declaration":"background-color: var(--darken_90);"},"darken_95":{"declaration":"background-color: var(--darken_95);"},"darken_100":{"declaration":"background-color: var(--darken_100);"},"lighten_00":{"declaration":"background-color: var(--lighten_00);"},"lighten_05":{"declaration":"background-color: var(--lighten_05);"},"lighten_10":{"declaration":"background-color: var(--lighten_10);"},"lighten_20":{"declaration":"background-color: var(--lighten_20);"},"lighten_30":{"declaration":"background-color: var(--lighten_30);"},"lighten_40":{"declaration":"background-color: var(--lighten_40);"},"lighten_50":{"declaration":"background-color: var(--lighten_50);"},"lighten_60":{"declaration":"background-color: var(--lighten_60);"},"lighten_70":{"declaration":"background-color: var(--lighten_70);"},"lighten_80":{"declaration":"background-color: var(--lighten_80);"},"lighten_90":{"declaration":"background-color: var(--lighten_90);"},"lighten_95":{"declaration":"background-color: var(--lighten_95);"},"lighten_100":{"declaration":"background-color: var(--lighten_100);"},"fg_00":{"declaration":"background-color: var(--fg_00);"},"fg_05":{"declaration":"background-color: var(--fg_05);"},"fg_10":{"declaration":"background-color: var(--fg_10);"},"fg_20":{"declaration":"background-color: var(--fg_20);"},"fg_30":{"declaration":"background-color: var(--fg_30);"},"fg_40":{"declaration":"background-color: var(--fg_40);"},"fg_50":{"declaration":"background-color: var(--fg_50);"},"fg_60":{"declaration":"background-color: var(--fg_60);"},"fg_70":{"declaration":"background-color: var(--fg_70);"},"fg_80":{"declaration":"background-color: var(--fg_80);"},"fg_90":{"declaration":"background-color: var(--fg_90);"},"fg_95":{"declaration":"background-color: var(--fg_95);"},"fg_100":{"declaration":"background-color: var(--fg_100);"},"bg_00":{"declaration":"background-color: var(--bg_00);"},"bg_05":{"declaration":"background-color: var(--bg_05);"},"bg_10":{"declaration":"background-color: var(--bg_10);"},"bg_20":{"declaration":"background-color: var(--bg_20);"},"bg_30":{"declaration":"background-color: var(--bg_30);"},"bg_40":{"declaration":"background-color: var(--bg_40);"},"bg_50":{"declaration":"background-color: var(--bg_50);"},"bg_60":{"declaration":"background-color: var(--bg_60);"},"bg_70":{"declaration":"background-color: var(--bg_70);"},"bg_80":{"declaration":"background-color: var(--bg_80);"},"bg_90":{"declaration":"background-color: var(--bg_90);"},"bg_95":{"declaration":"background-color: var(--bg_95);"},"bg_100":{"declaration":"background-color: var(--bg_100);"},"border_color_00":{"declaration":"border-color: var(--border_color_00); --border_color: var(--border_color_00);"},"border_color_05":{"declaration":"border-color: var(--border_color_05); --border_color: var(--border_color_05);"},"border_color_10":{"declaration":"border-color: var(--border_color_10); --border_color: var(--border_color_10);"},"border_color_20":{"declaration":"border-color: var(--border_color_20); --border_color: var(--border_color_20);"},"border_color_30":{"declaration":"border-color: var(--border_color_30); --border_color: var(--border_color_30);"},"border_color_40":{"declaration":"border-color: var(--border_color_40); --border_color: var(--border_color_40);"},"border_color_50":{"declaration":"border-color: var(--border_color_50); --border_color: var(--border_color_50);"},"border_color_60":{"declaration":"border-color: var(--border_color_60); --border_color: var(--border_color_60);"},"border_color_70":{"declaration":"border-color: var(--border_color_70); --border_color: var(--border_color_70);"},"border_color_80":{"declaration":"border-color: var(--border_color_80); --border_color: var(--border_color_80);"},"border_color_90":{"declaration":"border-color: var(--border_color_90); --border_color: var(--border_color_90);"},"border_color_95":{"declaration":"border-color: var(--border_color_95); --border_color: var(--border_color_95);"},"border_color_100":{"declaration":"border-color: var(--border_color_100); --border_color: var(--border_color_100);"},"border_a_00":{"declaration":"border-color: var(--palette_a_00); --border_color: var(--palette_a_00);"},"border_a_05":{"declaration":"border-color: var(--palette_a_05); --border_color: var(--palette_a_05);"},"border_a_10":{"declaration":"border-color: var(--palette_a_10); --border_color: var(--palette_a_10);"},"border_a_20":{"declaration":"border-color: var(--palette_a_20); --border_color: var(--palette_a_20);"},"border_a_30":{"declaration":"border-color: var(--palette_a_30); --border_color: var(--palette_a_30);"},"border_a_40":{"declaration":"border-color: var(--palette_a_40); --border_color: var(--palette_a_40);"},"border_a_50":{"declaration":"border-color: var(--palette_a_50); --border_color: var(--palette_a_50);"},"border_a_60":{"declaration":"border-color: var(--palette_a_60); --border_color: var(--palette_a_60);"},"border_a_70":{"declaration":"border-color: var(--palette_a_70); --border_color: var(--palette_a_70);"},"border_a_80":{"declaration":"border-color: var(--palette_a_80); --border_color: var(--palette_a_80);"},"border_a_90":{"declaration":"border-color: var(--palette_a_90); --border_color: var(--palette_a_90);"},"border_a_95":{"declaration":"border-color: var(--palette_a_95); --border_color: var(--palette_a_95);"},"border_a_100":{"declaration":"border-color: var(--palette_a_100); --border_color: var(--palette_a_100);"},"border_b_00":{"declaration":"border-color: var(--palette_b_00); --border_color: var(--palette_b_00);"},"border_b_05":{"declaration":"border-color: var(--palette_b_05); --border_color: var(--palette_b_05);"},"border_b_10":{"declaration":"border-color: var(--palette_b_10); --border_color: var(--palette_b_10);"},"border_b_20":{"declaration":"border-color: var(--palette_b_20); --border_color: var(--palette_b_20);"},"border_b_30":{"declaration":"border-color: var(--palette_b_30); --border_color: var(--palette_b_30);"},"border_b_40":{"declaration":"border-color: var(--palette_b_40); --border_color: var(--palette_b_40);"},"border_b_50":{"declaration":"border-color: var(--palette_b_50); --border_color: var(--palette_b_50);"},"border_b_60":{"declaration":"border-color: var(--palette_b_60); --border_color: var(--palette_b_60);"},"border_b_70":{"declaration":"border-color: var(--palette_b_70); --border_color: var(--palette_b_70);"},"border_b_80":{"declaration":"border-color: var(--palette_b_80); --border_color: var(--palette_b_80);"},"border_b_90":{"declaration":"border-color: var(--palette_b_90); --border_color: var(--palette_b_90);"},"border_b_95":{"declaration":"border-color: var(--palette_b_95); --border_color: var(--palette_b_95);"},"border_b_100":{"declaration":"border-color: var(--palette_b_100); --border_color: var(--palette_b_100);"},"border_c_00":{"declaration":"border-color: var(--palette_c_00); --border_color: var(--palette_c_00);"},"border_c_05":{"declaration":"border-color: var(--palette_c_05); --border_color: var(--palette_c_05);"},"border_c_10":{"declaration":"border-color: var(--palette_c_10); --border_color: var(--palette_c_10);"},"border_c_20":{"declaration":"border-color: var(--palette_c_20); --border_color: var(--palette_c_20);"},"border_c_30":{"declaration":"border-color: var(--palette_c_30); --border_color: var(--palette_c_30);"},"border_c_40":{"declaration":"border-color: var(--palette_c_40); --border_color: var(--palette_c_40);"},"border_c_50":{"declaration":"border-color: var(--palette_c_50); --border_color: var(--palette_c_50);"},"border_c_60":{"declaration":"border-color: var(--palette_c_60); --border_color: var(--palette_c_60);"},"border_c_70":{"declaration":"border-color: var(--palette_c_70); --border_color: var(--palette_c_70);"},"border_c_80":{"declaration":"border-color: var(--palette_c_80); --border_color: var(--palette_c_80);"},"border_c_90":{"declaration":"border-color: var(--palette_c_90); --border_color: var(--palette_c_90);"},"border_c_95":{"declaration":"border-color: var(--palette_c_95); --border_color: var(--palette_c_95);"},"border_c_100":{"declaration":"border-color: var(--palette_c_100); --border_color: var(--palette_c_100);"},"border_d_00":{"declaration":"border-color: var(--palette_d_00); --border_color: var(--palette_d_00);"},"border_d_05":{"declaration":"border-color: var(--palette_d_05); --border_color: var(--palette_d_05);"},"border_d_10":{"declaration":"border-color: var(--palette_d_10); --border_color: var(--palette_d_10);"},"border_d_20":{"declaration":"border-color: var(--palette_d_20); --border_color: var(--palette_d_20);"},"border_d_30":{"declaration":"border-color: var(--palette_d_30); --border_color: var(--palette_d_30);"},"border_d_40":{"declaration":"border-color: var(--palette_d_40); --border_color: var(--palette_d_40);"},"border_d_50":{"declaration":"border-color: var(--palette_d_50); --border_color: var(--palette_d_50);"},"border_d_60":{"declaration":"border-color: var(--palette_d_60); --border_color: var(--palette_d_60);"},"border_d_70":{"declaration":"border-color: var(--palette_d_70); --border_color: var(--palette_d_70);"},"border_d_80":{"declaration":"border-color: var(--palette_d_80); --border_color: var(--palette_d_80);"},"border_d_90":{"declaration":"border-color: var(--palette_d_90); --border_color: var(--palette_d_90);"},"border_d_95":{"declaration":"border-color: var(--palette_d_95); --border_color: var(--palette_d_95);"},"border_d_100":{"declaration":"border-color: var(--palette_d_100); --border_color: var(--palette_d_100);"},"border_e_00":{"declaration":"border-color: var(--palette_e_00); --border_color: var(--palette_e_00);"},"border_e_05":{"declaration":"border-color: var(--palette_e_05); --border_color: var(--palette_e_05);"},"border_e_10":{"declaration":"border-color: var(--palette_e_10); --border_color: var(--palette_e_10);"},"border_e_20":{"declaration":"border-color: var(--palette_e_20); --border_color: var(--palette_e_20);"},"border_e_30":{"declaration":"border-color: var(--palette_e_30); --border_color: var(--palette_e_30);"},"border_e_40":{"declaration":"border-color: var(--palette_e_40); --border_color: var(--palette_e_40);"},"border_e_50":{"declaration":"border-color: var(--palette_e_50); --border_color: var(--palette_e_50);"},"border_e_60":{"declaration":"border-color: var(--palette_e_60); --border_color: var(--palette_e_60);"},"border_e_70":{"declaration":"border-color: var(--palette_e_70); --border_color: var(--palette_e_70);"},"border_e_80":{"declaration":"border-color: var(--palette_e_80); --border_color: var(--palette_e_80);"},"border_e_90":{"declaration":"border-color: var(--palette_e_90); --border_color: var(--palette_e_90);"},"border_e_95":{"declaration":"border-color: var(--palette_e_95); --border_color: var(--palette_e_95);"},"border_e_100":{"declaration":"border-color: var(--palette_e_100); --border_color: var(--palette_e_100);"},"border_f_00":{"declaration":"border-color: var(--palette_f_00); --border_color: var(--palette_f_00);"},"border_f_05":{"declaration":"border-color: var(--palette_f_05); --border_color: var(--palette_f_05);"},"border_f_10":{"declaration":"border-color: var(--palette_f_10); --border_color: var(--palette_f_10);"},"border_f_20":{"declaration":"border-color: var(--palette_f_20); --border_color: var(--palette_f_20);"},"border_f_30":{"declaration":"border-color: var(--palette_f_30); --border_color: var(--palette_f_30);"},"border_f_40":{"declaration":"border-color: var(--palette_f_40); --border_color: var(--palette_f_40);"},"border_f_50":{"declaration":"border-color: var(--palette_f_50); --border_color: var(--palette_f_50);"},"border_f_60":{"declaration":"border-color: var(--palette_f_60); --border_color: var(--palette_f_60);"},"border_f_70":{"declaration":"border-color: var(--palette_f_70); --border_color: var(--palette_f_70);"},"border_f_80":{"declaration":"border-color: var(--palette_f_80); --border_color: var(--palette_f_80);"},"border_f_90":{"declaration":"border-color: var(--palette_f_90); --border_color: var(--palette_f_90);"},"border_f_95":{"declaration":"border-color: var(--palette_f_95); --border_color: var(--palette_f_95);"},"border_f_100":{"declaration":"border-color: var(--palette_f_100); --border_color: var(--palette_f_100);"},"border_g_00":{"declaration":"border-color: var(--palette_g_00); --border_color: var(--palette_g_00);"},"border_g_05":{"declaration":"border-color: var(--palette_g_05); --border_color: var(--palette_g_05);"},"border_g_10":{"declaration":"border-color: var(--palette_g_10); --border_color: var(--palette_g_10);"},"border_g_20":{"declaration":"border-color: var(--palette_g_20); --border_color: var(--palette_g_20);"},"border_g_30":{"declaration":"border-color: var(--palette_g_30); --border_color: var(--palette_g_30);"},"border_g_40":{"declaration":"border-color: var(--palette_g_40); --border_color: var(--palette_g_40);"},"border_g_50":{"declaration":"border-color: var(--palette_g_50); --border_color: var(--palette_g_50);"},"border_g_60":{"declaration":"border-color: var(--palette_g_60); --border_color: var(--palette_g_60);"},"border_g_70":{"declaration":"border-color: var(--palette_g_70); --border_color: var(--palette_g_70);"},"border_g_80":{"declaration":"border-color: var(--palette_g_80); --border_color: var(--palette_g_80);"},"border_g_90":{"declaration":"border-color: var(--palette_g_90); --border_color: var(--palette_g_90);"},"border_g_95":{"declaration":"border-color: var(--palette_g_95); --border_color: var(--palette_g_95);"},"border_g_100":{"declaration":"border-color: var(--palette_g_100); --border_color: var(--palette_g_100);"},"border_h_00":{"declaration":"border-color: var(--palette_h_00); --border_color: var(--palette_h_00);"},"border_h_05":{"declaration":"border-color: var(--palette_h_05); --border_color: var(--palette_h_05);"},"border_h_10":{"declaration":"border-color: var(--palette_h_10); --border_color: var(--palette_h_10);"},"border_h_20":{"declaration":"border-color: var(--palette_h_20); --border_color: var(--palette_h_20);"},"border_h_30":{"declaration":"border-color: var(--palette_h_30); --border_color: var(--palette_h_30);"},"border_h_40":{"declaration":"border-color: var(--palette_h_40); --border_color: var(--palette_h_40);"},"border_h_50":{"declaration":"border-color: var(--palette_h_50); --border_color: var(--palette_h_50);"},"border_h_60":{"declaration":"border-color: var(--palette_h_60); --border_color: var(--palette_h_60);"},"border_h_70":{"declaration":"border-color: var(--palette_h_70); --border_color: var(--palette_h_70);"},"border_h_80":{"declaration":"border-color: var(--palette_h_80); --border_color: var(--palette_h_80);"},"border_h_90":{"declaration":"border-color: var(--palette_h_90); --border_color: var(--palette_h_90);"},"border_h_95":{"declaration":"border-color: var(--palette_h_95); --border_color: var(--palette_h_95);"},"border_h_100":{"declaration":"border-color: var(--palette_h_100); --border_color: var(--palette_h_100);"},"border_i_00":{"declaration":"border-color: var(--palette_i_00); --border_color: var(--palette_i_00);"},"border_i_05":{"declaration":"border-color: var(--palette_i_05); --border_color: var(--palette_i_05);"},"border_i_10":{"declaration":"border-color: var(--palette_i_10); --border_color: var(--palette_i_10);"},"border_i_20":{"declaration":"border-color: var(--palette_i_20); --border_color: var(--palette_i_20);"},"border_i_30":{"declaration":"border-color: var(--palette_i_30); --border_color: var(--palette_i_30);"},"border_i_40":{"declaration":"border-color: var(--palette_i_40); --border_color: var(--palette_i_40);"},"border_i_50":{"declaration":"border-color: var(--palette_i_50); --border_color: var(--palette_i_50);"},"border_i_60":{"declaration":"border-color: var(--palette_i_60); --border_color: var(--palette_i_60);"},"border_i_70":{"declaration":"border-color: var(--palette_i_70); --border_color: var(--palette_i_70);"},"border_i_80":{"declaration":"border-color: var(--palette_i_80); --border_color: var(--palette_i_80);"},"border_i_90":{"declaration":"border-color: var(--palette_i_90); --border_color: var(--palette_i_90);"},"border_i_95":{"declaration":"border-color: var(--palette_i_95); --border_color: var(--palette_i_95);"},"border_i_100":{"declaration":"border-color: var(--palette_i_100); --border_color: var(--palette_i_100);"},"border_j_00":{"declaration":"border-color: var(--palette_j_00); --border_color: var(--palette_j_00);"},"border_j_05":{"declaration":"border-color: var(--palette_j_05); --border_color: var(--palette_j_05);"},"border_j_10":{"declaration":"border-color: var(--palette_j_10); --border_color: var(--palette_j_10);"},"border_j_20":{"declaration":"border-color: var(--palette_j_20); --border_color: var(--palette_j_20);"},"border_j_30":{"declaration":"border-color: var(--palette_j_30); --border_color: var(--palette_j_30);"},"border_j_40":{"declaration":"border-color: var(--palette_j_40); --border_color: var(--palette_j_40);"},"border_j_50":{"declaration":"border-color: var(--palette_j_50); --border_color: var(--palette_j_50);"},"border_j_60":{"declaration":"border-color: var(--palette_j_60); --border_color: var(--palette_j_60);"},"border_j_70":{"declaration":"border-color: var(--palette_j_70); --border_color: var(--palette_j_70);"},"border_j_80":{"declaration":"border-color: var(--palette_j_80); --border_color: var(--palette_j_80);"},"border_j_90":{"declaration":"border-color: var(--palette_j_90); --border_color: var(--palette_j_90);"},"border_j_95":{"declaration":"border-color: var(--palette_j_95); --border_color: var(--palette_j_95);"},"border_j_100":{"declaration":"border-color: var(--palette_j_100); --border_color: var(--palette_j_100);"},"outline_color_00":{"declaration":"outline-color: var(--shade_00);"},"outline_color_05":{"declaration":"outline-color: var(--shade_05);"},"outline_color_10":{"declaration":"outline-color: var(--shade_10);"},"outline_color_20":{"declaration":"outline-color: var(--shade_20);"},"outline_color_30":{"declaration":"outline-color: var(--shade_30);"},"outline_color_40":{"declaration":"outline-color: var(--shade_40);"},"outline_color_50":{"declaration":"outline-color: var(--shade_50);"},"outline_color_60":{"declaration":"outline-color: var(--shade_60);"},"outline_color_70":{"declaration":"outline-color: var(--shade_70);"},"outline_color_80":{"declaration":"outline-color: var(--shade_80);"},"outline_color_90":{"declaration":"outline-color: var(--shade_90);"},"outline_color_95":{"declaration":"outline-color: var(--shade_95);"},"outline_color_100":{"declaration":"outline-color: var(--shade_100);"},"outline_a_00":{"declaration":"outline-color: var(--palette_a_00); --outline_color: var(--palette_a_00);"},"outline_a_05":{"declaration":"outline-color: var(--palette_a_05); --outline_color: var(--palette_a_05);"},"outline_a_10":{"declaration":"outline-color: var(--palette_a_10); --outline_color: var(--palette_a_10);"},"outline_a_20":{"declaration":"outline-color: var(--palette_a_20); --outline_color: var(--palette_a_20);"},"outline_a_30":{"declaration":"outline-color: var(--palette_a_30); --outline_color: var(--palette_a_30);"},"outline_a_40":{"declaration":"outline-color: var(--palette_a_40); --outline_color: var(--palette_a_40);"},"outline_a_50":{"declaration":"outline-color: var(--palette_a_50); --outline_color: var(--palette_a_50);"},"outline_a_60":{"declaration":"outline-color: var(--palette_a_60); --outline_color: var(--palette_a_60);"},"outline_a_70":{"declaration":"outline-color: var(--palette_a_70); --outline_color: var(--palette_a_70);"},"outline_a_80":{"declaration":"outline-color: var(--palette_a_80); --outline_color: var(--palette_a_80);"},"outline_a_90":{"declaration":"outline-color: var(--palette_a_90); --outline_color: var(--palette_a_90);"},"outline_a_95":{"declaration":"outline-color: var(--palette_a_95); --outline_color: var(--palette_a_95);"},"outline_a_100":{"declaration":"outline-color: var(--palette_a_100); --outline_color: var(--palette_a_100);"},"outline_b_00":{"declaration":"outline-color: var(--palette_b_00); --outline_color: var(--palette_b_00);"},"outline_b_05":{"declaration":"outline-color: var(--palette_b_05); --outline_color: var(--palette_b_05);"},"outline_b_10":{"declaration":"outline-color: var(--palette_b_10); --outline_color: var(--palette_b_10);"},"outline_b_20":{"declaration":"outline-color: var(--palette_b_20); --outline_color: var(--palette_b_20);"},"outline_b_30":{"declaration":"outline-color: var(--palette_b_30); --outline_color: var(--palette_b_30);"},"outline_b_40":{"declaration":"outline-color: var(--palette_b_40); --outline_color: var(--palette_b_40);"},"outline_b_50":{"declaration":"outline-color: var(--palette_b_50); --outline_color: var(--palette_b_50);"},"outline_b_60":{"declaration":"outline-color: var(--palette_b_60); --outline_color: var(--palette_b_60);"},"outline_b_70":{"declaration":"outline-color: var(--palette_b_70); --outline_color: var(--palette_b_70);"},"outline_b_80":{"declaration":"outline-color: var(--palette_b_80); --outline_color: var(--palette_b_80);"},"outline_b_90":{"declaration":"outline-color: var(--palette_b_90); --outline_color: var(--palette_b_90);"},"outline_b_95":{"declaration":"outline-color: var(--palette_b_95); --outline_color: var(--palette_b_95);"},"outline_b_100":{"declaration":"outline-color: var(--palette_b_100); --outline_color: var(--palette_b_100);"},"outline_c_00":{"declaration":"outline-color: var(--palette_c_00); --outline_color: var(--palette_c_00);"},"outline_c_05":{"declaration":"outline-color: var(--palette_c_05); --outline_color: var(--palette_c_05);"},"outline_c_10":{"declaration":"outline-color: var(--palette_c_10); --outline_color: var(--palette_c_10);"},"outline_c_20":{"declaration":"outline-color: var(--palette_c_20); --outline_color: var(--palette_c_20);"},"outline_c_30":{"declaration":"outline-color: var(--palette_c_30); --outline_color: var(--palette_c_30);"},"outline_c_40":{"declaration":"outline-color: var(--palette_c_40); --outline_color: var(--palette_c_40);"},"outline_c_50":{"declaration":"outline-color: var(--palette_c_50); --outline_color: var(--palette_c_50);"},"outline_c_60":{"declaration":"outline-color: var(--palette_c_60); --outline_color: var(--palette_c_60);"},"outline_c_70":{"declaration":"outline-color: var(--palette_c_70); --outline_color: var(--palette_c_70);"},"outline_c_80":{"declaration":"outline-color: var(--palette_c_80); --outline_color: var(--palette_c_80);"},"outline_c_90":{"declaration":"outline-color: var(--palette_c_90); --outline_color: var(--palette_c_90);"},"outline_c_95":{"declaration":"outline-color: var(--palette_c_95); --outline_color: var(--palette_c_95);"},"outline_c_100":{"declaration":"outline-color: var(--palette_c_100); --outline_color: var(--palette_c_100);"},"outline_d_00":{"declaration":"outline-color: var(--palette_d_00); --outline_color: var(--palette_d_00);"},"outline_d_05":{"declaration":"outline-color: var(--palette_d_05); --outline_color: var(--palette_d_05);"},"outline_d_10":{"declaration":"outline-color: var(--palette_d_10); --outline_color: var(--palette_d_10);"},"outline_d_20":{"declaration":"outline-color: var(--palette_d_20); --outline_color: var(--palette_d_20);"},"outline_d_30":{"declaration":"outline-color: var(--palette_d_30); --outline_color: var(--palette_d_30);"},"outline_d_40":{"declaration":"outline-color: var(--palette_d_40); --outline_color: var(--palette_d_40);"},"outline_d_50":{"declaration":"outline-color: var(--palette_d_50); --outline_color: var(--palette_d_50);"},"outline_d_60":{"declaration":"outline-color: var(--palette_d_60); --outline_color: var(--palette_d_60);"},"outline_d_70":{"declaration":"outline-color: var(--palette_d_70); --outline_color: var(--palette_d_70);"},"outline_d_80":{"declaration":"outline-color: var(--palette_d_80); --outline_color: var(--palette_d_80);"},"outline_d_90":{"declaration":"outline-color: var(--palette_d_90); --outline_color: var(--palette_d_90);"},"outline_d_95":{"declaration":"outline-color: var(--palette_d_95); --outline_color: var(--palette_d_95);"},"outline_d_100":{"declaration":"outline-color: var(--palette_d_100); --outline_color: var(--palette_d_100);"},"outline_e_00":{"declaration":"outline-color: var(--palette_e_00); --outline_color: var(--palette_e_00);"},"outline_e_05":{"declaration":"outline-color: var(--palette_e_05); --outline_color: var(--palette_e_05);"},"outline_e_10":{"declaration":"outline-color: var(--palette_e_10); --outline_color: var(--palette_e_10);"},"outline_e_20":{"declaration":"outline-color: var(--palette_e_20); --outline_color: var(--palette_e_20);"},"outline_e_30":{"declaration":"outline-color: var(--palette_e_30); --outline_color: var(--palette_e_30);"},"outline_e_40":{"declaration":"outline-color: var(--palette_e_40); --outline_color: var(--palette_e_40);"},"outline_e_50":{"declaration":"outline-color: var(--palette_e_50); --outline_color: var(--palette_e_50);"},"outline_e_60":{"declaration":"outline-color: var(--palette_e_60); --outline_color: var(--palette_e_60);"},"outline_e_70":{"declaration":"outline-color: var(--palette_e_70); --outline_color: var(--palette_e_70);"},"outline_e_80":{"declaration":"outline-color: var(--palette_e_80); --outline_color: var(--palette_e_80);"},"outline_e_90":{"declaration":"outline-color: var(--palette_e_90); --outline_color: var(--palette_e_90);"},"outline_e_95":{"declaration":"outline-color: var(--palette_e_95); --outline_color: var(--palette_e_95);"},"outline_e_100":{"declaration":"outline-color: var(--palette_e_100); --outline_color: var(--palette_e_100);"},"outline_f_00":{"declaration":"outline-color: var(--palette_f_00); --outline_color: var(--palette_f_00);"},"outline_f_05":{"declaration":"outline-color: var(--palette_f_05); --outline_color: var(--palette_f_05);"},"outline_f_10":{"declaration":"outline-color: var(--palette_f_10); --outline_color: var(--palette_f_10);"},"outline_f_20":{"declaration":"outline-color: var(--palette_f_20); --outline_color: var(--palette_f_20);"},"outline_f_30":{"declaration":"outline-color: var(--palette_f_30); --outline_color: var(--palette_f_30);"},"outline_f_40":{"declaration":"outline-color: var(--palette_f_40); --outline_color: var(--palette_f_40);"},"outline_f_50":{"declaration":"outline-color: var(--palette_f_50); --outline_color: var(--palette_f_50);"},"outline_f_60":{"declaration":"outline-color: var(--palette_f_60); --outline_color: var(--palette_f_60);"},"outline_f_70":{"declaration":"outline-color: var(--palette_f_70); --outline_color: var(--palette_f_70);"},"outline_f_80":{"declaration":"outline-color: var(--palette_f_80); --outline_color: var(--palette_f_80);"},"outline_f_90":{"declaration":"outline-color: var(--palette_f_90); --outline_color: var(--palette_f_90);"},"outline_f_95":{"declaration":"outline-color: var(--palette_f_95); --outline_color: var(--palette_f_95);"},"outline_f_100":{"declaration":"outline-color: var(--palette_f_100); --outline_color: var(--palette_f_100);"},"outline_g_00":{"declaration":"outline-color: var(--palette_g_00); --outline_color: var(--palette_g_00);"},"outline_g_05":{"declaration":"outline-color: var(--palette_g_05); --outline_color: var(--palette_g_05);"},"outline_g_10":{"declaration":"outline-color: var(--palette_g_10); --outline_color: var(--palette_g_10);"},"outline_g_20":{"declaration":"outline-color: var(--palette_g_20); --outline_color: var(--palette_g_20);"},"outline_g_30":{"declaration":"outline-color: var(--palette_g_30); --outline_color: var(--palette_g_30);"},"outline_g_40":{"declaration":"outline-color: var(--palette_g_40); --outline_color: var(--palette_g_40);"},"outline_g_50":{"declaration":"outline-color: var(--palette_g_50); --outline_color: var(--palette_g_50);"},"outline_g_60":{"declaration":"outline-color: var(--palette_g_60); --outline_color: var(--palette_g_60);"},"outline_g_70":{"declaration":"outline-color: var(--palette_g_70); --outline_color: var(--palette_g_70);"},"outline_g_80":{"declaration":"outline-color: var(--palette_g_80); --outline_color: var(--palette_g_80);"},"outline_g_90":{"declaration":"outline-color: var(--palette_g_90); --outline_color: var(--palette_g_90);"},"outline_g_95":{"declaration":"outline-color: var(--palette_g_95); --outline_color: var(--palette_g_95);"},"outline_g_100":{"declaration":"outline-color: var(--palette_g_100); --outline_color: var(--palette_g_100);"},"outline_h_00":{"declaration":"outline-color: var(--palette_h_00); --outline_color: var(--palette_h_00);"},"outline_h_05":{"declaration":"outline-color: var(--palette_h_05); --outline_color: var(--palette_h_05);"},"outline_h_10":{"declaration":"outline-color: var(--palette_h_10); --outline_color: var(--palette_h_10);"},"outline_h_20":{"declaration":"outline-color: var(--palette_h_20); --outline_color: var(--palette_h_20);"},"outline_h_30":{"declaration":"outline-color: var(--palette_h_30); --outline_color: var(--palette_h_30);"},"outline_h_40":{"declaration":"outline-color: var(--palette_h_40); --outline_color: var(--palette_h_40);"},"outline_h_50":{"declaration":"outline-color: var(--palette_h_50); --outline_color: var(--palette_h_50);"},"outline_h_60":{"declaration":"outline-color: var(--palette_h_60); --outline_color: var(--palette_h_60);"},"outline_h_70":{"declaration":"outline-color: var(--palette_h_70); --outline_color: var(--palette_h_70);"},"outline_h_80":{"declaration":"outline-color: var(--palette_h_80); --outline_color: var(--palette_h_80);"},"outline_h_90":{"declaration":"outline-color: var(--palette_h_90); --outline_color: var(--palette_h_90);"},"outline_h_95":{"declaration":"outline-color: var(--palette_h_95); --outline_color: var(--palette_h_95);"},"outline_h_100":{"declaration":"outline-color: var(--palette_h_100); --outline_color: var(--palette_h_100);"},"outline_i_00":{"declaration":"outline-color: var(--palette_i_00); --outline_color: var(--palette_i_00);"},"outline_i_05":{"declaration":"outline-color: var(--palette_i_05); --outline_color: var(--palette_i_05);"},"outline_i_10":{"declaration":"outline-color: var(--palette_i_10); --outline_color: var(--palette_i_10);"},"outline_i_20":{"declaration":"outline-color: var(--palette_i_20); --outline_color: var(--palette_i_20);"},"outline_i_30":{"declaration":"outline-color: var(--palette_i_30); --outline_color: var(--palette_i_30);"},"outline_i_40":{"declaration":"outline-color: var(--palette_i_40); --outline_color: var(--palette_i_40);"},"outline_i_50":{"declaration":"outline-color: var(--palette_i_50); --outline_color: var(--palette_i_50);"},"outline_i_60":{"declaration":"outline-color: var(--palette_i_60); --outline_color: var(--palette_i_60);"},"outline_i_70":{"declaration":"outline-color: var(--palette_i_70); --outline_color: var(--palette_i_70);"},"outline_i_80":{"declaration":"outline-color: var(--palette_i_80); --outline_color: var(--palette_i_80);"},"outline_i_90":{"declaration":"outline-color: var(--palette_i_90); --outline_color: var(--palette_i_90);"},"outline_i_95":{"declaration":"outline-color: var(--palette_i_95); --outline_color: var(--palette_i_95);"},"outline_i_100":{"declaration":"outline-color: var(--palette_i_100); --outline_color: var(--palette_i_100);"},"outline_j_00":{"declaration":"outline-color: var(--palette_j_00); --outline_color: var(--palette_j_00);"},"outline_j_05":{"declaration":"outline-color: var(--palette_j_05); --outline_color: var(--palette_j_05);"},"outline_j_10":{"declaration":"outline-color: var(--palette_j_10); --outline_color: var(--palette_j_10);"},"outline_j_20":{"declaration":"outline-color: var(--palette_j_20); --outline_color: var(--palette_j_20);"},"outline_j_30":{"declaration":"outline-color: var(--palette_j_30); --outline_color: var(--palette_j_30);"},"outline_j_40":{"declaration":"outline-color: var(--palette_j_40); --outline_color: var(--palette_j_40);"},"outline_j_50":{"declaration":"outline-color: var(--palette_j_50); --outline_color: var(--palette_j_50);"},"outline_j_60":{"declaration":"outline-color: var(--palette_j_60); --outline_color: var(--palette_j_60);"},"outline_j_70":{"declaration":"outline-color: var(--palette_j_70); --outline_color: var(--palette_j_70);"},"outline_j_80":{"declaration":"outline-color: var(--palette_j_80); --outline_color: var(--palette_j_80);"},"outline_j_90":{"declaration":"outline-color: var(--palette_j_90); --outline_color: var(--palette_j_90);"},"outline_j_95":{"declaration":"outline-color: var(--palette_j_95); --outline_color: var(--palette_j_95);"},"outline_j_100":{"declaration":"outline-color: var(--palette_j_100); --outline_color: var(--palette_j_100);"},"border_width_1":{"declaration":"border-width: var(--border_width_1);"},"border_width_2":{"declaration":"border-width: var(--border_width_2);"},"border_width_3":{"declaration":"border-width: var(--border_width_3);"},"border_width_4":{"declaration":"border-width: var(--border_width_4);"},"border_width_5":{"declaration":"border-width: var(--border_width_5);"},"border_width_6":{"declaration":"border-width: var(--border_width_6);"},"border_width_7":{"declaration":"border-width: var(--border_width_7);"},"border_width_8":{"declaration":"border-width: var(--border_width_8);"},"border_width_9":{"declaration":"border-width: var(--border_width_9);"},"outline_width_1":{"declaration":"outline-width: var(--border_width_1);"},"outline_width_2":{"declaration":"outline-width: var(--border_width_2);"},"outline_width_3":{"declaration":"outline-width: var(--border_width_3);"},"outline_width_4":{"declaration":"outline-width: var(--border_width_4);"},"outline_width_5":{"declaration":"outline-width: var(--border_width_5);"},"outline_width_6":{"declaration":"outline-width: var(--border_width_6);"},"outline_width_7":{"declaration":"outline-width: var(--border_width_7);"},"outline_width_8":{"declaration":"outline-width: var(--border_width_8);"},"outline_width_9":{"declaration":"outline-width: var(--border_width_9);"},"outline_width_focus":{"declaration":"outline-width: var(--outline_width_focus);"},"outline_width_active":{"declaration":"outline-width: var(--outline_width_active);"},"border_radius_xs3":{"declaration":"border-radius: var(--border_radius_xs3);"},"border_radius_xs2":{"declaration":"border-radius: var(--border_radius_xs2);"},"border_radius_xs":{"declaration":"border-radius: var(--border_radius_xs);"},"border_radius_sm":{"declaration":"border-radius: var(--border_radius_sm);"},"border_radius_md":{"declaration":"border-radius: var(--border_radius_md);"},"border_radius_lg":{"declaration":"border-radius: var(--border_radius_lg);"},"border_radius_xl":{"declaration":"border-radius: var(--border_radius_xl);"},"border_top_left_radius_xs3":{"declaration":"border-top-left-radius: var(--border_radius_xs3);"},"border_top_left_radius_xs2":{"declaration":"border-top-left-radius: var(--border_radius_xs2);"},"border_top_left_radius_xs":{"declaration":"border-top-left-radius: var(--border_radius_xs);"},"border_top_left_radius_sm":{"declaration":"border-top-left-radius: var(--border_radius_sm);"},"border_top_left_radius_md":{"declaration":"border-top-left-radius: var(--border_radius_md);"},"border_top_left_radius_lg":{"declaration":"border-top-left-radius: var(--border_radius_lg);"},"border_top_left_radius_xl":{"declaration":"border-top-left-radius: var(--border_radius_xl);"},"border_top_right_radius_xs3":{"declaration":"border-top-right-radius: var(--border_radius_xs3);"},"border_top_right_radius_xs2":{"declaration":"border-top-right-radius: var(--border_radius_xs2);"},"border_top_right_radius_xs":{"declaration":"border-top-right-radius: var(--border_radius_xs);"},"border_top_right_radius_sm":{"declaration":"border-top-right-radius: var(--border_radius_sm);"},"border_top_right_radius_md":{"declaration":"border-top-right-radius: var(--border_radius_md);"},"border_top_right_radius_lg":{"declaration":"border-top-right-radius: var(--border_radius_lg);"},"border_top_right_radius_xl":{"declaration":"border-top-right-radius: var(--border_radius_xl);"},"border_bottom_left_radius_xs3":{"declaration":"border-bottom-left-radius: var(--border_radius_xs3);"},"border_bottom_left_radius_xs2":{"declaration":"border-bottom-left-radius: var(--border_radius_xs2);"},"border_bottom_left_radius_xs":{"declaration":"border-bottom-left-radius: var(--border_radius_xs);"},"border_bottom_left_radius_sm":{"declaration":"border-bottom-left-radius: var(--border_radius_sm);"},"border_bottom_left_radius_md":{"declaration":"border-bottom-left-radius: var(--border_radius_md);"},"border_bottom_left_radius_lg":{"declaration":"border-bottom-left-radius: var(--border_radius_lg);"},"border_bottom_left_radius_xl":{"declaration":"border-bottom-left-radius: var(--border_radius_xl);"},"border_bottom_right_radius_xs3":{"declaration":"border-bottom-right-radius: var(--border_radius_xs3);"},"border_bottom_right_radius_xs2":{"declaration":"border-bottom-right-radius: var(--border_radius_xs2);"},"border_bottom_right_radius_xs":{"declaration":"border-bottom-right-radius: var(--border_radius_xs);"},"border_bottom_right_radius_sm":{"declaration":"border-bottom-right-radius: var(--border_radius_sm);"},"border_bottom_right_radius_md":{"declaration":"border-bottom-right-radius: var(--border_radius_md);"},"border_bottom_right_radius_lg":{"declaration":"border-bottom-right-radius: var(--border_radius_lg);"},"border_bottom_right_radius_xl":{"declaration":"border-bottom-right-radius: var(--border_radius_xl);"},"shadow_xs":{"declaration":"box-shadow: var(--shadow_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_sm":{"declaration":"box-shadow: var(--shadow_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_md":{"declaration":"box-shadow: var(--shadow_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_lg":{"declaration":"box-shadow: var(--shadow_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_xl":{"declaration":"box-shadow: var(--shadow_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_top_xs":{"declaration":"box-shadow: var(--shadow_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_top_sm":{"declaration":"box-shadow: var(--shadow_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_top_md":{"declaration":"box-shadow: var(--shadow_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_top_lg":{"declaration":"box-shadow: var(--shadow_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_top_xl":{"declaration":"box-shadow: var(--shadow_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_bottom_xs":{"declaration":"box-shadow: var(--shadow_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_bottom_sm":{"declaration":"box-shadow: var(--shadow_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_bottom_md":{"declaration":"box-shadow: var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_bottom_lg":{"declaration":"box-shadow: var(--shadow_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_bottom_xl":{"declaration":"box-shadow: var(--shadow_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_inset_xs":{"declaration":"box-shadow: var(--shadow_inset_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_inset_sm":{"declaration":"box-shadow: var(--shadow_inset_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_inset_md":{"declaration":"box-shadow: var(--shadow_inset_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_inset_lg":{"declaration":"box-shadow: var(--shadow_inset_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_inset_xl":{"declaration":"box-shadow: var(--shadow_inset_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_inset_top_xs":{"declaration":"box-shadow: var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_inset_top_sm":{"declaration":"box-shadow: var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_inset_top_md":{"declaration":"box-shadow: var(--shadow_inset_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_inset_top_lg":{"declaration":"box-shadow: var(--shadow_inset_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_inset_top_xl":{"declaration":"box-shadow: var(--shadow_inset_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_inset_bottom_xs":{"declaration":"box-shadow: var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_inset_bottom_sm":{"declaration":"box-shadow: var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_inset_bottom_md":{"declaration":"box-shadow: var(--shadow_inset_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_inset_bottom_lg":{"declaration":"box-shadow: var(--shadow_inset_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_inset_bottom_xl":{"declaration":"box-shadow: var(--shadow_inset_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_color_umbra":{"declaration":"--shadow_color: var(--shadow_color_umbra);"},"shadow_color_highlight":{"declaration":"--shadow_color: var(--shadow_color_highlight);"},"shadow_color_glow":{"declaration":"--shadow_color: var(--shadow_color_glow);"},"shadow_color_shroud":{"declaration":"--shadow_color: var(--shadow_color_shroud);"},"shadow_alpha_00":{"declaration":"--shadow_alpha: var(--shadow_alpha_00);"},"shadow_alpha_05":{"declaration":"--shadow_alpha: var(--shadow_alpha_05);"},"shadow_alpha_10":{"declaration":"--shadow_alpha: var(--shadow_alpha_10);"},"shadow_alpha_20":{"declaration":"--shadow_alpha: var(--shadow_alpha_20);"},"shadow_alpha_30":{"declaration":"--shadow_alpha: var(--shadow_alpha_30);"},"shadow_alpha_40":{"declaration":"--shadow_alpha: var(--shadow_alpha_40);"},"shadow_alpha_50":{"declaration":"--shadow_alpha: var(--shadow_alpha_50);"},"shadow_alpha_60":{"declaration":"--shadow_alpha: var(--shadow_alpha_60);"},"shadow_alpha_70":{"declaration":"--shadow_alpha: var(--shadow_alpha_70);"},"shadow_alpha_80":{"declaration":"--shadow_alpha: var(--shadow_alpha_80);"},"shadow_alpha_90":{"declaration":"--shadow_alpha: var(--shadow_alpha_90);"},"shadow_alpha_95":{"declaration":"--shadow_alpha: var(--shadow_alpha_95);"},"shadow_alpha_100":{"declaration":"--shadow_alpha: var(--shadow_alpha_100);"},"shadow_a_00":{"declaration":"--shadow_color: var(--palette_a_00);"},"shadow_a_05":{"declaration":"--shadow_color: var(--palette_a_05);"},"shadow_a_10":{"declaration":"--shadow_color: var(--palette_a_10);"},"shadow_a_20":{"declaration":"--shadow_color: var(--palette_a_20);"},"shadow_a_30":{"declaration":"--shadow_color: var(--palette_a_30);"},"shadow_a_40":{"declaration":"--shadow_color: var(--palette_a_40);"},"shadow_a_50":{"declaration":"--shadow_color: var(--palette_a_50);"},"shadow_a_60":{"declaration":"--shadow_color: var(--palette_a_60);"},"shadow_a_70":{"declaration":"--shadow_color: var(--palette_a_70);"},"shadow_a_80":{"declaration":"--shadow_color: var(--palette_a_80);"},"shadow_a_90":{"declaration":"--shadow_color: var(--palette_a_90);"},"shadow_a_95":{"declaration":"--shadow_color: var(--palette_a_95);"},"shadow_a_100":{"declaration":"--shadow_color: var(--palette_a_100);"},"shadow_b_00":{"declaration":"--shadow_color: var(--palette_b_00);"},"shadow_b_05":{"declaration":"--shadow_color: var(--palette_b_05);"},"shadow_b_10":{"declaration":"--shadow_color: var(--palette_b_10);"},"shadow_b_20":{"declaration":"--shadow_color: var(--palette_b_20);"},"shadow_b_30":{"declaration":"--shadow_color: var(--palette_b_30);"},"shadow_b_40":{"declaration":"--shadow_color: var(--palette_b_40);"},"shadow_b_50":{"declaration":"--shadow_color: var(--palette_b_50);"},"shadow_b_60":{"declaration":"--shadow_color: var(--palette_b_60);"},"shadow_b_70":{"declaration":"--shadow_color: var(--palette_b_70);"},"shadow_b_80":{"declaration":"--shadow_color: var(--palette_b_80);"},"shadow_b_90":{"declaration":"--shadow_color: var(--palette_b_90);"},"shadow_b_95":{"declaration":"--shadow_color: var(--palette_b_95);"},"shadow_b_100":{"declaration":"--shadow_color: var(--palette_b_100);"},"shadow_c_00":{"declaration":"--shadow_color: var(--palette_c_00);"},"shadow_c_05":{"declaration":"--shadow_color: var(--palette_c_05);"},"shadow_c_10":{"declaration":"--shadow_color: var(--palette_c_10);"},"shadow_c_20":{"declaration":"--shadow_color: var(--palette_c_20);"},"shadow_c_30":{"declaration":"--shadow_color: var(--palette_c_30);"},"shadow_c_40":{"declaration":"--shadow_color: var(--palette_c_40);"},"shadow_c_50":{"declaration":"--shadow_color: var(--palette_c_50);"},"shadow_c_60":{"declaration":"--shadow_color: var(--palette_c_60);"},"shadow_c_70":{"declaration":"--shadow_color: var(--palette_c_70);"},"shadow_c_80":{"declaration":"--shadow_color: var(--palette_c_80);"},"shadow_c_90":{"declaration":"--shadow_color: var(--palette_c_90);"},"shadow_c_95":{"declaration":"--shadow_color: var(--palette_c_95);"},"shadow_c_100":{"declaration":"--shadow_color: var(--palette_c_100);"},"shadow_d_00":{"declaration":"--shadow_color: var(--palette_d_00);"},"shadow_d_05":{"declaration":"--shadow_color: var(--palette_d_05);"},"shadow_d_10":{"declaration":"--shadow_color: var(--palette_d_10);"},"shadow_d_20":{"declaration":"--shadow_color: var(--palette_d_20);"},"shadow_d_30":{"declaration":"--shadow_color: var(--palette_d_30);"},"shadow_d_40":{"declaration":"--shadow_color: var(--palette_d_40);"},"shadow_d_50":{"declaration":"--shadow_color: var(--palette_d_50);"},"shadow_d_60":{"declaration":"--shadow_color: var(--palette_d_60);"},"shadow_d_70":{"declaration":"--shadow_color: var(--palette_d_70);"},"shadow_d_80":{"declaration":"--shadow_color: var(--palette_d_80);"},"shadow_d_90":{"declaration":"--shadow_color: var(--palette_d_90);"},"shadow_d_95":{"declaration":"--shadow_color: var(--palette_d_95);"},"shadow_d_100":{"declaration":"--shadow_color: var(--palette_d_100);"},"shadow_e_00":{"declaration":"--shadow_color: var(--palette_e_00);"},"shadow_e_05":{"declaration":"--shadow_color: var(--palette_e_05);"},"shadow_e_10":{"declaration":"--shadow_color: var(--palette_e_10);"},"shadow_e_20":{"declaration":"--shadow_color: var(--palette_e_20);"},"shadow_e_30":{"declaration":"--shadow_color: var(--palette_e_30);"},"shadow_e_40":{"declaration":"--shadow_color: var(--palette_e_40);"},"shadow_e_50":{"declaration":"--shadow_color: var(--palette_e_50);"},"shadow_e_60":{"declaration":"--shadow_color: var(--palette_e_60);"},"shadow_e_70":{"declaration":"--shadow_color: var(--palette_e_70);"},"shadow_e_80":{"declaration":"--shadow_color: var(--palette_e_80);"},"shadow_e_90":{"declaration":"--shadow_color: var(--palette_e_90);"},"shadow_e_95":{"declaration":"--shadow_color: var(--palette_e_95);"},"shadow_e_100":{"declaration":"--shadow_color: var(--palette_e_100);"},"shadow_f_00":{"declaration":"--shadow_color: var(--palette_f_00);"},"shadow_f_05":{"declaration":"--shadow_color: var(--palette_f_05);"},"shadow_f_10":{"declaration":"--shadow_color: var(--palette_f_10);"},"shadow_f_20":{"declaration":"--shadow_color: var(--palette_f_20);"},"shadow_f_30":{"declaration":"--shadow_color: var(--palette_f_30);"},"shadow_f_40":{"declaration":"--shadow_color: var(--palette_f_40);"},"shadow_f_50":{"declaration":"--shadow_color: var(--palette_f_50);"},"shadow_f_60":{"declaration":"--shadow_color: var(--palette_f_60);"},"shadow_f_70":{"declaration":"--shadow_color: var(--palette_f_70);"},"shadow_f_80":{"declaration":"--shadow_color: var(--palette_f_80);"},"shadow_f_90":{"declaration":"--shadow_color: var(--palette_f_90);"},"shadow_f_95":{"declaration":"--shadow_color: var(--palette_f_95);"},"shadow_f_100":{"declaration":"--shadow_color: var(--palette_f_100);"},"shadow_g_00":{"declaration":"--shadow_color: var(--palette_g_00);"},"shadow_g_05":{"declaration":"--shadow_color: var(--palette_g_05);"},"shadow_g_10":{"declaration":"--shadow_color: var(--palette_g_10);"},"shadow_g_20":{"declaration":"--shadow_color: var(--palette_g_20);"},"shadow_g_30":{"declaration":"--shadow_color: var(--palette_g_30);"},"shadow_g_40":{"declaration":"--shadow_color: var(--palette_g_40);"},"shadow_g_50":{"declaration":"--shadow_color: var(--palette_g_50);"},"shadow_g_60":{"declaration":"--shadow_color: var(--palette_g_60);"},"shadow_g_70":{"declaration":"--shadow_color: var(--palette_g_70);"},"shadow_g_80":{"declaration":"--shadow_color: var(--palette_g_80);"},"shadow_g_90":{"declaration":"--shadow_color: var(--palette_g_90);"},"shadow_g_95":{"declaration":"--shadow_color: var(--palette_g_95);"},"shadow_g_100":{"declaration":"--shadow_color: var(--palette_g_100);"},"shadow_h_00":{"declaration":"--shadow_color: var(--palette_h_00);"},"shadow_h_05":{"declaration":"--shadow_color: var(--palette_h_05);"},"shadow_h_10":{"declaration":"--shadow_color: var(--palette_h_10);"},"shadow_h_20":{"declaration":"--shadow_color: var(--palette_h_20);"},"shadow_h_30":{"declaration":"--shadow_color: var(--palette_h_30);"},"shadow_h_40":{"declaration":"--shadow_color: var(--palette_h_40);"},"shadow_h_50":{"declaration":"--shadow_color: var(--palette_h_50);"},"shadow_h_60":{"declaration":"--shadow_color: var(--palette_h_60);"},"shadow_h_70":{"declaration":"--shadow_color: var(--palette_h_70);"},"shadow_h_80":{"declaration":"--shadow_color: var(--palette_h_80);"},"shadow_h_90":{"declaration":"--shadow_color: var(--palette_h_90);"},"shadow_h_95":{"declaration":"--shadow_color: var(--palette_h_95);"},"shadow_h_100":{"declaration":"--shadow_color: var(--palette_h_100);"},"shadow_i_00":{"declaration":"--shadow_color: var(--palette_i_00);"},"shadow_i_05":{"declaration":"--shadow_color: var(--palette_i_05);"},"shadow_i_10":{"declaration":"--shadow_color: var(--palette_i_10);"},"shadow_i_20":{"declaration":"--shadow_color: var(--palette_i_20);"},"shadow_i_30":{"declaration":"--shadow_color: var(--palette_i_30);"},"shadow_i_40":{"declaration":"--shadow_color: var(--palette_i_40);"},"shadow_i_50":{"declaration":"--shadow_color: var(--palette_i_50);"},"shadow_i_60":{"declaration":"--shadow_color: var(--palette_i_60);"},"shadow_i_70":{"declaration":"--shadow_color: var(--palette_i_70);"},"shadow_i_80":{"declaration":"--shadow_color: var(--palette_i_80);"},"shadow_i_90":{"declaration":"--shadow_color: var(--palette_i_90);"},"shadow_i_95":{"declaration":"--shadow_color: var(--palette_i_95);"},"shadow_i_100":{"declaration":"--shadow_color: var(--palette_i_100);"},"shadow_j_00":{"declaration":"--shadow_color: var(--palette_j_00);"},"shadow_j_05":{"declaration":"--shadow_color: var(--palette_j_05);"},"shadow_j_10":{"declaration":"--shadow_color: var(--palette_j_10);"},"shadow_j_20":{"declaration":"--shadow_color: var(--palette_j_20);"},"shadow_j_30":{"declaration":"--shadow_color: var(--palette_j_30);"},"shadow_j_40":{"declaration":"--shadow_color: var(--palette_j_40);"},"shadow_j_50":{"declaration":"--shadow_color: var(--palette_j_50);"},"shadow_j_60":{"declaration":"--shadow_color: var(--palette_j_60);"},"shadow_j_70":{"declaration":"--shadow_color: var(--palette_j_70);"},"shadow_j_80":{"declaration":"--shadow_color: var(--palette_j_80);"},"shadow_j_90":{"declaration":"--shadow_color: var(--palette_j_90);"},"shadow_j_95":{"declaration":"--shadow_color: var(--palette_j_95);"},"shadow_j_100":{"declaration":"--shadow_color: var(--palette_j_100);"},"width_xs5":{"declaration":"width: var(--space_xs5);"},"width_xs4":{"declaration":"width: var(--space_xs4);"},"width_xs3":{"declaration":"width: var(--space_xs3);"},"width_xs2":{"declaration":"width: var(--space_xs2);"},"width_xs":{"declaration":"width: var(--space_xs);"},"width_sm":{"declaration":"width: var(--space_sm);"},"width_md":{"declaration":"width: var(--space_md);"},"width_lg":{"declaration":"width: var(--space_lg);"},"width_xl":{"declaration":"width: var(--space_xl);"},"width_xl2":{"declaration":"width: var(--space_xl2);"},"width_xl3":{"declaration":"width: var(--space_xl3);"},"width_xl4":{"declaration":"width: var(--space_xl4);"},"width_xl5":{"declaration":"width: var(--space_xl5);"},"width_xl6":{"declaration":"width: var(--space_xl6);"},"width_xl7":{"declaration":"width: var(--space_xl7);"},"width_xl8":{"declaration":"width: var(--space_xl8);"},"width_xl9":{"declaration":"width: var(--space_xl9);"},"width_xl10":{"declaration":"width: var(--space_xl10);"},"width_xl11":{"declaration":"width: var(--space_xl11);"},"width_xl12":{"declaration":"width: var(--space_xl12);"},"width_xl13":{"declaration":"width: var(--space_xl13);"},"width_xl14":{"declaration":"width: var(--space_xl14);"},"width_xl15":{"declaration":"width: var(--space_xl15);"},"height_xs5":{"declaration":"height: var(--space_xs5);"},"height_xs4":{"declaration":"height: var(--space_xs4);"},"height_xs3":{"declaration":"height: var(--space_xs3);"},"height_xs2":{"declaration":"height: var(--space_xs2);"},"height_xs":{"declaration":"height: var(--space_xs);"},"height_sm":{"declaration":"height: var(--space_sm);"},"height_md":{"declaration":"height: var(--space_md);"},"height_lg":{"declaration":"height: var(--space_lg);"},"height_xl":{"declaration":"height: var(--space_xl);"},"height_xl2":{"declaration":"height: var(--space_xl2);"},"height_xl3":{"declaration":"height: var(--space_xl3);"},"height_xl4":{"declaration":"height: var(--space_xl4);"},"height_xl5":{"declaration":"height: var(--space_xl5);"},"height_xl6":{"declaration":"height: var(--space_xl6);"},"height_xl7":{"declaration":"height: var(--space_xl7);"},"height_xl8":{"declaration":"height: var(--space_xl8);"},"height_xl9":{"declaration":"height: var(--space_xl9);"},"height_xl10":{"declaration":"height: var(--space_xl10);"},"height_xl11":{"declaration":"height: var(--space_xl11);"},"height_xl12":{"declaration":"height: var(--space_xl12);"},"height_xl13":{"declaration":"height: var(--space_xl13);"},"height_xl14":{"declaration":"height: var(--space_xl14);"},"height_xl15":{"declaration":"height: var(--space_xl15);"},"width_atmost_xs":{"declaration":"width: 100%; max-width: var(--distance_xs);"},"width_atmost_sm":{"declaration":"width: 100%; max-width: var(--distance_sm);"},"width_atmost_md":{"declaration":"width: 100%; max-width: var(--distance_md);"},"width_atmost_lg":{"declaration":"width: 100%; max-width: var(--distance_lg);"},"width_atmost_xl":{"declaration":"width: 100%; max-width: var(--distance_xl);"},"width_atleast_xs":{"declaration":"width: 100%; min-width: var(--distance_xs);"},"width_atleast_sm":{"declaration":"width: 100%; min-width: var(--distance_sm);"},"width_atleast_md":{"declaration":"width: 100%; min-width: var(--distance_md);"},"width_atleast_lg":{"declaration":"width: 100%; min-width: var(--distance_lg);"},"width_atleast_xl":{"declaration":"width: 100%; min-width: var(--distance_xl);"},"height_atmost_xs":{"declaration":"height: 100%; max-height: var(--distance_xs);"},"height_atmost_sm":{"declaration":"height: 100%; max-height: var(--distance_sm);"},"height_atmost_md":{"declaration":"height: 100%; max-height: var(--distance_md);"},"height_atmost_lg":{"declaration":"height: 100%; max-height: var(--distance_lg);"},"height_atmost_xl":{"declaration":"height: 100%; max-height: var(--distance_xl);"},"height_atleast_xs":{"declaration":"height: 100%; min-height: var(--distance_xs);"},"height_atleast_sm":{"declaration":"height: 100%; min-height: var(--distance_sm);"},"height_atleast_md":{"declaration":"height: 100%; min-height: var(--distance_md);"},"height_atleast_lg":{"declaration":"height: 100%; min-height: var(--distance_lg);"},"height_atleast_xl":{"declaration":"height: 100%; min-height: var(--distance_xl);"},"top_xs5":{"declaration":"top: var(--space_xs5);"},"top_xs4":{"declaration":"top: var(--space_xs4);"},"top_xs3":{"declaration":"top: var(--space_xs3);"},"top_xs2":{"declaration":"top: var(--space_xs2);"},"top_xs":{"declaration":"top: var(--space_xs);"},"top_sm":{"declaration":"top: var(--space_sm);"},"top_md":{"declaration":"top: var(--space_md);"},"top_lg":{"declaration":"top: var(--space_lg);"},"top_xl":{"declaration":"top: var(--space_xl);"},"top_xl2":{"declaration":"top: var(--space_xl2);"},"top_xl3":{"declaration":"top: var(--space_xl3);"},"top_xl4":{"declaration":"top: var(--space_xl4);"},"top_xl5":{"declaration":"top: var(--space_xl5);"},"top_xl6":{"declaration":"top: var(--space_xl6);"},"top_xl7":{"declaration":"top: var(--space_xl7);"},"top_xl8":{"declaration":"top: var(--space_xl8);"},"top_xl9":{"declaration":"top: var(--space_xl9);"},"top_xl10":{"declaration":"top: var(--space_xl10);"},"top_xl11":{"declaration":"top: var(--space_xl11);"},"top_xl12":{"declaration":"top: var(--space_xl12);"},"top_xl13":{"declaration":"top: var(--space_xl13);"},"top_xl14":{"declaration":"top: var(--space_xl14);"},"top_xl15":{"declaration":"top: var(--space_xl15);"},"right_xs5":{"declaration":"right: var(--space_xs5);"},"right_xs4":{"declaration":"right: var(--space_xs4);"},"right_xs3":{"declaration":"right: var(--space_xs3);"},"right_xs2":{"declaration":"right: var(--space_xs2);"},"right_xs":{"declaration":"right: var(--space_xs);"},"right_sm":{"declaration":"right: var(--space_sm);"},"right_md":{"declaration":"right: var(--space_md);"},"right_lg":{"declaration":"right: var(--space_lg);"},"right_xl":{"declaration":"right: var(--space_xl);"},"right_xl2":{"declaration":"right: var(--space_xl2);"},"right_xl3":{"declaration":"right: var(--space_xl3);"},"right_xl4":{"declaration":"right: var(--space_xl4);"},"right_xl5":{"declaration":"right: var(--space_xl5);"},"right_xl6":{"declaration":"right: var(--space_xl6);"},"right_xl7":{"declaration":"right: var(--space_xl7);"},"right_xl8":{"declaration":"right: var(--space_xl8);"},"right_xl9":{"declaration":"right: var(--space_xl9);"},"right_xl10":{"declaration":"right: var(--space_xl10);"},"right_xl11":{"declaration":"right: var(--space_xl11);"},"right_xl12":{"declaration":"right: var(--space_xl12);"},"right_xl13":{"declaration":"right: var(--space_xl13);"},"right_xl14":{"declaration":"right: var(--space_xl14);"},"right_xl15":{"declaration":"right: var(--space_xl15);"},"bottom_xs5":{"declaration":"bottom: var(--space_xs5);"},"bottom_xs4":{"declaration":"bottom: var(--space_xs4);"},"bottom_xs3":{"declaration":"bottom: var(--space_xs3);"},"bottom_xs2":{"declaration":"bottom: var(--space_xs2);"},"bottom_xs":{"declaration":"bottom: var(--space_xs);"},"bottom_sm":{"declaration":"bottom: var(--space_sm);"},"bottom_md":{"declaration":"bottom: var(--space_md);"},"bottom_lg":{"declaration":"bottom: var(--space_lg);"},"bottom_xl":{"declaration":"bottom: var(--space_xl);"},"bottom_xl2":{"declaration":"bottom: var(--space_xl2);"},"bottom_xl3":{"declaration":"bottom: var(--space_xl3);"},"bottom_xl4":{"declaration":"bottom: var(--space_xl4);"},"bottom_xl5":{"declaration":"bottom: var(--space_xl5);"},"bottom_xl6":{"declaration":"bottom: var(--space_xl6);"},"bottom_xl7":{"declaration":"bottom: var(--space_xl7);"},"bottom_xl8":{"declaration":"bottom: var(--space_xl8);"},"bottom_xl9":{"declaration":"bottom: var(--space_xl9);"},"bottom_xl10":{"declaration":"bottom: var(--space_xl10);"},"bottom_xl11":{"declaration":"bottom: var(--space_xl11);"},"bottom_xl12":{"declaration":"bottom: var(--space_xl12);"},"bottom_xl13":{"declaration":"bottom: var(--space_xl13);"},"bottom_xl14":{"declaration":"bottom: var(--space_xl14);"},"bottom_xl15":{"declaration":"bottom: var(--space_xl15);"},"left_xs5":{"declaration":"left: var(--space_xs5);"},"left_xs4":{"declaration":"left: var(--space_xs4);"},"left_xs3":{"declaration":"left: var(--space_xs3);"},"left_xs2":{"declaration":"left: var(--space_xs2);"},"left_xs":{"declaration":"left: var(--space_xs);"},"left_sm":{"declaration":"left: var(--space_sm);"},"left_md":{"declaration":"left: var(--space_md);"},"left_lg":{"declaration":"left: var(--space_lg);"},"left_xl":{"declaration":"left: var(--space_xl);"},"left_xl2":{"declaration":"left: var(--space_xl2);"},"left_xl3":{"declaration":"left: var(--space_xl3);"},"left_xl4":{"declaration":"left: var(--space_xl4);"},"left_xl5":{"declaration":"left: var(--space_xl5);"},"left_xl6":{"declaration":"left: var(--space_xl6);"},"left_xl7":{"declaration":"left: var(--space_xl7);"},"left_xl8":{"declaration":"left: var(--space_xl8);"},"left_xl9":{"declaration":"left: var(--space_xl9);"},"left_xl10":{"declaration":"left: var(--space_xl10);"},"left_xl11":{"declaration":"left: var(--space_xl11);"},"left_xl12":{"declaration":"left: var(--space_xl12);"},"left_xl13":{"declaration":"left: var(--space_xl13);"},"left_xl14":{"declaration":"left: var(--space_xl14);"},"left_xl15":{"declaration":"left: var(--space_xl15);"},"inset_xs5":{"declaration":"inset: var(--space_xs5);"},"inset_xs4":{"declaration":"inset: var(--space_xs4);"},"inset_xs3":{"declaration":"inset: var(--space_xs3);"},"inset_xs2":{"declaration":"inset: var(--space_xs2);"},"inset_xs":{"declaration":"inset: var(--space_xs);"},"inset_sm":{"declaration":"inset: var(--space_sm);"},"inset_md":{"declaration":"inset: var(--space_md);"},"inset_lg":{"declaration":"inset: var(--space_lg);"},"inset_xl":{"declaration":"inset: var(--space_xl);"},"inset_xl2":{"declaration":"inset: var(--space_xl2);"},"inset_xl3":{"declaration":"inset: var(--space_xl3);"},"inset_xl4":{"declaration":"inset: var(--space_xl4);"},"inset_xl5":{"declaration":"inset: var(--space_xl5);"},"inset_xl6":{"declaration":"inset: var(--space_xl6);"},"inset_xl7":{"declaration":"inset: var(--space_xl7);"},"inset_xl8":{"declaration":"inset: var(--space_xl8);"},"inset_xl9":{"declaration":"inset: var(--space_xl9);"},"inset_xl10":{"declaration":"inset: var(--space_xl10);"},"inset_xl11":{"declaration":"inset: var(--space_xl11);"},"inset_xl12":{"declaration":"inset: var(--space_xl12);"},"inset_xl13":{"declaration":"inset: var(--space_xl13);"},"inset_xl14":{"declaration":"inset: var(--space_xl14);"},"inset_xl15":{"declaration":"inset: var(--space_xl15);"},"p_0":{"declaration":"padding: 0;"},"p_xs5":{"declaration":"padding: var(--space_xs5);"},"p_xs4":{"declaration":"padding: var(--space_xs4);"},"p_xs3":{"declaration":"padding: var(--space_xs3);"},"p_xs2":{"declaration":"padding: var(--space_xs2);"},"p_xs":{"declaration":"padding: var(--space_xs);"},"p_sm":{"declaration":"padding: var(--space_sm);"},"p_md":{"declaration":"padding: var(--space_md);"},"p_lg":{"declaration":"padding: var(--space_lg);"},"p_xl":{"declaration":"padding: var(--space_xl);"},"p_xl2":{"declaration":"padding: var(--space_xl2);"},"p_xl3":{"declaration":"padding: var(--space_xl3);"},"p_xl4":{"declaration":"padding: var(--space_xl4);"},"p_xl5":{"declaration":"padding: var(--space_xl5);"},"p_xl6":{"declaration":"padding: var(--space_xl6);"},"p_xl7":{"declaration":"padding: var(--space_xl7);"},"p_xl8":{"declaration":"padding: var(--space_xl8);"},"p_xl9":{"declaration":"padding: var(--space_xl9);"},"p_xl10":{"declaration":"padding: var(--space_xl10);"},"p_xl11":{"declaration":"padding: var(--space_xl11);"},"p_xl12":{"declaration":"padding: var(--space_xl12);"},"p_xl13":{"declaration":"padding: var(--space_xl13);"},"p_xl14":{"declaration":"padding: var(--space_xl14);"},"p_xl15":{"declaration":"padding: var(--space_xl15);"},"pt_0":{"declaration":"padding-top: 0;"},"pt_xs5":{"declaration":"padding-top: var(--space_xs5);"},"pt_xs4":{"declaration":"padding-top: var(--space_xs4);"},"pt_xs3":{"declaration":"padding-top: var(--space_xs3);"},"pt_xs2":{"declaration":"padding-top: var(--space_xs2);"},"pt_xs":{"declaration":"padding-top: var(--space_xs);"},"pt_sm":{"declaration":"padding-top: var(--space_sm);"},"pt_md":{"declaration":"padding-top: var(--space_md);"},"pt_lg":{"declaration":"padding-top: var(--space_lg);"},"pt_xl":{"declaration":"padding-top: var(--space_xl);"},"pt_xl2":{"declaration":"padding-top: var(--space_xl2);"},"pt_xl3":{"declaration":"padding-top: var(--space_xl3);"},"pt_xl4":{"declaration":"padding-top: var(--space_xl4);"},"pt_xl5":{"declaration":"padding-top: var(--space_xl5);"},"pt_xl6":{"declaration":"padding-top: var(--space_xl6);"},"pt_xl7":{"declaration":"padding-top: var(--space_xl7);"},"pt_xl8":{"declaration":"padding-top: var(--space_xl8);"},"pt_xl9":{"declaration":"padding-top: var(--space_xl9);"},"pt_xl10":{"declaration":"padding-top: var(--space_xl10);"},"pt_xl11":{"declaration":"padding-top: var(--space_xl11);"},"pt_xl12":{"declaration":"padding-top: var(--space_xl12);"},"pt_xl13":{"declaration":"padding-top: var(--space_xl13);"},"pt_xl14":{"declaration":"padding-top: var(--space_xl14);"},"pt_xl15":{"declaration":"padding-top: var(--space_xl15);"},"pr_0":{"declaration":"padding-right: 0;"},"pr_xs5":{"declaration":"padding-right: var(--space_xs5);"},"pr_xs4":{"declaration":"padding-right: var(--space_xs4);"},"pr_xs3":{"declaration":"padding-right: var(--space_xs3);"},"pr_xs2":{"declaration":"padding-right: var(--space_xs2);"},"pr_xs":{"declaration":"padding-right: var(--space_xs);"},"pr_sm":{"declaration":"padding-right: var(--space_sm);"},"pr_md":{"declaration":"padding-right: var(--space_md);"},"pr_lg":{"declaration":"padding-right: var(--space_lg);"},"pr_xl":{"declaration":"padding-right: var(--space_xl);"},"pr_xl2":{"declaration":"padding-right: var(--space_xl2);"},"pr_xl3":{"declaration":"padding-right: var(--space_xl3);"},"pr_xl4":{"declaration":"padding-right: var(--space_xl4);"},"pr_xl5":{"declaration":"padding-right: var(--space_xl5);"},"pr_xl6":{"declaration":"padding-right: var(--space_xl6);"},"pr_xl7":{"declaration":"padding-right: var(--space_xl7);"},"pr_xl8":{"declaration":"padding-right: var(--space_xl8);"},"pr_xl9":{"declaration":"padding-right: var(--space_xl9);"},"pr_xl10":{"declaration":"padding-right: var(--space_xl10);"},"pr_xl11":{"declaration":"padding-right: var(--space_xl11);"},"pr_xl12":{"declaration":"padding-right: var(--space_xl12);"},"pr_xl13":{"declaration":"padding-right: var(--space_xl13);"},"pr_xl14":{"declaration":"padding-right: var(--space_xl14);"},"pr_xl15":{"declaration":"padding-right: var(--space_xl15);"},"pb_0":{"declaration":"padding-bottom: 0;"},"pb_xs5":{"declaration":"padding-bottom: var(--space_xs5);"},"pb_xs4":{"declaration":"padding-bottom: var(--space_xs4);"},"pb_xs3":{"declaration":"padding-bottom: var(--space_xs3);"},"pb_xs2":{"declaration":"padding-bottom: var(--space_xs2);"},"pb_xs":{"declaration":"padding-bottom: var(--space_xs);"},"pb_sm":{"declaration":"padding-bottom: var(--space_sm);"},"pb_md":{"declaration":"padding-bottom: var(--space_md);"},"pb_lg":{"declaration":"padding-bottom: var(--space_lg);"},"pb_xl":{"declaration":"padding-bottom: var(--space_xl);"},"pb_xl2":{"declaration":"padding-bottom: var(--space_xl2);"},"pb_xl3":{"declaration":"padding-bottom: var(--space_xl3);"},"pb_xl4":{"declaration":"padding-bottom: var(--space_xl4);"},"pb_xl5":{"declaration":"padding-bottom: var(--space_xl5);"},"pb_xl6":{"declaration":"padding-bottom: var(--space_xl6);"},"pb_xl7":{"declaration":"padding-bottom: var(--space_xl7);"},"pb_xl8":{"declaration":"padding-bottom: var(--space_xl8);"},"pb_xl9":{"declaration":"padding-bottom: var(--space_xl9);"},"pb_xl10":{"declaration":"padding-bottom: var(--space_xl10);"},"pb_xl11":{"declaration":"padding-bottom: var(--space_xl11);"},"pb_xl12":{"declaration":"padding-bottom: var(--space_xl12);"},"pb_xl13":{"declaration":"padding-bottom: var(--space_xl13);"},"pb_xl14":{"declaration":"padding-bottom: var(--space_xl14);"},"pb_xl15":{"declaration":"padding-bottom: var(--space_xl15);"},"pl_0":{"declaration":"padding-left: 0;"},"pl_xs5":{"declaration":"padding-left: var(--space_xs5);"},"pl_xs4":{"declaration":"padding-left: var(--space_xs4);"},"pl_xs3":{"declaration":"padding-left: var(--space_xs3);"},"pl_xs2":{"declaration":"padding-left: var(--space_xs2);"},"pl_xs":{"declaration":"padding-left: var(--space_xs);"},"pl_sm":{"declaration":"padding-left: var(--space_sm);"},"pl_md":{"declaration":"padding-left: var(--space_md);"},"pl_lg":{"declaration":"padding-left: var(--space_lg);"},"pl_xl":{"declaration":"padding-left: var(--space_xl);"},"pl_xl2":{"declaration":"padding-left: var(--space_xl2);"},"pl_xl3":{"declaration":"padding-left: var(--space_xl3);"},"pl_xl4":{"declaration":"padding-left: var(--space_xl4);"},"pl_xl5":{"declaration":"padding-left: var(--space_xl5);"},"pl_xl6":{"declaration":"padding-left: var(--space_xl6);"},"pl_xl7":{"declaration":"padding-left: var(--space_xl7);"},"pl_xl8":{"declaration":"padding-left: var(--space_xl8);"},"pl_xl9":{"declaration":"padding-left: var(--space_xl9);"},"pl_xl10":{"declaration":"padding-left: var(--space_xl10);"},"pl_xl11":{"declaration":"padding-left: var(--space_xl11);"},"pl_xl12":{"declaration":"padding-left: var(--space_xl12);"},"pl_xl13":{"declaration":"padding-left: var(--space_xl13);"},"pl_xl14":{"declaration":"padding-left: var(--space_xl14);"},"pl_xl15":{"declaration":"padding-left: var(--space_xl15);"},"px_0":{"declaration":"padding-left: 0;\tpadding-right: 0;"},"px_xs5":{"declaration":"padding-left: var(--space_xs5);\tpadding-right: var(--space_xs5);"},"px_xs4":{"declaration":"padding-left: var(--space_xs4);\tpadding-right: var(--space_xs4);"},"px_xs3":{"declaration":"padding-left: var(--space_xs3);\tpadding-right: var(--space_xs3);"},"px_xs2":{"declaration":"padding-left: var(--space_xs2);\tpadding-right: var(--space_xs2);"},"px_xs":{"declaration":"padding-left: var(--space_xs);\tpadding-right: var(--space_xs);"},"px_sm":{"declaration":"padding-left: var(--space_sm);\tpadding-right: var(--space_sm);"},"px_md":{"declaration":"padding-left: var(--space_md);\tpadding-right: var(--space_md);"},"px_lg":{"declaration":"padding-left: var(--space_lg);\tpadding-right: var(--space_lg);"},"px_xl":{"declaration":"padding-left: var(--space_xl);\tpadding-right: var(--space_xl);"},"px_xl2":{"declaration":"padding-left: var(--space_xl2);\tpadding-right: var(--space_xl2);"},"px_xl3":{"declaration":"padding-left: var(--space_xl3);\tpadding-right: var(--space_xl3);"},"px_xl4":{"declaration":"padding-left: var(--space_xl4);\tpadding-right: var(--space_xl4);"},"px_xl5":{"declaration":"padding-left: var(--space_xl5);\tpadding-right: var(--space_xl5);"},"px_xl6":{"declaration":"padding-left: var(--space_xl6);\tpadding-right: var(--space_xl6);"},"px_xl7":{"declaration":"padding-left: var(--space_xl7);\tpadding-right: var(--space_xl7);"},"px_xl8":{"declaration":"padding-left: var(--space_xl8);\tpadding-right: var(--space_xl8);"},"px_xl9":{"declaration":"padding-left: var(--space_xl9);\tpadding-right: var(--space_xl9);"},"px_xl10":{"declaration":"padding-left: var(--space_xl10);\tpadding-right: var(--space_xl10);"},"px_xl11":{"declaration":"padding-left: var(--space_xl11);\tpadding-right: var(--space_xl11);"},"px_xl12":{"declaration":"padding-left: var(--space_xl12);\tpadding-right: var(--space_xl12);"},"px_xl13":{"declaration":"padding-left: var(--space_xl13);\tpadding-right: var(--space_xl13);"},"px_xl14":{"declaration":"padding-left: var(--space_xl14);\tpadding-right: var(--space_xl14);"},"px_xl15":{"declaration":"padding-left: var(--space_xl15);\tpadding-right: var(--space_xl15);"},"py_0":{"declaration":"padding-top: 0;\tpadding-bottom: 0;"},"py_xs5":{"declaration":"padding-top: var(--space_xs5);\tpadding-bottom: var(--space_xs5);"},"py_xs4":{"declaration":"padding-top: var(--space_xs4);\tpadding-bottom: var(--space_xs4);"},"py_xs3":{"declaration":"padding-top: var(--space_xs3);\tpadding-bottom: var(--space_xs3);"},"py_xs2":{"declaration":"padding-top: var(--space_xs2);\tpadding-bottom: var(--space_xs2);"},"py_xs":{"declaration":"padding-top: var(--space_xs);\tpadding-bottom: var(--space_xs);"},"py_sm":{"declaration":"padding-top: var(--space_sm);\tpadding-bottom: var(--space_sm);"},"py_md":{"declaration":"padding-top: var(--space_md);\tpadding-bottom: var(--space_md);"},"py_lg":{"declaration":"padding-top: var(--space_lg);\tpadding-bottom: var(--space_lg);"},"py_xl":{"declaration":"padding-top: var(--space_xl);\tpadding-bottom: var(--space_xl);"},"py_xl2":{"declaration":"padding-top: var(--space_xl2);\tpadding-bottom: var(--space_xl2);"},"py_xl3":{"declaration":"padding-top: var(--space_xl3);\tpadding-bottom: var(--space_xl3);"},"py_xl4":{"declaration":"padding-top: var(--space_xl4);\tpadding-bottom: var(--space_xl4);"},"py_xl5":{"declaration":"padding-top: var(--space_xl5);\tpadding-bottom: var(--space_xl5);"},"py_xl6":{"declaration":"padding-top: var(--space_xl6);\tpadding-bottom: var(--space_xl6);"},"py_xl7":{"declaration":"padding-top: var(--space_xl7);\tpadding-bottom: var(--space_xl7);"},"py_xl8":{"declaration":"padding-top: var(--space_xl8);\tpadding-bottom: var(--space_xl8);"},"py_xl9":{"declaration":"padding-top: var(--space_xl9);\tpadding-bottom: var(--space_xl9);"},"py_xl10":{"declaration":"padding-top: var(--space_xl10);\tpadding-bottom: var(--space_xl10);"},"py_xl11":{"declaration":"padding-top: var(--space_xl11);\tpadding-bottom: var(--space_xl11);"},"py_xl12":{"declaration":"padding-top: var(--space_xl12);\tpadding-bottom: var(--space_xl12);"},"py_xl13":{"declaration":"padding-top: var(--space_xl13);\tpadding-bottom: var(--space_xl13);"},"py_xl14":{"declaration":"padding-top: var(--space_xl14);\tpadding-bottom: var(--space_xl14);"},"py_xl15":{"declaration":"padding-top: var(--space_xl15);\tpadding-bottom: var(--space_xl15);"},"m_0":{"declaration":"margin: 0;"},"m_auto":{"declaration":"margin: auto;"},"m_xs5":{"declaration":"margin: var(--space_xs5);"},"m_xs4":{"declaration":"margin: var(--space_xs4);"},"m_xs3":{"declaration":"margin: var(--space_xs3);"},"m_xs2":{"declaration":"margin: var(--space_xs2);"},"m_xs":{"declaration":"margin: var(--space_xs);"},"m_sm":{"declaration":"margin: var(--space_sm);"},"m_md":{"declaration":"margin: var(--space_md);"},"m_lg":{"declaration":"margin: var(--space_lg);"},"m_xl":{"declaration":"margin: var(--space_xl);"},"m_xl2":{"declaration":"margin: var(--space_xl2);"},"m_xl3":{"declaration":"margin: var(--space_xl3);"},"m_xl4":{"declaration":"margin: var(--space_xl4);"},"m_xl5":{"declaration":"margin: var(--space_xl5);"},"m_xl6":{"declaration":"margin: var(--space_xl6);"},"m_xl7":{"declaration":"margin: var(--space_xl7);"},"m_xl8":{"declaration":"margin: var(--space_xl8);"},"m_xl9":{"declaration":"margin: var(--space_xl9);"},"m_xl10":{"declaration":"margin: var(--space_xl10);"},"m_xl11":{"declaration":"margin: var(--space_xl11);"},"m_xl12":{"declaration":"margin: var(--space_xl12);"},"m_xl13":{"declaration":"margin: var(--space_xl13);"},"m_xl14":{"declaration":"margin: var(--space_xl14);"},"m_xl15":{"declaration":"margin: var(--space_xl15);"},"mt_0":{"declaration":"margin-top: 0;"},"mt_auto":{"declaration":"margin-top: auto;"},"mt_xs5":{"declaration":"margin-top: var(--space_xs5);"},"mt_xs4":{"declaration":"margin-top: var(--space_xs4);"},"mt_xs3":{"declaration":"margin-top: var(--space_xs3);"},"mt_xs2":{"declaration":"margin-top: var(--space_xs2);"},"mt_xs":{"declaration":"margin-top: var(--space_xs);"},"mt_sm":{"declaration":"margin-top: var(--space_sm);"},"mt_md":{"declaration":"margin-top: var(--space_md);"},"mt_lg":{"declaration":"margin-top: var(--space_lg);"},"mt_xl":{"declaration":"margin-top: var(--space_xl);"},"mt_xl2":{"declaration":"margin-top: var(--space_xl2);"},"mt_xl3":{"declaration":"margin-top: var(--space_xl3);"},"mt_xl4":{"declaration":"margin-top: var(--space_xl4);"},"mt_xl5":{"declaration":"margin-top: var(--space_xl5);"},"mt_xl6":{"declaration":"margin-top: var(--space_xl6);"},"mt_xl7":{"declaration":"margin-top: var(--space_xl7);"},"mt_xl8":{"declaration":"margin-top: var(--space_xl8);"},"mt_xl9":{"declaration":"margin-top: var(--space_xl9);"},"mt_xl10":{"declaration":"margin-top: var(--space_xl10);"},"mt_xl11":{"declaration":"margin-top: var(--space_xl11);"},"mt_xl12":{"declaration":"margin-top: var(--space_xl12);"},"mt_xl13":{"declaration":"margin-top: var(--space_xl13);"},"mt_xl14":{"declaration":"margin-top: var(--space_xl14);"},"mt_xl15":{"declaration":"margin-top: var(--space_xl15);"},"mr_0":{"declaration":"margin-right: 0;"},"mr_auto":{"declaration":"margin-right: auto;"},"mr_xs5":{"declaration":"margin-right: var(--space_xs5);"},"mr_xs4":{"declaration":"margin-right: var(--space_xs4);"},"mr_xs3":{"declaration":"margin-right: var(--space_xs3);"},"mr_xs2":{"declaration":"margin-right: var(--space_xs2);"},"mr_xs":{"declaration":"margin-right: var(--space_xs);"},"mr_sm":{"declaration":"margin-right: var(--space_sm);"},"mr_md":{"declaration":"margin-right: var(--space_md);"},"mr_lg":{"declaration":"margin-right: var(--space_lg);"},"mr_xl":{"declaration":"margin-right: var(--space_xl);"},"mr_xl2":{"declaration":"margin-right: var(--space_xl2);"},"mr_xl3":{"declaration":"margin-right: var(--space_xl3);"},"mr_xl4":{"declaration":"margin-right: var(--space_xl4);"},"mr_xl5":{"declaration":"margin-right: var(--space_xl5);"},"mr_xl6":{"declaration":"margin-right: var(--space_xl6);"},"mr_xl7":{"declaration":"margin-right: var(--space_xl7);"},"mr_xl8":{"declaration":"margin-right: var(--space_xl8);"},"mr_xl9":{"declaration":"margin-right: var(--space_xl9);"},"mr_xl10":{"declaration":"margin-right: var(--space_xl10);"},"mr_xl11":{"declaration":"margin-right: var(--space_xl11);"},"mr_xl12":{"declaration":"margin-right: var(--space_xl12);"},"mr_xl13":{"declaration":"margin-right: var(--space_xl13);"},"mr_xl14":{"declaration":"margin-right: var(--space_xl14);"},"mr_xl15":{"declaration":"margin-right: var(--space_xl15);"},"mb_0":{"declaration":"margin-bottom: 0;"},"mb_auto":{"declaration":"margin-bottom: auto;"},"mb_xs5":{"declaration":"margin-bottom: var(--space_xs5);"},"mb_xs4":{"declaration":"margin-bottom: var(--space_xs4);"},"mb_xs3":{"declaration":"margin-bottom: var(--space_xs3);"},"mb_xs2":{"declaration":"margin-bottom: var(--space_xs2);"},"mb_xs":{"declaration":"margin-bottom: var(--space_xs);"},"mb_sm":{"declaration":"margin-bottom: var(--space_sm);"},"mb_md":{"declaration":"margin-bottom: var(--space_md);"},"mb_lg":{"declaration":"margin-bottom: var(--space_lg);"},"mb_xl":{"declaration":"margin-bottom: var(--space_xl);"},"mb_xl2":{"declaration":"margin-bottom: var(--space_xl2);"},"mb_xl3":{"declaration":"margin-bottom: var(--space_xl3);"},"mb_xl4":{"declaration":"margin-bottom: var(--space_xl4);"},"mb_xl5":{"declaration":"margin-bottom: var(--space_xl5);"},"mb_xl6":{"declaration":"margin-bottom: var(--space_xl6);"},"mb_xl7":{"declaration":"margin-bottom: var(--space_xl7);"},"mb_xl8":{"declaration":"margin-bottom: var(--space_xl8);"},"mb_xl9":{"declaration":"margin-bottom: var(--space_xl9);"},"mb_xl10":{"declaration":"margin-bottom: var(--space_xl10);"},"mb_xl11":{"declaration":"margin-bottom: var(--space_xl11);"},"mb_xl12":{"declaration":"margin-bottom: var(--space_xl12);"},"mb_xl13":{"declaration":"margin-bottom: var(--space_xl13);"},"mb_xl14":{"declaration":"margin-bottom: var(--space_xl14);"},"mb_xl15":{"declaration":"margin-bottom: var(--space_xl15);"},"ml_0":{"declaration":"margin-left: 0;"},"ml_auto":{"declaration":"margin-left: auto;"},"ml_xs5":{"declaration":"margin-left: var(--space_xs5);"},"ml_xs4":{"declaration":"margin-left: var(--space_xs4);"},"ml_xs3":{"declaration":"margin-left: var(--space_xs3);"},"ml_xs2":{"declaration":"margin-left: var(--space_xs2);"},"ml_xs":{"declaration":"margin-left: var(--space_xs);"},"ml_sm":{"declaration":"margin-left: var(--space_sm);"},"ml_md":{"declaration":"margin-left: var(--space_md);"},"ml_lg":{"declaration":"margin-left: var(--space_lg);"},"ml_xl":{"declaration":"margin-left: var(--space_xl);"},"ml_xl2":{"declaration":"margin-left: var(--space_xl2);"},"ml_xl3":{"declaration":"margin-left: var(--space_xl3);"},"ml_xl4":{"declaration":"margin-left: var(--space_xl4);"},"ml_xl5":{"declaration":"margin-left: var(--space_xl5);"},"ml_xl6":{"declaration":"margin-left: var(--space_xl6);"},"ml_xl7":{"declaration":"margin-left: var(--space_xl7);"},"ml_xl8":{"declaration":"margin-left: var(--space_xl8);"},"ml_xl9":{"declaration":"margin-left: var(--space_xl9);"},"ml_xl10":{"declaration":"margin-left: var(--space_xl10);"},"ml_xl11":{"declaration":"margin-left: var(--space_xl11);"},"ml_xl12":{"declaration":"margin-left: var(--space_xl12);"},"ml_xl13":{"declaration":"margin-left: var(--space_xl13);"},"ml_xl14":{"declaration":"margin-left: var(--space_xl14);"},"ml_xl15":{"declaration":"margin-left: var(--space_xl15);"},"mx_0":{"declaration":"margin-left: 0;\tmargin-right: 0;"},"mx_auto":{"declaration":"margin-left: auto;\tmargin-right: auto;"},"mx_xs5":{"declaration":"margin-left: var(--space_xs5);\tmargin-right: var(--space_xs5);"},"mx_xs4":{"declaration":"margin-left: var(--space_xs4);\tmargin-right: var(--space_xs4);"},"mx_xs3":{"declaration":"margin-left: var(--space_xs3);\tmargin-right: var(--space_xs3);"},"mx_xs2":{"declaration":"margin-left: var(--space_xs2);\tmargin-right: var(--space_xs2);"},"mx_xs":{"declaration":"margin-left: var(--space_xs);\tmargin-right: var(--space_xs);"},"mx_sm":{"declaration":"margin-left: var(--space_sm);\tmargin-right: var(--space_sm);"},"mx_md":{"declaration":"margin-left: var(--space_md);\tmargin-right: var(--space_md);"},"mx_lg":{"declaration":"margin-left: var(--space_lg);\tmargin-right: var(--space_lg);"},"mx_xl":{"declaration":"margin-left: var(--space_xl);\tmargin-right: var(--space_xl);"},"mx_xl2":{"declaration":"margin-left: var(--space_xl2);\tmargin-right: var(--space_xl2);"},"mx_xl3":{"declaration":"margin-left: var(--space_xl3);\tmargin-right: var(--space_xl3);"},"mx_xl4":{"declaration":"margin-left: var(--space_xl4);\tmargin-right: var(--space_xl4);"},"mx_xl5":{"declaration":"margin-left: var(--space_xl5);\tmargin-right: var(--space_xl5);"},"mx_xl6":{"declaration":"margin-left: var(--space_xl6);\tmargin-right: var(--space_xl6);"},"mx_xl7":{"declaration":"margin-left: var(--space_xl7);\tmargin-right: var(--space_xl7);"},"mx_xl8":{"declaration":"margin-left: var(--space_xl8);\tmargin-right: var(--space_xl8);"},"mx_xl9":{"declaration":"margin-left: var(--space_xl9);\tmargin-right: var(--space_xl9);"},"mx_xl10":{"declaration":"margin-left: var(--space_xl10);\tmargin-right: var(--space_xl10);"},"mx_xl11":{"declaration":"margin-left: var(--space_xl11);\tmargin-right: var(--space_xl11);"},"mx_xl12":{"declaration":"margin-left: var(--space_xl12);\tmargin-right: var(--space_xl12);"},"mx_xl13":{"declaration":"margin-left: var(--space_xl13);\tmargin-right: var(--space_xl13);"},"mx_xl14":{"declaration":"margin-left: var(--space_xl14);\tmargin-right: var(--space_xl14);"},"mx_xl15":{"declaration":"margin-left: var(--space_xl15);\tmargin-right: var(--space_xl15);"},"my_0":{"declaration":"margin-top: 0;\tmargin-bottom: 0;"},"my_auto":{"declaration":"margin-top: auto;\tmargin-bottom: auto;"},"my_xs5":{"declaration":"margin-top: var(--space_xs5);\tmargin-bottom: var(--space_xs5);"},"my_xs4":{"declaration":"margin-top: var(--space_xs4);\tmargin-bottom: var(--space_xs4);"},"my_xs3":{"declaration":"margin-top: var(--space_xs3);\tmargin-bottom: var(--space_xs3);"},"my_xs2":{"declaration":"margin-top: var(--space_xs2);\tmargin-bottom: var(--space_xs2);"},"my_xs":{"declaration":"margin-top: var(--space_xs);\tmargin-bottom: var(--space_xs);"},"my_sm":{"declaration":"margin-top: var(--space_sm);\tmargin-bottom: var(--space_sm);"},"my_md":{"declaration":"margin-top: var(--space_md);\tmargin-bottom: var(--space_md);"},"my_lg":{"declaration":"margin-top: var(--space_lg);\tmargin-bottom: var(--space_lg);"},"my_xl":{"declaration":"margin-top: var(--space_xl);\tmargin-bottom: var(--space_xl);"},"my_xl2":{"declaration":"margin-top: var(--space_xl2);\tmargin-bottom: var(--space_xl2);"},"my_xl3":{"declaration":"margin-top: var(--space_xl3);\tmargin-bottom: var(--space_xl3);"},"my_xl4":{"declaration":"margin-top: var(--space_xl4);\tmargin-bottom: var(--space_xl4);"},"my_xl5":{"declaration":"margin-top: var(--space_xl5);\tmargin-bottom: var(--space_xl5);"},"my_xl6":{"declaration":"margin-top: var(--space_xl6);\tmargin-bottom: var(--space_xl6);"},"my_xl7":{"declaration":"margin-top: var(--space_xl7);\tmargin-bottom: var(--space_xl7);"},"my_xl8":{"declaration":"margin-top: var(--space_xl8);\tmargin-bottom: var(--space_xl8);"},"my_xl9":{"declaration":"margin-top: var(--space_xl9);\tmargin-bottom: var(--space_xl9);"},"my_xl10":{"declaration":"margin-top: var(--space_xl10);\tmargin-bottom: var(--space_xl10);"},"my_xl11":{"declaration":"margin-top: var(--space_xl11);\tmargin-bottom: var(--space_xl11);"},"my_xl12":{"declaration":"margin-top: var(--space_xl12);\tmargin-bottom: var(--space_xl12);"},"my_xl13":{"declaration":"margin-top: var(--space_xl13);\tmargin-bottom: var(--space_xl13);"},"my_xl14":{"declaration":"margin-top: var(--space_xl14);\tmargin-bottom: var(--space_xl14);"},"my_xl15":{"declaration":"margin-top: var(--space_xl15);\tmargin-bottom: var(--space_xl15);"},"gap_xs5":{"declaration":"gap: var(--space_xs5);"},"gap_xs4":{"declaration":"gap: var(--space_xs4);"},"gap_xs3":{"declaration":"gap: var(--space_xs3);"},"gap_xs2":{"declaration":"gap: var(--space_xs2);"},"gap_xs":{"declaration":"gap: var(--space_xs);"},"gap_sm":{"declaration":"gap: var(--space_sm);"},"gap_md":{"declaration":"gap: var(--space_md);"},"gap_lg":{"declaration":"gap: var(--space_lg);"},"gap_xl":{"declaration":"gap: var(--space_xl);"},"gap_xl2":{"declaration":"gap: var(--space_xl2);"},"gap_xl3":{"declaration":"gap: var(--space_xl3);"},"gap_xl4":{"declaration":"gap: var(--space_xl4);"},"gap_xl5":{"declaration":"gap: var(--space_xl5);"},"gap_xl6":{"declaration":"gap: var(--space_xl6);"},"gap_xl7":{"declaration":"gap: var(--space_xl7);"},"gap_xl8":{"declaration":"gap: var(--space_xl8);"},"gap_xl9":{"declaration":"gap: var(--space_xl9);"},"gap_xl10":{"declaration":"gap: var(--space_xl10);"},"gap_xl11":{"declaration":"gap: var(--space_xl11);"},"gap_xl12":{"declaration":"gap: var(--space_xl12);"},"gap_xl13":{"declaration":"gap: var(--space_xl13);"},"gap_xl14":{"declaration":"gap: var(--space_xl14);"},"gap_xl15":{"declaration":"gap: var(--space_xl15);"},"column_gap_xs5":{"declaration":"column-gap: var(--space_xs5);"},"column_gap_xs4":{"declaration":"column-gap: var(--space_xs4);"},"column_gap_xs3":{"declaration":"column-gap: var(--space_xs3);"},"column_gap_xs2":{"declaration":"column-gap: var(--space_xs2);"},"column_gap_xs":{"declaration":"column-gap: var(--space_xs);"},"column_gap_sm":{"declaration":"column-gap: var(--space_sm);"},"column_gap_md":{"declaration":"column-gap: var(--space_md);"},"column_gap_lg":{"declaration":"column-gap: var(--space_lg);"},"column_gap_xl":{"declaration":"column-gap: var(--space_xl);"},"column_gap_xl2":{"declaration":"column-gap: var(--space_xl2);"},"column_gap_xl3":{"declaration":"column-gap: var(--space_xl3);"},"column_gap_xl4":{"declaration":"column-gap: var(--space_xl4);"},"column_gap_xl5":{"declaration":"column-gap: var(--space_xl5);"},"column_gap_xl6":{"declaration":"column-gap: var(--space_xl6);"},"column_gap_xl7":{"declaration":"column-gap: var(--space_xl7);"},"column_gap_xl8":{"declaration":"column-gap: var(--space_xl8);"},"column_gap_xl9":{"declaration":"column-gap: var(--space_xl9);"},"column_gap_xl10":{"declaration":"column-gap: var(--space_xl10);"},"column_gap_xl11":{"declaration":"column-gap: var(--space_xl11);"},"column_gap_xl12":{"declaration":"column-gap: var(--space_xl12);"},"column_gap_xl13":{"declaration":"column-gap: var(--space_xl13);"},"column_gap_xl14":{"declaration":"column-gap: var(--space_xl14);"},"column_gap_xl15":{"declaration":"column-gap: var(--space_xl15);"},"row_gap_xs5":{"declaration":"row-gap: var(--space_xs5);"},"row_gap_xs4":{"declaration":"row-gap: var(--space_xs4);"},"row_gap_xs3":{"declaration":"row-gap: var(--space_xs3);"},"row_gap_xs2":{"declaration":"row-gap: var(--space_xs2);"},"row_gap_xs":{"declaration":"row-gap: var(--space_xs);"},"row_gap_sm":{"declaration":"row-gap: var(--space_sm);"},"row_gap_md":{"declaration":"row-gap: var(--space_md);"},"row_gap_lg":{"declaration":"row-gap: var(--space_lg);"},"row_gap_xl":{"declaration":"row-gap: var(--space_xl);"},"row_gap_xl2":{"declaration":"row-gap: var(--space_xl2);"},"row_gap_xl3":{"declaration":"row-gap: var(--space_xl3);"},"row_gap_xl4":{"declaration":"row-gap: var(--space_xl4);"},"row_gap_xl5":{"declaration":"row-gap: var(--space_xl5);"},"row_gap_xl6":{"declaration":"row-gap: var(--space_xl6);"},"row_gap_xl7":{"declaration":"row-gap: var(--space_xl7);"},"row_gap_xl8":{"declaration":"row-gap: var(--space_xl8);"},"row_gap_xl9":{"declaration":"row-gap: var(--space_xl9);"},"row_gap_xl10":{"declaration":"row-gap: var(--space_xl10);"},"row_gap_xl11":{"declaration":"row-gap: var(--space_xl11);"},"row_gap_xl12":{"declaration":"row-gap: var(--space_xl12);"},"row_gap_xl13":{"declaration":"row-gap: var(--space_xl13);"},"row_gap_xl14":{"declaration":"row-gap: var(--space_xl14);"},"row_gap_xl15":{"declaration":"row-gap: var(--space_xl15);"}} \ No newline at end of file +{"pixelated":{"declaration":"\n\t\t\timage-rendering: -webkit-optimize-contrast; /* Safari */\n\t\t\timage-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */\n\t\t\timage-rendering: pixelated; /* in case crisp-edges isn't supported */\n\t\t\timage-rendering: crisp-edges; /* the recommended pixel art setting according to MDN */\n\t\t"},"circular":{"declaration":"border-radius: 50%;"},"box":{"declaration":"\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t"},"column":{"comment":"like `.box` but uncentered","declaration":"\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t"},"row":{"comment":"can be used to override the direction of a `.box`","declaration":"\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: row;\n\t\t\talign-items: center;\n\t\t"},"ellipsis":{"declaration":"\n\t\t\tdisplay: block;\n\t\t\twhite-space: nowrap;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t"},"selectable":{"ruleset":"\n\t\t\t.selectable {\n\t\t\t\t--button_fill: color-mix(in oklab, var(--shade_50) 8%, transparent);\n\t\t\t\t--button_fill_hover: color-mix(in oklab, var(--shade_50) 16%, transparent);\n\t\t\t\t--button_fill_active: color-mix(in oklab, var(--shade_50) 24%, transparent);\n\t\t\t\tcursor: pointer;\n\t\t\t\tbackground-color: var(--button_fill);\n\t\t\t\tborder-color: var(--border_color_30);\n\t\t\t\tborder-style: var(--border_style);\n\t\t\t\tborder-width: var(--border_width);\n\t\t\t}\n\t\t\t.selectable:hover {\n\t\t\t\tbackground-color: var(--button_fill_hover);\n\t\t\t\tborder-color: var(--border_color_20);\n\t\t\t}\n\t\t\t.selectable.selected,\n\t\t\t.selectable:active {\n\t\t\t\tbackground-color: var(--button_fill_active);\n\t\t\t\tborder-color: var(--accent_50);\n\t\t\t}\n\t\t\t.selectable.selected {\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t\t.selectable.selected.deselectable:not(:disabled) {\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\t\t"},"clickable":{"ruleset":"\n\t\t\t.clickable {\n\t\t\t\tcursor: pointer;\n\t\t\t\ttransform: var(--clickable_transform, scale3d(1, 1, 1));\n\t\t\t\ttransform-origin: var(--clickable_transform_origin);\n\t\t\t\ttransition-duration: var(--clickable_transition_duration); /* default to instant, chunky/lofi */\n\t\t\t}\n\t\t\t.clickable:focus {\n\t\t\t\ttransform: var(--clickable_transform_focus, scale3d(1.07, 1.07, 1.07));\n\t\t\t}\n\t\t\t.clickable:hover {\n\t\t\t\ttransform: var(--clickable_transform_hover, scale3d(1.1, 1.1, 1.1));\n\t\t\t}\n\t\t\t.clickable:active,\n\t\t\t.clickable.active {\n\t\t\t\ttransform: var(--clickable_transform_active, scale3d(1.2, 1.2, 1.2));\n\t\t\t}\n\t\t"},"pane":{"comment":"A pane is a box floating over the page, like for dialogs. By default it's opaque, resetting the background to the initial depth.","declaration":"\n\t\t\tbackground-color: var(--shade_00);\n\t\t\tbox-shadow: var(--pane_shadow, var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha_50), transparent));\n\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t"},"panel":{"comment":"A panel is a box embedded into the page, useful for visually isolating content.","declaration":"\n\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t\tbackground-color: var(--fg_10);\n\t\t"},"xs":{"comment":"Smallest sizing, two steps down from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_xs);\n\t\t\t--input_height: var(--space_xl3);\n\t\t\t--input_height_compact: var(--space_xl2);\n\t\t\t--input_padding_x: var(--space_sm);\n\t\t\t--chip_padding_x: var(--space_xs3);\n\t\t\t--icon_size: var(--icon_size_xs);\n\t\t\t--menuitem_padding: var(--space_xs5) var(--space_xs3);\n\t\t\t--flow_margin: var(--space_sm);\n\t\t"},"sm":{"comment":"Smaller sizing, one step down from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_sm);\n\t\t\t--input_height: var(--space_xl4);\n\t\t\t--input_height_compact: var(--space_xl3);\n\t\t\t--input_padding_x: var(--space_md);\n\t\t\t--chip_padding_x: var(--space_xs2);\n\t\t\t--icon_size: var(--icon_size_sm);\n\t\t\t--menuitem_padding: var(--space_xs4) var(--space_xs2);\n\t\t\t--flow_margin: var(--space_md);\n\t\t"},"md":{"comment":"Default sizing restated explicitly, useful as a cascade reset within a sized parent.","declaration":"\n\t\t\t--font_size: var(--font_size_md);\n\t\t\t--input_height: var(--space_xl5);\n\t\t\t--input_height_compact: var(--space_xl4);\n\t\t\t--input_padding_x: var(--space_lg);\n\t\t\t--chip_padding_x: var(--space_xs);\n\t\t\t--icon_size: var(--icon_size_md);\n\t\t\t--menuitem_padding: var(--space_xs3) var(--space_xs);\n\t\t\t--flow_margin: var(--space_lg);\n\t\t"},"lg":{"comment":"Larger sizing, one step up from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_lg);\n\t\t\t--input_height: var(--space_xl6);\n\t\t\t--input_height_compact: var(--space_xl5);\n\t\t\t--input_padding_x: var(--space_xl);\n\t\t\t--chip_padding_x: var(--space_sm);\n\t\t\t--icon_size: var(--icon_size_lg);\n\t\t\t--menuitem_padding: var(--space_xs2) var(--space_sm);\n\t\t\t--flow_margin: var(--space_xl);\n\t\t"},"xl":{"comment":"Largest sizing, two steps up from the default, cascading to children. Works on individual elements or containers.","declaration":"\n\t\t\t--font_size: var(--font_size_xl);\n\t\t\t--input_height: var(--space_xl7);\n\t\t\t--input_height_compact: var(--space_xl6);\n\t\t\t--input_padding_x: var(--space_xl2);\n\t\t\t--chip_padding_x: var(--space_md);\n\t\t\t--icon_size: var(--icon_size_xl);\n\t\t\t--menuitem_padding: var(--space_xs) var(--space_md);\n\t\t\t--flow_margin: var(--space_xl2);\n\t\t"},"mb_flow":{"comment":"Flow-aware margin-bottom that responds to --flow_margin overrides from size composites.","declaration":"margin-bottom: var(--flow_margin, var(--space_lg));"},"mt_flow":{"comment":"Flow-aware margin-top that responds to --flow_margin overrides from size composites.","declaration":"margin-top: var(--flow_margin, var(--space_lg));"},"icon_button":{"comment":"\n\t\t\tTODO other button variants?\n\t\t\tTODO this is slightly strange that it doesn't use --icon_size.\n\t\t\tThese are used as modifiers to buttons.\n\t\t","declaration":"\n\t\t\twidth: var(--input_height);\n\t\t\theight: var(--input_height);\n\t\t\tmin-width: var(--input_height);\n\t\t\tmin-height: var(--input_height);\n\t\t\tflex-shrink: 0;\n\t\t\tline-height: 1;\n\t\t\tfont-weight: 900;\n\t\t\tpadding: 0;\n\t\t"},"plain":{"comment":"TODO maybe this belongs with the reset, like `selected`? or does `selected` belong here?","ruleset":"\n\t\t\t.plain:not(:hover) {\n\t\t\t\t--border_color: transparent;\n\t\t\t\tbox-shadow: none;\n\t\t\t\t--button_fill: transparent;\n\t\t\t}\n\t\t\t.plain:hover, .plain:active {\n\t\t\t\t--border_color: transparent;\n\t\t\t}\n\t\t"},"menuitem":{"ruleset":"\n\t\t\t.menuitem {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tborder-radius: 0;\n\t\t\t\tposition: relative;\n\t\t\t\tcursor: pointer;\n\t\t\t\twidth: 100%;\n\t\t\t\tmin-height: var(--menuitem_min_height, var(--input_height_compact));\n\t\t\t\tdisplay: flex;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\talign-items: center;\n\t\t\t\tpadding: var(--menuitem_padding, var(--space_xs3) var(--space_xs));\n\t\t\t}\n\t\t\t.menuitem.selected {\n\t\t\t\t/* TODO different patterns for border and surface? */\n\t\t\t\t--border_color: var(--accent_50);\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t\t.menuitem.selected.deselectable:not(:disabled) {\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\t\t\t.menuitem:hover {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t}\n\t\t\t.menuitem:active,\n\t\t\t.menuitem.selected:hover {\n\t\t\t\t--border_color: var(--border_color_30);\n\t\t\t\tbackground-color: var(--fg_20);\n\t\t\t}\n\t\t\t.menuitem.plain {\n\t\t\t\tborder: none;\n\t\t\t}\n\t\t\t.menuitem .content {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tflex: 1;\n\t\t\t\t/* allows the flex children to shrink */\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\t\t\t.menuitem .icon {\n\t\t\t\twidth: var(--icon_size, var(--icon_size_md));\n\t\t\t\tmargin-right: var(--space_sm);\n\t\t\t\tflex-shrink: 0;\n\t\t\t\ttext-align: center;\n\t\t\t\tfont-weight: 900;\n\t\t\t}\n\t\t\t.menuitem .title {\n\t\t\t\tmargin-right: var(--space_lg);\n\t\t\t\tflex-shrink: 1;\n\t\t\t\toverflow: hidden;\n\t\t\t\twhite-space: nowrap;\n\t\t\t\ttext-overflow: ellipsis;\n\t\t\t\tline-height: var(--line_height_lg); /* prevents the bottom of g's and others from being cut off */\n\t\t\t}\n\t\t"},"chevron":{"ruleset":"\n\t\t\t.chevron {\n\t\t\t\tposition: relative;\n\t\t\t\theight: 8px;\n\t\t\t}\n\t\t\t.chevron::before {\n\t\t\t\tdisplay: block;\n\t\t\t\tcontent: '';\n\t\t\t\tborder: 4px solid transparent;\n\t\t\t\tborder-left-color: var(--text_70);\n\t\t\t}\n\t\t"},"chip":{"ruleset":"\n\t\t\t.chip {\n\t\t\t\tfont-weight: 500;\n\t\t\t\tfont-size: var(--font_size, inherit);\n\t\t\t\tpadding-left: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tpadding-right: var(--chip_padding_x, var(--space_xs));\n\t\t\t\tbackground-color: var(--fg_10);\n\t\t\t\tborder-radius: var(--border_radius, var(--border_radius_xs));\n\t\t\t}\n\t\t\ta.chip {\n\t\t\t\tfont-weight: 600;\n\t\t\t}\n\t\t\t.chip.palette_a {\n\t\t\t\tcolor: var(--palette_a_50);\n\t\t\t\tbackground-color: var(--palette_a_10);\n\t\t\t}\n\t\t\t.chip.palette_b {\n\t\t\t\tcolor: var(--palette_b_50);\n\t\t\t\tbackground-color: var(--palette_b_10);\n\t\t\t}\n\t\t\t.chip.palette_c {\n\t\t\t\tcolor: var(--palette_c_50);\n\t\t\t\tbackground-color: var(--palette_c_10);\n\t\t\t}\n\t\t\t.chip.palette_d {\n\t\t\t\tcolor: var(--palette_d_50);\n\t\t\t\tbackground-color: var(--palette_d_10);\n\t\t\t}\n\t\t\t.chip.palette_e {\n\t\t\t\tcolor: var(--palette_e_50);\n\t\t\t\tbackground-color: var(--palette_e_10);\n\t\t\t}\n\t\t\t.chip.palette_f {\n\t\t\t\tcolor: var(--palette_f_50);\n\t\t\t\tbackground-color: var(--palette_f_10);\n\t\t\t}\n\t\t\t.chip.palette_g {\n\t\t\t\tcolor: var(--palette_g_50);\n\t\t\t\tbackground-color: var(--palette_g_10);\n\t\t\t}\n\t\t\t.chip.palette_h {\n\t\t\t\tcolor: var(--palette_h_50);\n\t\t\t\tbackground-color: var(--palette_h_10);\n\t\t\t}\n\t\t\t.chip.palette_i {\n\t\t\t\tcolor: var(--palette_i_50);\n\t\t\t\tbackground-color: var(--palette_i_10);\n\t\t\t}\n\t\t\t.chip.palette_j {\n\t\t\t\tcolor: var(--palette_j_50);\n\t\t\t\tbackground-color: var(--palette_j_10);\n\t\t\t}\n\t\t"},"font_family_sans":{"declaration":"font-family: var(--font_family_sans);"},"font_family_serif":{"declaration":"font-family: var(--font_family_serif);"},"font_family_mono":{"declaration":"font-family: var(--font_family_mono);"},"line_height_xs":{"declaration":"line-height: var(--line_height_xs);"},"line_height_sm":{"declaration":"line-height: var(--line_height_sm);"},"line_height_md":{"declaration":"line-height: var(--line_height_md);"},"line_height_lg":{"declaration":"line-height: var(--line_height_lg);"},"line_height_xl":{"declaration":"line-height: var(--line_height_xl);"},"font_size_xs":{"declaration":"font-size: var(--font_size_xs); --font_size: var(--font_size_xs);"},"font_size_sm":{"declaration":"font-size: var(--font_size_sm); --font_size: var(--font_size_sm);"},"font_size_md":{"declaration":"font-size: var(--font_size_md); --font_size: var(--font_size_md);"},"font_size_lg":{"declaration":"font-size: var(--font_size_lg); --font_size: var(--font_size_lg);"},"font_size_xl":{"declaration":"font-size: var(--font_size_xl); --font_size: var(--font_size_xl);"},"font_size_xl2":{"declaration":"font-size: var(--font_size_xl2); --font_size: var(--font_size_xl2);"},"font_size_xl3":{"declaration":"font-size: var(--font_size_xl3); --font_size: var(--font_size_xl3);"},"font_size_xl4":{"declaration":"font-size: var(--font_size_xl4); --font_size: var(--font_size_xl4);"},"font_size_xl5":{"declaration":"font-size: var(--font_size_xl5); --font_size: var(--font_size_xl5);"},"font_size_xl6":{"declaration":"font-size: var(--font_size_xl6); --font_size: var(--font_size_xl6);"},"font_size_xl7":{"declaration":"font-size: var(--font_size_xl7); --font_size: var(--font_size_xl7);"},"font_size_xl8":{"declaration":"font-size: var(--font_size_xl8); --font_size: var(--font_size_xl8);"},"font_size_xl9":{"declaration":"font-size: var(--font_size_xl9); --font_size: var(--font_size_xl9);"},"icon_size_xs":{"declaration":"font-size: var(--icon_size_xs); --font_size: var(--icon_size_xs);"},"icon_size_sm":{"declaration":"font-size: var(--icon_size_sm); --font_size: var(--icon_size_sm);"},"icon_size_md":{"declaration":"font-size: var(--icon_size_md); --font_size: var(--icon_size_md);"},"icon_size_lg":{"declaration":"font-size: var(--icon_size_lg); --font_size: var(--icon_size_lg);"},"icon_size_xl":{"declaration":"font-size: var(--icon_size_xl); --font_size: var(--icon_size_xl);"},"icon_size_xl2":{"declaration":"font-size: var(--icon_size_xl2); --font_size: var(--icon_size_xl2);"},"icon_size_xl3":{"declaration":"font-size: var(--icon_size_xl3); --font_size: var(--icon_size_xl3);"},"text_min":{"declaration":"color: var(--text_min); --text_color: var(--text_min);"},"text_00":{"declaration":"color: var(--text_00); --text_color: var(--text_00);"},"text_05":{"declaration":"color: var(--text_05); --text_color: var(--text_05);"},"text_10":{"declaration":"color: var(--text_10); --text_color: var(--text_10);"},"text_20":{"declaration":"color: var(--text_20); --text_color: var(--text_20);"},"text_30":{"declaration":"color: var(--text_30); --text_color: var(--text_30);"},"text_40":{"declaration":"color: var(--text_40); --text_color: var(--text_40);"},"text_50":{"declaration":"color: var(--text_50); --text_color: var(--text_50);"},"text_60":{"declaration":"color: var(--text_60); --text_color: var(--text_60);"},"text_70":{"declaration":"color: var(--text_70); --text_color: var(--text_70);"},"text_80":{"declaration":"color: var(--text_80); --text_color: var(--text_80);"},"text_90":{"declaration":"color: var(--text_90); --text_color: var(--text_90);"},"text_95":{"declaration":"color: var(--text_95); --text_color: var(--text_95);"},"text_100":{"declaration":"color: var(--text_100); --text_color: var(--text_100);"},"text_max":{"declaration":"color: var(--text_max); --text_color: var(--text_max);"},"shade_min":{"declaration":"background-color: var(--shade_min);"},"shade_00":{"declaration":"background-color: var(--shade_00);"},"shade_05":{"declaration":"background-color: var(--shade_05);"},"shade_10":{"declaration":"background-color: var(--shade_10);"},"shade_20":{"declaration":"background-color: var(--shade_20);"},"shade_30":{"declaration":"background-color: var(--shade_30);"},"shade_40":{"declaration":"background-color: var(--shade_40);"},"shade_50":{"declaration":"background-color: var(--shade_50);"},"shade_60":{"declaration":"background-color: var(--shade_60);"},"shade_70":{"declaration":"background-color: var(--shade_70);"},"shade_80":{"declaration":"background-color: var(--shade_80);"},"shade_90":{"declaration":"background-color: var(--shade_90);"},"shade_95":{"declaration":"background-color: var(--shade_95);"},"shade_100":{"declaration":"background-color: var(--shade_100);"},"shade_max":{"declaration":"background-color: var(--shade_max);"},"hue_a":{"declaration":"--hue: var(--hue_a);"},"hue_b":{"declaration":"--hue: var(--hue_b);"},"hue_c":{"declaration":"--hue: var(--hue_c);"},"hue_d":{"declaration":"--hue: var(--hue_d);"},"hue_e":{"declaration":"--hue: var(--hue_e);"},"hue_f":{"declaration":"--hue: var(--hue_f);"},"hue_g":{"declaration":"--hue: var(--hue_g);"},"hue_h":{"declaration":"--hue: var(--hue_h);"},"hue_i":{"declaration":"--hue: var(--hue_i);"},"hue_j":{"declaration":"--hue: var(--hue_j);"},"color_a_00":{"declaration":"color: var(--palette_a_00); --text_color: var(--palette_a_00);"},"color_a_05":{"declaration":"color: var(--palette_a_05); --text_color: var(--palette_a_05);"},"color_a_10":{"declaration":"color: var(--palette_a_10); --text_color: var(--palette_a_10);"},"color_a_20":{"declaration":"color: var(--palette_a_20); --text_color: var(--palette_a_20);"},"color_a_30":{"declaration":"color: var(--palette_a_30); --text_color: var(--palette_a_30);"},"color_a_40":{"declaration":"color: var(--palette_a_40); --text_color: var(--palette_a_40);"},"color_a_50":{"declaration":"color: var(--palette_a_50); --text_color: var(--palette_a_50);"},"color_a_60":{"declaration":"color: var(--palette_a_60); --text_color: var(--palette_a_60);"},"color_a_70":{"declaration":"color: var(--palette_a_70); --text_color: var(--palette_a_70);"},"color_a_80":{"declaration":"color: var(--palette_a_80); --text_color: var(--palette_a_80);"},"color_a_90":{"declaration":"color: var(--palette_a_90); --text_color: var(--palette_a_90);"},"color_a_95":{"declaration":"color: var(--palette_a_95); --text_color: var(--palette_a_95);"},"color_a_100":{"declaration":"color: var(--palette_a_100); --text_color: var(--palette_a_100);"},"color_b_00":{"declaration":"color: var(--palette_b_00); --text_color: var(--palette_b_00);"},"color_b_05":{"declaration":"color: var(--palette_b_05); --text_color: var(--palette_b_05);"},"color_b_10":{"declaration":"color: var(--palette_b_10); --text_color: var(--palette_b_10);"},"color_b_20":{"declaration":"color: var(--palette_b_20); --text_color: var(--palette_b_20);"},"color_b_30":{"declaration":"color: var(--palette_b_30); --text_color: var(--palette_b_30);"},"color_b_40":{"declaration":"color: var(--palette_b_40); --text_color: var(--palette_b_40);"},"color_b_50":{"declaration":"color: var(--palette_b_50); --text_color: var(--palette_b_50);"},"color_b_60":{"declaration":"color: var(--palette_b_60); --text_color: var(--palette_b_60);"},"color_b_70":{"declaration":"color: var(--palette_b_70); --text_color: var(--palette_b_70);"},"color_b_80":{"declaration":"color: var(--palette_b_80); --text_color: var(--palette_b_80);"},"color_b_90":{"declaration":"color: var(--palette_b_90); --text_color: var(--palette_b_90);"},"color_b_95":{"declaration":"color: var(--palette_b_95); --text_color: var(--palette_b_95);"},"color_b_100":{"declaration":"color: var(--palette_b_100); --text_color: var(--palette_b_100);"},"color_c_00":{"declaration":"color: var(--palette_c_00); --text_color: var(--palette_c_00);"},"color_c_05":{"declaration":"color: var(--palette_c_05); --text_color: var(--palette_c_05);"},"color_c_10":{"declaration":"color: var(--palette_c_10); --text_color: var(--palette_c_10);"},"color_c_20":{"declaration":"color: var(--palette_c_20); --text_color: var(--palette_c_20);"},"color_c_30":{"declaration":"color: var(--palette_c_30); --text_color: var(--palette_c_30);"},"color_c_40":{"declaration":"color: var(--palette_c_40); --text_color: var(--palette_c_40);"},"color_c_50":{"declaration":"color: var(--palette_c_50); --text_color: var(--palette_c_50);"},"color_c_60":{"declaration":"color: var(--palette_c_60); --text_color: var(--palette_c_60);"},"color_c_70":{"declaration":"color: var(--palette_c_70); --text_color: var(--palette_c_70);"},"color_c_80":{"declaration":"color: var(--palette_c_80); --text_color: var(--palette_c_80);"},"color_c_90":{"declaration":"color: var(--palette_c_90); --text_color: var(--palette_c_90);"},"color_c_95":{"declaration":"color: var(--palette_c_95); --text_color: var(--palette_c_95);"},"color_c_100":{"declaration":"color: var(--palette_c_100); --text_color: var(--palette_c_100);"},"color_d_00":{"declaration":"color: var(--palette_d_00); --text_color: var(--palette_d_00);"},"color_d_05":{"declaration":"color: var(--palette_d_05); --text_color: var(--palette_d_05);"},"color_d_10":{"declaration":"color: var(--palette_d_10); --text_color: var(--palette_d_10);"},"color_d_20":{"declaration":"color: var(--palette_d_20); --text_color: var(--palette_d_20);"},"color_d_30":{"declaration":"color: var(--palette_d_30); --text_color: var(--palette_d_30);"},"color_d_40":{"declaration":"color: var(--palette_d_40); --text_color: var(--palette_d_40);"},"color_d_50":{"declaration":"color: var(--palette_d_50); --text_color: var(--palette_d_50);"},"color_d_60":{"declaration":"color: var(--palette_d_60); --text_color: var(--palette_d_60);"},"color_d_70":{"declaration":"color: var(--palette_d_70); --text_color: var(--palette_d_70);"},"color_d_80":{"declaration":"color: var(--palette_d_80); --text_color: var(--palette_d_80);"},"color_d_90":{"declaration":"color: var(--palette_d_90); --text_color: var(--palette_d_90);"},"color_d_95":{"declaration":"color: var(--palette_d_95); --text_color: var(--palette_d_95);"},"color_d_100":{"declaration":"color: var(--palette_d_100); --text_color: var(--palette_d_100);"},"color_e_00":{"declaration":"color: var(--palette_e_00); --text_color: var(--palette_e_00);"},"color_e_05":{"declaration":"color: var(--palette_e_05); --text_color: var(--palette_e_05);"},"color_e_10":{"declaration":"color: var(--palette_e_10); --text_color: var(--palette_e_10);"},"color_e_20":{"declaration":"color: var(--palette_e_20); --text_color: var(--palette_e_20);"},"color_e_30":{"declaration":"color: var(--palette_e_30); --text_color: var(--palette_e_30);"},"color_e_40":{"declaration":"color: var(--palette_e_40); --text_color: var(--palette_e_40);"},"color_e_50":{"declaration":"color: var(--palette_e_50); --text_color: var(--palette_e_50);"},"color_e_60":{"declaration":"color: var(--palette_e_60); --text_color: var(--palette_e_60);"},"color_e_70":{"declaration":"color: var(--palette_e_70); --text_color: var(--palette_e_70);"},"color_e_80":{"declaration":"color: var(--palette_e_80); --text_color: var(--palette_e_80);"},"color_e_90":{"declaration":"color: var(--palette_e_90); --text_color: var(--palette_e_90);"},"color_e_95":{"declaration":"color: var(--palette_e_95); --text_color: var(--palette_e_95);"},"color_e_100":{"declaration":"color: var(--palette_e_100); --text_color: var(--palette_e_100);"},"color_f_00":{"declaration":"color: var(--palette_f_00); --text_color: var(--palette_f_00);"},"color_f_05":{"declaration":"color: var(--palette_f_05); --text_color: var(--palette_f_05);"},"color_f_10":{"declaration":"color: var(--palette_f_10); --text_color: var(--palette_f_10);"},"color_f_20":{"declaration":"color: var(--palette_f_20); --text_color: var(--palette_f_20);"},"color_f_30":{"declaration":"color: var(--palette_f_30); --text_color: var(--palette_f_30);"},"color_f_40":{"declaration":"color: var(--palette_f_40); --text_color: var(--palette_f_40);"},"color_f_50":{"declaration":"color: var(--palette_f_50); --text_color: var(--palette_f_50);"},"color_f_60":{"declaration":"color: var(--palette_f_60); --text_color: var(--palette_f_60);"},"color_f_70":{"declaration":"color: var(--palette_f_70); --text_color: var(--palette_f_70);"},"color_f_80":{"declaration":"color: var(--palette_f_80); --text_color: var(--palette_f_80);"},"color_f_90":{"declaration":"color: var(--palette_f_90); --text_color: var(--palette_f_90);"},"color_f_95":{"declaration":"color: var(--palette_f_95); --text_color: var(--palette_f_95);"},"color_f_100":{"declaration":"color: var(--palette_f_100); --text_color: var(--palette_f_100);"},"color_g_00":{"declaration":"color: var(--palette_g_00); --text_color: var(--palette_g_00);"},"color_g_05":{"declaration":"color: var(--palette_g_05); --text_color: var(--palette_g_05);"},"color_g_10":{"declaration":"color: var(--palette_g_10); --text_color: var(--palette_g_10);"},"color_g_20":{"declaration":"color: var(--palette_g_20); --text_color: var(--palette_g_20);"},"color_g_30":{"declaration":"color: var(--palette_g_30); --text_color: var(--palette_g_30);"},"color_g_40":{"declaration":"color: var(--palette_g_40); --text_color: var(--palette_g_40);"},"color_g_50":{"declaration":"color: var(--palette_g_50); --text_color: var(--palette_g_50);"},"color_g_60":{"declaration":"color: var(--palette_g_60); --text_color: var(--palette_g_60);"},"color_g_70":{"declaration":"color: var(--palette_g_70); --text_color: var(--palette_g_70);"},"color_g_80":{"declaration":"color: var(--palette_g_80); --text_color: var(--palette_g_80);"},"color_g_90":{"declaration":"color: var(--palette_g_90); --text_color: var(--palette_g_90);"},"color_g_95":{"declaration":"color: var(--palette_g_95); --text_color: var(--palette_g_95);"},"color_g_100":{"declaration":"color: var(--palette_g_100); --text_color: var(--palette_g_100);"},"color_h_00":{"declaration":"color: var(--palette_h_00); --text_color: var(--palette_h_00);"},"color_h_05":{"declaration":"color: var(--palette_h_05); --text_color: var(--palette_h_05);"},"color_h_10":{"declaration":"color: var(--palette_h_10); --text_color: var(--palette_h_10);"},"color_h_20":{"declaration":"color: var(--palette_h_20); --text_color: var(--palette_h_20);"},"color_h_30":{"declaration":"color: var(--palette_h_30); --text_color: var(--palette_h_30);"},"color_h_40":{"declaration":"color: var(--palette_h_40); --text_color: var(--palette_h_40);"},"color_h_50":{"declaration":"color: var(--palette_h_50); --text_color: var(--palette_h_50);"},"color_h_60":{"declaration":"color: var(--palette_h_60); --text_color: var(--palette_h_60);"},"color_h_70":{"declaration":"color: var(--palette_h_70); --text_color: var(--palette_h_70);"},"color_h_80":{"declaration":"color: var(--palette_h_80); --text_color: var(--palette_h_80);"},"color_h_90":{"declaration":"color: var(--palette_h_90); --text_color: var(--palette_h_90);"},"color_h_95":{"declaration":"color: var(--palette_h_95); --text_color: var(--palette_h_95);"},"color_h_100":{"declaration":"color: var(--palette_h_100); --text_color: var(--palette_h_100);"},"color_i_00":{"declaration":"color: var(--palette_i_00); --text_color: var(--palette_i_00);"},"color_i_05":{"declaration":"color: var(--palette_i_05); --text_color: var(--palette_i_05);"},"color_i_10":{"declaration":"color: var(--palette_i_10); --text_color: var(--palette_i_10);"},"color_i_20":{"declaration":"color: var(--palette_i_20); --text_color: var(--palette_i_20);"},"color_i_30":{"declaration":"color: var(--palette_i_30); --text_color: var(--palette_i_30);"},"color_i_40":{"declaration":"color: var(--palette_i_40); --text_color: var(--palette_i_40);"},"color_i_50":{"declaration":"color: var(--palette_i_50); --text_color: var(--palette_i_50);"},"color_i_60":{"declaration":"color: var(--palette_i_60); --text_color: var(--palette_i_60);"},"color_i_70":{"declaration":"color: var(--palette_i_70); --text_color: var(--palette_i_70);"},"color_i_80":{"declaration":"color: var(--palette_i_80); --text_color: var(--palette_i_80);"},"color_i_90":{"declaration":"color: var(--palette_i_90); --text_color: var(--palette_i_90);"},"color_i_95":{"declaration":"color: var(--palette_i_95); --text_color: var(--palette_i_95);"},"color_i_100":{"declaration":"color: var(--palette_i_100); --text_color: var(--palette_i_100);"},"color_j_00":{"declaration":"color: var(--palette_j_00); --text_color: var(--palette_j_00);"},"color_j_05":{"declaration":"color: var(--palette_j_05); --text_color: var(--palette_j_05);"},"color_j_10":{"declaration":"color: var(--palette_j_10); --text_color: var(--palette_j_10);"},"color_j_20":{"declaration":"color: var(--palette_j_20); --text_color: var(--palette_j_20);"},"color_j_30":{"declaration":"color: var(--palette_j_30); --text_color: var(--palette_j_30);"},"color_j_40":{"declaration":"color: var(--palette_j_40); --text_color: var(--palette_j_40);"},"color_j_50":{"declaration":"color: var(--palette_j_50); --text_color: var(--palette_j_50);"},"color_j_60":{"declaration":"color: var(--palette_j_60); --text_color: var(--palette_j_60);"},"color_j_70":{"declaration":"color: var(--palette_j_70); --text_color: var(--palette_j_70);"},"color_j_80":{"declaration":"color: var(--palette_j_80); --text_color: var(--palette_j_80);"},"color_j_90":{"declaration":"color: var(--palette_j_90); --text_color: var(--palette_j_90);"},"color_j_95":{"declaration":"color: var(--palette_j_95); --text_color: var(--palette_j_95);"},"color_j_100":{"declaration":"color: var(--palette_j_100); --text_color: var(--palette_j_100);"},"bg_a_00":{"declaration":"background-color: var(--palette_a_00);"},"bg_a_05":{"declaration":"background-color: var(--palette_a_05);"},"bg_a_10":{"declaration":"background-color: var(--palette_a_10);"},"bg_a_20":{"declaration":"background-color: var(--palette_a_20);"},"bg_a_30":{"declaration":"background-color: var(--palette_a_30);"},"bg_a_40":{"declaration":"background-color: var(--palette_a_40);"},"bg_a_50":{"declaration":"background-color: var(--palette_a_50);"},"bg_a_60":{"declaration":"background-color: var(--palette_a_60);"},"bg_a_70":{"declaration":"background-color: var(--palette_a_70);"},"bg_a_80":{"declaration":"background-color: var(--palette_a_80);"},"bg_a_90":{"declaration":"background-color: var(--palette_a_90);"},"bg_a_95":{"declaration":"background-color: var(--palette_a_95);"},"bg_a_100":{"declaration":"background-color: var(--palette_a_100);"},"bg_b_00":{"declaration":"background-color: var(--palette_b_00);"},"bg_b_05":{"declaration":"background-color: var(--palette_b_05);"},"bg_b_10":{"declaration":"background-color: var(--palette_b_10);"},"bg_b_20":{"declaration":"background-color: var(--palette_b_20);"},"bg_b_30":{"declaration":"background-color: var(--palette_b_30);"},"bg_b_40":{"declaration":"background-color: var(--palette_b_40);"},"bg_b_50":{"declaration":"background-color: var(--palette_b_50);"},"bg_b_60":{"declaration":"background-color: var(--palette_b_60);"},"bg_b_70":{"declaration":"background-color: var(--palette_b_70);"},"bg_b_80":{"declaration":"background-color: var(--palette_b_80);"},"bg_b_90":{"declaration":"background-color: var(--palette_b_90);"},"bg_b_95":{"declaration":"background-color: var(--palette_b_95);"},"bg_b_100":{"declaration":"background-color: var(--palette_b_100);"},"bg_c_00":{"declaration":"background-color: var(--palette_c_00);"},"bg_c_05":{"declaration":"background-color: var(--palette_c_05);"},"bg_c_10":{"declaration":"background-color: var(--palette_c_10);"},"bg_c_20":{"declaration":"background-color: var(--palette_c_20);"},"bg_c_30":{"declaration":"background-color: var(--palette_c_30);"},"bg_c_40":{"declaration":"background-color: var(--palette_c_40);"},"bg_c_50":{"declaration":"background-color: var(--palette_c_50);"},"bg_c_60":{"declaration":"background-color: var(--palette_c_60);"},"bg_c_70":{"declaration":"background-color: var(--palette_c_70);"},"bg_c_80":{"declaration":"background-color: var(--palette_c_80);"},"bg_c_90":{"declaration":"background-color: var(--palette_c_90);"},"bg_c_95":{"declaration":"background-color: var(--palette_c_95);"},"bg_c_100":{"declaration":"background-color: var(--palette_c_100);"},"bg_d_00":{"declaration":"background-color: var(--palette_d_00);"},"bg_d_05":{"declaration":"background-color: var(--palette_d_05);"},"bg_d_10":{"declaration":"background-color: var(--palette_d_10);"},"bg_d_20":{"declaration":"background-color: var(--palette_d_20);"},"bg_d_30":{"declaration":"background-color: var(--palette_d_30);"},"bg_d_40":{"declaration":"background-color: var(--palette_d_40);"},"bg_d_50":{"declaration":"background-color: var(--palette_d_50);"},"bg_d_60":{"declaration":"background-color: var(--palette_d_60);"},"bg_d_70":{"declaration":"background-color: var(--palette_d_70);"},"bg_d_80":{"declaration":"background-color: var(--palette_d_80);"},"bg_d_90":{"declaration":"background-color: var(--palette_d_90);"},"bg_d_95":{"declaration":"background-color: var(--palette_d_95);"},"bg_d_100":{"declaration":"background-color: var(--palette_d_100);"},"bg_e_00":{"declaration":"background-color: var(--palette_e_00);"},"bg_e_05":{"declaration":"background-color: var(--palette_e_05);"},"bg_e_10":{"declaration":"background-color: var(--palette_e_10);"},"bg_e_20":{"declaration":"background-color: var(--palette_e_20);"},"bg_e_30":{"declaration":"background-color: var(--palette_e_30);"},"bg_e_40":{"declaration":"background-color: var(--palette_e_40);"},"bg_e_50":{"declaration":"background-color: var(--palette_e_50);"},"bg_e_60":{"declaration":"background-color: var(--palette_e_60);"},"bg_e_70":{"declaration":"background-color: var(--palette_e_70);"},"bg_e_80":{"declaration":"background-color: var(--palette_e_80);"},"bg_e_90":{"declaration":"background-color: var(--palette_e_90);"},"bg_e_95":{"declaration":"background-color: var(--palette_e_95);"},"bg_e_100":{"declaration":"background-color: var(--palette_e_100);"},"bg_f_00":{"declaration":"background-color: var(--palette_f_00);"},"bg_f_05":{"declaration":"background-color: var(--palette_f_05);"},"bg_f_10":{"declaration":"background-color: var(--palette_f_10);"},"bg_f_20":{"declaration":"background-color: var(--palette_f_20);"},"bg_f_30":{"declaration":"background-color: var(--palette_f_30);"},"bg_f_40":{"declaration":"background-color: var(--palette_f_40);"},"bg_f_50":{"declaration":"background-color: var(--palette_f_50);"},"bg_f_60":{"declaration":"background-color: var(--palette_f_60);"},"bg_f_70":{"declaration":"background-color: var(--palette_f_70);"},"bg_f_80":{"declaration":"background-color: var(--palette_f_80);"},"bg_f_90":{"declaration":"background-color: var(--palette_f_90);"},"bg_f_95":{"declaration":"background-color: var(--palette_f_95);"},"bg_f_100":{"declaration":"background-color: var(--palette_f_100);"},"bg_g_00":{"declaration":"background-color: var(--palette_g_00);"},"bg_g_05":{"declaration":"background-color: var(--palette_g_05);"},"bg_g_10":{"declaration":"background-color: var(--palette_g_10);"},"bg_g_20":{"declaration":"background-color: var(--palette_g_20);"},"bg_g_30":{"declaration":"background-color: var(--palette_g_30);"},"bg_g_40":{"declaration":"background-color: var(--palette_g_40);"},"bg_g_50":{"declaration":"background-color: var(--palette_g_50);"},"bg_g_60":{"declaration":"background-color: var(--palette_g_60);"},"bg_g_70":{"declaration":"background-color: var(--palette_g_70);"},"bg_g_80":{"declaration":"background-color: var(--palette_g_80);"},"bg_g_90":{"declaration":"background-color: var(--palette_g_90);"},"bg_g_95":{"declaration":"background-color: var(--palette_g_95);"},"bg_g_100":{"declaration":"background-color: var(--palette_g_100);"},"bg_h_00":{"declaration":"background-color: var(--palette_h_00);"},"bg_h_05":{"declaration":"background-color: var(--palette_h_05);"},"bg_h_10":{"declaration":"background-color: var(--palette_h_10);"},"bg_h_20":{"declaration":"background-color: var(--palette_h_20);"},"bg_h_30":{"declaration":"background-color: var(--palette_h_30);"},"bg_h_40":{"declaration":"background-color: var(--palette_h_40);"},"bg_h_50":{"declaration":"background-color: var(--palette_h_50);"},"bg_h_60":{"declaration":"background-color: var(--palette_h_60);"},"bg_h_70":{"declaration":"background-color: var(--palette_h_70);"},"bg_h_80":{"declaration":"background-color: var(--palette_h_80);"},"bg_h_90":{"declaration":"background-color: var(--palette_h_90);"},"bg_h_95":{"declaration":"background-color: var(--palette_h_95);"},"bg_h_100":{"declaration":"background-color: var(--palette_h_100);"},"bg_i_00":{"declaration":"background-color: var(--palette_i_00);"},"bg_i_05":{"declaration":"background-color: var(--palette_i_05);"},"bg_i_10":{"declaration":"background-color: var(--palette_i_10);"},"bg_i_20":{"declaration":"background-color: var(--palette_i_20);"},"bg_i_30":{"declaration":"background-color: var(--palette_i_30);"},"bg_i_40":{"declaration":"background-color: var(--palette_i_40);"},"bg_i_50":{"declaration":"background-color: var(--palette_i_50);"},"bg_i_60":{"declaration":"background-color: var(--palette_i_60);"},"bg_i_70":{"declaration":"background-color: var(--palette_i_70);"},"bg_i_80":{"declaration":"background-color: var(--palette_i_80);"},"bg_i_90":{"declaration":"background-color: var(--palette_i_90);"},"bg_i_95":{"declaration":"background-color: var(--palette_i_95);"},"bg_i_100":{"declaration":"background-color: var(--palette_i_100);"},"bg_j_00":{"declaration":"background-color: var(--palette_j_00);"},"bg_j_05":{"declaration":"background-color: var(--palette_j_05);"},"bg_j_10":{"declaration":"background-color: var(--palette_j_10);"},"bg_j_20":{"declaration":"background-color: var(--palette_j_20);"},"bg_j_30":{"declaration":"background-color: var(--palette_j_30);"},"bg_j_40":{"declaration":"background-color: var(--palette_j_40);"},"bg_j_50":{"declaration":"background-color: var(--palette_j_50);"},"bg_j_60":{"declaration":"background-color: var(--palette_j_60);"},"bg_j_70":{"declaration":"background-color: var(--palette_j_70);"},"bg_j_80":{"declaration":"background-color: var(--palette_j_80);"},"bg_j_90":{"declaration":"background-color: var(--palette_j_90);"},"bg_j_95":{"declaration":"background-color: var(--palette_j_95);"},"bg_j_100":{"declaration":"background-color: var(--palette_j_100);"},"accent_00":{"declaration":"color: var(--accent_00); --text_color: var(--accent_00);"},"accent_05":{"declaration":"color: var(--accent_05); --text_color: var(--accent_05);"},"accent_10":{"declaration":"color: var(--accent_10); --text_color: var(--accent_10);"},"accent_20":{"declaration":"color: var(--accent_20); --text_color: var(--accent_20);"},"accent_30":{"declaration":"color: var(--accent_30); --text_color: var(--accent_30);"},"accent_40":{"declaration":"color: var(--accent_40); --text_color: var(--accent_40);"},"accent_50":{"declaration":"color: var(--accent_50); --text_color: var(--accent_50);"},"accent_60":{"declaration":"color: var(--accent_60); --text_color: var(--accent_60);"},"accent_70":{"declaration":"color: var(--accent_70); --text_color: var(--accent_70);"},"accent_80":{"declaration":"color: var(--accent_80); --text_color: var(--accent_80);"},"accent_90":{"declaration":"color: var(--accent_90); --text_color: var(--accent_90);"},"accent_95":{"declaration":"color: var(--accent_95); --text_color: var(--accent_95);"},"accent_100":{"declaration":"color: var(--accent_100); --text_color: var(--accent_100);"},"positive_00":{"declaration":"color: var(--positive_00); --text_color: var(--positive_00);"},"positive_05":{"declaration":"color: var(--positive_05); --text_color: var(--positive_05);"},"positive_10":{"declaration":"color: var(--positive_10); --text_color: var(--positive_10);"},"positive_20":{"declaration":"color: var(--positive_20); --text_color: var(--positive_20);"},"positive_30":{"declaration":"color: var(--positive_30); --text_color: var(--positive_30);"},"positive_40":{"declaration":"color: var(--positive_40); --text_color: var(--positive_40);"},"positive_50":{"declaration":"color: var(--positive_50); --text_color: var(--positive_50);"},"positive_60":{"declaration":"color: var(--positive_60); --text_color: var(--positive_60);"},"positive_70":{"declaration":"color: var(--positive_70); --text_color: var(--positive_70);"},"positive_80":{"declaration":"color: var(--positive_80); --text_color: var(--positive_80);"},"positive_90":{"declaration":"color: var(--positive_90); --text_color: var(--positive_90);"},"positive_95":{"declaration":"color: var(--positive_95); --text_color: var(--positive_95);"},"positive_100":{"declaration":"color: var(--positive_100); --text_color: var(--positive_100);"},"negative_00":{"declaration":"color: var(--negative_00); --text_color: var(--negative_00);"},"negative_05":{"declaration":"color: var(--negative_05); --text_color: var(--negative_05);"},"negative_10":{"declaration":"color: var(--negative_10); --text_color: var(--negative_10);"},"negative_20":{"declaration":"color: var(--negative_20); --text_color: var(--negative_20);"},"negative_30":{"declaration":"color: var(--negative_30); --text_color: var(--negative_30);"},"negative_40":{"declaration":"color: var(--negative_40); --text_color: var(--negative_40);"},"negative_50":{"declaration":"color: var(--negative_50); --text_color: var(--negative_50);"},"negative_60":{"declaration":"color: var(--negative_60); --text_color: var(--negative_60);"},"negative_70":{"declaration":"color: var(--negative_70); --text_color: var(--negative_70);"},"negative_80":{"declaration":"color: var(--negative_80); --text_color: var(--negative_80);"},"negative_90":{"declaration":"color: var(--negative_90); --text_color: var(--negative_90);"},"negative_95":{"declaration":"color: var(--negative_95); --text_color: var(--negative_95);"},"negative_100":{"declaration":"color: var(--negative_100); --text_color: var(--negative_100);"},"caution_00":{"declaration":"color: var(--caution_00); --text_color: var(--caution_00);"},"caution_05":{"declaration":"color: var(--caution_05); --text_color: var(--caution_05);"},"caution_10":{"declaration":"color: var(--caution_10); --text_color: var(--caution_10);"},"caution_20":{"declaration":"color: var(--caution_20); --text_color: var(--caution_20);"},"caution_30":{"declaration":"color: var(--caution_30); --text_color: var(--caution_30);"},"caution_40":{"declaration":"color: var(--caution_40); --text_color: var(--caution_40);"},"caution_50":{"declaration":"color: var(--caution_50); --text_color: var(--caution_50);"},"caution_60":{"declaration":"color: var(--caution_60); --text_color: var(--caution_60);"},"caution_70":{"declaration":"color: var(--caution_70); --text_color: var(--caution_70);"},"caution_80":{"declaration":"color: var(--caution_80); --text_color: var(--caution_80);"},"caution_90":{"declaration":"color: var(--caution_90); --text_color: var(--caution_90);"},"caution_95":{"declaration":"color: var(--caution_95); --text_color: var(--caution_95);"},"caution_100":{"declaration":"color: var(--caution_100); --text_color: var(--caution_100);"},"info_00":{"declaration":"color: var(--info_00); --text_color: var(--info_00);"},"info_05":{"declaration":"color: var(--info_05); --text_color: var(--info_05);"},"info_10":{"declaration":"color: var(--info_10); --text_color: var(--info_10);"},"info_20":{"declaration":"color: var(--info_20); --text_color: var(--info_20);"},"info_30":{"declaration":"color: var(--info_30); --text_color: var(--info_30);"},"info_40":{"declaration":"color: var(--info_40); --text_color: var(--info_40);"},"info_50":{"declaration":"color: var(--info_50); --text_color: var(--info_50);"},"info_60":{"declaration":"color: var(--info_60); --text_color: var(--info_60);"},"info_70":{"declaration":"color: var(--info_70); --text_color: var(--info_70);"},"info_80":{"declaration":"color: var(--info_80); --text_color: var(--info_80);"},"info_90":{"declaration":"color: var(--info_90); --text_color: var(--info_90);"},"info_95":{"declaration":"color: var(--info_95); --text_color: var(--info_95);"},"info_100":{"declaration":"color: var(--info_100); --text_color: var(--info_100);"},"bg_accent_00":{"declaration":"background-color: var(--accent_00);"},"bg_accent_05":{"declaration":"background-color: var(--accent_05);"},"bg_accent_10":{"declaration":"background-color: var(--accent_10);"},"bg_accent_20":{"declaration":"background-color: var(--accent_20);"},"bg_accent_30":{"declaration":"background-color: var(--accent_30);"},"bg_accent_40":{"declaration":"background-color: var(--accent_40);"},"bg_accent_50":{"declaration":"background-color: var(--accent_50);"},"bg_accent_60":{"declaration":"background-color: var(--accent_60);"},"bg_accent_70":{"declaration":"background-color: var(--accent_70);"},"bg_accent_80":{"declaration":"background-color: var(--accent_80);"},"bg_accent_90":{"declaration":"background-color: var(--accent_90);"},"bg_accent_95":{"declaration":"background-color: var(--accent_95);"},"bg_accent_100":{"declaration":"background-color: var(--accent_100);"},"bg_positive_00":{"declaration":"background-color: var(--positive_00);"},"bg_positive_05":{"declaration":"background-color: var(--positive_05);"},"bg_positive_10":{"declaration":"background-color: var(--positive_10);"},"bg_positive_20":{"declaration":"background-color: var(--positive_20);"},"bg_positive_30":{"declaration":"background-color: var(--positive_30);"},"bg_positive_40":{"declaration":"background-color: var(--positive_40);"},"bg_positive_50":{"declaration":"background-color: var(--positive_50);"},"bg_positive_60":{"declaration":"background-color: var(--positive_60);"},"bg_positive_70":{"declaration":"background-color: var(--positive_70);"},"bg_positive_80":{"declaration":"background-color: var(--positive_80);"},"bg_positive_90":{"declaration":"background-color: var(--positive_90);"},"bg_positive_95":{"declaration":"background-color: var(--positive_95);"},"bg_positive_100":{"declaration":"background-color: var(--positive_100);"},"bg_negative_00":{"declaration":"background-color: var(--negative_00);"},"bg_negative_05":{"declaration":"background-color: var(--negative_05);"},"bg_negative_10":{"declaration":"background-color: var(--negative_10);"},"bg_negative_20":{"declaration":"background-color: var(--negative_20);"},"bg_negative_30":{"declaration":"background-color: var(--negative_30);"},"bg_negative_40":{"declaration":"background-color: var(--negative_40);"},"bg_negative_50":{"declaration":"background-color: var(--negative_50);"},"bg_negative_60":{"declaration":"background-color: var(--negative_60);"},"bg_negative_70":{"declaration":"background-color: var(--negative_70);"},"bg_negative_80":{"declaration":"background-color: var(--negative_80);"},"bg_negative_90":{"declaration":"background-color: var(--negative_90);"},"bg_negative_95":{"declaration":"background-color: var(--negative_95);"},"bg_negative_100":{"declaration":"background-color: var(--negative_100);"},"bg_caution_00":{"declaration":"background-color: var(--caution_00);"},"bg_caution_05":{"declaration":"background-color: var(--caution_05);"},"bg_caution_10":{"declaration":"background-color: var(--caution_10);"},"bg_caution_20":{"declaration":"background-color: var(--caution_20);"},"bg_caution_30":{"declaration":"background-color: var(--caution_30);"},"bg_caution_40":{"declaration":"background-color: var(--caution_40);"},"bg_caution_50":{"declaration":"background-color: var(--caution_50);"},"bg_caution_60":{"declaration":"background-color: var(--caution_60);"},"bg_caution_70":{"declaration":"background-color: var(--caution_70);"},"bg_caution_80":{"declaration":"background-color: var(--caution_80);"},"bg_caution_90":{"declaration":"background-color: var(--caution_90);"},"bg_caution_95":{"declaration":"background-color: var(--caution_95);"},"bg_caution_100":{"declaration":"background-color: var(--caution_100);"},"bg_info_00":{"declaration":"background-color: var(--info_00);"},"bg_info_05":{"declaration":"background-color: var(--info_05);"},"bg_info_10":{"declaration":"background-color: var(--info_10);"},"bg_info_20":{"declaration":"background-color: var(--info_20);"},"bg_info_30":{"declaration":"background-color: var(--info_30);"},"bg_info_40":{"declaration":"background-color: var(--info_40);"},"bg_info_50":{"declaration":"background-color: var(--info_50);"},"bg_info_60":{"declaration":"background-color: var(--info_60);"},"bg_info_70":{"declaration":"background-color: var(--info_70);"},"bg_info_80":{"declaration":"background-color: var(--info_80);"},"bg_info_90":{"declaration":"background-color: var(--info_90);"},"bg_info_95":{"declaration":"background-color: var(--info_95);"},"bg_info_100":{"declaration":"background-color: var(--info_100);"},"darken_00":{"declaration":"background-color: var(--darken_00);"},"darken_05":{"declaration":"background-color: var(--darken_05);"},"darken_10":{"declaration":"background-color: var(--darken_10);"},"darken_20":{"declaration":"background-color: var(--darken_20);"},"darken_30":{"declaration":"background-color: var(--darken_30);"},"darken_40":{"declaration":"background-color: var(--darken_40);"},"darken_50":{"declaration":"background-color: var(--darken_50);"},"darken_60":{"declaration":"background-color: var(--darken_60);"},"darken_70":{"declaration":"background-color: var(--darken_70);"},"darken_80":{"declaration":"background-color: var(--darken_80);"},"darken_90":{"declaration":"background-color: var(--darken_90);"},"darken_95":{"declaration":"background-color: var(--darken_95);"},"darken_100":{"declaration":"background-color: var(--darken_100);"},"lighten_00":{"declaration":"background-color: var(--lighten_00);"},"lighten_05":{"declaration":"background-color: var(--lighten_05);"},"lighten_10":{"declaration":"background-color: var(--lighten_10);"},"lighten_20":{"declaration":"background-color: var(--lighten_20);"},"lighten_30":{"declaration":"background-color: var(--lighten_30);"},"lighten_40":{"declaration":"background-color: var(--lighten_40);"},"lighten_50":{"declaration":"background-color: var(--lighten_50);"},"lighten_60":{"declaration":"background-color: var(--lighten_60);"},"lighten_70":{"declaration":"background-color: var(--lighten_70);"},"lighten_80":{"declaration":"background-color: var(--lighten_80);"},"lighten_90":{"declaration":"background-color: var(--lighten_90);"},"lighten_95":{"declaration":"background-color: var(--lighten_95);"},"lighten_100":{"declaration":"background-color: var(--lighten_100);"},"border_color_00":{"declaration":"border-color: var(--border_color_00); --border_color: var(--border_color_00);"},"border_color_05":{"declaration":"border-color: var(--border_color_05); --border_color: var(--border_color_05);"},"border_color_10":{"declaration":"border-color: var(--border_color_10); --border_color: var(--border_color_10);"},"border_color_20":{"declaration":"border-color: var(--border_color_20); --border_color: var(--border_color_20);"},"border_color_30":{"declaration":"border-color: var(--border_color_30); --border_color: var(--border_color_30);"},"border_color_40":{"declaration":"border-color: var(--border_color_40); --border_color: var(--border_color_40);"},"border_color_50":{"declaration":"border-color: var(--border_color_50); --border_color: var(--border_color_50);"},"border_color_60":{"declaration":"border-color: var(--border_color_60); --border_color: var(--border_color_60);"},"border_color_70":{"declaration":"border-color: var(--border_color_70); --border_color: var(--border_color_70);"},"border_color_80":{"declaration":"border-color: var(--border_color_80); --border_color: var(--border_color_80);"},"border_color_90":{"declaration":"border-color: var(--border_color_90); --border_color: var(--border_color_90);"},"border_color_95":{"declaration":"border-color: var(--border_color_95); --border_color: var(--border_color_95);"},"border_color_100":{"declaration":"border-color: var(--border_color_100); --border_color: var(--border_color_100);"},"border_a_00":{"declaration":"border-color: var(--palette_a_00); --border_color: var(--palette_a_00);"},"border_a_05":{"declaration":"border-color: var(--palette_a_05); --border_color: var(--palette_a_05);"},"border_a_10":{"declaration":"border-color: var(--palette_a_10); --border_color: var(--palette_a_10);"},"border_a_20":{"declaration":"border-color: var(--palette_a_20); --border_color: var(--palette_a_20);"},"border_a_30":{"declaration":"border-color: var(--palette_a_30); --border_color: var(--palette_a_30);"},"border_a_40":{"declaration":"border-color: var(--palette_a_40); --border_color: var(--palette_a_40);"},"border_a_50":{"declaration":"border-color: var(--palette_a_50); --border_color: var(--palette_a_50);"},"border_a_60":{"declaration":"border-color: var(--palette_a_60); --border_color: var(--palette_a_60);"},"border_a_70":{"declaration":"border-color: var(--palette_a_70); --border_color: var(--palette_a_70);"},"border_a_80":{"declaration":"border-color: var(--palette_a_80); --border_color: var(--palette_a_80);"},"border_a_90":{"declaration":"border-color: var(--palette_a_90); --border_color: var(--palette_a_90);"},"border_a_95":{"declaration":"border-color: var(--palette_a_95); --border_color: var(--palette_a_95);"},"border_a_100":{"declaration":"border-color: var(--palette_a_100); --border_color: var(--palette_a_100);"},"border_b_00":{"declaration":"border-color: var(--palette_b_00); --border_color: var(--palette_b_00);"},"border_b_05":{"declaration":"border-color: var(--palette_b_05); --border_color: var(--palette_b_05);"},"border_b_10":{"declaration":"border-color: var(--palette_b_10); --border_color: var(--palette_b_10);"},"border_b_20":{"declaration":"border-color: var(--palette_b_20); --border_color: var(--palette_b_20);"},"border_b_30":{"declaration":"border-color: var(--palette_b_30); --border_color: var(--palette_b_30);"},"border_b_40":{"declaration":"border-color: var(--palette_b_40); --border_color: var(--palette_b_40);"},"border_b_50":{"declaration":"border-color: var(--palette_b_50); --border_color: var(--palette_b_50);"},"border_b_60":{"declaration":"border-color: var(--palette_b_60); --border_color: var(--palette_b_60);"},"border_b_70":{"declaration":"border-color: var(--palette_b_70); --border_color: var(--palette_b_70);"},"border_b_80":{"declaration":"border-color: var(--palette_b_80); --border_color: var(--palette_b_80);"},"border_b_90":{"declaration":"border-color: var(--palette_b_90); --border_color: var(--palette_b_90);"},"border_b_95":{"declaration":"border-color: var(--palette_b_95); --border_color: var(--palette_b_95);"},"border_b_100":{"declaration":"border-color: var(--palette_b_100); --border_color: var(--palette_b_100);"},"border_c_00":{"declaration":"border-color: var(--palette_c_00); --border_color: var(--palette_c_00);"},"border_c_05":{"declaration":"border-color: var(--palette_c_05); --border_color: var(--palette_c_05);"},"border_c_10":{"declaration":"border-color: var(--palette_c_10); --border_color: var(--palette_c_10);"},"border_c_20":{"declaration":"border-color: var(--palette_c_20); --border_color: var(--palette_c_20);"},"border_c_30":{"declaration":"border-color: var(--palette_c_30); --border_color: var(--palette_c_30);"},"border_c_40":{"declaration":"border-color: var(--palette_c_40); --border_color: var(--palette_c_40);"},"border_c_50":{"declaration":"border-color: var(--palette_c_50); --border_color: var(--palette_c_50);"},"border_c_60":{"declaration":"border-color: var(--palette_c_60); --border_color: var(--palette_c_60);"},"border_c_70":{"declaration":"border-color: var(--palette_c_70); --border_color: var(--palette_c_70);"},"border_c_80":{"declaration":"border-color: var(--palette_c_80); --border_color: var(--palette_c_80);"},"border_c_90":{"declaration":"border-color: var(--palette_c_90); --border_color: var(--palette_c_90);"},"border_c_95":{"declaration":"border-color: var(--palette_c_95); --border_color: var(--palette_c_95);"},"border_c_100":{"declaration":"border-color: var(--palette_c_100); --border_color: var(--palette_c_100);"},"border_d_00":{"declaration":"border-color: var(--palette_d_00); --border_color: var(--palette_d_00);"},"border_d_05":{"declaration":"border-color: var(--palette_d_05); --border_color: var(--palette_d_05);"},"border_d_10":{"declaration":"border-color: var(--palette_d_10); --border_color: var(--palette_d_10);"},"border_d_20":{"declaration":"border-color: var(--palette_d_20); --border_color: var(--palette_d_20);"},"border_d_30":{"declaration":"border-color: var(--palette_d_30); --border_color: var(--palette_d_30);"},"border_d_40":{"declaration":"border-color: var(--palette_d_40); --border_color: var(--palette_d_40);"},"border_d_50":{"declaration":"border-color: var(--palette_d_50); --border_color: var(--palette_d_50);"},"border_d_60":{"declaration":"border-color: var(--palette_d_60); --border_color: var(--palette_d_60);"},"border_d_70":{"declaration":"border-color: var(--palette_d_70); --border_color: var(--palette_d_70);"},"border_d_80":{"declaration":"border-color: var(--palette_d_80); --border_color: var(--palette_d_80);"},"border_d_90":{"declaration":"border-color: var(--palette_d_90); --border_color: var(--palette_d_90);"},"border_d_95":{"declaration":"border-color: var(--palette_d_95); --border_color: var(--palette_d_95);"},"border_d_100":{"declaration":"border-color: var(--palette_d_100); --border_color: var(--palette_d_100);"},"border_e_00":{"declaration":"border-color: var(--palette_e_00); --border_color: var(--palette_e_00);"},"border_e_05":{"declaration":"border-color: var(--palette_e_05); --border_color: var(--palette_e_05);"},"border_e_10":{"declaration":"border-color: var(--palette_e_10); --border_color: var(--palette_e_10);"},"border_e_20":{"declaration":"border-color: var(--palette_e_20); --border_color: var(--palette_e_20);"},"border_e_30":{"declaration":"border-color: var(--palette_e_30); --border_color: var(--palette_e_30);"},"border_e_40":{"declaration":"border-color: var(--palette_e_40); --border_color: var(--palette_e_40);"},"border_e_50":{"declaration":"border-color: var(--palette_e_50); --border_color: var(--palette_e_50);"},"border_e_60":{"declaration":"border-color: var(--palette_e_60); --border_color: var(--palette_e_60);"},"border_e_70":{"declaration":"border-color: var(--palette_e_70); --border_color: var(--palette_e_70);"},"border_e_80":{"declaration":"border-color: var(--palette_e_80); --border_color: var(--palette_e_80);"},"border_e_90":{"declaration":"border-color: var(--palette_e_90); --border_color: var(--palette_e_90);"},"border_e_95":{"declaration":"border-color: var(--palette_e_95); --border_color: var(--palette_e_95);"},"border_e_100":{"declaration":"border-color: var(--palette_e_100); --border_color: var(--palette_e_100);"},"border_f_00":{"declaration":"border-color: var(--palette_f_00); --border_color: var(--palette_f_00);"},"border_f_05":{"declaration":"border-color: var(--palette_f_05); --border_color: var(--palette_f_05);"},"border_f_10":{"declaration":"border-color: var(--palette_f_10); --border_color: var(--palette_f_10);"},"border_f_20":{"declaration":"border-color: var(--palette_f_20); --border_color: var(--palette_f_20);"},"border_f_30":{"declaration":"border-color: var(--palette_f_30); --border_color: var(--palette_f_30);"},"border_f_40":{"declaration":"border-color: var(--palette_f_40); --border_color: var(--palette_f_40);"},"border_f_50":{"declaration":"border-color: var(--palette_f_50); --border_color: var(--palette_f_50);"},"border_f_60":{"declaration":"border-color: var(--palette_f_60); --border_color: var(--palette_f_60);"},"border_f_70":{"declaration":"border-color: var(--palette_f_70); --border_color: var(--palette_f_70);"},"border_f_80":{"declaration":"border-color: var(--palette_f_80); --border_color: var(--palette_f_80);"},"border_f_90":{"declaration":"border-color: var(--palette_f_90); --border_color: var(--palette_f_90);"},"border_f_95":{"declaration":"border-color: var(--palette_f_95); --border_color: var(--palette_f_95);"},"border_f_100":{"declaration":"border-color: var(--palette_f_100); --border_color: var(--palette_f_100);"},"border_g_00":{"declaration":"border-color: var(--palette_g_00); --border_color: var(--palette_g_00);"},"border_g_05":{"declaration":"border-color: var(--palette_g_05); --border_color: var(--palette_g_05);"},"border_g_10":{"declaration":"border-color: var(--palette_g_10); --border_color: var(--palette_g_10);"},"border_g_20":{"declaration":"border-color: var(--palette_g_20); --border_color: var(--palette_g_20);"},"border_g_30":{"declaration":"border-color: var(--palette_g_30); --border_color: var(--palette_g_30);"},"border_g_40":{"declaration":"border-color: var(--palette_g_40); --border_color: var(--palette_g_40);"},"border_g_50":{"declaration":"border-color: var(--palette_g_50); --border_color: var(--palette_g_50);"},"border_g_60":{"declaration":"border-color: var(--palette_g_60); --border_color: var(--palette_g_60);"},"border_g_70":{"declaration":"border-color: var(--palette_g_70); --border_color: var(--palette_g_70);"},"border_g_80":{"declaration":"border-color: var(--palette_g_80); --border_color: var(--palette_g_80);"},"border_g_90":{"declaration":"border-color: var(--palette_g_90); --border_color: var(--palette_g_90);"},"border_g_95":{"declaration":"border-color: var(--palette_g_95); --border_color: var(--palette_g_95);"},"border_g_100":{"declaration":"border-color: var(--palette_g_100); --border_color: var(--palette_g_100);"},"border_h_00":{"declaration":"border-color: var(--palette_h_00); --border_color: var(--palette_h_00);"},"border_h_05":{"declaration":"border-color: var(--palette_h_05); --border_color: var(--palette_h_05);"},"border_h_10":{"declaration":"border-color: var(--palette_h_10); --border_color: var(--palette_h_10);"},"border_h_20":{"declaration":"border-color: var(--palette_h_20); --border_color: var(--palette_h_20);"},"border_h_30":{"declaration":"border-color: var(--palette_h_30); --border_color: var(--palette_h_30);"},"border_h_40":{"declaration":"border-color: var(--palette_h_40); --border_color: var(--palette_h_40);"},"border_h_50":{"declaration":"border-color: var(--palette_h_50); --border_color: var(--palette_h_50);"},"border_h_60":{"declaration":"border-color: var(--palette_h_60); --border_color: var(--palette_h_60);"},"border_h_70":{"declaration":"border-color: var(--palette_h_70); --border_color: var(--palette_h_70);"},"border_h_80":{"declaration":"border-color: var(--palette_h_80); --border_color: var(--palette_h_80);"},"border_h_90":{"declaration":"border-color: var(--palette_h_90); --border_color: var(--palette_h_90);"},"border_h_95":{"declaration":"border-color: var(--palette_h_95); --border_color: var(--palette_h_95);"},"border_h_100":{"declaration":"border-color: var(--palette_h_100); --border_color: var(--palette_h_100);"},"border_i_00":{"declaration":"border-color: var(--palette_i_00); --border_color: var(--palette_i_00);"},"border_i_05":{"declaration":"border-color: var(--palette_i_05); --border_color: var(--palette_i_05);"},"border_i_10":{"declaration":"border-color: var(--palette_i_10); --border_color: var(--palette_i_10);"},"border_i_20":{"declaration":"border-color: var(--palette_i_20); --border_color: var(--palette_i_20);"},"border_i_30":{"declaration":"border-color: var(--palette_i_30); --border_color: var(--palette_i_30);"},"border_i_40":{"declaration":"border-color: var(--palette_i_40); --border_color: var(--palette_i_40);"},"border_i_50":{"declaration":"border-color: var(--palette_i_50); --border_color: var(--palette_i_50);"},"border_i_60":{"declaration":"border-color: var(--palette_i_60); --border_color: var(--palette_i_60);"},"border_i_70":{"declaration":"border-color: var(--palette_i_70); --border_color: var(--palette_i_70);"},"border_i_80":{"declaration":"border-color: var(--palette_i_80); --border_color: var(--palette_i_80);"},"border_i_90":{"declaration":"border-color: var(--palette_i_90); --border_color: var(--palette_i_90);"},"border_i_95":{"declaration":"border-color: var(--palette_i_95); --border_color: var(--palette_i_95);"},"border_i_100":{"declaration":"border-color: var(--palette_i_100); --border_color: var(--palette_i_100);"},"border_j_00":{"declaration":"border-color: var(--palette_j_00); --border_color: var(--palette_j_00);"},"border_j_05":{"declaration":"border-color: var(--palette_j_05); --border_color: var(--palette_j_05);"},"border_j_10":{"declaration":"border-color: var(--palette_j_10); --border_color: var(--palette_j_10);"},"border_j_20":{"declaration":"border-color: var(--palette_j_20); --border_color: var(--palette_j_20);"},"border_j_30":{"declaration":"border-color: var(--palette_j_30); --border_color: var(--palette_j_30);"},"border_j_40":{"declaration":"border-color: var(--palette_j_40); --border_color: var(--palette_j_40);"},"border_j_50":{"declaration":"border-color: var(--palette_j_50); --border_color: var(--palette_j_50);"},"border_j_60":{"declaration":"border-color: var(--palette_j_60); --border_color: var(--palette_j_60);"},"border_j_70":{"declaration":"border-color: var(--palette_j_70); --border_color: var(--palette_j_70);"},"border_j_80":{"declaration":"border-color: var(--palette_j_80); --border_color: var(--palette_j_80);"},"border_j_90":{"declaration":"border-color: var(--palette_j_90); --border_color: var(--palette_j_90);"},"border_j_95":{"declaration":"border-color: var(--palette_j_95); --border_color: var(--palette_j_95);"},"border_j_100":{"declaration":"border-color: var(--palette_j_100); --border_color: var(--palette_j_100);"},"outline_color_00":{"declaration":"outline-color: var(--shade_00);"},"outline_color_05":{"declaration":"outline-color: var(--shade_05);"},"outline_color_10":{"declaration":"outline-color: var(--shade_10);"},"outline_color_20":{"declaration":"outline-color: var(--shade_20);"},"outline_color_30":{"declaration":"outline-color: var(--shade_30);"},"outline_color_40":{"declaration":"outline-color: var(--shade_40);"},"outline_color_50":{"declaration":"outline-color: var(--shade_50);"},"outline_color_60":{"declaration":"outline-color: var(--shade_60);"},"outline_color_70":{"declaration":"outline-color: var(--shade_70);"},"outline_color_80":{"declaration":"outline-color: var(--shade_80);"},"outline_color_90":{"declaration":"outline-color: var(--shade_90);"},"outline_color_95":{"declaration":"outline-color: var(--shade_95);"},"outline_color_100":{"declaration":"outline-color: var(--shade_100);"},"outline_a_00":{"declaration":"outline-color: var(--palette_a_00); --outline_color: var(--palette_a_00);"},"outline_a_05":{"declaration":"outline-color: var(--palette_a_05); --outline_color: var(--palette_a_05);"},"outline_a_10":{"declaration":"outline-color: var(--palette_a_10); --outline_color: var(--palette_a_10);"},"outline_a_20":{"declaration":"outline-color: var(--palette_a_20); --outline_color: var(--palette_a_20);"},"outline_a_30":{"declaration":"outline-color: var(--palette_a_30); --outline_color: var(--palette_a_30);"},"outline_a_40":{"declaration":"outline-color: var(--palette_a_40); --outline_color: var(--palette_a_40);"},"outline_a_50":{"declaration":"outline-color: var(--palette_a_50); --outline_color: var(--palette_a_50);"},"outline_a_60":{"declaration":"outline-color: var(--palette_a_60); --outline_color: var(--palette_a_60);"},"outline_a_70":{"declaration":"outline-color: var(--palette_a_70); --outline_color: var(--palette_a_70);"},"outline_a_80":{"declaration":"outline-color: var(--palette_a_80); --outline_color: var(--palette_a_80);"},"outline_a_90":{"declaration":"outline-color: var(--palette_a_90); --outline_color: var(--palette_a_90);"},"outline_a_95":{"declaration":"outline-color: var(--palette_a_95); --outline_color: var(--palette_a_95);"},"outline_a_100":{"declaration":"outline-color: var(--palette_a_100); --outline_color: var(--palette_a_100);"},"outline_b_00":{"declaration":"outline-color: var(--palette_b_00); --outline_color: var(--palette_b_00);"},"outline_b_05":{"declaration":"outline-color: var(--palette_b_05); --outline_color: var(--palette_b_05);"},"outline_b_10":{"declaration":"outline-color: var(--palette_b_10); --outline_color: var(--palette_b_10);"},"outline_b_20":{"declaration":"outline-color: var(--palette_b_20); --outline_color: var(--palette_b_20);"},"outline_b_30":{"declaration":"outline-color: var(--palette_b_30); --outline_color: var(--palette_b_30);"},"outline_b_40":{"declaration":"outline-color: var(--palette_b_40); --outline_color: var(--palette_b_40);"},"outline_b_50":{"declaration":"outline-color: var(--palette_b_50); --outline_color: var(--palette_b_50);"},"outline_b_60":{"declaration":"outline-color: var(--palette_b_60); --outline_color: var(--palette_b_60);"},"outline_b_70":{"declaration":"outline-color: var(--palette_b_70); --outline_color: var(--palette_b_70);"},"outline_b_80":{"declaration":"outline-color: var(--palette_b_80); --outline_color: var(--palette_b_80);"},"outline_b_90":{"declaration":"outline-color: var(--palette_b_90); --outline_color: var(--palette_b_90);"},"outline_b_95":{"declaration":"outline-color: var(--palette_b_95); --outline_color: var(--palette_b_95);"},"outline_b_100":{"declaration":"outline-color: var(--palette_b_100); --outline_color: var(--palette_b_100);"},"outline_c_00":{"declaration":"outline-color: var(--palette_c_00); --outline_color: var(--palette_c_00);"},"outline_c_05":{"declaration":"outline-color: var(--palette_c_05); --outline_color: var(--palette_c_05);"},"outline_c_10":{"declaration":"outline-color: var(--palette_c_10); --outline_color: var(--palette_c_10);"},"outline_c_20":{"declaration":"outline-color: var(--palette_c_20); --outline_color: var(--palette_c_20);"},"outline_c_30":{"declaration":"outline-color: var(--palette_c_30); --outline_color: var(--palette_c_30);"},"outline_c_40":{"declaration":"outline-color: var(--palette_c_40); --outline_color: var(--palette_c_40);"},"outline_c_50":{"declaration":"outline-color: var(--palette_c_50); --outline_color: var(--palette_c_50);"},"outline_c_60":{"declaration":"outline-color: var(--palette_c_60); --outline_color: var(--palette_c_60);"},"outline_c_70":{"declaration":"outline-color: var(--palette_c_70); --outline_color: var(--palette_c_70);"},"outline_c_80":{"declaration":"outline-color: var(--palette_c_80); --outline_color: var(--palette_c_80);"},"outline_c_90":{"declaration":"outline-color: var(--palette_c_90); --outline_color: var(--palette_c_90);"},"outline_c_95":{"declaration":"outline-color: var(--palette_c_95); --outline_color: var(--palette_c_95);"},"outline_c_100":{"declaration":"outline-color: var(--palette_c_100); --outline_color: var(--palette_c_100);"},"outline_d_00":{"declaration":"outline-color: var(--palette_d_00); --outline_color: var(--palette_d_00);"},"outline_d_05":{"declaration":"outline-color: var(--palette_d_05); --outline_color: var(--palette_d_05);"},"outline_d_10":{"declaration":"outline-color: var(--palette_d_10); --outline_color: var(--palette_d_10);"},"outline_d_20":{"declaration":"outline-color: var(--palette_d_20); --outline_color: var(--palette_d_20);"},"outline_d_30":{"declaration":"outline-color: var(--palette_d_30); --outline_color: var(--palette_d_30);"},"outline_d_40":{"declaration":"outline-color: var(--palette_d_40); --outline_color: var(--palette_d_40);"},"outline_d_50":{"declaration":"outline-color: var(--palette_d_50); --outline_color: var(--palette_d_50);"},"outline_d_60":{"declaration":"outline-color: var(--palette_d_60); --outline_color: var(--palette_d_60);"},"outline_d_70":{"declaration":"outline-color: var(--palette_d_70); --outline_color: var(--palette_d_70);"},"outline_d_80":{"declaration":"outline-color: var(--palette_d_80); --outline_color: var(--palette_d_80);"},"outline_d_90":{"declaration":"outline-color: var(--palette_d_90); --outline_color: var(--palette_d_90);"},"outline_d_95":{"declaration":"outline-color: var(--palette_d_95); --outline_color: var(--palette_d_95);"},"outline_d_100":{"declaration":"outline-color: var(--palette_d_100); --outline_color: var(--palette_d_100);"},"outline_e_00":{"declaration":"outline-color: var(--palette_e_00); --outline_color: var(--palette_e_00);"},"outline_e_05":{"declaration":"outline-color: var(--palette_e_05); --outline_color: var(--palette_e_05);"},"outline_e_10":{"declaration":"outline-color: var(--palette_e_10); --outline_color: var(--palette_e_10);"},"outline_e_20":{"declaration":"outline-color: var(--palette_e_20); --outline_color: var(--palette_e_20);"},"outline_e_30":{"declaration":"outline-color: var(--palette_e_30); --outline_color: var(--palette_e_30);"},"outline_e_40":{"declaration":"outline-color: var(--palette_e_40); --outline_color: var(--palette_e_40);"},"outline_e_50":{"declaration":"outline-color: var(--palette_e_50); --outline_color: var(--palette_e_50);"},"outline_e_60":{"declaration":"outline-color: var(--palette_e_60); --outline_color: var(--palette_e_60);"},"outline_e_70":{"declaration":"outline-color: var(--palette_e_70); --outline_color: var(--palette_e_70);"},"outline_e_80":{"declaration":"outline-color: var(--palette_e_80); --outline_color: var(--palette_e_80);"},"outline_e_90":{"declaration":"outline-color: var(--palette_e_90); --outline_color: var(--palette_e_90);"},"outline_e_95":{"declaration":"outline-color: var(--palette_e_95); --outline_color: var(--palette_e_95);"},"outline_e_100":{"declaration":"outline-color: var(--palette_e_100); --outline_color: var(--palette_e_100);"},"outline_f_00":{"declaration":"outline-color: var(--palette_f_00); --outline_color: var(--palette_f_00);"},"outline_f_05":{"declaration":"outline-color: var(--palette_f_05); --outline_color: var(--palette_f_05);"},"outline_f_10":{"declaration":"outline-color: var(--palette_f_10); --outline_color: var(--palette_f_10);"},"outline_f_20":{"declaration":"outline-color: var(--palette_f_20); --outline_color: var(--palette_f_20);"},"outline_f_30":{"declaration":"outline-color: var(--palette_f_30); --outline_color: var(--palette_f_30);"},"outline_f_40":{"declaration":"outline-color: var(--palette_f_40); --outline_color: var(--palette_f_40);"},"outline_f_50":{"declaration":"outline-color: var(--palette_f_50); --outline_color: var(--palette_f_50);"},"outline_f_60":{"declaration":"outline-color: var(--palette_f_60); --outline_color: var(--palette_f_60);"},"outline_f_70":{"declaration":"outline-color: var(--palette_f_70); --outline_color: var(--palette_f_70);"},"outline_f_80":{"declaration":"outline-color: var(--palette_f_80); --outline_color: var(--palette_f_80);"},"outline_f_90":{"declaration":"outline-color: var(--palette_f_90); --outline_color: var(--palette_f_90);"},"outline_f_95":{"declaration":"outline-color: var(--palette_f_95); --outline_color: var(--palette_f_95);"},"outline_f_100":{"declaration":"outline-color: var(--palette_f_100); --outline_color: var(--palette_f_100);"},"outline_g_00":{"declaration":"outline-color: var(--palette_g_00); --outline_color: var(--palette_g_00);"},"outline_g_05":{"declaration":"outline-color: var(--palette_g_05); --outline_color: var(--palette_g_05);"},"outline_g_10":{"declaration":"outline-color: var(--palette_g_10); --outline_color: var(--palette_g_10);"},"outline_g_20":{"declaration":"outline-color: var(--palette_g_20); --outline_color: var(--palette_g_20);"},"outline_g_30":{"declaration":"outline-color: var(--palette_g_30); --outline_color: var(--palette_g_30);"},"outline_g_40":{"declaration":"outline-color: var(--palette_g_40); --outline_color: var(--palette_g_40);"},"outline_g_50":{"declaration":"outline-color: var(--palette_g_50); --outline_color: var(--palette_g_50);"},"outline_g_60":{"declaration":"outline-color: var(--palette_g_60); --outline_color: var(--palette_g_60);"},"outline_g_70":{"declaration":"outline-color: var(--palette_g_70); --outline_color: var(--palette_g_70);"},"outline_g_80":{"declaration":"outline-color: var(--palette_g_80); --outline_color: var(--palette_g_80);"},"outline_g_90":{"declaration":"outline-color: var(--palette_g_90); --outline_color: var(--palette_g_90);"},"outline_g_95":{"declaration":"outline-color: var(--palette_g_95); --outline_color: var(--palette_g_95);"},"outline_g_100":{"declaration":"outline-color: var(--palette_g_100); --outline_color: var(--palette_g_100);"},"outline_h_00":{"declaration":"outline-color: var(--palette_h_00); --outline_color: var(--palette_h_00);"},"outline_h_05":{"declaration":"outline-color: var(--palette_h_05); --outline_color: var(--palette_h_05);"},"outline_h_10":{"declaration":"outline-color: var(--palette_h_10); --outline_color: var(--palette_h_10);"},"outline_h_20":{"declaration":"outline-color: var(--palette_h_20); --outline_color: var(--palette_h_20);"},"outline_h_30":{"declaration":"outline-color: var(--palette_h_30); --outline_color: var(--palette_h_30);"},"outline_h_40":{"declaration":"outline-color: var(--palette_h_40); --outline_color: var(--palette_h_40);"},"outline_h_50":{"declaration":"outline-color: var(--palette_h_50); --outline_color: var(--palette_h_50);"},"outline_h_60":{"declaration":"outline-color: var(--palette_h_60); --outline_color: var(--palette_h_60);"},"outline_h_70":{"declaration":"outline-color: var(--palette_h_70); --outline_color: var(--palette_h_70);"},"outline_h_80":{"declaration":"outline-color: var(--palette_h_80); --outline_color: var(--palette_h_80);"},"outline_h_90":{"declaration":"outline-color: var(--palette_h_90); --outline_color: var(--palette_h_90);"},"outline_h_95":{"declaration":"outline-color: var(--palette_h_95); --outline_color: var(--palette_h_95);"},"outline_h_100":{"declaration":"outline-color: var(--palette_h_100); --outline_color: var(--palette_h_100);"},"outline_i_00":{"declaration":"outline-color: var(--palette_i_00); --outline_color: var(--palette_i_00);"},"outline_i_05":{"declaration":"outline-color: var(--palette_i_05); --outline_color: var(--palette_i_05);"},"outline_i_10":{"declaration":"outline-color: var(--palette_i_10); --outline_color: var(--palette_i_10);"},"outline_i_20":{"declaration":"outline-color: var(--palette_i_20); --outline_color: var(--palette_i_20);"},"outline_i_30":{"declaration":"outline-color: var(--palette_i_30); --outline_color: var(--palette_i_30);"},"outline_i_40":{"declaration":"outline-color: var(--palette_i_40); --outline_color: var(--palette_i_40);"},"outline_i_50":{"declaration":"outline-color: var(--palette_i_50); --outline_color: var(--palette_i_50);"},"outline_i_60":{"declaration":"outline-color: var(--palette_i_60); --outline_color: var(--palette_i_60);"},"outline_i_70":{"declaration":"outline-color: var(--palette_i_70); --outline_color: var(--palette_i_70);"},"outline_i_80":{"declaration":"outline-color: var(--palette_i_80); --outline_color: var(--palette_i_80);"},"outline_i_90":{"declaration":"outline-color: var(--palette_i_90); --outline_color: var(--palette_i_90);"},"outline_i_95":{"declaration":"outline-color: var(--palette_i_95); --outline_color: var(--palette_i_95);"},"outline_i_100":{"declaration":"outline-color: var(--palette_i_100); --outline_color: var(--palette_i_100);"},"outline_j_00":{"declaration":"outline-color: var(--palette_j_00); --outline_color: var(--palette_j_00);"},"outline_j_05":{"declaration":"outline-color: var(--palette_j_05); --outline_color: var(--palette_j_05);"},"outline_j_10":{"declaration":"outline-color: var(--palette_j_10); --outline_color: var(--palette_j_10);"},"outline_j_20":{"declaration":"outline-color: var(--palette_j_20); --outline_color: var(--palette_j_20);"},"outline_j_30":{"declaration":"outline-color: var(--palette_j_30); --outline_color: var(--palette_j_30);"},"outline_j_40":{"declaration":"outline-color: var(--palette_j_40); --outline_color: var(--palette_j_40);"},"outline_j_50":{"declaration":"outline-color: var(--palette_j_50); --outline_color: var(--palette_j_50);"},"outline_j_60":{"declaration":"outline-color: var(--palette_j_60); --outline_color: var(--palette_j_60);"},"outline_j_70":{"declaration":"outline-color: var(--palette_j_70); --outline_color: var(--palette_j_70);"},"outline_j_80":{"declaration":"outline-color: var(--palette_j_80); --outline_color: var(--palette_j_80);"},"outline_j_90":{"declaration":"outline-color: var(--palette_j_90); --outline_color: var(--palette_j_90);"},"outline_j_95":{"declaration":"outline-color: var(--palette_j_95); --outline_color: var(--palette_j_95);"},"outline_j_100":{"declaration":"outline-color: var(--palette_j_100); --outline_color: var(--palette_j_100);"},"border_width_1":{"declaration":"border-width: var(--border_width_1);"},"border_width_2":{"declaration":"border-width: var(--border_width_2);"},"border_width_3":{"declaration":"border-width: var(--border_width_3);"},"border_width_4":{"declaration":"border-width: var(--border_width_4);"},"border_width_5":{"declaration":"border-width: var(--border_width_5);"},"border_width_6":{"declaration":"border-width: var(--border_width_6);"},"border_width_7":{"declaration":"border-width: var(--border_width_7);"},"border_width_8":{"declaration":"border-width: var(--border_width_8);"},"border_width_9":{"declaration":"border-width: var(--border_width_9);"},"outline_width_1":{"declaration":"outline-width: var(--border_width_1);"},"outline_width_2":{"declaration":"outline-width: var(--border_width_2);"},"outline_width_3":{"declaration":"outline-width: var(--border_width_3);"},"outline_width_4":{"declaration":"outline-width: var(--border_width_4);"},"outline_width_5":{"declaration":"outline-width: var(--border_width_5);"},"outline_width_6":{"declaration":"outline-width: var(--border_width_6);"},"outline_width_7":{"declaration":"outline-width: var(--border_width_7);"},"outline_width_8":{"declaration":"outline-width: var(--border_width_8);"},"outline_width_9":{"declaration":"outline-width: var(--border_width_9);"},"outline_width_focus":{"declaration":"outline-width: var(--outline_width_focus);"},"outline_width_active":{"declaration":"outline-width: var(--outline_width_active);"},"border_radius_xs3":{"declaration":"border-radius: var(--border_radius_xs3);"},"border_radius_xs2":{"declaration":"border-radius: var(--border_radius_xs2);"},"border_radius_xs":{"declaration":"border-radius: var(--border_radius_xs);"},"border_radius_sm":{"declaration":"border-radius: var(--border_radius_sm);"},"border_radius_md":{"declaration":"border-radius: var(--border_radius_md);"},"border_radius_lg":{"declaration":"border-radius: var(--border_radius_lg);"},"border_radius_xl":{"declaration":"border-radius: var(--border_radius_xl);"},"border_top_left_radius_xs3":{"declaration":"border-top-left-radius: var(--border_radius_xs3);"},"border_top_left_radius_xs2":{"declaration":"border-top-left-radius: var(--border_radius_xs2);"},"border_top_left_radius_xs":{"declaration":"border-top-left-radius: var(--border_radius_xs);"},"border_top_left_radius_sm":{"declaration":"border-top-left-radius: var(--border_radius_sm);"},"border_top_left_radius_md":{"declaration":"border-top-left-radius: var(--border_radius_md);"},"border_top_left_radius_lg":{"declaration":"border-top-left-radius: var(--border_radius_lg);"},"border_top_left_radius_xl":{"declaration":"border-top-left-radius: var(--border_radius_xl);"},"border_top_right_radius_xs3":{"declaration":"border-top-right-radius: var(--border_radius_xs3);"},"border_top_right_radius_xs2":{"declaration":"border-top-right-radius: var(--border_radius_xs2);"},"border_top_right_radius_xs":{"declaration":"border-top-right-radius: var(--border_radius_xs);"},"border_top_right_radius_sm":{"declaration":"border-top-right-radius: var(--border_radius_sm);"},"border_top_right_radius_md":{"declaration":"border-top-right-radius: var(--border_radius_md);"},"border_top_right_radius_lg":{"declaration":"border-top-right-radius: var(--border_radius_lg);"},"border_top_right_radius_xl":{"declaration":"border-top-right-radius: var(--border_radius_xl);"},"border_bottom_left_radius_xs3":{"declaration":"border-bottom-left-radius: var(--border_radius_xs3);"},"border_bottom_left_radius_xs2":{"declaration":"border-bottom-left-radius: var(--border_radius_xs2);"},"border_bottom_left_radius_xs":{"declaration":"border-bottom-left-radius: var(--border_radius_xs);"},"border_bottom_left_radius_sm":{"declaration":"border-bottom-left-radius: var(--border_radius_sm);"},"border_bottom_left_radius_md":{"declaration":"border-bottom-left-radius: var(--border_radius_md);"},"border_bottom_left_radius_lg":{"declaration":"border-bottom-left-radius: var(--border_radius_lg);"},"border_bottom_left_radius_xl":{"declaration":"border-bottom-left-radius: var(--border_radius_xl);"},"border_bottom_right_radius_xs3":{"declaration":"border-bottom-right-radius: var(--border_radius_xs3);"},"border_bottom_right_radius_xs2":{"declaration":"border-bottom-right-radius: var(--border_radius_xs2);"},"border_bottom_right_radius_xs":{"declaration":"border-bottom-right-radius: var(--border_radius_xs);"},"border_bottom_right_radius_sm":{"declaration":"border-bottom-right-radius: var(--border_radius_sm);"},"border_bottom_right_radius_md":{"declaration":"border-bottom-right-radius: var(--border_radius_md);"},"border_bottom_right_radius_lg":{"declaration":"border-bottom-right-radius: var(--border_radius_lg);"},"border_bottom_right_radius_xl":{"declaration":"border-bottom-right-radius: var(--border_radius_xl);"},"shadow_xs":{"declaration":"box-shadow: var(--shadow_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_sm":{"declaration":"box-shadow: var(--shadow_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_md":{"declaration":"box-shadow: var(--shadow_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_lg":{"declaration":"box-shadow: var(--shadow_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_xl":{"declaration":"box-shadow: var(--shadow_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_top_xs":{"declaration":"box-shadow: var(--shadow_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_top_sm":{"declaration":"box-shadow: var(--shadow_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_top_md":{"declaration":"box-shadow: var(--shadow_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_top_lg":{"declaration":"box-shadow: var(--shadow_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_top_xl":{"declaration":"box-shadow: var(--shadow_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_bottom_xs":{"declaration":"box-shadow: var(--shadow_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_bottom_sm":{"declaration":"box-shadow: var(--shadow_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_bottom_md":{"declaration":"box-shadow: var(--shadow_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_bottom_lg":{"declaration":"box-shadow: var(--shadow_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_bottom_xl":{"declaration":"box-shadow: var(--shadow_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_inset_xs":{"declaration":"box-shadow: var(--shadow_inset_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_inset_sm":{"declaration":"box-shadow: var(--shadow_inset_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_inset_md":{"declaration":"box-shadow: var(--shadow_inset_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_inset_lg":{"declaration":"box-shadow: var(--shadow_inset_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_inset_xl":{"declaration":"box-shadow: var(--shadow_inset_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_inset_top_xs":{"declaration":"box-shadow: var(--shadow_inset_top_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_inset_top_sm":{"declaration":"box-shadow: var(--shadow_inset_top_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_inset_top_md":{"declaration":"box-shadow: var(--shadow_inset_top_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_inset_top_lg":{"declaration":"box-shadow: var(--shadow_inset_top_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_inset_top_xl":{"declaration":"box-shadow: var(--shadow_inset_top_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_inset_bottom_xs":{"declaration":"box-shadow: var(--shadow_inset_bottom_xs) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_30)), transparent);"},"shadow_inset_bottom_sm":{"declaration":"box-shadow: var(--shadow_inset_bottom_sm) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_40)), transparent);"},"shadow_inset_bottom_md":{"declaration":"box-shadow: var(--shadow_inset_bottom_md) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_50)), transparent);"},"shadow_inset_bottom_lg":{"declaration":"box-shadow: var(--shadow_inset_bottom_lg) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_60)), transparent);"},"shadow_inset_bottom_xl":{"declaration":"box-shadow: var(--shadow_inset_bottom_xl) color-mix(in oklab, var(--shadow_color, var(--shadow_color_umbra)) var(--shadow_alpha, var(--shadow_alpha_70)), transparent);"},"shadow_color_umbra":{"declaration":"--shadow_color: var(--shadow_color_umbra);"},"shadow_color_highlight":{"declaration":"--shadow_color: var(--shadow_color_highlight);"},"shadow_color_glow":{"declaration":"--shadow_color: var(--shadow_color_glow);"},"shadow_color_shroud":{"declaration":"--shadow_color: var(--shadow_color_shroud);"},"shadow_alpha_00":{"declaration":"--shadow_alpha: var(--shadow_alpha_00);"},"shadow_alpha_05":{"declaration":"--shadow_alpha: var(--shadow_alpha_05);"},"shadow_alpha_10":{"declaration":"--shadow_alpha: var(--shadow_alpha_10);"},"shadow_alpha_20":{"declaration":"--shadow_alpha: var(--shadow_alpha_20);"},"shadow_alpha_30":{"declaration":"--shadow_alpha: var(--shadow_alpha_30);"},"shadow_alpha_40":{"declaration":"--shadow_alpha: var(--shadow_alpha_40);"},"shadow_alpha_50":{"declaration":"--shadow_alpha: var(--shadow_alpha_50);"},"shadow_alpha_60":{"declaration":"--shadow_alpha: var(--shadow_alpha_60);"},"shadow_alpha_70":{"declaration":"--shadow_alpha: var(--shadow_alpha_70);"},"shadow_alpha_80":{"declaration":"--shadow_alpha: var(--shadow_alpha_80);"},"shadow_alpha_90":{"declaration":"--shadow_alpha: var(--shadow_alpha_90);"},"shadow_alpha_95":{"declaration":"--shadow_alpha: var(--shadow_alpha_95);"},"shadow_alpha_100":{"declaration":"--shadow_alpha: var(--shadow_alpha_100);"},"shadow_a_00":{"declaration":"--shadow_color: var(--palette_a_00);"},"shadow_a_05":{"declaration":"--shadow_color: var(--palette_a_05);"},"shadow_a_10":{"declaration":"--shadow_color: var(--palette_a_10);"},"shadow_a_20":{"declaration":"--shadow_color: var(--palette_a_20);"},"shadow_a_30":{"declaration":"--shadow_color: var(--palette_a_30);"},"shadow_a_40":{"declaration":"--shadow_color: var(--palette_a_40);"},"shadow_a_50":{"declaration":"--shadow_color: var(--palette_a_50);"},"shadow_a_60":{"declaration":"--shadow_color: var(--palette_a_60);"},"shadow_a_70":{"declaration":"--shadow_color: var(--palette_a_70);"},"shadow_a_80":{"declaration":"--shadow_color: var(--palette_a_80);"},"shadow_a_90":{"declaration":"--shadow_color: var(--palette_a_90);"},"shadow_a_95":{"declaration":"--shadow_color: var(--palette_a_95);"},"shadow_a_100":{"declaration":"--shadow_color: var(--palette_a_100);"},"shadow_b_00":{"declaration":"--shadow_color: var(--palette_b_00);"},"shadow_b_05":{"declaration":"--shadow_color: var(--palette_b_05);"},"shadow_b_10":{"declaration":"--shadow_color: var(--palette_b_10);"},"shadow_b_20":{"declaration":"--shadow_color: var(--palette_b_20);"},"shadow_b_30":{"declaration":"--shadow_color: var(--palette_b_30);"},"shadow_b_40":{"declaration":"--shadow_color: var(--palette_b_40);"},"shadow_b_50":{"declaration":"--shadow_color: var(--palette_b_50);"},"shadow_b_60":{"declaration":"--shadow_color: var(--palette_b_60);"},"shadow_b_70":{"declaration":"--shadow_color: var(--palette_b_70);"},"shadow_b_80":{"declaration":"--shadow_color: var(--palette_b_80);"},"shadow_b_90":{"declaration":"--shadow_color: var(--palette_b_90);"},"shadow_b_95":{"declaration":"--shadow_color: var(--palette_b_95);"},"shadow_b_100":{"declaration":"--shadow_color: var(--palette_b_100);"},"shadow_c_00":{"declaration":"--shadow_color: var(--palette_c_00);"},"shadow_c_05":{"declaration":"--shadow_color: var(--palette_c_05);"},"shadow_c_10":{"declaration":"--shadow_color: var(--palette_c_10);"},"shadow_c_20":{"declaration":"--shadow_color: var(--palette_c_20);"},"shadow_c_30":{"declaration":"--shadow_color: var(--palette_c_30);"},"shadow_c_40":{"declaration":"--shadow_color: var(--palette_c_40);"},"shadow_c_50":{"declaration":"--shadow_color: var(--palette_c_50);"},"shadow_c_60":{"declaration":"--shadow_color: var(--palette_c_60);"},"shadow_c_70":{"declaration":"--shadow_color: var(--palette_c_70);"},"shadow_c_80":{"declaration":"--shadow_color: var(--palette_c_80);"},"shadow_c_90":{"declaration":"--shadow_color: var(--palette_c_90);"},"shadow_c_95":{"declaration":"--shadow_color: var(--palette_c_95);"},"shadow_c_100":{"declaration":"--shadow_color: var(--palette_c_100);"},"shadow_d_00":{"declaration":"--shadow_color: var(--palette_d_00);"},"shadow_d_05":{"declaration":"--shadow_color: var(--palette_d_05);"},"shadow_d_10":{"declaration":"--shadow_color: var(--palette_d_10);"},"shadow_d_20":{"declaration":"--shadow_color: var(--palette_d_20);"},"shadow_d_30":{"declaration":"--shadow_color: var(--palette_d_30);"},"shadow_d_40":{"declaration":"--shadow_color: var(--palette_d_40);"},"shadow_d_50":{"declaration":"--shadow_color: var(--palette_d_50);"},"shadow_d_60":{"declaration":"--shadow_color: var(--palette_d_60);"},"shadow_d_70":{"declaration":"--shadow_color: var(--palette_d_70);"},"shadow_d_80":{"declaration":"--shadow_color: var(--palette_d_80);"},"shadow_d_90":{"declaration":"--shadow_color: var(--palette_d_90);"},"shadow_d_95":{"declaration":"--shadow_color: var(--palette_d_95);"},"shadow_d_100":{"declaration":"--shadow_color: var(--palette_d_100);"},"shadow_e_00":{"declaration":"--shadow_color: var(--palette_e_00);"},"shadow_e_05":{"declaration":"--shadow_color: var(--palette_e_05);"},"shadow_e_10":{"declaration":"--shadow_color: var(--palette_e_10);"},"shadow_e_20":{"declaration":"--shadow_color: var(--palette_e_20);"},"shadow_e_30":{"declaration":"--shadow_color: var(--palette_e_30);"},"shadow_e_40":{"declaration":"--shadow_color: var(--palette_e_40);"},"shadow_e_50":{"declaration":"--shadow_color: var(--palette_e_50);"},"shadow_e_60":{"declaration":"--shadow_color: var(--palette_e_60);"},"shadow_e_70":{"declaration":"--shadow_color: var(--palette_e_70);"},"shadow_e_80":{"declaration":"--shadow_color: var(--palette_e_80);"},"shadow_e_90":{"declaration":"--shadow_color: var(--palette_e_90);"},"shadow_e_95":{"declaration":"--shadow_color: var(--palette_e_95);"},"shadow_e_100":{"declaration":"--shadow_color: var(--palette_e_100);"},"shadow_f_00":{"declaration":"--shadow_color: var(--palette_f_00);"},"shadow_f_05":{"declaration":"--shadow_color: var(--palette_f_05);"},"shadow_f_10":{"declaration":"--shadow_color: var(--palette_f_10);"},"shadow_f_20":{"declaration":"--shadow_color: var(--palette_f_20);"},"shadow_f_30":{"declaration":"--shadow_color: var(--palette_f_30);"},"shadow_f_40":{"declaration":"--shadow_color: var(--palette_f_40);"},"shadow_f_50":{"declaration":"--shadow_color: var(--palette_f_50);"},"shadow_f_60":{"declaration":"--shadow_color: var(--palette_f_60);"},"shadow_f_70":{"declaration":"--shadow_color: var(--palette_f_70);"},"shadow_f_80":{"declaration":"--shadow_color: var(--palette_f_80);"},"shadow_f_90":{"declaration":"--shadow_color: var(--palette_f_90);"},"shadow_f_95":{"declaration":"--shadow_color: var(--palette_f_95);"},"shadow_f_100":{"declaration":"--shadow_color: var(--palette_f_100);"},"shadow_g_00":{"declaration":"--shadow_color: var(--palette_g_00);"},"shadow_g_05":{"declaration":"--shadow_color: var(--palette_g_05);"},"shadow_g_10":{"declaration":"--shadow_color: var(--palette_g_10);"},"shadow_g_20":{"declaration":"--shadow_color: var(--palette_g_20);"},"shadow_g_30":{"declaration":"--shadow_color: var(--palette_g_30);"},"shadow_g_40":{"declaration":"--shadow_color: var(--palette_g_40);"},"shadow_g_50":{"declaration":"--shadow_color: var(--palette_g_50);"},"shadow_g_60":{"declaration":"--shadow_color: var(--palette_g_60);"},"shadow_g_70":{"declaration":"--shadow_color: var(--palette_g_70);"},"shadow_g_80":{"declaration":"--shadow_color: var(--palette_g_80);"},"shadow_g_90":{"declaration":"--shadow_color: var(--palette_g_90);"},"shadow_g_95":{"declaration":"--shadow_color: var(--palette_g_95);"},"shadow_g_100":{"declaration":"--shadow_color: var(--palette_g_100);"},"shadow_h_00":{"declaration":"--shadow_color: var(--palette_h_00);"},"shadow_h_05":{"declaration":"--shadow_color: var(--palette_h_05);"},"shadow_h_10":{"declaration":"--shadow_color: var(--palette_h_10);"},"shadow_h_20":{"declaration":"--shadow_color: var(--palette_h_20);"},"shadow_h_30":{"declaration":"--shadow_color: var(--palette_h_30);"},"shadow_h_40":{"declaration":"--shadow_color: var(--palette_h_40);"},"shadow_h_50":{"declaration":"--shadow_color: var(--palette_h_50);"},"shadow_h_60":{"declaration":"--shadow_color: var(--palette_h_60);"},"shadow_h_70":{"declaration":"--shadow_color: var(--palette_h_70);"},"shadow_h_80":{"declaration":"--shadow_color: var(--palette_h_80);"},"shadow_h_90":{"declaration":"--shadow_color: var(--palette_h_90);"},"shadow_h_95":{"declaration":"--shadow_color: var(--palette_h_95);"},"shadow_h_100":{"declaration":"--shadow_color: var(--palette_h_100);"},"shadow_i_00":{"declaration":"--shadow_color: var(--palette_i_00);"},"shadow_i_05":{"declaration":"--shadow_color: var(--palette_i_05);"},"shadow_i_10":{"declaration":"--shadow_color: var(--palette_i_10);"},"shadow_i_20":{"declaration":"--shadow_color: var(--palette_i_20);"},"shadow_i_30":{"declaration":"--shadow_color: var(--palette_i_30);"},"shadow_i_40":{"declaration":"--shadow_color: var(--palette_i_40);"},"shadow_i_50":{"declaration":"--shadow_color: var(--palette_i_50);"},"shadow_i_60":{"declaration":"--shadow_color: var(--palette_i_60);"},"shadow_i_70":{"declaration":"--shadow_color: var(--palette_i_70);"},"shadow_i_80":{"declaration":"--shadow_color: var(--palette_i_80);"},"shadow_i_90":{"declaration":"--shadow_color: var(--palette_i_90);"},"shadow_i_95":{"declaration":"--shadow_color: var(--palette_i_95);"},"shadow_i_100":{"declaration":"--shadow_color: var(--palette_i_100);"},"shadow_j_00":{"declaration":"--shadow_color: var(--palette_j_00);"},"shadow_j_05":{"declaration":"--shadow_color: var(--palette_j_05);"},"shadow_j_10":{"declaration":"--shadow_color: var(--palette_j_10);"},"shadow_j_20":{"declaration":"--shadow_color: var(--palette_j_20);"},"shadow_j_30":{"declaration":"--shadow_color: var(--palette_j_30);"},"shadow_j_40":{"declaration":"--shadow_color: var(--palette_j_40);"},"shadow_j_50":{"declaration":"--shadow_color: var(--palette_j_50);"},"shadow_j_60":{"declaration":"--shadow_color: var(--palette_j_60);"},"shadow_j_70":{"declaration":"--shadow_color: var(--palette_j_70);"},"shadow_j_80":{"declaration":"--shadow_color: var(--palette_j_80);"},"shadow_j_90":{"declaration":"--shadow_color: var(--palette_j_90);"},"shadow_j_95":{"declaration":"--shadow_color: var(--palette_j_95);"},"shadow_j_100":{"declaration":"--shadow_color: var(--palette_j_100);"},"width_xs5":{"declaration":"width: var(--space_xs5);"},"width_xs4":{"declaration":"width: var(--space_xs4);"},"width_xs3":{"declaration":"width: var(--space_xs3);"},"width_xs2":{"declaration":"width: var(--space_xs2);"},"width_xs":{"declaration":"width: var(--space_xs);"},"width_sm":{"declaration":"width: var(--space_sm);"},"width_md":{"declaration":"width: var(--space_md);"},"width_lg":{"declaration":"width: var(--space_lg);"},"width_xl":{"declaration":"width: var(--space_xl);"},"width_xl2":{"declaration":"width: var(--space_xl2);"},"width_xl3":{"declaration":"width: var(--space_xl3);"},"width_xl4":{"declaration":"width: var(--space_xl4);"},"width_xl5":{"declaration":"width: var(--space_xl5);"},"width_xl6":{"declaration":"width: var(--space_xl6);"},"width_xl7":{"declaration":"width: var(--space_xl7);"},"width_xl8":{"declaration":"width: var(--space_xl8);"},"width_xl9":{"declaration":"width: var(--space_xl9);"},"width_xl10":{"declaration":"width: var(--space_xl10);"},"width_xl11":{"declaration":"width: var(--space_xl11);"},"width_xl12":{"declaration":"width: var(--space_xl12);"},"width_xl13":{"declaration":"width: var(--space_xl13);"},"width_xl14":{"declaration":"width: var(--space_xl14);"},"width_xl15":{"declaration":"width: var(--space_xl15);"},"height_xs5":{"declaration":"height: var(--space_xs5);"},"height_xs4":{"declaration":"height: var(--space_xs4);"},"height_xs3":{"declaration":"height: var(--space_xs3);"},"height_xs2":{"declaration":"height: var(--space_xs2);"},"height_xs":{"declaration":"height: var(--space_xs);"},"height_sm":{"declaration":"height: var(--space_sm);"},"height_md":{"declaration":"height: var(--space_md);"},"height_lg":{"declaration":"height: var(--space_lg);"},"height_xl":{"declaration":"height: var(--space_xl);"},"height_xl2":{"declaration":"height: var(--space_xl2);"},"height_xl3":{"declaration":"height: var(--space_xl3);"},"height_xl4":{"declaration":"height: var(--space_xl4);"},"height_xl5":{"declaration":"height: var(--space_xl5);"},"height_xl6":{"declaration":"height: var(--space_xl6);"},"height_xl7":{"declaration":"height: var(--space_xl7);"},"height_xl8":{"declaration":"height: var(--space_xl8);"},"height_xl9":{"declaration":"height: var(--space_xl9);"},"height_xl10":{"declaration":"height: var(--space_xl10);"},"height_xl11":{"declaration":"height: var(--space_xl11);"},"height_xl12":{"declaration":"height: var(--space_xl12);"},"height_xl13":{"declaration":"height: var(--space_xl13);"},"height_xl14":{"declaration":"height: var(--space_xl14);"},"height_xl15":{"declaration":"height: var(--space_xl15);"},"width_atmost_xs":{"declaration":"width: 100%; max-width: var(--distance_xs);"},"width_atmost_sm":{"declaration":"width: 100%; max-width: var(--distance_sm);"},"width_atmost_md":{"declaration":"width: 100%; max-width: var(--distance_md);"},"width_atmost_lg":{"declaration":"width: 100%; max-width: var(--distance_lg);"},"width_atmost_xl":{"declaration":"width: 100%; max-width: var(--distance_xl);"},"width_atleast_xs":{"declaration":"width: 100%; min-width: var(--distance_xs);"},"width_atleast_sm":{"declaration":"width: 100%; min-width: var(--distance_sm);"},"width_atleast_md":{"declaration":"width: 100%; min-width: var(--distance_md);"},"width_atleast_lg":{"declaration":"width: 100%; min-width: var(--distance_lg);"},"width_atleast_xl":{"declaration":"width: 100%; min-width: var(--distance_xl);"},"height_atmost_xs":{"declaration":"height: 100%; max-height: var(--distance_xs);"},"height_atmost_sm":{"declaration":"height: 100%; max-height: var(--distance_sm);"},"height_atmost_md":{"declaration":"height: 100%; max-height: var(--distance_md);"},"height_atmost_lg":{"declaration":"height: 100%; max-height: var(--distance_lg);"},"height_atmost_xl":{"declaration":"height: 100%; max-height: var(--distance_xl);"},"height_atleast_xs":{"declaration":"height: 100%; min-height: var(--distance_xs);"},"height_atleast_sm":{"declaration":"height: 100%; min-height: var(--distance_sm);"},"height_atleast_md":{"declaration":"height: 100%; min-height: var(--distance_md);"},"height_atleast_lg":{"declaration":"height: 100%; min-height: var(--distance_lg);"},"height_atleast_xl":{"declaration":"height: 100%; min-height: var(--distance_xl);"},"top_xs5":{"declaration":"top: var(--space_xs5);"},"top_xs4":{"declaration":"top: var(--space_xs4);"},"top_xs3":{"declaration":"top: var(--space_xs3);"},"top_xs2":{"declaration":"top: var(--space_xs2);"},"top_xs":{"declaration":"top: var(--space_xs);"},"top_sm":{"declaration":"top: var(--space_sm);"},"top_md":{"declaration":"top: var(--space_md);"},"top_lg":{"declaration":"top: var(--space_lg);"},"top_xl":{"declaration":"top: var(--space_xl);"},"top_xl2":{"declaration":"top: var(--space_xl2);"},"top_xl3":{"declaration":"top: var(--space_xl3);"},"top_xl4":{"declaration":"top: var(--space_xl4);"},"top_xl5":{"declaration":"top: var(--space_xl5);"},"top_xl6":{"declaration":"top: var(--space_xl6);"},"top_xl7":{"declaration":"top: var(--space_xl7);"},"top_xl8":{"declaration":"top: var(--space_xl8);"},"top_xl9":{"declaration":"top: var(--space_xl9);"},"top_xl10":{"declaration":"top: var(--space_xl10);"},"top_xl11":{"declaration":"top: var(--space_xl11);"},"top_xl12":{"declaration":"top: var(--space_xl12);"},"top_xl13":{"declaration":"top: var(--space_xl13);"},"top_xl14":{"declaration":"top: var(--space_xl14);"},"top_xl15":{"declaration":"top: var(--space_xl15);"},"right_xs5":{"declaration":"right: var(--space_xs5);"},"right_xs4":{"declaration":"right: var(--space_xs4);"},"right_xs3":{"declaration":"right: var(--space_xs3);"},"right_xs2":{"declaration":"right: var(--space_xs2);"},"right_xs":{"declaration":"right: var(--space_xs);"},"right_sm":{"declaration":"right: var(--space_sm);"},"right_md":{"declaration":"right: var(--space_md);"},"right_lg":{"declaration":"right: var(--space_lg);"},"right_xl":{"declaration":"right: var(--space_xl);"},"right_xl2":{"declaration":"right: var(--space_xl2);"},"right_xl3":{"declaration":"right: var(--space_xl3);"},"right_xl4":{"declaration":"right: var(--space_xl4);"},"right_xl5":{"declaration":"right: var(--space_xl5);"},"right_xl6":{"declaration":"right: var(--space_xl6);"},"right_xl7":{"declaration":"right: var(--space_xl7);"},"right_xl8":{"declaration":"right: var(--space_xl8);"},"right_xl9":{"declaration":"right: var(--space_xl9);"},"right_xl10":{"declaration":"right: var(--space_xl10);"},"right_xl11":{"declaration":"right: var(--space_xl11);"},"right_xl12":{"declaration":"right: var(--space_xl12);"},"right_xl13":{"declaration":"right: var(--space_xl13);"},"right_xl14":{"declaration":"right: var(--space_xl14);"},"right_xl15":{"declaration":"right: var(--space_xl15);"},"bottom_xs5":{"declaration":"bottom: var(--space_xs5);"},"bottom_xs4":{"declaration":"bottom: var(--space_xs4);"},"bottom_xs3":{"declaration":"bottom: var(--space_xs3);"},"bottom_xs2":{"declaration":"bottom: var(--space_xs2);"},"bottom_xs":{"declaration":"bottom: var(--space_xs);"},"bottom_sm":{"declaration":"bottom: var(--space_sm);"},"bottom_md":{"declaration":"bottom: var(--space_md);"},"bottom_lg":{"declaration":"bottom: var(--space_lg);"},"bottom_xl":{"declaration":"bottom: var(--space_xl);"},"bottom_xl2":{"declaration":"bottom: var(--space_xl2);"},"bottom_xl3":{"declaration":"bottom: var(--space_xl3);"},"bottom_xl4":{"declaration":"bottom: var(--space_xl4);"},"bottom_xl5":{"declaration":"bottom: var(--space_xl5);"},"bottom_xl6":{"declaration":"bottom: var(--space_xl6);"},"bottom_xl7":{"declaration":"bottom: var(--space_xl7);"},"bottom_xl8":{"declaration":"bottom: var(--space_xl8);"},"bottom_xl9":{"declaration":"bottom: var(--space_xl9);"},"bottom_xl10":{"declaration":"bottom: var(--space_xl10);"},"bottom_xl11":{"declaration":"bottom: var(--space_xl11);"},"bottom_xl12":{"declaration":"bottom: var(--space_xl12);"},"bottom_xl13":{"declaration":"bottom: var(--space_xl13);"},"bottom_xl14":{"declaration":"bottom: var(--space_xl14);"},"bottom_xl15":{"declaration":"bottom: var(--space_xl15);"},"left_xs5":{"declaration":"left: var(--space_xs5);"},"left_xs4":{"declaration":"left: var(--space_xs4);"},"left_xs3":{"declaration":"left: var(--space_xs3);"},"left_xs2":{"declaration":"left: var(--space_xs2);"},"left_xs":{"declaration":"left: var(--space_xs);"},"left_sm":{"declaration":"left: var(--space_sm);"},"left_md":{"declaration":"left: var(--space_md);"},"left_lg":{"declaration":"left: var(--space_lg);"},"left_xl":{"declaration":"left: var(--space_xl);"},"left_xl2":{"declaration":"left: var(--space_xl2);"},"left_xl3":{"declaration":"left: var(--space_xl3);"},"left_xl4":{"declaration":"left: var(--space_xl4);"},"left_xl5":{"declaration":"left: var(--space_xl5);"},"left_xl6":{"declaration":"left: var(--space_xl6);"},"left_xl7":{"declaration":"left: var(--space_xl7);"},"left_xl8":{"declaration":"left: var(--space_xl8);"},"left_xl9":{"declaration":"left: var(--space_xl9);"},"left_xl10":{"declaration":"left: var(--space_xl10);"},"left_xl11":{"declaration":"left: var(--space_xl11);"},"left_xl12":{"declaration":"left: var(--space_xl12);"},"left_xl13":{"declaration":"left: var(--space_xl13);"},"left_xl14":{"declaration":"left: var(--space_xl14);"},"left_xl15":{"declaration":"left: var(--space_xl15);"},"inset_xs5":{"declaration":"inset: var(--space_xs5);"},"inset_xs4":{"declaration":"inset: var(--space_xs4);"},"inset_xs3":{"declaration":"inset: var(--space_xs3);"},"inset_xs2":{"declaration":"inset: var(--space_xs2);"},"inset_xs":{"declaration":"inset: var(--space_xs);"},"inset_sm":{"declaration":"inset: var(--space_sm);"},"inset_md":{"declaration":"inset: var(--space_md);"},"inset_lg":{"declaration":"inset: var(--space_lg);"},"inset_xl":{"declaration":"inset: var(--space_xl);"},"inset_xl2":{"declaration":"inset: var(--space_xl2);"},"inset_xl3":{"declaration":"inset: var(--space_xl3);"},"inset_xl4":{"declaration":"inset: var(--space_xl4);"},"inset_xl5":{"declaration":"inset: var(--space_xl5);"},"inset_xl6":{"declaration":"inset: var(--space_xl6);"},"inset_xl7":{"declaration":"inset: var(--space_xl7);"},"inset_xl8":{"declaration":"inset: var(--space_xl8);"},"inset_xl9":{"declaration":"inset: var(--space_xl9);"},"inset_xl10":{"declaration":"inset: var(--space_xl10);"},"inset_xl11":{"declaration":"inset: var(--space_xl11);"},"inset_xl12":{"declaration":"inset: var(--space_xl12);"},"inset_xl13":{"declaration":"inset: var(--space_xl13);"},"inset_xl14":{"declaration":"inset: var(--space_xl14);"},"inset_xl15":{"declaration":"inset: var(--space_xl15);"},"p_0":{"declaration":"padding: 0;"},"p_xs5":{"declaration":"padding: var(--space_xs5);"},"p_xs4":{"declaration":"padding: var(--space_xs4);"},"p_xs3":{"declaration":"padding: var(--space_xs3);"},"p_xs2":{"declaration":"padding: var(--space_xs2);"},"p_xs":{"declaration":"padding: var(--space_xs);"},"p_sm":{"declaration":"padding: var(--space_sm);"},"p_md":{"declaration":"padding: var(--space_md);"},"p_lg":{"declaration":"padding: var(--space_lg);"},"p_xl":{"declaration":"padding: var(--space_xl);"},"p_xl2":{"declaration":"padding: var(--space_xl2);"},"p_xl3":{"declaration":"padding: var(--space_xl3);"},"p_xl4":{"declaration":"padding: var(--space_xl4);"},"p_xl5":{"declaration":"padding: var(--space_xl5);"},"p_xl6":{"declaration":"padding: var(--space_xl6);"},"p_xl7":{"declaration":"padding: var(--space_xl7);"},"p_xl8":{"declaration":"padding: var(--space_xl8);"},"p_xl9":{"declaration":"padding: var(--space_xl9);"},"p_xl10":{"declaration":"padding: var(--space_xl10);"},"p_xl11":{"declaration":"padding: var(--space_xl11);"},"p_xl12":{"declaration":"padding: var(--space_xl12);"},"p_xl13":{"declaration":"padding: var(--space_xl13);"},"p_xl14":{"declaration":"padding: var(--space_xl14);"},"p_xl15":{"declaration":"padding: var(--space_xl15);"},"pt_0":{"declaration":"padding-top: 0;"},"pt_xs5":{"declaration":"padding-top: var(--space_xs5);"},"pt_xs4":{"declaration":"padding-top: var(--space_xs4);"},"pt_xs3":{"declaration":"padding-top: var(--space_xs3);"},"pt_xs2":{"declaration":"padding-top: var(--space_xs2);"},"pt_xs":{"declaration":"padding-top: var(--space_xs);"},"pt_sm":{"declaration":"padding-top: var(--space_sm);"},"pt_md":{"declaration":"padding-top: var(--space_md);"},"pt_lg":{"declaration":"padding-top: var(--space_lg);"},"pt_xl":{"declaration":"padding-top: var(--space_xl);"},"pt_xl2":{"declaration":"padding-top: var(--space_xl2);"},"pt_xl3":{"declaration":"padding-top: var(--space_xl3);"},"pt_xl4":{"declaration":"padding-top: var(--space_xl4);"},"pt_xl5":{"declaration":"padding-top: var(--space_xl5);"},"pt_xl6":{"declaration":"padding-top: var(--space_xl6);"},"pt_xl7":{"declaration":"padding-top: var(--space_xl7);"},"pt_xl8":{"declaration":"padding-top: var(--space_xl8);"},"pt_xl9":{"declaration":"padding-top: var(--space_xl9);"},"pt_xl10":{"declaration":"padding-top: var(--space_xl10);"},"pt_xl11":{"declaration":"padding-top: var(--space_xl11);"},"pt_xl12":{"declaration":"padding-top: var(--space_xl12);"},"pt_xl13":{"declaration":"padding-top: var(--space_xl13);"},"pt_xl14":{"declaration":"padding-top: var(--space_xl14);"},"pt_xl15":{"declaration":"padding-top: var(--space_xl15);"},"pr_0":{"declaration":"padding-right: 0;"},"pr_xs5":{"declaration":"padding-right: var(--space_xs5);"},"pr_xs4":{"declaration":"padding-right: var(--space_xs4);"},"pr_xs3":{"declaration":"padding-right: var(--space_xs3);"},"pr_xs2":{"declaration":"padding-right: var(--space_xs2);"},"pr_xs":{"declaration":"padding-right: var(--space_xs);"},"pr_sm":{"declaration":"padding-right: var(--space_sm);"},"pr_md":{"declaration":"padding-right: var(--space_md);"},"pr_lg":{"declaration":"padding-right: var(--space_lg);"},"pr_xl":{"declaration":"padding-right: var(--space_xl);"},"pr_xl2":{"declaration":"padding-right: var(--space_xl2);"},"pr_xl3":{"declaration":"padding-right: var(--space_xl3);"},"pr_xl4":{"declaration":"padding-right: var(--space_xl4);"},"pr_xl5":{"declaration":"padding-right: var(--space_xl5);"},"pr_xl6":{"declaration":"padding-right: var(--space_xl6);"},"pr_xl7":{"declaration":"padding-right: var(--space_xl7);"},"pr_xl8":{"declaration":"padding-right: var(--space_xl8);"},"pr_xl9":{"declaration":"padding-right: var(--space_xl9);"},"pr_xl10":{"declaration":"padding-right: var(--space_xl10);"},"pr_xl11":{"declaration":"padding-right: var(--space_xl11);"},"pr_xl12":{"declaration":"padding-right: var(--space_xl12);"},"pr_xl13":{"declaration":"padding-right: var(--space_xl13);"},"pr_xl14":{"declaration":"padding-right: var(--space_xl14);"},"pr_xl15":{"declaration":"padding-right: var(--space_xl15);"},"pb_0":{"declaration":"padding-bottom: 0;"},"pb_xs5":{"declaration":"padding-bottom: var(--space_xs5);"},"pb_xs4":{"declaration":"padding-bottom: var(--space_xs4);"},"pb_xs3":{"declaration":"padding-bottom: var(--space_xs3);"},"pb_xs2":{"declaration":"padding-bottom: var(--space_xs2);"},"pb_xs":{"declaration":"padding-bottom: var(--space_xs);"},"pb_sm":{"declaration":"padding-bottom: var(--space_sm);"},"pb_md":{"declaration":"padding-bottom: var(--space_md);"},"pb_lg":{"declaration":"padding-bottom: var(--space_lg);"},"pb_xl":{"declaration":"padding-bottom: var(--space_xl);"},"pb_xl2":{"declaration":"padding-bottom: var(--space_xl2);"},"pb_xl3":{"declaration":"padding-bottom: var(--space_xl3);"},"pb_xl4":{"declaration":"padding-bottom: var(--space_xl4);"},"pb_xl5":{"declaration":"padding-bottom: var(--space_xl5);"},"pb_xl6":{"declaration":"padding-bottom: var(--space_xl6);"},"pb_xl7":{"declaration":"padding-bottom: var(--space_xl7);"},"pb_xl8":{"declaration":"padding-bottom: var(--space_xl8);"},"pb_xl9":{"declaration":"padding-bottom: var(--space_xl9);"},"pb_xl10":{"declaration":"padding-bottom: var(--space_xl10);"},"pb_xl11":{"declaration":"padding-bottom: var(--space_xl11);"},"pb_xl12":{"declaration":"padding-bottom: var(--space_xl12);"},"pb_xl13":{"declaration":"padding-bottom: var(--space_xl13);"},"pb_xl14":{"declaration":"padding-bottom: var(--space_xl14);"},"pb_xl15":{"declaration":"padding-bottom: var(--space_xl15);"},"pl_0":{"declaration":"padding-left: 0;"},"pl_xs5":{"declaration":"padding-left: var(--space_xs5);"},"pl_xs4":{"declaration":"padding-left: var(--space_xs4);"},"pl_xs3":{"declaration":"padding-left: var(--space_xs3);"},"pl_xs2":{"declaration":"padding-left: var(--space_xs2);"},"pl_xs":{"declaration":"padding-left: var(--space_xs);"},"pl_sm":{"declaration":"padding-left: var(--space_sm);"},"pl_md":{"declaration":"padding-left: var(--space_md);"},"pl_lg":{"declaration":"padding-left: var(--space_lg);"},"pl_xl":{"declaration":"padding-left: var(--space_xl);"},"pl_xl2":{"declaration":"padding-left: var(--space_xl2);"},"pl_xl3":{"declaration":"padding-left: var(--space_xl3);"},"pl_xl4":{"declaration":"padding-left: var(--space_xl4);"},"pl_xl5":{"declaration":"padding-left: var(--space_xl5);"},"pl_xl6":{"declaration":"padding-left: var(--space_xl6);"},"pl_xl7":{"declaration":"padding-left: var(--space_xl7);"},"pl_xl8":{"declaration":"padding-left: var(--space_xl8);"},"pl_xl9":{"declaration":"padding-left: var(--space_xl9);"},"pl_xl10":{"declaration":"padding-left: var(--space_xl10);"},"pl_xl11":{"declaration":"padding-left: var(--space_xl11);"},"pl_xl12":{"declaration":"padding-left: var(--space_xl12);"},"pl_xl13":{"declaration":"padding-left: var(--space_xl13);"},"pl_xl14":{"declaration":"padding-left: var(--space_xl14);"},"pl_xl15":{"declaration":"padding-left: var(--space_xl15);"},"px_0":{"declaration":"padding-left: 0;\tpadding-right: 0;"},"px_xs5":{"declaration":"padding-left: var(--space_xs5);\tpadding-right: var(--space_xs5);"},"px_xs4":{"declaration":"padding-left: var(--space_xs4);\tpadding-right: var(--space_xs4);"},"px_xs3":{"declaration":"padding-left: var(--space_xs3);\tpadding-right: var(--space_xs3);"},"px_xs2":{"declaration":"padding-left: var(--space_xs2);\tpadding-right: var(--space_xs2);"},"px_xs":{"declaration":"padding-left: var(--space_xs);\tpadding-right: var(--space_xs);"},"px_sm":{"declaration":"padding-left: var(--space_sm);\tpadding-right: var(--space_sm);"},"px_md":{"declaration":"padding-left: var(--space_md);\tpadding-right: var(--space_md);"},"px_lg":{"declaration":"padding-left: var(--space_lg);\tpadding-right: var(--space_lg);"},"px_xl":{"declaration":"padding-left: var(--space_xl);\tpadding-right: var(--space_xl);"},"px_xl2":{"declaration":"padding-left: var(--space_xl2);\tpadding-right: var(--space_xl2);"},"px_xl3":{"declaration":"padding-left: var(--space_xl3);\tpadding-right: var(--space_xl3);"},"px_xl4":{"declaration":"padding-left: var(--space_xl4);\tpadding-right: var(--space_xl4);"},"px_xl5":{"declaration":"padding-left: var(--space_xl5);\tpadding-right: var(--space_xl5);"},"px_xl6":{"declaration":"padding-left: var(--space_xl6);\tpadding-right: var(--space_xl6);"},"px_xl7":{"declaration":"padding-left: var(--space_xl7);\tpadding-right: var(--space_xl7);"},"px_xl8":{"declaration":"padding-left: var(--space_xl8);\tpadding-right: var(--space_xl8);"},"px_xl9":{"declaration":"padding-left: var(--space_xl9);\tpadding-right: var(--space_xl9);"},"px_xl10":{"declaration":"padding-left: var(--space_xl10);\tpadding-right: var(--space_xl10);"},"px_xl11":{"declaration":"padding-left: var(--space_xl11);\tpadding-right: var(--space_xl11);"},"px_xl12":{"declaration":"padding-left: var(--space_xl12);\tpadding-right: var(--space_xl12);"},"px_xl13":{"declaration":"padding-left: var(--space_xl13);\tpadding-right: var(--space_xl13);"},"px_xl14":{"declaration":"padding-left: var(--space_xl14);\tpadding-right: var(--space_xl14);"},"px_xl15":{"declaration":"padding-left: var(--space_xl15);\tpadding-right: var(--space_xl15);"},"py_0":{"declaration":"padding-top: 0;\tpadding-bottom: 0;"},"py_xs5":{"declaration":"padding-top: var(--space_xs5);\tpadding-bottom: var(--space_xs5);"},"py_xs4":{"declaration":"padding-top: var(--space_xs4);\tpadding-bottom: var(--space_xs4);"},"py_xs3":{"declaration":"padding-top: var(--space_xs3);\tpadding-bottom: var(--space_xs3);"},"py_xs2":{"declaration":"padding-top: var(--space_xs2);\tpadding-bottom: var(--space_xs2);"},"py_xs":{"declaration":"padding-top: var(--space_xs);\tpadding-bottom: var(--space_xs);"},"py_sm":{"declaration":"padding-top: var(--space_sm);\tpadding-bottom: var(--space_sm);"},"py_md":{"declaration":"padding-top: var(--space_md);\tpadding-bottom: var(--space_md);"},"py_lg":{"declaration":"padding-top: var(--space_lg);\tpadding-bottom: var(--space_lg);"},"py_xl":{"declaration":"padding-top: var(--space_xl);\tpadding-bottom: var(--space_xl);"},"py_xl2":{"declaration":"padding-top: var(--space_xl2);\tpadding-bottom: var(--space_xl2);"},"py_xl3":{"declaration":"padding-top: var(--space_xl3);\tpadding-bottom: var(--space_xl3);"},"py_xl4":{"declaration":"padding-top: var(--space_xl4);\tpadding-bottom: var(--space_xl4);"},"py_xl5":{"declaration":"padding-top: var(--space_xl5);\tpadding-bottom: var(--space_xl5);"},"py_xl6":{"declaration":"padding-top: var(--space_xl6);\tpadding-bottom: var(--space_xl6);"},"py_xl7":{"declaration":"padding-top: var(--space_xl7);\tpadding-bottom: var(--space_xl7);"},"py_xl8":{"declaration":"padding-top: var(--space_xl8);\tpadding-bottom: var(--space_xl8);"},"py_xl9":{"declaration":"padding-top: var(--space_xl9);\tpadding-bottom: var(--space_xl9);"},"py_xl10":{"declaration":"padding-top: var(--space_xl10);\tpadding-bottom: var(--space_xl10);"},"py_xl11":{"declaration":"padding-top: var(--space_xl11);\tpadding-bottom: var(--space_xl11);"},"py_xl12":{"declaration":"padding-top: var(--space_xl12);\tpadding-bottom: var(--space_xl12);"},"py_xl13":{"declaration":"padding-top: var(--space_xl13);\tpadding-bottom: var(--space_xl13);"},"py_xl14":{"declaration":"padding-top: var(--space_xl14);\tpadding-bottom: var(--space_xl14);"},"py_xl15":{"declaration":"padding-top: var(--space_xl15);\tpadding-bottom: var(--space_xl15);"},"m_0":{"declaration":"margin: 0;"},"m_auto":{"declaration":"margin: auto;"},"m_xs5":{"declaration":"margin: var(--space_xs5);"},"m_xs4":{"declaration":"margin: var(--space_xs4);"},"m_xs3":{"declaration":"margin: var(--space_xs3);"},"m_xs2":{"declaration":"margin: var(--space_xs2);"},"m_xs":{"declaration":"margin: var(--space_xs);"},"m_sm":{"declaration":"margin: var(--space_sm);"},"m_md":{"declaration":"margin: var(--space_md);"},"m_lg":{"declaration":"margin: var(--space_lg);"},"m_xl":{"declaration":"margin: var(--space_xl);"},"m_xl2":{"declaration":"margin: var(--space_xl2);"},"m_xl3":{"declaration":"margin: var(--space_xl3);"},"m_xl4":{"declaration":"margin: var(--space_xl4);"},"m_xl5":{"declaration":"margin: var(--space_xl5);"},"m_xl6":{"declaration":"margin: var(--space_xl6);"},"m_xl7":{"declaration":"margin: var(--space_xl7);"},"m_xl8":{"declaration":"margin: var(--space_xl8);"},"m_xl9":{"declaration":"margin: var(--space_xl9);"},"m_xl10":{"declaration":"margin: var(--space_xl10);"},"m_xl11":{"declaration":"margin: var(--space_xl11);"},"m_xl12":{"declaration":"margin: var(--space_xl12);"},"m_xl13":{"declaration":"margin: var(--space_xl13);"},"m_xl14":{"declaration":"margin: var(--space_xl14);"},"m_xl15":{"declaration":"margin: var(--space_xl15);"},"mt_0":{"declaration":"margin-top: 0;"},"mt_auto":{"declaration":"margin-top: auto;"},"mt_xs5":{"declaration":"margin-top: var(--space_xs5);"},"mt_xs4":{"declaration":"margin-top: var(--space_xs4);"},"mt_xs3":{"declaration":"margin-top: var(--space_xs3);"},"mt_xs2":{"declaration":"margin-top: var(--space_xs2);"},"mt_xs":{"declaration":"margin-top: var(--space_xs);"},"mt_sm":{"declaration":"margin-top: var(--space_sm);"},"mt_md":{"declaration":"margin-top: var(--space_md);"},"mt_lg":{"declaration":"margin-top: var(--space_lg);"},"mt_xl":{"declaration":"margin-top: var(--space_xl);"},"mt_xl2":{"declaration":"margin-top: var(--space_xl2);"},"mt_xl3":{"declaration":"margin-top: var(--space_xl3);"},"mt_xl4":{"declaration":"margin-top: var(--space_xl4);"},"mt_xl5":{"declaration":"margin-top: var(--space_xl5);"},"mt_xl6":{"declaration":"margin-top: var(--space_xl6);"},"mt_xl7":{"declaration":"margin-top: var(--space_xl7);"},"mt_xl8":{"declaration":"margin-top: var(--space_xl8);"},"mt_xl9":{"declaration":"margin-top: var(--space_xl9);"},"mt_xl10":{"declaration":"margin-top: var(--space_xl10);"},"mt_xl11":{"declaration":"margin-top: var(--space_xl11);"},"mt_xl12":{"declaration":"margin-top: var(--space_xl12);"},"mt_xl13":{"declaration":"margin-top: var(--space_xl13);"},"mt_xl14":{"declaration":"margin-top: var(--space_xl14);"},"mt_xl15":{"declaration":"margin-top: var(--space_xl15);"},"mr_0":{"declaration":"margin-right: 0;"},"mr_auto":{"declaration":"margin-right: auto;"},"mr_xs5":{"declaration":"margin-right: var(--space_xs5);"},"mr_xs4":{"declaration":"margin-right: var(--space_xs4);"},"mr_xs3":{"declaration":"margin-right: var(--space_xs3);"},"mr_xs2":{"declaration":"margin-right: var(--space_xs2);"},"mr_xs":{"declaration":"margin-right: var(--space_xs);"},"mr_sm":{"declaration":"margin-right: var(--space_sm);"},"mr_md":{"declaration":"margin-right: var(--space_md);"},"mr_lg":{"declaration":"margin-right: var(--space_lg);"},"mr_xl":{"declaration":"margin-right: var(--space_xl);"},"mr_xl2":{"declaration":"margin-right: var(--space_xl2);"},"mr_xl3":{"declaration":"margin-right: var(--space_xl3);"},"mr_xl4":{"declaration":"margin-right: var(--space_xl4);"},"mr_xl5":{"declaration":"margin-right: var(--space_xl5);"},"mr_xl6":{"declaration":"margin-right: var(--space_xl6);"},"mr_xl7":{"declaration":"margin-right: var(--space_xl7);"},"mr_xl8":{"declaration":"margin-right: var(--space_xl8);"},"mr_xl9":{"declaration":"margin-right: var(--space_xl9);"},"mr_xl10":{"declaration":"margin-right: var(--space_xl10);"},"mr_xl11":{"declaration":"margin-right: var(--space_xl11);"},"mr_xl12":{"declaration":"margin-right: var(--space_xl12);"},"mr_xl13":{"declaration":"margin-right: var(--space_xl13);"},"mr_xl14":{"declaration":"margin-right: var(--space_xl14);"},"mr_xl15":{"declaration":"margin-right: var(--space_xl15);"},"mb_0":{"declaration":"margin-bottom: 0;"},"mb_auto":{"declaration":"margin-bottom: auto;"},"mb_xs5":{"declaration":"margin-bottom: var(--space_xs5);"},"mb_xs4":{"declaration":"margin-bottom: var(--space_xs4);"},"mb_xs3":{"declaration":"margin-bottom: var(--space_xs3);"},"mb_xs2":{"declaration":"margin-bottom: var(--space_xs2);"},"mb_xs":{"declaration":"margin-bottom: var(--space_xs);"},"mb_sm":{"declaration":"margin-bottom: var(--space_sm);"},"mb_md":{"declaration":"margin-bottom: var(--space_md);"},"mb_lg":{"declaration":"margin-bottom: var(--space_lg);"},"mb_xl":{"declaration":"margin-bottom: var(--space_xl);"},"mb_xl2":{"declaration":"margin-bottom: var(--space_xl2);"},"mb_xl3":{"declaration":"margin-bottom: var(--space_xl3);"},"mb_xl4":{"declaration":"margin-bottom: var(--space_xl4);"},"mb_xl5":{"declaration":"margin-bottom: var(--space_xl5);"},"mb_xl6":{"declaration":"margin-bottom: var(--space_xl6);"},"mb_xl7":{"declaration":"margin-bottom: var(--space_xl7);"},"mb_xl8":{"declaration":"margin-bottom: var(--space_xl8);"},"mb_xl9":{"declaration":"margin-bottom: var(--space_xl9);"},"mb_xl10":{"declaration":"margin-bottom: var(--space_xl10);"},"mb_xl11":{"declaration":"margin-bottom: var(--space_xl11);"},"mb_xl12":{"declaration":"margin-bottom: var(--space_xl12);"},"mb_xl13":{"declaration":"margin-bottom: var(--space_xl13);"},"mb_xl14":{"declaration":"margin-bottom: var(--space_xl14);"},"mb_xl15":{"declaration":"margin-bottom: var(--space_xl15);"},"ml_0":{"declaration":"margin-left: 0;"},"ml_auto":{"declaration":"margin-left: auto;"},"ml_xs5":{"declaration":"margin-left: var(--space_xs5);"},"ml_xs4":{"declaration":"margin-left: var(--space_xs4);"},"ml_xs3":{"declaration":"margin-left: var(--space_xs3);"},"ml_xs2":{"declaration":"margin-left: var(--space_xs2);"},"ml_xs":{"declaration":"margin-left: var(--space_xs);"},"ml_sm":{"declaration":"margin-left: var(--space_sm);"},"ml_md":{"declaration":"margin-left: var(--space_md);"},"ml_lg":{"declaration":"margin-left: var(--space_lg);"},"ml_xl":{"declaration":"margin-left: var(--space_xl);"},"ml_xl2":{"declaration":"margin-left: var(--space_xl2);"},"ml_xl3":{"declaration":"margin-left: var(--space_xl3);"},"ml_xl4":{"declaration":"margin-left: var(--space_xl4);"},"ml_xl5":{"declaration":"margin-left: var(--space_xl5);"},"ml_xl6":{"declaration":"margin-left: var(--space_xl6);"},"ml_xl7":{"declaration":"margin-left: var(--space_xl7);"},"ml_xl8":{"declaration":"margin-left: var(--space_xl8);"},"ml_xl9":{"declaration":"margin-left: var(--space_xl9);"},"ml_xl10":{"declaration":"margin-left: var(--space_xl10);"},"ml_xl11":{"declaration":"margin-left: var(--space_xl11);"},"ml_xl12":{"declaration":"margin-left: var(--space_xl12);"},"ml_xl13":{"declaration":"margin-left: var(--space_xl13);"},"ml_xl14":{"declaration":"margin-left: var(--space_xl14);"},"ml_xl15":{"declaration":"margin-left: var(--space_xl15);"},"mx_0":{"declaration":"margin-left: 0;\tmargin-right: 0;"},"mx_auto":{"declaration":"margin-left: auto;\tmargin-right: auto;"},"mx_xs5":{"declaration":"margin-left: var(--space_xs5);\tmargin-right: var(--space_xs5);"},"mx_xs4":{"declaration":"margin-left: var(--space_xs4);\tmargin-right: var(--space_xs4);"},"mx_xs3":{"declaration":"margin-left: var(--space_xs3);\tmargin-right: var(--space_xs3);"},"mx_xs2":{"declaration":"margin-left: var(--space_xs2);\tmargin-right: var(--space_xs2);"},"mx_xs":{"declaration":"margin-left: var(--space_xs);\tmargin-right: var(--space_xs);"},"mx_sm":{"declaration":"margin-left: var(--space_sm);\tmargin-right: var(--space_sm);"},"mx_md":{"declaration":"margin-left: var(--space_md);\tmargin-right: var(--space_md);"},"mx_lg":{"declaration":"margin-left: var(--space_lg);\tmargin-right: var(--space_lg);"},"mx_xl":{"declaration":"margin-left: var(--space_xl);\tmargin-right: var(--space_xl);"},"mx_xl2":{"declaration":"margin-left: var(--space_xl2);\tmargin-right: var(--space_xl2);"},"mx_xl3":{"declaration":"margin-left: var(--space_xl3);\tmargin-right: var(--space_xl3);"},"mx_xl4":{"declaration":"margin-left: var(--space_xl4);\tmargin-right: var(--space_xl4);"},"mx_xl5":{"declaration":"margin-left: var(--space_xl5);\tmargin-right: var(--space_xl5);"},"mx_xl6":{"declaration":"margin-left: var(--space_xl6);\tmargin-right: var(--space_xl6);"},"mx_xl7":{"declaration":"margin-left: var(--space_xl7);\tmargin-right: var(--space_xl7);"},"mx_xl8":{"declaration":"margin-left: var(--space_xl8);\tmargin-right: var(--space_xl8);"},"mx_xl9":{"declaration":"margin-left: var(--space_xl9);\tmargin-right: var(--space_xl9);"},"mx_xl10":{"declaration":"margin-left: var(--space_xl10);\tmargin-right: var(--space_xl10);"},"mx_xl11":{"declaration":"margin-left: var(--space_xl11);\tmargin-right: var(--space_xl11);"},"mx_xl12":{"declaration":"margin-left: var(--space_xl12);\tmargin-right: var(--space_xl12);"},"mx_xl13":{"declaration":"margin-left: var(--space_xl13);\tmargin-right: var(--space_xl13);"},"mx_xl14":{"declaration":"margin-left: var(--space_xl14);\tmargin-right: var(--space_xl14);"},"mx_xl15":{"declaration":"margin-left: var(--space_xl15);\tmargin-right: var(--space_xl15);"},"my_0":{"declaration":"margin-top: 0;\tmargin-bottom: 0;"},"my_auto":{"declaration":"margin-top: auto;\tmargin-bottom: auto;"},"my_xs5":{"declaration":"margin-top: var(--space_xs5);\tmargin-bottom: var(--space_xs5);"},"my_xs4":{"declaration":"margin-top: var(--space_xs4);\tmargin-bottom: var(--space_xs4);"},"my_xs3":{"declaration":"margin-top: var(--space_xs3);\tmargin-bottom: var(--space_xs3);"},"my_xs2":{"declaration":"margin-top: var(--space_xs2);\tmargin-bottom: var(--space_xs2);"},"my_xs":{"declaration":"margin-top: var(--space_xs);\tmargin-bottom: var(--space_xs);"},"my_sm":{"declaration":"margin-top: var(--space_sm);\tmargin-bottom: var(--space_sm);"},"my_md":{"declaration":"margin-top: var(--space_md);\tmargin-bottom: var(--space_md);"},"my_lg":{"declaration":"margin-top: var(--space_lg);\tmargin-bottom: var(--space_lg);"},"my_xl":{"declaration":"margin-top: var(--space_xl);\tmargin-bottom: var(--space_xl);"},"my_xl2":{"declaration":"margin-top: var(--space_xl2);\tmargin-bottom: var(--space_xl2);"},"my_xl3":{"declaration":"margin-top: var(--space_xl3);\tmargin-bottom: var(--space_xl3);"},"my_xl4":{"declaration":"margin-top: var(--space_xl4);\tmargin-bottom: var(--space_xl4);"},"my_xl5":{"declaration":"margin-top: var(--space_xl5);\tmargin-bottom: var(--space_xl5);"},"my_xl6":{"declaration":"margin-top: var(--space_xl6);\tmargin-bottom: var(--space_xl6);"},"my_xl7":{"declaration":"margin-top: var(--space_xl7);\tmargin-bottom: var(--space_xl7);"},"my_xl8":{"declaration":"margin-top: var(--space_xl8);\tmargin-bottom: var(--space_xl8);"},"my_xl9":{"declaration":"margin-top: var(--space_xl9);\tmargin-bottom: var(--space_xl9);"},"my_xl10":{"declaration":"margin-top: var(--space_xl10);\tmargin-bottom: var(--space_xl10);"},"my_xl11":{"declaration":"margin-top: var(--space_xl11);\tmargin-bottom: var(--space_xl11);"},"my_xl12":{"declaration":"margin-top: var(--space_xl12);\tmargin-bottom: var(--space_xl12);"},"my_xl13":{"declaration":"margin-top: var(--space_xl13);\tmargin-bottom: var(--space_xl13);"},"my_xl14":{"declaration":"margin-top: var(--space_xl14);\tmargin-bottom: var(--space_xl14);"},"my_xl15":{"declaration":"margin-top: var(--space_xl15);\tmargin-bottom: var(--space_xl15);"},"gap_xs5":{"declaration":"gap: var(--space_xs5);"},"gap_xs4":{"declaration":"gap: var(--space_xs4);"},"gap_xs3":{"declaration":"gap: var(--space_xs3);"},"gap_xs2":{"declaration":"gap: var(--space_xs2);"},"gap_xs":{"declaration":"gap: var(--space_xs);"},"gap_sm":{"declaration":"gap: var(--space_sm);"},"gap_md":{"declaration":"gap: var(--space_md);"},"gap_lg":{"declaration":"gap: var(--space_lg);"},"gap_xl":{"declaration":"gap: var(--space_xl);"},"gap_xl2":{"declaration":"gap: var(--space_xl2);"},"gap_xl3":{"declaration":"gap: var(--space_xl3);"},"gap_xl4":{"declaration":"gap: var(--space_xl4);"},"gap_xl5":{"declaration":"gap: var(--space_xl5);"},"gap_xl6":{"declaration":"gap: var(--space_xl6);"},"gap_xl7":{"declaration":"gap: var(--space_xl7);"},"gap_xl8":{"declaration":"gap: var(--space_xl8);"},"gap_xl9":{"declaration":"gap: var(--space_xl9);"},"gap_xl10":{"declaration":"gap: var(--space_xl10);"},"gap_xl11":{"declaration":"gap: var(--space_xl11);"},"gap_xl12":{"declaration":"gap: var(--space_xl12);"},"gap_xl13":{"declaration":"gap: var(--space_xl13);"},"gap_xl14":{"declaration":"gap: var(--space_xl14);"},"gap_xl15":{"declaration":"gap: var(--space_xl15);"},"column_gap_xs5":{"declaration":"column-gap: var(--space_xs5);"},"column_gap_xs4":{"declaration":"column-gap: var(--space_xs4);"},"column_gap_xs3":{"declaration":"column-gap: var(--space_xs3);"},"column_gap_xs2":{"declaration":"column-gap: var(--space_xs2);"},"column_gap_xs":{"declaration":"column-gap: var(--space_xs);"},"column_gap_sm":{"declaration":"column-gap: var(--space_sm);"},"column_gap_md":{"declaration":"column-gap: var(--space_md);"},"column_gap_lg":{"declaration":"column-gap: var(--space_lg);"},"column_gap_xl":{"declaration":"column-gap: var(--space_xl);"},"column_gap_xl2":{"declaration":"column-gap: var(--space_xl2);"},"column_gap_xl3":{"declaration":"column-gap: var(--space_xl3);"},"column_gap_xl4":{"declaration":"column-gap: var(--space_xl4);"},"column_gap_xl5":{"declaration":"column-gap: var(--space_xl5);"},"column_gap_xl6":{"declaration":"column-gap: var(--space_xl6);"},"column_gap_xl7":{"declaration":"column-gap: var(--space_xl7);"},"column_gap_xl8":{"declaration":"column-gap: var(--space_xl8);"},"column_gap_xl9":{"declaration":"column-gap: var(--space_xl9);"},"column_gap_xl10":{"declaration":"column-gap: var(--space_xl10);"},"column_gap_xl11":{"declaration":"column-gap: var(--space_xl11);"},"column_gap_xl12":{"declaration":"column-gap: var(--space_xl12);"},"column_gap_xl13":{"declaration":"column-gap: var(--space_xl13);"},"column_gap_xl14":{"declaration":"column-gap: var(--space_xl14);"},"column_gap_xl15":{"declaration":"column-gap: var(--space_xl15);"},"row_gap_xs5":{"declaration":"row-gap: var(--space_xs5);"},"row_gap_xs4":{"declaration":"row-gap: var(--space_xs4);"},"row_gap_xs3":{"declaration":"row-gap: var(--space_xs3);"},"row_gap_xs2":{"declaration":"row-gap: var(--space_xs2);"},"row_gap_xs":{"declaration":"row-gap: var(--space_xs);"},"row_gap_sm":{"declaration":"row-gap: var(--space_sm);"},"row_gap_md":{"declaration":"row-gap: var(--space_md);"},"row_gap_lg":{"declaration":"row-gap: var(--space_lg);"},"row_gap_xl":{"declaration":"row-gap: var(--space_xl);"},"row_gap_xl2":{"declaration":"row-gap: var(--space_xl2);"},"row_gap_xl3":{"declaration":"row-gap: var(--space_xl3);"},"row_gap_xl4":{"declaration":"row-gap: var(--space_xl4);"},"row_gap_xl5":{"declaration":"row-gap: var(--space_xl5);"},"row_gap_xl6":{"declaration":"row-gap: var(--space_xl6);"},"row_gap_xl7":{"declaration":"row-gap: var(--space_xl7);"},"row_gap_xl8":{"declaration":"row-gap: var(--space_xl8);"},"row_gap_xl9":{"declaration":"row-gap: var(--space_xl9);"},"row_gap_xl10":{"declaration":"row-gap: var(--space_xl10);"},"row_gap_xl11":{"declaration":"row-gap: var(--space_xl11);"},"row_gap_xl12":{"declaration":"row-gap: var(--space_xl12);"},"row_gap_xl13":{"declaration":"row-gap: var(--space_xl13);"},"row_gap_xl14":{"declaration":"row-gap: var(--space_xl14);"},"row_gap_xl15":{"declaration":"row-gap: var(--space_xl15);"}} \ No newline at end of file diff --git a/src/test/styles.test.ts b/src/test/styles.test.ts index fd0f35d1e..7c7f8ab74 100644 --- a/src/test/styles.test.ts +++ b/src/test/styles.test.ts @@ -59,7 +59,7 @@ test('variables in the CSS exist', () => { */ const known_without_variables = new Set([ ...theme_knob_hook_names, - 'fill', // contextual variable set by button palette classes (e.g., .palette_a sets --fill: var(--palette_a_40)) + 'fill', // contextual variable set by button palette classes (e.g., .palette_a sets --fill: var(--palette_a_50)) 'button_fill', 'button_fill_hover', 'button_fill_active', diff --git a/src/test/theme_editor_state.test.ts b/src/test/theme_editor_state.test.ts index 6325766fe..7ca31293c 100644 --- a/src/test/theme_editor_state.test.ts +++ b/src/test/theme_editor_state.test.ts @@ -231,6 +231,38 @@ describe('discard_confirm_message', () => { }); }); +describe('gates', () => { + test('an untouched draft passes the lint and gates', () => { + const editor = create_editor(); + assert.deepEqual(editor.issues, []); + assert.isTrue(editor.check_report.ok); + assert.isAbove(editor.check_report.entries.length, 0); + }); + + test('a gate-breaking edit surfaces failing entries', () => { + const editor = create_editor(); + // collapsing the shade lightness ramp to a single value breaks + // monotonicity in both schemes + editor.set_value('shade_lightness_00', '0.5', 'light'); + editor.set_value('shade_lightness_00', '0.5', 'dark'); + editor.set_value('shade_lightness_100', '0.5', 'light'); + editor.set_value('shade_lightness_100', '0.5', 'dark'); + assert.isFalse(editor.check_report.ok); + assert.isTrue( + editor.check_report.entries.some((e) => e.gate === 'monotonicity' && !e.pass), + 'the collapsed ramp fails the monotonicity gate' + ); + }); + + test('an unknown variable is a lint error', () => { + const editor = create_editor(); + editor.set_value('not_a_real_variable', '1', 'light'); + assert.isTrue( + editor.issues.some((i) => i.level === 'error' && i.variable === 'not_a_real_variable') + ); + }); +}); + // keep the type import "used" for the linter across fixture literals const _theme_type_check: Theme = base_theme; void _theme_type_check; diff --git a/src/test/vite_plugin_fuz_css.dev.test.ts b/src/test/vite_plugin_fuz_css.dev.test.ts index 2a3164731..e13bc57a8 100644 --- a/src/test/vite_plugin_fuz_css.dev.test.ts +++ b/src/test/vite_plugin_fuz_css.dev.test.ts @@ -5,6 +5,7 @@ import { dirname, join } from 'node:path'; import { rm } from 'node:fs/promises'; import { vite_plugin_fuz_css, type VitePluginFuzCssOptions } from '$lib/vite_plugin_fuz_css.ts'; +import { default_cache_deps } from '$lib/deps_defaults.ts'; const fixture_root = join(dirname(fileURLToPath(import.meta.url)), 'fixtures/vite_dev'); @@ -73,4 +74,56 @@ describe('vite_plugin_fuz_css dev pre-scan', () => { await server.close(); } }); + + test('a file that throws mid-scan does not abort the rest of the pre-scan', async () => { + // `each_concurrent` is fail-fast - without per-file isolation one bad + // file would silently skip every file not yet in flight for the + // server's life. The fixture is smaller than the scan concurrency, so + // the discriminating assertion is the log shape: the per-file message + // (isolated) vs the whole-scan `pre-scan failed:` abort (fail-fast). + const errors: Array<string> = []; + const noop = () => {}; + const server = await createServer({ + root: fixture_root, + configFile: false, + customLogger: { + info: noop, + warn: noop, + warnOnce: noop, + error: (msg) => void errors.push(msg), + clearScreen: noop, + hasErrorLogged: () => false, + hasWarned: false + }, + server: { middlewareMode: true, ws: false }, + optimizeDeps: { noDiscovery: true }, + plugins: [ + vite_plugin_fuz_css({ + filter_file: filter_fixture_file, + deps: { + ...default_cache_deps, + read_text: async (options) => { + if (options.path.endsWith('island.html')) { + throw new Error('synthetic read failure'); + } + return default_cache_deps.read_text(options); + } + } + }) + ] + }); + try { + const result = await server.transformRequest('/__fuz.css'); + assert(result); + assert(result.code.includes('.p_md'), 'other files still extract'); + assert(!result.code.includes('.gap_lg'), 'the throwing file is skipped'); + assert( + errors.some((m) => m.includes('pre-scan failed to extract')), + 'the failure is logged per file' + ); + assert(!errors.some((m) => m.includes('pre-scan failed:')), 'the scan itself does not abort'); + } finally { + await server.close(); + } + }); }); diff --git a/vite.config.ts b/vite.config.ts index f102c1d68..d8203fc28 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,7 +6,6 @@ import { vite_plugin_pkg_json } from '@fuzdev/fuz_ui/vite_plugin_pkg_json.ts'; import { vite_plugin_fuz_css } from './src/lib/vite_plugin_fuz_css.ts'; import { alpha_variants, - darken_lighten_variants, intensity_variants, outline_width_variants, palette_variants, @@ -20,17 +19,13 @@ import { // static extraction can't see them - declared here from the variant lists // instead of per-page @fuz-classes hint walls const docs_classes: Array<string> = [ - ...alpha_variants.map((v) => `fg_${v}`), - ...alpha_variants.map((v) => `bg_${v}`), - ...darken_lighten_variants.map((v) => `darken_${v}`), - ...darken_lighten_variants.map((v) => `lighten_${v}`), ...alpha_variants.map((v) => `border_color_${v}`), ...outline_width_variants.map((v) => `outline_width_${v}`), ...shadow_variant_prefixes.flatMap((p) => shadow_size_variants.map((s) => `${p}${s}`)), ...shadow_alpha_variants.map((v) => `shadow_alpha_${v}`), ...shadow_semantic_values.map((v) => `shadow_color_${v}`), ...palette_variants.flatMap((l) => intensity_variants.map((i) => `shadow_${l}_${i}`)), - ...palette_variants.map((l) => `palette_${l}_50`) + ...palette_variants.map((l) => `color_${l}_50`) ]; export default defineConfig({