From 9c943dfd55330b37e8b5eef83f563cc25fc07ded Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 17 Mar 2026 14:03:05 -0400 Subject: [PATCH 01/13] wip --- .changeset/rare-cars-prove.md | 5 ++ CLAUDE.md | 4 +- src/lib/css_class_composites.ts | 22 ++++++-- src/lib/style.css | 2 +- src/routes/docs/buttons/+page.svelte | 38 +++++++------- src/routes/docs/chips/+page.svelte | 24 ++++----- src/routes/docs/classes/+page.svelte | 3 +- src/routes/docs/forms/+page.svelte | 12 ++--- src/routes/docs/semantic/+page.svelte | 6 +-- src/routes/docs/typography/+page.svelte | 14 +++--- src/routes/fuz.css | 2 +- src/test/fixtures/css_classes_fixture.json | 10 ++-- src/test/generate_classes_css.test.ts | 56 ++++++++++++++++++--- src/test/modified_class_interpreter.test.ts | 6 +-- 14 files changed, 136 insertions(+), 68 deletions(-) create mode 100644 .changeset/rare-cars-prove.md diff --git a/.changeset/rare-cars-prove.md b/.changeset/rare-cars-prove.md new file mode 100644 index 000000000..6bf5864b4 --- /dev/null +++ b/.changeset/rare-cars-prove.md @@ -0,0 +1,5 @@ +--- +'@fuzdev/fuz_css': minor +--- + +replace `.compact` with `.sm` and add `.xs-.xl` diff --git a/CLAUDE.md b/CLAUDE.md index 507323c08..25f47785c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,8 +114,8 @@ See `GenFuzCssOptions` and `VitePluginFuzCssOptions` types for configuration. - **Token classes** - Map to style variables: `p_md`, `color_a_50`, `gap_lg` - **Composite classes** - Multi-property shortcuts: `box`, `column`, `row`, `ellipsis`, `pixelated`, `circular`, `selectable`, `clickable`, `pane`, - `panel`, `compact` (consider `.loose`/`.spacious` counterpart), `icon_button`, - `plain`, `menuitem`, `chevron`, `chip` + `panel`, `sm` (tighter sizing), `md` (default sizing / cascade reset), + `icon_button`, `plain`, `menuitem`, `chevron`, `chip` - **Literal classes** - CSS `property:value` syntax: `display:flex`, `opacity:50%` All class types support modifiers: responsive (`md:`), state (`hover:`), diff --git a/src/lib/css_class_composites.ts b/src/lib/css_class_composites.ts index fa0accd33..e4cf2e02e 100644 --- a/src/lib/css_class_composites.ts +++ b/src/lib/css_class_composites.ts @@ -108,8 +108,7 @@ export const css_class_composites: Record - - With .compact + + With .sm

- The .compact + The .sm composite class sizes things more tightly with smaller fonts, inputs, padding, border radii, and flow margins.

- compact`} /> + sm`} />
- +
-

.compact with .plain and .icon_button:

+

.sm with .plain and .icon_button:

+++\n\n\n\n`} + content={`\n\n\n\n`} />
- - - - + + + +
-

.compact with colors:

+

.sm with colors:

color_h\n\n`} + content={`\n\n`} />
- - - + + +

- .compact overrides custom properties, so children inherit compactness: + .sm overrides custom properties, so children inherit the sizing:

\n\t\n\t\n\t\n`} + content={`
\n\t\n\t\n\t\n
`} /> -
+
diff --git a/src/routes/docs/chips/+page.svelte b/src/routes/docs/chips/+page.svelte index e5c2d72b1..d04f2d371 100644 --- a/src/routes/docs/chips/+page.svelte +++ b/src/routes/docs/chips/+page.svelte @@ -63,34 +63,34 @@ - - With .compact + + With .sm

- The .compact + The .sm composite class provides tighter sizing with smaller fonts, inputs, padding, border radii, and flow margins.

- compact`} /> + sm`} />
- compact + sm normal
color_a\ncolor_b\ncolor_c`} + content={`color_a\ncolor_b\ncolor_c`} />
- color_a - color_b - color_c + color_a + color_b + color_c

