liftbox

Category filter

Toggle buttons that narrow a gallery and announce how many items are left.

GalleryFilter is a group of toggle buttons with an "All" option first. It only reports the choice. You filter the slides and pass the same list to the grid and the lightbox, so the index the grid reports is the index the lightbox shows.

12 images

import { useRef, useState } from "react";
import { GalleryFilter, JustifiedGrid, Lightbox, type GridHandle, type Slide } from "liftbox";

type Photo = Slide & { trade: "roofs" | "walls" };

export function Gallery({ photos }: { photos: Photo[] }) {
  const [trade, setTrade] = useState<Photo["trade"] | null>(null);
  const [index, setIndex] = useState(-1);
  const grid = useRef<GridHandle>(null);
  const shown = trade ? photos.filter((photo) => photo.trade === trade) : photos;

  return (
    <>
      <GalleryFilter
        categories={["roofs", "walls"]}
        value={trade}
        onChange={setTrade}
        count={shown.length}
      />
      <JustifiedGrid ref={grid} slides={shown} onOpen={setIndex} />
      <Lightbox
        slides={shown}
        open={index >= 0}
        index={Math.max(index, 0)}
        onIndexChange={setIndex}
        onClose={() => setIndex(-1)}
        getThumbnail={(i) => grid.current?.getThumbnail(i)}
      />
    </>
  );
}

The filter works with any list, not only the built-in grids.

Props

Prop

Type

Labels

<GalleryFilter
  categories={trades}
  value={trade}
  onChange={setTrade}
  count={shown.length}
  labels={{
    group: "Filtrar por ofício",
    all: "Todos",
    category: (id) => t(`trades.${id}`),
    count: (n) => (n === 1 ? "1 foto" : `${n} fotos`),
  }}
/>

Prop

Type

defaultFilterLabels holds the English defaults: Filter, All, the category itself, and 1 image or n images.

Accessibility

  • The buttons sit in a role="group" named by labels.group.
  • Each button is a toggle with aria-pressed. The selected one is true.
  • A visually hidden polite live region announces labels.count(count) whenever the visible count changes.
  • Buttons are at least 44px tall by default.

Styling

VariableDefaultUsed for
--eg-filter-gap8pxSpace between buttons
--eg-filter-height44pxMinimum button height
--eg-filter-padding16pxInline padding
--eg-filter-radius999pxButton corners
--eg-filter-font-size0.875remButton text
--eg-filter-colorinheritButton text
--eg-filter-backgroundtransparentButtons at rest
--eg-filter-border-colorrgb(127 127 127 / 0.5)Buttons at rest
--eg-filter-active-backgroundCanvasTextPressed button
--eg-filter-active-colorCanvasPressed button text
--eg-filter-focus-colorcurrentColorFocus ring

Classes: eg-filter, eg-filter-group, eg-filter-button and eg-filter-status. classNames adds your own to root, group, button and status.

Edit this page on GitHub

On this page