fractalstyler2 0.0.2 → 0.3.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 (59) hide show
  1. package/README.md +60 -45
  2. package/dist/cli.js +54 -11
  3. package/dist/index.d.ts +32 -1
  4. package/dist/index.js +73 -4
  5. package/dist/mcp/export.d.ts +13 -0
  6. package/dist/mcp/export.js +76 -0
  7. package/dist/mcp/schemas/compile_fractals.json +20 -0
  8. package/dist/mcp/schemas/css_to_fractals.json +16 -0
  9. package/dist/mcp/schemas/generate_component.json +45 -0
  10. package/dist/mcp/schemas/get_design_tokens.json +23 -0
  11. package/dist/mcp/schemas/instructions.md +18 -0
  12. package/dist/mcp/schemas/list_fractals.json +20 -0
  13. package/dist/mcp/schemas/snap_to_tokens.json +25 -0
  14. package/dist/mcp/schemas/validate_recipe.json +16 -0
  15. package/dist/mcp/server.d.ts +7 -0
  16. package/dist/mcp/server.js +837 -0
  17. package/dist/styles/_00_tokens.sass +177 -0
  18. package/{templates/_config.sass → dist/styles/_01_config.sass} +11 -5
  19. package/dist/styles/_02_fonts.sass +13 -0
  20. package/dist/styles/_03_responsive.sass +31 -0
  21. package/dist/styles/{_atoms.sass → _04_atoms.sass} +4 -4
  22. package/{templates/_molecules.sass → dist/styles/_05_molecules.sass} +16 -12
  23. package/dist/styles/_06_recipes.sass +189 -0
  24. package/dist/styles/{_base.sass → _07_base.sass} +3 -2
  25. package/dist/styles/_08_blocks.sass +433 -0
  26. package/dist/styles/{_utilities.sass → _09_utilities.sass} +96 -16
  27. package/dist/styles/_10_layouts.sass +373 -0
  28. package/dist/styles/_11_own.sass +21 -0
  29. package/dist/styles/_fractals.sass +7 -6
  30. package/dist/styles/index.sass +14 -16
  31. package/mcp.json +11 -0
  32. package/package.json +14 -6
  33. package/plugin.json +22 -0
  34. package/skills/fractal-styler/SKILL.md +83 -0
  35. package/skills/fractal-styler/references/fractals.md +28 -0
  36. package/skills/fractal-styler/references/tokens.md +36 -0
  37. package/skills/style-migration/SKILL.md +45 -0
  38. package/templates/_00_tokens.sass +177 -0
  39. package/{dist/styles/_config.sass → templates/_01_config.sass} +11 -5
  40. package/templates/_02_fonts.sass +13 -0
  41. package/templates/_03_responsive.sass +31 -0
  42. package/templates/{_atoms.sass → _04_atoms.sass} +4 -4
  43. package/{dist/styles/_molecules.sass → templates/_05_molecules.sass} +16 -12
  44. package/templates/_06_recipes.sass +189 -0
  45. package/templates/{_base.sass → _07_base.sass} +3 -2
  46. package/templates/_08_blocks.sass +433 -0
  47. package/templates/{_utilities.sass → _09_utilities.sass} +96 -16
  48. package/templates/_10_layouts.sass +373 -0
  49. package/templates/_11_own.sass +21 -0
  50. package/templates/_fractals.sass +7 -6
  51. package/templates/index.sass +14 -16
  52. package/dist/styles/_blocks.sass +0 -88
  53. package/dist/styles/_layouts.sass +0 -108
  54. package/dist/styles/_responsive.sass +0 -43
  55. package/dist/styles/_tokens.sass +0 -127
  56. package/templates/_blocks.sass +0 -88
  57. package/templates/_layouts.sass +0 -108
  58. package/templates/_responsive.sass +0 -43
  59. package/templates/_tokens.sass +0 -127
