@vegastack/design-tokens 0.1.0 → 0.4.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.
@@ -51,12 +51,18 @@
51
51
  --scroll-fade-t: 0px;
52
52
  }
53
53
  to {
54
- --scroll-fade-t: var(--_scroll-fade-size-t, var(--scroll-fade-size, min(12%, calc(var(--spacing) * 10))));
54
+ --scroll-fade-t: var(
55
+ --_scroll-fade-size-t,
56
+ var(--scroll-fade-size, min(12%, calc(var(--spacing) * 10)))
57
+ );
55
58
  }
56
59
  }
57
60
  @keyframes scroll-fade-reveal-b {
58
61
  from {
59
- --scroll-fade-b: var(--_scroll-fade-size-b, var(--scroll-fade-size, min(12%, calc(var(--spacing) * 10))));
62
+ --scroll-fade-b: var(
63
+ --_scroll-fade-size-b,
64
+ var(--scroll-fade-size, min(12%, calc(var(--spacing) * 10)))
65
+ );
60
66
  }
61
67
  to {
62
68
  --scroll-fade-b: 0px;
@@ -67,12 +73,18 @@
67
73
  --scroll-fade-s: 0px;
68
74
  }
69
75
  to {
70
- --scroll-fade-s: var(--_scroll-fade-size-s, var(--scroll-fade-size, min(12%, calc(var(--spacing) * 10))));
76
+ --scroll-fade-s: var(
77
+ --_scroll-fade-size-s,
78
+ var(--scroll-fade-size, min(12%, calc(var(--spacing) * 10)))
79
+ );
71
80
  }
72
81
  }
73
82
  @keyframes scroll-fade-reveal-e {
74
83
  from {
75
- --scroll-fade-e: var(--_scroll-fade-size-e, var(--scroll-fade-size, min(12%, calc(var(--spacing) * 10))));
84
+ --scroll-fade-e: var(
85
+ --_scroll-fade-size-e,
86
+ var(--scroll-fade-size, min(12%, calc(var(--spacing) * 10)))
87
+ );
76
88
  }
77
89
  to {
78
90
  --scroll-fade-e: 0px;
@@ -553,7 +565,6 @@
553
565
  }
554
566
  }
555
567
 
556
-
557
568
  /* ---------------------------------------------------------------------------
558
569
  * scrollbar-* — minimal native scrollbar helpers (VegaStack addition; not in
559
570
  * upstream shadcn tailwind.css). Used by message-scroller's viewport.
@@ -573,7 +584,6 @@
573
584
  scrollbar-gutter: stable;
574
585
  }
575
586
 
576
-
577
587
  /* ---------------------------------------------------------------------------
578
588
  * motion-* — the state-feedback animation vocabulary (VegaStack addition,
579
589
  * Phase M). Mount-triggered CSS animations for the "keyed presence" mechanism:
@@ -586,9 +596,18 @@
586
596
  * (the "success bounce" — spring ease, tiny overshoot).
587
597
  * - motion-enter-up : fade+rise arrival for content (chat messages,
588
598
  * skeleton→content reveal).
599
+ * - motion-flash : highlight-on-change ring pulse (live updates); color
600
+ * via --vs-flash-color, defaults to `ring`.
589
601
  * - motion-shake : horizontal error jiggle. Replayable: re-trigger by
590
602
  * remounting (key bump) or toggling the class after
591
603
  * `animationend`.
604
+ * - motion-indeterminate : the ONE sanctioned looping utility — the sweeping
605
+ * segment of an indeterminate Progress. Looping motion is
606
+ * otherwise banned; an indeterminate bar has no other way
607
+ * to say "still working" (audit M-06).
608
+ *
609
+ * `motion-dock-in` / `motion-dock-out` below are the one PAIR in this family and
610
+ * the one that is not keyed presence — see their own header.
592
611
  * ------------------------------------------------------------------------- */
