fractalstyler2 0.1.0 → 0.4.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 +61 -59
- package/dist/cli.js +47 -9
- 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.js +28 -12
- package/dist/styles/{_tokens.sass → _00_tokens.sass} +75 -64
- package/dist/styles/{_config.sass → _01_config.sass} +28 -36
- 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/_09_utilities.sass +208 -0
- 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 +6 -3
- 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/{_tokens.sass → _00_tokens.sass} +73 -64
- package/templates/{_config.sass → _01_config.sass} +24 -36
- 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/_09_utilities.sass +207 -0
- 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/_utilities.sass +0 -125
- package/templates/_blocks.sass +0 -88
- package/templates/_layouts.sass +0 -108
- package/templates/_responsive.sass +0 -43
- package/templates/_utilities.sass +0 -125
package/plugin.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fractalstyler2",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Fractal-composition styling system and design token engine for SvelteKit and AI design tools (OpenDesign, Pencil, Cursor).",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Amrit",
|
|
7
|
+
"url": "https://github.com/fractal-mandala"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"svelte",
|
|
12
|
+
"sveltekit",
|
|
13
|
+
"sass",
|
|
14
|
+
"css",
|
|
15
|
+
"design-system",
|
|
16
|
+
"mcp",
|
|
17
|
+
"tokens",
|
|
18
|
+
"fractals"
|
|
19
|
+
],
|
|
20
|
+
"homepage": "https://github.com/fractal-mandala/fractalstyler2",
|
|
21
|
+
"repository": "https://github.com/fractal-mandala/fractalstyler2"
|
|
22
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fractal-styler
|
|
3
|
+
description: Design, compose, and build UI components and page layouts using the fractalstyler2 SASS mixin system and fluid design tokens in SvelteKit. Use when authoring components, applying design tokens, styling layouts, or compiling fractal recipes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fractal Styler (fractalstyler2)
|
|
7
|
+
|
|
8
|
+
`fractalstyler2` is a fractal-composition styling system for SvelteKit and modern web applications. Every styling decision is a reusable SASS mixin ("a fractal"), and components and layouts are clean recipes composed of smaller fractals.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## The Four Tiers of Composition
|
|
13
|
+
|
|
14
|
+
| Tier | Role | Examples |
|
|
15
|
+
| :--- | :--- | :--- |
|
|
16
|
+
| **1. Tokens & Config** | Fluid Utopia scales & resolvers | `space(m)` → `var(--space-m)`, `radius(12)` → `var(--radius-12)` |
|
|
17
|
+
| **2. Atoms** | Single layout/styling decisions | `+box`, `+row`, `+gap(s)`, `+pad(m)`, `+border`, `+radius(12)`, `+bg(surface)`, `+ink(primary)` |
|
|
18
|
+
| **3. Molecules** | Compositions of atoms | `+stack(s)`, `+cluster(xs)`, `+surface(surface, s, 12)`, `+cover(80vh)`, `+frame(16/9)`, `+with-sidebar` |
|
|
19
|
+
| **4. Components & Layouts** | Recipes of molecules | `.card`, `.panel`, `.button`, `.badge` · `.grid-3`, `.card-grid`, `.hero`, `.holy-grail`, `.docs`, `.app-shell` |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Golden Rules for Agents
|
|
24
|
+
|
|
25
|
+
1. **Never hardcode values that tokens cover**: Use `+gap(m)`, `+radius(12)`, `+bg(surface)` instead of arbitrary pixel values.
|
|
26
|
+
2. **Compose fractals; don't write raw CSS**: Raw CSS is strictly for unique lines that no fractal covers.
|
|
27
|
+
3. **State on `data-*` / `aria-*` attributes**: Never use modifier classes like `.is-active` or `.btn--primary`. Use `&[data-elevated]`, `&[data-variant='primary']`.
|
|
28
|
+
4. **Markup stays semantic**: Prefer `<article class="card">` over 20 nested utility classes.
|
|
29
|
+
5. **Mobile-first**: Define base layout first, grow with `+at(md/lg/xl)` and `+cols()`.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## How to Author a Svelte 5 Component
|
|
34
|
+
|
|
35
|
+
```svelte
|
|
36
|
+
<script lang="ts">
|
|
37
|
+
let {
|
|
38
|
+
title = 'Pricing Tier',
|
|
39
|
+
price = '$29',
|
|
40
|
+
highlighted = false,
|
|
41
|
+
children
|
|
42
|
+
} = $props();
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<article class="pricing-card" data-highlighted={highlighted || undefined}>
|
|
46
|
+
<div class="row ycenter xbetween">
|
|
47
|
+
<h3 class="text-xl">{title}</h3>
|
|
48
|
+
{#if highlighted}
|
|
49
|
+
<span class="badge" data-variant="theme">Popular</span>
|
|
50
|
+
{/if}
|
|
51
|
+
</div>
|
|
52
|
+
<div class="row ycenter gap-2xs">
|
|
53
|
+
<span class="text-4xl font-bold">{price}</span>
|
|
54
|
+
<span class="muted">/month</span>
|
|
55
|
+
</div>
|
|
56
|
+
{#if children}
|
|
57
|
+
{@render children()}
|
|
58
|
+
{/if}
|
|
59
|
+
</article>
|
|
60
|
+
|
|
61
|
+
<style lang="sass">
|
|
62
|
+
@use '$lib/styles/fractals' as *
|
|
63
|
+
|
|
64
|
+
.pricing-card
|
|
65
|
+
+surface(surface, m, 16)
|
|
66
|
+
+stack(m)
|
|
67
|
+
transition: transform 150ms ease, box-shadow 150ms ease
|
|
68
|
+
|
|
69
|
+
&[data-highlighted]
|
|
70
|
+
+border(all, var(--theme))
|
|
71
|
+
+shadow(lg)
|
|
72
|
+
</style>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## MCP Tools Integration
|
|
78
|
+
|
|
79
|
+
If the host environment connects to the `fractalstyler2` MCP server, use the available tools:
|
|
80
|
+
* `get_design_tokens`: Query available space, typography, and radius steps.
|
|
81
|
+
* `compile_fractals`: Compile SASS mixin recipes to verify CSS output.
|
|
82
|
+
* `snap_to_tokens`: Snap canvas pixel values to tokens.
|
|
83
|
+
* `validate_recipe`: Lint SASS/Svelte against the golden rules.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Fractals Catalog Reference
|
|
2
|
+
|
|
3
|
+
## Atom Fractals (Single Decisions)
|
|
4
|
+
|
|
5
|
+
* **Layout Flow**: `+box(x, y)` (flex col), `+row(x, y)` (flex row), `+wrap`, `+grid(cols)`, `+auto-grid(min, gap)`, `+center` (grid center).
|
|
6
|
+
* **Spacing**: `+gap(v)`, `+pad(v)`, `+px(v)`, `+py(v)`, `+mx-auto`, `+my-auto`.
|
|
7
|
+
* **Sizing**: `+w(v)`, `+h(v)`, `+full`, `+square(v)`, `+grow(n)`, `+shrink(n)`, `+min0`.
|
|
8
|
+
* **Surfaces**: `+bg(role)`, `+ink(role)`, `+border(side, color)`, `+radius(v)`, `+shadow(v)`.
|
|
9
|
+
* **Position**: `+relative`, `+absolute(inset)`, `+sticky(top)`, `+fill`.
|
|
10
|
+
* **Type**: `+type(v)`, `+weight(w)`, `+leading(lh)`, `+truncate`, `+clamp-lines(n)`.
|
|
11
|
+
* **Motion**: `+transition(props, dur, ease)`, `+ring(color)`.
|
|
12
|
+
|
|
13
|
+
## Molecule Fractals (Compositions of Atoms)
|
|
14
|
+
|
|
15
|
+
* **`+stack($gap: xs, $x: null)`**: Vertical flex column rhythm with uniform gap.
|
|
16
|
+
* **`+cluster($gap: xs, $x: start, $y: center)`**: Wrapping flex row for tag clouds, chips, and button rows.
|
|
17
|
+
* **`+center-column($max: 60ch, $pad: s)`**: Bounded reading column with max-width measure and auto margins.
|
|
18
|
+
* **`+cover($min: 100vh, $pad: s)`**: Full-height container with a vertically centered focal child (`> .center`).
|
|
19
|
+
* **`+frame($ratio: 16 / 9)`**: Fixed aspect-ratio container for media.
|
|
20
|
+
* **`+reel($gap: xs)`**: Horizontal scroll-snap rail.
|
|
21
|
+
* **`+with-sidebar($rail: 240px, $gap: s, $min: 60%)`**: Intrinsic sidebar + fluid main layout.
|
|
22
|
+
* **`+surface($bg: surface, $pad: s, $radius: 12, $elevation: none)`**: The all-in-one material mixin.
|
|
23
|
+
* **`+cols($map, $gap: s)`**: Responsive grid mapped across breakpoints, e.g. `+cols((base: 1, sm: 2, lg: 3), m)`.
|
|
24
|
+
|
|
25
|
+
## Responsive Utilities
|
|
26
|
+
* `+at(sm | md | lg | xl)`: Mobile-first min-width media query.
|
|
27
|
+
* `+until(sm | md | lg | xl)`: Max-width media query.
|
|
28
|
+
* `+cq`, `+cq-at(width)`, `+cq-until(width)`: Container queries.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Design Tokens Reference
|
|
2
|
+
|
|
3
|
+
## Space Scale (Fluid Utopia)
|
|
4
|
+
| Step | Token Variable | Approximate Px | Clamp Formula |
|
|
5
|
+
| :--- | :--- | :--- | :--- |
|
|
6
|
+
| `3xs` | `--space-3xs` | ~5px | `clamp(0.3125rem, 0.3125rem + 0vw, 0.3125rem)` |
|
|
7
|
+
| `2xs` | `--space-2xs` | ~9px | `clamp(0.5625rem, 0.5369rem + 0.1136vw, 0.625rem)` |
|
|
8
|
+
| `xs` | `--space-xs` | ~14px | `clamp(0.875rem, 0.8494rem + 0.1136vw, 0.9375rem)` |
|
|
9
|
+
| `s` | `--space-s` | ~18px | `clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)` |
|
|
10
|
+
| `m` | `--space-m` | ~27px | `clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem)` |
|
|
11
|
+
| `l` | `--space-l` | ~36px | `clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem)` |
|
|
12
|
+
| `xl` | `--space-xl` | ~54px | `clamp(3.375rem, 3.2216rem + 0.6818vw, 3.75rem)` |
|
|
13
|
+
| `2xl` | `--space-2xl` | ~72px | `clamp(4.5rem, 4.2955rem + 0.9091vw, 5rem)` |
|
|
14
|
+
| `3xl` | `--space-3xl` | ~108px | `clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem)` |
|
|
15
|
+
| `s-l` | `--space-s-l` | ~18→40px | `clamp(1.125rem, 0.5625rem + 2.5vw, 2.5rem)` |
|
|
16
|
+
|
|
17
|
+
## Typography Scale
|
|
18
|
+
| Step | Token Variable | Fluid Clamp |
|
|
19
|
+
| :--- | :--- | :--- |
|
|
20
|
+
| `xs` | `--text-xs` | `0.75rem` (12px) |
|
|
21
|
+
| `sm` | `--text-sm` | `clamp(0.9375rem, 0.9119rem + 0.1136vw, 1rem)` |
|
|
22
|
+
| `md` | `--text-md` | `clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)` |
|
|
23
|
+
| `lg` | `--text-lg` | `clamp(1.35rem, 1.2631rem + 0.3864vw, 1.5625rem)` |
|
|
24
|
+
| `xl` | `--text-xl` | `clamp(1.62rem, 1.4837rem + 0.6057vw, 1.9531rem)` |
|
|
25
|
+
| `2xl` | `--text-2xl` | `clamp(1.944rem, 1.7405rem + 0.9044vw, 2.4414rem)` |
|
|
26
|
+
| `3xl` | `--text-3xl` | `clamp(2.3328rem, 2.0387rem + 1.3072vw, 3.0518rem)` |
|
|
27
|
+
| `4xl` | `--text-4xl` | `clamp(2.7994rem, 2.384rem + 1.8461vw, 3.8147rem)` |
|
|
28
|
+
|
|
29
|
+
## Radius Scale
|
|
30
|
+
`0`, `2`, `4`, `6`, `8`, `12`, `16`, `24`, `full`
|
|
31
|
+
|
|
32
|
+
## Color & Surface Roles
|
|
33
|
+
* **Surfaces**: `--bg`, `--bg-surface`, `--bg-raised`
|
|
34
|
+
* **Ink**: `--text-primary`, `--text-secondary`, `--text-muted`, `--text-inverse`
|
|
35
|
+
* **Borders**: `--border`, `--border-strong`
|
|
36
|
+
* **Brand**: `--theme`, `--theme-hover`, `--theme-active`, `--ring`
|
|
@@ -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`.
|
|
@@ -1,16 +1,70 @@
|
|
|
1
1
|
// =============================================================================
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// tokens stay defined during SSR and with
|
|
5
|
-
// prefers-color-scheme and
|
|
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
6
|
// =============================================================================
|
|
7
7
|
|
|
8
|
+
=light-theme-tokens
|
|
9
|
+
color-scheme: light
|
|
10
|
+
--bg: #fdfefe
|
|
11
|
+
--bg-surface: #f8f7f7
|
|
12
|
+
--bg-raised: #f1f5f9
|
|
13
|
+
--bg-panel: #F1F3F5
|
|
14
|
+
--bg-footer: #E9ECEF
|
|
15
|
+
--bg-popover: #FFFFFF
|
|
16
|
+
--bg-dialog: #FFFFFF
|
|
17
|
+
--bg-terminal: #0F172A
|
|
18
|
+
--bg-input: #FFFFFF
|
|
19
|
+
--bg-canvas: #F8F9FA
|
|
20
|
+
--text-primary: #0f172a
|
|
21
|
+
--text-secondary: #5b6472
|
|
22
|
+
--text-muted: #929497
|
|
23
|
+
--text-inverse: #ffffff
|
|
24
|
+
--state-hover: #E2E8F0
|
|
25
|
+
--state-hover-subtle: #F1F5F9
|
|
26
|
+
--state-selected: #CBD5E1
|
|
27
|
+
--border: #E2E8F0
|
|
28
|
+
--border-subtle: #EDF2F7
|
|
29
|
+
--theme-color: #04825B
|
|
30
|
+
--theme-color-alt: #047857
|
|
31
|
+
--theme: var(--theme-color)
|
|
32
|
+
--theme-hover: var(--theme-color-alt)
|
|
33
|
+
--ring: rgba(0, 127, 78, 0.35)
|
|
34
|
+
|
|
35
|
+
=dark-theme-tokens
|
|
36
|
+
color-scheme: dark
|
|
37
|
+
--bg: #0E1118
|
|
38
|
+
--bg-surface: #141824
|
|
39
|
+
--bg-raised: #1C2232
|
|
40
|
+
--bg-panel: #101420
|
|
41
|
+
--bg-footer: #0B0D14
|
|
42
|
+
--bg-popover: #20273A
|
|
43
|
+
--bg-dialog: #20273A
|
|
44
|
+
--bg-terminal: #080A0F
|
|
45
|
+
--bg-input: #121622
|
|
46
|
+
--bg-canvas: #0E1118
|
|
47
|
+
--border: #28324A
|
|
48
|
+
--border-subtle: #1E2538
|
|
49
|
+
--text-primary: #EDF2F7
|
|
50
|
+
--text-secondary: #9BB0C7
|
|
51
|
+
--text-muted: #5D7087
|
|
52
|
+
--state-hover: #263047
|
|
53
|
+
--state-hover-subtle: #1B2233
|
|
54
|
+
--state-selected: #32405E
|
|
55
|
+
--theme-color: #38BDF8
|
|
56
|
+
--theme-color-alt: #0EA5E9
|
|
57
|
+
--theme: var(--theme-color)
|
|
58
|
+
--theme-hover: var(--theme-color-alt)
|
|
59
|
+
--text-inverse: #0E1118
|
|
60
|
+
--ring: rgba(16, 185, 129, 0.4)
|
|
61
|
+
|
|
8
62
|
:root
|
|
9
63
|
color-scheme: light
|
|
10
64
|
|
|
11
65
|
// Fonts
|
|
12
|
-
--font-sans:
|
|
13
|
-
--font-mono:
|
|
66
|
+
--font-sans: "Google Sans Flex", sans-serif
|
|
67
|
+
--font-mono: "JetBrains Mono", monospace
|
|
14
68
|
|
|
15
69
|
// Fluid type scale (Utopia 360→1240)
|
|
16
70
|
--text-xs: 0.75rem
|
|
@@ -34,9 +88,10 @@
|
|
|
34
88
|
--space-3xl: clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem)
|
|
35
89
|
--space-s-l: clamp(1.125rem, 0.5625rem + 2.5vw, 2.5rem)
|
|
36
90
|
|
|
37
|
-
// Radius
|
|
91
|
+
// Radius (Concentric hierarchy)
|
|
38
92
|
--radius-0: 0
|
|
39
93
|
--radius-2: 2px
|
|
94
|
+
--radius-3: 3px
|
|
40
95
|
--radius-4: 4px
|
|
41
96
|
--radius-6: 6px
|
|
42
97
|
--radius-8: 8px
|
|
@@ -45,6 +100,11 @@
|
|
|
45
100
|
--radius-24: 24px
|
|
46
101
|
--radius-full: 9999px
|
|
47
102
|
|
|
103
|
+
// Control Heights
|
|
104
|
+
--control-h-sm: 26px
|
|
105
|
+
--control-h-md: 32px
|
|
106
|
+
--control-h-lg: 38px
|
|
107
|
+
|
|
48
108
|
// Shadow
|
|
49
109
|
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06)
|
|
50
110
|
--shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08)
|
|
@@ -58,70 +118,19 @@
|
|
|
58
118
|
--z-toast: 300
|
|
59
119
|
|
|
60
120
|
// Layout
|
|
61
|
-
--header-height:
|
|
121
|
+
--header-height: 48px
|
|
62
122
|
--footer-height: 56px
|
|
63
123
|
--measure: 65ch
|
|
64
124
|
|
|
65
|
-
// Light
|
|
66
|
-
|
|
67
|
-
--bg-surface: #f8f7f7
|
|
68
|
-
--bg-raised: #f1f5f9
|
|
69
|
-
--text-primary: #0f172a
|
|
70
|
-
--text-secondary: #5b6472
|
|
71
|
-
--text-muted: #929497
|
|
72
|
-
--text-inverse: #ffffff
|
|
73
|
-
--border: #e2e8f0
|
|
74
|
-
--border-strong: #cbd5e1
|
|
75
|
-
--theme: #007f4e
|
|
76
|
-
--theme-hover: #00663f
|
|
77
|
-
--theme-active: #005031
|
|
78
|
-
--ring: rgba(0, 127, 78, 0.35)
|
|
125
|
+
// Default Light Palette
|
|
126
|
+
+light-theme-tokens
|
|
79
127
|
|
|
80
128
|
@media (prefers-color-scheme: dark)
|
|
81
129
|
:root:not([data-mode='light'])
|
|
82
|
-
|
|
83
|
-
--bg: #131313
|
|
84
|
-
--bg-surface: #1e1e1e
|
|
85
|
-
--bg-raised: #2b2b2b
|
|
86
|
-
--text-primary: #f9fafb
|
|
87
|
-
--text-secondary: #a7adb7
|
|
88
|
-
--text-muted: #6b7280
|
|
89
|
-
--text-inverse: #0b0f17
|
|
90
|
-
--border: #2f3030
|
|
91
|
-
--border-strong: #4a4b4b
|
|
92
|
-
--theme: #10b981
|
|
93
|
-
--theme-hover: #34d399
|
|
94
|
-
--theme-active: #059669
|
|
95
|
-
--ring: rgba(16, 185, 129, 0.4)
|
|
130
|
+
+dark-theme-tokens
|
|
96
131
|
|
|
97
132
|
[data-mode='dark']
|
|
98
|
-
|
|
99
|
-
--bg: #131313
|
|
100
|
-
--bg-surface: #1e1e1e
|
|
101
|
-
--bg-raised: #2b2b2b
|
|
102
|
-
--text-primary: #f9fafb
|
|
103
|
-
--text-secondary: #a7adb7
|
|
104
|
-
--text-muted: #6b7280
|
|
105
|
-
--text-inverse: #0b0f17
|
|
106
|
-
--border: #2f3030
|
|
107
|
-
--border-strong: #4a4b4b
|
|
108
|
-
--theme: #10b981
|
|
109
|
-
--theme-hover: #34d399
|
|
110
|
-
--theme-active: #059669
|
|
111
|
-
--ring: rgba(16, 185, 129, 0.4)
|
|
133
|
+
+dark-theme-tokens
|
|
112
134
|
|
|
113
135
|
[data-mode='light']
|
|
114
|
-
|
|
115
|
-
--bg: #fdfefe
|
|
116
|
-
--bg-surface: #f8f7f7
|
|
117
|
-
--bg-raised: #f1f5f9
|
|
118
|
-
--text-primary: #0f172a
|
|
119
|
-
--text-secondary: #5b6472
|
|
120
|
-
--text-muted: #929497
|
|
121
|
-
--text-inverse: #ffffff
|
|
122
|
-
--border: #e2e8f0
|
|
123
|
-
--border-strong: #cbd5e1
|
|
124
|
-
--theme: #007f4e
|
|
125
|
-
--theme-hover: #00663f
|
|
126
|
-
--theme-active: #005031
|
|
127
|
-
--ring: rgba(0, 127, 78, 0.35)
|
|
136
|
+
+light-theme-tokens
|
|
@@ -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,68 +11,56 @@
|
|
|
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
|
-
$surface-roles: bg surface raised
|
|
22
|
-
$ink-roles: primary secondary muted inverse
|
|
23
21
|
|
|
24
|
-
|
|
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
25
|
|
|
26
|
-
|
|
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
27
|
|
|
28
|
-
//
|
|
29
|
-
@function
|
|
28
|
+
// --- Universal Token Resolver --------------------------------------------------
|
|
29
|
+
@function tok($group, $v)
|
|
30
30
|
@if $v == null
|
|
31
31
|
@return null
|
|
32
|
-
@
|
|
33
|
-
@return var(--space-#{$v})
|
|
34
|
-
@else if meta.type-of($v) == number and math.is-unitless($v)
|
|
32
|
+
@if meta.type-of($v) == number and math.is-unitless($v)
|
|
35
33
|
@return #{$v}px
|
|
36
|
-
@
|
|
37
|
-
@return $v
|
|
34
|
+
@if meta.type-of($v) == string and not (math.unit($v) != '')
|
|
35
|
+
@return var(--#{$group}-#{$v})
|
|
36
|
+
@return $v
|
|
37
|
+
|
|
38
|
+
// --- Resolvers ----------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
@function space($v)
|
|
41
|
+
@return tok(space, $v)
|
|
38
42
|
|
|
39
43
|
@function radius($v)
|
|
40
|
-
@
|
|
41
|
-
@return var(--radius-#{$v})
|
|
42
|
-
@else if meta.type-of($v) == number and math.is-unitless($v)
|
|
43
|
-
@return #{$v}px
|
|
44
|
-
@else
|
|
45
|
-
@return $v
|
|
44
|
+
@return tok(radius, $v)
|
|
46
45
|
|
|
47
46
|
@function text-size($v)
|
|
48
|
-
@
|
|
49
|
-
@return var(--text-#{$v})
|
|
50
|
-
@else
|
|
51
|
-
@return $v
|
|
47
|
+
@return tok(text, $v)
|
|
52
48
|
|
|
53
49
|
@function shadow($v)
|
|
54
|
-
@
|
|
55
|
-
@return var(--shadow-#{$v})
|
|
56
|
-
@else
|
|
57
|
-
@return $v
|
|
50
|
+
@return tok(shadow, $v)
|
|
58
51
|
|
|
59
52
|
@function surface($role)
|
|
60
53
|
@if $role == bg
|
|
61
54
|
@return var(--bg)
|
|
62
|
-
@
|
|
63
|
-
@return var(--bg-#{$role})
|
|
55
|
+
@return tok(bg, $role)
|
|
64
56
|
|
|
65
57
|
@function ink($role)
|
|
66
58
|
@if $role == primary
|
|
67
59
|
@return var(--text-primary)
|
|
68
|
-
@
|
|
69
|
-
@return var(--text-#{$role})
|
|
60
|
+
@return tok(text, $role)
|
|
70
61
|
|
|
71
62
|
@function align($v)
|
|
72
|
-
|
|
73
|
-
@if $m
|
|
74
|
-
@return $m
|
|
75
|
-
@return $v
|
|
63
|
+
@return map.get($align-map, $v) or $v
|
|
76
64
|
|
|
77
65
|
@function bp($name)
|
|
78
66
|
@return map.get($breakpoints, $name)
|
|
@@ -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
|
|