@handamade/psi-react 0.3.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 (89) hide show
  1. package/README.md +43 -0
  2. package/dist/AspectRatio/AspectRatio.d.ts +9 -0
  3. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -0
  4. package/dist/Button/Button.d.ts +21 -0
  5. package/dist/Button/Button.d.ts.map +1 -0
  6. package/dist/Card/Card.d.ts +13 -0
  7. package/dist/Card/Card.d.ts.map +1 -0
  8. package/dist/Checkbox/Checkbox.d.ts +9 -0
  9. package/dist/Checkbox/Checkbox.d.ts.map +1 -0
  10. package/dist/IconButton/IconButton.d.ts +21 -0
  11. package/dist/IconButton/IconButton.d.ts.map +1 -0
  12. package/dist/Input/Input.d.ts +13 -0
  13. package/dist/Input/Input.d.ts.map +1 -0
  14. package/dist/NavBar/NavBar.d.ts +13 -0
  15. package/dist/NavBar/NavBar.d.ts.map +1 -0
  16. package/dist/Select/Select.d.ts +18 -0
  17. package/dist/Select/Select.d.ts.map +1 -0
  18. package/dist/Switch/Switch.d.ts +9 -0
  19. package/dist/Switch/Switch.d.ts.map +1 -0
  20. package/dist/Tag/Tag.d.ts +15 -0
  21. package/dist/Tag/Tag.d.ts.map +1 -0
  22. package/dist/Tooltip/Tooltip.d.ts +13 -0
  23. package/dist/Tooltip/Tooltip.d.ts.map +1 -0
  24. package/dist/a11y-meta.d.ts +15 -0
  25. package/dist/a11y-meta.d.ts.map +1 -0
  26. package/dist/icons/IconArrowDown.d.ts +7 -0
  27. package/dist/icons/IconArrowDown.d.ts.map +1 -0
  28. package/dist/icons/IconArrowUpRight.d.ts +7 -0
  29. package/dist/icons/IconArrowUpRight.d.ts.map +1 -0
  30. package/dist/icons/IconCheck.d.ts +7 -0
  31. package/dist/icons/IconCheck.d.ts.map +1 -0
  32. package/dist/icons/IconChevronDown.d.ts +7 -0
  33. package/dist/icons/IconChevronDown.d.ts.map +1 -0
  34. package/dist/icons/IconChevronRight.d.ts +7 -0
  35. package/dist/icons/IconChevronRight.d.ts.map +1 -0
  36. package/dist/icons/IconClose.d.ts +7 -0
  37. package/dist/icons/IconClose.d.ts.map +1 -0
  38. package/dist/icons/IconCopy.d.ts +7 -0
  39. package/dist/icons/IconCopy.d.ts.map +1 -0
  40. package/dist/icons/IconEdit.d.ts +7 -0
  41. package/dist/icons/IconEdit.d.ts.map +1 -0
  42. package/dist/icons/IconExternalLink.d.ts +7 -0
  43. package/dist/icons/IconExternalLink.d.ts.map +1 -0
  44. package/dist/icons/IconEye.d.ts +7 -0
  45. package/dist/icons/IconEye.d.ts.map +1 -0
  46. package/dist/icons/IconEyeOff.d.ts +7 -0
  47. package/dist/icons/IconEyeOff.d.ts.map +1 -0
  48. package/dist/icons/IconGitHub.d.ts +7 -0
  49. package/dist/icons/IconGitHub.d.ts.map +1 -0
  50. package/dist/icons/IconInstagram.d.ts +7 -0
  51. package/dist/icons/IconInstagram.d.ts.map +1 -0
  52. package/dist/icons/IconLinkedIn.d.ts +7 -0
  53. package/dist/icons/IconLinkedIn.d.ts.map +1 -0
  54. package/dist/icons/IconLoader.d.ts +7 -0
  55. package/dist/icons/IconLoader.d.ts.map +1 -0
  56. package/dist/icons/IconMinus.d.ts +7 -0
  57. package/dist/icons/IconMinus.d.ts.map +1 -0
  58. package/dist/icons/IconPlus.d.ts +7 -0
  59. package/dist/icons/IconPlus.d.ts.map +1 -0
  60. package/dist/icons/IconSearch.d.ts +7 -0
  61. package/dist/icons/IconSearch.d.ts.map +1 -0
  62. package/dist/icons/IconSettings.d.ts +7 -0
  63. package/dist/icons/IconSettings.d.ts.map +1 -0
  64. package/dist/icons/IconTrash.d.ts +7 -0
  65. package/dist/icons/IconTrash.d.ts.map +1 -0
  66. package/dist/icons/IconUser.d.ts +7 -0
  67. package/dist/icons/IconUser.d.ts.map +1 -0
  68. package/dist/icons/IconX.d.ts +7 -0
  69. package/dist/icons/IconX.d.ts.map +1 -0
  70. package/dist/icons/index.d.ts +23 -0
  71. package/dist/icons/index.d.ts.map +1 -0
  72. package/dist/index.d.ts +24 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +594 -0
  75. package/dist/manifest.json +413 -0
  76. package/dist/styles.css +1 -0
  77. package/docs/AspectRatio.md +27 -0
  78. package/docs/Button.md +50 -0
  79. package/docs/Card.md +39 -0
  80. package/docs/Checkbox.md +34 -0
  81. package/docs/IconButton.md +49 -0
  82. package/docs/Input.md +36 -0
  83. package/docs/NavBar.md +29 -0
  84. package/docs/Select.md +37 -0
  85. package/docs/Switch.md +34 -0
  86. package/docs/Tag.md +47 -0
  87. package/docs/Tooltip.md +36 -0
  88. package/llms.txt +43 -0
  89. package/package.json +46 -0
