theme

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package theme defines capability-aware theming primitives for Nabat and its extensions.

A Theme carries one or more Palette entries, a default variant, and cross-variant defaults. Theme.Resolve picks a variant from Capabilities and returns an immutable ResolvedTheme queried by Token or accessor.

The package does not depend on the nabat root package or IOStreams; extensions can import it to read styles without pulling in command or IO machinery.

Index

Examples

Constants

View Source
const (
	// Default is the theme installed when the caller does not pass
	// nabat.WithTheme. Capability-aware: defers to the terminal's
	// detected color profile and background luminance.
	Default = "default"

	// Minimal is a low-color theme that relies on bold instead of
	// foreground colors. It declares variant=notty so the framework
	// disables chroma syntax highlighting and forces glamour into
	// plain-text mode, matching pipe-friendly defaults.
	Minimal = "minimal"

	// Charm is the higher-contrast palette aligned with Charm.land
	// defaults, suitable for dark terminals.
	Charm = "charm"

	// Dracula ships Dracula Classic (dark) and Alucard Classic (light)
	// per draculatheme.com/spec; pickVariant selects by terminal
	// luminance. Dark uses chroma/glamour/huh "dracula"; light uses
	// chroma "github", glamour "light", and token-derived prompts.
	Dracula = "dracula"

	// Gruvbox is the retro groove palette from morhetz/gruvbox; dark and
	// light variants switch via pickVariant. Pairs with chroma's "gruvbox"
	// and "gruvbox-light" styles.
	Gruvbox = "gruvbox"

	// CatppuccinLatte is the Catppuccin Latte palette for light
	// backgrounds. Pairs with chroma's "catppuccin-latte" style.
	CatppuccinLatte = "catppuccin-latte"

	// CatppuccinFrappe is the Catppuccin Frappé palette for dark
	// backgrounds. Pairs with chroma's "catppuccin-frappe" style.
	CatppuccinFrappe = "catppuccin-frappe"

	// CatppuccinMacchiato is the Catppuccin Macchiato palette for dark
	// backgrounds. Pairs with chroma's "catppuccin-macchiato" style.
	CatppuccinMacchiato = "catppuccin-macchiato"

	// CatppuccinMocha is the Catppuccin Mocha palette for dark
	// backgrounds. Pairs with chroma's "catppuccin-mocha" style.
	CatppuccinMocha = "catppuccin-mocha"

	// Nabat is the brand palette: warm Persian rock-candy tones
	// (saffron, pistachio, pomegranate, turquoise). It ships with a
	// matching framework-owned chroma style and huh adapter, both
	// referenced by name from the manifest.
	Nabat = "nabat"

	// Nord is the Nord palette (Polar Night, Snow Storm, Frost,
	// Aurora) for dark backgrounds. Pairs with chroma's "nord" style.
	Nord = "nord"

	// Solarized is the canonical Solarized palette (base tones plus eight
	// accents). Dark/light variants follow https://ethanschoonover.com/solarized/;
	// pickVariant selects by terminal luminance. Pairs with chroma's
	// "solarized-dark" / "solarized-light" styles.
	Solarized = "solarized"
)

Untyped string constants for built-in theme names. Pass to [nabat.WithTheme]. Untyped so they mix with env/config strings. Each name matches an embedded data/<name>.json file.

Example:

nabat.New("myctl", nabat.WithTheme(theme.Dracula))

Variables

View Source
var DefaultAliases = map[Token]Token{
	ListEnumerator: TextMuted,
	TreeEnumerator: ListEnumerator,
	TableBorder:    TextMuted,

	ListItem:    TextPrimary,
	TreeItem:    TextPrimary,
	TableCell:   TextPrimary,
	TableHeader: TextTitle,

	SpinnerActive: StatusInfo,
	StatusActive:  StatusInfo,
}

DefaultAliases is the framework fall-through map used when a Token has no direct entry in the resolved Palette. ResolvedTheme.Style walks the chain until it hits a style or bottoms out at the zero lipgloss.Style.

Palette.Aliases overrides entries here; an empty target disables the default for that key. Cycles stop at Style time (zero style) and are reported at Theme.Resolve.

Functions

func All

func All() map[string]Theme

All returns a defensive copy of the registry, parsing every manifest. Callers may mutate the map without affecting the registry.

Parse errors panic. For per-theme error handling, iterate Names and call Get instead.

func ChromaFromTokens

func ChromaFromTokens(name string, tokens map[Token]lipgloss.Style) *chroma.Style

ChromaFromTokens derives a chroma style from semantic tokens.

func ChromaPreset

func ChromaPreset(v Variant) string

ChromaPreset picks the upstream chroma style name for v. Theme.Resolve uses it when chroma slots are empty.

func GlamourFromTokens

func GlamourFromTokens(tokens map[Token]lipgloss.Style, base *ansi.StyleConfig) *ansi.StyleConfig

GlamourFromTokens derives a glamour style from semantic tokens. The base style is cloned so callers can pass one of glamour's built-in dark/light/notty presets and keep untouched slots intact.

func GlamourPreset

func GlamourPreset(v Variant, c Capabilities) string

GlamourPreset picks the upstream glamour style name for v and c. Theme.Resolve uses it when glamour slots are empty.

func HuhFromTokens

func HuhFromTokens(tokens map[Token]lipgloss.Style) huh.Theme

HuhFromTokens derives a huh.Theme from a per-token style map via PromptFromTokens. Prefer PromptFromTokens when the Nabat-native Prompt value is useful; this stays for callers that want huh.Theme directly.

func Names

func Names() []string

Names returns the registered theme names in lexical order.

Example

ExampleNames demonstrates that Names returns themes in lexical order, which is what callers typically want for completion lists.

package main

import (
	"fmt"

	"nabat.dev/theme"
)

func main() {
	names := theme.Names()
	for i := 1; i < len(names); i++ {
		if names[i] < names[i-1] {
			fmt.Println("unsorted")
			return
		}
	}
	fmt.Println("sorted")
}
Output:
sorted

func Schema

func Schema() []byte

Schema returns the embedded JSON Schema for the theme manifest format. The slice is a fresh copy on each call. A read failure panics (broken build).

Types

type Capabilities

type Capabilities struct {
	// Dark reports whether the terminal background is dark.
	Dark bool

	// BackgroundHex is the exact terminal background color when the
	// detector could read it. Empty when detection could not run.
	BackgroundHex string

	// Profile is the active [colorprofile.Profile] for the primary
	// output stream.
	Profile colorprofile.Profile

	// Interactive reports whether primary output is a TTY and input
	// allows prompting.
	Interactive bool

	// Width is the terminal width in cells, or 0 when unmeasured.
	Width int

	// Hyperlinks reports OSC 8 hyperlink support.
	Hyperlinks bool

	// Unicode is the terminal's Unicode capability tier.
	Unicode UnicodeLevel

	// ReducedMotion reports whether animations should be suppressed
	// (NO_MOTION, REDUCE_MOTION, and similar flags).
	ReducedMotion bool
}

Capabilities describes the rendering surface a Theme resolves against. Themes branch on these fields for colors, plain-text fallbacks, and glamour presets.

Capabilities is a plain constructible struct: the theme package has no IOStreams dependency, so tests build values directly. The nabat root package owns detection. When detection is uncertain, the framework reports the safer (less-feature) value.

type Metadata

type Metadata struct {
	// Name is the manifest "name" field and [Get] registry key.
	Name string

	// Description is the manifest "description" field, or empty.
	Description string

	// Default is the fallback variant for [Theme.Resolve]. Empty for
	// single-variant themes.
	Default string

	// Variants is the sorted list of declared variant keys. Always
	// non-empty (schema requires at least one).
	Variants []string

	// TokenNames is the sorted, deduplicated set of token paths any
	// variant declares.
	TokenNames []string
}