@@ -0,0 +1,177 @@
1
+ // =============================================================================
2
+ // 00_TOKENS — the single source of truth for runtime CSS custom properties.
3
+ // -----------------------------------------------------------------------------
4
+ // Light is the marker-free default so tokens stay defined during SSR and with
5
+ // JS disabled; dark comes from prefers-color-scheme and explicit [data-mode='dark'].
6
+ // =============================================================================
7
+
8
+ :root
9
+ color-scheme: light
10
+
11
+ // Fonts
12
+ --font-sans: "Google Sans Flex", sans-serif
13
+ --font-mono: "JetBrains Mono", monospace
14
+
15
+ // Fluid type scale (Utopia 360→1240)
16
+ --text-xs: 0.75rem
17
+ --text-sm: clamp(0.9375rem, 0.9119rem + 0.1136vw, 1rem)
18
+ --text-md: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)
19
+ --text-lg: clamp(1.35rem, 1.2631rem + 0.3864vw, 1.5625rem)
20
+ --text-xl: clamp(1.62rem, 1.4837rem + 0.6057vw, 1.9531rem)
21
+ --text-2xl: clamp(1.944rem, 1.7405rem + 0.9044vw, 2.4414rem)
22
+ --text-3xl: clamp(2.3328rem, 2.0387rem + 1.3072vw, 3.0518rem)
23
+ --text-4xl: clamp(2.7994rem, 2.384rem + 1.8461vw, 3.8147rem)
24
+
25
+ // Fluid space scale
26
+ --space-3xs: clamp(0.3125rem, 0.3125rem + 0vw, 0.3125rem)
27
+ --space-2xs: clamp(0.5625rem, 0.5369rem + 0.1136vw, 0.625rem)
28
+ --space-xs: clamp(0.875rem, 0.8494rem + 0.1136vw, 0.9375rem)
29
+ --space-s: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)
30
+ --space-m: clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem)
31
+ --space-l: clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem)
32
+ --space-xl: clamp(3.375rem, 3.2216rem + 0.6818vw, 3.75rem)
33
+ --space-2xl: clamp(4.5rem, 4.2955rem + 0.9091vw, 5rem)
34
+ --space-3xl: clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem)
35
+ --space-s-l: clamp(1.125rem, 0.5625rem + 2.5vw, 2.5rem)
36
+
37
+ // Radius (Concentric 2px, 3px, 4px, 6px hierarchy)
38
+ --radius-0: 0
39
+ --radius-2: 2px
40
+ --radius-3: 3px
41
+ --radius-4: 4px
42
+ --radius-6: 6px
43
+ --radius-8: 6px
44
+ --radius-12: 6px
45
+ --radius-16: 6px
46
+ --radius-24: 6px
47
+ --radius-full: 9999px
48
+
49
+ // Control Heights
50
+ --control-h-sm: 26px
51
+ --control-h-md: 32px
52
+ --control-h-lg: 38px
53
+
54
+ // Shadow
55
+ --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06)
56
+ --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08)
57
+ --shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.12)
58
+
59
+ // Layering
60
+ --z-base: 0
61
+ --z-raised: 10
62
+ --z-sticky: 100
63
+ --z-modal: 200
64
+ --z-toast: 300
65
+
66
+ // Layout
67
+ --header-height: 48px
68
+ --footer-height: 56px
69
+ --measure: 65ch
70
+
71
+ // Light palette (marker-free default)
72
+ --bg: #fdfefe
73
+ --bg-surface: #f8f7f7
74
+ --bg-raised: #f1f5f9
75
+ --bg-panel: #F1F3F5
76
+ --bg-footer: #E9ECEF
77
+ --bg-popover: #FFFFFF
78
+ --bg-dialog: #FFFFFF
79
+ --bg-terminal: #0F172A
80
+ --bg-input: #FFFFFF
81
+ --bg-canvas: #F8F9FA
82
+ --text-primary: #0f172a
83
+ --text-secondary: #5b6472
84
+ --text-muted: #929497
85
+ --text-inverse: #ffffff
86
+ --state-hover: #E2E8F0
87
+ --state-hover-subtle: #F1F5F9
88
+ --state-selected: #CBD5E1
89
+ --border: #E2E8F0
90
+ --border-subtle: #EDF2F7
91
+ --theme-color: #04825B
92
+ --theme-color-alt: #047857
93
+ --theme: var(--theme-color)
94
+ --theme-hover: var(--theme-color-alt)
95
+ --ring: rgba(0, 127, 78, 0.35)
96
+
97
+ @media (prefers-color-scheme: dark)
98
+ :root:not([data-mode='light'])
99
+ color-scheme: dark
100
+ --bg: #0E1118
101
+ --bg-surface: #141824
102
+ --bg-raised: #1C2232
103
+ --bg-panel: #101420
104
+ --bg-footer: #0B0D14
105
+ --bg-popover: #20273A
106
+ --bg-dialog: #20273A
107
+ --bg-terminal: #080A0F
108
+ --bg-input: #121622
109
+ --bg-canvas: #0E1118
110
+ --border: #28324A
111
+ --border-subtle: #1E2538
112
+ --text-primary: #EDF2F7
113
+ --text-secondary: #9BB0C7
114
+ --text-muted: #5D7087
115
+ --state-hover: #263047
116
+ --state-hover-subtle: #1B2233
117
+ --state-selected: #32405E
118
+ --theme-color: #38BDF8
119
+ --theme-color-alt: #0EA5E9
120
+ --theme: var(--theme-color)
121
+ --theme-hover: var(--theme-color-alt)
122
+ --text-inverse: #0E1118
123
+ --ring: rgba(16, 185, 129, 0.4)
124
+
125
+ [data-mode='dark']
126
+ color-scheme: dark
127
+ --bg: #0E1118
128
+ --bg-surface: #141824
129
+ --bg-raised: #1C2232
130
+ --bg-panel: #101420
131
+ --bg-footer: #0B0D14
132
+ --bg-popover: #20273A
133
+ --bg-dialog: #20273A
134
+ --bg-terminal: #080A0F
135
+ --bg-input: #121622
136
+ --bg-canvas: #0E1118
137
+ --border: #28324A
138
+ --border-subtle: #1E2538
139
+ --text-primary: #EDF2F7
140
+ --text-secondary: #9BB0C7
141
+ --text-muted: #5D7087
142
+ --state-hover: #263047
143
+ --state-hover-subtle: #1B2233
144
+ --state-selected: #32405E
145
+ --theme-color: #38BDF8
146
+ --theme-color-alt: #0EA5E9
147
+ --theme: var(--theme-color)
148
+ --theme-hover: var(--theme-color-alt)
149
+ --text-inverse: #0E1118
150
+ --ring: rgba(16, 185, 129, 0.4)
151
+
152
+ [data-mode='light']
153
+ color-scheme: light
154
+ --bg: #fdfefe
155
+ --bg-surface: #f8f7f7
156
+ --bg-raised: #f1f5f9
157
+ --bg-panel: #F1F3F5
158
+ --bg-footer: #E9ECEF
159
+ --bg-popover: #FFFFFF
160
+ --bg-dialog: #FFFFFF
161
+ --bg-terminal: #0F172A
162
+ --bg-input: #FFFFFF
163
+ --bg-canvas: #F8F9FA
164
+ --text-primary: #0f172a
165
+ --text-secondary: #5b6472
166
+ --text-muted: #929497
167
+ --text-inverse: #ffffff
168
+ --state-hover: #E2E8F0
169
+ --state-hover-subtle: #F1F5F9
170
+ --state-selected: #CBD5E1
171
+ --border: #E2E8F0
172
+ --border-subtle: #EDF2F7
173
+ --theme-color: #04825B
174
+ --theme-color-alt: #047857
175
+ --theme: var(--theme-color)
176
+ --theme-hover: var(--theme-color-alt)
177
+ --ring: rgba(0, 127, 78, 0.35)
@@ -1,5 +1,5 @@
1
1
  // =============================================================================
