@reqvire-org/ads 0.3.7 → 0.3.9
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/dist/atomic-composition.mjs +85 -54
- package/dist/atomic-trace.mjs +92 -0
- package/dist/bootstrap-ds/scripts/build-mcp-previews.mjs +2 -1
- package/dist/bootstrap-ds/vite.config.ts +2 -1
- package/dist/check.mjs +14 -3
- package/dist/linaria/atomic-css.cjs +2 -0
- package/dist/linaria/atomic-styled.cjs +2 -0
- package/dist/linaria/preserve-values.cjs +32 -0
- package/dist/linaria.d.mts +1 -0
- package/dist/linaria.mjs +9 -0
- package/dist/mcp/preview-widget.bundle.js +1 -1
- package/dist/mcp/resource-loader.js +5 -2
- package/dist/mcp/server.mjs +4 -2
- package/dist/scenario-trace.mjs +149 -0
- package/dist/scenarios-compiler.mjs +2 -1
- package/dist/scenarios.mjs +22 -6
- package/dist/skills/ads-break-ui/SKILL.md +10 -2
- package/dist/skills/ads-break-ui/references/atomic-styles.md +122 -7
- package/dist/skills/ads-break-ui/references/scenarios.md +83 -0
- package/dist/skills/ads-designer/SKILL.md +13 -3
- package/dist/skills/ads-designer/assets/bootstrap-ds/scripts/build-mcp-previews.mjs +2 -1
- package/dist/skills/ads-designer/assets/bootstrap-ds/vite.config.ts +2 -1
- package/dist/skills/ads-designer/references/atomic-styles.md +122 -7
- package/dist/skills/ads-designer/references/scenarios.md +83 -0
- package/dist/skills/ads-import/SKILL.md +10 -2
- package/dist/skills/ads-import/references/atomic-styles.md +122 -7
- package/dist/skills/ads-import/references/scenarios.md +83 -0
- package/dist/skills/ads-migrate/SKILL.md +10 -2
- package/dist/skills/ads-migrate/references/atomic-styles.md +122 -7
- package/dist/skills/ads-migrate/references/scenarios.md +83 -0
- package/dist/skills/ads-motion/SKILL.md +10 -2
- package/dist/skills/ads-motion/references/atomic-styles.md +122 -7
- package/dist/skills/ads-motion/references/scenarios.md +83 -0
- package/dist/source-values.mjs +247 -0
- package/dist/verification/browser.mjs +21 -12
- package/dist/verification/playwright-reporter.cjs +6 -2
- package/dist/verification.mjs +8 -3
- package/docs/ADS.md +18 -5
- package/docs/ARCHITECTURE.md +19 -0
- package/docs/BEHAVIORS.md +15 -1
- package/docs/CHANGELOG.md +28 -1
- package/docs/MCP.md +18 -0
- package/package.json +10 -1
|
@@ -81,6 +81,91 @@ const treatment = css`
|
|
|
81
81
|
This is a distinct state, not two competing base atoms. Whether changing padding
|
|
82
82
|
on hover is desirable remains a product decision.
|
|
83
83
|
|
|
84
|
+
## Preserve custom-property arithmetic during extraction
|
|
85
|
+
|
|
86
|
+
Custom properties can hold an unevaluated token stream that becomes arithmetic
|
|
87
|
+
only after substitution into a later `calc()`. For example:
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
--i: 2;
|
|
91
|
+
--scale: var(--i) * 0.05 + 1;
|
|
92
|
+
transform: scale(calc(-1 * var(--scale)));
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The result is scale `0.9`: substitution does not add parentheses around the
|
|
96
|
+
custom property. Some Linaria/Stylis extraction versions strip the whitespace
|
|
97
|
+
around the bare `+`, leaving `0.05+1`. The browser then treats the transform as
|
|
98
|
+
invalid. This can happen with CSS minification disabled. Source guards cannot
|
|
99
|
+
prove what a later extractor emits.
|
|
100
|
+
|
|
101
|
+
Use ADS's build-only resolver in **both** the application's Linaria configuration
|
|
102
|
+
and its separately configured MCP preview builder:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
import wyw from "@wyw-in-js/vite";
|
|
106
|
+
import { linariaTagResolver } from "@reqvire-org/ads/linaria";
|
|
107
|
+
|
|
108
|
+
// In the project's Vite plugins:
|
|
109
|
+
wyw({ tagResolver: linariaTagResolver });
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The resolver keeps the project's installed atomic processor and preserves opaque
|
|
113
|
+
custom-property values through its nesting/atomization pass. It does not add a
|
|
114
|
+
browser runtime or alter token ownership. Existing projects must adopt it in
|
|
115
|
+
each extraction pipeline; upgrading ADS alone does not rewrite custom builders.
|
|
116
|
+
If an existing `tagResolver` handles other tags, delegate atomic imports to
|
|
117
|
+
`linariaTagResolver(source, tag)` and retain the existing fallback. WyW config
|
|
118
|
+
files also support this resolver when a build integration requires serializable
|
|
119
|
+
inline options.
|
|
120
|
+
|
|
121
|
+
For a source workaround, write a self-contained formula and consume it directly:
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
--scale: calc(1 - var(--i) * 0.05);
|
|
125
|
+
transform: scale(var(--scale));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This also produces `0.9`. Simply wrapping the old value in `calc()` while keeping
|
|
129
|
+
the outer `-1 *` changes its grouping and result. Verify extracted CSS text and a
|
|
130
|
+
computed style after any rewrite, including nested `var()` fallbacks/transforms.
|
|
131
|
+
Do not globally insert spaces into already extracted CSS: that can change valid
|
|
132
|
+
signed numbers, identifiers, strings or URLs.
|
|
133
|
+
|
|
134
|
+
## Follow helpers and forwarded classes
|
|
135
|
+
|
|
136
|
+
Composition can cross a helper or a component boundary. For example, in component
|
|
137
|
+
implementation source:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
function baseClasses() { return base; }
|
|
141
|
+
function Primitive({ className, ...props }) {
|
|
142
|
+
return <button {...props} className={cx(baseClasses(), className)} />;
|
|
143
|
+
}
|
|
144
|
+
function CompactPrimitive(props) {
|
|
145
|
+
return <Primitive {...props} className={small} />;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The base and small atoms can reach the same button. Inspect both owners when
|
|
150
|
+
correcting their property vocabulary. The wrapper's class does not style every
|
|
151
|
+
descendant: a class on a child or sibling element is a separate composition.
|
|
152
|
+
An imported stylesheet alone does not show that its classes co-apply.
|
|
153
|
+
|
|
154
|
+
Spread order matters. `<button className={base} {...props} />` can replace the
|
|
155
|
+
entire className; `<button {...props} className={cx(base, className)} />` composes
|
|
156
|
+
it explicitly. Keep the product's intended forwarding behavior when fixing an
|
|
157
|
+
overlap. Do not change the public API simply to make tracing easier.
|
|
158
|
+
|
|
159
|
+
Warnings include a bounded `composition` path through traced helpers and
|
|
160
|
+
components. A `target` identifies a statically reached DOM element. This
|
|
161
|
+
establishes source composition, not that a scenario mounted it or that its
|
|
162
|
+
computed result is wrong. `writingModeDependent: true` identifies overlaps that
|
|
163
|
+
change with direction or writing mode; evaluate those against supported modes.
|
|
164
|
+
Literal overlaps with provably equal values are omitted where the analyzer can
|
|
165
|
+
expand them safely. Token formulas, inheritance and runtime values still need
|
|
166
|
+
review. A margin shorthand and `margin-inline: auto` may agree horizontally but
|
|
167
|
+
disagree in vertical text, so horizontal equality alone is insufficient.
|
|
168
|
+
|
|
84
169
|
## Respond to a warning
|
|
85
170
|
|
|
86
171
|
1. Follow the warning's related file/line locations and inspect the actual `cx`
|
|
@@ -101,6 +186,24 @@ the conflict. Prefer resolving the composition itself. If an overlap is
|
|
|
101
186
|
intentional, document why its precedence is stable and verify the resulting
|
|
102
187
|
behavior rather than changing product capabilities to silence a warning.
|
|
103
188
|
|
|
189
|
+
Use existing behavior contracts and `ads_verify` for important geometry. Register
|
|
190
|
+
a check beside its owner, link its real scenario and use the project's browser
|
|
191
|
+
profile. An assertion can inspect the actual control in the App frame:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
const frame = await openBehaviorScenario(page, "compact-spacing");
|
|
195
|
+
await expect(frame.getByRole("button", { name: "Compact" }))
|
|
196
|
+
.toHaveCSS("padding-inline-start", "8px");
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The name, check ID and expected value above are examples. Derive assertions from
|
|
200
|
+
the project's intended API and design, with relevant variants, open/hover/focus
|
|
201
|
+
states, themes and supported writing modes. Wait for observable readiness; an
|
|
202
|
+
event-stream connection can prevent `networkidle` from settling. A generic
|
|
203
|
+
overlap scan cannot decide which dimensions the product intended. A failed
|
|
204
|
+
registered geometry assertion is blocking through the existing verification
|
|
205
|
+
contract; an overlap warning remains a review signal.
|
|
206
|
+
|
|
104
207
|
## What ADS checks
|
|
105
208
|
|
|
106
209
|
`atomic-composition-review` is a nonblocking warning, returned under `advisories`
|
|
@@ -108,14 +211,26 @@ with `severity: "warning"`. CLI checks still exit successfully when there are no
|
|
|
108
211
|
errors; MCP checks and mutations preserve the warning and its guidance URI.
|
|
109
212
|
|
|
110
213
|
The source review examines atomic Linaria templates and statically traceable
|
|
111
|
-
`cx` arguments, including aliases, local imports,
|
|
112
|
-
expressions and variant-object
|
|
214
|
+
`cx` arguments, including aliases, local imports/re-exports, local helper returns,
|
|
215
|
+
arguments/defaults, nested composition, conditional expressions and variant-object
|
|
216
|
+
lookups. It follows explicit `className` forwarding through local function
|
|
217
|
+
components, props/rest spreads and imported React/Preact `memo`/`forwardRef`
|
|
218
|
+
wrappers to an intrinsic JSX element. It checks known overlaps involving box
|
|
113
219
|
spacing, inset, borders/corners, dimensions, overflow, backgrounds, typography,
|
|
114
220
|
motion and common layout shorthands, plus `all` resets. It compares shared selector/condition contexts; separate alternatives
|
|
115
|
-
within one conditional or lookup are not assumed to apply together.
|
|
221
|
+
within one conditional or lookup are not assumed to apply together. Matching
|
|
222
|
+
atomic property/selector/condition keys are replaced during traced `cx`
|
|
223
|
+
composition before comparing the surviving declarations.
|
|
116
224
|
|
|
117
225
|
This is a bounded source review, not a CSS engine or a proof of precedence. It
|
|
118
|
-
does not
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
226
|
+
does not execute helpers or import project modules, enumerate every external
|
|
227
|
+
class, or model arbitrary runtime mutation, component rendering, selectors or
|
|
228
|
+
overlapping media queries. Recursive calls, unsupported call/control-flow paths,
|
|
229
|
+
unresolved DOM targets and traversal limits produce bounded
|
|
230
|
+
`atomic-composition-limit` informational advisories where known atomic styles
|
|
231
|
+
reach an unresolved composition. Ordinary externally supplied className values
|
|
232
|
+
remain open inputs, not guaranteed safe styles. Depth, work and alternative
|
|
233
|
+
limits keep source inspection bounded; an unreported path or an empty warning
|
|
234
|
+
list does not establish visual correctness. Actual failed registered
|
|
235
|
+
browser/behavior checks remain blocking; potentially intentional source overlaps
|
|
236
|
+
remain warnings.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Scenario render evidence and public class helpers
|
|
2
|
+
|
|
3
|
+
Use this reference for `scenario-render`, `scenario-coverage` or `showcase-class`
|
|
4
|
+
diagnostics. MCP serves the same guide as `ads://guides/scenarios`.
|
|
5
|
+
|
|
6
|
+
## Public children can share their parent's scenario
|
|
7
|
+
|
|
8
|
+
Declare the public parts actually demonstrated in `covers`, even when their
|
|
9
|
+
parent renders them internally:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
// Runtime owner
|
|
13
|
+
export function Part() { return <span>Content</span>; }
|
|
14
|
+
export function Frame({ visible = true } = {}) {
|
|
15
|
+
return <section>{visible && <Part />}</section>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Colocated Scenario.tsx
|
|
19
|
+
import { Frame } from "@ds";
|
|
20
|
+
export const scenario = { title: "Frame", covers: ["Frame", "Part"] };
|
|
21
|
+
export default function Scenario() { return <Frame />; }
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
ADS follows returned JSX through local components/helpers, aliases, registered
|
|
25
|
+
framework factories, memo/ref wrappers and context providers. Fixture props,
|
|
26
|
+
spreads and destructuring defaults select the child path. An explicit
|
|
27
|
+
`visible={false}` does not cover `Part`. When a condition remains unknown, only
|
|
28
|
+
parts returned on every possible path receive transitive credit. Imports, unused
|
|
29
|
+
JSX, event-handler bodies and uncalled functions do not establish child rendering.
|
|
30
|
+
Only the explicitly declared `covers` entries acquire coverage; dependencies are
|
|
31
|
+
not silently added to metadata or the catalogue.
|
|
32
|
+
|
|
33
|
+
Tracing is bounded and does not execute the project. Recursion, written/escaped inputs,
|
|
34
|
+
opaque external wrappers and unsupported control flow can prevent proof. The
|
|
35
|
+
diagnostic explains known limits. Use meaningful fixture states or another real
|
|
36
|
+
scenario; do not make a valid public part private or duplicate it in a parent
|
|
37
|
+
just to satisfy a guard. Static evidence does not establish successful mounting,
|
|
38
|
+
visibility, interaction, every state or visual parity. Verify those in the built
|
|
39
|
+
App and registered browser checks.
|
|
40
|
+
|
|
41
|
+
## Scenarios may consume verified public style helpers
|
|
42
|
+
|
|
43
|
+
A deliberately public helper can expose its owner's Linaria treatment:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// Runtime owner
|
|
47
|
+
const treatment = css`padding-inline: var(--space-4);`;
|
|
48
|
+
export function controlClasses() {
|
|
49
|
+
return cx("control-hook", treatment);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Scenario: consume the public API; define no styles here
|
|
53
|
+
<PublicLink className={controlClasses()}>Documentation</PublicLink>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The sample token and APIs are illustrative; use the project's real exports and
|
|
57
|
+
tokens. Import helpers through the public ADS entry. ADS traces helper calls,
|
|
58
|
+
returns, local aliases, finite known selections and Linaria `cx` composition.
|
|
59
|
+
Styling must originate in component/pattern Linaria through a public callable
|
|
60
|
+
helper. A literal stable hook authored in that runtime owner may accompany the
|
|
61
|
+
verified styles. Each nonempty return path needs actual Linaria declarations;
|
|
62
|
+
empty templates and hook-only/string-only returns do not qualify.
|
|
63
|
+
|
|
64
|
+
These are still rejected:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
<PublicLink className="custom-layout" />
|
|
68
|
+
<PublicLink className={arbitraryPublicFunctionReturningAString()} />
|
|
69
|
+
<PublicLink className={controlClasses({ className: "custom-layout" })} />
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Scenario-supplied strings, generated styles in fixtures, private style imports,
|
|
73
|
+
spoofed Linaria functions and untraceable results acquire no permission by being
|
|
74
|
+
passed through a public function. Known className values in props/spreads receive
|
|
75
|
+
the same provenance check. Opaque spreads remain outside complete source proof.
|
|
76
|
+
An `exercises` entry records behavioral use; it never grants styling permission.
|
|
77
|
+
|
|
78
|
+
Keep helper signatures compatible with the product API. Fix ownership/provenance
|
|
79
|
+
in the runtime owner when genuinely wrong; retain intentional public APIs when
|
|
80
|
+
reporting an unsupported trace. Demo-only sizing, framing and arrangement still
|
|
81
|
+
belong in public review/layout patterns. A style helper is not permission to
|
|
82
|
+
author a second design in the scenario. For cascade risks and extraction issues,
|
|
83
|
+
read [atomic-styles.md](atomic-styles.md).
|
|
@@ -44,10 +44,18 @@ use the client's normal source-editing tools.
|
|
|
44
44
|
For owner drafts, token-sample reconciliation, coupled transactions and local
|
|
45
45
|
constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
|
|
46
46
|
|
|
47
|
-
For
|
|
47
|
+
For parent/child `covers`, public style-helper use in scenarios, or
|
|
48
|
+
`scenario-render` / `showcase-class` findings, read
|
|
49
|
+
[scenario contracts](references/scenarios.md). Traced parents can cover declared
|
|
50
|
+
public children; verified public Linaria helpers do not grant custom scenario CSS.
|
|
51
|
+
|
|
52
|
+
For atomic base/variant styling, helper/wrapper class composition, extracted
|
|
53
|
+
custom-property arithmetic, or an
|
|
54
|
+
`atomic-composition-review` / `atomic-composition-limit` advisory, read
|
|
48
55
|
[atomic style composition](references/atomic-styles.md). The same guide is served
|
|
49
56
|
as `ads://guides/atomic-styles`. It explains shorthand/logical conflicts and
|
|
50
|
-
matching-property fixes
|
|
57
|
+
matching-property fixes, plus the build resolver for preserving opaque custom
|
|
58
|
+
properties through extraction. Review warnings in the real `cx` composition and verify
|
|
51
59
|
computed styles across affected sizes/states; retain intentional product behavior.
|
|
52
60
|
|
|
53
61
|
Controllers are UI behavior owners: private helpers stay with their component or
|
|
@@ -81,6 +81,91 @@ const treatment = css`
|
|
|
81
81
|
This is a distinct state, not two competing base atoms. Whether changing padding
|
|
82
82
|
on hover is desirable remains a product decision.
|
|
83
83
|
|
|
84
|
+
## Preserve custom-property arithmetic during extraction
|
|
85
|
+
|
|
86
|
+
Custom properties can hold an unevaluated token stream that becomes arithmetic
|
|
87
|
+
only after substitution into a later `calc()`. For example:
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
--i: 2;
|
|
91
|
+
--scale: var(--i) * 0.05 + 1;
|
|
92
|
+
transform: scale(calc(-1 * var(--scale)));
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The result is scale `0.9`: substitution does not add parentheses around the
|
|
96
|
+
custom property. Some Linaria/Stylis extraction versions strip the whitespace
|
|
97
|
+
around the bare `+`, leaving `0.05+1`. The browser then treats the transform as
|
|
98
|
+
invalid. This can happen with CSS minification disabled. Source guards cannot
|
|
99
|
+
prove what a later extractor emits.
|
|
100
|
+
|
|
101
|
+
Use ADS's build-only resolver in **both** the application's Linaria configuration
|
|
102
|
+
and its separately configured MCP preview builder:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
import wyw from "@wyw-in-js/vite";
|
|
106
|
+
import { linariaTagResolver } from "@reqvire-org/ads/linaria";
|
|
107
|
+
|
|
108
|
+
// In the project's Vite plugins:
|
|
109
|
+
wyw({ tagResolver: linariaTagResolver });
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The resolver keeps the project's installed atomic processor and preserves opaque
|
|
113
|
+
custom-property values through its nesting/atomization pass. It does not add a
|
|
114
|
+
browser runtime or alter token ownership. Existing projects must adopt it in
|
|
115
|
+
each extraction pipeline; upgrading ADS alone does not rewrite custom builders.
|
|
116
|
+
If an existing `tagResolver` handles other tags, delegate atomic imports to
|
|
117
|
+
`linariaTagResolver(source, tag)` and retain the existing fallback. WyW config
|
|
118
|
+
files also support this resolver when a build integration requires serializable
|
|
119
|
+
inline options.
|
|
120
|
+
|
|
121
|
+
For a source workaround, write a self-contained formula and consume it directly:
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
--scale: calc(1 - var(--i) * 0.05);
|
|
125
|
+
transform: scale(var(--scale));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This also produces `0.9`. Simply wrapping the old value in `calc()` while keeping
|
|
129
|
+
the outer `-1 *` changes its grouping and result. Verify extracted CSS text and a
|
|
130
|
+
computed style after any rewrite, including nested `var()` fallbacks/transforms.
|
|
131
|
+
Do not globally insert spaces into already extracted CSS: that can change valid
|
|
132
|
+
signed numbers, identifiers, strings or URLs.
|
|
133
|
+
|
|
134
|
+
## Follow helpers and forwarded classes
|
|
135
|
+
|
|
136
|
+
Composition can cross a helper or a component boundary. For example, in component
|
|
137
|
+
implementation source:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
function baseClasses() { return base; }
|
|
141
|
+
function Primitive({ className, ...props }) {
|
|
142
|
+
return <button {...props} className={cx(baseClasses(), className)} />;
|
|
143
|
+
}
|
|
144
|
+
function CompactPrimitive(props) {
|
|
145
|
+
return <Primitive {...props} className={small} />;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The base and small atoms can reach the same button. Inspect both owners when
|
|
150
|
+
correcting their property vocabulary. The wrapper's class does not style every
|
|
151
|
+
descendant: a class on a child or sibling element is a separate composition.
|
|
152
|
+
An imported stylesheet alone does not show that its classes co-apply.
|
|
153
|
+
|
|
154
|
+
Spread order matters. `<button className={base} {...props} />` can replace the
|
|
155
|
+
entire className; `<button {...props} className={cx(base, className)} />` composes
|
|
156
|
+
it explicitly. Keep the product's intended forwarding behavior when fixing an
|
|
157
|
+
overlap. Do not change the public API simply to make tracing easier.
|
|
158
|
+
|
|
159
|
+
Warnings include a bounded `composition` path through traced helpers and
|
|
160
|
+
components. A `target` identifies a statically reached DOM element. This
|
|
161
|
+
establishes source composition, not that a scenario mounted it or that its
|
|
162
|
+
computed result is wrong. `writingModeDependent: true` identifies overlaps that
|
|
163
|
+
change with direction or writing mode; evaluate those against supported modes.
|
|
164
|
+
Literal overlaps with provably equal values are omitted where the analyzer can
|
|
165
|
+
expand them safely. Token formulas, inheritance and runtime values still need
|
|
166
|
+
review. A margin shorthand and `margin-inline: auto` may agree horizontally but
|
|
167
|
+
disagree in vertical text, so horizontal equality alone is insufficient.
|
|
168
|
+
|
|
84
169
|
## Respond to a warning
|
|
85
170
|
|
|
86
171
|
1. Follow the warning's related file/line locations and inspect the actual `cx`
|
|
@@ -101,6 +186,24 @@ the conflict. Prefer resolving the composition itself. If an overlap is
|
|
|
101
186
|
intentional, document why its precedence is stable and verify the resulting
|
|
102
187
|
behavior rather than changing product capabilities to silence a warning.
|
|
103
188
|
|
|
189
|
+
Use existing behavior contracts and `ads_verify` for important geometry. Register
|
|
190
|
+
a check beside its owner, link its real scenario and use the project's browser
|
|
191
|
+
profile. An assertion can inspect the actual control in the App frame:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
const frame = await openBehaviorScenario(page, "compact-spacing");
|
|
195
|
+
await expect(frame.getByRole("button", { name: "Compact" }))
|
|
196
|
+
.toHaveCSS("padding-inline-start", "8px");
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The name, check ID and expected value above are examples. Derive assertions from
|
|
200
|
+
the project's intended API and design, with relevant variants, open/hover/focus
|
|
201
|
+
states, themes and supported writing modes. Wait for observable readiness; an
|
|
202
|
+
event-stream connection can prevent `networkidle` from settling. A generic
|
|
203
|
+
overlap scan cannot decide which dimensions the product intended. A failed
|
|
204
|
+
registered geometry assertion is blocking through the existing verification
|
|
205
|
+
contract; an overlap warning remains a review signal.
|
|
206
|
+
|
|
104
207
|
## What ADS checks
|
|
105
208
|
|
|
106
209
|
`atomic-composition-review` is a nonblocking warning, returned under `advisories`
|
|
@@ -108,14 +211,26 @@ with `severity: "warning"`. CLI checks still exit successfully when there are no
|
|
|
108
211
|
errors; MCP checks and mutations preserve the warning and its guidance URI.
|
|
109
212
|
|
|
110
213
|
The source review examines atomic Linaria templates and statically traceable
|
|
111
|
-
`cx` arguments, including aliases, local imports,
|
|
112
|
-
expressions and variant-object
|
|
214
|
+
`cx` arguments, including aliases, local imports/re-exports, local helper returns,
|
|
215
|
+
arguments/defaults, nested composition, conditional expressions and variant-object
|
|
216
|
+
lookups. It follows explicit `className` forwarding through local function
|
|
217
|
+
components, props/rest spreads and imported React/Preact `memo`/`forwardRef`
|
|
218
|
+
wrappers to an intrinsic JSX element. It checks known overlaps involving box
|
|
113
219
|
spacing, inset, borders/corners, dimensions, overflow, backgrounds, typography,
|
|
114
220
|
motion and common layout shorthands, plus `all` resets. It compares shared selector/condition contexts; separate alternatives
|
|
115
|
-
within one conditional or lookup are not assumed to apply together.
|
|
221
|
+
within one conditional or lookup are not assumed to apply together. Matching
|
|
222
|
+
atomic property/selector/condition keys are replaced during traced `cx`
|
|
223
|
+
composition before comparing the surviving declarations.
|
|
116
224
|
|
|
117
225
|
This is a bounded source review, not a CSS engine or a proof of precedence. It
|
|
118
|
-
does not
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
226
|
+
does not execute helpers or import project modules, enumerate every external
|
|
227
|
+
class, or model arbitrary runtime mutation, component rendering, selectors or
|
|
228
|
+
overlapping media queries. Recursive calls, unsupported call/control-flow paths,
|
|
229
|
+
unresolved DOM targets and traversal limits produce bounded
|
|
230
|
+
`atomic-composition-limit` informational advisories where known atomic styles
|
|
231
|
+
reach an unresolved composition. Ordinary externally supplied className values
|
|
232
|
+
remain open inputs, not guaranteed safe styles. Depth, work and alternative
|
|
233
|
+
limits keep source inspection bounded; an unreported path or an empty warning
|
|
234
|
+
list does not establish visual correctness. Actual failed registered
|
|
235
|
+
browser/behavior checks remain blocking; potentially intentional source overlaps
|
|
236
|
+
remain warnings.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Scenario render evidence and public class helpers
|
|
2
|
+
|
|
3
|
+
Use this reference for `scenario-render`, `scenario-coverage` or `showcase-class`
|
|
4
|
+
diagnostics. MCP serves the same guide as `ads://guides/scenarios`.
|
|
5
|
+
|
|
6
|
+
## Public children can share their parent's scenario
|
|
7
|
+
|
|
8
|
+
Declare the public parts actually demonstrated in `covers`, even when their
|
|
9
|
+
parent renders them internally:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
// Runtime owner
|
|
13
|
+
export function Part() { return <span>Content</span>; }
|
|
14
|
+
export function Frame({ visible = true } = {}) {
|
|
15
|
+
return <section>{visible && <Part />}</section>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Colocated Scenario.tsx
|
|
19
|
+
import { Frame } from "@ds";
|
|
20
|
+
export const scenario = { title: "Frame", covers: ["Frame", "Part"] };
|
|
21
|
+
export default function Scenario() { return <Frame />; }
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
ADS follows returned JSX through local components/helpers, aliases, registered
|
|
25
|
+
framework factories, memo/ref wrappers and context providers. Fixture props,
|
|
26
|
+
spreads and destructuring defaults select the child path. An explicit
|
|
27
|
+
`visible={false}` does not cover `Part`. When a condition remains unknown, only
|
|
28
|
+
parts returned on every possible path receive transitive credit. Imports, unused
|
|
29
|
+
JSX, event-handler bodies and uncalled functions do not establish child rendering.
|
|
30
|
+
Only the explicitly declared `covers` entries acquire coverage; dependencies are
|
|
31
|
+
not silently added to metadata or the catalogue.
|
|
32
|
+
|
|
33
|
+
Tracing is bounded and does not execute the project. Recursion, written/escaped inputs,
|
|
34
|
+
opaque external wrappers and unsupported control flow can prevent proof. The
|
|
35
|
+
diagnostic explains known limits. Use meaningful fixture states or another real
|
|
36
|
+
scenario; do not make a valid public part private or duplicate it in a parent
|
|
37
|
+
just to satisfy a guard. Static evidence does not establish successful mounting,
|
|
38
|
+
visibility, interaction, every state or visual parity. Verify those in the built
|
|
39
|
+
App and registered browser checks.
|
|
40
|
+
|
|
41
|
+
## Scenarios may consume verified public style helpers
|
|
42
|
+
|
|
43
|
+
A deliberately public helper can expose its owner's Linaria treatment:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// Runtime owner
|
|
47
|
+
const treatment = css`padding-inline: var(--space-4);`;
|
|
48
|
+
export function controlClasses() {
|
|
49
|
+
return cx("control-hook", treatment);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Scenario: consume the public API; define no styles here
|
|
53
|
+
<PublicLink className={controlClasses()}>Documentation</PublicLink>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The sample token and APIs are illustrative; use the project's real exports and
|
|
57
|
+
tokens. Import helpers through the public ADS entry. ADS traces helper calls,
|
|
58
|
+
returns, local aliases, finite known selections and Linaria `cx` composition.
|
|
59
|
+
Styling must originate in component/pattern Linaria through a public callable
|
|
60
|
+
helper. A literal stable hook authored in that runtime owner may accompany the
|
|
61
|
+
verified styles. Each nonempty return path needs actual Linaria declarations;
|
|
62
|
+
empty templates and hook-only/string-only returns do not qualify.
|
|
63
|
+
|
|
64
|
+
These are still rejected:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
<PublicLink className="custom-layout" />
|
|
68
|
+
<PublicLink className={arbitraryPublicFunctionReturningAString()} />
|
|
69
|
+
<PublicLink className={controlClasses({ className: "custom-layout" })} />
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Scenario-supplied strings, generated styles in fixtures, private style imports,
|
|
73
|
+
spoofed Linaria functions and untraceable results acquire no permission by being
|
|
74
|
+
passed through a public function. Known className values in props/spreads receive
|
|
75
|
+
the same provenance check. Opaque spreads remain outside complete source proof.
|
|
76
|
+
An `exercises` entry records behavioral use; it never grants styling permission.
|
|
77
|
+
|
|
78
|
+
Keep helper signatures compatible with the product API. Fix ownership/provenance
|
|
79
|
+
in the runtime owner when genuinely wrong; retain intentional public APIs when
|
|
80
|
+
reporting an unsupported trace. Demo-only sizing, framing and arrangement still
|
|
81
|
+
belong in public review/layout patterns. A style helper is not permission to
|
|
82
|
+
author a second design in the scenario. For cascade risks and extraction issues,
|
|
83
|
+
read [atomic-styles.md](atomic-styles.md).
|
|
@@ -46,10 +46,18 @@ For owner drafts, token-sample reconciliation, coupled transactions and local
|
|
|
46
46
|
constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
For
|
|
49
|
+
For parent/child `covers`, public style-helper use in scenarios, or
|
|
50
|
+
`scenario-render` / `showcase-class` findings, read
|
|
51
|
+
[scenario contracts](references/scenarios.md). Traced parents can cover declared
|
|
52
|
+
public children; verified public Linaria helpers do not grant custom scenario CSS.
|
|
53
|
+
|
|
54
|
+
For atomic base/variant styling, helper/wrapper class composition, extracted
|
|
55
|
+
custom-property arithmetic, or an
|
|
56
|
+
`atomic-composition-review` / `atomic-composition-limit` advisory, read
|
|
50
57
|
[atomic style composition](references/atomic-styles.md). The same guide is served
|
|
51
58
|
as `ads://guides/atomic-styles`. It explains shorthand/logical conflicts and
|
|
52
|
-
matching-property fixes
|
|
59
|
+
matching-property fixes, plus the build resolver for preserving opaque custom
|
|
60
|
+
properties through extraction. Review warnings in the real `cx` composition and verify
|
|
53
61
|
computed styles across affected sizes/states; retain intentional product behavior.
|
|
54
62
|
|
|
55
63
|
Controllers are UI behavior owners: private helpers stay with their component or
|
|
@@ -81,6 +81,91 @@ const treatment = css`
|
|
|
81
81
|
This is a distinct state, not two competing base atoms. Whether changing padding
|
|
82
82
|
on hover is desirable remains a product decision.
|
|
83
83
|
|
|
84
|
+
## Preserve custom-property arithmetic during extraction
|
|
85
|
+
|
|
86
|
+
Custom properties can hold an unevaluated token stream that becomes arithmetic
|
|
87
|
+
only after substitution into a later `calc()`. For example:
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
--i: 2;
|
|
91
|
+
--scale: var(--i) * 0.05 + 1;
|
|
92
|
+
transform: scale(calc(-1 * var(--scale)));
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The result is scale `0.9`: substitution does not add parentheses around the
|
|
96
|
+
custom property. Some Linaria/Stylis extraction versions strip the whitespace
|
|
97
|
+
around the bare `+`, leaving `0.05+1`. The browser then treats the transform as
|
|
98
|
+
invalid. This can happen with CSS minification disabled. Source guards cannot
|
|
99
|
+
prove what a later extractor emits.
|
|
100
|
+
|
|
101
|
+
Use ADS's build-only resolver in **both** the application's Linaria configuration
|
|
102
|
+
and its separately configured MCP preview builder:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
import wyw from "@wyw-in-js/vite";
|
|
106
|
+
import { linariaTagResolver } from "@reqvire-org/ads/linaria";
|
|
107
|
+
|
|
108
|
+
// In the project's Vite plugins:
|
|
109
|
+
wyw({ tagResolver: linariaTagResolver });
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The resolver keeps the project's installed atomic processor and preserves opaque
|
|
113
|
+
custom-property values through its nesting/atomization pass. It does not add a
|
|
114
|
+
browser runtime or alter token ownership. Existing projects must adopt it in
|
|
115
|
+
each extraction pipeline; upgrading ADS alone does not rewrite custom builders.
|
|
116
|
+
If an existing `tagResolver` handles other tags, delegate atomic imports to
|
|
117
|
+
`linariaTagResolver(source, tag)` and retain the existing fallback. WyW config
|
|
118
|
+
files also support this resolver when a build integration requires serializable
|
|
119
|
+
inline options.
|
|
120
|
+
|
|
121
|
+
For a source workaround, write a self-contained formula and consume it directly:
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
--scale: calc(1 - var(--i) * 0.05);
|
|
125
|
+
transform: scale(var(--scale));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This also produces `0.9`. Simply wrapping the old value in `calc()` while keeping
|
|
129
|
+
the outer `-1 *` changes its grouping and result. Verify extracted CSS text and a
|
|
130
|
+
computed style after any rewrite, including nested `var()` fallbacks/transforms.
|
|
131
|
+
Do not globally insert spaces into already extracted CSS: that can change valid
|
|
132
|
+
signed numbers, identifiers, strings or URLs.
|
|
133
|
+
|
|
134
|
+
## Follow helpers and forwarded classes
|
|
135
|
+
|
|
136
|
+
Composition can cross a helper or a component boundary. For example, in component
|
|
137
|
+
implementation source:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
function baseClasses() { return base; }
|
|
141
|
+
function Primitive({ className, ...props }) {
|
|
142
|
+
return <button {...props} className={cx(baseClasses(), className)} />;
|
|
143
|
+
}
|
|
144
|
+
function CompactPrimitive(props) {
|
|
145
|
+
return <Primitive {...props} className={small} />;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The base and small atoms can reach the same button. Inspect both owners when
|
|
150
|
+
correcting their property vocabulary. The wrapper's class does not style every
|
|
151
|
+
descendant: a class on a child or sibling element is a separate composition.
|
|
152
|
+
An imported stylesheet alone does not show that its classes co-apply.
|
|
153
|
+
|
|
154
|
+
Spread order matters. `<button className={base} {...props} />` can replace the
|
|
155
|
+
entire className; `<button {...props} className={cx(base, className)} />` composes
|
|
156
|
+
it explicitly. Keep the product's intended forwarding behavior when fixing an
|
|
157
|
+
overlap. Do not change the public API simply to make tracing easier.
|
|
158
|
+
|
|
159
|
+
Warnings include a bounded `composition` path through traced helpers and
|
|
160
|
+
components. A `target` identifies a statically reached DOM element. This
|
|
161
|
+
establishes source composition, not that a scenario mounted it or that its
|
|
162
|
+
computed result is wrong. `writingModeDependent: true` identifies overlaps that
|
|
163
|
+
change with direction or writing mode; evaluate those against supported modes.
|
|
164
|
+
Literal overlaps with provably equal values are omitted where the analyzer can
|
|
165
|
+
expand them safely. Token formulas, inheritance and runtime values still need
|
|
166
|
+
review. A margin shorthand and `margin-inline: auto` may agree horizontally but
|
|
167
|
+
disagree in vertical text, so horizontal equality alone is insufficient.
|
|
168
|
+
|
|
84
169
|
## Respond to a warning
|
|
85
170
|
|
|
86
171
|
1. Follow the warning's related file/line locations and inspect the actual `cx`
|
|
@@ -101,6 +186,24 @@ the conflict. Prefer resolving the composition itself. If an overlap is
|
|
|
101
186
|
intentional, document why its precedence is stable and verify the resulting
|
|
102
187
|
behavior rather than changing product capabilities to silence a warning.
|
|
103
188
|
|
|
189
|
+
Use existing behavior contracts and `ads_verify` for important geometry. Register
|
|
190
|
+
a check beside its owner, link its real scenario and use the project's browser
|
|
191
|
+
profile. An assertion can inspect the actual control in the App frame:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
const frame = await openBehaviorScenario(page, "compact-spacing");
|
|
195
|
+
await expect(frame.getByRole("button", { name: "Compact" }))
|
|
196
|
+
.toHaveCSS("padding-inline-start", "8px");
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The name, check ID and expected value above are examples. Derive assertions from
|
|
200
|
+
the project's intended API and design, with relevant variants, open/hover/focus
|
|
201
|
+
states, themes and supported writing modes. Wait for observable readiness; an
|
|
202
|
+
event-stream connection can prevent `networkidle` from settling. A generic
|
|
203
|
+
overlap scan cannot decide which dimensions the product intended. A failed
|
|
204
|
+
registered geometry assertion is blocking through the existing verification
|
|
205
|
+
contract; an overlap warning remains a review signal.
|
|
206
|
+
|
|
104
207
|
## What ADS checks
|
|
105
208
|
|
|
106
209
|
`atomic-composition-review` is a nonblocking warning, returned under `advisories`
|
|
@@ -108,14 +211,26 @@ with `severity: "warning"`. CLI checks still exit successfully when there are no
|
|
|
108
211
|
errors; MCP checks and mutations preserve the warning and its guidance URI.
|
|
109
212
|
|
|
110
213
|
The source review examines atomic Linaria templates and statically traceable
|
|
111
|
-
`cx` arguments, including aliases, local imports,
|
|
112
|
-
expressions and variant-object
|
|
214
|
+
`cx` arguments, including aliases, local imports/re-exports, local helper returns,
|
|
215
|
+
arguments/defaults, nested composition, conditional expressions and variant-object
|
|
216
|
+
lookups. It follows explicit `className` forwarding through local function
|
|
217
|
+
components, props/rest spreads and imported React/Preact `memo`/`forwardRef`
|
|
218
|
+
wrappers to an intrinsic JSX element. It checks known overlaps involving box
|
|
113
219
|
spacing, inset, borders/corners, dimensions, overflow, backgrounds, typography,
|
|
114
220
|
motion and common layout shorthands, plus `all` resets. It compares shared selector/condition contexts; separate alternatives
|
|
115
|
-
within one conditional or lookup are not assumed to apply together.
|
|
221
|
+
within one conditional or lookup are not assumed to apply together. Matching
|
|
222
|
+
atomic property/selector/condition keys are replaced during traced `cx`
|
|
223
|
+
composition before comparing the surviving declarations.
|
|
116
224
|
|
|
117
225
|
This is a bounded source review, not a CSS engine or a proof of precedence. It
|
|
118
|
-
does not
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
226
|
+
does not execute helpers or import project modules, enumerate every external
|
|
227
|
+
class, or model arbitrary runtime mutation, component rendering, selectors or
|
|
228
|
+
overlapping media queries. Recursive calls, unsupported call/control-flow paths,
|
|
229
|
+
unresolved DOM targets and traversal limits produce bounded
|
|
230
|
+
`atomic-composition-limit` informational advisories where known atomic styles
|
|
231
|
+
reach an unresolved composition. Ordinary externally supplied className values
|
|
232
|
+
remain open inputs, not guaranteed safe styles. Depth, work and alternative
|
|
233
|
+
limits keep source inspection bounded; an unreported path or an empty warning
|
|
234
|
+
list does not establish visual correctness. Actual failed registered
|
|
235
|
+
browser/behavior checks remain blocking; potentially intentional source overlaps
|
|
236
|
+
remain warnings.
|