From 9b4abd2858ebfd1b663dcbdf525687f75967a4a8 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Wed, 7 Jan 2026 07:00:33 -0500 Subject: [PATCH 01/56] wip --- .changeset/empty-knives-walk.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/empty-knives-walk.md diff --git a/.changeset/empty-knives-walk.md b/.changeset/empty-knives-walk.md new file mode 100644 index 000000000..dbfa077b1 --- /dev/null +++ b/.changeset/empty-knives-walk.md @@ -0,0 +1,5 @@ +--- +'@fuzdev/fuz_css': minor +--- + +implement CSS literal classes From 73b6f72439f0a1ddb033527c242b02ef41acc9a0 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Wed, 7 Jan 2026 08:59:19 -0500 Subject: [PATCH 02/56] wip --- src/routes/docs/classes/+page.svelte | 667 +++++++++++++++------- src/routes/docs/introduction/+page.svelte | 13 +- src/routes/fuz.css | 3 - 3 files changed, 457 insertions(+), 226 deletions(-) diff --git a/src/routes/docs/classes/+page.svelte b/src/routes/docs/classes/+page.svelte index 58ea9bf97..91e3cf90e 100644 --- a/src/routes/docs/classes/+page.svelte +++ b/src/routes/docs/classes/+page.svelte @@ -6,237 +6,453 @@ import TomeSection from '@fuzdev/fuz_ui/TomeSection.svelte'; import TomeLink from '@fuzdev/fuz_ui/TomeLink.svelte'; - import UnfinishedImplementationWarning from '$routes/docs/UnfinishedImplementationWarning.svelte'; import ModuleLink from '$routes/ModuleLink.svelte'; const LIBRARY_ITEM_NAME = 'classes'; - const GLYPH_IDEA = '⌆'; // TODO from fuz_util, upstreamed from Zzz? + const GLYPH_IDEA = '⌆'; const tome = get_tome_by_name(LIBRARY_ITEM_NAME); - - // TODO show these with `Details` hiding their expanded set of values or something better (interpolated using this shorthand as the source of truth? isn't that complex) - - // TODO generate these from `$lib/css_classes.ts` - const style_utility_groups: Array<{group: string; items: Array}> = [ - { - group: 'Position and display', - items: [ - `position_static|relative|absolute|fixed|sticky|$globals`, - // TODO think about making `display_` bold, and making this more systematic in general - `display_none|contents|block|flow_root|inline|inline_block|run_in|list_item|inline_list_item|flex|inline_flex|grid|inline_grid|ruby|block_ruby|table|inline_table|$globals`, - `visibility_visible|hidden|collapse|$globals`, - `float_left|right|none|inline_start|inline_end|$globals`, - 'opacity_0-100', - 'overflow_auto|hidden|scroll|clip|visible', - 'overflow_x|y_auto|hidden|scroll|clip|visible', - `overflow_wrap_normal|anywhere|break_word|$globals`, - `scrollbar_width_auto|thin|none|$globals`, - `scrollbar_gutter_auto|stable|stable_both_edges|$globals`, - ], - }, - { - group: 'Flexbox and grid', - items: [ - 'flex_1', - 'flex_wrap_wrap|wrap_reverse|nowrap|$globals', - 'flex_direction_row|column|row_reverse|column_reverse|$globals', - 'flex_grow|shrink_1|0', - 'align_items_center|start|end|baseline|stretch', - 'align_content_center|start|end|baseline|space_between|space_around|space_evenly|stretch', - 'align_self_center|start|end|baseline|stretch', - 'justify_content_center|start|end|left|right|space_between|space_around|space_evenly|stretch', - 'justify_items_center|start|end|left|right|baseline|stretch', - 'justify_self_center|start|end|left|right|baseline|stretch', - ], - }, - { - group: 'Sizing and spacing', - items: [ - 'width|height_0|100|1px-3px|auto|max_content|min_content|fit_content|stretch', - 'width|height_xs5-xl15', - 'top|bottom|left|right_0|100|1px-3px|auto', - 'top|bottom|left|right_xs5-xl15', - 'inset_0|1px-3px|xs5-xl15', - 'p|pt|pr|pb|pl|px|py_xs5-xl15', - 'p|pt|pr|pb|pl|px|py_0|1px-3px', - 'pt|pr|pb|pl_100', - 'm|mt|mr|mb|ml|mx|my_xs5-xl15', - 'm|mt|mr|mb|ml|mx|my_0|1px-3px|auto', - 'mt|mr|mb|ml_100', - 'gap_xs5-xl15', - 'column|row_gap_xs5-xl15', - 'width_upto_xs-xl', // TODO rename? min/max? minned/maxxed? atmost/atleast? - 'width_atleast_xs-xl', - 'height_upto_xs-xl', - 'height_atleast_xs-xl', - ], - }, - { - group: 'Typography', - items: [ - 'font_family_sans|serif|mono', - 'line_height_xs-xl|0|1', - 'font_size_xs-xl9', - 'icon_size_xs-xl3', - 'text_align_start|end|left|right|center|justify|justify_all|match_parent', - 'vertical_align_baseline|sub|super|text_top|text_bottom|middle|top|bottom', - `word_break_normal|break_all|keep_all|$globals`, - 'white_space_normal|nowrap|pre|pre_wrap|pre_line|break_spaces', - `white_space_collapse_collapse|preserve|preserve_breaks|preserve_spaces|break_spaces|$globals`, - 'text_wrap_wrap|nowrap|balance|pretty|stable', - `user_select_none|auto|text|all|$globals`, - 'font_weight_100-900', - 'ellipsis', - ], - }, - { - group: 'Colors', - items: [ - 'text_color_0-10', - 'darken|lighten_1-9', - 'bg|fg', - 'bg|fg_1-9', - 'color_darken|lighten_1-9', - 'color_bg|fg', - 'color_bg|fg_1-9', - 'hue_a-j', - 'color_a-j_1-9', - 'bg_a-j_1-9', - ], - }, - { - group: 'Borders and outlines', - items: [ - 'border_color_1-5', // TODO change this - 'border_color_a-j', - 'border_color_transparent', - 'border_width_0-9', - 'outline_width_0|focused|active', - `border_style_none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset|$globals`, - 'border_radius_xs3-xl', - 'border_radius_0-100', - 'border_top|bottom_left|right_radius_xs3-xl', - 'border_top|bottom_left|right_radius_0-100', - ], - }, - { - group: 'Shadows', - items: [ - 'shadow_xs-xl', - 'shadow_top|bottom_xs-xl', - 'shadow_inset_xs-xl', - 'shadow_inset_top|bottom_xs-xl', - 'shadow_color_highlight|glow|shroud', - 'shadow_color_a-j', - 'shadow_alpha_1-5', - 'shadow_inherit|none', - ], - }, - { - group: 'Transforms and visual effects', - items: ['flip_x|y|xy', 'pixelated'], - }, - { - group: 'Composite classes', - items: [ - 'box', - 'column', - 'row', - 'formatted', - 'selected', - 'selectable', - 'clickable', - 'pane', - 'panel', - 'icon_button', - 'plain', - 'menu_item', - 'chevron', - 'chip', - ], - }, - ]; - - // TODO extract a `GithubLink` like `MdnLink` - - Both the docs and implementation of these need a lot more work. - - -

