mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 21:27:14 +01:00
Some checks are pending
crdt_sync_dart tests / test (push) Waiting to run
crdt_sync_flutter tests / test (push) Waiting to run
crdt-sync pre-commit / pre-commit (push) Waiting to run
crdt-sync tests / test (push) Waiting to run
dependency freshness / dependencies on newest stable (push) Waiting to run
design_system tests / test (push) Waiting to run
file length / file-length (push) Waiting to run
freedays pre-commit / pre-commit (push) Waiting to run
freedays tests / test (push) Waiting to run
gatelock pre-commit / pre-commit (push) Waiting to run
gatelock tests / test (push) Waiting to run
github_device_auth tests / test (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
music_theory tests / test (push) Waiting to run
palette drift / palette (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
sync_settings_ui tests / test (push) Waiting to run
web_ui tests / test (push) Waiting to run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
116 lines
4.8 KiB
Markdown
116 lines
4.8 KiB
Markdown
# @kuhyx/web-ui
|
|
|
|
The shared token + component layer for kuhy's web apps — the web counterpart to
|
|
`~/src/utils/design_system` (Dart). Owns the frozen palette, the spacing/radius/type
|
|
scales, and the components that were each reimplemented in two or more repos.
|
|
|
|
Consumers import the tokens rather than transcribing the token table by hand.
|
|
Source of truth for the *values* is `~/src/utils/unified-design-system/DOCS-tokens.md`;
|
|
this package is its executable form for the web.
|
|
|
|
## Install
|
|
|
|
```jsonc
|
|
// package.json — pnpm only, see "Why the &path: suffix" below.
|
|
"dependencies": {
|
|
"@kuhyx/web-ui": "github:kuhyx/utils#web_ui-v0.3.1&path:/web_ui"
|
|
}
|
|
```
|
|
|
|
```ts
|
|
import "@kuhyx/web-ui/tokens.css"; // once, at the app entry point
|
|
import "@kuhyx/web-ui/range-slider.css"; // only if you use RangeSlider
|
|
|
|
import { RangeSlider, fuzzyMatch, quantileValue } from "@kuhyx/web-ui";
|
|
```
|
|
|
|
### Why the `&path:/web_ui` suffix
|
|
|
|
`utils` is a monorepo with no root `package.json`, and **npm cannot install from
|
|
a subdirectory of a git repo** — `github:user/repo#tag` clones the root, looks
|
|
for `package.json` there, and fails with `ENOENT` (verified against npm 11.16).
|
|
pnpm supports the subdirectory via `&path:/<dir>`, which is why both consumers
|
|
are pnpm. This is the only difference from the Dart (`git: {url, ref, path}`) and
|
|
Python (`#subdirectory=`) conventions the rest of `utils` follows.
|
|
|
|
### Why `dist/` is committed
|
|
|
|
Vite does not transpile dependencies, so shipping raw `.tsx` would force
|
|
per-repo bundler config on every consumer. The tag carries a prebuilt `dist/`
|
|
(ESM + `.d.ts`, JSX already compiled, `.ts` specifiers rewritten to `.js`), so
|
|
consumers stay at zero config. **`pnpm build` before tagging, always** — a tag
|
|
whose `dist/` is stale ships old code to every consumer with no error.
|
|
|
|
## What's here
|
|
|
|
| Export | Notes |
|
|
| --- | --- |
|
|
| `tokens.css` | The `:root` block: palette, spacing, radius, type, shadows, focus ring. Also sets `:focus-visible` globally. |
|
|
| `RangeSlider` | Dual-thumb slider over a value *distribution*, not a linear ramp. |
|
|
| `range-slider.css` | Its styling, entirely in tokens. |
|
|
| `quantile.ts` helpers | `nth`, `clamp01`, `quantileValue`, `valueQuantile`, `fractionFromPointer`. |
|
|
| `fuzzyMatch` | Case-insensitive subsequence match. |
|
|
|
|
### The categorical ramp
|
|
|
|
`--cat-1` … `--cat-6` are for charts, tags and category dots — N mutually
|
|
distinguishable colours with **no** inherent meaning. Never use them for
|
|
semantic state; `--success`/`--warning`/`--danger` own that.
|
|
|
|
They deliberately leave the warm wedge the rest of the palette sits in, because
|
|
hue separation is the whole point of a categorical scale. The values were chosen
|
|
by search under a ≥20 CIE ΔE floor across normal vision *and* deuteranopia,
|
|
protanopia and tritanopia, ≥3:1 contrast on **both** backgrounds, and a
|
|
monotonic lightness staircase so the ramp survives greyscale. Re-run the proof
|
|
with `python3 ~/src/utils/unified-design-system/scripts/ramp_check.py` — it exits
|
|
non-zero if an edit breaks any of it. A seventh hue is not available: the best
|
|
candidate reaches only ΔE 17.9. Group the tail into "other" instead.
|
|
|
|
`fractionFromPointer` is deliberately pure geometry: it makes the slider's
|
|
behaviour testable without layout, which jsdom does not implement.
|
|
|
|
### RangeSlider is keyboard-operable
|
|
|
|
Both donor copies were pointer-only. The design system requires that every
|
|
action be reachable with the keyboard alone, and a `role="slider"` that ignores
|
|
arrow keys is an ARIA role that lies — so the shared version adds:
|
|
|
|
| Key | Effect |
|
|
| --- | --- |
|
|
| `←` `↓` / `→` `↑` | Step to the next **distinct** value |
|
|
| `PageDown` / `PageUp` | Step ten distinct values |
|
|
| `Home` / `End` | Jump to the distribution's bounds |
|
|
|
|
Pass `label` **and** `format` to get the built-in head (name + value readout);
|
|
omit them for a bare track when the caller draws its own chrome, as
|
|
dufs-cloud's `SizeRange`/`DurationRange` wrappers do.
|
|
|
|
Stepping targets the next *distinct* value, not the next index. Real
|
|
distributions are lumpy — ~1500 of awesome-mcp-explorer's ~2981 star counts are
|
|
`0` — and an index step inside a run of equal values moves the index while
|
|
leaving the value, the thumb and the filter untouched. That reads as a dead
|
|
arrow key, and jsdom's evenly-spaced fixtures do not catch it.
|
|
|
|
## Development
|
|
|
|
```bash
|
|
pnpm install
|
|
pnpm test # vitest
|
|
pnpm coverage # 100% branches/functions/lines/statements, enforced
|
|
pnpm lint # tsc --noEmit && eslint
|
|
pnpm build # tsc -b + copy CSS into dist/
|
|
```
|
|
|
|
The 100% bar matches Phase 1's `design_system`: a shared layer two repos depend
|
|
on does not get to be the least-tested code in the tree.
|
|
|
|
## Releasing
|
|
|
|
```bash
|
|
pnpm test && pnpm build
|
|
git add web_ui && git commit -m "web_ui: <what changed>"
|
|
git tag web_ui-v<major>.<minor>.<patch> && git push origin main --tags
|
|
```
|
|
|
|
Then bump the tag in each consumer's `package.json` and re-run `pnpm install`.
|