Metadata describes a built-in manifest without invoking Theme.Resolve. Manifest returns a fresh value each call; mutating slices in place is safe and does not affect the registry.

func Manifest

func Manifest(name string) (Metadata, error)

Manifest returns Metadata for a built-in theme without resolving it. Unknown names return the same "available: [...]" error shape as Get. The returned value is freshly constructed; the decode is memoized.

type Override

type Override interface {
	// contains filtered or unexported methods
}

Override is a per-Palette mutation from the Set* helpers. Applied to every variant of the underlying Theme, so a single token tweak affects whichever variant Theme.Resolve picks.

Prefer the Set* helpers (or nabat.WithThemeOverride) over implementing Override directly.

func SetAlias

func SetAlias(tok, target Token) Override

SetAlias returns an Override that sets tok's fall-through target in Palette.Aliases. An empty target disables the framework default for that key.

func SetChroma

func SetChroma(s *chroma.Style) Override

SetChroma returns an Override that sets an owned *chroma.Style and clears Palette.ChromaName so the cascade uses the pointer alone.

func SetChromaName

func SetChromaName(name string) Override

SetChromaName returns an Override that sets the upstream chroma style name and clears Palette.Chroma.

func SetGlamour

func SetGlamour(s *ansi.StyleConfig) Override

SetGlamour returns an Override that sets an owned *ansi.StyleConfig and clears Palette.GlamourName and Palette.GlamourFor.

func SetGlamourName

func SetGlamourName(name string) Override

SetGlamourName returns an Override that sets the upstream glamour preset name and clears Palette.Glamour and Palette.GlamourFor.

func SetHuh

func SetHuh(h huh.Theme) Override

SetHuh returns an Override that sets the huh.Theme for interactive prompts. Nil reverts to the PromptFromTokens fallback at Theme.Resolve time.

func SetToken

func SetToken(t Token, s lipgloss.Style) Override

SetToken returns an Override that records s under token t, shadowing any existing palette value. Applies to every variant.

type Palette

type Palette struct {
	// Tokens is the per-token style map. Missing tokens fall through
	// the alias chain ([Aliases] over [DefaultAliases]) before the
	// zero [lipgloss.Style].
	Tokens map[Token]lipgloss.Style

	// Aliases overrides [DefaultAliases] for this palette. A non-empty
	// mapping replaces the default; Aliases[X] = "" disables the
	// default for that key without substituting another.
	Aliases map[Token]Token

	// Chroma is an owned [*chroma.Style]. Non-nil wins over
	// [Palette.ChromaName]; when both are zero, [ChromaFromTokens]
	// supplies the default.
	Chroma *chroma.Style

	// ChromaName is the upstream chroma style name used when
	// [Palette.Chroma] is nil. Unknown names fall through to chroma's
	// own default at render time.
	ChromaName string

	// Glamour is an owned [*ansi.StyleConfig]. Non-nil wins over
	// [Palette.GlamourName] and [Palette.GlamourFor].
	Glamour *ansi.StyleConfig

	// GlamourName is the upstream glamour preset name used when
	// Glamour and GlamourFor are nil. Unknown names fall through to
	// glamour's own default.
	GlamourName string

	// GlamourFor is a capability-aware factory called by [Theme.Resolve]
	// when Glamour and GlamourName are zero. Errors surface from
	// [Theme.ResolveErr] (and are discarded by [Theme.Resolve]).
	GlamourFor func(Capabilities) (*ansi.StyleConfig, error)

	// Prompt is the Nabat-native prompt style block, converted to a
	// [huh.Theme] at resolve time. Zero falls back to
	// [PromptFromTokens]. [Palette.Huh] wins over Prompt when set.
	Prompt Prompt

	// Huh is the [huh.Theme] for interactive prompts. Non-nil wins
	// over [Prompt] and [PromptFromTokens]. Use it when the closed
	// [Prompt] surface is not enough.
	Huh huh.Theme
}

Palette is the per-variant style data a Theme carries. Theme.Resolve picks one palette and fills nil or empty cascade slots with framework defaults.

Chroma and glamour use a three-way cascade (owned value, registered name, capability default) folded into one value at resolve time.

type Prompt

type Prompt struct {
	// Title styles the group / section title above prompts.
	Title lipgloss.Style

	// Description styles explanatory text under each prompt.
	Description lipgloss.Style

	// Cursor styles the text-input cursor.
	Cursor lipgloss.Style

	// Placeholder styles the text-input placeholder.
	Placeholder lipgloss.Style

	// SelectedOption styles the currently selected list item.
	SelectedOption lipgloss.Style

	// UnselectedOption styles items that are not selected.
	UnselectedOption lipgloss.Style

	// SelectedPrefix styles the marker next to the selected item.
	SelectedPrefix lipgloss.Style

	// UnselectedPrefix styles the marker next to non-selected items.
	UnselectedPrefix lipgloss.Style

	// Error styles error indicators and messages.
	Error lipgloss.Style

	// Help styles the keybind footer text.
	Help lipgloss.Style

	// Selector styles the active-row indicator and navigation arrows.
	Selector lipgloss.Style

	// ButtonFocused styles the focused submit / next button.
	ButtonFocused lipgloss.Style

	// ButtonBlurred styles the inactive button.
	ButtonBlurred lipgloss.Style

	// Border is the form / card border. The zero [lipgloss.Border]
	// leaves huh's base border untouched.
	Border lipgloss.Border

	// BorderColor is the focused field left-border foreground. The
	// zero value leaves huh's base border color untouched.
	BorderColor color.Color
}

Prompt is the closed Nabat-native style block for interactive prompts. Each field is a lipgloss.Style for one slot; the zero style means inherit huh's base styling for that slot.

For huh's full surface, set Palette.Huh instead; a non-nil Huh wins over Prompt. Prefix markers use lipgloss.Style.SetString for their literal text.

func PromptFromTokens

func PromptFromTokens(tokens map[Token]lipgloss.Style) Prompt

PromptFromTokens derives a Prompt from a per-token style map. It is the fallback when a Palette declares neither Palette.Prompt nor Palette.Huh.

func (Prompt) Huh

func (p Prompt) Huh() huh.Theme

Huh returns a huh.Theme derived from this Prompt, starting from huh.ThemeBase and overlaying set fields. Zero-style fields inherit the base.

Blurred styling reuses Focused for a stable cross-state look; authors who need separate blurred styles set Palette.Huh directly. Border and BorderColor apply to Focused.Base and Focused.Card when set; Blurred fields always use a hidden border so the focused left border acts as the focus indicator.

func (Prompt) IsZero

func (p Prompt) IsZero() bool

IsZero reports whether every field on p is unset. The framework uses it to fall back to PromptFromTokens when a palette omits Prompt.

A Prompt with only [Border] or [BorderColor] set is non-zero. The check inspects fields individually because lipgloss.Style is not comparable.

type PromptKnobs

type PromptKnobs struct {
	// SelectedPrefix is the literal prefix before selected options.
	SelectedPrefix string

	// UnselectedPrefix is the literal prefix before unselected options.
	UnselectedPrefix string

	// Border is the form/card border. The zero [lipgloss.Border]
	// leaves the prompt border unchanged.
	Border lipgloss.Border

	// BorderColor is the focused field left-border foreground. The
	// zero value leaves the prompt border color unchanged.
	BorderColor color.Color
}

PromptKnobs carries theme-wide prompt settings shared across variants.

func (PromptKnobs) Apply

func (k PromptKnobs) Apply(p Prompt) Prompt

Apply overlays non-zero knobs from k onto p and returns the result.

func (PromptKnobs) IsZero

