@estiva-app/ui 0.6.0 → 0.8.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 (71) hide show
  1. package/README.md +23 -6
  2. package/dist/Button.d.ts +12 -5
  3. package/dist/Button.d.ts.map +1 -1
  4. package/dist/Checkbox.d.ts +19 -5
  5. package/dist/Checkbox.d.ts.map +1 -1
  6. package/dist/IconButton.d.ts +9 -2
  7. package/dist/IconButton.d.ts.map +1 -1
  8. package/dist/PersonTrigger.d.ts +2 -1
  9. package/dist/PersonTrigger.d.ts.map +1 -1
  10. package/dist/Reaction.d.ts +54 -0
  11. package/dist/Reaction.d.ts.map +1 -0
  12. package/dist/Tabs.d.ts +4 -1
  13. package/dist/Tabs.d.ts.map +1 -1
  14. package/dist/cn.d.ts +10 -1
  15. package/dist/cn.d.ts.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +383 -329
  19. package/dist/index.js.map +4 -4
  20. package/package.json +17 -3
  21. package/src/AppShell.stories.tsx +7 -1
  22. package/src/AppShell.tsx +1 -1
  23. package/src/Avatar.stories.tsx +3 -1
  24. package/src/Banner.stories.tsx +6 -1
  25. package/src/Banner.tsx +2 -2
  26. package/src/Breadcrumb.stories.tsx +3 -0
  27. package/src/Button.mdx +17 -3
  28. package/src/Button.stories.tsx +4 -1
  29. package/src/Button.test.tsx +119 -0
  30. package/src/Button.tsx +37 -23
  31. package/src/Checkbox.mdx +17 -6
  32. package/src/Checkbox.stories.tsx +10 -5
  33. package/src/Checkbox.test.tsx +73 -0
  34. package/src/Checkbox.tsx +52 -25
  35. package/src/Chip.stories.tsx +4 -0
  36. package/src/Chip.tsx +5 -5
  37. package/src/ChipInput.stories.tsx +4 -0
  38. package/src/DialogShell.tsx +1 -1
  39. package/src/EditableText.stories.tsx +6 -1
  40. package/src/IconButton.mdx +12 -1
  41. package/src/IconButton.stories.tsx +9 -4
  42. package/src/IconButton.test.tsx +102 -0
  43. package/src/IconButton.tsx +30 -17
  44. package/src/IdentityMenu.stories.tsx +5 -1
  45. package/src/Kbd.tsx +2 -2
  46. package/src/NavItem.stories.tsx +3 -0
  47. package/src/Person.stories.tsx +3 -0
  48. package/src/PersonTrigger.mdx +9 -1
  49. package/src/PersonTrigger.stories.tsx +3 -0
  50. package/src/PersonTrigger.test.tsx +52 -0
  51. package/src/PersonTrigger.tsx +8 -9
  52. package/src/Reaction.mdx +49 -0
  53. package/src/Reaction.stories.tsx +49 -0
  54. package/src/Reaction.tsx +86 -0
  55. package/src/Select.stories.tsx +7 -2
  56. package/src/Sidebar.stories.tsx +6 -0
  57. package/src/Tabs.mdx +13 -4
  58. package/src/Tabs.test.tsx +117 -0
  59. package/src/Tabs.tsx +45 -34
  60. package/src/Toast.tsx +8 -8
  61. package/src/TopBar.stories.tsx +7 -1
  62. package/src/cn.test.ts +20 -1
  63. package/src/cn.ts +18 -2
  64. package/src/index.ts +1 -0
  65. package/stories/Choosing.mdx +101 -0
  66. package/stories/DesignTokens.mdx +10 -0
  67. package/stories/GettingStarted.mdx +108 -0
  68. package/stories/Introduction.mdx +36 -0
  69. package/stories/TokensPage.tsx +293 -0
  70. package/tailwind-preset.js +17 -0
  71. package/tokens.css +48 -0
package/README.md CHANGED
@@ -15,6 +15,13 @@ every Estiva app — Peek, Ship, and whatever comes next.
15
15
  - Primitives — arriving one at a time (see Storybook). Each has stories that
