# OhMyEtch Facets > Complete reference for facet components and recipes. This file contains all documentation content in a single document following the llmstxt.org standard. ## Argument Modes # Argument Modes Argument mode decides what a facet value becomes in the backend query. Select, SearchSelect, Radio List, and Checkbox List share the `FacetQueryProperties` mode system. Search Facet implements the same mode names separately because text search needs partial-match aliases and multi-argument text bundling. ## Mode Summary | Mode | Runtime facet type | Used by | Value meaning | | --- | --- | --- | --- | | `default` | selected query argument, such as `category_name` or `post_type` | Search and option facets | The value is assigned to a standard WP query argument. | | `meta` | `meta_value` | Search and option facets | The value is compared against a configured post meta key. | | `tax` | `tax_query` for option facets, `tax_query_like` for Search Facet | Search and option facets | The value is interpreted as a term field for a configured taxonomy. | | `date` | `date_query` | Search and option facets | The value is interpreted as a date range for a configured post date column. | | `acf_relationship` | `acf_relationship` | Search and option facets | The value is a related post ID, or comma-separated IDs, matched against a serialized relationship meta field. | | multi-argument Search Facet | `query_bundle` | Search Facet only | One typed text value expands into multiple query clauses. | | multi-argument option facet | `mapped_bundle` | Select, SearchSelect, Radio List, Checkbox List | One authored option expands into multiple query clauses with per-option mapped values. | ## Default Query Argument Default mode writes a standard query argument. Option facets use values from `FacetQueryProperties`, including: ```text search, p, name, title, post_type, order, orderby, category_name, tag, author, author_name, year, monthnum, posts_per_page ``` Search Facet has a filter-oriented set and uses partial aliases for text fields: ```text search, p, title_like, name_like, post_type, category_name_like, tag_like, author, author_name_like, year, monthnum ``` Use default mode for common query arguments like post type, category slug, tag slug, author, year, month, ordering, and posts per page. ## Meta Argument Meta mode uses: - `meta_key` - `meta_compare` - `meta_type` Option facets default `meta_compare` to `=`, while Search Facet defaults it to `LIKE`. Supported compares are: ```text =, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, BETWEEN, NOT BETWEEN, EXISTS, NOT EXISTS ``` Use comma-separated values for `IN`, `NOT IN`, `BETWEEN`, and `NOT BETWEEN`. E2E coverage verifies exact numeric matches, char matches, bool matches, range compares, substring compares, set compares, and values with spaces. ## Taxonomy Argument Tax mode uses: - `taxonomy` - `tax_field` Supported fields are: ```text slug, term_id, name, term_taxonomy_id ``` Option facets produce exact `tax_query` clauses. Search Facet produces `tax_query_like`, so it can search term names or slugs by typed text where supported by the backend clause handling. The taxonomy E2E scenario verifies `facet_color` with all four fields and also covers multiple taxonomy filters. ## Date Query Date mode uses a configured date column: ```text post_date, post_modified, post_date_gmt, post_modified_gmt ``` Authored option values should use: ```text YYYY-MM-DD,YYYY-MM-DD ``` The first date is the start, and the second is the end. Search Facet sends the typed input through the same `date_query` facet type. ## ACF Relationship ACF relationship mode uses `acf_relationship_argument.meta_key`, which is the relationship field stored on the posts being filtered. The value must be a numeric related post ID, or a comma-separated list of related post IDs. It does not reverse-resolve a bidirectional relationship stored only on the related posts. Example rule: ```text If you filter Events by Speaker, the Event posts must store the selected Speaker IDs in the configured relationship field. ``` The `wp_query_args` E2E scenario verifies select and checkbox ACF relationship filters, counts, unavailable state, comma-list option values, and direct URL hydration. ## Search Facet Query Bundle When `target_multiple_query_arguments` is enabled on Search Facet, the control renders as `query_bundle`. One typed search value is applied to every row in `query_argument_mappings`. The `query_argument_relation` setting controls whether the generated clauses are joined with `AND` or `OR`. Search mappings normalize common exact types to partial search aliases, for example `title` becomes `title_like` and `category_name` becomes `category_name_like`. Use this when one text field should search several places, such as title or priority, or title or category. ## Option Facet Mapped Bundle When `target_multiple_query_arguments` is enabled on Select, SearchSelect, Radio List, or Checkbox List, the root facet type becomes `mapped_bundle`. The parent facet defines `query_argument_mappings`. Each option defines `query_value_mappings`. At runtime, an option is valid only when its value mappings can be matched to the parent mapping keys. Example: ```text Parent mappings: - category -> default category_name - priority -> meta facet_priority Option "News + High priority": - category = facets-e2e-news - priority = high ``` The mapped bundle E2E coverage verifies filtering, URL restore, counts, unavailable state, and availability updates after other filters change. --- ## Availability, Counts, and Options # Availability, Counts, and Options Availability is the runtime metadata that tells option facets which values currently produce results. It is computed per target after a facet request and then applied by the client to Select, SearchSelect, Radio List, and Checkbox List controls. ## Options Behavior `options_behavior` controls what happens to unavailable options. | Value | Behavior | | --- | --- | | `all` | Keep every authored option visible and enabled. Counts can still update. | | `disable` | Keep unavailable options visible, but mark them disabled and prevent selection. | | `hide` | Hide unavailable options from the UI. | The option behavior E2E scenario verifies all three modes across Select, SearchSelect, Radio List, and Checkbox List. ## Dynamic Counts `show_counts` enables result counts on option values when availability data exists. Counts are based on the current target scope and the other active filters. For example, after a search narrows results to one post, category options can update to `News category (1)` and `Guides category (0)`. Count rendering depends on each option's `count_display`: - `inline` appends the count to the label text. - `span` writes the count into a separate counter span. ## Cross-Facet Behavior Facet availability is cross-facet. A change in one control can change the option state of another control connected to the same target. Example from E2E behavior: ```text Search "Foo" narrows results to Alpha Foo. Category "news" remains available with count 1. Category "guides" becomes unavailable with count 0. ``` This works for single-mode facets and for mapped bundles. Mapped bundle availability is keyed by a generated control scope so each authored bundle can be counted independently. ## Selected Values That Become Unavailable Select and SearchSelect can reset a selected value when that value becomes unavailable in `disable` or `hide` mode. The option behavior tests cover auto-reset for selected categories after a search makes the selected category unavailable. Radio and checkbox behavior also syncs disabled, hidden, ARIA, and selected state through their runtimes so stale state is not left in the DOM after an update. ## Default Selections Authored default options initialize controls only when there is no URL, runtime, or restored value already present. - Select supports authored default options and has a built-in empty option using the `default_label`. - Radio List supports default options and can also render a built-in reset option. - Checkbox List supports multiple authored defaults. - SearchSelect options do not expose `is_default` in the current PHP component. Direct URL state wins over authored defaults. The URL parameter E2E suite verifies direct-link hydration for several facet families. ## Performance Boundary Availability computation may fan out across descriptors, including mapped bundle descriptors. The endpoint limits expensive paths with query budgets, descriptor truncation metadata, and sampled post IDs. Authoring a large number of mapped bundles is supported, but it has a real availability cost because every bundle may need a count. Use `show_counts` and `disable` or `hide` when users benefit from availability feedback. Leave `options_behavior` as `all` and `show_counts` as false when the UI does not need dynamic option state. --- ## Checkbox List Facet # Checkbox List Facet `Checkbox List Facet` is a multi-select option list. It sends all selected option values for the configured target and argument mode. ## Authoring Structure ```text Checkbox List Facet └── Checkbox List Facet Option ``` ## Parent Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Settings | `target` | empty | Shared target ID. | | Settings | `argument_mode` | `default` | Single argument mode. | | Settings | `target_multiple_query_arguments` | `false` | Enables mapped bundle mode. | | Settings | `options_behavior` | `all` | Availability behavior. | | Settings | `show_counts` | `false` | Enables dynamic counts. | | Settings | `aria_label` | `Filter options` | Accessible name for the checkbox group. | | Mode groups | mode-specific props | varies | See [Argument Modes](../argument-modes). | | Styling | `class` | `ome-checkbox-list-facet-root-default` | Root list class. | ## Option Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Content | `value` | empty | Sent value in single-argument mode. | | Content | `label` | `Option` | Visible label. | | Metadata | `key`, `value` | empty | Optional rows for custom JavaScript integrations. | | Query Value Mappings | `mapping_key`, `value` | empty | Values used when parent mapped bundle mode is enabled. | | Settings | `disabled` | `false` | Prevents selection. | | Settings | `is_default` | `false` | Preselects the option when no restored value exists. | | Settings | `mode` | `checkbox` | Option presentation: `checkbox` or `button`. | | Settings | `used_with_multiple_arguments` | `false` | Switches option authoring to query value mappings. | | Settings | `count_display` | `inline` | Inline count text or separate count span. | | Styling | option, button, indicator, tick, label, input, count classes | component defaults | Option presentation classes. | ## Runtime Notes - Multiple options can be selected at the same time. - `mode = button` changes presentation only; the facet remains multi-select. - The runtime syncs `data-ome-selected-values`, hidden transport inputs, `aria-checked`, disabled state, hidden state, and count displays. ## Example ```text Checkbox List Facet: - target = posts - argument_mode = default - facet_key = name Options: - beta-bar - gamma-baz ``` --- ## Facet Target # Facet Target `Facet Target` is the result container. Facet controls do not replace content directly; they send state for a target ID, and the target receives refreshed loop markup from the facet endpoint. ## Structure ```text Facet Target ├── loop slot └── fallback slot ``` The loop slot renders the normal results. The fallback slot renders when the refreshed loop has no items. ## Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Structure | `tag` | `div` | HTML tag for the live result wrapper. | | Structure | `fallback_tag` | `div` | HTML tag for the fallback wrapper. | | Settings | `target` | empty | Shared target ID used by connected facets. | | Settings | `rerun_scripts` | `true` | Reruns inline scripts returned with refreshed markup. | | Settings | `preview_fallback` | `false` | Shows fallback in Etch preview instead of loop content. | | Settings | `scroll_to_top` | `false` | Scrolls to this target after connected facet updates. | | Settings | `used_with_map` | `false` | Marks the target as paired with a Map Facet. | | Settings | `load_more_on_scroll` | `false` | Enables infinite scroll controls inside the target. | | Settings | `load_more_batch_size` | `10` | Batch size for infinite scroll. | | Styling | `class` | `ome-facet-target-default` | Root target class. | ## Runtime Contract The rendered target uses `data-ome-facet-target`, `data-ome-facet-loop`, `aria-live="polite"`, and target-specific data attributes for scripts, scrolling, map pairing, and infinite scroll. If `load_more_on_scroll` is enabled, the component renders hidden controls for `offset` and `posts_per_page`. Those controls are part of the same facet request flow as the visible Load More Facet. ## Nested Loop Rule If a target lives inside another Etch loop, the outer loop params are resolved during the original render before the template is stored. The nested loop E2E scenario verifies that a Search Facet refresh keeps the correct outer-loop context after AJAX refresh. ## Example ```text Target ID: article-results Facet Target: - target = article-results - loop slot = WP Query loop - fallback slot = "No posts found." Search Facet: - target = article-results - default query argument = search ``` --- ## Load More Facet # Load More Facet `Load More Facet` requests the next batch of items for a connected target. It renders hidden `offset` and `posts_per_page` controls plus a visible button. ## Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Settings | `target` | empty | Shared target ID. | | Settings | `batch_size` | `10` | Number of additional items requested per click. | | Content | `label` | `Load more` | Fallback label when the default slot is empty. | | Styling | `class` | `ome-load-more-facet-default` | Button class. | ## Runtime Notes - The button uses the same target-scoped facet request flow as other controls. - It updates hidden offset state before requesting the next batch. - Normal facet changes reset load-more controls for the target. - Infinite scroll on `Facet Target` uses the same offset and batch-size concepts without a visible button. Use Load More Facet when users should deliberately request another batch. Use target `load_more_on_scroll` when the page should fetch more automatically near the bottom of the target. --- ## Map Facet # Map Facet `Map Facet` filters a target by the visible map bounds. It sends a `geo_bbox` facet value and reads `Map POI` elements from the connected target loop. ## Authoring Structure ```text Map Facet Facet Target └── loop slot └── Map POI ``` The map and target must share the same target ID. ## Map Facet Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Settings | `target` | empty | Shared target ID. | | Settings | `lat_meta_key` | `latitude` | Backend meta key for latitude. | | Settings | `lng_meta_key` | `longitude` | Backend meta key for longitude. | | Settings | `center_lat` | `0` | Initial map center latitude. | | Settings | `center_lng` | `0` | Initial map center longitude. | | Settings | `zoom` | `4` | Initial Leaflet zoom. | | Settings | `min_zoom` | `2` | Minimum Leaflet zoom. | | Settings | `max_zoom` | `18` | Maximum Leaflet zoom. | | Settings | `tile_url` | empty | Optional custom tile layer URL. | | Settings | `fit_bounds_on_filter` | `true` | Allows runtime to fit map bounds from filtered POIs. | | Settings | `bbox_debounce_ms` | `300` | Delay before map movement updates the bbox value. | | Styling | `class` | `ome-map-facet-root-default` | Map root class. | | Styling | `bubble_class` | empty | Optional class added to generated cluster bubbles. | ## Map POI Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Content | `value` | empty | Stable POI identifier. | | Content | `lat` | empty | POI latitude. | | Content | `lng` | empty | POI longitude. | | Styling | `class` | empty | Optional class for the hidden POI carrier. | ## Runtime Notes - `Map Facet` renders `data-ome-facet="geo_bbox"`. - The bbox value serializes as `south,west,north,east`. - `Map POI` is hidden DOM data, not visible listing content. - POI hover and selected slots can provide marker popup content. - Large responses can switch to lightweight coordinate-only POI data. ## Example ```text Map Facet: - target = stores - lat_meta_key = store_lat - lng_meta_key = store_lng Facet Target: - target = stores - used_with_map = true Map POI inside loop: - value = current post ID or slug - lat = current item store_lat - lng = current item store_lng ``` --- ## Pagination Facet # Pagination Facet `Pagination Facet` controls the current page for a target. It renders hidden `offset` state and receives pagination metadata from facet responses. ## Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Settings | `target` | empty | Shared target ID. | | Settings | `sibling_count` | `1` | Numeric page buttons shown on each side of the current page. | | Settings | `tag` | `nav` | Root wrapper tag. | | Settings | `aria_label` | `Pagination` | Accessible navigation label. | | Settings | `hide_when_single_page` | `true` | Hides pagination when only one page exists. | | Settings | `scroll_to_top` | `false` | Scrolls to the target after pagination changes. | | Labels | `prev_label` | `Previous` | Previous button label. | | Labels | `next_label` | `Next` | Next button label. | | Range | `show_range` | `true` | Shows the live range summary. | | Range | `range_label` | `Showing` | Prefix for the range summary. | | Styling | `class`, `range_class` | component defaults | Root and range summary classes. | ## Runtime Notes - Pagination sends `offset` for the target. - Responses update current page, total pages, total items, per-page, and offset. - Changing a normal facet resets pagination to page 1. - Pagination hides on zero results even when `hide_when_single_page` is false. The multi-facet E2E scenario verifies pagination reset after a facet value changes. --- ## Radio List Facet # Radio List Facet `Radio List Facet` is a single-select option list. It renders a `radiogroup`, stores the selected value in a hidden input, and can include a built-in reset item before authored options. ## Authoring Structure ```text Radio List Facet └── Radio List Facet Option ``` ## Parent Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Settings | `target` | empty | Shared target ID. | | Settings | `argument_mode` | `default` | Single argument mode. | | Settings | `target_multiple_query_arguments` | `false` | Enables mapped bundle mode. | | Settings | `options_behavior` | `all` | Availability behavior. | | Settings | `show_counts` | `false` | Enables dynamic counts. | | Settings | `aria_label` | `Filter options` | Accessible name for the radiogroup. | | Reset | `show_reset_option` | `false` | Adds a reset choice before authored options. | | Reset | `reset_option_mode` | `radio` | Reset item presentation: `radio` or `button`. | | Reset | `reset_label` | `Reset selection` | Reset item label. | | Mode groups | mode-specific props | varies | See [Argument Modes](../argument-modes). | | Styling | `class` | `ome-radio-list-facet-root-default` | Root list class. | ## Option Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Content | `value` | empty | Sent value in single-argument mode. | | Content | `label` | `Option` | Visible label. | | Metadata | `key`, `value` | empty | Optional rows for custom JavaScript integrations. | | Query Value Mappings | `mapping_key`, `value` | empty | Values used when parent mapped bundle mode is enabled. | | Settings | `disabled` | `false` | Prevents selection. | | Settings | `is_default` | `false` | Authored default or reset choice depending on reset setup. | | Settings | `mode` | `radio` | Option presentation: `radio` or `button`. | | Settings | `used_with_multiple_arguments` | `false` | Switches option authoring to query value mappings. | | Settings | `count_display` | `inline` | Inline count text or separate count span. | | Styling | option, button, indicator, label, count classes | component defaults | Option presentation classes. | ## Runtime Notes - Only one option can be selected. - The built-in reset option uses an empty value. - `mode = button` changes presentation only; the facet remains single-select. - The runtime manages `aria-checked`, roving focus, disabled state, selected state, and highlighted state. --- ## Reset Facet # Reset Facet `Reset Facet` clears active controls for a target and requests the unfiltered target state. ## Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Settings | `target` | empty | Shared target ID. | | Settings | `label` | `Reset filters` | Fallback button label when the default slot is empty. | | Styling | `class` | `ome-reset-facet-default` | Button class. | ## Runtime Notes - Reset Facet clears all registered facet controls for the same target. - It also clears URL state for that target. - Radio List's built-in reset option clears only that radio list selection. Reset Facet clears the whole target scope. Use Reset Facet when users need one clear action for a complete faceted interface. --- ## Search Facet # Search Facet `Search Facet` renders a search input. Unlike option facets, it sends the typed value from the input instead of an authored option value. ## Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Settings | `target` | empty | Shared target ID. | | Settings | `placeholder` | `Search...` | Input placeholder. | | Settings | `argument_mode` | `default` | Single argument mode when multi-argument search is disabled. | | Settings | `target_multiple_query_arguments` | `false` | Renders a `query_bundle` search input when enabled. | | Settings | `query_argument_relation` | `AND` | Relation for multi-argument search mappings. | | Default Query Argument | `facet_key` | `search` | Query argument for default mode. | | Meta Argument | `meta_key`, `meta_compare`, `meta_type` | ``, `LIKE`, `char` | Meta query settings. | | Taxonomy Argument | `taxonomy`, `tax_field` | ``, `slug` | Taxonomy search settings. | | Date Argument | `column` | `post_date` | Date query column. | | ACF Relationship Argument | `meta_key` | empty | Relationship field stored on filtered posts. | | Query Argument Mappings | `mapping_key`, mode-specific props | empty | Multi-argument search rows. | | Styling | `class` | `ome-search-facet-default` | Input class. | ## Default Query Keys Search Facet supports filter-focused keys: ```text search, p, title_like, name_like, post_type, category_name_like, tag_like, author, author_name_like, year, monthnum ``` Use Search Facet for text input and partial matching. Use an option facet when users should choose from known values. ## Multi-Argument Search When `target_multiple_query_arguments` is true, the rendered input type is `query_bundle`. The typed value is copied into every row in `query_argument_mappings`. ```text target_multiple_query_arguments = true query_argument_relation = OR query_argument_mappings: - native-search -> default search - priority -> meta facet_priority LIKE char ``` This makes one input search across native search and a meta field. ## ACF Relationship Search ACF relationship mode expects numeric related post IDs. It filters the relationship field stored on the posts being filtered. --- ## SearchSelect Facet # SearchSelect Facet `SearchSelect Facet` is a searchable option selector. It is useful when the authored option set is large enough to need filtering inside the dropdown. ## Authoring Structure ```text SearchSelect Facet └── SearchSelect Facet Option ``` ## Parent Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Settings | `target` | empty | Shared target ID. | | Settings | `argument_mode` | `default` | Single argument mode. | | Settings | `target_multiple_query_arguments` | `false` | Enables mapped bundle mode. | | Settings | `options_behavior` | `all` | Availability behavior. | | Settings | `show_counts` | `false` | Enables dynamic counts. | | Settings | `selection_mode` | `single` | `single` or `multiple`. | | Settings | `placeholder` | `Search...` | Search input placeholder. | | Settings | `default_label` | `Select option...` | Text shown when nothing is selected. | | Mode groups | mode-specific props | varies | See [Argument Modes](../argument-modes). | | Styling | `class`, `control_class`, `trigger_class`, `input_class`, `content_class`, `selection_class` | component defaults | Classes for the searchable select shell. | ## Option Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Content | `value` | empty | Sent value in single-argument mode. | | Content | `label` | `Option Label` | Visible label, search text, and pill label. | | Metadata | `key`, `value` | empty | Optional rows for custom JavaScript integrations. | | Query Value Mappings | `mapping_key`, `value` | empty | Values used when parent mapped bundle mode is enabled. | | Settings | `disabled` | `false` | Prevents selection. | | Settings | `used_with_multiple_arguments` | `false` | Switches option authoring to query value mappings. | | Settings | `count_display` | `inline` | Inline count text or separate count span. | | Styling | `class`, span count classes | component defaults | Option and count classes. | ## Runtime Notes - The root stores selected state with `data-ome-selected-value` and `data-ome-selected-values`. - `selection_mode = multiple` renders multiple selected values as removable selections. - SearchSelect uses the shared search-select runtime plus facet-specific query binding. - SearchSelect options do not expose an authored `is_default` prop in the current PHP component. ## Example ```text SearchSelect Facet: - target = posts - argument_mode = default - facet_key = name - placeholder = Search by slug Option: - value = beta-bar - label = Beta Bar Post ``` --- ## Select Facet # Select Facet `Select Facet` renders a custom listbox-style dropdown. It can select one value or multiple values, and it can either send one query clause or expand an option into a mapped bundle. ## Authoring Structure ```text Select Facet └── Select Facet Option ``` ## Parent Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Settings | `target` | empty | Shared target ID. | | Settings | `argument_mode` | `default` | Single argument mode. | | Settings | `target_multiple_query_arguments` | `false` | Enables mapped bundle mode. | | Settings | `options_behavior` | `all` | Availability behavior. | | Settings | `show_counts` | `false` | Enables dynamic counts. | | Settings | `selection_mode` | `single` | `single` or `multiple`. | | Settings | `tag` | `div` | Root wrapper tag. | | Settings | `default_label` | `Select option...` | Trigger label and built-in clear option label. | | Mode groups | mode-specific props | varies | See [Argument Modes](../argument-modes). | | Styling | `class`, `trigger_class`, `trigger_label_class`, `content_class`, `item_class` | component defaults | Classes for the dropdown shell and items. | ## Option Props | Group | Prop | Default | Meaning | | --- | --- | --- | --- | | Content | `value` | empty | Sent value in single-argument mode. | | Content | `label` | `Option Label` | Visible option label. | | Metadata | `key`, `value` | empty | Optional rows for custom JavaScript integrations. | | Query Value Mappings | `mapping_key`, `value` | empty | Values used when parent mapped bundle mode is enabled. | | Settings | `disabled` | `false` | Prevents selection. | | Settings | `is_default` | `false` | Initial selected option when no restored value exists. | | Settings | `used_with_multiple_arguments` | `false` | Switches option authoring to query value mappings. | | Settings | `count_display` | `inline` | Inline count text or separate count span. | | Styling | `class`, span count classes | component defaults | Option and count classes. | ## Runtime Notes - The root stores selected state in `data-ome-selected-value` and `data-ome-selected-values`. - Single selection uses one value. Multiple selection serializes multiple values for the same facet key. - A built-in empty option uses `__ome-empty__` internally and clears the selection. - In mapped bundle mode, option `query_value_mappings` must match parent `query_argument_mappings` by `mapping_key`. ## Example ```text Select Facet: - target = products - argument_mode = default - facet_key = category_name - selection_mode = single Option: - value = shoes - label = Shoes ``` --- ## How Facets Work # How Facets Work Facet runtime is target-centric. Every control identifies a `target`, and every `Facet Target` with the same target ID participates in that target scope. ## Render and Refresh Lifecycle 1. During the original page render, `FacetBlockInterceptor` detects `Facet Target` components. 2. The target's loop and fallback slots are normalized and stored as the refresh template. 3. Facet controls register query descriptors for the same target ID. 4. On interaction, the client builds scoped facet clauses and calls the facet REST endpoint. 5. The endpoint applies those clauses to the target loop config, re-renders the stored target template, computes pagination and availability metadata, and returns markup. 6. The client replaces the target content, updates URL state, updates option availability, applies pagination metadata, and reruns returned scripts when the target allows it. ## Target ID Rules - The `target` string is the connection between controls and result containers. - Controls with different target IDs do not affect each other. - Multiple target containers can share a target ID, but they share the same scoped request state. - Empty target IDs are not useful at runtime; the PHP components show inline builder guidance when target is missing. ## Facet Target Behavior `Facet Target` owns the content that gets replaced. Its loop slot is the normal result state, and its fallback slot is shown when the refreshed loop produces no rendered items. Important target props: | Prop | Meaning | | --- | --- | | `target` | Shared target ID. | | `rerun_scripts` | Allows scripts returned by refreshed markup to run again. Defaults to `true`. | | `preview_fallback` | Shows the fallback slot in Etch preview instead of the loop slot. | | `scroll_to_top` | Scrolls to the target after connected facet updates. | | `used_with_map` | Marks the target as map-paired, so map and result updates coordinate correctly. | | `load_more_on_scroll` | Adds internal offset and posts-per-page controls for infinite scroll. | | `load_more_batch_size` | Batch size used by infinite scroll. | Nested loops have a specific rule: if a Facet Target lives inside another Etch loop, outer loop params must be resolved during the original render pass. The current implementation freezes those params through `FacetLoopParamResolver` before the target template is stored, and the E2E `nested_loop_context` scenario verifies that refreshed results keep the correct outer-loop context. ## URL State Facet state is serialized under the target ID. The E2E URL tests cover direct-link hydration for search, checkbox, radio, select, pagination, map state, ACF relationship, and mapped bundle filters. Examples: ```text ome[products][category_name]=shoes ome[products][meta_value:price]=50 ome[products][tax_query:facet_color]=red ome[products][acf_relationship:facet_related_posts]=123 ome[products][geo_bbox]=40.5,-74.3,40.9,-73.7 ``` The exact URL key depends on the facet clause type and mode-specific options such as `meta_key`, `taxonomy`, or date `column`. ## Pagination, Load More, and Reset - `Pagination Facet` writes pagination state for a target and receives total pages, total items, current page, per-page, and offset metadata from responses. - `Load More Facet` writes hidden offset and posts-per-page controls, then appends another batch through the same target request path. - `load_more_on_scroll` on `Facet Target` uses the same internal offset mechanism without requiring a visible button. - `Reset Facet` clears all controls connected to the target and returns the target to its unfiltered state. When any normal facet value changes, pagination resets to page 1. The multi-facet E2E scenario verifies that changing `category_name` after navigating to page 2 resets pagination before showing the narrowed result set. ## Map Pairing `Map Facet` is a facet control with type `geo_bbox`. It sends the map viewport as a bounding box and reads Map POI elements from the connected target loop. Use a map-paired setup when: - the target loop renders listings, - each listing includes a `Map POI`, - the map and result target share the same target ID, - the target sets `used_with_map` when map coordination is needed. The map runtime can use detailed POI markup for smaller sets and lightweight coordinate responses for large sets. --- ## Facet Overview # Facet Overview Facets are controls that update one or more `Facet Target` result containers. A facet does not own the results. It sends a value for a shared target ID, the backend re-renders that target's stored Etch loop template, and the runtime swaps the returned markup into the target. The basic structure is: ```text Search Facet, Select Facet, Radio List Facet, Checkbox List Facet, Map Facet, Pagination Facet, Load More Facet, Reset Facet | | shared target ID v Facet Target | v Etch loop slot + fallback slot ``` One target can be controlled by many facets. The E2E scenarios combine `post_type`, `category_name`, `tag`, meta queries, date queries, ACF relationship filters, pagination, reset, load more, and map bounds against the same target. The combined result is an intersection unless a specific mode documents a different behavior. ## What Authors Configure Every useful facet setup needs three decisions: | Decision | Where it lives | What it controls | | --- | --- | --- | | Target binding | Facet `target` and Facet Target `target` | Which result container receives updates. | | Argument mode | Facet settings and mode-specific groups | Which query clause the selected value becomes. | | Authored values | Option values, search input value, map bounds, pagination controls | The value sent for that query clause. | The component family pages describe the markup and props for each family. The system pages explain how those props work together across families. ## Component Families - [Facet Target](./component-families/facet-target) stores and refreshes the result template. - [Search Facet](./component-families/search-facet) sends text input values. - [Select Facet](./component-families/select-facet) sends single or multiple selected option values. - [SearchSelect Facet](./component-families/search-select-facet) sends searchable single or multiple option values. - [Radio List Facet](./component-families/radio-list-facet) sends one selected option value and can include a reset option. - [Checkbox List Facet](./component-families/checkbox-list-facet) sends multiple selected option values. - [Pagination Facet](./component-families/pagination-facet) sends page offsets for a target. - [Load More Facet](./component-families/load-more-facet) requests another batch for a target. - [Reset Facet](./component-families/reset-facet) clears all active filters for a target. - [Map Facet](./component-families/map-facet) sends a geographic bounding box and reads Map POI data from the target loop. ## Start Here Read these pages in order when building a facet interface: 1. [How Facets Work](./how-facets-work) for the request and target lifecycle. 2. [Argument Modes](./argument-modes) for the exact meaning of each mode. 3. [Shared Props](./shared-props) for props shared across select, search select, radio, checkbox, and search. 4. [Availability, Counts, and Options](./availability-counts-and-options) for dynamic option state. 5. [Recipes](./recipes) for codebase-backed examples from the E2E scenarios. --- ## Facet Recipes # Facet Recipes These recipes use real scenarios from the test suite. They describe authoring intent and the important prop choices rather than copying fixture-only helper code. ## CPT and Taxonomy Filter Use this when a result loop lists a custom post type and users need to filter by a custom taxonomy. Example data: ```text Post type: facettestcpt Taxonomy: facet_color Terms: red, blue, green Target ID: catalog-results ``` Authoring setup: 1. Add a `Facet Target` with `target = catalog-results`. 2. Place the WP Query loop in the target loop slot. The loop should include the desired post type. 3. Add a `Select Facet`, `Radio List Facet`, or `Checkbox List Facet` with the same `target`. 4. Set `argument_mode = tax`. 5. Set `taxonomy = facet_color`. 6. Set `tax_field = slug` when option values are slugs like `red`. 7. Add options with values matching the selected field. The taxonomy E2E suite verifies `slug`, `term_id`, `name`, and `term_taxonomy_id`. ## Multiple Facets to One Target Use this when users need to narrow the same result grid with several independent controls. Example: ```text Target ID: multi-combo-target Facet 1: Select post_type = post Facet 2: Select category_name = facets-e2e-news Facet 3: Select tag = facets-e2e-beta Facet 4: Radio meta facet_priority = high ``` Each facet uses the same `target`. The backend combines the active clauses, and the result is the intersection. The E2E suite verifies two-way, three-way, and four-way intersections, fallback on zero results, recovery after clearing one facet, and pagination reset after changing a facet. ## Meta Filter Use meta mode when the filtered value lives in post meta. Example data: ```text Meta key: facet_price Values: 10, 25, 50, 75, 100 ``` Authoring setup: ```text argument_mode = meta meta_key = facet_price meta_compare = BETWEEN meta_type = numeric option value = 25,75 ``` The meta query E2E suite verifies exact matches, numeric ranges, char values, bool values, `LIKE`, `NOT LIKE`, `IN`, `NOT IN`, `BETWEEN`, and `NOT BETWEEN`. ## Date Range Filter Use date mode when authored options represent date ranges. ```text argument_mode = date column = post_date option value = 2024-01-01,2024-12-31 ``` The first date is the start date and the second date is the end date. Supported columns are `post_date`, `post_modified`, `post_date_gmt`, and `post_modified_gmt`. ## ACF Relationship Filter Use ACF relationship mode when the posts being filtered store related post IDs in a relationship field. ```text Filtered posts: Projects Related posts: People Relationship field on Projects: project_people Facet option value: numeric Person post ID ``` Authoring setup: ```text argument_mode = acf_relationship acf_relationship_argument.meta_key = project_people option value = 123 ``` The directional rule is important: the relationship field must be stored on the posts being filtered. The facet does not reverse-resolve a relationship stored only on the related posts. Comma-separated option values are supported for matching any of several related IDs: ```text option value = 123,456 ``` ## Mapped Bundle Option Use mapped bundle when one option should apply several query clauses. Example option: ```text Label: News + High priority category mapping: category_name = facets-e2e-news priority mapping: facet_priority = high ``` Parent facet: ```text target_multiple_query_arguments = true query_argument_mappings: - mapping_key = category, argument_mode = default, facet_key = category_name - mapping_key = priority, argument_mode = meta, meta_key = facet_priority, meta_compare = =, meta_type = char ``` Option: ```text used_with_multiple_arguments = true query_value_mappings: - mapping_key = category, value = facets-e2e-news - mapping_key = priority, value = high ``` The runtime expands the selected option into both clauses. ## Search Across Multiple Arguments Use Search Facet multi-argument mode when one input should search more than one field. ```text target_multiple_query_arguments = true query_argument_relation = OR query_argument_mappings: - mapping_key = native-search, argument_mode = default, facet_key = search - mapping_key = priority, argument_mode = meta, meta_key = facet_priority, meta_compare = LIKE, meta_type = char ``` Typing one value creates a `query_bundle` and applies the typed value to each mapped row. Search mappings normalize common text fields to partial aliases, so `title` behaves like `title_like`. ## Map and POIs Use Map Facet when the target loop renders listings with coordinates. Authoring setup: 1. Add `Map Facet` with `target = map-results`. 2. Configure `lat_meta_key` and `lng_meta_key`. 3. Add `Facet Target` with the same `target` and `used_with_map = true`. 4. Inside the target loop, add `Map POI`. 5. Set POI `value`, `lat`, and `lng` from the current loop item. The map runtime reads initial POIs from the DOM and sends `geo_bbox` values as the map moves. ## Nested Loop Target If the target is inside an outer Etch loop, keep the target template inside the original render context. The current implementation stores resolved outer-loop params before AJAX refresh, and the `nested_loop_context` E2E scenario verifies that searching for `Alice` still returns `Alice Alpha` and `Alice Beta` from the correct outer-loop rows. --- ## Shared Props # Shared Props Many facet families share the same logic even when their markup is different. The most important distinction is whether a prop belongs to the parent facet or to an authored option. ## Parent Facet Props These props apply to Select, SearchSelect, Radio List, and Checkbox List through `FacetQueryProperties`. Search Facet has matching props implemented in its own component file. | Prop | Applies to | Why it matters | | --- | --- | --- | | `target` | All facet controls | Connects the control to the Facet Target with the same target ID. | | `argument_mode` | Search and option facets when `target_multiple_query_arguments` is false | Chooses the single query mode. | | `target_multiple_query_arguments` | Search and option facets | Switches from one query target to multi-argument behavior. | | `options_behavior` | Select, SearchSelect, Radio List, Checkbox List | Controls unavailable option behavior after availability data changes. | | `show_counts` | Select, SearchSelect, Radio List, Checkbox List | Enables dynamic result counts on options. | | `selection_mode` | Select and SearchSelect | Chooses single or multiple selected values. | | `aria_label` | Radio List and Checkbox List | Names the `radiogroup` or `group` for assistive technology. | | `default_label` | Select and SearchSelect | Text shown when nothing is selected, and the built-in clear item label for Select. | | `placeholder` | Search and SearchSelect | Text shown in the input before a value is typed. | ## Single-Argument Groups When `target_multiple_query_arguments` is false, the selected `argument_mode` decides which group is active: | Group | Active when | Required values | | --- | --- | --- | | `default_query_argument` | `argument_mode` is `default` | `facet_key` | | `meta_argument` | `argument_mode` is `meta` | `meta_key`, `meta_compare`, `meta_type` | | `taxonomy_argument` | `argument_mode` is `tax` | `taxonomy`, `tax_field` | | `date_argument` | `argument_mode` is `date` | `column` | | `acf_relationship_argument` | `argument_mode` is `acf_relationship` | `meta_key` | ## Multi-Argument Parent Props When `target_multiple_query_arguments` is true, the parent facet uses `query_argument_mappings`. Each row needs: | Prop | Meaning | | --- | --- | | `mapping_key` | Stable row key referenced by option value mappings. | | `argument_mode` | Query mode for this mapped row. | | `facet_key` | Used when row mode is `default`. | | `meta_key`, `meta_compare`, `meta_type` | Used when row mode is `meta`. | | `taxonomy`, `tax_field` | Used when row mode is `tax`. | | `column` | Used when row mode is `date`. | For Search Facet, the typed input becomes the value for every mapped row. For option facets, each option supplies its own values through `query_value_mappings`. ## Option Props Select, SearchSelect, Radio List, and Checkbox List option components share these concepts: | Prop | Applies to | Meaning | | --- | --- | --- | | `content.value` | Single-argument option mode | Value sent when the option is selected. | | `content.label` | All option modes | Visible label and count base text. | | `metadata` | All option modes | Optional key/value rows serialized for custom JavaScript integrations. | | `query_value_mappings` | Multi-argument option mode | Per-option values keyed by parent `mapping_key`. | | `disabled` | All option families | Prevents authored selection. | | `is_default` | Select, Radio List, Checkbox List | Marks initial selection when no URL or runtime value already exists. | | `used_with_multiple_arguments` | Option components | Shows mapping-driven authoring instead of manual `content.value`. | | `count_display` | Option components | Chooses inline count text or a separate count span. | `used_with_multiple_arguments` is an authoring switch for option components. It should match the parent facet's multi-argument setup: enable it when the parent has `target_multiple_query_arguments` enabled and the option needs `query_value_mappings`. ## Count Display `count_display` supports: | Value | Behavior | | --- | --- | | `inline` | Runtime appends counts to the visible label text. | | `span` | Runtime writes counts into a separate `data-ome-facet-option-count` span. | Use `span` when the count needs separate styling or layout. The PHP components include Etch preview count props for span mode so authors can style the counter before runtime data exists. ## Reset Props Radio List has built-in reset props: | Prop | Meaning | | --- | --- | | `show_reset_option` | Adds a reset choice before authored options. | | `reset_option_mode` | Renders the reset choice as radio-style or button-style. | | `reset_label` | Label for the built-in reset choice. | Reset Facet is a separate component. It clears all controls for the target rather than only one radio list selection.