fractalstyler2 0.0.1 → 0.1.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 +93 -64
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +113 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +821 -0
- package/dist/{components → styles}/_blocks.sass +1 -1
- package/dist/{fractals/index.sass → styles/_fractals.sass} +3 -1
- package/dist/{components → styles}/_layouts.sass +1 -1
- package/dist/styles/index.sass +11 -10
- package/package.json +18 -6
- package/templates/_atoms.sass +155 -0
- package/templates/_base.sass +43 -0
- package/templates/_blocks.sass +88 -0
- package/templates/_config.sass +78 -0
- package/templates/_fractals.sass +13 -0
- package/templates/_layouts.sass +108 -0
- package/templates/_molecules.sass +93 -0
- package/templates/_responsive.sass +43 -0
- package/templates/_tokens.sass +127 -0
- package/templates/_utilities.sass +125 -0
- package/templates/index.sass +20 -0
- /package/dist/{fractals → styles}/_atoms.sass +0 -0
- /package/dist/{fractals → styles}/_base.sass +0 -0
- /package/dist/{fractals → styles}/_config.sass +0 -0
- /package/dist/{fractals → styles}/_molecules.sass +0 -0
- /package/dist/{fractals → styles}/_responsive.sass +0 -0
- /package/dist/{fractals → styles}/_tokens.sass +0 -0
- /package/dist/{fractals → styles}/_utilities.sass +0 -0
package/README.md
CHANGED
|
@@ -3,62 +3,35 @@
|
|
|
3
3
|
A fractal-composition styling system for SvelteKit. **Every style unit is a SASS
|
|
4
4
|
mixin ("a fractal"), and components and layouts are recipes of smaller fractals.**
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
product is framework-agnostic SASS surfaced through a SvelteKit library; the JS
|
|
8
|
-
runtime is just a tiny mode helper.
|
|
6
|
+
Use it either as a **shadcn-style scaffolder** (copying editable SASS into your project's `src/lib/styles`) or as a **direct package dependency**.
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
---
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
fractals. They form four self-similar tiers — each tier is "a recipe of the one
|
|
14
|
-
below":
|
|
15
|
-
|
|
16
|
-
| Tier | What | Examples |
|
|
17
|
-
| --- | --- | --- |
|
|
18
|
-
| **Config** | scales-as-data + resolvers | `space()`, `radius()`, `align()`, `$breakpoints` |
|
|
19
|
-
| **Atoms** | one decision each | `+box` `+row` `+gap` `+pad` `+border` `+radius` `+bg` `+ink` `+type` `+sticky` |
|
|
20
|
-
| **Molecules** | graphs of atoms | `+stack` `+cluster` `+cover` `+frame` `+surface` `+cols` `+auto-grid` |
|
|
21
|
-
| **Components / Layouts** | graphs of molecules | `.card` `.button` · `.grid-3` `.hero` `.holy-grail` `.docs` `.app-shell` |
|
|
10
|
+
## Quickstart (shadcn-style Scaffolding)
|
|
22
11
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
> **A utility class and a semantic component are the same fractal, consumed two ways.**
|
|
26
|
-
> `.gap-m { +gap(m) }` binds the fractal to markup; `.hero { +cover; +stack(m) }`
|
|
27
|
-
> composes it into a component. One vocabulary, you pick where each is used.
|
|
28
|
-
|
|
29
|
-
## Install
|
|
12
|
+
Scaffold the complete, customizable SASS design system directly into your project's `src/lib/styles`:
|
|
30
13
|
|
|
31
14
|
```bash
|
|
32
|
-
|
|
33
|
-
|
|
15
|
+
# Scaffold into src/lib/styles
|
|
16
|
+
npx fractalstyler2 init
|
|
34
17
|
|
|
35
|
-
|
|
18
|
+
# Or using pnpm
|
|
19
|
+
pnpm dlx fractalstyler2 init
|
|
20
|
+
```
|
|
36
21
|
|
|
37
|
-
|
|
38
|
-
blocks, layouts). Import once, globally (e.g. in your root `+layout.svelte`):
|
|
22
|
+
### Next Steps in Your Project
|
|
39
23
|
|
|
24
|
+
**1. Import the stylesheet in your root `+layout.svelte`:**
|
|
40
25
|
```svelte
|
|
41
26
|
<script>
|
|
42
|
-
import '
|
|
27
|
+
import '$lib/styles/index.sass';
|
|
43
28
|
</script>
|
|
44
29
|
```
|
|
45
30
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```svelte
|
|
49
|
-
<div class="grid-3">
|
|
50
|
-
<article class="card"><h3 class="text-lg">Composable</h3></article>
|
|
51
|
-
<article class="card" data-elevated><h3 class="text-lg">Elevated</h3></article>
|
|
52
|
-
<article class="card"><h3 class="text-lg">Semantic</h3></article>
|
|
53
|
-
</div>
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
**2 — Compose your own components from the fractals.** Import the pure API
|
|
57
|
-
(emits nothing until called) inside any component's SASS:
|
|
58
|
-
|
|
31
|
+
**2. Compose your components using the fractals:**
|
|
59
32
|
```svelte
|
|
60
33
|
<style lang="sass">
|
|
61
|
-
@use '
|
|
34
|
+
@use '$lib/styles/fractals' as *
|
|
62
35
|
|
|
63
36
|
.pricing-card
|
|
64
37
|
+surface(surface, l, 16, md) // bg + border + radius + pad + shadow
|
|
@@ -67,48 +40,104 @@ Then write thin, semantic markup:
|
|
|
67
40
|
</style>
|
|
68
41
|
```
|
|
69
42
|
|
|
70
|
-
|
|
71
|
-
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Alternative: Direct Package Dependency
|
|
72
46
|
|
|
73
|
-
|
|
47
|
+
If you prefer importing from `node_modules` instead of scaffolding local files:
|
|
74
48
|
|
|
49
|
+
```bash
|
|
50
|
+
npm install fractalstyler2
|
|
51
|
+
# or
|
|
52
|
+
pnpm add fractalstyler2
|
|
75
53
|
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
54
|
+
|
|
55
|
+
Your app also needs `sass`:
|
|
56
|
+
```bash
|
|
57
|
+
npm install -D sass
|
|
58
|
+
# or
|
|
59
|
+
pnpm add -D sass
|
|
81
60
|
```
|
|
82
61
|
|
|
83
|
-
|
|
84
|
-
molecules, utilities), `src/lib/components/` (blocks, layouts),
|
|
85
|
-
`src/lib/styles/` (the emit entry).
|
|
62
|
+
### Use from `node_modules`:
|
|
86
63
|
|
|
87
|
-
|
|
64
|
+
```svelte
|
|
65
|
+
<script>
|
|
66
|
+
import 'fractalstyler2/styles';
|
|
67
|
+
</script>
|
|
88
68
|
|
|
89
|
-
|
|
90
|
-
|
|
69
|
+
<style lang="sass">
|
|
70
|
+
@use 'fractalstyler2/fractals' as *
|
|
71
|
+
|
|
72
|
+
.card
|
|
73
|
+
+surface(surface, s, 12)
|
|
74
|
+
+stack(s)
|
|
75
|
+
</style>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## The Model
|
|
81
|
+
|
|
82
|
+
A fractal is a mixin: a tiny reusable styling decision that composes other
|
|
83
|
+
fractals. They form four self-similar tiers — each tier is "a recipe of the one
|
|
84
|
+
below":
|
|
85
|
+
|
|
86
|
+
| Tier | What | Examples |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| **Config** | scales-as-data + resolvers | `space()`, `radius()`, `align()`, `$breakpoints` |
|
|
89
|
+
| **Atoms** | one decision each | `+box` `+row` `+gap` `+pad` `+border` `+radius` `+bg` `+ink` `+type` `+sticky` |
|
|
90
|
+
| **Molecules** | graphs of atoms | `+stack` `+cluster` `+cover` `+frame` `+surface` `+cols` `+auto-grid` |
|
|
91
|
+
| **Components / Layouts** | graphs of molecules | `.card` `.button` · `.grid-3` `.hero` `.holy-grail` `.docs` `.app-shell` |
|
|
92
|
+
|
|
93
|
+
The one idea that makes it click:
|
|
94
|
+
|
|
95
|
+
> **A utility class and a semantic component are the same fractal, consumed two ways.**
|
|
96
|
+
> `.gap-m { +gap(m) }` binds the fractal to markup; `.hero { +cover; +stack(m) }`
|
|
97
|
+
> composes it into a component. One vocabulary, you pick where each is used.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Color Mode
|
|
102
|
+
|
|
103
|
+
Light is the marker-free default (SSR-safe). Dark comes from `prefers-color-scheme` and from an explicit `data-mode="dark"` on `<html>`:
|
|
91
104
|
|
|
92
105
|
```js
|
|
93
106
|
import { toggleMode, setMode } from 'fractalstyler2';
|
|
94
107
|
toggleMode(); // flip light/dark
|
|
95
|
-
setMode('dark'); // force
|
|
108
|
+
setMode('dark'); // force mode
|
|
96
109
|
```
|
|
97
110
|
|
|
98
|
-
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## CLI & MCP Server
|
|
114
|
+
|
|
115
|
+
### Scaffolder CLI
|
|
116
|
+
```bash
|
|
117
|
+
fractalstyler2 init [dest] [options]
|
|
118
|
+
|
|
119
|
+
Arguments:
|
|
120
|
+
dest Target directory for SASS partials (default: src/lib/styles)
|
|
99
121
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
122
|
+
Options:
|
|
123
|
+
-f, --force Overwrite files if they already exist
|
|
124
|
+
-h, --help Show help message
|
|
125
|
+
```
|
|
103
126
|
|
|
104
|
-
|
|
127
|
+
### Model Context Protocol (MCP) Server
|
|
128
|
+
Use `fractalstyler2` directly with AI canvas tools (e.g. **OpenDesign**, **Pencil**), IDEs (**Cursor**, **Antigravity**), and assistants (**Claude Desktop**):
|
|
105
129
|
|
|
106
130
|
```bash
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
131
|
+
# Start MCP server over stdio
|
|
132
|
+
npx fractalstyler2-mcp
|
|
133
|
+
# or
|
|
134
|
+
npx fractalstyler2 mcp
|
|
110
135
|
```
|
|
111
136
|
|
|
137
|
+
Exposes tools to compile fractals to live CSS, snap arbitrary canvas values to Utopia tokens, generate Svelte 5 + SASS components, and lint recipes. See [docs/10-mcp-server.md](docs/10-mcp-server.md) for configuration details.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
112
141
|
## License
|
|
113
142
|
|
|
114
143
|
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, copyFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve, relative } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
function getTemplatesDir() {
|
|
7
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const candidates = [
|
|
9
|
+
join(HERE, '..', 'templates'),
|
|
10
|
+
join(HERE, '..', '..', 'templates'),
|
|
11
|
+
join(HERE, 'templates')
|
|
12
|
+
];
|
|
13
|
+
for (const candidate of candidates) {
|
|
14
|
+
if (existsSync(candidate))
|
|
15
|
+
return candidate;
|
|
16
|
+
}
|
|
17
|
+
throw new Error(`Templates directory not found. Searched in: ${candidates.join(', ')}`);
|
|
18
|
+
}
|
|
19
|
+
function printUsage() {
|
|
20
|
+
console.log(`
|
|
21
|
+
fractalstyler2 — SASS fractal design system scaffolder & MCP server
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
npx fractalstyler2 init [dest] [options]
|
|
25
|
+
npx fractalstyler2 mcp
|
|
26
|
+
|
|
27
|
+
Commands:
|
|
28
|
+
init [dest] Scaffold SASS partials into target directory (default: src/lib/styles)
|
|
29
|
+
mcp Start the Model Context Protocol (MCP) server for OpenDesign, Claude, etc.
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
-f, --force Overwrite files if they already exist
|
|
33
|
+
-h, --help Show this help message
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
npx fractalstyler2 init
|
|
37
|
+
npx fractalstyler2 init src/lib/styles --force
|
|
38
|
+
npx fractalstyler2 mcp
|
|
39
|
+
`);
|
|
40
|
+
}
|
|
41
|
+
function init(destArg, force) {
|
|
42
|
+
const dest = destArg ?? 'src/lib/styles';
|
|
43
|
+
const cwd = process.cwd();
|
|
44
|
+
const targetDir = resolve(cwd, dest);
|
|
45
|
+
console.log(`\n▲ fractalstyler2 init\n`);
|
|
46
|
+
console.log(`Scaffolding SASS design system into: ${dest}\n`);
|
|
47
|
+
mkdirSync(targetDir, { recursive: true });
|
|
48
|
+
const templatesDir = getTemplatesDir();
|
|
49
|
+
const files = readdirSync(templatesDir).filter((f) => f.endsWith('.sass'));
|
|
50
|
+
let created = 0;
|
|
51
|
+
let overwritten = 0;
|
|
52
|
+
let skipped = 0;
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
const targetFile = join(targetDir, file);
|
|
55
|
+
const relPath = relative(cwd, targetFile);
|
|
56
|
+
if (existsSync(targetFile)) {
|
|
57
|
+
if (force) {
|
|
58
|
+
copyFileSync(join(templatesDir, file), targetFile);
|
|
59
|
+
console.log(` \x1b[33moverwrite\x1b[0m ${relPath}`);
|
|
60
|
+
overwritten++;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.log(` \x1b[90mskip\x1b[0m ${relPath} (already exists, use --force to overwrite)`);
|
|
64
|
+
skipped++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
copyFileSync(join(templatesDir, file), targetFile);
|
|
69
|
+
console.log(` \x1b[32mcreate\x1b[0m ${relPath}`);
|
|
70
|
+
created++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
console.log(`\nDone: ${created} created, ${overwritten} overwritten, ${skipped} skipped.\n`);
|
|
74
|
+
const importPath = dest === 'src/lib/styles' ? '$lib/styles/index.sass' : `${dest}/index.sass`;
|
|
75
|
+
const fractalsPath = dest === 'src/lib/styles' ? '$lib/styles/fractals' : `${dest}/fractals`;
|
|
76
|
+
console.log(`Next steps:
|
|
77
|
+
1. Import the stylesheet once globally (e.g. in src/routes/+layout.svelte):
|
|
78
|
+
<script>
|
|
79
|
+
import '${importPath}';
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
2. Compose your components with fractal mixins:
|
|
83
|
+
<style lang="sass">
|
|
84
|
+
@use '${fractalsPath}' as *
|
|
85
|
+
|
|
86
|
+
.card
|
|
87
|
+
+surface(surface, s, 12)
|
|
88
|
+
+stack(s)
|
|
89
|
+
</style>
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
const args = process.argv.slice(2);
|
|
93
|
+
const showHelp = args.includes('-h') || args.includes('--help');
|
|
94
|
+
const force = args.includes('-f') || args.includes('--force');
|
|
95
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
96
|
+
const command = positional[0];
|
|
97
|
+
if (showHelp || (command && !['init', 'mcp'].includes(command))) {
|
|
98
|
+
if (command && !['init', 'mcp'].includes(command)) {
|
|
99
|
+
console.error(`Unknown command: ${command}`);
|
|
100
|
+
}
|
|
101
|
+
printUsage();
|
|
102
|
+
process.exit(command && !['init', 'mcp'].includes(command) ? 1 : 0);
|
|
103
|
+
}
|
|
104
|
+
else if (command === 'mcp') {
|
|
105
|
+
import('./mcp/server.js');
|
|
106
|
+
}
|
|
107
|
+
else if (command === 'init') {
|
|
108
|
+
init(positional[1], force);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Default to init if no subcommand given (e.g., `npx fractalstyler2`)
|
|
112
|
+
init(positional[0], force);
|
|
113
|
+
}
|