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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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