Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Highly opinionated SolidJS component library — batteries and kitchen sink included, but
optimized and shiny.

**103 components.** HeroUI-parity API, daisyUI-style theming, Tailwind v4 tokens, light and
**101 component families.** HeroUI-parity API, daisyUI-style theming, Tailwind v4 tokens, light and
dark themes built in.

**[→ Browse every component, live](https://js.software)**
Expand Down
1 change: 1 addition & 0 deletions docs/api-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,7 @@ extensionUrl?: string
icon?: string | JSX.Element
onDismiss?: () => void
onInstall?: () => void
showDelayMs?: number
storageKey?: string
texts?: FirefoxPWABannerTexts
```
Expand Down
2 changes: 1 addition & 1 deletion docs/how-to-setup-playground.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default defineConfig({
@source "./";
@source "../../UI/src";

/* The library ships no glyphs. This is what resolves icon-[...] tokens, and it
/* The library ships no glyphs. This resolves Iconify utility tokens, and it
scans YOUR source, so an icon only exists if you wrote it. */
@plugin "@iconify/tailwind4";
```
Expand Down
389 changes: 191 additions & 198 deletions docs/release-readiness-2026-09-12.md

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion docs/ui-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
agents alike, and for every consuming application. Those apps link here rather than
keeping their own copy — one library, one set of conventions, no drift.

SolidJS component library, HeroUI-parity API, daisyUI-style theming. ~104 components.
SolidJS component library, HeroUI-parity API, daisyUI-style theming. 101 public
component families.

This file is **how to USE the library** (as a consumer, or when writing demos and
examples). For **modifying the library itself**, read
Expand All @@ -26,6 +27,11 @@ import "@pathscale/ui/index.css"; // tokens + theme
Subpath exports also exist: `./components/*`, `./primitives/*`, `./hooks/*`, `./motion`,
`./styles/*`.

`componentFamilies` is the canonical public visual-family inventory. Documentation
and showcase applications can import it with `ComponentFamily` and
`ComponentFamilyId` from the root barrel, then fail their own build when a newly
shipped family has no page or example.

Layout components require the application compiler before the normal Solid transform. See [Layouts](./layouts.md) for the Rsbuild configuration, exact failure behavior, and porting report.

## Theming
Expand Down Expand Up @@ -154,6 +160,11 @@ Components require `solid-layouts >=0.2.4` so caller styles reach their root ele
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.
- `FirefoxPWABanner` waits 2000ms before appearing by default. Pass
`showDelayMs` when the surrounding onboarding flow needs a different delay;
the same option is available as `ImmersiveLanding.firefoxPWAConfig.showDelayMs`.
The banner still applies its Firefox, standalone-mode, and dismissal checks
before starting that delay.
- `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`).
Expand Down
2 changes: 2 additions & 0 deletions layouts.library.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
"FieldsetLegend",
"FirefoxPWABanner",
"Flex",
"FlexGrid",
"FlexGridRoot",
"Footer",
"Form",
"FormContext",
Expand Down
2 changes: 0 additions & 2 deletions layouts.lint-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@
"src/components/chip/Chip.layout.tsx:warning:legacy-template:legacy component-shaped Layout keeps presentation in component code",
"src/components/chip/Chip.layout.tsx:warning:manual-classes:manual class composition belongs in the recipe",
"src/components/chip/Chip.layout.tsx:warning:manual-classes:manual class composition belongs in the recipe",
"src/components/close-button/CloseButton.layout.tsx:warning:legacy-template:legacy component-shaped Layout keeps presentation in component code",
"src/components/close-button/CloseButton.layout.tsx:warning:manual-classes:manual class composition belongs in the recipe",
"src/components/collapsible/Collapsible.layout.tsx:warning:legacy-template:legacy component-shaped Layout keeps presentation in component code",
"src/components/collapsible/Collapsible.layout.tsx:warning:legacy-template:legacy component-shaped Layout keeps presentation in component code",
"src/components/collapsible/Collapsible.layout.tsx:warning:legacy-template:legacy component-shaped Layout keeps presentation in component code",
Expand Down
16 changes: 16 additions & 0 deletions scripts/check-contracts.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { readdirSync, readFileSync, existsSync } from "node:fs";
import { join } from "node:path";
import { missingRecipeFlagUsages } from "./component-state-contract";
import { componentFamilies } from "../src/component-families";

const COMPONENTS_DIR = "src/components";
const CONTRIBUTING = "CONTRIBUTING.md";
Expand Down Expand Up @@ -30,6 +31,21 @@ type Violation = { component: string; rule: string; detail: string; section: str

const violations: Violation[] = [];

const layoutLibrary = JSON.parse(readFileSync("layouts.library.json", "utf8")) as {
exports?: string[];
};
const layoutExports = new Set(layoutLibrary.exports ?? []);
for (const family of componentFamilies) {
if (!layoutExports.has(family.name)) {
fail(
family.id,
"layouts-manifest",
`${family.name} is public but absent from layouts.library.json exports`,
"Structure",
);
}
}

function fail(component: string, rule: string, detail: string, section: string) {
violations.push({ component, rule, detail, section });
}
Expand Down
Loading
Loading