@usableapp/cardds 0.2.1 → 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.
- package/README.md +34 -104
- package/cardds.js +6 -445
- package/css/actions.css +3 -8
- package/css/card.css +2 -2
- package/css/sheet.css +26 -9
- package/css/step.css +6 -6
- package/css/tokens.css +2 -0
- package/dist/actions/Dropdown.d.ts +34 -17
- package/dist/actions/Dropdown.js +41 -11
- package/dist/actions/IconBtn.d.ts +296 -9
- package/dist/actions/IconBtn.js +6 -8
- package/dist/cardds.css +39 -25
- package/dist/choice/Calendar.d.ts +17 -6
- package/dist/choice/Calendar.js +9 -3
- package/dist/choice/Slider.d.ts +1 -1
- package/dist/choice/Slider.js +12 -8
- package/dist/forms/Pin.d.ts +9 -6
- package/dist/forms/Pin.js +47 -18
- package/dist/media/Postcard.d.ts +8 -4
- package/dist/media/Postcard.js +8 -3
- package/dist/numbers/Picker.d.ts +19 -7
- package/dist/numbers/Picker.js +51 -13
- package/dist/scaffold/BaseContent.d.ts +4 -4
- package/dist/scaffold/BaseContent.js +4 -4
- package/dist/sheets/Drawer.d.ts +14 -3
- package/dist/sheets/Drawer.js +29 -5
- package/dist/sheets/Sheet.d.ts +28 -15
- package/dist/sheets/Sheet.js +157 -11
- package/dist/sheets/SheetStack.d.ts +13 -1
- package/dist/sheets/SheetStack.js +74 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# cardds
|
|
2
2
|
|
|
3
|
+
> **cardds is a UI framework, not one project's design system.** A project takes it and puts its own values and
|
|
4
|
+
> content into the UI — tokens (`theme.css`), props, slots, composition — without touching cardds. When a project
|
|
5
|
+
> cannot get what it needs from outside, that is a gap in the framework: report it; it is closed here as a
|
|
6
|
+
> GENERAL extension point, never as that project's special case. A consumer project never edits or releases cardds.
|
|
7
|
+
>
|
|
3
8
|
> **For:** people and AI alike — this is the reference, the one place a rule of the system is written.
|
|
4
9
|
> The other docs only point here: `CLAUDE.md` (AI working IN this repo — decisions and gotchas),
|
|
5
10
|
> `docs/guides/building-with-cardds.md` (AI composing screens in React; shipped to Claude Design),
|
|
@@ -9,8 +14,7 @@ Card-first mobile design system, **React-first**: `src/` is the component
|
|
|
9
14
|
library — one thin component per pattern, emitting exactly the markup the CSS
|
|
10
15
|
documents. The CSS (`css/*.css`)
|
|
11
16
|
stays the only truth: a component never styles anything, it only picks classes
|
|
12
|
-
from props. The
|
|
13
|
-
class names rather than components. The demo is the gallery (`npm run dev`):
|
|
17
|
+
from props. The demo is the gallery (`npm run dev`):
|
|
14
18
|
every component's stories, live, searchable.
|
|
15
19
|
|
|
16
20
|
Repo: **https://github.com/everysundays/cardds** (private — this is the
|
|
@@ -49,21 +53,9 @@ inlines the 56 symbols of `icons.svg` at build time, so no sprite file ships.
|
|
|
49
53
|
aliased to `src/`, so the gallery and the package share one truth with no build between them.
|
|
50
54
|
`/` every story of every component, `/:Name` one component — each `.design-sync/previews/<Name>.tsx`
|
|
51
55
|
story live in a 375×812 phone cell, grouped by `src/<group>/`, searchable; switches for the palette
|
|
52
|
-
(`own` = a colour well per colour base), inspect (box outlines + dimensions),
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
**CSS only** (no React, no build):
|
|
56
|
-
|
|
57
|
-
```html
|
|
58
|
-
<link rel="stylesheet" href="cardds.css">
|
|
59
|
-
<script src="cardds.js" defer></script> <!-- optional: the behaviours above -->
|
|
60
|
-
<html data-palette="clay"> <!-- omit for b&w wireframe -->
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Everything renders without `cardds.js`: a dropdown still opens (a native
|
|
64
|
-
popover) but won't update its label, a stack still displays but won't open, a
|
|
65
|
-
sheet renders every state but only moves when a class changes, a pin is six
|
|
66
|
-
plain inputs.
|
|
56
|
+
(`own` = a colour well per colour base), inspect (box outlines + dimensions), a mock keyboard (focus a field:
|
|
57
|
+
it comes up over the phone cell and publishes `--kb` there — to SEE what a keyboard leaves of the screen; it
|
|
58
|
+
types nothing), text size, and a slider per size base. Nothing is hand-listed: add a preview file or a story and it shows.
|
|
67
59
|
|
|
68
60
|
`tests/fixtures/*.html` are the pages the geometry tests measure — not a demo. Nothing here is verified by eye.
|
|
69
61
|
|
|
@@ -82,38 +74,6 @@ import '@usableapp/cardds/dist/cardds.css'; // the CSS, fonts resolve from the
|
|
|
82
74
|
```
|
|
83
75
|
|
|
84
76
|
|
|
85
|
-
**CSS only:** there's no build and no dependencies, so installing is cloning once, then
|
|
86
|
-
copying the static files into your project — never edit the clone, never
|
|
87
|
-
fork the CSS:
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
# once, wherever you keep it (~/Sites/cardds is the convention on this machine)
|
|
91
|
-
git clone git@github.com:everysundays/cardds.git ~/Sites/cardds
|
|
92
|
-
# to update it later
|
|
93
|
-
git -C ~/Sites/cardds pull
|
|
94
|
-
|
|
95
|
-
# then vendor the static files into the consuming project
|
|
96
|
-
cp -R ~/Sites/cardds/css ~/Sites/cardds/fonts ~/Sites/cardds/cardds.css \
|
|
97
|
-
~/Sites/cardds/cardds.js ~/Sites/cardds/icons.svg \
|
|
98
|
-
./assets/
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
`cardds.css` is just the import bundle for `css/*.css`, so keep the `css/`
|
|
102
|
-
folder next to it, and `fonts/` beside that (`css/fonts.css` reaches the faces
|
|
103
|
-
as `../fonts/`). `icons.svg` is referenced by path from your markup
|
|
104
|
-
(`<use href="icons.svg#bell">`), so put it where those references resolve.
|
|
105
|
-
Then follow the rules below — the short version is: tune your own `theme.css`
|
|
106
|
-
(a copy of `css/theme.template.css`, see "A project's theme") and nothing else, and never write a raw hex or px in a component.
|
|
107
|
-
|
|
108
|
-
The repo is private, so cloning needs an account with access (`everysundays`
|
|
109
|
-
on GitHub) — set that up as an SSH host alias if you already use a different
|
|
110
|
-
account for `git@github.com` day to day, the way this machine's `~/.ssh/config`
|
|
111
|
-
does it for other `everysundays` repos.
|
|
112
|
-
|
|
113
|
-
**Record what you copied.** A vendored copy with no record of the commit it came
|
|
114
|
-
from can't be told apart from a fork: write the commit hash (and a sha256 per
|
|
115
|
-
file) beside the copy, so a hand edit or a stale copy can be caught.
|
|
116
|
-
|
|
117
77
|
Working with Claude in another project? The `/cardds` skill
|
|
118
78
|
(`~/.claude/skills/cardds/`) carries the install and the contract, and points
|
|
119
79
|
back here. It is deliberately thin — **this repo stays the source of truth**,
|
|
@@ -125,10 +85,10 @@ if the contract itself changes.
|
|
|
125
85
|
`src/<group>/<Name>.tsx`, one export per class family; sub-parts are their own
|
|
126
86
|
components (`CardHead`, `KvRow`, `TrackStep`, `SheetBody`). A component
|
|
127
87
|
chooses classes from props (`tone`, `state`, `primary`, `sm`…) and lays the
|
|
128
|
-
contract's children in order
|
|
129
|
-
(
|
|
130
|
-
|
|
131
|
-
|
|
88
|
+
contract's children in order. A component that has STATE is controlled like an input
|
|
89
|
+
(`state` / `open` / `value` / `flipped` / `selected` + its `on…`, or a `default…` twin), keeps its own behaviour
|
|
90
|
+
inside itself and only ever asks through that callback; it never knows another component — the app wires them
|
|
91
|
+
(2026-09-21; `cardds.js` is down to `--kb`). Never fix a look in a wrapper: fix the CSS, the
|
|
132
92
|
wrapper follows. **Claude Design** builds with these components through
|
|
133
93
|
`/design-sync` (`.design-sync/`: the sync config, the authored preview
|
|
134
94
|
stories, the conventions the design agent reads) — a new component needs a
|
|
@@ -190,7 +150,7 @@ whose markup has rules, because the sheet's box is the step itself:
|
|
|
190
150
|
<!-- acts: the words + a sheet -->
|
|
191
151
|
<section class="step">
|
|
192
152
|
<section class="base-content">…the words…</section>
|
|
193
|
-
<article class="card sheet sheet--half"
|
|
153
|
+
<article class="card sheet sheet--half">
|
|
194
154
|
<button class="sheet__handle" type="button" aria-label="ปรับความสูง"></button>
|
|
195
155
|
<div class="card__head"><h2 class="t-h2">…</h2><span class="chip">…</span></div>
|
|
196
156
|
<div class="sheet__body">
|
|
@@ -233,17 +193,24 @@ whose markup has rules, because the sheet's box is the step itself:
|
|
|
233
193
|
- **The bar is outside the step**, after it. `.step` reads the bar's room
|
|
234
194
|
from `--bar-reserve`; any parent that hosts the `.action-bar` counts
|
|
235
195
|
(`<body>`, an app shell, a demo frame).
|
|
196
|
+
- **Every sheet has one shape** (Lh 2026-09-21): the handle and the head stay at the top and never scroll · the
|
|
197
|
+
foot's button stands at the bottom — above the keyboard when there is one (`--kb`: a step gives the keyboard
|
|
198
|
+
its room, a stage sheet gives it up itself as `--_lift`) · everything between them scrolls, as far as it
|
|
199
|
+
needs to: the body's bottom padding is the part that slid under (`--_cover`), so the last row comes up to
|
|
200
|
+
the button at half and 3q as it does at full.
|
|
236
201
|
- **Layers: the base, the sheet over it, the top bar** (Lh 2026-09-20). The `.action-bar` belongs to the
|
|
237
202
|
BASE — the move of a page of words (a pager, or one wide button). It is never in front of a sheet. A
|
|
238
203
|
screen with a sheet finishes ON the sheet: a `.card__foot`, the sheet's last child — ONE row, the page's
|
|
239
204
|
conclusion. The foot stands on the screen's bottom edge at half, 3q and full alike (a layer as tall as
|
|
240
205
|
the sheet, slid up by the share the sheet slid down — `sheet.css`), and is gone at peek. Nothing inside
|
|
241
|
-
the sheet's body commits on its own (a `Composer` there takes no `send`).
|
|
242
|
-
|
|
206
|
+
the sheet's body commits on its own (a `Composer` there takes no `send`). This is how cardds's own screens are
|
|
207
|
+
composed — guidance; the framework does not enforce it: a component never changes because another one is there.
|
|
243
208
|
- **Never write a sheet's height, and never a `.sheet-stage` inside a step.**
|
|
244
209
|
State is a class (`sheet--peek` / `--half` / `--full`); the sheet is always
|
|
245
|
-
the step's full height and only slides.
|
|
246
|
-
|
|
210
|
+
the step's full height and only slides. In React the APP owns it: `<Sheet state={s}
|
|
211
|
+
onStateChange={setS}>` (controlled) or `<Sheet defaultState="half">` (it keeps its own). The sheet's own behaviour —
|
|
212
|
+
the handle's tap and drag, the focus raise, the climb when the keyboard moves the cut — lives in the component and
|
|
213
|
+
only ever ASKS through `onStateChange(state, reason)`; nothing outside the sheet is touched.
|
|
247
214
|
|
|
248
215
|
## Centred card markup
|
|
249
216
|
|
|
@@ -291,56 +258,19 @@ between at 40% down the screen (see *Anatomy*, "Where the centre is"):
|
|
|
291
258
|
- **BaseContent that moves** (Lh 2026-09-20) — two ways, never both on one screen:
|
|
292
259
|
- *Pages of words:* `.step > .pages > .base-content` (one `aria-current="step"`, the rest `inert`) — every page
|
|
293
260
|
one height, the round pager turns them (`slideTo()`), the words slide like a page. No sheet there.
|
|
294
|
-
- *
|
|
295
|
-
sheet
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
261
|
+
- *Sheets the app calls:* the base offers WAYS IN — sign in · new phone · join — as buttons (`.cta-pack`); each
|
|
262
|
+
way has a sheet that holds its WHOLE form and its own submit on its foot, starting `.sheet--away` (not on the
|
|
263
|
+
screen: slid out, hidden once gone, `inert`). **The wiring is the app's, not cardds's**: a `useState` of which
|
|
264
|
+
sheet is on and where, each button sets it, each `<Sheet state onStateChange>` reads it — one at a time, the same
|
|
265
|
+
button raises a peeked sheet, the handle stops at peek (`states="peek full"`). Story `Step · Calls` is those few
|
|
266
|
+
lines; it could as well be a route, which is what makes the phone's back button close a sheet. CSS's only part:
|
|
267
|
+
behind a peeked sheet the words keep `--sheet-peek` of room (the box declares the token), so the buttons under
|
|
268
|
+
it scroll clear.
|
|
301
269
|
**Never a form taken apart** — one sheet per field, a "send" left on the base: a form lives whole in ONE sheet.
|
|
302
270
|
Fixture `tests/fixtures/calls.html`, tests `tests/calls.spec.js`, stories `Step · Walks` / `Step · Calls`.
|
|
303
271
|
- **A pager turns pages of words, never a screen with a sheet** (Lh 2026-09-20). A
|
|
304
272
|
sheet means this screen has work that cannot be walked past: no ← →, no bar at all — the sheet's
|
|
305
|
-
own foot finishes it.
|
|
306
|
-
down while the sheet is on the screen, a peeking one included: dimmed, no pointer
|
|
307
|
-
(`actions.css`), `inert` (`cardds.js`).
|
|
308
|
-
|
|
309
|
-
## Files
|
|
310
|
-
|
|
311
|
-
| file | what | touch it? |
|
|
312
|
-
|---|---|---|
|
|
313
|
-
| `css/fonts.css` + `fonts/` | the two `@font-face`s: PK Nonthaburi (body, `--font-ui`), FC Pride (display, `--font-display`); the font files and their licences | when swapping a face |
|
|
314
|
-
| `css/tokens.css` | spacing, radius, type levels, motion (`--motion-sheet`), the human centre (`--screen-centre`), grayscale role defaults; registers the two sheet tokens (`--sheet-peek`, `--sheet-half`) so script can read them resolved | rarely |
|
|
315
|
-
| `css/theme.template.css` | **a project's theme** — the four size bases + the colour roles, at their defaults; NOT in the bundle: copied into the project as its `theme.css`, loaded after `cardds.css` | copy it, don't edit it |
|
|
316
|
-
| `css/palette.css` | **color settings — palettes as role-token overrides** | yes, this one |
|
|
317
|
-
| `css/base.css` | reset, surface, typography classes, `.base-content` | rarely |
|
|
318
|
-
| `css/card.css` | `.card` + its skeleton (`.card__head` / content / `.card__foot`), `.card-list`, `.card--row`, `.icon-row`, stats, `.card--centre` (one card at the human centre of a fill screen) and `.screen__centre` (a small group there) | when adding variants |
|
|
319
|
-
| `css/stack.css` | **sheet stack** — `.sheet-stack`: the menu, sheets stacked (heads peek, up to 5; `--tap` tap-to-open, the back button dissolves in; `--closed` nothing open, heads at the bottom, the base above (`.sheet-stack__base`); `--display` the title is the peek, `--fanned` wider peeks); always edge to edge; a stacked sheet is a `.card` | rarely |
|
|
320
|
-
| `css/sheet.css` | **sheet single** — `.sheet`: a card as bottom sheet, full height always, a state is how far it slid: `--peek` / `--half` / `--full` (translate only); handle · head · `.sheet__body` (the head pinned, the body scrolls); `.sheet-stage` (overlay stage: map sheet, a `__hero` — one thing centred in the band a half sheet leaves open, `--dim`, `--raised`, ask drawer) | when adding states |
|
|
321
|
-
| `css/step.css` | `.step` — one screen, one task: `.base-content` (reads) + optional `.sheet` (acts), the bar's room, the keyboard's room | rarely |
|
|
322
|
-
| `css/forms.css` | `.field` (outlined input, label = placeholder), `.add-row` | when adding controls |
|
|
323
|
-
| `css/journey.css` | `.route`, `.tile-badge`, `.note-row` — trip/status primitives | when adding variants |
|
|
324
|
-
| `css/actions.css` | topbar, chips, buttons (`.btn--xl`), `.dropdown` (the standard select: pill + `.menu` popover), `.action-bar` (+ `--pager` for previous/next, two round icon buttons, never labels; `--tiers` for a control row above the buttons), `.fab` | when adding actions |
|
|
325
|
-
| `css/numbers.css` | `.ring` gauge, `.track` steps, `.card--band` + `.band-stack`, `.dotgrid`, `.picker`, `.badge`, `.bars` | big numbers |
|
|
326
|
-
| `css/people.css` | `.avatar` (sm/lg/xl, outline, add, on, halo), `.avatar-stack`, `.avatar-pick` | people |
|
|
327
|
-
| `css/choice.css` | `.check`, `.toggle`, `.chip-grid` + `.chip--pick` (+ `--sign`), `.day-strip`, `.calendar`, `.mood`, `.pin`, `.composer`, `.slider` | choice controls |
|
|
328
|
-
| `css/lists.css` | `.row` / `.rows` (frameless rows in a card), `.kv`, `.kv-grid`, `.thread` + `.bubble` (chat: theirs left, mine right, no tail), `.timeline`, `.legend`, `.link` | lists inside cards |
|
|
329
|
-
| `css/media.css` | `.card--cover`, `.quote`, `.tile-grid` + `.tile`, `.card--fold`, `.mosaic`, `.wave` | media & display |
|
|
330
|
-
| `css/layover.css` | `.modal` (the ask: `__lift` + `__drawer`, closes on any drawer button → `cardds:modal`), `.menu` (popover), `.banner` (details), `.float-bar`, `.callout`, `.deck` | cards over content |
|
|
331
|
-
| `icons.svg` | Lucide sprite (ISC), 56 minimal stroke icons | add symbols as needed |
|
|
332
|
-
| `src/` → `dist/` | the React library: `src/<group>/<Name>.tsx`, one thin component per pattern, JSDoc = the rule; built by `npm run build` (tsc) into `dist/` with `.d.ts`; `src/type/icons.ts` is generated from `icons.svg` | when adding a component |
|
|
333
|
-
| `gallery/` | the gallery site: `npm run dev` → `/` every story, `/:Name` one component (`gallery/src/Gallery.tsx` globs `.design-sync/previews/`) | the demo |
|
|
334
|
-
| `tests/fixtures/` | the HTML pages (`index`, `sheet`, `stack`, `elements`) the geometry tests measure — ex `demo/`, not a demo | when a geometry test needs a new case |
|
|
335
|
-
| `docs/handoff/` | the Claude Design handoff spec (the Commons Time Bank app built from it was dropped 2026-09-16) | reference |
|
|
336
|
-
| `scripts/` | `build-icons.mjs` (icons.svg → `src/type/icons.ts`), `flatten-css.mjs` (`cardds.css` + imports → `dist/cardds.css`) | rarely |
|
|
337
|
-
| `docs/guides/` | `building-with-cardds.md` — the anatomy, the step, the two sheet systems and the vocabulary in React terms; shipped to Claude Design as its guidelines | when the contract changes |
|
|
338
|
-
| `cardds.js` | the behaviours (delegated, class/property toggles only — everything renders without it): dropdown value sync, `.sheet-stack--tap` open/close (tolerates one wrapper per sheet; close reads the tapped sheet), `.pin` auto-advance / Backspace / paste + `cardds:pin`, the sheet handle (tap / drag-snap / Enter / Space), the focus and keyboard raises (`--kb`), `cardds:sheet` | rarely |
|
|
339
|
-
|
|
340
|
-
## Anatomy — base, menus, content, and the card skeleton
|
|
341
|
-
|
|
342
|
-
Every screen is the same four things, and every card the same three:
|
|
343
|
-
|
|
273
|
+
own foot finishes it. (Guidance — not enforced.)
|
|
344
274
|
- A screen is **base, top menu, bottom menu, content**. The base is the
|
|
345
275
|
surface; the menus are the chrome above and below (`.topbar` / `.appbar`,
|
|
346
276
|
`.action-bar` / `.float-bar`); content is everything between.
|