@designdaddy/form
v0.1.1
Published
Design Daddy form — attribute-driven fields, validation, and layout
Downloads
30
Maintainers
Readme
@designdaddy/form
Attribute-driven React form. Pass field config via props — the package ships no labels, options, or sample data.
Styles are built in — import Form only. No theme package and no separate CSS import required. The form wraps itself in .fds-root with its own tokens. Theme it with theme / className / style props (same idea as @designdaddy/datagrid).
109 unique field types (+ aliases), documented by category in the docs UI.
Table of contents
- Install
- Quick start
- What to use (Form vs pieces)
- Field types by category
- Field attributes
- Styled controls
- Form props by feature
- Steps, review, and draft
- Styling
- Exports
- Local pack / publish
- Run the docs UI
Install
npm install @designdaddy/formPeer dependency: React 17+ (18 / 19 supported).
In this monorepo:
cd designdaddy
npm installQuick start
import { Form } from "@designdaddy/form";
const fields = [
{ id: "email", type: "email", label: "Email", required: true, autoComplete: "email" },
{ id: "password", type: "password", label: "Password", required: true, strengthIndicator: true },
{
type: "section",
id: "prefs",
title: "Preferences",
columns: 2,
fields: [
{
id: "role",
type: "select",
label: "Role",
options: [
{ value: "designer", label: "Designer" },
{ value: "developer", label: "Developer" },
],
},
],
},
];
export default function SignupForm() {
return (
<Form
fields={fields}
className="my-form"
theme={{ primary: "#0f766e", radius: "10px" }}
submitLabel="Save"
resetLabel="Reset"
onSubmit={(values) => console.log(values)}
/>
);
}No extra CSS import — styles load with Form.
What to use (Form vs pieces)
| Need | Use |
|------|-----|
| Full form (fields + validation + actions + theme) | Form |
| Custom layout / wizard chrome | FormShell + FormSection + FormField |
| Step indicator only | FormSteps |
| Button row only | FormActions |
| Map a type → control | FieldRenderer / FIELD_REGISTRY |
| Validation / theme helpers (no UI) | validateFields, buildInitialValues, themeToCssVars, … |
Field types by category
Use type on each field. Aliases: telephone→tel, datetime→datetime-local, zip→postal, swift→ifsc.
| Category | Count | Types |
|----------|------:|-------|
| Native / HTML | 33 | text, password, email, search, tel, url, number, range, date, time, datetime-local, month, week, color, checkbox, radio, file, hidden, submit, reset, button, image, textarea, select, multiselect, optgroup, datalist, postal, username, progress, meter, output |
| Selection | 14 | toggle, searchable-select, autocomplete, combobox, dependent-select, tree-select, dual-list, transfer-list, sortable-list, card-select, image-select, icon-select, lookup, mention |
| Media | 10 | signature, image-upload, avatar-upload, camera, audio-recorder, audio-upload, video-recorder, video-upload, qr-scanner, barcode-scanner |
| Values & design | 19 | otp, pin, tags, chips, rating, currency, percentage, measurement, duration, timezone, language, country, state, city, font, icon, emoji, color-palette, gradient |
| Location | 3 | gps, map-picker, address-search |
| Payments | 6 | credit-card, cvv, expiry, upi, bank-account, ifsc |
| Security / bio | 7 | captcha, security-question, security-answer, fingerprint, face, iris, voice |
| Structure | 2 | repeater, matrix |
| Scheduling | 3 | appointment, calendar, time-slot |
| Editors & AI | 8 | richtext, code, json, sql, markdown, xml-yaml, ai-prompt, ai-autocomplete |
| Consent | 3 | terms, privacy, cookie |
| Layout | 1 | section (groups nested fields; not an input) |
| Total | 109 | |
Docs split each category into pages of up to 5 types, each with demo + props + styling.
Field attributes
| Attribute | Purpose |
|-----------|---------|
| id | Value key (required for inputs) |
| type | Control type |
| label | Visible label (you supply it) |
| required / requiredMessage | Required + * |
| optional / optionalLabel / markOptional | Mark optional |
| placeholder | Empty-state text inside the control |
| help / tooltip | Hint below field / extra tip |
| defaultValue | Initial value |
| disabled / readOnly / hidden | State |
| visibleWhen | { field, equals } or function |
| validate / pattern / min / max / minLength / maxLength | Validation |
| validateOn | blur | change (per field) |
| mask | 0 digit, A letter, * alnum |
| size / fullWidth / width | sm | md | lg, full grid span, CSS width |
| labelPosition | above | beside |
| autoComplete / ariaLabel / name / htmlId | A11y / browser attrs |
| options / optionsMap / getOptions | Choices / cascading |
| characterCounter | With maxLength on textarea |
| className / inputClassName | Extra classes on wrapper / input |
| section fields | title, description, columns, collapsible, fields[] |
Type-specific attributes (e.g. minuteStep, chooseLabel, dependsOn, length) are listed on each field page in the docs.
Styled controls
Several native-looking types render as custom UI. Pass native: true on select or file for the browser default.
Date & time
date, time, datetime-local, month, and week use popover pickers.
{
id: "startsAt",
type: "datetime-local",
label: "Starts at",
placeholder: "Select date & time",
minuteStep: 1,
minYear: 1950,
maxYear: 2035,
todayLabel: "Today",
clearLabel: "Clear",
locale: "en-IN",
}Emoji
{
id: "reaction",
type: "emoji",
label: "Emoji",
ariaLabel: "Choose emoji",
options: ["😀", "😃", "👍", "❤️", "🎉"],
columns: 8,
}File upload
{
id: "resume",
type: "file",
label: "Resume",
accept: ".pdf,.doc",
chooseLabel: "Browse files",
dragHint: "or drag and drop here",
}Form props by feature
Data and values
| Prop | Purpose |
|------|---------|
| fields | Field / section config |
| values / defaultValues | Controlled or uncontrolled |
| onChange / onValuesChange | Value callbacks |
Validation
| Prop | Purpose |
|------|---------|
| errors | Controlled errors map |
| validateOn | blur (default) | change |
| beforeSubmit | async guard; return false to cancel |
Actions
| Prop | Purpose |
|------|---------|
| onSubmit / onReset / onDraftSave | Actions |
| submitLabel / resetLabel / draftLabel | Button copy (omit = hide that button) |
| showActions | Show default action row (default true) |
| renderActions | Custom action slot |
Steps and review
| Prop | Purpose |
|------|---------|
| steps | [{ id, label, fieldIds }] — or fields: ["id", …] shorthand |
| currentStep / onStepChange | Controlled step |
| review / onReview | Review screen before submit |
| confirmMessage | Confirm dialog before submit |
Layout and state
| Prop | Purpose |
|------|---------|
| columns | Default section columns |
| labelPosition | Default label layout (above | beside) |
| loading / disabled / readOnly | Form-wide state |
Styling (like Data Grid)
| Prop | Purpose |
|------|---------|
| className | Extra class on .fds-root |
| style | Inline styles / CSS variables |
| theme | Design tokens → CSS variables (see Styling) |
Form element
| Prop | Purpose |
|------|---------|
| id / name / method / action | Native form attrs |
| autoComplete / noValidate | Browser form behavior (noValidate default true) |
Steps, review, and draft
<Form
fields={allFields}
steps={[
{ id: "account", label: "Account", fieldIds: ["email", "password"] },
{ id: "profile", label: "Profile", fieldIds: ["name", "role"] },
]}
review
draftLabel="Save draft"
submitLabel="Finish"
onDraftSave={console.log}
onSubmit={console.log}
/>fieldIds filters the top-level fields list. You can also pass fields: ["email", "password"] as a shorthand for the same thing.
Styling
Control appearance with props — same pattern as Data Grid.
<Form
className="my-form"
theme={{
primary: "#0f766e",
primaryDark: "#115e59",
primarySoft: "rgba(15, 118, 110, 0.14)",
border: "#99f6e4",
borderStrong: "#5eead4",
text: "#042f2e",
muted: "#0f766e",
background: "#ecfdf5",
surface: "#ccfbf1",
panel: "#ffffff",
radius: "12px",
controlHeight: "2.5rem",
}}
style={{ "--fds-gap": "1.1rem", "--fds-text-md": "13px" }}
fields={fields}
submitLabel="Save"
onSubmit={console.log}
/>/* Import AFTER Form so overrides win */
.my-form .fds-btn--primary {
letter-spacing: 0.02em;
}
.my-form .fds-field__label {
text-transform: uppercase;
font-size: 11px;
}Theme keys → CSS variables
| theme key | CSS variable |
|-----------|----------------|
| primary | --fds-teal |
| primaryDark | --fds-teal-dark |
| primarySoft | --fds-teal-soft |
| border / borderStrong | --fds-border / --fds-border-strong |
| text / muted | --fds-text / --fds-muted |
| danger / success | --fds-danger / --fds-success |
| background / surface / panel | --fds-bg / --fds-surface / --fds-panel |
| radius / gap | --fds-radius / --fds-gap |
| fontFamily | --fds-font-family |
| textXs / textSm / textMd / textBase | Type scale |
| controlHeight | --fds-control-h |
| raw "--fds-…" | passed through |
Or use the helper:
import { themeToCssVars } from "@designdaddy/form";
const style = themeToCssVars({ primary: "#0f766e", radius: "10px" });Dark theme
When the host sets data-theme="dark" on a parent (e.g. docs), .fds-root switches to dark tokens automatically. Your theme prop still overrides those variables.
Limits
- Prefer
themeand CSS variables for colors, radius, and type. - Scope class overrides under your
className(e.g..my-form .fds-control). - Do not remove
.fds-root,.fds-shell, or.fds-fieldfrom the tree. - Import app CSS after
@designdaddy/formso overrides win.
Built-in classes
| Class | What it styles |
|-------|----------------|
| .fds-root | Root + tokens (receives theme / style) |
| .fds-shell / __progress / __body / __actions | Layout slots |
| .fds-section / __fields | Sections |
| .fds-field / __label / __control / __help / __error | Field chrome |
| .fds-control | Inputs / textareas |
| .fds-select / __trigger | Custom select |
| .fds-file-upload | File / media dropzone |
| .fds-picker / .fds-cal | Date / time pickers |
| .fds-btn / --primary / --ghost | Actions |
| .fds-steps | Step indicator |
Exports
import {
Form,
FormField,
FormSection,
FormShell,
FormActions,
FormSteps,
FieldRenderer,
FIELD_REGISTRY,
FIELD_TYPES,
normalizeType,
validateFields,
buildInitialValues,
flattenFields,
themeToCssVars,
} from "@designdaddy/form";Optional CSS path (usually unnecessary):
import "@designdaddy/form/styles.css";Local pack / publish
cd packages/form
npm run build
npm pack
# then in a host app: npm install ../path/to/designdaddy-form-0.1.0.tgzRun the docs UI
From the monorepo root:
npm run dev:docsOpen Form in the sidebar. Every page includes Props and Styling sections.
| Path | Content |
|------|---------|
| /docs/form/overview | Overview, quick start, props summary, styling |
| /docs/form/props | Form props by feature |
| /docs/form/styling | Live theme={{…}} demo + token table |
| /docs/form/attributes | Field attributes |
| /docs/form/native-01 … | Native types (5 per page) |
| /docs/form/selection-01 … | Selection types |
| /docs/form/media-01 … | Media types |
| /docs/form/values-01 … | Values & design |
| /docs/form/location-01 | Location |
| /docs/form/payments-01 … | Payments |
| /docs/form/security-01 … | Security / bio |
| /docs/form/structure-01 | Structure |
| /docs/form/scheduling-01 | Scheduling |
| /docs/form/editors-01 … | Editors & AI |
| /docs/form/consent-01 | Consent |
| /docs/form/layout-01 | section layout |
Hover a sidebar page link to see the types on that page (tooltip).
Folder layout:
packages/form/src/
Form/ # façade (theme / className / style)
FormField/
FormSection/
FormShell/
FormActions/
FormSteps/
fields/ # FieldRenderer + type implementations
utils/ # FIELD_TYPES, validateFields, themeToCssVars, …
styles/form.css