fractalstyler2 0.4.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +114 -95
  2. package/dist/cli.js +105 -25
  3. package/dist/components/Accordion.svelte +44 -0
  4. package/dist/components/Accordion.svelte.d.ts +23 -0
  5. package/dist/components/AccordionItem.svelte +44 -0
  6. package/dist/components/AccordionItem.svelte.d.ts +21 -0
  7. package/dist/components/AppShell.svelte +86 -0
  8. package/dist/components/AppShell.svelte.d.ts +39 -0
  9. package/dist/components/ColorPicker.svelte +38 -0
  10. package/dist/components/ColorPicker.svelte.d.ts +6 -0
  11. package/dist/components/Hero.svelte +14 -0
  12. package/dist/components/Hero.svelte.d.ts +9 -0
  13. package/dist/components/LayoutPicker.svelte +34 -0
  14. package/dist/components/LayoutPicker.svelte.d.ts +6 -0
  15. package/dist/components/MotionPicker.svelte +34 -0
  16. package/dist/components/MotionPicker.svelte.d.ts +6 -0
  17. package/dist/components/PageShell.svelte +14 -0
  18. package/dist/components/PageShell.svelte.d.ts +9 -0
  19. package/dist/components/PageSplit.svelte +26 -0
  20. package/dist/components/PageSplit.svelte.d.ts +18 -0
  21. package/dist/components/ShapePicker.svelte +37 -0
  22. package/dist/components/ShapePicker.svelte.d.ts +6 -0
  23. package/dist/css/fractalstyler.css +10643 -0
  24. package/dist/css/fractalstyler.min.css +2 -0
  25. package/dist/index.d.ts +18 -6
  26. package/dist/index.js +30 -12
  27. package/dist/mcp/schemas/compile_fractals.json +3 -3
  28. package/dist/mcp/schemas/css_to_fractals.json +1 -1
  29. package/dist/mcp/schemas/generate_component.json +2 -2
  30. package/dist/mcp/schemas/instructions.md +41 -14
  31. package/dist/mcp/schemas/list_fractals.json +9 -7
  32. package/dist/mcp/schemas/validate_recipe.json +2 -2
  33. package/dist/mcp/server.d.ts +1 -1
  34. package/dist/mcp/server.js +334 -237
  35. package/dist/presets.core.d.ts +42 -0
  36. package/dist/presets.core.js +190 -0
  37. package/dist/presets.svelte.d.ts +6 -0
  38. package/dist/presets.svelte.js +21 -0
  39. package/dist/styles/_00_presets.sass +111 -0
  40. package/dist/styles/_00_themes.sass +1061 -0
  41. package/dist/styles/_00_tokens.sass +57 -17
  42. package/dist/styles/_01_config.sass +77 -70
  43. package/dist/styles/_02_dimensions.sass +174 -0
  44. package/dist/styles/_03_containers.sass +105 -0
  45. package/dist/styles/_04_layouts.sass +100 -0
  46. package/dist/styles/_05_shells.sass +374 -0
  47. package/dist/styles/_06_visuals.sass +236 -0
  48. package/dist/styles/_07_interactions.sass +112 -0
  49. package/dist/styles/_08_own.sass +4 -0
  50. package/dist/styles/canonical-markups.md +160 -0
  51. package/dist/styles/index.sass +11 -18
  52. package/dist/themes.d.ts +7 -0
  53. package/dist/themes.js +45 -0
  54. package/dist/version.d.ts +1 -0
  55. package/dist/version.js +2 -0
  56. package/package.json +26 -15
  57. package/plugin.json +11 -10
  58. package/registry.json +2002 -0
  59. package/skills/fractal-styler/SKILL.md +137 -50
  60. package/skills/fractal-styler/references/fractals.md +416 -28
  61. package/skills/fractal-styler/references/tokens.md +110 -36
  62. package/dist/styles/_02_fonts.sass +0 -13
  63. package/dist/styles/_03_responsive.sass +0 -31
  64. package/dist/styles/_04_atoms.sass +0 -155
  65. package/dist/styles/_05_molecules.sass +0 -97
  66. package/dist/styles/_06_recipes.sass +0 -189
  67. package/dist/styles/_07_base.sass +0 -44
  68. package/dist/styles/_08_blocks.sass +0 -433
  69. package/dist/styles/_09_utilities.sass +0 -208
  70. package/dist/styles/_10_layouts.sass +0 -373
  71. package/dist/styles/_11_own.sass +0 -21
  72. package/dist/styles/_fractals.sass +0 -14
  73. package/skills/style-migration/SKILL.md +0 -45
  74. package/templates/_00_tokens.sass +0 -136
  75. package/templates/_01_config.sass +0 -66
  76. package/templates/_02_fonts.sass +0 -13
  77. package/templates/_03_responsive.sass +0 -31
  78. package/templates/_04_atoms.sass +0 -155
  79. package/templates/_05_molecules.sass +0 -97
  80. package/templates/_06_recipes.sass +0 -189
  81. package/templates/_07_base.sass +0 -44
  82. package/templates/_08_blocks.sass +0 -433
  83. package/templates/_09_utilities.sass +0 -207
  84. package/templates/_10_layouts.sass +0 -373
  85. package/templates/_11_own.sass +0 -21
  86. package/templates/_fractals.sass +0 -14
  87. package/templates/index.sass +0 -18
package/README.md CHANGED
@@ -1,145 +1,164 @@
1
- # fractalstyler2
1
+ # Fractalstyler
2
2
 
3
- A fractal-composition styling system for SvelteKit. **Every style unit is a SASS mixin ("a fractal"), and components and layouts are recipes of smaller fractals.**
3
+ > **A composition styling system plain CSS or editable SASS with a fluid design token engine and a runtime preset architecture.**
4
+ >
5
+ > The class registry is the public API. You compose in markup; you do not write a stylesheet.
4
6
 
5
- Use it either as a **shadcn-style scaffolder** (copying editable SASS directly into your project's `src/lib/styles`) or as a **direct package dependency**.
7
+ [![npm version](https://img.shields.io/npm/v/fractalstyler2.svg?style=flat-square)](https://www.npmjs.com/package/fractalstyler2)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)
6
9
 
7
- ---
10
+ Every styling decision in `Fractalstyler` is a modular fractal: unitary tokens, dimensions, and container primitives that compose into higher layers while retaining mathematical harmony, responsiveness, and zero-runtime bloat.
8
11
 
9
- ## Quickstart (shadcn-style Scaffolding)
12
+ Ships two ways, from one source:
10
13
 
11
- Scaffold the complete, customizable SASS design system directly into your project:
14
+ | | | |
15
+ |:--|:--|:--|
16
+ | **Plain CSS** | one file, no toolchain | `npx fractalstyler2 init --css` |
17
+ | **SASS** | editable partials, retunable generators | `npx fractalstyler2 init` |
12
18
 
13
- ```bash
14
- # Scaffold into src/lib/styles
15
- npx fractalstyler2 init
19
+ The compiled CSS is emitted from the same partials the SASS path scaffolds, so
20
+ the two are byte-identical. SASS buys exactly one thing: the ability to retune
21
+ the generators — the literal ladder and the responsive seam — before compiling.
22
+
23
+ ---
16
24
 
17
- # Or using pnpm
18
- pnpm --package=fractalstyler2 dlx fractalstyler2 init
25
+ ## The Fractal Mental Model
26
+
27
+ ```
28
+ Tokens (L0) ──► Dimensions (L1) ──► Containers (L2) ──► Layouts (L3) ──► Shells (L4) ──► Visuals (L5)
19
29
  ```
20
30
 
21
- ### Next Steps in Your Project
31
+ 1. **Tokens (L0)**: 31 semantic colour tokens, 41 themes, and fluid Utopia typography/space scales.
32
+ 2. **Dimensions (L1)**: 17 space families (`.gap-sm`, `.pad-md`, `.marg-xs`), negative margins (`.marg--sm`), literal pixel utilities (`.w-120`, `.radius-8`), and `-mob` / `-desk` bands.
33
+ 3. **Containers (L2)**: Flexible flow primitives (`.box`, `.row`, `.grid`) with strict physical X/Y axis alignment (`.xcenter`, `.ycenter`, `.xbetween`).
34
+ 4. **Layouts (L3)**: Gridding Golden Rules ($3\to1$, $4\to2\to1$, $6\to3\to2\to1$), `.card-grid`, reading measure `.prose`, and scroll-snap `.reel`.
35
+ 5. **Shells (L4)**: Full application frames (`.app-shell`, `.app-header`, `.app-main`, `.sidebar-left`, `.sidebar-right`, `.page-split`, `.drawer`, `.dialog`, `.popover`, `.accordion`).
36
+ 6. **Visuals & Interactions (L5)**: Bare surfaces (`.bg`, `.surface`, `.raised`), text inks, 1px hairline borders, the button quartet (`.primary`, `.ghost`, `.active`, `.is-icon`), and form controls (`.input`, `.select`).
22
37
 
23
- **1. Import the stylesheet once globally in your root `src/routes/+layout.svelte`:**
24
- ```svelte
25
- <script>
26
- import '$lib/styles/index.sass';
27
- </script>
38
+ ---
39
+
40
+ ## Quick Start
41
+
42
+ ### Plain CSS — no build step
43
+
44
+ Most projects want this. One stylesheet, nothing to configure:
45
+
46
+ ```bash
47
+ npx fractalstyler2 init --css
28
48
  ```
29
49
 
30
- **2. Compose your components using the pure mixin barrel:**
31
- ```svelte
32
- <div class="pricing-card">
33
- <span class="badge" data-status="released">Pro Plan</span>
34
- <h3>$29/mo</h3>
35
- <p>Full access to all fractal components and layouts.</p>
36
- </div>
37
-
38
- <style lang="sass">
39
- @use '$lib/styles/fractals' as *
40
-
41
- .pricing-card
42
- +surface(surface, m, 6, md) // bg + border + radius(6px) + padding + shadow
43
- +stack(s, start) // flex-column + gap(s) + top-anchored flow
44
- </style>
50
+ ```html
51
+ <link rel="stylesheet" href="/src/styles/fractalstyler.css" />
45
52
  ```
46
53
 
47
- ---
54
+ Themes and the four preset axes are classes and attributes, so they work with
55
+ JavaScript disabled:
48
56
 
49
- ## Alternative: Direct Package Dependency
57
+ ```html
58
+ <html class="theme-night-dark" data-mode="dark" data-shape="sharp">
59
+ ```
50
60
 
51
- If you prefer importing from `node_modules` instead of scaffolding local files:
61
+ For runtime switching and zero-flicker restore, there is a framework-free
62
+ runtime at `fractalstyler2/presets`:
63
+
64
+ ```js
65
+ import { initPresets, setPreset, getPresetScript } from 'fractalstyler2/presets';
66
+ initPresets();
67
+ ```
68
+
69
+ ### SASS — if you want to retune the generators
52
70
 
53
71
  ```bash
54
- pnpm add fractalstyler2
55
- pnpm add -D sass
72
+ pnpm add -D sass fractalstyler2
73
+ npx fractalstyler2 init
56
74
  ```
57
75
 
58
- ### Use from `node_modules`:
76
+ In your root layout (`src/routes/+layout.svelte`):
59
77
 
60
78
  ```svelte
61
- <script>
62
- import 'fractalstyler2/styles';
79
+ <script lang="ts">
80
+ import 'fractalstyler2/styles';
81
+ import { initPresets, getPresetScript } from 'fractalstyler2';
82
+ import { onMount } from 'svelte';
83
+
84
+ let { children } = $props();
85
+
86
+ onMount(() => {
87
+ initPresets();
88
+ });
63
89
  </script>
64
90
 
65
- <style lang="sass">
66
- @use 'fractalstyler2/fractals' as *
91
+ <svelte:head>
92
+ {@html `<script>${getPresetScript()}</script>`}
93
+ </svelte:head>
67
94
 
68
- .card
69
- +surface(surface, s, 6)
70
- +stack(s)
71
- </style>
95
+ {@render children()}
72
96
  ```
73
97
 
74
98
  ---
75
99
 
76
- ## The Physical Scale Hierarchy
100
+ ## The Four Preset Tuning Axes
77
101
 
78
- The design system files in `src/lib/styles/` are numbered sequentially from indivisible math to page layouts:
102
+ Fractalstyler includes a runtime tuning engine governing four fundamental aesthetic axes:
79
103
 
104
+ ```html
105
+ <!-- HTML root attribute contract -->
106
+ <html data-layout="tight" data-shape="sharp" data-color="clean" data-motion="active">
80
107
  ```
81
- index.sass ──► Master Entrypoint (Forwards fractals + loads CSS cascade)
82
- _fractals.sass ──► Pure Mixin Barrel (Emits 0 bytes CSS)
83
-
84
- ── Phase 1: Pure Functions & Math (Zero CSS Emitted) ─────────────────────────
85
- _00_tokens.sass ──► Raw CSS custom properties on :root & theme modes
86
- _01_config.sass ──► Scales, maps, resolvers (space, radius, surface, ink)
87
- _02_fonts.sass ──► Local @font-face declarations
88
- _03_responsive.sass ──► Breakpoint helpers (+at, +below, +between)
89
- _04_atoms.sass ──► Single-decision primitives (+box, +row, +bg, +ink, +pad, +gap)
90
- _05_molecules.sass ──► Compositions of atoms (+stack, +cluster, +surface, +cols)
91
- _06_recipes.sass ──► Macro component archetypes (=control, =select, =card, =partition)
92
-
93
- ── Phase 2: CSS Cascade Emitters (Loaded once globally by index.sass) ─────────
94
- _07_base.sass ──► Global HTML element resets
95
- _08_blocks.sass ──► Semantic component classes (.surface, .panel, .select, .badge)
96
- _09_utilities.sass ──► 1:1 atomic markup projections (.pad-*, .pad-top-*, .gap-*, .hide-desktop)
97
- _10_layouts.sass ──► Page layout templates (.docs, .card-grid, .hero, .holy-grail)
98
- _11_own.sass ──► Bespoke local project overrides
99
- ```
108
+
109
+ | Axis | Options | Description |
110
+ |:---|:---|:---|
111
+ | **Layout** (`data-layout`) | `tight` · `comfortable` *(default)* · `sprawling` | Scales gap and padding density factors independently. |
112
+ | **Shape** (`data-shape`) | `round` · `curved` *(default)* · `pro` · `sharp` | Controls corner radii scale down to strict $0\text{px}$ sharp edges. |
113
+ | **Color** (`data-color`) | `clean` · `general` *(default)* · `vibrant` | Adjusts background contrasts and surface elevations. |
114
+ | **Motion** (`data-motion`) | `reduced` · `heavy` · `active` *(default)* · `springy` | Sets transition durations and spring easing physics. |
100
115
 
101
116
  ---
102
117
 
103
- ## The Strict 21-Token Contract
118
+ ## The Complete Grepable Registry
104
119
 
105
- All themes and surface colors resolve from the 21 variables defined in `_00_tokens.sass`:
120
+ The master class dictionary is documented in [**`REGISTRY.md`**](./REGISTRY.md) and programmatically exposed via [`registry.json`](./registry.json).
106
121
 
107
- - **Surfaces**: `--bg`, `--bg-surface`, `--bg-raised`, `--bg-panel`, `--bg-footer`, `--bg-popover`, `--bg-dialog`, `--bg-terminal`, `--bg-input`, `--bg-canvas`
108
- - **Text & Ink**: `--text-primary`, `--text-secondary`, `--text-muted`, `--text-inverse`
109
- - **States & Feedback**: `--state-hover`, `--state-hover-subtle`, `--state-selected`
110
- - **Borders & Accents**: `--border`, `--border-subtle`, `--theme-color`, `--theme-color-alt` *(aliased to `--theme`)*
122
+ To regenerate the registry after editing stylesheet files:
111
123
 
112
- ---
124
+ ```bash
125
+ pnpm registry
126
+ ```
113
127
 
114
- ## Color Mode
128
+ ---
115
129
 
116
- Light is the marker-free default (SSR-safe). Dark mode is activated via `prefers-color-scheme: dark` or explicitly via `data-mode="dark"` on `<html>`:
130
+ ## Documentation Index
117
131
 
118
- ```js
119
- import { toggleMode, setMode } from 'fractalstyler2';
120
- toggleMode(); // flip light/dark
121
- setMode('dark'); // force mode
122
- ```
132
+ | Chapter | Document | Scope & Contents |
133
+ |:---|:---|:---|
134
+ | **01** | [**`01-introduction.md`**](./docs/01-introduction.md) | Philosophy, $L0 \to L5$ mental model, and design invariants. |
135
+ | **02** | [**`02-getting-started.md`**](./docs/02-getting-started.md) | Installation (CSS or SASS), CLI init, SvelteKit setup, and the 41 built-in themes. |
136
+ | **03** | [**`03-structure.md`**](./docs/03-structure.md) | Numbered physical scale (`_00` through `_08`) and cascade order. |
137
+ | **04** | [**`04-tokens.md`**](./docs/04-tokens.md) | 30 semantic colors, fluid Utopia scales, and dark/light modes. |
138
+ | **05** | [**`05-dimensions.md`**](./docs/05-dimensions.md) | 17 space families, the literal px ladder, and `-mob`/`-desk` bands. |
139
+ | **06** | [**`06-containers.md`**](./docs/06-containers.md) | `.box`, `.row`, `.grid`, and physical X/Y axis alignment. |
140
+ | **07** | [**`07-layouts.md`**](./docs/07-layouts.md) | Gridding Golden Rules, `.card-grid`, `.prose`, and `.reel`. |
141
+ | **08** | [**`08-shells-and-markups.md`**](./docs/08-shells-and-markups.md) | Canonical App Shell, role-bound sidebars, mobile disclosures, overlays. |
142
+ | **09** | [**`09-visuals-and-interactions.md`**](./docs/09-visuals-and-interactions.md) | Surfaces, inks, hairline borders, buttons, and form inputs. |
143
+ | **10** | [**`10-presets.md`**](./docs/10-presets.md) | Presets runtime, `presets.svelte.ts` API, and Svelte UI pickers. |
144
+ | **11** | [**`11-mcp-server.md`**](./docs/11-mcp-server.md) | Model Context Protocol server tools and agent connection configs. |
145
+ | **12** | [**`12-agent-plugin.md`**](./docs/12-agent-plugin.md) | `agent-plugins.org` spec, the bundled `fractal-styler` skill. |
146
+ | **13** | [**`13-cookbook.md`**](./docs/13-cookbook.md) | The Zero-SASS Recipe Gallery: real-world UI patterns composed in HTML. |
147
+ | **Registry** | [**`REGISTRY.md`**](./REGISTRY.md) | Comprehensive, grepable class dictionary and token table. |
123
148
 
124
149
  ---
125
150
 
126
- ## Documentation Index
151
+ ## AI Agent Integration & MCP
127
152
 
128
- | Guide | Description |
129
- |---|---|
130
- | [01. Philosophy](docs/01-philosophy.md) | The fractal model, self-similarity, and dual consumption |
131
- | [02. Structure](docs/02-structure.md) | File anatomy, the 12-file numbered scale, and cascade order |
132
- | [03. Getting Started](docs/03-getting-started.md) | Scaffolding, imports, and wiring into SvelteKit |
133
- | [04. Fractals Reference](docs/04-fractals-reference.md) | Comprehensive reference for every atom and molecule mixin |
134
- | [05. Tokens & Theming](docs/05-tokens-and-theming.md) | Fluid scales, the 21-variable contract, and dark mode |
135
- | [06. Utilities Reference](docs/06-utilities.md) | Generated atomic classes, directional padding, and visibility |
136
- | [07. Components & Layouts](docs/07-components-and-layouts.md) | Shipped blocks, cards, and page layout templates |
137
- | [08. Recipes & Patterns](docs/08-recipes.md) | Worked examples: pricing card, responsive docs shell, dialog |
138
- | [09. Migration from v1](docs/09-migration-from-v1.md) | Upgrading from older unnumbered stylesheets |
139
- | [DESIGN.md](DESIGN.md) | Golden UI invariants, card containment laws, and defect prevention |
153
+ Fractalstyler2 includes a native MCP server (`fractalstyler2-mcp`) providing 7 tools for AI coding agents:
154
+ - `compile_fractals`: Compiles a SASS snippet to CSS, for the rare declaration that genuinely does not compose.
155
+ - `get_design_tokens`: Structured token queries by category.
156
+ - `snap_to_tokens`: Snaps raw pixel measurements to nearest token steps.
157
+ - `css_to_fractals`: Converts raw CSS declarations to idiomatic markup classes.
158
+ - `validate_recipe`: Lints snippets against design system rules.
140
159
 
141
160
  ---
142
161
 
143
162
  ## License
144
163
 
145
- MIT
164
+ MIT © [Fractal Mandala](https://github.com/fractalmandala)
package/dist/cli.js CHANGED
@@ -3,22 +3,45 @@ import { existsSync, mkdirSync, readdirSync, copyFileSync } from 'node:fs';
3
3
  import { dirname, join, resolve, relative } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import process from 'node:process';
6
- function getTemplatesDir() {
6
+ /**
7
+ * Where the scaffoldable partials live.
8
+ *
9
+ * These ARE the published stylesheet — `svelte-package` copies
10
+ * src/lib/styles into dist/styles, so `init` hands over the same files the
11
+ * package imports. There is deliberately no separate templates/ copy to
12
+ * drift out of sync.
13
+ *
14
+ * Resolves for both layouts: dist/cli.js -> dist/styles, and the in-repo
15
+ * src/lib/cli.ts -> src/lib/styles.
16
+ */
17
+ function getStylesDir() {
7
18
  const HERE = dirname(fileURLToPath(import.meta.url));
8
19
  const candidates = [
9
- join(HERE, '..', 'templates'),
10
- join(HERE, '..', '..', 'templates'),
11
- join(HERE, 'templates')
20
+ join(HERE, 'styles'),
21
+ join(HERE, '..', 'styles'),
22
+ join(HERE, '..', 'lib', 'styles')
12
23
  ];
13
24
  for (const candidate of candidates) {
14
- if (existsSync(candidate))
25
+ // index.sass is the marker: an empty or unrelated directory is not it.
26
+ if (existsSync(join(candidate, 'index.sass')))
15
27
  return candidate;
16
28
  }
17
- throw new Error(`Templates directory not found. Searched in: ${candidates.join(', ')}`);
29
+ throw new Error(`fractalstyler2 stylesheet source not found. Searched in:\n ${candidates.join('\n ')}`);
30
+ }
31
+ /** The compiled CSS distribution, emitted by scripts/build-css.js at pack time. */
32
+ function getCssDir() {
33
+ const HERE = dirname(fileURLToPath(import.meta.url));
34
+ const candidates = [join(HERE, 'css'), join(HERE, '..', 'css'), join(HERE, '..', 'dist', 'css')];
35
+ for (const candidate of candidates) {
36
+ if (existsSync(join(candidate, 'fractalstyler.css')))
37
+ return candidate;
38
+ }
39
+ throw new Error(`fractalstyler2 CSS distribution not found. Searched in:\n ${candidates.join('\n ')}\n` +
40
+ `If you are working from a clone, run \`npm run css\` first.`);
18
41
  }
19
42
  function printUsage() {
20
43
  console.log(`
21
- fractalstyler2 — SASS fractal design system scaffolder & MCP server
44
+ fractalstyler2 — composition styling system scaffolder & MCP server
22
45
 
23
46
  Usage:
24
47
  npx fractalstyler2 init [dest] [options]
@@ -27,31 +50,41 @@ Usage:
27
50
  npx fractalstyler2 mcp:export [dest]
28
51
 
29
52
  Commands:
30
- init [dest] Scaffold SASS partials into target directory (default: src/lib/styles)
53
+ init [dest] Scaffold the design system into a target directory
54
+ (default: src/lib/styles)
31
55
  mcp Start the Model Context Protocol (MCP) server for OpenDesign, Claude, etc.
32
56
  mcp:install Automatically install MCP schemas & config into Antigravity & OpenCode
33
57
  mcp:export [dest] Export static MCP tool schema JSON files to target directory (default: .mcp)
34
58
 
35
59
  Options:
60
+ --css Scaffold the compiled stylesheet (no toolchain needed)
61
+ --sass Scaffold the editable SASS partials (the default)
36
62
  -f, --force Overwrite files if they already exist
37
63
  -h, --help Show this help message
38
64
 
39
65
  Examples:
40
66
  npx fractalstyler2 init
67
+ npx fractalstyler2 init --css
68
+ npx fractalstyler2 init src/styles --css
41
69
  npx fractalstyler2 mcp
42
70
  npx fractalstyler2 mcp:install
43
71
  npx fractalstyler2 mcp:export ~/.gemini/antigravity/mcp/fractalstyler2
44
72
  `);
45
73
  }
46
- function init(destArg, force) {
47
- const dest = destArg ?? 'src/lib/styles';
74
+ function init(destArg, force, flavour) {
75
+ const dest = destArg ?? (flavour === 'css' ? 'src/styles' : 'src/lib/styles');
48
76
  const cwd = process.cwd();
49
77
  const targetDir = resolve(cwd, dest);
50
78
  console.log(`\n▲ fractalstyler2 init\n`);
51
- console.log(`Scaffolding SASS design system into: ${dest}\n`);
79
+ console.log(`Scaffolding ${flavour === 'css' ? 'compiled CSS' : 'SASS'} into: ${dest}\n`);
52
80
  mkdirSync(targetDir, { recursive: true });
53
- const templatesDir = getTemplatesDir();
54
- const files = readdirSync(templatesDir).filter((f) => f.endsWith('.sass'));
81
+ const stylesDir = flavour === 'css' ? getCssDir() : getStylesDir();
82
+ const files = flavour === 'css'
83
+ ? ['fractalstyler.css', 'fractalstyler.min.css']
84
+ : readdirSync(stylesDir).filter((f) => f.endsWith('.sass'));
85
+ // The L4 shells are only half a definition without their markup, so the
86
+ // contract ships alongside the stylesheet in both flavours.
87
+ const markups = join(getStylesDir(), 'canonical-markups.md');
55
88
  let created = 0;
56
89
  let overwritten = 0;
57
90
  let skipped = 0;
@@ -60,7 +93,7 @@ function init(destArg, force) {
60
93
  const relPath = relative(cwd, targetFile);
61
94
  if (existsSync(targetFile)) {
62
95
  if (force) {
63
- copyFileSync(join(templatesDir, file), targetFile);
96
+ copyFileSync(join(stylesDir, file), targetFile);
64
97
  console.log(` \x1b[33moverwrite\x1b[0m ${relPath}`);
65
98
  overwritten++;
66
99
  }
@@ -70,33 +103,80 @@ function init(destArg, force) {
70
103
  }
71
104
  }
72
105
  else {
73
- copyFileSync(join(templatesDir, file), targetFile);
106
+ copyFileSync(join(stylesDir, file), targetFile);
74
107
  console.log(` \x1b[32mcreate\x1b[0m ${relPath}`);
75
108
  created++;
76
109
  }
77
110
  }
111
+ if (existsSync(markups)) {
112
+ const target = join(targetDir, 'canonical-markups.md');
113
+ if (!existsSync(target) || force) {
114
+ copyFileSync(markups, target);
115
+ console.log(` \x1b[32mcreate\x1b[0m ${relative(cwd, target)}`);
116
+ created++;
117
+ }
118
+ }
78
119
  console.log(`\nDone: ${created} created, ${overwritten} overwritten, ${skipped} skipped.\n`);
120
+ if (flavour === 'css') {
121
+ const cssPath = `${dest}/fractalstyler.css`;
122
+ console.log(`Next steps:
123
+ 1. Link the stylesheet once, in your document head:
124
+ <link rel="stylesheet" href="/${cssPath}" />
125
+
126
+ Or import it, if you have a bundler:
127
+ import '${cssPath}';
128
+
129
+ 2. Compose in markup. The registry is the API — .box, .row and .grid
130
+ carry most layouts, with .gap-* / .pad-* for space and .surface /
131
+ .border for the dress:
132
+ <div class="row ycenter xbetween gap-sm pad-md surface border">
133
+ <span class="text-md weight-600">Title</span>
134
+ <button class="button primary">Continue</button>
135
+ </div>
136
+
137
+ 3. Themes and presets need no JavaScript to work:
138
+ <html class="theme-night-dark" data-mode="dark" data-shape="sharp">
139
+
140
+ To let people change them at runtime, and to avoid a flash of the
141
+ wrong theme on load:
142
+ import { initPresets, setPreset, getPresetScript } from 'fractalstyler2/presets';
143
+
144
+ 4. For page and application shells, copy the structures in the scaffolded
145
+ canonical-markups.md verbatim. The responsive behaviour — rails that
146
+ retract, drawers that open — follows from the markup, so an
147
+ approximation of it will not behave.
148
+
149
+ 5. Before writing any custom CSS, check docs/13-cookbook.md. Nearly every
150
+ common pattern is already composable.
151
+ `);
152
+ return;
153
+ }
79
154
  const importPath = dest === 'src/lib/styles' ? '$lib/styles/index.sass' : `${dest}/index.sass`;
80
- const fractalsPath = dest === 'src/lib/styles' ? '$lib/styles/fractals' : `${dest}/fractals`;
155
+ const ownPath = dest === 'src/lib/styles' ? '$lib/styles/_08_own.sass' : `${dest}/_08_own.sass`;
81
156
  console.log(`Next steps:
82
157
  1. Import the stylesheet once globally (e.g. in src/routes/+layout.svelte):
83
158
  <script>
84
159
  import '${importPath}';
85
160
  </script>
86
161
 
87
- 2. Compose your components with fractal mixins:
88
- <style lang="sass">
89
- @use '${fractalsPath}' as *
162
+ 2. Compose in markup. The registry is the API — .box, .row and .grid
163
+ carry most layouts, with .gap-* / .pad-* for space and .surface /
164
+ .border for the dress:
165
+ <div class="row ycenter xbetween gap-sm pad-md surface border">
166
+ <span class="text-md weight-600">Title</span>
167
+ <button class="button primary">Continue</button>
168
+ </div>
90
169
 
91
- .card
92
- +surface(surface, s, 6)
93
- +stack(s)
94
- </style>
170
+ 3. Before writing any custom class, check docs/13-cookbook.md — nearly
171
+ every common pattern is already composable. Keep
172
+ ${ownPath}
173
+ for what genuinely is not (third-party widget overrides, mostly).
95
174
  `);
96
175
  }
97
176
  const args = process.argv.slice(2);
98
177
  const showHelp = args.includes('-h') || args.includes('--help');
99
178
  const force = args.includes('-f') || args.includes('--force');
179
+ const flavour = args.includes('--css') ? 'css' : 'sass';
100
180
  const positional = args.filter((a) => !a.startsWith('-'));
101
181
  const command = positional[0];
102
182
  const VALID_COMMANDS = ['init', 'mcp', 'mcp:export', 'mcp:install'];
@@ -143,9 +223,9 @@ else if (command === 'mcp:install') {
143
223
  });
144
224
  }
145
225
  else if (command === 'init') {
146
- init(positional[1], force);
226
+ init(positional[1], force, flavour);
147
227
  }
148
228
  else {
149
229
  // Default to init if no subcommand given (e.g., `npx fractalstyler2`)
150
- init(positional[0], force);
230
+ init(positional[0], force, flavour);
151
231
  }
@@ -0,0 +1,44 @@
1
+ <script lang="ts" module>
2
+ export interface AccordionCtx {
3
+ /** Called by an item when it opens, so the group can close the others. */
4
+ claim: (id: symbol) => void;
5
+ register: (id: symbol, close: () => void) => () => void;
6
+ }
7
+ export const ACCORDION = Symbol('fs2.accordion');
8
+ </script>
9
+
10
+ <script lang="ts">
11
+ import { setContext, type Snippet } from 'svelte';
12
+
13
+ /**
14
+ * Accordion group, from canonical-markups.md §3.
15
+ *
16
+ * `single` is the "accordion parent that regulates what closes when what
17
+ * opens" the introduction describes: opening one item closes its siblings.
18
+ * Default is independent items, which is what most navigation trees want.
19
+ */
20
+ interface Props {
21
+ children: Snippet;
22
+ /** Only one item open at a time. */
23
+ single?: boolean;
24
+ class?: string;
25
+ }
26
+ let { children, single = false, class: className = '' }: Props = $props();
27
+
28
+ const items = new Map<symbol, () => void>();
29
+
30
+ setContext<AccordionCtx>(ACCORDION, {
31
+ register(id, close) {
32
+ items.set(id, close);
33
+ return () => items.delete(id);
34
+ },
35
+ claim(id) {
36
+ if (!single) return;
37
+ for (const [other, close] of items) if (other !== id) close();
38
+ }
39
+ });
40
+ </script>
41
+
42
+ <div class="accordion {className}">
43
+ {@render children()}
44
+ </div>
@@ -0,0 +1,23 @@
1
+ export interface AccordionCtx {
2
+ /** Called by an item when it opens, so the group can close the others. */
3
+ claim: (id: symbol) => void;
4
+ register: (id: symbol, close: () => void) => () => void;
5
+ }
6
+ export declare const ACCORDION: unique symbol;
7
+ import { type Snippet } from 'svelte';
8
+ /**
9
+ * Accordion group, from canonical-markups.md §3.
10
+ *
11
+ * `single` is the "accordion parent that regulates what closes when what
12
+ * opens" the introduction describes: opening one item closes its siblings.
13
+ * Default is independent items, which is what most navigation trees want.
14
+ */
15
+ interface Props {
16
+ children: Snippet;
17
+ /** Only one item open at a time. */
18
+ single?: boolean;
19
+ class?: string;
20
+ }
21
+ declare const Accordion: import("svelte").Component<Props, {}, "">;
22
+ type Accordion = ReturnType<typeof Accordion>;
23
+ export default Accordion;
@@ -0,0 +1,44 @@
1
+ <script lang="ts">
2
+ import { getContext, onMount, type Snippet } from 'svelte';
3
+ import { ACCORDION, type AccordionCtx } from './Accordion.svelte';
4
+
5
+ /**
6
+ * One disclosure, from canonical-markups.md §3.
7
+ *
8
+ * Emits all three parts of the contract: `.accordion-item` carries `.open`,
9
+ * `.accordion-content` is the grid that animates, and `.accordion-panel` is
10
+ * the element that actually clips. The panel is the easiest to omit by hand
11
+ * and the failure is silent — the content simply never collapses.
12
+ *
13
+ * State stays on native attributes: the trigger carries `aria-expanded`.
14
+ */
15
+ interface Props {
16
+ /** The always-visible trigger row. */
17
+ trigger: Snippet;
18
+ children: Snippet;
19
+ open?: boolean;
20
+ class?: string;
21
+ }
22
+ let { trigger, children, open = $bindable(false), class: className = '' }: Props = $props();
23
+
24
+ const id = Symbol();
25
+ const group = getContext<AccordionCtx | undefined>(ACCORDION);
26
+
27
+ onMount(() => group?.register(id, () => (open = false)));
28
+
29
+ function toggle(): void {
30
+ open = !open;
31
+ if (open) group?.claim(id);
32
+ }
33
+ </script>
34
+
35
+ <div class="accordion-item {className}" class:open>
36
+ <button type="button" class="accordion-trigger" aria-expanded={open} onclick={toggle}>
37
+ {@render trigger()}
38
+ </button>
39
+ <div class="accordion-content">
40
+ <div class="accordion-panel box gap-2xs pad-x-xs">
41
+ {@render children()}
42
+ </div>
43
+ </div>
44
+ </div>
@@ -0,0 +1,21 @@
1
+ import { type Snippet } from 'svelte';
2
+ /**
3
+ * One disclosure, from canonical-markups.md §3.
4
+ *
5
+ * Emits all three parts of the contract: `.accordion-item` carries `.open`,
6
+ * `.accordion-content` is the grid that animates, and `.accordion-panel` is
7
+ * the element that actually clips. The panel is the easiest to omit by hand
8
+ * and the failure is silent — the content simply never collapses.
9
+ *
10
+ * State stays on native attributes: the trigger carries `aria-expanded`.
11
+ */
12
+ interface Props {
13
+ /** The always-visible trigger row. */
14
+ trigger: Snippet;
15
+ children: Snippet;
16
+ open?: boolean;
17
+ class?: string;
18
+ }
19
+ declare const AccordionItem: import("svelte").Component<Props, {}, "open">;
20
+ type AccordionItem = ReturnType<typeof AccordionItem>;
21
+ export default AccordionItem;