@usableapp/cardds 0.7.98 → 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`
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.98",
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.",