@motion-proto/live-tokens 0.94.3 → 0.96.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/.claude/skills/live-tokens-create-page/SKILL.md +7 -3
- package/CHANGELOG.md +115 -0
- package/README.md +6 -6
- package/dist-plugin/{chunk-7XSUHJUH.js → chunk-4J6JPSVS.js} +53 -2
- package/dist-plugin/{chunk-W5VQV32K.js → chunk-GYRULB2X.js} +1 -1
- package/dist-plugin/{chunk-ONYAIXCZ.js → chunk-QQASYPW7.js} +32 -9
- package/dist-plugin/index.cjs +598 -17
- package/dist-plugin/index.d.cts +9 -1
- package/dist-plugin/index.d.ts +9 -1
- package/dist-plugin/index.js +513 -6
- package/dist-plugin/migrateData/index.cjs +32 -9
- package/dist-plugin/migrateData/index.js +2 -2
- package/dist-plugin/setColors/index.cjs +32 -22
- package/dist-plugin/setColors/index.js +2 -15
- package/dist-plugin/setGeometry/index.cjs +32 -9
- package/dist-plugin/setGeometry/index.js +1 -1
- package/dist-plugin/tokensCssMigrations/index.cjs +53 -2
- package/dist-plugin/tokensCssMigrations/index.d.cts +9 -2
- package/dist-plugin/tokensCssMigrations/index.d.ts +9 -2
- package/dist-plugin/tokensCssMigrations/index.js +1 -1
- package/package.json +8 -3
- package/src/demo/Demo.svelte +39 -5
- package/src/demo/DemoBar.svelte +23 -5
- package/src/demo/FloatingTokenTags.css +33 -4
- package/src/demo/FloatingTokenTags.svelte +81 -68
- package/src/demo/Section.svelte +1 -7
- package/src/demo/SourceLinks.svelte +5 -4
- package/src/demo/TestingLoops.svelte +167 -35
- package/src/demo/evalResults.ts +7 -7
- package/src/demo/sections/SectionClaude.svelte +20 -13
- package/src/demo/sections/SectionFooter.svelte +4 -5
- package/src/demo/sections/SectionGetStarted.svelte +5 -6
- package/src/demo/sections/SectionHero.svelte +43 -23
- package/src/demo/sections/SectionKit.svelte +141 -45
- package/src/demo/sections/SectionLayers.svelte +3 -3
- package/src/demo/sections/SectionOffering.svelte +8 -23
- package/src/editor/bootstrap.ts +12 -6
- package/src/editor/component-editor/ImageEditor.svelte +1 -1
- package/src/editor/component-editor/ImageLightboxEditor.svelte +3 -4
- package/src/editor/component-editor/registration.ts +36 -0
- package/src/editor/component-editor/registry.ts +5 -12
- package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +11 -2
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +7 -0
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +1 -2
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +17 -2
- package/src/editor/core/store/editorPersistence.ts +6 -1
- package/src/editor/core/store/editorRenderer.ts +1 -4
- package/src/editor/core/store/editorStore.ts +5 -9
- package/src/editor/core/store/editorTypes.ts +2 -2
- package/src/editor/core/themes/migrations/2026-10-15-responsive-columns.ts +40 -0
- package/src/editor/core/themes/migrations/index.ts +2 -0
- package/src/editor/core/themes/slices/columns.ts +112 -42
- package/src/editor/docs/Docs.svelte +25 -22
- package/src/editor/docs/chapters.ts +1 -0
- package/src/editor/docs/content/editing-tokens.md +5 -3
- package/src/editor/docs/content/production-css.md +116 -0
- package/src/editor/docs/content/themes-workflow.md +2 -0
- package/src/editor/docs/content.generated.ts +3 -2
- package/src/editor/index.ts +2 -1
- package/src/editor/overlay/LiveEditorOverlay.svelte +67 -19
- package/src/editor/overlay/LiveTokensRouter.svelte +8 -4
- package/src/editor/pages/Editor.svelte +15 -1
- package/src/editor/pages/EditorShell.svelte +312 -99
- package/src/editor/skill-atlas/SkillAtlas.svelte +111 -24
- package/src/editor/skill-atlas/TreeCanvas.svelte +14 -0
- package/src/editor/skill-atlas/skillSources.generated.ts +1 -1
- package/src/editor/skill-atlas/trees/create-page.ts +24 -24
- package/src/editor/skill-atlas/wireLayout.ts +62 -0
- package/src/editor/ui/BezierCurveEditor.svelte +3 -0
- package/src/editor/ui/FontStackEditor.svelte +2 -2
- package/src/editor/ui/PaletteEditor.svelte +64 -6
- package/src/editor/ui/UIPaletteSelector.svelte +2 -0
- package/src/editor/ui/colors/ColorsTab.svelte +5 -2
- package/src/editor/ui/palette/OverridesPanel.svelte +1 -0
- package/src/editor/ui/palette/ScaleCurveEditor.svelte +127 -18
- package/src/editor/ui/sections/ColumnsSection.svelte +79 -49
- package/src/editor/ui/sections/ShadowsSection.svelte +14 -0
- package/src/editor/ui/sections/WashesSection.svelte +11 -0
- package/src/editor/ui/sketch/SketchTab.svelte +1 -1
- package/src/live-tokens/data/colors-and-type/autumn.json +1 -4
- package/src/live-tokens/data/colors-and-type/default.json +1 -4
- package/src/live-tokens/data/colors-and-type/halloween.json +1 -4
- package/src/live-tokens/data/colors-and-type/midnight-study.json +1 -4
- package/src/live-tokens/data/colors-and-type/ocean.json +1 -4
- package/src/live-tokens/data/colors-and-type/royal-velvet.json +1 -4
- package/src/live-tokens/data/colors-and-type/sketchy.json +1 -4
- package/src/live-tokens/data/colors-and-type/spring-meadow.json +1 -4
- package/src/live-tokens/data/colors-and-type/sunset.json +1 -4
- package/src/live-tokens/data/themes/autumn.json +1 -4
- package/src/live-tokens/data/themes/halloween.json +1 -4
- package/src/live-tokens/data/themes/midnight-study.json +1 -4
- package/src/live-tokens/data/themes/ocean.json +1 -4
- package/src/live-tokens/data/themes/royal-velvet.json +1 -4
- package/src/live-tokens/data/themes/sketchy.json +1 -4
- package/src/live-tokens/data/themes/spring-meadow.json +1 -4
- package/src/live-tokens/data/themes/sunset.json +1 -4
- package/src/live-tokens/data/tokens.generated.css +1 -4
- package/src/system/components/ImageLightbox.svelte +9 -2
- package/src/system/components/SectionDivider.svelte +4 -0
- package/src/system/styles/tokens.css +30 -4
- package/template/src/main.ts +2 -2
- package/template/src/pages/Home.svelte +11 -8
- package/template/vite.config.ts +7 -3
- package/src/system/assets/offering.webp +0 -0
- /package/dist-plugin/{chunk-O6GQ6GBH.js → chunk-A4TBP4MM.js} +0 -0
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
<p class="hero-tagline">
|
|
31
31
|
Design system authoring for Svelte.<br />
|
|
32
|
-
Edit tokens and components directly in the browser.<br />Ship plain CSS.
|
|
32
|
+
Edit tokens and components directly in the browser.<br class="desktop-break" /> Ship plain CSS.
|
|
33
33
|
</p>
|
|
34
34
|
</div>
|
|
35
35
|
|
|
@@ -63,18 +63,20 @@
|
|
|
63
63
|
.hero {
|
|
64
64
|
grid-column: 1 / -1;
|
|
65
65
|
position: relative;
|
|
66
|
-
padding: var(--space-48) 0
|
|
66
|
+
padding: var(--space-48) 0 0;
|
|
67
67
|
display: grid;
|
|
68
|
-
grid-template-columns:
|
|
69
|
-
column-gap: var(--columns-gutter);
|
|
68
|
+
grid-template-columns: subgrid;
|
|
70
69
|
}
|
|
71
70
|
|
|
71
|
+
/* The title runs across the kite on the full content width; the tagline
|
|
72
|
+
keeps the first five columns. */
|
|
72
73
|
.hero-text {
|
|
73
|
-
grid-column:
|
|
74
|
+
grid-column: var(--kit-content);
|
|
74
75
|
grid-row: 1;
|
|
75
|
-
display:
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
display: grid;
|
|
77
|
+
grid-template-columns: subgrid;
|
|
78
|
+
align-content: start;
|
|
79
|
+
row-gap: var(--space-16);
|
|
78
80
|
position: relative;
|
|
79
81
|
padding: var(--space-96) 0 0;
|
|
80
82
|
}
|
|
@@ -89,36 +91,37 @@
|
|
|
89
91
|
|
|
90
92
|
.hero-title {
|
|
91
93
|
font-family: var(--font-display);
|
|
92
|
-
font-size: var(--font-size
|
|
94
|
+
font-size: var(--display-xl-font-size);
|
|
93
95
|
font-weight: var(--font-weight-semibold);
|
|
94
96
|
font-variation-settings: 'opsz' 144, 'SOFT' 30;
|
|
95
97
|
color: var(--text-primary);
|
|
96
98
|
line-height: .9;
|
|
97
99
|
margin: 0;
|
|
100
|
+
grid-column: 1 / -1;
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
.hero-italic {
|
|
101
104
|
font-family: var(--heading-xl-font-family);
|
|
102
|
-
font-size: var(--font-size
|
|
105
|
+
font-size: var(--display-lg-font-size);
|
|
103
106
|
font-weight: var(--font-weight-normal);
|
|
104
107
|
color: var(--text-brand);
|
|
105
108
|
font-variation-settings: 'opsz' 144, 'SOFT' 100;
|
|
106
|
-
white-space: nowrap;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
.hero-tagline {
|
|
110
112
|
font-family: var(--body-md-font-family);
|
|
111
|
-
font-size: var(--font-size
|
|
113
|
+
font-size: var(--body-2xl-font-size);
|
|
112
114
|
color: var(--text-primary);
|
|
113
115
|
line-height: 1.2;
|
|
114
|
-
|
|
116
|
+
grid-column: 1 / span 5;
|
|
115
117
|
margin: var(--space-8) 0 0;
|
|
118
|
+
text-wrap: pretty;
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
/* One row: the CTAs on the left rail, the credits on the right, sharing a
|
|
119
122
|
top edge so the buttons sit on the brand-mark line. */
|
|
120
123
|
.hero-actions {
|
|
121
|
-
grid-column:
|
|
124
|
+
grid-column: var(--kit-content);
|
|
122
125
|
grid-row: 2;
|
|
123
126
|
display: grid;
|
|
124
127
|
grid-template-columns: max-content 1fr;
|
|
@@ -148,7 +151,7 @@
|
|
|
148
151
|
|
|
149
152
|
.hero-byline {
|
|
150
153
|
font-family: var(--font-sans);
|
|
151
|
-
font-size: var(--font-size
|
|
154
|
+
font-size: var(--body-md-font-size);
|
|
152
155
|
font-weight: var(--font-weight-normal);
|
|
153
156
|
line-height: var(--line-height-normal);
|
|
154
157
|
color: var(--text-secondary);
|
|
@@ -164,20 +167,31 @@
|
|
|
164
167
|
text-decoration: underline;
|
|
165
168
|
}
|
|
166
169
|
|
|
167
|
-
@media (max-width:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
@media (max-width: 1200px) {
|
|
171
|
+
/* Stacked, the illustration sits between the title and the tagline, so the
|
|
172
|
+
wrapper steps aside and each takes its own row of the hero grid. */
|
|
173
|
+
.hero-text {
|
|
174
|
+
display: contents;
|
|
175
|
+
}
|
|
176
|
+
.hero-title {
|
|
170
177
|
grid-row: 1;
|
|
171
|
-
height: 28rem;
|
|
172
178
|
}
|
|
173
|
-
.hero-
|
|
179
|
+
.hero-kite {
|
|
174
180
|
grid-column: 1 / -1;
|
|
175
181
|
grid-row: 2;
|
|
176
|
-
|
|
182
|
+
height: 28rem;
|
|
183
|
+
margin-top: var(--space-32);
|
|
177
184
|
}
|
|
178
|
-
.hero-
|
|
185
|
+
.hero-tagline {
|
|
179
186
|
grid-column: 1 / -1;
|
|
180
187
|
grid-row: 3;
|
|
188
|
+
margin-top: var(--space-16);
|
|
189
|
+
}
|
|
190
|
+
.desktop-break {
|
|
191
|
+
display: none;
|
|
192
|
+
}
|
|
193
|
+
.hero-actions {
|
|
194
|
+
grid-row: 4;
|
|
181
195
|
grid-template-columns: minmax(0, 1fr);
|
|
182
196
|
row-gap: var(--space-16);
|
|
183
197
|
margin-top: var(--space-12);
|
|
@@ -189,9 +203,15 @@
|
|
|
189
203
|
}
|
|
190
204
|
}
|
|
191
205
|
|
|
206
|
+
/* A phone wraps the second line, so the title steps up a size and the two
|
|
207
|
+
lines get a gap to tell them apart. */
|
|
192
208
|
@media (max-width: 600px) {
|
|
193
209
|
.hero-title {
|
|
194
|
-
font-size: var(--font-size
|
|
210
|
+
font-size: var(--display-2xl-font-size);
|
|
211
|
+
}
|
|
212
|
+
.hero-italic {
|
|
213
|
+
display: block;
|
|
214
|
+
margin-top: var(--space-16);
|
|
195
215
|
}
|
|
196
216
|
.hero-controls {
|
|
197
217
|
grid-template-columns: minmax(0, 1fr);
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
import Button from '../../system/components/Button.svelte';
|
|
3
3
|
import SectionDivider from '../../system/components/SectionDivider.svelte';
|
|
4
4
|
import SegmentedControl from '../../system/components/SegmentedControl.svelte';
|
|
5
|
+
import { MediaQuery } from 'svelte/reactivity';
|
|
5
6
|
import Panel from '../../system/components/Panel.svelte';
|
|
6
7
|
import Section from '../Section.svelte';
|
|
7
8
|
|
|
8
9
|
let { prose = true }: { prose?: boolean } = $props();
|
|
9
10
|
|
|
11
|
+
// Matches the demo's phone breakpoint in Demo.svelte.
|
|
12
|
+
const phone = new MediaQuery('max-width: 600px');
|
|
13
|
+
|
|
10
14
|
type Override = 'shape' | 'color';
|
|
11
15
|
|
|
12
16
|
let shape = $state(false);
|
|
@@ -35,39 +39,98 @@
|
|
|
35
39
|
return out;
|
|
36
40
|
})();
|
|
37
41
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
'
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
42
|
+
const FAMILIES = ['brand', 'accent', 'special', 'info', 'success', 'warning', 'danger'] as const;
|
|
43
|
+
type Family = (typeof FAMILIES)[number];
|
|
44
|
+
// The family each swapped variant paints from. Outline follows primary.
|
|
45
|
+
const SOURCES = { primary: 'brand', success: 'success', warning: 'warning', danger: 'danger' } as const;
|
|
46
|
+
type Swapped = keyof typeof SOURCES;
|
|
47
|
+
// Themes can give two families near-identical hues (Autumn's warning and
|
|
48
|
+
// accent share one), so a fixed swap can land on the colour it replaces.
|
|
49
|
+
const MIN_HUE_SHIFT = 60;
|
|
50
|
+
const SAME_HUE = 15;
|
|
51
|
+
|
|
52
|
+
let colorMap = $state<Record<Swapped, Family> | null>(null);
|
|
53
|
+
let colorStep = 0;
|
|
54
|
+
|
|
55
|
+
function familyHues(): Record<Family, number> {
|
|
56
|
+
const probe = document.createElement('span');
|
|
57
|
+
document.body.appendChild(probe);
|
|
58
|
+
const hues = {} as Record<Family, number>;
|
|
59
|
+
for (const family of FAMILIES) {
|
|
60
|
+
probe.style.color = `var(--color-${family}-500)`;
|
|
61
|
+
hues[family] = Number(/oklch\(\S+ \S+ ([\d.]+)/.exec(getComputedStyle(probe).color)?.[1] ?? 0);
|
|
62
|
+
}
|
|
63
|
+
probe.remove();
|
|
64
|
+
return hues;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function hueGap(a: number, b: number): number {
|
|
68
|
+
const d = Math.abs(a - b) % 360;
|
|
69
|
+
return Math.min(d, 360 - d);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Each variant takes a family far enough round the wheel from its own,
|
|
73
|
+
* on a hue no other variant has where the theme has enough, rotated by
|
|
74
|
+
* `step`. Hue, not family: two families can share one. */
|
|
75
|
+
function pickColorMap(step: number): Record<Swapped, Family> {
|
|
76
|
+
const hues = familyHues();
|
|
77
|
+
const used: number[] = [];
|
|
78
|
+
const map = {} as Record<Swapped, Family>;
|
|
79
|
+
for (const [variant, source] of Object.entries(SOURCES) as [Swapped, Family][]) {
|
|
80
|
+
const byGap = FAMILIES.filter((f) => f !== source).sort((a, b) => hueGap(hues[b], hues[source]) - hueGap(hues[a], hues[source]));
|
|
81
|
+
const far = byGap.filter((f) => hueGap(hues[f], hues[source]) >= MIN_HUE_SHIFT);
|
|
82
|
+
const pool = far.length > 0 ? far : byGap.slice(0, 1);
|
|
83
|
+
const rotated = pool.map((_, i) => pool[(i + step) % pool.length]);
|
|
84
|
+
map[variant] = rotated.find((f) => used.every((h) => hueGap(h, hues[f]) >= SAME_HUE)) ?? rotated[0];
|
|
85
|
+
used.push(hues[map[variant]]);
|
|
86
|
+
}
|
|
87
|
+
return map;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function nextColorMap(): Record<Swapped, Family> {
|
|
91
|
+
const current = JSON.stringify(colorMap);
|
|
92
|
+
for (let tries = 0; tries < FAMILIES.length; tries++) {
|
|
93
|
+
const map = pickColorMap(++colorStep);
|
|
94
|
+
if (JSON.stringify(map) !== current) return map;
|
|
95
|
+
}
|
|
96
|
+
return pickColorMap(colorStep);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function colorOverride({ primary: p, success: s, warning: w, danger: d }: Record<Swapped, Family>): Record<string, string> {
|
|
100
|
+
return {
|
|
101
|
+
'--button-primary-surface': `var(--surface-${p}-high)`,
|
|
102
|
+
'--button-primary-border': `var(--border-${p})`,
|
|
103
|
+
'--button-primary-hover-surface': `var(--surface-${p}-higher)`,
|
|
104
|
+
'--button-primary-hover-border': `var(--border-${p}-strong)`,
|
|
105
|
+
|
|
106
|
+
'--button-outline-border': `var(--border-${p})`,
|
|
107
|
+
'--button-outline-text': `var(--text-${p})`,
|
|
108
|
+
'--button-outline-hover-border': `var(--border-${p}-strong)`,
|
|
109
|
+
|
|
110
|
+
'--button-success-surface': `var(--surface-${s}-low)`,
|
|
111
|
+
'--button-success-border': `var(--border-${s})`,
|
|
112
|
+
'--button-success-text': `var(--text-${s})`,
|
|
113
|
+
'--button-success-hover-surface': `var(--surface-${s}-higher)`,
|
|
114
|
+
'--button-success-hover-border': `var(--border-${s}-strong)`,
|
|
115
|
+
|
|
116
|
+
'--button-warning-surface': `var(--surface-${w}-low)`,
|
|
117
|
+
'--button-warning-border': `var(--border-${w})`,
|
|
118
|
+
'--button-warning-text': `var(--text-${w})`,
|
|
119
|
+
'--button-warning-hover-surface': `var(--surface-${w}-high)`,
|
|
120
|
+
'--button-warning-hover-border': `var(--border-${w}-medium)`,
|
|
121
|
+
|
|
122
|
+
'--button-danger-surface': `var(--surface-${d}-low)`,
|
|
123
|
+
'--button-danger-border': `var(--border-${d})`,
|
|
124
|
+
'--button-danger-text': `var(--text-${d})`,
|
|
125
|
+
'--button-danger-hover-surface': `var(--surface-${d}-high)`,
|
|
126
|
+
'--button-danger-hover-border': `var(--border-${d}-medium)`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
66
129
|
|
|
67
130
|
let stageStyle = $derived.by(() => {
|
|
68
131
|
const merged: Record<string, string> = {};
|
|
69
132
|
if (shape) Object.assign(merged, shapeOverride);
|
|
70
|
-
if (color) Object.assign(merged, colorOverride);
|
|
133
|
+
if (color && colorMap) Object.assign(merged, colorOverride(colorMap));
|
|
71
134
|
return Object.entries(merged)
|
|
72
135
|
.map(([k, v]) => `${k}: ${v}`)
|
|
73
136
|
.join('; ');
|
|
@@ -96,9 +159,11 @@
|
|
|
96
159
|
lastApplied = shape ? 'shape' : color ? 'color' : null;
|
|
97
160
|
return;
|
|
98
161
|
}
|
|
162
|
+
// Every press of Color is a new swap, so it never toggles back off.
|
|
99
163
|
if (v === 'color') {
|
|
100
|
-
|
|
101
|
-
|
|
164
|
+
colorMap = nextColorMap();
|
|
165
|
+
color = true;
|
|
166
|
+
lastApplied = 'color';
|
|
102
167
|
}
|
|
103
168
|
}
|
|
104
169
|
</script>
|
|
@@ -156,7 +221,7 @@
|
|
|
156
221
|
<div class="swap-block">
|
|
157
222
|
<div class="swap-control">
|
|
158
223
|
<SectionDivider title="Swap tokens with a click" variant="sm" />
|
|
159
|
-
<SegmentedControl {segments} value={activeSegment} onchange={handleSelect} />
|
|
224
|
+
<SegmentedControl {segments} value={activeSegment} onchange={handleSelect} size={phone.current ? 'small' : 'default'} />
|
|
160
225
|
</div>
|
|
161
226
|
|
|
162
227
|
<Panel style={stageStyle}>
|
|
@@ -184,7 +249,7 @@
|
|
|
184
249
|
columns and the row stops at column 9, leaving column 10 open on the right. */
|
|
185
250
|
.kit-grid {
|
|
186
251
|
display: grid;
|
|
187
|
-
grid-template-columns: repeat(calc(var(--columns-count) - 2), 1fr);
|
|
252
|
+
grid-template-columns: repeat(calc(var(--columns-count) - 2), minmax(0, 1fr));
|
|
188
253
|
column-gap: var(--columns-gutter);
|
|
189
254
|
row-gap: var(--space-32);
|
|
190
255
|
}
|
|
@@ -202,7 +267,8 @@
|
|
|
202
267
|
padding: 0 0 var(--space-12);
|
|
203
268
|
}
|
|
204
269
|
|
|
205
|
-
/* Title
|
|
270
|
+
/* Title and body match the Card in the Layers section at desktop sizes, and
|
|
271
|
+
take the text styles' phone sizes below it. */
|
|
206
272
|
.kit-icon {
|
|
207
273
|
font-size: var(--icon-size-2xl);
|
|
208
274
|
color: var(--text-primary);
|
|
@@ -211,7 +277,7 @@
|
|
|
211
277
|
.kit-title {
|
|
212
278
|
color: var(--text-primary);
|
|
213
279
|
font-family: var(--font-sans);
|
|
214
|
-
font-size: var(--font-size
|
|
280
|
+
font-size: var(--heading-md-font-size);
|
|
215
281
|
font-weight: var(--font-weight-medium);
|
|
216
282
|
line-height: var(--line-height-normal);
|
|
217
283
|
}
|
|
@@ -219,7 +285,7 @@
|
|
|
219
285
|
.kit-body {
|
|
220
286
|
padding: var(--space-12) 0 0;
|
|
221
287
|
font-family: var(--font-sans);
|
|
222
|
-
font-size: var(--font-size
|
|
288
|
+
font-size: var(--body-xl-font-size);
|
|
223
289
|
line-height: var(--line-height-normal);
|
|
224
290
|
font-weight: var(--font-weight-normal);
|
|
225
291
|
color: var(--text-secondary);
|
|
@@ -270,7 +336,7 @@
|
|
|
270
336
|
--sketch-hatch-color: var(--color-brand-500);
|
|
271
337
|
}
|
|
272
338
|
|
|
273
|
-
/* The head only carries a surface of its own when stacked (see the
|
|
339
|
+
/* The head only carries a surface of its own when stacked (see the 1200
|
|
274
340
|
breakpoint). Here it has none, and --sketch-fill inherits, so without this
|
|
275
341
|
the layer would draw the lead's wash a second time behind the head. */
|
|
276
342
|
.kit-lead .kit-head {
|
|
@@ -288,14 +354,14 @@
|
|
|
288
354
|
}
|
|
289
355
|
|
|
290
356
|
.kit-lead .kit-title {
|
|
291
|
-
font-size: var(--font-size
|
|
357
|
+
font-size: var(--heading-xl-font-size);
|
|
292
358
|
font-weight: var(--font-weight-bold);
|
|
293
359
|
}
|
|
294
360
|
|
|
295
361
|
.kit-lead .kit-body {
|
|
296
362
|
grid-column: 4 / span 6;
|
|
297
363
|
padding: var(--space-32) 0;
|
|
298
|
-
font-size: var(--font-size
|
|
364
|
+
font-size: var(--body-2xl-font-size);
|
|
299
365
|
color: var(--text-primary);
|
|
300
366
|
}
|
|
301
367
|
|
|
@@ -304,7 +370,7 @@
|
|
|
304
370
|
.kit-preview {
|
|
305
371
|
margin-top: var(--space-32);
|
|
306
372
|
display: grid;
|
|
307
|
-
grid-template-columns: repeat(calc(var(--columns-count) - 2), 1fr);
|
|
373
|
+
grid-template-columns: repeat(calc(var(--columns-count) - 2), minmax(0, 1fr));
|
|
308
374
|
column-gap: var(--columns-gutter);
|
|
309
375
|
}
|
|
310
376
|
|
|
@@ -349,7 +415,7 @@
|
|
|
349
415
|
.stage-eyebrow {
|
|
350
416
|
align-self: center;
|
|
351
417
|
font-family: var(--font-sans);
|
|
352
|
-
font-size: var(--font-size
|
|
418
|
+
font-size: var(--body-md-font-size);
|
|
353
419
|
font-weight: var(--font-weight-medium);
|
|
354
420
|
letter-spacing: var(--letter-spacing-wide);
|
|
355
421
|
text-transform: uppercase;
|
|
@@ -373,9 +439,10 @@
|
|
|
373
439
|
justify-content: center;
|
|
374
440
|
}
|
|
375
441
|
|
|
376
|
-
@media (max-width:
|
|
377
|
-
.kit-grid
|
|
378
|
-
|
|
442
|
+
@media (max-width: 1200px) {
|
|
443
|
+
.kit-grid,
|
|
444
|
+
.kit-preview {
|
|
445
|
+
grid-template-columns: minmax(0, 1fr);
|
|
379
446
|
}
|
|
380
447
|
.kit-item {
|
|
381
448
|
grid-column: auto;
|
|
@@ -404,7 +471,36 @@
|
|
|
404
471
|
}
|
|
405
472
|
.kit-lead .kit-body {
|
|
406
473
|
padding: var(--space-16) var(--space-20);
|
|
407
|
-
font-size: var(--font-size
|
|
474
|
+
font-size: var(--body-xl-font-size);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/* Phone: the lead's text lines up with the page margin, and the head's tint
|
|
479
|
+
bleeds out to the screen edges. The tint is a border-image outset, which
|
|
480
|
+
paints past the box without widening the section. The swap control spans
|
|
481
|
+
the column, its segments sharing the width. */
|
|
482
|
+
@media (max-width: 600px) {
|
|
483
|
+
.swap-control {
|
|
484
|
+
margin-top: var(--space-8);
|
|
485
|
+
flex-direction: column;
|
|
486
|
+
align-items: stretch;
|
|
487
|
+
gap: var(--space-8);
|
|
488
|
+
}
|
|
489
|
+
.swap-control :global(.segmented-control) {
|
|
490
|
+
display: flex;
|
|
491
|
+
}
|
|
492
|
+
.swap-control :global(.segment) {
|
|
493
|
+
flex: 1 1 0;
|
|
494
|
+
min-width: 0;
|
|
495
|
+
justify-content: center;
|
|
496
|
+
}
|
|
497
|
+
.kit-lead .kit-head {
|
|
498
|
+
padding-inline: 0;
|
|
499
|
+
background: none;
|
|
500
|
+
border-image: conic-gradient(var(--kit-head-tint) 0 0) fill 0 / 0 / 0 var(--columns-margin);
|
|
501
|
+
}
|
|
502
|
+
.kit-lead .kit-body {
|
|
503
|
+
padding-inline: 0;
|
|
408
504
|
}
|
|
409
505
|
}
|
|
410
506
|
</style>
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
<style>
|
|
29
29
|
.arch {
|
|
30
30
|
display: grid;
|
|
31
|
-
grid-template-columns: repeat(2, 1fr);
|
|
31
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
32
32
|
column-gap: var(--columns-gutter);
|
|
33
33
|
row-gap: var(--space-32);
|
|
34
34
|
}
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
border-radius: var(--radius-sm);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
@media (max-width:
|
|
49
|
+
@media (max-width: 1200px) {
|
|
50
50
|
.arch {
|
|
51
|
-
grid-template-columns: 1fr;
|
|
51
|
+
grid-template-columns: minmax(0, 1fr);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
</style>
|
|
@@ -17,13 +17,15 @@
|
|
|
17
17
|
.offering {
|
|
18
18
|
grid-column: 1 / -1;
|
|
19
19
|
display: grid;
|
|
20
|
-
grid-template-columns:
|
|
21
|
-
column-gap: var(--columns-gutter);
|
|
20
|
+
grid-template-columns: subgrid;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
.offering-quote {
|
|
25
|
-
grid-column:
|
|
24
|
+
grid-column: var(--kit-narrow);
|
|
25
|
+
justify-self: center;
|
|
26
|
+
max-width: 24em;
|
|
26
27
|
margin: 0;
|
|
28
|
+
font-size: var(--heading-lg-font-size);
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
.offering-quote blockquote {
|
|
@@ -38,12 +40,13 @@
|
|
|
38
40
|
|
|
39
41
|
.offering-quote p {
|
|
40
42
|
font-family: var(--font-serif);
|
|
41
|
-
font-size: var(--font-size
|
|
43
|
+
font-size: var(--heading-lg-font-size);
|
|
42
44
|
font-weight: var(--font-weight-normal);
|
|
43
45
|
color: var(--text-primary);
|
|
44
46
|
line-height: 1.2;
|
|
45
47
|
margin: 0;
|
|
46
48
|
hyphens: none;
|
|
49
|
+
text-wrap: pretty;
|
|
47
50
|
text-indent: -0.55em;
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -65,29 +68,11 @@
|
|
|
65
68
|
margin-top: 0;
|
|
66
69
|
font-family: var(--font-serif);
|
|
67
70
|
font-weight: var(--font-weight-normal);
|
|
68
|
-
font-size: var(--font-size
|
|
71
|
+
font-size: var(--heading-lg-font-size);
|
|
69
72
|
font-variation-settings: 'opsz' 144, 'SOFT' 100;
|
|
70
73
|
letter-spacing: 0.01em;
|
|
71
74
|
color: var(--text-primary);
|
|
72
75
|
text-align: right;
|
|
73
76
|
padding-right: 0.4em;
|
|
74
77
|
}
|
|
75
|
-
|
|
76
|
-
@media (max-width: 960px) {
|
|
77
|
-
.offering-quote {
|
|
78
|
-
grid-column: 2 / -2;
|
|
79
|
-
}
|
|
80
|
-
.offering-quote p {
|
|
81
|
-
font-size: var(--font-size-xl);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
@media (max-width: 600px) {
|
|
86
|
-
.offering-quote {
|
|
87
|
-
grid-column: 1 / -1;
|
|
88
|
-
}
|
|
89
|
-
.offering-quote p {
|
|
90
|
-
font-size: var(--font-size-4xl);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
78
|
</style>
|
package/src/editor/bootstrap.ts
CHANGED
|
@@ -18,11 +18,8 @@ import { mount, type Component } from 'svelte';
|
|
|
18
18
|
import * as cssVarSync from './core/cssVarSync';
|
|
19
19
|
import * as router from './core/routing/router';
|
|
20
20
|
import * as columnsOverlay from './overlay/columnsOverlay';
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
import * as liveStateStream from './core/themes/liveStateStream';
|
|
24
|
-
import { initializeTheme } from './core/themes/themeInit';
|
|
25
|
-
import { registerComponent, type RegisterComponentEntry } from './component-editor/registry';
|
|
21
|
+
import { registerComponent } from './component-editor/registration';
|
|
22
|
+
import type { RegisterComponentEntry } from './component-editor/registry';
|
|
26
23
|
import { registerSketchStyle, type RegisterSketchStyleInput } from './core/sketch/sketchRegistry';
|
|
27
24
|
|
|
28
25
|
export interface BootLiveTokensOptions {
|
|
@@ -43,7 +40,6 @@ export async function bootLiveTokens(
|
|
|
43
40
|
cssVarSync.init();
|
|
44
41
|
router.init();
|
|
45
42
|
columnsOverlay.init();
|
|
46
|
-
editorStore.init();
|
|
47
43
|
|
|
48
44
|
// Before the DEV block: `initializeTheme` recovers the open theme's style by
|
|
49
45
|
// name against the pool, so the pool has to be whole by the time it runs.
|
|
@@ -52,6 +48,16 @@ export async function bootLiveTokens(
|
|
|
52
48
|
}
|
|
53
49
|
|
|
54
50
|
if (import.meta.env.DEV) {
|
|
51
|
+
// Imported here, never statically, so a build drops the editor. The
|
|
52
|
+
// registry alone imports every shipped component and editor.
|
|
53
|
+
const [editorStore, themeDocumentSync, liveStateStream, { initializeTheme }] = await Promise.all([
|
|
54
|
+
import('./core/store/editorStore'),
|
|
55
|
+
import('./core/themes/themeDocumentSync'),
|
|
56
|
+
import('./core/themes/liveStateStream'),
|
|
57
|
+
import('./core/themes/themeInit'),
|
|
58
|
+
import('./component-editor/registry'),
|
|
59
|
+
]);
|
|
60
|
+
editorStore.init();
|
|
55
61
|
themeDocumentSync.init();
|
|
56
62
|
liveStateStream.init();
|
|
57
63
|
if (opts.components) {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
import VariantGroup from './scaffolding/VariantGroup.svelte';
|
|
51
51
|
import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
|
|
52
52
|
import { buildSiblings } from './scaffolding/siblings';
|
|
53
|
-
import demoImageUrl from '../../system/assets/
|
|
53
|
+
import demoImageUrl from '../../system/assets/newspaper.webp';
|
|
54
54
|
import { editorState, setComponentAlias } from '../core/store/editorStore';
|
|
55
55
|
|
|
56
56
|
let aliases = $derived(
|
|
@@ -45,13 +45,12 @@
|
|
|
45
45
|
import ImageLightbox from '../../system/components/ImageLightbox.svelte';
|
|
46
46
|
import VariantGroup from './scaffolding/VariantGroup.svelte';
|
|
47
47
|
import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
|
|
48
|
-
import offeringUrl from '../../system/assets/offering.webp';
|
|
49
48
|
import newspaperUrl from '../../system/assets/newspaper.webp';
|
|
50
49
|
|
|
51
50
|
const demoImages = [
|
|
52
|
-
//
|
|
53
|
-
// them to exercise self-measure from the loaded image.
|
|
54
|
-
{ src:
|
|
51
|
+
// The first entry carries explicit dimensions (the no-reflow path); the second
|
|
52
|
+
// omits them to exercise self-measure from the loaded image.
|
|
53
|
+
{ src: newspaperUrl, alt: 'Newspaper', width: 2048, height: 1152 },
|
|
55
54
|
{ src: newspaperUrl, alt: 'Newspaper' },
|
|
56
55
|
];
|
|
57
56
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RegisterComponentEntry } from './registry';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* `registerComponent` lives apart from `registry.ts` so a production build can
|
|
5
|
+
* call it without bundling the registry, which imports every shipped component
|
|
6
|
+
* and editor. Entries wait here until the registry loads, which happens in dev
|
|
7
|
+
* only.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type Sink = (entry: RegisterComponentEntry) => void;
|
|
11
|
+
|
|
12
|
+
const pending: RegisterComponentEntry[] = [];
|
|
13
|
+
let sink: Sink | null = null;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Register a consumer-authored component with the editor. Call from `main.ts`
|
|
17
|
+
* before app mount.
|
|
18
|
+
*
|
|
19
|
+
* Collision rule: if `entry.id` matches a built-in id, a warning is logged and
|
|
20
|
+
* the custom entry wins (the custom editor and schema replace the built-in for
|
|
21
|
+
* the rest of the session).
|
|
22
|
+
*
|
|
23
|
+
* Side effect: registers the schema with the editor store so reset-to-default
|
|
24
|
+
* and sibling-group resolution work for the new component.
|
|
25
|
+
*/
|
|
26
|
+
export function registerComponent(entry: RegisterComponentEntry): void {
|
|
27
|
+
if (sink) sink(entry);
|
|
28
|
+
else pending.push(entry);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Called once by `registry.ts` as it loads. Drains the entries registered
|
|
32
|
+
before it, in call order, and takes every later one directly. */
|
|
33
|
+
export function attachRegistry(next: Sink): void {
|
|
34
|
+
sink = next;
|
|
35
|
+
for (const entry of pending.splice(0)) next(entry);
|
|
36
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Component } from 'svelte';
|
|
2
2
|
import type { CatalogueEntry, Token, IntrinsicSpec } from './scaffolding/types';
|
|
3
3
|
import { registerComponentSchema } from '../core/store/editorStore';
|
|
4
|
+
import { attachRegistry } from './registration';
|
|
5
|
+
|
|
6
|
+
export { registerComponent } from './registration';
|
|
4
7
|
|
|
5
8
|
import BadgeEditor, { allTokens as badgeTokens } from './BadgeEditor.svelte';
|
|
6
9
|
import { catalogue as badgeCatalogue } from '../../system/components/Badge.svelte';
|
|
@@ -433,18 +436,7 @@ const customRegistry = new Map<string, RegistryEntry>();
|
|
|
433
436
|
/** Argument shape for `registerComponent()`. `origin` is set internally to `'custom'`. */
|
|
434
437
|
export type RegisterComponentEntry = Omit<RegistryEntry, 'origin'>;
|
|
435
438
|
|
|
436
|
-
|
|
437
|
-
* Register a consumer-authored component at runtime. Call from `main.ts`
|
|
438
|
-
* before app mount.
|
|
439
|
-
*
|
|
440
|
-
* Collision rule: if `entry.id` matches a built-in id, a warning is logged and
|
|
441
|
-
* the custom entry wins (the custom editor and schema replace the built-in for
|
|
442
|
-
* the rest of the session).
|
|
443
|
-
*
|
|
444
|
-
* Side effect: registers the schema with the editor store so reset-to-default
|
|
445
|
-
* and sibling-group resolution work for the new component.
|
|
446
|
-
*/
|
|
447
|
-
export function registerComponent(entry: RegisterComponentEntry): void {
|
|
439
|
+
function addCustomComponent(entry: RegisterComponentEntry): void {
|
|
448
440
|
if (entry.id in builtInRegistry) {
|
|
449
441
|
console.warn(
|
|
450
442
|
`[registerComponent] custom component "${entry.id}" overrides a built-in. The custom editor will be used.`,
|
|
@@ -496,6 +488,7 @@ export function getComponentIds(): ReadonlyArray<string> {
|
|
|
496
488
|
for (const entry of Object.values(builtInRegistry)) {
|
|
497
489
|
registerComponentSchema(entry.id, entry.schema);
|
|
498
490
|
}
|
|
491
|
+
attachRegistry(addCustomComponent);
|
|
499
492
|
|
|
500
493
|
/**
|
|
501
494
|
* Validate that the server's filesystem scan matches the merged registry's id list.
|