# Go Backend Design System Design

**Date:** 2026-05-09

**Goal**

Create a long-term design system for `go-backend` that fits `go + templ + htmx`, uses Tailwind as the styling foundation, exposes reusable `templ` components as the real UI API, and generates a static catalog site outside the app.

**Scope**

- Keep Tailwind as the styling engine for the Go app.
- Create a repo-owned `templ` UI layer for shared primitives and patterns.
- Define shared design tokens for color, typography, spacing, radius, shadows, and motion.
- Build a first static catalog generator in Go that renders component docs and previews to a generated folder outside the app.
- Document and enforce shared component usage rules.
- Migrate `/tablos` to the new shared component layer as the first real consumer.

**Out of Scope**

- A new React or separate frontend stack for documentation.
- A live in-app `/design-system` route.
- A large third-party component library as the primary abstraction layer.
- Full app-wide migration in the first milestone.
- Visual snapshot tooling in the first milestone.

**Why This Approach**

The current stack is server-rendered and HTML-first. `templ` and `htmx` work best when components are also HTML-first and can be expressed as small reusable render units with explicit variants. Tailwind should remain the utility and token compiler, but the design system itself should be owned by the repo through reusable `templ` components, not by page-specific class strings.

This keeps:

- no frontend framework dependency
- no client-side component runtime
- strong reuse in server-rendered pages
- low drift between documentation and production code

**Architecture**

The design system should have two outputs that share a single source of truth:

1. `go-backend/internal/web/ui/`
   This contains the real reusable UI implementation used by the app.

2. a generated static catalog outside the app, for example under `docs/design-system/`
   This contains reference pages, previews, and usage snippets for the same components.

The catalog must be generated from the same `templ` component implementations and example fixtures used by the app. It must not be maintained as a parallel manual site.

**Design System Layers**

The system should be built in three layers.

**1. Tokens**

Tokens define the visual language and are the only place where raw visual constants should be normalized.

Initial token groups:

- colors
  - `surface`
  - `surface-2`
  - `text`
  - `text-muted`
  - `primary`
  - `danger`
  - `success`
  - `warning`
  - `border`
- typography
  - font families
  - type scale
  - font weights
  - line heights
- spacing
  - shared spacing scale
- radius
  - `sm`, `md`, `lg`, `xl`
- shadows
  - subtle through elevated
- motion
  - transition durations and easing

Tokens should be exposed in Tailwind and optionally mirrored as semantic CSS custom properties where useful.

**2. Primitives**

These are the first reusable building blocks.

Initial component set:

- `Button`
- `IconButton`
- `Badge`
- `Input`
- `Textarea`
- `FormField`
- `Card`
- `Modal`
- `Table`
- `EmptyState`

These primitives should be intentionally small and opinionated. They should not accept arbitrary class injection as their primary API. Variants and sizes should be semantic and explicit.

**3. Patterns**

Patterns compose primitives into repeatable interaction units for app features.

Initial patterns:

- HTMX submit button states
- destructive action buttons
- filter/toolbar rows
- modal forms
- list/grid layout shells
- empty results blocks

Patterns should exist only when repeated behavior or structure justifies them.

**File Structure**

Suggested structure:

- `go-backend/internal/web/ui/`
  - `button.templ`
  - `icon_button.templ`
  - `badge.templ`
  - `input.templ`
  - `textarea.templ`
  - `form_field.templ`
  - `card.templ`
  - `modal.templ`
  - `table.templ`
  - `empty_state.templ`
  - `tokens.go`
  - `variants.go`
- `go-backend/internal/web/ui/catalog/`
  - component metadata
  - example fixtures
  - page definitions
- `go-backend/cmd/designsystem/`
  - static catalog generator entrypoint
- `docs/design-system/`
  - generated output

The existing `internal/web/views/` package should consume `ui` components rather than own repeated button, modal, badge, and table markup.

**Component API Rules**

Components should follow these rules from day one:

- page templates should compose shared components, not invent new shared-looking markup
- variants should be semantic:
  - `primary`
  - `secondary`
  - `ghost`
  - `danger`
  - `success`
- sizes should be limited:
  - `sm`
  - `md`
  - `lg`
- destructive actions should always use shared destructive components or variants
- icon-only actions should use shared `IconButton`
- modal shells should never be duplicated in feature templates
- all components should permit HTMX attributes to pass through cleanly

An escape hatch may exist for rare layout needs, but it should not become the default authoring pattern.

**Catalog Generator**

The catalog should be generated by a small Go command, not a separate frontend.

Suggested flow:

1. Register catalog pages and examples in Go.
2. Render them through `templ`.
3. Write static HTML files into `docs/design-system/`.
4. Include generated navigation and shared page chrome.

The generator should output at least:

- `index.html`
- `tokens.html`
- one page per component
- optional `patterns.html`

Each component page should include:

- title and purpose
- variant list
- size list
- one or more rendered previews
- usage snippet
- guidance notes when needed

**Documentation Content**

The catalog should document both visual and usage contracts.

Minimum documentation sections:

- overview
- tokens
- primitives
- patterns
- naming rules
- usage rules
- migration guidance

The catalog should be useful both for implementation and visual review. It does not need to be visually elaborate in the first milestone, but it must be clear and stable.

**Migration Strategy**

The first migration target should be `/tablos`, because it already exercises:

- primary buttons
- icon buttons
- badges
- cards
- tables
- filters/toolbars
- modal forms
- destructive actions

Migration order:

1. create tokens and the `ui` package structure
2. implement `Button`, `IconButton`, `Badge`, `Input`, `FormField`, `Modal`, `Card`, `Table`
3. build the static catalog generator
4. generate initial catalog pages
5. migrate `/tablos` to use the shared primitives
6. add tests that assert the shared components are being used in key flows

After `/tablos`, the next likely consumers are overview and auth pages.

**Testing Strategy**

The design system should be tested at three levels.

**1. Component Rendering Tests**

Focused Go tests should cover:

- variant rendering
- size rendering
- destructive action markup
- icon button structure
- modal shell structure
- table structure
- critical class contracts where shared styling depends on them

**2. Catalog Generator Tests**

The generator should be tested for:

- expected file output
- component page registration
- rendered preview presence
- snippet rendering

**3. Consumer Migration Tests**

Feature tests should prove key pages use the shared system where expected.

For `/tablos`, tests should assert:

- shared action button usage
- shared table/list structure
- shared modal shell usage
- destructive action consistency

**Delivery Rules**

To prevent drift, the following rules should apply once the first primitives exist:

- no new shared-looking button markup outside the `ui` package
- no duplicated delete button implementations
- no duplicated modal shell implementations
- no duplicated badge status implementations
- no page-level component variants without first checking whether the shared primitive should absorb them

These rules are part of the design system, not optional style advice.

**Success Criteria**

This effort is successful when:

- the Go app has a reusable `templ` UI layer
- Tailwind remains the utility foundation, not the primary abstraction
- the static catalog is generated from the same component code as production
- `/tablos` uses shared primitives instead of ad hoc repeated markup
- new UI work has a clear place to live and a consistent API to build on

**Recommendation**

Use Tailwind as the token and utility foundation, but make `templ` components the real design-system API. Generate a static catalog from those same components with a small Go tool. Start small, keep the primitive set opinionated, and use `/tablos` as the first proving ground before broader migration.
