@hegemonart/get-design-done 1.33.6 → 1.34.2
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/.claude-plugin/marketplace.json +7 -3
- package/.claude-plugin/plugin.json +6 -2
- package/CHANGELOG.md +46 -0
- package/README.md +18 -0
- package/agents/compose-executor.md +142 -0
- package/agents/design-context-builder.md +37 -1
- package/agents/design-verifier.md +21 -18
- package/agents/email-executor.md +148 -0
- package/agents/flutter-executor.md +147 -0
- package/agents/swift-executor.md +226 -0
- package/connections/android-emulator.md +107 -0
- package/connections/connections.md +6 -0
- package/connections/litmus.md +134 -0
- package/connections/xcode-simulator.md +108 -0
- package/package.json +1 -1
- package/reference/email-design.md +219 -0
- package/reference/native-platforms.md +273 -0
- package/reference/registry.json +14 -0
- package/scripts/lib/design-tokens/_native-shared.cjs +206 -0
- package/scripts/lib/design-tokens/compose.cjs +150 -0
- package/scripts/lib/design-tokens/flutter.cjs +128 -0
- package/scripts/lib/design-tokens/index.cjs +13 -0
- package/scripts/lib/design-tokens/swift.cjs +122 -0
- package/scripts/lib/email/validate-email-html.cjs +157 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Xcode Simulator — Connection Specification
|
|
2
|
+
|
|
3
|
+
This file is the connection specification for the **Xcode iOS Simulator** within the get-design-done pipeline. Its role is to provide *rendered* evidence for native-iOS work: it lets the verify stage capture a SwiftUI snapshot from a booted simulator so a native screen can be audited against its design intent, the same way `connections/preview.md` provides browser screenshots for web. It is the iOS half of the native-verify connection pair (the Android half is `connections/android-emulator.md`).
|
|
4
|
+
|
|
5
|
+
**It is OPTIONAL.** Most users — and CI — have no Xcode installed. Per **D-03** this connection is **never hard-required**: when it is absent the verify stage degrades gracefully to code-only structural verification, and no blocker is raised unless a `must_have` explicitly demands rendered evidence. The `swift-executor` agent generates compilable SwiftUI **without** any simulator (D-04/D-10); the simulator only adds rendered confirmation when it happens to be available.
|
|
6
|
+
|
|
7
|
+
See `connections/preview.md` for the sibling browser-preview connection this spec mirrors. (The connection index + capability matrix is maintained separately as part of phase closeout, not by this doc.)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
**Prerequisites (all OPTIONAL — absence is the common, supported case):**
|
|
14
|
+
|
|
15
|
+
- **macOS** — the iOS Simulator runs only on macOS. On Linux/Windows this connection is permanently `not_configured`; that is expected and never an error.
|
|
16
|
+
- **Xcode** (full app, not just the Command Line Tools) — provides the Simulator runtimes.
|
|
17
|
+
- **`simctl`** — Apple's simulator-control CLI, available once Xcode is installed.
|
|
18
|
+
|
|
19
|
+
**Why it is optional:** GDD's default test suite and the typical design user have no Xcode. Hard-requiring a simulator would block native-iOS work for the majority of environments. The pipeline therefore treats rendered iOS evidence as an **enhancement, not a requirement** (mirroring `connections/preview.md`'s stance) — exactly the D-03 guarantee.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Tools
|
|
24
|
+
|
|
25
|
+
This connection is driven by Apple's simulator CLI rather than an MCP server.
|
|
26
|
+
|
|
27
|
+
| Capability | Command (illustrative) | Returns | Pipeline use |
|
|
28
|
+
|------------|------------------------|---------|--------------|
|
|
29
|
+
| Availability check | list installed simulator devices | device list (may be empty) | **Lightweight probe** — see below |
|
|
30
|
+
| Boot a device | boot a named device by UDID | boot confirmation | Bring a simulator up before snapshot capture (verify stage only) |
|
|
31
|
+
| Capture a snapshot | screenshot the booted device | PNG image | Rendered evidence for the verify stage |
|
|
32
|
+
| Shut down | shut down the booted device | confirmation | Clean up after capture |
|
|
33
|
+
|
|
34
|
+
The **availability check** is preferred for probing because it does not boot a device — it only reports whether the toolchain and runtimes are present. Booting and capture happen **only** in the verify stage, **only** when a `must_have` calls for rendered evidence and the probe reported `available`.
|
|
35
|
+
|
|
36
|
+
> The default `npm test` suite **never** invokes these commands. Static validation of the native executors is hermetic (D-10) — frontmatter + reference checks only, no Xcode, no simulator, no spawn.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Which Stages Use This Connection
|
|
41
|
+
|
|
42
|
+
| Stage | Skill/Agent | Purpose | Simulator required? |
|
|
43
|
+
|-------|------------|---------|---------------------|
|
|
44
|
+
| design | `agents/swift-executor.md` | Generate compilable SwiftUI views | **No** — code generation needs no simulator (D-04/D-10) |
|
|
45
|
+
| verify | `agents/design-verifier.md` (native-verify branch) | Rendered SwiftUI snapshot audit **when available**, else degrade | No — optional; degrades per Fallback below |
|
|
46
|
+
|
|
47
|
+
This connection feeds the **34.1-05 native-verify branch**: verify probes it at stage entry and either captures a rendered snapshot (if `available`) or follows the degrade path. The `swift-executor` (design stage) never touches it.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Availability Probe
|
|
52
|
+
|
|
53
|
+
Probe **before** any boot/capture, and write the result to `.design/STATE.md` `<connections>` immediately.
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Step P1 — toolchain check:
|
|
57
|
+
detect whether Xcode + the simulator CLI are present on this machine
|
|
58
|
+
→ not macOS, or Xcode absent → xcode-simulator: not_configured
|
|
59
|
+
→ present → proceed to Step P2
|
|
60
|
+
|
|
61
|
+
Step P2 — runtime/device check:
|
|
62
|
+
list installed simulator devices (does NOT boot one)
|
|
63
|
+
→ list returns successfully (even empty) → xcode-simulator: available
|
|
64
|
+
→ command errors / permission denied → xcode-simulator: unavailable
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Three-value status schema** (mirrors `connections/preview.md`'s three-value model):
|
|
68
|
+
|
|
69
|
+
| Value | Meaning |
|
|
70
|
+
|-------|---------|
|
|
71
|
+
| `available` | Xcode + the simulator CLI are present and the device-list command succeeded (array, even empty). Rendered snapshots are possible. |
|
|
72
|
+
| `unavailable` | The toolchain is present but the device-list command errored (no runtimes installed, permission denied, internal error). Treat as no-simulator. |
|
|
73
|
+
| `not_configured` | Not macOS, or Xcode is not installed. The common, fully-supported case — never an error. |
|
|
74
|
+
|
|
75
|
+
Write the result to `.design/STATE.md`:
|
|
76
|
+
|
|
77
|
+
```xml
|
|
78
|
+
<connections>
|
|
79
|
+
preview: available
|
|
80
|
+
xcode-simulator: not_configured
|
|
81
|
+
</connections>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Fallback Behavior
|
|
87
|
+
|
|
88
|
+
When `xcode-simulator` is `not_configured` or `unavailable`, the verify stage **degrades gracefully — no error is raised, no blocker is appended** (the D-03 guarantee: this connection **never hard-requires a simulator**). The degrade is a two-step ladder, exactly mirroring how `connections/preview.md` falls back when no browser is available:
|
|
89
|
+
|
|
90
|
+
1. **Snapshot-diff on a supplied screenshot** — if the brief/context provides a reference screenshot of the intended iOS screen, the verify stage diffs against that supplied image (using the Phase-23 visual primitives). No live simulator is needed.
|
|
91
|
+
2. **Code-only structural audit** — if no screenshot exists either, the verify stage falls back to a **code-only** audit of the generated SwiftUI: structural checks against `reference/platforms.md` (safe-area handling present, no left-edge gesture conflict, Dynamic Type styles used, no sub-11pt text) and token-bridge conformance, with **no rendered evidence**. The native screen is verified **without a simulator**.
|
|
92
|
+
|
|
93
|
+
Phase-4B-style rendered-evidence checks are marked `[SKIPPED — simulator not available]`; the verifier continues to gap analysis with partial scores. **No `<blocker>` is appended for a missing simulator** — rendered iOS evidence is an enhancement, not a requirement. The **only** exception: if a `must_have` *explicitly* requires simulator-rendered evidence, THEN (and only then) the verifier appends a blocker noting the simulator is unavailable.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## STATE.md Integration
|
|
98
|
+
|
|
99
|
+
Every stage that uses this connection writes its probe result to `.design/STATE.md` under `<connections>` (see schema above). The verify stage **re-probes at stage entry** rather than blindly trusting a prior status, because toolchain availability can change between sessions — however, a status already written earlier in the **same session** can be trusted for the rest of that session (mirroring `connections/preview.md`).
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Caveats and Pitfalls
|
|
104
|
+
|
|
105
|
+
1. **Absence is the default, not a failure.** On the vast majority of machines this connection is `not_configured`. That is the supported, expected state — the pipeline produces SwiftUI and verifies it code-only without ever touching a simulator (D-03/D-10). Never treat `not_configured` as an error or a reason to block.
|
|
106
|
+
2. **The probe must not boot a device.** Use the device-**list** command for probing; booting a simulator just to check availability is slow and unnecessary. Boot only in the verify stage, only when rendered evidence is actually required and the probe said `available`.
|
|
107
|
+
3. **Snapshots are screenshots — save by path.** A captured simulator snapshot is a full PNG. Save it to `.design/screenshots/<screen>.png` and reference it by path in markdown; do not embed base64 inline. `.design/` is gitignored, so snapshots are not committed.
|
|
108
|
+
4. **The default test suite never spawns a simulator.** All native-executor validation is structural and hermetic (D-10). A live simulator is exclusively the verify stage's opt-in, degraded-mode enhancement (D-03) — it is never part of `npm test`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hegemonart/get-design-done",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.2",
|
|
4
4
|
"description": "A design-quality pipeline for AI coding agents: brief, plan, implement, and verify UI work against your design system.",
|
|
5
5
|
"author": "Hegemon",
|
|
6
6
|
"homepage": "https://github.com/hegemonart/get-design-done",
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Email Design — Constraint Catalogue
|
|
2
|
+
|
|
3
|
+
This reference is the **email-constraint catalogue**: the hard email-client rules an
|
|
4
|
+
email template MUST honor. Email is a *constrained* HTML/CSS surface — the modern
|
|
5
|
+
HTML/CSS the web executor emits (flexbox, grid, `<style>` sheets, `position`) is
|
|
6
|
+
dropped or mis-rendered by most email clients. This file is the **authority** that the
|
|
7
|
+
email-executor (Phase 34.2-02) generates against and the design-verifier's email branch
|
|
8
|
+
(34.2-03) audits against; neither re-derives these rules. The deterministic subset of
|
|
9
|
+
this catalogue is checked by [`scripts/lib/email/validate-email-html.cjs`](../scripts/lib/email/validate-email-html.cjs),
|
|
10
|
+
whose emitted `rule` ids are the constraint-ids defined below (the spec is the authority).
|
|
11
|
+
|
|
12
|
+
It is the sibling of [`reference/platforms.md`](./platforms.md). The two files have
|
|
13
|
+
distinct jobs and must not be confused:
|
|
14
|
+
|
|
15
|
+
| File | Job |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `reference/platforms.md` (Phase 19) | Interaction **conventions** — navigation, safe areas, gestures, native typography, haptics. *Behavioral* knowledge for native/web screens. |
|
|
18
|
+
| `reference/email-design.md` (Phase 34.2, this file) | The email **constraint catalogue** — table-based layout, inline styles, MSO conditional comments, dark-mode `color-scheme`, ~600px max-width, image/alt rules, and top-20-client quirks. *Structural* knowledge an email template implements. |
|
|
19
|
+
|
|
20
|
+
Per **D-02** there is **no `mjml` runtime dependency**: MJML source is the agent's
|
|
21
|
+
canonical artifact and the HTML is derived by the agent, not a build step. Per **D-03**
|
|
22
|
+
email verify is this catalogue's *static* validator plus an *optional* Litmus /
|
|
23
|
+
Email-on-Acid render-test connection that degrades to the static validator when absent.
|
|
24
|
+
Per **D-10** the static checks are deterministic (same HTML string → same result), with
|
|
25
|
+
no network and no `mjml`, so the default `npm test` stays green on any machine.
|
|
26
|
+
|
|
27
|
+
Each constraint carries a **rule-id** (`EM-<CLASS>-NN`). Section 8 marks exactly which
|
|
28
|
+
ids the static validator asserts versus which are render-tested guidance only.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. Purpose
|
|
33
|
+
|
|
34
|
+
Phase 19 shipped platform *references*; Phase 23 shipped the token engine; Phase 34.1
|
|
35
|
+
added native generators. Email is the remaining untouched product surface, and its
|
|
36
|
+
constraints are unlike both web and native: a fifteen-year-old rendering engine
|
|
37
|
+
(Outlook/Word), aggressive `<head>` stripping (Gmail), and a long tail of per-client
|
|
38
|
+
quirks. Instead of each email being authored from memory, the constraints live once
|
|
39
|
+
here (the catalogue) and once in the validator (the statically-checkable subset), and
|
|
40
|
+
the executor + verifier consume them. This file is the single SC#9-email authority.
|
|
41
|
+
|
|
42
|
+
The catalogue is **prose + tables**, not an implementation. Illustrative snippets are
|
|
43
|
+
kept to 2–3 lines. The implementation is `validate-email-html.cjs`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. Layout — tables, not flexbox/grid/position
|
|
48
|
+
|
|
49
|
+
Email layout uses nested `role="presentation"` **tables**, never the modern CSS box
|
|
50
|
+
primitives. `display:flex`, `display:grid`, and `position:absolute|fixed|sticky` are
|
|
51
|
+
dropped or mis-rendered by Outlook (Word engine), older Gmail, and many mobile clients,
|
|
52
|
+
collapsing a layout to a single column or off-screen elements.
|
|
53
|
+
|
|
54
|
+
| Rule-id | Constraint |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| **EM-LAYOUT-01** | Layout is built from `role="presentation"` tables. The forbidden modern primitives — `display:flex`, `display:grid`, `position:absolute`, `position:fixed` (and `position:sticky`) — MUST NOT appear in any `style`. *(Statically checkable: their presence is flagged.)* |
|
|
57
|
+
| EM-LAYOUT-02 | Body content sits in a fixed-/max-width container of ~**600px** (the safe width across the desktop preview pane and most mobile clients). Wider tables clip in Outlook and force horizontal scroll on mobile. |
|
|
58
|
+
| EM-LAYOUT-03 | Use `cellpadding="0" cellspacing="0" border="0"` on layout tables and prefer cell `padding` over margins (margins are inconsistently honored). |
|
|
59
|
+
| EM-LAYOUT-04 | Single primary column on mobile; multi-column desktop layouts degrade to stacked rows. Do not rely on `float` for columns (use side-by-side `<td>`s or `align`). |
|
|
60
|
+
| EM-LAYOUT-05 | Set explicit `width` on tables/cells; never assume an intrinsic content width. Outlook ignores CSS `max-width` on many elements — pair it with a `<!--[if mso]>` ghost table (see §4). |
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0"
|
|
64
|
+
style="max-width:600px;margin:0 auto;"><tr><td>…</td></tr></table>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 3. Styling — inline, not a `<style>` block
|
|
70
|
+
|
|
71
|
+
Visual styling lives in **inline `style="…"` attributes** on each element, NOT in a
|
|
72
|
+
`<head><style>` sheet. Gmail (and several webmail clients) strip or heavily limit
|
|
73
|
+
`<head>` styles, so any rule that *must* apply is inlined. A `<style>` block is
|
|
74
|
+
**tolerated only** for progressive enhancement that degrades gracefully — chiefly
|
|
75
|
+
`@media` queries for responsive/dark-mode — never as the primary styling mechanism.
|
|
76
|
+
|
|
77
|
+
| Rule-id | Constraint |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| **EM-STYLE-01** | Core/visual styling is inline. A `<style>` block used as the PRIMARY styling mechanism (non-`@media` rules, or a large sheet) is forbidden because Gmail strips it. *(Statically checkable: a non-trivial `<style>` block carrying non-`@media` rules is flagged; a small `@media`-only block is tolerated.)* |
|
|
80
|
+
| EM-STYLE-02 | Avoid CSS shorthand that clients mangle (`background` shorthand, unitless line-heights in some clients); prefer longhand (`background-color`). |
|
|
81
|
+
| EM-STYLE-03 | No external stylesheets (`<link rel="stylesheet">`) and no `@import` — clients strip them. |
|
|
82
|
+
| EM-STYLE-04 | A small `@media`-only `<style>` for responsive breakpoints / dark-mode is the one accepted `<style>` use; it must enhance, not be required for, a readable layout. |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 4. Outlook / MSO conditional comments
|
|
87
|
+
|
|
88
|
+
Outlook on Windows renders with Microsoft Word's HTML engine. It needs **MSO
|
|
89
|
+
conditional comments** to apply Outlook-specific fallbacks (ghost tables for centering
|
|
90
|
+
and width, VML for background images and bulletproof buttons) and to hide modern markup
|
|
91
|
+
it would mangle.
|
|
92
|
+
|
|
93
|
+
| Rule-id | Constraint |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| **EM-MSO-01** | A full email document MUST include at least one MSO conditional comment — `<!--[if mso]> … <![endif]-->` and/or `<!--[if !mso]><!--> … <!--<![endif]-->`. *(Statically checkable: absence in a full-email document is flagged.)* |
|
|
96
|
+
| EM-MSO-02 | Use `<!--[if mso]>` **ghost tables** to give Outlook an explicit fixed width and centering that it would otherwise drop from CSS `max-width`/`margin:auto`. |
|
|
97
|
+
| EM-MSO-03 | Use **VML** (`<v:roundrect>`, `<v:fill>`) inside `<!--[if mso]>` for rounded "bulletproof" buttons and background images, since Outlook ignores `border-radius` and CSS `background-image`. |
|
|
98
|
+
| EM-MSO-04 | Add the MSO DPI/namespace head fixes (`o:OfficeDocumentSettings`, `xmlns:v`/`xmlns:o`, `mso-line-height-rule:exactly`) to stabilize spacing and image scaling. |
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<!--[if mso]><table role="presentation" width="600"><tr><td><![endif]-->
|
|
102
|
+
<!-- modern markup here -->
|
|
103
|
+
<!--[if mso]></td></tr></table><![endif]-->
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 5. Dark mode
|
|
109
|
+
|
|
110
|
+
Clients invert or remap colors in dark mode inconsistently (Outlook.com fully inverts;
|
|
111
|
+
Apple Mail and Gmail partially; some not at all). Declare a **`color-scheme`** signal so
|
|
112
|
+
clients keep the intended palette, and use `prefers-color-scheme` for explicit dark
|
|
113
|
+
overrides.
|
|
114
|
+
|
|
115
|
+
| Rule-id | Constraint |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| **EM-DARK-01** | Declare a `color-scheme` signal: a `<meta name="color-scheme" content="light dark">` and/or a `color-scheme: light dark;` CSS declaration and/or a `@media (prefers-color-scheme: dark)` block. At least one MUST be present. *(Statically checkable: total absence of any color-scheme signal is flagged. A `<meta name="color-scheme">` alone satisfies it — decoupled from any `<style>` block.)* |
|
|
118
|
+
| EM-DARK-02 | Pair `color-scheme` with `supported-color-schemes` (meta) for Apple Mail / Outlook. |
|
|
119
|
+
| EM-DARK-03 | Beware forced color inversion: set explicit `background-color` AND `color` on text containers so an inverting client cannot produce unreadable low-contrast pairs. |
|
|
120
|
+
| EM-DARK-04 | Provide dark-mode-friendly logos (transparent PNG or a `prefers-color-scheme` image swap) so a dark background does not hide a dark logo. |
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 6. Images & accessibility
|
|
125
|
+
|
|
126
|
+
Images are often blocked by default and must degrade gracefully; accessibility rules
|
|
127
|
+
prevent broken layouts and unreadable content. *(Guidance — not all statically
|
|
128
|
+
asserted; see §8.)*
|
|
129
|
+
|
|
130
|
+
| Rule-id | Constraint |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| EM-IMG-01 | Every `<img>` carries explicit `width` and `height` attributes (Outlook needs them; prevents reflow when images load). |
|
|
133
|
+
| EM-IMG-02 | Every `<img>` carries meaningful `alt` text (shown when images are blocked) and `display:block` to avoid the baseline gap below images. |
|
|
134
|
+
| EM-IMG-03 | Serve retina images at 2× and constrain with `width`/`height` so they render crisp without breaking the layout. |
|
|
135
|
+
| EM-IMG-04 | Buttons are **bulletproof** (table/`<a>` with padding + VML fallback for Outlook), never an image-only CTA that vanishes when images are blocked. |
|
|
136
|
+
| EM-A11Y-01 | Set the document `lang`, a real `<title>`, and a logical heading/reading order; ensure WCAG-AA text contrast that survives dark-mode inversion. |
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 7. Top-20 client quirks
|
|
141
|
+
|
|
142
|
+
The top-20-by-market-share email clients and their headline quirks. *(Catalogue —
|
|
143
|
+
most are render-tested via the optional Litmus / Email-on-Acid connection (34.2-02),
|
|
144
|
+
NOT statically checkable; see §8.)*
|
|
145
|
+
|
|
146
|
+
| Rule-id | Client | Headline quirk |
|
|
147
|
+
| --- | --- | --- |
|
|
148
|
+
| EM-CLIENT-01 | Apple Mail (iOS) | Most standards-compliant; honors `<style>` + media queries; auto-scales text — set `meta viewport`. |
|
|
149
|
+
| EM-CLIENT-02 | Apple Mail (macOS) | WebKit-based, robust; respects `prefers-color-scheme`; watch auto-link of dates/addresses. |
|
|
150
|
+
| EM-CLIENT-03 | Gmail (web) | **Strips `<head>` styles** beyond a limited `<style>`; clips messages over ~102KB ("[Message clipped]"); requires inline styles. |
|
|
151
|
+
| EM-CLIENT-04 | Gmail (mobile app, default account) | Supports a `<style>` block + media queries; same ~102KB clip; no `:hover` reliability. |
|
|
152
|
+
| EM-CLIENT-05 | Gmail (mobile, non-default / IMAP "GANGA") | **Strips `<style>` entirely** — only inline styles survive; the strictest Gmail mode. |
|
|
153
|
+
| EM-CLIENT-06 | Outlook 2016–2021 (Windows) | **Word engine**: no flexbox/grid/`position`, ignores `max-width`/`border-radius`/CSS `background-image`; needs ghost tables + VML + `mso-line-height-rule:exactly`. |
|
|
154
|
+
| EM-CLIENT-07 | Outlook (Microsoft 365, Windows) | Word engine like 2016/2019; DPI scaling bugs — set explicit image `width`/`height`. |
|
|
155
|
+
| EM-CLIENT-08 | Outlook.com (web) | Different (better) engine than desktop; **aggressive dark-mode color inversion**; rewrites some colors. |
|
|
156
|
+
| EM-CLIENT-09 | Outlook (macOS) | WebKit-based (unlike Windows); far more capable; still test buttons/spacing. |
|
|
157
|
+
| EM-CLIENT-10 | Outlook (mobile, iOS/Android) | Largely fine; respects media queries; watch link color overrides. |
|
|
158
|
+
| EM-CLIENT-11 | Yahoo Mail | Supports media queries; strips/rewrites some `class`/`id`; avoid unsupported pseudo-classes. |
|
|
159
|
+
| EM-CLIENT-12 | AOL Mail | Shares Yahoo's engine; similar class/id handling. |
|
|
160
|
+
| EM-CLIENT-13 | Samsung Mail (Android) | Webview-based; media-query support varies by version; test stacking. |
|
|
161
|
+
| EM-CLIENT-14 | Android default / Gmail-for-non-Gmail | Inline-only safe mode; treat like the strict Gmail IMAP mode. |
|
|
162
|
+
| EM-CLIENT-15 | Thunderbird | Gecko-based, capable; honors most CSS; test dark theme. |
|
|
163
|
+
| EM-CLIENT-16 | Windows 10/11 Mail | EdgeHTML/Chromium-ish; generally capable; watch padding. |
|
|
164
|
+
| EM-CLIENT-17 | Proton Mail | Sanitizes aggressively; strips remote content until approved; inline-safe. |
|
|
165
|
+
| EM-CLIENT-18 | Fastmail | Standards-friendly; respects media queries and dark mode. |
|
|
166
|
+
| EM-CLIENT-19 | Zoho Mail | Reasonable CSS support; test buttons + dark mode. |
|
|
167
|
+
| EM-CLIENT-20 | GMX / Web.de | EU webmail; limited CSS; lean on inline styles + tables. |
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 8. Statically-checkable vs render-tested
|
|
172
|
+
|
|
173
|
+
This table is the **contract** the validator's `rule` ids map to. The four rule-ids
|
|
174
|
+
below are the deterministic subset that `scripts/lib/email/validate-email-html.cjs`
|
|
175
|
+
asserts via regex/string analysis of the supplied HTML string. Every other rule-id in
|
|
176
|
+
this catalogue is **render-tested guidance** — verified by the optional Litmus /
|
|
177
|
+
Email-on-Acid render-test connection (34.2-02), never asserted by the static validator.
|
|
178
|
+
|
|
179
|
+
| Rule-id | Check | Statically checked by the validator? | How verified otherwise |
|
|
180
|
+
| --- | --- | --- | --- |
|
|
181
|
+
| **EM-LAYOUT-01** | No `display:flex` / `display:grid` / `position:absolute\|fixed` in any `style` | **YES** — presence flagged | — |
|
|
182
|
+
| **EM-STYLE-01** | No `<style>` block as the PRIMARY styling mechanism (non-`@media` rules / large sheet); inline styling expected | **YES** — a non-trivial `<style>` block flagged; a small `@media`-only block tolerated | — |
|
|
183
|
+
| **EM-MSO-01** | An MSO conditional comment (`<!--[if mso]>` / `<!--[if !mso]>`) is present in a full-email document | **YES** — absence flagged | — |
|
|
184
|
+
| **EM-DARK-01** | A `color-scheme` signal is present (meta `color-scheme` and/or CSS `color-scheme` and/or `prefers-color-scheme`) | **YES** — total absence flagged (a `<meta name="color-scheme">` alone satisfies it) | — |
|
|
185
|
+
| EM-LAYOUT-02..05 | ~600px width, cellpadding, single-column, explicit widths | No | Render test (Litmus) |
|
|
186
|
+
| EM-STYLE-02..04 | Longhand props, no external CSS, tolerated `@media` | No | Render test |
|
|
187
|
+
| EM-MSO-02..04 | Ghost tables, VML, DPI/namespace head | No | Render test (Outlook) |
|
|
188
|
+
| EM-DARK-02..04 | `supported-color-schemes`, explicit bg+color, dark logos | No | Render test (dark mode) |
|
|
189
|
+
| EM-IMG-01..04, EM-A11Y-01 | width/height, alt, display:block, bulletproof buttons, lang/contrast | No | Render test + a11y review |
|
|
190
|
+
| EM-CLIENT-01..20 | Per-client headline quirks | No | Render test (cross-client screenshots) |
|
|
191
|
+
|
|
192
|
+
Notes on the four statically-checked rules:
|
|
193
|
+
|
|
194
|
+
- **EM-STYLE-01 heuristic (deterministic).** A `<style>…</style>` block is flagged as the
|
|
195
|
+
primary styling mechanism when, after removing `@media { … }` groups and `@font-face`
|
|
196
|
+
from the block, residual CSS rules remain **or** the block exceeds a generous size
|
|
197
|
+
threshold. A small dark-mode/responsive `@media`-only block (per EM-STYLE-04) is
|
|
198
|
+
therefore tolerated and does NOT trip the check.
|
|
199
|
+
- **EM-DARK-01 is decoupled from `<style>`.** A `<meta name="color-scheme" content="…">`
|
|
200
|
+
in `<head>` satisfies EM-DARK-01 on its own, so a fully-inline email with only a
|
|
201
|
+
color-scheme meta (and no `<style>` at all) passes both EM-STYLE-01 and EM-DARK-01.
|
|
202
|
+
- **EM-MSO-01 fires only on a full email.** A document is treated as a full email when it
|
|
203
|
+
has `<html>`/`<body>` or a layout `<table>`; a bare fragment is not flagged for a
|
|
204
|
+
missing MSO comment.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 9. Cross-references
|
|
209
|
+
|
|
210
|
+
- [`reference/platforms.md`](./platforms.md) — the interaction-conventions sibling
|
|
211
|
+
(navigation, safe areas, gestures). The email-executor reads **this** file for the
|
|
212
|
+
email constraints, that file for general platform behavior.
|
|
213
|
+
- [`scripts/lib/email/validate-email-html.cjs`](../scripts/lib/email/validate-email-html.cjs)
|
|
214
|
+
— the deterministic static validator that asserts the §8 subset; its `rule` ids are
|
|
215
|
+
the constraint-ids defined here.
|
|
216
|
+
- [`reference/registry.json`](./registry.json) — this catalogue is registered as the
|
|
217
|
+
`email-design` entry (type `heuristic`, phase `34.2`) so the registry round-trip test
|
|
218
|
+
(`test/suite/reference-registry.test.cjs`) stays green (D-05, the 33.5-01 / 34.1-01
|
|
219
|
+
lesson).
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Native Platforms — Token-Bridge Spec
|
|
2
|
+
|
|
3
|
+
This reference is the **token→native-code bridge**: it specifies how the canonical
|
|
4
|
+
CSS-token form produced by the Phase-23 token engine
|
|
5
|
+
(`scripts/lib/design-tokens/`) maps onto the three native theme systems —
|
|
6
|
+
SwiftUI, Jetpack Compose, and Flutter — and pins down the **precision contract**
|
|
7
|
+
that defines what "token identity preserved" means for the deterministic
|
|
8
|
+
round-trip (`reference/native-platforms.md` is the authority that
|
|
9
|
+
`test/suite/native-token-bridge.test.cjs` asserts against).
|
|
10
|
+
|
|
11
|
+
It is the sibling of [`reference/platforms.md`](./platforms.md). Those two files
|
|
12
|
+
have distinct jobs and must not be confused:
|
|
13
|
+
|
|
14
|
+
| File | Job |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `reference/platforms.md` (Phase 19) | Interaction **conventions** — navigation, safe areas, gestures, native typography, haptics. *Behavioral* knowledge the executors reference when laying out a screen. |
|
|
17
|
+
| `reference/native-platforms.md` (Phase 34.1, this file) | The token→theme **bridge** — how a design token (`#3B82F6`, `16px`, `Inter`) becomes a SwiftUI `Color` / Compose `Color(0x…)` / Flutter `Color(0x…)`, plus the precision contract for the round-trip. *Structural* knowledge the emitters implement. |
|
|
18
|
+
|
|
19
|
+
Per **D-02** the bridge **extends** the Phase-23 engine with three new emitters
|
|
20
|
+
(`scripts/lib/design-tokens/{swift,compose,flutter}.cjs`) rather than forking a
|
|
21
|
+
separate native IR. There is one canonical token form (below) and one set of
|
|
22
|
+
readers; the native emitters are additional *sinks* on the same facade. Per
|
|
23
|
+
**D-10** the round-trip operates at the **token level** — deterministic emit +
|
|
24
|
+
re-extract with documented precision — never full-view parsing and never a live
|
|
25
|
+
simulator, so the default `npm test` stays green on any machine.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. Purpose
|
|
30
|
+
|
|
31
|
+
Phase 19 shipped platform *references* but zero generators; Phase 23 shipped a
|
|
32
|
+
multi-source token reader (`css-vars` / `js-const` / `tailwind` / `figma`) that
|
|
33
|
+
all normalise to a single flat `{ tokens: Record<string, string> }` map. Phase
|
|
34
|
+
34.1 crosses from platform-knowledge into platform-execution: instead of each
|
|
35
|
+
native executor re-deriving "how does `#3B82F6` become a SwiftUI `Color`", the
|
|
36
|
+
mapping lives once here (the spec) and once in the emitters (the
|
|
37
|
+
implementation), and the executors consume it. This amortizes the Phase-23 token
|
|
38
|
+
investment across SwiftUI / Compose / Flutter.
|
|
39
|
+
|
|
40
|
+
The canonical CSS-token form is the **single input** to all three native
|
|
41
|
+
emitters. This spec maps that one input to three native theme systems and states
|
|
42
|
+
the precision each mapping preserves.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. Canonical input shape
|
|
47
|
+
|
|
48
|
+
The emitters consume the exact map shape the Phase-23 readers return: a **flat
|
|
49
|
+
`{ tokens: Record<string, string> }` object** whose keys are the design-token
|
|
50
|
+
names with the leading `--` stripped (exactly as `css-vars.cjs` returns) and
|
|
51
|
+
whose values are the raw token values as strings.
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
{ tokens: { "color-primary": "#3B82F6", "space-4": "16px", "font-family-body": "Inter, system-ui" } }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Each emitter accepts either the full Phase-23 `TokenSet`
|
|
58
|
+
(`{ tokens, source?, format? }`) **or** a bare `{ tokens }` object — it reads
|
|
59
|
+
`tokenSet.tokens` and throws a `TypeError` only when no `.tokens` object is
|
|
60
|
+
present.
|
|
61
|
+
|
|
62
|
+
### Prefix → category inference
|
|
63
|
+
|
|
64
|
+
Token **category** is inferred from the key prefix. The emitters use this table
|
|
65
|
+
to decide whether a value is a color, a dimension, or a string:
|
|
66
|
+
|
|
67
|
+
| Key prefix | Category | Native treatment |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| `color-` | color | hex → native channel form (§3–§5, §6 COLOR) |
|
|
70
|
+
| `space-`, `spacing-` | dimension | px → pt / dp / logical px (§6 DIMENSION) |
|
|
71
|
+
| `radius-` | dimension | px → pt / dp / logical px |
|
|
72
|
+
| `size-` | dimension | px → pt / dp / logical px |
|
|
73
|
+
| `font-`, `text-` | typography | string pass-through (family / weight / named size) |
|
|
74
|
+
| `shadow-` | other | string pass-through (composite values are non-mappable) |
|
|
75
|
+
| *(anything else)* | other | value-sniffed: a `#…` value is treated as color, an `Npx` value as dimension, otherwise string pass-through |
|
|
76
|
+
|
|
77
|
+
A value is **always** re-sniffed regardless of prefix, so a `#…` value under a
|
|
78
|
+
non-color prefix is still emitted as a color and a `Npx` value under a non-space
|
|
79
|
+
prefix is still emitted as a dimension. The prefix is the hint; the value is the
|
|
80
|
+
authority. This keeps the bridge robust against arbitrary token-naming schemes.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 3. SwiftUI mapping
|
|
85
|
+
|
|
86
|
+
Target: a Swift source string exposing an `enum` of static theme constants
|
|
87
|
+
(`enum GDDTheme { … }`) — colors as `Color`, dimensions as `CGFloat` points,
|
|
88
|
+
typography families as `String` (and an optional `Font` helper).
|
|
89
|
+
|
|
90
|
+
| Token | SwiftUI form |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| color `#RRGGBB` | `Color(red: R/255.0, green: G/255.0, blue: B/255.0, opacity: A/255.0)` from the 8-bit channels |
|
|
93
|
+
| dimension `Npx` | `CGFloat` point literal — integer `N` (pt) |
|
|
94
|
+
| font-family | `String` literal (`"Inter, system-ui"`) |
|
|
95
|
+
|
|
96
|
+
Illustrative (2-line) snippet:
|
|
97
|
+
|
|
98
|
+
```swift
|
|
99
|
+
static let colorPrimary = Color(red: 59.0/255.0, green: 130.0/255.0, blue: 246.0/255.0, opacity: 255.0/255.0)
|
|
100
|
+
static let space4: CGFloat = 16
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
SwiftUI uses normalized `0.0…1.0` channel fractions; to keep the round-trip
|
|
104
|
+
**exact** the emitter writes each channel as the 8-bit numerator over `255.0`
|
|
105
|
+
(e.g. `59.0/255.0`) rather than a pre-divided decimal — the re-extractor reads
|
|
106
|
+
the numerator back as the integer channel, avoiding float drift. The `Color` /
|
|
107
|
+
`Font` / `ViewModifier` consumption pattern (applying the constants to views) is
|
|
108
|
+
the executor's job; this emitter produces the *constants*.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 4. Jetpack Compose mapping
|
|
113
|
+
|
|
114
|
+
Target: a Kotlin source string with `Color` vals, a `Shapes` block (from
|
|
115
|
+
`radius-` tokens), a `Typography` block (from `font-`/`text-` tokens), and a
|
|
116
|
+
`MaterialTheme` wiring (`object GDDTheme { val Colors… ; val Shapes… ; val Typography… }`).
|
|
117
|
+
|
|
118
|
+
| Token | Compose form |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| color `#RRGGBB` | `Color(0xAARRGGBB)` long literal (alpha-first, 8 hex digits) |
|
|
121
|
+
| dimension `Npx` | `N.dp` (integer dp) |
|
|
122
|
+
| radius `Npx` | `RoundedCornerShape(N.dp)` inside `Shapes` |
|
|
123
|
+
| typography family | `String` (fed into a `TextStyle.fontFamily` slot / `Typography`) |
|
|
124
|
+
|
|
125
|
+
Illustrative (2-line) snippet:
|
|
126
|
+
|
|
127
|
+
```kotlin
|
|
128
|
+
val colorPrimary = Color(0xFF3B82F6)
|
|
129
|
+
val space4 = 16.dp
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Compose packs ARGB into a single `0xAARRGGBB` long; alpha is the high byte. The
|
|
133
|
+
re-extractor reads the 8 hex digits straight back to the channels.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 5. Flutter mapping
|
|
138
|
+
|
|
139
|
+
Target: a Dart source string building a `ThemeData` whose `colorScheme`
|
|
140
|
+
(`ColorScheme`) carries the color tokens and whose `textTheme` (`TextTheme`)
|
|
141
|
+
carries the typography tokens, plus a constants class
|
|
142
|
+
(`class GDDTheme { … }`).
|
|
143
|
+
|
|
144
|
+
| Token | Flutter form |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| color `#RRGGBB` | `Color(0xAARRGGBB)` (alpha-first, 8 hex digits) |
|
|
147
|
+
| dimension `Npx` | logical-px **double** — `N.0` |
|
|
148
|
+
| typography family | `String` (`fontFamily: 'Inter'`) |
|
|
149
|
+
|
|
150
|
+
Illustrative (2-line) snippet:
|
|
151
|
+
|
|
152
|
+
```dart
|
|
153
|
+
static const colorPrimary = Color(0xFF3B82F6);
|
|
154
|
+
static const space4 = 16.0;
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Flutter measures in logical pixels and keeps the value as a `double` (`16.0`),
|
|
158
|
+
so — unlike pt/dp — Flutter dimensions are **not** rounded to integers; the
|
|
159
|
+
fractional part survives.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 6. PRECISION CONTRACT
|
|
164
|
+
|
|
165
|
+
This section is the crux. It defines, per value category, exactly what
|
|
166
|
+
information the emit → re-extract round-trip preserves. The test asserts token
|
|
167
|
+
identity **within this precision** — not bit-exact floats, not lossy
|
|
168
|
+
approximation. An emitter is correct **iff** `reextract(emit({tokens}))`
|
|
169
|
+
reproduces every token in the identity set under these rules.
|
|
170
|
+
|
|
171
|
+
### COLOR — 8-bit-per-channel, EXACT
|
|
172
|
+
|
|
173
|
+
- Accepted input forms: `#RGB`, `#RRGGBB`, `#RGBA`, `#RRGGBBAA` (case-insensitive).
|
|
174
|
+
- `#RGB` / `#RGBA` shorthand **expands** to `#RRGGBB` / `#RRGGBBAA` by
|
|
175
|
+
duplicating each nibble (`#3af` → `#33aaff`). This expansion is part of the
|
|
176
|
+
contract: the re-extractor recovers the **expanded** `#RRGGBB(AA)` form, so
|
|
177
|
+
`#3af` round-trips to `#33aaff` (canonically equal, the documented identity).
|
|
178
|
+
- Each channel is an 8-bit integer (0–255) and is preserved **exactly** — no
|
|
179
|
+
channel may be off by one. SwiftUI stores channels as `N.0/255.0` numerators;
|
|
180
|
+
Compose/Flutter store them in a `0xAARRGGBB` literal. Both forms recover the
|
|
181
|
+
identical 8-bit channels.
|
|
182
|
+
- **Alpha:** when the input has no alpha (`#RGB`/`#RRGGBB`) the emitted color is
|
|
183
|
+
**opaque** — alpha byte `0xFF` (Compose/Flutter) / `opacity: 255.0/255.0`
|
|
184
|
+
(SwiftUI). The re-extractor emits an alpha channel **only when the original
|
|
185
|
+
had one**: a 6-digit input round-trips to a 6-digit `#RRGGBB` (the implied
|
|
186
|
+
opaque alpha is dropped on the way back); an 8-digit input round-trips to the
|
|
187
|
+
8-digit `#RRGGBBAA`. This keeps `#3B82F6 → #3B82F6` an exact identity.
|
|
188
|
+
|
|
189
|
+
### DIMENSION — integer pt/dp, logical-px double
|
|
190
|
+
|
|
191
|
+
- Accepted input: `Npx` or a bare unit-less number (`16px`, `16`). The unit is
|
|
192
|
+
normalised to `px` on the canonical side.
|
|
193
|
+
- **iOS (pt) / Android (dp):** rounded to the nearest integer, **round-half-up**
|
|
194
|
+
(`15.5px` → `16`). Because rounding is lossy for non-integers, the round-trip
|
|
195
|
+
**identity set** is restricted to integer-px dimensions (e.g. `16px`), which
|
|
196
|
+
recover exactly: `16px → 16 (pt/dp) → 16px`.
|
|
197
|
+
- **Flutter (logical px):** kept as a `double` (`16px` → `16.0`), so Flutter
|
|
198
|
+
does **not** round and a fractional dimension survives. The re-extractor
|
|
199
|
+
recovers `Npx` by stripping the trailing `.0` for whole numbers.
|
|
200
|
+
- The re-extractor always recovers the canonical `Npx` string form, so the
|
|
201
|
+
emit→re-extract identity for an integer dimension is `"16px" === "16px"`.
|
|
202
|
+
|
|
203
|
+
### `rem` / `em` — passed through verbatim (non-mappable)
|
|
204
|
+
|
|
205
|
+
`rem`/`em` values depend on a root/element font-size that the token map does not
|
|
206
|
+
carry, so they are **not** converted. They are treated as **non-mappable**
|
|
207
|
+
(below): emitted verbatim into a raw slot and **excluded** from the round-trip
|
|
208
|
+
identity set. (A future plan may add an explicit base-size option; until then,
|
|
209
|
+
verbatim pass-through is the stated, deterministic behavior.)
|
|
210
|
+
|
|
211
|
+
### TYPOGRAPHY / NAMED VALUES — string pass-through
|
|
212
|
+
|
|
213
|
+
`font-family`, `font-weight`, named sizes, and any other string token are
|
|
214
|
+
emitted **verbatim** as a string literal and recovered **string-equal**
|
|
215
|
+
(`"Inter, system-ui" → "Inter, system-ui"`). No normalisation, no quoting
|
|
216
|
+
changes that alter the recovered string.
|
|
217
|
+
|
|
218
|
+
### NON-MAPPABLE — verbatim, EXCLUDED from the identity set
|
|
219
|
+
|
|
220
|
+
Values the emitter cannot represent as a native primitive — CSS `var(--x)`
|
|
221
|
+
references, `calc(…)` expressions, gradients (`linear-gradient(…)`), and `rem`/
|
|
222
|
+
`em` dimensions — are **passed through verbatim** into a raw-string slot
|
|
223
|
+
(a trailing comment such as `// non-mappable: <name> = <value>` or the language
|
|
224
|
+
equivalent) so no information is lost, and are **explicitly excluded** from the
|
|
225
|
+
round-trip identity assertion. The contract documents them as
|
|
226
|
+
"verbatim / not round-tripped": the test asserts the verbatim value appears in
|
|
227
|
+
the emitted source, and does **not** assert it survives re-extraction as a typed
|
|
228
|
+
token.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 7. The round-trip (what the test locks)
|
|
233
|
+
|
|
234
|
+
For each emitter the bridge guarantees:
|
|
235
|
+
|
|
236
|
+
1. **Determinism.** `emit(x) === emit(x)` byte-for-byte. Keys are iterated in a
|
|
237
|
+
stable sorted order; no `Date`, no `process.env`, no filesystem, no network in
|
|
238
|
+
the emit path (D-10).
|
|
239
|
+
2. **Identity within precision.** For the identity set (color + integer
|
|
240
|
+
dimension + typography), `reextract(emit({tokens}))` deep-equals `{tokens}`
|
|
241
|
+
under the precision rules above (8-bit color channels exact with `#RGB`
|
|
242
|
+
expansion; integer pt/dp; logical-px double; family/weight string-equal).
|
|
243
|
+
3. **Verbatim exclusion.** Non-mappable values appear verbatim in the source and
|
|
244
|
+
are not part of the identity assertion.
|
|
245
|
+
|
|
246
|
+
Each emitter module exports a symmetric re-extractor
|
|
247
|
+
(`reextractSwift` / `reextractCompose` / `reextractFlutter`) that parses the
|
|
248
|
+
emitted native source back into a `{ tokens }` map, so the round-trip is
|
|
249
|
+
deterministic and bijective on the identity set and reusable by the Phase-34.1
|
|
250
|
+
regression baseline.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 8. Registration
|
|
255
|
+
|
|
256
|
+
This reference is registered in
|
|
257
|
+
[`reference/registry.json`](./registry.json) as the `native-platforms` entry
|
|
258
|
+
(type `heuristic`, phase `34.1`) so the registry round-trip test
|
|
259
|
+
(`test/suite/reference-registry.test.cjs`) stays green — every `reference/*.md`
|
|
260
|
+
must be registered and resolve (D-05, the 33.5-01 lesson).
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 9. Cross-references
|
|
265
|
+
|
|
266
|
+
- [`reference/platforms.md`](./platforms.md) — the interaction-conventions
|
|
267
|
+
sibling (navigation, safe areas, gestures, native typography). Executors read
|
|
268
|
+
**both**: this file for the token→theme bridge, that file for layout/behavior.
|
|
269
|
+
- `scripts/lib/design-tokens/` — the Phase-23 token engine this bridge extends
|
|
270
|
+
(`index.cjs` facade + `css-vars` / `js-const` / `tailwind` / `figma` readers +
|
|
271
|
+
the new `swift` / `compose` / `flutter` emitters).
|
|
272
|
+
- `test/fixtures/mapper-outputs/tokens.json` — the canonical token fixture the
|
|
273
|
+
round-trip test derives its map from.
|
package/reference/registry.json
CHANGED
|
@@ -888,6 +888,20 @@
|
|
|
888
888
|
"type": "data",
|
|
889
889
|
"phase": 33.6,
|
|
890
890
|
"description": "Phase 33.6 catalog-derived OpenRouter price sub-table — per-model prompt/completion $/tok snapshot of .design/cache/openrouter-models.json; derived view, the dynamic catalog is the source of truth (D-11 registry round-trip)."
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
"name": "native-platforms",
|
|
894
|
+
"path": "reference/native-platforms.md",
|
|
895
|
+
"type": "heuristic",
|
|
896
|
+
"phase": 34.1,
|
|
897
|
+
"description": "Phase 34.1 token-bridge spec — maps the canonical CSS-token form (Phase 23) to SwiftUI Color/Font/ViewModifier, Jetpack Compose Color/Shapes/Typography/MaterialTheme, and Flutter ThemeData/ColorScheme/TextTheme, with the precision contract (color hex→8-bit channels exact, dimension px→pt/dp/logical px) defining token-identity for the round-trip."
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
"name": "email-design",
|
|
901
|
+
"path": "reference/email-design.md",
|
|
902
|
+
"type": "heuristic",
|
|
903
|
+
"phase": 34.2,
|
|
904
|
+
"description": "Phase 34.2 email-constraint catalogue — table-based layout (not flexbox/grid/position), inline styles (not a <style> block), MSO conditional comments for Outlook, dark-mode color-scheme/prefers-color-scheme handling, ~600px max-width, image/alt rules, and top-20-client quirks; the authority the email-executor generates against and the design-verifier email branch audits against, and the rule-id source for scripts/lib/email/validate-email-html.cjs."
|
|
891
905
|
}
|
|
892
906
|
]
|
|
893
907
|
}
|