@kyzarky/ui 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
@@ -1,8 +1,9 @@
1
1
  # @kyzarky/ui
2
2
 
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.
3
+ Design tokens and React components for Ky Merritt's apps: a Tailwind CSS 4 theme with
4
+ light and dark values, and the components built on it
5
+ ([ADR 0002](docs/decisions/0002-first-scope.md)). First consumer: Fluent Prior. Next:
6
+ Merritt's Mini Mind.
6
7
 
7
8
  ## Use
8
9
 
@@ -17,7 +18,9 @@ Then import the theme after Tailwind in the app's CSS:
17
18
  @import "@kyzarky/ui/theme.css";
18
19
  ```
19
20
 
20
- It needs Tailwind CSS 4. **Pin an exact version and upgrade on purpose,** so work in
21
+ It needs Tailwind CSS 4, and React 19 for the components. The import also has the app's
22
+ Tailwind scan the package's components, so their classes are generated with the app's own.
23
+ **Pin an exact version and upgrade on purpose,** so work in
21
24
  progress here never changes an app under it. The [changelog](CHANGELOG.md) says what each
22
25
  version changes and how to upgrade.
23
26
 
@@ -78,6 +81,26 @@ own rule wins wherever it is. For example, a green brand color:
78
81
  `ring` follows `primary` unless you set it too. Keep the contrast of every pair in the
79
82
  table above.
80
83
 
84
+ ## Components
85
+
86
+ ```tsx
87
+ import { List, ListRow, Meter } from "@kyzarky/ui";
88
+
89
+ <List heading={<h3>Grammar</h3>} description="Optional help text.">
90
+ <ListRow label="Question word order" detail="99% · 9 answers">
91
+ <Meter value={0.99} />
92
+ </ListRow>
93
+ </List>
94
+ ```
95
+
96
+ - **`List`** is a group of rows with a divider between them. Pass the `heading` as an
97
+ element, at whatever level the page needs; `List` sets its look. A `List` that follows
98
+ something sits 32px below it.
99
+ - **`ListRow`** is one row: a `label`, a `detail` on the right, and optional children
100
+ below for a graphic.
101
+ - **`Meter`** is a bar for a `value` from 0 to 1. It's hidden from screen readers, so
102
+ state the value in the row's `detail`.
103
+
81
104
  ## Rules
82
105
 
83
106
  - **Less space inside a group than between groups,** at least 1:2.
@@ -111,8 +134,9 @@ table above.
111
134
 
112
135
  | Command | What it does |
113
136
  |---|---|
114
- | `npm run verify` | Typecheck, lint, and test |
115
- | `npm test` | The contrast and fallback tests, and a Tailwind build of the theme |
137
+ | `npm run verify` | Typecheck, lint, test, and build |
138
+ | `npm run build` | Compiles `src` to `dist` with `tsc`. `npm pack` and `npm publish` run it first |
139
+ | `npm test` | The contrast, fallback, and component tests, and a Tailwind build of the theme |
116
140
  | `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
141
  | `npm run format` | Biome: format and apply safe lint fixes |
118
142
 
