@moshevents/tokens 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -42,6 +42,32 @@ Then import the CSS once:
42
42
 
43
43
  `npm update @moshevents/tokens` pulls new token values — no vendored copy to sync.
44
44
 
45
+ ### Light + dark
46
+
47
+ **Light is the default everywhere; dark is opt-in** (Garrett, 2026-10-10). Semantic tokens
48
+ (`--color-bg`, `--surface-*`, `--text-*`, `--border-*`, `--fill-*`, `--shadow-*`,
49
+ `--gradient-surface`) carry **two values** in `tokens.json`
50
+ (`"$value": { "light": …, "dark": … }`). Brand and status hues are single-valued.
51
+
52
+ - **Website (`tokens.css`)** — `:root` is the **light** palette (the site is light-first).
53
+ A dark-by-design surface (event page, event builder, ticket modal) opts in with
54
+ `data-theme="dark"` on its root element and the semantic tokens flip underneath it.
55
+ Theme is attribute-driven, never `prefers-color-scheme` — resolve the preference in JS
56
+ and set the attribute (the venue dashboard already does this).
57
+ - **Admin (`theme.css`)** — `@theme` carries the **light** values too. The console keeps its
58
+ dark look by setting `data-theme="dark"` on `<html>`, which rebinds the same Tailwind
59
+ variables; remove the attribute and it is light.
60
+ - **iOS** — two-valued colors generate as dynamic `UIColor { trait in … }`, so they follow
61
+ the system appearance automatically; SwiftUI `Color` wraps the same UIColor.
62
+
63
+ **Role surfaces** (`--surface-canvas|card|field|inset|overlay`) say *what* a surface is; the
64
+ numbered ramp (`--surface-0…7`) says *how far off the canvas* it sits. New components should
65
+ read the role tokens — they are what make light and dark both look right (cards are white on
66
+ light but lifted on dark; the gray tint on light appears only in fields and insets).
67
+
68
+ Adding a two-valued token: give it `{ "light", "dark" }` and run `npm run build`; every
69
+ output picks up both. Everything else stays single-valued.
70
+
45
71
  **iOS — SwiftPM `MoshTokens`.** Xcode → Add Package Dependencies →
46
72
  `https://github.com/moshorg/mosh-tokens`, pin a version, then:
47
73
 
@@ -76,9 +102,10 @@ project is the rendered view of the same values. **One source, two audiences.**
76
102
 
77
103
  ## Notes / open decisions
78
104
 
79
- - **Dark-only today.** Every token is a single dark-mode value (matches web). iOS supports
80
- light + dark; when we wire iOS fully (Phase 3), either add light/dark variants to the
81
- schema (`{ "$value": { "light": …, "dark": … } }`) or commit iOS to dark-only.
105
+ - **Light values are the neutral ramp** (matches iOS `Theme.swift` and the public website:
106
+ `#111111` text, `#f2f2f3` cards). The venue dashboard's light mode uses a *slate* ramp
107
+ (`#0f172a` text, slate alphas — Garrett, 2026-09-16) via its own `--vd-*` layer; whether
108
+ that becomes a token variant or stays a dashboard override is an open decision.
82
109
  - **iOS dimension conversion** is `px → pt` 1:1 and `rem × 16`; revisit if the iOS type
83
110
  scale should differ from web.
84
111
  - **Gradients/shadows** generate for web (CSS) and admin (Tailwind) but are omitted from
package/dist/theme.css CHANGED
@@ -9,31 +9,42 @@
9
9
  --color-brand-soft: #ff3d3d;
10
10
  --color-brand-deep: #b30000;
11
11
  --color-accent-deep: #b30000;
