# Agency: Selected work

> A grid of project covers with a filter by discipline; the grid reflows on a spring.

A shadcn/ui block from the [@eased](https://eased.dev/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/eased/agency-work).

- Page, with a live preview: https://shadcn.exchange/r/eased/agency-work
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/eased/agency-work-light-55dc76112371023e.png
- Registry item: https://eased.dev/r/agency-work.json
- Listed: 2026-10-23, source last changed 2026-10-23

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/eased/agency-work.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `motion`
- Registry items: `https://eased.dev/r/eased-tokens.json`, `https://eased.dev/r/eased-motion.json`, `https://eased.dev/r/agency-core.json`, `https://eased.dev/r/segmented.json`

## Files

### `src/registry/eased/templates/agency/work.tsx`

```tsx
"use client";

import { AnimatePresence, motion } from "motion/react";
import { useState } from "react";
import { enter, exit, reducedFade, smooth, useSafeReducedMotion } from "../../lib/motion";
import { Segmented } from "../../segmented/segmented";
import { ClientArt } from "./client-art";
import { projects, work, type Discipline, type Project } from "./content";
import { ArrowLink, Section, useHref } from "./kit";

type Filter = "All" | Discipline;

/**
 * Selected work: a filter by discipline over a grid of covers, each cover the client's site
 * drawn at card size. Filtering fades the leavers out and slides the rest into place on the
 * smooth spring; under reduced motion the grid changes at once with a fade.
 */
export function Work() {
  const [filter, setFilter] = useState<Filter>("All");
  const reduced = useSafeReducedMotion();
  const shown = filter === "All" ? projects : projects.filter((p) => p.disciplines.includes(filter));
  const options = [work.all, ...work.filters].map((value) => ({ value: value as Filter, label: value }));

  return (
    <Section id="work" label={work.label} className="flex flex-col gap-pad-lg">
      <div className="flex flex-wrap items-center justify-between gap-in">
        <div className="max-w-full overflow-x-auto [scrollbar-width:none]">
          <Segmented options={options} value={filter} onValueChange={setFilter} aria-label="Filter by discipline" />
        </div>
        <ArrowLink href={work.more.href}>{work.more.label}</ArrowLink>
      </div>
      <ul className="grid gap-x-tiles gap-y-pad-lg sm:grid-cols-2 lg:grid-cols-3">
        <AnimatePresence initial={false} mode="popLayout">
          {shown.map((p) => (
            <motion.li
              key={p.id}
              layout={!reduced}
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0, transition: reduced ? reducedFade : exit }}
              transition={reduced ? reducedFade : { layout: smooth, opacity: enter }}
            >
              <WorkCard project={p} />
            </motion.li>
          ))}
        </AnimatePresence>
      </ul>
    </Section>
  );
}

/** A cover and its caption; the whole card is one link (rule 59), its cover lifting a shade on hover. */
export function WorkCard({ project }: { project: Project }) {
  const to = useHref();
  return (
    <article className="group relative flex flex-col gap-in">
      <div className="image-ring relative aspect-[4/3] overflow-hidden rounded-panel">
        <div aria-hidden className="absolute inset-0 transition-transform duration-(--eased-duration-fast) ease-enter group-hover-fine:scale-[1.02] reduced:transition-none">
          <ClientArt id={project.id} />
        </div>
      </div>
      <div className="flex min-w-0 flex-col gap-1">
        <h3 className="text-ui-md font-medium text-text">
          <a href={to(`/work#${project.id}`)} className="focus-ring rounded-control-xs after:absolute after:inset-0 after:rounded-panel">
            {project.client}
          </a>
        </h3>
        <p className="text-ui text-pretty text-text-secondary">{project.title}</p>
        <p className="text-ui-sm text-text-muted">{project.disciplines.join(", ")}</p>
      </div>
    </article>
  );
}
```