package/dist/List.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { ReactNode } from "react";
2
+ interface ListProps {
3
+ /**
4
+ * The heading element, at whatever level the page needs: `<h3>Grammar</h3>`. The level
5
+ * is the page's to choose; the look is set here. Tailwind's preflight makes headings
6
+ * inherit size and weight, so the wrapper's classes apply to it.
7
+ */
8
+ heading?: ReactNode;
9
+ /** A line of help text between the heading and the rows. */
10
+ description?: ReactNode;
11
+ /** `ListRow`s. */
12
+ children: ReactNode;
13
+ }
14
+ export declare function List({ heading, description, children }: ListProps): import("react").JSX.Element;
15
+ export {};
package/dist/List.js ADDED
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function List({ heading, description, children }) {
3
+ return (_jsxs("div", { className: "mt-8 first:mt-0", children: [heading && _jsx("div", { className: "font-semibold text-base", children: heading }), description && _jsx("p", { className: "mt-1 text-muted-foreground text-sm", children: description }), _jsx("ul", { className: "mt-2 divide-y divide-border", children: children })] }));
4
+ }
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ interface ListRowProps {
3
+ label: ReactNode;
4
+ /** Secondary text on the right. If the row has a graphic, this states its value. */
5
+ detail?: ReactNode;
6
+ /** A graphic for the row, such as a `Meter`. */
7
+ children?: ReactNode;
8
+ }
9
+ export declare function ListRow({ label, detail, children }: ListRowProps): import("react").JSX.Element;
10
+ export {};
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function ListRow({ label, detail, children }) {
3
+ return (_jsxs("li", { className: "py-3", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-3 text-sm", children: [_jsx("span", { children: label }), detail && _jsx("span", { className: "shrink-0 text-muted-foreground tabular-nums", children: detail })] }), children && _jsx("div", { className: "mt-2", children: children })] }));
4
+ }
@@ -0,0 +1,6 @@
1
+ interface MeterProps {
2
+ /** From 0 to 1. Values outside that are clamped, and NaN draws an empty bar. */
3
+ value: number;
4
+ }
5
+ export declare function Meter({ value }: MeterProps): import("react").JSX.Element;
6
+ export {};
package/dist/Meter.js ADDED
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export function Meter({ value }) {
3
+ // `|| 0` turns NaN into 0. An invalid width would be ignored, leaving the bar full.
4
+ const percent = Math.min(1, Math.max(0, value || 0)) * 100;
5
+ return (_jsx("div", { "aria-hidden": "true", className: "h-2 overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-primary", style: { width: `${percent}%` } }) }));
6
+ }
@@ -0,0 +1,3 @@
1
+ export { List } from "./List.js";
2
+ export { ListRow } from "./ListRow.js";
3
+ export { Meter } from "./Meter.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // The package's components. The tokens they use come from theme.css.
2
+ export { List } from "./List.js";
3
+ export { ListRow } from "./ListRow.js";
4
+ export { Meter } from "./Meter.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kyzarky/ui",
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",
3
+ "version": "0.2.0",
4
+ "description": "Design tokens and React components for Ky Merritt's apps, built on Tailwind CSS 4",
5
5
  "license": "MIT",
6
6
  "author": "Ky Merritt",
7
7
  "repository": {
@@ -10,30 +10,42 @@
10
10
  },
11
11
  "type": "module",
12
12
  "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
13
17
  "./theme.css": "./theme.css"
14
18
  },
15
19
  "files": [
16
- "theme.css"
20
+ "theme.css",
21
+ "dist"
17
22
  ],
18
23
  "publishConfig": {
19
24
  "access": "public"
20
25
  },
21
26
  "scripts": {
27
+ "build": "tsc -p tsconfig.build.json",
28
+ "prepack": "npm run build",
22
29
  "typecheck": "tsc --noEmit",
23
30
  "lint": "biome check .",
24
31
  "format": "biome check --write .",
25
32
  "test": "vitest run",
26
- "verify": "npm run typecheck && npm run lint && npm run test",
27
- "test:browsers": "node browser-check.ts"
33
+ "test:browsers": "node browser-check.ts",
34
+ "verify": "npm run typecheck && npm run lint && npm run test && npm run build"
28
35
  },
29
36
  "peerDependencies": {
37
+ "react": "^19.0.0",
30
38
  "tailwindcss": "^4.0.0"
31
39
  },
32
40
  "devDependencies": {
33
41
  "@biomejs/biome": "^2.5.15",
34
42
  "@tailwindcss/node": "^4.3.3",
35
43
  "@types/node": "^26.6.4",
44
+ "@types/react": "^19.3.0",
45
+ "@types/react-dom": "^19.3.0",
36
46
  "playwright": "^1.64.0",
47
+ "react": "^19.3.0",
48
+ "react-dom": "^19.3.0",
37
49
  "tailwindcss": "^4.3.3",
38
50
  "typescript": "~6.0.3",
39
51
  "vitest": "^5.0.3"
package/theme.css CHANGED
@@ -9,6 +9,10 @@
9
9
  color-scheme itself. Components use the utilities (bg-card, text-muted-foreground) and
10
10
  never Tailwind's dark: variant. */
11
11
 
12
+ /* The components are published as compiled files full of Tailwind class names. This has
13
+ the app's Tailwind scan them, so it generates those classes along with the app's own. */
14
+ @source "./dist";
15
+
12
16
  /* In the base layer, so an app's own unlayered rules win wherever it puts them. That's
13
17
  how an app themes the package: by redefining a token. */
14
18
  @layer base {