fractalstyler2 0.4.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +114 -95
  2. package/dist/cli.js +105 -25
  3. package/dist/components/Accordion.svelte +44 -0
  4. package/dist/components/Accordion.svelte.d.ts +23 -0
  5. package/dist/components/AccordionItem.svelte +44 -0
  6. package/dist/components/AccordionItem.svelte.d.ts +21 -0
  7. package/dist/components/AppShell.svelte +86 -0
  8. package/dist/components/AppShell.svelte.d.ts +39 -0
  9. package/dist/components/ColorPicker.svelte +38 -0
  10. package/dist/components/ColorPicker.svelte.d.ts +6 -0
  11. package/dist/components/Hero.svelte +14 -0
  12. package/dist/components/Hero.svelte.d.ts +9 -0
  13. package/dist/components/LayoutPicker.svelte +34 -0
  14. package/dist/components/LayoutPicker.svelte.d.ts +6 -0
  15. package/dist/components/MotionPicker.svelte +34 -0
  16. package/dist/components/MotionPicker.svelte.d.ts +6 -0
  17. package/dist/components/PageShell.svelte +14 -0
  18. package/dist/components/PageShell.svelte.d.ts +9 -0
  19. package/dist/components/PageSplit.svelte +26 -0
  20. package/dist/components/PageSplit.svelte.d.ts +18 -0
  21. package/dist/components/ShapePicker.svelte +37 -0
  22. package/dist/components/ShapePicker.svelte.d.ts +6 -0
  23. package/dist/css/fractalstyler.css +10643 -0
  24. package/dist/css/fractalstyler.min.css +2 -0
  25. package/dist/index.d.ts +18 -6
  26. package/dist/index.js +30 -12
  27. package/dist/mcp/schemas/compile_fractals.json +3 -3
  28. package/dist/mcp/schemas/css_to_fractals.json +1 -1
  29. package/dist/mcp/schemas/generate_component.json +2 -2
  30. package/dist/mcp/schemas/instructions.md +41 -14
  31. package/dist/mcp/schemas/list_fractals.json +9 -7
  32. package/dist/mcp/schemas/validate_recipe.json +2 -2
  33. package/dist/mcp/server.d.ts +1 -1
  34. package/dist/mcp/server.js +334 -237
  35. package/dist/presets.core.d.ts +42 -0
  36. package/dist/presets.core.js +190 -0
  37. package/dist/presets.svelte.d.ts +6 -0
  38. package/dist/presets.svelte.js +21 -0
  39. package/dist/styles/_00_presets.sass +111 -0
  40. package/dist/styles/_00_themes.sass +1061 -0
  41. package/dist/styles/_00_tokens.sass +57 -17
  42. package/dist/styles/_01_config.sass +77 -70
  43. package/dist/styles/_02_dimensions.sass +174 -0
  44. package/dist/styles/_03_containers.sass +105 -0
  45. package/dist/styles/_04_layouts.sass +100 -0
  46. package/dist/styles/_05_shells.sass +374 -0
  47. package/dist/styles/_06_visuals.sass +236 -0
  48. package/dist/styles/_07_interactions.sass +112 -0
  49. package/dist/styles/_08_own.sass +4 -0
  50. package/dist/styles/canonical-markups.md +160 -0
  51. package/dist/styles/index.sass +11 -18
  52. package/dist/themes.d.ts +7 -0
  53. package/dist/themes.js +45 -0
  54. package/dist/version.d.ts +1 -0
  55. package/dist/version.js +2 -0
  56. package/package.json +26 -15
  57. package/plugin.json +11 -10
  58. package/registry.json +2002 -0
  59. package/skills/fractal-styler/SKILL.md +137 -50
  60. package/skills/fractal-styler/references/fractals.md +416 -28
  61. package/skills/fractal-styler/references/tokens.md +110 -36
  62. package/dist/styles/_02_fonts.sass +0 -13
  63. package/dist/styles/_03_responsive.sass +0 -31
  64. package/dist/styles/_04_atoms.sass +0 -155
  65. package/dist/styles/_05_molecules.sass +0 -97
  66. package/dist/styles/_06_recipes.sass +0 -189
  67. package/dist/styles/_07_base.sass +0 -44
  68. package/dist/styles/_08_blocks.sass +0 -433
  69. package/dist/styles/_09_utilities.sass +0 -208
  70. package/dist/styles/_10_layouts.sass +0 -373
  71. package/dist/styles/_11_own.sass +0 -21
  72. package/dist/styles/_fractals.sass +0 -14
  73. package/skills/style-migration/SKILL.md +0 -45
  74. package/templates/_00_tokens.sass +0 -136
  75. package/templates/_01_config.sass +0 -66
  76. package/templates/_02_fonts.sass +0 -13
  77. package/templates/_03_responsive.sass +0 -31
  78. package/templates/_04_atoms.sass +0 -155
  79. package/templates/_05_molecules.sass +0 -97
  80. package/templates/_06_recipes.sass +0 -189
  81. package/templates/_07_base.sass +0 -44
  82. package/templates/_08_blocks.sass +0 -433
  83. package/templates/_09_utilities.sass +0 -207
  84. package/templates/_10_layouts.sass +0 -373
  85. package/templates/_11_own.sass +0 -21
  86. package/templates/_fractals.sass +0 -14
  87. package/templates/index.sass +0 -18