- .compact overrides custom properties, so children are compact too: + .sm overrides custom properties, so children inherit the sizing:

\n\tone\n\ttwo\n\tthree\n
`} + content={`
\n\tone\n\ttwo\n\tthree\n
`} /> -
+
one two diff --git a/src/routes/docs/classes/+page.svelte b/src/routes/docs/classes/+page.svelte index 71ae8d64e..6cce92f49 100644 --- a/src/routes/docs/classes/+page.svelte +++ b/src/routes/docs/classes/+page.svelte @@ -1055,7 +1055,8 @@ export const gen = gen_fuz_css({
  • .icon_button - icon button styling
  • .pixelated - crisp pixel-art rendering
  • .circular - 50% border-radius
  • -
  • .compact - tighter sizing, cascading to children
  • +
  • .sm - tighter sizing, cascading to children
  • +
  • .md - default sizing, cascade reset
  • .mb_flow - flow-aware margin-bottom
  • .mt_flow - flow-aware margin-top
  • diff --git a/src/routes/docs/forms/+page.svelte b/src/routes/docs/forms/+page.svelte index b04785b39..5f1cc30a2 100644 --- a/src/routes/docs/forms/+page.svelte +++ b/src/routes/docs/forms/+page.svelte @@ -205,25 +205,25 @@ - - With .compact + + With .sm

    - The .compact + The .sm composite class provides tighter sizing with smaller fonts, inputs, padding, border radii, and flow margins. Apply directly or on a container to cascade to children.

    + content={`
    ...
    `} />
    -
    +
    - .compact + .sm
    `} + content={`
    \n\t

    sm heading

    \n\t

    sm paragraph

    \n\t

    sm paragraph

    \n
    `} />
    -
    -

    compact

    -

    Paragraph in a compact container with tighter flow margins between elements.

    +
    +

    sm

    +

    Paragraph in a sm container with tighter flow margins between elements.

    Another paragraph showing the reduced spacing.

    • list item one
    • diff --git a/src/routes/fuz.css b/src/routes/fuz.css index 1dade2489..fbdae38c1 100644 --- a/src/routes/fuz.css +++ b/src/routes/fuz.css @@ -1734,7 +1734,7 @@ body { background-color: var(--fg_10); } /* Tighter sizing by overriding variables, cascading to children. Works on individual elements or containers. */ -.compact { +.sm { --font_size: var(--font_size_sm); --input_height: var(--space_xl3); --input_height_sm: var(--space_xl2); diff --git a/src/test/fixtures/css_classes_fixture.json b/src/test/fixtures/css_classes_fixture.json index d0db395a7..eac64a3d8 100644 --- a/src/test/fixtures/css_classes_fixture.json +++ b/src/test/fixtures/css_classes_fixture.json @@ -31,16 +31,20 @@ "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" }, - "compact": { + "sm": { "comment": "Tighter sizing by overriding variables, 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_xl3);\n\t\t\t--input_height_sm: var(--space_xl2);\n\t\t\t--input_padding_x: var(--space_sm);\n\t\t\t--min_height: var(--space_xl3);\n\t\t\t--border_radius: var(--border_radius_xs2);\n\t\t\t--icon_size: var(--icon_size_sm);\n\t\t\t--menuitem_padding: var(--space_xs4) var(--space_xs3);\n\t\t\t--flow_margin: var(--space_md);\n\t\t" }, + "md": { + "comment": "Explicit default sizing, useful as a cascade reset within a sized parent like .sm.", + "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_sm: var(--space_xl4);\n\t\t\t--input_padding_x: var(--space_lg);\n\t\t\t--min_height: var(--input_height);\n\t\t\t--border_radius: var(--border_radius_sm);\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" + }, "mb_flow": { - "comment": "Flow-aware margin-bottom that responds to --flow_margin overrides like .compact.", + "comment": "Flow-aware margin-bottom that responds to --flow_margin overrides like .sm.", "declaration": "margin-bottom: var(--flow_margin, var(--space_lg));" }, "mt_flow": { - "comment": "Flow-aware margin-top that responds to --flow_margin overrides like .compact.", + "comment": "Flow-aware margin-top that responds to --flow_margin overrides like .sm.", "declaration": "margin-top: var(--flow_margin, var(--space_lg));" }, "icon_button": { diff --git a/src/test/generate_classes_css.test.ts b/src/test/generate_classes_css.test.ts index 25015eedb..f159df7b5 100644 --- a/src/test/generate_classes_css.test.ts +++ b/src/test/generate_classes_css.test.ts @@ -806,10 +806,10 @@ describe('generate_classes_css', () => { }); }); - describe('compact composite', () => { - test('generates compact class with density overrides', () => { + describe('sm composite', () => { + test('generates sm class with density overrides', () => { const result = generate_classes_css({ - class_names: ['compact'], + class_names: ['sm'], class_definitions: css_class_composites, interpreters: [], css_properties: null, @@ -817,7 +817,7 @@ describe('generate_classes_css', () => { expect_css_contains( result.css, - '.compact {', + '.sm {', '--font_size: var(--font_size_sm);', '--input_height: var(--space_xl3);', '--input_height_sm: var(--space_xl2);', @@ -831,9 +831,9 @@ describe('generate_classes_css', () => { expect(result.diagnostics).toHaveLength(0); }); - test('compact tracks used variables', () => { + test('sm tracks used variables', () => { const result = generate_classes_css({ - class_names: ['compact'], + class_names: ['sm'], class_definitions: css_class_composites, interpreters: [], css_properties: null, @@ -851,6 +851,50 @@ describe('generate_classes_css', () => { }); }); + describe('md composite', () => { + test('generates md class with default sizing', () => { + const result = generate_classes_css({ + class_names: ['md'], + class_definitions: css_class_composites, + interpreters: [], + css_properties: null, + }); + + expect_css_contains( + result.css, + '.md {', + '--font_size: var(--font_size_md);', + '--input_height: var(--space_xl5);', + '--input_height_sm: var(--space_xl4);', + '--input_padding_x: var(--space_lg);', + '--min_height: var(--input_height);', + '--border_radius: var(--border_radius_sm);', + '--icon_size: var(--icon_size_md);', + '--menuitem_padding: var(--space_xs3) var(--space_xs);', + '--flow_margin: var(--space_lg);', + ); + expect(result.diagnostics).toHaveLength(0); + }); + + test('md tracks used variables', () => { + const result = generate_classes_css({ + class_names: ['md'], + class_definitions: css_class_composites, + interpreters: [], + css_properties: null, + }); + + expect(result.variables_used.has('font_size_md')).toBe(true); + expect(result.variables_used.has('space_xl5')).toBe(true); + expect(result.variables_used.has('space_xl4')).toBe(true); + expect(result.variables_used.has('space_lg')).toBe(true); + expect(result.variables_used.has('border_radius_sm')).toBe(true); + expect(result.variables_used.has('icon_size_md')).toBe(true); + expect(result.variables_used.has('space_xs3')).toBe(true); + expect(result.variables_used.has('space_xs')).toBe(true); + }); + }); + describe('flow margin composites', () => { test('mb_flow generates flow-aware margin-bottom', () => { const result = generate_classes_css({ diff --git a/src/test/modified_class_interpreter.test.ts b/src/test/modified_class_interpreter.test.ts index 9a519ae09..df6b51696 100644 --- a/src/test/modified_class_interpreter.test.ts +++ b/src/test/modified_class_interpreter.test.ts @@ -492,9 +492,9 @@ describe('modified_class_interpreter', () => { expect(result.diagnostics).toHaveLength(0); }); - test('md:compact generates media query with density overrides', () => { + test('md:sm generates media query with density overrides', () => { const result = generate_classes_css({ - class_names: ['md:compact'], + class_names: ['md:sm'], class_definitions: css_class_composites, interpreters: [modified_class_interpreter], css_properties: null, @@ -503,7 +503,7 @@ describe('modified_class_interpreter', () => { expect_css_contains( result.css, '@media (width >= 48rem)', - '.md\\:compact', + '.md\\:sm', '--font_size: var(--font_size_sm);', '--input_height: var(--space_xl3);', '--border_radius: var(--border_radius_xs2);', From fe41e9920799418a9ee507589781e7b3008339d5 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 17 Mar 2026 14:17:50 -0400 Subject: [PATCH 02/13] wip --- src/lib/css_class_composites.ts | 3 +- src/lib/css_class_interpreters.ts | 17 ++++++--- src/routes/library.json | 4 +- src/test/modified_class_interpreter.test.ts | 41 +++++++++++++++++++++ 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/lib/css_class_composites.ts b/src/lib/css_class_composites.ts index e4cf2e02e..8314461d5 100644 --- a/src/lib/css_class_composites.ts +++ b/src/lib/css_class_composites.ts @@ -124,8 +124,7 @@ export const css_class_composites: Record { ); expect(result.diagnostics).toHaveLength(0); }); + + test('hover:sm applies hover state to sm composite', () => { + const result = generate_classes_css({ + class_names: ['hover:sm'], + class_definitions: css_class_composites, + interpreters: [modified_class_interpreter], + css_properties: null, + }); + + expect_css_contains(result.css, '.hover\\:sm:hover', '--font_size: var(--font_size_sm);'); + expect(result.diagnostics).toHaveLength(0); + }); + + test('hover:md applies hover state to md composite', () => { + const result = generate_classes_css({ + class_names: ['hover:md'], + class_definitions: css_class_composites, + interpreters: [modified_class_interpreter], + css_properties: null, + }); + + expect_css_contains(result.css, '.hover\\:md:hover', '--font_size: var(--font_size_md);'); + expect(result.diagnostics).toHaveLength(0); + }); + + test('lg:sm generates lg breakpoint with sm sizing', () => { + const result = generate_classes_css({ + class_names: ['lg:sm'], + class_definitions: css_class_composites, + interpreters: [modified_class_interpreter], + css_properties: null, + }); + + expect_css_contains( + result.css, + '@media (width >= 64rem)', + '.lg\\:sm', + '--font_size: var(--font_size_sm);', + ); + expect(result.diagnostics).toHaveLength(0); + }); }); describe('error propagation', () => { From de8ad2b725c1ff78554e99afdc1c2524cb19ec83 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 17 Mar 2026 14:18:56 -0400 Subject: [PATCH 03/13] wip --- src/lib/css_literal.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/css_literal.ts b/src/lib/css_literal.ts index b5a1a82c1..92f32a433 100644 --- a/src/lib/css_literal.ts +++ b/src/lib/css_literal.ts @@ -106,7 +106,7 @@ export const load_css_properties = async (): Promise> => { * Custom properties (--*) always return true. * * @param property - the CSS property name to validate - * @param properties - Set of valid CSS properties from `load_css_properties()`. + * @param properties - Set of valid CSS properties from `load_css_properties()` * Pass `null` to skip validation. * @returns true if valid CSS property or custom property */ @@ -140,7 +140,7 @@ const find_closest_match = (typo: string, candidates: Iterable): string * Suggests a correct property name for a typo using Levenshtein distance. * * @param typo - the mistyped property name - * @param properties - Set of valid CSS properties from `load_css_properties()`. + * @param properties - Set of valid CSS properties from `load_css_properties()` * Pass `null` to skip suggestions. * @returns the suggested property or null if no close match (Levenshtein distance > 2) */ @@ -444,7 +444,7 @@ export const extract_and_validate_modifiers = ( * Parses a CSS-literal class name into its components. * * @param class_name - the class name to parse - * @param css_properties - Set of valid CSS properties from `load_css_properties()`. + * @param css_properties - Set of valid CSS properties from `load_css_properties()` * Pass `null` to skip property validation. * @returns `CssLiteralParseResult` with parsed data or error */ @@ -594,7 +594,7 @@ export interface CssLiteralOutput { * * @param class_name - the class name to interpret * @param escaped_class_name - the CSS-escaped version of the class name - * @param css_properties - Set of valid CSS properties from `load_css_properties()`. + * @param css_properties - Set of valid CSS properties from `load_css_properties()` * Pass `null` to skip property validation. * @returns result with output and warnings on success, or error on failure */ From ef31a50dce07e6fe65e5a1097dd2e23d558c40a1 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 17 Mar 2026 14:19:04 -0400 Subject: [PATCH 04/13] wip --- src/routes/library.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/library.json b/src/routes/library.json index c1e46f1ac..78a4ead78 100644 --- a/src/routes/library.json +++ b/src/routes/library.json @@ -1900,7 +1900,7 @@ { "name": "properties", "type": "Set | null", - "description": "Set of valid CSS properties from `load_css_properties()`.\n Pass `null` to skip validation." + "description": "Set of valid CSS properties from `load_css_properties()`\n Pass `null` to skip validation." } ] }, @@ -1921,7 +1921,7 @@ { "name": "properties", "type": "Set | null", - "description": "Set of valid CSS properties from `load_css_properties()`.\n Pass `null` to skip suggestions." + "description": "Set of valid CSS properties from `load_css_properties()`\n Pass `null` to skip suggestions." } ] }, @@ -2044,7 +2044,7 @@ { "name": "css_properties", "type": "Set | null", - "description": "Set of valid CSS properties from `load_css_properties()`.\n Pass `null` to skip property validation." + "description": "Set of valid CSS properties from `load_css_properties()`\n Pass `null` to skip property validation." } ] }, @@ -2135,7 +2135,7 @@ { "name": "css_properties", "type": "Set | null", - "description": "Set of valid CSS properties from `load_css_properties()`.\n Pass `null` to skip property validation." + "description": "Set of valid CSS properties from `load_css_properties()`\n Pass `null` to skip property validation." } ] }, From 5b579c4e05eacfa038e53a4ec19efad6a1b623c5 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 17 Mar 2026 14:55:19 -0400 Subject: [PATCH 05/13] wip --- src/routes/docs/chips/+page.svelte | 4 +-- src/routes/docs/typography/+page.svelte | 30 ++++++++++++++++++-- src/routes/fuz.css | 12 ++++++++ src/test/modified_class_interpreter.test.ts | 31 +++++++++++++++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/src/routes/docs/chips/+page.svelte b/src/routes/docs/chips/+page.svelte index d04f2d371..43f9de1fb 100644 --- a/src/routes/docs/chips/+page.svelte +++ b/src/routes/docs/chips/+page.svelte @@ -71,9 +71,9 @@ composite class provides tighter sizing with smaller fonts, inputs, padding, border radii, and flow margins.

      - sm`} /> + small`} />
      - sm + small normal
      \n\t

      sm heading

      \n\t

      sm paragraph

      \n\t

      sm paragraph

      \n
    `} + content={`
    \n\t

    small heading

    \n\t

    small paragraph

    \n\t

    small paragraph

    \n
    `} />
    -

    sm

    -

    Paragraph in a sm container with tighter flow margins between elements.

    +

    small

    +

    Paragraph in a small container with tighter flow margins between elements.

    Another paragraph showing the reduced spacing.

    • list item one
    • @@ -240,6 +240,30 @@
    + + + + Reset with .md + +

    + The .md + composite class resets sizing to the + defaults. Use it inside a sized container to restore normal sizing for a subtree. +

    + \n\t

    small text

    \n\t
    \n\t\t

    back to normal

    \n\t
    \n
    `} + /> +
    +
    +

    small region

    +

    Everything here is small.

    +
    +

    normal nested inside

    +

    This region is back to default sizing despite the parent being small.

    +
    +
    +
    +