The JSHeroes conference site: Astro 7, Tailwind CSS 4, content in markdown and content collections. Fully static, no client framework.
pnpm install
pnpm dev # http://localhost:4321, plus a /styleguide route in dev onlyRequires Node 22.12+ (.nvmrc) and pnpm 10 (pinned in package.json).
| Script | What it does |
|---|---|
pnpm build / pnpm preview |
Production build and local preview |
pnpm check |
astro check, TypeScript strict |
pnpm format / pnpm format:check |
Prettier (Astro + Tailwind class sorter) |
CI (.github/workflows/ci.yml) runs format check, astro check and build on every pull request.
Edit these, in this order:
src/data/event.ts: year, edition, dates, venue, ticket link. Markdown pages read it through{{event.year}},{{event.dates}},{{event.venue}}and friends.src/data/flags.ts:CFP_OPEN,WAITLIST_OPEN,TICKETS_ON_SALE. The call to action in the nav, hero and footer follows them.src/data/editions.ts: add last edition (motif, tile colour, group photo inpublic/img/gallery).- People, talks and agenda:
src/content/people,src/content/speaker-talks,src/data/agenda.ts, sponsors insrc/data/sponsors.ts. - Homepage wording:
src/data/home.ts, one file for all section copy.
src/
assets/ base.css (tokens, type, buttons), motion.css, motifs/ (pixel SVGs), bears/
components/
ui/ design-system primitives: Section, Button, Motif, Frieze, Bear, PageHeader...
home/ one component per homepage section
charts/ server-rendered bar and donut charts (transparency page)
content/ markdown collections: people, speaker-talks, blog, tags
data/ typed data: event, flags, editions, agenda, sponsors, communities...
layouts/ BaseLayout (SEO, shell), PageLayout (interior header), MarkdownLayout, Blog*
pages/ routes; .md pages use MarkdownLayout, .astro pages compose sections
plugins/ remark-event: {{event.*}} tokens in markdown
scripts/ motion.ts: scroll reveals
dev/ styleguide, injected only under `pnpm dev`
design/ design handoffs (v1) kept for reference
scripts/ audit tooling, see below
- People are one collection with a
role(speaker,host,organizer,volunteer,ambassador,guest-writer). Photos are typed image fields next to the markdown (src/images/people). Speakers can have a greyscaleportraitcutout for the plate;scripts/cutouts.pymakes them. - Blog posts reference their author (
author: person-id) and tags; add.mdtosrc/content/blog. - Pages in
src/pages/*.mdsetlayout: ../layouts/MarkdownLayout.astroand can addeyebrow,intro,highlight(the word scribbled under the title) andbear.
Tailwind only, plus a small set of component classes in base.css (.btn, .h1-hero, .h2-section, .lede, .eyebrow, .prose). Colour tokens live in @theme in base.css: black-pearl is the ink colour, paper, band, soft, muted-strong and rule are the neutrals, dodger-blue, accent-pink, accent-orange, accent-green the brand colours. Motifs are pixel SVGs rendered at a whole number of pixels per cell (<Motif name="m4_brand" cell={8} />).
All in src/assets/motion.css: data-motion="spin|sway|bob|twinkle" on motifs, data-reveal="up|fade|pixel|stitch|pop" for scroll reveals. Everything is behind prefers-reduced-motion and scripting: enabled, with a CSS failsafe that shows content if the script never runs. See the Motion section of /styleguide.
Scripts in scripts/ check a running site (use a production build):
pnpm build && pnpm preview --port 4400
node scripts/audit.mjs / # overflow at 320-1920px + axe at 390 and 1440
node scripts/keyboard.mjs / # tab order, focus rings, menu, dialog, flip tiles
node scripts/structure.mjs / # headings, landmarks, target sizes
node scripts/motion.mjs / # nothing moves under prefers-reduced-motion
node scripts/shots.mjs / 1440 # screenshots per [data-section] into .shots/They use Playwright (pnpm exec playwright install chromium once).