A named, inheritable bundle of visual attributes.
A face describes how text should look: foreground/background colors,
bold/italic/underline, underline style and color, strikethrough, blend
(opacity), and GUI-only font properties. Faces form an inheritance chain
where nil attributes resolve up to the parent face.
Inheritance
Every face except "default" has a parent. When resolving a face's
effective style, nil fields inherit from the parent. The resolution
walks up the chain until it finds a non-nil value or reaches "default".
The inheritance chain follows tree-sitter capture name structure:
"keyword.function" inherits from "keyword", which inherits from
"default". This replaces the suffix-fallback lookup in
Theme.style_for_capture/2 with proper structural inheritance.
GUI-only fields
font_family, font_weight, font_slant, and font_features are
silently ignored in the TUI backend. The TUI uses bold/italic attrs
from the base fields; the GUI uses the font fields to select specific
font variants.
Blend
The blend field (0-100) controls opacity. In the GUI, this maps to
alpha blending in the Metal shader. In the TUI, values below 50 map
to the dim attribute; values 50-100 are rendered at full brightness.
nil means inherit from parent (default is 100 = fully opaque).
Summary
Types
RGB color as a 24-bit integer (e.g., 0xFF6C6B).
Font slant for GUI rendering.
Font weight for GUI rendering.
Underline rendering style.
Functions
The default face. All inheritance chains terminate here.
Creates a face from a style keyword list (as used in Theme syntax maps).
Infers the parent face name from a dotted capture name.
Creates an anonymous face from keyword attributes.
Resolves a face's effective attributes by walking the inheritance chain.
Converts a face to a style keyword list compatible with Protocol.style().
Types
@type color() :: non_neg_integer()
RGB color as a 24-bit integer (e.g., 0xFF6C6B).
@type font_slant() :: :roman | :italic | :oblique
Font slant for GUI rendering.
@type font_weight() :: :thin | :light | :regular | :medium | :bold | :black
Font weight for GUI rendering.
@type t() :: %Minga.Core.Face{ bg: color() | nil, blend: 0..100 | nil, bold: boolean() | nil, fg: color() | nil, font_family: String.t() | nil, font_features: %{required(String.t()) => boolean()} | nil, font_slant: font_slant() | nil, font_weight: font_weight() | nil, inherit: String.t() | nil, italic: boolean() | nil, name: String.t(), reverse: boolean() | nil, strikethrough: boolean() | nil, underline: boolean() | nil, underline_color: color() | nil, underline_style: underline_style() | nil }
@type underline_style() :: :line | :curl | :dashed | :dotted | :double
Underline rendering style.
Functions
@spec default() :: t()
The default face. All inheritance chains terminate here.
Doom One colors, no decorations, fully opaque.
Creates a face from a style keyword list (as used in Theme syntax maps).
This converts the existing [fg: 0xFF6C6B, bold: true] format into a
Face struct. The name and optional inherit are provided separately.
Examples
iex> Face.from_style("keyword", [fg: 0xC678DD, bold: true])
%Face{name: "keyword", fg: 0xC678DD, bold: true, inherit: nil}
iex> Face.from_style("keyword.function", [fg: 0xC678DD, bold: true], inherit: "keyword")
%Face{name: "keyword.function", fg: 0xC678DD, bold: true, inherit: "keyword"}
Infers the parent face name from a dotted capture name.
Strips the last .segment to produce the parent. Single-segment
names (e.g., "keyword") return "default".
Examples
iex> Face.infer_parent("keyword.function.builtin")
"keyword.function"
iex> Face.infer_parent("keyword")
"default"
iex> Face.infer_parent("default")
nil
Creates an anonymous face from keyword attributes.
Convenience constructor for inline styles in renderer modules.
Equivalent to from_style("_", attrs) but more concise.
Examples
iex> Face.new(fg: 0xFF6C6B, bold: true)
%Face{name: "_", fg: 0xFF6C6B, bold: true}
iex> Face.new()
%Face{name: "_"}
Resolves a face's effective attributes by walking the inheritance chain.
Takes a face and a lookup function (String.t() -> t() | nil) that
retrieves parent faces by name. Returns a fully resolved face with no
nil attribute values (every field has a concrete value from the
nearest ancestor that defines it, bottoming out at default/0).
Examples
iex> comment = %Face{name: "comment", inherit: "default", fg: 0x5B6268, italic: true}
iex> resolved = Face.resolve(comment, fn "default" -> Face.default(); _ -> nil end)
iex> resolved.fg
0x5B6268
iex> resolved.italic
true
iex> resolved.bold
false
Converts a face to a style keyword list compatible with Protocol.style().
This is the bridge between the face system and the existing render
pipeline. Only includes fields that differ from the base face
(defaults to default/0), matching the existing convention where
styles are sparse (absent fields use the editor's default
colors/attributes).
The keyword list may include: fg, bg, bold, italic, underline,
strikethrough, underline_style, underline_color, blend.