@usableapp/cardds 0.7.43 → 0.7.45
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 +18 -0
- package/README.md +6 -1
- package/cardds.css +1 -0
- package/css/actions.css +5 -0
- package/css/finish.css +77 -0
- package/css/layover.css +5 -5
- package/css/numbers.css +11 -7
- package/css/step.css +9 -0
- package/css/theme.template.css +2 -0
- package/css/tokens.css +14 -3
- package/dist/actions/Chip.d.ts +3 -2
- package/dist/actions/Chip.js +1 -1
- package/dist/actions/Dot.d.ts +4 -1
- package/dist/actions/Dot.js +2 -2
- package/dist/cardds.css +123 -15
- package/dist/layover/Snackbar.d.ts +3 -3
- package/dist/layover/Snackbar.js +2 -2
- package/dist/templates/CallSheet.d.ts +6 -3
- package/dist/templates/CallSheet.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
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.45 — 2026-10-03
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- **`Track` is as tall as the top bar, and a step is a mark, not a control** (Lh 2026-10-03): the pill is `--header-h` tall (60px, was 72) and each step's round is 32px (was 56 — the size of a control, it read as something to press), its glyph 18px, the connector 8px, a `num` at caption size. Nothing to change in a screen: same markup, same props. If your theme sets `--track-pad` or `--track-dot-font`, your value still wins — only the defaults moved (`--track-pad` 8px → 14px, `--track-dot-font` title → caption).
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **A `Track` as the screen's top** (Lh 2026-10-03): `<Screen fill top={<Track>…</Track>}>` — the progress of a task in several sheets stands where a bar would, in the screen's inset, and stays in sight over every sheet. A sheet at full stops 16px under it and never touches it — the same 16px it keeps from the screen's sides and from the status bar. Knobs `--track-top-gap` (16px, the room under it) · `--track-top-lead` (16px, the air over it, under the status bar). Stories `Track · OnTop` (numbers) · `OnTopIcons`.
|
|
12
|
+
- **`CallSheet top`**: the template takes the Screen's `top` (before, it had none and could not be given one) — `<CallSheet top={<Track>…</Track>} …>`. Without it nothing changes: no top, the page starts at the lead.
|
|
13
|
+
|
|
14
|
+
## 0.7.44 — 2026-10-02
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- **The signal set is the standard** (Lh 2026-10-02): four colours, four meanings, in every project and in the wireframe — `--signal-ok` green (it went right) · `--signal-warn` yellow (look out) · `--signal-error` red (it went wrong) · `--signal-info` blue (for your information). A theme may retune a shade in its `theme.css`, never a meaning. New: `--signal-ok-soft` · `--signal-warn-soft` · `--signal-error-soft` · `--signal-info-soft` — the light ground of each (the signal washed 14% into the surface); it follows the shade you set. Never the colour alone: an icon or words say it too.
|
|
18
|
+
- **`Dot tone`** and **`Chip dot`**: `ok` · `warn` · `error` · `info` — `<Chip dot="ok">พร้อม</Chip>`. `dot="a"` / `"b"` are unchanged. Story `Dot · Tones`.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **`Snackbar` stands on the ok signal**: its ground is `--signal-ok-soft` with no border (was white with a border), and its glyph is `check` in `--signal-ok` by default — before, no `icon` meant no glyph; now pass `icon={false}` for none. New knob `--snackbar-icon-ink`. A screen that passed `icon="check"` needs no change.
|
|
22
|
+
|
|
5
23
|
## 0.7.43 — 2026-10-02
|
|
6
24
|
|
|
7
25
|
### Added
|
package/README.md
CHANGED
|
@@ -306,6 +306,11 @@ whose markup has rules, because the sheet's box is the step itself:
|
|
|
306
306
|
opened, a `SheetStack` sheet opened, a `Modal` arrived. Once it has settled: the field marked `data-autofocus`, else its
|
|
307
307
|
first field, else its heading (a step's title, a modal's question) or, for a sheet come onto the screen, its handle. A field
|
|
308
308
|
that already has the cursor keeps it. One mechanic for all of them (`focusWhenActive`, src/state.ts); an app writes no focus code.
|
|
309
|
+
- **The signal set — four colours, four meanings, everywhere** (Lh 2026-10-02): `--signal-ok` green (it went right) ·
|
|
310
|
+
`--signal-warn` yellow (look out) · `--signal-error` red (it went wrong) · `--signal-info` blue (for your information).
|
|
311
|
+
The wireframe has them too. A theme may retune a shade, never a meaning. Each is dark enough for words and edges;
|
|
312
|
+
`--signal-*-soft` is its light ground (the signal washed 14% into the surface). Never the colour alone: an icon or
|
|
313
|
+
words say it too. Read by: `Field`/`Pin`/`FormNote` error, `Dot tone` / `Chip dot="ok|warn|error|info"`, `Snackbar` (ok).
|
|
309
314
|
- **Good news is a `Snackbar`, a warning or a question is a `Modal`** (Lh 2026-10-02): a `Snackbar` is a quick line over the
|
|
310
315
|
screen — something went well, said in passing. A child of the `Screen`; it slides in from the top (or `bottom`, above the
|
|
311
316
|
bottom layer), stays `duration` (4 s), slides out, never takes the cursor, never stops the member. `open` +
|
|
@@ -623,7 +628,7 @@ That file is where a project's look lives. Bases and roles:
|
|
|
623
628
|
| the size **of** things | `--tap-base` (3rem = 48) | `--tap*`, `--btn-h`, `--chip-h`, `--bar-h`, `--header-h`, `--fab`, `--icon*`, `--avatar*`, `--dot*`, `--ring*`, `--tile-*`. `--tap*` never under `--tap-floor` (44px) |
|
|
624
629
|
| faces | `--font-ui`, `--font-display` | |
|
|
625
630
|
| colour | `--color-surface`, `--color-ink`, `--color-paper`, `--color-brand`, `--color-on-brand` — no default: unset = the wireframe | every role: surface / words, the three card tones (paper · the brand washed 14% into the surface · the brand), the muted inks (the ink washed into their ground), accent, neutral actions, `--dot-a` |
|
|
626
|
-
| colour roles (the escape hatch) | `--surface`, `--on-surface(-muted)`, `--card-{1,2,3}-{bg,ink}`, `--card-border`, `--card-muted`, `--card-2-muted`, `--accent`, `--on-accent`, `--action-{bg,ink,border}`, `--pos`, `--neg`, `--dot-{a,b}` | a role set directly wins over its derivation; the tints derive from the roles |
|
|
631
|
+
| colour roles (the escape hatch) | `--surface`, `--on-surface(-muted)`, `--card-{1,2,3}-{bg,ink}`, `--card-border`, `--card-muted`, `--card-2-muted`, `--accent`, `--on-accent`, `--action-{bg,ink,border}`, `--pos`, `--neg`, `--dot-{a,b}`, `--signal-{ok,warn,error,info}` | a role set directly wins over its derivation; the tints derive from the roles |
|
|
627
632
|
|
|
628
633
|
Rules:
|
|
629
634
|
|
package/cardds.css
CHANGED
|
@@ -16,3 +16,4 @@
|
|
|
16
16
|
@import url("css/lists.css"); /* row, rows, key-value, kv grid, timeline, legend, link */
|
|
17
17
|
@import url("css/media.css"); /* cover card, quote, tile grid, fold, mosaic, wave */
|
|
18
18
|
@import url("css/layover.css"); /* ask drawer, popover menu, banner, float bar, callout */
|
|
19
|
+
@import url("css/finish.css"); /* light, shadow, edges — only under data-finish="lit" */
|
package/css/actions.css
CHANGED
|
@@ -179,6 +179,11 @@
|
|
|
179
179
|
The drop: a line box is centred on its ascender room (Thai marks), the letters beside the dot sit lower — the dot rides down to the
|
|
180
180
|
letters' own middle, in em so it follows the text size (Lh 2026-09-22: "ตัวหนังสือกับไฟเสมอกัน") */
|
|
181
181
|
.dot--b { background: var(--dot-b); }
|
|
182
|
+
/* the signal set on a dot (Lh 2026-10-02): the words beside it say what — never the colour alone */
|
|
183
|
+
.dot--ok { background: var(--signal-ok); }
|
|
184
|
+
.dot--warn { background: var(--signal-warn); }
|
|
185
|
+
.dot--error { background: var(--signal-error); }
|
|
186
|
+
.dot--info { background: var(--signal-info); }
|
|
182
187
|
.chip--on { background: var(--chip-on-bg, var(--accent)); color: var(--chip-on-ink, var(--on-accent)); border-color: transparent; }
|
|
183
188
|
/* muted: an untouched default — the words wash out, the edge stays; emphasis is the difference from the neighbours (README 13) */
|
|
184
189
|
.chip--muted { color: var(--chip-muted-ink, var(--card-muted)); }
|
package/css/finish.css
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
cardds/finish.css — FINISH: light, shadow and edges. A style of its own (Lh 2026-10-02: the plain
|
|
3
|
+
look read flat, "not finished"), apart from colour: palette.css says WHICH colours, this says how
|
|
4
|
+
the material is lit. Apply with <html data-finish="lit"> beside any palette (or none, or "own").
|
|
5
|
+
No attribute = nothing here applies — the flat look, unchanged.
|
|
6
|
+
|
|
7
|
+
The light comes from above:
|
|
8
|
+
- a thing that stands UP (card, sheet, button) has a lit top edge, a hairline ring and a shadow
|
|
9
|
+
that grows with its height: card 1 · sheet / float 2 — the layers cardds already has;
|
|
10
|
+
- a thing that is SUNK (a field, a pin cell) has its shadow inside, under its top edge;
|
|
11
|
+
- information inside a card stays flat: only a container or a control gets an edge.
|
|
12
|
+
Shadows are mixed from the ink (--on-surface), never plain black, so they sit in the palette.
|
|
13
|
+
============================================================ */
|
|
14
|
+
|
|
15
|
+
[data-finish="lit"] {
|
|
16
|
+
--_shade: var(--on-surface);
|
|
17
|
+
--finish-edge: inset 0 1px 0 rgb(255 255 255 / 0.55); /* the lit top edge */
|
|
18
|
+
--finish-ring: 0 0 0 1px color-mix(in srgb, var(--_shade) 7%, transparent); /* the hairline round a raised thing */
|
|
19
|
+
|
|
20
|
+
/* the three shadows every component already reads — layered: one close and crisp, one far and soft */
|
|
21
|
+
--shadow-card: var(--finish-edge), var(--finish-ring),
|
|
22
|
+
0 1px 2px color-mix(in srgb, var(--_shade) 8%, transparent),
|
|
23
|
+
0 0.375rem 0.75rem -0.125rem color-mix(in srgb, var(--_shade) 8%, transparent),
|
|
24
|
+
0 1.25rem 2.5rem -0.75rem color-mix(in srgb, var(--_shade) 14%, transparent);
|
|
25
|
+
--shadow-float: var(--finish-edge), var(--finish-ring),
|
|
26
|
+
0 1px 2px color-mix(in srgb, var(--_shade) 12%, transparent),
|
|
27
|
+
0 0.5rem 1.25rem -0.25rem color-mix(in srgb, var(--_shade) 22%, transparent);
|
|
28
|
+
--shadow-sheet: var(--finish-edge), var(--finish-ring),
|
|
29
|
+
0 -1px 2px color-mix(in srgb, var(--_shade) 6%, transparent),
|
|
30
|
+
0 -0.75rem 2.5rem color-mix(in srgb, var(--_shade) 18%, transparent);
|
|
31
|
+
|
|
32
|
+
/* a control: its own small lift, and the sheen that runs down its face */
|
|
33
|
+
--finish-control: var(--finish-edge), var(--finish-ring),
|
|
34
|
+
0 1px 2px color-mix(in srgb, var(--_shade) 12%, transparent),
|
|
35
|
+
0 0.25rem 0.5rem -0.125rem color-mix(in srgb, var(--_shade) 12%, transparent);
|
|
36
|
+
--finish-control-down: var(--finish-ring), inset 0 1px 2px color-mix(in srgb, var(--_shade) 18%, transparent);
|
|
37
|
+
--finish-sheen: linear-gradient(to bottom, rgb(255 255 255 / 0.16), rgb(255 255 255 / 0) 60%);
|
|
38
|
+
--finish-sunk: inset 0 1px 2px color-mix(in srgb, var(--_shade) 14%, transparent), 0 1px 0 rgb(255 255 255 / 0.5);
|
|
39
|
+
|
|
40
|
+
/* the shadow is the edge: the wireframe's lines step back (as in every colour palette) */
|
|
41
|
+
--card-border: transparent;
|
|
42
|
+
--action-border: transparent;
|
|
43
|
+
--outline: color-mix(in srgb, currentColor 14%, transparent);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* ---- raised: cards, sheets, the stack ---- */
|
|
47
|
+
[data-finish="lit"] :is(.card, .band-stack) { box-shadow: var(--shadow-card); }
|
|
48
|
+
[data-finish="lit"] .band-stack > .card--band { box-shadow: none; }
|
|
49
|
+
/* a sheet lies on the floor and is lit from above: its shadow falls UP the screen, onto what it covers */
|
|
50
|
+
[data-finish="lit"] .sheet { box-shadow: var(--sheet-shadow, var(--shadow-sheet)); }
|
|
51
|
+
[data-finish="lit"] .modal { box-shadow: var(--modal-shadow, var(--shadow-float)); }
|
|
52
|
+
[data-finish="lit"] .sheet-stack > .card,
|
|
53
|
+
[data-finish="lit"] .sheet-stack > :not(.card, .sheet-stack__base) > .card { box-shadow: var(--shadow-sheet); }
|
|
54
|
+
|
|
55
|
+
/* ---- controls: a filled button stands up, and goes down under the finger ---- */
|
|
56
|
+
[data-finish="lit"] .btn:not(.btn--quiet),
|
|
57
|
+
[data-finish="lit"] .icon-btn:not(.icon-btn-group > *, .stack-sheet__tab:not(.icon-btn--invert)) {
|
|
58
|
+
background-image: var(--finish-sheen);
|
|
59
|
+
box-shadow: var(--finish-control);
|
|
60
|
+
}
|
|
61
|
+
/* the main button throws its own colour */
|
|
62
|
+
[data-finish="lit"] .btn--primary,
|
|
63
|
+
[data-finish="lit"] .icon-btn--invert:not(.icon-btn-group > *) {
|
|
64
|
+
box-shadow: var(--finish-edge),
|
|
65
|
+
0 1px 2px color-mix(in srgb, var(--_shade) 20%, transparent),
|
|
66
|
+
0 0.5rem 1rem -0.25rem color-mix(in srgb, var(--accent) 45%, transparent);
|
|
67
|
+
}
|
|
68
|
+
[data-finish="lit"] :is(.btn:not(.btn--quiet), .icon-btn:not(.icon-btn-group > *, .stack-sheet__tab:not(.icon-btn--invert))):active:not(:disabled) {
|
|
69
|
+
background-image: none;
|
|
70
|
+
box-shadow: var(--finish-control-down);
|
|
71
|
+
}
|
|
72
|
+
[data-finish="lit"] .icon-btn-group { box-shadow: var(--finish-control); }
|
|
73
|
+
[data-finish="lit"] .toggle { box-shadow: var(--finish-sunk); }
|
|
74
|
+
[data-finish="lit"] .toggle::after { box-shadow: 0 1px 2px color-mix(in srgb, var(--_shade) 35%, transparent); }
|
|
75
|
+
|
|
76
|
+
/* ---- sunk: what the member types into ---- */
|
|
77
|
+
[data-finish="lit"] :is(.field, .pin__cell) { box-shadow: var(--finish-sunk); }
|
package/css/layover.css
CHANGED
|
@@ -312,7 +312,7 @@
|
|
|
312
312
|
<div class="snackbar snackbar--open" role="status"><div class="snackbar__body"><svg class="icon"/>
|
|
313
313
|
<span class="snackbar__words"><span class="snackbar__text">…</span><span class="snackbar__caption">…</span></span></div></div>
|
|
314
314
|
A LAYER of the screen (its child), over the top bar; --bottom = at the foot, above the screen's bottom layer (--bottom-h).
|
|
315
|
-
Rigid like a sheet: `translate` only. `button.snackbar__body` = it can be tapped. Never a warning — that is a Modal. */
|
|
315
|
+
Its ground is the OK signal's soft ground (--signal-ok-soft), its glyph the signal itself: the sign that it went right. Rigid like a sheet: `translate` only. `button.snackbar__body` = it can be tapped. Never a warning — that is a Modal. */
|
|
316
316
|
.snackbar {
|
|
317
317
|
--_out: -1;
|
|
318
318
|
position: fixed; inset-inline: 0; top: calc(var(--sp-3) + var(--safe-top));
|
|
@@ -330,15 +330,15 @@
|
|
|
330
330
|
display: flex; align-items: center; gap: var(--snackbar-gap, var(--sp-3));
|
|
331
331
|
max-width: 100%; min-height: var(--snackbar-min-h, var(--tap));
|
|
332
332
|
padding: var(--snackbar-pad-y, var(--sp-2)) var(--snackbar-pad-x, var(--sp-5));
|
|
333
|
-
border: var(--border-w) solid var(--snackbar-border,
|
|
333
|
+
border: var(--border-w) solid var(--snackbar-border, transparent);
|
|
334
334
|
border-radius: var(--snackbar-radius, var(--r-card));
|
|
335
|
-
background: var(--snackbar-bg, var(--
|
|
336
|
-
color: var(--snackbar-ink, var(--
|
|
335
|
+
background: var(--snackbar-bg, var(--signal-ok-soft));
|
|
336
|
+
color: var(--snackbar-ink, var(--on-surface));
|
|
337
337
|
box-shadow: var(--snackbar-shadow, var(--shadow-float));
|
|
338
338
|
font: inherit; text-align: start;
|
|
339
339
|
pointer-events: auto;
|
|
340
340
|
}
|
|
341
|
-
.snackbar__body > .icon { flex: none; }
|
|
341
|
+
.snackbar__body > .icon { flex: none; color: var(--snackbar-icon-ink, var(--signal-ok)); }
|
|
342
342
|
.snackbar__words { display: grid; min-width: 0; overflow-wrap: anywhere; }
|
|
343
343
|
.snackbar__text { font: var(--snackbar-text-font, var(--type-body)); }
|
|
344
344
|
.snackbar__caption { font: var(--snackbar-caption-font, var(--type-caption)); opacity: var(--dim); }
|
package/css/numbers.css
CHANGED
|
@@ -39,8 +39,12 @@
|
|
|
39
39
|
.ring--xs .ring__value { font: var(--type-caption); }
|
|
40
40
|
|
|
41
41
|
/* ---- track: step progress in a pill — done · done · todo ----
|
|
42
|
-
<ol class="track"><li class="track__step is-done">icon</li> …</ol>
|
|
42
|
+
<ol class="track"><li class="track__step is-done">icon</li> …</ol>
|
|
43
|
+
As tall as the top bar (--header-h), and a step is a MARK, not a control: its round is well under
|
|
44
|
+
the tap floor, so it reads as progress and never as something to press (Lh 2026-10-03). */
|
|
43
45
|
.track {
|
|
46
|
+
--_step: calc(var(--tap-base) * 2 / 3); /* 32 the round of one step */
|
|
47
|
+
--_pad: calc((var(--header-h) - var(--_step)) / 2); /* 14 round + its room = the top bar's height; the end rounds sit concentric in the pill */
|
|
44
48
|
--_bg: var(--card-3-bg);
|
|
45
49
|
--_ink: var(--card-3-ink);
|
|
46
50
|
/* the track lays down its own dark ground, so like the dark card slot it
|
|
@@ -51,7 +55,7 @@
|
|
|
51
55
|
--on-accent: var(--_bg);
|
|
52
56
|
list-style: none;
|
|
53
57
|
margin: 0;
|
|
54
|
-
padding: var(--track-pad, var(--
|
|
58
|
+
padding: var(--track-pad, var(--_pad));
|
|
55
59
|
display: flex;
|
|
56
60
|
align-items: center;
|
|
57
61
|
border-radius: var(--track-radius, var(--r-chip));
|
|
@@ -70,22 +74,22 @@
|
|
|
70
74
|
.track__step::before {
|
|
71
75
|
content: "";
|
|
72
76
|
flex: 1;
|
|
73
|
-
height: var(--sp-
|
|
77
|
+
height: var(--sp-2);
|
|
74
78
|
background: var(--ink-rail); /* the track's own ink at 30% — identical to color-mix(in srgb, var(--_ink) 30%, transparent): --ink-rail already reads currentColor, and .track sets color to --_ink */
|
|
75
79
|
}
|
|
76
80
|
.track__step:first-child::before { display: none; }
|
|
77
81
|
.track__step.is-done::before { background: var(--accent); }
|
|
78
82
|
.track__step > .icon,
|
|
79
83
|
.track__step > .track__dot {
|
|
80
|
-
width: var(--
|
|
81
|
-
height: var(--
|
|
84
|
+
width: var(--_step);
|
|
85
|
+
height: var(--_step);
|
|
82
86
|
flex: none;
|
|
83
|
-
padding: calc(
|
|
87
|
+
padding: calc(var(--_step) * 7 / 32); /* the glyph = 18 in the 32 round */
|
|
84
88
|
border-radius: 50%;
|
|
85
89
|
background: var(--ink-rail);
|
|
86
90
|
color: var(--_ink);
|
|
87
91
|
}
|
|
88
|
-
.track__step > .track__dot { display: grid; place-items: center; padding: 0; font: var(--track-dot-font, var(--type-
|
|
92
|
+
.track__step > .track__dot { display: grid; place-items: center; padding: 0; font: var(--track-dot-font, var(--type-caption)); font-variant-numeric: tabular-nums; } /* a number in the round (TrackStep num) */
|
|
89
93
|
.track__step.is-done > .icon,
|
|
90
94
|
.track__step.is-done > .track__dot { background: var(--accent); color: var(--on-accent); }
|
|
91
95
|
|
package/css/step.css
CHANGED
|
@@ -141,6 +141,15 @@
|
|
|
141
141
|
them — timebank #137); the words keep the screen's gap as their lead (--_lead, .appbar + .step above) */
|
|
142
142
|
.screen--fill > .appbar:has(+ .step) { margin-bottom: calc(-1 * var(--screen-gap)); }
|
|
143
143
|
.screen--fill > .appbar + .sheet-stage { margin-top: 0; }
|
|
144
|
+
/* a TRACK as the screen's TOP (Lh 2026-10-03): the walk's progress stands where a bar would, a row of the column, in the screen's
|
|
145
|
+
inset — and it never touches what comes up under it: the step (and so a sheet at full) starts --track-top-gap below it. That
|
|
146
|
+
room is the track's own, not the screen's gap; --track-top-lead is the air over it, under the status bar. Both = the screen's side inset (16): the pill
|
|
147
|
+
stands the same distance from everything around it (Lh 2026-10-03: 8 over was too little, 24 under too much). */
|
|
148
|
+
.screen--fill > .track:first-child {
|
|
149
|
+
flex: none;
|
|
150
|
+
margin-top: var(--track-top-lead, var(--sp-4));
|
|
151
|
+
margin-bottom: calc(var(--track-top-gap, var(--sp-4)) - var(--screen-gap));
|
|
152
|
+
}
|
|
144
153
|
/* a walk page that OPENS on a picture (Lh 2026-09-23): the picture takes the lead's room — as wide as the content (edge to edge), from the top edge, as tall as
|
|
145
154
|
the lead + the picture's own height (--page-media-h), square — so the words start where they always did */
|
|
146
155
|
.step > .pages > .page:has(> :is(img, .ph):first-child) { margin-top: 0; }
|
package/css/theme.template.css
CHANGED
|
@@ -49,5 +49,7 @@
|
|
|
49
49
|
--card-border --card-muted --card-2-muted
|
|
50
50
|
--accent --on-accent --action-bg --action-ink --action-border
|
|
51
51
|
--pos --neg --dot-a --dot-b
|
|
52
|
+
--signal-ok --signal-warn --signal-error --signal-info (the signal set: green · yellow · red · blue — retune the shade,
|
|
53
|
+
keep the meaning; dark enough for words. --signal-*-soft, the light ground of each, follows by itself)
|
|
52
54
|
--dim (0.7: how far secondary words fade on a dark card, a drawer, a tile's sub line — one share for all) */
|
|
53
55
|
}
|
package/css/tokens.css
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
--tap-floor: 2.75rem; /* 44 the HIG floor — no --tap-base takes a touch target under it */
|
|
47
47
|
--tap: max(var(--tap-floor), var(--tap-base)); /* 48 primary touch target */
|
|
48
48
|
--tap-sm: max(var(--tap-floor), calc(var(--tap-base) * 11 / 12)); /* 44 minimum touch target */
|
|
49
|
-
--tap-lg: max(var(--tap-floor), calc(var(--tap-base) * 7 / 6)); /* 56 hero controls:
|
|
49
|
+
--tap-lg: max(var(--tap-floor), calc(var(--tap-base) * 7 / 6)); /* 56 hero controls: send, field with action */
|
|
50
50
|
--btn-h: calc(var(--tap-base) * 13 / 12); /* 52 button height */
|
|
51
51
|
--btn-xl-h: calc(var(--tap-base) * 11 / 6); /* 88 the one giant CTA */
|
|
52
52
|
--chip-h: calc(var(--tap-base) * 19 / 24); /* 38 chip height */
|
|
@@ -210,12 +210,15 @@
|
|
|
210
210
|
--neg: #3d3d3d;
|
|
211
211
|
--dot-a: var(--color-brand, #141414);
|
|
212
212
|
--dot-b: #8a8a8a;
|
|
213
|
-
|
|
213
|
+
/* THE SIGNAL SET (Lh 2026-09-25; the standard, Lh 2026-10-02): ok (green) · warn (yellow) · error (red) · info (blue) — the same four
|
|
214
|
+
meanings in every project, wireframe included; a theme may retune the shade, never the meaning. Each is dark enough for words and
|
|
215
|
+
edges and comes with an icon and words, never the colour alone. `--signal-*-soft` (below :root) = its light ground. */
|
|
216
|
+
--signal-warn: #8a6d00;
|
|
214
217
|
--signal-ok: #2e7d32;
|
|
215
218
|
--signal-info: #2f5f9e;
|
|
216
219
|
/* highlights (Lh 2026-09-25): four atmosphere colours a palette may set for decoration — a count, a mark, a chart dot; the wireframe keeps them grey */
|
|
217
220
|
--highlight-1: #3d3d3d; --highlight-2: #8a8a8a; --highlight-3: #5f5f5f; --highlight-4: #b0b0b0;
|
|
218
|
-
--signal-error: #c62828; /*
|
|
221
|
+
--signal-error: #c62828; /* something went wrong — a field's edge, the words that say what */
|
|
219
222
|
|
|
220
223
|
/* derived tints — read the element's own ink, so they work on any slot */
|
|
221
224
|
--outline: var(--action-border); /* control edges (palette.css softens) */
|
|
@@ -244,6 +247,14 @@
|
|
|
244
247
|
bars take over the page when the app runs edge to edge (viewport-fit=cover: Android Chrome, an iPhone home-screen app).
|
|
245
248
|
PUBLISHED: backgrounds still run to the edge; what the member reads or taps stands clear of them. 0 without cover.
|
|
246
249
|
--_safe-b-open = the bottom inset the keyboard does NOT already cover (a keyboard up hides the gesture bar). */
|
|
250
|
+
/* a signal's SOFT ground: the signal washed 14% into the surface (tone 2's formula) — what a Snackbar stands on. Re-derived where a
|
|
251
|
+
palette is set, so a palette's own signal shade gives its own soft */
|
|
252
|
+
:root, [data-palette] {
|
|
253
|
+
--signal-ok-soft: color-mix(in srgb, var(--signal-ok) 14%, var(--surface));
|
|
254
|
+
--signal-warn-soft: color-mix(in srgb, var(--signal-warn) 14%, var(--surface));
|
|
255
|
+
--signal-error-soft: color-mix(in srgb, var(--signal-error) 14%, var(--surface));
|
|
256
|
+
--signal-info-soft: color-mix(in srgb, var(--signal-info) 14%, var(--surface));
|
|
257
|
+
}
|
|
247
258
|
:root { --safe-top: env(safe-area-inset-top, 0px); --safe-bottom: env(safe-area-inset-bottom, 0px); }
|
|
248
259
|
* { --_safe-b-open: max(0px, var(--safe-bottom) - var(--kb, 0px)); }
|
|
249
260
|
|
package/dist/actions/Chip.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type ComponentProps, type ReactNode } from 'react';
|
|
2
|
+
import { type DotTone } from './Dot.js';
|
|
2
3
|
export interface ChipProps extends ComponentProps<'span'> {
|
|
3
4
|
/** filled accent: the selected / live state */
|
|
4
5
|
on?: boolean;
|
|
5
6
|
/** quiet: an untouched default value — muted words, the thin edge, nothing to find the eye (README rule 13) */
|
|
6
7
|
muted?: boolean;
|
|
7
|
-
/** a leading signal dot: `a` (the accent signal)
|
|
8
|
-
dot?: 'a' | 'b';
|
|
8
|
+
/** a leading signal dot: `a` (the accent signal), `b` (the secondary), or one of the signal set — ok · warn · error · info */
|
|
9
|
+
dot?: 'a' | 'b' | DotTone;
|
|
9
10
|
/** the element: span (default), a link, or a button */
|
|
10
11
|
as?: 'span' | 'a' | 'button';
|
|
11
12
|
href?: string;
|
package/dist/actions/Chip.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Dot } from './Dot.js';
|
|
|
5
5
|
/** 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). */
|
|
6
6
|
export function Chip({ on, muted, dot, as, href, className, children, ...rest }) {
|
|
7
7
|
const el = as ?? (href ? 'a' : 'span');
|
|
8
|
-
return createElement(el, { href, type: el === 'button' ? 'button' : undefined, className: cx('chip', on && 'chip--on', muted && 'chip--muted', className), ...rest }, dot && _jsx(Dot, { b: dot === 'b' }), children);
|
|
8
|
+
return createElement(el, { href, type: el === 'button' ? 'button' : undefined, className: cx('chip', on && 'chip--on', muted && 'chip--muted', className), ...rest }, dot && _jsx(Dot, { b: dot === 'b', tone: dot === 'a' || dot === 'b' ? undefined : dot }), children);
|
|
9
9
|
}
|
|
10
10
|
/** ChipPick — a chip that is a choice: full-width in a ChipGrid (9:00 · 9:30 · …), or `sign` on a ChipRow (+ off, × on — the
|
|
11
11
|
* interest picker); the picked one fills with accent. A native radio/checkbox inside the label carries the state. */
|
package/dist/actions/Dot.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
|
+
export type DotTone = 'ok' | 'warn' | 'error' | 'info';
|
|
2
3
|
export interface DotProps extends ComponentProps<'span'> {
|
|
3
4
|
/** the secondary signal colour */
|
|
4
5
|
b?: boolean;
|
|
6
|
+
/** a signal from the standard set: ok (green) · warn (yellow) · error (red) · info (blue) — the words beside it say what */
|
|
7
|
+
tone?: DotTone;
|
|
5
8
|
}
|
|
6
9
|
/** Dot — the signal dot in chips, legends and row metas. */
|
|
7
|
-
export declare function Dot({ b, className, ...rest }: DotProps): import("react").JSX.Element;
|
|
10
|
+
export declare function Dot({ b, tone, className, ...rest }: DotProps): import("react").JSX.Element;
|
package/dist/actions/Dot.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from '../cx.js';
|
|
3
3
|
/** Dot — the signal dot in chips, legends and row metas. */
|
|
4
|
-
export function Dot({ b, className, ...rest }) {
|
|
5
|
-
return _jsx("span", { className: cx('dot', b && 'dot--b', className), ...rest });
|
|
4
|
+
export function Dot({ b, tone, className, ...rest }) {
|
|
5
|
+
return _jsx("span", { className: cx('dot', b && 'dot--b', tone && `dot--${tone}`, className), ...rest });
|
|
6
6
|
}
|
package/dist/cardds.css
CHANGED
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
--tap-floor: 2.75rem; /* 44 the HIG floor — no --tap-base takes a touch target under it */
|
|
88
88
|
--tap: max(var(--tap-floor), var(--tap-base)); /* 48 primary touch target */
|
|
89
89
|
--tap-sm: max(var(--tap-floor), calc(var(--tap-base) * 11 / 12)); /* 44 minimum touch target */
|
|
90
|
-
--tap-lg: max(var(--tap-floor), calc(var(--tap-base) * 7 / 6)); /* 56 hero controls:
|
|
90
|
+
--tap-lg: max(var(--tap-floor), calc(var(--tap-base) * 7 / 6)); /* 56 hero controls: send, field with action */
|
|
91
91
|
--btn-h: calc(var(--tap-base) * 13 / 12); /* 52 button height */
|
|
92
92
|
--btn-xl-h: calc(var(--tap-base) * 11 / 6); /* 88 the one giant CTA */
|
|
93
93
|
--chip-h: calc(var(--tap-base) * 19 / 24); /* 38 chip height */
|
|
@@ -251,12 +251,15 @@
|
|
|
251
251
|
--neg: #3d3d3d;
|
|
252
252
|
--dot-a: var(--color-brand, #141414);
|
|
253
253
|
--dot-b: #8a8a8a;
|
|
254
|
-
|
|
254
|
+
/* THE SIGNAL SET (Lh 2026-09-25; the standard, Lh 2026-10-02): ok (green) · warn (yellow) · error (red) · info (blue) — the same four
|
|
255
|
+
meanings in every project, wireframe included; a theme may retune the shade, never the meaning. Each is dark enough for words and
|
|
256
|
+
edges and comes with an icon and words, never the colour alone. `--signal-*-soft` (below :root) = its light ground. */
|
|
257
|
+
--signal-warn: #8a6d00;
|
|
255
258
|
--signal-ok: #2e7d32;
|
|
256
259
|
--signal-info: #2f5f9e;
|
|
257
260
|
/* highlights (Lh 2026-09-25): four atmosphere colours a palette may set for decoration — a count, a mark, a chart dot; the wireframe keeps them grey */
|
|
258
261
|
--highlight-1: #3d3d3d; --highlight-2: #8a8a8a; --highlight-3: #5f5f5f; --highlight-4: #b0b0b0;
|
|
259
|
-
--signal-error: #c62828; /*
|
|
262
|
+
--signal-error: #c62828; /* something went wrong — a field's edge, the words that say what */
|
|
260
263
|
|
|
261
264
|
/* derived tints — read the element's own ink, so they work on any slot */
|
|
262
265
|
--outline: var(--action-border); /* control edges (palette.css softens) */
|
|
@@ -285,6 +288,14 @@
|
|
|
285
288
|
bars take over the page when the app runs edge to edge (viewport-fit=cover: Android Chrome, an iPhone home-screen app).
|
|
286
289
|
PUBLISHED: backgrounds still run to the edge; what the member reads or taps stands clear of them. 0 without cover.
|
|
287
290
|
--_safe-b-open = the bottom inset the keyboard does NOT already cover (a keyboard up hides the gesture bar). */
|
|
291
|
+
/* a signal's SOFT ground: the signal washed 14% into the surface (tone 2's formula) — what a Snackbar stands on. Re-derived where a
|
|
292
|
+
palette is set, so a palette's own signal shade gives its own soft */
|
|
293
|
+
:root, [data-palette] {
|
|
294
|
+
--signal-ok-soft: color-mix(in srgb, var(--signal-ok) 14%, var(--surface));
|
|
295
|
+
--signal-warn-soft: color-mix(in srgb, var(--signal-warn) 14%, var(--surface));
|
|
296
|
+
--signal-error-soft: color-mix(in srgb, var(--signal-error) 14%, var(--surface));
|
|
297
|
+
--signal-info-soft: color-mix(in srgb, var(--signal-info) 14%, var(--surface));
|
|
298
|
+
}
|
|
288
299
|
:root { --safe-top: env(safe-area-inset-top, 0px); --safe-bottom: env(safe-area-inset-bottom, 0px); }
|
|
289
300
|
* { --_safe-b-open: max(0px, var(--safe-bottom) - var(--kb, 0px)); }
|
|
290
301
|
|
|
@@ -1888,6 +1899,15 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
1888
1899
|
them — timebank #137); the words keep the screen's gap as their lead (--_lead, .appbar + .step above) */
|
|
1889
1900
|
.screen--fill > .appbar:has(+ .step) { margin-bottom: calc(-1 * var(--screen-gap)); }
|
|
1890
1901
|
.screen--fill > .appbar + .sheet-stage { margin-top: 0; }
|
|
1902
|
+
/* a TRACK as the screen's TOP (Lh 2026-10-03): the walk's progress stands where a bar would, a row of the column, in the screen's
|
|
1903
|
+
inset — and it never touches what comes up under it: the step (and so a sheet at full) starts --track-top-gap below it. That
|
|
1904
|
+
room is the track's own, not the screen's gap; --track-top-lead is the air over it, under the status bar. Both = the screen's side inset (16): the pill
|
|
1905
|
+
stands the same distance from everything around it (Lh 2026-10-03: 8 over was too little, 24 under too much). */
|
|
1906
|
+
.screen--fill > .track:first-child {
|
|
1907
|
+
flex: none;
|
|
1908
|
+
margin-top: var(--track-top-lead, var(--sp-4));
|
|
1909
|
+
margin-bottom: calc(var(--track-top-gap, var(--sp-4)) - var(--screen-gap));
|
|
1910
|
+
}
|
|
1891
1911
|
/* a walk page that OPENS on a picture (Lh 2026-09-23): the picture takes the lead's room — as wide as the content (edge to edge), from the top edge, as tall as
|
|
1892
1912
|
the lead + the picture's own height (--page-media-h), square — so the words start where they always did */
|
|
1893
1913
|
.step > .pages > .page:has(> :is(img, .ph):first-child) { margin-top: 0; }
|
|
@@ -2234,6 +2254,11 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
2234
2254
|
The drop: a line box is centred on its ascender room (Thai marks), the letters beside the dot sit lower — the dot rides down to the
|
|
2235
2255
|
letters' own middle, in em so it follows the text size (Lh 2026-09-22: "ตัวหนังสือกับไฟเสมอกัน") */
|
|
2236
2256
|
.dot--b { background: var(--dot-b); }
|
|
2257
|
+
/* the signal set on a dot (Lh 2026-10-02): the words beside it say what — never the colour alone */
|
|
2258
|
+
.dot--ok { background: var(--signal-ok); }
|
|
2259
|
+
.dot--warn { background: var(--signal-warn); }
|
|
2260
|
+
.dot--error { background: var(--signal-error); }
|
|
2261
|
+
.dot--info { background: var(--signal-info); }
|
|
2237
2262
|
.chip--on { background: var(--chip-on-bg, var(--accent)); color: var(--chip-on-ink, var(--on-accent)); border-color: transparent; }
|
|
2238
2263
|
/* muted: an untouched default — the words wash out, the edge stays; emphasis is the difference from the neighbours (README 13) */
|
|
2239
2264
|
.chip--muted { color: var(--chip-muted-ink, var(--card-muted)); }
|
|
@@ -2500,8 +2525,12 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
2500
2525
|
.ring--xs .ring__value { font: var(--type-caption); }
|
|
2501
2526
|
|
|
2502
2527
|
/* ---- track: step progress in a pill — done · done · todo ----
|
|
2503
|
-
<ol class="track"><li class="track__step is-done">icon</li> …</ol>
|
|
2528
|
+
<ol class="track"><li class="track__step is-done">icon</li> …</ol>
|
|
2529
|
+
As tall as the top bar (--header-h), and a step is a MARK, not a control: its round is well under
|
|
2530
|
+
the tap floor, so it reads as progress and never as something to press (Lh 2026-10-03). */
|
|
2504
2531
|
.track {
|
|
2532
|
+
--_step: calc(var(--tap-base) * 2 / 3); /* 32 the round of one step */
|
|
2533
|
+
--_pad: calc((var(--header-h) - var(--_step)) / 2); /* 14 round + its room = the top bar's height; the end rounds sit concentric in the pill */
|
|
2505
2534
|
--_bg: var(--card-3-bg);
|
|
2506
2535
|
--_ink: var(--card-3-ink);
|
|
2507
2536
|
/* the track lays down its own dark ground, so like the dark card slot it
|
|
@@ -2512,7 +2541,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
2512
2541
|
--on-accent: var(--_bg);
|
|
2513
2542
|
list-style: none;
|
|
2514
2543
|
margin: 0;
|
|
2515
|
-
padding: var(--track-pad, var(--
|
|
2544
|
+
padding: var(--track-pad, var(--_pad));
|
|
2516
2545
|
display: flex;
|
|
2517
2546
|
align-items: center;
|
|
2518
2547
|
border-radius: var(--track-radius, var(--r-chip));
|
|
@@ -2531,22 +2560,22 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
2531
2560
|
.track__step::before {
|
|
2532
2561
|
content: "";
|
|
2533
2562
|
flex: 1;
|
|
2534
|
-
height: var(--sp-
|
|
2563
|
+
height: var(--sp-2);
|
|
2535
2564
|
background: var(--ink-rail); /* the track's own ink at 30% — identical to color-mix(in srgb, var(--_ink) 30%, transparent): --ink-rail already reads currentColor, and .track sets color to --_ink */
|
|
2536
2565
|
}
|
|
2537
2566
|
.track__step:first-child::before { display: none; }
|
|
2538
2567
|
.track__step.is-done::before { background: var(--accent); }
|
|
2539
2568
|
.track__step > .icon,
|
|
2540
2569
|
.track__step > .track__dot {
|
|
2541
|
-
width: var(--
|
|
2542
|
-
height: var(--
|
|
2570
|
+
width: var(--_step);
|
|
2571
|
+
height: var(--_step);
|
|
2543
2572
|
flex: none;
|
|
2544
|
-
padding: calc(
|
|
2573
|
+
padding: calc(var(--_step) * 7 / 32); /* the glyph = 18 in the 32 round */
|
|
2545
2574
|
border-radius: 50%;
|
|
2546
2575
|
background: var(--ink-rail);
|
|
2547
2576
|
color: var(--_ink);
|
|
2548
2577
|
}
|
|
2549
|
-
.track__step > .track__dot { display: grid; place-items: center; padding: 0; font: var(--track-dot-font, var(--type-
|
|
2578
|
+
.track__step > .track__dot { display: grid; place-items: center; padding: 0; font: var(--track-dot-font, var(--type-caption)); font-variant-numeric: tabular-nums; } /* a number in the round (TrackStep num) */
|
|
2550
2579
|
.track__step.is-done > .icon,
|
|
2551
2580
|
.track__step.is-done > .track__dot { background: var(--accent); color: var(--on-accent); }
|
|
2552
2581
|
|
|
@@ -3939,7 +3968,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
3939
3968
|
<div class="snackbar snackbar--open" role="status"><div class="snackbar__body"><svg class="icon"/>
|
|
3940
3969
|
<span class="snackbar__words"><span class="snackbar__text">…</span><span class="snackbar__caption">…</span></span></div></div>
|
|
3941
3970
|
A LAYER of the screen (its child), over the top bar; --bottom = at the foot, above the screen's bottom layer (--bottom-h).
|
|
3942
|
-
Rigid like a sheet: `translate` only. `button.snackbar__body` = it can be tapped. Never a warning — that is a Modal. */
|
|
3971
|
+
Its ground is the OK signal's soft ground (--signal-ok-soft), its glyph the signal itself: the sign that it went right. Rigid like a sheet: `translate` only. `button.snackbar__body` = it can be tapped. Never a warning — that is a Modal. */
|
|
3943
3972
|
.snackbar {
|
|
3944
3973
|
--_out: -1;
|
|
3945
3974
|
position: fixed; inset-inline: 0; top: calc(var(--sp-3) + var(--safe-top));
|
|
@@ -3957,17 +3986,96 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
3957
3986
|
display: flex; align-items: center; gap: var(--snackbar-gap, var(--sp-3));
|
|
3958
3987
|
max-width: 100%; min-height: var(--snackbar-min-h, var(--tap));
|
|
3959
3988
|
padding: var(--snackbar-pad-y, var(--sp-2)) var(--snackbar-pad-x, var(--sp-5));
|
|
3960
|
-
border: var(--border-w) solid var(--snackbar-border,
|
|
3989
|
+
border: var(--border-w) solid var(--snackbar-border, transparent);
|
|
3961
3990
|
border-radius: var(--snackbar-radius, var(--r-card));
|
|
3962
|
-
background: var(--snackbar-bg, var(--
|
|
3963
|
-
color: var(--snackbar-ink, var(--
|
|
3991
|
+
background: var(--snackbar-bg, var(--signal-ok-soft));
|
|
3992
|
+
color: var(--snackbar-ink, var(--on-surface));
|
|
3964
3993
|
box-shadow: var(--snackbar-shadow, var(--shadow-float));
|
|
3965
3994
|
font: inherit; text-align: start;
|
|
3966
3995
|
pointer-events: auto;
|
|
3967
3996
|
}
|
|
3968
|
-
.snackbar__body > .icon { flex: none; }
|
|
3997
|
+
.snackbar__body > .icon { flex: none; color: var(--snackbar-icon-ink, var(--signal-ok)); }
|
|
3969
3998
|
.snackbar__words { display: grid; min-width: 0; overflow-wrap: anywhere; }
|
|
3970
3999
|
.snackbar__text { font: var(--snackbar-text-font, var(--type-body)); }
|
|
3971
4000
|
.snackbar__caption { font: var(--snackbar-caption-font, var(--type-caption)); opacity: var(--dim); }
|
|
3972
4001
|
@media (prefers-reduced-motion: reduce) { .snackbar { transition: none; } }
|
|
3973
4002
|
|
|
4003
|
+
/* ---- css/finish.css — light, shadow, edges — only under data-finish="lit" ---- */
|
|
4004
|
+
/* ============================================================
|
|
4005
|
+
cardds/finish.css — FINISH: light, shadow and edges. A style of its own (Lh 2026-10-02: the plain
|
|
4006
|
+
look read flat, "not finished"), apart from colour: palette.css says WHICH colours, this says how
|
|
4007
|
+
the material is lit. Apply with <html data-finish="lit"> beside any palette (or none, or "own").
|
|
4008
|
+
No attribute = nothing here applies — the flat look, unchanged.
|
|
4009
|
+
|
|
4010
|
+
The light comes from above:
|
|
4011
|
+
- a thing that stands UP (card, sheet, button) has a lit top edge, a hairline ring and a shadow
|
|
4012
|
+
that grows with its height: card 1 · sheet / float 2 — the layers cardds already has;
|
|
4013
|
+
- a thing that is SUNK (a field, a pin cell) has its shadow inside, under its top edge;
|
|
4014
|
+
- information inside a card stays flat: only a container or a control gets an edge.
|
|
4015
|
+
Shadows are mixed from the ink (--on-surface), never plain black, so they sit in the palette.
|
|
4016
|
+
============================================================ */
|
|
4017
|
+
|
|
4018
|
+
[data-finish="lit"] {
|
|
4019
|
+
--_shade: var(--on-surface);
|
|
4020
|
+
--finish-edge: inset 0 1px 0 rgb(255 255 255 / 0.55); /* the lit top edge */
|
|
4021
|
+
--finish-ring: 0 0 0 1px color-mix(in srgb, var(--_shade) 7%, transparent); /* the hairline round a raised thing */
|
|
4022
|
+
|
|
4023
|
+
/* the three shadows every component already reads — layered: one close and crisp, one far and soft */
|
|
4024
|
+
--shadow-card: var(--finish-edge), var(--finish-ring),
|
|
4025
|
+
0 1px 2px color-mix(in srgb, var(--_shade) 8%, transparent),
|
|
4026
|
+
0 0.375rem 0.75rem -0.125rem color-mix(in srgb, var(--_shade) 8%, transparent),
|
|
4027
|
+
0 1.25rem 2.5rem -0.75rem color-mix(in srgb, var(--_shade) 14%, transparent);
|
|
4028
|
+
--shadow-float: var(--finish-edge), var(--finish-ring),
|
|
4029
|
+
0 1px 2px color-mix(in srgb, var(--_shade) 12%, transparent),
|
|
4030
|
+
0 0.5rem 1.25rem -0.25rem color-mix(in srgb, var(--_shade) 22%, transparent);
|
|
4031
|
+
--shadow-sheet: var(--finish-edge), var(--finish-ring),
|
|
4032
|
+
0 -1px 2px color-mix(in srgb, var(--_shade) 6%, transparent),
|
|
4033
|
+
0 -0.75rem 2.5rem color-mix(in srgb, var(--_shade) 18%, transparent);
|
|
4034
|
+
|
|
4035
|
+
/* a control: its own small lift, and the sheen that runs down its face */
|
|
4036
|
+
--finish-control: var(--finish-edge), var(--finish-ring),
|
|
4037
|
+
0 1px 2px color-mix(in srgb, var(--_shade) 12%, transparent),
|
|
4038
|
+
0 0.25rem 0.5rem -0.125rem color-mix(in srgb, var(--_shade) 12%, transparent);
|
|
4039
|
+
--finish-control-down: var(--finish-ring), inset 0 1px 2px color-mix(in srgb, var(--_shade) 18%, transparent);
|
|
4040
|
+
--finish-sheen: linear-gradient(to bottom, rgb(255 255 255 / 0.16), rgb(255 255 255 / 0) 60%);
|
|
4041
|
+
--finish-sunk: inset 0 1px 2px color-mix(in srgb, var(--_shade) 14%, transparent), 0 1px 0 rgb(255 255 255 / 0.5);
|
|
4042
|
+
|
|
4043
|
+
/* the shadow is the edge: the wireframe's lines step back (as in every colour palette) */
|
|
4044
|
+
--card-border: transparent;
|
|
4045
|
+
--action-border: transparent;
|
|
4046
|
+
--outline: color-mix(in srgb, currentColor 14%, transparent);
|
|
4047
|
+
}
|
|
4048
|
+
|
|
4049
|
+
/* ---- raised: cards, sheets, the stack ---- */
|
|
4050
|
+
[data-finish="lit"] :is(.card, .band-stack) { box-shadow: var(--shadow-card); }
|
|
4051
|
+
[data-finish="lit"] .band-stack > .card--band { box-shadow: none; }
|
|
4052
|
+
/* a sheet lies on the floor and is lit from above: its shadow falls UP the screen, onto what it covers */
|
|
4053
|
+
[data-finish="lit"] .sheet { box-shadow: var(--sheet-shadow, var(--shadow-sheet)); }
|
|
4054
|
+
[data-finish="lit"] .modal { box-shadow: var(--modal-shadow, var(--shadow-float)); }
|
|
4055
|
+
[data-finish="lit"] .sheet-stack > .card,
|
|
4056
|
+
[data-finish="lit"] .sheet-stack > :not(.card, .sheet-stack__base) > .card { box-shadow: var(--shadow-sheet); }
|
|
4057
|
+
|
|
4058
|
+
/* ---- controls: a filled button stands up, and goes down under the finger ---- */
|
|
4059
|
+
[data-finish="lit"] .btn:not(.btn--quiet),
|
|
4060
|
+
[data-finish="lit"] .icon-btn:not(.icon-btn-group > *, .stack-sheet__tab:not(.icon-btn--invert)) {
|
|
4061
|
+
background-image: var(--finish-sheen);
|
|
4062
|
+
box-shadow: var(--finish-control);
|
|
4063
|
+
}
|
|
4064
|
+
/* the main button throws its own colour */
|
|
4065
|
+
[data-finish="lit"] .btn--primary,
|
|
4066
|
+
[data-finish="lit"] .icon-btn--invert:not(.icon-btn-group > *) {
|
|
4067
|
+
box-shadow: var(--finish-edge),
|
|
4068
|
+
0 1px 2px color-mix(in srgb, var(--_shade) 20%, transparent),
|
|
4069
|
+
0 0.5rem 1rem -0.25rem color-mix(in srgb, var(--accent) 45%, transparent);
|
|
4070
|
+
}
|
|
4071
|
+
[data-finish="lit"] :is(.btn:not(.btn--quiet), .icon-btn:not(.icon-btn-group > *, .stack-sheet__tab:not(.icon-btn--invert))):active:not(:disabled) {
|
|
4072
|
+
background-image: none;
|
|
4073
|
+
box-shadow: var(--finish-control-down);
|
|
4074
|
+
}
|
|
4075
|
+
[data-finish="lit"] .icon-btn-group { box-shadow: var(--finish-control); }
|
|
4076
|
+
[data-finish="lit"] .toggle { box-shadow: var(--finish-sunk); }
|
|
4077
|
+
[data-finish="lit"] .toggle::after { box-shadow: 0 1px 2px color-mix(in srgb, var(--_shade) 35%, transparent); }
|
|
4078
|
+
|
|
4079
|
+
/* ---- sunk: what the member types into ---- */
|
|
4080
|
+
[data-finish="lit"] :is(.field, .pin__cell) { box-shadow: var(--finish-sunk); }
|
|
4081
|
+
|
|
@@ -10,8 +10,8 @@ export interface SnackbarProps extends Omit<ComponentProps<'div'>, 'onClick'> {
|
|
|
10
10
|
onOpenChange?: (open: boolean, reason: SnackbarReason) => void;
|
|
11
11
|
/** how long it stays, in ms (default 4000); 0 = until the app closes it */
|
|
12
12
|
duration?: number;
|
|
13
|
-
/**
|
|
14
|
-
icon?: IconName;
|
|
13
|
+
/** the glyph before the words — `check` by default (it went right); `false` = none */
|
|
14
|
+
icon?: IconName | false;
|
|
15
15
|
/** a second, smaller line under the words */
|
|
16
16
|
caption?: ReactNode;
|
|
17
17
|
/** at the screen's foot, above its bottom layer, instead of the top */
|
|
@@ -22,7 +22,7 @@ export interface SnackbarProps extends Omit<ComponentProps<'div'>, 'onClick'> {
|
|
|
22
22
|
onClosed?: () => void;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* Snackbar — a quick GOOD-NEWS line laid over the screen: something went well, said in passing. It slides in from the screen's top
|
|
25
|
+
* Snackbar — a quick GOOD-NEWS line laid over the screen: something went well, said in passing: a check and the words on the soft tone. It slides in from the screen's top
|
|
26
26
|
* (or `bottom`), stays `duration`, slides out, never takes the cursor and never stops the member. Not for a warning or a question —
|
|
27
27
|
* that is a `Modal`. A child of the `Screen`. Controlled like an input: `open` + `onOpenChange(open, reason)`, or `defaultOpen`;
|
|
28
28
|
* ONE at a time — the queue is the app's (what it shows is the app's own notification, kept in the app's list; a tap = `onTap`).
|
package/dist/layover/Snackbar.js
CHANGED
|
@@ -4,12 +4,12 @@ import { cx } from '../cx.js';
|
|
|
4
4
|
import { Icon } from '../type/Icon.js';
|
|
5
5
|
import { reducedMotion, useControlled, useLatest } from '../state.js';
|
|
6
6
|
/**
|
|
7
|
-
* Snackbar — a quick GOOD-NEWS line laid over the screen: something went well, said in passing. It slides in from the screen's top
|
|
7
|
+
* Snackbar — a quick GOOD-NEWS line laid over the screen: something went well, said in passing: a check and the words on the soft tone. It slides in from the screen's top
|
|
8
8
|
* (or `bottom`), stays `duration`, slides out, never takes the cursor and never stops the member. Not for a warning or a question —
|
|
9
9
|
* that is a `Modal`. A child of the `Screen`. Controlled like an input: `open` + `onOpenChange(open, reason)`, or `defaultOpen`;
|
|
10
10
|
* ONE at a time — the queue is the app's (what it shows is the app's own notification, kept in the app's list; a tap = `onTap`).
|
|
11
11
|
*/
|
|
12
|
-
export function Snackbar({ open, defaultOpen = true, onOpenChange, duration = 4000, icon, caption, bottom, onTap, onClosed, className, children, ...rest }) {
|
|
12
|
+
export function Snackbar({ open, defaultOpen = true, onOpenChange, duration = 4000, icon = 'check', caption, bottom, onTap, onClosed, className, children, ...rest }) {
|
|
13
13
|
const [shown, request] = useControlled(open, defaultOpen, onOpenChange);
|
|
14
14
|
const [entered, setEntered] = useState(false); // its first frame is out of sight, so one mounted open slides in
|
|
15
15
|
const closed = useLatest(onClosed);
|
|
@@ -21,10 +21,13 @@ export interface CallSheetWay {
|
|
|
21
21
|
/** what that button brings up */
|
|
22
22
|
sheet: CallSheetSheet;
|
|
23
23
|
}
|
|
24
|
-
export interface CallSheetProps extends Omit<ComponentProps<typeof Screen>, 'children' | 'fill' | '
|
|
24
|
+
export interface CallSheetProps extends Omit<ComponentProps<typeof Screen>, 'children' | 'fill' | 'bottom' | 'title'> {
|
|
25
|
+
/** TOP (optional, the Screen's own): none by default — the page starts at the lead (15%). A walk in several sheets may stand
|
|
26
|
+
* its `Track` here: it stays in sight over every sheet, a full sheet stops under it. */
|
|
27
|
+
top?: ComponentProps<typeof Screen>['top'];
|
|
25
28
|
/** a picture above the words (optional) */
|
|
26
29
|
media?: ReactNode;
|
|
27
|
-
/** the page's one heading — its h1
|
|
30
|
+
/** the page's one heading — its h1 */
|
|
28
31
|
heading: ReactNode;
|
|
29
32
|
/** the words: a string is one paragraph */
|
|
30
33
|
body?: ReactNode;
|
|
@@ -46,7 +49,7 @@ export interface CallSheetProps extends Omit<ComponentProps<typeof Screen>, 'chi
|
|
|
46
49
|
children?: ReactNode;
|
|
47
50
|
}
|
|
48
51
|
/**
|
|
49
|
-
* CallSheet — TEMPLATE, a `Screen` with its parts filled — no top · content: a `Step` holding ONE `Page` (picture · heading (the h1) ·
|
|
52
|
+
* CallSheet — TEMPLATE, a `Screen` with its parts filled — no top (or the one given: `top`) · content: a `Step` holding ONE `Page` (picture · heading (the h1) ·
|
|
50
53
|
* words · the button, or several — `ways`) and the `Sheet` each button calls · no bottom (a sheet finishes on its own foot). The page
|
|
51
54
|
* starts at the screen's lead (15%). The sheets wait `away`; a button brings its own up full; the handle takes it down to peek.
|
|
52
55
|
* One sheet up at a time — a sheet holds that way's WHOLE form.
|
|
@@ -12,7 +12,7 @@ import { Text } from '../type/Text.js';
|
|
|
12
12
|
import { Btn } from '../actions/Btn.js';
|
|
13
13
|
const index = (o) => (o === true ? 0 : typeof o === 'number' ? o : null);
|
|
14
14
|
/**
|
|
15
|
-
* CallSheet — TEMPLATE, a `Screen` with its parts filled — no top · content: a `Step` holding ONE `Page` (picture · heading (the h1) ·
|
|
15
|
+
* CallSheet — TEMPLATE, a `Screen` with its parts filled — no top (or the one given: `top`) · content: a `Step` holding ONE `Page` (picture · heading (the h1) ·
|
|
16
16
|
* words · the button, or several — `ways`) and the `Sheet` each button calls · no bottom (a sheet finishes on its own foot). The page
|
|
17
17
|
* starts at the screen's lead (15%). The sheets wait `away`; a button brings its own up full; the handle takes it down to peek.
|
|
18
18
|
* One sheet up at a time — a sheet holds that way's WHOLE form.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usableapp/cardds",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.45",
|
|
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.",
|