@usableapp/cardds 0.7.72 → 0.7.73
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/CHANGELOG.md +17 -0
- package/README.md +5 -3
- package/bin/cardds.mjs +24 -8
- package/css/people.css +6 -0
- package/css/stamp.css +4 -0
- package/dist/actions/Chip.d.ts +7 -0
- package/dist/actions/Dropdown.d.ts +1 -0
- package/dist/actions/Fab.d.ts +3 -0
- package/dist/actions/IconBtnGroup.d.ts +1 -0
- package/dist/cardds.css +10 -0
- package/dist/cards/Card.d.ts +2 -0
- package/dist/cards/CardHead.d.ts +5 -0
- package/dist/cards/CardHead.js +5 -5
- package/dist/catalog.json +1 -1
- package/dist/choice/Calendar.d.ts +4 -2
- package/dist/choice/DayStrip.d.ts +1 -0
- package/dist/choice/Mood.d.ts +1 -0
- package/dist/choice/Slider.d.ts +2 -0
- package/dist/forms/Composer.d.ts +2 -0
- package/dist/forms/Field.d.ts +2 -0
- package/dist/forms/FileBtn.d.ts +1 -0
- package/dist/journey/NoteRow.d.ts +1 -0
- package/dist/journey/Route.d.ts +2 -0
- package/dist/layover/Callout.d.ts +4 -1
- package/dist/lists/Kv.d.ts +3 -0
- package/dist/lists/Row.d.ts +3 -1
- package/dist/media/Mosaic.d.ts +2 -0
- package/dist/media/Postcard.d.ts +4 -0
- package/dist/media/Tile.d.ts +6 -0
- package/dist/numbers/Bars.d.ts +1 -0
- package/dist/numbers/DotGrid.d.ts +1 -0
- package/dist/numbers/Picker.d.ts +1 -0
- package/dist/numbers/Track.d.ts +1 -0
- package/dist/people/Avatar.d.ts +7 -2
- package/dist/people/Avatar.js +4 -4
- package/dist/people/AvatarPick.d.ts +1 -0
- package/dist/post/PostLetter.d.ts +2 -1
- package/dist/scaffold/LayerHistory.d.ts +1 -0
- package/dist/scaffold/Screen.d.ts +1 -0
- package/dist/sheets/StackSheet.d.ts +2 -0
- package/dist/stamp/StampBook.d.ts +3 -1
- package/dist/stamp/StampBook.js +2 -2
- package/dist/templates/CallSheet.d.ts +2 -0
- package/dist/templates/Home.d.ts +2 -0
- package/dist/templates/Walkthrough.d.ts +5 -1
- package/lib/catalog.mjs +84 -21
- package/lib/mcp.mjs +23 -7
- package/lib/role.mjs +30 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
> For: a project that consumes `@usableapp/cardds` and its AI — what changed between versions, written as before → after, so a screen can be brought up to date without reading the source. Newest first. A consumer never edits cardds; if something here leaves you without a way to do what you did before, report the gap.
|
|
4
4
|
|
|
5
|
+
## 0.7.73 — 2026-10-08
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **`StampBook bare`: the album page with no ground** (Lh 2026-10-08) — three stamps across, lying on what holds the book and as large as it allows: each stamp is the whole width of its third, a small gap between. `<StampBook bare places={12}>`. The free `places` are still drawn. For the name, give the `Stamp` its `name` (it is drawn inside the stamp) and leave `StampCell label` out. The grounded page is as before.
|
|
9
|
+
- **`AvatarStack more`: the count that trails the stack, unframed and large** (Lh 2026-10-08) — `<AvatarStack more="+9">`: plain title-size words after the heads, readable at arm's length. Before → after: `<Avatar size="sm" on>+9</Avatar>` as the last child (a small accent round — still there, unchanged) → `more="+9"` when the number must be seen.
|
|
10
|
+
- **An `AvatarStack` holds `Stamp`s** — a `<Stamp bare>` in a stack has a width of its own (as tall as an `sm` avatar) and overlaps a little, like the heads. Before: the app had to set the stamp's width by hand.
|
|
11
|
+
- **The catalog says what a component is FOR** (Lh 2026-10-08) — each component has a role: what it represents (a person, a number, words…; for a control, the kind of data it carries), whether it shows one thing or a group (in brief, in detail), and what the member can do with it (pick · act · write · navigate, or nothing). `npx cardds get <Name>` prints it; `npx cardds list` groups by it; `npx cardds find` asks by it in any language: `npx cardds find --represents person --shape group [--interact pick|none] [words…]`. Over MCP, `find_components` takes `represents`, `shape`, `interact`. A screen already drawn matches when its parts together cover what was asked.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **`npx cardds find` that matches nothing answers with one line** — a hint to ask in English words or by the axes; it used to list every screen.
|
|
15
|
+
- **`npx cardds scaffold <id> --html` warns when the still picture is not the layout** — a screen with a `Sheet`, a `Step`, a `Modal`, a `Menu`… is placed by JavaScript when it runs, so its static HTML starts with a comment saying so (and the CLI says it on stderr): use the tsx there. `--css <href>` sets where `cardds.css` is from the file's place.
|
|
16
|
+
- **`npx cardds list` output** — `{ name, group, shape, interact, represents }` per component (`--json`); it was `{ name, group }`. The catalog lists only what the package exports (`PostCorner`, `FormNote` are gone from it).
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **`npx cardds … --root` takes an absolute path** — it was joined to the working folder and found no catalog.
|
|
20
|
+
- **`npx cardds … --root` (and the MCP pointed at a checkout) no longer blanks that checkout's running gallery** — the catalog's build shared the dev server's dependency cache.
|
|
21
|
+
|
|
5
22
|
## 0.7.72 — 2026-10-08
|
|
6
23
|
|
|
7
24
|
### Added
|
package/README.md
CHANGED
|
@@ -142,10 +142,12 @@ A session asked for a prototype need not read `src/`: the package ships a catalo
|
|
|
142
142
|
`npm run build`) of every component — props, slots — and every story with its code and its rendered HTML. Ask it first:
|
|
143
143
|
|
|
144
144
|
```bash
|
|
145
|
-
npx cardds find
|
|
145
|
+
npx cardds find [words…] [--represents person] [--shape group] [--interact pick|none]
|
|
146
|
+
# by the AXES first (docs/roles.md: what it stands for · one or a group · what the member can do), words second;
|
|
147
|
+
# the screens already drawn, then components, then what is used with them (nothing → a hint, not a list)
|
|
146
148
|
npx cardds scaffold <id> # a whole file from a screen: tsx (the code) or --html (a static document linking cardds.css)
|
|
147
|
-
npx cardds get <Name> [--html] # one component: props (a ReactNode prop is a slot) + variations with code
|
|
148
|
-
npx cardds list # every name
|
|
149
|
+
npx cardds get <Name> [--html] # one component: its role, props (a ReactNode prop is a slot) + variations with code
|
|
150
|
+
npx cardds list # every name by what it is for: one · group-brief · group-detail, the show-only first, then by what the member can do
|
|
149
151
|
npx cardds check <dir> # the rules, before a screen is called done
|
|
150
152
|
```
|
|
151
153
|
|
package/bin/cardds.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// `npx cardds init` — set a project up the ONE way (Lh 2026-09-29): the project's theme.css, the check script, the ESLint rules.
|
|
3
3
|
// `npx cardds check [dir…]` — the rules of lint/rules.mjs over the project's source; exit 1 on an error.
|
|
4
|
-
// `npx cardds list | get <Name> | find
|
|
4
|
+
// `npx cardds list | get <Name> | find [words…] [--represents a,b] [--shape s] [--interact k|none] | scaffold [id]` — the component catalog (lib/catalog.mjs) for an AI session; `npx cardds mcp` serves the same over MCP.
|
|
5
5
|
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
6
|
-
import { dirname, join, relative } from 'node:path';
|
|
6
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { checkSource } from '../lint/rules.mjs';
|
|
9
9
|
import * as catalog from '../lib/catalog.mjs';
|
|
@@ -70,15 +70,26 @@ import them, never copy their markup: the behaviour lives in the component. Gall
|
|
|
70
70
|
return 0;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// the exports beside the components that are not components (docs/roles.md rows `a function, not a component`…): shape `—` and nothing to tell them from a structural part, so they are named
|
|
74
|
+
const HELPERS = new Set(['applyMask', 'slideTo', 'useLayer', 'useLayers', 'useOptionalLayer', 'registerIcons', 'LayerHistory']);
|
|
73
75
|
const one = (text) => text.replace(/\s+/g, ' ').trim();
|
|
74
76
|
const indent = (text, by = ' ') => text.replace(/^(?=.)/gm, by);
|
|
75
77
|
|
|
76
78
|
// each catalog command: the function's result as compact text (--json prints the result itself)
|
|
77
79
|
const asText = {
|
|
78
|
-
list: (
|
|
80
|
+
list: (components) => {
|
|
81
|
+
const named = (c) => (c.represents.length ? `${c.name} (${c.represents.join('·')})` : c.name);
|
|
82
|
+
const row = (label, cs) => (cs.length ? [` ${label}${cs.map(named).join(' ')}`] : []);
|
|
83
|
+
const section = (shape, cs) => shape === '—'
|
|
84
|
+
? [...row('helpers: ', cs.filter((c) => HELPERS.has(c.name))), ...row('structure: ', cs.filter((c) => !HELPERS.has(c.name)))]
|
|
85
|
+
: [...row('show ', cs.filter((c) => !c.interact)), ...catalog.KINDS.flatMap((k) => row(k.padEnd(10), cs.filter((c) => c.interact && c.interact.includes(k))))];
|
|
86
|
+
return catalog.SHAPES.map((shape) => [shape, components.filter((c) => c.shape === shape)])
|
|
87
|
+
.filter(([, cs]) => cs.length).flatMap(([shape, cs]) => [shape, ...section(shape, cs)]).join('\n');
|
|
88
|
+
},
|
|
79
89
|
get: (c, { html }) => [
|
|
80
90
|
`${c.name} (${c.group})${c.element ? ` — forwards the <${c.element}> attributes` : ''}`,
|
|
81
91
|
one(c.description),
|
|
92
|
+
...(c.role ? [`role: represents ${[c.role.represents, c.role.count, c.role.density, c.role.job].flat().join(' · ')}${c.role.standsInFor.length ? `; stands in for: ${c.role.standsInFor.map((s) => (s.condition ? `${s.name} (${s.condition})` : s.name)).join('; ')}` : ''}`] : []),
|
|
82
93
|
'\nprops',
|
|
83
94
|
...c.props.map((p) => ` ${p.name}${p.optional ? '?' : ''}: ${p.type}${p.slot ? ' [slot]' : ''}${p.description ? ` — ${one(p.description)}` : ''}`),
|
|
84
95
|
`\nvariations (${c.variations.length})`,
|
|
@@ -86,27 +97,32 @@ const asText = {
|
|
|
86
97
|
...(c.alsoUsedIn ? [`\nalso used in: ${c.alsoUsedIn.join(' ')}\n(--all prints their code too)`] : []),
|
|
87
98
|
].join('\n'),
|
|
88
99
|
find: (r) => [
|
|
89
|
-
...(r.fallback ? [`nothing matches "${r.query}" — ${r.hint}
|
|
100
|
+
...(r.fallback ? [`nothing matches "${r.query}" — ${r.hint} (${r.screens} screens drawn)`]
|
|
101
|
+
: r.results.length ? r.results.map((x) => `${x.kind === 'screen' ? 'screen ' : 'component '}${x.id ?? x.name}${x.role ? ` [${[...x.role.represents, x.role.shape, x.role.interact ? x.role.interact.join('+') : 'show'].join('·')}]` : ''} — ${x.summary}`) : [r.hint]),
|
|
90
102
|
...(r.usedTogether?.length ? [`\nused together: ${r.usedTogether.map((u) => `${u.name} (${u.stories})`).join(' ')}`] : []),
|
|
91
103
|
].join('\n'),
|
|
92
|
-
scaffold: (r) => typeof r === 'string' ? r.trimEnd() : r.map((x) => `${x.id} — ${x.summary}`).join('\n'),
|
|
104
|
+
scaffold: (r) => typeof r === 'string' ? r.trimEnd() : r.html ? r.html.trimEnd() : r.map((x) => `${x.id} — ${x.summary}`).join('\n'),
|
|
93
105
|
};
|
|
94
106
|
|
|
95
107
|
function ask(cmd, argv) {
|
|
96
108
|
const flag = (name) => { const i = argv.indexOf(name); return i >= 0 && argv.splice(i, 1).length > 0; };
|
|
97
109
|
const at = argv.indexOf('--root');
|
|
98
|
-
const root = at >= 0 ?
|
|
110
|
+
const root = at >= 0 ? resolve(cwd, argv.splice(at, 2)[1] ?? '') : undefined;
|
|
99
111
|
const cssAt = argv.indexOf('--css'), css = cssAt >= 0 ? argv.splice(cssAt, 2)[1] : undefined;
|
|
112
|
+
const option = (name) => { const i = argv.indexOf(name); return i >= 0 ? argv.splice(i, 2)[1] : undefined; };
|
|
113
|
+
const commas = (v) => v?.split(',').map((x) => x.trim()).filter(Boolean);
|
|
114
|
+
const axes = cmd === 'find' ? { represents: commas(option('--represents')), shape: option('--shape'), interact: ((v) => (v === 'none' ? false : commas(v)))(option('--interact')) } : undefined;
|
|
100
115
|
const json = flag('--json'), html = flag('--html'), all = flag('--all');
|
|
101
116
|
if (cmd === 'mcp') return import('../lib/mcp.mjs').then((m) => m.serve({ root }));
|
|
102
117
|
const cat = catalog.load({ root });
|
|
103
118
|
const q = argv.join(' ');
|
|
104
|
-
const result = cmd === 'list' ? catalog.list(cat) : cmd === 'get' ? catalog.get(cat, q, { html, all }) : cmd === 'find' ? catalog.find(cat, q) : catalog.scaffold(cat, q || undefined, { format: html ? 'html' : 'tsx', css });
|
|
119
|
+
const result = cmd === 'list' ? catalog.list(cat) : cmd === 'get' ? catalog.get(cat, q, { html, all }) : cmd === 'find' ? catalog.find(cat, q, axes) : catalog.scaffold(cat, q || undefined, { format: html ? 'html' : 'tsx', css });
|
|
105
120
|
if (result.error) { console.error(`cardds ${cmd}: ${result.error}${result.closest ? ` — closest: ${result.closest.join(', ')}` : ''}`); return 1; }
|
|
121
|
+
if (result.warning && !json) console.error(`cardds scaffold: ${result.warning}`);
|
|
106
122
|
console.log(json ? JSON.stringify(result) : asText[cmd](result, { html }));
|
|
107
123
|
return 0;
|
|
108
124
|
}
|
|
109
125
|
|
|
110
126
|
const code = cmd === 'check' ? check(args) : cmd === 'init' ? init() : ['list', 'get', 'find', 'scaffold', 'mcp'].includes(cmd) ? await ask(cmd, args)
|
|
111
|
-
: (console.log('usage: cardds init | check [dir…] | list | get <Name> [--html] | find
|
|
127
|
+
: (console.log('usage: cardds init | check [dir…] | list | get <Name> [--html] | find [words…] [--represents a,b] [--shape s] [--interact k|none] | scaffold [id] [--html [--css <href>]] | mcp (list get find scaffold mcp: --root <path> --json)'), cmd ? 2 : 0);
|
|
112
128
|
process.exitCode = code; // not process.exit: a big answer piped to a reader is cut off by an exit before it is flushed
|
package/css/people.css
CHANGED
|
@@ -50,6 +50,12 @@
|
|
|
50
50
|
.avatar-stack { display: inline-flex; align-items: center; }
|
|
51
51
|
.avatar-stack > .avatar { box-shadow: 0 0 0 var(--sp-1) var(--card-bg, var(--surface)); }
|
|
52
52
|
.avatar-stack > .avatar + .avatar { margin-left: calc(-1 * var(--sp-3)); }
|
|
53
|
+
/* stamps in the stack: a Stamp is as wide as its column, and an inline row has none — so it brings one, as tall as an sm head (4:5) and a little overlapped, like the heads
|
|
54
|
+
<span class="avatar-stack"><span class="stamp stamp--bare">…</span><span class="stamp stamp--bare">…</span><span class="avatar-stack__more">+9</span></span> */
|
|
55
|
+
.avatar-stack > .stamp { --_stack-w: calc(var(--avatar-sm) * 4 / 5); flex: none; width: var(--avatar-stack-stamp-w, var(--_stack-w)); margin-inline: 0; }
|
|
56
|
+
.avatar-stack > .stamp + .stamp { margin-left: calc(-1 * var(--sp-2)); }
|
|
57
|
+
/* more: the count that trails the stack — plain words on the ground, title size so it reads at arm's length (the accent "+7" avatar is the small, framed way) */
|
|
58
|
+
.avatar-stack__more { margin-left: var(--avatar-stack-more-gap, var(--sp-3)); font: var(--avatar-stack-more-font, var(--type-title)); color: var(--avatar-stack-more-ink, inherit); white-space: nowrap; }
|
|
53
59
|
|
|
54
60
|
/* ---- avatar chooser: big heads with name + role under, scrolls sideways ----
|
|
55
61
|
<div class="avatar-pick"><button class="avatar-pick__item"><span class="avatar avatar--lg avatar--add">+</span><span class="avatar-pick__name">Add</span><span class="avatar-pick__role">member</span></button>…</div> */
|
package/css/stamp.css
CHANGED
|
@@ -150,6 +150,10 @@
|
|
|
150
150
|
color: var(--stamp-cell-on-ink, var(--on-accent));
|
|
151
151
|
}
|
|
152
152
|
.stamp-cell__on > .icon { width: 70%; height: 70%; }
|
|
153
|
+
/* --bare: no ground, no padding — the stamps lie on what holds the book and fill it: three across, each the whole width of its third (the Stamp's own cap is lifted), the gap the least that still keeps three apart (the shadow's room), the row gap the same. The name is the Stamp's own; free places still draw.
|
|
154
|
+
<div class="stamp-book stamp-book--bare"> */
|
|
155
|
+
.stamp-book--bare { gap: var(--stamp-book-bare-gap, var(--sp-2)); padding: 0; border-radius: 0; background: none; }
|
|
156
|
+
.stamp-book--bare :is(.stamp, .stamp-place) { max-width: none; }
|
|
153
157
|
/* --strip: ONE line of small stamps that scrolls sideways — the tray a member picks from. No ground. */
|
|
154
158
|
.stamp-book--strip {
|
|
155
159
|
--_strip: calc(var(--tap) * 1.5);
|
package/dist/actions/Chip.d.ts
CHANGED
|
@@ -9,7 +9,9 @@ export interface ChipProps extends ComponentProps<'span'> {
|
|
|
9
9
|
dot?: 'a' | 'b' | DotTone;
|
|
10
10
|
/** the element: span (default), a link, or a button */
|
|
11
11
|
as?: 'span' | 'a' | 'button';
|
|
12
|
+
/** makes the chip a link (an `a`) */
|
|
12
13
|
href?: string;
|
|
14
|
+
/** the chip's words */
|
|
13
15
|
children?: ReactNode;
|
|
14
16
|
}
|
|
15
17
|
/** Chip — a status or attribute tag (with an optional signal Dot), or a small tap target; `on` when it is the live one. For a choice use ChipPick (`sign` for the +/× interest picker). */
|
|
@@ -303,10 +305,15 @@ export interface ChipPickProps extends Omit<ComponentProps<'label'>, 'onChange'>
|
|
|
303
305
|
type?: 'radio' | 'checkbox';
|
|
304
306
|
/** the +/× at the end: an interest / filter chip on a row, hugging its word (was ChipToggle) */
|
|
305
307
|
sign?: boolean;
|
|
308
|
+
/** the input's name: the radios of one choice share it (that is what makes them one group); a checkbox's is its form field */
|
|
306
309
|
name?: string;
|
|
310
|
+
/** what the input reports when this chip is picked */
|
|
307
311
|
value?: string;
|
|
312
|
+
/** CONTROLLED: picked. The app owns it — pair it with `onChange` */
|
|
308
313
|
checked?: boolean;
|
|
314
|
+
/** UNCONTROLLED: picked on arrival; the input then keeps its own */
|
|
309
315
|
defaultChecked?: boolean;
|
|
316
|
+
/** the native input's change event: the chip was picked, or (checkbox) toggled */
|
|
310
317
|
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
|
311
318
|
}
|
|
312
319
|
/** ChipPick — a chip that is a choice: full-width in a ChipGrid (9:00 · 9:30 · …), or `sign` on a ChipRow (+ off, × on — the
|
|
@@ -35,6 +35,7 @@ export interface MenuProps extends Omit<ComponentProps<'div'>, 'popover' | 'onCh
|
|
|
35
35
|
value?: string;
|
|
36
36
|
/** …or the value it starts with */
|
|
37
37
|
defaultValue?: string;
|
|
38
|
+
/** a MenuItem was picked (a value menu): the item's `value` */
|
|
38
39
|
onChange?: (value: string) => void;
|
|
39
40
|
/** the trigger sits at the far right: the panel grows leftwards */
|
|
40
41
|
end?: boolean;
|
package/dist/actions/Fab.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
2
|
import { type IconName } from '../type/Icon.js';
|
|
3
3
|
export interface FabProps extends ComponentProps<'button'> {
|
|
4
|
+
/** the accessible name — required, the button has no text */
|
|
4
5
|
label: string;
|
|
6
|
+
/** the icon (default plus); children replace it */
|
|
5
7
|
icon?: IconName;
|
|
8
|
+
/** filled accent (default true); `invert={false}` for a plain one */
|
|
6
9
|
invert?: boolean;
|
|
7
10
|
}
|
|
8
11
|
/** Fab — the square floating action at the bottom-right of a stage or screen (above the bar): an IconBtn with the fab's place. */
|
|
@@ -8,6 +8,7 @@ export interface IconBtnGroupProps extends Omit<ComponentProps<'div'>, 'onChange
|
|
|
8
8
|
select?: 'one' | 'many';
|
|
9
9
|
/** which are on — each child IconBtn names itself with its own `value` */
|
|
10
10
|
value?: Picked;
|
|
11
|
+
/** UNCONTROLLED: which are on at first, in the same shape as `value`; the group then keeps its own. Pair of `value` */
|
|
11
12
|
defaultValue?: Picked;
|
|
12
13
|
/** asks the app: `one` → the pressed value · `many` → the new list */
|
|
13
14
|
onChange?: (value: Picked) => void;
|
package/dist/cardds.css
CHANGED
|
@@ -3099,6 +3099,12 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
3099
3099
|
.avatar-stack { display: inline-flex; align-items: center; }
|
|
3100
3100
|
.avatar-stack > .avatar { box-shadow: 0 0 0 var(--sp-1) var(--card-bg, var(--surface)); }
|
|
3101
3101
|
.avatar-stack > .avatar + .avatar { margin-left: calc(-1 * var(--sp-3)); }
|
|
3102
|
+
/* stamps in the stack: a Stamp is as wide as its column, and an inline row has none — so it brings one, as tall as an sm head (4:5) and a little overlapped, like the heads
|
|
3103
|
+
<span class="avatar-stack"><span class="stamp stamp--bare">…</span><span class="stamp stamp--bare">…</span><span class="avatar-stack__more">+9</span></span> */
|
|
3104
|
+
.avatar-stack > .stamp { --_stack-w: calc(var(--avatar-sm) * 4 / 5); flex: none; width: var(--avatar-stack-stamp-w, var(--_stack-w)); margin-inline: 0; }
|
|
3105
|
+
.avatar-stack > .stamp + .stamp { margin-left: calc(-1 * var(--sp-2)); }
|
|
3106
|
+
/* more: the count that trails the stack — plain words on the ground, title size so it reads at arm's length (the accent "+7" avatar is the small, framed way) */
|
|
3107
|
+
.avatar-stack__more { margin-left: var(--avatar-stack-more-gap, var(--sp-3)); font: var(--avatar-stack-more-font, var(--type-title)); color: var(--avatar-stack-more-ink, inherit); white-space: nowrap; }
|
|
3102
3108
|
|
|
3103
3109
|
/* ---- avatar chooser: big heads with name + role under, scrolls sideways ----
|
|
3104
3110
|
<div class="avatar-pick"><button class="avatar-pick__item"><span class="avatar avatar--lg avatar--add">+</span><span class="avatar-pick__name">Add</span><span class="avatar-pick__role">member</span></button>…</div> */
|
|
@@ -4048,6 +4054,10 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
4048
4054
|
color: var(--stamp-cell-on-ink, var(--on-accent));
|
|
4049
4055
|
}
|
|
4050
4056
|
.stamp-cell__on > .icon { width: 70%; height: 70%; }
|
|
4057
|
+
/* --bare: no ground, no padding — the stamps lie on what holds the book and fill it: three across, each the whole width of its third (the Stamp's own cap is lifted), the gap the least that still keeps three apart (the shadow's room), the row gap the same. The name is the Stamp's own; free places still draw.
|
|
4058
|
+
<div class="stamp-book stamp-book--bare"> */
|
|
4059
|
+
.stamp-book--bare { gap: var(--stamp-book-bare-gap, var(--sp-2)); padding: 0; border-radius: 0; background: none; }
|
|
4060
|
+
.stamp-book--bare :is(.stamp, .stamp-place) { max-width: none; }
|
|
4051
4061
|
/* --strip: ONE line of small stamps that scrolls sideways — the tray a member picks from. No ground. */
|
|
4052
4062
|
.stamp-book--strip {
|
|
4053
4063
|
--_strip: calc(var(--tap) * 1.5);
|
package/dist/cards/Card.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface CardProps extends ComponentProps<'article'> {
|
|
|
23
23
|
band?: boolean;
|
|
24
24
|
/** the element: article (default), a when it links, label when it wraps an input, div/section */
|
|
25
25
|
as?: 'article' | 'div' | 'a' | 'label' | 'section' | 'li';
|
|
26
|
+
/** the link's target, for `as="a"` */
|
|
26
27
|
href?: string;
|
|
27
28
|
/** THE HEAD, filled in place: the heading (wraps freely) — the card writes its CardHead first. For a head of your own shape
|
|
28
29
|
* (a chip before the words, a tile badge) write `<CardHead>` as the first child instead, and leave these out */
|
|
@@ -911,6 +912,7 @@ export declare function Card({ tone, paper, sm, row, cover, fold, centre, band,
|
|
|
911
912
|
colorRendering?: import("csstype").Property.ColorRendering | undefined;
|
|
912
913
|
glyphOrientationVertical?: import("csstype").Property.GlyphOrientationVertical | undefined;
|
|
913
914
|
} | undefined;
|
|
915
|
+
/** the link's target, for `as="a"` */
|
|
914
916
|
href?: string;
|
|
915
917
|
key?: import("react").Key | null | undefined;
|
|
916
918
|
defaultChecked?: boolean | undefined;
|
package/dist/cards/CardHead.d.ts
CHANGED
|
@@ -13,4 +13,9 @@ export interface CardHeadProps extends ComponentProps<'div'> {
|
|
|
13
13
|
/** the sheet measured this title as wider than its line, so the heading runs sideways */
|
|
14
14
|
long?: boolean;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* CardHead — the card's head: the heading on the left takes the room, the contextual info hugs the far right.
|
|
18
|
+
* Use `heading` (+ `caption`) and `end` for the standard head, or pass children for a custom one
|
|
19
|
+
* (the FIRST child takes the room). Flat markup: no wrapper element, no <p> — the caption is a span on a row of its own (card.css). Never put buttons here — they go in the CardFoot.
|
|
20
|
+
*/
|
|
16
21
|
export declare function CardHead({ heading, caption, end, top, level, long, className, children, ...rest }: CardHeadProps): import("react").JSX.Element;
|
package/dist/cards/CardHead.js
CHANGED
|
@@ -3,11 +3,6 @@ import { Children, cloneElement, isValidElement, useContext } from 'react';
|
|
|
3
3
|
import { cx } from '../cx.js';
|
|
4
4
|
import { Text } from '../type/Text.js';
|
|
5
5
|
import { StackHeadCtx } from '../sheets/stackCard.js';
|
|
6
|
-
/**
|
|
7
|
-
* CardHead — the card's head: the heading on the left takes the room, the contextual info hugs the far right.
|
|
8
|
-
* Use `heading` (+ `caption`) and `end` for the standard head, or pass children for a custom one
|
|
9
|
-
* (the FIRST child takes the room). Flat markup: no wrapper element, no <p> — the caption is a span on a row of its own (card.css). Never put buttons here — they go in the CardFoot.
|
|
10
|
-
*/
|
|
11
6
|
const onTitleKey = (e) => {
|
|
12
7
|
if (e.key !== 'Enter' && e.key !== ' ')
|
|
13
8
|
return;
|
|
@@ -32,6 +27,11 @@ function markTitle(node, tab, long) {
|
|
|
32
27
|
} : {}),
|
|
33
28
|
});
|
|
34
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* CardHead — the card's head: the heading on the left takes the room, the contextual info hugs the far right.
|
|
32
|
+
* Use `heading` (+ `caption`) and `end` for the standard head, or pass children for a custom one
|
|
33
|
+
* (the FIRST child takes the room). Flat markup: no wrapper element, no <p> — the caption is a span on a row of its own (card.css). Never put buttons here — they go in the CardFoot.
|
|
34
|
+
*/
|
|
35
35
|
export function CardHead({ heading, caption, end, top, level = 'h2', long, className, children, ...rest }) {
|
|
36
36
|
const tab = useContext(StackHeadCtx);
|
|
37
37
|
const H = level === 'h1' ? 'h1' : 'h2';
|