Work

Case study · Nonprofit · Civil rights and economic mobility · 2026

Urban League of Greater AtlantaA century of service, built for the next one.

A custom WordPress platform, an events system built for browsing, and a modernization plan leadership could act on.

ULGA events app with a featured event, countdown and Register button
ULGA homepage concierge on a phone

The Urban League of Greater Atlanta has opened doors to opportunity since 1920. I rebuilt its website as a custom WordPress platform and continue to care for it every month. In 2026 that partnership grew into a modernization program: a new events platform, a homepage that asks people what they need, a guided giving journey ready to install, and a plan that protects the League's voice and its visibility in search.

Client
Urban League of Greater Atlanta
Role
Lead designer and developer, ongoing partner
Platform
Custom WordPress on Hello Elementor, custom plugins, SiteGround
Timeline
Ongoing care; 2026 modernization program
Deliverables
Site, events platform, donor journey (ready to install), roadmap, image library

Two ways to read this. Every chapter opens in plain language: what changed, and for whom. Technical readers can open Under the hood for the engineering.

100events in the new calendar, seeded from the full history
0disruption to the old calendar while the new one was built
4special-event pages with their own template
6modernization workstreams on a six-week schedule
27illustrative scenes, tagged on three axes
5homepage paths, each one or two clicks from help

The challenge

A rich but text-heavy site, a dated plugin calendar, and a growing need to manage wording consistently across dozens of pages.

The approach

Owned components over fragile plugins, built alongside what already worked, with a plan that preserves meaning first and measures everything.

The result

A live events platform and homepage concierge, a donor journey ready to install, a site-wide wording tool, and a modernization roadmap for leadership.

The partnership

A custom platform, cared for every month

The League's website was redesigned and rebuilt as a custom WordPress installation, and I have kept caring for it since: monthly updates, new pages, announcements and fixes.

That steady relationship is why the bigger 2026 work could move quickly. The foundation was already ours to build on.

  • Custom-built on the Hello Elementor base theme
  • Monthly care and updates
  • Announcements staff can drop onto any page
Under the hoodFor technical readers
  • WordPress on the Hello Elementor theme, built with Elementor containers and custom components.
  • Announcements such as the new tenant rental assistance program started as self-contained HTML blocks and are now a reusable Elementor widget (see Components the staff can run).
  • Hosted on SiteGround with its Speed Optimizer; custom work is built to survive its CSS combining and image lazy-loading (see Care and craft).
WordPressHello ElementorElementor containersSiteGround
Fig 1.1Homepage slides carry live countdowns to the next workshop.
Fig 1.2Signature Events: the League's flagship gatherings in one place.
Fig 1.3Make a donation, on a phone.

Intent navigation

“What brings you here today?”

The League does a great deal, and its pages were rich but text-heavy. Visitors had to read and hunt.

Now the homepage greets people like a concierge. They pick what they came for, such as getting help, volunteering, sponsoring, attending an event or donating, and go straight there. A real person is always one phone call away.

  • Five plain choices on arrival
  • Every destination within two clicks
  • A progress cue that tells people they're already on their way
Under the hoodFor technical readers
  • Designed from an intent map: each “I want to…” resolves to a single best destination across the League's pillars, events, giving and reports.
  • Live on the homepage as five photo cards with a progress cue and a direct phone line, built in Elementor containers.
  • Proposed next step: encode the map as a config-driven intent tree the team can edit without a developer, and reuse the same questions as FAQ content for search and AI answers.
Intent mappingElementorConfig-driven content
Fig 2.1Five photo cards, a phone number, and a progress bar: a quarter of the way there just by arriving.
Fig 2.2Further down, the same idea for clients, partners, events and emergency help.
Fig 2.3The concierge on a phone.

Custom plugin · ULGA Events

An events platform built for browsing

The old calendar plugin was capable but dated on phones and hard to extend. The new events platform feels like a streaming app: a featured event with a live countdown, rows for what's happening soon and what's open for registration, and rows by topic, from small business to housing to civic life.

It was built right beside the old calendar, which kept running untouched until the new one proved itself. Every past and upcoming event was brought over first, so nothing started empty.

  • Featured, Browse, Calendar and Archive views
  • Live countdowns, one-tap registration, add to calendar
  • Built alongside the old calendar with zero disruption
Under the hoodFor technical readers
  • Custom plugin “ULGA Events”: its own post type (ulga_event) and an [ulga_events] shortcode, chosen over a hosted service so the League owns the platform outright.
  • A written non-disruption contract: every identifier is namespaced (post type ulga_event, URLs /event/<slug>/, its own taxonomies, _ulga_ev_ meta, ulga/v1 REST routes, ulga_events_* hooks and options, .ulga-ev CSS), so nothing can collide with EventON, which kept running untouched.
  • Seeded from all 80 published EventON events. EventON never exposed its dates and venues through the API, so the history was collected from the rendered pages, normalized, and imported with an idempotent importer keyed on the original EventON ID: dry run by default, admin screen plus WP-CLI, never deletes. The calendar now holds 100 events.
  • Every imported row keeps its provenance (original ID, source URL and the raw date line as rendered), so any value can be traced back. The import audit surfaced real data defects (end times before start times, mismatched registration dates, 28 events with no venue) and handed staff a cleanup worksheet instead of silently changing their records.
  • Data model built for the API: all meta registered with show_in_rest; dates stored as ISO 8601 strings with an IANA timezone; RFC 5545 recurrence rules; registration, capacity, cost and online fields.
  • Fast by default: the main page renders upcoming events only (74 of the original 80 were past), with the archive on its own linkable, indexable URL. Events stay “upcoming” until they end, not when they start, so multi-day events don't vanish on day one.
  • Calendar export (one event or the whole feed as ICS), a REST endpoint with category filtering, overridable templates, and a homepage featured-events carousel shipped as both a shortcode and an Elementor widget with full style controls, replacing the old widget that read EventON.
  • About 5,500 lines of PHP, JavaScript and CSS across 14 classes.
  • Visual system: a dark, app-like interface in Anton, Bebas Neue and Inter on a red and gold palette.
  • Now the primary experience on /events (plugin version 0.10.0, confirmed live October 2026), with EventON still installed as a fallback. The migration plan runs build, shadow, pilot and cutover, with a minutes-long rollback at every phase.
  • SiteGround's CSS optimizer broke the page's stylesheets when combining files; solved by inlining the module's CSS behind a single wp-config switch.
Custom post typeShortcodeWordPress RESTParallel-run migration
Fig 3.1Featured: the next big moment, with a live countdown.
Fig 3.2Happening soon, this month, open for registration.
Fig 3.3Event pages with topic chips, countdown, Register and Add to calendar.
Fig 3.4Everything to decide and register, in one panel.
Fig 3.5Built phone-first.

Special events

Signature moments, given their own stage

The League's biggest gatherings, like the Economic Mobility Summit and Legacy Impact Gala, Spirit of the League and Super Saturday, deserve more than a calendar entry.

Each now has its own page type, so staff can tell the full story of a signature event while the calendar keeps handling dates and registration.

  • Four special-event pages
  • A Signature Events hub
  • Calendar and stories kept in step
Under the hoodFor technical readers
  • A second custom post type (ulga_special_event) for long-form signature event pages, separate from dated calendar entries.
  • A Signature Events landing page built from custom components.
  • Four live special-event pages confirmed through the WordPress REST API (Meet the Money People, Super Saturday, Spirit of the League, and the Economic Mobility Summit & Legacy Impact Gala).
Custom post typeElementor templates
Fig 4.1The Economic Mobility Summit & Legacy Impact Gala.
Fig 4.2The gala, on a phone.

Donor journey

Earning the gift before asking for it

A donate page that leads with a payment form asks for money before it earns the gift. The donor journey turns the ask into four short steps: pick a path, choose the work you care about, see what your amount actually does, and land in the right place to finish.

Large gifts get a human: a $25,000 prospect is offered a conversation alongside the checkout. The journey is built, packaged and ready to install on the donate page.

  • Give, partner, or make a major or legacy gift
  • Amounts framed by what they do
  • Gifts of $1,000 or more can request a personal follow-up
Under the hoodFor technical readers
  • Packaged as a native Elementor widget under an “Urban League” category, editable by staff across six content sections and a style tab: headings, images, colors, amounts and routing email.
  • Impact areas map to the League's eight causes: homeownership, small business, education, high-demand careers, reentry, childcare, health and financial literacy.
  • Gifts below the threshold deep-link into the existing EveryAction form with amount and frequency already filled in, removing the re-entry step where giving flows typically lose people.
  • Partnership, major and planned gifts capture the full lead and route it to fund development, with a newsletter opt-in.
Elementor widgetEveryAction handoffLead routing
Fig 5.1The journey, presented to leadership before install.
Fig 5.2The donate page it is built to transform.

Strategy

A modernization plan leadership could act on

Leadership received the plan as a website: six workstreams, a six-week schedule showing who owns each piece, and principles that guide every decision.

The language work moves first because it is low risk, while the larger builds run in parallel. The League writes and approves its own words; I supply the evidence and the implementation.

  • Six workstreams in six weeks
  • Clear ownership: the League owns the words
  • Principles: meaning over mechanics, no deceptive tactics, own the technology
Under the hoodFor technical readers
  • Workstreams: 01 editorial audit and remediation, 02 SEO and AI-answer protection, 03 events platform, 04 intent navigation, 05 representation, 06 donor onboarding.
  • Events plan defined phase-by-phase exit criteria (for example, 7 to 14 days of shadowing with zero data drift before any pilot) and a rollback at every step.
  • Built as a static site so every number and finding is linkable, printable and versioned.
Information designStatic siteVercel
Fig 6.1Each workstream gets its own page with phases and exit criteria.
Fig 6.2The roadmap at a glance: six weeks, two swimlanes.

Custom plugin · LSJD Term Swap

One wording standard, applied everywhere

When the League updated how it describes some of its work, changing words page by page would have taken weeks and missed spots. It also risked erasing the names of programs, partners and research the League is known for.

