Skip to content

Repository files navigation

CodeYam Academy — the catalog

A browsable catalog of what you can build with software: Application Ideas (59 things worth building) and Types of Applications (the 24 shapes software takes). Every card expands to how you would actually approach it.

This is the discovery-catalog branch — a different design from main, which holds the Proposal and the interactive Map. Here the whole site is discovery: the catalog is the argument, and there is no prose to read first.

Built with Astro and hostable free on GitHub Pages. The catalog lives in typed content collections (markdown under src/content/) rather than a database — there is no server to run and nothing to pay for.

Setup

git clone <this repo>
npm run setup      # installs dependencies and the Playwright browser
npm run dev        # http://127.0.0.1:4321

Other commands:

npm run build      # type-check + static build into dist/
npm run test       # unit tests (vitest)
npm run check      # astro type-check only

The two surfaces

/ — Types of Applications. 24 types, ordered as a climb rather than alphabetically: each roughly adds one capability and one class of problem to the one before — and the cards are numbered 01 to 24 so that ordering is visible, since in a three-column grid the eye reads across rather than down the argument. Expanding a card gives five sections in one column at a readable measure: examples, what it is, good for, the trade-offs, and the technologies typically used.

Every field in the schema is rendered by the surface that reads it. Frontmatter nothing displays is a maintenance cost with no reader, so a new field should arrive with the markup that shows it.

Examples lead, because a type is an abstraction and the fastest way to understand one is to see things you recognise built that way. The list is the type's curated exampleIdeas followed by every other idea that names this type, capped at ten with a link to the rest — so adding an idea needs no edit to any type.

Types lead because the taxonomy is the frame: knowing that a static site is free and unbreakable, and that a database is what buys you shared state, is what makes any individual idea legible.

/ideas — Application Ideas. 59 ideas, from a personal website you finish this afternoon to firmware on a chip. Each card carries a one-line pitch, its family, a rough effort, and how many ways there are to build it. Expanding it gives two sections in one column: what it is, and how you can build it — three or four approaches, easiest first, each with a summary, its pros and cons, and a link to the application type it corresponds to.

The approaches are where the two collections meet, and they are the argument of the whole catalog: an idea is not one thing to build. A run tracker is a form you fill in after a run, or an installable web page, or a native app, or a phone that syncs to a web app — four different costs for four different versions of the same sentence. There are 199 approaches across the 59 ideas.

These are two real routes, not a JavaScript tab toggle — each is separately linkable and the browser Back button undoes a switch.

How the two collections join up

Ideas and types point at each other by slug, in two places.

Each approach names its type. That is the load-bearing link: an idea says "you could build this as a browser-only app, or as a native mobile app", and each of those is a real type with a card of its own. Inverting that mapping is where a type's example ideas come from — a type claims an idea exactly when that idea describes building it that way, so the two directions cannot drift.

Each idea also has a shape: one type slug, the form it most naturally takes, which is where its category colour and its complexity are read from. It is a single value on purpose. An idea used to carry a types array of every shape it might take, and once the approaches existed that array was wrong for 55 of 59 ideas — the approaches already enumerate the options, and far more honestly, since they say what each one costs.

A type also lists a few curated exampleIdeas, which lead its derived ones.

Zod cannot check a reference into a sibling collection, so src/lib/catalog.ts resolves all of these at build time and drops any that point at nothing — a typo costs one link, not the build.

Families are the nine buckets in src/lib/families.ts that the filter, the coloured rail on every card, and the --fam-* colour tokens all key off. They are defined in code rather than content because adding a tenth is a design decision.

The filter

Four facets — Category, Time, Money, Complexity — each a labelled button showing its current answer, opening a modal picker of options with live counts. A facet names its question out loud rather than asking the reader to infer a grouping from nine unlabelled colour chips, which is what the first version did and why the catalog read as denser than it is.

Options within one facet are OR-ed; facets are AND-ed with each other and with the search box, so every question answered narrows the result. Option counts are recomputed against everything else that is selected, so opening "Time" while "Games" is on says how many games take a weekend.

Every facet a card can be filtered by is also displayed on that card — category, complexity, time, and money all appear in the collapsed head. A result that cannot explain why it matched makes a filter feel broken even when it is correct.

Where the values come from:

Facet Types Ideas
Category its family the family of its shape
Time time in frontmatter time in frontmatter
Money money in frontmatter money in frontmatter
Complexity complexity (1–5) the complexity of its shape

time and money are enums, deliberately coarse — "a weekend" and "a week or two" are different decisions, "nine days" and "eleven days" are not. They sit alongside the free-text effort and hosting sentences a card actually displays: the sentence is for reading, the band is for filtering. The enums in src/content/config.ts and the option lists in src/lib/facets.ts have to agree — a value with no option is unreachable, an option with no value filters to nothing.

Every card is rendered into the page up front; the filter only hides the ones that do not match, which is why the catalog stays a static file with no data fetching. The rule itself is a pure function in src/lib/filter.ts (unit-tested, no DOM), and the script in FilterBar.astro is a thin binding layer.

Selections are mirrored into the URL, so a filtered view is a link: /ideas?category=hardware, /?time=weekend&money=free. That is what lets a type card link to "all database-backed ideas" without knowing anything about the other page's markup.

Without JavaScript there is nothing to filter with, so the bar ships hidden and the script reveals it: the page degrades to the complete catalog rather than to a row of dead buttons, and <details> still opens on click.

Project shape

src/
  content/
    config.ts              # typed collection schemas (the data contract)
    types/                 # the 24 application types — one markdown file each
    ideas/                 # the 59 ideas — one markdown file each
  lib/
    families.ts            # the nine families, and resolving an idea into one
    facets.ts              # the four filter questions and their options
    filter.ts              # the catalog filter, as pure functions
    catalog.ts             # collections -> view models, cross-links resolved
    collections.ts         # shared ordering helper
  layouts/
    BaseLayout.astro       # shell: masthead + <head> meta + footer
  components/
    SiteHeader.astro       # wordmark + the two-route switcher
    Hero.astro             # kicker, headline, standfirst
    FilterBar.astro        # facet buttons, modal pickers, search, URL sync
    Legend.astro           # what the nine family colours mean
    ComplexityMeter.astro  # the shared 1-5 machinery meter
    IdeaCard.astro         # one idea, collapsed then expanded
    TypeCard.astro         # one type, with its complexity meter
    Detail.astro           # one labelled block inside an expanded card
    OpenOnHash.astro       # makes a link to a card open that card
  pages/
    index.astro            # Types of Applications (home)
    ideas.astro            # Application Ideas
  styles/
    tokens.css             # design tokens as CSS custom properties
scripts/
  og-template.html         # source for the social preview image
  og.mjs                   # renders it to public/og.png

Editing the catalog

Adding an idea or a type is a content edit, not a code change: add a markdown file under src/content/ideas/ or src/content/types/ with the frontmatter the schema in src/content/config.ts requires. The filename is the slug that cross-links point at.

order decides the sequence. For types it is the editorial climb — rung 9 follows rung 8 because the argument says so, not because of how the files are named. Everything the cards render lives in frontmatter; neither collection uses a markdown body.

Design

Dark, dense, and monospace-inflected, because the job is browsing several dozen cards at once rather than reading one document. Colour is semantic: the nine --fam-* tokens are the families and nothing else, and --accent means interactive state. The site is deliberately single-theme, with no webfonts and no external assets.

Every text colour clears 4.5:1 against every surface it sits on, which is the constraint that matters most here — the smallest text on the site is 11px monospace carrying the effort, the cost, and every section label.

Both catalogs are navigable by heading: the page is an h1, every card title an h2, every section inside a card an h3. Card titles are real headings rather than styled spans, so the 24 types and 59 ideas exist in the document outline.

The social preview image is generated, not drawn: edit scripts/og-template.html and run node scripts/og.mjs to rewrite public/og.png. It uses the same tokens as the site, so it cannot drift from the design by hand.

Deploy to GitHub Pages

Already configured. Pushing to main triggers .github/workflows/deploy.yml, which builds the site and publishes it to https://codeyam-ai.github.io/codeyam-academy-site/.

The base path. A GitHub Pages project site is served from a repo subpath, so the build needs base: '/codeyam-academy-site' — but a base path applies to the dev server too, and with it always on npm run dev serves at that subpath and every codeyam scenario pointing at / or /ideas 404s. So astro.config.mjs reads the base from SITE_BASE, which only the deploy workflow sets. Dev stays at the root; the deployed build gets the prefix.

To reproduce exactly what CI builds:

SITE_BASE=/codeyam-academy-site npm run build

This is also why internal links go through withBase() in src/lib/urls.ts rather than being written as plain /ideas — see The filter.

Custom domain. Add a CNAME file to public/ containing the domain, drop SITE_BASE from the workflow so the base returns to /, point site at the domain, and update DNS. No markup changes are needed: the links collapse back to plain paths on their own. DEPLOY_SETUP.md has the exact A records and CNAME value.

An optional CMS (so the curriculum can be edited without touching markdown) is documented in CMS_SETUP.md; it is not wired up by default.

Develop this project with codeyam-editor

This project is built with codeyam-editor — code and runnable data scenarios are authored side by side against a live preview.

# Clone the repo
git clone https://github.com/codeyam-ai/codeyam-academy-site && cd codeyam-academy-site

# Install codeyam-editor
npm install -g @codeyam-editor/codeyam-editor@latest

# Launch the editor (split-screen terminal + live preview)
codeyam-editor start

Scenario gallery

The captured scenarios in .codeyam/ still describe the Proposal and Map surfaces this branch replaced, so the gallery is empty until they are recaptured against the catalog.

Releases

Packages

Contributors

Languages