@tangle-network/brand 1.15.5 → 1.15.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.15.7
4
+
5
+ ### Patch Changes
6
+
7
+ - cfc08fb: Accent text now reads at 4.5:1 on a selected item's primary tint (10–20%) over the canvas, card and muted surfaces in every theme scope. Darkened light `--accent-text`: canonical, legacy-light, agents-light and website light #5047eb → #3c32e9; aubergine-light #6d28d9 → #6423c8; arena-light #047857 → #03674b; tangle-light #4f46e5 → #453be3; system light iris-11 (#5753c6) → #4e4ac3. Lightened hospitality dark #76b791 → #99caad, with its `--md3-primary` kept equal to the accent text. A new `accent-on-tint.test.ts` asserts the pair for every scope, sharing the scope list with the Switch contrast suite through `theme-scopes.ts`. Agent App projects these values from Brand, so apps pick them up with the next Agent App release built on this version.
8
+
9
+ ## 1.15.6
10
+
11
+ ### Patch Changes
12
+
13
+ - fc13212: Lower the Blueprint drift baseline to its develop head 92ab705d1: raw palette 204 -> 0, hex 118 -> 0, arbitrary color 49 -> 45, local primitives 2 -> 0.
14
+
3
15
  ## 1.15.5
4
16
 
5
17
  ### Patch Changes
@@ -856,108 +856,33 @@
856
856
  "apps/web"
857
857
  ],
858
858
  "branch": "develop",
859
- "head": "517650bcac5a",
860
- "tsx_files": 365,
859
+ "head": "92ab705d1313",
860
+ "tsx_files": 357,
861
861
  "css_files": 2,
862
862
  "versions": {
863
863
  "agent-app": [
864
- "0.53.9"
864
+ "0.53.17"
865
865
  ],
866
866
  "brand": [
867
- "^1.13.0"
867
+ "^1.15.2"
868
868
  ],
869
869
  "sandbox-ui": [
870
- "0.122.3"
870
+ "0.125.3"
871
871
  ],
872
872
  "ui": [
873
- "^11.19.0"
873
+ "^11.21.0"
874
874
  ]
875
875
  },
876
876
  "importing_files": {
877
877
  "brand": 0,
878
- "ui": 174,
878
+ "ui": 184,
879
879
  "sandbox-ui": 3,
880
880
  "agent-app": 3
881
881
  },
882
882
  "by_file": {
883
- "raw_palette": {
884
- "apps/web/src/components/@settings/tabs/connections/components/PullFromGitHubDialog.tsx": 13,
885
- "apps/web/src/components/@settings/tabs/connections/components/PushToGitHubDialog.tsx": 1,
886
- "apps/web/src/components/@settings/tabs/partner-dashboard/PartnerAnalyticsTab.tsx": 1,
887
- "apps/web/src/components/@settings/tabs/partner-dashboard/PartnerDashboardTab.tsx": 3,
888
- "apps/web/src/components/@settings/tabs/partner-dashboard/PartnerProjectsTab.tsx": 1,
889
- "apps/web/src/components/@settings/tabs/partner-dashboard/PartnerQuestsTab.tsx": 6,
890
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/DomainManagementSection.tsx": 4,
891
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/FaucetTransactionHistory.tsx": 11,
892
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/InviteCodesSection.tsx": 2,
893
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/PoolBreakdownTooltip.tsx": 3,
894
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/ProjectDetailModal.tsx": 1,
895
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/SeatsTable.tsx": 13,
896
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/UnlinkDomainModal.tsx": 1,
897
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/dialogs/InviteDialog.tsx": 6,
898
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/dialogs/RemovalPolicyDialog.tsx": 4,
899
- "apps/web/src/components/@settings/tabs/partner-dashboard/partner-quests/EditQuestDialog.tsx": 1,
900
- "apps/web/src/components/@settings/tabs/partner-dashboard/partner-quests/QuestDetailModal.tsx": 1,
901
- "apps/web/src/components/@settings/tabs/partner-dashboard/partner-quests/faucet-status.ts": 9,
902
- "apps/web/src/components/batch/ExecutionCardPrimitives.tsx": 2,
903
- "apps/web/src/components/chat/Messages.client.tsx": 4,
904
- "apps/web/src/components/chat/extensions/StarterTemplates.tsx": 3,
905
- "apps/web/src/components/chat/run/InlineToolItem.tsx": 2,
906
- "apps/web/src/components/chat/sections/ProjectHistoryShowcase.tsx": 3,
907
- "apps/web/src/components/chat/toolExtras/GrepResultsPreview.tsx": 6,
908
- "apps/web/src/components/header/ProfileDropdown.tsx": 3,
909
- "apps/web/src/components/judge/ScoringForm.tsx": 1,
910
- "apps/web/src/components/leaderboard/ChampionHero.tsx": 1,
911
- "apps/web/src/components/leaderboard/PodiumRow.tsx": 6,
912
- "apps/web/src/components/quests/CompletedQuestsHistory.tsx": 3,
913
- "apps/web/src/components/quests/EventCard.tsx": 4,
914
- "apps/web/src/components/quests/QuestCard.tsx": 2,
915
- "apps/web/src/components/quests/QuestDetailModal.tsx": 2,
916
- "apps/web/src/components/quests/QuestGrid.tsx": 2,
917
- "apps/web/src/components/quests/RankingsTable.tsx": 1,
918
- "apps/web/src/components/quests/competition/CompetitionHero.tsx": 10,
919
- "apps/web/src/components/quests/competition/CompetitionLeaderboard.tsx": 2,
920
- "apps/web/src/components/quests/competition/CompetitionResults.tsx": 4,
921
- "apps/web/src/components/sidebar/Menu.client.tsx": 2,
922
- "apps/web/src/data/pricing.tsx": 14,
923
- "apps/web/src/routes/judge.tsx": 1,
924
- "apps/web/src/routes/leaderboard._index.tsx": 1,
925
- "apps/web/src/routes/partner-onboarding.tsx": 17,
926
- "apps/web/src/routes/partner.invite.$code.tsx": 14,
927
- "apps/web/src/routes/partner_.$partnerId.events.tsx": 11,
928
- "apps/web/src/routes/quests.tsx": 2
929
- },
930
- "hex": {
931
- "apps/web/src/components/@settings/tabs/connections/NetlifyConnection.tsx": 3,
932
- "apps/web/src/components/@settings/tabs/partner-dashboard/components/DomainManagementSection.tsx": 1,
933
- "apps/web/src/components/batch/BatchInput.tsx": 6,
934
- "apps/web/src/components/batch/analytics/ProfileLeaderboard.tsx": 2,
935
- "apps/web/src/components/batch/analytics/TokenBreakdownChart.tsx": 5,
936
- "apps/web/src/components/charts/RealnessHeatmap.tsx": 2,
937
- "apps/web/src/components/chat/CodeBlock.tsx": 4,
938
- "apps/web/src/components/chat/extensions/StarterTemplates.tsx": 2,
939
- "apps/web/src/components/chat/sections/ChatInput.tsx": 6,
940
- "apps/web/src/components/deployments/tabs/LogsTab.tsx": 1,
941
- "apps/web/src/components/emails/BaseEmailTemplate.tsx": 6,
942
- "apps/web/src/components/emails/EventPhaseEmail.tsx": 9,
943
- "apps/web/src/components/emails/InactiveProjectWarningEmail.tsx": 1,
944
- "apps/web/src/components/emails/PartnerApplicationReceivedEmail.tsx": 4,
945
- "apps/web/src/components/emails/PartnerApprovedEmail.tsx": 4,
946
- "apps/web/src/components/emails/PartnerPaymentFailedEmail.tsx": 6,
947
- "apps/web/src/components/emails/PaymentFailedEmail.tsx": 8,
948
- "apps/web/src/components/emails/PrizeWonEmail.tsx": 8,
949
- "apps/web/src/components/emails/QuestCompletedEmail.tsx": 4,
950
- "apps/web/src/components/layout/Logo.tsx": 4,
951
- "apps/web/src/components/leaderboard/CostFrontier.tsx": 2,
952
- "apps/web/src/components/leaderboard/RowSparkline.tsx": 2,
953
- "apps/web/src/components/profiles/ProfileEditorModal.tsx": 10,
954
- "apps/web/src/components/quests/RewardCelebration.tsx": 10,
955
- "apps/web/src/components/quests/competition/CompetitionAnalytics.tsx": 3,
956
- "apps/web/src/components/ui/marble-avatar.tsx": 5
957
- },
883
+ "raw_palette": {},
884
+ "hex": {},
958
885
  "arbitrary_color": {
959
- "apps/web/src/components/@settings/tabs/connections/NetlifyConnection.tsx": 3,
960
- "apps/web/src/components/deployments/tabs/LogsTab.tsx": 1,
961
886
  "apps/web/src/config/partners/data-balancer.ts": 8,
962
887
  "apps/web/src/config/partners/data-coinbase.ts": 8,
963
888
  "apps/web/src/config/partners/data-dspy.ts": 9,
@@ -967,46 +892,15 @@
967
892
  "css_var_defs": {
968
893
  "apps/web/src/components/blur-fade/fade.module.css": 2
969
894
  },
970
- "local_primitive_count": {
971
- "apps/web/src/components/leaderboard/primitives.tsx": 1,
972
- "apps/web/src/components/sidebar/Menu.client.tsx": 1
973
- }
895
+ "local_primitive_count": {}
974
896
  },
975
- "raw_palette": 204,
976
- "hex": 118,
977
- "arbitrary_color": 49,
897
+ "raw_palette": 0,
898
+ "hex": 0,
899
+ "arbitrary_color": 45,
978
900
  "css_var_defs": 2,
979
- "local_primitive_count": 2,
980
- "palette_top": [
981
- [
982
- "text-amber-500",
983
- 14
984
- ],
985
- [
986
- "bg-amber-500",
987
- 10
988
- ],
989
- [
990
- "border-red-500",
991
- 7
992
- ],
993
- [
994
- "bg-amber-400",
995
- 6
996
- ],
997
- [
998
- "bg-red-500",
999
- 6
1000
- ],
1001
- [
1002
- "border-amber-500",
1003
- 6
1004
- ]
1005
- ],
1006
- "local_primitives": {
1007
- "Menu": 1,
1008
- "StatusBadge": 1
1009
- }
901
+ "local_primitive_count": 0,
902
+ "palette_top": [],
903
+ "local_primitives": {}
1010
904
  },
1011
905
  "super": {
1012
906
  "repo": "tangle-network/super-agent",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/brand",
3
- "version": "1.15.5",
3
+ "version": "1.15.7",
4
4
  "description": "Tangle brand system — design tokens, logo, Tailwind v4 theme. Single source of truth for every Tangle app.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,42 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { compositeOver, contrastRatio } from "./css-test-utils";
3
+ import { resolve, scopes } from "./theme-scopes";
4
+
5
+ /**
6
+ * Selected and active states draw accent text on a primary tint. In apps on
7
+ * the Agent App preset, `text-primary` resolves to `--accent-text`, and a
8
+ * selected filter chip, tab or count badge is `text-primary` on `bg-primary/10`
9
+ * to `bg-primary/20`. That text is body-sized, so WCAG 1.4.3 asks 4.5:1 against
10
+ * the tinted fill, not only against the bare surface the other suites check.
11
+ * Brand's own theme maps `text-primary` to the primary fill instead; this
12
+ * suite does not cover that pairing, and stacked tints are out of range.
13
+ *
14
+ * This suite composites the primary fill at each opacity products use over
15
+ * every surface a selected item sits on, in every scope a consumer can select,
16
+ * and holds the accent text to 4.5:1 on each result.
17
+ */
18
+ const TINTS = [0.1, 0.15, 0.2] as const;
19
+ const SURFACES = [
20
+ ["canvas", "hsl-background"],
21
+ ["card", "hsl-card"],
22
+ ["muted", "hsl-muted"],
23
+ ] as const;
24
+
25
+ describe("accent text reads on the selected tint in every theme scope", () => {
26
+ for (const { name, sources } of scopes) {
27
+ it(name, () => {
28
+ const accentText = resolve(sources, "accent-text");
29
+ const primary = resolve(sources, "hsl-primary");
30
+ for (const [surfaceName, token] of SURFACES) {
31
+ const surface = resolve(sources, token);
32
+ for (const alpha of TINTS) {
33
+ const tint = compositeOver(primary, surface, alpha);
34
+ expect(
35
+ contrastRatio(accentText, tint),
36
+ `accent text on primary/${alpha * 100} over ${surfaceName}`,
37
+ ).toBeGreaterThanOrEqual(4.5);
38
+ }
39
+ }
40
+ });
41
+ }
42
+ });
@@ -1,6 +1,6 @@
1
1
  /* GENERATED by scripts/gen-compat.mjs. Do not edit.
2
2
  * Source: tokens.css + named-themes.css
3
- * SHA-256: 9d5eee8a83628c74a74b439891d4fdff80ebc80e178fd009e0c0e54191c0a211
3
+ * SHA-256: ba2f212c39d2e271aacad642df566bad7871b0a4addcb44a7744e9e7f58276c7
4
4
  * Light-default compatibility; all explicit modes remain canonical.
5
5
  */
6
6
  :where(:root),
@@ -598,7 +598,7 @@
598
598
  --accent-gradient-strong: linear-gradient(135deg, #4F46E5, #5B4ED4);
599
599
  --accent-surface-soft: rgba(76, 63, 198, 0.05);
600
600
  --accent-surface-strong: rgba(76, 63, 198, 0.13);
601
- --accent-text: #5047eb;
601
+ --accent-text: #3c32e9;
602
602
  --font-sans: "Inter", "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;
603
603
  --font-display: "Inter", "Geist", sans-serif;
604
604
  --font-mono: "Geist Mono", "JetBrains Mono", ui-monospace, monospace;
@@ -893,7 +893,7 @@
893
893
  --border-subtle: rgba(60,32,110,0.10);
894
894
  --border-default: rgba(60,32,110,0.16);
895
895
  --border-hover: rgba(60,32,110,0.30);
896
- --accent-text: #6d28d9;
896
+ --accent-text: #6423c8;
897
897
  --brand-primary: #6d28d9;
898
898
  --brand-glow: #8b5cf6;
899
899
  --text-primary: #160f22;
@@ -985,7 +985,7 @@
985
985
  --border-subtle: rgba(6,95,70,0.10);
986
986
  --border-default: rgba(6,95,70,0.16);
987
987
  --border-hover: rgba(6,95,70,0.30);
988
- --accent-text: #047857;
988
+ --accent-text: #03674b;
989
989
  --brand-primary: #059669;
990
990
  --brand-glow: #10b981;
991
991
  --text-primary: #06231a;
@@ -1193,7 +1193,7 @@
1193
1193
  --border-subtle: rgba(20,20,40,0.08);
1194
1194
  --border-default: rgba(20,20,40,0.14);
1195
1195
  --border-hover: rgba(20,20,40,0.26);
1196
- --accent-text: #4f46e5;
1196
+ --accent-text: #453be3;
1197
1197
  --brand-primary: #6366f1;
1198
1198
  --brand-glow: #818cf8;
1199
1199
  --text-primary: #15131f;
@@ -1353,7 +1353,7 @@
1353
1353
  --md3-on-surface-variant: #afbbb4;
1354
1354
  --md3-outline: #8a938e;
1355
1355
  --md3-outline-variant: #48504c;
1356
- --md3-primary: #76b791;
1356
+ --md3-primary: #99caad;
1357
1357
  --md3-primary-dim: #396a52;
1358
1358
  --md3-primary-container: #396a52;
1359
1359
  --md3-on-primary: #11221a;
@@ -1375,7 +1375,7 @@
1375
1375
  --border-hover: rgba(227, 233, 229, 0.28);
1376
1376
  --border-accent: rgba(118, 183, 145, 0.35);
1377
1377
  --border-accent-hover: rgba(118, 183, 145, 0.50);
1378
- --accent-text: #76b791;
1378
+ --accent-text: #99caad;
1379
1379
  --accent-surface-soft: rgba(118, 183, 145, 0.07);
1380
1380
  --accent-surface-strong: rgba(57, 106, 82, 0.40);
1381
1381
  --accent-gradient-strong: linear-gradient(135deg, #396a52, #76b791);
@@ -106,7 +106,7 @@
106
106
  --md3-surface-container: #f4f1fc; --md3-surface-container-high: #ece6f8;
107
107
  --md3-surface-container-highest: #e4dcf4; --md3-outline-variant: #e0d8f0;
108
108
  --border-subtle: rgba(60,32,110,0.10); --border-default: rgba(60,32,110,0.16);
109
- --border-hover: rgba(60,32,110,0.30); --accent-text: #6d28d9;
109
+ --border-hover: rgba(60,32,110,0.30); --accent-text: #6423c8;
110
110
  --brand-primary: #6d28d9; --brand-glow: #8b5cf6;
111
111
  --text-primary: #160f22; --text-secondary: #3f3460; --text-muted: #585070; --text-dim: #655d82;
112
112
  --md3-surface: var(--bg-root); --md3-surface-dim: var(--depth-2);
@@ -152,7 +152,7 @@
152
152
  --md3-surface-container: #f0f6f2; --md3-surface-container-high: #e6f0ea;
153
153
  --md3-surface-container-highest: #dcebe2; --md3-outline-variant: #d7e8de;
154
154
  --border-subtle: rgba(6,95,70,0.10); --border-default: rgba(6,95,70,0.16);
155
- --border-hover: rgba(6,95,70,0.30); --accent-text: #047857;
155
+ --border-hover: rgba(6,95,70,0.30); --accent-text: #03674b;
156
156
  --brand-primary: #059669; --brand-glow: #10b981;
157
157
  --text-primary: #06231a; --text-secondary: #285048; --text-muted: #455f52; --text-dim: #4e6d62;
158
158
  --md3-surface: var(--bg-root); --md3-surface-dim: var(--depth-2);
@@ -263,7 +263,7 @@
263
263
  --md3-surface-container: #f4f4f9; --md3-surface-container-high: #edecf4;
264
264
  --md3-surface-container-highest: #e5e4ee; --md3-outline-variant: #e2e1ec;
265
265
  --border-subtle: rgba(20,20,40,0.08); --border-default: rgba(20,20,40,0.14);
266
- --border-hover: rgba(20,20,40,0.26); --accent-text: #4f46e5;
266
+ --border-hover: rgba(20,20,40,0.26); --accent-text: #453be3;
267
267
  --brand-primary: #6366f1; --brand-glow: #818cf8;
268
268
  --text-primary: #15131f; --text-secondary: #3f3a52; --text-muted: #59556b; --text-dim: #67627b;
269
269
  }
@@ -393,7 +393,7 @@
393
393
  --md3-surface-container-highest: #383e3b; --md3-surface-variant: #323835;
394
394
  --md3-on-surface: #e3e9e5; --md3-on-surface-variant: #afbbb4;
395
395
  --md3-outline: #8a938e; --md3-outline-variant: #48504c;
396
- --md3-primary: #76b791; --md3-primary-dim: #396a52; --md3-primary-container: #396a52;
396
+ --md3-primary: #99caad; --md3-primary-dim: #396a52; --md3-primary-container: #396a52;
397
397
  --md3-on-primary: #11221a; --md3-surface-tint: #76b791;
398
398
  --depth-1: #141816; --depth-2: #2d3330; --depth-3: #323835; --depth-4: #383e3b;
399
399
  --bg-root: #141816; --bg-dark: #141816; --bg-section: #141816;
@@ -402,7 +402,7 @@
402
402
  --border-subtle: rgba(227, 233, 229, 0.10); --border-default: rgba(227, 233, 229, 0.16);
403
403
  --border-hover: rgba(227, 233, 229, 0.28);
404
404
  --border-accent: rgba(118, 183, 145, 0.35); --border-accent-hover: rgba(118, 183, 145, 0.50);
405
- --accent-text: #76b791;
405
+ --accent-text: #99caad;
406
406
  --accent-surface-soft: rgba(118, 183, 145, 0.07); --accent-surface-strong: rgba(57, 106, 82, 0.40);
407
407
  --accent-gradient-strong: linear-gradient(135deg, #396a52, #76b791);
408
408
  --brand-primary: #529873; --brand-cool: #76b791; --brand-glow: #94c7a9; --brand-vibrant: #529873;
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { contrastRatio } from "./css-test-utils";
3
+ import { resolve, scopes, themes } from "./theme-scopes";
4
+
5
+ /**
6
+ * The token pairs @tangle-network/ui's Switch is drawn with.
7
+ *
8
+ * A switch is non-text content, so WCAG 1.4.11 asks 3:1 for the parts that show
9
+ * its state: the track's edge against the surface it sits on, and the thumb
10
+ * against the track. Off is a muted-foreground outline and thumb on the
11
+ * surface. On is an accent-text track carrying a card-coloured thumb, so the
12
+ * thumb-on-track pair is the same pair as track-on-card, inverted. The primary
13
+ * fill cannot do this job: it is tuned to carry white text and measures about
14
+ * 2.4:1 on the dark card and canvas.
15
+ *
16
+ * These are text tokens, held to 4.5:1 elsewhere for SOME themes; this suite
17
+ * asserts the 3:1 the Switch depends on in EVERY scope a consumer can select.
18
+ */
19
+ describe("the Switch's token pairs clear 3:1 in every theme scope", () => {
20
+ it("covers every named theme", () => {
21
+ const rules = themes.replace(/\/\*[\s\S]*?\*\//g, "");
22
+ const declared = new Set([...rules.matchAll(/\[data-theme="([a-z-]+)"\]/g)].map((m) => m[1]));
23
+ // Hospitality and website take their mode from .dark, so one name covers two scopes.
24
+ const covered = new Set(scopes.map((s) => s.name.replace(/ (light|dark)$/, "")));
25
+ for (const name of declared) {
26
+ expect(covered.has(name), `[data-theme="${name}"] needs a scope here`).toBe(true);
27
+ }
28
+ });
29
+
30
+ for (const { name, sources } of scopes) {
31
+ it(name, () => {
32
+ const card = resolve(sources, "hsl-card");
33
+ const canvas = resolve(sources, "hsl-background");
34
+ const accentText = resolve(sources, "accent-text");
35
+ const mutedFg = resolve(sources, "hsl-muted-foreground");
36
+
37
+ // On: the accent-text track against the surface, and the card thumb on it.
38
+ expect(contrastRatio(accentText, card), "on track on card / card thumb on track").toBeGreaterThanOrEqual(3);
39
+ expect(contrastRatio(accentText, canvas), "on track on canvas").toBeGreaterThanOrEqual(3);
40
+ // Off: the muted-foreground outline and thumb against the surface.
41
+ expect(contrastRatio(mutedFg, card), "off outline on card").toBeGreaterThanOrEqual(3);
42
+ expect(contrastRatio(mutedFg, canvas), "off outline on canvas").toBeGreaterThanOrEqual(3);
43
+ });
44
+ }
45
+ });
@@ -221,7 +221,7 @@
221
221
  --on-ink: #ffffff;
222
222
  --accent-solid: var(--iris-9);
223
223
  --accent-solid-hover: var(--iris-10);
224
- --accent-text: var(--iris-11);
224
+ --accent-text: #4e4ac3;
225
225
  --accent-soft: var(--iris-3);
226
226
  --accent-soft-hover: var(--iris-4);
227
227
  --accent-line: var(--iris-7);
@@ -0,0 +1,105 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { blockIn, blocksIn, hslToRgb } from "./css-test-utils";
4
+
5
+ /**
6
+ * Every theme scope a consumer can select, each with its CSS sources listed
7
+ * from the most specific to the baseline it inherits from, and a resolver that
8
+ * follows `var()` and `hsl(var())` through them the way the cascade does.
9
+ * Contrast suites iterate these so a new named theme cannot skip a check.
10
+ */
11
+ const read = (name: string) =>
12
+ readFileSync(path.resolve(import.meta.dirname, name), "utf8");
13
+ const tokens = read("tokens.css");
14
+ export const themes = read("named-themes.css");
15
+ const legacy = read("legacy-light.css");
16
+ const system = read("system.css");
17
+ const ladders = read("ladders.css");
18
+
19
+ /** The spine block of a selector: the rule that declares the surface ladder. */
20
+ function spine(source: string, selector: string): string {
21
+ const block = blocksIn(source, selector).find((b) => b.includes("--hsl-background:"));
22
+ if (!block) throw new Error(`missing spine block: ${selector}`);
23
+ return block;
24
+ }
25
+
26
+ const DARK = spine(tokens, ".dark");
27
+ const LIGHT = spine(tokens, ".light");
28
+ // The shared light-ink group that also lists every named light theme.
29
+ const LIGHT_INK = blocksIn(themes, '[data-theme="agents-light"]').find(
30
+ (b) => !b.includes("--hsl-background:"),
31
+ );
32
+ if (!LIGHT_INK) throw new Error("missing the shared named-light ink group");
33
+
34
+ const named = (selector: string) => spine(themes, selector);
35
+
36
+ /**
37
+ * Each scope lists its sources from the most specific to the baseline it
38
+ * inherits from, as the cascade resolves them.
39
+ */
40
+ export const scopes: Array<{ name: string; sources: string[] }> = [
41
+ { name: "canonical dark", sources: [DARK] },
42
+ { name: "canonical light", sources: [LIGHT, DARK] },
43
+ {
44
+ name: "legacy-light default",
45
+ sources: [spine(legacy, ":where(:root)"), spine(legacy, ".dark")],
46
+ },
47
+ {
48
+ name: "system dark",
49
+ sources: [spine(system, ".dark"), blockIn(ladders, ".dark")],
50
+ },
51
+ {
52
+ name: "system light",
53
+ sources: [
54
+ spine(system, ".light"),
55
+ blockIn(ladders, ".light"),
56
+ spine(system, ".dark"),
57
+ blockIn(ladders, ".dark"),
58
+ ],
59
+ },
60
+ ...["aubergine", "arena", "super", "tangle-dark", "agents"].map((name) => ({
61
+ name,
62
+ sources: [named(`[data-theme="${name}"]`), DARK],
63
+ })),
64
+ ...["aubergine-light", "arena-light", "super-light", "tangle-light", "agents-light"].map(
65
+ (name) => ({
66
+ name,
67
+ sources: [named(`[data-theme="${name}"]`), LIGHT_INK, LIGHT, DARK],
68
+ }),
69
+ ),
70
+ { name: "intelligence", sources: [named('.dark[data-theme="intelligence"]'), DARK] },
71
+ {
72
+ name: "hospitality light",
73
+ sources: [named('[data-theme="hospitality"]:where(:not(.dark))'), LIGHT, DARK],
74
+ },
75
+ { name: "hospitality dark", sources: [named('.dark[data-theme="hospitality"]'), DARK] },
76
+ {
77
+ name: "website light",
78
+ sources: [named('[data-theme="website"]:where(:not(.dark))'), LIGHT, DARK],
79
+ },
80
+ { name: "website dark", sources: [named('.dark[data-theme="website"]'), DARK] },
81
+ ];
82
+
83
+ export type Rgb = [number, number, number];
84
+
85
+ /**
86
+ * Resolve a token to RGB through the scope's sources, following `var()` and
87
+ * `hsl(var())` references the way the cascade would: from the first source that
88
+ * declares each name.
89
+ */
90
+ export function resolve(sources: string[], token: string): Rgb {
91
+ const raw = sources
92
+ .map((css) => css.match(new RegExp(`(?<![\\w-])--${token}:\\s*([^;]+);`))?.[1]?.trim())
93
+ .find((value) => value !== undefined);
94
+ if (raw === undefined) throw new Error(`--${token} resolves to nothing`);
95
+
96
+ const ref = /^(?:hsl\()?var\(--([a-z0-9-]+)\)\)?$/.exec(raw);
97
+ if (ref) return resolve(sources, ref[1]);
98
+ const hex = /^#([0-9a-fA-F]{6})\b/.exec(raw);
99
+ if (hex) {
100
+ return [0, 2, 4].map((i) => Number.parseInt(hex[1].slice(i, i + 2), 16)) as Rgb;
101
+ }
102
+ const hsl = /^([\d.]+)\s+([\d.]+)%\s+([\d.]+)%/.exec(raw);
103
+ if (hsl) return hslToRgb({ h: Number(hsl[1]), s: Number(hsl[2]), l: Number(hsl[3]) });
104
+ throw new Error(`--${token} has an unreadable value: ${raw}`);
105
+ }
@@ -852,7 +852,7 @@
852
852
  --accent-gradient-strong: linear-gradient(135deg, #4F46E5, #5B4ED4);
853
853
  --accent-surface-soft: rgba(76, 63, 198, 0.05);
854
854
  --accent-surface-strong: rgba(76, 63, 198, 0.13);
855
- --accent-text: #5047eb;
855
+ --accent-text: #3c32e9;
856
856
 
857
857
  /* Typography — unified on Inter */
858
858
  --font-sans: "Inter", "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;