# `Minga.Core.Face`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/core/face.ex#L1)

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).

# `color`

```elixir
@type color() :: non_neg_integer()
```

RGB color as a 24-bit integer (e.g., `0xFF6C6B`).

# `font_slant`

```elixir
@type font_slant() :: :roman | :italic | :oblique
```

Font slant for GUI rendering.

# `font_weight`

```elixir
@type font_weight() :: :thin | :light | :regular | :medium | :bold | :black
```

Font weight for GUI rendering.

# `t`

```elixir
@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()) =&gt; 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
}
```

# `underline_style`

```elixir
@type underline_style() :: :line | :curl | :dashed | :dotted | :double
```

Underline rendering style.

# `default`

```elixir
@spec default() :: t()
```

The default face. All inheritance chains terminate here.

Doom One colors, no decorations, fully opaque.

# `from_style`

```elixir
@spec from_style(String.t(), keyword(), keyword()) :: t()
```

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"}

# `infer_parent`

```elixir
@spec infer_parent(String.t()) :: String.t() | nil
```

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

# `new`

```elixir
@spec new(keyword()) :: t()
```

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: "_"}

# `resolve`

```elixir
@spec resolve(t(), (String.t() -&gt; t() | nil)) :: t()
```

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

# `to_style`

```elixir
@spec to_style(t(), t()) :: keyword()
```

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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