@@ -28,9 +28,20 @@
28
28
  --border-subtle: #EDF2F7
29
29
  --theme-color: #04825B
30
30
  --theme-color-alt: #047857
31
- --theme: var(--theme-color)
32
- --theme-hover: var(--theme-color-alt)
31
+ --success: #10B981
32
+ --success-hover: #059669
33
+ --warning: #F59E0B
34
+ --warning-hover: #D97706
35
+ --danger: #EF4444
36
+ --danger-hover: #DC2626
37
+ --info: #3B82F6
38
+ --info-hover: #2563EB
39
+ --feedback-error: #DC2626
33
40
  --ring: rgba(0, 127, 78, 0.35)
41
+ // Per-mode shadows (registry v1)
42
+ --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06)
43
+ --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08)
44
+ --shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.12)
34
45
 
35
46
  =dark-theme-tokens
36
47
  color-scheme: dark
@@ -54,10 +65,21 @@
54
65
  --state-selected: #32405E
55
66
  --theme-color: #38BDF8
56
67
  --theme-color-alt: #0EA5E9
57
- --theme: var(--theme-color)
58
- --theme-hover: var(--theme-color-alt)
59
68
  --text-inverse: #0E1118
69
+ --success: #34D399
70
+ --success-hover: #6EE7B7
71
+ --warning: #FBBF24
72
+ --warning-hover: #FCD34D
73
+ --danger: #F87171
74
+ --danger-hover: #FCA5A5
75
+ --info: #60A5FA
76
+ --info-hover: #93C5FD
77
+ --feedback-error: #F87171
60
78
  --ring: rgba(16, 185, 129, 0.4)
79
+ // Per-mode shadows (registry v1)
80
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4)
81
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35)
82
+ --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.45)
61
83
 
62
84
  :root
63
85
  color-scheme: light
@@ -76,17 +98,17 @@
76
98
  --text-3xl: clamp(2.3328rem, 2.0387rem + 1.3072vw, 3.0518rem)
77
99
  --text-4xl: clamp(2.7994rem, 2.384rem + 1.8461vw, 3.8147rem)
78
100
 
79
- // Fluid space scale
101
+ // Fluid space scale — the comfortable values. data-layout scales at USE
102
+ // sites: gaps/margins × var(--gap-scale), paddings × var(--pad-scale).
80
103
  --space-3xs: clamp(0.3125rem, 0.3125rem + 0vw, 0.3125rem)
81
104
  --space-2xs: clamp(0.5625rem, 0.5369rem + 0.1136vw, 0.625rem)
82
105
  --space-xs: clamp(0.875rem, 0.8494rem + 0.1136vw, 0.9375rem)
83
- --space-s: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)
84
- --space-m: clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem)
85
- --space-l: clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem)
106
+ --space-sm: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)
107
+ --space-md: clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem)
108
+ --space-lg: clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem)
86
109
  --space-xl: clamp(3.375rem, 3.2216rem + 0.6818vw, 3.75rem)
87
110
  --space-2xl: clamp(4.5rem, 4.2955rem + 0.9091vw, 5rem)
88
111
  --space-3xl: clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem)
89
- --space-s-l: clamp(1.125rem, 0.5625rem + 2.5vw, 2.5rem)
90
112
 
91
113
  // Radius (Concentric hierarchy)
92
114
  --radius-0: 0
@@ -100,15 +122,25 @@
100
122
  --radius-24: 24px
101
123
  --radius-full: 9999px
102
124
 
125
+ // Radius channels — compositions read these; shape presets remap them.
126
+ // Defaults = curved (registry v1); unset data-shape IS curved.
127
+ --radius-sm: 8px
128
+ --radius-md: 16px
129
+ --radius-lg: 24px
130
+
131
+ // Motion language (registry v1) — durations and easings all compositions read.
132
+ --motion-fast: 120ms
133
+ --motion-base: 160ms
134
+ --motion-slow: 240ms
135
+ --ease-out: cubic-bezier(0.16, 1, 0.3, 1)
136
+ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1)
137
+
103
138
  // Control Heights
104
139
  --control-h-sm: 26px
105
140
  --control-h-md: 32px
106
141
  --control-h-lg: 38px
107
142
 
108
- // Shadow
109
- --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06)
110
- --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08)
111
- --shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.12)
143
+ // Shadow values live in the theme mixins (per-mode since registry v1).
112
144
 
113
145
  // Layering
114
146
  --z-base: 0
@@ -119,14 +151,24 @@
119
151
 
120
152
  // Layout
121
153
  --header-height: 48px
122
- --footer-height: 56px
154
+ --footer-height: 32px
123
155
  --measure: 65ch
156
+ --page-gutter: clamp(1rem, 4vw, 2rem)
157
+ --sidebar-width: 260px
158
+ --toc-width: 240px
159
+ --card-min: 16rem
160
+ --breakpoint: 768px
124
161
 
125
162
  // Default Light Palette
126
163
  +light-theme-tokens
127
164
 
165
+ // Plain :root, NOT :root:not([data-mode='light']). The :not() was redundant —
166
+ // [data-mode='light'] is emitted below and wins on source order at equal
167
+ // specificity — and it was harmful: :not() pushed this to (0,2,0), which
168
+ // outranks a .theme-* class at (0,1,0). Under OS dark mode every theme's
169
+ // surface tokens were silently overridden by these generic defaults.
128
170
  @media (prefers-color-scheme: dark)
129
- :root:not([data-mode='light'])
171
+ :root
130
172
  +dark-theme-tokens
131
173
 
132
174
  [data-mode='dark']
@@ -134,5 +176,3 @@
134
176
 
135
177
  [data-mode='light']
136
178
  +light-theme-tokens
137
-
138
-
@@ -1,70 +1,77 @@
1
- // =============================================================================
2
- // 01_CONFIG — the finite vocabulary every fractal draws from.
3
- // -----------------------------------------------------------------------------
4
- // Scales live here as Sass data; resolvers turn a keyword-or-raw value into the
5
- // right token var() or unit. Fractals never hardcode a px or a token name —
6
- // they route through space(), radius(), etc. so the system speaks one language.
7
- // =============================================================================
8
-
9
- @use 'sass:map'
10
- @use 'sass:math'
11
- @use 'sass:meta'
12
- @use 'sass:list'
13
-
14
- // --- Scales (mirror the custom properties in _00_tokens.sass) ----------------
15
- $breakpoints: (sm: 640px, md: 768px, lg: 1024px, xl: 1240px)
16
-
17
- $space-steps: 3xs 2xs xs s m l xl 2xl 3xl s-l
18
- $radius-steps: 0 2 3 4 6 8 12 16 24 full
19
- $text-steps: xs sm md lg xl 2xl 3xl 4xl
20
- $shadow-steps: sm md lg
21
-
22
- // Surfaces mapped to the 21 theme token contract
23
- $surface-roles: bg surface raised panel footer popover dialog terminal input canvas
24
- $ink-roles: primary secondary muted inverse theme-color theme-color-alt
25
-
26
- $align-map: (start: flex-start, end: flex-end, center: center, between: space-between, around: space-around, evenly: space-evenly, stretch: stretch, baseline: baseline)
27
-
28
- // --- Universal Token Resolver --------------------------------------------------
29
- @function tok($group, $v)
30
- @if $v == null
31
- @return null
32
- @if meta.type-of($v) == number
33
- @if math.is-unitless($v)
34
- @return #{$v}px
35
- @else
36
- @return $v
37
- @if meta.type-of($v) == string
38
- @return var(--#{$group}-#{$v})
39
- @return $v
40
-
41
- // --- Resolvers ----------------------------------------------------------------
42
-
43
- @function space($v)
44
- @return tok(space, $v)
45
-
46
- @function radius($v)
47
- @return tok(radius, $v)
48
-
49
- @function text-size($v)
50
- @return tok(text, $v)
51
-
52
- @function shadow($v)
53
- @return tok(shadow, $v)
54
-
55
- @function surface($role)
56
- @if $role == bg
57
- @return var(--bg)
58
- @return tok(bg, $role)
59
-
60
- @function ink($role)
61
- @if $role == primary
62
- @return var(--text-primary)
63
- @return tok(text, $role)
64
-
65
- @function align($v)
66
- @return map.get($align-map, $v) or $v
67
-
68
- @function bp($name)
69
- @return map.get($breakpoints, $name)
70
-
1
+ // ============================================================================
2
+ // config — the builder knobs the Contract names. All !default: overridable at
3
+ // compile time, never edited in place. The numbered filename is not a valid
4
+ // Sass identifier, so a consumer MUST namespace the configuring load:
5
+ //
6
+ // @use 'styles/01_config' as cfg with ($literal-max: 256)
7
+ // @use 'styles/index' as *
8
+ //
9
+ // Configure before anything else loads 01_config, or Sass rejects it.
10
+ // ============================================================================
11
+
12
+ // The responsive seam. Every loop-generated class takes -mob (below) and
13
+ // -desk (at or above) this width.
14
+ $breakpoint: 768px !default
15
+ $responsive-modes: (mob: 'all and (max-width: #{$breakpoint - 1})', desk: 'all and (min-width: #{$breakpoint})') !default
16
+
17
+ // Literal ladder: the discrete pixel values .gap-{N}, .radius-{N}, .w-{N}… emit.
18
+ //
19
+ // Fine at the bottom, where interfaces genuinely need 1px and 2px precision,
20
+ // then multiples of $literal-step the rest of the way. A continuous 1…256
21
+ // range emitted 7,302 rungs per band to serve maybe forty useful values;
22
+ // nobody types .marg-left--193.
23
+ $literal-fine: (0, 1, 2, 4, 6, 8, 12) !default
24
+ $literal-step: 8 !default
25
+ $literal-max: 512 !default
26
+
27
+ // Space literals stop earlier than sizing ones. Gaps, paddings and margins are
28
+ // 24 of the 28 classes emitted per rung (marg doubles for negatives), so they
29
+ // dominate the output — and a 512px gap is meaningless where a 512px width is
30
+ // not. Large spacing is the preset steps' job: --space-3xl is already ~120px.
31
+ $literal-space-max: 64 !default
32
+
33
+ // Radius stops early for the same reason: .radius-full already handles pills,
34
+ // and the shape presets top out at --radius-lg (~24-32px). A 512px corner is
35
+ // not a corner.
36
+ $literal-radius-max: 64 !default
37
+
38
+ // Named steps for grid stepping and the shell rails (not the responsive seam).
39
+ $breakpoints: (sm: 640px, md: 768px, lg: 1024px, xl: 1280px) !default
40
+
41
+ // ============================================================================
42
+ // base — element baseline. Builder infrastructure, not registry classes: the
43
+ // registry is a list of classes; this only makes documents predictable so the
44
+ // classes behave. Deliberately minimal — no typographic opinions beyond body.
45
+ // ============================================================================
46
+
47
+ *,
48
+ *::before,
49
+ *::after
50
+ box-sizing: border-box
51
+
52
+ *
53
+ margin: 0
54
+
55
+ body, html
56
+ font-family: var(--font-sans)
57
+ font-size: var(--text-sm)
58
+ line-height: 1.5
59
+ color: var(--text-primary)
60
+ background: var(--bg)
61
+ box-sizing: border-box
62
+
63
+ img,
64
+ video,
65
+ svg
66
+ display: block
67
+ max-width: 100%
68
+
69
+ button,
70
+ input,
71
+ select,
72
+ textarea
73
+ font: inherit
74
+ color: inherit
75
+
76
+ a
77
+ color: inherit
@@ -0,0 +1,174 @@
1
+ // ============================================================================
2
+ // dimensions — L1. The loop-generated half of the registry: 17 space families,
3
+ // radius, size, viewport heights, shadows — each class in three bands (base,
4
+ // -mob, -desk) per Contract 7. Presets are token-routed; literals are px.
5
+ // ============================================================================
6
+
7
+ @use 'sass:string'
8
+ @use 'sass:list'
9
+ @use 'sass:math'
10
+ @use '01_config' as *
11
+
12
+ // The literal ladder, built once: the fine tail, then every $literal-step up
13
+ // to $literal-max. Radius and sizing iterate all of it.
14
+ $literals: $literal-fine
15
+ @for $i from 2 through math.div($literal-max, $literal-step)
16
+ $literals: list.append($literals, $i * $literal-step)
17
+
18
+ // The space families take the same ladder truncated at $literal-space-max,
19
+ // radius at $literal-radius-max. Only sizing runs the whole thing.
20
+ $space-literals: ()
21
+ @each $n in $literals
22
+ @if $n <= $literal-space-max
23
+ $space-literals: list.append($space-literals, $n)
24
+
25
+ $radius-literals: ()
26
+ @each $n in $literals
27
+ @if $n <= $literal-radius-max
28
+ $radius-literals: list.append($radius-literals, $n)
29
+ $steps: ('3xs' '2xs' 'xs' 'sm' 'md' 'lg' 'xl' '2xl' '3xl')
30
+
31
+ // Space families, role-split for the layout preset: gaps/margins separate
32
+ // content (× --gap-scale); paddings are the container's breathing (× --pad-scale).
33
+ $gap-families: (gap: gap, rgap: row-gap, cgap: column-gap)
34
+
35
+ $pad-families: (pad: padding, pad-x: padding-inline, pad-y: padding-block, pad-top: padding-top, pad-right: padding-right, pad-bottom: padding-bottom, pad-left: padding-left)
36
+
37
+ // Negatives ride margin families only (Contract: `--` infix).
38
+ $marg-families: (marg: margin, marg-x: margin-inline, marg-y: margin-block, marg-top: margin-top, marg-right: margin-right, marg-bottom: margin-bottom, marg-left: margin-left)
39
+
40
+ // --- Base band ---------------------------------------------------------------
41
+ @each $name, $prop in $gap-families
42
+ @each $step in $steps
43
+ .#{$name}-#{$step}
44
+ #{$prop}: calc(var(--space-#{$step}) * var(--gap-scale, 1))
45
+ @each $n in $space-literals
46
+ .#{$name}-#{$n}
47
+ #{$prop}: #{$n}px
48
+
49
+ @each $name, $prop in $pad-families
50
+ @each $step in $steps
51
+ .#{$name}-#{$step}
52
+ #{$prop}: calc(var(--space-#{$step}) * var(--pad-scale, 1))
53
+ @each $n in $space-literals
54
+ .#{$name}-#{$n}
55
+ #{$prop}: #{$n}px
56
+
57
+ @each $name, $prop in $marg-families
58
+ @each $step in $steps
59
+ .#{$name}-#{$step}
60
+ #{$prop}: calc(var(--space-#{$step}) * var(--gap-scale, 1))
61
+ .#{$name}--#{$step}
62
+ #{$prop}: calc(var(--space-#{$step}) * var(--gap-scale, 1) * -1)
63
+ @each $n in $space-literals
64
+ .#{$name}-#{$n}
65
+ #{$prop}: #{$n}px
66
+ .#{$name}--#{$n}
67
+ #{$prop}: -#{$n}px
68
+
69
+ // Radius — literal px (0 = the reset); never rides shape presets.
70
+ @each $n in $radius-literals
71
+ .radius-#{$n}
72
+ border-radius: #{$n}px
73
+
74
+ .radius-full
75
+ border-radius: var(--radius-full)
76
+
77
+ // The radius CHANNELS as classes. Unlike the numeric ladder these follow
78
+ // data-shape, so a card on .radius-md goes round -> curved -> pro -> sharp with
79
+ // the preset. Reach for these first; the literals are for exact requirements.
80
+ @each $step in ('sm' 'md' 'lg')
81
+ .radius-#{$step}
82
+ border-radius: var(--radius-#{$step})
83
+
84
+ // Size
85
+ @each $n in $literals
86
+ .w-#{$n}
87
+ width: #{$n}px
88
+ .h-#{$n}
89
+ height: #{$n}px
90
+ .square-#{$n}
91
+ width: #{$n}px
92
+ height: #{$n}px
93
+
94
+ .wfull
95
+ width: 100%
96
+ .hfull
97
+ height: 100%
98
+ .full
99
+ width: 100%
100
+ height: 100%
101
+ .min0
102
+ min-width: 0
103
+ min-height: 0
104
+
105
+ // Viewport heights
106
+ .hfull-vh
107
+ min-height: 100vh
108
+ .hfull-vh-fitted
109
+ min-height: calc(100vh - var(--header-height, 48px) - var(--footer-height))
110
+
111
+ // --- Responsive bands: -mob / -desk, generated wholesale (Contract 7) --------
112
+ @each $mode, $query in $responsive-modes
113
+ @media #{string.unquote($query)}
114
+ @each $name, $prop in $gap-families
115
+ @each $step in $steps
116
+ .#{$name}-#{$step}-#{$mode}
117
+ #{$prop}: calc(var(--space-#{$step}) * var(--gap-scale, 1))
118
+ @each $n in $space-literals
119
+ .#{$name}-#{$n}-#{$mode}
120
+ #{$prop}: #{$n}px
121
+ @each $name, $prop in $pad-families
122
+ @each $step in $steps
123
+ .#{$name}-#{$step}-#{$mode}
124
+ #{$prop}: calc(var(--space-#{$step}) * var(--pad-scale, 1))
125
+ @each $n in $space-literals
126
+ .#{$name}-#{$n}-#{$mode}
127
+ #{$prop}: #{$n}px
128
+ @each $name, $prop in $marg-families
129
+ @each $step in $steps
130
+ .#{$name}-#{$step}-#{$mode}
131
+ #{$prop}: calc(var(--space-#{$step}) * var(--gap-scale, 1))
132
+ .#{$name}--#{$step}-#{$mode}
133
+ #{$prop}: calc(var(--space-#{$step}) * var(--gap-scale, 1) * -1)
134
+ @each $n in $space-literals
135
+ .#{$name}-#{$n}-#{$mode}
136
+ #{$prop}: #{$n}px
137
+ .#{$name}--#{$n}-#{$mode}
138
+ #{$prop}: -#{$n}px
139
+ @each $n in $radius-literals
140
+ .radius-#{$n}-#{$mode}
141
+ border-radius: #{$n}px
142
+ .radius-full-#{$mode}
143
+ border-radius: var(--radius-full)
144
+ @each $step in ('sm' 'md' 'lg')
145
+ .radius-#{$step}-#{$mode}
146
+ border-radius: var(--radius-#{$step})
147
+ @each $n in $literals
148
+ .w-#{$n}-#{$mode}
149
+ width: #{$n}px
150
+ .h-#{$n}-#{$mode}
151
+ height: #{$n}px
152
+ .square-#{$n}-#{$mode}
153
+ width: #{$n}px
154
+ height: #{$n}px
155
+ .wfull-#{$mode}
156
+ width: 100%
157
+ .hfull-#{$mode}
158
+ height: 100%
159
+ .full-#{$mode}
160
+ width: 100%
161
+ height: 100%
162
+ .min0-#{$mode}
163
+ min-width: 0
164
+ min-height: 0
165
+ .hfull-vh-#{$mode}
166
+ min-height: 100vh
167
+ .hfull-vh-fitted-#{$mode}
168
+ min-height: calc(100vh - var(--header-height, 48px) - var(--footer-height))
169
+ .shadow-sm-#{$mode}
170
+ box-shadow: var(--shadow-sm)
171
+ .shadow-md-#{$mode}
172
+ box-shadow: var(--shadow-md)
173
+ .shadow-lg-#{$mode}
174
+ box-shadow: var(--shadow-lg)
@@ -0,0 +1,105 @@
1
+ // ============================================================================
2
+ // flow — L2. Containers and the alignment universe, verbatim from the
3
+ // registry. Alignment classes are nested under their base — never standalone.
4
+ // x/y are physical axes, always (Contract 4).
5
+ // ============================================================================
6
+ .box
7
+ display: flex
8
+ flex-direction: column
9
+ &.xcenter
10
+ align-items: center
11
+ text-align: center
12
+ &.xleft
13
+ align-items: flex-start
14
+ text-align: left
15
+ &.xright
16
+ align-items: flex-end
17
+ text-align: right
18
+ &.ycenter
19
+ justify-content: center
20
+ &.ytop
21
+ justify-content: flex-start
22
+ &.ybot
23
+ justify-content: flex-end
24
+ &.ybetween
25
+ justify-content: space-between
26
+ &.yevenly
27
+ justify-content: space-evenly
28
+ &.yaround
29
+ justify-content: space-around
30
+
31
+ .row
32
+ display: flex
33
+ flex-direction: row
34
+ &.xleft
35
+ justify-content: flex-start
36
+ &.xcenter
37
+ justify-content: center
38
+ &.xright
39
+ justify-content: flex-end
40
+ &.xbetween
41
+ justify-content: space-between
42
+ &.xevenly
43
+ justify-content: space-evenly
44
+ &.xaround
45
+ justify-content: space-around
46
+ &.ycenter
47
+ align-items: center
48
+ &.ytop
49
+ align-items: flex-start
50
+ &.ybot
51
+ align-items: flex-end
52
+
53
+ .grid
54
+ display: grid
55
+ &.center
56
+ place-items: center
57
+ &.xcenter
58
+ justify-items: center
59
+ text-align: center
60
+ &.xleft
61
+ justify-items: start
62
+ text-align: left
63
+ &.xright
64
+ justify-items: end
65
+ text-align: right
66
+ &.xstretch
67
+ justify-items: stretch
68
+ &.ycenter
69
+ align-items: center
70
+ &.ytop
71
+ align-items: start
72
+ &.ybot
73
+ align-items: end
74
+ &.ystretch
75
+ align-items: stretch
76
+ &.xbetween
77
+ justify-content: space-between
78
+ &.xevenly
79
+ justify-content: space-evenly
80
+ &.xaround
81
+ justify-content: space-around
82
+ &.ybetween
83
+ align-content: space-between
84
+ &.yevenly
85
+ align-content: space-evenly
86
+ &.yaround
87
+ align-content: space-around
88
+
89
+ .wrap
90
+ flex-wrap: wrap
91
+
92
+ .grow
93
+ flex: 1 1 0%
94
+
95
+ .shrink-0
96
+ flex-shrink: 0
97
+
98
+ .relative
99
+ position: relative
100
+ .absolute
101
+ position: absolute
102
+ .fixed
103
+ position: fixed
104
+ .sticky
105
+ position: sticky
@@ -0,0 +1,100 @@
1
+ // ============================================================================
2
+ // layouts — L3. Grids (gridding golden rules), card-grid, prose, frame
3
+ // presets, reel. Grids are pure stepping: no default gap — compose .gap-*
4
+ // (self-sufficiency; a default gap is an opinion the system doesn't hold).
5
+ // ============================================================================
6
+
7
+ @use 'sass:string'
8
+ @use 'sass:list'
9
+ @use 'sass:map'
10
+ @use '01_config' as *
11
+
12
+ $sm: map.get($breakpoints, sm)
13
+ $md: map.get($breakpoints, md)
14
+ $lg: map.get($breakpoints, lg)
15
+
16
+ // Gridding golden rules: exactly 3 items → 3→1, never 2+1; count 4 or a
17
+ // multiple of 4 → 4→2→1, never 3+1; 6 → 6→3→2→1 (both rules hold throughout).
18
+ .grid-1
19
+ display: grid
20
+ grid-template-columns: repeat(1, minmax(0, 1fr))
21
+
22
+ .grid-2
23
+ display: grid
24
+ grid-template-columns: repeat(1, minmax(0, 1fr))
25
+ @media #{string.unquote('(min-width: #{$md})')}
26
+ grid-template-columns: repeat(2, minmax(0, 1fr))
27
+
28
+ .grid-3
29
+ display: grid
30
+ grid-template-columns: repeat(1, minmax(0, 1fr))
31
+ @media #{string.unquote('(min-width: #{$lg})')}
32
+ grid-template-columns: repeat(3, minmax(0, 1fr))
33
+
34
+ .grid-4
35
+ display: grid
36
+ grid-template-columns: repeat(1, minmax(0, 1fr))
37
+ @media #{string.unquote('(min-width: #{$sm})')}
38
+ grid-template-columns: repeat(2, minmax(0, 1fr))
39
+ @media #{string.unquote('(min-width: #{$lg})')}
40
+ grid-template-columns: repeat(4, minmax(0, 1fr))
41
+
42
+ .grid-6
43
+ display: grid
44
+ grid-template-columns: repeat(1, minmax(0, 1fr))
45
+ @media #{string.unquote('(min-width: #{$sm})')}
46
+ grid-template-columns: repeat(2, minmax(0, 1fr))
47
+ @media #{string.unquote('(min-width: #{$md})')}
48
+ grid-template-columns: repeat(3, minmax(0, 1fr))
49
+ @media #{string.unquote('(min-width: #{$lg})')}
50
+ grid-template-columns: repeat(6, minmax(0, 1fr))
51
+
52
+ // Auto-fit repeatable cards — column count negotiable, min track from config.
53
+ .card-grid
54
+ display: grid
55
+ grid-template-columns: repeat(auto-fit, minmax(min(var(--card-min), 100%), 1fr))
56
+
57
+ // Prose — the reading measure (≤ 760px) + typographic rhythm (rhythm authored).
58
+ .prose
59
+ max-width: min(100%, 760px)
60
+ margin-inline: auto
61
+ line-height: 1.65
62
+ // Rhythm comes from gap, not from adjacent-sibling margins: one rule
63
+ // instead of a combinator, and it cannot leak into nested content.
64
+ display: flex
65
+ flex-direction: column
66
+ gap: calc(var(--space-sm) * var(--gap-scale, 1))
67
+ h1, h2, h3, h4
68
+ line-height: 1.25
69
+ font-weight: 600
70
+ // Extra breathing above a heading, on top of the gap.
71
+ margin-top: calc(var(--space-2xs) * var(--gap-scale, 1))
72
+ &:first-child
73
+ margin-top: 0
74
+
75
+ // Frame presets — aspect-ratio media boxes (replaces any frame abstraction).
76
+ $frame-ratios: ('16-9': '16 / 9', '9-16': '9 / 16', '4-3': '4 / 3', '3-4': '3 / 4', '3-2': '3 / 2', '2-3': '2 / 3', '1-1': '1 / 1')
77
+ @each $name, $ratio in $frame-ratios
78
+ .frame-#{$name}
79
+ aspect-ratio: #{$ratio}
80
+ overflow: hidden
81
+ position: relative
82
+ img, video, iframe
83
+ width: 100%
84
+ height: 100%
85
+ object-fit: cover
86
+
87
+ // Reel — the human-driven scroll-snap rail (filmstrip). Open for carousel duty.
88
+ .reel
89
+ display: flex
90
+ flex-direction: row
91
+ overflow-x: auto
92
+ overscroll-behavior-inline: contain
93
+ scroll-snap-type: inline mandatory
94
+ // The system's one permitted `>`. Snap targets must be the rail's own
95
+ // children: a descendant selector would make nested elements snap too and
96
+ // the rail would catch in the wrong places. Kept deliberately — see the
97
+ // combinator guard in scripts/validate-deck.js.
98
+ > *
99
+ flex: 0 0 auto
100
+ scroll-snap-align: start