Search cards and decks

Search every deck by word, pinyin, meaning or note. Accents, tone marks and the ʻokina are optional.

Docs · 9 of 9 How it's built

How it's built

A static Astro site with a few React islands, styled with Tailwind and served from Cloudflare. All of the content is checked before a single page is built.

The stack

  • Astro 6, building a fully static site.
  • React for the parts that need state in the browser.
  • Tailwind CSS 4 for styling.
  • TypeScript in strict mode throughout.
  • Cloudflare Workers to serve the built files.

Every page is built ahead of time

Running the build turns the content into plain HTML files: the home page, every subject, category and deck page, every study screen, the Play pages, these docs, and a 404 page. There’s no server-side code, no database and no API. Nothing is fetched from other sites while you browse.

Most of each page is plain HTML. A few parts are React islands, components that come alive in the browser:

  • the study screen, with its swipe deck and settings sheet
  • the Play page’s picker, and the mixed session that deals hands
  • the Add to favorites and Add to mix buttons on deck pages
  • the search palette
  • the light and dark button
  • the Learn through chips

Smaller jobs use a few lines of plain script instead: the word-list filter, the header’s glass effect when the page scrolls, the home page’s demo, the header’s mix badge, the home page’s Continue link, and the Cleared badges on tiles.

Content is checked at build time

Cards, decks, categories, subjects, languages and packs are JSON files in Astro content collections (see Subjects, languages and decks). Each collection has a schema written with Zod, so a missing field, a wrong type or a reference to a file that doesn’t exist stops the build with a message saying where.

Some rules span several files, such as “every back is in a language its subject offers”, so a second pass checks those once everything is loaded. It also checks spelling rules like the ʻokina, and the words allowed in a part of speech. Any problem fails the build. See Checks at build time for the list.

These pages are content too: Markdown files in their own collection, rendered with the same layout as the rest of the site.

Search without a server

The build also writes /search-index.json: every studyable deck and card in one file, with short field names to keep it small. The search palette fetches it the first time it opens and does all of its matching and ranking in the browser. The word-list filter uses the same matching rules. See Search.

Play without a server

Every playable deck is also written out on its own, as a small JSON file such as /data/decks/chinese/hsk-1/deck-01.json. The Play page and mixed sessions are built with a light list of every subject, category, deck and pack, with no cards in it. A session then fetches only the decks its current hand draws from. Your mix, packs, favorites and best scores are read from your browser. See Play and packs.

Moving between pages

Astro’s client router swaps pages without a full reload, with a short fade and rise. The header stays in place across swaps, and a tile’s title morphs into the heading of the page it opens. Light or dark mode and the answer language are applied by a small script before each page draws, so neither flashes.

The home page demo

The “How it works” demo on the home page is driven by your scroll position. A small script turns the scroll into two numbers, and CSS works out every card’s position, flip and swipe from them. There’s no animation library. With reduced motion turned on, or without JavaScript, the demo is a static row of the same five steps.

Hosting on Cloudflare

The built files are served by Cloudflare Workers as static assets, with no Worker script in front of them.

  • Addresses have no trailing slash, such as /chinese/hsk-1.
  • An unknown address gets the 404 page, with a 404 status.
  • Scripts, styles and fonts are fingerprinted, so they’re cached for a year.
  • Every branch gets its own preview address before it’s merged.

Fonts

Text is set in Geist, with Geist Mono for readings and labels, and Noto Serif SC for Chinese characters. Astro downloads them during the build and serves them from the site itself.