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.
- package/README.md +60 -45
- package/dist/cli.js +54 -11
- package/dist/index.d.ts +32 -1
- package/dist/index.js +73 -4
- package/dist/mcp/export.d.ts +13 -0
- package/dist/mcp/export.js +76 -0
- package/dist/mcp/schemas/compile_fractals.json +20 -0
- package/dist/mcp/schemas/css_to_fractals.json +16 -0
- package/dist/mcp/schemas/generate_component.json +45 -0
- package/dist/mcp/schemas/get_design_tokens.json +23 -0
- package/dist/mcp/schemas/instructions.md +18 -0
- package/dist/mcp/schemas/list_fractals.json +20 -0
- package/dist/mcp/schemas/snap_to_tokens.json +25 -0
- package/dist/mcp/schemas/validate_recipe.json +16 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +837 -0
- package/dist/styles/_00_tokens.sass +177 -0
- package/{templates/_config.sass → dist/styles/_01_config.sass} +11 -5
- package/dist/styles/_02_fonts.sass +13 -0
- package/dist/styles/_03_responsive.sass +31 -0
- package/dist/styles/{_atoms.sass → _04_atoms.sass} +4 -4
- package/{templates/_molecules.sass → dist/styles/_05_molecules.sass} +16 -12
- package/dist/styles/_06_recipes.sass +189 -0
- package/dist/styles/{_base.sass → _07_base.sass} +3 -2
- package/dist/styles/_08_blocks.sass +433 -0
- package/dist/styles/{_utilities.sass → _09_utilities.sass} +96 -16
- package/dist/styles/_10_layouts.sass +373 -0
- package/dist/styles/_11_own.sass +21 -0
- package/dist/styles/_fractals.sass +7 -6
- package/dist/styles/index.sass +14 -16
- package/mcp.json +11 -0
- package/package.json +14 -6
- package/plugin.json +22 -0
- package/skills/fractal-styler/SKILL.md +83 -0
- package/skills/fractal-styler/references/fractals.md +28 -0
- package/skills/fractal-styler/references/tokens.md +36 -0
- package/skills/style-migration/SKILL.md +45 -0
- package/templates/_00_tokens.sass +177 -0
- package/{dist/styles/_config.sass → templates/_01_config.sass} +11 -5
- package/templates/_02_fonts.sass +13 -0
- package/templates/_03_responsive.sass +31 -0
- package/templates/{_atoms.sass → _04_atoms.sass} +4 -4
- package/{dist/styles/_molecules.sass → templates/_05_molecules.sass} +16 -12
- package/templates/_06_recipes.sass +189 -0
- package/templates/{_base.sass → _07_base.sass} +3 -2
- package/templates/_08_blocks.sass +433 -0
- package/templates/{_utilities.sass → _09_utilities.sass} +96 -16
- package/templates/_10_layouts.sass +373 -0
- package/templates/_11_own.sass +21 -0
- package/templates/_fractals.sass +7 -6
- package/templates/index.sass +14 -16
- package/dist/styles/_blocks.sass +0 -88
- package/dist/styles/_layouts.sass +0 -108
- package/dist/styles/_responsive.sass +0 -43
- package/dist/styles/_tokens.sass +0 -127
- package/templates/_blocks.sass +0 -88
- package/templates/_layouts.sass +0 -108
- package/templates/_responsive.sass +0 -43
- package/templates/_tokens.sass +0 -127
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: style-migration
|
|
3
|
+
description: Migrate legacy CSS, Tailwind utilities, or fractals-styler (v1) classes into modern fractalstyler2 SASS mixin recipes and token scales. Use when refactoring stylesheets, updating legacy components, or cleaning up utility class soup.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Style Migration & Refactoring Skill
|
|
7
|
+
|
|
8
|
+
This skill guides the refactoring and migration of existing styles into idiomatic `fractalstyler2`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. Quick Replacement Matrix
|
|
13
|
+
|
|
14
|
+
| Legacy Pattern | Problem | fractalstyler2 Replacement |
|
|
15
|
+
| :--- | :--- | :--- |
|
|
16
|
+
| `gap8`, `gap16`, `gap24` | Dynamic JIT classes removed in v2 | Markup: `.gap-xs`, `.gap-s`, `.gap-m`<br>SASS: `+gap(8)` or `+gap(s)` |
|
|
17
|
+
| `pad8`, `pad16`, `pad24` | Dynamic JIT classes removed in v2 | Markup: `.pad-xs`, `.pad-s`, `.pad-m`<br>SASS: `+pad(16)` or `+pad(m)` |
|
|
18
|
+
| `<div class="center">` (as reading text column) | In v2, `.center` dead-centers items in a grid | SASS: `.reading-col { +center-column }` |
|
|
19
|
+
| `<div class="stack">` | Markup class `.stack` does not exist | SASS: `.container { +stack(s) }` |
|
|
20
|
+
| `<div class="cluster">` | Markup class `.cluster` does not exist | SASS: `.tag-row { +cluster(xs) }` |
|
|
21
|
+
| `w100`, `h100` | Removed aliases | `.wfull`, `.hfull` |
|
|
22
|
+
| `min-w-0`, `min-h-0` | Removed utilities | `.min0` |
|
|
23
|
+
| `[data-variant='quiet']` | Renamed on button | `[data-variant='ghost']` |
|
|
24
|
+
| `.appshell` | Renamed layout | `.app-shell` |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. Refactoring Procedure
|
|
29
|
+
|
|
30
|
+
1. **Identify the Container**: Is it a card, panel, button, reading column, or grid?
|
|
31
|
+
2. **Select the Molecule**:
|
|
32
|
+
- Vertical list of items → `+stack(s)`
|
|
33
|
+
- Tag list / button row → `+cluster(xs)`
|
|
34
|
+
- Box with border/radius/padding → `+surface(surface, s, 12)`
|
|
35
|
+
- Full height hero → `+cover(80vh, xl)`
|
|
36
|
+
3. **Strip Utility Soup from Markup**: Replace long class strings (`class="box pad16 gap12 w100 min-w-0 radius12 border"`) with a single semantic class (`class="card"` or `class="feed-item"`).
|
|
37
|
+
4. **Move Decisions to `<style lang="sass">`**:
|
|
38
|
+
```sass
|
|
39
|
+
@use '$lib/styles/fractals' as *
|
|
40
|
+
|
|
41
|
+
.feed-item
|
|
42
|
+
+surface(surface, s, 12)
|
|
43
|
+
+stack(s)
|
|
44
|
+
```
|
|
45
|
+
5. **Verify**: Use the MCP tool `validate_recipe` or `npx sass src/lib/styles/index.sass /tmp/check.css`.
|
|
@@ -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
|
-
//
|
|
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
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
5
|
+
// _09_utilities.sass project it to a utility class. Same fractal, two consumers.
|
|
6
6
|
// =============================================================================
|
|
7
7
|
|
|
8
|
-
@use '
|
|
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:
|
|
105
|
+
=radius($v: 4)
|
|
106
106
|
border-radius: radius($v)
|
|
107
107
|
|
|
108
108
|
=shadow($v: md)
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
// =============================================================================
|
|
2
|
-
//
|
|
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 '
|
|
10
|
-
@use '
|
|
11
|
-
@use '
|
|
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
|
-
|
|
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 +
|
|
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:
|
|
77
|
+
=surface($bg: surface, $pad: null, $radius: 6, $elevation: none)
|
|
75
78
|
+bg($bg)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
//
|
|
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-
|
|
36
|
+
text-decoration-color: var(--border-subtle)
|
|
36
37
|
|
|
37
38
|
:where(button, input, select, textarea)
|
|
38
39
|
font: inherit
|