Component Philosophy

How we build and share UI components across Qpoint projects.

Eight Principles

1. Functional, not decorative

Every component exists because it solves a UI problem. If a component doesn't make someone's workflow better, it doesn't belong in the library. Color is semantic — grape signals interaction, leaf signals success, grey provides structure. We don't add color for visual interest.

2. Composition over configuration

Components use slots for flexible content, not deeply nested prop trees. UxButton has an icon slot, not iconName + iconPosition + iconSize props. Complex behavior is composed by the consuming project, not baked into the shared component.

3. Tailwind-native

Components use Tailwind classes for styling. Scoped CSS is used only for things Tailwind cannot express: pseudo-elements, computed dimensions, and dynamic style bindings. Consumers can extend or override with additional Tailwind classes naturally.

4. Progressive enhancement

Base components are simple. Advanced behavior — analytics tracking, modal help overlays, drag-and-drop — is added by the consuming project through event handlers, wrapper components, and composables. This keeps the shared library lightweight and avoids forcing dependencies on every consumer.

5. Accessibility by default

Shared components ship with proper ARIA attributes, keyboard navigation, and focus management. Modal uses Headless UI because it handles focus trapping, aria-modal, and Escape key correctly. Interactive components support :disabled and aria-disabled.

6. States are explicit

Every interactive component defines and styles all states: default, hover, focus, active, disabled, and loading. No implicit or unstyled states.

7. Replicate first, optimize later

When extracting a component from a sister project into the shared library, match the existing behavior exactly. No refactoring, no API changes. Improvements come in subsequent cycles after the component is stable and running in production from the shared layer.

8. Consume during implementation

The design site is the first consumer of every shared component. Building a component and documenting it are the same act — you instantiate it in a demo, pass props, try variations, and stress-test data extremes before it ships.

The build workflow: Build in the layer → create the demo with all props exposed → add scenario presets (empty, minimal, typical, overflow, error) → verify all states at each scenario → ship only after the demo proves it works.

The regression workflow: Reproduce the production bug in the demo → fix in the layer → verify the fix renders correctly → mark the regression permanently → ship. The demo IS the test.

Design Lineage

These principles aren't arbitrary preferences. They reflect a deeper understanding of how complex systems are designed — ideas rooted in design methodology and validated through decades of practice.

Decomposition is the foundation

Large problems can only be addressed by breaking them into modular sub-systems, each with a functional purpose and clear interface points. Every component in the library is a self-contained module: it has a job (functional, not decorative), connection points (props, slots, events), and internal freedom. Principles 1 and 6 define what the module does and how it signals its condition.

Modularity enables collective work

When multiple people or projects need to build from the same system, they need shared terms that describe the big picture without requiring knowledge of every module's internals. The component library serves this role — a shared vocabulary where UxButton, UxInput, and UxModal are understood terms with known behavior. Composition over configuration keeps interfaces simple so changes inside a module don't ripple across the system.

Design is assemblage, not creation from nothing

The library's components are the notes; compositions are the music. Progressive enhancement reflects this directly. Base components are simple pieces. Advanced behavior is composed by the consuming project, combining pieces into structures that serve their specific needs. The brilliance is in the arrangement, not in the primitives.

Replicate first is problem formulation

Understanding what you have is the first step in any design process. When extracting a component, matching existing behavior exactly is the equivalent of studying the problem space before proposing solutions. Premature optimization is premature problem formulation.

Consume during implementation is continuous validation

The demo isn't documentation that happens after the fact — it's the act of understanding the component through direct use. Passing real data, testing extremes, and reproducing bugs on the design site is problem formulation applied to every component at every stage of its lifecycle. The regression workflow ensures that once a problem is understood, the understanding persists.

The edge of chaos

The component library embodies the productive tension between freedom and structure. The constrained token palette and explicit states provide order. The slot-based composition and progressive enhancement provide freedom. Neither alone produces good work. Together, they create the bounded space where genuine innovation occurs.

For more on these ideas, see the Design Methodology page.

The Unified Pantry

The layer is a single shared pantry for all Qpoint visual components. Generic primitives and domain-specific presentational components all live here. Nuxt tree-shakes aggressively, so unused components cost nothing at runtime. The value is stocking ingredients once so any current or future project can use them without an extraction cycle.

Belongs in the layer

  • Generic primitives: buttons, inputs, toggles, checkboxes, modals, tags, icons
  • Domain-specific UI: security cards, data-metric stats, prose blocks, health dials
  • Documentation tools: DevCanvas, DevFrame, DevLabel

Boundary test: "Does it render UI without side effects beyond its own props/emits?" → layer.

Stays local in consumers

  • Filters that call app-specific composables
  • Pages that fetch from Firestore or external APIs
  • Layouts tied to auth state or navigation guards
  • Components that import from Pinia stores or project-specific services

Boundary test: "Does it have side effects beyond its own props/emits?" → local.

Design Guidelines

Color is semantic

Components accept variant names that map to the brand palette, not arbitrary colors.

VariantColorUse
defaultGrapePrimary actions
strokeGrape outlineSecondary actions
dangerWarn redDestructive actions
successLeafConfirmation

Size is constrained

Size variants use named steps — micro, tiny, small, medium, large — not arbitrary pixel values. Each step has defined dimensions for padding, font size, and height.

Transitions are consistent

Interactive state transitions use duration-200. Content transitions use 500ms ease. Page-level transitions are managed by Nuxt.