@motion-proto/live-tokens 0.44.0 → 0.46.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 (41) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/dist-plugin/index.cjs +1 -1
  3. package/dist-plugin/index.js +1 -1
  4. package/package.json +1 -1
  5. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +0 -1
  6. package/src/editor/component-editor/scaffolding/ComponentFileMenu.svelte +1 -1
  7. package/src/editor/core/palettes/colorHarmony.ts +35 -6
  8. package/src/editor/core/palettes/paletteDerivation.ts +2 -3
  9. package/src/editor/core/store/editorTypes.ts +2 -2
  10. package/src/editor/docs/CodeBlock.svelte +0 -2
  11. package/src/editor/docs/Docs.svelte +0 -3
  12. package/src/editor/pages/ComponentEditorPage.svelte +3 -5
  13. package/src/editor/pages/EditorShell.svelte +3 -5
  14. package/src/editor/ui/ColorEditPanel.svelte +19 -22
  15. package/src/editor/ui/EditorViewSwitcher.svelte +2 -3
  16. package/src/editor/ui/FileLoadList.svelte +7 -9
  17. package/src/editor/ui/ManifestFileManager.svelte +1 -3
  18. package/src/editor/ui/ProjectFontsSection.svelte +0 -4
  19. package/src/editor/ui/SurfacesTab.svelte +3 -3
  20. package/src/editor/ui/TextStylesSection.svelte +6 -8
  21. package/src/editor/ui/ThemeFileManager.svelte +1 -3
  22. package/src/editor/ui/UIEasingSelector.svelte +0 -2
  23. package/src/editor/ui/UIFontFamilySelector.svelte +9 -3
  24. package/src/editor/ui/UIMenuButton.svelte +230 -0
  25. package/src/editor/ui/UIPaletteSelector.svelte +0 -1
  26. package/src/editor/ui/VariablesTab.svelte +3 -4
  27. package/src/editor/ui/colors/AxisNumeral.svelte +70 -0
  28. package/src/editor/ui/colors/ColorReadouts.svelte +0 -2
  29. package/src/editor/ui/colors/ColorStory.svelte +24 -77
  30. package/src/editor/ui/colors/ColorWheel.svelte +48 -29
  31. package/src/editor/ui/colors/ColorsTab.svelte +361 -212
  32. package/src/editor/ui/colors/HarmonyAxesList.svelte +264 -141
  33. package/src/editor/ui/colors/LightnessBar.svelte +0 -2
  34. package/src/editor/ui/colors/harmonyDrag.ts +10 -0
  35. package/src/editor/ui/colors/harmonyModeIcons.ts +5 -0
  36. package/src/editor/ui/colors/paletteBaseColor.ts +25 -39
  37. package/src/live-tokens/data/tokens.generated.css +28 -28
  38. package/src/system/components/ImageLightbox.svelte +0 -2
  39. package/src/system/components/SectionDivider.svelte +4 -4
  40. package/src/editor/core/palettes/recommendText.ts +0 -216
  41. package/src/editor/core/palettes/solveTextContrast.ts +0 -254
@@ -5,7 +5,9 @@
5
5
  import { editorState, beginScope, commitScope, cancelScope, type Scope } from '../../core/store/editorStore';
6
6
  import { setBaseHueChroma, setBaseChroma, setAxisHue, setAxisHues } from './paletteBaseColor';
7
7
  import { maxChroma } from './colorWheelMath';
8
- import { applyHarmonyToAxes, modeActiveAxes, AXIS_ROLES, type HarmonyMode } from '../../core/palettes/colorHarmony';
8
+ import { applyHarmonyToAxes, axisLabel, axisStatuses, type HarmonyMode } from '../../core/palettes/colorHarmony';
9
+ import AxisNumeral from './AxisNumeral.svelte';
10
+ import { modeLabel } from './harmonyModeIcons';
9
11
 
