Skip to content

refactor(forms)!: stop auto-colouring fields on native :user-invalid/:user-valid - #603

Merged
jackgranatowski merged 1 commit into
mainfrom
claude/button-size-visibility-issue-40xlwy
Jul 12, 2026
Merged

refactor(forms)!: stop auto-colouring fields on native :user-invalid/:user-valid#603
jackgranatowski merged 1 commit into
mainfrom
claude/button-size-visibility-issue-40xlwy

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Why

Follow-up to #602 (removing the .sf-card/.sf-btn auto-shrink). An audit of the whole framework for "the component silently decides something for you" patterns turned up one more: optional/forms.css flipped --sf-field-border-color to --sf-color-danger / --sf-color-success automatically whenever the browser's own constraint validation (:user-invalid / :user-valid) fired.

That means a required or pattern-constrained field could render "broken" (red border) as soon as it was touched — before the user submitted anything, and before the app ran its own validation logic. Same category of surprise as the card/button rule: the framework overriding an element's appearance based on browser-internal state the consumer never opted into.

What

  • Delete the input/select/textarea :user-invalid / :user-valid rules in optional/forms.css (13 lines).
  • Unaffected: the --sf-field-border-color token itself and all its consumers (resting border, focus, autofill), and 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. These remain the one way to colour a field's validation state — add the class when your code decides the field is invalid.
  • No registry/docs/demos regeneration needed: --sf-field-border-color was never declared as a :root token (pure var()-fallback hook, only ever set by the removed rule and by the state classes), so removing the automatic trigger doesn't change the public token surface at all. Confirmed: 755 tokens / registry +0/-0 before and after.
  • Update stale test comments in tests/forms.spec.js that referenced the removed mechanism.
  • Document the break in docs/migration.md and CHANGELOG.md.

On testing this

I did not add a Playwright assertion asserting the removal directly: :user-invalid doesn't reliably match via synthetic focus()/blur() in headless automation (verified in Chromium — it didn't even match before this change, so a test built on that trigger would be flaky by construction, not a real regression guard). The explicit path already has solid, non-flaky coverage in tests/states-full.spec.js (.sf-is-invalid/.sf-is-valid/etc. → correct --sf-field-border-color), which is untouched by this PR.

Verification

Full node --test unit suite (109/109) and all 10 CSS gates pass, confirming no token-surface drift.

Notes

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • Breaking Changes
    • Form fields no longer automatically change border color based on native browser validation states.
    • Use explicit validation classes such as .sf-is-invalid and .sf-is-valid to control field styling.
    • See the migration guide for upgrade details.

…:user-valid

optional/forms.css flipped --sf-field-border-color to --sf-color-danger /
--sf-color-success automatically whenever the browser's own constraint
validation (:user-invalid / :user-valid) fired. A required or
pattern-constrained field could render "broken" (red border) as soon as it was
touched, before the user submitted anything or the app ran its own validation —
the framework silently overriding an element's appearance based on
browser-internal state the consumer never opted into. This is the same
category of surprise as the .sf-card/.sf-btn auto-shrink rule removed
previously, so it is removed too.

- Delete the input/select/textarea :user-invalid / :user-valid rules
  (optional/forms.css). The token itself, its resting/focus/autofill
  consumers, and the explicit .sf-is-invalid / .sf-is-valid / .sf-is-warning /
  .sf-is-info / .sf-is-danger state classes (core/states.css) are unchanged —
  they remain the one way to colour a field's validation state.
- No token-registry/docs/demos regeneration needed: --sf-field-border-color was
  never declared as a :root token (pure var()-fallback hook), so its removal
  from the automatic trigger doesn't change the public token surface.
