# Go Backend CSS Sources Per Primitive Design

**Date:** 2026-05-10

**Goal**

Refactor the Go backend design-system CSS so each UI primitive owns its own source stylesheet beside its UI code while the app and generated catalog continue shipping a single compiled `go-backend/static/styles.css`.

**Chosen Approach**

Split CSS at the source level by primitive and co-locate those source files with the UI primitives that own them, then aggregate them into the existing `styles.css` output during the build. This keeps runtime behavior unchanged while making the CSS easier to scale, review, and extend.

**Scope**

- Move shared design-system CSS rules into source files organized by primitive and stored beside the primitive code.
- Keep one final shipped stylesheet: `go-backend/static/styles.css`.
- Keep app templates and generated design-system pages linking the same single output file.
- Add a small verification layer that proves the final built stylesheet still contains representative selectors for the primitives.

**Out Of Scope**

- Shipping multiple CSS files to the browser
- Changing component HTML contracts just to fit the refactor
- Replacing the current Tailwind pipeline
- Reworking unrelated dashboard or app styles outside the design-system slice
- Introducing CSS-in-JS or Go-generated CSS

**Architecture**

The design-system CSS should have two layers:

1. **Primitive source files**
   - one file per UI primitive or catalog responsibility
   - stored beside the owning primitive or catalog code
   - these files are the new source of truth for design-system styling

2. **Aggregate build entry**
   - a single stylesheet entry imports those primitive files in a deterministic order
   - the build continues emitting `go-backend/static/styles.css`

This preserves the current runtime contract:

- app pages still load `/static/styles.css`
- generated catalog pages still load `../../go-backend/static/styles.css`

**Source File Structure**

Recommended source locations:

- `go-backend/internal/web/ui/base.css`
- `go-backend/internal/web/ui/button.css`
- `go-backend/internal/web/ui/badge.css`
- `go-backend/internal/web/ui/icon-button.css`
- `go-backend/internal/web/ui/input.css`
- `go-backend/internal/web/ui/textarea.css`
- `go-backend/internal/web/ui/form-field.css`
- `go-backend/internal/web/ui/modal.css`
- `go-backend/internal/web/ui/table.css`
- `go-backend/internal/web/ui/empty-state.css`
- `go-backend/internal/web/ui/card.css`
- `go-backend/internal/web/ui/spacing.css`
- `go-backend/internal/web/ui/app.css`
- `go-backend/internal/web/ui/catalog/catalog.css`

The build should read those files in a stable explicit order so later files can intentionally override earlier shared rules where needed.

**Responsibility Boundaries**

`catalog/catalog.css`

- catalog page chrome
- catalog example wrappers
- catalog-only preview helpers

Primitive CSS files

- only selectors directly tied to that primitive’s API or rendering contract
- examples:
  - `button.css` owns `.ui-button*`
  - `badge.css` owns `.ui-badge*`
  - `icon-button.css` owns `.ui-icon-button*` and `.borderless-icon-button*`
  - `card.css` owns `.ui-card*`
  - `spacing.css` owns `.ui-space-*`

Shared non-design-system app selectors such as dashboard-only `.project-*` styles should not be mixed into primitive CSS just because they are nearby in the current file. They should live in an explicit `internal/web/ui/app.css` source file so ownership stays within the UI layer without falsely attaching them to a primitive.

**Build Contract**

The build should continue producing:

- `go-backend/static/styles.css`

Preferred shape:

- the build reads an explicit ordered list of co-located source files
- the output path remains unchanged

The builder should not rely on a single `static/css` source directory anymore. HTML consumers stay unchanged.

**Ordering Rules**

Import order should be explicit and stable:

1. base and global UI-layer rules
2. catalog layout and wrappers
3. low-level primitives used broadly
4. higher-level primitives
5. app-only UI-layer rules last

This avoids accidental cascade regressions during future additions.

**Migration Strategy**

Refactor incrementally but land as one coherent slice:

- create the new co-located source files beside UI primitives and catalog code
- move selectors from the monolithic stylesheet into those files without renaming selectors
- regenerate `go-backend/static/styles.css`
- verify component tests and catalog generation still pass

Selector names should remain stable during this slice. The purpose is ownership and scalability, not visual redesign.

**Testing**

Verification should cover:

- UI tests still passing for existing primitives
- catalog tests still passing
- design-system generator tests still passing
- representative selectors from each extracted primitive still present in the built `styles.css`

Representative examples:

- `.ui-button-solid.ui-button-default`
- `.ui-badge-warning`
- `.ui-icon-button-solid.ui-icon-button-neutral`
- `.ui-card`
- `.ui-space-x-md`
- `.catalog-page`

**Risks**

Primary risks:

- breaking cascade order while moving selectors
- accidentally mixing app-only selectors into primitive files
- changing build inputs without regenerating the checked-in output

Mitigation:

- keep selectors unchanged
- move files in clearly bounded groups
- verify the built artifact and tests after the refactor

**Success Criteria**

This work is complete when:

- each design-system primitive has its own source CSS file beside its UI code
- catalog-specific CSS is separated from primitive CSS and stored beside catalog code
- the app still ships one `go-backend/static/styles.css`
- app and catalog HTML consumers remain unchanged
- tests and generated catalog output still pass