func (k PromptKnobs) IsZero() bool

IsZero reports whether k leaves every prompt knob unset.

type Requirement

type Requirement struct {
	Consumer string
	Tokens   []Token
}

Requirement declares the Token set a consumer reads from a ResolvedTheme. Consumer identifies the requester in diagnostics (for example "logging extension"). Plain data; pass by value; treat Tokens as read-only.

func CoreRequirements

func CoreRequirements() []Requirement

CoreRequirements returns the Requirement entries the nabat root package reads from a ResolvedTheme. The framework includes this list automatically; extensions declare only what they add.

func Require

func Require(consumer string, tokens ...Token) Requirement

Require returns a Requirement for consumer and the tokens it reads.

Example:

return theme.Require("logging extension",
    theme.StatusInfo, theme.StatusWarning, theme.StatusError,
)

type ResolvedTheme

type ResolvedTheme struct {
	// contains filtered or unexported fields
}

ResolvedTheme is the immutable result of Theme.Resolve, queried by Token or accessor. Safe for concurrent use after construction. Slice results are defensive copies; shared style pointers are read-only. The zero value yields empty styles and nil chroma/glamour.

func (ResolvedTheme) CheckRequirements

func (r ResolvedTheme) CheckRequirements(reqs []Requirement) error

CheckRequirements returns an error listing consumers whose tokens this ResolvedTheme does not fully cover. nil means all satisfied.

Diagnostic format:

theme "minimal" is missing tokens required by:
  - logging extension: status.info, status.warning
  - core help renderer: text.title

func (ResolvedTheme) Chroma

func (r ResolvedTheme) Chroma() *chroma.Style

Chroma returns the resolved *chroma.Style for syntax highlighting. nil means no highlighting; the upstream chroma fallback applies at render time.

func (ResolvedTheme) Glamour

func (r ResolvedTheme) Glamour() *ansi.StyleConfig

Glamour returns the resolved *ansi.StyleConfig for markdown rendering. nil means use glamour's own default at render time.

func (ResolvedTheme) HasToken

func (r ResolvedTheme) HasToken(t Token) bool

HasToken reports whether token t is covered on this ResolvedTheme, either directly or via the alias chain.

A token explicitly set to the zero lipgloss.Style counts as covered (the author opted in). Consumers that need the style still call ResolvedTheme.Style.

func (ResolvedTheme) Huh

func (r ResolvedTheme) Huh() huh.Theme

Huh returns the huh.Theme from Palette.Huh, or the PromptFromTokens fallback when that field was nil.

func (ResolvedTheme) ListEnumerator

func (r ResolvedTheme) ListEnumerator() list.Enumerator

ListEnumerator returns the default list enumerator. The zero ResolvedTheme returns nil (list.Bullet at the call site); Theme.Resolve always produces a non-nil value.

func (ResolvedTheme) MissingTokens

func (r ResolvedTheme) MissingTokens(req Requirement) []Token

MissingTokens returns tokens from req that this ResolvedTheme does not cover. The result is sorted lexically; empty means fully satisfied.

func (ResolvedTheme) Name

func (r ResolvedTheme) Name() string

Name returns the theme name from Theme.Name. Empty means a nameless theme, typically the zero ResolvedTheme fallback.

func (ResolvedTheme) Style

func (r ResolvedTheme) Style(t Token) lipgloss.Style

Style returns the lipgloss.Style for token t, walking the alias chain when t has no direct entry. The chain is the merge of DefaultAliases and Palette.Aliases folded at Theme.Resolve time.

When the chain bottoms out, Style returns the zero lipgloss.Style (terminal default). A cyclic chain returns the zero style via a per-call seen set; Theme.Resolve also reports cycles at construction.

func (ResolvedTheme) TableBorder

func (r ResolvedTheme) TableBorder() lipgloss.Border

TableBorder returns the default table border. The zero ResolvedTheme returns the zero lipgloss.Border; Theme.Resolve always produces a usable border (lipgloss.NormalBorder when none was set).

func (ResolvedTheme) Tokens

func (r ResolvedTheme) Tokens() []Token

Tokens returns the tokens this theme set explicitly. The slice is freshly allocated; callers may modify it without affecting the theme.

func (ResolvedTheme) Variant

func (r ResolvedTheme) Variant() Variant

Variant returns the variant Theme.Resolve picked. VariantUnset means no declaration; treat it as compatible with every Capabilities snapshot. Use it for diagnostics, not to skip applying the theme (Resolve already picked a compatible palette).

type Resolver

type Resolver interface {
	Resolve(Capabilities) ResolvedTheme
}

Resolver resolves a theme against runtime Capabilities. Most themes declare one Palette per Variant and use Theme.Resolve. Implement Resolver directly only when palette choice cannot be expressed that way.

Theme implements Resolver.

Example

ExampleResolver shows the escape hatch for themes that need to pick a palette based on runtime Capabilities in a way one Palette per Variant cannot express. Most themes never need this — the built-in catalog and every straight programmatic theme satisfies Resolver via the Theme.Resolve method that comes with the struct.

package main

import (
	"fmt"

	"charm.land/lipgloss/v2"

	"nabat.dev/theme"
)

func main() {
	r := capabilityAwareResolver{}
	rt := r.Resolve(theme.Capabilities{Dark: false, Interactive: true})
	fmt.Println(rt.Style(theme.StatusError).GetBold())
}

// capabilityAwareResolver picks a palette by examining capabilities at
// resolve time, branching on the Profile field in a way one Palette
// per Variant cannot express directly.
type capabilityAwareResolver struct{}

func (capabilityAwareResolver) Resolve(c theme.Capabilities) theme.ResolvedTheme {
	fg := lipgloss.Color("#E05454")
	if !c.Dark {
		fg = lipgloss.Color("#A03030")
	}
	t := theme.Theme{
		Name: "capability-aware",
		Variants: map[theme.Variant]theme.Palette{
			theme.VariantUnset: {
				Tokens: map[theme.Token]lipgloss.Style{
					theme.StatusError: lipgloss.NewStyle().Foreground(fg).Bold(true),
				},
			},
		},
	}
	return t.Resolve(c)
}
Output:
true

type Theme

type Theme struct {
	// Name identifies the theme in errors, the catalog key, and
	// [ResolvedTheme.Name]. Built-ins require it; programmatic themes
	// may leave it empty.
	Name string

	// Variants maps each declared [Variant] to its [Palette].
	// An empty map yields a zero [ResolvedTheme] from [Theme.Resolve]
	// (every Style call returns the zero [lipgloss.Style]).
	Variants map[Variant]Palette

	// Default is the variant [Theme.Resolve] picks when [Capabilities]
	// do not match a declared variant. For a single-variant theme the
	// lone key wins even when Default is [VariantUnset].
	Default Variant

	// ListEnum is the default enumerator for list output. Nil resolves
	// to [list.Bullet].
	ListEnum list.Enumerator

	// TableBorder is the default table border. The zero
	// [lipgloss.Border] resolves to [lipgloss.NormalBorder].
	TableBorder lipgloss.Border

	// PromptKnobs are theme-wide prompt settings applied to
	// token-derived and palette prompt styles.
	PromptKnobs PromptKnobs
}

Theme is declarative CLI styling: Palette entries per Variant, a default variant, and cross-variant defaults. Theme.Resolve returns an immutable ResolvedTheme. Safe to copy by value, but the copy shares the Variants map; use Theme.Clone or [nabat.WithThemeOverride] to tweak. Implements Resolver.

Example

ExampleTheme demonstrates the canonical declarative shape: a Theme is data — one Palette per declared variant, plus cross-variant defaults. Resolution picks a variant based on Capabilities and applies framework defaults for any nil/empty cascade slot.

package main

import (
	"fmt"

	"charm.land/lipgloss/v2"

	"nabat.dev/theme"
)

func main() {
	t := theme.Theme{
		Name:    "plain",
		Default: theme.VariantDark,
		Variants: map[theme.Variant]theme.Palette{
			theme.VariantDark: {
				Tokens: map[theme.Token]lipgloss.Style{
					theme.StatusSuccess: lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#7EC87E")),
				},
			},
		},
	}

	r := t.Resolve(theme.Capabilities{Dark: true, Interactive: true})
	fmt.Println(r.Name())
	fmt.Println(r.Style(theme.StatusSuccess).GetBold())
}
Output:
plain
true

func Get

func Get(name string) (Theme, error)

Get returns the Theme registered under name. On a miss the error lists every available name:

t, err := theme.Get("draculaa") // typo
// err: nabat/theme: no theme named "draculaa"; available: [...]

The first call parses and caches the embedded manifest; later calls return a cloned cached Theme. Parse errors surface on first access.

Example

ExampleGet shows the typical lookup path: resolve a built-in theme by name, then call Theme.Resolve against a Capabilities snapshot to read the resulting ResolvedTheme. Most consumers reach this through nabat.WithTheme rather than calling Get directly.

package main

import (
	"fmt"

	"nabat.dev/theme"
)

func main() {
	t, err := theme.Get(theme.Default)
	if err != nil {
		fmt.Println("error:", err)
		return
	}

	r := t.Resolve(theme.Capabilities{Dark: true, Interactive: true})
	fmt.Println(r.Name())
}
Output:
default
Example (Unknown)

ExampleGet_unknown shows the actionable error returned when a theme name does not exist in the catalog. The error lists every available name so users can fix typos without consulting docs.

package main

import (
	"fmt"

	"nabat.dev/theme"
)

func main() {
	_, err := theme.Get("draculaa")
	fmt.Println(err != nil)
}
Output:
true

func (Theme) Clone

func (t Theme) Clone() Theme

Clone returns a shallow copy of t with an independent Variants map. Palette values inside the map are shared; assigning to Clone().Variants[k] does not affect the original.

func (Theme) Resolve

func (t Theme) Resolve(c Capabilities) ResolvedTheme

Resolve picks a Variant for Capabilities, fills zero Palette fields with framework defaults, and returns an immutable ResolvedTheme safe to share across goroutines.

One declared variant wins (Theme.Default ignored); otherwise Default picks. A zero default with multiple variants yields the zero ResolvedTheme. Resolve discards callback and alias-cycle errors; use Theme.ResolveErr for those. On Palette.GlamourFor failure the glamour slot stays empty.

func (Theme) ResolveErr

func (t Theme) ResolveErr(c Capabilities) (ResolvedTheme, error)

ResolveErr behaves like Theme.Resolve but also returns errors from per-Palette callbacks (today Palette.GlamourFor) and alias-cycle validation.

On failure ResolveErr still returns a usable ResolvedTheme (failed glamour or alias slots stay empty) plus a non-nil error. Callers must check the error for diagnostics and may still apply the returned theme.

func (Theme) Validate

func (t Theme) Validate() error

Validate reports structural problems with the theme. It fails when Theme.Default names a missing variant, or Default is set with an empty Theme.Variants. Empty themes without Default are valid.

func (Theme) With

func (t Theme) With(overrides ...Override) Theme

With returns a derived Theme with overrides applied to every variant. The receiver is not modified; the returned theme owns fresh per-variant maps. Overrides apply left to right; the right-most override of a token wins. With panics if any override is nil.

Example:

dracula, _ := theme.Get(theme.Dracula)
mine := dracula.With(
    theme.SetToken(theme.StatusError, magenta),
    theme.SetAlias(theme.ListItem, theme.TextSecondary),
)

type Token

type Token string

