@usableapp/cardds 0.7.97 → 0.7.99

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/docs/reference.md CHANGED
@@ -575,6 +575,12 @@ between at 40% down the screen (see *Anatomy*, "Where the centre is"):
575
575
  them. Seen in timebank's help-ask prototype: a Composer, a Dropdown and a
576
576
  map on white cards inside the help sheet, the cards the colour of the
577
577
  ground.
578
+ `field-off-sheet` (error) flags the same controls when their nearest
579
+ container in the file is a `Page`, `Pages`, `Step` or `Screen`: a form is
580
+ never on the ground. A form over a screen is a `Sheet` in a
581
+ `SheetStage over dim` (gallery `Home/AskSheet`). A bar's control is not a
582
+ form on the ground: a `Slider` in an `ActionBarTier`, a `Dropdown` in a
583
+ `FilterRow` or a `TopBar`'s actions, a `Toggle` in an `AppBar`.
578
584
  - **Two sheet systems, one material.** *Sheet single* (`.sheet`, `sheet.css`)
579
585
  is ONE sheet the member acts in, whatever it holds: a short entry in front
580
586
  of a step's card, a place on a map, a payment on a dim page — in a `.step`
@@ -957,8 +963,8 @@ Rules:
957
963
  - **cardds does not pick the colours; it measures two things about them.** A theme's
958
964
  values are the project's call and the derivations are only starting points; any
959
965
  role can be set outright. What cardds checks is that the member can see each card
