From c05447a8a730b979e77982cd4bbb367d42035af4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Jul 2026 10:03:53 +0000 Subject: [PATCH 1/2] =?UTF-8?q?feat(forms):=20add=20.sf-live-validate=20?= =?UTF-8?q?=E2=80=94=20opt-in=20native=20validation=20feedback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the removal of unconditional :user-invalid/:user-valid auto-colouring. On reflection the removal was the right call — :user-invalid fires on simple focus+blur (verified: a real submit-button click is the reliable trigger; bare focus()/blur() does not fire it reliably even inside a
), so a still-empty required field could be marked invalid mid multi-field form-fill, before any submit was attempted. This is a known anti-pattern; Bootstrap's own .was-validated gate exists specifically to avoid firing validation styling from bare native pseudo-classes. .sf-live-validate is that same gate, framework-native: apply it to a or
to scope the :user-invalid/:user-valid -> --sf-field-border-color pivot to that subtree, active only once a submit has actually been attempted. Restores the zero-JS convenience the removed behaviour offered, without its premature-feedback failure mode — and without requiring per-field JS the way the explicit .sf-is-invalid/.sf-is-valid classes do. - optional/forms.css: new scoped rule pair, right where the unconditional version used to live. - docs/token-annotations.json: class description (flows into docs/api-index.{json,md} and docs/classes.md via `npm run docs`). - docs/migration.md: cross-reference added to the existing forms breaking-change section (this PR is additive, not itself breaking). - CHANGELOG.md: Features entry. - tests/forms.spec.js: new deterministic Playwright test. :user-invalid does not reliably match via synthetic focus()/blur() in headless automation (the reason the removal PR shipped without a trigger-level test), but a real submit-button click does, reliably, once the native validation bubble is suppressed via preventDefault() on the `invalid` event — the standard pairing for custom validation styling. Asserts --sf-field-border-color directly (not border-top-color, which briefly reflects Chromium's own transient bubble-UI rendering) — matching the assertion style already used in tests/states-full.spec.js for the same token. Verified in both themes via a direct Chromium harness before porting into the Playwright spec. Full node --test unit suite (109/109) and all 10 CSS gates pass; audit confirms 293 .sf-classes (+1). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GnwqYHgHNAWPbsUJWUeY1w --- CHANGELOG.md | 5 ++- configurator/src/data/classes.generated.json | 10 ++++++ demos/full-api-demo-with-overrides.html | 2 +- demos/full-api-demo.html | 2 +- docs/api-index.json | 34 +++++++++++++++++--- docs/api-index.md | 12 +++++-- docs/classes.md | 10 +++++- docs/migration.md | 14 ++++++++ docs/registry.json | 3 +- docs/token-annotations.json | 1 + optional/forms.css | 20 ++++++++++++ tests/forms.spec.js | 28 ++++++++++++++++ 12 files changed, 128 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2223e9a..e7b033bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## Unreleased +### Features +- **forms:** add `.sf-live-validate` — an opt-in scope (apply to a ``/`
`) that restores native `:user-invalid`/`:user-valid` → `--sf-field-border-color` feedback within that subtree, gated behind a real submit attempt rather than firing unconditionally on simple focus+blur. The zero-JS convenience of the removed automatic behaviour, without its premature-feedback failure mode. + ### Breaking Changes - **components:** `.sf-btn` nested in `.sf-card` no longer auto-shrinks its label to `--sf-text-s`. Buttons in cards now render at their own size like anywhere else; add `.sf-btn--s` for a compact card action. The orphaned `--sf-card-btn-font-size` token is removed. See `docs/migration.md`. -- **forms:** form fields no longer auto-colour on native `:user-invalid` / `:user-valid` — a field no longer looks "broken" the instant the browser considers it invalid, before your app or the user submits anything. The explicit `.sf-is-invalid` / `.sf-is-valid` (and `.sf-is-warning` / `.sf-is-info` / `.sf-is-danger`) state classes are unaffected and remain the way to colour a field's validation state. See `docs/migration.md`. +- **forms:** form fields no longer auto-colour on native `:user-invalid` / `:user-valid` — a field no longer looks "broken" the instant the browser considers it invalid, before your app or the user submits anything. The explicit `.sf-is-invalid` / `.sf-is-valid` (and `.sf-is-warning` / `.sf-is-info` / `.sf-is-danger`) state classes are unaffected and remain the way to colour a field's validation state; `.sf-live-validate` opts a `` back into native-triggered feedback. See `docs/migration.md`. ## [0.7.15] - 2026-07-11 diff --git a/configurator/src/data/classes.generated.json b/configurator/src/data/classes.generated.json index 3b8b7071..082cf659 100644 --- a/configurator/src/data/classes.generated.json +++ b/configurator/src/data/classes.generated.json @@ -2284,6 +2284,16 @@ "optional": true, "layer": "slashed.utilities" }, + { + "name": "sf-live-validate", + "selector": ".sf-live-validate", + "kind": "form", + "category": "Forms (classless)", + "group": "", + "description": "Scopes native constraint-validation feedback to this subtree: within it, :user-invalid/:user-valid on input/select/textarea drive --sf-field-border-color (the same token .sf-is-invalid/.sf-is-valid set explicitly). Apply to a or
. Off by default so a still-empty required field isn't marked invalid before a submit is attempted.", + "optional": true, + "layer": "slashed.forms" + }, { "name": "sf-marker--action", "selector": ".sf-marker--action", diff --git a/demos/full-api-demo-with-overrides.html b/demos/full-api-demo-with-overrides.html index 6b5b3afc..3cd9b3dd 100644 --- a/demos/full-api-demo-with-overrides.html +++ b/demos/full-api-demo-with-overrides.html @@ -122,7 +122,7 @@

SLASHED Full API Demo

-

v0.7.15 · full bundle from jsDelivr CDN · 328 classes · 755 tokens (265 configurable)

+

v0.7.15 · full bundle from jsDelivr CDN · 329 classes · 755 tokens (265 configurable)

ultimate-override.css is ACTIVE. Every value below is recomputed from perturbed knob tokens — toggle it off in the toolbar, or compare against the un-overridden page.
diff --git a/demos/full-api-demo.html b/demos/full-api-demo.html index 583e6553..0b41d8e4 100644 --- a/demos/full-api-demo.html +++ b/demos/full-api-demo.html @@ -121,7 +121,7 @@

SLASHED Full API Demo

-

v0.7.15 · full bundle from jsDelivr CDN · 328 classes · 755 tokens (265 configurable)

+

v0.7.15 · full bundle from jsDelivr CDN · 329 classes · 755 tokens (265 configurable)

Baseline render with default tokens. Use the toolbar to switch theme, toggle the ultimate override live, or replay motion. The always-on override variant is full-api-demo-with-overrides.html.
diff --git a/docs/api-index.json b/docs/api-index.json index 8c2e1aff..63d99bb5 100644 --- a/docs/api-index.json +++ b/docs/api-index.json @@ -26,14 +26,14 @@ "optimal" ], "counts": { - "total": 1084, + "total": 1085, "by_type": { - "class": 329, + "class": 330, "token": 755 }, "by_tier": { "INTERNAL": 1, - "PUBLIC": 1024, + "PUBLIC": 1025, "PUBLIC-ADVANCED": 59 }, "by_role": { @@ -41,8 +41,8 @@ "knob": 265 }, "tokens": 755, - "classes": 329, - "sf_classes": 292, + "classes": 330, + "sf_classes": 293, "is_classes": 28, "unprefixed_classes": 9, "by_category": { @@ -50,6 +50,7 @@ "Component tokens": 46, "Components": 30, "Core tokens": 622, + "Forms (classless)": 1, "Layout primitives": 145, "Layout tokens": 53, "Macro classes": 58, @@ -24879,6 +24880,29 @@ "full" ] }, + { + "name": "sf-live-validate", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-live-validate", + "prefix": "sf", + "kind": "form", + "category": "Forms (classless)", + "area": "forms", + "group": "", + "description": "Scopes native constraint-validation feedback to this subtree: within it, :user-invalid/:user-valid on input/select/textarea drive --sf-field-border-color (the same token .sf-is-invalid/.sf-is-valid set explicitly). Apply to a or
. Off by default so a still-empty required field isn't marked invalid before a submit is attempted.", + "isVariant": false, + "baseClass": null, + "optional": true, + "layer": "slashed.forms", + "sourceFiles": [ + "optional/forms.css" + ], + "bundles": [ + "full", + "optimal" + ] + }, { "name": "sf-marker--action", "type": "class", diff --git a/docs/api-index.md b/docs/api-index.md index f7824d95..d57ce805 100644 --- a/docs/api-index.md +++ b/docs/api-index.md @@ -10,11 +10,11 @@ and a short description. The machine-readable companion (with all columns) is [registry.json](registry.json); for the tier contract see [architecture.md](architecture.md). -**1084 elements** — 755 tokens, 329 classes. +**1085 elements** — 755 tokens, 330 classes. | Tier | Count | Meaning | |---|---|---| -| PUBLIC | 1024 | Everyday surface. SemVer-stable. | +| PUBLIC | 1025 | Everyday surface. SemVer-stable. | | PUBLIC-ADVANCED | 59 | Same SemVer guarantee; niche/powerful. | | INTERNAL | 1 | Implementation detail; may change without a major bump. | @@ -795,7 +795,7 @@ and a short description. The machine-readable companion (with all columns) is | `--sf-surface-bg-size` | PUBLIC | knob | surface | `cover` | background-size for the .sf-surface-bg image. Default: cover. | | `--sf-surface-color` | PUBLIC | consumption | surface | `var(--sf-color-base)` | Input for the generic .sf-surface macro. Set any color (including palette shades); the macro derives background, auto-contrast foreground, and the contextual token set from it. | -## Classes (329) +## Classes (330) ### Accessibility (8) @@ -845,6 +845,12 @@ and a short description. The machine-readable companion (with all columns) is | `.sf-card--elevated` | PUBLIC | component | .sf-card | Floating card: hides the border, applies a larger shadow. | | `.sf-card--interactive` | PUBLIC | component | .sf-card | Adds a pointer cursor and a shadow/translate lift on hover or keyboard focus. Pair with .sf-clickable-parent for a fully clickable card. Lift is suppressed under prefers-reduced-motion. | +### Forms (classless) (1) + +| Class | Tier | Kind | Group | Description | +|---|---|---|---|---| +| `.sf-live-validate` | PUBLIC | form | — | Scopes native constraint-validation feedback to this subtree: within it, :user-invalid/:user-valid on input/select/textarea drive --sf-field-border-color (the same token .sf-is-invalid/.sf-is-valid set explicitly). Apply to a or
. Off by default so a still-empty required field isn't marked invalid before a submit is attempted. | + ### Layout primitives (145) | Class | Tier | Kind | Group | Description | diff --git a/docs/classes.md b/docs/classes.md index d4b7a4d1..a39eb213 100644 --- a/docs/classes.md +++ b/docs/classes.md @@ -3,7 +3,7 @@ > **Generated** from source by `scripts/gen-class-reference.js` — > run `npm run docs:classes` to refresh. Do not edit by hand. -**292 .sf-classes, 28 .sf-is-classes.** Every `.sf-*` layout/macro class and `.sf-is-*` state class. +**293 .sf-classes, 28 .sf-is-classes.** Every `.sf-*` layout/macro class and `.sf-is-*` state class. See [architecture.md](architecture.md) for layer order and naming conventions. ## Layout primitives (`core/layout.css`) @@ -317,6 +317,14 @@ See [architecture.md](architecture.md) for layer order and naming conventions. | `.sf-theme-light` | | `.sf-theme-transition` | +## Forms (`optional/forms.css`) + +1 classes. + +| Class | +|---| +| `.sf-live-validate` | + ## Components (`optional/components.css`) 32 classes. diff --git a/docs/migration.md b/docs/migration.md index 5b179bae..c01f5472 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -93,6 +93,20 @@ The underlying `--sf-field-border-color` token and its consumers (resting border, focus, autofill) are unchanged — only the automatic pseudo-class trigger is gone. +**Want the native-triggered feedback back, without writing JS?** Add +`.sf-live-validate` to the `` (or a `
`) — it re-enables the +`:user-invalid`/`:user-valid` → `--sf-field-border-color` pivot, scoped to that +subtree, but only once a submit has actually been attempted (not on simple +focus+blur, which is what made the original unconditional behaviour fire +prematurely mid-form-fill): + +```html + + + + +``` + ### `--sf-touch-target` decoupled from `--sf-size-l`; size scale regularised (breaking) `--sf-touch-target` used to be `var(--sf-size-l)`, which coupled the WCAG 2.5.5 diff --git a/docs/registry.json b/docs/registry.json index 982b3f83..0e65ba51 100644 --- a/docs/registry.json +++ b/docs/registry.json @@ -21,7 +21,7 @@ ], "counts": { "tokens": 755, - "sf_classes": 292, + "sf_classes": 293, "is_classes": 28 } }, @@ -978,6 +978,7 @@ "sf-link--subtle", "sf-link-external", "sf-list-none", + "sf-live-validate", "sf-marker--action", "sf-marker--primary", "sf-marker--secondary", diff --git a/docs/token-annotations.json b/docs/token-annotations.json index d8c9081a..63b3a2be 100644 --- a/docs/token-annotations.json +++ b/docs/token-annotations.json @@ -1063,6 +1063,7 @@ "sf-link--reverse": "Link color modifier: reverses the default link color to work on dark/inverted backgrounds. Sets color to --sf-color-text--inverse.", "sf-link--subtle": "Link color modifier: renders the link in the body text color instead of the default action color. Underline still indicates the link on hover.", "sf-link-external": "Automatically appends an external-link icon (via ::after content) to indicate the link opens in a new tab or goes to an external domain.", + "sf-live-validate": "Scopes native constraint-validation feedback to this subtree: within it, :user-invalid/:user-valid on input/select/textarea drive --sf-field-border-color (the same token .sf-is-invalid/.sf-is-valid set explicitly). Apply to a
or
. Off by default so a still-empty required field isn't marked invalid before a submit is attempted.", "sf-no-tap-highlight": "Removes the mobile tap highlight color (-webkit-tap-highlight-color: transparent). Use on interactive elements with a custom active state.", "sf-not-prose": "Opt-out of sf-prose typography styles within a prose block. Apply to an element that should keep default browser or component styles.", "sf-overflow-fade": "Adds a gradient fade at the inline-end of an overflowing element to hint at hidden content. Use inside sf-reel or any scroll container.", diff --git a/optional/forms.css b/optional/forms.css index 91d972c0..eaedcd50 100644 --- a/optional/forms.css +++ b/optional/forms.css @@ -86,6 +86,26 @@ } /* stylelint-enable selector-pseudo-class-no-unknown */ + /* Opt-in native validation feedback. Unconditional :user-invalid/:user-valid + styling was removed in 0.8.0 (see docs/migration.md): it fires on simple + focus+blur, so a still-empty required field could be marked invalid mid + multi-field form-fill, before any submit was attempted — the pattern + Bootstrap's own .was-validated gate exists to avoid. .sf-live-validate is + that same gate: put it on a or
to scope the pivot to + that subtree, so it only lights up on a real submit attempt (or explicit + interaction) instead of applying everywhere by default. */ + .sf-live-validate input:user-invalid, + .sf-live-validate select:user-invalid, + .sf-live-validate textarea:user-invalid { + --sf-field-border-color: var(--sf-color-danger); + } + + .sf-live-validate input:user-valid, + .sf-live-validate select:user-valid, + .sf-live-validate textarea:user-valid { + --sf-field-border-color: var(--sf-color-success); + } + /* Numeric inputs */ input[type="number"] { font-variant-numeric: var(--sf-font-numeric, tabular-nums); diff --git a/tests/forms.spec.js b/tests/forms.spec.js index dfe1cde9..4c12f89b 100644 --- a/tests/forms.spec.js +++ b/tests/forms.spec.js @@ -67,6 +67,34 @@ for (const theme of ['light', 'dark']) { expect(cs['border-top-color']).toBe('rgb(1, 2, 3)'); }); + test('.sf-live-validate scopes native validation feedback to a submitted form', async ({ page }) => { + // Two forms, both with an invalid required field, both submitted (a real, + // trusted click — :user-invalid needs genuine interaction, not a bare JS + // focus()/blur() call, to match reliably). Only "validated" carries the + // opt-in class, so only its field should pick up --sf-field-border-color. + // The invalid-event listener suppresses the native validation bubble — + // the standard pairing for custom validation styling — so the assertion + // isn't confused by the browser's own transient bubble-UI rendering. + await mount(page, ` + +
+ + `); + await page.locator('#plain button[type=submit]').click(); + await page.locator('#validated button[type=submit]').click(); + const [plainVar, validatedVar, danger] = await page.evaluate(() => [ + getComputedStyle(document.getElementById('plain-input')).getPropertyValue('--sf-field-border-color').trim(), + getComputedStyle(document.getElementById('validated-input')).getPropertyValue('--sf-field-border-color').trim(), + getComputedStyle(document.body).getPropertyValue('--sf-color-danger').trim(), + ]); + expect(plainVar).toBe(''); // unscoped: no auto-colouring, even on submit + expect(validatedVar).toBe(danger); // scoped: native invalidity now drives the token + }); + test('textarea is vertically resizable with a minimum height', async ({ page }) => { await mount(page, ``); const cs = await computed(page, ['resize', 'min-block-size']); From b6809da23d3f8d1ef4365137d4e2827ff0a05dac Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Jul 2026 10:13:15 +0000 Subject: [PATCH 2/2] =?UTF-8?q?docs(forms):=20address=20review=20=E2=80=94?= =?UTF-8?q?=20llm-guide=20entry,=20category=20label,=20cascade-safety=20no?= =?UTF-8?q?te?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address CodeRabbit + Greptile review on PR #604: - docs/llm-guide.md: document .sf-live-validate as the opt-in gate for native :user-invalid/:user-valid feedback (CLAUDE.md mandates reviewing this file for any optional/*.css change; both reviewers independently flagged the omission, even though llm-guide.md was previously 100% token-only). - scripts/lib/api-index/extract.js: optional/forms.css was hardcoded 'Forms (classless)' from when the file had zero .sf-* classes; now that .sf-live-validate exists, drop the stale qualifier. Regenerated docs/api-index.{json,md} + configurator's synced copy. - optional/forms.css: add a code comment addressing Greptile's cascade concern — verified (not just asserted) in Chromium that a manually-applied .sf-is-invalid always wins over .sf-live-validate's native-triggered colour, regardless of selector specificity, because slashed.states is declared AFTER slashed.forms in the layer order (core/layers.css) — cascade layer order beats specificity. Greptile's specific claim (specificity 0-2-1 beats 0-1-0, so the native rule would silently override the manual one) doesn't hold once layers are accounted for; documented the real (safe) behaviour instead of "fixing" a conflict that doesn't exist. Full node --test unit suite (109/109) and all 10 CSS gates pass. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GnwqYHgHNAWPbsUJWUeY1w --- configurator/src/data/classes.generated.json | 2 +- docs/api-index.json | 4 ++-- docs/api-index.md | 2 +- docs/llm-guide.md | 10 +++++++++- optional/forms.css | 10 +++++++++- scripts/lib/api-index/extract.js | 2 +- 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/configurator/src/data/classes.generated.json b/configurator/src/data/classes.generated.json index 082cf659..3e24e7a2 100644 --- a/configurator/src/data/classes.generated.json +++ b/configurator/src/data/classes.generated.json @@ -2288,7 +2288,7 @@ "name": "sf-live-validate", "selector": ".sf-live-validate", "kind": "form", - "category": "Forms (classless)", + "category": "Forms", "group": "", "description": "Scopes native constraint-validation feedback to this subtree: within it, :user-invalid/:user-valid on input/select/textarea drive --sf-field-border-color (the same token .sf-is-invalid/.sf-is-valid set explicitly). Apply to a
or
. Off by default so a still-empty required field isn't marked invalid before a submit is attempted.", "optional": true, diff --git a/docs/api-index.json b/docs/api-index.json index 63d99bb5..889a8443 100644 --- a/docs/api-index.json +++ b/docs/api-index.json @@ -50,7 +50,7 @@ "Component tokens": 46, "Components": 30, "Core tokens": 622, - "Forms (classless)": 1, + "Forms": 1, "Layout primitives": 145, "Layout tokens": 53, "Macro classes": 58, @@ -24887,7 +24887,7 @@ "selector": ".sf-live-validate", "prefix": "sf", "kind": "form", - "category": "Forms (classless)", + "category": "Forms", "area": "forms", "group": "", "description": "Scopes native constraint-validation feedback to this subtree: within it, :user-invalid/:user-valid on input/select/textarea drive --sf-field-border-color (the same token .sf-is-invalid/.sf-is-valid set explicitly). Apply to a or
. Off by default so a still-empty required field isn't marked invalid before a submit is attempted.", diff --git a/docs/api-index.md b/docs/api-index.md index d57ce805..1a1fde20 100644 --- a/docs/api-index.md +++ b/docs/api-index.md @@ -845,7 +845,7 @@ and a short description. The machine-readable companion (with all columns) is | `.sf-card--elevated` | PUBLIC | component | .sf-card | Floating card: hides the border, applies a larger shadow. | | `.sf-card--interactive` | PUBLIC | component | .sf-card | Adds a pointer cursor and a shadow/translate lift on hover or keyboard focus. Pair with .sf-clickable-parent for a fully clickable card. Lift is suppressed under prefers-reduced-motion. | -### Forms (classless) (1) +### Forms (1) | Class | Tier | Kind | Group | Description | |---|---|---|---|---| diff --git a/docs/llm-guide.md b/docs/llm-guide.md index 1cf8c678..4759c759 100644 --- a/docs/llm-guide.md +++ b/docs/llm-guide.md @@ -1091,10 +1091,18 @@ Used by `.sf-is-active`, `.sf-is-current`, etc. in `core/states.css`. Allow comp --sf-link-external-marker: " ↗" /* marker for .sf-link-external */ /* Scoped override hooks — set per-field/form to override global borders/text */ ---sf-field-border-color /* set by validation states (error/success/warning/info/danger) */ +--sf-field-border-color /* set by validation states (error/success/warning/info/danger); also set natively by :user-invalid/:user-valid within .sf-live-validate (opt-in — see below) */ --sf-field-text-color /* set by validation states for text color feedback */ ``` +Native browser validation (`:user-invalid`/`:user-valid`) does NOT colour a field's +border by default — add `.sf-live-validate` to the ``/`
` to opt a +subtree into that feedback, gated behind a real submit attempt (not simple +focus+blur, which would flag a still-empty required field prematurely). The +explicit `.sf-is-invalid`/`.sf-is-valid` state classes (`core/states.css`) always +win over `.sf-live-validate`'s native-triggered colour, regardless of specificity — +`slashed.states` is declared after `slashed.forms` in the layer order (`core/layers.css`). + Component tokens from `optional/tokens.components.css`: ```css diff --git a/optional/forms.css b/optional/forms.css index eaedcd50..05bd901d 100644 --- a/optional/forms.css +++ b/optional/forms.css @@ -93,7 +93,15 @@ Bootstrap's own .was-validated gate exists to avoid. .sf-live-validate is that same gate: put it on a or
to scope the pivot to that subtree, so it only lights up on a real submit attempt (or explicit - interaction) instead of applying everywhere by default. */ + interaction) instead of applying everywhere by default. + + Safe to combine with manual validation: .sf-is-invalid/.sf-is-valid + (core/states.css, @layer slashed.states) always win over the rules below + regardless of selector specificity, because slashed.states is declared + AFTER slashed.forms in the layer order (core/layers.css) — cascade layer + order beats specificity. A field you've manually marked .sf-is-invalid + (e.g. a server-side error) can't be silently un-coloured by these rules + even if the browser considers it natively valid. */ .sf-live-validate input:user-invalid, .sf-live-validate select:user-invalid, .sf-live-validate textarea:user-invalid { diff --git a/scripts/lib/api-index/extract.js b/scripts/lib/api-index/extract.js index f48d8cf7..43c03c3f 100644 --- a/scripts/lib/api-index/extract.js +++ b/scripts/lib/api-index/extract.js @@ -79,7 +79,7 @@ export const FILE_META = { 'core/motion.css': { category: 'Motion / animation', area: 'motion', kind: 'motion' }, 'core/print.css': { category: 'Print', area: 'print', kind: 'print' }, 'core/themes.css': { category: 'Theme utilities', area: 'theme', kind: 'theme' }, - 'optional/forms.css': { category: 'Forms (classless)', area: 'forms', kind: 'form' }, + 'optional/forms.css': { category: 'Forms', area: 'forms', kind: 'form' }, 'optional/components.css': { category: 'Components', area: 'components', kind: 'component' }, 'optional/theme-example.css': { category: 'Theme example', area: 'theme', kind: 'theme' }, 'optional/utilities.css': { category: 'Utilities', area: 'utilities', kind: 'utility' },