12
- --color-bg: #000000;
13
- --color-surface-0: #0a0a0a;
14
- --color-surface-1: #121212;
15
- --color-surface-2: #151515;
16
- --color-surface-3: #1a1a1a;
17
- --color-surface-4: #1f1f1f;
18
- --color-surface-5: #252525;
19
- --color-surface-6: #2a2a2a;
20
- --color-surface-7: #3a3a3a;
21
- --color-text-primary: #ffffff;
22
- --color-text-secondary: #cccccc;
23
- --color-text-tertiary: #aaaaaa;
24
- --color-text-muted: #888888;
25
- --color-text-faint: #666666;
26
- --color-text-disabled: #555555;
27
- --color-border-subtle: rgba(255, 255, 255, 0.06);
28
- --color-border-default: rgba(255, 255, 255, 0.10);
29
- --color-border-strong: rgba(255, 255, 255, 0.15);
12
+ --color-bg: #ffffff;
13
+ --color-surface-0: #fafafa;
14
+ --color-surface-1: #f5f5f5;
15
+ --color-surface-2: #f2f2f3;
16
+ --color-surface-3: #ececee;
17
+ --color-surface-4: #e6e6e8;
18
+ --color-surface-5: #e0e0e2;
19
+ --color-surface-6: #d4d4d8;
20
+ --color-surface-7: #c4c4c8;
21
+ --color-surface-canvas: #ffffff;
22
+ --color-surface-card: #ffffff;
23
+ --color-surface-field: #f5f5f5;
24
+ --color-surface-inset: #f2f2f3;
25
+ --color-surface-overlay: #ffffff;
26
+ --color-text-primary: #111111;
27
+ --color-text-secondary: #555555;
28
+ --color-text-tertiary: #6e6e73;
29
+ --color-text-muted: #777777;
30
+ --color-text-faint: #9a9a9f;
31
+ --color-text-disabled: #a8a8ad;
32
+ --color-text-on-brand: #ffffff;
33
+ --color-text-brand: #ff2929;
34
+ --color-text-success: #15803d;
35
+ --color-text-warning: #b45309;
36
+ --color-text-danger: #dc2626;
37
+ --color-border-subtle: rgba(0, 0, 0, 0.06);
38
+ --color-border-default: rgba(0, 0, 0, 0.10);
39
+ --color-border-strong: rgba(0, 0, 0, 0.15);
30
40
  --color-border-brand: rgba(255, 41, 41, 0.50);
31
- --color-fill-ghost: rgba(255, 255, 255, 0.04);
32
- --color-fill-hover: rgba(255, 255, 255, 0.08);
33
- --color-fill-active: rgba(255, 255, 255, 0.12);
41
+ --color-border-focus: rgba(0, 0, 0, 0.30);
42
+ --color-fill-ghost: rgba(0, 0, 0, 0.04);
43
+ --color-fill-hover: rgba(0, 0, 0, 0.08);
44
+ --color-fill-active: rgba(0, 0, 0, 0.12);
34
45
  --color-scrim: rgba(0, 0, 0, 0.50);
35
46
  --color-scrim-strong: rgba(0, 0, 0, 0.70);
36
- --color-success: #22c55e;
47
+ --color-success: #16a34a;
37
48
  --color-success-bright: #4ade80;
38
49
  --color-danger: #ef4444;
39
50
  --color-danger-bright: #f87171;