16
16
  need no app data; if a story needs a fixture, the component is not a
17
17
  primitive and does not belong here.
18
+ - Behaviour comes from [Base UI](https://base-ui.com) (`@base-ui/react`, a
19
+ dependency installed with the package): keyboard, focus, roles, forms.
20
+ The components move onto it one stage at a time; `Tabs`, `Checkbox`,
21
+ `Button`, `IconButton` and `PersonTrigger` are on it (2026-09-07), the
22
+ rest follow the plan in `K:\Estiva\migration docs\PLAN.md`, whose §16
23
+ lists every component, its Base UI part, and what it gains. Nothing
24
+ changes how a component looks.
18
25
 
19
26
  ## Using it
20
27
 
@@ -74,6 +81,11 @@ permission. A library people are afraid to deviate from becomes a tax.
74
81
  If the colour you need has no token, the token is missing: add it here, in
75
82
  every theme, rather than reaching for hex in an app.
76
83
 
84
+ A transparent colour is a token too. A wash, an outline, a glow or the scrim
85
+ lives in `tokens.css`, not in an opacity modifier on another token:
86
+ `bg-bg-inset/40` compiles to nothing, because the tokens are plain `var()`
87
+ values.
88
+
77
89
  ### The tailwind-merge pitfall — retired for `cn()` users
78
90
 
79
91
  Stock `tailwind-merge` **silently drops** a custom text-size class
@@ -98,9 +110,10 @@ the last two; a hover-opened one owes all five.
98
110
  ### Only offer actions that can succeed
99
111
 
100
112
  An action appears only where it works. A control that cannot succeed right
101
- now is disabled *with its reason* (a tooltip), and a control someone may
102
- never use is absent, not disabled. Primitives expose what that needs: a
103
- `disabled` that can explain itself, never a button that fails.
113
+ now is disabled *with its reason*, and a control someone may never use is
114
+ absent, not disabled. `Button` and `IconButton` carry it as
115
+ `disabledReason="Sign in first"`: disabled, still reachable by Tab, the
116
+ reason shown as a tooltip. Never a button that fails.
104
117
 
105
118
  ## Themes
106
119
 
@@ -119,9 +132,13 @@ stay in Peek's own `.signal` block.
119
132
 
120
133
  ```
121
134
  npm install
122
- npm test # every theme defines every token the preset names, and no other
135
+ npm test # tokens (every theme defines every token the preset names, and no other), cn(), and each component's page claims
136
+ npm run lint # the token contract: no class Tailwind does not generate, no ramp, no raw colour
137
+ npm run typecheck
123
138
  npm run storybook # http://localhost:6008 — Design Tokens, and each primitive, in signal and ship
124
139
  ```
125
140
 
126
- Extracting a primitive from Peek must not change how Peek renders:
127
- screenshot-compare before and after, in Peek's own Storybook.
141
+ A change must not change how an app renders: screenshot-compare before and
142
+ after, in both themes here and in the apps' own Storybooks. `CLAUDE.md` has
143
+ the whole contract: what a component, a story, a page and a port are held
144
+ to, and how a change is proved.
package/dist/Button.d.ts CHANGED
@@ -4,16 +4,20 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react';
4
4
  * adopt: a `destructive` variant — the muted button in the error colour, for
5
5
  * "Delete project" and its kind; a destructive action is a button like any
6
6
  * other, not a dotted link — and `type="button"` by default, so a button
7
- * inside a form submits it only when asked to.
7
+ * inside a form submits it only when asked to. On Base UI's Button since
8
+ * stage 2 of the migration (2026-09-07).
8
9
  *
9
10
  * Three variants and two sizes: 32px default / 24px small, 6px radius, 500
10
- * weight. Sizes are spelled as arbitrary values for the reason the README
11
- * records: tailwind-merge drops a custom `text-{size}` that is followed by a
12
- * `text-{colour}`.
11
+ * weight. The sizes are the `btn-default` and `btn-small` type tokens.
13
12
  *
14
13
  * The primary reads in `text-inverse` on the accent — a theme decides what
15
14
  * that is (dark on Peek's light accents, light on Ship's dark one). Under
16
15
  * Signal it is also semibold, as Peek has it.
16
+ *
17
+ * `disabledReason` is the package's own rule, "only offer actions that can
18
+ * succeed", done once: the button is disabled, stays reachable by keyboard
19
+ * (Base UI's `focusableWhenDisabled`), and shows the reason as a tooltip on
20
+ * hover. Ship wrote that wrapper by hand six times.
17
21
  */
18
22
  export type ButtonVariant = 'primary' | 'outlined' | 'muted' | 'destructive';
19
23
  export type ButtonSize = 'default' | 'small';
@@ -22,7 +26,10 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
22
26
  size?: ButtonSize;
23
27
  /** 16px, stroke 1.5 on the default size; 14px on small. */
24
28
  leadingIcon?: ReactNode;
29
+ /** Why the action cannot succeed right now. Disables the button, keeps it
30
+ * reachable by keyboard, and shows the reason as a tooltip on hover. */
31
+ disabledReason?: string;
25
32
  children: ReactNode;
26
33
  }
27
- export declare function Button({ variant, size, leadingIcon, className, children, disabled, type, ...props }: ButtonProps): import("react").JSX.Element;
34
+ export declare function Button({ variant, size, leadingIcon, className, children, disabled, disabledReason, type, ...props }: ButtonProps): import("react").JSX.Element;
28
35
  //# sourceMappingURL=Button.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../src/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAG5D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,aAAa,CAAA;AAC5E,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,CAAA;AAE5C,MAAM,WAAW,WAAY,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;IAC1E,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,MAAM,CAAC,EACrB,OAAiB,EACjB,IAAgB,EAChB,WAAW,EACX,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,IAAe,EACf,GAAG,KAAK,EACT,EAAE,WAAW,+BA2Bb"}
