theme

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2026 License: AGPL-3.0 Imports: 13 Imported by: 0

Documentation

Overview

Package theme is the single source of style for internal/ui: semantic roles, never raw colours at call sites. This file defines the role enum only; embedded theme data, the contrast/CVD validators, the palette search, and the degradation ladder land after user review of this list.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Emphasis

func Emphasis(r Role) (bold, dim bool)

Emphasis reports the typographic weight a role carries independent of colour, so it survives NO_COLOR and the no-colour/ASCII tier.

Types

type ContrastCheck

type ContrastCheck struct {
	FG, BG Role
	Min    float64
	Label  string
}

ContrastCheck is one required foreground/background pair and the WCAG 2.1 ratio it must meet. Body text needs 4.5:1 (AA); large text and UI components need 3:1. AllContrastChecks is the single source of truth for which pairs the UI actually renders — role.RoleBorder is deliberately absent: no state is carried by border alone, so it is decorative and exempt from WCAG 1.4.11 (wireframes.md section 7).

func AllContrastChecks

func AllContrastChecks() []ContrastCheck

type ContrastFailure

type ContrastFailure struct {
	Check ContrastCheck
	Ratio float64
}

ContrastFailure is one gate-table check a theme did not meet.

func ValidateContrast

func ValidateContrast(t Theme) ([]ContrastFailure, error)

ValidateContrast checks every gate-table pair against a theme's colours and returns every failure found (nil if the theme passes clean).

func (ContrastFailure) String

func (f ContrastFailure) String() string

type Dichromacy

type Dichromacy int

Dichromacy is a colour-vision deficiency simulated by the Vienot, Brettel and Mollon (1999) LMS model: sRGB -> linear -> LMS (via the Hunt-Pointer-Estevez matrix), project out the missing cone response in LMS space, convert back. research-panes.md section 3.

const (
	Protanopia Dichromacy = iota
	Deuteranopia
	Tritanopia
)

type Role

type Role string

Role names one semantic colour slot. A Theme maps every Role to a colour; a view layer never holds a literal colour, only a Role.

const (
	RoleBG          Role = "bg"
	RoleBGSubtle    Role = "bg-subtle"
	RoleBGInset     Role = "bg-inset"
	RoleFG          Role = "fg"
	RoleFGMuted     Role = "fg-muted"
	RoleFGSubtle    Role = "fg-subtle"
	RoleBorder      Role = "border"       // decorative; no state, exempt from WCAG 1.4.11
	RoleBorderFocus Role = "border-focus" // carries state; must meet 3:1
	RoleAccent      Role = "accent"       // chrome only: prompt marker, focus ring, selection. Never a status.
	RoleAccentFG    Role = "accent-fg"
	RoleSuccess     Role = "success"
	RoleWarning     Role = "warning"
	RoleDanger      Role = "danger"
	RoleInfo        Role = "info"
)

Base roles. wireframes.md section 7.

const (
	RoleKeyword  Role = "keyword"
	RoleString   Role = "string"
	RoleNumber   Role = "number"
	RoleComment  Role = "comment"
	RoleFunction Role = "function"
	RoleType     Role = "type"
	RoleVariable Role = "variable"
)

Syntax roles. wireframes.md section 7.

const (
	RoleDiffAddFG Role = "diff-add-fg"
	RoleDiffAddBG Role = "diff-add-bg"
	RoleDiffDelFG Role = "diff-del-fg"
	RoleDiffDelBG Role = "diff-del-bg"
	RoleDiffHunk  Role = "diff-hunk"
)

Diff roles. wireframes.md section 7.

const (
	RoleBGSelection   Role = "bg-selection"     // picker/completion selected row; not accent
	RoleDiffAddEmphBG Role = "diff-add-emph-bg" // word-level diff emphasis
	RoleDiffDelEmphBG Role = "diff-del-emph-bg" // word-level diff emphasis
	RoleGutter        Role = "gutter"           // dimmer than border, not decorative
	RoleLink          Role = "link"             // actionable file paths/URLs; distinct from info
	RoleFGInverse     Role = "fg-inverse"       // text on success/warning/danger fills
)

Roles the Phase 0 mock needed that the original supplied list did not contain. wireframes.md section 7 / research.md finding 3.

func AllRoles

func AllRoles() []Role

AllRoles lists every role a Theme must define, in the order shown in wireframes-panes.md section 18.

func StatusRoles

func StatusRoles() []Role

StatusRoles is the set that must stay mutually separable under dichromat simulation. research-panes.md section 3: accent is chrome and is exempt from this check.

type SearchOptions

type SearchOptions struct {
	// MinContrast is the WCAG contrast ratio each candidate colour must
	// meet against bg (config.WCAGAALarge is the conventional floor for
	// a status word).
	MinContrast float64
	// Iterations bounds how many full {success,warning,danger,info}
	// candidate sets are tried. More iterations trade search time for a
	// better worst-case separation.
	Iterations int
	// Seed makes the search reproducible: same bg + options + seed
	// always finds the same palette.
	Seed int64
}

SearchOptions configures SearchStatusPalette.

type SearchResult

type SearchResult struct {
	Colors  map[Role]string
	WorstDE float64
	Detail  []SeparationPair
}

SearchResult is one candidate palette and its measured worst-case separation.

func SearchStatusPalette

func SearchStatusPalette(bg string, opts SearchOptions) (SearchResult, error)

SearchStatusPalette searches hue/saturation/lightness space for a {success, warning, danger, info} set that meets MinContrast against bg and maximises worst-case CVD separation (WorstCaseSeparation), within the conventional hue windows in statusHueWindows.

research-panes.md section 3.2: hand-picking a status palette lost to this kind of search by 4x on worst-case separation. This function is for generating a *new* first-party or user theme against the constraint; it never rewrites an existing theme's shipped colours.

type SeparationPair

type SeparationPair struct {
	A, B       Role
	WorstDE    float64
	WorstUnder string // "normal", "protanopia", "deuteranopia", or "tritanopia"
}

SeparationPair is one status-role pair's worst measured separation across normal vision and all three dichromacies.

func HardFailSeparation

func HardFailSeparation(t Theme) (worst float64, detail []SeparationPair, ok bool, err error)

HardFailSeparation reports whether a theme's worst-case status separation meets its own documented CVDBudget. This is the build-time gate: hard-fail for first-party themes, informational for third-party (research-panes.md section 3.1: no third-party palette in the survey was CVD-clean, so a hard gate there would reject nearly every upstream scheme).

func WorstCaseSeparation

func WorstCaseSeparation(colors map[Role]string, roles []Role) (worst float64, detail []SeparationPair, err error)

WorstCaseSeparation measures every pair of the given status roles under normal vision and all three dichromacies, and returns the worst (smallest) dE found, plus the per-pair detail. research-panes.md section 3: the set that must stay separable is {success, warning, danger, info}; accent is chrome and is exempt.

type Style

type Style struct {
	Hex     string // set for TrueColor and Tier256; empty otherwise
	ANSI16  int    // set for Tier16; -1 otherwise
	NoColor bool   // true for TierASCII and TierNoTTY
	Bold    bool
	Dim     bool
}

Style is the fully-resolved, tier-appropriate representation of one role: a hex colour where the tier supports it, an explicit ANSI16 index at the 16-colour tier, and structural emphasis that survives every tier including no-colour (research-panes.md section 2.1: NO_COLOR "disables colors but preserves text decoration").

type Theme

type Theme struct {
	Name       string          `json:"name"`
	Label      string          `json:"label"`
	Dark       bool            `json:"dark"`
	FirstParty bool            `json:"first_party"`
	CVDBudget  float64         `json:"cvd_budget"`
	Credits    string          `json:"credits,omitempty"`
	Colors     map[Role]string `json:"colors"`
	// ANSI16 gives each role an explicit ANSI SGR colour index (0-15) for
	// the 16-colour degradation tier. A generic nearest-RGB downsample
	// turns an achromatic accent to silver (research.md finding 8); this
	// map is the fix, authored per theme rather than computed.
	ANSI16 map[Role]int `json:"ansi16"`
}

Theme is data, not code: a Role -> hex colour map, plus an explicit 16-colour tier map and the CVD budget this theme trades against. A view layer never holds a literal colour, only a Role, and looks it up through a Theme via Resolve.

func Embedded

func Embedded() ([]Theme, error)

Embedded loads every theme shipped in themes/*.json, sorted by name.

func LoadUserDir

func LoadUserDir(dir string) ([]Theme, error)

LoadUserDir loads user-supplied theme JSON files from a config directory. A missing directory is not an error: it means no user themes are installed.

func (Theme) Ansi16

func (t Theme) Ansi16(r Role) (int, bool)

Ansi16 returns the ANSI SGR colour index (0-15) for a role, and whether it is defined.

func (Theme) Color

func (t Theme) Color(r Role) (string, bool)

Color returns the truecolor hex value for a role, and whether it is defined.

func (Theme) Resolve

func (t Theme) Resolve(r Role, tier Tier) Style

Resolve returns the Style a role should render as at the given tier. Tier256 uses the theme's truecolor hex and lets the terminal/library downsample it (research.md finding 8: safe at 256, only 16 needs an explicit map); Tier16 uses the theme's authored ANSI16 index directly, never a computed nearest match.

type Tier

type Tier = colorprofile.Profile

Tier is the colour-profile degradation tier. It re-exports colorprofile.Profile directly rather than wrapping it, so Detect below stays a thin pass-through to the library the design calls for (research-panes.md section 2.1) instead of a hand-rolled duplicate.

const (
	TierTrueColor Tier = colorprofile.TrueColor
	Tier256       Tier = colorprofile.ANSI256
	Tier16        Tier = colorprofile.ANSI
	TierASCII     Tier = colorprofile.Ascii
	TierNoTTY     Tier = colorprofile.NoTTY
)

func Detect

func Detect(w io.Writer, env []string) Tier

Detect resolves the colour tier from the output stream and environment, honouring NO_COLOR, CLICOLOR, CLICOLOR_FORCE, TERM and COLORTERM.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL