@waukeshamakerspace/design-tokens 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Waukesha Makerspace, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # @waukeshamakerspace/design-tokens
2
+
3
+ Waukesha Makerspace brand + semantic design tokens, extracted from the token
4
+ system pioneered in `makers-cms`. One package, consumed by every WMI UI, so
5
+ the brand hexes live in exactly one place.
6
+
7
+ - **Brand layer** (fixed): Burnt Orange `#E8600A`, Charcoal `#2B2B2B`, Golden
8
+ Yellow `#F5A623`, Warm White `#F7F3EE` — from
9
+ `marketing/brand/brand-guidelines.md`.
10
+ - **Semantic layer** (themable): `surface`, `foreground`, `border`, `primary`,
11
+ `secondary`, `accent`, `link`, `ring`, `warning`/`success`/`danger` — each
12
+ with light **and** dark values. Components use these, never brand hexes.
13
+ - **Fonts**: Oswald (headings) + Open Sans (body), wired via `--font-heading`
14
+ / `--font-body`.
15
+
16
+ Cross-cutting UI rules (mobile-first, device-theme behavior, accessibility)
17
+ live in the architecture repo: `waukesha-makers-architecture/shared/ui-guidelines.md`.
18
+
19
+ ## Install
20
+
21
+ ```sh
22
+ npm install @waukeshamakerspace/design-tokens
23
+ ```
24
+
25
+ Enable Renovate or Dependabot in consuming apps so new token releases arrive
26
+ as automatic bump PRs.
27
+
28
+ ## Dark mode model
29
+
30
+ `css/tokens.css` resolves the theme in this order:
31
+
32
+ 1. `.dark` / `.light` class on `<html>` wins (set pre-paint by the theme-init
33
+ script, or by an app-level toggle).
34
+ 2. No class present → the OS `prefers-color-scheme` applies via media query.
35
+
36
+ So plain-CSS consumers follow the device theme with **zero JS**. Apps using
37
+ class-based styling (Tailwind `dark:`, MUI color schemes) inline the
38
+ theme-init script so the class follows the device too:
39
+
40
+ ```tsx
41
+ // Next.js: in app/layout.tsx <head>, before first paint
42
+ import { themeInitScript } from '@waukeshamakerspace/design-tokens/theme-init';
43
+
44
+ <script dangerouslySetInnerHTML={{ __html: themeInitScript }} />
45
+ ```
46
+
47
+ ```ts
48
+ // SPA: top of the entry module
49
+ import { initTheme } from '@waukeshamakerspace/design-tokens/theme-init';
50
+ initTheme();
51
+ ```
52
+
53
+ ### On-the-fly theme switching
54
+
55
+ Every app must expose a simple toggle so both modes can be exercised during
56
+ development (see `shared/ui-guidelines.md`). Build it on the exported
57
+ helpers so it coordinates with the OS-change listener instead of fighting it:
58
+
59
+ ```tsx
60
+ import { toggleTheme, setTheme, getTheme } from '@waukeshamakerspace/design-tokens/theme-init';
61
+
62
+ <button onClick={() => toggleTheme()}>Theme</button>
63
+ // or explicit: setTheme('dark') / setTheme('light') / setTheme('system')
64
+ ```
65
+
66
+ A toggle choice is session-only (not persisted): it suppresses OS-change
67
+ syncing until `setTheme('system')` or a reload, which return the app to
68
+ following the device theme.
69
+
70
+ ## Usage
71
+
72
+ ### Tailwind v4 (makers-cms style)
73
+
74
+ ```css
75
+ /* globals.css */
76
+ @import "tailwindcss";
77
+ @import "@waukeshamakerspace/design-tokens/css/tailwind.css";
78
+ ```
79
+
80
+ Gives every token as a utility (`bg-surface`, `text-foreground-muted`,
81
+ `bg-primary hover:bg-primary-hover`, `font-heading`) plus a class-based
82
+ `dark:` variant.
83
+
84
+ ### Tailwind v3
85
+
86
+ ```js
87
+ // tailwind.config.js
88
+ module.exports = {
89
+ presets: [require('@waukeshamakerspace/design-tokens/tailwind-preset')],
90
+ // ...
91
+ };
92
+ ```
93
+
94
+ ```css
95
+ /* global stylesheet */
96
+ @import "@waukeshamakerspace/design-tokens/css/tokens.css";
97
+ ```
98
+
99
+ Same utility names as v4. Caveat: opacity modifiers (`bg-primary/50`) don't
100
+ work on var()-based colors in v3.
101
+
102
+ ### MUI v6+
103
+
104
+ ```tsx
105
+ import { createMakersTheme } from '@waukeshamakerspace/design-tokens/mui';
106
+
107
+ const theme = createMakersTheme(); // pass overrides to deep-merge
108
+ <ThemeProvider theme={theme}>...</ThemeProvider>
109
+ ```
110
+
111
+ Light/dark schemes flip on the same `.light`/`.dark` class (via MUI
112
+ `cssVariables.colorSchemeSelector`), so MUI stays in sync with the CSS tokens.
113
+ `themeOptions` is also exported if you'd rather call `createTheme` yourself.
114
+
115
+ ### Plain CSS / anything else
116
+
117
+ ```css
118
+ @import "@waukeshamakerspace/design-tokens/css/tokens.css";
119
+
120
+ .card {
121
+ background: var(--surface-raised);
122
+ color: var(--foreground);
123
+ border: 1px solid var(--border);
124
+ }
125
+ ```
126
+
127
+ ### Fonts
128
+
129
+ Next.js apps: load Oswald + Open Sans with `next/font` and map them to
130
+ `--font-heading` / `--font-body` (see makers-cms `layout.tsx`).
131
+ Everything else: `@import "@waukeshamakerspace/design-tokens/css/fonts.css";`
132
+ (loads Google Fonts and wires the variables).
133
+
134
+ ### Brand assets (logos + favicons)
135
+
136
+ The package ships the logo masters and a favicon set generated from them:
137
+
138
+ | Path | What it is |
139
+ | --- | --- |
140
+ | `assets/logo.png` | Full logo, dark artwork, transparent, 1500×1500 |
141
+ | `assets/logo-white.png` | Full logo, white artwork, transparent, 1500×1500 |
142
+ | `assets/favicon.ico` | 16/32/48 multi-size, warm-white plate |
143
+ | `assets/apple-touch-icon.png` | 180×180, warm-white plate |
144
+ | `assets/icon-192.png` / `assets/icon-512.png` | PWA / web-manifest icons, transparent |
145
+
146
+ **Bundler apps (Vite, webpack, Next `<Image>`/`metadata`):** import the file —
147
+ the bundler hashes the URL, so rebrands bust caches automatically.
148
+
149
+ ```ts
150
+ import logo from '@waukeshamakerspace/design-tokens/assets/logo.png';
151
+ ```
152
+
153
+ ```ts
154
+ // Next.js app router: app/layout.tsx
155
+ import favicon from '@waukeshamakerspace/design-tokens/assets/favicon.ico';
156
+ import appleIcon from '@waukeshamakerspace/design-tokens/assets/apple-touch-icon.png';
157
+
158
+ export const metadata = {
159
+ icons: { icon: favicon.src, apple: appleIcon.src },
160
+ };
161
+ ```
162
+
163
+ **Anything that needs the files in `public/`:** copy at build time, e.g.
164
+
165
+ ```jsonc
166
+ // package.json
167
+ "prebuild": "cp node_modules/@waukeshamakerspace/design-tokens/assets/favicon.ico public/"
168
+ ```
169
+
170
+ ### Token values in JS
171
+
172
+ ```js
173
+ import { brand, light, dark, fonts } from '@waukeshamakerspace/design-tokens';
174
+ ```
175
+
176
+ ## Editing tokens
177
+
178
+ `index.js` is the single source of truth for values; `assets/logo.png` /
179
+ `assets/logo-white.png` are the masters for imagery. Edit those, then:
180
+
181
+ ```sh
182
+ npm run build # regenerates css/tokens.css + the assets/ favicon set
183
+ ```
184
+
185
+ Never edit `css/tokens.css` or the generated icons (`favicon.ico`,
186
+ `apple-touch-icon.png`, `icon-*.png`) by hand. Brand-layer changes must go
187
+ through `marketing/brand/brand-guidelines.md` first.
188
+
189
+ ## Releasing
190
+
191
+ Automated by release-please. Write [Conventional Commits](https://www.conventionalcommits.org)
192
+ (`fix:` → patch, `feat:` → minor, `feat!:` → major); pushes to main accumulate
193
+ into a release PR, and merging it publishes to npm. Don't bump `version` by
194
+ hand.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/css/fonts.css ADDED
@@ -0,0 +1,13 @@
1
+ /* Optional: brand fonts for apps NOT using next/font.
2
+ *
3
+ * Loads Oswald + Open Sans from Google Fonts and wires the font tokens.
4
+ * Next.js apps should skip this file and set --font-heading / --font-body
5
+ * via next/font instead (self-hosted, no layout shift).
6
+ */
7
+
8
+ @import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&family=Open+Sans:ital,wght@0,400;0,600;0,700;1,400&display=swap');
9
+
10
+ :root {
11
+ --font-heading: 'Oswald', 'Arial Narrow', sans-serif;
12
+ --font-body: 'Open Sans', Arial, Helvetica, sans-serif;
13
+ }
@@ -0,0 +1,64 @@
1
+ /* Tailwind v4 entry point. In your app's globals.css:
2
+ *
3
+ * @import "tailwindcss";
4
+ * @import "@waukeshamakerspace/design-tokens/css/tailwind.css";
5
+ *
6
+ * Provides the token variables, a class-based `dark:` variant, and Tailwind
7
+ * utilities for every token (bg-surface, text-foreground-muted, bg-primary,
8
+ * font-heading, …).
9
+ */
10
+
11
+ @import "./tokens.css";
12
+
13
+ /* `dark:` follows the .dark class on <html> (kept in sync with the OS theme
14
+ by the theme-init script). */
15
+ @custom-variant dark (&:where(.dark, .dark *));
16
+
17
+ @theme inline {
18
+ /* Brand utilities — use only when you mean the literal color in both themes */
19
+ --color-burnt-orange: var(--brand-burnt-orange);
20
+ --color-charcoal: var(--brand-charcoal);
21
+ --color-golden-yellow: var(--brand-golden-yellow);
22
+ --color-warm-white: var(--brand-warm-white);
23
+
24
+ /* Semantic utilities — use these everywhere */
25
+ --color-surface: var(--surface);
26
+ --color-surface-muted: var(--surface-muted);
27
+ --color-surface-raised: var(--surface-raised);
28
+ --color-surface-inverse: var(--surface-inverse);
29
+
30
+ --color-foreground: var(--foreground);
31
+ --color-foreground-muted: var(--foreground-muted);
32
+ --color-foreground-subtle: var(--foreground-subtle);
33
+ --color-foreground-inverse: var(--foreground-inverse);
34
+
35
+ --color-border: var(--border);
36
+ --color-border-strong: var(--border-strong);
37
+
38
+ --color-primary: var(--primary);
39
+ --color-primary-hover: var(--primary-hover);
40
+ --color-primary-foreground: var(--primary-foreground);
41
+
42
+ --color-secondary: var(--secondary);
43
+ --color-secondary-hover: var(--secondary-hover);
44
+ --color-secondary-foreground: var(--secondary-foreground);
45
+
46
+ --color-accent: var(--accent);
47
+ --color-accent-foreground: var(--accent-foreground);
48
+
49
+ --color-link: var(--link);
50
+ --color-link-hover: var(--link-hover);
51
+
52
+ --color-ring: var(--ring);
53
+
54
+ --color-warning: var(--warning);
55
+ --color-warning-foreground: var(--warning-foreground);
56
+ --color-success: var(--success);
57
+ --color-success-foreground: var(--success-foreground);
58
+ --color-danger: var(--danger);
59
+ --color-danger-foreground: var(--danger-foreground);
60
+
61
+ /* Fonts — apps define --font-heading / --font-body (next/font or fonts.css) */
62
+ --font-heading: var(--font-heading);
63
+ --font-body: var(--font-body);
64
+ }
package/css/tokens.css ADDED
@@ -0,0 +1,115 @@
1
+ /* GENERATED FILE — do not edit. Edit index.js and run `npm run build`. */
2
+
3
+ /* ────────────────────────────────────────────────────────────────────────────
4
+ Waukesha Makerspace design tokens.
5
+
6
+ Two layers:
7
+ 1. BRAND (--brand-*) — fixed colors from marketing/brand/brand-guidelines.md.
8
+ Reach for these only when you mean the literal color regardless of theme.
9
+ 2. SEMANTIC — themable, role-based (surface, foreground, primary, link, …).
10
+ Components should use these by default; they flip for dark mode.
11
+
12
+ Dark mode resolution order:
13
+ - `.dark` / `.light` class on <html> wins (set pre-paint by the
14
+ theme-init script exported from '@waukeshamakerspace/design-tokens/theme-init').
15
+ - With no class present, the OS preference applies via media query.
16
+ ──────────────────────────────────────────────────────────────────────── */
17
+
18
+ :root {
19
+ --brand-burnt-orange: #e8600a;
20
+ --brand-charcoal: #2b2b2b;
21
+ --brand-golden-yellow: #f5a623;
22
+ --brand-warm-white: #f7f3ee;
23
+
24
+ --surface: #ffffff;
25
+ --surface-muted: var(--brand-warm-white);
26
+ --surface-raised: #ffffff;
27
+ --surface-inverse: var(--brand-charcoal);
28
+ --foreground: var(--brand-charcoal);
29
+ --foreground-muted: #6b6b6b;
30
+ --foreground-subtle: #9a9a9a;
31
+ --foreground-inverse: var(--brand-warm-white);
32
+ --border: #e5e5e5;
33
+ --border-strong: #c5c5c5;
34
+ --primary: var(--brand-burnt-orange);
35
+ --primary-hover: #c0500a;
36
+ --primary-foreground: var(--brand-warm-white);
37
+ --secondary: var(--brand-charcoal);
38
+ --secondary-hover: #1a1a1a;
39
+ --secondary-foreground: var(--brand-warm-white);
40
+ --accent: var(--brand-golden-yellow);
41
+ --accent-foreground: var(--brand-charcoal);
42
+ --link: var(--brand-burnt-orange);
43
+ --link-hover: #c0500a;
44
+ --ring: var(--brand-burnt-orange);
45
+ --warning: var(--brand-golden-yellow);
46
+ --warning-foreground: var(--brand-charcoal);
47
+ --success: #16a34a;
48
+ --success-foreground: #ffffff;
49
+ --danger: #dc2626;
50
+ --danger-foreground: #ffffff;
51
+ }
52
+
53
+ /* Dark theme — explicit class */
54
+ :root.dark {
55
+ --surface: #161616;
56
+ --surface-muted: #232323;
57
+ --surface-raised: #2a2a2a;
58
+ --surface-inverse: var(--brand-warm-white);
59
+ --foreground: var(--brand-warm-white);
60
+ --foreground-muted: #a8a8a8;
61
+ --foreground-subtle: #707070;
62
+ --foreground-inverse: var(--brand-charcoal);
63
+ --border: #3a3a3a;
64
+ --border-strong: #5a5a5a;
65
+ --primary: #ff7a2e;
66
+ --primary-hover: #ff9050;
67
+ --primary-foreground: var(--brand-warm-white);
68
+ --secondary: var(--brand-warm-white);
69
+ --secondary-hover: #ffffff;
70
+ --secondary-foreground: var(--brand-charcoal);
71
+ --accent: var(--brand-golden-yellow);
72
+ --accent-foreground: var(--brand-charcoal);
73
+ --link: #ff7a2e;
74
+ --link-hover: #ff9050;
75
+ --ring: #ff7a2e;
76
+ --warning: var(--brand-golden-yellow);
77
+ --warning-foreground: var(--brand-charcoal);
78
+ --success: #22c55e;
79
+ --success-foreground: #ffffff;
80
+ --danger: #ef4444;
81
+ --danger-foreground: #ffffff;
82
+ }
83
+
84
+ /* Dark theme — OS preference, when no explicit class overrides it */
85
+ @media (prefers-color-scheme: dark) {
86
+ :root:not(.light) {
87
+ --surface: #161616;
88
+ --surface-muted: #232323;
89
+ --surface-raised: #2a2a2a;
90
+ --surface-inverse: var(--brand-warm-white);
91
+ --foreground: var(--brand-warm-white);
92
+ --foreground-muted: #a8a8a8;
93
+ --foreground-subtle: #707070;
94
+ --foreground-inverse: var(--brand-charcoal);
95
+ --border: #3a3a3a;
96
+ --border-strong: #5a5a5a;
97
+ --primary: #ff7a2e;
98
+ --primary-hover: #ff9050;
99
+ --primary-foreground: var(--brand-warm-white);
100
+ --secondary: var(--brand-warm-white);
101
+ --secondary-hover: #ffffff;
102
+ --secondary-foreground: var(--brand-charcoal);
103
+ --accent: var(--brand-golden-yellow);
104
+ --accent-foreground: var(--brand-charcoal);
105
+ --link: #ff7a2e;
106
+ --link-hover: #ff9050;
107
+ --ring: #ff7a2e;
108
+ --warning: var(--brand-golden-yellow);
109
+ --warning-foreground: var(--brand-charcoal);
110
+ --success: #22c55e;
111
+ --success-foreground: #ffffff;
112
+ --danger: #ef4444;
113
+ --danger-foreground: #ffffff;
114
+ }
115
+ }
package/index.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ export type BrandColorName =
2
+ | 'burnt-orange'
3
+ | 'charcoal'
4
+ | 'golden-yellow'
5
+ | 'warm-white';
6
+
7
+ export type SemanticTokenName =
8
+ | 'surface'
9
+ | 'surface-muted'
10
+ | 'surface-raised'
11
+ | 'surface-inverse'
12
+ | 'foreground'
13
+ | 'foreground-muted'
14
+ | 'foreground-subtle'
15
+ | 'foreground-inverse'
16
+ | 'border'
17
+ | 'border-strong'
18
+ | 'primary'
19
+ | 'primary-hover'
20
+ | 'primary-foreground'
21
+ | 'secondary'
22
+ | 'secondary-hover'
23
+ | 'secondary-foreground'
24
+ | 'accent'
25
+ | 'accent-foreground'
26
+ | 'link'
27
+ | 'link-hover'
28
+ | 'ring'
29
+ | 'warning'
30
+ | 'warning-foreground'
31
+ | 'success'
32
+ | 'success-foreground'
33
+ | 'danger'
34
+ | 'danger-foreground';
35
+
36
+ export declare const brand: Record<BrandColorName, string>;
37
+ export declare const light: Record<SemanticTokenName, string>;
38
+ export declare const dark: Record<SemanticTokenName, string>;
39
+ export declare const fonts: { heading: string; body: string };
40
+ export declare const semanticTokenNames: SemanticTokenName[];
package/index.js ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Waukesha Makerspace design tokens — single source of truth for values.
3
+ *
4
+ * Brand layer comes from marketing/brand/brand-guidelines.md. The semantic
5
+ * layer (including the dark palette) was designed in makers-cms and is
6
+ * canonized here; makers-cms and other apps consume it from this package.
7
+ *
8
+ * `npm run build` regenerates css/tokens.css from these objects — edit here,
9
+ * never in the generated CSS.
10
+ */
11
+
12
+ /** Fixed brand palette. Use only when you mean the literal color in both themes. */
13
+ export const brand = {
14
+ 'burnt-orange': '#e8600a',
15
+ charcoal: '#2b2b2b',
16
+ 'golden-yellow': '#f5a623',
17
+ 'warm-white': '#f7f3ee',
18
+ };
19
+
20
+ /** Semantic tokens — light theme. Components should reach for these, not brand. */
21
+ export const light = {
22
+ surface: '#ffffff',
23
+ 'surface-muted': brand['warm-white'],
24
+ 'surface-raised': '#ffffff',
25
+ 'surface-inverse': brand.charcoal,
26
+
27
+ foreground: brand.charcoal,
28
+ 'foreground-muted': '#6b6b6b',
29
+ 'foreground-subtle': '#9a9a9a',
30
+ 'foreground-inverse': brand['warm-white'],
31
+
32
+ border: '#e5e5e5',
33
+ 'border-strong': '#c5c5c5',
34
+
35
+ primary: brand['burnt-orange'],
36
+ 'primary-hover': '#c0500a',
37
+ 'primary-foreground': brand['warm-white'],
38
+
39
+ secondary: brand.charcoal,
40
+ 'secondary-hover': '#1a1a1a',
41
+ 'secondary-foreground': brand['warm-white'],
42
+
43
+ accent: brand['golden-yellow'],
44
+ 'accent-foreground': brand.charcoal,
45
+
46
+ link: brand['burnt-orange'],
47
+ 'link-hover': '#c0500a',
48
+
49
+ ring: brand['burnt-orange'],
50
+
51
+ warning: brand['golden-yellow'],
52
+ 'warning-foreground': brand.charcoal,
53
+ success: '#16a34a',
54
+ 'success-foreground': '#ffffff',
55
+ danger: '#dc2626',
56
+ 'danger-foreground': '#ffffff',
57
+ };
58
+
59
+ /** Semantic tokens — dark theme. Same keys as `light`. */
60
+ export const dark = {
61
+ ...light,
62
+ surface: '#161616',
63
+ 'surface-muted': '#232323',
64
+ 'surface-raised': '#2a2a2a',
65
+ 'surface-inverse': brand['warm-white'],
66
+
67
+ foreground: brand['warm-white'],
68
+ 'foreground-muted': '#a8a8a8',
69
+ 'foreground-subtle': '#707070',
70
+ 'foreground-inverse': brand.charcoal,
71
+
72
+ border: '#3a3a3a',
73
+ 'border-strong': '#5a5a5a',
74
+
75
+ primary: '#ff7a2e', // brightened burnt-orange for AA contrast on dark surfaces
76
+ 'primary-hover': '#ff9050',
77
+
78
+ secondary: brand['warm-white'],
79
+ 'secondary-hover': '#ffffff',
80
+ 'secondary-foreground': brand.charcoal,
81
+
82
+ link: '#ff7a2e',
83
+ 'link-hover': '#ff9050',
84
+
85
+ ring: '#ff7a2e',
86
+
87
+ success: '#22c55e',
88
+ danger: '#ef4444',
89
+ };
90
+
91
+ /**
92
+ * Brand font stacks. Apps wire these to `--font-heading` / `--font-body` —
93
+ * via next/font (Next.js) or by importing css/fonts.css (everything else).
94
+ */
95
+ export const fonts = {
96
+ heading: "'Oswald', 'Arial Narrow', sans-serif",
97
+ body: "'Open Sans', Arial, Helvetica, sans-serif",
98
+ };
99
+
100
+ /** Every semantic token name, for adapters that need to enumerate them. */
101
+ export const semanticTokenNames = Object.keys(light);
package/mui.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { Theme, ThemeOptions } from '@mui/material/styles';
2
+
3
+ export declare const themeOptions: ThemeOptions;
4
+
5
+ /** Create the brand theme; extra args deep-merge as overrides. */
6
+ export declare function createMakersTheme(...overrides: object[]): Theme;
package/mui.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * MUI v6+ adapter. Builds the Waukesha Makerspace theme from the token
3
+ * values in index.js, with light/dark color schemes flipped by the
4
+ * `.light` / `.dark` class on <html> (same mechanism as the CSS tokens —
5
+ * use the theme-init script to set the class from the device theme).
6
+ *
7
+ * import { createMakersTheme } from '@waukeshamakerspace/design-tokens/mui';
8
+ * const theme = createMakersTheme(); // or createMakersTheme({ ...overrides })
9
+ */
10
+ import { createTheme } from '@mui/material/styles';
11
+ import { light, dark, fonts } from './index.js';
12
+
13
+ const palette = (t) => ({
14
+ primary: {
15
+ main: t.primary,
16
+ dark: t['primary-hover'],
17
+ contrastText: t['primary-foreground'],
18
+ },
19
+ secondary: { main: t.secondary, contrastText: t['secondary-foreground'] },
20
+ success: { main: t.success, contrastText: t['success-foreground'] },
21
+ error: { main: t.danger, contrastText: t['danger-foreground'] },
22
+ warning: { main: t.warning, contrastText: t['warning-foreground'] },
23
+ background: { default: t.surface, paper: t['surface-raised'] },
24
+ text: { primary: t.foreground, secondary: t['foreground-muted'] },
25
+ divider: t.border,
26
+ });
27
+
28
+ const heading = `var(--font-heading, ${fonts.heading})`;
29
+ const body = `var(--font-body, ${fonts.body})`;
30
+
31
+ export const themeOptions = {
32
+ cssVariables: { colorSchemeSelector: 'class' },
33
+ colorSchemes: {
34
+ light: { palette: palette(light) },
35
+ dark: { palette: palette(dark) },
36
+ },
37
+ shape: { borderRadius: 12 },
38
+ typography: {
39
+ fontFamily: body,
40
+ h1: { fontFamily: heading, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.01em' },
41
+ h2: { fontFamily: heading, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.01em' },
42
+ h3: { fontFamily: heading, fontWeight: 700, textTransform: 'uppercase' },
43
+ h4: { fontFamily: heading, fontWeight: 600, textTransform: 'uppercase' },
44
+ h5: { fontFamily: heading, fontWeight: 600 },
45
+ h6: { fontFamily: heading, fontWeight: 600 },
46
+ button: { fontFamily: heading, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.03em' },
47
+ },
48
+ components: {
49
+ MuiCard: {
50
+ styleOverrides: {
51
+ root: { borderRadius: 14, backgroundImage: 'none' },
52
+ },
53
+ },
54
+ MuiButton: {
55
+ defaultProps: { disableElevation: true },
56
+ styleOverrides: {
57
+ root: { borderRadius: 12 },
58
+ },
59
+ },
60
+ MuiAppBar: {
61
+ defaultProps: { elevation: 0, color: 'default' },
62
+ styleOverrides: {
63
+ root: {
64
+ backgroundColor: 'var(--mui-palette-background-paper)',
65
+ color: 'var(--mui-palette-text-primary)',
66
+ borderBottom: '1px solid var(--mui-palette-divider)',
67
+ },
68
+ },
69
+ },
70
+ },
71
+ };
72
+
73
+ /** Create the brand theme; extra args deep-merge as overrides. */
74
+ export function createMakersTheme(...overrides) {
75
+ return createTheme(themeOptions, ...overrides);
76
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@waukeshamakerspace/design-tokens",
3
+ "version": "0.1.0",
4
+ "description": "Waukesha Makerspace brand + semantic design tokens: CSS variables with automatic light/dark theming, Tailwind (v3 + v4) and MUI adapters",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/WaukeshaMakerspace/design-tokens.git"
9
+ },
10
+ "type": "module",
11
+ "main": "./index.js",
12
+ "types": "./index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./index.d.ts",
16
+ "default": "./index.js"
17
+ },
18
+ "./css": "./css/tokens.css",
19
+ "./css/tokens.css": "./css/tokens.css",
20
+ "./css/tailwind.css": "./css/tailwind.css",
21
+ "./css/fonts.css": "./css/fonts.css",
22
+ "./tailwind-preset": "./tailwind-preset.cjs",
23
+ "./mui": {
24
+ "types": "./mui.d.ts",
25
+ "default": "./mui.js"
26
+ },
27
+ "./theme-init": {
28
+ "types": "./theme-init.d.ts",
29
+ "default": "./theme-init.js"
30
+ },
31
+ "./assets/*": "./assets/*"
32
+ },
33
+ "files": [
34
+ "index.js",
35
+ "index.d.ts",
36
+ "css/",
37
+ "assets/",
38
+ "tailwind-preset.cjs",
39
+ "mui.js",
40
+ "mui.d.ts",
41
+ "theme-init.js",
42
+ "theme-init.d.ts"
43
+ ],
44
+ "scripts": {
45
+ "build": "npm run build:css && npm run build:icons",
46
+ "build:css": "node scripts/build-css.mjs",
47
+ "build:icons": "node scripts/build-icons.mjs",
48
+ "prepublishOnly": "npm run build"
49
+ },
50
+ "peerDependencies": {
51
+ "@mui/material": ">=6",
52
+ "tailwindcss": ">=3"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "@mui/material": {
56
+ "optional": true
57
+ },
58
+ "tailwindcss": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "keywords": [
63
+ "design-tokens",
64
+ "waukesha-makerspace",
65
+ "theme",
66
+ "tailwind",
67
+ "mui"
68
+ ],
69
+ "devDependencies": {
70
+ "png-to-ico": "^3.0.1",
71
+ "sharp": "^0.35.3"
72
+ }
73
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Tailwind v3 preset. Maps token *names* to the CSS variables from
3
+ * css/tokens.css — import that file (or '@waukeshamakerspace/design-tokens/css')
4
+ * in your global stylesheet, then:
5
+ *
6
+ * // tailwind.config.js
7
+ * module.exports = {
8
+ * presets: [require('@waukeshamakerspace/design-tokens/tailwind-preset')],
9
+ * ...
10
+ * }
11
+ *
12
+ * Gives the same utility names as the Tailwind v4 layer (bg-surface,
13
+ * text-foreground-muted, bg-primary, font-heading, …).
14
+ *
15
+ * Caveat: because values are var() references, Tailwind v3 opacity modifiers
16
+ * (e.g. bg-primary/50) do not work on these colors.
17
+ */
18
+ module.exports = {
19
+ darkMode: 'class',
20
+ theme: {
21
+ extend: {
22
+ colors: {
23
+ // Brand — literal colors, both themes
24
+ 'burnt-orange': 'var(--brand-burnt-orange)',
25
+ charcoal: 'var(--brand-charcoal)',
26
+ 'golden-yellow': 'var(--brand-golden-yellow)',
27
+ 'warm-white': 'var(--brand-warm-white)',
28
+
29
+ // Semantic — use these everywhere
30
+ surface: {
31
+ DEFAULT: 'var(--surface)',
32
+ muted: 'var(--surface-muted)',
33
+ raised: 'var(--surface-raised)',
34
+ inverse: 'var(--surface-inverse)',
35
+ },
36
+ foreground: {
37
+ DEFAULT: 'var(--foreground)',
38
+ muted: 'var(--foreground-muted)',
39
+ subtle: 'var(--foreground-subtle)',
40
+ inverse: 'var(--foreground-inverse)',
41
+ },
42
+ border: {
43
+ DEFAULT: 'var(--border)',
44
+ strong: 'var(--border-strong)',
45
+ },
46
+ primary: {
47
+ DEFAULT: 'var(--primary)',
48
+ hover: 'var(--primary-hover)',
49
+ foreground: 'var(--primary-foreground)',
50
+ },
51
+ secondary: {
52
+ DEFAULT: 'var(--secondary)',
53
+ hover: 'var(--secondary-hover)',
54
+ foreground: 'var(--secondary-foreground)',
55
+ },
56
+ accent: {
57
+ DEFAULT: 'var(--accent)',
58
+ foreground: 'var(--accent-foreground)',
59
+ },
60
+ link: {
61
+ DEFAULT: 'var(--link)',
62
+ hover: 'var(--link-hover)',
63
+ },
64
+ ring: 'var(--ring)',
65
+ warning: {
66
+ DEFAULT: 'var(--warning)',
67
+ foreground: 'var(--warning-foreground)',
68
+ },
69
+ success: {
70
+ DEFAULT: 'var(--success)',
71
+ foreground: 'var(--success-foreground)',
72
+ },
73
+ danger: {
74
+ DEFAULT: 'var(--danger)',
75
+ foreground: 'var(--danger-foreground)',
76
+ },
77
+ },
78
+ fontFamily: {
79
+ heading: ['var(--font-heading)', 'sans-serif'],
80
+ body: ['var(--font-body)', 'sans-serif'],
81
+ },
82
+ },
83
+ },
84
+ };
@@ -0,0 +1,20 @@
1
+ export type ThemeMode = 'light' | 'dark';
2
+
3
+ /** Sync <html> with the device theme now and on every OS-theme change. */
4
+ export declare function initTheme(win?: Window): void;
5
+
6
+ /** The theme currently applied to <html>. */
7
+ export declare function getTheme(win?: Window): ThemeMode;
8
+
9
+ /**
10
+ * Switch theme on the fly. 'light' / 'dark' override the device theme for
11
+ * this page session; 'system' clears the override and resumes following the
12
+ * device.
13
+ */
14
+ export declare function setTheme(mode: ThemeMode | 'system', win?: Window): void;
15
+
16
+ /** Flip between light and dark; returns the new mode. */
17
+ export declare function toggleTheme(win?: Window): ThemeMode;
18
+
19
+ /** Same behavior as initTheme(), as a string to inline in <head> pre-paint. */
20
+ export declare const themeInitScript: string;
package/theme-init.js ADDED
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Device-theme sync + on-the-fly theme switching.
3
+ *
4
+ * Follows prefers-color-scheme by applying `.dark` or `.light` to <html>,
5
+ * and keeps it in sync when the OS theme changes. Apps must also expose a
6
+ * simple toggle (see setTheme/toggleTheme below) so either mode can be
7
+ * exercised on the fly — per shared/ui-guidelines.md. A toggle choice is
8
+ * session-only (not persisted): it suppresses OS-change syncing until
9
+ * setTheme('system') or a reload returns the app to following the device.
10
+ *
11
+ * Next.js / SSR — inline the script in <head> so the class is set before
12
+ * first paint (no flash of the wrong theme):
13
+ *
14
+ * import { themeInitScript } from '@waukeshamakerspace/design-tokens/theme-init';
15
+ * <script dangerouslySetInnerHTML={{ __html: themeInitScript }} />
16
+ *
17
+ * SPAs — call initTheme() at the top of your entry module instead.
18
+ *
19
+ * Note: the CSS tokens also carry a prefers-color-scheme media-query
20
+ * fallback, so pages render the right theme even without this script; the
21
+ * script exists so class-based styling (Tailwind `dark:`, MUI color schemes)
22
+ * follows the device too.
23
+ */
24
+
25
+ // The manual-override flag lives on <html> (data-theme-override) rather than
26
+ // in module state so the inline <head> script and this module coordinate
27
+ // without sharing scope.
28
+
29
+ const apply = (root, isDark) => {
30
+ root.classList.toggle('dark', isDark);
31
+ root.classList.toggle('light', !isDark);
32
+ };
33
+
34
+ /** Sync <html> with the device theme now and on every OS-theme change. */
35
+ export function initTheme(win = window) {
36
+ const media = win.matchMedia('(prefers-color-scheme: dark)');
37
+ const root = win.document.documentElement;
38
+ apply(root, media.matches);
39
+ media.addEventListener('change', (e) => {
40
+ if (!root.dataset.themeOverride) apply(root, e.matches);
41
+ });
42
+ }
43
+
44
+ /** The theme currently applied to <html>. */
45
+ export function getTheme(win = window) {
46
+ return win.document.documentElement.classList.contains('dark') ? 'dark' : 'light';
47
+ }
48
+
49
+ /**
50
+ * Switch theme on the fly. 'light' / 'dark' override the device theme for
51
+ * this page session; 'system' clears the override and resumes following the
52
+ * device.
53
+ */
54
+ export function setTheme(mode, win = window) {
55
+ const root = win.document.documentElement;
56
+ if (mode === 'system') {
57
+ delete root.dataset.themeOverride;
58
+ apply(root, win.matchMedia('(prefers-color-scheme: dark)').matches);
59
+ } else {
60
+ root.dataset.themeOverride = mode;
61
+ apply(root, mode === 'dark');
62
+ }
63
+ }
64
+
65
+ /** Flip between light and dark; returns the new mode. */
66
+ export function toggleTheme(win = window) {
67
+ const next = getTheme(win) === 'dark' ? 'light' : 'dark';
68
+ setTheme(next, win);
69
+ return next;
70
+ }
71
+
72
+ /** Same behavior as initTheme(), as a string to inline in <head> pre-paint. */
73
+ export const themeInitScript = `(function () {
74
+ var media = window.matchMedia('(prefers-color-scheme: dark)');
75
+ var root = document.documentElement;
76
+ function apply(isDark) {
77
+ root.classList.toggle('dark', isDark);
78
+ root.classList.toggle('light', !isDark);
79
+ }
80
+ apply(media.matches);
81
+ media.addEventListener('change', function (e) {
82
+ if (!root.dataset.themeOverride) apply(e.matches);
83
+ });
84
+ })();`;