Designed & built In development

1800Outdoor

A structured directory of outdoor resources built on a 15-table relational taxonomy, with Postgres full-text search, synonym expansion, and editorial publishing rules that prevent thin pages.

Project
1800Outdoor.com
Role
Founder & Developer
Timeline
2026
localhost:1800 — local development build
The 1800outdoor homepage: a hero headed "Find trusted outdoor resources faster" over a forest photograph, with a resource search field and a Browse by activity row of ticket-stub cards for Archery, Backpacking, Birdwatching, Camping, and Canoeing.

What it is

A curated directory of outdoor resources — brands, blogs, apps, guides, retailers, local businesses, nonprofits, and government and safety organizations — connected through categories, locations and tags.

Directories are an information architecture problem wearing a website costume, which is why this one interested me.

The taxonomy

The schema runs to 15 tables with Postgres enums, modeling listings against categories, locations and tags as real relationships rather than a flat table with a type column. That’s what makes a listing reachable from multiple legitimate paths — by category, by place, by tag, or through a curated “best of” page — without duplicating it.

Content is authored as markdown and imported into the database through a pipeline, so entries are reviewable as files and queryable as data.

Postgres full-text search with synonym expansion, which is the detail that actually matters. Users don’t search the vocabulary a taxonomy uses — someone looking for a “rucksack” needs to find things filed under “backpack.” Expanding query terms against a synonym set is the difference between a directory that answers questions and one that only works if you already know its language.

The same principle as metadata and title work on an enterprise knowledge base: findability is about the searcher’s words, not the author’s.

An editorial rule worth mentioning

City pages don’t publish until they have at least five listings.

That constraint is enforced in code, and it exists because thin local pages are the standard failure mode of directory sites — they get built for SEO, offer nothing, and drag down the credibility of everything around them. Better to have fewer pages that are worth landing on.

Implementation notes

The application is built — 34 pages including per-type detail routes and an admin area for listings, taxonomy, content pages, submissions and claims — but the public domain currently serves a launching-soon page while the directory fills out, so the screenshots here are the application running locally against its seeded catalog. Worth stating plainly: the platform works, the catalog isn’t deep enough to open yet, which is the same editorial judgment as the five-listing rule above applied to the site as a whole. Submission and claim flows let listings be proposed and owned. Dynamic OG images and favicons are generated at the edge, JSON-LD structured data is built per page type, and local development runs against embedded PGlite so there’s no database to provision to work on it. Five migrations, 48 seeded listings, admin protected by HTTP basic auth.

Inside the project

2
localhost:1800 — local development build
The Oregon hub listing resources as ticket-stub cards, each showing a category and region label, a "Best for" line, descriptive copy, tags such as Official Source and Family Friendly, and a trust score, status, and scope footer.
Every listing carries a trust score, a verification status, and a scope — regional or statewide — so the directory says how much confidence to place in each entry rather than presenting all results as equivalent.
localhost:1800 — local development build
The Outdoor categories index showing the Activities section as a grid of cards for Archery, Backpacking, Birdwatching, Camping, Canoeing, Cross-Country Skiing, Fishing, Geocaching, Hiking, Hunting, Kayaking, and Mountain Biking, each with a one-line scope description.
The category index exposes the taxonomy directly. Each activity states its scope in a line, so the browse path tells you what a category contains before you commit a click.

Questions about this work?

Happy to go deeper on any of it — the decisions, the trade-offs, or what I'd do differently.