package/docs/Input.md ADDED
@@ -0,0 +1,36 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # Input
3
+
4
+
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `size` | `24 \| 32 \| 40 \| 48` | 32 | no | Height in px (24 \| 32 \| 40 \| 48). |
11
+ | `error` | `boolean` | false | no | Show error styling. |
12
+ | `ref` | `Ref<HTMLInputElement>` | — | no | Forwarded ref to the underlying `<input>` element. |
13
+ | `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |
14
+
15
+ ## Keyboard & assistive tech
16
+
17
+ | Keys | Behavior |
18
+ |---|---|
19
+ | Tab | Focuses the native <input>; focus ring on the field. |
20
+
21
+ error sets a red border only — pair with aria-invalid and aria-describedby pointing at your error text.
22
+
23
+ ## Theming
24
+
25
+ Override `--psi-input-*` custom properties at any scope; interactive states derive automatically (L - 0.04 hover, L - 0.08 active).
26
+
27
+
28
+
29
+ ## Rules
30
+
31
+ - One accent per visual group; everything else neutral or ghost.
32
+ - danger only for actions with real consequences.
33
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
34
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
35
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
36
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
package/docs/NavBar.md ADDED
@@ -0,0 +1,29 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # NavBar
3
+
4
+
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `brand` | `ReactNode` | — | no | Brand slot (wordmark / logo link), leading edge. |
11
+ | `actions` | `ReactNode` | — | no | Trailing actions slot (theme switch, CTA). |
12
+ | `children` | `ReactNode` | — | no | Nav links. |
13
+ | `ref` | `Ref<HTMLElement>` | — | no | Forwarded ref to the header element. |
14
+ | `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |
15
+
16
+ ## Theming
17
+
18
+ Override `--psi-navbar-*` custom properties at any scope; interactive states derive automatically (L - 0.04 hover, L - 0.08 active).
19
+
20
+
21
+
22
+ ## Rules
23
+
24
+ - One accent per visual group; everything else neutral or ghost.
25
+ - danger only for actions with real consequences.
26
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
27
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
28
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
29
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
package/docs/Select.md ADDED
@@ -0,0 +1,37 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # Select
3
+
4
+
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `size` | `24 \| 32 \| 40 \| 48` | 32 | no | Height in px (24 \| 32 \| 40 \| 48). Replaces the native `size` attribute (which sets the visible option-row count on a listbox-style `<select>`) with the design system's control height in pixels. |
11
+ | `error` | `boolean` | false | no | Show error styling. |
12
+ | `ref` | `Ref<HTMLSelectElement>` | — | no | Forwarded ref to the underlying `<select>` element. |
13
+ | `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |
14
+
15
+ ## Keyboard & assistive tech
16
+
17
+ | Keys | Behavior |
18
+ |---|---|
19
+ | Tab | Focuses the native <select>. |
20
+ | Arrow keys / typeahead | Native option navigation. |
21
+
22
+ error: same contract as Input.
23
+
24
+ ## Theming
25
+
26
+ Override `--psi-select-*` custom properties at any scope; interactive states derive automatically (L - 0.04 hover, L - 0.08 active).
27
+
28
+
29
+
30
+ ## Rules
31
+
32
+ - One accent per visual group; everything else neutral or ghost.
33
+ - danger only for actions with real consequences.
34
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
35
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
36
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
37
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
package/docs/Switch.md ADDED
@@ -0,0 +1,34 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # Switch
3
+
4
+
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `children` | `ReactNode` | — | no | Label text. |
11
+ | `ref` | `Ref<HTMLInputElement>` | — | no | Forwarded ref to the underlying `<input type="checkbox" role="switch">` element. |
12
+ | `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |
13
+
14
+ ## Keyboard & assistive tech
15
+
16
+ | Keys | Behavior |
17
+ |---|---|
18
+ | Space | Toggles. Native checkbox input with role="switch"; announced as a switch, not a checkbox. |
19
+
20
+
21
+ ## Theming
22
+
23
+ Override `--psi-switch-*` custom properties at any scope; interactive states derive automatically (L - 0.04 hover, L - 0.08 active).
24
+
25
+
26
+
27
+ ## Rules
28
+
29
+ - One accent per visual group; everything else neutral or ghost.
30
+ - danger only for actions with real consequences.
31
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
32
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
33
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
34
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
package/docs/Tag.md ADDED
@@ -0,0 +1,47 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # Tag
3
+
4
+
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `variant` | `"neutral" \| "accent" \| "success" \| "warning" \| "danger"` | neutral | no | Semantic color variant. |
11
+ | `subtle` | `boolean` | false | no | Use tinted background with semantic foreground. |
12
+ | `onDismiss` | `(() => void)` | — | no | When provided, renders a dismiss button that calls this handler. |
13
+ | `ref` | `Ref<HTMLSpanElement>` | — | no | Forwarded ref to the underlying `<span>` element. |
14
+ | `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |
15
+
16
+ ## Keyboard & assistive tech
17
+
18
+ | Keys | Behavior |
19
+ |---|---|
20
+ | Enter / Space (dismiss button) | onDismiss renders a real <button>; keyboard-dismissible. |
21
+
22
+ Passive label otherwise; not in the tab order without onDismiss.
23
+
24
+ ## Theming
25
+
26
+ Override `--psi-tag-*` custom properties at any scope; interactive states derive automatically (L - 0.04 hover, L - 0.08 active).
27
+
28
+ ## Variant guidance
29
+
30
+ - **accent** — Primary action, draws attention. Submit, CTA, main action in a group.
31
+ - **accent-subtle** — Accent tone, lower visual weight. Selected state, active filter, soft CTA.
32
+ - **neutral** — Default, structurally present. Secondary actions, toolbar buttons.
33
+ - **neutral-subtle** — Minimal chrome. Inline actions, table row actions.
34
+ - **ghost** — No visible container until hover. Icon-only triggers, compact toolbars.
35
+ - **outline** — Bordered ghost — visible structure, no fill until hover. Marketing CTA, download button; hover fills accent.
36
+ - **danger** — Destructive action. Delete, remove, disconnect.
37
+ - **danger-subtle** — Destructive context, low urgency. Warning badges, soft destructive hints.
38
+ - **success | warning** — Status communication (Tag only). Status badges, labels.
39
+
40
+ ## Rules
41
+
42
+ - One accent per visual group; everything else neutral or ghost.
43
+ - danger only for actions with real consequences.
44
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
45
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
46
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
47
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
@@ -0,0 +1,36 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # Tooltip
3
+
4
+
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `content` | `ReactNode` | — | yes | Tooltip content. |
11
+ | `children` | `ReactElement<Record<string, unknown>, string \| JSXElementConstructor<any>>` | — | yes | The trigger element (must accept ref, onMouseEnter/Leave, onFocus/Blur). |
12
+ | `placement` | `"top" \| "bottom" \| "left" \| "right"` | top | no | Preferred placement. |
13
+
14
+ ## Keyboard & assistive tech
15
+
16
+ | Keys | Behavior |
17
+ |---|---|
18
+ | Tab (focus trigger) | Shows immediately on focus (no delay). |
19
+ | Escape | Dismisses while visible (WCAG 1.4.13). |
20
+
21
+ Hover opens after a short delay; content is linked via aria-describedby while visible. Trigger must accept onMouseEnter/Leave and onFocus/Blur props (cloned in automatically) — no ref forwarding required.
22
+
23
+ ## Theming
24
+
25
+ Override `--psi-tooltip-*` custom properties at any scope; interactive states derive automatically (L - 0.04 hover, L - 0.08 active).
26
+
27
+
28
+
29
+ ## Rules
30
+
31
+ - One accent per visual group; everything else neutral or ghost.
32
+ - danger only for actions with real consequences.
33
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
34
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
35
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
36
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
package/llms.txt ADDED
@@ -0,0 +1,43 @@
1
+ # @handamade/psi-react
2
+
3
+ 11 React 19 components consuming @handamade/psi-tokens. Zero runtime deps, CSS Modules, ref-as-prop.
4
+
5
+ ## Machine-readable artifacts
6
+ - dist/manifest.json: full component/prop inventory with types and defaults
7
+ - docs/{Component}.md: per-component docs (props, theming, variant guidance)
8
+
9
+ ## Rules for generated code
10
+ - import { Button } from "@handamade/psi-react"; import "@handamade/psi-react/styles";
11
+ - Also import from @handamade/psi-tokens: base.css, a theme css, components.css, utilities.css.
12
+ - size is a px number (24|32|40|48), never "sm"/"md"/"lg".
13
+ - variant vocabulary: accent | accent-subtle | neutral | neutral-subtle | ghost | danger | danger-subtle | outline.
14
+ - One accent per visual group. danger only for destructive actions.
15
+
16
+ ## Button: href + outline (D33/D34)
17
+ - Button renders as an <a> when given `href` (plus optional `target`/`rel`); disabled anchors get aria-disabled, no href attribute, and pointer-events: none instead of the disabled attribute.
18
+ - `outline` is a bordered-ghost variant: visible structure, no fill until hover — good for marketing CTAs and download buttons.
19
+ - --psi-button-font overrides button typography across all sizes in one declaration; brands can set it via componentOverrides (ember → mono).
20
+
21
+ ## Card
22
+ - <Card variant="stacked" | "featured" media={...} hoverLift>: stacked places media above the body, featured places media beside it (~1.6fr/1fr, stacks under the md breakpoint). hoverLift lifts the card translateY(-6px) over --psi-duration-350. Border uses --psi-border-faint.
23
+
24
+ ## NavBar
25
+ - <NavBar brand={...} actions={...}>{links}</NavBar>: brand slot leading, nav links centered/inline, actions slot trailing (theme switch, CTA). Renders inside .psi-container. Background defaults to --psi-scrim-heavy so content can scroll underneath; border uses --psi-border-faint.
26
+
27
+ ## AspectRatio
28
+ - <AspectRatio ratio={16/10}>{children}</AspectRatio>: a frame div with CSS aspect-ratio set from the numeric ratio prop — wrap images/media to reserve layout space before load. Combine with the .psi-media-tint utility for the brand media-tint treatment.
29
+
30
+ ## Icons (22)
31
+ - Full icon set: IconPlus, IconMinus, IconCheck, IconClose, IconChevronDown, IconChevronRight, IconSearch, IconSettings, IconUser, IconEye, IconEyeOff, IconEdit, IconCopy, IconTrash, IconLoader, IconExternalLink, plus D36 additions — arrows IconArrowDown and IconArrowUpRight, and social glyphs IconLinkedIn, IconGitHub, IconX, IconInstagram.
32
+
33
+ ## Compositions (recipes for what has no component yet)
34
+
35
+ - Form field: there is no Field/Label component. Compose: `<label htmlFor>` styled with `.psi-text-14-20-medium`; the control (Input/Select); error text in `.psi-text-12-16-regular` with `color: var(--psi-fg-danger)`, linked via `aria-describedby` on the control plus `aria-invalid` and `error` when invalid. Spacing: `ds-gap-6` label→control, `ds-gap-24` between fields, `ds-gap-12` for toggle groups, `ds-gap-8` for button rows and label+Tag rows.
36
+ - Stacks/rows: `.psi-gap-*` utilities set only the gap — they do not create a flex/grid container. Pair them with `display: flex` (column for stacks, row for button groups).
37
+ - Native pass-through: components spread unlisted props onto their underlying element (Input → `<input>`, Select → `<select>`, Button → `<button>`/`<a>`), so `value`, `onChange`, `type`, `id`, `autoComplete`, `checked` etc. all work even though manifest.json lists only the component-specific props. Exception: Tooltip takes no host props — put handlers/attributes on its trigger child.
38
+ - Theming, programmatic: the standard setup is `data-psi-theme` on `<html>`. From JS, set it once at app init: `document.documentElement.dataset.dsTheme = "dark"` (React: in a root-level effect). Theme CSS does NOT paint the page background — paint `background: var(--psi-bg-primary)` at your app root yourself. For a themed subtree, set `data-psi-theme` on a wrapper and paint its background the same way; brand font combos do not reach nested subtrees (see guidance.fonts.scope).
39
+ - Size selection: 32 is the default (compact UI); 40 for comfortable forms and marketing pages; 24 for inline/table-row actions; 48 for hero CTAs.
40
+ - Tags & the accent rule (D40): Tags are passive labels — they do NOT count against "one accent per visual group" (that rule governs buttons/CTAs). Badge mapping: highlight (e.g. a plan badge) → `accent-subtle`; meta → `neutral-subtle`; status → `success`/`warning`/`danger`.
41
+ - CSS import order: all DS stylesheets are order-independent — base.css opens with an explicit `@layer psi.base, psi.theme, psi.components, psi.utilities;` statement. Recommended listing for readability: base → theme → components → utilities → @handamade/psi-react/styles.
42
+ - Widths: content widths are app-level; the system tokenizes the page container only (`--psi-container-max`). Component width tokens arrive with Field/Dialog (next cycle).
43
+ - Full semantic token roster: `@handamade/psi-tokens/resolved/<theme>.json` has every token with resolved values — this file lists only the patterns.
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@handamade/psi-react",
3
+ "version": "0.3.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ },
12
+ "./styles": "./dist/styles.css",
13
+ "./manifest.json": "./dist/manifest.json"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "docs",
18
+ "README.md",
19
+ "llms.txt"
20
+ ],
21
+ "peerDependencies": {
22
+ "@handamade/psi-tokens": ">=0.2.0 <1.0.0",
23
+ "react": "^19.0.0",
24
+ "react-dom": "^19.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@testing-library/jest-dom": "^6.9.1",
28
+ "@testing-library/react": "^16.3.2",
29
+ "@testing-library/user-event": "^14.6.1",
30
+ "@types/react": "^19.2.17",
31
+ "@types/react-dom": "^19.2.3",
32
+ "@vitejs/plugin-react": "^4.3.0",
33
+ "jsdom": "^29.1.1",
34
+ "react": "^19.2.0",
35
+ "react-docgen-typescript": "^2.4.0",
36
+ "react-dom": "^19.2.0",
37
+ "tsx": "^4.22.4",
38
+ "vite": "^6.0.0",
39
+ "vitest": "^3.2.6",
40
+ "@handamade/psi-tokens": "0.3.0"
41
+ },
42
+ "scripts": {
43
+ "build": "vite build && tsc -p tsconfig.build.json && tsx scripts/emit-manifest.ts && tsx scripts/emit-docs.ts",
44
+ "dev": "vite build --watch"
45
+ }
46
+ }