fractalstyler2 0.0.1
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 +114 -0
- package/dist/components/_blocks.sass +88 -0
- package/dist/components/_layouts.sass +108 -0
- package/dist/fractals/_atoms.sass +155 -0
- package/dist/fractals/_base.sass +43 -0
- package/dist/fractals/_config.sass +78 -0
- package/dist/fractals/_molecules.sass +93 -0
- package/dist/fractals/_responsive.sass +43 -0
- package/dist/fractals/_tokens.sass +127 -0
- package/dist/fractals/_utilities.sass +125 -0
- package/dist/fractals/index.sass +11 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/dist/styles/index.sass +19 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# fractalstyler2
|
|
2
|
+
|
|
3
|
+
A fractal-composition styling system for SvelteKit. **Every style unit is a SASS
|
|
4
|
+
mixin ("a fractal"), and components and layouts are recipes of smaller fractals.**
|
|
5
|
+
|
|
6
|
+
This is a standalone package with no relationship to any earlier styler. The
|
|
7
|
+
product is framework-agnostic SASS surfaced through a SvelteKit library; the JS
|
|
8
|
+
runtime is just a tiny mode helper.
|
|
9
|
+
|
|
10
|
+
## The idea
|
|
11
|
+
|
|
12
|
+
A fractal is a mixin: a tiny reusable styling decision that composes other
|
|
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` |
|
|
22
|
+
|
|
23
|
+
The one idea that makes it click:
|
|
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
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm add fractalstyler2
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Use
|
|
36
|
+
|
|
37
|
+
**1 — Emit the ready-made stylesheet** (tokens, base reset, utility classes,
|
|
38
|
+
blocks, layouts). Import once, globally (e.g. in your root `+layout.svelte`):
|
|
39
|
+
|
|
40
|
+
```svelte
|
|
41
|
+
<script>
|
|
42
|
+
import 'fractalstyler2/styles';
|
|
43
|
+
</script>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then write thin, semantic markup:
|
|
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
|
+
|
|
59
|
+
```svelte
|
|
60
|
+
<style lang="sass">
|
|
61
|
+
@use 'fractalstyler2/fractals' as *
|
|
62
|
+
|
|
63
|
+
.pricing-card
|
|
64
|
+
+surface(surface, l, 16, md) // bg + border + radius + pad + shadow
|
|
65
|
+
+stack(m, center) // flex-column + gap + centered
|
|
66
|
+
text-align: center
|
|
67
|
+
</style>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Values prefer the finite token scale and fall back to raw units in the same
|
|
71
|
+
call: `+gap(m)` → `var(--space-m)`, `+gap(16)` → `16px`. No JIT class explosion.
|
|
72
|
+
|
|
73
|
+
## What ships
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
fractalstyler2/
|
|
77
|
+
├─ styles → the full emitted stylesheet (import 'fractalstyler2/styles')
|
|
78
|
+
├─ fractals → the mixin/function API (@use 'fractalstyler2/fractals' as *)
|
|
79
|
+
├─ tokens → just the custom properties (@use 'fractalstyler2/tokens')
|
|
80
|
+
└─ (default) → { version, setMode, toggleMode }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Source layout: `src/lib/fractals/` (config, tokens, base, responsive, atoms,
|
|
84
|
+
molecules, utilities), `src/lib/components/` (blocks, layouts),
|
|
85
|
+
`src/lib/styles/` (the emit entry).
|
|
86
|
+
|
|
87
|
+
## Color mode
|
|
88
|
+
|
|
89
|
+
Light is the marker-free default (SSR-safe). Dark comes from
|
|
90
|
+
`prefers-color-scheme` and from an explicit `data-mode="dark"` on `<html>`:
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
import { toggleMode, setMode } from 'fractalstyler2';
|
|
94
|
+
toggleMode(); // flip light/dark
|
|
95
|
+
setMode('dark'); // force
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Included layouts
|
|
99
|
+
|
|
100
|
+
`.grid-3` (responsive 1→2→3), `.card-grid` (intrinsic auto-fit), `.hero`,
|
|
101
|
+
`.holy-grail`, `.docs`, `.app-shell` — each a few fractal calls in
|
|
102
|
+
`src/lib/components/_layouts.sass`.
|
|
103
|
+
|
|
104
|
+
## Develop
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pnpm install
|
|
108
|
+
pnpm dev # demo showcase at /
|
|
109
|
+
pnpm run prepack # build the distributable package into dist/
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// BLOCKS — components as recipes of fractals. Note how little CSS is here:
|
|
3
|
+
// each block is a handful of fractal calls plus its own genuinely-unique lines.
|
|
4
|
+
// State is expressed with data-*/aria-*, never modifier classes.
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
@use '../fractals' as *
|
|
8
|
+
|
|
9
|
+
.card
|
|
10
|
+
+surface(surface, s, 12)
|
|
11
|
+
+stack(s)
|
|
12
|
+
&[data-elevated]
|
|
13
|
+
+shadow(md)
|
|
14
|
+
|
|
15
|
+
.panel
|
|
16
|
+
+surface(raised, m, 16)
|
|
17
|
+
+stack(m)
|
|
18
|
+
|
|
19
|
+
.badge
|
|
20
|
+
+row(center, center)
|
|
21
|
+
display: inline-flex
|
|
22
|
+
+px(2xs)
|
|
23
|
+
+type(xs)
|
|
24
|
+
+weight(500)
|
|
25
|
+
+radius(full)
|
|
26
|
+
+bg(raised)
|
|
27
|
+
+ink(secondary)
|
|
28
|
+
padding-block: 2px
|
|
29
|
+
|
|
30
|
+
.avatar
|
|
31
|
+
+center
|
|
32
|
+
+square(var(--avatar-size, 40px))
|
|
33
|
+
+radius(full)
|
|
34
|
+
+bg(raised)
|
|
35
|
+
+ink(secondary)
|
|
36
|
+
overflow: hidden
|
|
37
|
+
|
|
38
|
+
.input
|
|
39
|
+
display: block
|
|
40
|
+
+w(100%)
|
|
41
|
+
+px(2xs)
|
|
42
|
+
+type(md)
|
|
43
|
+
+leading(1.5)
|
|
44
|
+
+border
|
|
45
|
+
+radius(6)
|
|
46
|
+
+bg(bg)
|
|
47
|
+
+ink(primary)
|
|
48
|
+
padding-block: 8px
|
|
49
|
+
&:focus-visible
|
|
50
|
+
+ring
|
|
51
|
+
|
|
52
|
+
.button
|
|
53
|
+
+row(center, center)
|
|
54
|
+
+gap(2xs)
|
|
55
|
+
+px(s)
|
|
56
|
+
+radius(6)
|
|
57
|
+
+weight(500)
|
|
58
|
+
+transition(background-color)
|
|
59
|
+
cursor: pointer
|
|
60
|
+
padding-block: 8px
|
|
61
|
+
user-select: none
|
|
62
|
+
// Exceptions: variants live on data-variant, not in the class name.
|
|
63
|
+
&[data-variant='primary']
|
|
64
|
+
background: var(--theme)
|
|
65
|
+
color: var(--text-inverse)
|
|
66
|
+
&:hover
|
|
67
|
+
background: var(--theme-hover)
|
|
68
|
+
&[data-variant='ghost']
|
|
69
|
+
background: transparent
|
|
70
|
+
+ink(secondary)
|
|
71
|
+
&:hover
|
|
72
|
+
+bg(raised)
|
|
73
|
+
|
|
74
|
+
.divider
|
|
75
|
+
border: none
|
|
76
|
+
+border(top)
|
|
77
|
+
margin-block: space(s)
|
|
78
|
+
|
|
79
|
+
.kbd
|
|
80
|
+
display: inline-block
|
|
81
|
+
+px(3xs)
|
|
82
|
+
+type(xs)
|
|
83
|
+
+leading(1)
|
|
84
|
+
+radius(4)
|
|
85
|
+
+bg(raised)
|
|
86
|
+
border: 1px solid var(--border-strong)
|
|
87
|
+
font-family: var(--font-mono)
|
|
88
|
+
padding-block: 2px
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// LAYOUTS — the largest fractals. Same recipe move, page scale.
|
|
3
|
+
// These are the concrete deliverables you named: a responsive 3-col grid, a
|
|
4
|
+
// hero, a holy-grail, a docs template. Each is a short composition of fractals.
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
@use '../fractals' as *
|
|
8
|
+
|
|
9
|
+
// 1. Responsive grid of 3 columns — 1 → 2 → 3 across breakpoints.
|
|
10
|
+
.grid-3
|
|
11
|
+
+cols((base: 1, sm: 2, lg: 3), m)
|
|
12
|
+
|
|
13
|
+
// A grid that never needs breakpoints: it fits as many columns as will hold
|
|
14
|
+
// the min track. Reach for this before .grid-3 when column count is negotiable.
|
|
15
|
+
.card-grid
|
|
16
|
+
+auto-grid(16rem, m)
|
|
17
|
+
|
|
18
|
+
// 2. Hero — full-viewport cover with a centered, measured message.
|
|
19
|
+
.hero
|
|
20
|
+
+cover(80vh, xl)
|
|
21
|
+
> .center
|
|
22
|
+
+stack(m, center)
|
|
23
|
+
text-align: center
|
|
24
|
+
max-width: 45ch
|
|
25
|
+
|
|
26
|
+
// 3. Holy grail — header / (nav · main · aside) / footer.
|
|
27
|
+
.holy-grail
|
|
28
|
+
display: grid
|
|
29
|
+
min-height: 100vh
|
|
30
|
+
grid-template-rows: auto 1fr auto
|
|
31
|
+
grid-template-areas: 'header' 'main' 'footer'
|
|
32
|
+
> .hg-header
|
|
33
|
+
grid-area: header
|
|
34
|
+
> .hg-footer
|
|
35
|
+
grid-area: footer
|
|
36
|
+
> .hg-body
|
|
37
|
+
grid-area: main
|
|
38
|
+
display: grid
|
|
39
|
+
grid-template-columns: 1fr
|
|
40
|
+
+gap(m)
|
|
41
|
+
+pad(m)
|
|
42
|
+
+at(lg)
|
|
43
|
+
> .hg-body
|
|
44
|
+
grid-template-columns: var(--nav-w, 220px) minmax(0, 1fr) var(--aside-w, 260px)
|
|
45
|
+
> .hg-nav
|
|
46
|
+
+sticky(var(--space-m))
|
|
47
|
+
align-self: start
|
|
48
|
+
> .hg-aside
|
|
49
|
+
+sticky(var(--space-m))
|
|
50
|
+
align-self: start
|
|
51
|
+
|
|
52
|
+
// 4. Docs template — sidebar nav + reading column + on-page TOC.
|
|
53
|
+
.docs
|
|
54
|
+
display: grid
|
|
55
|
+
min-height: 100vh
|
|
56
|
+
grid-template-columns: 1fr
|
|
57
|
+
> .docs-nav
|
|
58
|
+
display: none
|
|
59
|
+
> .docs-toc
|
|
60
|
+
display: none
|
|
61
|
+
> .docs-main
|
|
62
|
+
+min0
|
|
63
|
+
+stack(m)
|
|
64
|
+
+center-column(72ch, s-l)
|
|
65
|
+
+py(l)
|
|
66
|
+
+at(md)
|
|
67
|
+
grid-template-columns: var(--docs-nav-w, 240px) minmax(0, 1fr)
|
|
68
|
+
> .docs-nav
|
|
69
|
+
+box
|
|
70
|
+
+sticky(0)
|
|
71
|
+
+py(l)
|
|
72
|
+
+px(s)
|
|
73
|
+
height: 100vh
|
|
74
|
+
overflow-y: auto
|
|
75
|
+
+border(right)
|
|
76
|
+
+at(xl)
|
|
77
|
+
grid-template-columns: var(--docs-nav-w, 240px) minmax(0, 1fr) var(--docs-toc-w, 220px)
|
|
78
|
+
> .docs-toc
|
|
79
|
+
+box
|
|
80
|
+
+sticky(0)
|
|
81
|
+
+py(l)
|
|
82
|
+
+px(s)
|
|
83
|
+
height: 100vh
|
|
84
|
+
overflow-y: auto
|
|
85
|
+
+ink(secondary)
|
|
86
|
+
|
|
87
|
+
// 5. App shell — sticky header, fluid body, footer. The frame most apps live in.
|
|
88
|
+
.app-shell
|
|
89
|
+
+box
|
|
90
|
+
+relative
|
|
91
|
+
isolation: isolate
|
|
92
|
+
min-height: 100vh
|
|
93
|
+
> .app-header
|
|
94
|
+
+row(null, center)
|
|
95
|
+
+sticky(0)
|
|
96
|
+
+px(m)
|
|
97
|
+
z-index: var(--z-sticky)
|
|
98
|
+
height: var(--header-height)
|
|
99
|
+
+bg(surface)
|
|
100
|
+
+border(bottom)
|
|
101
|
+
> .app-main
|
|
102
|
+
+grow
|
|
103
|
+
+min0
|
|
104
|
+
> .app-footer
|
|
105
|
+
+row(null, center)
|
|
106
|
+
+px(m)
|
|
107
|
+
min-height: var(--footer-height)
|
|
108
|
+
+border(top)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// ATOM FRACTALS — the indivisible units. One visual decision each.
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// Every atom is a mixin. Call it inside a class to compose, or let
|
|
5
|
+
// _utilities.sass project it to a utility class. Same fractal, two consumers.
|
|
6
|
+
// =============================================================================
|
|
7
|
+
|
|
8
|
+
@use 'config' as *
|
|
9
|
+
|
|
10
|
+
// --- Flow: the two axes of layout ------------------------------------------
|
|
11
|
+
// A box is a vertical stack, a row is horizontal. Optional semantic alignment:
|
|
12
|
+
// +box(center) → items centered on the cross axis
|
|
13
|
+
// +row(between, center) → spread on main axis, centered on cross axis
|
|
14
|
+
=box($x: null, $y: null)
|
|
15
|
+
display: flex
|
|
16
|
+
flex-direction: column
|
|
17
|
+
@if $x
|
|
18
|
+
align-items: align($x)
|
|
19
|
+
@if $y
|
|
20
|
+
justify-content: align($y)
|
|
21
|
+
|
|
22
|
+
=row($x: null, $y: null)
|
|
23
|
+
display: flex
|
|
24
|
+
flex-direction: row
|
|
25
|
+
@if $x
|
|
26
|
+
justify-content: align($x)
|
|
27
|
+
@if $y
|
|
28
|
+
align-items: align($y)
|
|
29
|
+
|
|
30
|
+
=wrap
|
|
31
|
+
flex-wrap: wrap
|
|
32
|
+
|
|
33
|
+
=grid($cols: 1)
|
|
34
|
+
display: grid
|
|
35
|
+
grid-template-columns: repeat($cols, minmax(0, 1fr))
|
|
36
|
+
|
|
37
|
+
// A grid that reflows on its own — no breakpoints, no counting columns.
|
|
38
|
+
=auto-grid($min: 15rem, $gap: s)
|
|
39
|
+
display: grid
|
|
40
|
+
grid-template-columns: repeat(auto-fit, minmax(min(#{$min}, 100%), 1fr))
|
|
41
|
+
gap: space($gap)
|
|
42
|
+
|
|
43
|
+
// Dead-center anything (grid is the one-liner that works for a single child).
|
|
44
|
+
=center
|
|
45
|
+
display: grid
|
|
46
|
+
place-items: center
|
|
47
|
+
|
|
48
|
+
// --- Spacing ---------------------------------------------------------------
|
|
49
|
+
=gap($v: s)
|
|
50
|
+
gap: space($v)
|
|
51
|
+
|
|
52
|
+
=pad($v: s)
|
|
53
|
+
padding: space($v)
|
|
54
|
+
|
|
55
|
+
=px($v: s)
|
|
56
|
+
padding-inline: space($v)
|
|
57
|
+
|
|
58
|
+
=py($v: s)
|
|
59
|
+
padding-block: space($v)
|
|
60
|
+
|
|
61
|
+
=mx-auto
|
|
62
|
+
margin-inline: auto
|
|
63
|
+
|
|
64
|
+
=my-auto
|
|
65
|
+
margin-block: auto
|
|
66
|
+
|
|
67
|
+
// --- Size ------------------------------------------------------------------
|
|
68
|
+
=w($v: 100%)
|
|
69
|
+
width: $v
|
|
70
|
+
|
|
71
|
+
=h($v: 100%)
|
|
72
|
+
height: $v
|
|
73
|
+
|
|
74
|
+
=full
|
|
75
|
+
width: 100%
|
|
76
|
+
height: 100%
|
|
77
|
+
|
|
78
|
+
=square($v)
|
|
79
|
+
width: $v
|
|
80
|
+
height: $v
|
|
81
|
+
|
|
82
|
+
=grow($n: 1)
|
|
83
|
+
flex-grow: $n
|
|
84
|
+
|
|
85
|
+
=shrink($n: 0)
|
|
86
|
+
flex-shrink: $n
|
|
87
|
+
|
|
88
|
+
=min0
|
|
89
|
+
min-width: 0
|
|
90
|
+
min-height: 0
|
|
91
|
+
|
|
92
|
+
// --- Surface & skin --------------------------------------------------------
|
|
93
|
+
=bg($role: surface)
|
|
94
|
+
background-color: surface($role)
|
|
95
|
+
|
|
96
|
+
=ink($role: primary)
|
|
97
|
+
color: ink($role)
|
|
98
|
+
|
|
99
|
+
=border($side: all, $color: var(--border))
|
|
100
|
+
@if $side == all
|
|
101
|
+
border: 1px solid $color
|
|
102
|
+
@else
|
|
103
|
+
border-#{$side}: 1px solid $color
|
|
104
|
+
|
|
105
|
+
=radius($v: 12)
|
|
106
|
+
border-radius: radius($v)
|
|
107
|
+
|
|
108
|
+
=shadow($v: md)
|
|
109
|
+
box-shadow: shadow($v)
|
|
110
|
+
|
|
111
|
+
// --- Position --------------------------------------------------------------
|
|
112
|
+
=relative
|
|
113
|
+
position: relative
|
|
114
|
+
|
|
115
|
+
=absolute($inset: null)
|
|
116
|
+
position: absolute
|
|
117
|
+
@if $inset
|
|
118
|
+
inset: $inset
|
|
119
|
+
|
|
120
|
+
=sticky($top: 0)
|
|
121
|
+
position: sticky
|
|
122
|
+
top: $top
|
|
123
|
+
|
|
124
|
+
=fill
|
|
125
|
+
position: absolute
|
|
126
|
+
inset: 0
|
|
127
|
+
|
|
128
|
+
// --- Type ------------------------------------------------------------------
|
|
129
|
+
=type($v)
|
|
130
|
+
font-size: text-size($v)
|
|
131
|
+
|
|
132
|
+
=weight($w: 500)
|
|
133
|
+
font-weight: $w
|
|
134
|
+
|
|
135
|
+
=leading($lh: 1.5)
|
|
136
|
+
line-height: $lh
|
|
137
|
+
|
|
138
|
+
=truncate
|
|
139
|
+
overflow: hidden
|
|
140
|
+
white-space: nowrap
|
|
141
|
+
text-overflow: ellipsis
|
|
142
|
+
|
|
143
|
+
=clamp-lines($n: 2)
|
|
144
|
+
display: -webkit-box
|
|
145
|
+
-webkit-line-clamp: $n
|
|
146
|
+
-webkit-box-orient: vertical
|
|
147
|
+
overflow: hidden
|
|
148
|
+
|
|
149
|
+
// --- Motion ----------------------------------------------------------------
|
|
150
|
+
=transition($props: all, $dur: 150ms, $ease: ease)
|
|
151
|
+
transition: $props $dur $ease
|
|
152
|
+
|
|
153
|
+
=ring($color: var(--ring))
|
|
154
|
+
outline: 2px solid $color
|
|
155
|
+
outline-offset: 1px
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// BASE — a minimal, opinionated reset so the tokens actually reach the page.
|
|
3
|
+
// Kept tiny: box model, sane media defaults, and wiring body to the palette.
|
|
4
|
+
// =============================================================================
|
|
5
|
+
|
|
6
|
+
*, *::before, *::after
|
|
7
|
+
box-sizing: border-box
|
|
8
|
+
|
|
9
|
+
html
|
|
10
|
+
-webkit-text-size-adjust: 100%
|
|
11
|
+
|
|
12
|
+
body
|
|
13
|
+
margin: 0
|
|
14
|
+
min-height: 100vh
|
|
15
|
+
font-family: var(--font-sans)
|
|
16
|
+
font-size: var(--text-md)
|
|
17
|
+
line-height: 1.5
|
|
18
|
+
color: var(--text-primary)
|
|
19
|
+
background-color: var(--bg)
|
|
20
|
+
-webkit-font-smoothing: antialiased
|
|
21
|
+
|
|
22
|
+
:where(h1, h2, h3, h4, h5, h6, p, figure, blockquote)
|
|
23
|
+
margin: 0
|
|
24
|
+
|
|
25
|
+
:where(h1, h2, h3, h4)
|
|
26
|
+
line-height: 1.15
|
|
27
|
+
text-wrap: balance
|
|
28
|
+
|
|
29
|
+
:where(img, picture, video, canvas, svg)
|
|
30
|
+
display: block
|
|
31
|
+
max-width: 100%
|
|
32
|
+
|
|
33
|
+
:where(a)
|
|
34
|
+
color: inherit
|
|
35
|
+
text-decoration-color: var(--border-strong)
|
|
36
|
+
|
|
37
|
+
:where(button, input, select, textarea)
|
|
38
|
+
font: inherit
|
|
39
|
+
color: inherit
|
|
40
|
+
|
|
41
|
+
:where(:focus-visible)
|
|
42
|
+
outline: 2px solid var(--ring)
|
|
43
|
+
outline-offset: 2px
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// CONFIG — the finite vocabulary every fractal draws from.
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// Scales live here as Sass data; resolvers turn a keyword-or-raw value into the
|
|
5
|
+
// right token var() or unit. Fractals never hardcode a px or a token name —
|
|
6
|
+
// they route through space(), radius(), etc. so the system speaks one language.
|
|
7
|
+
// =============================================================================
|
|
8
|
+
|
|
9
|
+
@use 'sass:map'
|
|
10
|
+
@use 'sass:math'
|
|
11
|
+
@use 'sass:meta'
|
|
12
|
+
@use 'sass:list'
|
|
13
|
+
|
|
14
|
+
// --- Scales (mirror the custom properties in _tokens.sass) --------------------
|
|
15
|
+
$breakpoints: (sm: 640px, md: 768px, lg: 1024px, xl: 1240px)
|
|
16
|
+
|
|
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
|
|
19
|
+
$text-steps: xs sm md lg xl 2xl 3xl 4xl
|
|
20
|
+
$shadow-steps: sm md lg
|
|
21
|
+
$surface-roles: bg surface raised
|
|
22
|
+
$ink-roles: primary secondary muted inverse
|
|
23
|
+
|
|
24
|
+
$align-map: (start: flex-start, end: flex-end, center: center, between: space-between, around: space-around, evenly: space-evenly, stretch: stretch, baseline: baseline)
|
|
25
|
+
|
|
26
|
+
// --- Resolvers ----------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
// space(m) → var(--space-m) | space(18) → 18px | space(2rem) → 2rem
|
|
29
|
+
@function space($v)
|
|
30
|
+
@if $v == null
|
|
31
|
+
@return null
|
|
32
|
+
@else if list.index($space-steps, $v)
|
|
33
|
+
@return var(--space-#{$v})
|
|
34
|
+
@else if meta.type-of($v) == number and math.is-unitless($v)
|
|
35
|
+
@return #{$v}px
|
|
36
|
+
@else
|
|
37
|
+
@return $v
|
|
38
|
+
|
|
39
|
+
@function radius($v)
|
|
40
|
+
@if list.index($radius-steps, $v)
|
|
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
|
|
46
|
+
|
|
47
|
+
@function text-size($v)
|
|
48
|
+
@if list.index($text-steps, $v)
|
|
49
|
+
@return var(--text-#{$v})
|
|
50
|
+
@else
|
|
51
|
+
@return $v
|
|
52
|
+
|
|
53
|
+
@function shadow($v)
|
|
54
|
+
@if list.index($shadow-steps, $v)
|
|
55
|
+
@return var(--shadow-#{$v})
|
|
56
|
+
@else
|
|
57
|
+
@return $v
|
|
58
|
+
|
|
59
|
+
@function surface($role)
|
|
60
|
+
@if $role == bg
|
|
61
|
+
@return var(--bg)
|
|
62
|
+
@else
|
|
63
|
+
@return var(--bg-#{$role})
|
|
64
|
+
|
|
65
|
+
@function ink($role)
|
|
66
|
+
@if $role == primary
|
|
67
|
+
@return var(--text-primary)
|
|
68
|
+
@else
|
|
69
|
+
@return var(--text-#{$role})
|
|
70
|
+
|
|
71
|
+
@function align($v)
|
|
72
|
+
$m: map.get($align-map, $v)
|
|
73
|
+
@if $m
|
|
74
|
+
@return $m
|
|
75
|
+
@return $v
|
|
76
|
+
|
|
77
|
+
@function bp($name)
|
|
78
|
+
@return map.get($breakpoints, $name)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// MOLECULE FRACTALS — self-similar: each is a small graph of atom fractals.
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// This is the payoff of the model. A molecule reads like a recipe of atoms,
|
|
5
|
+
// and a component (next layer up) reads like a recipe of molecules. Same move
|
|
6
|
+
// at every scale — that is the "fractal" in fractalstyler2.
|
|
7
|
+
// =============================================================================
|
|
8
|
+
|
|
9
|
+
@use 'config' as *
|
|
10
|
+
@use 'atoms' as *
|
|
11
|
+
@use 'responsive' as *
|
|
12
|
+
|
|
13
|
+
// Stack — vertical rhythm with one shared gap.
|
|
14
|
+
=stack($gap: xs, $x: null)
|
|
15
|
+
+box($x)
|
|
16
|
+
+gap($gap)
|
|
17
|
+
|
|
18
|
+
// Cluster — wrapping row of chips/tags/buttons.
|
|
19
|
+
=cluster($gap: xs, $x: start, $y: center)
|
|
20
|
+
+row($x, $y)
|
|
21
|
+
+wrap
|
|
22
|
+
+gap($gap)
|
|
23
|
+
|
|
24
|
+
// Center — a centered reading column bounded by a measure.
|
|
25
|
+
=center-column($max: var(--measure, 60ch), $pad: s)
|
|
26
|
+
max-width: $max
|
|
27
|
+
+mx-auto
|
|
28
|
+
+px($pad)
|
|
29
|
+
|
|
30
|
+
// Cover — fills a min-height with a centered focal child.
|
|
31
|
+
=cover($min: 100vh, $pad: s)
|
|
32
|
+
+box
|
|
33
|
+
min-height: $min
|
|
34
|
+
+pad($pad)
|
|
35
|
+
> *
|
|
36
|
+
margin-block: 0
|
|
37
|
+
> .center
|
|
38
|
+
+my-auto
|
|
39
|
+
|
|
40
|
+
// Frame — fixed aspect-ratio media box.
|
|
41
|
+
=frame($ratio: 16 / 9)
|
|
42
|
+
aspect-ratio: $ratio
|
|
43
|
+
overflow: hidden
|
|
44
|
+
> :is(img, video, iframe)
|
|
45
|
+
+full
|
|
46
|
+
object-fit: cover
|
|
47
|
+
|
|
48
|
+
// Reel — horizontal scroll-snap rail.
|
|
49
|
+
=reel($gap: xs)
|
|
50
|
+
+row
|
|
51
|
+
+gap($gap)
|
|
52
|
+
overflow-x: auto
|
|
53
|
+
overscroll-behavior-inline: contain
|
|
54
|
+
scroll-snap-type: inline mandatory
|
|
55
|
+
> *
|
|
56
|
+
scroll-snap-align: start
|
|
57
|
+
flex: 0 0 auto
|
|
58
|
+
|
|
59
|
+
// Sidebar — an intrinsic rail beside a fluid main; wraps when tight.
|
|
60
|
+
=with-sidebar($rail: 240px, $gap: s, $min: 60%)
|
|
61
|
+
+row
|
|
62
|
+
+wrap
|
|
63
|
+
+gap($gap)
|
|
64
|
+
> .rail
|
|
65
|
+
flex-basis: $rail
|
|
66
|
+
flex-grow: 1
|
|
67
|
+
> .flow
|
|
68
|
+
flex-basis: 0
|
|
69
|
+
flex-grow: 999
|
|
70
|
+
min-width: $min
|
|
71
|
+
|
|
72
|
+
// Surface — the "material" fractal: skin + elevation in one call.
|
|
73
|
+
// The building block of every card/panel/popover.
|
|
74
|
+
=surface($bg: surface, $pad: s, $radius: 12, $elevation: none)
|
|
75
|
+
+bg($bg)
|
|
76
|
+
+border
|
|
77
|
+
+radius($radius)
|
|
78
|
+
+pad($pad)
|
|
79
|
+
@if $elevation != none
|
|
80
|
+
+shadow($elevation)
|
|
81
|
+
|
|
82
|
+
// Responsive column grid from a per-breakpoint map:
|
|
83
|
+
// +cols((base: 1, sm: 2, lg: 3))
|
|
84
|
+
=cols($map, $gap: s)
|
|
85
|
+
display: grid
|
|
86
|
+
grid-auto-flow: row
|
|
87
|
+
+gap($gap)
|
|
88
|
+
@each $bp, $n in $map
|
|
89
|
+
@if $bp == base
|
|
90
|
+
grid-template-columns: repeat($n, minmax(0, 1fr))
|
|
91
|
+
@else
|
|
92
|
+
+at($bp)
|
|
93
|
+
grid-template-columns: repeat($n, minmax(0, 1fr))
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// RESPONSIVE FRACTALS — the units that let any other fractal grow with the page.
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// These are self-similar too: +at(md) wraps any block of fractal calls in a
|
|
5
|
+
// breakpoint, so a "responsive component" is just a component whose fractals
|
|
6
|
+
// are re-stated inside +at(...).
|
|
7
|
+
// =============================================================================
|
|
8
|
+
|
|
9
|
+
@use 'sass:map'
|
|
10
|
+
@use 'config' as *
|
|
11
|
+
|
|
12
|
+
// +at(md) — mobile-first min-width query by name (sm/md/lg/xl) or raw length.
|
|
13
|
+
=at($name)
|
|
14
|
+
$edge: $name
|
|
15
|
+
@if map.has-key($breakpoints, $name)
|
|
16
|
+
$edge: bp($name)
|
|
17
|
+
@media (min-width: #{$edge})
|
|
18
|
+
@content
|
|
19
|
+
|
|
20
|
+
// +until(md) — max-width query (one below the named edge).
|
|
21
|
+
=until($name)
|
|
22
|
+
$edge: $name
|
|
23
|
+
@if map.has-key($breakpoints, $name)
|
|
24
|
+
$edge: bp($name)
|
|
25
|
+
@media (max-width: #{$edge})
|
|
26
|
+
@content
|
|
27
|
+
|
|
28
|
+
// Container-query fractals — component-scoped responsiveness.
|
|
29
|
+
=cq
|
|
30
|
+
container-type: inline-size
|
|
31
|
+
|
|
32
|
+
=cq-at($width)
|
|
33
|
+
@container (min-width: #{$width})
|
|
34
|
+
@content
|
|
35
|
+
|
|
36
|
+
=cq-until($width)
|
|
37
|
+
@container (max-width: #{$width})
|
|
38
|
+
@content
|
|
39
|
+
|
|
40
|
+
// Motion / preference-aware fractal.
|
|
41
|
+
=reduce-motion
|
|
42
|
+
@media (prefers-reduced-motion: reduce)
|
|
43
|
+
@content
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// TOKENS — the only place literal values live. Everything else is a fractal
|
|
3
|
+
// that consumes these custom properties. Light is the marker-free default so
|
|
4
|
+
// tokens stay defined during SSR and with JS disabled; dark comes from
|
|
5
|
+
// prefers-color-scheme and from an explicit [data-mode='dark'].
|
|
6
|
+
// =============================================================================
|
|
7
|
+
|
|
8
|
+
:root
|
|
9
|
+
color-scheme: light
|
|
10
|
+
|
|
11
|
+
// Fonts
|
|
12
|
+
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif
|
|
13
|
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace
|
|
14
|
+
|
|
15
|
+
// Fluid type scale (Utopia 360→1240)
|
|
16
|
+
--text-xs: 0.75rem
|
|
17
|
+
--text-sm: clamp(0.9375rem, 0.9119rem + 0.1136vw, 1rem)
|
|
18
|
+
--text-md: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)
|
|
19
|
+
--text-lg: clamp(1.35rem, 1.2631rem + 0.3864vw, 1.5625rem)
|
|
20
|
+
--text-xl: clamp(1.62rem, 1.4837rem + 0.6057vw, 1.9531rem)
|
|
21
|
+
--text-2xl: clamp(1.944rem, 1.7405rem + 0.9044vw, 2.4414rem)
|
|
22
|
+
--text-3xl: clamp(2.3328rem, 2.0387rem + 1.3072vw, 3.0518rem)
|
|
23
|
+
--text-4xl: clamp(2.7994rem, 2.384rem + 1.8461vw, 3.8147rem)
|
|
24
|
+
|
|
25
|
+
// Fluid space scale
|
|
26
|
+
--space-3xs: clamp(0.3125rem, 0.3125rem + 0vw, 0.3125rem)
|
|
27
|
+
--space-2xs: clamp(0.5625rem, 0.5369rem + 0.1136vw, 0.625rem)
|
|
28
|
+
--space-xs: clamp(0.875rem, 0.8494rem + 0.1136vw, 0.9375rem)
|
|
29
|
+
--space-s: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem)
|
|
30
|
+
--space-m: clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem)
|
|
31
|
+
--space-l: clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem)
|
|
32
|
+
--space-xl: clamp(3.375rem, 3.2216rem + 0.6818vw, 3.75rem)
|
|
33
|
+
--space-2xl: clamp(4.5rem, 4.2955rem + 0.9091vw, 5rem)
|
|
34
|
+
--space-3xl: clamp(6.75rem, 6.4432rem + 1.3636vw, 7.5rem)
|
|
35
|
+
--space-s-l: clamp(1.125rem, 0.5625rem + 2.5vw, 2.5rem)
|
|
36
|
+
|
|
37
|
+
// Radius
|
|
38
|
+
--radius-0: 0
|
|
39
|
+
--radius-2: 2px
|
|
40
|
+
--radius-4: 4px
|
|
41
|
+
--radius-6: 6px
|
|
42
|
+
--radius-8: 8px
|
|
43
|
+
--radius-12: 12px
|
|
44
|
+
--radius-16: 16px
|
|
45
|
+
--radius-24: 24px
|
|
46
|
+
--radius-full: 9999px
|
|
47
|
+
|
|
48
|
+
// Shadow
|
|
49
|
+
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06)
|
|
50
|
+
--shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08)
|
|
51
|
+
--shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.12)
|
|
52
|
+
|
|
53
|
+
// Layering
|
|
54
|
+
--z-base: 0
|
|
55
|
+
--z-raised: 10
|
|
56
|
+
--z-sticky: 100
|
|
57
|
+
--z-modal: 200
|
|
58
|
+
--z-toast: 300
|
|
59
|
+
|
|
60
|
+
// Layout
|
|
61
|
+
--header-height: 64px
|
|
62
|
+
--footer-height: 56px
|
|
63
|
+
--measure: 65ch
|
|
64
|
+
|
|
65
|
+
// Light palette (marker-free default)
|
|
66
|
+
--bg: #fdfefe
|
|
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)
|
|
79
|
+
|
|
80
|
+
@media (prefers-color-scheme: dark)
|
|
81
|
+
:root:not([data-mode='light'])
|
|
82
|
+
color-scheme: dark
|
|
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)
|
|
96
|
+
|
|
97
|
+
[data-mode='dark']
|
|
98
|
+
color-scheme: dark
|
|
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)
|
|
112
|
+
|
|
113
|
+
[data-mode='light']
|
|
114
|
+
color-scheme: light
|
|
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)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// UTILITY PROJECTION — the *other* way to consume a fractal.
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// A fractal is a mixin. Here we bind a subset of them to class names so they
|
|
5
|
+
// stay usable straight from markup (`class="box gap-m pad-l"`). This is the
|
|
6
|
+
// key idea: utility classes are not a separate system — they are a generated
|
|
7
|
+
// projection of the exact same fractals your components compose. Emit only the
|
|
8
|
+
// atoms you want in markup; delete this whole file if you go SASS-only.
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
@use 'config' as *
|
|
12
|
+
@use 'atoms' as *
|
|
13
|
+
@use 'molecules' as *
|
|
14
|
+
@use 'responsive' as *
|
|
15
|
+
|
|
16
|
+
// --- Flow ------------------------------------------------------------------
|
|
17
|
+
.box
|
|
18
|
+
+box
|
|
19
|
+
.row
|
|
20
|
+
+row
|
|
21
|
+
&.wrap
|
|
22
|
+
+wrap
|
|
23
|
+
|
|
24
|
+
// Alignment modifiers, semantic and axis-aware.
|
|
25
|
+
.box
|
|
26
|
+
&.xcenter
|
|
27
|
+
align-items: center
|
|
28
|
+
&.xleft
|
|
29
|
+
align-items: flex-start
|
|
30
|
+
&.xright
|
|
31
|
+
align-items: flex-end
|
|
32
|
+
&.ycenter
|
|
33
|
+
justify-content: center
|
|
34
|
+
&.ytop
|
|
35
|
+
justify-content: flex-start
|
|
36
|
+
&.ybot
|
|
37
|
+
justify-content: flex-end
|
|
38
|
+
|
|
39
|
+
.row
|
|
40
|
+
&.ycenter
|
|
41
|
+
align-items: center
|
|
42
|
+
&.ytop
|
|
43
|
+
align-items: flex-start
|
|
44
|
+
&.ybot
|
|
45
|
+
align-items: flex-end
|
|
46
|
+
&.xbetween
|
|
47
|
+
justify-content: space-between
|
|
48
|
+
&.xevenly
|
|
49
|
+
justify-content: space-evenly
|
|
50
|
+
&.xleft
|
|
51
|
+
justify-content: flex-start
|
|
52
|
+
&.xright
|
|
53
|
+
justify-content: flex-end
|
|
54
|
+
|
|
55
|
+
// Fixed column grids.
|
|
56
|
+
@for $n from 1 through 6
|
|
57
|
+
.grid-cols-#{$n}
|
|
58
|
+
+grid($n)
|
|
59
|
+
|
|
60
|
+
.center
|
|
61
|
+
+center
|
|
62
|
+
|
|
63
|
+
// --- Spacing scale → gap-* / pad-* -----------------------------------------
|
|
64
|
+
@each $s in $space-steps
|
|
65
|
+
.gap-#{$s}
|
|
66
|
+
+gap($s)
|
|
67
|
+
.pad-#{$s}
|
|
68
|
+
+pad($s)
|
|
69
|
+
.px-#{$s}
|
|
70
|
+
+px($s)
|
|
71
|
+
.py-#{$s}
|
|
72
|
+
+py($s)
|
|
73
|
+
|
|
74
|
+
// --- Radius / shadow / surface ---------------------------------------------
|
|
75
|
+
@each $r in $radius-steps
|
|
76
|
+
.radius-#{$r}
|
|
77
|
+
+radius($r)
|
|
78
|
+
|
|
79
|
+
@each $s in $shadow-steps
|
|
80
|
+
.shadow-#{$s}
|
|
81
|
+
+shadow($s)
|
|
82
|
+
|
|
83
|
+
@each $role in $surface-roles
|
|
84
|
+
.bg-#{$role}
|
|
85
|
+
+bg($role)
|
|
86
|
+
|
|
87
|
+
.border
|
|
88
|
+
+border
|
|
89
|
+
@each $side in top right bottom left
|
|
90
|
+
.border-#{$side}
|
|
91
|
+
+border($side)
|
|
92
|
+
|
|
93
|
+
// --- Type ------------------------------------------------------------------
|
|
94
|
+
@each $t in $text-steps
|
|
95
|
+
.text-#{$t}
|
|
96
|
+
+type($t)
|
|
97
|
+
|
|
98
|
+
.truncate
|
|
99
|
+
+truncate
|
|
100
|
+
|
|
101
|
+
// Semantic type primitives — so you stop retyping `.text-sm .lh15 .muted`.
|
|
102
|
+
.body
|
|
103
|
+
+type(md)
|
|
104
|
+
+leading(1.6)
|
|
105
|
+
+ink(primary)
|
|
106
|
+
.muted
|
|
107
|
+
+ink(secondary)
|
|
108
|
+
.eyebrow
|
|
109
|
+
+type(sm)
|
|
110
|
+
+ink(muted)
|
|
111
|
+
text-transform: uppercase
|
|
112
|
+
letter-spacing: 0.06em
|
|
113
|
+
font-weight: 500
|
|
114
|
+
|
|
115
|
+
// --- Sizing shortcuts ------------------------------------------------------
|
|
116
|
+
.wfull
|
|
117
|
+
+w(100%)
|
|
118
|
+
.hfull
|
|
119
|
+
+h(100%)
|
|
120
|
+
.full
|
|
121
|
+
+full
|
|
122
|
+
.grow
|
|
123
|
+
+grow
|
|
124
|
+
.min0
|
|
125
|
+
+min0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// FRACTALS — the pure API barrel. Emits NO css on its own.
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// In any component partial: @use '../fractals' as *
|
|
5
|
+
// then call fractals freely: +surface(...) +stack(...) space(m) ...
|
|
6
|
+
// =============================================================================
|
|
7
|
+
|
|
8
|
+
@forward 'config'
|
|
9
|
+
@forward 'responsive'
|
|
10
|
+
@forward 'atoms'
|
|
11
|
+
@forward 'molecules'
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const version = "0.0.1";
|
|
2
|
+
export type Mode = 'light' | 'dark';
|
|
3
|
+
/** Set the color mode by writing the [data-mode] marker on <html>. */
|
|
4
|
+
export declare function setMode(mode: Mode): void;
|
|
5
|
+
/** Toggle between light and dark, returning the new mode. */
|
|
6
|
+
export declare function toggleMode(): Mode;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// fractalstyler2 — a fractal-composition styling system for SvelteKit.
|
|
2
|
+
//
|
|
3
|
+
// The product is the SASS in ./fractals and ./components, surfaced as:
|
|
4
|
+
// import 'fractalstyler2/styles' // emit the full stylesheet
|
|
5
|
+
// @use 'fractalstyler2/fractals' as * // compose your own with the mixins
|
|
6
|
+
//
|
|
7
|
+
// This entry ships only tiny runtime helpers. There is deliberately no CSS-in-JS
|
|
8
|
+
// and no component coupling — the styling layer is framework-agnostic SASS.
|
|
9
|
+
export const version = '0.0.1';
|
|
10
|
+
/** Set the color mode by writing the [data-mode] marker on <html>. */
|
|
11
|
+
export function setMode(mode) {
|
|
12
|
+
if (typeof document === 'undefined')
|
|
13
|
+
return;
|
|
14
|
+
document.documentElement.setAttribute('data-mode', mode);
|
|
15
|
+
}
|
|
16
|
+
/** Toggle between light and dark, returning the new mode. */
|
|
17
|
+
export function toggleMode() {
|
|
18
|
+
const current = typeof document !== 'undefined' ? document.documentElement.getAttribute('data-mode') : null;
|
|
19
|
+
const next = current === 'dark' ? 'light' : 'dark';
|
|
20
|
+
setMode(next);
|
|
21
|
+
return next;
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// STYLES ENTRY — the full emitted stylesheet in cascade order.
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// 1. tokens the CSS custom properties (only literal values)
|
|
5
|
+
// 2. utilities atom fractals projected to markup classes (optional layer)
|
|
6
|
+
// 3. blocks components composed from fractals
|
|
7
|
+
// 4. layouts page templates composed from fractals
|
|
8
|
+
//
|
|
9
|
+
// Consume from a SvelteKit app: import 'fractalstyler2/styles'
|
|
10
|
+
// or, from SASS: @use 'fractalstyler2/styles'
|
|
11
|
+
// The fractal mixins/functions themselves emit nothing until called, so output
|
|
12
|
+
// contains only what you use.
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
@use '../fractals/tokens'
|
|
16
|
+
@use '../fractals/base'
|
|
17
|
+
@use '../fractals/utilities'
|
|
18
|
+
@use '../components/blocks'
|
|
19
|
+
@use '../components/layouts'
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fractalstyler2",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A fractal-composition styling system for SvelteKit — every style unit is a SASS mixin, and components and layouts are recipes of smaller fractals.",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "vite dev",
|
|
7
|
+
"build": "vite build && npm run prepack",
|
|
8
|
+
"preview": "vite preview",
|
|
9
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
10
|
+
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
11
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
12
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"!dist/**/*.test.*",
|
|
17
|
+
"!dist/**/*.spec.*"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": [
|
|
20
|
+
"**/*.css"
|
|
21
|
+
],
|
|
22
|
+
"svelte": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"svelte": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./styles": "./dist/styles/index.sass",
|
|
31
|
+
"./fractals": "./dist/fractals/index.sass",
|
|
32
|
+
"./tokens": "./dist/fractals/_tokens.sass",
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"svelte": "^5.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@sveltejs/adapter-auto": "^7.0.1",
|
|
40
|
+
"@sveltejs/kit": "^2.63.0",
|
|
41
|
+
"@sveltejs/package": "^2.5.8",
|
|
42
|
+
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
43
|
+
"publint": "^0.3.21",
|
|
44
|
+
"sass": "^1.102.0",
|
|
45
|
+
"svelte": "^5.56.1",
|
|
46
|
+
"svelte-check": "^4.6.0",
|
|
47
|
+
"typescript": "^6.0.3",
|
|
48
|
+
"vite": "^8.0.16"
|
|
49
|
+
},
|
|
50
|
+
"keywords": [
|
|
51
|
+
"svelte",
|
|
52
|
+
"sveltekit",
|
|
53
|
+
"sass",
|
|
54
|
+
"css",
|
|
55
|
+
"design-system",
|
|
56
|
+
"mixins",
|
|
57
|
+
"fractal"
|
|
58
|
+
]
|
|
59
|
+
}
|