@@ -42,7 +53,7 @@
42
53
  --gradient-brand: linear-gradient(135deg, #ff2929, #b30000);
43
54
  --gradient-red: linear-gradient(to top, #ff3d3d, #ff4d4d);
44
55
  --gradient-warm: linear-gradient(135deg, #ff6b6b, #ff2929);
45
- --gradient-surface: linear-gradient(145deg, #2a2a2a, #1a1a1a);
56
+ --gradient-surface: linear-gradient(145deg, #ffffff, #f2f2f3);
46
57
  --gradient-shimmer: linear-gradient(90deg, transparent, #ff4d4d, transparent);
47
58
  --radius-xs: 4px;
48
59
  --radius-sm: 6px;
@@ -79,11 +90,52 @@
79
90
  --font-weight-semibold: 600;
80
91
  --font-weight-bold: 700;
81
92
  --font-weight-black: 900;
93
+ --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08);
94
+ --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.12);
95
+ --shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.16);
96
+ --shadow-glow-brand: 0 0 20px rgba(255, 41, 41, 0.5), 0 0 40px rgba(255, 41, 41, 0.2);
97
+ --shadow-glow-brand-soft: 0 0 24px rgba(255, 77, 77, 0.3);
98
+ }
99
+
100
+ /* Dark rebinding — opt in per subtree (or on <html>) with data-theme="dark". */
101
+ [data-theme="dark"] {
102
+ color-scheme: dark;
103
+ --color-bg: #000000;
104
+ --color-surface-0: #0a0a0a;
105
+ --color-surface-1: #121212;
106
+ --color-surface-2: #151515;
107
+ --color-surface-3: #1a1a1a;
108
+ --color-surface-4: #1f1f1f;
109
+ --color-surface-5: #252525;
110
+ --color-surface-6: #2a2a2a;
111
+ --color-surface-7: #3a3a3a;
112
+ --color-surface-canvas: #000000;
113
+ --color-surface-card: #1a1a1a;
114
+ --color-surface-field: #151515;
115
+ --color-surface-inset: #1f1f1f;
116
+ --color-surface-overlay: #151515;
117
+ --color-text-primary: #ffffff;
118
+ --color-text-secondary: #cccccc;
119
+ --color-text-tertiary: #aaaaaa;
120
+ --color-text-muted: #888888;
121
+ --color-text-faint: #666666;
122
+ --color-text-disabled: #555555;
123
+ --color-text-brand: #ff6b6b;
124
+ --color-text-success: #4ade80;
125
+ --color-text-warning: #fbbf24;
126
+ --color-text-danger: #f87171;
127
+ --color-border-subtle: rgba(255, 255, 255, 0.06);
128
+ --color-border-default: rgba(255, 255, 255, 0.10);
129
+ --color-border-strong: rgba(255, 255, 255, 0.15);
130
+ --color-border-focus: rgba(255, 255, 255, 0.30);
131
+ --color-fill-ghost: rgba(255, 255, 255, 0.04);
132
+ --color-fill-hover: rgba(255, 255, 255, 0.08);
133
+ --color-fill-active: rgba(255, 255, 255, 0.12);
134
+ --color-success: #22c55e;
135
+ --gradient-surface: linear-gradient(145deg, #2a2a2a, #1a1a1a);
82
136
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.5);
83
137
  --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5);
84
138
  --shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.6);
85
- --shadow-glow-brand: 0 0 20px rgba(255, 41, 41, 0.5), 0 0 40px rgba(255, 41, 41, 0.2);
86
- --shadow-glow-brand-soft: 0 0 24px rgba(255, 77, 77, 0.3);
87
139
  }
88
140
 
89
141
  /* Channel vars (rgba(var(--…), <alpha>)) — outside @theme on purpose. */
package/dist/tokens.css CHANGED
@@ -1,6 +1,11 @@
1
1
  /* AUTO-GENERATED from tokens.json by build.mjs — do not edit by hand. */
2
- /* Mosh Design System — CSS custom properties (website). */
2
+ /* Mosh Design System — CSS custom properties (website).
3
+ :root is the LIGHT palette (the site is light-first). Wrap a dark-by-design
4
+ surface in data-theme="dark" and the semantic tokens below flip; brand and
5
+ status hues stay put. Theme is attribute-driven, never prefers-color-scheme —
6
+ resolve the user's preference in JS and set the attribute. */
3
7
  :root {
8
+ color-scheme: light;
4
9
  --color-brand: #ff2929; /* primary brand red */
5
10
  --color-brand-bright: #ff4d4d; /* brighter accent / hover */
6
11
  --color-brand-light: #ff6b6b; /* light red, best for text on dark */
@@ -8,31 +13,42 @@
8
13
  --color-brand-deep: #b30000; /* pressed / deep */
9
14
  --color-accent-deep: #b30000; /* secondary accent, pairs with red */
10
15
  --color-brand-rgb: 255, 41, 41; /* brand red channels — for rgba(var(--color-brand-rgb), <alpha>) */
11
- --color-bg: #000000; /* app background */
12
- --surface-0: #0a0a0a;
13
- --surface-1: #121212;
14
- --surface-2: #151515;
15
- --surface-3: #1a1a1a;
16
- --surface-4: #1f1f1f;
17
- --surface-5: #252525;
18
- --surface-6: #2a2a2a;
19
- --surface-7: #3a3a3a;
20
- --text-primary: #ffffff;
21
- --text-secondary: #cccccc;
22
- --text-tertiary: #aaaaaa;
23
- --text-muted: #888888;
24
- --text-faint: #666666;
25
- --text-disabled: #555555;
26
- --border-subtle: rgba(255, 255, 255, 0.06);
27
- --border-default: rgba(255, 255, 255, 0.10);
28
- --border-strong: rgba(255, 255, 255, 0.15);
16
+ --color-bg: #ffffff; /* app background */
17
+ --surface-0: #fafafa;
18
+ --surface-1: #f5f5f5;
19
+ --surface-2: #f2f2f3;
20
+ --surface-3: #ececee;
21
+ --surface-4: #e6e6e8;
22
+ --surface-5: #e0e0e2;
23
+ --surface-6: #d4d4d8;
24
+ --surface-7: #c4c4c8;
25
+ --surface-canvas: #ffffff; /* ROLE: page canvas (alias of color-bg) */
26
+ --surface-card: #ffffff; /* ROLE: cards/panels — white on light (borders carry separation), surface-3 on dark */
27
+ --surface-field: #f5f5f5; /* ROLE: text fields */
28
+ --surface-inset: #f2f2f3; /* ROLE: chips, banners, table headers — the only place the gray tint appears on light */
29
+ --surface-overlay: #ffffff; /* ROLE: modal cards, dropdown menus, popovers */
30
+ --text-primary: #111111;
31
+ --text-secondary: #555555;
32
+ --text-tertiary: #6e6e73;
33
+ --text-muted: #777777;
34
+ --text-faint: #9a9a9f;
35
+ --text-disabled: #a8a8ad;
36
+ --text-on-brand: #ffffff; /* text/icon on a brand-red fill */
37
+ --text-brand: #ff2929; /* brand-colored text (selected nav, links) — full red vibrates on black, so dark uses the light red */
38
+ --text-success: #15803d; /* status text; light value darkened for contrast on white */
39
+ --text-warning: #b45309;
40
+ --text-danger: #dc2626;
41
+ --border-subtle: rgba(0, 0, 0, 0.06);
42
+ --border-default: rgba(0, 0, 0, 0.10);
43
+ --border-strong: rgba(0, 0, 0, 0.15);
29
44
  --border-brand: rgba(255, 41, 41, 0.50);
30
- --fill-ghost: rgba(255, 255, 255, 0.04);
31
- --fill-hover: rgba(255, 255, 255, 0.08);
32
- --fill-active: rgba(255, 255, 255, 0.12);
45
+ --border-focus: rgba(0, 0, 0, 0.30); /* field focus ring — neutral, never brand red (red focus reads as a validation error) */
46
+ --fill-ghost: rgba(0, 0, 0, 0.04);
47
+ --fill-hover: rgba(0, 0, 0, 0.08);
48
+ --fill-active: rgba(0, 0, 0, 0.12);
33
49
  --scrim: rgba(0, 0, 0, 0.50);
34
50
  --scrim-strong: rgba(0, 0, 0, 0.70);
35
- --color-success: #22c55e;
51
+ --color-success: #16a34a;
36
52
  --color-success-bright: #4ade80;
37
53
  --color-danger: #ef4444;
38
54
  --color-danger-bright: #f87171;
@@ -43,7 +59,7 @@
43
59
  --gradient-brand: linear-gradient(135deg, #ff2929, #b30000); /* red → deep red */
44
60
  --gradient-red: linear-gradient(to top, #ff3d3d, #ff4d4d);
45
61
  --gradient-warm: linear-gradient(135deg, #ff6b6b, #ff2929);
46
- --gradient-surface: linear-gradient(145deg, #2a2a2a, #1a1a1a);
62
+ --gradient-surface: linear-gradient(145deg, #ffffff, #f2f2f3);
47
63
  --gradient-shimmer: linear-gradient(90deg, transparent, #ff4d4d, transparent);
48
64
  --radius-xs: 4px;
49
65
  --radius-sm: 6px;
@@ -51,7 +67,7 @@
51
67
  --radius-lg: 10px;
52
68
  --radius-xl: 12px;
53
69
  --radius-2xl: 16px;
54
- --radius-pill: 999px; /* buttons/badges are pill-shaped */
70
+ --radius-pill: 999px; /* badges, chips, toggles, search field — NOT buttons (Button radius scales with height: 8/10/14px) */
55
71
  --radius-circle: 50%;
56
72
  --space-1: 4px;
57
73
  --space-2: 8px;
@@ -80,9 +96,49 @@
80
96
  --weight-semibold: 600; /* most common UI weight */
81
97
  --weight-bold: 700;
82
98
  --weight-black: 900;
99
+ --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08);
100
+ --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.12);
101
+ --shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.16);
102
+ --glow-brand: 0 0 20px rgba(255, 41, 41, 0.5), 0 0 40px rgba(255, 41, 41, 0.2); /* signature red glow — primary actions */
103
+ --glow-brand-soft: 0 0 24px rgba(255, 77, 77, 0.3);
104
+ }
105
+
106
+ [data-theme="dark"] {
107
+ color-scheme: dark;
108
+ --color-bg: #000000;
109
+ --surface-0: #0a0a0a;
110
+ --surface-1: #121212;
111
+ --surface-2: #151515;
112
+ --surface-3: #1a1a1a;
113
+ --surface-4: #1f1f1f;
114
+ --surface-5: #252525;
115
+ --surface-6: #2a2a2a;
116
+ --surface-7: #3a3a3a;
117
+ --surface-canvas: #000000;
118
+ --surface-card: #1a1a1a;
119
+ --surface-field: #151515;
120
+ --surface-inset: #1f1f1f;
121
+ --surface-overlay: #151515;
122
+ --text-primary: #ffffff;
123
+ --text-secondary: #cccccc;
124
+ --text-tertiary: #aaaaaa;
125
+ --text-muted: #888888;
126
+ --text-faint: #666666;
127
+ --text-disabled: #555555;
128
+ --text-brand: #ff6b6b;
129
+ --text-success: #4ade80;
130
+ --text-warning: #fbbf24;
131
+ --text-danger: #f87171;
132
+ --border-subtle: rgba(255, 255, 255, 0.06);
133
+ --border-default: rgba(255, 255, 255, 0.10);
134
+ --border-strong: rgba(255, 255, 255, 0.15);
135
+ --border-focus: rgba(255, 255, 255, 0.30);
136
+ --fill-ghost: rgba(255, 255, 255, 0.04);
137
+ --fill-hover: rgba(255, 255, 255, 0.08);
138
+ --fill-active: rgba(255, 255, 255, 0.12);
139
+ --color-success: #22c55e;
140
+ --gradient-surface: linear-gradient(145deg, #2a2a2a, #1a1a1a);
83
141
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.5);
84
142
  --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5);
85
143
  --shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.6);
86
- --glow-brand: 0 0 20px rgba(255, 41, 41, 0.5), 0 0 40px rgba(255, 41, 41, 0.2); /* signature red glow — primary actions */
87
- --glow-brand-soft: 0 0 24px rgba(255, 77, 77, 0.3);
88
144
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moshevents/tokens",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Mosh Design System — design tokens (CSS variables + Tailwind theme) for web/admin; iOS consumes the SwiftPM target.",
5
5
  "type": "module",
6
6
  "files": [