Klarhimmel UI

Search results

App, search results preview

Install

npx shadcn add @klarhimmel/app-search-results

Usage

import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppSearchResults from '@/components/klarhimmel/blocks/app-search-results';

export default function Section() {
  return (
    <Theme theme="brand">
      <BlockSurface>
        <AppSearchResults props={{
          "eyebrow": "Exempelhotellet",
          "title": "Search the hotel",
          "description": "Rooms, treatments and events in one place.",
          "searchLabel": "Search",
          "searchButton": "Search",
          "query": "spa",
          "facets": [
            {
              "name": "Type",
              "options": [
                {
                  "label": "Treatments",
                  "checked": false
                },
                {
                  "label": "Rooms",
                  "checked": false
                },
                {
                  "label": "Events",
                  "checked": false
                }
              ]
            },
            {
              "name": "Price",
              "options": [
                {
                  "label": "Under 1 000 kr",
                  "checked": false
                },
                {
                  "label": "1 000 kr or more",
                  "checked": false
                }
              ]
            }
          ],
          "results": [
            {
              "title": "Spa day for two",
              "summary": "Pool, sauna and a sixty-minute treatment each, with lunch in the garden room.",
              "href": "#spa-day",
              "meta": "Treatments · 1 800 kr",
              "matches": [
                "Treatments",
                "1 000 kr or more"
              ]
            },
            {
              "title": "Deep tissue massage",
              "summary": "Sixty minutes with a therapist, booked in the spa on the ground floor.",
              "href": "#massage",
              "meta": "Treatments · 890 kr",
              "matches": [
                "Treatments",
                "Under 1 000 kr"
              ]
            },
            {
              "title": "Room 311, balcony and bath",
              "summary": "A double room with spa access included for every night of your stay.",
              "href": "#room-311",
              "meta": "Rooms · 1 490 kr",
              "matches": [
                "Rooms",
                "1 000 kr or more"
              ]
            },
            {
              "title": "Spa weekend for groups",
              "summary": "Eight to twenty guests: treatments, dinner and a guided morning swim.",
              "href": "#group-weekend",
              "meta": "Events · 14 500 kr",
              "matches": [
                "Events",
                "1 000 kr or more"
              ]
            },
            {
              "title": "Spa gift card",
              "summary": "A card for any treatment, valid for twelve months and sent by email.",
              "href": "#gift-card",
              "meta": "Treatments · 500 kr",
              "matches": [
                "Treatments",
                "Under 1 000 kr"
              ]
            }
          ],
          "resultsLabel": "results",
          "singular": "result",
          "forLabel": "for",
          "removeLabel": "Remove filter",
          "clearAll": "Clear all filters",
          "filtersLabel": "Filters",
          "noResults": {
            "title": "Nothing matches that search",
            "hint": "Check the spelling, use fewer words or remove a filter.",
            "suggestionsLabel": "Try one of these"
          },
          "suggestions": [
            "massage",
            "sauna",
            "weekend"
          ],
          "state": "filled",
          "messages": {
            "loading": "Searching…",
            "error": "The search failed. Try again.",
            "retry": "Try again"
          }
        }} />
      </BlockSurface>
    </Theme>
  );
}

States

search results in 4 forms: filled, empty, loading, error.

  • state: filled

    Exempelhotellet

    Search the hotel

    Rooms, treatments and events in one place.

    Filters

    Type
    Price

    5 results for “spa”

    1. Spa day for two

      Pool, sauna and a sixty-minute treatment each, with lunch in the garden room.

      Treatments · 1 800 kr

      • Treatments
      • 1 000 kr or more
    2. Deep tissue massage

      Sixty minutes with a therapist, booked in the spa on the ground floor.

      Treatments · 890 kr

      • Treatments
      • Under 1 000 kr
    3. Room 311, balcony and bath

      A double room with spa access included for every night of your stay.

      Rooms · 1 490 kr

      • Rooms
      • 1 000 kr or more
    4. Spa weekend for groups

      Eight to twenty guests: treatments, dinner and a guided morning swim.

      Events · 14 500 kr

      • Events
      • 1 000 kr or more
    5. Spa gift card

      A card for any treatment, valid for twelve months and sent by email.

      Treatments · 500 kr

      • Treatments
      • Under 1 000 kr
  • state: empty

    Exempelhotellet

    Search the hotel

    Rooms, treatments and events in one place.

    Filters

    Type
    Price

    0 results for “spa”

    Nothing matches that search

    Check the spelling, use fewer words or remove a filter.

    Try one of these

  • state: loading

    Exempelhotellet

    Search the hotel

    Rooms, treatments and events in one place.

    Filters

    Type
    Price
    Searching…
  • state: error

    Exempelhotellet

    Search the hotel

    Rooms, treatments and events in one place.

    Filters

    Type
    Price

Notes

When to use it

A results page with a search box, checkbox filters and a count. For a plain sortable list of records, `app-data-table` and `app-data-filters` are the better pair; this block is for content that people search by words.

Writing for it

- Each result has a title (80), a summary (160) and a matches list of up to 3 words. The matches must be written exactly like the facet option labels, because that is how filtering and the counts work. - Within one facet the options add up, and across facets they narrow. Make that obvious in the facet names (30), for example Type and Price, not two facets that overlap. - Give suggestions (up to 4, 40 characters each) as real searches that return something, since they only appear on the no-results state.

Watch out for

The text search runs on the results you supply, so 12 results is the ceiling and anything beyond that must be paged by your own app. The count in each option updates with the other filters and shows blank outside the filled state.

Usage

The block in a page, with its default content (long lists shortened). Every prop is in the table below.

section.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppSearchResults from '@/components/klarhimmel/blocks/app-search-results';

export default function Section() {
  return (
    <Theme theme="brand">
      <BlockSurface>
        <AppSearchResults props={{
          "eyebrow": "Exempelhotellet",
          "title": "Search the hotel",
          "description": "Rooms, treatments and events in one pla…",
          "searchLabel": "Search",
          "searchButton": "Search",
          "query": "spa",
          "facets": [
            {
              "name": "Type",
              "options": [
                {
                  "label": "Treatments",
                  "checked": false
                }
              ]
            }
          ],
          "results": [
            {
              "title": "Spa day for two",
              "summary": "Pool, sauna and a sixty-minute treatmen…",
              "href": "#spa-day",
              "meta": "Treatments · 1 800 kr",
              "matches": [
                "Treatments"
              ]
            }
          ],
          "resultsLabel": "results",
          "singular": "result",
          "forLabel": "for",
          "removeLabel": "Remove filter",
          "clearAll": "Clear all filters",
          "filtersLabel": "Filters",
          "noResults": {
            "title": "Nothing matches that search",
            "hint": "Check the spelling, use fewer words or …",
            "suggestionsLabel": "Try one of these"
          },
          "suggestions": [
            "massage"
          ],
          "state": "filled",
          "messages": {
            "loading": "Searching…",
            "error": "The search failed. Try again.",
            "retry": "Try again"
          }
        }} />
      </BlockSurface>
    </Theme>
  );
}

Props

facets are checkbox groups and results name the options they belong to (matches, in the same words as the option labels); counts are worked out from the results given. query is where the page opens; the labels build the announced count ("5 results for spa").

PropTypeDefaultDescription
eyebrowstring'Exempelhotellet'Text, up to 60 characters. Optional; empty is not rendered.
titlestring'Search the hotel'Text, up to 120 characters.
descriptionstring'Rooms, treatments and events in one…'Text, up to 300 characters. Optional; empty is not rendered.
searchLabelstring'Search'Text, up to 40 characters.
searchButtonstring'Search'Text, up to 30 characters.
querystring'spa'Text, up to 80 characters.
facets{ name, options }[]2 itemsList of 1 to 3. Each item: name, options.
results{ title, summary, href, meta, matches }[]5 itemsList of up to 12. Each item: title, summary, href, meta (optional), matches.
resultsLabelstring'results'Text, up to 20 characters.
singularstring'result'Text, up to 20 characters.
forLabelstring'for'Text, up to 10 characters.
removeLabelstring'Remove filter'Text, up to 30 characters.
clearAllstring'Clear all filters'Text, up to 30 characters.
filtersLabelstring'Filters'Text, up to 30 characters.
noResults{ title, hint, suggestionsLabel }{…}Fields: title, hint, suggestionsLabel.
suggestionsstring[]3 itemsList of up to 4.
state'filled' | 'empty' | 'loading' | 'error''filled'One of 'filled', 'empty', 'loading', 'error'.
messages{ loading, error, retry }{…}Fields: loading, error, retry.
headingLevel'1' | '2'-One of '1', '2'. Optional; empty is not rendered.

Install

Installs

app-search-results
Uses
  • Badge
  • Button
  • Checkbox
  • Input
  • Skeleton
About
A results page: a search box, checkbox facets whose counts follow the other filters, active filters as removable chips, a result count that is announced when it changes, and a no-results state with suggestions.
States
Filled, empty, loading, error. The count is announced; removing a filter moves focus to it; no results offers suggestions.