@edux-design/forms
v0.0.13
Published
React form primitives that share a common context so every label, control, and validation message stays connected and accessible. The package powers the `Field`, `Label`, `Input`, `Textarea`, `Checkbox`, `Radio`, `Switch`, and `Feedback` components used a
Readme
@edux-design/forms
React form primitives that share a common context so every label, control, and validation message stays connected and accessible. The package powers the Field, Label, Input, Textarea, Checkbox, Radio, Switch, and Feedback components used across the Edux design system.
- Accessible by default – the
FieldProviderwireshtmlFor,aria-describedby, and live regions automatically. - Composable – mix any primitives inside
<Field>and re-exportField.Feedbackfor colocated messaging. - Design-token aware – components rely on the shared utility
cxhelper plus the Edux tokens for borders, colors, spacing, and focus rings. - Feature complete inputs – variants, start/end icons, clear buttons, and controlled/uncontrolled support ship in every text control.
Installation
npm install @edux-design/forms @edux-design/utils @edux-design/icons
# or
pnpm add @edux-design/forms @edux-design/utils @edux-design/iconsPeer dependencies:
react^19.1.0react-dom^19.1.0
After installing, ensure your build pipeline can consume both ESM and CJS outputs (generated via tsup). The published bundle exposes /dist/index.{js,mjs} plus type declarations.
Quick Start
import { Field, Label, Input, Textarea, Checkbox, Radio } from "@edux-design/forms";
export function SignupForm() {
return (
<form className="flex flex-col gap-8 w-full max-w-md">
<Field>
<Label hint="required" description="We’ll only email account alerts.">
Email address
</Label>
<Input
type="email"
placeholder="[email protected]"
variant="primary"
clearable
/>
<Field.Feedback tone="error">
This email is already registered.
</Field.Feedback>
</Field>
<Field>
<Label hint="optional">Bio</Label>
<Textarea rows={5} placeholder="Tell us about yourself…" />
</Field>
<Field>
<Label>Contact me</Label>
<Radio />
</Field>
<Field>
<Label hint="required">Terms</Label>
<Checkbox aria-label="Agree to the terms and conditions" />
<Field.Feedback tone="corrective">
Agreeing is required before continuing.
</Field.Feedback>
</Field>
</form>
);
}Every Field instance mounts a internal context. Label, Input, Textarea, Radio, and Field.Feedback consume that context so they stay synchronized even if rendered in separate components.
Components
<Field>
- Provides layout (
flex,gap-8) and context wiring. - Accepts any children; typically wraps
Label, a control (Input,Textarea,Radio), and optionalField.Feedback. - Set
orientation="horizontal"when you need inline layouts (e.g., Switch + label) without affecting other form groups. - Use
justify="between"withorientation="horizontal"to align left labels and right controls across a column. - Re-exports
Field.Feedbackfor ergonomic composition.
<Label>
- Consumes the field context and sets
htmlFor. - Supports
hint="required" | "optional"and adescriptionprop rendered beneath the main label. - Pass
hiddento visually hide the label while keeping it accessible.
<Input>
- Extends standard
<input>props and forwards refs. - Variants:
primary,error,corrective,success,inactive. - Optional
startIcon,endIcon, andclearablebutton (uses theCloseicon). - When
clearableis set, clicking the clear button dispatchesonChangewith an empty value, so controlled inputs update seamlessly. - Honors
aria-invalid,aria-disabled, and disables itself for theinactivevariant.
<Textarea>
- Mirrors the Input API while targeting multiline content.
- Supports
rows,startIcon,endIcon,clearable, and the same variants. - Uses
resize-yby default; pass inline styles to opt into both-axis resizing.
<Radio>
- Minimal radio control bound to the surrounding
Fieldcontext. - Implements hover, focus, and checked states with Tailwind utility classes.
- Currently exposes a single style; future work will add “active” token colors (see FIXME in source).
<Checkbox>
- Square control that mirrors native checkbox behavior while layering on design-token driven states (hover, focus, pressed, inactive, and error).
- Supports the same variants as text inputs plus an
indeterminateappearance for tri-state flows. - Automatically shares IDs and
aria-describedbyreferences with the wrappingField, so helper text and validation copy are announced. - Pairs well with
<Label>for field titles; passaria-labelor wrap inline helper text next to the component when you need per-option copy.
<Switch>
- Toggle-style checkbox that maps the native input to Edux track and thumb visuals, complete with iconography that switches between the Close and Check glyphs.
- Variants mirror the checkbox control (
primary,success,corrective,error,inactive) so validation states stay consistent. - Integrates with the
Fieldcontext to automatically syncid, focus management, andaria-describedbystrings built from registered feedback messages. - Disables itself automatically when
variant="inactive"and exposes hover, focus, pressed, and checked states for every other variant.
<Field.Feedback>
- Semantic messaging component with tones:
info,success,warning,error,corrective. - Registers its
idwith the parentField, so any controls automatically receivearia-describedby. tone="error"switches torole="alert"/aria-live="assertive"; other tones stayaria-live="polite".- Includes a circular icon container that colors itself based on the selected tone.
Accessibility Model
- Context-generated IDs –
FieldProviderassigns a uniquelabelHTMLForId; the same ID drives<label htmlFor>and the child control’sid. - Description registry –
Field.Feedbackregisters/unregisters its identifier so inputs build thearia-describedbystring automatically, even if multiple feedback blocks mount. - Live regions – error messages announce immediately via
role="alert", while neutral/success tones fall back to polite announcements. - Focus styling – every interactive element shares the branded
focus:shadow-focusutility, ensuring visible focus outlines without extra work.
To enforce the contract, hooks throw if you render any primitive outside <Field>, making misconfigurations obvious during development.
Theming & Variants
Variants map directly to semantic tokens defined in @edux-design/design-tokens and consumed via Tailwind utility classes:
| Variant | Border / Background intent |
| ------------ | -------------------------------------------------------------- |
| primary | Default border; highlights on hover/focus with primary color. |
| error | Danger border + subtle danger background for immediate errors. |
| corrective | Soft highlight for “nudge” messages without blocking input. |
| success | Success border for confirmed entries. |
| inactive | Muted border/background, disables the control automatically. |
Apply variants per control to reflect validation state. Combine with Field.Feedback to reinforce messaging.
Building & Contributing
pnpm build(ornpm run build) bundles the package viatsupintodist/.pnpm devstartstsupin watch mode.pnpm lintandpnpm check-typeskeep code quality high.
Stories live under src/demos/*.stories.jsx; use them as references for new component states and to manually verify visual behavior.
Roadmap / Known Gaps
- Radio buttons need dedicated “active” token colors (tracked via FIXME in
Radio.jsx). - Feedback icons currently reuse the
Closeglyph for all tones; alternative glyphs per tone may improve clarity. - Clear button animations exist, but the components still rely on manually passing
valuefor controlled usage—add tests to prevent regressions.
Contributions should follow the existing Tailwind utility conventions and rely on the shared tokens/utilities package to stay consistent with the broader system.
