@motion-proto/live-tokens 0.91.2 → 0.92.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 (46) hide show
  1. package/.claude/skills/live-tokens-create-page/SKILL.md +10 -3
  2. package/.claude/skills/live-tokens-set-type/SKILL.md +3 -3
  3. package/CHANGELOG.md +140 -0
  4. package/bin/lib/tokenVocabulary.mjs +2 -2
  5. package/bin/rules/testRuns.mjs +1 -1
  6. package/bin/rules/tokens.mjs +3 -3
  7. package/dist-plugin/chunk-JOSSYGR2.js +1634 -0
  8. package/dist-plugin/index.cjs +932 -15
  9. package/dist-plugin/index.js +1 -1
  10. package/dist-plugin/setType/index.cjs +23 -2
  11. package/dist-plugin/setType/index.js +23 -2
  12. package/dist-plugin/tokensCssMigrations/index.cjs +936 -15
  13. package/dist-plugin/tokensCssMigrations/index.d.cts +23 -3
  14. package/dist-plugin/tokensCssMigrations/index.d.ts +23 -3
  15. package/dist-plugin/tokensCssMigrations/index.js +5 -1
  16. package/package.json +1 -1
  17. package/src/app/site.css +243 -9
  18. package/src/demo/TestingLoops.svelte +9 -9
  19. package/src/editor/core/fonts/weightCoverage.ts +31 -2
  20. package/src/editor/core/palettes/tokenRegistry.ts +36 -37
  21. package/src/editor/core/themes/parsers/mediaBlocks.ts +46 -0
  22. package/src/editor/core/typeScale/typeScale.ts +341 -0
  23. package/src/editor/skill-atlas/SourcePane.svelte +3 -3
  24. package/src/editor/skill-atlas/skillSources.generated.ts +2 -2
  25. package/src/editor/skill-atlas/trees/create-page.ts +14 -14
  26. package/src/editor/skill-atlas/trees/set-type.ts +2 -2
  27. package/src/editor/ui/UIFontFamilySelector.svelte +5 -1
  28. package/src/editor/ui/UIFontWeightSelector.svelte +4 -0
  29. package/src/editor/ui/UILetterSpacingSelector.svelte +4 -0
  30. package/src/editor/ui/UILineHeightSelector.svelte +4 -0
  31. package/src/editor/ui/UITextTransformSelector.svelte +4 -0
  32. package/src/editor/ui/UIVariantSelector.svelte +6 -8
  33. package/src/editor/ui/VariablesTab.svelte +4 -4
  34. package/src/editor/ui/sections/textStyles.ts +44 -92
  35. package/src/editor/ui/text-styles/ControlBody.svelte +100 -0
  36. package/src/editor/ui/text-styles/ResetIcon.svelte +60 -0
  37. package/src/editor/ui/text-styles/StepRow.svelte +330 -0
  38. package/src/editor/ui/text-styles/StyleControl.svelte +128 -0
  39. package/src/editor/ui/text-styles/TextStylesSection.svelte +551 -0
  40. package/src/editor/ui/text-styles/textStyleEdits.ts +308 -0
  41. package/src/editor/ui/variantScales.ts +16 -0
  42. package/src/system/styles/tokens.css +412 -48
  43. package/src/testing-js/page-compliance.contract.js +233 -80
  44. package/src/testing-js/page-compliance.contract.js.map +1 -1
  45. package/dist-plugin/chunk-GWMEYBZ2.js +0 -715
  46. package/src/editor/ui/TextStylesSection.svelte +0 -171
