Skip to content

Agency Case Study Grid with Images

Dark Tailwind CSS portfolio grid of agency case studies with project photos, client, year, key results and service filter buttons.

Live preview

Open in new tab

Code

A dark, editorial case study grid for agency and studio portfolios, built with Tailwind CSS and a small vanilla JavaScript filter. Each card pairs a project photo with the client, the year, a one-line story and two headline results, so visitors see outcomes before they click through to the full case study.

How it works

The root is section.acs-work. Its header holds the eyebrow, the heading and a role="group" of filter buttons, each with a data-acs-filter value (all, branding, product, web) and an aria-pressed state. Tailwind's aria-pressed: variants paint the active button lime, so no classes are swapped by hand.

The cards live in a ul grid that is one column on phones, two from sm and three from lg. Every li.acs-card carries data-acs-category and contains a 16:10 object-cover image with a service badge, a client and year line, an h3 with the case study link and a dl with two results. The link uses after:absolute after:inset-0 to stretch over the whole card, so the entire card is clickable while the accessible name stays the short title. mt-auto on the dl keeps result rows aligned when titles wrap.

On DOMContentLoaded the script initializes every .acs-work. A click on a filter button calls applyFilter(), which updates aria-pressed, sets hidden on cards whose category does not match, replays the acs-in entrance animation on cards that return through .acs-card--in, and writes a summary such as "Showing 2 of 6 case studies in Web." into the status region. Without JavaScript all six cards stay visible.

Customization

  • --acs-accent in style.css: changes the hover underline and the focus ring around a card; also replace lime-300 in the markup for the badges, results and active filter.
  • data-acs-category on each li.acs-card and data-acs-filter on the buttons: add your own services, for example strategy or motion.
  • aspect-[16/10] on .acs-img: use aspect-square or aspect-[4/5] for portrait-led work.
  • sm:grid-cols-2 lg:grid-cols-3 on the ul: switch to xl:grid-cols-4 for a denser wall of projects.
  • bg-neutral-950 on .acs-work: swap for bg-stone-100 and dark text utilities for a light version.
  • transform: scale(1.05) in the .acs-card:hover .acs-img rule: increase or remove the hover zoom.
  • Add a third div inside a card's dl and change grid-cols-2 to grid-cols-3 to show more results.

Accessibility

Filters are real buttons with aria-pressed, grouped under an aria-label, and every change is announced through a visually hidden role="status" region. Results use a dl so each number is read together with its label. Hidden cards use the hidden attribute, so they leave the reading order and the Tab order. The stretched link gives one focus stop per card, and :has(.acs-link:focus-visible) draws a lime ring around the whole card. Images have descriptive alt text, light grey text on near-black keeps contrast high, and hover zoom and entrance motion are removed under prefers-reduced-motion: reduce.

Usage notes

The project photos come from Unsplash and are placeholders; replace them with your own work and update the alt text, links and figures.

Requirements

Add these to your page (already included in the download):

  • <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

The Tailwind browser build is for prototyping. In production, compile Tailwind with its CLI or your bundler.

Agency Case Study Grid with Images – Tailwind CSS portfolios component screenshot
Agency Case Study Grid with Images · Tailwind CSS Portfolios

More Portfolios

Developer Portfolio Page – Tailwind CSS portfolios preview

Developer Portfolio Page

One-page Tailwind CSS developer portfolio with sticky mobile menu, hero stats, filterable project grid, experience timeline and a validated contact form.