Skip to content

Add mobile realisations and resolve structures by canvas type - #238

Merged
vicmaster merged 4 commits into
masterfrom
phase-30-slice-e
Nov 1, 2026
Merged

vicmaster merged 4 commits into
masterfrom
phase-30-slice-e

Conversation

@vicmaster

Copy link
Copy Markdown
Owner

Phase 30 slice E. Slice D gave framesmith the mobile primitives, but an agent asked for a phone screen still had nothing to start from: every structure was a desktop shape, and stamping dashboard onto a 390px canvas produced a sidebar and a 1192px main column. This PR gives each structure an anatomy and a canvas type, adds six mobile realisations built from the primitives, and extends the pattern gate to them.

Anatomy and realisation

A structure now realises an anatomy (a screen kind) for one canvas type. It gains anatomy (default: its own name) and surface (web, mobile, or any for a component that stamps on every type; default web). A mobile realisation is named <anatomy>-mobile, so structure names stay unique. The registry refuses, at load and on registration, two structures that realise the same anatomy on the same type, and a name that shadows another structure's anatomy.

Resolution by canvas type

apply_structure resolves the name by the canvas's declared type: dashboard on a mobile canvas stamps dashboard-mobile, and the result names the anatomy and surface it stamped. A realisation name is used as is. A name with no realisation for the type is refused with the anatomies of the same kind that have one; for example, pricing on a phone lists the six mobile page anatomies. The new realisation parameter stamps the other type's realisation anyway. list_structures takes canvasId or type and lists what stamps on that canvas. With neither it returns the web listing, and passing both is an error. Each entry carries anatomy, surface and surfaces (the types its anatomy is realised on).

Six mobile realisations

Realisation Shape
dashboard-mobile Large-title nav bar, a headline metric card, a two-up stat row, a recent list of rows, a pinned four-tab bar
settings-mobile Grouped list rows (toggles, values, chevrons) and a destructive sign-out button
auth-mobile Title, two fields, and the primary and secondary buttons held at the bottom of the screen
onboarding-mobile One page of a paged flow: skip, glyph, heading, body, page dots, a full-width continue
feed-mobile A full-bleed list of rows and a pinned four-tab bar (a new, mobile-first anatomy)
detail-mobile Back nav bar, media slot, title, body, an attribute list, and a primary action pinned over the home indicator (a new anatomy)

Each starts with a status-bar, ends with a home-indicator or a pinned tab-bar, and builds every tap target from a primitive. None writes safeArea, because the pinned chrome makes the renderer reserve the insets. So a realisation is also correct on a canvas made mobile with canvas_set_type. Eight components are marked to stamp on every type: form-field, stat-card, empty-state, skeleton-card, skeleton-stat-card, kpi-card, status-chip and initials-avatar. The others are web-only, either because they hold hand-built tap targets below 44px (data-table, toolbar, toggle-row) or because they are not phone idioms.

What the stamp now does for primitives

  • The mobile primitives read six colour tokens through their defaults without naming them. A stamp holding a primitive now seeds those tokens. Before this, an unthemed settings-mobile drew near-black row titles on the dark default background. A canvas that already resolves $primary, $surface or $error keeps them, because the primitives fall back to those.
  • Every copy field of a primitive is listed in placeholders, with prop set to the field's path (title, subtitle, trailing.text, items[0].label, …). The docs and the stamp's instruction say that a nested field is set by writing its whole object or array.

Changes to existing behaviour

  • A button now honours maxWidth. The renderer had dropped it, so a long label could push its row past the column.
  • empty-state's action is now a button primitive instead of a hand-built 37px frame, on web too. Its hint moved from $caption to $text-sm (12px to 14px by default), because nine words at 12px fail the phone prose floor.
  • form-field's placeholder truncates to one line, so a long placeholder no longer wraps and clips inside the 44px input at phone width.

Pattern gate

  • Fast mode (test-patterns.ts) holds every realisation to the web bar: a score above 95 and zero cliché tells. It also requires zero findings from the mobile ruleset, at any severity. This runs under the default theme and the four presets, plus every personality's generated phone design system. On a phone the presets lend their colours, spacing, radius and depth, but not their type scale. The dark, light and material presets set a 14–15px body, which the type-floor rule correctly flags on any phone screen that sets text in $body. The spec records this change to FR-E4.
  • Full tier (new test-patterns-mobile.ts) runs detailed mode as stamped and with every list tripled, so the screen scrolls past its pinned bar. It also runs a full five-perturbation stress pass, unthemed and under each personality. Components marked for every type are checked on a phone in detailed mode and under stress.
  • test-scaffold-system.ts now stamps each structure on a canvas of its own type.

Known limits, recorded in the spec

  • The renderer scales any font size of 24px or more against a 1440 design width. On a 390 artboard, $title (28px) therefore renders at 17px, the same size as body text. The nav bar's 34px large title is fixed and unaffected. This predates the slice and applies to every narrow artboard, so it is left for a separate change. Slice G's Ridgeline rebuild will show it.
  • A scaffold node that names $accent or $bg-surface itself still seeds it over a canvas that defines only $primary / $surface, as every scaffold always has. The presets and generate_design_system define both tokens, and the docs tell a hand-themed canvas to do the same.
  • On a phone project, the diversification hint reports that the realisations repeat heroTreatment: none. Five of the six have no hero, so this is accurate, but on a phone it can only be satisfied by forcing a web structure. The hint is advisory.

Docs

The changes are covered in the list_structures, apply_structure and canvas_create docstrings, INSTRUCTIONS, GOTCHAS, the README structures section, and a new "Starting a phone screen" section in docs/GUIDELINES.md. The list_structures docstring had also been missing the four state scaffolds; they are added. test-discoverability.ts now checks that every mobile realisation is named in INSTRUCTIONS, every mobile anatomy in GUIDELINES and GOTCHAS, and the new parameters in the docstrings.

Testing

  • test-structures.ts covers the registry invariant, the listing, every resolution branch, the seeding and the placeholders. The new test-structures-mcp.ts drives the real list_structures and apply_structure handlers over MCP stdio.
  • Five verifiers checked the build: spec conformance, a 49-mutant mutation round, an adversarial regression review, a docs audit and a cold reader. Every confirmed issue was fixed, and the new checks were shown failing on the previous commit.
    • Two realisation components failed stress under conditions the gate had not covered: onboarding-mobile's heading under the editorial personality, and form-field on a phone.
    • The colour seeding overrode a canvas's own fallback colours.
    • empty-state's action was too small on a phone and invisible to the rules.
    • The placeholder list was incomplete.
  • A second-round verifier checked the fixes and found no code regression; its doc findings are applied.
  • npm run test:full passes (113/113), and tsc is clean.

@vicmaster
vicmaster merged commit d2f3ffe into master Nov 1, 2026
3 checks passed
@vicmaster
vicmaster deleted the phase-30-slice-e branch November 1, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant