feat(01-02): add ui package enums, helpers, base CSS
- tokens.go: semantic token constants - variants.go: Size/ButtonVariant/ButtonTone/BadgeVariant enums + Normalized* - helpers.go: mergeAttrs for templ.Attributes - base.css: resets, :focus-visible ring (no nesting)
This commit is contained in:
parent
5b19a8815c
commit
1ff8e681da
5 changed files with 171 additions and 14 deletions
|
|
@ -2,17 +2,4 @@ module backend
|
|||
|
||||
go 1.26.1
|
||||
|
||||
require (
|
||||
github.com/a-h/templ v0.3.1020 // indirect
|
||||
github.com/go-chi/chi/v5 v5.2.5 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.9.2 // indirect
|
||||
github.com/mfridman/interpolate v0.0.2 // indirect
|
||||
github.com/pressly/goose/v3 v3.27.1 // indirect
|
||||
github.com/sethvargo/go-retry v0.3.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
)
|
||||
require github.com/a-h/templ v0.3.1020
|
||||
|
|
|
|||
28
backend/internal/web/ui/base.css
Normal file
28
backend/internal/web/ui/base.css
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* base.css — global resets and accessibility floor for the design system.
|
||||
* Plain CSS only (no @apply, no nesting) so the file is consumable by both
|
||||
* Tailwind v4 standalone (with @source scanning Go files) and any other
|
||||
* downstream CSS pipeline.
|
||||
*/
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||
color: #0f172a;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
outline: 2px solid #2563eb;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
16
backend/internal/web/ui/helpers.go
Normal file
16
backend/internal/web/ui/helpers.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package ui
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
|
||||
// mergeAttrs returns a new templ.Attributes containing every key from base,
|
||||
// with override keys taking precedence on collision. Either input may be nil.
|
||||
func mergeAttrs(base, override templ.Attributes) templ.Attributes {
|
||||
out := templ.Attributes{}
|
||||
for k, v := range base {
|
||||
out[k] = v
|
||||
}
|
||||
for k, v := range override {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
21
backend/internal/web/ui/tokens.go
Normal file
21
backend/internal/web/ui/tokens.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Package ui hosts the design-system primitives (Button, Card, Badge) and the
|
||||
// shared enum surface (Size, ButtonVariant, ButtonTone, BadgeVariant) used by
|
||||
// every templ page in the application.
|
||||
//
|
||||
// Phase 1 ships a minimal subset of the semantic token surface declared here.
|
||||
// Later phases extend the rules in *.css files rather than restructuring the
|
||||
// enum/token surface — these constants exist so consumers can refer to tokens
|
||||
// by name without hard-coding string literals.
|
||||
package ui
|
||||
|
||||
// Semantic token names. Phase 1 ships these for forward compatibility; the
|
||||
// CSS rules in base.css / button.css / card.css / badge.css do not yet
|
||||
// dereference these constants. Future phases will route component variants
|
||||
// through tokens (e.g. ButtonVariantDanger -> TokenDanger).
|
||||
const (
|
||||
TokenPrimary string = "primary"
|
||||
TokenNeutral string = "neutral"
|
||||
TokenWarning string = "warning"
|
||||
TokenSuccess string = "success"
|
||||
TokenDanger string = "danger"
|
||||
)
|
||||
105
backend/internal/web/ui/variants.go
Normal file
105
backend/internal/web/ui/variants.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package ui
|
||||
|
||||
// Size is the canonical component size enum. Phase 1 only renders SizeMD via
|
||||
// CSS, but every component normalizes Size so future phases can drop in
|
||||
// `.ui-button-...-sm` / `.ui-button-...-lg` rules without changing call sites.
|
||||
type Size string
|
||||
|
||||
const (
|
||||
SizeSM Size = "sm"
|
||||
SizeMD Size = "md"
|
||||
SizeLG Size = "lg"
|
||||
)
|
||||
|
||||
// ButtonVariant is the semantic-color enum for Button.
|
||||
type ButtonVariant string
|
||||
|
||||
const (
|
||||
ButtonVariantDefault ButtonVariant = "default"
|
||||
ButtonVariantNeutral ButtonVariant = "neutral"
|
||||
ButtonVariantWarning ButtonVariant = "warning"
|
||||
ButtonVariantSuccess ButtonVariant = "success"
|
||||
ButtonVariantDanger ButtonVariant = "danger"
|
||||
)
|
||||
|
||||
// ButtonTone is the visual-weight enum for Button (solid vs. soft).
|
||||
type ButtonTone string
|
||||
|
||||
const (
|
||||
ButtonToneSolid ButtonTone = "solid"
|
||||
ButtonToneSoft ButtonTone = "soft"
|
||||
)
|
||||
|
||||
// BadgeVariant is the semantic-color enum for Badge.
|
||||
type BadgeVariant string
|
||||
|
||||
const (
|
||||
BadgeVariantInfo BadgeVariant = "info"
|
||||
BadgeVariantWarning BadgeVariant = "warning"
|
||||
BadgeVariantSuccess BadgeVariant = "success"
|
||||
BadgeVariantDanger BadgeVariant = "danger"
|
||||
)
|
||||
|
||||
// NormalizedSize returns the safe default (SizeMD) for the zero value and any
|
||||
// value not in the declared set.
|
||||
func NormalizedSize(size Size) Size {
|
||||
switch size {
|
||||
case SizeSM, SizeLG:
|
||||
return size
|
||||
default:
|
||||
return SizeMD
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizedButtonVariant returns the safe default (ButtonVariantDefault) for
|
||||
// the zero value and any value not in the declared set.
|
||||
func NormalizedButtonVariant(variant ButtonVariant) ButtonVariant {
|
||||
switch variant {
|
||||
case ButtonVariantNeutral, ButtonVariantWarning, ButtonVariantSuccess, ButtonVariantDanger:
|
||||
return variant
|
||||
default:
|
||||
return ButtonVariantDefault
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizedButtonTone returns the safe default (ButtonToneSolid) for the zero
|
||||
// value and any value not in the declared set.
|
||||
func NormalizedButtonTone(tone ButtonTone) ButtonTone {
|
||||
switch tone {
|
||||
case ButtonToneSoft:
|
||||
return tone
|
||||
default:
|
||||
return ButtonToneSolid
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizedBadgeVariant returns the safe default (BadgeVariantInfo) for the
|
||||
// zero value and any value not in the declared set.
|
||||
func NormalizedBadgeVariant(variant BadgeVariant) BadgeVariant {
|
||||
switch variant {
|
||||
case BadgeVariantWarning, BadgeVariantSuccess, BadgeVariantDanger:
|
||||
return variant
|
||||
default:
|
||||
return BadgeVariantInfo
|
||||
}
|
||||
}
|
||||
|
||||
// ButtonClass assembles the deterministic class string for a Button. Inputs
|
||||
// are normalized before assembly so callers can pass zero values safely.
|
||||
//
|
||||
// Example: ButtonClass(ButtonVariantDefault, ButtonToneSolid, SizeMD) ==
|
||||
// "ui-button ui-button-solid-default-md".
|
||||
func ButtonClass(variant ButtonVariant, tone ButtonTone, size Size) string {
|
||||
v := NormalizedButtonVariant(variant)
|
||||
t := NormalizedButtonTone(tone)
|
||||
s := NormalizedSize(size)
|
||||
return "ui-button ui-button-" + string(t) + "-" + string(v) + "-" + string(s)
|
||||
}
|
||||
|
||||
// BadgeClass assembles the deterministic class string for a Badge.
|
||||
//
|
||||
// Example: BadgeClass(BadgeVariantInfo) == "ui-badge ui-badge-info".
|
||||
func BadgeClass(variant BadgeVariant) string {
|
||||
v := NormalizedBadgeVariant(variant)
|
||||
return "ui-badge ui-badge-" + string(v)
|
||||
}
|
||||
Loading…
Reference in a new issue