Skip to content

fix(core): correct astw: prefix order in shipped components, and the prefix advice in docs/ - #515

Merged
interacsean merged 6 commits into
mainfrom
fix/1651-1652-astw-prefix-order-and-docs
Sep 17, 2026
Merged

interacsean merged 6 commits into
mainfrom
fix/1651-1652-astw-prefix-order-and-docs

Conversation

@interacsean

@interacsean interacsean commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Two related astw: defects:

  • tailor-inc/platform-planning#1652 — misordered prefix silently drops 14 utility classes in shipped components (code fix, changes rendering)
  • tailor-inc/platform-planning#1651 — remove astw: prefix advice from the public docs (docs only, no runtime effect)

Scoped to avoid #536. That PR owns the bundled skill's styling boundary and ships it as a lint rule. This branch deliberately does not touch catalogue/**, packages/core/src/components/layout/types.ts, or the lockfile, so the two have zero file overlap — verified: the conflict set between #536 and this branch is byte-identical to the one between #536 and main alone. This branch adds none of its own.


The code fix (#1652)

Tailwind v4 only recognises a configured prefix as the first segment of a class name, so hover:astw:opacity-100 emits no CSS at all — no error, no warning. 14 such classes across four components were dead in the shipped stylesheet, and still are on main.

Verified against the built CSS by exact-string match — all 14 selectors present where none were before — then in the browser:

Component Before After (measured)
Alert description, ×5 variants same colour as title muted colour, distinct in all 5
Alert dismiss focus ring box-shadow: none 2px ring in --ringthe a11y fix
MetricCard dark-mode deltas light value persisted green L 59→79, red L 48→64
AppearanceSwitcher indicator visible display: none
CsvImporter last mapping row 1px border 0px (first row still 1px)

Regression guard: packages/core/src/astw-prefix-order.test.ts. Snapshots record class names, not resolved CSS, so they stay green on this defect. Each evasion was probed before being closed: a doubled astw:hover:astw: prefix, .css files (globals.css drives five @apply astw:* rules), and a file-count assertion loose enough to tolerate losing all of components/. It also names the boundaries it cannot cover.

The docs (#1651)

Seventeen of the 55 classes these pages recommended are absent from the shipped stylesheet. docs/ now states the same boundary #536 sets for the skill, so the two agree once it lands: ordinary utilities on your own markup and on documented layout hooks; props, variants, or composition for a component's own appearance. Each site moved to the supported form (Layout gap={6}, Button variant="destructive", …) rather than to a specificity hack.

styling-theming.md keeps the measurement as explanation of why a plain utility cannot override an AppShell default — which is what sends you to a prop — and points at #536's no-astw-prefix rule.

Also converts docs/components/ai-chat.md (landed on main with the same advice), the Vite showcase's ai-chat / layouts / primitives pages, and ActionPanel's JSDoc, which ships in the published .d.ts.

One thing #536 will need to fix on rebase

#514 has since landed on main and corrects the table-in-card rule; #536 predates it and would regress it. Measured in examples/vite-app, insets from the card's left edge:

form title text first column text
#536containerClassName="px-6" 25px 49px
#514 (main) 25px 25px ✅

Table.Head and Table.Cell already carry first:pl-6 / last:pr-6 (table.tsx:122,138), so container padding stacks. Affected lines on feat/oxlint-plugin-app-shell, all in catalogue/src/fundamental/components.md: 388, 464, 470, 479, and 488 (the DON'T example is inverted — it calls the absence of px-6 the bug). This PR's docs follow #514.

Known gap

Menu exposes no destructive variant, and its data-highlighted:text-accent-foreground outranks an unprefixed utility on specificity. The one destructive Menu.Item in the showcase uses an inline style; verified it holds the destructive colour while highlighted, where a utility class flips to accent-foreground. #536's props-or-composition line has no answer here — worth a follow-up.

Verification

  • pnpm fmt:check; turbo run lint test type-check check-dts build — 21/21 tasks
  • Browser-verified on the Vite showcase
  • No astw: left in examples/, and none in docs/ outside the deliberate "never write this" explanation
  • Merges cleanly into current main

🤖 Generated with Claude Code

@interacsean

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Code Review completed successfully!

Code review complete for PR #515: no High or Medium issues found in changed package code or exports, so no inline review comments were needed.

Generated by Code Review for #515

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Code Metrics Report

main (c60dbf7) #515 (52445f9) +/-
Coverage 87.4% 87.4% 0.0%
Test Execution Time 1m40s 2m14s +34s
Details
  |                     | main (c60dbf7) | #515 (52445f9) | +/-  |
  |---------------------|----------------|----------------|------|
  | Coverage            |          87.4% |          87.4% | 0.0% |
  |   Files             |            204 |            204 |    0 |
  |   Lines             |           6014 |           6014 |    0 |
  |   Covered           |           5257 |           5257 |    0 |
- | Test Execution Time |          1m40s |          2m14s | +34s |

Code coverage of files in pull request scope (20.0% → 20.0%)

Files Coverage +/- Status
packages/core/src/components/action-panel/ActionPanel.tsx 100.0% 0.0% modified
packages/core/src/components/alert/alert.tsx 100.0% 0.0% modified
packages/core/src/components/appearance-switcher/appearance-switcher.tsx 93.3% 0.0% modified
packages/core/src/components/csv-importer/CsvImporter.tsx 0.5% 0.0% modified
packages/core/src/components/metric-card/MetricCard.tsx 100.0% 0.0% modified

Reported by octocov

interacsean added a commit that referenced this pull request Sep 9, 2026
A hostile review of #515 found one real regression and several wrong or
self-contradictory claims. Each fix below is measured, not reasoned.

Regression (introduced by the docs commit):
- form-demo.tsx used `astw:items-stretch` on Field.Root, which sets
  `astw:items-start`. As an `astw:` class it de-duped and worked; stripped
  to plain it does not, so both survived and align-items stayed
  `flex-start`. Measured in the browser at all three sites. Now
  `items-stretch!` -> `stretch`. This is an override, and the rule this PR
  documents was applied wrongly to it.

The documented rule was incomplete:
- A binary add/override test misses values set under a state variant or on
  a descendant. `.astw\:hover\:text-accent-foreground:hover` is (0,2,0)
  against a plain `.text-destructive` at (0,1,0), so a ghost Button's
  destructive text vanishes on hover — the library works around this
  itself in ActionPanel. Added rule 4, and `text-destructive!` in sheet.md.

The cascade explanation was inverted:
- It claimed AppShell's sheet is "imported before yours" yet "appears
  later in the cascade", which cannot both hold. It is imported *after*
  the app's Tailwind output in the documented setup — that is *why* it
  wins. Also states the precondition the rule depends on, since flipping
  the import order flips which value wins.
- "Roughly half" of `astw:` classes are unshipped -> the measured 17 of 55.

A second shipped JSDoc still advertised the prefix:
- ActionPanel.tsx told consumers to "Use `astw:w-full`" and shipped in
  dist/app-shell.d.ts. Doubly wrong: ActionPanel already applies it.
  The .d.ts is now free of `astw:`.

Guard test had exploitable false negatives (each probed, then fixed):
- doubled `astw:hover:astw:x` passed `startsWith` — now checks that
  `astw:` occurs exactly once, at position 0
- `.css` was unscanned while globals.css drives five `@apply astw:*` rules
  — glob widened
- the loose `> 100` file assertion tolerated losing all of components/
- decisions/date-picker.md carried a real `hover:astw:text-foreground`
  that no glob could see; fixed, and the test now names its boundaries

Wrong or stale prose:
- description-card.md advertised `p-6` as an addition; the card already
  insets content 24px, so it double-padded
- purchase-order-demo.tsx kept a rationale this PR falsified
- design-system.md mandated `!` then called it a smell, flagging the
  catalogue's own required table-in-card pattern
- sidebar-item.md still handed consumers an internal class to paste
- layout.md swapped `space-y-4` for `gap-6!`, changing the example's
  meaning; `space-y-4` is a legitimate addition

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@interacsean interacsean changed the title fix(core): correct astw: prefix order in shipped components, and the prefix advice in docs fix(core): correct astw: prefix order in shipped components, and extend #536's styling boundary to docs/ Sep 16, 2026
@interacsean
interacsean force-pushed the fix/1651-1652-astw-prefix-order-and-docs branch from 12342d1 to e85ced0 Compare September 16, 2026 01:47
@interacsean interacsean changed the title fix(core): correct astw: prefix order in shipped components, and extend #536's styling boundary to docs/ fix(core): correct astw: prefix order in shipped components, and the prefix advice in docs/ Sep 16, 2026
@interacsean
interacsean marked this pull request as ready for review September 16, 2026 02:30
@interacsean
interacsean requested a review from a team as a code owner September 16, 2026 02:30
interacsean and others added 3 commits September 16, 2026 17:27
…#1652)

Tailwind v4 only recognises a configured prefix as the first segment of a
class name, so `hover:astw:opacity-100` and friends emitted no CSS at all —
no error, no warning. 14 such classes across Alert, MetricCard,
AppearanceSwitcher and CsvImporter were dead in the shipped stylesheet.

Verified against the built CSS: all 14 selectors are present by exact-string
match where none were before, and confirmed in the browser — Alert's per-
variant muted description text and its dismiss focus ring (previously no
focus indicator at all), MetricCard's dark-mode deltas, the appearance
switcher's radio indicator, and CsvImporter's last-row border.

Adds src/astw-prefix-order.test.ts as a guard — snapshots record class
names rather than resolved CSS, so they stay green on this defect. It
checks that `astw:` occurs exactly once and at position 0, scans .css too
(globals.css drives five `@apply astw:*` rules), and names the boundaries
it cannot cover. Each evasion was probed before being closed.

Also fixes the one misordered class outside packages/core, in
decisions/date-picker.md, and records the ordering rule in the internal
component-authoring reference.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`astw:` is the library's internal Tailwind prefix. A consumer's Tailwind
build has no `astw` prefix configured, so it never generates `astw:*`
classes — an `astw:` class in app code only resolves if app-shell happens
to already ship that exact utility. 17 of 55 classes sampled from these
pages are absent from the shipped stylesheet, and Tailwind emits nothing
for an unknown utility: no error, no warning.

Adopts the boundary #536 sets for the bundled skill, so the two agree once
it lands: ordinary utilities on your own markup and on documented layout
hooks; props, variants, or composition for a component's own appearance.
Each site moved to the supported form — `Layout gap={6}`, `Button
variant="destructive"`, and so on — rather than to a specificity hack.

styling-theming.md keeps the measurement as explanation rather than
advice: it says why a plain utility cannot override an AppShell default,
which is what sends you to a prop, and points at #536's no-astw-prefix
lint rule.

Deliberately leaves catalogue/ and Layout's `gap` JSDoc to #536 so the two
branches do not overlap.

Table-in-card guidance follows #514: the cells carry their own
first:pl-6 / last:pr-6, so the container adds no horizontal padding.
Measured in examples/vite-app — with `containerClassName="px-6"` the first
column sits at 49px against a card title at 25px; without it, both at 25px.

Also converts the Vite showcase's ai-chat, layouts and primitives pages
(layouts also carried two misordered `dark:astw:opacity-20`), and
ActionPanel's JSDoc, which ships in the published .d.ts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#536 landed reconciled against #514, and its merged form differs from the
branch this docs pass was written against: it keeps `Card.Content
className="px-0!"` for the one table-in-card case rather than restructuring
around it. Docs that never mention `!` would contradict the shipped skill.

Props, variants and composition stay the primary guidance, matching §5.
`!` is now documented as the last resort it is in the catalogue, using the
catalogue's own example, and with the note that the table container still
adds no horizontal padding — the cells carry first:pl-6 / last:pr-6, so
`containerClassName="px-6"` there would push the first column 24px past the
card title.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
IzumiSy and others added 3 commits September 17, 2026 10:52
Review feedback: this belongs in linting, not in the test suite. Removed
rather than left in place, so the gap is momentary instead of becoming
tech debt.

Backlogged as an oxlint rule. It is not a one-line config change: #536's
no-astw-prefix is the consumer-facing rule and would flag every legitimate
use inside packages/core, that plugin is not wired into this repo's own
lint, and oxlint 1.64 ships no generic restricted-syntax rule that reaches
inside class strings. The board item also notes #1700 may remove the
prefix entirely and make the rule moot.

The 14 fixes in this PR stand on their own; nothing else in the suite
covers the defect, since snapshots record class names rather than
resolved CSS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@interacsean
interacsean merged commit 7748583 into main Sep 17, 2026
8 checks passed
@interacsean
interacsean deleted the fix/1651-1652-astw-prefix-order-and-docs branch September 17, 2026 02:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants