Skip to content

RFC: UI component taxonomy for compounds + FormCheckbox composability #295

Description

@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 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

  1. Should we introduce explicit *Primitive naming, or move/rename existing components?
  2. For checkbox: do we want a fully composable compound (Root/Control/Indicator/Label/HelpText) in atoms and keep FormCheckbox as a recipe?
  3. Should Select remain in molecules (due to layout + StatusText), with a new SelectPrimitive in atoms?
  4. 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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions