@overdoser/react-toolkit 0.0.16 → 0.5.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 (57) hide show
  1. package/AGENTS.md +53 -0
  2. package/CHANGELOG.md +226 -0
  3. package/README.md +102 -3
  4. package/components/Alert/Alert.d.ts +34 -0
  5. package/components/Alert/index.d.ts +2 -0
  6. package/components/Avatar/Avatar.d.ts +36 -0
  7. package/components/Avatar/index.d.ts +2 -0
  8. package/components/Badge/Badge.d.ts +25 -0
  9. package/components/Badge/index.d.ts +2 -0
  10. package/components/Breadcrumbs/Breadcrumbs.d.ts +44 -0
  11. package/components/Breadcrumbs/index.d.ts +2 -0
  12. package/components/Divider/Divider.d.ts +28 -0
  13. package/components/Divider/index.d.ts +2 -0
  14. package/components/Draggable/Draggable.d.ts +50 -0
  15. package/components/Draggable/index.d.ts +2 -0
  16. package/components/Drawer/Drawer.d.ts +65 -0
  17. package/components/Drawer/index.d.ts +2 -0
  18. package/components/Dropzone/Dropzone.d.ts +50 -0
  19. package/components/Dropzone/index.d.ts +2 -0
  20. package/components/Form/Form.d.ts +16 -1
  21. package/components/Form/FormField.d.ts +17 -2
  22. package/components/Form/formFieldDefaults.d.ts +9 -0
  23. package/components/Link/Link.d.ts +10 -0
  24. package/components/Pagination/Pagination.d.ts +48 -0
  25. package/components/Pagination/index.d.ts +4 -0
  26. package/components/Pagination/paginationRange.d.ts +8 -0
  27. package/components/Popover/Popover.d.ts +7 -0
  28. package/components/Skeleton/Skeleton.d.ts +28 -0
  29. package/components/Skeleton/index.d.ts +2 -0
  30. package/components/Spinner/Spinner.d.ts +29 -0
  31. package/components/Spinner/index.d.ts +2 -0
  32. package/components/Tabs/Tabs.d.ts +66 -0
  33. package/components/Tabs/index.d.ts +2 -0
  34. package/components/Timer/Timer.d.ts +103 -0
  35. package/components/Timer/index.d.ts +2 -0
  36. package/components/Toast/Toast.d.ts +82 -0
  37. package/components/Toast/index.d.ts +2 -0
  38. package/components/Toast/toastStore.d.ts +28 -0
  39. package/components/Tooltip/Tooltip.d.ts +38 -0
  40. package/components/Tooltip/index.d.ts +2 -0
  41. package/components/inputs/Checkbox/Checkbox.d.ts +11 -1
  42. package/components/inputs/Checkbox/CheckboxGroup.d.ts +83 -0
  43. package/components/inputs/Checkbox/index.d.ts +2 -0
  44. package/components/inputs/DatePicker/DatePicker.d.ts +58 -0
  45. package/components/inputs/DatePicker/dateUtils.d.ts +16 -0
  46. package/components/inputs/DatePicker/index.d.ts +2 -0
  47. package/components/inputs/NumberInput/NumberInput.d.ts +41 -0
  48. package/components/inputs/NumberInput/index.d.ts +2 -0
  49. package/components/inputs/Slider/Slider.d.ts +44 -0
  50. package/components/inputs/Slider/index.d.ts +2 -0
  51. package/index.css +1 -1
  52. package/index.d.ts +38 -2
  53. package/index.js +5047 -2698
  54. package/index.layered.css +5 -0
  55. package/llms.txt +331 -5
  56. package/manifest.json +2057 -289
  57. package/package.json +3 -2
package/AGENTS.md CHANGED
@@ -19,6 +19,11 @@ For exhaustive component reference (every prop, every variant, every signature),
19
19
  import '@overdoser/react-toolkit/theme.css';
20
20
  ```
21
21
  Without this, components render unstyled.
22
+ - **Want `className` / `classes.*` overrides to always win regardless of bundler order?** Import the layered twin instead:
23
+ ```ts
24
+ import '@overdoser/react-toolkit/theme.layered.css';
25
+ ```
26
+ It wraps all toolkit CSS in a `crk` cascade layer, so any of your own *unlayered* rules beat it deterministically. See "Customizing styles" below.
22
27
 
23
28
  ## Import rules
24
29
 
@@ -44,12 +49,40 @@ For exhaustive component reference (every prop, every variant, every signature),
44
49
  - Multi-pick with chips → `<Select multiple options={...} onValuesChange={...} />`.
45
50
  - You want it to look like a "menu trigger" instead of a form input → `<Dropdown options={...} value={...} onChange={...} />` (select-mode dropdown).
46
51
 
52
+ - **Need to pick several values from a small fixed set?** → `<CheckboxGroup options={...} value={...} onChange={...} />` (a checkbox list, or `variant="chips"` for toggle chips). For a large/searchable set, prefer `<Select multiple>` instead.
53
+
47
54
  - **Need a menu (Edit / Delete / Archive)?** → `<Dropdown trigger={...}>` with `<DropdownItem>` children.
48
55
 
49
56
  - **Need a tooltip / hover card / contextual panel?** → `<Popover trigger={...} content={...} />`.
50
57
 
51
58
  - **Need a confirm prompt or a full dialog?** → `<Modal>` with `Modal.Header`/`Body`/`Footer`. Always provide either `aria-label` or use a `Modal.Header` (it's auto-wired as `aria-labelledby`).
52
59
 
60
+ - **Need a slide-in side panel?** → `<Drawer open onClose side="right">` with `Drawer.Header`/`Body`/`Footer`. Same focus-trap/scroll-lock/Escape behavior as `Modal`, but anchored to a screen edge. Use `Modal` for centered dialogs, `Drawer` for side panels.
61
+
62
+ - **Need a hover/focus hint label?** → `<Tooltip content="…">{trigger}</Tooltip>` — lightweight, delayed, non-interactive. For click-triggered or interactive floating content use `Popover` instead.
63
+
64
+ - **Need a user avatar?** → `<Avatar src name status />` (initials/icon fallback). **A separator?** → `<Divider>` (add `label` for a centered caption; `orientation="vertical"` inside a flex row). **Breadcrumb trail?** → `<Breadcrumbs>` with `Breadcrumbs.Item` (last item auto-marked `current`).
65
+
66
+ - **Need standalone page navigation?** → `<Pagination count={…} page={…} onChange={…} />` (windowed with ellipses, ≤9 chips by default; `total`+`pageSize` instead of `count` also works). Note: `Table` already has its own built-in pagination — only use `Pagination` for non-table lists.
67
+
68
+ - **Need a numeric/range input?** → `<Slider>` (single number, or a `[lo, hi]` tuple for a range) for choosing along a continuum; `<NumberInput>` for an exact number with steppers (`onChange(number | null)`, binds in `FormField`).
69
+
70
+ - **Need a date input?** → `<DatePicker value onChange min max disabledDate />` — pop-up calendar, single date, binds in `FormField` (`value: Date | null`).
71
+
72
+ - **Need file upload?** → `<Dropzone onFiles accept maxSize multiple />` — drag-drop or click; it emits accepted/rejected files but leaves the file-list UI to you.
73
+
74
+ - **Need a countdown / stopwatch / elapsed display?** → `<Timer mode="countdown|stopwatch" duration={…} variant="digital|ring" label="…" />`. Drive it with `autoStart` + an imperative ref (`start`/`pause`/`reset`/`restart`), or make it display-only with `value`.
75
+
76
+ - **Need a movable panel / floating widget the user can drag around?** → `<Draggable>` wrapping any content. Add a `<Draggable.Handle>` (e.g. a title bar) to restrict the grab area; without one the whole surface drags. Uncontrolled by default (`defaultPosition`); pass `position` + `onDrag` to control it. It's a free-move/reposition primitive — not a sortable list or drag-and-drop-between-zones helper.
77
+
78
+ - **Need transient notifications (toasts)?** → Mount `<ToastProvider>` once near the root (sets `position` / `duration` / `max`); it's the renderer and owns the portal/stack — do NOT render toast JSX yourself. To trigger, import the standalone `toast({ variant, title, description })` and call it from anywhere (no hook/context needed); `toast.dismiss(id)` / `toast.dismissAll()` are attached. A `useToast()` hook returns the same `{ toast, dismiss, dismissAll }` if you prefer hook style.
79
+
80
+ - **Need to switch between sections of content?** → `<Tabs>` with `Tabs.List` / `Tabs.Tab` / `Tabs.Panel`. Each `Tab` and its `Panel` share a `value`. Uncontrolled via `defaultValue` or controlled via `value` + `onValueChange`. Keyboard nav is built in.
81
+
82
+ - **Need an inline status message?** → `<Alert variant="info|success|warning|danger">`. Add `title` for a heading, `onClose` to make it dismissible. For *transient* feedback prefer Toast; use Alert for persistent, in-flow messaging.
83
+
84
+ - **Need a status pill / count / tag?** → `<Badge>`. For loading states: `<Spinner>` (indeterminate activity, inherits text color) and `<Skeleton>` (content placeholder — `variant="text|circle|rect"`, `lines` for multi-line text).
85
+
53
86
  - **Need a data table?** → `<Table>`.
54
87
  - All data in memory → don't pass `onSort`; pass `pagination` (without `totalRows`) for client-side paging.
55
88
  - Data from a server → pass `sortConfig` + `onSort`, and `pagination` with `totalRows`.
@@ -96,6 +129,26 @@ For exhaustive component reference (every prop, every variant, every signature),
96
129
  </FormField>
97
130
  ```
98
131
 
132
+ ### Wire a CheckboxGroup (multi-value) inside a Form
133
+ ```tsx
134
+ <FormField name="interests" label="Interests" rules={{ validate: (v) => v.length > 0 || 'Pick at least one' }}>
135
+ <CheckboxGroup
136
+ variant="chips" // or omit for a checkbox list
137
+ options={[
138
+ { value: 'design', label: 'Design' },
139
+ { value: 'eng', label: 'Engineering' },
140
+ ]}
141
+ />
142
+ </FormField>
143
+ ```
144
+ `CheckboxGroup` binds `value: string[]` / `onChange(values)` directly — no bridge needed.
145
+
146
+ ## Customizing styles
147
+
148
+ - **Theme tokens:** override `--crk-*` custom properties on `:root` (colors, fonts, spacing, radii). This is the primary, stable customization surface.
149
+ - **Per-element overrides:** use each component's `classes` prop (and `className`) — internal class names are hashed and unstable, so never target them directly.
150
+ - **Make overrides deterministic:** `className` / `classes.*` are plain CSS classes; they only beat the toolkit's built-in class if your stylesheet is inserted *after* it, which the bundler controls. To guarantee precedence, import `@overdoser/react-toolkit/theme.layered.css` (everything ships in a `crk` cascade layer, so your unlayered rules always win), **or** add `@import '@overdoser/react-toolkit/theme.css' layer(crk);` as the first line of your global CSS. Caveat: an aggressive global reset that is itself unlayered will then also override the toolkit's base styles — put your reset in its own earlier layer if so. `theme.css` and `theme.layered.css` are identical except the layer wrapper — import exactly one, and token (`--crk-*`) theming works the same in either.
151
+
99
152
  ## Recipes
100
153
 
101
154
  Full copy-paste-able files live alongside this doc. Each is one self-contained file that you can drop into a project. Adjust types/imports for your codebase.
package/CHANGELOG.md ADDED
@@ -0,0 +1,226 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@overdoser/react-toolkit` are documented here.
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
5
+ project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.5.0]
8
+
9
+ Adds a segmented `Timer` display and reworked `Drawer` motion/variants, and —
10
+ importantly — fixes a build-config bug that broke variant styling in the
11
+ published package.
12
+
13
+ ### Fixed
14
+
15
+ - **Published variant styles were missing.** The library was built with CSS
16
+ Modules `localsConvention: 'camelCaseOnly'`, which dropped the original class
17
+ keys from the exported map. Components look classes up by their literal name
18
+ (e.g. `styles['side_left']`, `styles['pos_top-right']`), so in the **built
19
+ package** those lookups returned `undefined` and the rules never applied —
20
+ affecting `Drawer` (side/position + animation), `Tabs` (`line`/`solid`/`pill`),
21
+ `Toast` (positions), `Timer` (label placement), and `Avatar` (status dot).
22
+ Switched to `camelCase` (keeps both key forms). Tests were unaffected because
23
+ the test env keeps literal keys, so this only surfaced in consuming apps.
24
+ - **`Drawer` slide animation** now plays reliably in both directions. The open
25
+ was animating like a fade and the close slid the wrong way; it now uses
26
+ per-side keyframes applied as a single, unambiguous class.
27
+ - **`Drawer` closes back the way it opened** even when the consumer resets the
28
+ `side` prop while toggling `open` (the common `side={side ?? 'right'}`
29
+ pattern). The opened side/size are frozen for the duration of the close.
30
+ - **`Pagination`** active-page flicker removed — the selected chip no longer
31
+ cross-fades its background and text colour (the active state now applies
32
+ instantly; only hover backgrounds transition).
33
+ - **`Draggable`** prop types: `onDragStart`/`onDragEnd` no longer conflict with
34
+ the native `div` drag handlers (both are now omitted from the extended props).
35
+
36
+ ### Added
37
+
38
+ - **`Timer` `variant="segments"`** — one cell per unit (days/hours/minutes/
39
+ seconds) with the unit label under each number. `units` selects which cells
40
+ (the leading unit absorbs overflow), `unitFormat` (`short` → `d/h/m/s`,
41
+ `long` → `days/hours/minutes/seconds`, fixed — no singular/plural),
42
+ `unitLabels` overrides, `segmentVariant` (`boxed`/`plain`), `renderSegment`
43
+ for fully custom cells, and `segmentSeparator`. New `TimerUnit` / `TimerSegment`
44
+ exports.
45
+ - **`Drawer` `variant`** (`'temporary' | 'persistent'`, default `'temporary'`).
46
+ `temporary` overlays a dimmed scrim and is modal (scrim/Escape close);
47
+ `persistent` shows no scrim and leaves the page interactive. Replaces the
48
+ short-lived `backdrop` prop. The scrim is also lighter, and the panel slides
49
+ with a drawer-style easing.
50
+
51
+ ## [0.4.0]
52
+
53
+ Eleven new components spanning overlays, navigation, inputs, and a flexible
54
+ timer. Fully additive — no breaking changes.
55
+
56
+ ### Added
57
+
58
+ - **`Drawer`** — off-canvas panel sliding from any edge (`left`/`right`/`top`/
59
+ `bottom`), with `Drawer.Header`/`Body`/`Footer`, focus trap, scroll lock, and
60
+ Escape/backdrop close (same family as `Modal`).
61
+ - **`Tooltip`** — lightweight hover/focus label with an open `delay`, viewport
62
+ flipping, Escape-to-close, and `aria-describedby` wiring.
63
+ - **`Avatar`** — image with initials/icon fallback, five sizes, circle/square,
64
+ and a presence `status` dot.
65
+ - **`Divider`** — horizontal/vertical separator with an optional centered label.
66
+ - **`Breadcrumbs`** / **`Breadcrumbs.Item`** — breadcrumb nav with auto
67
+ separators and `aria-current` on the current crumb.
68
+ - **`Pagination`** — windowed page navigation with ellipses
69
+ (`1 … 6 7 8 9 10 … 99`; at most 9 chips by default), prev/next and optional
70
+ first/last controls. Accepts `count` or `total` + `pageSize`. Also exports the
71
+ pure `paginationRange()` helper.
72
+ - **`Slider`** — single- or dual-thumb (range) input with pointer drag and full
73
+ keyboard support (arrows, Home/End, PageUp/Down).
74
+ - **`NumberInput`** — numeric field with stepper buttons, min/max clamping,
75
+ arrow-key stepping, and `precision`. Emits `onChange(number | null)`, so it
76
+ binds directly in `FormField`.
77
+ - **`DatePicker`** — date field with a pop-up calendar: single-date selection,
78
+ full keyboard navigation, `min`/`max`, `disabledDate`, `weekStartsOn`, and
79
+ locale-aware labels. Binds in `FormField` (`value: Date | null`).
80
+ - **`Dropzone`** — file upload via drag-and-drop or click, with `accept` and
81
+ `maxSize` validation (`onFiles` / `onReject`); the file-list UI is left to you.
82
+ - **`Timer`** — countdown or stopwatch, rendered as digits or a progress ring,
83
+ with an attachable `label`. Drive it uncontrolled (`autoStart` + an imperative
84
+ `TimerHandle` ref: `start`/`pause`/`reset`/`restart`), bind to a target date
85
+ (`to`), or make it display-only with `value`. Fires `onComplete`/`onTick`.
86
+
87
+ ### Docs
88
+
89
+ - `README`, `llms.txt`, `manifest.json`, and `AGENTS.md` updated for all eleven
90
+ components (table rows, full prop reference, and decision-flow guidance).
91
+
92
+ ## [0.3.1]
93
+
94
+ ### Fixed
95
+
96
+ - **`Badge`** text now sits vertically centered in the pill. It was the only
97
+ pill-shaped element using `line-height: 1`, which rendered the glyph optically
98
+ high; it now matches the `Button`/chip pattern (`box-sizing: border-box`,
99
+ `justify-content: center`, `line-height` tight).
100
+
101
+ ## [0.3.0]
102
+
103
+ Six new components covering feedback, navigation, and loading states. Additive —
104
+ the only behavior note is an internal change to `useToast` (see below).
105
+
106
+ ### Added
107
+
108
+ - **`Toast`** — imperative notification system backed by a global store. Mount
109
+ `<ToastProvider>` once (it's the renderer); trigger with the standalone
110
+ `toast(options)` (importable, no hook/context needed — also `toast.dismiss(id)`
111
+ / `toast.dismissAll()`) or the `useToast()` hook, which returns the same
112
+ `{ toast, dismiss, dismissAll }`. Variants (`neutral | info | success |
113
+ warning | danger`), six `position`s, `duration` / `max`, auto-dismiss that
114
+ pauses on hover, and assertive vs. polite live regions by variant. Firing a
115
+ toast with no provider mounted logs a dev warning instead of failing silently.
116
+ - **`Tabs`** — accessible tabs via `Tabs.List` / `Tabs.Tab` / `Tabs.Panel`
117
+ (linked by matching `value`). `line` / `solid` / `pill` variants, horizontal
118
+ or vertical, controlled or uncontrolled, full keyboard navigation (arrows +
119
+ Home/End, roving tabindex), and a `keepMounted` panel option.
120
+ - **`Alert`** — inline status message (`info | success | warning | danger`) with
121
+ optional `title`, default/overridable/hidden `icon`, and an optional dismiss
122
+ button.
123
+ - **`Badge`** — status pill with 5 variants, `soft` / `solid` / `outline`
124
+ appearance, three sizes, and an optional status `dot`.
125
+ - **`Spinner`** — indeterminate loading spinner that inherits `currentColor`,
126
+ with a visually-hidden a11y `label`.
127
+ - **`Skeleton`** — loading placeholder (`text` with multi-`lines`, `circle`,
128
+ `rect`); `pulse` / `wave` / `none` animation; respects reduced motion.
129
+
130
+ ### Changed
131
+
132
+ - **`useToast()` no longer throws when called outside a `ToastProvider`.** Toast
133
+ state now lives in a global store, so the hook (and the new standalone
134
+ `toast()`) work anywhere; the provider is purely the renderer. If you relied on
135
+ the previous throw to detect a missing provider, note that `toast()` now warns
136
+ in development instead.
137
+
138
+ ## [0.2.0]
139
+
140
+ Additive release — a new `Draggable` container and a toggle-switch style for
141
+ `Checkbox`. No breaking changes; no default behavior changed.
142
+
143
+ ### Added
144
+
145
+ - **`Draggable` component** — a free-move container that wraps any children and
146
+ can be dragged anywhere on screen. Position is a `translate(x, y)` offset from
147
+ the element's natural layout position. Add a `<Draggable.Handle>` to restrict
148
+ the grab area (e.g. a title bar); without one, the whole surface drags, and a
149
+ focused handle is keyboard-movable (arrow keys). Props: `defaultPosition` /
150
+ `position` (uncontrolled / controlled), `axis` (`both | x | y`), `bounds`
151
+ (`viewport | parent | none`), `keyboardStep`, `disabled`, `onDragStart` /
152
+ `onDrag` / `onDragEnd`, and a `classes` override (`{ root, handle }`).
153
+ - **`Checkbox` `variant` prop** (`'checkbox' | 'switch'`, default `'checkbox'`).
154
+ `'switch'` renders a sliding toggle; the underlying `<input type="checkbox">`
155
+ (a11y + form behavior) is identical, so it binds inside `FormField` unchanged.
156
+ `indeterminate` is ignored for switches.
157
+
158
+ ## [0.1.0]
159
+
160
+ This release adds a `CheckboxGroup` component, a cascade-layered stylesheet for
161
+ deterministic style overrides, and viewport-aware popovers — plus a round of
162
+ `Form` ergonomics. A few **defaults changed** (forms, popovers, links); they are
163
+ all opt-out (see ⚠️ below).
164
+
165
+ ### ⚠️ Notable default changes (visual)
166
+
167
+ No API was removed or renamed, but some components now **render differently by
168
+ default**. To restore the previous behavior:
169
+
170
+ - **Forms reserve space for validation messages** so an error no longer shifts
171
+ the layout, the error now appears *between the input and the description*
172
+ (instead of replacing the description), inter-field/element gaps are tighter,
173
+ and labels are bolder (weight 700). Opt out per field or form-wide with
174
+ `reserveErrorSpace={false}`.
175
+ - **Popovers reposition to stay in the viewport** (flip to the opposite side /
176
+ shift near a screen edge). Opt out with `<Popover autoPosition={false} />`.
177
+ - **Links that open in a new tab show an inline ↗ icon** (`external` or a manual
178
+ `target="_blank"`). Opt out with `<Link hideExternalIcon />`.
179
+ - `theme.css` is **unchanged** — the new cascade layer is opt-in via
180
+ `theme.layered.css`, so existing stylesheet imports are unaffected.
181
+
182
+ ### Added
183
+
184
+ - **`CheckboxGroup` component** — multi-value selection from a fixed option set,
185
+ rendered as a checkbox list (`variant="checkbox"`, default) or toggleable chips
186
+ (`variant="chips"`). Supports `chipShape` (`pill | rounded | square`),
187
+ `chipCheckmark` (with constant chip width — padding compensates for the mark),
188
+ `orientation`, `disabled`, `error`, `name`, `required`, and a `classes`
189
+ override (`{ group, option, chip, chipSelected }`). Binds directly inside
190
+ `FormField` (`value: string[]` / `onChange(values)`), no bridge needed.
191
+ - **`theme.layered.css` stylesheet export** — identical to `theme.css` but
192
+ wrapped in a `@layer crk` cascade layer, so consumer `className` / `classes.*`
193
+ overrides win deterministically regardless of bundler import order. Import
194
+ exactly one of the two.
195
+ - **`Form` props** `reserveErrorSpace` (form-wide default for all fields) and
196
+ `fieldClasses` (form-wide `classes` default, merged with each field's own).
197
+ - **`FormField` prop** `reserveErrorSpace` (`boolean | number | string`) and a
198
+ `--crk-field-error-min-height` token to size the reserved space.
199
+ - **`Popover` prop** `autoPosition` (default `true`) for viewport-aware flipping
200
+ and edge shifting.
201
+ - **`Link` prop** `hideExternalIcon` to suppress the new-tab icon.
202
+ - `FormFieldClasses` gained a `message` key (the validation/reserve slot).
203
+
204
+ ### Changed
205
+
206
+ - `FormField` validation layout reworked so the field's outer height stays
207
+ constant when a one-line error toggles (the reserved spacer sits after the
208
+ description and swaps 1:1 with the error). Multi-line errors grow past it.
209
+ - Form-wide reservation off (`<Form reserveErrorSpace={false}>`) uses a slightly
210
+ larger inter-field gap to compensate for the absent reserved line.
211
+
212
+ ### Fixed
213
+
214
+ - **Searchable single-select** dropdown was mispositioned / rendered as a ~1px
215
+ sliver in portal mode because it anchored to the trigger button (hidden while
216
+ open); it now anchors to the always-visible wrapper.
217
+ - **Native `Select`** silently dropped `onValueChange` and could trip React's
218
+ controlled-field warning when given `value` + `onValueChange`; it now wires
219
+ both `onChange` and `onValueChange`.
220
+
221
+ ### Docs
222
+
223
+ - `README`, `llms.txt`, `manifest.json`, and `AGENTS.md` updated for all of the
224
+ above, including a `theme.css` vs `theme.layered.css` comparison and the
225
+ cascade-layer caveat (an unlayered global reset also beats layered toolkit
226
+ styles — put resets in an earlier layer).
package/README.md CHANGED
@@ -28,22 +28,97 @@ Override `--crk-*` CSS custom properties to customize the theme:
28
28
  }
29
29
  ```
30
30
 
31
+ For per-element tweaks, every component accepts `className` and a `classes` prop
32
+ (internal class names are hashed — never target them directly).
33
+
34
+ ### `theme.css` vs `theme.layered.css`
35
+
36
+ The package ships two stylesheets with **identical content** — same rules, same
37
+ hashed class names, same `--crk-*` tokens. The only difference is that
38
+ `theme.layered.css` wraps everything in a CSS cascade layer:
39
+
40
+ ```css
41
+ @layer crk {
42
+ /* ...the entire stylesheet... */
43
+ }
44
+ ```
45
+
46
+ That wrapper changes how your overrides compete with the toolkit's styles.
47
+ Import **exactly one** of the two (never both).
48
+
49
+ **The problem with plain `theme.css`.** The overrides you pass via `className` /
50
+ `classes.*` are plain CSS classes, so they tie with the toolkit's own class on
51
+ specificity — the winner is decided by **stylesheet source order**, which your
52
+ bundler controls. In many setups the toolkit CSS is injected *after* your app
53
+ CSS, so your overrides silently lose.
54
+
55
+ **What the layer fixes.** A core rule of cascade layers: **any unlayered style
56
+ beats any layered style, regardless of specificity or import order.** Since
57
+ `theme.layered.css` puts the whole toolkit in the `crk` layer, your own
58
+ (unlayered) rules — including every `className` / `classes.*` override — always
59
+ win, deterministically:
60
+
61
+ ```ts
62
+ import '@overdoser/react-toolkit/theme.layered.css'; // instead of theme.css
63
+ ```
64
+
65
+ (If you import CSS from a `.css` entry instead of JS, the equivalent is
66
+ `@import '@overdoser/react-toolkit/theme.css' layer(crk);` as the first line.)
67
+
68
+ | | `theme.css` | `theme.layered.css` |
69
+ | --- | --- | --- |
70
+ | Override wins by | specificity, then import order (bundler-dependent) | **always** (unlayered beats layered) |
71
+ | `--crk-*` token theming | ✅ | ✅ (identical) |
72
+ | Affected by a global CSS reset | only via normal specificity/order | ⚠️ an *unlayered* reset also beats the toolkit's base styles |
73
+
74
+ **The one caveat.** Because layered styles lose to *all* unlayered styles, an
75
+ aggressive global reset (e.g. `button { font: inherit }`) would also override the
76
+ toolkit's base styles under `theme.layered.css`. If you ship a heavy reset, put
77
+ it in its own earlier layer so the order is explicit:
78
+
79
+ ```css
80
+ @layer reset, crk;
81
+ ```
82
+
83
+ **Rule of thumb:** if you heavily customize component internals via
84
+ `classes` / `className`, use `theme.layered.css`. Otherwise `theme.css` is fine.
85
+ Theme-token (`--crk-*`) overrides work the same in both.
86
+
31
87
  ## Components
32
88
 
33
89
  | Component | Description |
34
90
  | --- | --- |
35
91
  | **Button** | Variants: `primary`, `secondary`, `danger`, `ghost`. Multiple sizes. Loading states with `dots`, `shimmer`, and `border` animations. |
36
- | **Link** | Styled link with variants and external link support. |
92
+ | **Link** | Styled link with variants and external link support (inline new-tab icon, toggleable). |
37
93
  | **Typography** | Renders `h1`-`h6`, `p`, `span`, `label`. Supports `weight`, `color`, `align`, and `truncate`. |
38
94
  | **List / ListItem** | Ordered and unordered lists with configurable spacing. |
39
95
  | **Table** | Sortable columns, multi-sort with Ctrl+click, pagination, and server-side sort support. |
40
96
  | **Dropdown** | Menu dropdown with chevron indicator. Also works as a selectable form input with `options`, `value`, and `onChange`. |
41
- | **Popover** | Positioned popover anchored to a trigger element. |
97
+ | **Popover** | Positioned popover anchored to a trigger element, with viewport-aware auto-flip. |
42
98
  | **Modal** | Portal-based modal with `Header`, `Body`, and `Footer` compound components. Includes focus trap and escape/backdrop close. |
99
+ | **Draggable** | Free-move container for any content. Optional `Draggable.Handle`, axis lock, viewport/parent bounds, keyboard moves. |
100
+ | **Toast** | Imperative notifications: `ToastProvider` renders, standalone `toast()` (or `useToast()`) triggers. Variants, positions, auto-dismiss with hover-pause. |
101
+ | **Tabs** | Accessible tabs (`Tabs.List` / `Tabs.Tab` / `Tabs.Panel`). Line/solid/pill variants, vertical, keyboard nav. |
102
+ | **Alert** | Inline status message (info/success/warning/danger) with optional title, icon, and dismiss. |
103
+ | **Badge** | Small status pill. Variants, `soft`/`solid`/`outline` appearance, sizes, optional status dot. |
104
+ | **Spinner** | Indeterminate loading spinner; inherits `currentColor`. |
105
+ | **Skeleton** | Loading placeholder — text (multi-line), circle, or rect; pulse/wave animation. |
106
+ | **Drawer** | Off-canvas panel sliding from any edge. `Header`/`Body`/`Footer`, focus trap, Escape/backdrop close. |
107
+ | **Tooltip** | Lightweight hover/focus label with open delay and viewport-aware flipping. |
108
+ | **Avatar** | Image with initials/icon fallback, sizes, circle/square, presence dot. |
109
+ | **Divider** | Horizontal/vertical separator, optional centered label. |
110
+ | **Breadcrumbs** | Breadcrumb nav with auto separators and `aria-current`. |
111
+ | **Pagination** | Windowed page nav with ellipses (`1 … 6 7 8 9 10 … 99`), prev/next/first/last. |
112
+ | **Slider** | Single or dual-thumb range input; pointer + keyboard. |
113
+ | **NumberInput** | Numeric field with steppers, min/max clamp, arrow-key step. |
114
+ | **Dropzone** | File upload via drag-drop or click, with `accept`/`maxSize` validation. |
115
+ | **Timer** | Countdown/stopwatch, digital or ring, attachable label, imperative controls. |
116
+ | **DatePicker** | Date field with a pop-up calendar, keyboard nav, min/max and disabled days. |
43
117
  | **Form / FormField** | Form wrapper with react-hook-form integration. |
