8.4 KiB
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
templUI 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
/tablosto 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-systemroute. - 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:
-
go-backend/internal/web/ui/This contains the real reusable UI implementation used by the app. -
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
surfacesurface-2texttext-mutedprimarydangersuccesswarningborder
- 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:
ButtonIconButtonBadgeInputTextareaFormFieldCardModalTableEmptyState
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.templicon_button.templbadge.templinput.templtextarea.templform_field.templcard.templmodal.templtable.templempty_state.templtokens.govariants.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:
primarysecondaryghostdangersuccess
- sizes should be limited:
smmdlg
- 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:
- Register catalog pages and examples in Go.
- Render them through
templ. - Write static HTML files into
docs/design-system/. - Include generated navigation and shared page chrome.
The generator should output at least:
index.htmltokens.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:
- create tokens and the
uipackage structure - implement
Button,IconButton,Badge,Input,FormField,Modal,Card,Table - build the static catalog generator
- generate initial catalog pages
- migrate
/tablosto use the shared primitives - 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
uipackage - 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
templUI layer - Tailwind remains the utility foundation, not the primary abstraction
- the static catalog is generated from the same component code as production
/tablosuses 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.