You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(sdui): no Tailwind in page source — guardrail + corrected guidance (ADR-0065) (#2495)
A kind:'html'/'react' page's `source` is runtime metadata, so the console's
build-time Tailwind never scans it — authored utility classNames silently produce
no CSS (the ADR-0065 failure that the Task Desk modal hit). The page fixes landed
in the showcase; this closes the loop so AI authors don't repeat the mistake.
Prevention (the guardrail):
- New `@objectstack/lint` rule `validatePageSourceStyling` flags `className`
attributes in source-tier pages, wired into `os validate` as step 3e — a
warning with the actionable fix (react: inline style + hsl(var(--token)) +
ObjectForm formType overlays; html: structured props + a JSON style object).
Verified: os validate now flags all 7 showcase source pages; 6 unit tests.
Corrected guidance (so the contract/docs/skill no longer teach the mistake):
- react-blocks contract generator note → "do NOT use Tailwind className; style
with inline style + hsl(var(--token)); overlays via ObjectForm formType"
(regenerated contract.json + react-blocks.md).
- objectstack-ui SKILL.md: html + react examples and prose rewritten off
Tailwind; the ADR-0065 styling section gains a source-tier subsection.
- layout-dsl.mdx: html tier row no longer says "HTML + Tailwind".
- ADR-0080 / ADR-0081: amendment notes pointing to ADR-0065 — the tiers stand,
only the styling primitive changes; never author Tailwind in page source.
Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add the source-page styling guardrail (ADR-0065): `os validate`/`os build` now flags Tailwind `className` in `kind:'html'`/`kind:'react'` page source, which silently produces no CSS because the build never scans authored metadata. New `validatePageSourceStyling` rule with an actionable inline-style/`hsl(var(--token))` fix; also corrects the react-blocks contract, the objectstack-ui skill, the layout-dsl docs, and ADR-0080/0081 away from the "HTML + Tailwind" framing.
Copy file name to clipboardExpand all lines: docs/adr/0080-ai-authored-ui-jsx-source.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,8 @@
11
11
12
12
---
13
13
14
+
> **Amendment (2026-06-30 — ADR-0065 styling correction).** The "HTML + **Tailwind**" framing for page `source` is **superseded on styling**. A page's `source` is *runtime metadata*, so the console's build-time Tailwind never scans it — authored utility `className`s silently produce no CSS (the exact failure ADR-0065 was written to prevent; the Task Desk modal's `bg-black/50` backdrop rendered transparent). The tiers themselves stand; only the styling **primitive** changes: `kind:'html'` styles via the registered components' structured props + a JSON `style` object; `kind:'react'` styles via inline `style={{}}` with `hsl(var(--token))` theme colors and renders drawer/modal overlays through `<ObjectForm formType="drawer"|"modal">` (never a hand-rolled `fixed inset-0`). **Do not author Tailwind classes in page source.** See [ADR-0065](./0065-sdui-styling-model.md).
15
+
14
16
## TL;DR
15
17
16
18
1.**[model] AI authors a constrained *JSX text*, not a JSON tree.** The JSON `SchemaNode` tree is a fine *compile target* and a terrible *edit surface* — verbose, out-of-distribution for the model, noisy to diff, and it loses manual tweaks on every regeneration. AI reads/edits JSX+Tailwind (its strength); the tree is **derived**, never hand-edited. (This is how v0/Markdoc/MDX keep source = code, output = compiled.)
Copy file name to clipboardExpand all lines: docs/adr/0081-trusted-react-page-tier.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,8 @@
11
11
12
12
---
13
13
14
+
> **Amendment (2026-06-30 — ADR-0065 styling correction).** The "HTML + **Tailwind**" framing for page `source` is **superseded on styling**. A page's `source` is *runtime metadata*, so the console's build-time Tailwind never scans it — authored utility `className`s silently produce no CSS (the exact failure ADR-0065 was written to prevent; the Task Desk modal's `bg-black/50` backdrop rendered transparent). The tiers themselves stand; only the styling **primitive** changes: `kind:'html'` styles via the registered components' structured props + a JSON `style` object; `kind:'react'` styles via inline `style={{}}` with `hsl(var(--token))` theme colors and renders drawer/modal overlays through `<ObjectForm formType="drawer"|"modal">` (never a hand-rolled `fixed inset-0`). **Do not author Tailwind classes in page source.** See [ADR-0065](./0065-sdui-styling-model.md).
15
+
14
16
## TL;DR
15
17
16
18
1.**[rename]`kind:'jsx'` → `kind:'html'`.** The constrained, parse-never-execute tier (ADR-0080) is renamed to match what it is: author-written **HTML + Tailwind** (expressed as constrained JSX) compiled to the SDUI tree. `'jsx'` stays as a **deprecated alias** (already-saved pages keep loading); all authored examples/docs move to `'html'`.
message: `${count} \`className\` attribute${count>1 ? 's' : ''} in ${String(kind)}-source page — Tailwind utilities in page source silently produce no CSS (the build never scans authored metadata; ADR-0065).`,
58
+
hint:
59
+
kind==='react'
60
+
? "Style with inline style={{}} using hsl(var(--token)) theme colors (e.g. color:'hsl(var(--foreground))', background:'hsl(var(--card))'); render drawer/modal via <ObjectForm formType=\"drawer\"|\"modal\"> instead of hand-rolled overlays."
61
+
: "Lay out with the components' structured props (<flex direction gap>, <grid columns>) and add CSS via a JSON style object style={{\"color\":\"hsl(var(--foreground))\"}}; do not use Tailwind className.",
Copy file name to clipboardExpand all lines: packages/spec/scripts/build-react-blocks-contract.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -97,7 +97,7 @@ const contract = {
97
97
version: 2,
98
98
adr: 'ADR-0081',
99
99
source: 'GENERATED from packages/spec/src/ui/react-blocks.ts — data props from the spec zod schemas, binding/controlled/callback from the React overlay.',
100
-
note: "Props each component accepts in kind:'react' page source. Reference blocks by their PascalCase tag. kind: data=declarative config (from the spec schema) · binding=connects to data · controlled=React state · callback=React function. Layout = plain HTML+Tailwind; these blocks are for DATA. Live data: const adapter = useAdapter(); adapter.find/findOne/create/update.",
100
+
note: "Props each component accepts in kind:'react' page source. Reference blocks by their PascalCase tag. kind: data=declarative config (from the spec schema) · binding=connects to data · controlled=React state · callback=React function. These blocks are for DATA. Live data: const adapter = useAdapter(); adapter.find/findOne/create/update. STYLING (ADR-0065) — a page's source is runtime metadata, so the console's build-time Tailwind NEVER scans it: utility classNAMES silently produce no CSS. Do NOT use Tailwind className in page source. (a) Layout/chrome: inline style={} with hsl(var(--token)) theme colors — e.g. color:'hsl(var(--foreground))', background:'hsl(var(--card))', border:'1px solid hsl(var(--border))', and px/flex for layout. (b) Overlays: render <ObjectForm formType='drawer'|'modal' open onOpenChange> (a pre-styled Sheet/Dialog) — never hand-roll a fixed inset-0 backdrop.",
0 commit comments