960
- ([`cardGrounds()`](#can-the-member-see-the-card) names a tone whose fill and edge both
961
- sit under 3:1 on its ground) and read what stands on it
966
+ ([`cardGrounds()`](#can-the-member-see-the-card) names a tone whose fill
967
+ sits under 3:1 on its ground)
962
968
  ([`inkGrounds()`](#ink-follows-its-ground) names a word or icon under 4.5:1 or 3:1).
963
969
  Both name the tokens to move.
964
970
  - The member's text-size setting is the same `--fs-base`, written on `<html>`
@@ -1024,10 +1030,9 @@ and with every card turned to each tone 1–5, about 27,000 pieces a pass, in `n
1024
1030
  ## Can the member see the card?
1025
1031
 
1026
1032
  A card is one boundary, and a boundary the member cannot see is no boundary (WCAG 2.2 SC 1.4.11, non-text contrast: 3:1 against what is
1027
- next to it). A card's boundary is its fill against the ground it stands on, or its edge (`--card-border`, or a finish's line).
1028
- In coloured mode the edge is `--on-surface-muted`, the ground's muted words, which a palette holds to AA on its surface; the wireframe's
1029
- is its black line. A light card on a light ground cannot reach 3:1 by its fill (a tone-1 card in a tone-1 sheet is 1:1), so the edge
1030
- carries it.
1033
+ next to it). A card stands apart from the ground it stands on by its FILL. In coloured mode and under every finish a card has no edge
1034
+ (`--card-border: transparent`; print and ink draw their own line as part of the finish); the wireframe keeps its black line. A card the
1035
+ member cannot see is answered by its fill or its ground, never by a border (Lh 2026-10-18).
1031
1036
 
1032
1037
  Your theme decides the colours, so run the check on your theme. `cardGrounds()` builds a card of each tone (1–5) on each ground a card
1033
1038
  stands on (the screen, a sheet's body, a `paper` sheet's body) where your theme is loaded, reads what the browser draws and returns one
@@ -1043,10 +1048,9 @@ expect(rows.filter((r) => !r.pass).map((r) => r.problem)).toEqual([]);
1043
1048
  ```
1044
1049
 
1045
1050
  A failing row names the tokens and the shortfall, for example
1046
- `tone 1 card on the screen: fill 1:1 (--card-1-bg #ffffff on --surface #ffffff), edge 1:1 (--card-border); one of them needs 3:1 — the fill is short by 2, or give --card-border a colour of 3:1 on #ffffff`.
1051
+ `tone 1 card on the screen: fill 1:1 (--card-1-bg #ffffff on --surface #ffffff); the fill needs 3:1 and is short by 2 — move --card-1-bg or --surface, never an edge`.
1047
1052
  It reports and changes nothing: which knob you move is your call. It runs in a browser on purpose: your theme's `var()` aliases,
1048
- `[data-palette]` blocks, finish and `color-mix()` are resolved by the same engine that draws the screen. A stock palette is held to the
1049
- same check in every finish (`tests/card-grounds.spec.js`).
1053
+ `[data-palette]` blocks, finish and `color-mix()` are resolved by the same engine that draws the screen.
1050
1054
 
1051
1055
  ## New palette
1052
1056
 
@@ -1080,7 +1084,7 @@ Four values, one attribute (`css/finish.css`; the header names each and its knob
1080
1084
  | `sticker` | a die-cut sticker: a white cut line, a short hard drop, a lone card leans (never one in a stack, a list or a feed), buttons with a bottom edge that press down | `--sticker-cut` (5px) · `--sticker-tilt` (0.8deg; `0deg` = straight) |
1081
1085
  | `ink` | a poster / comic print: a thick line in the surface's ink round every boundary and control, a hard offset in the same ink | `--ink-w` (2.5px) · `--ink-off` (5px) |
1082
1086
 
1083
- Under every finish the controls' wireframe border steps back (`--action-border`; ink puts its own line there) and the card keeps its edge (`--card-border`: lit's ring and sticker's cut do not reach 3:1 on a light ground, see [Can the member see the card?](#can-the-member-see-the-card)), a card in a closed stack's base stays flat, and the information inside a card stays flat — only a container or a control gets an edge. Colours are mixes of the roles, so a finish sits in any palette. The gallery's nav has a finish switch (key `f`); `Card · Finishes` shows the four side by side, `Home · Finish` a home under `print`.
1087
+ Under every finish the wireframe's two borders step back (`--card-border`, `--action-border`; ink puts its own line there), a card in a closed stack's base stays flat, and the information inside a card stays flat — only a container or a control gets an edge. Colours are mixes of the roles, so a finish sits in any palette. The gallery's nav has a finish switch (key `f`); `Card · Finishes` shows the four side by side, `Home · Finish` a home under `print`.
1084
1088
 
1085
1089
  ## Scale: everything is rem
1086
1090
 
package/lint/rules.mjs CHANGED
@@ -233,11 +233,23 @@ export function checkSource(source, file, { classes = carddsClasses() } = {}) {
233
233
  const stacks = span(/<SheetStack\b(?![\w.])/g), cards = span(/<Card\b(?![\w.])/g);
234
234
  const within = (at, el) => at > el.at && at < el.end;
235
235
  const innermost = (at) => cards.filter((c) => within(at, c)).at(-1);
236
+ // a form is never on the ground (Lh 2026-10-18, timebank's ask steps on a bare Page): the control's nearest container written in this file decides.
237
+ // None in the file (a step component's fragment) is not judged here — the file that places it is
238
+ const places = [
239
+ ...span(/<(?:Sheet|SheetBody|SheetStep|StackSheet|SheetStack)\b(?![\w.])/g).map((p) => ({ ...p, ground: false })),
240
+ ...span(/<(?:ActionBarTier|FilterRow|TopBar|AppBar)\b(?![\w.])/g).map((p) => ({ ...p, ground: false })), // a bar's control: a Slider in a control tier, a Dropdown in a filter row or a top bar's actions, a Toggle in an app bar
241
+ ...span(/<(?:Page|Pages|Step|Screen)\b(?![\w.])/g).map((p) => ({ ...p, ground: true })),
242
+ ...cards.map((p) => ({ ...p, ground: false })), // a card round it is field-in-card's finding
243
+ ].sort((a, b) => a.at - b.at);
244
+ const onGround = (at) => places.filter((p) => within(at, p)).at(-1)?.ground;
236
245
  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
246
  const kind = c[1] === 'Tile' ? tagAt(text, c.index).match(/\s(pick|upload)\b(?!\s*=\s*\{\s*false)/)?.[1] : null;
238
247
  if (c[1] === 'Tile' && !kind) continue; // a plain or stat Tile is read, not filled
239
248
  const card = innermost(c.index);
240
- if (!card) continue;
249
+ if (!card) {
250
+ if (onGround(c.index)) add('field-off-sheet', 'error', c.index, `<${kind ? `Tile ${kind}` : c[1]}> on the ground: a form is never on the ground, it sits only on a sheet (Lh 2026-10-18). A Page, Pages, Step or Screen is the base, not a sheet. Put the control on a Sheet's <SheetBody>, in a <SheetStep>, or on a stack sheet — a form over a screen is a <Sheet> in a <SheetStage over dim> (Home/AskSheet).`);
251
+ continue;
252
+ }
241
253
  const stack = stacks.filter((s) => card.at >= s.body && card.at < s.end).at(-1);
242
254
  if (stack && !cards.some((o) => o !== card && within(card.at, o) && o.at >= stack.body)) continue;
243
255
  const name = kind ? `Tile ${kind}` : c[1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usableapp/cardds",
3
- "version": "0.7.97",
3
+ "version": "0.7.99",
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.",