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-freeflicker-*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:optionslot 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:optionwhen the listbox is suggesting facets rather than records, so suggestions can look distinct from results.:suggestion_token— thestatus:/activetoken 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_editoritself withmax-sm:utilities, since a 252px calendar in a dropdown is unusable on a phone; this part exists for avanillahost 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 is0. Dimmed rather than hidden, and still selectable: hiding it answers "why did that option disappear?" with silence, where0answers 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 underon_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 whenfacet_triggeris set, and referenced by the input'saria-describedbyso the affordance is announced on focus rather than only being visible.:dispatch_hint— the "press Enter to search" affordance shown beside the input whiledispatch: :enterholds undispatched text (Spec 020). Also the target of the input'saria-describedbyin that state.:hint— the "keep typing to narrow results" / min-chars hint row; also the tail row shown once apaginate-d list hitsmax_windowsor a provider ignoring:offsetis 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, orFlicker.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_buttonwhich 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 (atitle) 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 thekey:valuetext 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:selectedslot (Spec 013): the container around the custom-rendered selected items (e.g. an avatar stack); replaces:chip_listwhen a:selectedslot 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 exceedsmax_visible.:kbd_hint— the<kbd>discoverability hint rendered whenactivate_with_keyboardis set (Spec 006); also used for the footer's ↑↓/↵/esc hints inFlicker.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 newresult.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_hintpiled 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).
Summary
Types
An override: a full theme, or a partial map/keyword list of parts.
A class-per-part theme map.
Functions
The daisyUI preset: input, menu, dropdown-content, and friends.
Resolves the effective theme for a component instance, merging (lowest to
highest precedence) vanilla/0, config :flicker, :default_theme, and
component_override.
The Tailwind preset: utility classes only, no component library.
The default preset: plain, framework-free flicker-* class names.
Types
An override: a full theme, or a partial map/keyword list of parts.
@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.
Functions
@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().
Resolves the effective theme for a component instance, merging (lowest to
highest precedence) vanilla/0, config :flicker, :default_theme, and
component_override.
@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().
@spec vanilla() :: t()
The default preset: plain, framework-free flicker-* class names.