44
118
  | **Input** | Text input with sizes, error state, and prefix/suffix slots. |
45
119
  | **Select** | Native `<select>` with a custom arrow indicator. |
46
- | **Checkbox** | Checkbox with label and indeterminate state support. |
120
+ | **Checkbox** | Checkbox with label and indeterminate state support; `variant="switch"` for a toggle switch. |
121
+ | **CheckboxGroup** | Multi-value selection as a checkbox list or toggleable chips. |
47
122
  | **Radio / RadioGroup** | Context-based radio group. |
48
123
  | **Textarea** | Textarea with resize control. |
49
124
 
@@ -95,6 +170,30 @@ function LoginForm() {
95
170
  }
96
171
  ```
97
172
 
173
+ ### Toast
174
+
175
+ Mount `ToastProvider` once (it's the renderer), then fire notifications with the
176
+ standalone `toast()` — no hook or context required:
177
+
178
+ ```tsx
179
+ import { ToastProvider, toast, Button } from '@overdoser/react-toolkit';
180
+
181
+ function App() {
182
+ return (
183
+ <ToastProvider position="top-right">
184
+ <Button onClick={() => toast({ variant: 'success', title: 'Saved', description: 'All set.' })}>
185
+ Save
186
+ </Button>
187
+ </ToastProvider>
188
+ );
189
+ }
190
+ ```
191
+
192
+ `toast()` is importable and callable from anywhere (event handlers, utilities,
193
+ outside React). It returns an id and exposes `toast.dismiss(id)` /
194
+ `toast.dismissAll()`. A `useToast()` hook returning the same
195
+ `{ toast, dismiss, dismissAll }` is also available if you prefer.
196
+
98
197
  ## Peer Dependencies
99
198
 
100
199
  - `react` >= 18
@@ -0,0 +1,34 @@
1
+ import { ComponentPropsWithRef, ReactNode } from 'react';
2
+ export type AlertVariant = 'info' | 'success' | 'warning' | 'danger';
3
+ export interface AlertClasses {
4
+ root: string;
5
+ icon: string;
6
+ content: string;
7
+ title: string;
8
+ message: string;
9
+ close: string;
10
+ }
11
+ export interface AlertProps extends Omit<ComponentPropsWithRef<'div'>, 'title'> {
12
+ /** Color/semantics. @default 'info' */
13
+ variant?: AlertVariant;
14
+ /** Optional bold heading above the message. */
15
+ title?: ReactNode;
16
+ /** Leading status icon. Pass a node to override, or `false` to hide. @default true */
17
+ icon?: ReactNode | boolean;
18
+ /** When provided, renders a dismiss "×" button that calls this handler. */
19
+ onClose?: () => void;
20
+ /** Accessible label for the dismiss button. @default 'Dismiss' */
21
+ closeLabel?: string;
22
+ /** Override class names on internal elements. */
23
+ classes?: Partial<AlertClasses>;
24
+ }
25
+ /**
26
+ * Inline status message — info, success, warning, or danger. Optionally titled,
27
+ * dismissible, and with a leading icon.
28
+ *
29
+ * @example
30
+ * <Alert variant="success" title="Saved" onClose={() => setShown(false)}>
31
+ * Your changes were saved.
32
+ * </Alert>
33
+ */
34
+ export declare const Alert: import('react').ForwardRefExoticComponent<Omit<AlertProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,2 @@
1
+ export { Alert } from './Alert';
2
+ export type { AlertProps, AlertClasses, AlertVariant } from './Alert';
@@ -0,0 +1,36 @@
1
+ import { ComponentPropsWithRef, ReactNode } from 'react';
2
+ export type AvatarStatus = 'online' | 'offline' | 'away' | 'busy';
3
+ export interface AvatarClasses {
4
+ root: string;
5
+ image: string;
6
+ fallback: string;
7
+ status: string;
8
+ }
9
+ export interface AvatarProps extends Omit<ComponentPropsWithRef<'span'>, 'children'> {
10
+ /** Image URL. If it fails to load (or is omitted), the initials/icon fallback shows. */
11
+ src?: string;
12
+ /** Alt text for the image and accessible name for the fallback. */
13
+ alt?: string;
14
+ /** Full name; used to derive initials when no image is available. */
15
+ name?: string;
16
+ /** Explicit fallback content (overrides derived initials). */
17
+ fallback?: ReactNode;
18
+ /** Avatar size. @default 'md' */
19
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
20
+ /** Outer shape. @default 'circle' */
21
+ shape?: 'circle' | 'square';
22
+ /** Presence indicator dot. */
23
+ status?: AvatarStatus;
24
+ /** Override class names on internal elements. */
25
+ classes?: Partial<AvatarClasses>;
26
+ }
27
+ /**
28
+ * User avatar with image, initials fallback, and an optional presence dot.
29
+ * Falls back to initials (from `name`) or a generic icon if the image is
30
+ * missing or fails to load.
31
+ *
32
+ * @example
33
+ * <Avatar src="/me.jpg" name="Ada Lovelace" />
34
+ * <Avatar name="Ada Lovelace" status="online" />
35
+ */
36
+ export declare const Avatar: import('react').ForwardRefExoticComponent<Omit<AvatarProps, "ref"> & import('react').RefAttributes<HTMLSpanElement>>;
@@ -0,0 +1,2 @@
1
+ export { Avatar } from './Avatar';
2
+ export type { AvatarProps, AvatarClasses, AvatarStatus } from './Avatar';
@@ -0,0 +1,25 @@
1
+ import { ComponentPropsWithRef } from 'react';
2
+ export interface BadgeClasses {
3
+ root: string;
4
+ dot: string;
5
+ }
6
+ export interface BadgeProps extends ComponentPropsWithRef<'span'> {
7
+ /** Color intent. @default 'neutral' */
8
+ variant?: 'neutral' | 'primary' | 'success' | 'warning' | 'danger';
9
+ /** Fill style — `soft` (tinted), `solid` (filled), or `outline`. @default 'soft' */
10
+ appearance?: 'soft' | 'solid' | 'outline';
11
+ /** Badge size. @default 'md' */
12
+ size?: 'sm' | 'md' | 'lg';
13
+ /** Render a leading status dot. @default false */
14
+ dot?: boolean;
15
+ /** Override class names on internal elements. */
16
+ classes?: Partial<BadgeClasses>;
17
+ }
18
+ /**
19
+ * Small inline status pill for labels, counts, and states.
20
+ *
21
+ * @example
22
+ * <Badge variant="success" dot>Active</Badge>
23
+ * <Badge variant="primary" appearance="solid">3</Badge>
24
+ */
25
+ export declare const Badge: import('react').ForwardRefExoticComponent<Omit<BadgeProps, "ref"> & import('react').RefAttributes<HTMLSpanElement>>;
@@ -0,0 +1,2 @@
1
+ export { Badge } from './Badge';
2
+ export type { BadgeProps, BadgeClasses } from './Badge';
@@ -0,0 +1,44 @@
1
+ import { ComponentPropsWithRef, ReactNode } from 'react';
2
+ export interface BreadcrumbsClasses {
3
+ root: string;
4
+ list: string;
5
+ item: string;
6
+ link: string;
7
+ current: string;
8
+ separator: string;
9
+ }
10
+ export interface BreadcrumbsProps extends Omit<ComponentPropsWithRef<'nav'>, 'children'> {
11
+ /** `Breadcrumbs.Item` children. Separators are inserted automatically. */
12
+ children?: ReactNode;
13
+ /** Separator between items. @default '/' */
14
+ separator?: ReactNode;
15
+ /** Accessible label for the nav landmark. @default 'Breadcrumb' */
16
+ label?: string;
17
+ /** Override class names on internal elements. */
18
+ classes?: Partial<BreadcrumbsClasses>;
19
+ }
20
+ export interface BreadcrumbItemProps extends Omit<ComponentPropsWithRef<'li'>, 'children'> {
21
+ /** Link target. Omit for a plain (non-link) crumb. */
22
+ href?: string;
23
+ /** Marks the current page — rendered as text with `aria-current="page"`. @default false */
24
+ current?: boolean;
25
+ children?: ReactNode;
26
+ }
27
+ export declare function BreadcrumbItem({ href, current, children, className, ...rest }: BreadcrumbItemProps): import("react/jsx-runtime").JSX.Element;
28
+ export declare namespace BreadcrumbItem {
29
+ var displayName: string;
30
+ }
31
+ /**
32
+ * Breadcrumb navigation. Compose with `Breadcrumbs.Item`; the last item is
33
+ * typically `current`. Separators are inserted automatically.
34
+ *
35
+ * @example
36
+ * <Breadcrumbs>
37
+ * <Breadcrumbs.Item href="/">Home</Breadcrumbs.Item>
38
+ * <Breadcrumbs.Item href="/library">Library</Breadcrumbs.Item>
39
+ * <Breadcrumbs.Item current>Data</Breadcrumbs.Item>
40
+ * </Breadcrumbs>
41
+ */
42
+ export declare const Breadcrumbs: import('react').ForwardRefExoticComponent<Omit<BreadcrumbsProps, "ref"> & import('react').RefAttributes<HTMLElement>> & {
43
+ Item: typeof BreadcrumbItem;
44
+ };
@@ -0,0 +1,2 @@
1
+ export { Breadcrumbs, BreadcrumbItem } from './Breadcrumbs';
2
+ export type { BreadcrumbsProps, BreadcrumbItemProps, BreadcrumbsClasses } from './Breadcrumbs';
@@ -0,0 +1,28 @@
1
+ import { ComponentPropsWithRef, ReactNode } from 'react';
2
+ export interface DividerClasses {
3
+ root: string;
4
+ line: string;
5
+ label: string;
6
+ }
7
+ export interface DividerProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
8
+ /** Layout direction. @default 'horizontal' */
9
+ orientation?: 'horizontal' | 'vertical';
10
+ /** Line style. @default 'solid' */
11
+ variant?: 'solid' | 'dashed' | 'dotted';
12
+ /** Optional centered label (horizontal only). Renders a line–label–line layout. */
13
+ label?: ReactNode;
14
+ /** Label alignment when `label` is set. @default 'center' */
15
+ labelPosition?: 'start' | 'center' | 'end';
16
+ /** Override class names on internal elements. */
17
+ classes?: Partial<DividerClasses>;
18
+ }
19
+ /**
20
+ * Visual separator between content. Horizontal or vertical, with an optional
21
+ * centered label.
22
+ *
23
+ * @example
24
+ * <Divider />
25
+ * <Divider label="OR" />
26
+ * <Divider orientation="vertical" />
27
+ */
28
+ export declare const Divider: import('react').ForwardRefExoticComponent<Omit<DividerProps, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,2 @@
1
+ export { Divider } from './Divider';
2
+ export type { DividerProps, DividerClasses } from './Divider';