10
12
  interface Props {
11
13
  selected: string | null;
@@ -29,12 +31,12 @@
29
31
 
30
32
  let { selected, onSelect, discLightness, onCustomize, absoluteChroma, activeMode, previewMode = null }: Props = $props();
31
33
 
32
- // One handle per axis (index = role). A bound axis reads its color from the
33
- // family's baseColor (hue equals axes[i].hue by invariant 1); an unbound axis
34
- // carries only its stored hue, previewing where a dropped color would land.
34
+ // One handle per axis. A bound axis reads its color from the family's
35
+ // baseColor (hue equals axes[i].hue by invariant 1); an unbound axis carries
36
+ // only its stored hue, previewing where an assigned color would land.
35
37
  let axisData = $derived(
36
38
  $editorState.harmonyAxes.map((axis, i) => {
37
- const common = { index: i, role: AXIS_ROLES[i], family: axis.family, bound: axis.family !== null };
39
+ const common = { index: i, label: axisLabel(i), family: axis.family, bound: axis.family !== null };
38
40
  if (axis.family !== null) {
39
41
  const spec = PALETTE_SPECS.find((s) => s.label === axis.family);
40
42
  const { l, c, h } = $editorState.palettes[axis.family]?.baseColor ?? spec!.initialColor;
@@ -50,8 +52,8 @@
50
52
  // active in the applied geometry (its slot is a distinct position). A bound
51
53
  // inactive axis keeps its family's color but is edited in dot mode, not axis
52
54
  // mode: the family renders as the free dot and sits out the global spin.
53
- let activeAxes = $derived(modeActiveAxes(activeMode));
54
- const isVisible = (t: { index: number }) => activeAxes[t.index];
55
+ let statuses = $derived(axisStatuses(activeMode, $editorState.harmonyAxes));
56
+ const isVisible = (t: { index: number }) => statuses[t.index] === 'on-wheel';
55
57
 
56
58
  // Reserved judgment call: keyboard nudge increments.
57
59
  const HUE_STEP = 2;
@@ -59,6 +61,7 @@
59
61
  const MARGIN = 52; // ring-to-edge gap: houses the external handles AND the axis numerals outside them
60
62
  const EXT_OFFSET = 20; // external handle radius beyond the disc rim (room for the dotted tether)
61
63
  const NUM_OFFSET = 24; // numeral radius beyond the external handles
64
+ const FREE_NUM_OFFSET = 15; // free-dot numeral, diagonal clearance past the enlarged dot
62
65
  const MIN_SIZE = 240;
63
66
  const MAX_SIZE = 560;
64
67
 
@@ -87,7 +90,7 @@
87
90
  // lossy and makes the non-dragged coordinate wobble). Writes also use the
88
91
  // pristine hue0/L0, so nothing accumulates across frames.
89
92
  type AxisView = {
90
- index: number; role: string; family: string | null; bound: boolean;
93
+ index: number; label: string; family: string | null; bound: boolean;
91
94
  hex: string; hue: number; chroma: number; lightness: number; rFrac: number;
92
95
  };
93
96
 
@@ -146,7 +149,9 @@
146
149
  // Writes go through setBaseHueChroma only — active axes never move; a bound
147
150
  // inactive axis follows its family via syncBoundAxisHue.
148
151
  let freeDot = $derived.by(() => {
149
- if (!selected || $editorState.harmonyAxes.some((a, i) => a.family === selected && activeAxes[i])) return null;
152
+ if (!selected) return null;
153
+ const axisIndex = $editorState.harmonyAxes.findIndex((a) => a.family === selected);
154
+ if (axisIndex !== -1 && statuses[axisIndex] === 'on-wheel') return null;
150
155
  const spec = PALETTE_SPECS.find((s) => s.label === selected);
151
156
  if (!spec) return null;
152
157
  const { l, c, h } = $editorState.palettes[selected]?.baseColor ?? spec.initialColor;
@@ -159,11 +164,22 @@
159
164
  }
160
165
  const r = rFrac * discRadius;
161
166
  return {
162
- family: selected, hex: oklchToHexClamped(l, c, h), hue, chroma: c, lightness: l,
167
+ family: selected, axisIndex: axisIndex === -1 ? null : axisIndex,
168
+ hex: oklchToHexClamped(l, c, h), hue, chroma: c, lightness: l,
163
169
  x: center + r * Math.cos(rad(hue)), y: center - r * Math.sin(rad(hue)),
164
170
  };
165
171
  });
166
172
 
173
+ // The dot serves two states: bound to an axis the mode left off the wheel, and
174
+ // unassigned. Only the first has an axis to name.
175
+ let freeDotLabel = $derived.by(() => {
176
+ if (!freeDot) return '';
177
+ const where = freeDot.axisIndex === null
178
+ ? 'unassigned'
179
+ : `${axisLabel(freeDot.axisIndex)}. Off the wheel in ${modeLabel(activeMode)}`;
180
+ return `${freeDot.family}, ${where}. Drag to adjust hue and chroma.`;
181
+ });
182
+
167
183
  let globalHandle = $derived.by(() => {
168
184
  const d = drag;
169
185
  const angle = d?.kind === 'global' ? d.angle : globalAngle;
@@ -315,7 +331,7 @@
315
331
  if (axis.family !== null) onSelect(axis.family);
316
332
  onCustomize();
317
333
  capture(e);
318
- openGesture(`colors: ${axis.role} axis rotate`);
334
+ openGesture(`colors: ${axis.label} rotate`);
319
335
  drag = { kind: 'axis', index: axis.index, angle: pointerAngle(e), start: startOf(axis) };
320
336
  applyAxis(e);
321
337
  }
@@ -483,7 +499,9 @@
483
499
  {/each}
484
500
 
485
501
  {#each visibleRender as t (t.index)}
486
- <span class="axis-num" class:selected={t.selected} style="left: {t.num.x}px; top: {t.num.y}px" aria-hidden="true">{t.index + 1}</span>
502
+ <span class="axis-num" style="left: {t.num.x}px; top: {t.num.y}px" aria-hidden="true">
503
+ <AxisNumeral index={t.index} status={statuses[t.index]} selected={t.selected} />
504
+ </span>
487
505
  {/each}
488
506
 
489
507
  {#each visibleRender as t (t.index)}
@@ -493,7 +511,7 @@
493
511
  class="dot"
494
512
  class:selected={t.selected}
495
513
  style="left: {t.dot.x}px; top: {t.dot.y}px; --fill: {t.hex}"
496
- aria-label={`${t.family} — drag along rail to adjust chroma`}
514
+ aria-label={`${t.family}, drag along the rail to adjust chroma`}
497
515
  title={`${t.family} (drag for chroma)`}
498
516
  onpointerdown={(e) => startChromaDrag(e, t)}
499
517
  onpointermove={moveDrag}
@@ -513,8 +531,8 @@
513
531
  class:unbound={!t.bound}
514
532
  class:selected={t.selected}
515
533
  style="left: {t.ext.x}px; top: {t.ext.y}px; transform: translate(-50%, -50%) rotate({t.iconRot}deg)"
516
- aria-label={t.bound ? `Rotate ${t.role} axis hue (${t.family})` : `Rotate ${t.role} axis hue`}
517
- title={t.bound ? `Rotate ${t.role} axis hue (${t.family})` : `Rotate ${t.role} axis hue`}
534
+ aria-label={t.bound ? `Rotate ${t.label} hue (${t.family})` : `Rotate ${t.label} hue`}
535
+ title={t.bound ? `Rotate ${t.label} hue (${t.family})` : `Rotate ${t.label} hue`}
518
536
  onpointerdown={(e) => startAxisDrag(e, t)}
519
537
  onpointermove={moveDrag}
520
538
  onpointerup={endDrag}
@@ -526,12 +544,21 @@
526
544
  {/each}
527
545
 
528
546
  {#if freeDot}
547
+ {#if freeDot.axisIndex !== null}
548
+ <span
549
+ class="axis-num"
550
+ style="left: {freeDot.x + FREE_NUM_OFFSET}px; top: {freeDot.y - FREE_NUM_OFFSET}px"
551
+ aria-hidden="true"
552
+ >
553
+ <AxisNumeral index={freeDot.axisIndex} status={statuses[freeDot.axisIndex]} selected />
554
+ </span>
555
+ {/if}
529
556
  <button
530
557
  type="button"
531
558
  class="dot selected"
532
559
  style="left: {freeDot.x}px; top: {freeDot.y}px; --fill: {freeDot.hex}"
533
- aria-label={`${freeDot.family} — drag to adjust hue and chroma`}
534
- title={`${freeDot.family} (drag freely)`}
560
+ aria-label={freeDotLabel}
561
+ title={freeDotLabel}
535
562
  onpointerdown={startFreeDrag}
536
563
  onpointermove={moveDrag}
537
564
  onpointerup={endDrag}
@@ -632,7 +659,7 @@
632
659
  }
633
660
 
634
661
  /* Unbound-axis ghost: a hollow greyscale marker on the external track (no color
635
- to preview, only where a dropped color's hue would land). */
662
+ to preview, only where an assigned color's hue would land). */
636
663
  .ghost.unbound {
637
664
  width: 0.7rem;
638
665
  height: 0.7rem;
@@ -731,24 +758,16 @@
731
758
  color: var(--ui-text-primary);
732
759
  }
733
760
 
734
- /* Axis numeral just outside the handle track. Positioned only — never rotated,
735
- so it stays upright at any angle. */
761
+ /* Axis numeral just outside the handle track. Positioning only — never
762
+ rotated, so the digit stays upright at any angle. */
736
763
  .axis-num {
737
764
  position: absolute;
738
765
  transform: translate(-50%, -50%);
739
- font-size: var(--ui-font-size-xs);
740
- font-weight: var(--ui-font-weight-semibold);
741
- color: var(--ui-text-tertiary);
766
+ display: flex;
742
767
  pointer-events: none;
743
- user-select: none;
744
768
  z-index: 3;
745
769
  }
746
770
 
747
- .axis-num.selected {
748
- color: var(--ui-text-primary);
749
- font-size: var(--ui-font-size-sm);
750
- }
751
-
752
771
  .dot:focus-visible,
753
772
  .ext-handle:focus-visible,
754
773
  .global-handle:focus-visible {