Teal Media · 2024
The Brookings Institute
A mission-driven thinktank center.
Timeline
3 months
Role
Front-End Developer
Stack
WordPress · HTML/CSS · JS

Brookings is a nonprofit public policy organization based in Washington, D.C. Working with Teal Media, I helped build out custom WordPress templates for their research-heavy content, balancing dense policy content with a clean, readable layout.
Overview
Brookings publishes a constant stream of research: reports, op-eds, expert commentary, event recaps. The redesign needed a front end that could hold all of that without feeling like an archive, dense, but never cluttered.
My part of the build was the front end: turning the design system into custom WordPress templates, and making sure hundreds of research posts, expert profiles, and program pages actually behaved consistently once real content landed in them.
What I built
- Built custom WordPress page templates for research posts, reports, and expert profiles
- Wrote the responsive HTML/CSS component library (cards, filters, pagination) reused across every content type
- Implemented the front-end JS behind the research library's live filtering and search
- Tuned typography and spacing specifically for long-form policy reading
Debounced search that respects the server
The research library filters live as you type. Firing a request per keystroke across an archive that size is a good way to melt a server, so the input is debounced and in-flight requests are cancelled when a newer one supersedes them, you get the feeling of instant filtering without the traffic that usually implies.
One component library, every content type
Brookings publishes reports, op-eds, expert profiles, event recaps, and commentary, and each has its own editorial rules. Instead of a bespoke template per type, I built one responsive HTML/CSS component set (cards, filters, pagination, bylines) that every template composes from. New content types get assembled from existing parts rather than designed from scratch.
Typography tuned for long-form policy
Most of these pages are read, not skimmed, some run to thousands of words of dense policy analysis. Measure, line height, and the spacing between headings and body were tuned specifically for sustained reading rather than inherited from a framework default. It's the least flashy work on the project and probably the most load-bearing.
// research-library.js: debounced live searchconst debounce = (fn, delay = 200) => { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); };}; searchInput.addEventListener("input", debounce((e) => { filterResearch(e.target.value);}));