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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

### 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`.

## [0.7.15] - 2026-07-11

Expand Down
26 changes: 26 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,32 @@ the size tier on that card:
.sf-card--compact .sf-btn { --sf-btn-font-size--size: var(--sf-text-s); }
```

### Form fields no longer auto-colour on native `:user-invalid` / `:user-valid` (breaking)

`optional/forms.css` used to flip `--sf-field-border-color` to `--sf-color-danger`
/ `--sf-color-success` automatically whenever the browser's own constraint
validation (`:user-invalid` / `:user-valid`) fired — so a `required` or
`pattern`-constrained field could render "broken" (red border) as soon as it was
touched, before the user submitted anything or your app ran its own validation.
This was the same category of surprise as the card/button rule above — the
framework silently overriding an element's appearance based on browser-internal
state you didn't opt into — so it has been removed.

**What changed for you:** native constraint validation no longer changes a
field's border colour by itself. The explicit state classes in `core/states.css`
— `.sf-is-invalid` / `.sf-is-error`, `.sf-is-valid` / `.sf-is-success`,
`.sf-is-warning`, `.sf-is-info`, `.sf-is-danger` — are unaffected and remain the
one way to colour a field's validation state; add the class when *your* code
(not the browser) decides the field is invalid:

```html
<input type="email" class="sf-is-invalid" aria-invalid="true">
```

The underlying `--sf-field-border-color` token and its consumers (resting
border, focus, autofill) are unchanged — only the automatic pseudo-class
trigger is gone.

### `--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
Expand Down
13 changes: 0 additions & 13 deletions optional/forms.css
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,6 @@
}
/* stylelint-enable selector-pseudo-class-no-unknown */

/* Native validation states */
input:user-invalid,
select:user-invalid,
textarea:user-invalid {
--sf-field-border-color: var(--sf-color-danger);
}

input:user-valid,
select:user-valid,
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);
Expand Down
6 changes: 3 additions & 3 deletions tests/forms.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
// previously with no dedicated spec (only touched incidentally by a11y.spec).
// Asserts the computed styling contract for text inputs, textarea, select, the
// disabled state, and the --sf-field-border-color token indirection that the
// :user-invalid / :user-valid states pivot on. Loads the optimal bundle (which
// includes optional/forms.css) in both themes.
// explicit .sf-is-invalid / .sf-is-valid state classes (core/states.css) set.
// Loads the optimal bundle (which includes optional/forms.css) in both themes.
import { test, expect } from '@playwright/test';
import { renderWithBundle, NO_TRANSITIONS_STYLE } from './render-helpers.js';

Expand Down Expand Up @@ -63,7 +63,7 @@ for (const theme of ['light', 'dark']) {
await mount(page, `<input type="text" id="t" style="--sf-field-border-color: rgb(1, 2, 3)">`);
const cs = await computed(page, ['border-top-color']);
// The border resolves through --sf-field-border-color (the same hook
// :user-invalid/:user-valid flip to danger/success).
// .sf-is-invalid/.sf-is-valid flip to danger/success — core/states.css).
expect(cs['border-top-color']).toBe('rgb(1, 2, 3)');
});

Expand Down