2
- // CONFIG — the finite vocabulary every fractal draws from.
2
+ // 01_CONFIG — the finite vocabulary every fractal draws from.
3
3
  // -----------------------------------------------------------------------------
4
4
  // Scales live here as Sass data; resolvers turn a keyword-or-raw value into the
5
5
  // right token var() or unit. Fractals never hardcode a px or a token name —
@@ -11,15 +11,17 @@
11
11
  @use 'sass:meta'
12
12
  @use 'sass:list'
13
13
 
14
- // --- Scales (mirror the custom properties in _tokens.sass) --------------------
14
+ // --- Scales (mirror the custom properties in _00_tokens.sass) ----------------
15
15
  $breakpoints: (sm: 640px, md: 768px, lg: 1024px, xl: 1240px)
16
16
 
17
17
  $space-steps: 3xs 2xs xs s m l xl 2xl 3xl s-l
18
- $radius-steps: 0 2 4 6 8 12 16 24 full
18
+ $radius-steps: 0 2 3 4 6 8 12 16 24 full
19
19
  $text-steps: xs sm md lg xl 2xl 3xl 4xl
20
20
  $shadow-steps: sm md lg
21
- $surface-roles: bg surface raised
22
- $ink-roles: primary secondary muted inverse
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
23
25
 
24
26
  $align-map: (start: flex-start, end: flex-end, center: center, between: space-between, around: space-around, evenly: space-evenly, stretch: stretch, baseline: baseline)
25
27
 
@@ -59,12 +61,16 @@ $align-map: (start: flex-start, end: flex-end, center: center, between: space-be
59
61
  @function surface($role)
60
62
  @if $role == bg
61
63
  @return var(--bg)
64
+ @else if list.index($surface-roles, $role)
65
+ @return var(--bg-#{$role})
62
66
  @else
63
67
  @return var(--bg-#{$role})
64
68
 
65
69
  @function ink($role)
66
70
  @if $role == primary
67
71
  @return var(--text-primary)
72
+ @else if list.index($ink-roles, $role)
73
+ @return var(--text-#{$role})
68
74
  @else
69
75
  @return var(--text-#{$role})
70
76
 
@@ -0,0 +1,13 @@
1
+ // =============================================================================
2
+ // 02_FONTS — local webfont declarations & font-family fallbacks.
3
+ // -----------------------------------------------------------------------------
4
+ // Downstream projects customize their font loading here.
5
+ // =============================================================================
6
+
7
+ // Define custom @font-face rules or load Google / local webfonts as needed:
8
+ // @font-face
9
+ // font-family: 'YourFont'
10
+ // src: url('/fonts/YourFont.woff2') format('woff2')
11
+ // font-weight: 100 900
12
+ // font-style: normal
13
+ // font-display: swap
@@ -0,0 +1,31 @@
1
+ // =============================================================================
2
+ // 03_RESPONSIVE — the breakpoint mixins. Viewport-aware conditional styling.
3
+ // -----------------------------------------------------------------------------
4
+ // Mobile-first defaults; use +at($bp) for min-width media queries.
5
+ // =============================================================================
6
+
7
+ @use '01_config' as *
8
+
9
+ =at($bp)
10
+ $width: bp($bp)
11
+ @if $width
12
+ @media (min-width: $width)
13
+ @content
14
+ @else
15
+ @media (min-width: $bp)
16
+ @content
17
+
18
+ =below($bp)
19
+ $width: bp($bp)
20
+ @if $width
21
+ @media (max-width: ($width - 1px))
22
+ @content
23
+ @else
24
+ @media (max-width: $bp)
25
+ @content
26
+
27
+ =between($min-bp, $max-bp)
28
+ $min: bp($min-bp)
29
+ $max: bp($max-bp)
30
+ @media (min-width: $min) and (max-width: ($max - 1px))
31
+ @content
@@ -1,11 +1,11 @@
1
1
  // =============================================================================
2
- // ATOM FRACTALS — the indivisible units. One visual decision each.
2
+ // 04_ATOMS — the indivisible units. One visual decision each.
3
3
  // -----------------------------------------------------------------------------
4
4
  // Every atom is a mixin. Call it inside a class to compose, or let
5
- // _utilities.sass project it to a utility class. Same fractal, two consumers.
5
+ // _09_utilities.sass project it to a utility class. Same fractal, two consumers.
6
6
  // =============================================================================
7
7
 
8
- @use 'config' as *
8
+ @use '01_config' as *
9
9
 
10
10
  // --- Flow: the two axes of layout ------------------------------------------
11
11
  // A box is a vertical stack, a row is horizontal. Optional semantic alignment:
@@ -102,7 +102,7 @@
102
102
  @else
103
103
  border-#{$side}: 1px solid $color
104
104
 
105
- =radius($v: 12)
105
+ =radius($v: 4)
106
106
  border-radius: radius($v)
107
107
 
108
108
  =shadow($v: md)
@@ -1,14 +1,15 @@
1
1
  // =============================================================================
2
- // MOLECULE FRACTALS — self-similar: each is a small graph of atom fractals.
2
+ // 05_MOLECULES — self-similar: each is a small graph of atom fractals.
3
3
  // -----------------------------------------------------------------------------
4
4
  // This is the payoff of the model. A molecule reads like a recipe of atoms,
5
5
  // and a component (next layer up) reads like a recipe of molecules. Same move
6
6
  // at every scale — that is the "fractal" in fractalstyler2.
7
7
  // =============================================================================
8
8
 
9
- @use 'config' as *
10
- @use 'atoms' as *
11
- @use 'responsive' as *
9
+ @use 'sass:string'
10
+ @use '01_config' as *
11
+ @use '04_atoms' as *
12
+ @use '03_responsive' as *
12
13
 
13
14
  // Stack — vertical rhythm with one shared gap.
14
15
  =stack($gap: xs, $x: null)
@@ -38,11 +39,13 @@
38
39
  +my-auto
39
40
 
40
41
  // Frame — fixed aspect-ratio media box.
41
- =frame($ratio: 16 / 9)
42
- aspect-ratio: $ratio
42
+ =frame($ratio: '16 / 9')
43
+ aspect-ratio: string.unquote('#{$ratio}')
43
44
  overflow: hidden
44
- > :is(img, video, iframe)
45
+ position: relative
46
+ > *
45
47
  +full
48
+ > :is(img, video, iframe)
46
49
  object-fit: cover
47
50
 
48
51
  // Reel — horizontal scroll-snap rail.
@@ -69,13 +72,14 @@
69
72
  flex-grow: 999
70
73
  min-width: $min
71
74
 
72
- // Surface — the "material" fractal: skin + elevation in one call.
75
+ // Surface — the "material" fractal: skin + border + optional radius + optional padding + elevation.
73
76
  // The building block of every card/panel/popover.
74
- =surface($bg: surface, $pad: s, $radius: 12, $elevation: none)
77
+ =surface($bg: surface, $pad: null, $radius: 6, $elevation: none)
75
78
  +bg($bg)
76
- +border
77
- +radius($radius)
78
- +pad($pad)
79
+ @if $radius != null
80
+ +radius($radius)
81
+ @if $pad != null
82
+ +pad($pad)
79
83
  @if $elevation != none
80
84
  +shadow($elevation)
81
85
 
@@ -0,0 +1,189 @@
1
+ // =============================================================================
2
+ // 06_RECIPES — macro mixins. Compositions of molecules and atoms.
3
+ // -----------------------------------------------------------------------------
4
+ // Recipes are mixins, NOT locked CSS classes. They take arguments and allow
5
+ // custom components to compose standard patterns with flexible parameters.
6
+ // =============================================================================
7
+
8
+ @use '01_config' as *
9
+ @use '04_atoms' as *
10
+ @use '05_molecules' as *
11
+
12
+ // Universal interactive control primitive (Button, Select trigger, Accordion header)
13
+ =control($size: md, $radius: 4)
14
+ +row(null, center)
15
+ +gap(2xs)
16
+ +radius($radius)
17
+ +border(all, var(--border))
18
+ +bg(surface)
19
+ +ink(primary)
20
+ +weight(500)
21
+ user-select: none
22
+ cursor: pointer
23
+ text-decoration: none
24
+ transition: background 120ms ease, border-color 120ms ease, transform 80ms ease
25
+
26
+ @if $size == sm
27
+ height: var(--control-h-sm, 26px)
28
+ +px(2xs)
29
+ +type(xs)
30
+ @else if $size == lg
31
+ height: var(--control-h-lg, 38px)
32
+ +px(s)
33
+ +type(md)
34
+ @else
35
+ height: var(--control-h-md, 32px)
36
+ +px(xs)
37
+ +type(sm)
38
+
39
+ &:hover
40
+ +bg(raised)
41
+ border-color: var(--border-subtle)
42
+
43
+ &:active
44
+ transform: scale(0.985)
45
+
46
+ &:focus-visible
47
+ +ring
48
+
49
+ &:disabled, &[aria-disabled='true']
50
+ opacity: 0.5
51
+ cursor: not-allowed
52
+ pointer-events: none
53
+
54
+ // Select input recipe (with embedded SVG chevron and optical line-height)
55
+ =select($size: md, $radius: 4)
56
+ +control($size, $radius)
57
+ appearance: none
58
+ -webkit-appearance: none
59
+ padding-right: 28px
60
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%237F91AA' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E")
61
+ background-repeat: no-repeat
62
+ background-position: right 8px center
63
+ line-height: 1.2
64
+ cursor: pointer
65
+ width: 100%
66
+
67
+ // Partition divider recipe with guaranteed breathing room
68
+ =partition($side: top, $pad: s)
69
+ @if $side == top
70
+ margin-top: auto
71
+ padding-top: space($pad)
72
+ border-top: 1px solid var(--border)
73
+ @else if $side == bottom
74
+ margin-bottom: space($pad)
75
+ padding-bottom: space($pad)
76
+ border-bottom: 1px solid var(--border)
77
+
78
+ // Pure CSS collapsible transition (0fr -> 1fr grid transition)
79
+ =collapsible
80
+ display: grid
81
+ grid-template-rows: 0fr
82
+ transition: grid-template-rows 140ms cubic-bezier(0.16, 1, 0.3, 1)
83
+
84
+ &[data-open='true'], &[aria-expanded='true']
85
+ grid-template-rows: 1fr
86
+
87
+ > .collapsible-inner
88
+ overflow: hidden
89
+ min-height: 0
90
+
91
+ // Marquee animation recipe
92
+ =marquee($speed: 30s, $gap: 1rem)
93
+ overflow: hidden
94
+ display: flex
95
+ user-select: none
96
+ gap: $gap
97
+ position: relative
98
+
99
+ &[data-fade='true']
100
+ mask-image: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%)
101
+
102
+ > [data-slot='marquee-track']
103
+ display: flex
104
+ flex-shrink: 0
105
+ justify-content: space-around
106
+ gap: $gap
107
+ min-width: 100%
108
+ animation: marquee-scroll $speed linear infinite
109
+
110
+ &[data-pause='true']:hover > [data-slot='marquee-track']
111
+ animation-play-state: paused
112
+
113
+ // Card recipe — surface material + top-anchored vertical stack
114
+ =card($bg: surface, $pad: null, $radius: 6, $elevation: none)
115
+ +surface($bg, $pad, $radius, $elevation)
116
+ +box(stretch, start)
117
+ +gap(s)
118
+ height: 100%
119
+ text-align: left
120
+
121
+ // Button recipe
122
+ =button($size: md, $radius: 4)
123
+ +control($size, $radius)
124
+ justify-content: center
125
+
126
+ // Badge recipe — compact pill chip with subtle border
127
+ =badge
128
+ +row(center, center)
129
+ display: inline-flex
130
+ +gap(3xs)
131
+ +px(2xs)
132
+ +type(xs)
133
+ +weight(500)
134
+ +radius(3)
135
+ +bg(raised)
136
+ +ink(secondary)
137
+ +border(all, var(--border))
138
+ padding-block: 2px
139
+
140
+ // Input recipe — text entry field
141
+ =input
142
+ display: block
143
+ +w(100%)
144
+ +px(xs)
145
+ +type(sm)
146
+ +leading(1.5)
147
+ +border(all, var(--border))
148
+ +radius(4)
149
+ +bg(input)
150
+ +ink(primary)
151
+ height: var(--control-h-md, 32px)
152
+ transition: border-color 120ms ease, box-shadow 120ms ease
153
+ &:focus-visible, &:focus
154
+ outline: none
155
+ border-color: var(--theme-color)
156
+ box-shadow: 0 0 0 2px var(--ring)
157
+
158
+ // Keycap recipe
159
+ =kbd
160
+ display: inline-block
161
+ +px(3xs)
162
+ +type(xs)
163
+ +leading(1)
164
+ +radius(3)
165
+ +bg(raised)
166
+ border: 1px solid var(--border)
167
+ font-family: var(--font-mono)
168
+ padding-block: 2px
169
+
170
+ // Overlay & Dialog surfaces
171
+ =dialog-surface
172
+ +bg(dialog)
173
+ +border(all, var(--border))
174
+ +radius(6)
175
+ +shadow(lg)
176
+ max-width: min(92vw, 540px)
177
+ width: 100%
178
+
179
+ =popover-surface
180
+ +bg(popover)
181
+ +border(all, var(--border))
182
+ +radius(6)
183
+ +shadow(md)
184
+
185
+ // Divider recipe
186
+ =divider
187
+ border: none
188
+ +border(top)
189
+ margin-block: space(s)
@@ -1,5 +1,5 @@
1
1
  // =============================================================================
2
- // BASE — a minimal, opinionated reset so the tokens actually reach the page.
2
+ // 07_BASE — a minimal, opinionated reset so the tokens actually reach the page.
3
3
  // Kept tiny: box model, sane media defaults, and wiring body to the palette.
4
4
  // =============================================================================
5
5
 
@@ -8,6 +8,7 @@
8
8
 
9
9
  html
10
10
  -webkit-text-size-adjust: 100%
11
+ font-family: var(--font-sans)
11
12
 
12
13
  body
13
14
  margin: 0
@@ -32,7 +33,7 @@ body
32
33
 
33
34
  :where(a)
34
35
  color: inherit
35
- text-decoration-color: var(--border-strong)
36
+ text-decoration-color: var(--border-subtle)
36
37
 
37
38
  :where(button, input, select, textarea)
38
39
  font: inherit