diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36acf26b..64e671c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,52 +43,22 @@ jobs: # the next corrective run rebuild the observability stack from zero. cache-on-failure: true - # Track the current ps-qa release so UI coverage cannot silently remain - # on an obsolete harness — but build each tool against the dependency set - # it was published with. + # The host is the browser, and the browser is not a crate a site can + # install: `chuzz-headless` is built from source by the composite action + # below, which also installs the driver. That replaced `qa-inspect-host`, + # which was a second headless browser with the web platform in only the + # other one -- no `URLSearchParams`, no `matchMedia`, no storage, no + # observers -- so every gap closed for the browser had to be closed a + # second time there by hand, or the sweep measured a browser nobody + # ships. # - # Resolving these fresh picks the newest `ps-blitz-*` that satisfies the - # requirements, and those crates are not compatible across minor versions: - # `qa-inspect-host` ended up compiled against `ps-blitz-script` 0.4.1 - # while its `tauri-runtime-blitz` expected 0.3.x, so the same type - # existed twice and every call between them failed to typecheck. That - # broke this step, and because everything below depends on it, Lint, Type - # Check, Build and the whole test suite were skipped rather than failed — - # a green-looking pipeline that had verified nothing. - # - # `--locked` is gone, and it did cost currency: a lockfile is frozen at - # publication, so the host was pinned to whatever engine existed the day - # it was released. A renderer fix could then never reach this gate without - # a host release, and that is not hypothetical -- `ps-blitz` 0.4.3 fixes - # three defects in checkbox activation, and the host published before it - # kept installing 0.4.2, so `switch`, `checkbox` and `radio` went on - # failing here against an engine that had already been fixed. - # - # The mismatch `--locked` was added for is gone with it: it happened when - # `qa-inspect-host` wanted `ps-blitz` 0.4.x while its `tauri-runtime-blitz` - # wanted 0.3.x, so the same type existed twice. Both now require `^0.4` - # and resolve to one copy. - # - # One command per tool, deliberately. `cargo install a b --locked` - # resolves the two together and downgrades to satisfy both — it picks - # `qa-inspect-host` 0.1.4 instead of 0.1.8. Installed separately they - # each get their own resolution and stay current. - - name: Install native QA tools - env: - # cargo install otherwise builds in disposable temporary directories, - # which leaves rust-cache nothing useful to restore on the next run. - CARGO_TARGET_DIR: target/qa-tools - run: | - # Pinned to a floor, not left to "latest". - # - # The sweep needs `--headless` and a host that can replace a text - # field without a font catalogue. Installing whatever is newest meant - # a version without those failed as `unexpected argument`, which - # reads as a broken workflow rather than a tool that is too old -- - # and a host that silently appended to a field would not have failed - # at all until a check disagreed about a value. - cargo install ps-qa --version "^0.6.2" - cargo install qa-inspect-host --version "^0.1.12" + # 0.1.12 is still on crates.io, so nothing broke when the crate was + # deleted from source; this is the last consumer to move off it. + - name: Headless browser host + id: qa + uses: pathscale/chuzz/.github/actions/headless-host@master + with: + token: ${{ secrets.SIBLING_REPOS_TOKEN }} - name: Install Dependencies run: bun install @@ -110,23 +80,20 @@ jobs: run: bun run build # Native renderer outcomes replace jsdom interaction tests as the release - # gate. One headless host per component keeps the complete sweep below two - # minutes while preserving a clean document between components. + # gate. One headless host per component preserves a clean document. - name: Rendered component outcomes env: # A shared runner takes longer than a local machine for outcomes that pass # locally well inside the budget. This widens scheduling deadlines only; every # declared rendered-state transition is still required. QA_TIMEOUT_SCALE: 2 - # This runner has no font catalogue, and it should not have one: a - # component library that needs a GUI stack installed to be tested is - # a component library nobody can test. With no fonts every glyph - # shapes to nothing, so anything sized by its text lays out flat and - # every paint assertion fails for a reason that says nothing about - # the component. The headless profile asks the same questions about - # layout instead; the full profile keeps the visual half and runs - # where there are fonts. See `PROFILES` in generate-checks.ts. - QA_PROFILE: headless + # The shared host action installs fonts without a desktop stack. + # Keep actual paint assertions in the release gate. + QA_PROFILE: full + # The harness looks for `chuzz-headless` on PATH; here it is the + # build the composite action above just made, so it is named + # outright. + QA_HOST: ${{ steps.qa.outputs.host }} run: | bun run qa:checks bun run qa:entries diff --git a/README.md b/README.md index 614ffcbd..caaeac0f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ yarn add @pathscale/ui solid-layouts && yarn add -D rsbuild-plugin-solid-layouts ## Setup -`@pathscale/ui` 2.x is a compiled Layout bundle. Configure the application compiler before +`@pathscale/ui` 3.x is a compiled Layout bundle. Configure the application compiler before Solid transforms JSX: ```ts @@ -58,11 +58,22 @@ import "@pathscale/ui/index.css"; export const Example = () => ( - + ); ``` +In the application's Tailwind v4 stylesheet, register the package as a source +(adjust the relative path from that stylesheet): + +```css +@source "../node_modules/@pathscale/ui"; +``` + +This includes the published `dist/responsive-classes.txt` scanner input. Grid and +Flex compose responsive classes at runtime, so scanning only the compiled JavaScript +misses utilities such as `md:grid-cols-3`. + ## Theming Two themes ship with the library: `light` (the default when no attribute is set) and `dark`. diff --git a/docs/api-contract.md b/docs/api-contract.md index 0353b649..122a26c3 100644 --- a/docs/api-contract.md +++ b/docs/api-contract.md @@ -11,7 +11,7 @@ invisible for a day behind a doc that looked correct the whole time. When an API change is intentional, run `bun run check:api -- --write`, read the diff, and commit it. The diff is the review. -186 components. An empty list means the component adds nothing beyond +187 components. An empty list means the component adds nothing beyond HTML attributes and `UIBaseProps`; that is an assertion, not a gap. --- @@ -226,6 +226,7 @@ _No props beyond HTML attributes and `UIBaseProps`._ ### Calendar ```ts +dateNames?: DateNames defaultValue?: Date disabled?: boolean isDateUnavailable?: (date: Date) => boolean @@ -253,11 +254,14 @@ elevation?: CardElevation flavor?: Flavor footer?: JSX.Element header?: JSX.Element +href?: string isInteractive?: boolean material?: Material padding?: Space radius?: Radius +rel?: string state?: CardState +target?: JSX.AnchorHTMLAttributes["target"] variant?: Variant ``` @@ -287,11 +291,14 @@ elevation?: CardElevation flavor?: Flavor footer?: JSX.Element header?: JSX.Element +href?: string isInteractive?: boolean material?: Material padding?: Space radius?: Radius +rel?: string state?: CardState +target?: JSX.AnchorHTMLAttributes["target"] variant?: Variant ``` @@ -446,8 +453,12 @@ wheelClass?: string ```ts class?: string +color?: ColorValue | string +defaultColor?: ColorValue | string +disabled?: boolean id?: string mode?: ColorWheelFlowerMode +onChange?: (color: ColorValue) => void palette?: readonly string[] ``` @@ -645,6 +656,7 @@ _No props beyond HTML attributes and `UIBaseProps`._ ### DatePicker ```ts +dateNames?: DateNames defaultOpen?: boolean defaultValue?: Date disabled?: boolean @@ -665,6 +677,7 @@ weekdayFormat?: CalendarWeekdayFormat ### DateRangePicker ```ts +dateNames?: DateNames defaultOpen?: boolean defaultValue?: DateRangeValue disabled?: boolean @@ -890,6 +903,7 @@ id?: string ### DrawerTrigger ```ts +as?: ValidComponent children: JSX.Element ``` @@ -912,6 +926,12 @@ placement?: DropdownPlacement children: JSX.Element ``` +### FieldErrorMessage + +```ts +message?: string +``` + ### FieldGroup ```ts @@ -936,6 +956,7 @@ _No props beyond HTML attributes and `UIBaseProps`._ ```ts extensionUrl?: string +icon?: string | JSX.Element onDismiss?: () => void onInstall?: () => void storageKey?: string @@ -1051,6 +1072,7 @@ i18n: I18nStore ```ts flavor?: Flavor height?: number +label?: string src?: string | JSX.Element width?: number ``` @@ -1537,6 +1559,7 @@ variant?: RadioGroupVariant ### RangeCalendar ```ts +dateNames?: DateNames defaultValue?: RangeCalendarValue disabled?: boolean isDateUnavailable?: (date: Date) => boolean @@ -1729,6 +1752,7 @@ variant?: TabsVariant ### Text ```ts +as?: TextAs children?: JSX.Element family?: TextFamily leading?: TextLeading diff --git a/docs/release-readiness-2026-09-12.md b/docs/release-readiness-2026-09-12.md new file mode 100644 index 00000000..8beb0e32 --- /dev/null +++ b/docs/release-readiness-2026-09-12.md @@ -0,0 +1,199 @@ +# UI release review — 12 September 2026 + +Status: **UI 3.2.0 package release approved and locally verified**. Website +deployments remain separately reviewed. Before creating a Fly dev instance, +contact the owner so they can be online for questions. + +## Concrete TODO + +The release script computes **3.2.0** from npm's 3.1.0 baseline and the branch's +conventional commits. Local release verification is complete; the repository's +release workflow assigns and publishes the version after the fast-forward. + +- [x] Build the candidate ps-qa and font-enabled host; verify post-action paint, + explicit gridcell targets, keyboard shortcuts, and transformed pointer actions. +- [x] Verify ps-blitz CI with the pinned coordinated stack. Run 34632611122 + passes at 97ca22ca, including the WebSocket listener and contrast-settling fixes. +- [x] Build and pack fresh UI source with solid-layouts 0.2.4; run the full native + component sweep, API/package gates, and consumer builds against the packed candidate. +- [x] Resolve Honey's cold first-submit failure and run the expanded 196-check suite, + including Platform Admin, App Admin, Guest, and password change/restore. +- [x] Finish reproducible recovery-code coverage. TOTP confirmation and Telegram + enrollment/login remain separate security gates for Honey. +- [x] Verify Worktables editing, cancellation, undo, findings, and zoom (112/112). +- [x] Confirm the packed Calendar fix in js.software (316/316). +- [x] Resolve Web3 carousel settling. Its complete final run passes 103/103 after + the landing surface was scoped away from the independently animated chat halo. +- [x] Repeat scoped site E2E against the final package, recording missing backend + contracts separately from library regressions. +- [x] Push the verified library, driver, runtime, and host changes to their + existing release branches and reconcile their descriptions with local evidence. +- [x] Release ps-blitz 0.4.8, blitz-control-protocol 0.5.0, ps-qa 0.7.1, + tauri-runtime-blitz 0.4.0, and the Chuzz 0.1.37 host in dependency order. +- [ ] Release UI 3.2.0 and verify a fresh consumer install from npm. +- [ ] Deploy only the separately reviewed website changes. + +## Release sequence + +1. [ps-blitz #95](https://github.com/pathscale/ps-blitz/pull/95): 0.4.8 published. +2. [ps-observability #21](https://github.com/pathscale/ps-observability/pull/21): + blitz-control-protocol 0.5.0 and ps-qa 0.7.1 published against that engine. +3. [tauri-runtime-blitz #57](https://github.com/pathscale/tauri-runtime-blitz/pull/57): + 0.4.0 published against the shared protocol. +4. [chuzz #45](https://github.com/pathscale/chuzz/pull/45) and + [#46](https://github.com/pathscale/chuzz/pull/46): shared document actions, + headless build gating, and the signed 0.1.37 host published. +5. [UI #289](https://github.com/pathscale/UI/pull/289): publish 3.2.0 after the + packaged library and its consumers passed against the released host and driver. +6. Review and deploy approved website PRs using the published library. + +The older handover put tauri-runtime-blitz before ps-observability. Its manifest +requires protocol 0.5, so that order cannot resolve. Registry dependency failures +before the upstream publications are expected and are separate from regressions. + +`solid-layouts` 0.2.4 has already published through +[PR #19](https://github.com/pathscale/solid-layouts/pull/19). It forwards caller +styles to the root slot; without it, UI Card positions and dimensions are dropped. +UI and consumer manifest floors and resolvable lockfiles are updated. UI has the +published 0.2.4 installed; final clean consumer installs remain part of this gate. +Worktables' clean install still awaits the separate house DSL SDK 0.1.2 release. + +## Library and harness findings + +| Finding | Current evidence | Remaining verification | +| --- | --- | --- | +| Native option labels and selected state disappeared in the control refactor | Six regression tests restored; the native Select fixture passes all 7 outcomes with published ps-blitz 0.4.8, protocol 0.5.0, ps-qa 0.7.1, and Chuzz 0.1.37 | Complete | +| Transformed client rectangles disagreed with painted controls | Four geometry tests, inline fragment tests, and full Linux suite pass; final pointer fixture and Worktables zoom/drag checks pass | CI host boundary and published integration | +| CI needs the coordinated unpublished runtime stack | Exact dependency revisions pinned; nested workspace exclusions resolved Cargo inheritance. Coordinated CI run 34632611122 passes with refreshed socket/contrast revisions | Verify registry integration after approval | +| Calendar cells captured selection state once | Cell state is now reactive; six native checks verify selection changes and controlled callbacks in both directions. Packed js.software run passes 316/316 | Owner review | +| Honey CreateApp stalled during socket connection | chuzz iterated a live listener array; the first RPC removed its open listener and skipped the next. Snapshot dispatch fixes three consecutive 15-step lifecycles and the full 192-check suite; refreshed coordinated CI passes | GUI build review and registry integration | +| Responsive layout classes were purged from consumers | Purge manifest includes responsive Grid/Flex classes; 24x landing checks passed | Full consumer rebuilds from the final package | +| Form submission discarded schema output | Typed schema output preserved; six native form checks and Honey's expanded suite pass | Owner review | +| ConnectionSettings missing from layout manifest | Export added; isolated package consumer build passed | Pays runtime checks | +| A fontless host weakens paint checks | The font-enabled release host builds; a fresh local sweep against the public dependency stack passes 273 checks across 75 component fixtures | Complete | +| ps-qa measured paint before input, then sampled contrast transitions too early | Reads moved after input; contrast honors the outcome and stability windows. Six native driver scenarios pass, including delayed repair and persistent contrast failure. Web3 theme group passes 18/18 | Final stack sweeps | +| ps-qa rejected explicit gridcell targets | Explicit role selectors now bypass the generic inventory role list while retaining actionability checks; native Calendar and packed js.software checks pass | Owner review | +| ps-qa's older drag diagnostic only scrolls containers | Actual pointer dragging/cancellation added to CLI and declarative checks; native fixture and Worktables movement/cancellation/undo checks pass | PR and CI review | +| UI sweep could accept bundles older than the library source | Staleness guard now includes library source and package output; confirmed it rejects the current outdated bundle before launching a host | Fresh full build and sweep | + +Use `/Users/revenge/code/ps-observability/target/debug/ps-qa` (0.7.1) and +`/Users/revenge/code/chuzz/target/release/chuzz-headless` for local candidate runs. +The installed `~/.cargo/bin/ps-qa` was 0.6.3 and is not the release candidate. +`qa-hosted --checks` takes a directory. Build before running: stale built files do +not verify source changes. Keep pointer, keyboard, scroll, paint, and persistence +failures visible; do not replace them with presence checks to obtain a pass. + +## Website scope + +All local checkouts are under `/Users/revenge/code`; remotes are `pathscale/`. +Counts below are earlier observed runs, not a final release verdict. They do not +prove that every product feature is covered, and must be repeated against the +final package and runtime. Check definitions have changed since some runs. + +| Repository / existing PR | Observed result or blocker | +| --- | --- | +| [honey.id #332](https://github.com/pathscale/honey.id/pull/332) | Expanded suite passes 193/196. The three failures truthfully identify the backend's empty API-key regeneration response; the dedicated error flow passes 18/18. Recovery-code generation, two login/rotation cycles, old-code rejection, save gates, unchanged-password login, and sign-out pass in a reusable 33/33 runner. TOTP and Telegram remain unverified. | +| [worktables.dev #9](https://github.com/pathscale/worktables.dev/pull/9) | UI/SVG editor replaces Cytoscape/ELK. Final packed-package run passes 112/112, including 33 designer and 7 findings checks. Real pointer movement, cancellation, undo, zoom, and emitted schema edits pass. Visual inspection confirms the UI cards, native SVG relationships, toolbar, and inspector. Clean install still awaits house DSL SDK 0.1.2. | +| [crates.vip #1](https://github.com/pathscale/crates.vip/pull/1) | Final packed-package run passes 71/71; authentication is deliberately bypassed in both client and backend, so this is not evidence of authenticated role coverage. | +| [24x.ai #11](https://github.com/pathscale/24x.ai/pull/11) | Final packed-package runs pass 141/141 at desktop width and 20/20 at phone width. The dev login still authenticates under Honey's dev application because 24x has no usable dev registration of its own. | +| [js.software #53](https://github.com/pathscale/js.software/pull/53) | Packed Calendar fix passes 316/316. CI includes all declared groups. | +| [kard.vip #8](https://github.com/pathscale/kard.vip/pull/8) | Final packed-package run passes 223/223; demo actions do not prove payment functionality. | +| [nofilter.io #340](https://github.com/pathscale/nofilter.io/pull/340) | Final packed-package run passes 129/129. | +| [pathscale.com #17](https://github.com/pathscale/pathscale.com/pull/17) | The public/UI flow and carousel pass against the final package. The dev run passes 105/124: the known username and wrong-password refusal work, but the correct-password callback does not create a protected session, stranding 19 dependent portal/settings checks. Owner confirmed the old portal has little value and need not block UI. Configured `pathscale-be` no longer exists; do not recreate without a reviewed deployment plan. | +| [pays.online #166](https://github.com/pathscale/pays.online/pull/166) | Packed-package typecheck and build pass after adopting shared ConnectionSettings. There is no ps-qa profile. Wallet settings call methods absent from the backend schema; onboarding contains unfinished handlers. The configured Honey app id is a UUID rather than the required 16-character public id. Real payment actions require a defined dev setup and review. | +| [promptsyntax.org #18](https://github.com/pathscale/promptsyntax.org/pull/18) | Final packed-package run passes 129/129. | +| [support.cafe #12](https://github.com/pathscale/support.cafe/pull/12) | Final packed-package run passes 104/104. | +| [web3.trading #18](https://github.com/pathscale/web3.trading/pull/18) | Final packed-package run passes 103/103. Theme contrast settles correctly, the last carousel slide remains stable for 500ms, and the guest chat closes. | +| [ui-starter-app #13](https://github.com/pathscale/ui-starter-app/pull/13) | Final packed-package run passes 144/144. | +| [agencyzero #211](https://github.com/pathscale/agencyzero/pull/211), [#212](https://github.com/pathscale/agencyzero/pull/212) | UI and control integration in scope; core-specific features are handed to a dedicated owner after UI is ready. | + +Honey verification must cover **Platform Admin, App Admin, and Guest** with real +allowed and denied behavior. Application creation, saved edits, deletion, logout, +session recovery, and relevant security settings need outcomes, not just screen +presence. Recovery verification is complete; TOTP and Telegram remain incomplete. Only uniquely named +disposable QA applications may be changed or deleted by the lifecycle checks. + +Honey's backend production approval has a separate security review item. The +July 27 audit's app-token trust concern still matches the inspected auth backend +at `60d37dc`: `src/services/auth/app_token.rs` checks that a caller-selected +source app exists and accepts its callback's user identity, without an explicit +source-to-target trust check. This is a source finding, not a live exploit test. +Do not treat passing recovery or UI checks as closing that backend boundary. + +Do not count the current Honey or js.software `biome` scripts as validation: +their manifests install the unrelated `biome` 0.3.3 package rather than +`@biomejs/biome`. Its CLI can return success without checking files. Their +TypeScript, build, and native E2E results above are separate evidence. Repairing +the formatter dependency and stale configuration remains tooling cleanup. + +The previous UI run stopped at Chuzz's stale GUI build path. Chuzz #45 and #46 +fixed that release path; the published 0.1.37 host then passed the complete local +273-check component sweep. The checks were kept at the full font-enabled profile. + +Honey's creation handler now awaits its mutation, so submitting covers the backend +request. Tracing showed validation completed but CreateApp was never sent when +GetApps and CreateApp queued during WebSocket connection. The first open listener +removed itself and chuzz skipped the next listener. The host now snapshots +listeners before dispatch; three clean lifecycles and the full suite pass. + +Pathscale restoration, if approved, should mirror crates.vip's low-cost deployment: +shared IPv4, shared CPU, one small machine. The crates backend's `fly.toml` and +`docs/deploy.md` are the reference. Do not assume the former Pathscale placeholder +callback or diagnostic dashboard is a production feature specification. + +`consulting.parcle.ai` is unmaintained, intentionally absent locally, and excluded +from this release gate. + +## Local changes and branches + +Preserve unrelated changes and append work to existing PRs. Do not recreate the +deleted scratchpad checkout or delete branches while auditing them. + +The scoped branch comparison found most apparently orphaned engine/control commits +already present as equivalent patches. Pays' remaining local work was inspected: + +- `feat/engine-console`: `6ed3396` adds a separate enforcement-engine contract, + connection, status and payment pages. Its default backend is localhost and it + has no verified deployment. The request-id control updates a signal after the + form has captured its defaults, so new-id and post-send rotation need behavioral + verification and correction before use. Preserve this feature branch; it is not + a missing UI migration fix and is not approved payment functionality. +- The following `aa6cccd` removes old theme/table dependencies; the current release + branch already contains the corresponding migration, so do not replay it blindly. +- `wip/local-save-20260815`: `c0560c1` and `5309222` contain signing design documents + and their correction. Preserve these for payment/backend review; they do not + change the shipped frontend. No remote branch contains `6ed3396` or `5309222`. + +No local branch was deleted. Any later integration belongs in the existing Pays +PR and must retain its backend and payment review requirements. +Worktables' four previously local commits and the verified editor replacement are +now pushed to its existing PR. They remain subject to owner review. + +Two ps-blitz patch-identity exceptions were inspected: `fix/engine-gaps`' response +metadata fetch is present in the release branch with later configurable user-agent +changes, and `release/engine-fixes`' remaining unique commit only bumps the old +version to 0.3.7. Neither needs replaying onto 0.4.8. + +The original `solid-layouts` checkout contains other local work; the published root +style fix was made in `/Users/revenge/code/solid-layouts-ui-release` to preserve it. + +## Final local release candidate + +The font-enabled host, ps-qa 0.7.1 driver, packed UI package, Honey, and Worktables +build successfully. UI's final native sweep passes 273 checks across 75 fixtures; +API/package checks pass across 187 components and 1,002 files. The full chuzz GUI +release build, workspace tests, and clippy pass. Worktables passes 112/112, JS +Software 316/316, Web3 103/103, 24x 141/141 plus 20/20 mobile, crates 71/71, +kard 223/223, nofilter 129/129, Prompt Syntax 129/129, support.cafe 104/104, +and the starter 144/144. + +Honey's complete run is 193/196 because the backend returns no regenerated API +key; the truthful error path passes 18/18 and the reusable recovery runner passes +33/33. Pathscale's public/UI flow passes, while its dev application callback does +not establish the protected session. Pays builds but has no native QA profile and +has explicit backend contract gaps. These site-specific boundaries do not indicate +a UI package regression. + +No deployment sign-off is implied. The owner must review the PRs, then the five +dependency releases must publish in order before registry CI and approved website +deployments can complete. diff --git a/docs/ui-usage.md b/docs/ui-usage.md index bae4b7f4..eda9d4c3 100644 --- a/docs/ui-usage.md +++ b/docs/ui-usage.md @@ -69,6 +69,8 @@ Nested glass is flattened to one pane on purpose, and both `prefers-reduced-transparency` and a browser without `backdrop-filter` fall back to a more opaque fill. +Components require `solid-layouts >=0.2.4` so caller styles reach their root elements reactively. + ## Component conventions (consumer-facing) - Booleans are HeroUI-style `is*` where they exist: `isDisabled`, `isInvalid`, @@ -125,14 +127,41 @@ to a more opaque fill. common: every accepted value did nothing, every implemented value was a type error. Now `CardState`, which is what it renders. A card is not a form control; interactivity is `isInteractive`. +- **Behaviour change — an interactive `Card` is a button only when it has + something to press.** `isInteractive` gave every card `role="button"` and + `tabindex="0"`, so the ordinary way to make a whole card navigate, + ``, produced a `button` inside a `link` + with the same name and the same box: invalid HTML, announced twice, and a + press by coordinate landing on whichever was on top. Measured on one site as + every card on it. `isInteractive` now means what it looks like it means, the + hover and press affordance; the button role and the keyboard activation + arrive with an `onClick`. + + So a card inside a link is fixed where it stands, with nothing to edit. A + card that carried a handler is unchanged. What does change is a card whose + click was handled by an ancestor rather than by the card: it keeps the + affordance and loses the role and the tab stop, so move the handler onto the + `Card`. + + Better still, if the card navigates, give it the `href` and drop the wrapper: + + ```tsx + + ``` + + `Card` then renders a real anchor, so middle-click, open-in-new-tab and copy + link address work, and there is nothing left to nest it inside. `role` is + also honoured properly now: `role="presentation"` no longer leaves + `tabindex="0"` behind. - `Slider.onChange` reports continuous values. Optional `Slider.onChangeEnd` reports the final changed value once on pointer release, pointer cancellation, keyboard release, or blur fallback. Its visible `label` is also copied to the semantic slider's `aria-label`, because not every renderer resolves `aria-labelledby` across a visually hidden label. - `Collapsible.Content` retains closed content by default. Set `keepMounted={false}` to mount it only while expanded; the check is reactive, so it mounts and unmounts as the state changes. - `Popover` accepts `anchorRect` as a rectangle or rectangle accessor when content must be positioned without a trigger element. - Compound components: `Dialog.Trigger`, `Tabs.List`, `Select.Option`, etc. (`Object.assign` statics; also exported flat: `AccordionRoot`, `AlertTitle`, …). Parts are styleable/testable via `data-slot="..."` and state attrs (`data-open`, `data-selected`, `data-invalid`). - `Tabs` does not require `ResizeObserver`. When it is unavailable, selection and keyboard behavior remain active and the indicator is measured on selection, mount, and window resize. -- `Flex`, `Grid` and `Navbar` take a polymorphic `as`. Nothing else does; reach - for the component that renders the element you want rather than repointing one - that does not. +- `Flex`, `Grid`, `Navbar` and `Text` take a polymorphic `as`, and `Card` and + `Button` take an `href` that makes them anchors. Nothing else is polymorphic; + reach for the component that renders the element you want rather than + repointing one that does not. ```tsx @@ -178,6 +207,82 @@ The available roles are `body`, `heading`, `display`, and `mono`. They resolve t `--font-body`, `--font-heading`, `--font-display`, and `--font-mono`, with `--font-sans` as the shared fallback. This works with PathScale Fonts and application-owned font faces. +**`family="heading"` is a typeface, not a heading.** It says which face to set +the text in and nothing about the document. Say which element the text *is* +with `as`: + +```tsx +Pricing +What you get +Every plan includes the same engine. +``` + +`as` defaults to `span`, which is right for a run of text inside a sentence and +is what `Text` has always rendered. It accepts `span`, `p`, `div`, `h1` through +`h6`, `strong` and `em`. + +Reach for it on every title. Sites read `family="heading"` as making a heading, +and the result was pages, landing pages among them, with no heading of any role +anywhere on them: a reader using headings to move through the page found +nothing to move between. If a page has a title, that title is an `h1`. + +### Naming a Switch, Radio or Checkbox + +All three render a visually hidden 1x1 `input` with a styled, `aria-hidden` +sibling beside it. That is the ordinary pattern and it is correct, and it means +the control has whatever name you give it and no name at all otherwise. There +is nothing visible for a name to be inferred from: the thing a reader sees is +the decoration. + +Give it children, which become the visible label and the name through the +wrapping `