@motir/design-system 0.1.1 → 0.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motir/design-system",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Motir's 3-axis design system (colour · style · type): distributable token CSS, the axis registries, the theme-apply API, and framework-agnostic React primitives. Shared by motir-core and planner-scaffolded products.",
5
5
  "license": "GPL-3.0-only",
6
6
  "repository": {
package/theme.css CHANGED
@@ -2510,14 +2510,28 @@
2510
2510
  asset carries the correction and the measurement. */
2511
2511
  /* pink — BUILT, waiting on checks (MOTIR-3003). The one saturated Tier-0 hue
2512
2512
  no other STATUS spends; sharing a hue across FAMILIES is this file's own
2513
- convention (--el-type-epic is also --color-accent). ΔE 33.2 / 26.2 from its
2514
- nearest neighbour in this palette.
2513
+ convention (--el-type-epic is also --color-accent).
2515
2514
  ⚠️ FIVE palettes re-map --color-accent onto something a status already uses,
2516
2515
  so each carries its own override below — the same per-palette follow-up
2517
2516
  --el-status-planning needed in citrine and candy (MOTIR-2425). Measured by
2518
2517
  tests/theme/statusHueSeparation.test.ts, which now folds a color-mix rather
2519
- than throwing on one. */
2520
- --el-status-implemented: var(--color-accent);
2518
+ than throwing on one.
2519
+ ⚠️ STEPPED TOWARD THE INK (MOTIR-3954). The bare --color-accent was 2.66:1 on
2520
+ --el-card and 2.44:1 on --el-surface in the BASE palette's LIGHT theme —
2521
+ under 1.4.11's 3:1 for a graphical object, and the one palette x theme in
2522
+ which this token missed the bar. A saturated pink at Radix-9 lightness has
2523
+ nowhere to go on white, so the recipe takes one step of the palette's own
2524
+ emphasis ink instead of a new hue: #cd58a2, 3.81 / 3.50 / 3.81 on
2525
+ card / surface / page-bg. --color-charcoal flips with the theme (#37352f
2526
+ light, #e5e5e5 dark), so the same expression DARKENS on white and LIGHTENS
2527
+ on near-black — dark goes 7.22 -> 8.42:1. Both inputs stay palette-tuned, so
2528
+ the four palettes that ride this base value (cobalt, evergreen, spectrum,
2529
+ garnet) re-skin it as before and each GAINS contrast; ΔE2000 to the nearest
2530
+ sibling stays >= 13.7 in all ten palettes x both themes (27.1 / 26.8 here).
2531
+ Why the base and not a [data-palette='motir'] block: <html> carries no
2532
+ data-appearance-scope, so a root palette block would be a second cascade
2533
+ nobody renders — the base layer IS how the browser reaches these values. */
2534
+ --el-status-implemented: color-mix(in srgb, var(--color-accent) 75%, var(--color-charcoal));
2521
2535
  --el-status-in-review: var(--color-primary); /* brand — differentiate from in-progress */
2522
2536
  --el-status-done: var(--color-success); /* green — resolved */
2523
2537
  --el-status-blocked: var(--color-warning); /* amber — can't proceed (gap fixed) */
@@ -3043,6 +3057,125 @@
3043
3057
  /* `motir` needs no block — it is the Tier-3 base above. Each later palette
3044
3058
  ADDS its [data-palette='<id>'] override here (light + dark companion). */
3045
3059
 
3060
+ /* ── The `motir` BASE palette, re-asserted for a SCOPED subtree (MOTIR-3933) ──
3061
+ `motir` is the Tier-0 default, so it historically had NO block: the base
3062
+ values simply were the palette. That is correct for `<html>` and wrong for a
3063
+ NESTED scope, which inherits its ancestor's palette and has nothing to
3064
+ override it with — a `motir` preview under a `graphite` document rendered as
3065
+ graphite. Every other palette re-asserts, so the base must too.
3066
+
3067
+ ⚠️ SCOPED BY `[data-appearance-scope]`, DELIBERATELY. `<html>` never carries
3068
+ that attribute, so this block matches only a nested preview and the ROOT
3069
+ cascade is byte-identical to before. It also keeps the block from reading as
3070
+ a palette OVERRIDE: several `tests/theme/*` guards DERIVE "which palettes
3071
+ override this token" from the stylesheet, and a bare `[data-palette='motir']`
3072
+ block silently enrolled the BASE palette in bars written for overrides. It is
3073
+ not an override — it is a scope reset — and the selector now says so.
3074
+
3075
+ These values are COPIES, and the duplication is guarded rather than trusted:
3076
+ `tests/theme/scopedPreviewIsolation.test.ts` asserts each declaration here
3077
+ equals the value the base layer declares for that token, so a change to
3078
+ `@theme` that is not mirrored here fails rather than drifts. The token SET is
3079
+ the union of what any `[data-palette]` block overrides — a new palette that
3080
+ overrides a token absent here fails the same test.
3081
+ ⚠️ The path above was `test/paletteScopeIsolation.test.tsx` until MOTIR-3954,
3082
+ which is a file that has never existed, and the VALUE assertion it promised
3083
+ had never been written either — only the token-SET one had. Both are true
3084
+ now. A citation to a guard is worth exactly as much as the guard. */
3085
+ [data-appearance-scope][data-palette='motir'] {
3086
+ --color-accent: #ff64c8;
3087
+ --color-accent-green: #1aae39;
3088
+ --color-accent-orange: #dd5b00;
3089
+ --color-accent-teal: #2a9d99;
3090
+ --color-background: #ffffff;
3091
+ --color-border: #e5e3df;
3092
+ --color-canvas: #ecebe7;
3093
+ --color-charcoal: #37352f;
3094
+ --color-destructive: #e03131;
3095
+ --color-destructive-foreground: #ffffff;
3096
+ --color-foreground: #1a1a1a;
3097
+ --color-hairline: #e5e3df;
3098
+ --color-hairline-soft: #ede9e4;
3099
+ --color-hairline-strong: #c8c4be;
3100
+ --color-info: #0075de;
3101
+ --color-ink: #1a1a1a;
3102
+ --color-link: #0070d2;
3103
+ --color-link-pressed: #005bab;
3104
+ --color-muted: #f3f4f6;
3105
+ --color-muted-foreground: #787671;
3106
+ --color-primary: #5645d4;
3107
+ --color-primary-fill: #5645d4;
3108
+ --color-primary-foreground: #ffffff;
3109
+ --color-primary-pressed: #4534b3;
3110
+ --color-slate: #5d5b54;
3111
+ --color-steel: #787671;
3112
+ --color-stone: #a4a097;
3113
+ --color-success: #1aae39;
3114
+ --color-surface: #f6f5f4;
3115
+ --color-surface-soft: #fafaf9;
3116
+ --color-tint-lavender: #e6e0f5;
3117
+ --color-tint-mint: #d9f3e1;
3118
+ --color-tint-peach: #ffe8d4;
3119
+ --color-tint-rose: #fde0ec;
3120
+ --color-tint-sky: #dcecfa;
3121
+ --color-tint-yellow: #fef7d6;
3122
+ --color-warning: #dd5b00;
3123
+ --el-priority-high: var(--color-warning);
3124
+ --el-sidebar-item-bg-hover: #eeede9;
3125
+ --el-status-blocked: var(--color-warning);
3126
+ --el-status-cancelled: var(--color-steel);
3127
+ --el-status-implemented: color-mix(in srgb, var(--color-accent) 75%, var(--color-charcoal));
3128
+ --el-status-in-review: var(--color-primary);
3129
+ --el-status-planning: var(--color-accent-teal);
3130
+ }
3131
+
3132
+ /* The base palette's DARK arm — only the tokens the global dark block
3133
+ overrides; the rest keep the light block's values above. Two selectors for
3134
+ the same reason every other palette now has two: the compound one for
3135
+ `<html>`, the descendant one for a nested scope. */
3136
+ [data-theme='dark'] [data-appearance-scope][data-palette='motir']:not([data-theme]),
3137
+ [data-appearance-scope][data-palette='motir'][data-theme='dark'] {
3138
+ --color-background: #0f0f0f;
3139
+ --color-border: #2a2a2a;
3140
+ --color-canvas: #0e0e0e;
3141
+ --color-charcoal: #e5e5e5;
3142
+ --color-foreground: #f3f4f6;
3143
+ --color-hairline: #2a2a2a;
3144
+ --color-hairline-soft: #1f1f1f;
3145
+ --color-hairline-strong: #3a3a3a;
3146
+ --color-ink: #f3f4f6;
3147
+ --color-link: #58a6ff;
3148
+ --color-link-pressed: #79b8ff;
3149
+ --color-muted: #1a1a1a;
3150
+ --color-muted-foreground: #a4a097;
3151
+ --color-primary: #7b6ce5;
3152
+ --color-primary-fill: #6c5cdd;
3153
+ --color-primary-foreground: #ffffff;
3154
+ --color-primary-pressed: #5645d4;
3155
+ --color-slate: #a4a097;
3156
+ --color-steel: #787671;
3157
+ --color-stone: #5d5b54;
3158
+ --color-surface: #1a1a1a;
3159
+ --color-surface-soft: #161616;
3160
+ --color-tint-lavender: #2a253a;
3161
+ --color-tint-mint: #1a2f25;
3162
+ --color-tint-peach: #3a2a1a;
3163
+ --color-tint-rose: #3a1f2a;
3164
+ --color-tint-sky: #1a2a3a;
3165
+ --color-tint-yellow: #3a341a;
3166
+ --el-sidebar-item-bg-hover: #222222;
3167
+ }
3168
+
3169
+ /* The `motir` BASE type pairing, re-asserted (MOTIR-3933) — same reason as
3170
+ the base palette above. The type axis carries no descendant rules, so these
3171
+ three role tokens are the whole of it. */
3172
+ [data-appearance-scope][data-type='motir'] {
3173
+ --font-mono: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
3174
+ --font-sans:
3175
+ var(--font-sans-source, -apple-system), BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
3176
+ --font-serif: var(--font-serif-source, Georgia), serif;
3177
+ }
3178
+
3046
3179
  /* ── Palette: Cobalt (data-palette="cobalt") — Subtask 7.3.49 ───────────────
3047
3180
  A cool, calm, trustworthy INSTITUTIONAL-BLUE re-skin: slate-cool surfaces, a
3048
3181
  confident cobalt primary/accent, a cool cyan highlight, cooled pastel tints.
@@ -3146,6 +3279,7 @@
3146
3279
  /* Cobalt — dark companion. A cool navy canvas (not the base near-black);
3147
3280
  brightened cobalt/link hues so they read AS text on the dark surface, and a
3148
3281
  darker accent fill so white labels still clear AA. */
3282
+ [data-theme='dark'] [data-palette='cobalt']:not([data-theme]),
3149
3283
  [data-palette='cobalt'][data-theme='dark'] {
3150
3284
  --color-background: #0b111d; /* deep cool navy canvas */
3151
3285
  --color-foreground: #e7edf6;
@@ -3344,6 +3478,7 @@
3344
3478
  inverts to near-WHITE ink (Vercel's dark white-button), the accent blue
3345
3479
  brightens so it reads AS text on the dark canvas, and semantic/type hues
3346
3480
  brighten to clear AA on near-black. */
3481
+ [data-theme='dark'] [data-palette='graphite']:not([data-theme]),
3347
3482
  [data-palette='graphite'][data-theme='dark'] {
3348
3483
  --color-background: #0c0d0f; /* stark cool near-black canvas */
3349
3484
  --color-foreground: #edeef0;
@@ -3519,6 +3654,7 @@
3519
3654
  /* Evergreen — dark companion. Bright emerald on a deep-forest canvas
3520
3655
  (Supabase-style: the bright accent FILL carries DARK label text). Mirrors the
3521
3656
  Tier-1 dark flip for the cooled neutrals. */
3657
+ [data-theme='dark'] [data-palette='evergreen']:not([data-theme]),
3522
3658
  [data-palette='evergreen'][data-theme='dark'] {
3523
3659
  --color-primary: #3ddc97;
3524
3660
  --color-primary-foreground: #06140d; /* dark ink on the bright emerald fill */
@@ -3611,6 +3747,15 @@
3611
3747
  /* Sans headlines — re-point the editorial serif role at the sans stack. */
3612
3748
  --font-serif:
3613
3749
  var(--font-sans-source, -apple-system), BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
3750
+ /* Re-asserted at the BASE value, not omitted (MOTIR-3933). An omitted role
3751
+ inherits the ANCESTOR's, which is invisible on `<html>` and wrong in a
3752
+ nested scope: this pairing under a mono ancestor rendered mono. Unlike the
3753
+ colour axis there is no `[data-appearance-scope]` layer to fall back
3754
+ through — `--font-*` are role tokens, not `--el-*` — so every
3755
+ `[data-type]` block must be TOTAL over the three roles. */
3756
+ --font-sans:
3757
+ var(--font-sans-source, -apple-system), BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
3758
+ --font-mono: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
3614
3759
  }
3615
3760
 
3616
3761
  [data-type='motir-mono'] {
@@ -3621,6 +3766,13 @@
3621
3766
  already is mono. ONLY --font-* tokens (the disjointness guard). */
3622
3767
  --font-serif: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
3623
3768
  --font-sans: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
3769
+ /* Re-asserted at the BASE value, not omitted (MOTIR-3933). An omitted role
3770
+ inherits the ANCESTOR's, which is invisible on `<html>` and wrong in a
3771
+ nested scope: this pairing under a mono ancestor rendered mono. Unlike the
3772
+ colour axis there is no `[data-appearance-scope]` layer to fall back
3773
+ through — `--font-*` are role tokens, not `--el-*` — so every
3774
+ `[data-type]` block must be TOTAL over the three roles. */
3775
+ --font-mono: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
3624
3776
  }
3625
3777
 
3626
3778
  /* ── Palette: Spectrum (data-palette="spectrum") — Subtask 7.3.52 ───────────
@@ -3725,6 +3877,7 @@
3725
3877
  /* Spectrum — dark companion. A deep cool-violet canvas (not the base
3726
3878
  near-black); brightened violet/pink/link hues so they read AS text on the
3727
3879
  dark surface, and a darker accent fill so white labels still clear AA. */
3880
+ [data-theme='dark'] [data-palette='spectrum']:not([data-theme]),
3728
3881
  [data-palette='spectrum'][data-theme='dark'] {
3729
3882
  --color-background: #14111f; /* deep cool-violet canvas */
3730
3883
  --color-foreground: #ece9f6;
@@ -3893,6 +4046,7 @@
3893
4046
  canvas (#0B0E11) + "Shark" cards (#1E2329); the gold brightens to #FCD535 and
3894
4047
  the CTA fill carries dark labels; semantics are Binance's real buy-green
3895
4048
  #0ECB81 / sell-red #F6465D. */
4049
+ [data-theme='dark'] [data-palette='amber']:not([data-theme]),
3896
4050
  [data-palette='amber'][data-theme='dark'] {
3897
4051
  --color-background: #0b0e11; /* Binance deepest trading-floor black */
3898
4052
  --color-foreground: #eaecef;
@@ -4048,6 +4202,7 @@
4048
4202
  /* Sienna — dark companion. Mistral is light-dominant (few dark tokens: Mistral
4049
4203
  Black #1f1f1f); a warm near-black canvas built from it, the flame brightened
4050
4204
  to read as text, the fill a bright flame with dark-ink labels. */
4205
+ [data-theme='dark'] [data-palette='sienna']:not([data-theme]),
4051
4206
  [data-palette='sienna'][data-theme='dark'] {
4052
4207
  --color-background: #15120d; /* warm near-black canvas (from Mistral Black) */
4053
4208
  --color-foreground: #f5efe2;
@@ -4191,6 +4346,7 @@
4191
4346
  /* Garnet — dark companion. Pinterest's Cod-Gray near-black canvas; the red
4192
4347
  brightens to read as text, the CTA fill stays the deep Pushpin red carrying
4193
4348
  WHITE labels, semantics brighten to clear AA. */
4349
+ [data-theme='dark'] [data-palette='garnet']:not([data-theme]),
4194
4350
  [data-palette='garnet'][data-theme='dark'] {
4195
4351
  --color-background: #0e0e0e; /* Cod-Gray near-black canvas */
4196
4352
  --color-foreground: #efefef;
@@ -4364,6 +4520,7 @@
4364
4520
  #090909 / gray-900 #1a1b1e); the Sunglow yellow brightens to read as text and
4365
4521
  the fill keeps dark Stratos labels; blue/green/red brighten one ramp step to
4366
4522
  clear AA on the dark canvas. All values are documented Mirotone steps. */
4523
+ [data-theme='dark'] [data-palette='citrine']:not([data-theme]),
4367
4524
  [data-palette='citrine'][data-theme='dark'] {
4368
4525
  --color-background: #090909; /* Miro black */
4369
4526
  --color-foreground: #f6f7f9; /* gray-50 */
@@ -4527,6 +4684,7 @@
4527
4684
  1/2/3); the bubblegum pink brightens to read as text, the fill is a bright
4528
4685
  candy pink with dark labels, and the rainbow tints become deep candy washes.
4529
4686
  All values are documented Radix dark steps. */
4687
+ [data-theme='dark'] [data-palette='candy']:not([data-theme]),
4530
4688
  [data-palette='candy'][data-theme='dark'] {
4531
4689
  --color-background: #191117; /* Radix Pink dark-1 — pink-tinted black */
4532
4690
  --color-foreground: #eeeef0; /* Mauve dark-12 */
@@ -4600,6 +4758,13 @@
4600
4758
  disjointness guard): no colour (--el / --color) or shape token. */
4601
4759
  --font-serif: var(--font-grotesk-source), ui-sans-serif, system-ui, sans-serif;
4602
4760
  --font-sans: var(--font-grotesk-source), ui-sans-serif, system-ui, sans-serif;
4761
+ /* Re-asserted at the BASE value, not omitted (MOTIR-3933). An omitted role
4762
+ inherits the ANCESTOR's, which is invisible on `<html>` and wrong in a
4763
+ nested scope: this pairing under a mono ancestor rendered mono. Unlike the
4764
+ colour axis there is no `[data-appearance-scope]` layer to fall back
4765
+ through — `--font-*` are role tokens, not `--el-*` — so every
4766
+ `[data-type]` block must be TOTAL over the three roles. */
4767
+ --font-mono: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
4603
4768
  }
4604
4769
 
4605
4770
  [data-type='editorial'] {
@@ -4611,6 +4776,15 @@
4611
4776
  size tweak. ONLY --font-* role tokens here (the disjointness guard): no
4612
4777
  colour (--el/--color) or shape (--radius/--spacing/--shadow) token. */
4613
4778
  --font-serif: var(--font-editorial-source), 'Fraunces', Georgia, serif;
4779
+ /* Re-asserted at the BASE value, not omitted (MOTIR-3933). An omitted role
4780
+ inherits the ANCESTOR's, which is invisible on `<html>` and wrong in a
4781
+ nested scope: this pairing under a mono ancestor rendered mono. Unlike the
4782
+ colour axis there is no `[data-appearance-scope]` layer to fall back
4783
+ through — `--font-*` are role tokens, not `--el-*` — so every
4784
+ `[data-type]` block must be TOTAL over the three roles. */
4785
+ --font-sans:
4786
+ var(--font-sans-source, -apple-system), BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
4787
+ --font-mono: var(--font-mono-source, ui-monospace), 'SF Mono', Menlo, monospace;
4614
4788
  }
4615
4789
 
4616
4790
  [data-type='mono-technical'] {