1
+ {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../src/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAK5D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,aAAa,CAAA;AAC5E,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,CAAA;AAE5C,MAAM,WAAW,WAAY,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;IAC1E,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;6EACyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,MAAM,CAAC,EACrB,OAAiB,EACjB,IAAgB,EAChB,WAAW,EACX,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,IAAe,EACf,GAAG,KAAK,EACT,EAAE,WAAW,+BA+Bb"}
@@ -1,11 +1,25 @@
1
1
  /**
2
2
  * A 16px square that fills with the accent when checked — Peek's Checkbox
3
- * (2026-09-01), verbatim.
3
+ * (2026-09-01), verbatim; on Base UI Checkbox since stage 1 of the migration
4
+ * (2026-09-06).
4
5
  *
5
- * Controlled only; parents own the state. Rendered as a button with the
6
- * checkbox role so it works standalone or inside clickable rows: a caller
7
- * that toggles on the row's own click passes no `onChange`, and the square
8
- * goes inert rather than stealing the click.
6
+ * Controlled only; parents own the state. Two renderings, one class list:
7
+ *
8
+ * - With `onChange` it is the control, as Base UI renders it: a
9
+ * `<span role="checkbox">` with a hidden `<input>` beside it. Space
10
+ * toggles, Enter does not, and its click never reaches the row around it.
11
+ * - Without `onChange` it is a picture of the state, for a row that toggles
12
+ * on its own click. It is hidden from assistive technology and takes no
13
+ * focus, so the row is one control and says the state itself
14
+ * (`aria-pressed`, or `aria-selected` on an option). Two focusable controls
15
+ * nested in one another is what axe's `nested-interactive` rule rejects.
16
+ *
17
+ * The tick is always in the box, hidden when unchecked. A box that is empty
18
+ * in one state and holds an icon in the other hangs on a line of text
19
+ * differently in each, and moved 1px on every click (Katerina, 2026-09-07:
20
+ * "it should be fixed"). With the tick always there, Base UI's `<span>` and
21
+ * the `<button>` this used to be land on the same pixel, measured. In a flex
22
+ * row nothing ever moved.
9
23
  */
10
24
  export interface CheckboxProps {
11
25
  checked: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../src/Checkbox.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAgB,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,+BAyBlG"}
1
+ {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../src/Checkbox.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAeD,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAgB,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,+BAwBlG"}
@@ -3,7 +3,11 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react';
3
3
  * Peek's IconButton (2026-08-28), verbatim: a square 4px-padded button
4
4
  * around a 16px icon, 8px radius, three variants, an optional tooltip that
5
5
  * portals above or below. Plus `type="button"` by default (Ship's addition),
6
- * so it never submits a form by accident.
6
+ * so it never submits a form by accident. On Base UI's Button since stage 2
7
+ * of the migration (2026-09-07).
8
+ *
9
+ * `disabledReason` disables it, keeps it reachable by keyboard, and shows
10
+ * the reason in place of the tooltip — see Button.
7
11
  */
8
12
  export type IconButtonVariant = 'muted' | 'outlined' | 'primary';
9
13
  export interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
@@ -13,8 +17,11 @@ export interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>
13
17
  * whose only other affordance is a keyboard shortcut. */
14
18
  tooltipShortcut?: string;
15
19
  tooltipPlacement?: 'top' | 'bottom';
20
+ /** Why the action cannot succeed right now. Disables the button, keeps it
21
+ * reachable by keyboard, and shows the reason as the tooltip. */
22
+ disabledReason?: string;
16
23
  /** The icon: 16px, stroke 1.5. */
17
24
  children: ReactNode;
18
25
  }
19
- export declare function IconButton({ variant, className, children, disabled, tooltip, tooltipShortcut, tooltipPlacement, type, ...props }: IconButtonProps): import("react").JSX.Element;
26
+ export declare function IconButton({ variant, className, children, disabled, disabledReason, tooltip, tooltipShortcut, tooltipPlacement, type, ...props }: IconButtonProps): import("react").JSX.Element;
20
27
  //# sourceMappingURL=IconButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"IconButton.d.ts","sourceRoot":"","sources":["../src/IconButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAI5D;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAA;AAEhE,MAAM,WAAW,eAAgB,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;IAC9E,OAAO,CAAC,EAAE,iBAAiB,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;8DAC0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gBAAgB,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAA;IACnC,kCAAkC;IAClC,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAiB,EACjB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,eAAe,EACf,gBAAgB,EAChB,IAAe,EACf,GAAG,KAAK,EACT,EAAE,eAAe,+BA8BjB"}
1
+ {"version":3,"file":"IconButton.d.ts","sourceRoot":"","sources":["../src/IconButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAK5D;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAA;AAEhE,MAAM,WAAW,eAAgB,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;IAC9E,OAAO,CAAC,EAAE,iBAAiB,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;8DAC0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gBAAgB,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAA;IACnC;sEACkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kCAAkC;IAClC,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAiB,EACjB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,OAAO,EACP,eAAe,EACf,gBAAgB,EAChB,IAAe,EACf,GAAG,KAAK,EACT,EAAE,eAAe,+BAkCjB"}
@@ -1,7 +1,8 @@
1
1
  import type { ComponentPropsWithRef } from 'react';
2
2
  import { type PersonProps } from './Person';
3
3
  /**
4
- * The person, as the button that opens the account menu.
4
+ * The person, as the button that opens the account menu. On Base UI's Button
5
+ * since stage 2 of the migration (2026-09-07).
5
6
  *
6
7
  * Two shapes, one component (Katerina, 2026-09-01):
7
8
  *
@@ -1 +1 @@
1
- {"version":3,"file":"PersonTrigger.d.ts","sourceRoot":"","sources":["../src/PersonTrigger.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,OAAO,CAAA;AAIlD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AAEnD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,EACpC,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IACnD,mFAAmF;IACnF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,+FAA+F;IAC/F,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAY,EAAE,OAAe,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,+BAiCtI"}
1
+ {"version":3,"file":"PersonTrigger.d.ts","sourceRoot":"","sources":["../src/PersonTrigger.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,OAAO,CAAA;AAKlD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AAEnD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,EACpC,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IACnD,mFAAmF;IACnF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,+FAA+F;IAC/F,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAY,EAAE,OAAe,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,+BA8BtI"}
@@ -0,0 +1,54 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from 'react';
2
+ /**
3
+ * A reaction: an emoji, how many people chose it, and whether you are one of
4
+ * them.
5
+ *
6
+ * **This exists because both apps had already built it, and neither could use
7
+ * the components that were here.** A reaction is a pill with a count that
8
+ * toggles — `Chip` is a pill and rules itself out for anything clickable,
9
+ * `Button` is clickable and is a 6px-radius rectangle, and `IconButton` has
10
+ * nowhere to put the count. So Peek re-typed Chip's class list with a border
11
+ * added, and Ship reached for a small Button with the count as its label. The
12
+ * two apps' reactions do not look alike today, and neither is what was drawn.
13
+ *
14
+ * ## The one thing it does that nothing else here does
15
+ *
16
+ * **It says the reaction is yours.** That is the state a reaction has and a
17
+ * chip does not: `pressed` fills it with the accent's muted tint and gives it
18
+ * an accent edge, so a glance separates "two people, one of them me" from "two
19
+ * people". Ship approximated it as `outlined` versus `muted` — measured at a
20
+ * 1px hairline against no border at all — which is legible and is not the
21
+ * signal Peek's accent fill gives.
22
+ *
23
+ * It is a real `<button>` with `aria-pressed`, so the state reaches assistive
24
+ * tech as a toggle rather than as a colour.
25
+ *
26
+ * ## `aria-label` is required, and the reason is specific
27
+ *
28
+ * The emoji is decorative here — it is `aria-hidden`, because a glyph read
29
+ * aloud is noise and its spoken name differs per screen reader — and the only
30
+ * visible text is the count. Without a label the control is announced as
31
+ * **"2"**, which is what Ship shipped and its own tests caught. Pass the
32
+ * meaning and the count: `"Makes sense, 2"`.
33
+ *
34
+ * ## Geometry
35
+ *
36
+ * Chip's pill, at a control's height: fully rounded, 24px to match `Button`
37
+ * `small`, the same 8px horizontal padding, the `chip` type token for the
38
+ * count so it sits at 11px/500 like every other count in the system.
39
+ */
40
+ export interface ReactionProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
41
+ /** The emoji, drawn decoratively — name the control with `aria-label`. */
42
+ emoji: ReactNode;
43
+ /** How many people reacted. Drawn as-is; `0` is not a reaction and is not drawn. */
44
+ count: number;
45
+ /** You are one of them: the accent tint and edge, and `aria-pressed`. */
46
+ pressed?: boolean;
47
+ /**
48
+ * Names the control. **Required** — the emoji is decorative and the count is
49
+ * the only visible text, so without this it is announced as a bare number.
50
+ */
51
+ 'aria-label': string;
52
+ }
53
+ export declare function Reaction({ emoji, count, pressed, className, type, ...props }: ReactionProps): import("react").JSX.Element;
54
+ //# sourceMappingURL=Reaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Reaction.d.ts","sourceRoot":"","sources":["../src/Reaction.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAG5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAC9F,0EAA0E;IAC1E,KAAK,EAAE,SAAS,CAAA;IAChB,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAe,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,+BA8BnG"}
package/dist/Tabs.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type { ReactNode } from 'react';
2
2
  /**
3
3
  * A row of tabs. Ship's Tabs (2026-09-01), which was Peek's TopicTabs with
4
- * the topic-specific ids taken out.
4
+ * the topic-specific ids taken out; on Base UI Tabs since stage 1 of the
5
+ * migration (2026-09-06), which is where the keyboard comes from: one Tab
6
+ * stop for the row, arrow keys between the tabs.
5
7
  *
6
8
  * A selected tab is a neutral fill (bg-active), not the accent tint —
7
9
  * Katerina's ruling (2026-08-27), extended to every app (2026-09-01). Two
@@ -28,6 +30,7 @@ export interface TabsProps<T extends string> {
28
30
  onChange: (id: T) => void;
29
31
  /** `default` 14px; `small` 12px, the denser geometry. */
30
32
  size?: 'default' | 'small';
33
+ /** Lands on the outer box, around the row. */
31
34
  className?: string;
32
35
  }
33
36
  export declare function Tabs<T extends string>({ tabs, active, onChange, size, className }: TabsProps<T>): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../src/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,SAAS,MAAM;IACtC,EAAE,EAAE,CAAC,CAAA;IACL,KAAK,EAAE,MAAM,CAAA;IACb,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wBAAwB;IACxB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM;IACzC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IACjB,MAAM,EAAE,CAAC,CAAA;IACT,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAA;IACzB,yDAAyD;IACzD,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAgB,EAAE,SAAS,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,+BAoC3G"}
1
+ {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../src/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC,SAAS,MAAM;IACtC,EAAE,EAAE,CAAC,CAAA;IACL,KAAK,EAAE,MAAM,CAAA;IACb,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wBAAwB;IACxB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM;IACzC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IACjB,MAAM,EAAE,CAAC,CAAA;IACT,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAA;IACzB,yDAAyD;IACzD,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;IAC1B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAgB,EAAE,SAAS,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,+BA2C3G"}
package/dist/cn.d.ts CHANGED
@@ -14,7 +14,16 @@ import { type ClassValue } from 'clsx';
14
14
  * imported here without dragging them into the browser bundle).
15
15
  */
16
16
  declare const FONT_SIZE_TOKENS: string[];
17
+ /**
18
+ * The preset's box-shadow and drop-shadow keys, for the same reason: stock
19
+ * tailwind-merge files an unknown `shadow-x` under shadow-COLOUR, so
20
+ * `shadow-focus-ring` beside `shadow-glow-warning` lost one of them, and
21
+ * `shadow-glow-warning` beside `shadow-md` kept both (measured 2026-09-06).
22
+ * `cn.test.ts` pins these to the preset too.
23
+ */
24
+ declare const BOX_SHADOW_TOKENS: string[];
25
+ declare const DROP_SHADOW_TOKENS: string[];
17
26
  /** Merge class names, last-one-wins on conflicting Tailwind utilities. */
18
27
  export declare function cn(...inputs: ClassValue[]): string;
19
- export { FONT_SIZE_TOKENS as _fontSizeTokens };
28
+ export { FONT_SIZE_TOKENS as _fontSizeTokens, BOX_SHADOW_TOKENS as _boxShadowTokens, DROP_SHADOW_TOKENS as _dropShadowTokens };
20
29
  //# sourceMappingURL=cn.d.ts.map
package/dist/cn.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cn.d.ts","sourceRoot":"","sources":["../src/cn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAA;AAG5C;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,gBAAgB,UAIrB,CAAA;AAMD,0EAA0E;AAC1E,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC;AAED,OAAO,EAAE,gBAAgB,IAAI,eAAe,EAAE,CAAA"}
1
+ {"version":3,"file":"cn.d.ts","sourceRoot":"","sources":["../src/cn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAA;AAG5C;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,gBAAgB,UAIrB,CAAA;AAED;;;;;;GAMG;AACH,QAAA,MAAM,iBAAiB,UAAsE,CAAA;AAC7F,QAAA,MAAM,kBAAkB,UAAkC,CAAA;AAY1D,0EAA0E;AAC1E,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC;AAED,OAAO,EAAE,gBAAgB,IAAI,eAAe,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,kBAAkB,IAAI,iBAAiB,EAAE,CAAA"}
package/dist/index.d.ts CHANGED
@@ -42,6 +42,7 @@ export { Sidebar, type SidebarProps } from './Sidebar';
42
42
  export { TopBar, type TopBarProps } from './TopBar';
43
43
  export { PersonTrigger, type PersonTriggerProps } from './PersonTrigger';
44
44
  export { Property, type PropertyProps } from './Property';
45
+ export { Reaction, type ReactionProps } from './Reaction';
45
46
  export { SearchInput, type SearchInputProps } from './SearchInput';
46
47
  export { SectionHeader, type SectionAction, type SectionHeaderProps } from './SectionHeader';
47
48
  export { SectionLabel } from './SectionLabel';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC1F,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAA;AACxF,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAClH,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACvF,OAAO,EAAE,GAAG,EAAE,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAC5H,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC1F,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;AACnE,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACtE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAClE,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACrE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAA;AAC3E,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,QAAQ,CAAA;AACxK,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAA;AACtD,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAA;AACtD,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAClE,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAC5F,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAA;AAC1D,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAA;AAC5G,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAA"}