Example#
Usage#
QuoteForm is the site's lead funnel: name, email, the current website, what they need, the budget and a message, with the honeypot and timestamp built in. Pass action (a URL or a server action) and the tiers; everything else has a literal default.
- Intents.
quote(the full form),product(name, email, message) andrelease-note(name, email and an optional message, "Ask for the note"). - Tiers and budgets print as toggle keys;
appTierIdhides the budget for the tier that does not need one.defaultspre-fill from the query, such as?tier=. - States. Sending keeps the fields editable; sent replaces the form with a success plate and moves focus to it; a failure shows the error above Send with "Email it instead", a mailto link built from what was typed.
- In a drawer the Send row is the drawer's sticky footer.
Anatomy#
- Name and emailSide by side from the sm width.
.mk-quote-form-pair - Tier keysWhat do you need, as toggle keys.
.mk-radio-group - Send rowThe action key, the view's one action.
.mk-quote-form-send - FallbackThe email address, always visible.
.mk-quote-form-fallback
Volume#
Reading layer: identical at every volume, so the most important form on the site never gets louder than its labels.
Quiet
Studio
Press
Rave
Content#
- Every label, hint and message is literal and kind: "Add your email, so I can reply."
- The fallback email is always on the page: "Prefer email? Write to studio@example.com".
- The success plate says what happens next and when.
Do and don't#
Do
Don't
A quote form with twelve fields, three of them about the company's founding year.
Accessibility#
- Every field is labelled; required ones are not marked, optional ones are.
- Sending keeps the fields editable; success replaces the form with a plate whose title takes focus and is announced politely.
- A failed send keeps every value and offers "Email it instead", a mailto link built from the fields.
| Keys | What happens |
|---|---|
| Tab | Name, email, website, the tier and budget groups, message, Send, the email fallback |
| Arrow keys in a group | Pick a tier or a budget |
| Enter | Sends; a failed check moves focus to the first field to fix |
The colour pairs it prints, with their contrast on both papers:
| Text | Surface | Role | Bone | Night |
|---|---|---|---|---|
--mk-text | --mk-surface | ui | not in this look | not in this look |
--mk-text-2 | --mk-surface | ui | not in this look | not in this look |
--mk-link | --mk-surface | ui | not in this look | not in this look |
--mk-on-action | --mk-action | control-sm | 14.44:1 · Lc 89.2, passes | 14.76:1 · Lc 89.4, passes |
Props#
| Prop | Type | Default | Description |
|---|---|---|---|
action required | string | ((data: FormData) => Promise<QuoteFormResult>) | none | |
intent | "product" | "quote" | "release-note" | quote | The site's three variants (default `quote`). |
tiers | readonly { readonly id: string; readonly label: string; readonly time?: string; }[] | none | Required for `quote`. |
appTierId | string | none | Hides the budget keys for that tier. |
budgets | readonly string[] | none | |
defaults | Partial<Record<"budget" | "message" | "tier" | "website", string>> | none | From `?tier=`, `?budget=` … |
source | string | none | A hidden input, ≤ 40 characters. |
fallbackEmail required | string | none | Shown always; used for the mailto fallback. |
copy | Partial<QuoteFormCopy> | none | |
success | ReactNode | none | Replaces the default success plate (the comic kit can pass a radio balloon). |
idPrefix | string | none | |
messageCopy | Readonly<Record<string, { readonly label?: string; readonly placeholder?: string; }>> | none | The message field's label and placeholder per tier id (20 §8: they follow the tier); falls back to `copy`. |
stickySend | boolean | none | Keeps the Send row in reach while a scrolling parent (a Drawer body) scrolls. |
Tokens#
System tokens (28)
--mk-border--mk-focus--mk-font-display--mk-font-text--mk-link--mk-radius-plate--mk-rule-soft--mk-space-2--mk-space-3--mk-space-4--mk-space-5--mk-space-6--mk-surface--mk-surface-raised--mk-text--mk-text-2--mk-track-head-card--mk-type-body-leading--mk-type-body-size--mk-type-body-weight--mk-type-field-label-weight--mk-type-head-card-leading--mk-type-head-card-size--mk-type-meta-leading--mk-type-meta-size--mk-type-meta-weight--mk-wd-head-card--mk-wght-head-card
Changelog#
Arrived in 0.1.0.
- The reading layer's controls, overlays, forms and visitor prefs (P2-T1 to P2-T5): every Button is the comic hit key, with IconButton, Link and Tooltip; Input, Textarea, Select, Checkbox and CheckboxGroup, Radio and RadioGroup, Switch, Slider with the ruler, RulerReadout and SegmentedControl; Dialog, Drawer, NavSheet, Tabs, Disclosure, Faq and faqJsonLd on Base UI 1.9 where the platform falls short; Field, Fieldset, Form with the site's inline validation, ErrorSummary, Callout, Honeypot, FormTimestamp and QuoteForm; PrefsScript under 1 KB, usePref and setPref with cross-tab sync, ThemeToggle and the visitor VolumeDial; VisuallyHidden.