@kyzarky/ui 0.0.0-stage → 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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -2
  3. package/package.json +39 -4
  4. package/theme.css +82 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ky Merritt
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 CHANGED
@@ -1,3 +1,148 @@
1
- # Temporary Holding Version
1
+ # @kyzarky/ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Design tokens for Ky Merritt's apps: a Tailwind CSS 4 theme with light and dark values.
4
+ React components arrive in 0.2.0 ([ADR 0002](docs/decisions/0002-first-scope.md)). First
5
+ consumer: Fluent Prior. Next: Merritt's Mini Mind.
6
+
7
+ ## Use
8
+
9
+ ```bash
10
+ npm install --save-exact @kyzarky/ui
11
+ ```
12
+
13
+ Then import the theme after Tailwind in the app's CSS:
14
+
15
+ ```css
16
+ @import "tailwindcss";
17
+ @import "@kyzarky/ui/theme.css";
18
+ ```
19
+
20
+ It needs Tailwind CSS 4. **Pin an exact version and upgrade on purpose,** so work in
21
+ progress here never changes an app under it. The [changelog](CHANGELOG.md) says what each
22
+ version changes and how to upgrade.
23
+
24
+ ## Tokens
25
+
26
+ The names are shadcn/ui's, plus three status colors. Each is a CSS variable (`--card`) and
27
+ a Tailwind color (`bg-card`, `text-card`, `border-card`, and so on).
28
+
29
+ | Token | Used for |
30
+ |---|---|
31
+ | `background` | The page |
32
+ | `foreground` | Text |
33
+ | `card` | Raised surfaces |
34
+ | `card-foreground` | Text on a card |
35
+ | `muted` | Subtle fills: meter tracks, inset boxes, the unselected segment |
36
+ | `muted-foreground` | Secondary text |
37
+ | `border` | Card edges and dividers. Decorative: too faint for a control's edge |
38
+ | `input` | Edges of form controls (3:1 or more) |
39
+ | `primary` | Brand: filled buttons, meters, links |
40
+ | `primary-foreground` | Text on `primary` |
41
+ | `ring` | Focus rings |
42
+ | `success`, `warning`, `destructive` | Results and notices, as text or as a `/15` badge |
43
+
44
+ The pairs this table implies meet WCAG AA in both modes: text on each surface, status
45
+ colors on their badges, and `input` and `ring` edges. The tests check each one.
46
+
47
+ ## Light and dark
48
+
49
+ Each token holds both values, and CSS's `light-dark()` picks one from the `color-scheme`
50
+ of the element using it. The theme sets `color-scheme: light dark` on `:root`, so the page
51
+ follows the system. To let people choose, set `color-scheme` yourself:
52
+
53
+ ```css
54
+ :root { color-scheme: light; }
55
+ .dark { color-scheme: dark; }
56
+ ```
57
+
58
+ Don't use Tailwind's `dark:` variant: the tokens already change.
59
+
60
+ - **Browsers:** both themes need `light-dark()`: Safari 17.5, Chrome 123, Firefox 120, or
61
+ later. A browser without it gets the light theme, whatever the system is set to
62
+ ([ADR 0003](docs/decisions/0003-light-fallback-and-browser-check.md)).
63
+ - **Reading a token in JavaScript** gives the text `light-dark(#f6f4ef, #15181a)`, not a
64
+ color. Read a computed property instead, such as
65
+ `getComputedStyle(document.body).backgroundColor`.
66
+
67
+ ## Theming
68
+
69
+ Redefine a token after the import. The theme's tokens sit in a cascade layer, so an app's
70
+ own rule wins wherever it is. For example, a green brand color:
71
+
72
+ ```css
73
+ :root {
74
+ --primary: light-dark(#2f6b3f, #5fd08a);
75
+ }
76
+ ```
77
+
78
+ `ring` follows `primary` unless you set it too. Keep the contrast of every pair in the
79
+ table above.
80
+
81
+ ## Rules
82
+
83
+ - **Less space inside a group than between groups,** at least 1:2.
84
+ - **Rows are separated by dividers; groups by space and a heading.**
85
+ - **Type roles:** page title `text-2xl` semibold; card title `text-lg` semibold; group
86
+ heading `text-base` semibold; body `text-base`; row labels and help text `text-sm`;
87
+ details `text-sm` in `muted-foreground`, with `tabular-nums` for numbers; counters and
88
+ chips `text-xs`.
89
+ - **Every control is at least 44px tall.**
90
+ - **A value shown as a graphic is also shown as text.**
91
+
92
+ ## Why
93
+
94
+ - One visual language across Ky's portfolio apps.
95
+ - Fluent Prior's "What you know" list is hard to read: it's hard to tell where one row ends
96
+ and the next starts. The fix is rules the app never had, and they belong here, not in
97
+ one app.
98
+ - A token-themed design system is the kind of frontend work Ky's job search targets.
99
+
100
+ ## Decisions
101
+
102
+ - **[ADR 0001](docs/decisions/0001-name-registry-and-contents.md), accepted:** the package
103
+ is `@kyzarky/ui`, published to public npm from this repo, with tokens and React
104
+ components.
105
+ - **[ADR 0002](docs/decisions/0002-first-scope.md), accepted:** the first scope, based on
106
+ a [style audit](docs/audit/README.md) of both apps.
107
+ - **[ADR 0003](docs/decisions/0003-light-fallback-and-browser-check.md), accepted:** a
108
+ light fallback for browsers without `light-dark()`, and a browser check in CI.
109
+
110
+ ## Development
111
+
112
+ | Command | What it does |
113
+ |---|---|
114
+ | `npm run verify` | Typecheck, lint, and test |
115
+ | `npm test` | The contrast and fallback tests, and a Tailwind build of the theme |
116
+ | `npm run test:browsers` | Opens `browser-check.html` in Chromium, Firefox, and WebKit, in light and dark. Needs `npx playwright install` once. Add engine names to run only those: `npm run test:browsers -- webkit` |
117
+ | `npm run format` | Biome: format and apply safe lint fixes |
118
+
119
+ ## Releasing
120
+
121
+ 1. In a pull request: bump `version` in `package.json` and add a changelog entry.
122
+ 2. Try it in an app before merging: `npm pack`, then in the app
123
+ `npm install --no-save <path to the .tgz>` and its `npm run verify`.
124
+ 3. After merging, tag the merge commit and push the tag:
125
+ `git tag v0.2.0 && git push origin v0.2.0`. The publish workflow checks that the tag
126
+ matches `package.json`, runs `verify`, and publishes with provenance. It skips a
127
+ version that's already on npm, so running it again is safe.
128
+
129
+ **How publishing is set up** ([ADR 0001](docs/decisions/0001-name-registry-and-contents.md)).
130
+ npm adds a trusted publisher only to a package that already exists, and a new one expires
131
+ unless it makes a successful publish within 2 days. So:
132
+
133
+ 1. `0.0.0` is a placeholder with no theme in it, published by hand so the package exists.
134
+ It's deprecated.
135
+ 2. The trusted publisher is added with
136
+ `npm trust github @kyzarky/ui --file publish.yml --repo KyzarKy/design-system --allow-publish`.
137
+ 3. Pushing `v0.1.0` within those 2 days has CI publish the first real version, which
138
+ confirms the setup. A replacement trusted publisher would have the same 2 days.
139
+ 4. Then publishing access is set to require two-factor auth and disallow tokens, in the
140
+ package's settings on npmjs.com.
141
+
142
+ ## How we work
143
+
144
+ Ky decides; Claude proposes and implements. Docs first: decisions go in ADRs before code.
145
+ Small commits, each saying what changed and why, how to verify it, and three questions an
146
+ interviewer might ask, with short answers. Never commit secrets. Never force-push. Commits
147
+ use Ky's GitHub noreply email (set in this repo's git config). In anything written in Ky's
148
+ voice: no em dashes, and use Oxford commas.
package/package.json CHANGED
@@ -1,6 +1,41 @@
1
1
  {
2
2
  "name": "@kyzarky/ui",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Design tokens for Ky Merritt's apps, as a Tailwind CSS 4 theme with light and dark values",
5
+ "license": "MIT",
6
+ "author": "Ky Merritt",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/KyzarKy/design-system.git"
10
+ },
11
+ "type": "module",
12
+ "exports": {
13
+ "./theme.css": "./theme.css"
14
+ },
15
+ "files": [
16
+ "theme.css"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "scripts": {
22
+ "typecheck": "tsc --noEmit",
23
+ "lint": "biome check .",
24
+ "format": "biome check --write .",
25
+ "test": "vitest run",
26
+ "verify": "npm run typecheck && npm run lint && npm run test",
27
+ "test:browsers": "node browser-check.ts"
28
+ },
29
+ "peerDependencies": {
30
+ "tailwindcss": "^4.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@biomejs/biome": "^2.5.15",
34
+ "@tailwindcss/node": "^4.3.3",
35
+ "@types/node": "^26.6.4",
36
+ "playwright": "^1.64.0",
37
+ "tailwindcss": "^4.3.3",
38
+ "typescript": "~6.0.3",
39
+ "vitest": "^5.0.3"
40
+ }
41
+ }
package/theme.css ADDED
@@ -0,0 +1,82 @@
1
+ /* The design tokens and the Tailwind utilities that use them (ADRs 0002 and 0003). An app
2
+ imports this after Tailwind:
3
+
4
+ @import "tailwindcss";
5
+ @import "@kyzarky/ui/theme.css";
6
+
7
+ Each token holds a light and a dark value. light-dark() picks one from the color-scheme
8
+ of the element using it, so the page follows the system unless the app sets
9
+ color-scheme itself. Components use the utilities (bg-card, text-muted-foreground) and
10
+ never Tailwind's dark: variant. */
11
+
12
+ /* In the base layer, so an app's own unlayered rules win wherever it puts them. That's
13
+ how an app themes the package: by redefining a token. */
14
+ @layer base {
15
+ /* The light theme, for browsers without light-dark() (Safari before 17.5). Without
16
+ these, every token is invalid there and each fill it colors disappears (ADR 0003).
17
+ theme.test.ts checks that each value equals the light value in the block below. */
18
+ :root {
19
+ color-scheme: light;
20
+
21
+ --background: #f6f4ef;
22
+ --foreground: #1f2326;
23
+ --card: #ffffff;
24
+ --card-foreground: var(--foreground);
25
+ --muted: #efece4;
26
+ --muted-foreground: #5b6166;
27
+ --border: #dedad0;
28
+ --input: #858279;
29
+ --primary: #1d6b73;
30
+ --primary-foreground: #ffffff;
31
+ --ring: var(--primary);
32
+ --success: #2a7548;
33
+ --warning: #915d00;
34
+ --destructive: #b1352b;
35
+ }
36
+
37
+ @supports (color: light-dark(#000, #fff)) {
38
+ :root {
39
+ color-scheme: light dark;
40
+
41
+ --background: light-dark(#f6f4ef, #15181a);
42
+ --foreground: light-dark(#1f2326, #eceae4);
43
+ --card: light-dark(#ffffff, #1e2225);
44
+ /* Subtle fills: meter tracks, inset boxes, the unselected segment. */
45
+ --muted: light-dark(#efece4, #272c30);
46
+ --muted-foreground: light-dark(#5b6166, #a3a9ad);
47
+ /* Decorative edges: cards and dividers. Too faint for a control's edge. */
48
+ --border: light-dark(#dedad0, #353b40);
49
+ /* Edges of form controls: at least 3:1 on card and background (WCAG 1.4.11). */
50
+ --input: light-dark(#858279, #737779);
51
+ --primary: light-dark(#1d6b73, #5fb3ba);
52
+ --primary-foreground: light-dark(#ffffff, #0e1a1c);
53
+ --success: light-dark(#2a7548, #6cc38f);
54
+ --warning: light-dark(#915d00, #e0a846);
55
+ --destructive: light-dark(#b1352b, #ef8a7f);
56
+ }
57
+ }
58
+
59
+ body {
60
+ background-color: var(--background);
61
+ color: var(--foreground);
62
+ }
63
+ }
64
+
65
+ /* inline: each utility reads the token where it's used, not a copy resolved at :root, so
66
+ an element that redefines a token gets its own value. */
67
+ @theme inline {
68
+ --color-background: var(--background);
69
+ --color-foreground: var(--foreground);
70
+ --color-card: var(--card);
71
+ --color-card-foreground: var(--card-foreground);
72
+ --color-muted: var(--muted);
73
+ --color-muted-foreground: var(--muted-foreground);
74
+ --color-border: var(--border);
75
+ --color-input: var(--input);
76
+ --color-primary: var(--primary);
77
+ --color-primary-foreground: var(--primary-foreground);
78
+ --color-ring: var(--ring);
79
+ --color-success: var(--success);
80
+ --color-warning: var(--warning);
81
+ --color-destructive: var(--destructive);
82
+ }