@usableapp/cardds 0.7.94 → 0.7.96
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 +76 -0
- package/README.md +110 -11
- package/bin/cardds.mjs +1 -1
- package/css/actions.css +9 -12
- package/css/base.css +3 -6
- package/css/card.css +11 -17
- package/css/choice.css +14 -11
- package/css/finish.css +4 -2
- package/css/journey.css +7 -4
- package/css/layover.css +16 -11
- package/css/lists.css +4 -9
- package/css/media.css +20 -16
- package/css/numbers.css +16 -18
- package/css/palette.css +79 -97
- package/css/people.css +5 -4
- package/css/post.css +2 -1
- package/css/sheet.css +13 -10
- package/css/sticky.css +6 -5
- package/css/theme.template.css +16 -7
- package/css/tokens.css +69 -35
- package/dist/actions/Chip.d.ts +1 -1
- package/dist/actions/Chip.js +1 -1
- package/dist/actions/Dropdown.d.ts +1 -1
- package/dist/actions/Dropdown.js +1 -1
- package/dist/cardds.css +280 -260
- package/dist/cards/cardGrounds.d.ts +22 -0
- package/dist/cards/cardGrounds.js +84 -0
- package/dist/cards/inkGrounds.d.ts +34 -0
- package/dist/cards/inkGrounds.js +181 -0
- package/dist/catalog.json +1 -1
- package/dist/choice/Calendar.d.ts +1 -1
- package/dist/choice/Calendar.js +1 -1
- package/dist/choice/Check.d.ts +2 -2
- package/dist/choice/Check.js +2 -2
- package/dist/choice/DayStrip.d.ts +1 -1
- package/dist/choice/DayStrip.js +1 -1
- package/dist/choice/Mood.d.ts +1 -1
- package/dist/choice/Mood.js +1 -1
- package/dist/choice/Slider.d.ts +1 -1
- package/dist/choice/Slider.js +1 -1
- package/dist/forms/AddRow.d.ts +1 -1
- package/dist/forms/AddRow.js +1 -1
- package/dist/forms/Composer.d.ts +1 -1
- package/dist/forms/Composer.js +1 -1
- package/dist/forms/Field.d.ts +4 -2
- package/dist/forms/Field.js +4 -2
- package/dist/forms/FileBtn.d.ts +1 -1
- package/dist/forms/FileBtn.js +1 -1
- package/dist/forms/Pin.d.ts +1 -1
- package/dist/forms/Pin.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/media/MapPick.d.ts +1 -1
- package/dist/media/MapPick.js +1 -1
- package/dist/media/Tile.d.ts +1 -1
- package/dist/media/Tile.js +1 -1
- package/dist/numbers/Picker.d.ts +1 -1
- package/dist/numbers/Picker.js +1 -1
- package/dist/people/AvatarPick.d.ts +2 -2
- package/dist/people/AvatarPick.js +1 -1
- package/lib/guide.mjs +1 -1
- package/lint/rules.mjs +37 -0
- package/package.json +1 -1
|
@@ -32,5 +32,5 @@ export interface CalendarProps extends Omit<ComponentProps<'div'>, 'title' | 'on
|
|
|
32
32
|
nav?: ReactNode;
|
|
33
33
|
}
|
|
34
34
|
/** Calendar — a month grid of 44px round cells: one accent day, optional marks; prev/next in the head. It shows ONE month and knows no dates:
|
|
35
|
-
* the app owns which month (`title`, `days`, `start` + `onPrev` / `onNext`) and which day (`selected` + `onSelect`, or `defaultSelected`). */
|
|
35
|
+
* the app owns which month (`title`, `days`, `start` + `onPrev` / `onNext`) and which day (`selected` + `onSelect`, or `defaultSelected`). On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
36
36
|
export declare function Calendar({ title, days, start, selected, defaultSelected, onSelect, onPrev, onNext, prevLabel, nextLabel, marks, marks2, disabled, weekdays, nav, className, ...rest }: CalendarProps): import("react").JSX.Element;
|
package/dist/choice/Calendar.js
CHANGED
|
@@ -4,7 +4,7 @@ import { cx } from '../cx.js';
|
|
|
4
4
|
import { IconBtn } from '../actions/IconBtn.js';
|
|
5
5
|
import { word } from '../words.js';
|
|
6
6
|
/** Calendar — a month grid of 44px round cells: one accent day, optional marks; prev/next in the head. It shows ONE month and knows no dates:
|
|
7
|
-
* the app owns which month (`title`, `days`, `start` + `onPrev` / `onNext`) and which day (`selected` + `onSelect`, or `defaultSelected`). */
|
|
7
|
+
* the app owns which month (`title`, `days`, `start` + `onPrev` / `onNext`) and which day (`selected` + `onSelect`, or `defaultSelected`). On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
8
8
|
export function Calendar({ title, days = 30, start = 1, selected, defaultSelected, onSelect, onPrev, onNext, prevLabel = word('previous'), nextLabel = word('next'), marks = [], marks2 = [], disabled = [], weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], nav, className, ...rest }) {
|
|
9
9
|
const [day, pick] = useControlled(selected, defaultSelected, onSelect);
|
|
10
10
|
return (_jsxs("div", { className: cx('calendar', className), ...rest, children: [_jsxs("div", { className: "calendar__head", children: [_jsx("span", { className: "t-title", children: title }), nav ?? (_jsxs("span", { className: "icon-row", children: [_jsx(IconBtn, { sm: true, icon: "chevron-left", label: prevLabel, onClick: onPrev }), _jsx(IconBtn, { sm: true, icon: "chevron-right", label: nextLabel, onClick: onNext })] }))] }), _jsxs("div", { className: "calendar__grid", children: [weekdays.map((d) => _jsx("span", { className: "calendar__wd", children: d }, d)), Array.from({ length: days }, (_, i) => i + 1).map((d) => (_jsx("button", { type: "button", className: cx('calendar__day', marks.includes(d) && 'calendar__day--mark', marks2.includes(d) && 'calendar__day--mark2'), "aria-selected": d === day ? 'true' : undefined, onClick: () => pick(d), disabled: disabled.includes(d), style: d === 1 ? { '--start': start } : undefined, children: d }, d)))] })] }));
|
package/dist/choice/Check.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface CheckProps extends ComponentProps<'input'> {
|
|
|
5
5
|
/** accessible name */
|
|
6
6
|
label?: string;
|
|
7
7
|
}
|
|
8
|
-
/** Check — the round check circle at a Row's end (48px); the row's label wraps it. */
|
|
8
|
+
/** Check — the round check circle at a Row's end (48px); the row's label wraps it. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
9
9
|
export declare function Check({ sm, label, className, ...rest }: CheckProps): import("react").JSX.Element;
|
|
10
10
|
export interface ToggleProps extends ComponentProps<'input'> {
|
|
11
11
|
/** accessible name, when no words show */
|
|
@@ -13,5 +13,5 @@ export interface ToggleProps extends ComponentProps<'input'> {
|
|
|
13
13
|
/** WORDS shown before the switch, naming it ("อยู่บ้าน") — the switch and its words are one label, one tap target */
|
|
14
14
|
text?: ReactNode;
|
|
15
15
|
}
|
|
16
|
-
/** Toggle — the big switch (64×36) at a Row's end; with `text`, a switch that names itself (a bar's own switch — "at home"). */
|
|
16
|
+
/** Toggle — the big switch (64×36) at a Row's end; with `text`, a switch that names itself (a bar's own switch — "at home"). In a row it stands on a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
17
17
|
export declare function Toggle({ label, text, className, ...rest }: ToggleProps): import("react").JSX.Element;
|
package/dist/choice/Check.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from '../cx.js';
|
|
3
|
-
/** Check — the round check circle at a Row's end (48px); the row's label wraps it. */
|
|
3
|
+
/** Check — the round check circle at a Row's end (48px); the row's label wraps it. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
4
4
|
export function Check({ sm, label, className, ...rest }) {
|
|
5
5
|
return _jsx("input", { type: "checkbox", className: cx('check', sm && 'check--sm', className), "aria-label": label, ...rest });
|
|
6
6
|
}
|
|
7
|
-
/** Toggle — the big switch (64×36) at a Row's end; with `text`, a switch that names itself (a bar's own switch — "at home"). */
|
|
7
|
+
/** Toggle — the big switch (64×36) at a Row's end; with `text`, a switch that names itself (a bar's own switch — "at home"). In a row it stands on a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
8
8
|
export function Toggle({ label, text, className, ...rest }) {
|
|
9
9
|
const input = _jsx("input", { type: "checkbox", role: "switch", className: cx('toggle', className), "aria-label": text == null ? label : undefined, ...rest });
|
|
10
10
|
if (text == null)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ComponentProps, ReactNode } from 'react';
|
|
2
2
|
export interface DayStripProps extends ComponentProps<'div'> {
|
|
3
3
|
}
|
|
4
|
-
/** DayStrip — a week of Days, one selected, today marked. */
|
|
4
|
+
/** DayStrip — a week of Days, one selected, today marked. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
5
5
|
export declare function DayStrip({ className, ...rest }: DayStripProps): import("react").JSX.Element;
|
|
6
6
|
export interface DayProps extends Omit<ComponentProps<'button'>, 'name'> {
|
|
7
7
|
/** weekday name (Thu) */
|
package/dist/choice/DayStrip.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from '../cx.js';
|
|
3
|
-
/** DayStrip — a week of Days, one selected, today marked. */
|
|
3
|
+
/** DayStrip — a week of Days, one selected, today marked. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
4
4
|
export function DayStrip({ className, ...rest }) {
|
|
5
5
|
return _jsx("div", { className: cx('day-strip', className), role: "tablist", ...rest });
|
|
6
6
|
}
|
package/dist/choice/Mood.d.ts
CHANGED
|
@@ -12,6 +12,6 @@ export interface MoodProps extends Omit<ComponentProps<'fieldset'>, 'onChange' |
|
|
|
12
12
|
/** the dot the member tapped */
|
|
13
13
|
onChange?: (value: MoodValue) => void;
|
|
14
14
|
}
|
|
15
|
-
/** Mood — five dots in a pill from bad to good; the picked one grows. */
|
|
15
|
+
/** Mood — five dots in a pill from bad to good; the picked one grows. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
16
16
|
export declare function Mood({ name, value, defaultValue, label, onChange, className, ...rest }: MoodProps): import("react").JSX.Element;
|
|
17
17
|
export {};
|
package/dist/choice/Mood.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { cx } from '../cx.js';
|
|
3
3
|
import { useControlled } from '../state.js';
|
|
4
4
|
import { word } from '../words.js';
|
|
5
|
-
/** Mood — five dots in a pill from bad to good; the picked one grows. */
|
|
5
|
+
/** Mood — five dots in a pill from bad to good; the picked one grows. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
6
6
|
export function Mood({ name = 'mood', value, defaultValue, label = word('mood'), onChange, className, ...rest }) {
|
|
7
7
|
const [picked, pick] = useControlled(value, defaultValue, onChange);
|
|
8
8
|
return (_jsx("fieldset", { className: cx('mood', className), "aria-label": label, ...rest, children: [1, 2, 3, 4, 5].map((n) => (_jsxs("label", { className: "mood__opt", children: [_jsx("input", { type: "radio", name: name, value: n, checked: n === picked, onChange: () => pick(n) }), _jsx("span", { className: "mood__dot" })] }, n))) }));
|
package/dist/choice/Slider.d.ts
CHANGED
|
@@ -9,5 +9,5 @@ export interface SliderProps extends Omit<ComponentProps<'input'>, 'className' |
|
|
|
9
9
|
/** inline style for the label wrapper, not the range input */
|
|
10
10
|
style?: React.CSSProperties;
|
|
11
11
|
}
|
|
12
|
-
/** Slider — one value along a hairline rail with a 48px knob; the whole range is thumb-tall. */
|
|
12
|
+
/** Slider — one value along a hairline rail with a 48px knob; the whole range is thumb-tall. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). (Or the control tier of an `ActionBar variant="tiers"`.) */
|
|
13
13
|
export declare function Slider({ label, glyph, className, style, onInput, ...input }: SliderProps): import("react").JSX.Element;
|
package/dist/choice/Slider.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
|
|
3
3
|
import { cx } from '../cx.js';
|
|
4
4
|
import { useStepCursor } from '../sheets/stepCursor.js';
|
|
5
5
|
const share = (value, min, max) => (max > min ? Math.min(1, Math.max(0, (value - min) / (max - min))) : 0);
|
|
6
|
-
/** Slider — one value along a hairline rail with a 48px knob; the whole range is thumb-tall. */
|
|
6
|
+
/** Slider — one value along a hairline rail with a 48px knob; the whole range is thumb-tall. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). (Or the control tier of an `ActionBar variant="tiers"`.) */
|
|
7
7
|
export function Slider({ label, glyph, className, style, onInput, ...input }) {
|
|
8
8
|
const cursor = useStepCursor();
|
|
9
9
|
const min = Number(input.min) || 0, max = Number(input.max) || 100;
|
package/dist/forms/AddRow.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
2
|
export interface AddRowProps extends ComponentProps<'button'> {
|
|
3
3
|
}
|
|
4
|
-
/** AddRow — the inline "+ add another" action under a list of fields. */
|
|
4
|
+
/** AddRow — the inline "+ add another" action under a list of fields. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
5
5
|
export declare function AddRow({ className, children, ...rest }: AddRowProps): import("react").JSX.Element;
|
package/dist/forms/AddRow.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from '../cx.js';
|
|
3
3
|
import { Icon } from '../type/Icon.js';
|
|
4
|
-
/** AddRow — the inline "+ add another" action under a list of fields. */
|
|
4
|
+
/** AddRow — the inline "+ add another" action under a list of fields. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
5
5
|
export function AddRow({ className, children, ...rest }) {
|
|
6
6
|
return (_jsxs("button", { type: "button", className: cx('add-row', className), ...rest, children: [_jsx("span", { className: "add-row__plus", children: _jsx(Icon, { name: "plus" }) }), " ", children] }));
|
|
7
7
|
}
|
package/dist/forms/Composer.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface ComposerProps extends Omit<ComponentProps<'textarea'>, 'classNa
|
|
|
13
13
|
/** inline style for the wrapper, not the textarea */
|
|
14
14
|
style?: React.CSSProperties;
|
|
15
15
|
}
|
|
16
|
-
/** Composer — a big write-in area (display type) with a bottom bar: icon · hint · send. The write-in step's sheet content. A second thing to write in the same
|
|
16
|
+
/** Composer — a big write-in area (display type) with a bottom bar: icon · hint · send. The write-in step's sheet content. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). A second thing to write in the same Composer (the reason under the request) is a `ComposerArea` child: the same words, behind a dashed rule. The bar is drawn only when it holds a lead, a hint or a send. */
|
|
17
17
|
export declare function Composer({ hint, lead, send, children, className, style, ...textarea }: ComposerProps): import("react").JSX.Element;
|
|
18
18
|
export interface ComposerAreaProps extends Omit<ComponentProps<'textarea'>, 'className' | 'children'> {
|
|
19
19
|
}
|
package/dist/forms/Composer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from '../cx.js';
|
|
3
3
|
import { useStepCursor } from '../sheets/stepCursor.js';
|
|
4
|
-
/** Composer — a big write-in area (display type) with a bottom bar: icon · hint · send. The write-in step's sheet content. A second thing to write in the same
|
|
4
|
+
/** Composer — a big write-in area (display type) with a bottom bar: icon · hint · send. The write-in step's sheet content. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). A second thing to write in the same Composer (the reason under the request) is a `ComposerArea` child: the same words, behind a dashed rule. The bar is drawn only when it holds a lead, a hint or a send. */
|
|
5
5
|
export function Composer({ hint, lead, send, children, className, style, ...textarea }) {
|
|
6
6
|
const cursor = useStepCursor();
|
|
7
7
|
return (_jsxs("div", { className: cx('composer', className), style: style, children: [_jsx("textarea", { className: "composer__input", ...textarea, ...cursor }), children, (lead != null || hint != null || send != null) && (_jsxs("div", { className: "composer__bar", children: [lead, hint != null && _jsx("span", { className: "composer__hint", children: hint }), send] }))] }));
|
package/dist/forms/Field.d.ts
CHANGED
|
@@ -32,7 +32,9 @@ export interface FieldProps extends Omit<ComponentProps<'input'>, 'className' |
|
|
|
32
32
|
/** the digits of `v`, written into `mask`; a mark is written only when a digit comes after it */
|
|
33
33
|
export declare function applyMask(v: string, mask: string): string;
|
|
34
34
|
/**
|
|
35
|
-
* Field — the flat outlined input: the
|
|
36
|
-
*
|
|
35
|
+
* Field — the flat outlined input, and where a form field goes is the SHEET: the paper the member fills in, never a Card (Lh 2026-10-18).
|
|
36
|
+
* It stands on a Sheet's `SheetBody`, in a `SheetStep`, or on a stack sheet, 1–3 per step; a card is for reading and pressing buttons
|
|
37
|
+
* (`cardds check` rule `field-in-card` flags a Field, or any control the member fills or picks, inside a `<Card>`).
|
|
38
|
+
* The LABEL IS THE PLACEHOLDER. 48px tall (56px with an action inside).
|
|
37
39
|
*/
|
|
38
40
|
export declare function Field({ icon, action, label, error, format, digits, formatError, mask, className, style, onBlur, onInput, onChange, ...input }: FieldProps): import("react").JSX.Element;
|
package/dist/forms/Field.js
CHANGED
|
@@ -20,8 +20,10 @@ export function applyMask(v, mask) {
|
|
|
20
20
|
return out;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Field — the flat outlined input: the
|
|
24
|
-
*
|
|
23
|
+
* Field — the flat outlined input, and where a form field goes is the SHEET: the paper the member fills in, never a Card (Lh 2026-10-18).
|
|
24
|
+
* It stands on a Sheet's `SheetBody`, in a `SheetStep`, or on a stack sheet, 1–3 per step; a card is for reading and pressing buttons
|
|
25
|
+
* (`cardds check` rule `field-in-card` flags a Field, or any control the member fills or picks, inside a `<Card>`).
|
|
26
|
+
* The LABEL IS THE PLACEHOLDER. 48px tall (56px with an action inside).
|
|
25
27
|
*/
|
|
26
28
|
export function Field({ icon, action, label, error, format, digits, formatError, mask, className, style, onBlur, onInput, onChange, ...input }) {
|
|
27
29
|
const ref = useRef(null);
|
package/dist/forms/FileBtn.d.ts
CHANGED
|
@@ -14,6 +14,6 @@ export interface FileBtnProps extends Omit<ComponentProps<'input'>, 'type' | 'cl
|
|
|
14
14
|
/**
|
|
15
15
|
* FileBtn — a Btn that opens the device's file picker: the button IS the label of a hidden file input, so the
|
|
16
16
|
* whole button is the tap target and nothing is forwarded by script. `accept="image/*" capture="environment"`
|
|
17
|
-
* opens the camera; without `capture` the gallery. The picked file arrives in `onChange` like any input.
|
|
17
|
+
* opens the camera; without `capture` the gallery. The picked file arrives in `onChange` like any input. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18).
|
|
18
18
|
*/
|
|
19
19
|
export declare function FileBtn({ primary, quiet, block, className, children, ...input }: FileBtnProps): import("react").JSX.Element;
|
package/dist/forms/FileBtn.js
CHANGED
|
@@ -4,7 +4,7 @@ import { useStepCursor } from '../sheets/stepCursor.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* FileBtn — a Btn that opens the device's file picker: the button IS the label of a hidden file input, so the
|
|
6
6
|
* whole button is the tap target and nothing is forwarded by script. `accept="image/*" capture="environment"`
|
|
7
|
-
* opens the camera; without `capture` the gallery. The picked file arrives in `onChange` like any input.
|
|
7
|
+
* opens the camera; without `capture` the gallery. The picked file arrives in `onChange` like any input. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18).
|
|
8
8
|
*/
|
|
9
9
|
export function FileBtn({ primary, quiet, block, className, children, ...input }) {
|
|
10
10
|
const cursor = useStepCursor();
|
package/dist/forms/Pin.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface PinProps extends Omit<ComponentProps<'div'>, 'onChange' | 'defa
|
|
|
31
31
|
autoFocus?: boolean;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* Pin — big code entry, one digit per pill; a filled cell turns accent. Lives in a
|
|
34
|
+
* Pin — big code entry, one digit per pill; a filled cell turns accent. Lives on a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18).
|
|
35
35
|
* Its own behaviour, inside it: typing a digit moves to the next cell, Backspace in an empty cell moves back and clears, the
|
|
36
36
|
* arrows walk the cells, a pasted code fills them from the one it landed in; only digits stay. The first cell takes the SMS
|
|
37
37
|
* one-time-code autofill. The code is a VALUE, controlled like an input: `value` + `onChange`, or `defaultValue`.
|
package/dist/forms/Pin.js
CHANGED
|
@@ -7,7 +7,7 @@ import { word } from '../words.js';
|
|
|
7
7
|
import { useStepCursor } from '../sheets/stepCursor.js';
|
|
8
8
|
const RESET_MS = 900; // a code that does not match: red + shake this long, then the cells empty for a new try
|
|
9
9
|
/**
|
|
10
|
-
* Pin — big code entry, one digit per pill; a filled cell turns accent. Lives in a
|
|
10
|
+
* Pin — big code entry, one digit per pill; a filled cell turns accent. Lives on a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18).
|
|
11
11
|
* Its own behaviour, inside it: typing a digit moves to the next cell, Backspace in an empty cell moves back and clears, the
|
|
12
12
|
* arrows walk the cells, a pasted code fills them from the one it landed in; only digits stay. The first cell takes the SMS
|
|
13
13
|
* one-time-code autofill. The code is a VALUE, controlled like an input: `value` + `onChange`, or `defaultValue`.
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export * from './scaffold/Centre.js';
|
|
|
21
21
|
export * from './scaffold/slideScreen.js';
|
|
22
22
|
export * from './scaffold/LayerHistory.js';
|
|
23
23
|
export * from './cards/Card.js';
|
|
24
|
+
export * from './cards/cardGrounds.js';
|
|
25
|
+
export * from './cards/inkGrounds.js';
|
|
24
26
|
export * from './cards/CardHead.js';
|
|
25
27
|
export * from './cards/CardFoot.js';
|
|
26
28
|
export * from './cards/CardParts.js';
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,8 @@ export * from './scaffold/Centre.js';
|
|
|
25
25
|
export * from './scaffold/slideScreen.js';
|
|
26
26
|
export * from './scaffold/LayerHistory.js';
|
|
27
27
|
export * from './cards/Card.js';
|
|
28
|
+
export * from './cards/cardGrounds.js';
|
|
29
|
+
export * from './cards/inkGrounds.js';
|
|
28
30
|
export * from './cards/CardHead.js';
|
|
29
31
|
export * from './cards/CardFoot.js';
|
|
30
32
|
export * from './cards/CardParts.js';
|
package/dist/media/MapPick.d.ts
CHANGED
|
@@ -27,6 +27,6 @@ export interface MapPickProps extends Omit<ComponentProps<'label'>, 'onChange'>
|
|
|
27
27
|
* its point (`top` / `left` in the map's box, as a Callout). Drawn small, so five fit on a phone's map; the tap is the
|
|
28
28
|
* full `--tap` square round the point, and the name takes a tap too. The marks of one choice share a `name` and are one
|
|
29
29
|
* radio group (arrow keys move between them); the picked one fills with accent. It knows nothing of the map or the sheet:
|
|
30
|
-
* it goes in a `MapArea` after the engine's mount, and the app owns which place is picked.
|
|
30
|
+
* it goes in a `MapArea` after the engine's mount, and the app owns which place is picked. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18).
|
|
31
31
|
*/
|
|
32
32
|
export declare function MapPick({ children, icon, top, left, name, value, checked, defaultChecked, onChange, disabled, className, style, ...rest }: MapPickProps): import("react").JSX.Element;
|
package/dist/media/MapPick.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Icon } from '../type/Icon.js';
|
|
|
6
6
|
* its point (`top` / `left` in the map's box, as a Callout). Drawn small, so five fit on a phone's map; the tap is the
|
|
7
7
|
* full `--tap` square round the point, and the name takes a tap too. The marks of one choice share a `name` and are one
|
|
8
8
|
* radio group (arrow keys move between them); the picked one fills with accent. It knows nothing of the map or the sheet:
|
|
9
|
-
* it goes in a `MapArea` after the engine's mount, and the app owns which place is picked.
|
|
9
|
+
* it goes in a `MapArea` after the engine's mount, and the app owns which place is picked. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18).
|
|
10
10
|
*/
|
|
11
11
|
export function MapPick({ children, icon, top, left, name, value, checked, defaultChecked, onChange, disabled, className, style, ...rest }) {
|
|
12
12
|
return (_jsxs("label", { className: cx('map-pick', className), style: { top, left, ...style }, ...rest, children: [_jsx("input", { type: "radio", name: name, value: value, checked: checked, defaultChecked: defaultChecked, onChange: onChange, disabled: disabled }), _jsx("span", { className: "map-pick__mark", children: icon && _jsx(Icon, { name: icon, size: "xs" }) }), _jsx("span", { className: "map-pick__name", children: children })] }));
|
package/dist/media/Tile.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export interface TileProps extends Omit<ComponentProps<'div'>, 'onChange'> {
|
|
|
42
42
|
/** div (default), button for a tap tile — pick tiles render as a label */
|
|
43
43
|
as?: 'div' | 'button';
|
|
44
44
|
}
|
|
45
|
-
/** Tile — a big square: icon top, label bottom; `pick` toggles, `stat` shows a value, `upload` is the dashed slot. */
|
|
45
|
+
/** Tile — a big square: icon top, label bottom; `pick` toggles, `stat` shows a value, `upload` is the dashed slot. A `pick` or `upload` tile is filled in: On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
46
46
|
export declare function Tile({ icon, label, value, sub, pick, type, name, checked, defaultChecked, onChange, on, stat, upload, wide, tone, as, className, children, ...rest }: TileProps): import("react").DetailedReactHTMLElement<{
|
|
47
47
|
translate?: "yes" | "no" | undefined | undefined;
|
|
48
48
|
slot?: string | undefined | undefined;
|
package/dist/media/Tile.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Icon } from '../type/Icon.js';
|
|
|
6
6
|
export function TileGrid({ cols3, bleed, className, ...rest }) {
|
|
7
7
|
return _jsx("div", { className: cx('tile-grid', cols3 && 'tile-grid--3', bleed && 'bleed', className), ...rest });
|
|
8
8
|
}
|
|
9
|
-
/** Tile — a big square: icon top, label bottom; `pick` toggles, `stat` shows a value, `upload` is the dashed slot. */
|
|
9
|
+
/** Tile — a big square: icon top, label bottom; `pick` toggles, `stat` shows a value, `upload` is the dashed slot. A `pick` or `upload` tile is filled in: On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
10
10
|
export function Tile({ icon, label, value, sub, pick, type = 'checkbox', name, checked, defaultChecked, onChange, on, stat, upload, wide, tone, as, className, children, ...rest }) {
|
|
11
11
|
const el = pick ? 'label' : as ?? 'div';
|
|
12
12
|
const text = value != null || (label != null && sub != null)
|
package/dist/numbers/Picker.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface PickerProps extends Omit<ComponentProps<'div'>, 'onChange' | 'd
|
|
|
15
15
|
* Picker — a horizontal snap picker, the centre value big between two accent rules; children are PickerItems.
|
|
16
16
|
* It holds a VALUE, controlled like an input: `value` + `onChange`, or `defaultValue`. The value IS the item standing in the centre
|
|
17
17
|
* of the rail: the picker scrolls the chosen one there, and when the member scrolls (or taps an item) it asks for the one that
|
|
18
|
-
* settled in the centre. The CSS lights the centred item by scroll position; the app never reads the DOM to know what was picked.
|
|
18
|
+
* settled in the centre. The CSS lights the centred item by scroll position; the app never reads the DOM to know what was picked. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18).
|
|
19
19
|
*/
|
|
20
20
|
export declare function Picker({ value, defaultValue, onChange, unit, label, className, children, ...rest }: PickerProps): import("react").JSX.Element;
|
|
21
21
|
export interface PickerItemProps extends Omit<ComponentProps<'button'>, 'value'> {
|
package/dist/numbers/Picker.js
CHANGED
|
@@ -7,7 +7,7 @@ const PickerContext = createContext(null);
|
|
|
7
7
|
* Picker — a horizontal snap picker, the centre value big between two accent rules; children are PickerItems.
|
|
8
8
|
* It holds a VALUE, controlled like an input: `value` + `onChange`, or `defaultValue`. The value IS the item standing in the centre
|
|
9
9
|
* of the rail: the picker scrolls the chosen one there, and when the member scrolls (or taps an item) it asks for the one that
|
|
10
|
-
* settled in the centre. The CSS lights the centred item by scroll position; the app never reads the DOM to know what was picked.
|
|
10
|
+
* settled in the centre. The CSS lights the centred item by scroll position; the app never reads the DOM to know what was picked. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18).
|
|
11
11
|
*/
|
|
12
12
|
export function Picker({ value, defaultValue, onChange, unit, label, className, children, ...rest }) {
|
|
13
13
|
const [current, pick] = useControlled(value, defaultValue, onChange);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ComponentProps, ReactNode } from 'react';
|
|
2
2
|
export interface AvatarPickProps extends ComponentProps<'div'> {
|
|
3
|
-
/** run the strip to the
|
|
3
|
+
/** run the strip to the sheet's edges */
|
|
4
4
|
bleed?: boolean;
|
|
5
5
|
/** the strip's accessible name (aria-label) */
|
|
6
6
|
label?: string;
|
|
7
7
|
}
|
|
8
|
-
/** AvatarPick — the chooser row: big heads with name and role under, scrolling sideways; children are AvatarPickItems. */
|
|
8
|
+
/** AvatarPick — the chooser row: big heads with name and role under, scrolling sideways; children are AvatarPickItems. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
9
9
|
export declare function AvatarPick({ bleed, label, className, ...rest }: AvatarPickProps): import("react").JSX.Element;
|
|
10
10
|
export interface AvatarPickItemProps extends Omit<ComponentProps<'button'>, 'name' | 'role'> {
|
|
11
11
|
/** the name under the head */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from '../cx.js';
|
|
3
|
-
/** AvatarPick — the chooser row: big heads with name and role under, scrolling sideways; children are AvatarPickItems. */
|
|
3
|
+
/** AvatarPick — the chooser row: big heads with name and role under, scrolling sideways; children are AvatarPickItems. On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
|
|
4
4
|
export function AvatarPick({ bleed, label, className, ...rest }) {
|
|
5
5
|
return _jsx("div", { className: cx('avatar-pick', bleed && 'bleed', className), "aria-label": label, ...rest });
|
|
6
6
|
}
|
package/lib/guide.mjs
CHANGED
|
@@ -7,7 +7,7 @@ ORDER: find → scaffold or get → write → check. First question: has cardds
|
|
|
7
7
|
TOOLS: find_components · scaffold_screen · get_component · list_components · check_source. No MCP: npx cardds find|scaffold|get|list|check (all take --json).
|
|
8
8
|
What no checker sees:
|
|
9
9
|
1. cardds is a framework you take from outside. Never edit, commit to or release cardds from a consumer project, never node_modules. What cannot be set from outside is a framework gap: STOP and tell the user exactly what you needed.
|
|
10
|
-
2. Adjust from outside only, widest first: theme.css bases → knobs (--<component>-<what>) → props/slots. The theme's values are the user's call; never pick them.
|
|
10
|
+
2. Adjust from outside only, widest first: theme.css bases → knobs (--<component>-<what>) → props/slots. The theme's values are the user's call; never pick them. cardGrounds() names a card the member cannot see on that theme; inkGrounds() a word or glyph they cannot read (README "Ink follows its ground").
|
|
11
11
|
3. Every component is controlled like an input (state/open/value + its on…, or default…) and none knows another: the app owns the state and wires them.
|
|
12
12
|
4. Import components, never copy their markup or classes: a component is the look AND the behaviour.
|
|
13
13
|
5. Mobile only, big targets; never adapt it to desktop.
|
package/lint/rules.mjs
CHANGED
|
@@ -227,5 +227,42 @@ export function checkSource(source, file, { classes = carddsClasses() } = {}) {
|
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
// 10 · the sheet is the paper the member fills in (Lh 2026-10-18): every control the member fills or picks stands on a Sheet's body, a SheetStep
|
|
231
|
+
// or a stack sheet; a card is for reading and pressing buttons. A Card that is a sheet of a SheetStack IS a sheet (the stack's material)
|
|
232
|
+
const span = (re) => [...text.matchAll(re)].map((m) => ({ at: m.index, body: m.index + tagAt(text, m.index).length, end: m.index + elementAt(text, m.index).length }));
|
|
233
|
+
const stacks = span(/<SheetStack\b(?![\w.])/g), cards = span(/<Card\b(?![\w.])/g);
|
|
234
|
+
const within = (at, el) => at > el.at && at < el.end;
|
|
235
|
+
const innermost = (at) => cards.filter((c) => within(at, c)).at(-1);
|
|
236
|
+
for (const c of text.matchAll(/<(Field|Composer|ComposerArea|Pin|Dropdown|Slider|Toggle|Check|ChipPick|Calendar|DayStrip|Day|Picker|Mood|AvatarPick|FileBtn|AddRow|MapPick|Tile)\b(?![\w.])/g)) {
|
|
237
|
+
const kind = c[1] === 'Tile' ? tagAt(text, c.index).match(/\s(pick|upload)\b(?!\s*=\s*\{\s*false)/)?.[1] : null;
|
|
238
|
+
if (c[1] === 'Tile' && !kind) continue; // a plain or stat Tile is read, not filled
|
|
239
|
+
const card = innermost(c.index);
|
|
240
|
+
if (!card) continue;
|
|
241
|
+
const stack = stacks.filter((s) => card.at >= s.body && card.at < s.end).at(-1);
|
|
242
|
+
if (stack && !cards.some((o) => o !== card && within(card.at, o) && o.at >= stack.body)) continue;
|
|
243
|
+
const name = kind ? `Tile ${kind}` : c[1];
|
|
244
|
+
add('field-in-card', 'error', c.index, `<${name}> inside a <Card>: the sheet is the paper the member fills in (Lh 2026-10-18) — a card is for reading and pressing buttons. Put the ${name} straight on a Sheet's <SheetBody>, in a <SheetStep>, or on a stack sheet, with no card round it.`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 11 · a worded pager bar: back and next never carry the same signal — the forward Btn (the last) is `primary`, the back one is not
|
|
248
|
+
for (const m of text.matchAll(/<(ActionBar|ActionBarTier)\b(?![\w.])/g)) {
|
|
249
|
+
const tag = tagAt(text, m.index);
|
|
250
|
+
if (!(m[1] === 'ActionBar' ? /\svariant\s*=\s*\{?\s*["']pager["']/ : /\spager\b(?!\s*=\s*\{\s*false)/).test(tag)) continue;
|
|
251
|
+
const el = elementAt(text, m.index);
|
|
252
|
+
const btns = [...el.slice(tag.length).matchAll(/<Btn\b(?![\w.])/g)].map((b) => m.index + tag.length + b.index);
|
|
253
|
+
if (btns.length < 2) continue;
|
|
254
|
+
// the two branches of a conditional (`last ? <Btn primary>Send</Btn> : <Btn primary>Next</Btn>`) are ONE slot: both are the forward one
|
|
255
|
+
const slot = []; let n = 0;
|
|
256
|
+
btns.forEach((at, i) => { if (i && /^\s*\)?\s*:\s*\(?\s*$/.test(text.slice(btns[i - 1] + elementAt(text, btns[i - 1]).length, at))) slot.push(n); else slot.push(++n); });
|
|
257
|
+
if (n < 2) continue;
|
|
258
|
+
btns.forEach((at, i) => {
|
|
259
|
+
const forward = slot[i] === n, primary = /\sprimary\b(?!\s*=\s*\{\s*false)/.test(tagAt(text, at));
|
|
260
|
+
if (primary !== forward) add('pager-forward-primary', 'error', at, forward
|
|
261
|
+
? 'the forward <Btn> of a worded pager is not `primary`: back and next carry the same signal. The last Btn (the step it leads to) is <Btn primary>; the back one stays plain.'
|
|
262
|
+
: 'a back <Btn> of a worded pager is `primary`: only the forward one (the last) is. Drop `primary` here.');
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
230
266
|
return findings.sort((a, b) => a.line - b.line);
|
|
231
267
|
}
|
|
268
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usableapp/cardds",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.96",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "card-first mobile design system, React-first: the components in src/ are thin wrappers over the CSS contract (css/*.css stays the only truth); gallery/ shows every story live (npm run dev), tests/ measures the geometry.",
|