Token names a semantic style slot in a ResolvedTheme. Tokens are dotted lowercase strings (for example "status.success") that identify role, not appearance. The constants below are Nabat's well-known set; token names are an open set, not an enum.

const (
	// StatusSuccess marks affirmative output (Context.Success, ok badges).
	StatusSuccess Token = "status.success"

	// StatusWarning marks warnings (Context.Warn, degraded operation).
	StatusWarning Token = "status.warning"

	// StatusError marks failure output (Context.Error, rejected commands).
	StatusError Token = "status.error"

	// StatusInfo marks neutral status narrative (Context.Info, retries).
	StatusInfo Token = "status.info"

	// TextPrimary styles primary body text and table/list/tree item text.
	TextPrimary Token = "text.primary"

	// TextSecondary styles descriptive text and prose.
	TextSecondary Token = "text.secondary"

	// TextTitle styles help titles, table headers, and section titles.
	TextTitle Token = "text.title"

	// TextLink styles hyperlinks.
	TextLink Token = "text.link"

	// AccentPrimary styles labels and key chrome accents.
	AccentPrimary Token = "accent.primary"

	// TextMuted styles de-emphasized chrome (borders, enumerators).
	TextMuted Token = "text.muted"

	// CodeSurface styles code block backgrounds.
	CodeSurface Token = "code.surface"

	// TableBorder styles characters drawn between table cells.
	TableBorder Token = "table.border"

	// TableHeader styles cells in a table header row.
	TableHeader Token = "table.header"

	// TableCell styles cells in a table data row.
	TableCell Token = "table.cell"

	// ListItem styles list item text.
	ListItem Token = "list.item"

	// ListEnumerator styles list enumerator markers.
	ListEnumerator Token = "list.enumerator"

	// TreeItem styles tree item text.
	TreeItem Token = "tree.item"

	// TreeEnumerator styles tree enumerator markers.
	TreeEnumerator Token = "tree.enumerator"

	// SpinnerActive styles the live [Spinner] icon. Unset themes fall
	// through to [StatusInfo] via [DefaultAliases].
	SpinnerActive Token = "spinner.active"

	// StatusActive styles the spinner icon on active [Status] rows.
	// Unset themes fall through to [StatusInfo] via [DefaultAliases].
	StatusActive Token = "status.active"
)

Well-known semantic tokens used by Nabat core consumers.

type UnicodeLevel

type UnicodeLevel uint8

UnicodeLevel is how much Unicode the terminal can render. Tiers are monotonic: higher values include the lower ones, so consumers can compare for "at least N".

const (
	UnicodeASCII UnicodeLevel = iota
	UnicodeWide
	UnicodeEmoji
)

UnicodeLevel constants:

  • UnicodeASCII: 7-bit ASCII only; use "+", "-", "|" for boxes.
  • UnicodeWide: box-drawing, bullets, and arrows (framework default).
  • UnicodeEmoji: emoji and multi-codepoint sequences.

type Variant

type Variant string

Variant is a theme's intended luminance or TTY context (dark, light, or notty). Runtime diagnostics and a future "--theme-variant" override read this slot.

The zero value (VariantUnset) means the theme did not declare a target; consumers should treat it as compatible with every Capabilities snapshot.

const (
	// VariantUnset is the zero value: no target variant declared.
	VariantUnset Variant = ""

	// VariantDark indicates a palette designed for dark terminals.
	VariantDark Variant = "dark"

	// VariantLight indicates a palette designed for light terminals.
	VariantLight Variant = "light"

	// VariantNoTTY indicates a palette designed for non-TTY output.
	VariantNoTTY Variant = "notty"
)

Variant constants match the manifest "variant" field and Theme.Default / Theme.Variants keys.

Directories

Path Synopsis
internal
manifest
Package manifest turns a Nabat theme manifest (DTCG JSON) into a *Compiled intermediate value.
Package manifest turns a Nabat theme manifest (DTCG JSON) into a *Compiled intermediate value.

Jump to

Keyboard shortcuts

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