# `Flicker.Theme`
[🔗](https://github.com/team-alembic/flicker/blob/v0.1.0/lib/flicker/theme.ex#L1)

A class-per-part theme map for the select component (ADR-002).

Flicker never hardcodes a CSS framework. Instead every visually distinct
part of the rendered markup has a named key here, and a preset supplies
the class string for each part.

Three presets ship:

  * `vanilla/0` — plain, framework-free `flicker-*` class names a host
    hooks their own CSS onto. The default.
  * `tailwind/0` — Tailwind utility classes, no component library.
  * `daisy_ui/0` — daisyUI component classes (`input`, `menu`,
    `dropdown-content`, ...).

Every preset covers every key below — see `Flicker.ThemeTest` for the
assertion that keeps that true as keys are added.

## Parts

  * `:wrapper` — the outermost element.
  * `:search_input` — the text input.
  * `:clear_button` — the button that clears the current selection.
  * `:listbox` — the results dropdown.
  * `:option` — a single result row.
  * `:option_active` — added to the active (keyboard-highlighted) option;
    the JS hook toggles this class client-side.
  * `:option_label` — the option's primary label span (default rendering
    only; an `:option` slot owns its own markup).
  * `:option_sublabel` — the option's secondary/sublabel span (default
    rendering only).
  * `:suggestion` — a facet key/value suggestion row (Spec 003); rendered
    in place of `:option` when the listbox is suggesting facets rather
    than records, so suggestions can look distinct from results.
  * `:suggestion_token` — the `status:`/`active` token span inside a
    suggestion row (kbd/mono treatment).
  * `:loading_state` — the listbox's loading row.
  * `:empty_state` — the listbox's no-results row.
  * `:error_state` — the listbox's error row.
  * `:facet_editor` — Spec 019 facet-editor part.
  * `:facet_editor_header` — Spec 019 facet-editor part.
  * `:facet_editor_body` — Spec 019 facet-editor part.
  * `:facet_editor_footer` — Spec 019 facet-editor part.
  * `:facet_editor_sheet` — the bottom-sheet variant of the pop-out. The
    shipped framework presets fold the sheet behaviour into `:facet_editor`
    itself with `max-sm:` utilities, since a 252px calendar in a dropdown is
    unusable on a phone; this part exists for a `vanilla` host that wants to
    target the sheet with its own CSS.
  * `:preset_rail` — Spec 019 facet-editor part.
  * `:preset_group_label` — Spec 019 facet-editor part.
  * `:preset_row` — Spec 019 facet-editor part.
  * `:preset_row_selected` — Spec 019 facet-editor part.
  * `:preset_row_range` — Spec 019 facet-editor part.
  * `:calendar` — Spec 019 facet-editor part.
  * `:calendar_nav` — Spec 019 facet-editor part.
  * `:calendar_nav_button` — Spec 019 facet-editor part.
  * `:calendar_month_label` — Spec 019 facet-editor part.
  * `:calendar_weekday` — Spec 019 facet-editor part.
  * `:calendar_grid` — Spec 019 facet-editor part.
  * `:calendar_day` — Spec 019 facet-editor part.
  * `:calendar_day_today` — Spec 019 facet-editor part.
  * `:calendar_day_selected` — Spec 019 facet-editor part.
  * `:calendar_day_in_range` — Spec 019 facet-editor part.
  * `:calendar_day_edge` — Spec 019 facet-editor part.
  * `:calendar_day_disabled` — Spec 019 facet-editor part.
  * `:dial` — Spec 019 facet-editor part.
  * `:dial_track` — Spec 019 facet-editor part.
  * `:dial_fill` — Spec 019 facet-editor part.
  * `:dial_thumb` — Spec 019 facet-editor part.
  * `:dial_input` — Spec 019 facet-editor part.
  * `:dial_value_label` — Spec 019 facet-editor part.
  * `:switch` — Spec 019 facet-editor part.
  * `:switch_thumb` — Spec 019 facet-editor part.
  * `:switch_on` — Spec 019 facet-editor part.
  * `:editor_error` — Spec 019 facet-editor part.
  * `:facet_count` — the match count rendered beside a facet value
    (Spec 021).
  * `:facet_count_zero` — added to a value row whose count is `0`. Dimmed
    rather than hidden, and still selectable: hiding it answers "why did that
    option disappear?" with silence, where `0` answers it.
  * `:facet_pill_invalid` — a committed-facet pill whose value failed to
    cast (Spec 023). Carries the error styling; the message itself renders in
    `:facet_error_message`, never as a tooltip.
  * `:facet_error_message` — the visible reason text beside an invalid pill.
  * `:facet_error_icon` — the error glyph, so the state is never signalled by
    colour alone.
  * `:facet_correction` — a suggested replacement value ("did you mean...").
  * `:facet_correction_accept` — the accept affordance on the top suggestion.
  * `:dispatch_blocked` — the visible "filter not applied" state under
    `on_invalid: :require`.
  * `:results_stale` — added to the listbox while a new search is in
    flight *and* previous results are still on screen (Spec 020). The
    previous set stays rendered rather than the list emptying and refilling;
    this part is what marks it as not-yet-current.
  * `:facet_trigger_hint` — the discoverability affordance naming the
    facet-entry trigger character ("Type @ to filter", Spec 024). Only
    rendered when `facet_trigger` is set, and referenced by the input's
    `aria-describedby` so the affordance is announced on focus rather than
    only being visible.
  * `:dispatch_hint` — the "press Enter to search" affordance shown
    beside the input while `dispatch: :enter` holds undispatched text
    (Spec 020). Also the target of the input's `aria-describedby` in that
    state.
  * `:hint` — the "keep typing to narrow results" / min-chars hint row;
    also the tail row shown once a `paginate`-d list hits `max_windows`
    or a provider ignoring `:offset` is detected (Spec 010) — windowing
    degrades into this same hint rather than a separate one.
  * `:loading_more` — `paginate`-d select only (Spec 010): the themed row
    rendered at the listbox tail while the next window loads.
  * `:chip_list` — multi-select: the wrapper around the selected chips.
  * `:chip` — multi-select: a single selected-value chip.
  * `:chip_remove` — multi-select: the per-chip remove button.
  * `:multi_field` — the bordered field box that holds tokens (multi-select
    chips, or `Flicker.search/1`'s committed facet pills) and the text
    input together, so the tokens sit *inside* the input, not above it.
    Replaces `:wrapper`+`:search_input`'s border for those cases.
  * `:multi_input` — the borderless text input inside `:multi_field` (the
    box owns the border; the input just grows to fill).
  * `:multi_clear` — multi-select: the "clear all" button inside
    `:multi_field`, vertically centred in the box (in normal flow, unlike
    the single-select `:clear_button` which overlays its input).
  * `:facet_pill_list` — `Flicker.search/1` (Spec 012): the wrapper around
    the committed-facet pills inside `:multi_field`.
  * `:facet_pill` — `Flicker.search/1`: a single committed-facet pill. Its
    field name is exposed on hover (a `title`) rather than always shown.
  * `:facet_pill_field` — the pill's small field label (e.g. "Status"),
    shown *without* a colon so the pill reads as a labelled value rather
    than as the `key:value` text it came from (Spec 012).
  * `:facet_pill_value` — the pill's primary value label (e.g. "Active").
  * `:facet_pill_remove` — the pill's remove (`×`) button.
  * `:selected_stack` — multi-select with a `:selected` slot (Spec 013): the
    container around the custom-rendered selected items (e.g. an avatar
    stack); replaces `:chip_list` when a `:selected` slot is given.
  * `:selected_item` — the per-item wrapper inside `:selected_stack`
    (positioning context for its remove control).
  * `:selected_overflow` — the "+N" token shown when the selection exceeds
    `max_visible`.
  * `:kbd_hint` — the `<kbd>` discoverability hint rendered when
    `activate_with_keyboard` is set (Spec 006); also used for the
    footer's ↑↓/↵/esc hints in `Flicker.palette/1` (Spec 008).
  * `:backdrop` — `Flicker.palette/1`: the full-viewport overlay backdrop
    behind the panel (Spec 008).
  * `:panel` — `Flicker.palette/1`: the centred dialog panel.
  * `:palette_input` — `Flicker.palette/1`: the large search input (in
    place of `:search_input`, which the plain select still uses).
  * `:group_header` — a non-interactive row rendered before the first
    result of each new `result.group` (Spec 008); unused when no result
    in the list carries a `:group`.
  * `:footer` — `Flicker.palette/1`: the panel's footer row of keyboard
    hints.
  * `:footer_hint` — `Flicker.palette/1`: the individual `<kbd>` chips
    inside the footer (↑↓/↵/esc). Distinct from `:kbd_hint`, which
    overlays the select input and is absolutely positioned — the footer
    chips sit in normal flow, so reusing `:kbd_hint` piled them all at the
    panel's right edge.
  * `:palette_close` — `Flicker.palette/1`: the panel's close control.
    Distinct from `:clear_button` (which overlays the select input, absolute
    positioned) so the close control doesn't land on top of the palette
    input.

## Resolution

`resolve/1` merges, in increasing precedence: `vanilla/0`, then
`config :flicker, default_theme:`, then a per-component override:

    config :flicker, default_theme: %{search_input: "my-input"}

    <Flicker.select theme={%{listbox: "my-listbox"}} ... />

An override may be a `%Flicker.Theme{}` struct (replaces the base
wholesale) or a map/keyword list of just the parts to change (merged onto
the base with `struct!/2`).

# `override`

```elixir
@type override() :: t() | map() | keyword() | nil
```

An override: a full theme, or a partial map/keyword list of parts.

# `t`

```elixir
@type t() :: %Flicker.Theme{
  backdrop: String.t(),
  calendar: String.t(),
  calendar_day: String.t(),
  calendar_day_disabled: String.t(),
  calendar_day_edge: String.t(),
  calendar_day_in_range: String.t(),
  calendar_day_selected: String.t(),
  calendar_day_today: String.t(),
  calendar_grid: String.t(),
  calendar_month_label: String.t(),
  calendar_nav: String.t(),
  calendar_nav_button: String.t(),
  calendar_weekday: String.t(),
  chip: String.t(),
  chip_list: String.t(),
  chip_remove: String.t(),
  clear_button: String.t(),
  dial: String.t(),
  dial_fill: String.t(),
  dial_input: String.t(),
  dial_thumb: String.t(),
  dial_track: String.t(),
  dial_value_label: String.t(),
  dispatch_blocked: String.t(),
  dispatch_hint: String.t(),
  editor_error: String.t(),
  empty_state: String.t(),
  error_state: String.t(),
  facet_correction: String.t(),
  facet_correction_accept: String.t(),
  facet_count: String.t(),
  facet_count_zero: String.t(),
  facet_editor: String.t(),
  facet_editor_body: String.t(),
  facet_editor_footer: String.t(),
  facet_editor_header: String.t(),
  facet_editor_sheet: String.t(),
  facet_error_icon: String.t(),
  facet_error_message: String.t(),
  facet_pill: String.t(),
  facet_pill_field: String.t(),
  facet_pill_invalid: String.t(),
  facet_pill_list: String.t(),
  facet_pill_remove: String.t(),
  facet_pill_value: String.t(),
  facet_trigger_hint: String.t(),
  footer: String.t(),
  footer_hint: String.t(),
  group_header: String.t(),
  hint: String.t(),
  kbd_hint: String.t(),
  listbox: String.t(),
  loading_more: String.t(),
  loading_state: String.t(),
  multi_clear: String.t(),
  multi_field: String.t(),
  multi_input: String.t(),
  option: String.t(),
  option_active: String.t(),
  option_label: String.t(),
  option_sublabel: String.t(),
  palette_close: String.t(),
  palette_input: String.t(),
  panel: String.t(),
  preset_group_label: String.t(),
  preset_rail: String.t(),
  preset_row: String.t(),
  preset_row_range: String.t(),
  preset_row_selected: String.t(),
  results_stale: String.t(),
  search_input: String.t(),
  selected_item: String.t(),
  selected_overflow: String.t(),
  selected_stack: String.t(),
  suggestion: String.t(),
  suggestion_token: String.t(),
  switch: String.t(),
  switch_on: String.t(),
  switch_thumb: String.t(),
  wrapper: String.t()
}
```

A class-per-part theme map.

# `daisy_ui`

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

The daisyUI preset: `input`, `menu`, `dropdown-content`, and friends.

    <Flicker.select theme={Flicker.Theme.daisy_ui()} ... />

or globally via `config :flicker, default_theme: Flicker.Theme.daisy_ui()`.

# `resolve`

```elixir
@spec resolve(override()) :: t()
```

Resolves the effective theme for a component instance, merging (lowest to
highest precedence) `vanilla/0`, `config :flicker, :default_theme`, and
`component_override`.

# `tailwind`

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

The Tailwind preset: utility classes only, no component library.

    <Flicker.select theme={Flicker.Theme.tailwind()} ... />

or globally via `config :flicker, default_theme: Flicker.Theme.tailwind()`.

# `vanilla`

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

The default preset: plain, framework-free `flicker-*` class names.

---

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