feat(design-library): add form input and navigation components#31087
Conversation
Add 7 Tier 1 design library components ported from the platform repo: - Input + Textarea (single-line and multi-line text fields with labels, icons, error/helper text) - Toggle (on/off switch with label and helper text) - Checkbox (tri-state Radix checkbox with label support) - Radio (generic RadioGroup + Radio with Radix primitives) - Tabs (compound Radix tabs with themed styling) - SegmentControl (pill-shaped segmented control using Button) - Slider (single-thumb and range mode with Radix primitives) Convention compliance applied during port: - Removed all 'use client' directives (Vite SPA) - Converted forwardRef to ref-as-prop (React 19) - Function declarations for all components - data-slot attributes on all root elements - Relative .js imports for NodeNext resolution - Named exports only (no default exports) - Exported variant functions and helper utilities for testing Part of LUM-1543 Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 910b266cd1
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
- Toggle: add data-slot="toggle" to root <span> in no-label code path - Checkbox: add data-slot="checkbox" to root <span> in no-label code path - SegmentControl: implement roving tabindex + arrow/Home/End key handling for the radiogroup role (WAI-ARIA radiogroup pattern) Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
There was a problem hiding this comment.
✦ APPROVE
Value: Eliminates ~98 platform import sites' worth of diff noise from upcoming domain migration PRs by landing the 7 most-used design-library components in one clean PR.
What this does: Ports Input, Textarea, Toggle, Checkbox, RadioGroup/Radio, Tabs, Slider, and SegmentControl from the platform repo into packages/design-library/. All components use Radix UI primitives, CSS variable tokens, and useId()-based a11y wiring.
Responding to bot findings:
Devin flagged data-slot missing from the no-label <span> wrapper in both Checkbox and Toggle. Partially valid:
- Checkbox (line 96) — confirmed. The no-label path returns
<span className={className}>{checkbox}</span>withoutdata-slot="checkbox". The innerCheckboxPrimitive.Rootstill carries the slot, but the outer wrapper is inconsistent with the labeled path's<div data-slot="checkbox">. Worth a quick fix for selector consistency. - Toggle (line 86) — Devin's finding is incorrect here. The no-label span already reads
<span data-slot="toggle" className={className}>. This one is fine as-is.
Codex P2 — SegmentControl keyboard navigation: Real gap. The component advertises role="radiogroup" + role="radio" but only handles onClick — AT users expect arrow keys to move between segments. Since this is a faithful port (not worse than the platform), acceptable to land now and file a follow-up ticket.
My observations:
--ghost-hover in tabs.tsx (non-blocking): TabsTrigger uses hover:bg-[var(--ghost-hover)]. Per the anti-patterns KB, --ghost-hover is exclusively for the ghost Button primitive — nav items, list rows, and tab triggers should use --surface-hover. This is a faithful port of whatever the platform uses, so not blocking this PR, but worth aligning in a follow-up cleanup pass.
Toggle Typography className redundancy (nit): toggle.tsx passes variant="body-medium-default" and then also adds "text-body-medium-default" to className. The other six components (checkbox, radio) pass the variant alone without repeating the class in className. Inconsistent but not broken.
Everything else is clean:
- Radix primitives used correctly throughout —
forceMounton Indicator for explicit icon control, roving tabindex inherited fromRadioGroupPrimitive,aria-labelledby/aria-describedbywired up properly FieldWrapper+fieldVariants(CVA) extracts the shared field chrome cleanly — both Input and Textarea share it without duplicationresolveSegmentSelectionandhandleToggleClickextracted as pure functions — testable without DOMSliderValue = number | [number, number]union +isRangeValue/toValueArray/fromValueArrayhelpers are a nice touch for type-safe range mode- Token discipline consistent: no hex values, no ad-hoc Tailwind colors, all through CSS vars
Merge gate: Devin didn't APPROVE (found issues), Codex didn't 👍. Needs one more human or bot sign-off before merge. Suggest triggering a Devin re-review once the data-slot span is patched in Checkbox.
Reviewed at 910b266c
Vellum Constitution — Yours: design-library components that are token-consistent and Radix-backed let every future migration PR trust its building blocks without second-guessing the primitives.
…class in toggle - TabsTrigger: replace --ghost-hover with --surface-hover (correct semantic token for nav-like elements; --ghost-hover is only defined in dark/velvet) - Toggle: remove duplicate text-body-medium-default className since Typography variant='body-medium-default' already applies it Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
2738ff3
|
All findings from the bot review (at
|
…ckfile - Add .storybook/**/*.tsx to packages/design-library/tsconfig.json so the renamed preview.tsx is included in typechecking (was only .storybook/**/*.ts). - Regenerate apps/web/bun.lock to include new Radix dependencies added by #31087 (react-checkbox, react-radio-group, react-slider, react-tabs). The previous lockfile had a stale snapshot of the design-library deps. Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
…to web app (#31089) * fix(design-library): fix Storybook docs dark mode + add token import to web app - Replace static preview.ts with preview.tsx that provides a custom ThemedDocsContainer. The container reads the current theme global via Storybook's DocsContextProps API and maps it to a matching Storybook theme (light/dark/velvet) created with create() from storybook/theming. This ensures all docs elements (wrapper, preview containers, headings, prop tables, code blocks) automatically pick up the correct theme colors through Storybook's ThemeProvider cascade. - Add @import "@vellum/design-library/tokens.css" to apps/web/src/index.css so the web app gets the design system CSS variables that 12+ component files already reference (var(--surface-base), var(--content-default), etc.). This is the documented integration path from tokens.css and was tracked in LUM-1607. Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai> * fix: add .storybook/*.tsx to tsconfig include + regenerate web app lockfile - Add .storybook/**/*.tsx to packages/design-library/tsconfig.json so the renamed preview.tsx is included in typechecking (was only .storybook/**/*.ts). - Regenerate apps/web/bun.lock to include new Radix dependencies added by #31087 (react-checkbox, react-radio-group, react-slider, react-tabs). The previous lockfile had a stale snapshot of the design-library deps. Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Prompt / plan
Port Tier 1 design library components from the platform repo to reduce diff noise in subsequent domain migration PRs. These 7 components cover 98 import sites across the platform codebase.
Components added:
fieldVariantsCVA.handleToggleClickfor testing.boolean | "indeterminate") with label support.RadioGroup<T>+Radio<T>with Radix primitives.Root/List/Trigger/Panel) with themed styling.Button. ExportsresolveSegmentSelectionfor testing.Convention compliance applied during port:
"use client"directives (Vite SPA, no SSR)forwardRef→ ref as regular prop (React 19)data-slotattributes on all root elements (shadcn/ui v4 convention).jsimports for NodeNext resolutionComponentProps<"element">instead ofHTMLAttributesfor element-specific props + refNew Radix dependencies added (exact versions matching platform):
@radix-ui/react-tabs@1.1.13@radix-ui/react-checkbox@1.3.3@radix-ui/react-radio-group@1.3.8@radix-ui/react-slider@1.3.6Part of LUM-1543
Test plan
bunx tsc --noEmitpasses in bothpackages/design-library/andapps/web/Link to Devin session: https://app.devin.ai/sessions/536ceadb023a4059908b0609b9833bc1
Requested by: @ashleeradka