@@ -0,0 +1,330 @@
1
+ <script lang="ts">
2
+ import UIFontFamilySelector from '../UIFontFamilySelector.svelte';
3
+ import UIFontWeightSelector from '../UIFontWeightSelector.svelte';
4
+ import UILetterSpacingSelector from '../UILetterSpacingSelector.svelte';
5
+ import UILineHeightSelector from '../UILineHeightSelector.svelte';
6
+ import UITextTransformSelector from '../UITextTransformSelector.svelte';
7
+ import type { TextStyle } from '../sections/textStyles';
8
+ import {
9
+ DEFAULTS,
10
+ VIEWPORTS,
11
+ axisName,
12
+ sizeName,
13
+ usageName,
14
+ type StyleAxis,
15
+ type Viewport,
16
+ } from '../../core/typeScale/typeScale';
17
+ import ControlBody from './ControlBody.svelte';
18
+ import ResetIcon from './ResetIcon.svelte';
19
+ import StyleControl from './StyleControl.svelte';
20
+ import {
21
+ SIZE_RANGE_PX,
22
+ SMALL_SIZE_PX,
23
+ faceLabel,
24
+ formatPx,
25
+ isLinked,
26
+ isStored,
27
+ linkExpression,
28
+ sizesOf,
29
+ valueOf,
30
+ weightLabel,
31
+ type LinkAxis,
32
+ type StyleTarget,
33
+ type TextStyleEdits,
34
+ type TokenView,
35
+ } from './textStyleEdits';
36
+
37
+ interface Props {
38
+ target: StyleTarget;
39
+ style: TextStyle;
40
+ view: TokenView;
41
+ /** The viewport the sample renders at and the sizes highlight. */
42
+ viewport: Viewport;
43
+ edits: TextStyleEdits;
44
+ }
45
+
46
+ let { target, style, view, viewport, edits }: Props = $props();
47
+
48
+ const VIEWPORT_LABEL: Record<Viewport, string> = { desktop: 'Desktop', tablet: 'Tablet', phone: 'Phone' };
49
+
50
+ let sizes = $derived(sizesOf(view, target));
51
+ let prefix = $derived(target.prefix);
52
+ const axis = (a: StyleAxis) => axisName(prefix, a);
53
+ let chip = $derived(
54
+ target.step === null
55
+ ? style.defaultElement
56
+ : DEFAULTS.usages[target.usage].elements[target.step]?.join(', '),
57
+ );
58
+
59
+ let sampleStyle = $derived(
60
+ [
61
+ `font-family: var(${axis('font-family')})`,
62
+ `font-size: var(${sizeName(prefix, viewport)})`,
63
+ `font-weight: var(${axis('font-weight')})`,
64
+ `line-height: var(${axis('line-height')})`,
65
+ `letter-spacing: var(${axis('letter-spacing')})`,
66
+ ...(style.hasTextTransform ? [`text-transform: var(${axis('text-transform')})`] : []),
67
+ ].join('; '),
68
+ );
69
+
70
+ // A typed value that the store leaves unchanged would otherwise stay in the
71
+ // input, so the input re-reads the size the store now resolves.
72
+ function editSize(vp: Viewport, input: HTMLInputElement) {
73
+ edits.setSize(target, vp, input.valueAsNumber);
74
+ input.value = formatPx(sizesOf(edits.view(), target)[vp].px);
75
+ }
76
+
77
+ function link(a: LinkAxis, label: (value: string) => string) {
78
+ return {
79
+ linked: isLinked(view, target, a),
80
+ text: label(valueOf(view, usageName(target.usage, a))),
81
+ title: `Linked to ${linkExpression(target, a)}`,
82
+ ontoggle: () => edits.toggleLink(target, a),
83
+ };
84
+ }
85
+
86
+ const write = (a: StyleAxis) => (value: string | null) => edits.writeToken(axis(a), value);
87
+ const reset = (a: StyleAxis) => () => edits.resetKey(axis(a));
88
+ </script>
89
+
90
+ <div class="step">
91
+ <div class="step-id">
92
+ <code class="step-name">{style.name}</code>
93
+ {#if chip}
94
+ <span class="chip">{chip}</span>
95
+ {/if}
96
+ {#if sizes[viewport].px < SMALL_SIZE_PX}
97
+ <span class="flag">Below {SMALL_SIZE_PX}px</span>
98
+ {/if}
99
+ </div>
100
+
101
+ <div class="sizes">
102
+ {#each VIEWPORTS as vp (vp)}
103
+ {@const size = sizes[vp]}
104
+ <div class="size-row" class:is-current={vp === viewport} data-reset-scope>
105
+ <span class="size-label">{VIEWPORT_LABEL[vp]}</span>
106
+ <ControlBody width="4.75rem" linked={!size.edited}>
107
+ <input
108
+ class="size-input"
109
+ type="number"
110
+ min={SIZE_RANGE_PX.min}
111
+ max={SIZE_RANGE_PX.max}
112
+ step="any"
113
+ value={formatPx(size.px)}
114
+ aria-label="{style.name} {vp} size in px"
115
+ title={size.edited ? `Recommended ${formatPx(size.recommendedPx)}px` : 'Recommended size'}
116
+ onchange={(e) => editSize(vp, e.currentTarget)}
117
+ />
118
+ {#snippet after()}
119
+ <span class="unit">px</span>
120
+ <ResetIcon
121
+ label="{style.name} {vp} size"
122
+ visible={size.edited}
123
+ onreset={() => edits.resetSize(target, vp)}
124
+ />
125
+ {/snippet}
126
+ </ControlBody>
127
+ </div>
128
+ {/each}
129
+ </div>
130
+
131
+ <div class="step-sample" style={sampleStyle}>{style.preview}</div>
132
+
133
+ <div class="step-controls">
134
+ <StyleControl
135
+ label="Face"
136
+ name="{style.name} face"
137
+ width="10rem"
138
+ resetVisible={isStored(view, axis('font-family'))}
139
+ onreset={reset('font-family')}
140
+ link={link('font-family', faceLabel)}
141
+ >
142
+ <UIFontFamilySelector variable={axis('font-family')} onwrite={write('font-family')} />
143
+ </StyleControl>
144
+ <StyleControl
145
+ label="Weight"
146
+ name="{style.name} weight"
147
+ width="8rem"
148
+ resetVisible={isStored(view, axis('font-weight'))}
149
+ onreset={reset('font-weight')}
150
+ link={link('font-weight', weightLabel)}
151
+ >
152
+ <UIFontWeightSelector variable={axis('font-weight')} onwrite={write('font-weight')} />
153
+ </StyleControl>
154
+ <StyleControl
155
+ label="Line height"
156
+ name="{style.name} line height"
157
+ width="8rem"
158
+ resetVisible={isStored(view, axis('line-height'))}
159
+ onreset={reset('line-height')}
160
+ >
161
+ <UILineHeightSelector variable={axis('line-height')} onwrite={write('line-height')} />
162
+ </StyleControl>
163
+ <StyleControl
164
+ label="Letter spacing"
165
+ name="{style.name} letter spacing"
166
+ width="9.5rem"
167
+ resetVisible={isStored(view, axis('letter-spacing'))}
168
+ onreset={reset('letter-spacing')}
169
+ >
170
+ <UILetterSpacingSelector variable={axis('letter-spacing')} onwrite={write('letter-spacing')} />
171
+ </StyleControl>
172
+ {#if style.hasTextTransform}
173
+ <StyleControl
174
+ label="Transform"
175
+ name="{style.name} transform"
176
+ width="8rem"
177
+ resetVisible={isStored(view, axis('text-transform'))}
178
+ onreset={reset('text-transform')}
179
+ >
180
+ <UITextTransformSelector variable={axis('text-transform')} onwrite={write('text-transform')} />
181
+ </StyleControl>
182
+ {/if}
183
+ </div>
184
+ </div>
185
+
186
+ <style>
187
+ .step {
188
+ display: grid;
189
+ grid-template-columns: 7.5rem 11.5rem minmax(0, 1fr);
190
+ column-gap: var(--ui-space-16);
191
+ row-gap: var(--ui-space-10);
192
+ padding: var(--ui-space-12) var(--ui-space-10);
193
+ border-bottom: 1px solid var(--ui-border-low);
194
+ }
195
+
196
+ .step:last-child {
197
+ border-bottom: none;
198
+ }
199
+
200
+ .step-id {
201
+ grid-row: 1 / span 2;
202
+ display: flex;
203
+ flex-direction: column;
204
+ align-items: flex-start;
205
+ gap: var(--ui-space-6);
206
+ min-width: 0;
207
+ }
208
+
209
+ .step-name {
210
+ font-family: var(--ui-font-mono);
211
+ font-size: var(--ui-font-size-sm);
212
+ color: var(--ui-text-primary);
213
+ }
214
+
215
+ .chip {
216
+ padding: 1px var(--ui-space-8);
217
+ border: 1px solid var(--ui-border-low);
218
+ border-radius: var(--ui-radius-full);
219
+ color: var(--ui-text-secondary);
220
+ font-family: var(--ui-font-mono);
221
+ font-size: var(--ui-font-size-xs);
222
+ }
223
+
224
+ .flag {
225
+ padding: 1px var(--ui-space-8);
226
+ border: 1px dashed var(--ui-border-high);
227
+ border-radius: var(--ui-radius-full);
228
+ color: var(--ui-text-primary);
229
+ font-size: var(--ui-font-size-xs);
230
+ white-space: nowrap;
231
+ }
232
+
233
+ .sizes {
234
+ grid-row: 1 / span 2;
235
+ display: grid;
236
+ align-content: start;
237
+ gap: var(--ui-space-6);
238
+ }
239
+
240
+ .size-row {
241
+ display: grid;
242
+ grid-template-columns: 3.5rem auto;
243
+ align-items: center;
244
+ gap: var(--ui-space-4);
245
+ }
246
+
247
+ .size-label {
248
+ color: var(--ui-text-muted);
249
+ font-size: var(--ui-font-size-xs);
250
+ }
251
+
252
+ .size-row.is-current .size-label {
253
+ color: var(--ui-text-primary);
254
+ font-weight: var(--ui-font-weight-semibold);
255
+ }
256
+
257
+ .size-input {
258
+ box-sizing: border-box;
259
+ width: 100%;
260
+ height: 1.75rem;
261
+ padding: 0 var(--ui-space-8);
262
+ background: var(--ui-surface-high);
263
+ border: 1px solid var(--ui-border-low);
264
+ border-radius: var(--ui-radius-md);
265
+ color: var(--ui-text-primary);
266
+ font: inherit;
267
+ font-size: var(--ui-font-size-sm);
268
+ font-variant-numeric: tabular-nums;
269
+ }
270
+
271
+ .size-input:hover {
272
+ border-color: var(--ui-border-high);
273
+ }
274
+
275
+ .size-input:focus-visible {
276
+ outline: 2px solid var(--ui-text-primary);
277
+ outline-offset: 2px;
278
+ }
279
+
280
+ .unit {
281
+ flex: none;
282
+ color: var(--ui-text-muted);
283
+ font-size: var(--ui-font-size-xs);
284
+ }
285
+
286
+ /* Samples take the editor's ink; colour sits outside a text style. */
287
+ .step-sample {
288
+ grid-column: 3;
289
+ min-width: 0;
290
+ max-width: min(100%, 22ch);
291
+ color: var(--ui-text-primary);
292
+ display: -webkit-box;
293
+ -webkit-line-clamp: 2;
294
+ line-clamp: 2;
295
+ -webkit-box-orient: vertical;
296
+ overflow: hidden;
297
+ overflow-wrap: anywhere;
298
+ text-wrap: balance;
299
+ }
300
+
301
+ .step-controls {
302
+ grid-column: 3;
303
+ align-self: end;
304
+ display: flex;
305
+ flex-wrap: wrap;
306
+ gap: var(--ui-space-12);
307
+ }
308
+
309
+ @media (max-width: 720px) {
310
+ .step {
311
+ grid-template-columns: minmax(0, 1fr);
312
+ }
313
+
314
+ .step-id {
315
+ grid-row: auto;
316
+ flex-direction: row;
317
+ align-items: center;
318
+ flex-wrap: wrap;
319
+ }
320
+
321
+ .sizes {
322
+ grid-row: auto;
323
+ }
324
+
325
+ .step-sample,
326
+ .step-controls {
327
+ grid-column: auto;
328
+ }
329
+ }
330
+ </style>
@@ -0,0 +1,128 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import ControlBody from './ControlBody.svelte';
4
+ import ResetIcon from './ResetIcon.svelte';
5
+
6
+ interface Link {
7
+ linked: boolean;
8
+ /** What the linked value shows, such as the usage's face. */
9
+ text: string;
10
+ /** The link expression, shown as the linked value's tooltip. */
11
+ title: string;
12
+ ontoggle: () => void;
13
+ }
14
+
15
+ interface Props {
16
+ label: string;
17
+ /** Names the control in accessible labels, such as `heading-xl face`. */
18
+ name: string;
19
+ width: string;
20
+ resetVisible: boolean;
21
+ onreset: () => void;
22
+ link?: Link;
23
+ children: Snippet;
24
+ }
25
+
26
+ let { label, name, width, resetVisible, onreset, link, children }: Props = $props();
27
+ </script>
28
+
29
+ <div class="ctl" data-reset-scope>
30
+ <div class="ctl-head">
31
+ <span class="ctl-label">{label}</span>
32
+ <ResetIcon label={name} visible={resetVisible} {onreset} />
33
+ </div>
34
+ <ControlBody {width} linked={link ? link.linked : null}>
35
+ {#if link?.linked}
36
+ <span class="linked-value" title={link.title}>{link.text}</span>
37
+ {:else}
38
+ {@render children()}
39
+ {/if}
40
+ {#snippet after()}
41
+ {#if link}
42
+ <button
43
+ type="button"
44
+ class="lock"
45
+ class:is-unlinked={!link.linked}
46
+ aria-pressed={link.linked}
47
+ aria-label="{link.linked ? 'Unlink' : 'Relink'} {name}"
48
+ title={link.linked ? 'Unlink' : 'Relink'}
49
+ onclick={link.ontoggle}
50
+ >
51
+ <i class="fas {link.linked ? 'fa-lock' : 'fa-lock-open'}" aria-hidden="true"></i>
52
+ </button>
53
+ {/if}
54
+ {/snippet}
55
+ </ControlBody>
56
+ </div>
57
+
58
+ <style>
59
+ .ctl {
60
+ display: flex;
61
+ flex-direction: column;
62
+ gap: var(--ui-space-4);
63
+ min-width: 0;
64
+ }
65
+
66
+ /* The label line has a fixed height and the reset icon trails the label, so
67
+ showing or hiding the icon moves nothing. */
68
+ .ctl-head {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: var(--ui-space-4);
72
+ height: 18px;
73
+ min-width: 0;
74
+ }
75
+
76
+ .ctl-label {
77
+ font-size: var(--ui-font-size-xs);
78
+ font-weight: var(--ui-font-weight-semibold);
79
+ color: var(--ui-text-secondary);
80
+ white-space: nowrap;
81
+ }
82
+
83
+ .linked-value {
84
+ box-sizing: border-box;
85
+ display: block;
86
+ height: 1.75rem;
87
+ padding: 0 var(--ui-space-8);
88
+ background: var(--ui-surface-higher);
89
+ border: 1px solid var(--ui-border-low);
90
+ border-left: none;
91
+ border-radius: 0 var(--ui-radius-md) var(--ui-radius-md) 0;
92
+ color: var(--ui-text-secondary);
93
+ font-size: var(--ui-font-size-sm);
94
+ line-height: calc(1.75rem - 2px);
95
+ white-space: nowrap;
96
+ overflow: hidden;
97
+ text-overflow: ellipsis;
98
+ }
99
+
100
+ .lock {
101
+ flex: none;
102
+ display: inline-grid;
103
+ place-items: center;
104
+ width: 24px;
105
+ height: 24px;
106
+ padding: 0;
107
+ background: none;
108
+ border: none;
109
+ border-radius: var(--ui-radius-md);
110
+ color: var(--ui-text-tertiary);
111
+ font-size: 0.8125rem;
112
+ cursor: pointer;
113
+ }
114
+
115
+ .lock:hover {
116
+ color: var(--ui-text-primary);
117
+ background: var(--ui-surface-higher);
118
+ }
119
+
120
+ .lock:focus-visible {
121
+ outline: 2px solid var(--ui-text-primary);
122
+ outline-offset: 1px;
123
+ }
124
+
125
+ .lock.is-unlinked {
126
+ color: var(--ui-link-broken);
127
+ }
128
+ </style>