593
612
  @keyframes vs-pop-in {
594
613
  from {
@@ -643,70 +662,135 @@
643
662
  animation: vs-shake var(--duration-slow) var(--motion-ease-standard);
644
663
  }
645
664
 
665
+ @keyframes vs-flash {
666
+ /* Highlight-on-change ring pulse (Wave 1, Attio record-flash adaptation):
667
+ * a 2px ring in the component's own accent (defaults to `ring`) swells and
668
+ * settles. End state = rest, so the base.css reduced-motion collapse renders
669
+ * it invisible. Override the color per call-site via `--vs-flash-color`
670
+ * (e.g. `[--vs-flash-color:var(--info)]` on a live-updating row). */
671
+ 0% {
672
+ box-shadow: 0 0 0 0 transparent;
673
+ }
674
+ 50% {
675
+ box-shadow: 0 0 0 2px var(--vs-flash-color, var(--ring));
676
+ }
677
+ to {
678
+ box-shadow: 0 0 0 0 transparent;
679
+ }
680
+ }
681
+
682
+ @utility motion-flash {
683
+ animation: vs-flash var(--duration-slow) var(--motion-ease-standard);
684
+ }
685
+
686
+ @keyframes vs-indeterminate {
687
+ /* Indeterminate progress (audit M-06, 2026-09-07): a 35%-wide segment sweeps the
688
+ * track and returns. The 0% and 100% stops are IDENTICAL on purpose — the global
689
+ * prefers-reduced-motion reset in base.css collapses every animation to its `to`
690
+ * state, so a reduced-motion user sees a static 35% segment resting at the start
691
+ * of the track instead of a bar that reads as 100% complete. */
692
+ 0% {
693
+ inset-inline-start: 0%;
694
+ }
695
+ 50% {
696
+ inset-inline-start: 65%;
697
+ }
698
+ 100% {
699
+ inset-inline-start: 0%;
700
+ }
701
+ }
702
+
703
+ @utility motion-indeterminate {
704
+ animation: vs-indeterminate var(--duration-indeterminate)
705
+ var(--motion-ease-standard) infinite;
706
+ }
707
+
646
708
  /* ---------------------------------------------------------------------------
647
- * .vs-marketing — the marketing dark-ground SCOPE MECHANISM (Phase B, audit
648
- * 17-brand-direction §Color & surface, CX-10). Marketing surfaces (hero,
649
- * section bands, the Terminal primitive) render the warm ramp's DARK end
650
- * regardless of the page/product theme — `.dark` alone can't express "product
651
- * light + marketing dark on the SAME page," which is exactly the co-existing
652
- * case this class exists for (docs-home is light by default but its hero is
653
- * dark-first). Any subtree wrapped in `.vs-marketing` (see the
654
- * `MarketingSurface` primitive, and `Terminal`, which self-scopes) gets the
655
- * same semantic custom properties `.dark` sets, scoped LOCALLY instead of
656
- * gated on the page-root class — every `bg-background` / `text-foreground` /
657
- * `border-border` / `bg-brand` utility inside it resolves through these, with
658
- * NO component code changes (any existing token-built component composes
659
- * correctly, since only the custom-property VALUES change, never the
660
- * utility-to-var wiring).
709
+ * motion-dock-in / motion-dock-out — the docked-control enter/exit pair
710
+ * (audit D11 amendment #7, 2026-09-07).
661
711
  *
662
- * Values are a RATIONED, MANUALLY-SYNCED duplicate of the resolved oklch()
663
- * output of `packages/design-tokens/tokens/semantic.dark.tokens.json` (the same
664
- * values `.dark` sets) — the same "one sanctioned literal duplication"
665
- * precedent as the `overlay` scrim token in that file. The principled fix is
666
- * a THIRD generated Style Dictionary platform run in `build-tokens.mjs`
667
- * (source `semantic.dark.tokens.json`, selector `.vs-marketing` instead of
668
- * `.dark`) so this can never drift silently — that's a tokens-BUILD-PIPELINE
669
- * change outside this component-level addition; flagged for whoever owns
670
- * `packages/design-tokens/build-tokens.mjs` next. If `semantic.dark.tokens.json`
671
- * changes, update this block to match (or land the generated version).
712
+ * A docked control — a bottom action bar, a floating scroll-to-end button —
713
+ * stays MOUNTED and flips a `data-active` attribute, so its motion is a
714
+ * transition, not a keyed animation. That is why this is a pair rather than one
715
+ * `@utility`: the two states carry different timing, which is the whole point.
672
716
  *
673
- * LIMITATION (documented, not fixed here): Base UI floating surfaces portal
674
- * to `<body>`, OUTSIDE this scoped subtree — a popover/tooltip/menu/dialog
675
- * opened from inside a `.vs-marketing` surface renders with the PAGE theme,
676
- * not the marketing dark ground. Marketing surfaces rarely need portals (a
677
- * hero/section band is not app chrome); the docs-home dogfood doesn't use one.
717
+ * The doctrine these encode: **an exit is never slower than its enter.** Both
718
+ * bars previously entered at `duration-base` and left at `duration-slow` with a
719
+ * `scale-95`, so dismissing one took half again as long as summoning it and the
720
+ * bar shrank as it went. Vercel, Linear and Base UI's own defaults all leave at
721
+ * or faster than they arrive. Enter is 150ms `emphasized`; exit is 100ms `exit`;
722
+ * a docked bar translates and fades and does not scale — scale reads as a popup,
723
+ * and a bar sliding off its own edge does not need it.
724
+ *
725
+ * 100ms has exactly one role in the system — this exit — so it lives here, in
726
+ * the utility that names it, rather than becoming a fifth global duration token
727
+ * that would invite reuse D11 did not sanction.
728
+ *
729
+ * These own the timing, the fade and the inert-while-hidden guard. The DISTANCE
730
+ * stays at the call site as ordinary `translate-*` utilities, because it is
731
+ * per-dock geometry (a bottom bar clears its own height plus the safe-area
732
+ * inset; an edge button clears 100%) and because Tailwind composes `translate-x`
733
+ * and `translate-y` through custom properties that a `translate:` declaration
734
+ * here would clobber — a horizontally-centred bar would lose its centring.
735
+ *
736
+ * The global prefers-reduced-motion reset in base.css collapses both to 0.01ms.
737
+ *
738
+ * <div data-active={open}
739
+ * className="data-[active=true]:motion-dock-in data-[active=true]:translate-y-0
740
+ * data-[active=false]:motion-dock-out data-[active=false]:translate-y-full" />
678
741
  * ------------------------------------------------------------------------- */
679
- .vs-marketing {
680
- color-scheme: dark;
681
-
682
- --background: oklch(0.175 0.003 75);
683
- --foreground: oklch(0.922 0.003 75);
684
- --card: oklch(0.205 0.003 75);
685
- --card-foreground: oklch(0.922 0.003 75);
686
- --popover: oklch(0.205 0.003 75);
687
- --popover-foreground: oklch(0.922 0.003 75);
688
-
689
- --primary: oklch(0.922 0.003 75);
690
- --primary-foreground: oklch(0.145 0.003 75);
691
- --primary-hover: oklch(0.97 0.003 75);
692
- --primary-active: oklch(0.994 0.002 75);
693
-
694
- --secondary: oklch(0.269 0.003 75);
695
- --secondary-foreground: oklch(0.922 0.003 75);
696
- --muted: oklch(0.269 0.003 75);
697
- --muted-foreground: oklch(0.66 0.003 75);
698
- --muted-foreground-faint: oklch(0.559 0.003 75);
699
- --accent: oklch(0.269 0.003 75);
700
- --accent-foreground: oklch(0.967 0.003 75);
701
-
702
- --border: oklch(0.269 0.003 75);
703
- --input: var(--border);
704
- --ring: var(--primary);
705
- --overlay: oklch(0 0 0 / 0.55);
706
-
707
- /* Brand phosphor — DARK half (13.3:1 on this ground, MK's pick), independent
708
- of whether `.dark` is present anywhere in the ancestor chain. Marker roles
709
- only (live-state dot, sparkline endpoint, eyebrow highlight, terminal
710
- prompt glyph) — see design.md §Brand & Marketing. */
711
- --brand: oklch(0.86 0.21 148);
742
+ @utility motion-dock-in {
743
+ transition-property: translate, opacity;
744
+ transition-duration: var(--duration-fast);
745
+ transition-timing-function: var(--motion-ease-emphasized);
746
+ opacity: 1;
712
747
  }
748
+
749
+ @utility motion-dock-out {
750
+ transition-property: translate, opacity;
751
+ transition-duration: 100ms;
752
+ transition-timing-function: var(--motion-ease-exit);
753
+ opacity: 0;
754
+ /* A docked-out control is off-screen: it must not swallow clicks on the way
755
+ out, and it must not be a hit target while parked. */
756
+ pointer-events: none;
757
+ }
758
+
759
+ /* ---------------------------------------------------------------------------
760
+ * BrandIcon theme-aware artwork swap (`<BrandIcon variant="auto">`).
761
+ *
762
+ * Single-colour brands ship artwork that only reads on ONE surface — GitHub's
763
+ * brand mark is #181717, invisible on a dark canvas. `thesvg` therefore ships
764
+ * `light` (dark ink, FOR light surfaces) and `dark` (white ink, FOR dark
765
+ * surfaces) variants; `auto` renders both and lets CSS reveal the right one.
766
+ *
767
+ * These are plain attribute selectors, NOT Tailwind utilities, on purpose:
768
+ * `@vegastack/design` is a published package, and a consumer's Tailwind build
769
+ * does not scan our `src`. A `dark:` utility authored inside the package would
770
+ * silently never be generated downstream (it wasn't generated in our own docs
771
+ * either — see the dead `packages/icons` @source). Shipping the rules in the
772
+ * stylesheet consumers already import makes the swap work everywhere.
773
+ *
774
+ * `.vs-marketing` is included so a brand mark inside the marketing dark ground
775
+ * picks the dark artwork even while the page theme is light.
776
+ * ------------------------------------------------------------------------- */
777
+ [data-brand-artwork="light"] {
778
+ display: contents;
779
+ }
780
+ [data-brand-artwork="dark"] {
781
+ display: none;
782
+ }
783
+ .dark [data-brand-artwork="light"],
784
+ .vs-marketing [data-brand-artwork="light"] {
785
+ display: none;
786
+ }
787
+ .dark [data-brand-artwork="dark"],
788
+ .vs-marketing [data-brand-artwork="dark"] {
789
+ display: contents;
790
+ }
791
+
792
+ /* `.vs-marketing`'s semantic variables and `color-scheme: dark` now live in
793
+ * generated theme.css. build-tokens.mjs emits the scope from the SAME resolved
794
+ * Style Dictionary dark run as `.dark`, and verify-theme-parity.mjs rejects any
795
+ * variable-set or value drift. The artwork selectors above remain here because
796
+ * they are presentation behavior, not token data. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vegastack/design-tokens",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
4
  "description": "VegaStack design tokens (DTCG → Tailwind v4 theme + TS)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -8,6 +8,9 @@
8
8
  "url": "git+https://github.com/VegaStack/vegastack-design.git",
9
9
  "directory": "packages/design-tokens"
10
10
  },
11
+ "engines": {
12
+ "node": ">=24.14.0"
13
+ },
11
14
  "type": "module",
12
15
  "sideEffects": [
13
16
  "**/*.css"
@@ -18,25 +21,27 @@
18
21
  "exports": {
19
22
  ".": {
20
23
  "types": "./dist/tokens.d.ts",
21
- "import": "./dist/tokens.js"
24
+ "import": "./dist/tokens.js",
25
+ "require": "./dist/tokens.cjs",
26
+ "default": "./dist/tokens.js"
22
27
  },
23
28
  "./theme.css": "./dist/theme.css",
24
29
  "./base.css": "./dist/base.css",
25
30
  "./utilities.css": "./dist/utilities.css",
26
- "./tokens.json": "./dist/tokens.json"
31
+ "./tokens.json": "./dist/tokens.json",
32
+ "./package.json": "./package.json"
33
+ },
34
+ "scripts": {
35
+ "build": "node build-tokens.mjs && node ../../tooling/contrast-check.mjs dist/theme.css && tsup src/tokens.ts --format esm,cjs --dts --out-dir dist",
36
+ "lint": "node ../../tooling/verify-theme-parity.mjs dist/theme.css && node ../../tooling/contrast-check.mjs dist/theme.css && node ../../tooling/design-lint.mjs --token-css src",
37
+ "typecheck": "tsc --noEmit"
27
38
  },
28
39
  "devDependencies": {
29
- "style-dictionary": "5.5.0",
30
- "typescript": "^6.0.3",
31
- "@vegastack/typescript-config": "0.0.0"
40
+ "@vegastack/typescript-config": "workspace:*",
41
+ "style-dictionary": "5.5.2",
42
+ "typescript": "catalog:"
32
43
  },
33
44
  "publishConfig": {
34
- "access": "public",
35
- "provenance": true
36
- },
37
- "scripts": {
38
- "build": "node build-tokens.mjs && node ../../tooling/contrast-check.mjs dist/theme.css && tsup src/tokens.ts --format esm --dts --out-dir dist",
39
- "lint": "node ../../tooling/contrast-check.mjs dist/theme.css && node ../../tooling/design-lint.mjs --token-css src",
40
- "typecheck": "tsc --noEmit"
45
+ "access": "public"
41
46
  }
42
- }
47
+ }