Skip to content

fix(theme): give the select trigger a 3:1 border - #342

Merged
alexgrozav merged 1 commit into
mainfrom
select-field-contrast-uxf-380
Oct 19, 2026
Merged

alexgrozav merged 1 commit into
mainfrom
select-field-contrast-uxf-380

Conversation

@alexgrozav

Copy link
Copy Markdown
Contributor

Description

The Select trigger copies the field surface into its own recipe, so it needs the same contrast fix as Input and Textarea. Its gray-200 border is 1.58:1 on white, ghost has no edge at rest, and read-only differs from the editable state only by a 1.09:1 tint. WCAG 1.4.11 asks for 3:1 on a control's boundary.

In useSelectRecipe:

  • solid and soft borders use gray-450 and gray-500 (light, dark and adaptive neutral), with the other one on hover.
  • ghost keeps a transparent fill and side borders, and gets a bottom border in the same colours with square bottom corners.
  • The invalid compound also sets borderBottomColor: "@color.error" in all four slots, so an invalid ghost trigger's bottom edge stays red.
  • Read-only adds a dashed border and keeps its tinted fill.

The lowest ratio is now 3.20:1 (gray-450 on the gray-100 soft fill). A new useSelectRecipeContrast.test.ts reads every border token from the recipe and checks it against the trigger's fill, the read-only fill and the four page backgrounds with culori's wcagContrast. It fails when a border goes back to gray-200.

The Select page gets a new Ghost description, a ## States section with Read-only, a 3:1 line under Accessibility and a corrected FAQ. Storybook gets a ReadOnly story, and the meta declares invalid, readonly and an enum type for variant. Without that type, Storybook drops variant URL args, because the inferred Vue prop type is a union.

Related issue

None.

Type of change

  • 🐞 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (fix or feature that changes existing behavior)
  • 📖 Documentation
  • ♻️ Refactor / internal (no functional change)
  • 🔧 Build / tooling / CI

Checklist

  • My commits follow Conventional Commits with a package scope (e.g. feat(theme): …)
  • I ran pnpm build:nodocs && pnpm lint && pnpm typecheck && pnpm test and everything passes
  • I added a changeset (pnpm changeset) for changes to publishable packages, or this change only touches docs/storybook/app/playground/tests
  • I added or updated tests where relevant
  • I updated documentation where relevant
  • I did not edit generated files (dist/, .styleframe/)
  • My PR targets main and stays focused in scope

Notes / screenshots

What ran: pnpm build:nodocs, pnpm --filter @styleframe/theme test, pnpm typecheck, oxlint and oxfmt on the touched files, pnpm --filter @styleframe/storybook build, and pnpm build:docs && pnpm --filter @styleframe/docs test:build. The root pnpm lint and pnpm test did not run, since the package checks cover this change.

The shared field surface used by Input, Textarea, InputDate and OTP gets the same fix in a separate PR.

🤖 Generated with Claude Code

The Select trigger's `gray-200` border is 1.58:1 on white, its `ghost`
variant has no edge at rest, and its read-only state differs from the
editable one only by a 1.09:1 tint, so none of them meets the 3:1 that
WCAG 1.4.11 asks of a control's boundary.

Move the `solid` and `soft` borders to `gray-450` and `gray-500`, with
the other one on hover. Give `ghost` a bottom border in the same colour
with square bottom corners, set the error bottom colour in the invalid
compound so an invalid ghost trigger stays red, and give read-only a
dashed border. A new contrast test checks every border token against
the trigger's fill, the read-only fill and the four page backgrounds.

Describe the new surface on the Select page, add a Read-only story, and
declare `invalid`, `readonly` and an enum `variant` type in the story
meta, so the controls and URL args reach the story.

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

vercel Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
styleframe-dev Ready Ready Preview Oct 19, 2026 7:33am UTC
styleframe-dev-storybook Ready Ready Preview Oct 19, 2026 7:33am UTC

Request Review

@alexgrozav
alexgrozav marked this pull request as ready for review October 19, 2026 07:53
@alexgrozav
alexgrozav merged commit 0f34e2b into main Oct 19, 2026
14 checks passed
@alexgrozav
alexgrozav deleted the select-field-contrast-uxf-380 branch October 19, 2026 07:53

This branch was successfully deployed

2 active deployments
Preview – styleframe-dev — a71a370f Deployed Oct 19, 2026 by vercel[bot]
Preview – styleframe-dev-storybook — a71a370f Deployed Oct 19, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant