@sableui/react 0.8.2 → 0.9.0
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/AGENTS.md +1 -1
- package/CHANGELOG.md +83 -0
- package/bin/sable.mjs +26 -11
- package/dist/blocks/index.d.ts +1 -1
- package/dist/components/app-shell/app-shell.js +2 -12
- package/dist/components/app-shell/app-shell.meta.js +22 -0
- package/dist/components/app-shell/index.js +2 -0
- package/dist/components/app-shell/skip-link.js +28 -0
- package/dist/components/button/button.js +35 -8
- package/dist/components/button/button.meta.js +6 -1
- package/dist/components/card/card.meta.js +1 -1
- package/dist/components/command-menu/command-menu.js +27 -2
- package/dist/components/date-tile/date-tile.meta.js +1 -0
- package/dist/components/feedback/feedback.meta.js +1 -0
- package/dist/components/fieldset/fieldset.js +22 -13
- package/dist/components/fieldset/fieldset.meta.js +25 -1
- package/dist/components/form/form.js +45 -9
- package/dist/components/form/form.meta.js +12 -3
- package/dist/components/media-tile/media-tile.js +16 -2
- package/dist/components/media-tile/media-tile.meta.js +8 -0
- package/dist/components/radio-card/radio-card.js +6 -2
- package/dist/components/radio-card/radio-card.meta.js +5 -0
- package/dist/components/sheet/sheet.js +1 -0
- package/dist/components/stepper/stepper.js +7 -2
- package/dist/components/stepper/stepper.meta.js +1 -1
- package/dist/components/toast/toast.js +2 -0
- package/dist/file-hashes.json +28 -27
- package/dist/index.d.ts +109 -22
- package/dist/index.js +1 -1
- package/dist/{meta-CcE_SeFu.d.ts → meta-DVG8ceze.d.ts} +9 -0
- package/dist/patterns/index.d.ts +1 -1
- package/dist/styles.css +30 -0
- package/llms/components/app-shell.md +26 -0
- package/llms/components/button.md +2 -1
- package/llms/components/card.md +1 -1
- package/llms/components/fieldset.md +21 -1
- package/llms/components/form.md +11 -2
- package/llms/components/media-tile.md +32 -0
- package/llms/components/radio-card.md +1 -0
- package/llms/components/stepper.md +1 -1
- package/llms/tasks/creating-a-preset.md +4 -1
- package/llms-full.txt +99 -7
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/registry.json +89 -10
- package/scripts/grade.mjs +14 -3
- package/scripts/sources.mjs +10 -2
- package/skills/sable/SKILL.md +23 -7
package/scripts/grade.mjs
CHANGED
|
@@ -304,6 +304,9 @@ const UNIVERSAL_PROPS = new Set([
|
|
|
304
304
|
"minLength", "min", "max", "step", "rows", "cols", "open", "selected", "multiple", "loading",
|
|
305
305
|
// Media attributes, for components that spread onto <video>/<audio>/<img> (Video, Image).
|
|
306
306
|
"autoPlay", "muted", "loop", "playsInline", "preload", "crossOrigin", "decoding", "fetchPriority", "srcSet", "sizes",
|
|
307
|
+
// Input attributes the form contract asks for (the numeric keypad, the
|
|
308
|
+
// return key's label), on components that spread onto <input>.
|
|
309
|
+
"inputMode", "enterKeyHint", "autoCapitalize", "autoCorrect", "pattern", "list", "accept", "capture",
|
|
307
310
|
])
|
|
308
311
|
const isEventProp = (p) => /^on[A-Z]/.test(p)
|
|
309
312
|
const isPassthrough = (p) => p.startsWith("data-") || p.startsWith("aria-") || p.startsWith("--")
|
|
@@ -500,6 +503,14 @@ for (const file of files) {
|
|
|
500
503
|
/* 7. Surfaces the app built itself (informational). */
|
|
501
504
|
if (/\.(tsx|jsx)$/.test(file)) {
|
|
502
505
|
for (const { index, ...s } of surfaceInventory(text)) inventory.push({ file: rel, line: lineOf(text, index), ...s })
|
|
506
|
+
// Arrow-key handling written by hand: list navigation is behaviour Sable
|
|
507
|
+
// owns (roving focus, active descendant, typeahead), and the storefront
|
|
508
|
+
// search wrote its own. A note, since a canvas or a game has its own keys.
|
|
509
|
+
const arrows = /\bonKeyDown\b[\s\S]{0,600}?["'`]Arrow(?:Up|Down)["'`]|["'`]Arrow(?:Up|Down)["'`][\s\S]{0,600}?\bonKeyDown\b/.exec(text)
|
|
510
|
+
if (arrows) {
|
|
511
|
+
const at = text.indexOf("Arrow", arrows.index)
|
|
512
|
+
inventory.push({ file: rel, line: lineOf(text, at), tag: "onKeyDown", kind: "keyboard", classes: "ArrowUp / ArrowDown handled by hand", consider: "CommandMenu, Combobox, Menu, Select, Toolbar or Tabs, whose keyboard model is built and tested" })
|
|
513
|
+
}
|
|
503
514
|
}
|
|
504
515
|
|
|
505
516
|
/* 8. Composition. Anything exporting a component that returns a big tree is
|
|
@@ -679,10 +690,10 @@ if (conformance.length) {
|
|
|
679
690
|
L(" composition no route-like files found")
|
|
680
691
|
}
|
|
681
692
|
if (inventory.length) {
|
|
682
|
-
const kinds = ["surface", "media frame", "round"].map((k) => [k, inventory.filter((s) => s.kind === k).length]).filter(([, n]) => n)
|
|
693
|
+
const kinds = ["surface", "media frame", "round", "keyboard"].map((k) => [k, inventory.filter((s) => s.kind === k).length]).filter(([, n]) => n)
|
|
683
694
|
L()
|
|
684
|
-
L(` app-owned surfaces (
|
|
685
|
-
for (const s of inventory.slice(0, 12)) L(` ${s.file}:${s.line} <${s.tag}> ${s.kind} (${s.classes}) → ${s.consider}`)
|
|
695
|
+
L(` app-owned surfaces (notes, not scored; keeping your own is fine) — ${inventory.length}: ${kinds.map(([k, n]) => `${n} ${k}`).join(" · ")}`)
|
|
696
|
+
for (const s of inventory.slice(0, 12)) L(` ${s.file}:${s.line} <${s.tag}> ${s.kind} (${s.classes}) → Sable has ${s.consider}`)
|
|
686
697
|
if (inventory.length > 12) L(` … ${inventory.length - 12} more (--json lists every one)`)
|
|
687
698
|
}
|
|
688
699
|
if (cliUsage) {
|
package/scripts/sources.mjs
CHANGED
|
@@ -195,13 +195,21 @@ export function sourcesCss(pkgRoot, root, outFile) {
|
|
|
195
195
|
* that component unstyled, silently — so it fails. A whole-package @source is
|
|
196
196
|
* correct, just heavy, so it only gets a note.
|
|
197
197
|
*/
|
|
198
|
+
const WHOLE_DIST = /@source[^;]*(?:@sableui\/react|packages\/sable)\/dist(?:\/\*\*)?[^;]*;/
|
|
199
|
+
|
|
198
200
|
export function sourcesCheck(pkgRoot, root, cssFiles) {
|
|
199
201
|
const generated = cssFiles.find((f) => f.text.includes(SOURCES_MARKER))
|
|
200
202
|
if (!generated) {
|
|
201
|
-
const whole = cssFiles.some((f) =>
|
|
202
|
-
return whole ? [{ status: "info", name: "css:size", detail: "@source scans all of Sable's dist, so every component's classes ship (about 32 KB gzipped).", fix: "sable sources --write src/sable-sources.css, then import it in place of that @source." }] : []
|
|
203
|
+
const whole = cssFiles.some((f) => WHOLE_DIST.test(f.text))
|
|
204
|
+
return whole ? [{ status: "info", name: "css:size", detail: "@source scans all of Sable's dist, so every component's classes ship (about 32 KB gzipped; an app using half the catalog lands near 20 KB).", fix: "sable sources --write src/sable-sources.css, then import it in place of that @source." }] : []
|
|
203
205
|
}
|
|
204
206
|
const rel = relative(root, generated.path)
|
|
207
|
+
// The generated list only trims anything once the whole-package @source is
|
|
208
|
+
// gone: left beside it, Tailwind still scans every module, and the CSS stays
|
|
209
|
+
// the size it was.
|
|
210
|
+
const leftover = cssFiles.find((f) => f !== generated && WHOLE_DIST.test(f.text))
|
|
211
|
+
if (leftover)
|
|
212
|
+
return [{ status: "warn", name: "css:sources", detail: `${relative(root, leftover.path)} still has the whole-package @source, so ${rel} trims nothing: Tailwind scans all of Sable either way.`, fix: `Delete that @source line; ${rel} replaces it.` }]
|
|
205
213
|
const { modules, unresolved } = sableModules(pkgRoot, appSources(root))
|
|
206
214
|
if (unresolved.length && !/\/dist\/\*\*\//.test(generated.text))
|
|
207
215
|
return [{ status: "fail", name: "css:sources", detail: `${rel} lists modules, but these imports could not be traced: ${unresolved.slice(0, 4).join(", ")}.`, fix: `sable sources --write ${rel}` }]
|
package/skills/sable/SKILL.md
CHANGED
|
@@ -29,9 +29,11 @@ References inform an original direction. A reference or preset is optional.
|
|
|
29
29
|
`"reduced"`), state still changes and nothing keeps moving.
|
|
30
30
|
6. Write the reuse inventory: the Sable components, parts, utilities, and token
|
|
31
31
|
groups used, and every app-owned UI or CSS addition with the catalog search
|
|
32
|
-
that justified it. Start from the app-owned surfaces `sable check` lists
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
that justified it. Start from the app-owned surfaces `sable check` lists,
|
|
33
|
+
which are notes, not failures: for each, write one line saying you used the
|
|
34
|
+
Sable part named beside it or kept your own, and why ("composed our own
|
|
35
|
+
product tile from AspectBox + Badge: the brief wants the price over the
|
|
36
|
+
photo"). Keeping your own layout is fine. Behaviour still comes from Sable.
|
|
35
37
|
7. Record what you learned: `sable docs learning-from-builds`. Keep conditional
|
|
36
38
|
lessons with evidence; never fabricate human approval or preference records.
|
|
37
39
|
|
|
@@ -142,9 +144,18 @@ The installed catalog and guides are authoritative for API and token names.
|
|
|
142
144
|
|
|
143
145
|
1. Search the installed catalog for every recognizable element or behavior:
|
|
144
146
|
`sable search "<need>" --json`. Inspect the matching
|
|
145
|
-
`sable component <Name> --props` and `--examples` before using it.
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
`sable component <Name> --props` and `--examples` before using it. What a
|
|
148
|
+
result asks of you depends on what it is:
|
|
149
|
+
- **Behaviour** (controls, fields, overlays, menus, tabs, anything with focus,
|
|
150
|
+
keyboard or ARIA): always use it, never rebuild it. A native element wearing
|
|
151
|
+
a component's classes, or arrow keys handled by hand, is a reinvention even
|
|
152
|
+
if it uses valid tokens.
|
|
153
|
+
- **Parts** (Card, Entity, AspectBox, Badge, Avatar, MediaImage, layout
|
|
154
|
+
primitives): compose them freely, in whatever arrangement the design needs.
|
|
155
|
+
- **References** (patterns, blocks, and compositions marked ◇ such as
|
|
156
|
+
MediaTile): use one when its layout fits the brief. When it doesn't, compose
|
|
157
|
+
your own from parts; that is not a gap and needs no apology. Apps on Sable
|
|
158
|
+
should not all look alike.
|
|
148
159
|
2. Resolve styling in this order: Sable component/variant, Sable utility or recipe,
|
|
149
160
|
semantic token, then app-owned CSS for a genuinely uncovered need. Retrieve
|
|
150
161
|
`sable utility <name>` and `sable token <group>` rather than guessing names.
|
|
@@ -170,7 +181,12 @@ The installed catalog and guides are authoritative for API and token names.
|
|
|
170
181
|
Save a short brief in the project's existing design location: audience, primary
|
|
171
182
|
task, content, constraints, selected identity, and meaningful states, plus each
|
|
172
183
|
route's width (a Region `max`, or fluid) and why, chosen by what the person does
|
|
173
|
-
there (`sable docs building-a-page`).
|
|
184
|
+
there (`sable docs building-a-page`). Name the product's signature surfaces: the
|
|
185
|
+
one to three places its identity lives (a shop's product card and search, a music
|
|
186
|
+
app's player and album tile). Design those from the brief's direction first,
|
|
187
|
+
composing Sable's parts and behaviour; take a Sable reference for one only when
|
|
188
|
+
it already matches that direction. Everything else (forms, settings, menus,
|
|
189
|
+
tables) takes Sable's defaults. Label invented sample content. A supplied preset is an explicit input: read its exact record and
|
|
174
190
|
revision, validate it through `sable preset --check <path> --json`, and read
|
|
175
191
|
`sable docs using-presets`. Preserve its tokens and guidance unless the user requests
|
|
176
192
|
a change or the new task requires a documented adaptation.
|