Skip to content

feat(shared): fields v2 — align fields with Toggle + Button system#6118

Open
tsahimatsliah wants to merge 15 commits into
mainfrom
feat/fields-v2-redesign
Open

feat(shared): fields v2 — align fields with Toggle + Button system#6118
tsahimatsliah wants to merge 15 commits into
mainfrom
feat/fields-v2-redesign

Conversation

@tsahimatsliah
Copy link
Copy Markdown
Member

@tsahimatsliah tsahimatsliah commented May 31, 2026

Summary

Brings the fields in line with the redesigned Toggle and the Button system, so fields, buttons and toggles read as one family.

  • New look & states. Replaces the legacy left-edge accent bar with a crisp, even ring for focus / invalid / readonly / password-strength, plus smooth surface transitions that match the toggle's motion. Reskins the shared BaseField primitive in place, so TextField (input), PasswordField, Textarea and SearchField all update across the product automatically. Dropdown is button-based and already follows the button language.
  • Shared size scale. New fieldSizes.ts exposes a FieldSize enum that mirrors ButtonSize exactly — identical heights, radii, value typography and icon sizes. A field and a button of the same size line up pixel-for-pixel in a shared strip, and a "medium" icon is the same glyph size whether it lives in a field or a button.
  • Opt-in, non-breaking. A new fieldSize prop threads through TextField/BaseFieldContainer (icons auto-size to match). When omitted, the legacy fieldType-driven dimensions are preserved, so existing call sites are unchanged.
  • Comparison page. New Storybook story Components/Fields/Field renders the previous vs new fields side-by-side (states, field types, password/search) against a frozen legacy snapshot, plus a dedicated field-vs-button size-alignment section.

Notes

  • size was already taken by the native input attribute, so the new prop is named fieldSize.
  • Touched files are strict-typecheck clean (scripts/typecheck-strict-changed.js) and shared lints clean.

Test plan

  • Storybook → Components/Fields/Field → review Comparison in light & dark; hover / focus / type each field.
  • Confirm the size-alignment rows show field height/radius/icon matching the adjacent button.
  • Spot-check real forms (auth, profile edit, search) for input/password/search visual regressions.
  • pnpm --filter shared test for TextField / PasswordField / SearchField (15 passing).

Made with Cursor

Preview domain

https://feat-fields-v2-redesign.preview.app.daily.dev

Reskin the shared field primitives to the new design language shared with
the redesigned Toggle and the Button system:

- Swap the legacy left-edge accent bar for a crisp, even ring on focus /
  invalid / readonly / password-strength, with smooth surface transitions
  (matching the toggle's motion). Applied to BaseField so input, password,
  textarea and search all update across the product.
- Add a button-aligned FieldSize scale (fieldSizes.ts) that mirrors
  ButtonSize exactly — same heights, radii, value typography and icon sizes.
  A field and a button of the same `size` now line up pixel-for-pixel when
  they sit together in one strip, including matching "medium" icon sizes.
- Thread an opt-in `fieldSize` prop through TextField / BaseFieldContainer
  (icons are auto-sized to match); legacy `fieldType` sizing is preserved
  when `fieldSize` is omitted, so existing call sites are unchanged.
- Add a Storybook before/after comparison page (Field.stories.tsx) with a
  frozen legacy snapshot, covering states, field types, password/search and
  a field-vs-button size-alignment section.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 31, 2026

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

Project Deployment Actions Updated (UTC)
daily-webapp Ready Ready Preview May 31, 2026 9:38pm
storybook Error Error May 31, 2026 9:38pm

Request Review

…own polish

- Add a faint resting border to every field (BaseField) so fields are
  delineated from the page, matching the Subtle/Button v2 language.
- Introduce FieldVariant (Filled / Outline) on TextField + Textarea:
  Filled sits on the floated surface, Outline is transparent and defined
  by its border with a faint hover fill.
- Restore the password strength left-edge indicator + colour-graded
  gradient on the new field (per design preference).
- Polish the dropdown popover: rounded-14 card, inset items, taller rows
  and smooth highlight transition.
- Expand the comparison story with Variants and Dropdown & textarea
  sections (wrapped in a QueryClientProvider for the dropdown).

Co-authored-by: Cursor <cursoragent@cursor.com>
- Invalid fields now use a red 1px border (same mechanism as the focus
  border) instead of an inset ring, and stay red while focused — matching
  the design and reading as one family with focus/read-only borders.
- Fix declarative validity: an externally-controlled `valid` prop is now
  authoritative on first render, so a field rendered already-invalid shows
  its error state immediately instead of waiting for interaction.
- Restore the disabled dim on fields (BaseFieldContainer + SearchField).
- Storybook: add comprehensive per-field state coverage — TextField
  states, field types, variants, sizes; PasswordField strength ladder;
  SearchField states/sizes/variants; Textarea states; Dropdown states/sizes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Consolidate the field redesign into one "Fields — before & after" page so
the team can play with old vs new side by side:

- Add a "What changed" summary and before/after rows for text input states,
  field types, password & search, textarea and dropdown (trigger + popover).
- Add a LegacyTextarea snapshot of the previous multi-line look for the
  textarea comparison.
- Keep the new-only sections (background variants, size alignment with
  buttons) on the same page.
- Fix stale "even ring" copy now that focus uses a 1px border.

Co-authored-by: Cursor <cursoragent@cursor.com>
On light theme especially, the faint grey fill plus low-contrast tertiary
content made an idle field look almost identical to its dimmed disabled
state. Brighten and define the resting state in both themes:

- Default (empty, editable) icon, value text and placeholder now use
  text-secondary instead of tertiary, so the field reads as active and
  clearly differs from the opacity-dimmed disabled state.
- Resting border steps up from border-subtlest-tertiary to
  border-subtlest-secondary so every field is delineated as a real input
  box rather than a flat grey block.

Co-authored-by: Cursor <cursoragent@cursor.com>
Align the internals of every field with the button system and clean up the
ad-hoc spacing/colors:

- Icons now scale with the field height (one rung below the button glyph scale
  so they stay balanced inside an input) and auto-size even without an explicit
  fieldSize.
- Icon-to-value spacing uses the button gap scale (fieldSizeToGap) instead of a
  flat 8px margin, applied as a row gap so every adornment is evenly spaced.
- Char counter drops back to a muted text-quaternary (was darkened as a side
  effect of the placeholder colour change) since it's metadata, not a value.
- Dropdown trigger text now uses text-secondary; the chevron gets an explicit
  size, a muted text-quaternary rest colour that brightens to primary on hover,
  and the no-op group-hover override is removed.
- SearchField adopts the same icon size / gap scale and the defined secondary
  resting border.

Co-authored-by: Cursor <cursoragent@cursor.com>
The Button's built-in Large horizontal padding (px-6) was overriding the
dropdown's intended px-3, leaving the chevron floating 24px from the right
edge. Force the inset with !pl-4/!pr-2.5 so the value aligns with the other
fields' 16px text inset and the chevron sits tight to the edge, and drop the
chevron from 24px to a refined 20px (shrink-0 keeps it from squishing).

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The search field's `!bg-background-default` blocks the base `.field:hover`
background, so hovering produced no feedback. Give it its own hover —
brighten the border to subtlest-primary and tint the surface — scoped to
`:not(.focused)` so it never overrides the focus ring while active.

Co-authored-by: Cursor <cursoragent@cursor.com>
The resting border was bumped to subtlest-secondary (40%) earlier, which read
as a dominant outline. Drop it to the exact Button v2 Float hairline — 15% of
border-subtlest-primary — centralized on `.field` so every field (and the
search field, which previously hard-set its own border) shares one faint,
consistent weight. Search hover now steps to 40% instead of full primary, and
the story's dropdown trigger uses the same hairline.

Co-authored-by: Cursor <cursoragent@cursor.com>
The textarea reserved a tight top inset (pt-1/pt-2) for the floating "title
inside" caption, but that caption only exists for a primary field with a
label. Without it the top sat at ~8px while the sides were 16px. Now the inner
label only renders when a label exists, and any field without that caption pads
every side equally (py-4). Adds Storybook coverage for both layouts.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add a size-aware `fieldSizeToIconLeftPadding` that mirrors the button's
icon-side reduction (IconSidePaddingV2) so a leading glyph hugs the edge
tighter than the value side and scales with field size. Replaces the flat
pl-3 in TextField and reduces the SearchField leading-icon inset to match.

Co-authored-by: Cursor <cursoragent@cursor.com>
…cale

BaseFieldContainer and SearchField hardcoded their radii (rounded-14/10,
rounded-12/14) which duplicated fieldSizeToRadius. Route both through the
map so every field's corner radius is the single button-aligned guideline
by size (16/14/12/10/8) with no divergent values. No visual change.

Co-authored-by: Cursor <cursoragent@cursor.com>
The button-aligned icon-side reduction (pl-2 etc.) sat the glyph too close
to the left edge. Restore the flat pl-3 inset on TextField, drop the
SearchField icon-left override, and remove the now-unused
fieldSizeToIconLeftPadding map. Radius consolidation is unaffected.

Co-authored-by: Cursor <cursoragent@cursor.com>
… radius

- useInputFieldFunctions: only surface an external invalid state once the
  field has content, so a pristine empty field with a value-derived
  valid={false} (e.g. valid={!!name}, valid={value.length>0}) no longer
  flashes a red border on mount. valid=true and server/submit errors (which
  carry content) still apply immediately.
- fieldSizes: drop the unused fieldSizeToHorizontalPadding map and the
  getFieldSizeTokens aggregator (only height/typo were consumed); TextField
  now reads the height/typo maps directly.
- TextField.module.css: remove the hardcoded 14px corner on the password
  strength bar; the parent's overflow-hidden clips it to the field's actual
  radius, so it stays correct at any field size.

Co-authored-by: Cursor <cursoragent@cursor.com>
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