RFC: UI component taxonomy for compounds + FormCheckbox composability
Summary
Re-evaluate the UI component taxonomy so compound primitives live in atoms, while opinionated compositions (labels/help text/layout/form affordances) live in molecules. Clarify how this affects Checkbox vs FormCheckbox, Select, and NumericInput, and decide whether to introduce a more composable FormCheckbox pattern to support N1’s interactive checkbox UI.
Motivation
Current layering mixes compound primitives and composed components across atoms/ and molecules/, which adds confusion and extra nesting when integrating form wrappers or templates. This is especially visible in the checkbox stack: Checkbox is a plain input (atom), while FormCheckbox is a compound, opinionated component (molecule). For N1, we expect more interactive checkbox UI that may require custom composition beyond what FormCheckbox allows today.
Current Implementation (Investigation)
Atoms
atoms/checkbox.tsx: plain <input type="checkbox"> with styling; no label/help text. (Storybook: Atoms/Checkbox)
atoms/numeric-input.tsx: compound pattern (Control/Input/Triggers/etc). (Storybook: Atoms/NumericInput)
Molecules
molecules/form-checkbox.tsx: compound + layout + label + help text + Zag.js state handling; wraps input inside a <label> and renders help text below. (Storybook: Molecules/FormCheckbox)
molecules/select.tsx: compound pattern but includes Label, StatusText, and layout. (Storybook: Molecules/Select)
molecules/form-input.tsx, form-textarea.tsx, form-numeric-input.tsx: composed, labeled form components with help text.
Templates
templates/comprehensive-form.stories.tsx mixes atoms + molecules:
- Uses
FormInput, FormTextarea, FormNumericInput, FormCheckbox (molecules)
- Uses
Select compound directly + Select.StatusText (manual help text)
- Uses
NumericInput compound via FormNumericInput
Storybook structure
- Atoms:
Checkbox, NumericInput, etc.
- Molecules:
FormCheckbox, Select, FormInput, FormTextarea, FormNumericInput
- Templates:
Comprehensive Form showcases mixed layering
Problem Statement
- The compound pattern is not consistently placed in atoms vs molecules.
FormCheckbox is opinionated and not easily composable for custom N1 UI (label placement, additional interactive content, etc.).
- Templates and wrappers sometimes need to wrap compounds inside molecules, which adds a redundant layer.
Goals
- Clear taxonomy: primitive compounds in atoms; composed/form-ready in molecules.
- Preserve “pure” compound primitives (no auto layout/help text at the primitive level).
- Improve composability for checkbox scenarios (especially N1).
- Minimize churn in consumer apps and storybook.
Non-Goals
- Changing visual design or token system.
- Migrating all components immediately.
- Deciding final structure of templates in this RFC.
Proposal (Options)
Option A — Keep as-is, document taxonomy
- Pros: minimal change
- Cons: keeps inconsistency, doesn’t solve FormCheckbox composability
Option B — Split compound primitives from composed wrappers
Introduce primitive compounds in atoms and keep composed UI in molecules.
- Example:
CheckboxPrimitive (or CheckboxRoot/Control/Indicator/Label in atoms)
FormCheckbox becomes a thin composition that uses the primitive
SelectPrimitive in atoms (compound, no help text/layout), Select in molecules as composed version
NumericInput stays atom (already primitive compound)
- Pros: clear separation, better composability
- Cons: adds new exports/migration work; storybook updates
Option C — Keep primitives pure but add composition subcomponents
- Keep
FormCheckbox but add optional subcomponents or slots to override layout
- Pros: smaller change
- Cons: may still be rigid; adds complexity to one component
Impact / Migration Notes
- Storybook: update categories if primitives move (e.g.,
Select to atoms or split SelectPrimitive vs Select).
- Templates: adjust imports to new primitives or composed wrappers.
- App usage: ensure old exports remain (deprecate with a path + migration guide).
Risks
- API churn for existing consumers.
- Confusion if both primitive and composed versions are exported without clear naming.
- Scope creep if we try to refactor multiple compounds at once.
Open Questions
- Should we introduce explicit
*Primitive naming, or move/rename existing components?
- For checkbox: do we want a fully composable compound (Root/Control/Indicator/Label/HelpText) in atoms and keep
FormCheckbox as a recipe?
- Should
Select remain in molecules (due to layout + StatusText), with a new SelectPrimitive in atoms?
- Which components count as “primitive compounds” vs “composed molecules” in our design system?
Suggested Next Steps
- Decide taxonomy criteria (what makes a component “primitive” vs “composed”).
- Decide checkbox composition strategy (primitive compound + wrapper vs enriched FormCheckbox).
- If approved, create a phased migration plan + storybook updates.
Stakeholders: @KaiUweCZE, @BleedingDev
RFC: UI component taxonomy for compounds + FormCheckbox composability
Summary
Re-evaluate the UI component taxonomy so compound primitives live in atoms, while opinionated compositions (labels/help text/layout/form affordances) live in molecules. Clarify how this affects
CheckboxvsFormCheckbox,Select, andNumericInput, and decide whether to introduce a more composable FormCheckbox pattern to support N1’s interactive checkbox UI.Motivation
Current layering mixes compound primitives and composed components across
atoms/andmolecules/, which adds confusion and extra nesting when integrating form wrappers or templates. This is especially visible in the checkbox stack:Checkboxis a plain input (atom), whileFormCheckboxis a compound, opinionated component (molecule). For N1, we expect more interactive checkbox UI that may require custom composition beyond whatFormCheckboxallows today.Current Implementation (Investigation)
Atoms
atoms/checkbox.tsx: plain<input type="checkbox">with styling; no label/help text. (Storybook:Atoms/Checkbox)atoms/numeric-input.tsx: compound pattern (Control/Input/Triggers/etc). (Storybook:Atoms/NumericInput)Molecules
molecules/form-checkbox.tsx: compound + layout + label + help text + Zag.js state handling; wraps input inside a<label>and renders help text below. (Storybook:Molecules/FormCheckbox)molecules/select.tsx: compound pattern but includesLabel,StatusText, and layout. (Storybook:Molecules/Select)molecules/form-input.tsx,form-textarea.tsx,form-numeric-input.tsx: composed, labeled form components with help text.Templates
templates/comprehensive-form.stories.tsxmixes atoms + molecules:FormInput,FormTextarea,FormNumericInput,FormCheckbox(molecules)Selectcompound directly +Select.StatusText(manual help text)NumericInputcompound viaFormNumericInputStorybook structure
Checkbox,NumericInput, etc.FormCheckbox,Select,FormInput,FormTextarea,FormNumericInputComprehensive Formshowcases mixed layeringProblem Statement
FormCheckboxis opinionated and not easily composable for custom N1 UI (label placement, additional interactive content, etc.).Goals
Non-Goals
Proposal (Options)
Option A — Keep as-is, document taxonomy
Option B — Split compound primitives from composed wrappers
Introduce primitive compounds in atoms and keep composed UI in molecules.
CheckboxPrimitive(orCheckboxRoot/Control/Indicator/Labelin atoms)FormCheckboxbecomes a thin composition that uses the primitiveSelectPrimitivein atoms (compound, no help text/layout),Selectin molecules as composed versionNumericInputstays atom (already primitive compound)Option C — Keep primitives pure but add composition subcomponents
FormCheckboxbut add optional subcomponents or slots to override layoutImpact / Migration Notes
Selectto atoms or splitSelectPrimitivevsSelect).Risks
Open Questions
*Primitivenaming, or move/rename existing components?FormCheckboxas a recipe?Selectremain in molecules (due to layout + StatusText), with a newSelectPrimitivein atoms?Suggested Next Steps
Stakeholders: @KaiUweCZE, @BleedingDev