reflection-check 0.0.3 → 0.0.5

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.
@@ -30,7 +30,7 @@ The IR currently supports four families:
30
30
  | --- | --- | --- | --- |
31
31
  | `browser-route` | browser routes | `smoke`, `full` | Render a route and evaluate DOM/layout/console expectations. |
32
32
  | `route-visual` | browser visual smoke cases | `smoke`, `full` | Compare route screenshots against read-only baselines. |
33
- | `component-visual` | Storybook component cases | `visual`, `full` | Compare component story screenshots against read-only baselines. |
33
+ | `component-visual` | Storybook or portal component cases | `visual`, `full` | Compare component screenshots against read-only baselines. |
34
34
  | `design-command` | design command checks | `design`, `full` | Run project-owned design contract commands. |
35
35
 
36
36
  All targets include:
@@ -59,20 +59,20 @@ console.log(ir.targets.map((target) => `${target.family}:${target.id}`));
59
59
  // ]
60
60
  ```
61
61
 
62
- The compiler preserves visual metadata that matters to later review, including zero-valued thresholds. Do not use truthiness checks when copying optional numeric metadata; use explicit `!== undefined` checks so values like `0` survive.
62
+ The compiler preserves visual metadata that matters to later review, including component source (`storybook` or `portal`), portal paths, story ids, zero-valued thresholds, explicit component `viewportSize` values, and component `framing`. Do not use truthiness checks when copying optional numeric metadata; use explicit `!== undefined` checks so values like `0` survive.
63
63
 
64
64
  ## Adapter contract
65
65
 
66
66
  Adapters should compile their input into the same target shape and mark targets with `source: 'adapter'`.
67
67
 
68
- Adapters must not make core runners aware of the source format. The runner should receive normalized route/story/visual/command information and should not branch on adapter names.
68
+ Adapters must not make core runners aware of the source format. The runner should receive normalized route/component/visual/command information and should not branch on adapter names.
69
69
 
70
70
  A good adapter is:
71
71
 
72
72
  - **optional** — normal Reflection config works without it;
73
73
  - **validated** — malformed adapter input fails before runner execution;
74
74
  - **neutral** — no external product names leak into core commands or reports unless the user explicitly names their own target ids;
75
- - **lossless enough for review** — route paths, viewports, expectations, baselines, thresholds, and blocking semantics are preserved in IR.
75
+ - **lossless enough for review** — route paths, component portal paths or Storybook ids, viewports, component viewport sizes, component framing, expectations, baselines, thresholds, and blocking semantics are preserved in IR.
76
76
 
77
77
  ## Route manifest adapter proof
78
78
 
@@ -82,7 +82,7 @@ If the matching browser route result is missing, the visual check becomes a revi
82
82
 
83
83
  ## Component visual baselines
84
84
 
85
- Component visual cases use Storybook. Reflection resolves the configured `storyId` through Storybook `/index.json`, opens the iframe URL, captures a screenshot, and compares it with the configured baseline.
85
+ Component visual cases can use Storybook or a Reflection-generated portal. Storybook cases resolve `storyId` through Storybook `/index.json`; portal cases open a configured `path` in a generated Vite runtime.
86
86
 
87
87
  ```ts
88
88
  component: {
@@ -96,7 +96,13 @@ component: {
96
96
  {
97
97
  id: 'button-primary',
98
98
  storyId: 'atoms-button--primary',
99
- viewport: 'component',
99
+ viewport: 'button-default',
100
+ viewportSize: { width: 390, height: 220 },
101
+ framing: {
102
+ background: '#ffffff',
103
+ align: 'center',
104
+ padding: 0
105
+ },
100
106
  baselineRoot: 'tests/fixtures/baselines',
101
107
  baseline: 'components/button/primary.chromium-linux.light.png',
102
108
  threshold: { maxDiffPixelRatio: 0.005 }
@@ -105,6 +111,44 @@ component: {
105
111
  }
106
112
  ```
107
113
 
114
+ Portal cases use the same case fields, but replace `storyId` with `path` and configure `component.portal`:
115
+
116
+ ```ts
117
+ component: {
118
+ portal: {
119
+ entry: './tests/reflection/react-portal.tsx',
120
+ readyUrl: 'http://127.0.0.1:6106'
121
+ },
122
+ cases: [
123
+ {
124
+ id: 'button-primary',
125
+ path: '/reflection/button/primary/light',
126
+ viewport: 'button-default',
127
+ viewportSize: { width: 390, height: 220 },
128
+ framing: {
129
+ rootSelector: '#reflection-root',
130
+ background: '#ffffff',
131
+ align: 'center',
132
+ padding: 0
133
+ },
134
+ baselineRoot: 'tests/fixtures/baselines',
135
+ baseline: 'components/button/primary.chromium-linux.light.png',
136
+ threshold: { maxDiffPixels: 0, maxDiffPixelRatio: 0 },
137
+ strict: true
138
+ }
139
+ ]
140
+ }
141
+ ```
142
+
143
+ For component baselines exported from a design tool, treat `viewportSize` and `framing` as part of the visual contract. The exported PNG width/height and the runtime screenshot width/height must be identical. `viewport` may be a built-in preset such as `component` or a semantic custom label such as `button-default`; when `viewportSize` is present, the explicit dimensions win. Portal cases require `viewportSize`, and the generated frame uses those dimensions directly.
144
+
145
+ `framing` is optional and only affects the screenshot when configured. It applies fixed canvas styles before capture so the runtime component can match a Figma frame:
146
+
147
+ - `rootSelector`: root to frame; defaults to `#storybook-root` for Storybook and `#reflection-root` for portal cases.
148
+ - `background`: CSS background matching the Figma frame fill.
149
+ - `align`: `center` or `start`; `center` places the component in the middle of the frame.
150
+ - `padding`: integer pixel padding inside the frame.
151
+
108
152
  ### Pseudo states
109
153
 
110
154
  Prefer story-controlled states. A story named `Button/Hover` or `Button/Focused` is usually more deterministic than moving the mouse in the browser.
@@ -130,6 +174,7 @@ When the browser must force a state, Reflection requires effective animation sta
130
174
  Reports include `statePolicy` metadata:
131
175
 
132
176
  - `story-controlled` when no `browserState` is configured;
177
+ - `portal-controlled` when the generated portal renders the state;
133
178
  - `browser-forced-with-stabilization` when Reflection applies hover/focus in the browser.
134
179
 
135
180
  ## Thresholds
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reflection-check",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "CLI for evidence-backed rendered UI validation.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,8 +45,9 @@
45
45
  "commander": "^14.0.3",
46
46
  "jiti": "^2.7.0",
47
47
  "pixelmatch": "^7.2.0",
48
- "pngjs": "^7.0.0",
49
48
  "playwright": "^1.60.0",
49
+ "pngjs": "^7.0.0",
50
+ "vite": "^8.0.16",
50
51
  "zod": "^4.4.3"
51
52
  },
52
53
  "devDependencies": {