Fuz CSS has three CSS files, two of which are required:

- -${'<' as string}script> - import '@fuzdev/fuz_css/style.css'; // required - import '@fuzdev/fuz_css/theme.css'; // required, can bring your own - import '$routes/fuz.css'; // optional, generated by \`gen_fuz_css\` - // ... -`} - /> + +

Fuz CSS provides three types of classes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
TypeExamplePurpose
token classes.p_md, .color_a_5, .gap_lgmap to style variables (CSS custom properties)
composite classes.box, .row, .ellipsismulti-property shortcuts
literal classes.display:flex, .hover:opacity:80%arbitrary CSS property:value pairs

- The fuz.css file is created on demand with the utility classes that your code - uses, if any. For now it requires Gro to - generate it, but it isn't hard to make your own integration using the helpers - gen_fuz_css.ts. I can add a Vite plugin if - there's demand. + Token classes are the primary choice for spacing, colors, and sizes - they + ensure consistency with the design system. Composite classes provide + shortcuts for repeated patterns. Literal classes are an escape hatch for arbitrary + CSS, especially useful for cross-component styling and responsive/state modifiers.

+ + + +

+ Token classes map to style variables. They use + underscore syntax because they're shorthand for design tokens, so they have consistent + casing in both JS and CSS: +

+ +

Spacing

+
    +
  • .p|pt|pr|pb|pl|px|py_xs5-xl15
  • +
  • .m|mt|mr|mb|ml|mx|my_xs5-xl15
  • +
  • .gap|column_gap|row_gap_xs5-xl15
  • +
  • .top|bottom|left|right_xs5-xl15
  • +
  • .inset_xs5-xl15
  • +
+

Sizing

+
    +
  • .width|height_xs5-xl15
  • +
  • .width_upto|atleast_xs-xl
  • +
  • .height_upto|atleast_xs-xl
  • +
+

Colors

+
    +
  • .color_a-j_1-9
  • +
  • .bg|fg_1-9
  • +
  • .bg_a-j_1-9
  • +
  • .text_color_0-10
  • +
  • .darken|lighten_1-9
  • +
  • .hue_a-j
  • +
+

Typography

+
    +
  • .font_family_sans|serif|mono
  • +
  • .font_size_xs-xl9
  • +
  • .line_height_xs-xl
  • +
  • .icon_size_xs-xl3
  • +
+

Borders

+
    +
  • .border_color_1-5
  • +
  • .border_color_a-j
  • +
  • .border_width_0-9
  • +
  • .border_radius_xs3-xl
  • +
  • .outline_width_0|focused|active
  • +
+

Shadows

+
    +
  • .shadow_xs-xl
  • +
  • .shadow_inset_xs-xl
  • +
  • .shadow_color_a-j
  • +
  • .shadow_alpha_1-5
  • +
+
+ + + +

Multi-property shortcuts for repeated patterns. Define your own in a composites file:

+ +

Built-in composites:

+
    +
  • .box - centered flex container
  • +
  • .row - horizontal flex row
  • +
  • .column - vertical flex column
  • +
  • .formatted - formatted text block
  • +
  • .ellipsis - text overflow ellipsis
  • +
  • .selected - selected state styling
  • +
  • .selectable - selectable element styling
  • +
  • .clickable - clickable element styling
  • +
  • .pane - pane container
  • +
  • .panel - panel container
  • +
  • .icon_button - icon button styling
  • +
  • .plain - plain/reset styling
  • +
  • .menu_item - menu item styling
  • +
  • .chevron - chevron indicator
  • +
  • .chip - chip/tag styling
  • +
+
+ + + +

+ Fuz supports CSS-literal syntax: property:value. +

+
    +
  • + similar to Tailwind but more verbose, nudging you toward Svelte's <style> tags +
  • +
  • enables composition across component boundaries
  • +
  • + more power than inline style attributes (modifiers for hover, responsive, dark + mode) +
  • +
+ +
+ + +
+ + +
+ + +
`} + /> +

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

+ + +

Common patterns

+

Layout and display:

+
    +
  • .display:none|block|flex|grid|inline|inline-block|contents
  • +
  • .position:static|relative|absolute|fixed|sticky
  • +
  • .visibility:visible|hidden|collapse
  • +
  • .overflow:auto|hidden|scroll|clip|visible
  • +
+

Flexbox and grid:

+
    +
  • .flex-direction:row|column|row-reverse|column-reverse
  • +
  • .flex-wrap:wrap|nowrap|wrap-reverse
  • +
  • .align-items:center|start|end|baseline|stretch
  • +
  • + .justify-content:center|start|end|space-between|space-around|space-evenly +
  • +
  • .flex:1, .flex-grow:1|0, .flex-shrink:1|0
  • +
+

Typography:

+
    +
  • .text-align:left|center|right|justify
  • +
  • .white-space:normal|nowrap|pre|pre-wrap|pre-line
  • +
  • .word-break:normal|break-all|keep-all
  • +
  • .text-wrap:wrap|nowrap|balance|pretty
  • +
  • .user-select:none|auto|text|all
  • +
+

Borders and effects:

+
    +
  • .border-style:none|solid|dashed|dotted
  • +
  • .float:left|right|none
  • +
  • .cursor:pointer|default|grab|text|not-allowed
  • +
  • .pointer-events:none|auto
  • +
+ + - + +

+ Modifiers wrap CSS in conditions. This is what makes utility classes more powerful than inline + styles - you can apply styles based on viewport, state, or color scheme. +

+ +

Responsive modifiers

+

Mobile-first breakpoints:

+ + + + + + + + + + + + + + + +
PrefixWidthCSS
sm:40rem (640px)@media (width >= 40rem)
md:48rem (768px)@media (width >= 48rem)
lg:64rem (1024px)@media (width >= 64rem)
xl:80rem (1280px)@media (width >= 80rem)
2xl:96rem (1536px)@media (width >= 96rem)
+ +
+ + +