How it works › Filters and facets

Filters and facets

The rail is <data-facets for="views-id"> beside a <data-views>. It draws the .base file's facets: list and edits one thing: the all-views filter, the same filter the Filter menu, the URL and an agent writing the file use. A tick in the rail shows in the table, the cards and the chart alike.

Every kind of facet

Live

robotparts' 1,149 actuators with one facet of each kind. The tabs McMaster-style, Amazon-style, Digi-Key-style and Obsidian-style each carry their own facets:, so switching tab switches the rail.

The live view starts when it scrolls into sight.

From the example Filters

Write it

A facet is a property name, or {property, as: <kind>, …options}. Every kind also takes label, info (the ⓘ), diagram and collapsed. An unknown kind draws as values and warns; it never breaks the page.

facets:
  - { as: current, label: Current filters }
  - { property: model, as: search, properties: [model, name], label: Search, placeholder: "Model or name, e.g. AK80" }
  - property: price
    as: popular
    label: Popular filters
    options:
      - { label: In stock, filter: 'in_stock == "yes"' }
      - { label: CAN bus, filter: 'buses.contains("can")' }
      - { label: Under $100, filter: 'price < 100' }
      - { label: Has a photo, filter: '!photo.isEmpty()' }
  - { property: form, as: tree, levels: [form, brand], label: Form › brand }
  - property: form
    as: gallery
    label: Form
    columns: 2
    info: "How the actuator is built: a hobby servo's case and horn, an integrated joint with its driver inside, a frameless direct-drive ring, a motor with a separate gearbox, or a motor inside a wheel."
    images:
      hobby servo: icons/form-hobby_servo.svg
      integrated actuator: icons/form-integrated_actuator.svg
      direct drive: icons/form-direct_drive.svg
      bare gearmotor: icons/form-bare_gearmotor.svg
      wheel hub: icons/form-wheel_hub.svg
  - property: drivetrain
    as: picture-list
    label: Drivetrain
    images:
      geared: icons/drivetrain-geared.svg
      direct_drive: icons/drivetrain-direct_drive.svg
      motor: icons/drivetrain-motor.svg
  - property: brand
    as: swatch
    label: Brand (logos)
    limit: 12
    images:
      Steadywin: logos/steadywin.webp
      Damiao: logos/damiao.webp
      JMC: logos/jmc.webp
  - { property: brand, as: values, exclude: true, label: Brand, searchable: true }
  - { property: in_stock, as: toggle, value: "yes", text: In stock only }
  - { property: in_stock, as: select, label: Stock }
  - { property: checks, as: toggle, value: cad, text: Has CAD, label: CAD }
  - { property: photo, as: has, properties: [photo, torque_density, price], label: Has data, match: all }
  - { property: price, as: buckets, buckets: [50, 100, 250, 500], unit: USD, label: Price }
  - { property: price, as: range, choices: [25, 50, 100, 250, 500, 1000, 2500], label: Price (min / max) }
  - { property: peak_torque, as: slider-histogram, log: true, label: Peak torque }
  - { property: mass, as: slider, log: true, label: Mass }
  - { property: od, as: range, diagram: diagrams/od.svg, label: Outer diameter, info: "Across the actuator's face, housing to housing; not the output flange." }
  - { property: length, as: range, diagram: diagrams/length.svg, label: Axial length, collapsed: true }
  - { property: torque_density, as: buckets, label: Peak N·m/kg }
  - { property: level, as: rating, max: 5, label: robotparts level }
  - { property: level, as: min-level, max: 5, label: Level }
  - { property: listed, as: date, presets: [7d, 30d, year], older: [14d, 90d], label: First listed }
  - { property: buses, as: chips, label: Bus }
  - { property: gear_ratio, as: grid, columns: 3, sort: value, limit: 15, label: Gear ratio, info: "Output turns per motor turn; 1 is direct drive." }
  - { property: voltage, as: listbox, sort: value, label: Voltage }

The kinds

as:DrawsWrites
valuescheckboxes with counts, Show N more, a find boxp == v, or {or:}; .contains on a list
selectAny + radios, or a dropdownone p == v
togglea switchp == <value>
has"Has a value" per property!p.isEmpty(), all or any
rangemin / max boxes (dates on a date)p >= a, p <= b
slider / slider-histogramtwo thumbs, the second over the distributionp >= a, p <= b
bucketspreset ranges as chipsp >= a, p < b
rating / min-level"★★★☆ & up", "Level ≥ N"p >= N
datefrom / until, presets, "Older than"p >= "date", p < "date"
chips, swatch, gallery, picture-list, gridpressable values: tags, logos, colours, picturesas values
listboxa native multi-selectas values
treea hierarchical menuone p == v per level
searcha text boxp.contains("x"), over one or more properties
popularnamed shortcuts with countseach option's clause
currentevery pick as a removable chip, Clear all(removes)

The URL

The rail's part of the filter stays readable in the address: ?brand=A,B, brand!=A (exclude), price=50..<100 (a range, < marking an exclusive end), model~=ak (search), has=photo. A clause no facet can draw stays as a custom filter, shown in the rail as "+N custom filters · Edit".

Keyboard and phone

Native controls or buttons with aria-pressed, each group labelled, the match count a polite live region. Under 700 px the rail is a closed disclosure above the view. The rail's right edge drags (Resizable panels).

Comes from docs/CONTRACT.md §7 and its 2026-10-26 amendment (filters) in docs/.