- Update stale test comments referencing the removed mechanism
  (tests/forms.spec.js). No new Playwright assertion was added for the removal
  itself: :user-invalid does not reliably match via synthetic focus/blur in
  headless automation (verified — it didn't even match before this change), so
  a test asserting its absence would be flaky by construction. The explicit
  path already has solid dedicated coverage in tests/states-full.spec.js.
- Document the break in docs/migration.md and CHANGELOG.md.

Verified: full node --test unit suite (109/109) and all 10 CSS gates pass with
no registry drift, confirming the token surface is untouched.

BREAKING CHANGE: form fields no longer auto-colour their border on native
:user-invalid / :user-valid. Add .sf-is-invalid / .sf-is-valid explicitly when
your code determines a field's validation state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01GnwqYHgHNAWPbsUJWUeY1w
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fe4d2849-6594-4539-9748-e4ef1c3ddd3d

📥 Commits

Reviewing files that changed from the base of the PR and between d4cf4d6 and b259ce0.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • docs/migration.md
  • optional/forms.css
  • tests/forms.spec.js
💤 Files with no reviewable changes (1)
  • optional/forms.css

📝 Walkthrough

Walkthrough

The forms stylesheet no longer auto-colours fields from native :user-invalid and :user-valid states. Explicit .sf-is-* classes remain the validation colouring mechanism, with changelog, migration documentation, and test comments updated accordingly.

Changes

Form validation styling

Layer / File(s) Summary
Explicit validation colour contract
optional/forms.css, CHANGELOG.md, docs/migration.md, tests/forms.spec.js
Removed native pseudo-class border-colour overrides, documented the breaking change, and clarified that explicit validation state classes continue to drive --sf-field-border-color.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

Suggested labels: codex

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the breaking change to stop native validation states from auto-colouring form fields.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/button-size-visibility-issue-40xlwy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the codex label Jul 12, 2026
@greptile-apps

greptile-apps Bot commented Jul 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR removes the :user-invalid / :user-valid auto-coloring rules from optional/forms.css, eliminating a "surprise" UX pattern where constrained fields would show a red or green border the instant they were touched — before any app-level validation ran. After this change, --sf-field-border-color is only set via the explicit .sf-is-invalid / .sf-is-valid (and related) state classes in core/states.css.

  • optional/forms.css: 13 lines of :user-invalid/:user-valid rules deleted; the --sf-field-border-color token and all its other consumers (resting border, focus, autofill) are untouched.
  • tests/forms.spec.js: Two stale comments updated to point to core/states.css and the explicit state classes; no test logic changed.
  • CHANGELOG.md / docs/migration.md: Breaking change documented with a clear upgrade path using the explicit .sf-is-invalid class and aria-invalid pattern.

Confidence Score: 5/5

Safe to merge — a clean, targeted deletion of 13 CSS lines with no effect on the remaining token surface, consumers, or test logic.

The CSS removal is minimal and surgical: only the two :user-invalid/:user-valid rule blocks are gone, every other use of --sf-field-border-color (resting border, focus, autofill) is untouched. A repo-wide search confirms no stale references to the removed pseudo-classes remain outside the new migration guide and changelog entry. The llm-guide description for --sf-field-border-color remains accurate because the explicit state classes still set it. Test comments are updated to match; no test logic is changed. Breaking-change documentation is complete and accurate.

No files require special attention.

Important Files Changed

Filename Overview
optional/forms.css Removes 13 lines of :user-invalid/:user-valid auto-coloring rules; remaining CSS is unaffected and still consumes --sf-field-border-color via the explicit var() fallback chain
tests/forms.spec.js Updates two inline comments to reference the explicit .sf-is-invalid/.sf-is-valid state classes instead of the removed :user-invalid/:user-valid pseudo-classes; test logic is unchanged
docs/migration.md Adds a clear migration guide section explaining the breaking change, what was removed, what remains, and an upgrade example — accurate and complete
CHANGELOG.md Adds breaking change entry for the forms auto-coloring removal under the Unreleased section, consistent in style with the sibling components entry above it

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User interacts with field] --> B{Validation trigger}

    B -->|BEFORE this PR| C[Browser fires :user-invalid / :user-valid]
    C --> D[forms.css auto-sets --sf-field-border-color to danger / success]
    D --> E[Field border changes immediately on first touch]

    B -->|AFTER this PR| F[Browser fires :user-invalid / :user-valid]
    F --> G[No CSS rule matches — border unchanged]

    B --> H[App adds .sf-is-invalid / .sf-is-valid]
    H --> I[core/states.css sets --sf-field-border-color]
    I --> J[Field border changes on app's own terms]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[User interacts with field] --> B{Validation trigger}

    B -->|BEFORE this PR| C[Browser fires :user-invalid / :user-valid]
    C --> D[forms.css auto-sets --sf-field-border-color to danger / success]
    D --> E[Field border changes immediately on first touch]

    B -->|AFTER this PR| F[Browser fires :user-invalid / :user-valid]
    F --> G[No CSS rule matches — border unchanged]

    B --> H[App adds .sf-is-invalid / .sf-is-valid]
    H --> I[core/states.css sets --sf-field-border-color]
    I --> J[Field border changes on app's own terms]
Loading

Reviews (1): Last reviewed commit: "refactor(forms)!: stop auto-colouring fi..." | Re-trigger Greptile

@jackgranatowski
jackgranatowski merged commit 2f8a2c8 into main Jul 12, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants