Motivation
The adapter architecture (ADR 0046) was validated with the USWDS adapter, but USWDS represents the prescriptive end of the design-system spectrum. Formspec needs to prove the behavior/adapter split works equally well for utility-first CSS frameworks. A Tailwind adapter validates that axis while also serving as the most accessible reference implementation for custom adapter authors — simpler DOM patterns, transparent class strategy, no design-system-specific knowledge required.
See ADR 0049 (thoughts/adr/0049-tailwind-css-adapter.md) for the full decision record.
Scope
Adapter (packages/formspec-adapters/src/tailwind/)
Implement all 15 component types using Tailwind utility classes on semantic HTML:
| Category |
Components |
| Text inputs |
TextInput, NumberInput, MoneyInput |
| Choice inputs |
RadioGroup, CheckboxGroup, Select |
| Toggle inputs |
Checkbox, Toggle |
| Specialized |
DatePicker, Slider, Rating, FileUpload, Signature |
| Interactive |
Wizard, Tabs |
Structure mirrors the USWDS adapter 1:1:
index.ts — RenderAdapter barrel export
shared.ts — createTailwindFieldDOM(), error helper
- One file per component type
integration-css.ts — Minimal/empty (validates ADR 0047)
CSS class cascade conflict resolution
Two complementary mechanisms:
cssClassReplace (spec-level, framework-agnostic) — new PresentationBlock property that replaces lower-cascade classes by utility prefix during mergeBlocks()
classStrategy: "tailwind-merge" (runtime, opt-in) — ThemeDocument-level option that runs resolved class lists through tailwind-merge as post-processing. Injected via setTailwindMerge() to avoid hard dependency.
Example app (examples/tailwind-demo/)
Vite app with @tailwindcss/vite plugin, definition, and theme demonstrating the adapter end-to-end.
Package export
formspec-adapters exports both uswdsAdapter and tailwindAdapter from its barrel.
Acceptance Criteria
References
Motivation
The adapter architecture (ADR 0046) was validated with the USWDS adapter, but USWDS represents the prescriptive end of the design-system spectrum. Formspec needs to prove the behavior/adapter split works equally well for utility-first CSS frameworks. A Tailwind adapter validates that axis while also serving as the most accessible reference implementation for custom adapter authors — simpler DOM patterns, transparent class strategy, no design-system-specific knowledge required.
See ADR 0049 (
thoughts/adr/0049-tailwind-css-adapter.md) for the full decision record.Scope
Adapter (
packages/formspec-adapters/src/tailwind/)Implement all 15 component types using Tailwind utility classes on semantic HTML:
Structure mirrors the USWDS adapter 1:1:
index.ts— RenderAdapter barrel exportshared.ts—createTailwindFieldDOM(), error helperintegration-css.ts— Minimal/empty (validates ADR 0047)CSS class cascade conflict resolution
Two complementary mechanisms:
cssClassReplace(spec-level, framework-agnostic) — newPresentationBlockproperty that replaces lower-cascade classes by utility prefix duringmergeBlocks()classStrategy: "tailwind-merge"(runtime, opt-in) —ThemeDocument-level option that runs resolved class lists throughtailwind-mergeas post-processing. Injected viasetTailwindMerge()to avoid hard dependency.Example app (
examples/tailwind-demo/)Vite app with
@tailwindcss/viteplugin, definition, and theme demonstrating the adapter end-to-end.Package export
formspec-adaptersexports bothuswdsAdapterandtailwindAdapterfrom its barrel.Acceptance Criteria
FieldRefscontract compliancecssClassReplaceworks in theme cascade mergingclassStrategy: "tailwind-merge"resolves utility conflicts when opt-in is configuredexamples/tailwind-demo/renders a functional form with Tailwind stylingReferences
thoughts/adr/0049-tailwind-css-adapter.md