@fractaldesign/fractalstyler 0.0.0-stage → 0.9.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/LICENSE +19 -0
- package/README.md +123 -2
- package/cli/main.mjs +141 -0
- package/cli/scaffold.mjs +406 -0
- package/data/recipes.json +61 -0
- package/dist/asset.d.ts +3 -0
- package/dist/css/fractalstyler.css +2453 -0
- package/dist/css/fractalstyler.min.css +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/styles/_00_config.sass +29 -0
- package/dist/styles/_00_fonts.sass +58 -0
- package/dist/styles/_00_tokens.sass +211 -0
- package/dist/styles/_01_base.sass +58 -0
- package/dist/styles/_02_dimensions.sass +70 -0
- package/dist/styles/_03_typography.sass +170 -0
- package/dist/styles/_04_containers.sass +173 -0
- package/dist/styles/_05_layouts.sass +104 -0
- package/dist/styles/_06_shells.sass +135 -0
- package/dist/styles/_07_interactions.sass +212 -0
- package/dist/styles/_08_visuals.sass +136 -0
- package/dist/styles/_09_own.sass +174 -0
- package/dist/styles/colorpacks.sass +119 -0
- package/dist/styles/index.sass +14 -0
- package/dist/styles/themeplates.sass +159 -0
- package/docs/REGISTRY.api.md +477 -0
- package/docs/REGISTRY.md +14 -0
- package/docs/references/configurations-api.md +220 -0
- package/docs/references/configurations.md +258 -0
- package/lint/browser.mjs +79 -0
- package/lint/cli.mjs +212 -0
- package/lint/lib/agent-reporter.mjs +51 -0
- package/lint/lib/fuzzy.mjs +45 -0
- package/lint/lib/registry.mjs +107 -0
- package/lint/lib/sass-linter.mjs +71 -0
- package/lint/lib/svelte-linter.mjs +116 -0
- package/lint/lib/token-linter.mjs +97 -0
- package/package.json +112 -5
- package/registry.json +3999 -0
- package/scripts/class-vocab.js +197 -0
- package/scripts/update-registry.js +934 -0
- package/skills/fractal-styler/references/fractals.md +630 -0
- package/skills/fractal-styler/references/tokens.md +226 -0
- package/skills/fractalstyler/SKILL.md +59 -0
- package/skills/fractalstyler/references/fractals.md +630 -0
- package/skills/fractalstyler/references/tokens.md +226 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Configurations API
|
|
3
|
+
description: Complete registry of every configuration variable, runtime token, attribute axis and class pattern in the current system, read verbatim from the Sass sources.
|
|
4
|
+
type: fractalstyler
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Configurations API — every variable, token, attribute and class in the system
|
|
8
|
+
|
|
9
|
+
> Companion to [`configurations.md`](configurations.md) (the *why* and *how to edit*).
|
|
10
|
+
> This document is the *what*: every Sass configuration variable in
|
|
11
|
+
> `_00_config.sass`, every CSS custom property in `_00_tokens.sass`, every
|
|
12
|
+
> `data-*` axis, and every class family the loops and partials emit — with
|
|
13
|
+
> values, defaults, consumers, and who reads what.
|
|
14
|
+
>
|
|
15
|
+
> Sources read verbatim:
|
|
16
|
+
> `_00_config.sass`, `_00_fonts.sass`, `_00_tokens.sass`, `_01_base.sass`,
|
|
17
|
+
> `_02_dimensions.sass`, `_03_typography.sass`, `_04_containers.sass`,
|
|
18
|
+
> `_05_layouts.sass`, `_06_shells.sass`, `_07_interactions.sass`,
|
|
19
|
+
> `_08_visuals.sass`, `_09_own.sass`, `index.sass`.
|
|
20
|
+
> `colorpacks.sass` and `themeplates.sass` are raw Sass variable palettes
|
|
21
|
+
> (`$green90`-style ladder constants); they emit no classes and no tokens and
|
|
22
|
+
> are intentionally not part of this API.
|
|
23
|
+
>
|
|
24
|
+
> Three kinds of API — do not mix them:
|
|
25
|
+
>
|
|
26
|
+
> | Kind | Example | Set where / when |
|
|
27
|
+
> |---|---|---|
|
|
28
|
+
> | **A. Sass source variables** | `$steps`, `$gap-families`, `$frame-ratios`, `$shadow-specs` | `src/lib/styles/_00_config.sass` — edit in place, then recompile |
|
|
29
|
+
> | **B. Runtime CSS props + `data-*` modes** | `--space-md`, `data-mode="dark"`, `data-variant="ghost"` | In the browser, live, no recompile |
|
|
30
|
+
> | **C. Classes** (loop families + partial classes) | `.gap-md`, `.grid-4`, `button[data-variant="ghost"]` | Baked at compile time from A; themed at runtime by B |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 1. A — Sass configuration (`_00_config.sass`)
|
|
35
|
+
|
|
36
|
+
The whole compile-time surface is seven variables. They are *not* `!default`
|
|
37
|
+
and have no `with (…)` override: they are the system's own config, edited in
|
|
38
|
+
place. Everything loop-generated (the entire L1 space ladder, the L3 frames,
|
|
39
|
+
the `--shade-*` elevation tokens) is expanded from them.
|
|
40
|
+
|
|
41
|
+
| Variable | Value | Consumed by | Effect of a change |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| `$steps` | `('xs' 'sm' 'md' 'bs' 'lg' 'xl' '2xl' '3xl')` — 8 steps | `_02_dimensions.sass` (gap/pad/mar loops), `scripts/update-registry.js` | Every space family is crossed with this list: a new step emits `.gap-…`/`.pad-…`/`.mar-…` rows for all 17 families — **only if** a matching `--space-<step>` token exists in `_00_tokens.sass`, else the emitted `var()` points at nothing |
|
|
44
|
+
| `$gap-families` | `(gap: gap, rgap: row-gap, cgap: column-gap)` — 3 families | `_02_dimensions.sass` | Key = class prefix, value = CSS property. Add `xgap: column-gap` and `.xgap-*` classes compile; remove a key and its classes cease to exist |
|
|
45
|
+
| `$pad-families` | `(pad: padding, px: padding-inline, py: padding-block, pt: padding-top, pr: padding-right, pb: padding-bottom, pl: padding-left)` — 7 families | `_02_dimensions.sass` | Same key→property contract. `px`/`py` are logical (inline/block, RTL-safe); named sides are physical |
|
|
46
|
+
| `$mar-families` | `(mar: margin, mx: margin-inline, my: margin-block, mt: margin-top, mr: margin-right, mb: margin-bottom, ml: margin-left)` — 7 families | `_02_dimensions.sass` | Same key→property contract. There are no negative-margin classes and no side-family aliases beyond these |
|
|
47
|
+
| `$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')` — 7 ratios | `_05_layouts.sass` (`.frame-*` loop) | Key = class suffix, value = `aspect-ratio` literal. Add `'21-9': '21 / 9'` and `.frame-21-9` compiles |
|
|
48
|
+
| `$breakpoints` | `(md: 768px, bs: 1024px, lg: 1280px)` | `_06_shells.sass` (`$md`/`$bs`/`$lg` locals → rail visibility media queries) | Renaming or moving a key moves the rail seams. **Not** a complete seam inventory: `_05_layouts` and parts of `_06` hardcode `1025px`/`1281px`/`1441px`/`769px` literals; change both together |
|
|
49
|
+
| `$shadow-specs` | 8 steps, each `(y1, b1, a1, y2, b2, a2)` — key/ambient layer pairs | `:root` loop in `_00_config.sass` | Emits the `--shade-*` token per step (`var(--shadow-rim)` + two shadow layers). The `.shadow-*` classes read the separate `--shadow-*` channel tokens instead |
|
|
50
|
+
|
|
51
|
+
The header table of the file also fixes the step ladder semantics for humans:
|
|
52
|
+
key alpha tightens `0.14 → 0.02`, ambient alpha widens `0.00 → 0.08`, key blur
|
|
53
|
+
`1 → 24px`, ambient blur `0 → 64px` as steps grow.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 2. B — runtime tokens (`_00_tokens.sass`)
|
|
58
|
+
|
|
59
|
+
Structure: two authoring mixins, `=light-theme-tokens` and
|
|
60
|
+
`=dark-theme-tokens`, are applied by `[data-mode='dark']` and
|
|
61
|
+
`[data-mode='light']` respectively. **A `data-mode` attribute on `<html>` is
|
|
62
|
+
mandatory** — the colour roles live inside the mode mixins, not `:root`;
|
|
63
|
+
without the attribute no colour token is defined. The `:root` block carries
|
|
64
|
+
everything mode-independent: fonts, type/space/radius scales, motion language,
|
|
65
|
+
control metrics, page chrome.
|
|
66
|
+
|
|
67
|
+
### 2.1 Colour roles (light / dark)
|
|
68
|
+
|
|
69
|
+
| Token | Light | Dark | Read by |
|
|
70
|
+
|---|---|---|---|
|
|
71
|
+
| `--white-fixed` | `#FFFFFF` | `#FFFFFF` | `.text-white`, `button[data-variant="primary"|"soft"|"destructive"]` |
|
|
72
|
+
| `--black-fixed` | `#171717` | `#171717` | `.text-black` |
|
|
73
|
+
| `--bg` | `#ffffff` | `#101010` | `.bg`, `button[data-variant="outline"]` |
|
|
74
|
+
| `--bg-surface` | `#FBFBF9` | `#202021` | `.surface`, `body`, `.app-header` |
|
|
75
|
+
| `--bg-panel` | `#FBFBF9` | `#1f1f20` | `.panel`, `.card` |
|
|
76
|
+
| `--bg-sunken` | `#F7F6F0` | `#1f1f20` | `.sunken` |
|
|
77
|
+
| `--bg-raised` | `#F4F4F5` | `#3a3a3a` | `.raised`, `.accordion-trigger:hover` |
|
|
78
|
+
| `--bg-extra` | `rgb(247, 248, 249)` | `#111111` | `.extra` |
|
|
79
|
+
| `--bg-input` | `#f8f9fa` | `#1c1c1d` | `.input`, `.select` |
|
|
80
|
+
| `--bg-button` | `#CED8D3` | `#171818` | reserved button fill channel |
|
|
81
|
+
| `--text-primary` | `#262627` | `#EDF2F7` | `.text-primary`, body, headings |
|
|
82
|
+
| `--text-secondary` | `#777777` | `#c7c7c7` | `.text-secondary` |
|
|
83
|
+
| `--text-muted` | `#a3a4a7` | `#7c7c7c` | `.text-muted`, `.breadcrumb-*`, `.inputs-wrapper label` |
|
|
84
|
+
| `--text-inverse` | `#ffffff` | `#7c7c7c` | `.text-inverse` — flips with mode by design |
|
|
85
|
+
| `--state-surface` | `#F2EFE9` | `#7c7c7c` | `button[data-variant="secondary"]` |
|
|
86
|
+
| `--state-hover` | `#ECE7DF` | `#4a4a4b` | `a.nav-link:hover`, `button[data-variant="secondary"|"outline"|"ghost"]:hover`, `button.icon:hover` |
|
|
87
|
+
| `--state-selected` | `#E4DFD5` | `#666668` | selected-state channel |
|
|
88
|
+
| `--border` | `#d8d8d8` | `#2f2f2f` | `.border`, `.bt`/`.br`/`.bb`/`.bl`, `.input`, `.select`, `.card`, `.accordion-item` |
|
|
89
|
+
| `--border-subtle` | `#eceaea` | `#1e1e1f` | `.border-subtle`, `.bt-sub`/… |
|
|
90
|
+
| `--border-strong` | `#cfcfcf` | `#7c7c7c` | `.border-strong`, `.bt-str`/…, `button[data-variant="secondary"]` |
|
|
91
|
+
| `--theme-color` | `#ff4400` | `#2F9E44` | `.link`, `.text-theme`, `button[data-variant="primary"]`, `a.primary`, focus rims |
|
|
92
|
+
| `--theme-color-alt` | `#be2f00` | `#36731d` | `.link:hover`, `a.primary` hover |
|
|
93
|
+
| `--theme-color-sub` | `#feb18a` | *(undefined in dark — inherits)* | `button[data-variant="soft"]` |
|
|
94
|
+
| `--success` / `--success-hover` | `#10B981` / `#059669` | `#34D399` / `#6EE7B7` | `.text-success`, `.bg-success` |
|
|
95
|
+
| `--warning` / `--warning-hover` | `#F59E0B` / `#D97706` | `#FBBF24` / `#FCD34D` | `.text-warning`, `.bg-warning` |
|
|
96
|
+
| `--danger` / `--danger-hover` | `#bf2a2a` / `#DC2626` | `#b21a1a` / `#FCA5A5` | `.text-danger`, `.bg-danger`, `button[data-variant="destructive"]` |
|
|
97
|
+
| `--info` / `--info-hover` | `#3B82F6` / `#2563EB` | `#60A5FA` / `#93C5FD` | `.text-info`, `.bg-info` |
|
|
98
|
+
| `--ring` | `rgba(0, 127, 78, 0.35)` | `rgba(16, 185, 129, 0.4)` | `.input`/`.select` focus, `.accordion-trigger` focus |
|
|
99
|
+
| `--shadow-color` | `15 13 42` | `0 0 0` | every `--shadow-*` channel (RGB triplet) |
|
|
100
|
+
| `--shadow-rim` | `transparent` | `inset 0 1px 0 rgba(255,255,255,0.09)` | prefixed onto `--shade-*` tokens |
|
|
101
|
+
| `--shadow-umbra` | *(undefined in light)* | `rgb(0 0 0 / 0.50)` | dark-only umbra channel |
|
|
102
|
+
| `--shadow-xs` … `--shadow-3xl` | two-layer key+ambient, identical values in both mixins | same | `.shadow-xs` … `.shadow-3xl` |
|
|
103
|
+
|
|
104
|
+
### 2.2 Mode-independent `:root` tokens
|
|
105
|
+
|
|
106
|
+
| Group | Tokens | Notes |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| Fonts | `--font-sans` (`"Timeless Sans", sans-serif`), `--timeless-sans`, `--timeless-grotesk` | `@font-face` families declared in `_00_fonts.sass`; `--font-mono` is *not* defined — `.mono` reads it and falls back to the browser stack until a mono face is added |
|
|
109
|
+
| Type scale | `--text-scaling: 1.25`; `--text-xs` `0.67rem`, `--text-sm` `0.75rem`, `--text-md` `0.875rem`, `--text-bs` `1rem`; `--text-lg`+ each `calc(previous * var(--text-scaling))` up to `--text-5xl` | Fixed rem rungs to base, then compounded — not fluid interpolation |
|
|
110
|
+
| Space scale | `--unit-space: 0.25rem`; `--space-xs` = unit, `sm` ×2, `md` ×3, `bs` ×4, `lg` ×6, `xl` ×8, `2xl` ×12, `3xl` ×16 | The rung set must mirror `$steps` — see §1 |
|
|
111
|
+
| Radius | `--radius-xs: 2px`, `sm: 4px`, `md: 8px`, `bs: 16px`, `lg: 32px`, `full: 9999px` | Six channels; no literal-radius ladder exists |
|
|
112
|
+
| Control metrics | `--control-h-sm: 21px`, `--control-h-bs: 32px`, `--control-h-lg: 38px`; `--height-sm: 16px`, `md: 20px`, `bs: 27px`, `lg: 32px` | `.h-*` classes read `--height-*`; `.input`/`.select` read `--height-lg` |
|
|
113
|
+
| Avatar / switch | `--avatar-size: 32px`, `--switch-w: 40px`, `--switch-h: 22px`, `--switch-thumb: 16px` | Load-bearing pairings documented in the source |
|
|
114
|
+
| Motion | `--transin1..3`, `--transout1..3` (cubic-beziers); `--speed0: 50ms`, `--speed1: 90ms`, `--speed2: 140ms`, `--speed3: 220ms`; `--motionin1..3`/`--motionout1..3` = duration + easing | Every `transition:` in the system reads these |
|
|
115
|
+
| Page chrome | `--header-height: 64px`, `--footer-height: 32px`, `--fit-height` (their sum), `--prose-clamp: 65ch`, `--content-clamp: 1600px`, `--app-inline: var(--space-lg)`, `--card-min: 240px`, `--sidebar-width`/`-left`/`-right: 280px`, `--z-sticky: 100`, `--z-raised: 200`, `--z-modal: 300`, `--z-toast: 500` | Shell geometry the L4 classes read |
|
|
116
|
+
| Pop colors | `--greenpop1..4`, `--orangepop1..6` | Named accent literals; `.orange1`…`.orange6` ink classes read the orange set |
|
|
117
|
+
| Elevation (from `_00_config.sass`) | `--shade-xs` … `--shade-3xl` | Loop-generated from `$shadow-specs` — rim + key layer + ambient layer |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 3. B — `data-*` attribute API
|
|
122
|
+
|
|
123
|
+
| Attribute | Where | Values | Effect |
|
|
124
|
+
|---|---|---|---|
|
|
125
|
+
| `data-mode` | `<html>` (mandatory) | `light` \| `dark` | Applies `=light-theme-tokens` / `=dark-theme-tokens` — the entire colour-role set |
|
|
126
|
+
| `data-shape` | `.card` | `modern` | `border-radius: var(--radius-sm)` |
|
|
127
|
+
| `data-shape` | `button` | `modern` \| `curved` \| `round` | Corner treatment: `--radius-sm` / `--radius-md` / `--radius-full` |
|
|
128
|
+
| `data-variant` | `button` | `primary` \| `secondary` \| `soft` \| `outline` \| `ghost` \| `link` \| `destructive` | Paint variant of the button quartet (`destructive` also runs the `vibrating` keyframes on hover) |
|
|
129
|
+
| `data-size` | `button` | `sm` \| `md` \| `bs` \| `lg` | Metrics variant: height + font-size channels |
|
|
130
|
+
|
|
131
|
+
Class-attribute states (not `data-*`, but part of the same runtime surface):
|
|
132
|
+
`.app-shell.open` (left rail becomes a fixed drawer below 1025px),
|
|
133
|
+
`.accordion-item.open` (expands `.accordion-content`),
|
|
134
|
+
`.ta-c.mleft`/`.ta-r.mleft` (de-centers below 1024px),
|
|
135
|
+
`.content-section.null|narrow-half|narrow-wide|narrow-full`,
|
|
136
|
+
`.input.small`/`.select.small`, `.hide-mobile`/`.hide-desktop`.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 4. C — class registry (summary)
|
|
141
|
+
|
|
142
|
+
414 concrete classes across the layers; the full inventory with properties,
|
|
143
|
+
kinds and examples is [`../REGISTRY.api.md`](../REGISTRY.api.md) (generated),
|
|
144
|
+
and the grep cheatsheet is [`../../REGISTRY.md`](../../REGISTRY.md).
|
|
145
|
+
|
|
146
|
+
| Layer | Partial | Count | Contents |
|
|
147
|
+
|---|---|---|---|
|
|
148
|
+
| L0 | `_01_base.sass` | 2 | `.bdr` debug outline, `.mode-wipe` view-transition kill switch |
|
|
149
|
+
| L1 | `_02_dimensions.sass` | 154 | 17 space families × 8 steps (136), 6 radius channels, `.wfull`/`.hfull`/`.full`, `.minw0`/`.minh0`/`.min0`, `.h-sm`…`.h-lg`, `.hfull-vh`, `.hfull-vh-fitted` |
|
|
150
|
+
| L2 | `_04_containers.sass` | 54 | `.box` + 10 alignment modifiers, `.row` + 9, `.grid` + 16, `.wrap`, `.grow`, `.shrink-0`, position set, `.scroll-y`/`.scroll-x`, `.card` (+`data-shape`), `.chip`, `.prose-section`, `.nav-section`, `.page-header`, `.inputs-wrapper`, `.checkbox-wrapper` |
|
|
151
|
+
| L3 | `_05_layouts.sass` | 23 | `.grid-1/2/3/4/6` (divisor stepping), `.cspan-1`…`.cspan-8`, `.card-grid`, `.prose`, 7 `.frame-*` (from `$frame-ratios`), `.reel` |
|
|
152
|
+
| L4 | `_06_shells.sass` | 16 | `.app-shell` (+`.open` drawer compound), `.app-header` (+`.clamped`), `.app-main` (+`.clamped`), `.main-section`, `.sidebar-left`, `.sidebar-right`, `.app-footer`, `.content-section` (+`.null`/`.narrow-half`/`.narrow-wide`/`.narrow-full`) |
|
|
153
|
+
| L5 | `_03_typography.sass` | 55 | `.text-xs`…`.text-5xl`, `.text-body*`, `.page-*`/`.card-*`/`.breadcrumb-*`/`.nav-*`/`.header-nav`, `.lh11`–`.lh16`, `.ls-*`, `.weight-300`–`.weight-800`, `.tt-*`, `.ta-*` (+`.mleft`), `.mono`, `.sans`, `.truncate` |
|
|
154
|
+
| L5 | `_07_interactions.sass` | 31 | `button` element base, `button.icon/modern/primary`, 7 `button[data-variant]`, 3 `button[data-shape]`, 4 `button[data-size]`, `.input`(+`.small`), `.select`(+`.small`), `.link`, `.link-plain`, `.link-parent` (+`.link-title` child), `a.nav-link`, `a.primary`(+`.modern`), `a.outline`(+`.modern`) |
|
|
155
|
+
| L5 | `_08_visuals.sass` | 52 | 6 bare backgrounds, 11 ink, 4 status fills, `.border`/`.border-subtle`/`.border-strong`, 12 partition borders (`.bt`/`.br`/`.bb`/`.bl` × 3 weights), 8 `.shadow-*`, `.hide-mobile`/`.hide-desktop`, `.orange1`–`.orange6` |
|
|
156
|
+
| Custom | `_09_own.sass` | 27 | `.site-logomotif`, `.logotype`, `.project-circle`, the `.mascot`/`.mascotgl` families, `.hero-name`, the `.accordion` family |
|
|
157
|
+
|
|
158
|
+
Every class entry in `registry.json` carries: `class`, `kind`
|
|
159
|
+
(`family`/`class`/`modifier`/`child`/`variant`/`compound`/`element`),
|
|
160
|
+
`layer`, `property` (declarations, with `@media` steps inline), `file`,
|
|
161
|
+
`description`, `example`.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 5. Fonts (`_00_fonts.sass`)
|
|
166
|
+
|
|
167
|
+
`@font-face` declarations only — no classes, no tokens:
|
|
168
|
+
|
|
169
|
+
| Family | File | Weight | Style |
|
|
170
|
+
|---|---|---|---|
|
|
171
|
+
| `Google Sans Flex` | `/fonts/GoogleSansFlex.woff2` | 300 800 | normal, italic |
|
|
172
|
+
| `Google Sans` | `/fonts/GoogleSans.woff2` | 400 700 | normal |
|
|
173
|
+
| `Google Sans` (italic) | `/fonts/GoogleSans-Italic.woff2` | 400 700 | italic |
|
|
174
|
+
| `Google Sans Code` | `/fonts/googlesanscode.woff2` | 300 800 | normal |
|
|
175
|
+
| `Timeless Sans` | `/fonts/TimelessSansVF.woff2` | 300 800 | normal, italic |
|
|
176
|
+
| `Timeless Grotesk` | `/fonts/TimelessSansVF.woff2` | 300 800 | normal, italic |
|
|
177
|
+
| `General Sans` | `/fonts/generalsans.woff2` | 300 700 | normal |
|
|
178
|
+
| `Satoshi` | `/fonts/satoshi.woff2` | 300 800 | normal |
|
|
179
|
+
| `Stack Sans` | `/fonts/stacksans.woff2` | 300 800 | normal |
|
|
180
|
+
|
|
181
|
+
`--font-sans` resolves to `Timeless Sans`; `Google Sans`/`General Sans`/
|
|
182
|
+
`Satoshi`/`Stack Sans` are alternate stacks a consumer can swap in.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 6. Token → defined-in → read-by index
|
|
187
|
+
|
|
188
|
+
| Token group | Defined in | Read by |
|
|
189
|
+
|---|---|---|
|
|
190
|
+
| `--shade-*` | `_00_config.sass` `:root` loop | (reserved elevation channel; `.shadow-*` classes read `--shadow-*` instead) |
|
|
191
|
+
| Colour roles (§2.1) | `_00_tokens.sass` mode mixins | L5 dress classes, interactions, body |
|
|
192
|
+
| `--font-sans`, `--timeless-*` | `_00_tokens.sass` `:root` | `body`, `.sans` |
|
|
193
|
+
| `--text-*` | `_00_tokens.sass` `:root` | `.text-*`, `.text-body*`, semantic type roles |
|
|
194
|
+
| `--space-*`, `--unit-space` | `_00_tokens.sass` `:root` | all 17 L1 space families |
|
|
195
|
+
| `--radius-*` | `_00_tokens.sass` `:root` | `.radius-*`, `.chip`, `.input`, `.select`, `.card`, `button[data-shape]`, `a.nav-link` |
|
|
196
|
+
| `--height-*`, `--control-h-*` | `_00_tokens.sass` `:root` | `.h-*`, `.input`/`.select`, `.accordion-trigger` |
|
|
197
|
+
| `--motion*`, `--speed*`, `--trans*` | `_00_tokens.sass` `:root` | every `transition:` in the system |
|
|
198
|
+
| `--header-height`, `--footer-height`, `--fit-height` | `_00_tokens.sass` `:root` | `.app-header`, `.app-main`, `.hfull-vh-fitted`, rails |
|
|
199
|
+
| `--prose-clamp`, `--content-clamp`, `--app-inline`, `--card-min` | `_00_tokens.sass` `:root` | `.prose`, `.app-header.clamped`, `.app-main.clamped`, shells, `.card-grid` |
|
|
200
|
+
| `--sidebar-width*`, `--z-*` | `_00_tokens.sass` `:root` | rails, `.app-header`, drawer |
|
|
201
|
+
| `--shadow-*`, `--shadow-color`, `--shadow-rim` | `_00_tokens.sass` mode mixins | `.shadow-*` classes, `--shade-*` composition |
|
|
202
|
+
| `--greenpop*`, `--orangepop*` | `_00_tokens.sass` `:root` | `.orange1`…`.orange6` (green set reserved) |
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 7. Regeneration pipeline
|
|
207
|
+
|
|
208
|
+
| Script | Emits |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `scripts/update-registry.js` | `registry.json`, `REGISTRY.md`, `docs/REGISTRY.md`, `skills/fractal-styler/references/fractals.md`, `skills/fractal-styler/references/tokens.md` |
|
|
211
|
+
| `scripts/class-vocab.js` | `docs/REGISTRY.api.md` (reads `registry.json`) |
|
|
212
|
+
| `lint/browser.mjs --out docs/browser.html` | `docs/browser.html` (searchable registry browser) |
|
|
213
|
+
| `scripts/build-css.js` | `dist/css/fractalstyler.css` + `.min.css` (plain-CSS distribution) |
|
|
214
|
+
| `scripts/validate-deck.js` | exit 0/1 — every class named in docs/components resolves against the compiled stylesheet |
|
|
215
|
+
| `lint/cli.mjs` | exit 0/1 — token purity and contract lint |
|
|
216
|
+
| `cli/main.mjs` (`fractalstyler` bin) | consumer scaffolds: `npx @fractaldesign/fractalstyler init\|eject\|lint` — track or eject the styles, place these docs into a project, wire the VS Code extension |
|
|
217
|
+
|
|
218
|
+
`pnpm registry` runs the first two; `pnpm browser` regenerates the browser
|
|
219
|
+
page; `pnpm check` runs the validator and the contract linter. Consumers get
|
|
220
|
+
the same registry builder after `eject` as `scripts/build-registry.mjs`.
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Configurations
|
|
3
|
+
description: How the styling system is configured — the Sass source variables, the token mixins, and the loop-generated class families, file by file, with edit recipes and pitfalls.
|
|
4
|
+
type: fractalstyler
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Configurations — how `src/lib/styles` is wired
|
|
8
|
+
|
|
9
|
+
> Companion to [`configurations-api.md`](configurations-api.md) (the *what* —
|
|
10
|
+
> every variable, token, attribute and class, verbatim).
|
|
11
|
+
> This document is the *why* and *how to edit*: what each partial owns, what
|
|
12
|
+
> each `$` variable in `_00_config.sass` drives, and what you must touch
|
|
13
|
+
> together when you change one of them.
|
|
14
|
+
|
|
15
|
+
## Golden rule for this whole document
|
|
16
|
+
|
|
17
|
+
**The `.sass` partials are the configuration.** There is no settings file, no
|
|
18
|
+
preset registry, no theme JSON — a "config change" is an edit to
|
|
19
|
+
`_00_config.sass` (loops and maps), `_00_tokens.sass` (tokens and mode
|
|
20
|
+
mixins), or a layer partial (classes), followed by regeneration:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm registry # update-registry.js + class-vocab.js → registry.json, REGISTRY.md, docs, skill refs
|
|
24
|
+
pnpm lint # token purity + contract lint
|
|
25
|
+
pnpm check # validate-deck.js + svelte-check
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Never edit the generated surfaces (`registry.json`, `REGISTRY.md`,
|
|
29
|
+
`docs/REGISTRY.api.md`, `docs/browser.html`, `dist/css/*`,
|
|
30
|
+
`skills/fractal-styler/references/*.md`) — they are overwritten on the next
|
|
31
|
+
run, by design.
|
|
32
|
+
|
|
33
|
+
## Overview
|
|
34
|
+
|
|
35
|
+
### Three moving parts
|
|
36
|
+
|
|
37
|
+
1. **`_00_config.sass`** — Sass variables (`$steps`, the family maps,
|
|
38
|
+
`$frame-ratios`, `$breakpoints`, `$shadow-specs`). The loop half of the
|
|
39
|
+
class system is *generated* from these.
|
|
40
|
+
2. **`_00_tokens.sass`** — CSS custom properties: two colour-mode mixins
|
|
41
|
+
(`=light-theme-tokens` / `=dark-theme-tokens`, applied by
|
|
42
|
+
`[data-mode='light']` / `[data-mode='dark']`) plus one mode-independent
|
|
43
|
+
`:root` block (type, space, radius, motion, control metrics, page chrome).
|
|
44
|
+
3. **The layer partials** (`_01`…`_09`) — hand-authored classes. Every class
|
|
45
|
+
rides a token; literals exist only where a token cannot (1px hairlines,
|
|
46
|
+
aspect ratios, breakpoint px).
|
|
47
|
+
|
|
48
|
+
`index.sass` composes them in cascade order: config and tokens first, then
|
|
49
|
+
`_01`…`_09`. `colorpacks.sass` and `themeplates.sass` are raw Sass colour
|
|
50
|
+
palettes consumed by hand-picked tokens; they emit no classes and no tokens
|
|
51
|
+
and are never part of the class API.
|
|
52
|
+
|
|
53
|
+
### Where to look while reading this doc
|
|
54
|
+
|
|
55
|
+
| Task | File to open |
|
|
56
|
+
|---|---|
|
|
57
|
+
| Change what the loops generate (steps, families, frames, shadow specs) | `_00_config.sass` |
|
|
58
|
+
| Change colours, add a colour role, dark mode | `_00_tokens.sass` |
|
|
59
|
+
| Add a font face | `_00_fonts.sass` |
|
|
60
|
+
| Add a spacing/radius/size utility | `_02_dimensions.sass` |
|
|
61
|
+
| Add type roles/modifiers | `_03_typography.sass` |
|
|
62
|
+
| Add a container or alignment rule | `_04_containers.sass` |
|
|
63
|
+
| Add a grid/layout/frame/reel rule | `_05_layouts.sass` |
|
|
64
|
+
| Add shell scaffolding | `_06_shells.sass` |
|
|
65
|
+
| Add inputs/links/buttons | `_07_interactions.sass` |
|
|
66
|
+
| Add backgrounds/ink/borders/shadows/visibility | `_08_visuals.sass` |
|
|
67
|
+
| Project-specific extensions | `_09_own.sass` |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 1. `_00_config.sass` — the loop generator
|
|
72
|
+
|
|
73
|
+
### 1.1. `$steps` — the step vocabulary
|
|
74
|
+
|
|
75
|
+
`('xs' 'sm' 'md' 'bs' 'lg' 'xl' '2xl' '3xl')` — eight steps. Every space
|
|
76
|
+
family (gap, pad, mar and their sides) is crossed with this list by the
|
|
77
|
+
`@each` loops in `_02_dimensions.sass`, so the entire L1 ladder is
|
|
78
|
+
`17 families × 8 steps = 136 classes`.
|
|
79
|
+
|
|
80
|
+
Editing it is a **two-file change**: a step without a matching
|
|
81
|
+
`--space-<step>` token in `_00_tokens.sass` compiles, but every class for it
|
|
82
|
+
points at an undefined `var()` — the declaration is dropped silently at
|
|
83
|
+
computed-value time. A token without the step is dead weight (no classes).
|
|
84
|
+
Change both, then run `pnpm registry`.
|
|
85
|
+
|
|
86
|
+
### 1.2. The family maps — `$gap-families`, `$pad-families`, `$mar-families`
|
|
87
|
+
|
|
88
|
+
Each map is `class prefix → CSS property`:
|
|
89
|
+
|
|
90
|
+
```sass
|
|
91
|
+
$gap-families: (gap: gap, rgap: row-gap, cgap: column-gap)
|
|
92
|
+
$pad-families: (pad: padding, px: padding-inline, py: padding-block, pt: padding-top, pr: padding-right, pb: padding-bottom, pl: padding-left)
|
|
93
|
+
$mar-families: (mar: margin, mx: margin-inline, my: margin-block, mt: margin-top, mr: margin-right, mb: margin-bottom, ml: margin-left)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`_02_dimensions.sass` does nothing but loop these maps over `$steps`. The
|
|
97
|
+
`px`/`py`/`mx`/`my` keys are logical (inline/block — RTL-safe); the named
|
|
98
|
+
sides (`pt`, `ml`, …) are physical. To add a family: add the map entry, and
|
|
99
|
+
the classes compile on the next build — no other edit needed. To retire one:
|
|
100
|
+
delete the entry and every class it generated disappears from the compiled
|
|
101
|
+
CSS (grep your markup first).
|
|
102
|
+
|
|
103
|
+
Note the naming drift this system retired: `.pad-x-*`/`.marg-*`/`.square-*`
|
|
104
|
+
literal ladders and `-mob`/`-desk` responsive bands **do not exist** — the
|
|
105
|
+
current families are the ones above, in one base band, all token-routed.
|
|
106
|
+
|
|
107
|
+
### 1.3. `$frame-ratios` — aspect-ratio media boxes
|
|
108
|
+
|
|
109
|
+
`('16-9': '16 / 9', …)` — key becomes the `.frame-<key>` suffix, value the
|
|
110
|
+
`aspect-ratio` literal. `_05_layouts.sass` loops it: each `.frame-*` gets
|
|
111
|
+
`aspect-ratio`, `overflow: hidden`, `position: relative`, and
|
|
112
|
+
`img, video, iframe, svg` fill-and-cover rules. Add a ratio, get a class;
|
|
113
|
+
that is the whole mechanism.
|
|
114
|
+
|
|
115
|
+
### 1.4. `$breakpoints` — the named seams
|
|
116
|
+
|
|
117
|
+
`(md: 768px, bs: 1024px, lg: 1280px)`. Consumed via `map.get` by
|
|
118
|
+
`_06_shells.sass` for the rail visibility seams. **This is not the complete
|
|
119
|
+
seam inventory**: `_05_layouts.sass` (grid stepping) and the `narrow-*`
|
|
120
|
+
widths in `_06_shells.sass` hardcode `769px`/`1025px`/`1281px`/`1441px`
|
|
121
|
+
literals. If you move a seam, move both the map entry and the literals —
|
|
122
|
+
grep `min-width` / `max-width` across the partials before assuming the map
|
|
123
|
+
is the only place a seam lives.
|
|
124
|
+
|
|
125
|
+
### 1.5. `$shadow-specs` — the elevation ladder
|
|
126
|
+
|
|
127
|
+
Eight steps of `(y1, b1, a1, y2, b2, a2)` pairs. The `:root` loop at the
|
|
128
|
+
bottom of `_00_config.sass` composes each into a `--shade-<step>` token:
|
|
129
|
+
`var(--shadow-rim)` + a tight key layer + a wide ambient layer. The header
|
|
130
|
+
comment table (key alpha, ambient alpha, key blur, ambient blur per step) is
|
|
131
|
+
the design intent — keep it in sync with the numbers.
|
|
132
|
+
|
|
133
|
+
The `.shadow-*` **classes** do not read these — they read the
|
|
134
|
+
`--shadow-<step>` channel tokens defined in the mode mixins of
|
|
135
|
+
`_00_tokens.sass`. Two ladders, two jobs: `--shade-*` is the composed
|
|
136
|
+
rim+key+ambient recipe, `--shadow-*` is the plain channel the utilities
|
|
137
|
+
apply.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 2. `_00_tokens.sass` — the token contract
|
|
142
|
+
|
|
143
|
+
### 2.1. Colour modes are mixins, and `data-mode` is mandatory
|
|
144
|
+
|
|
145
|
+
```sass
|
|
146
|
+
=light-theme-tokens
|
|
147
|
+
--bg: #ffffff
|
|
148
|
+
…
|
|
149
|
+
=dark-theme-tokens
|
|
150
|
+
--bg: #101010
|
|
151
|
+
…
|
|
152
|
+
|
|
153
|
+
[data-mode='dark']
|
|
154
|
+
+dark-theme-tokens
|
|
155
|
+
[data-mode='light']
|
|
156
|
+
+light-theme-tokens
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The colour roles live **only** inside these mixins. Unlike the old system
|
|
160
|
+
there is no marker-free light default: a page without a `data-mode`
|
|
161
|
+
attribute on `<html>` has no colour tokens defined at all. Whatever renders
|
|
162
|
+
the page (Svelte layout, static template) must stamp
|
|
163
|
+
`data-mode="light"` or `data-mode="dark"`.
|
|
164
|
+
|
|
165
|
+
### 2.2. The mode-independent `:root` block
|
|
166
|
+
|
|
167
|
+
Everything else: `--font-sans` and the timeless stacks, `--text-scaling` plus
|
|
168
|
+
the type rungs, `--unit-space` plus the space rungs, the six `--radius-*`
|
|
169
|
+
channels, control/avatar/switch metrics, the motion language
|
|
170
|
+
(`--speed*`, `--trans*`, `--motion*`), page chrome (`--header-height`,
|
|
171
|
+
`--footer-height`, `--fit-height`, `--prose-clamp`, `--content-clamp`,
|
|
172
|
+
`--app-inline`, `--card-min`, `--sidebar-width*`, `--z-*`), control heights
|
|
173
|
+
and the pop colors.
|
|
174
|
+
|
|
175
|
+
Known gap, kept honest: `.mono` reads `--font-mono`, which no partial defines
|
|
176
|
+
yet — it falls back to the browser mono stack until a mono face lands in
|
|
177
|
+
`_00_fonts.sass` and a `--font-mono` token is added here.
|
|
178
|
+
|
|
179
|
+
### 2.3. Editing rules
|
|
180
|
+
|
|
181
|
+
- **Add a colour role**: add it to *both* mixins (or deliberately to one —
|
|
182
|
+
see `--theme-color-sub`, light-only, and `--shadow-umbra`, dark-only — and
|
|
183
|
+
say so in a comment).
|
|
184
|
+
- **Add a step token**: mirror `$steps` (see §1.1).
|
|
185
|
+
- **Never** hardcode a hex or pixel in a layer partial that a token covers —
|
|
186
|
+
the lint (`lint/cli.mjs`) enforces token purity.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 3. The layer partials (`_01`–`_09`) — what each owns
|
|
191
|
+
|
|
192
|
+
| Partial | Layer | Owns | Conventions to keep |
|
|
193
|
+
|---|---|---|---|
|
|
194
|
+
| `_01_base.sass` | L0 | Element resets, view-transition defaults, `.bdr` | No classes beyond the debug outline and `.mode-wipe` |
|
|
195
|
+
| `_02_dimensions.sass` | L1 | The three `@each` family loops, radius channels, full/zero sizing, `.h-*` control heights, viewport heights | Classes here are generated or one-liners; no component logic |
|
|
196
|
+
| `_03_typography.sass` | L5 | `.text-*` primitives, semantic roles (`.page-title`, `.nav-link`…), `.lh*`, `.ls-*`, `.weight-*`, `.tt-*`, `.ta-*` (+`.mleft`), `.mono`, `.sans`, `.truncate` | Roles compose tokens; `.mleft` is the only media-query modifier |
|
|
197
|
+
| `_04_containers.sass` | L2 | `.box`/`.row`/`.grid` + nested alignment modifiers, `.wrap`, `.grow`, `.shrink-0`, positions, `.scroll-y`/`.scroll-x`, `.card`, `.chip`, section wrappers | Alignment modifiers **nest under their base** — never standalone; x* is always the horizontal/inline axis, y* the vertical/block axis |
|
|
198
|
+
| `_05_layouts.sass` | L3 | `.grid-1/2/3/4/6` (divisor stepping), `.cspan-*`, `.card-grid`, `.prose`, `.frame-*` loop, `.reel` | Grids are pure stepping — no default gap; compose `.gap-*`. The reel's `> *` is the system's one sanctioned child combinator (matched literally in `scripts/validate-deck.js`) |
|
|
199
|
+
| `_06_shells.sass` | L4 | `.app-shell` canon, header/main/footer, role-bound rails, `.content-section` widths | Rails are display:none below their seam and resurrected by media queries — left rail becomes an off-canvas drawer via `.app-shell.open` below 1025px |
|
|
200
|
+
| `_07_interactions.sass` | L5 | `.input`/`.select`, the link family, the `button` element base + its `data-variant`/`data-shape`/`data-size` axes, `a.primary`/`a.outline` | Buttons are styled via the bare `button` element plus attributes — there is no `.button` class |
|
|
201
|
+
| `_08_visuals.sass` | L5 | Bare backgrounds, ink, status fills, the partition-law border set, `.shadow-*`, `.hide-mobile`/`.hide-desktop`, `.orange1`–`.orange6` | Bare dress before compositions; components own their own hover states — there is no standalone `.hover`/`.transition` |
|
|
202
|
+
| `_09_own.sass` | Custom | The mascot families, `.hero-name`, the accordion, logotype bits | The sanctioned escape hatch; anything project-specific that cannot be expressed canonically |
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 4. Quick-edit cookbook
|
|
207
|
+
|
|
208
|
+
**Add a spacing step** (e.g. a hypothetical `4xl`):
|
|
209
|
+
1. `$steps` in `_00_config.sass` → append `'4xl'`.
|
|
210
|
+
2. `--space-4xl` in `_00_tokens.sass` `:root` → define the value.
|
|
211
|
+
3. `pnpm registry` — 17 new classes appear per family map.
|
|
212
|
+
|
|
213
|
+
**Add a space family** (e.g. `.igap-*` for inline gaps):
|
|
214
|
+
1. Add `igap: column-gap` to the right family map in `_00_config.sass`.
|
|
215
|
+
2. Nothing else — the loop and the registry pick it up.
|
|
216
|
+
|
|
217
|
+
**Add a frame ratio**:
|
|
218
|
+
1. Add `'21-9': '21 / 9'` to `$frame-ratios`.
|
|
219
|
+
2. `pnpm registry` — `.frame-21-9` compiles and is indexed.
|
|
220
|
+
|
|
221
|
+
**Move a breakpoint seam**:
|
|
222
|
+
1. Edit `$breakpoints` in `_00_config.sass`.
|
|
223
|
+
2. Grep `min-width|max-width` in `_05_layouts.sass` and `_06_shells.sass` for
|
|
224
|
+
hardcoded literals (769/1025/1281/1441) and move them together.
|
|
225
|
+
|
|
226
|
+
**Add a colour role**:
|
|
227
|
+
1. Add the token to `=light-theme-tokens` **and** `=dark-theme-tokens` in
|
|
228
|
+
`_00_tokens.sass`.
|
|
229
|
+
2. If it needs a class: bare one-liner in `_08_visuals.sass`
|
|
230
|
+
(`.text-<role>` / `.bg-<role>`), following the existing rows.
|
|
231
|
+
|
|
232
|
+
**Add any other class**:
|
|
233
|
+
1. Put it in the partial that owns its layer (see §3) — never invent a new
|
|
234
|
+
partial without approval.
|
|
235
|
+
2. Document it with a trailing `// …` on the selector line — that comment is
|
|
236
|
+
what `scripts/update-registry.js` lifts into the registry description.
|
|
237
|
+
3. `pnpm registry && pnpm lint && pnpm check`.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 5. Pitfalls that have bitten before
|
|
242
|
+
|
|
243
|
+
- **A `$steps` entry without its token** compiles clean and silently emits
|
|
244
|
+
classes that do nothing (undefined `var()`). Always change both files.
|
|
245
|
+
- **Editing generated files** (`registry.json`, `REGISTRY.md`,
|
|
246
|
+
`docs/REGISTRY.api.md`) — overwritten on the next `pnpm registry`.
|
|
247
|
+
- **Assuming `$breakpoints` is the whole seam story** — the grid and
|
|
248
|
+
narrow-width literals live outside the map.
|
|
249
|
+
- **Expecting a marker-free light mode** — colour tokens only exist under a
|
|
250
|
+
`data-mode` attribute; no attribute means no colours.
|
|
251
|
+
- **Naming drift**: `.pad-x-*`, `.marg-*`, `.square-*`, `.radius-0`,
|
|
252
|
+
`-mob`/`-desk` bands and negative-margin classes are gone. The living
|
|
253
|
+
vocabulary is `.pad`/`.px`/`.py`/`.pt`/`.pr`/`.pb`/`.pl`,
|
|
254
|
+
`.mar`/`.mx`/`.my`/`.mt`/`.mr`/`.mb`/`.ml`, the six radius channels — all
|
|
255
|
+
across the eight steps.
|
|
256
|
+
- **A child combinator** (`> *`) anywhere but the reel trips
|
|
257
|
+
`scripts/validate-deck.js`. That guard is deliberate; a second exception
|
|
258
|
+
should be a conscious edit there.
|
package/lint/browser.mjs
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// fractalstyler browser — interactive styling classes registry browser.
|
|
4
|
+
//
|
|
5
|
+
// Opens the class registry as a searchable, zero-dependency browser page.
|
|
6
|
+
// Usage:
|
|
7
|
+
// node lint/browser.mjs # opens in default browser
|
|
8
|
+
// node lint/browser.mjs --out <f> # writes HTML to specific file
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
import { execFileSync } from 'node:child_process';
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import os from 'node:os';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { renderBrowserHtml } from '../scripts/class-vocab.js';
|
|
17
|
+
|
|
18
|
+
const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
19
|
+
const REGISTRY_PATH = path.join(PKG_ROOT, 'registry.json');
|
|
20
|
+
|
|
21
|
+
function usage() {
|
|
22
|
+
console.log(`Usage:
|
|
23
|
+
fractalstyler browser [dir] # open registry browser
|
|
24
|
+
Options:
|
|
25
|
+
--registry <f> registry.json override
|
|
26
|
+
--out <file> write HTML to file instead of opening`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function openInBrowser(file) {
|
|
30
|
+
const opener =
|
|
31
|
+
process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open';
|
|
32
|
+
const args = process.platform === 'win32' ? ['/c', 'start', '', file] : [file];
|
|
33
|
+
try {
|
|
34
|
+
execFileSync(opener, args, { stdio: 'ignore' });
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function main() {
|
|
42
|
+
const args = process.argv.slice(2);
|
|
43
|
+
let out;
|
|
44
|
+
let registryOverride;
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < args.length; i++) {
|
|
47
|
+
if (args[i] === '--out') out = args[++i];
|
|
48
|
+
else if (args[i] === '--registry') registryOverride = args[++i];
|
|
49
|
+
else if (args[i] === '--help' || args[i] === '-h') {
|
|
50
|
+
usage();
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const regFile = registryOverride ? path.resolve(registryOverride) : REGISTRY_PATH;
|
|
56
|
+
if (!fs.existsSync(regFile)) {
|
|
57
|
+
console.error(`Registry file not found at: ${regFile}. Run "pnpm registry" first.`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const data = JSON.parse(fs.readFileSync(regFile, 'utf8'));
|
|
62
|
+
const html = renderBrowserHtml(data);
|
|
63
|
+
|
|
64
|
+
const target = out ? path.resolve(out) : path.join(os.tmpdir(), `fractalstyler-browser-${process.pid}.html`);
|
|
65
|
+
fs.writeFileSync(target, html, 'utf8');
|
|
66
|
+
|
|
67
|
+
if (out) {
|
|
68
|
+
console.log(`✔ Registry browser page written to: ${target}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(`✔ Registry: ${regFile}`);
|
|
73
|
+
console.log(`✔ Opening browser page: ${target}`);
|
|
74
|
+
if (!openInBrowser(target)) {
|
|
75
|
+
console.log(`Open file in your browser: file://${target}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
main();
|