So every live page was reviewed and each phrase got a clear decision: keep it, reword it, or move it. The League approved the new wording, and a custom tool now applies it across the whole site while protecting names, quotes and research titles.

  • A decision for every phrase, approved by the League
  • Program names, quotes and research titles protected
  • Applied site-wide by one tool the League controls
Under the hoodFor technical readers
  • A live crawl of the core pages, with every occurrence logged by page, context and decision in a searchable explorer that replaced the spreadsheet.
  • Approved rewords delivered as a page-by-page change sheet before anything changed on the live site.
  • Custom plugin “LSJD Term Swap” applies rewords on the fly or commits them to the database, with a protected-phrases list; switched on in September 2026.
Content auditCustom pluginProtected phrases

Search and AI visibility

Staying visible in search

When words change, an organization can lose the searches it has always ranked for. The plan keeps the League visible for the topics it owns, without hidden text or other tricks that search engines punish.

Modern search engines and AI assistants understand meaning and related words, so the plan focuses on meaning, and measures before and after every change.

  • No hidden text or other tricks search engines penalize
  • Questions people ask, answered on the page
  • Rankings measured before and after each round of changes
Under the hoodFor technical readers
  • The plan, to run before and alongside each round of wording changes:
  • Semantic and entity optimization so synonyms keep the League tied to its topics.
  • Defensible retention zones: exact terms stay where they are factual, such as news, cited legislation, research titles and quotes.
  • FAQ content and FAQPage schema to be built from the homepage intent questions, the format answer engines quote.
  • Off-site entity signals to reinforce (Candid, Google Business Profile, Wikidata, press) and honest alt text.
  • Baseline in Search Console plus a fixed set of AI-assistant test prompts before edits, then roll back anything that measurably hurts.
SEOAIOSchema.orgSearch Console

Image library

Picturing the whole community

The League serves rural and urban families across many backgrounds, and its imagery should show that. Rather than shopping for stock photos, I built a library of 27 illustrative scenes around the moments the League shows up for: a first home, a first paycheck, a town hall, a fresh start after reentry.

Every image is clearly labeled as AI-generated illustration, never presented as a real client or event.

  • 27 scenes in two sets: life events, and civic and reentry
  • Tagged by program pillar, life milestone and household
  • Honestly labeled, never used as tokenism
Under the hoodFor technical readers
  • Three-axis taxonomy: the League's five pillars, life milestones (first job to retirement, reentry and record clearing, voter registration) and age and household.
  • The taxonomy exposed the gap: civic engagement had one image; the second set took it to eight, plus two on reentry.
  • An asset register describes the people in each scene; the library ships as a filterable static site.
Taxonomy designGenerative imageryStatic site
Fig 9.1Every scene tagged and filterable. Labeled as AI-generated illustration.
Fig 9.2The representation standard behind the library.

Custom plugins · Staff toolkit

Components the staff can run

Small, repeated jobs used to need a developer: announcing a new program with a popup, or thanking sponsors with a row of logos. Now each is a ready-made block the League's staff drag onto any page and fill in themselves.

Both were built to be accessible from the start, so a moving logo strip can always be paused, and nobody is shown the same announcement over and over.

  • A popup for announcements and event flyers
  • A sponsor logo strip that scrolls only when it needs to
  • Every color, size and font editable without code
Under the hoodFor technical readers
  • ULGA Popup 1.0.0: an Elementor widget with image placement, heading, rich text, button and dismiss link; opens after a delay or from any #ulga-popup-ID link; frequency once per visit, every N days or every view; changing the ID re-shows it to people who closed it. Defaults reproduce the original Tenant Rental Assistance popup.
  • ULGA Sponsors 1.0.0: a logo strip that loops seamlessly only when logos overflow (or past a per-device count), with speed set in pixels per second so it stays constant however many logos there are.
  • Accessibility built in: a pause/play control as WCAG 2.2.2 requires, pausing on hover, keyboard focus and when off screen; a still grid for reduced-motion visitors; loop duplicates hidden from screen readers and the Tab key; drag and swipe support.
  • Per-logo alt text, links and visual weight adjustment; per-device sizing; grayscale-until-hover and faded edges as style options.
Elementor widgetsWCAG 2.2Reduced motionPHP 8

Care and craft

The details that keep it working

Real websites live with real hosting quirks. Part of caring for the League's site is making sure new work survives them, so a speed setting never breaks a page and every image actually appears.

  • Custom work that survives the host's speed optimizer
  • Images that load reliably inside custom blocks
  • EventON left untouched until its replacement proves itself
Under the hoodFor technical readers
  • SiteGround Speed Optimizer combines CSS and rewrites images to a 1x1 placeholder with the real URL parked in data-src. Custom blocks opt out with skip-lazy and data-no-lazy, plus a small script that restores data-src as a fallback.
  • When combined CSS broke the whole page, the events module's styles were inlined behind define('ULGA_EVENTS_INLINE_CSS', true), now the first thing to check when styling breaks site-wide.
  • SiteGround bot protection blocks server-side scraping, so site data is collected through a real browser.
PerformanceLazy loadingHosting constraints

Take it with you

The full story, on paper.

The complete case study and a one-page summary, designed for printing, sharing with a board, or attaching to a proposal.