Imported from operation-bifrost/operation-bifrost-front (
AGENTS.md). Install upstream withnpx skills add operation-bifrost/operation-bifrost-front. Copyright stays with the author.
AGENTS.md
Guidance for AI coding agents working in this repository.
Project context
Operation Bifrost is the public hub site for a Thai fan-localization collective. The first (and currently only) localized project surfaced here is Steins;Gate. The repo is structured so additional localization projects can be added later as sibling sections under their own URL segment (/steins-gate/, /<future-project>/, ...). See README.md for background on the translation project itself.
This site is not the translation tooling — it is the public-facing site that announces the project, tracks progress, and routes visitors to the Discord/Crowdin/etc.
Stack
- Astro 6 (
astro.config.mjs) with two UI framework integrations: React 19 (primary, for interactive UI / shadcn) and Svelte 5 (legacy / kept for incremental migration). - Tailwind CSS v4 via
@tailwindcss/vite— note this is the Vite-plugin / CSS-first config, not v3 PostCSS. There is notailwind.config.*. Tokens live insrc/styles/global.css(@theme/@theme inline) and per-project theme files (src/styles/themes/*.css). - Cloudflare Workers runtime via
@astrojs/cloudflare+wrangler(wrangler.jsonc). Worker types are regenerated intoworker-configuration.d.tson every dev/build viawrangler types. - Plausible analytics is wired in
src/layouts/base.astro(operationbifrost.com). - Package manager is Yarn 4 (
packageManager: yarn@4.9.1). legacy/holds the previous site implementation kept for reference — do not import from it in new code.
Commands
yarn dev # wrangler types + astro dev
yarn build # wrangler types + astro check + astro build
yarn preview # wrangler types + astro preview
yarn wrangler:dev # run the built worker locally
yarn wrangler:deploy # deploy to Cloudflare Workers
yarn assets:webp <folder> # convert png/jpg/jpeg → webp (add -r to recurse)
yarn assets:thumbs <folder> # generate -thumb.webp variants for srcset (add -r to recurse)
yarn assets:woff2 <folder> # convert ttf/otf fonts → woff2 (add -r to recurse)
The assets:* commands are independent asset jobs (under scripts/, sharing
scripts/lib/asset-job.mjs; powered by sharp + wawoff2) used to keep public/ lean.
Run them individually as needed. They process the given folder's top level only by
default — pass -r / --recursive to descend into subfolders. Each is idempotent
(skips existing outputs unless --force), keeps originals unless --delete-originals,
and supports --dry-run / --help. Note assets:woff2 compresses only — glyph
subsetting stays a manual, content-aware step.
astro check runs on every build and gates the build on type errors — fix type errors at the source, do not bypass.
Routing & per-project architecture
/redirects to/steins-gate/(astro.config.mjs,redirects). Treat/as a router shim, not a real landing page.- Each localization project owns a top-level URL segment and gets its own:
- Layout in
src/layouts/<project>.astrothat wrapsbase.astroand applies a theme class (e.g.theme-steins-gate dark) on<html>. - Pages in
src/pages/<project>/. - Components in
src/components/<project>/. - Theme CSS in
src/styles/themes/<project>.css, imported by that project's layout.
- Layout in
base.astroowns shared<head>(SEO/OG/Twitter meta, favicon, Plausible script, IBM Plex Sans Thai font). Per-project layouts pass props through and add their own<slot name="head">content if needed.- Theme tokens in
global.cssare declared once asvar(--<token>)references, then each.theme-<project>class supplies concrete values. To add a new project, ship a new theme class — do not redefine the token-to-utility mapping.
Design system direction
The component / motion stack is being introduced incrementally. Use these in order of preference when adding interactive UI:
- shadcn/ui (
components.jsonis already configured —new-yorkstyle,neutralbase,lucideicons, aliases@/components,@/components/ui,@/lib/utils,@/hooks). Install components via theshadcnskill (registered inskills-lock.json, lives under.claude/skills/shadcn). Ask Claude to use the "shadcn" skill — do not hand-author shadcn primitives. - DiceUI (https://www.diceui.com/docs/) as a shadcn-compatible extension for primitives shadcn does not cover (combobox, data-table, kanban, sortable, mention, etc.).
- React Bits (https://reactbits.dev/get-started/introduction) as the default registry for motion / decorative components.
All three are registered as namespaced shadcn registries in components.json under registries:
| Namespace | Registry URL |
|---|---|
@shadcn |
https://ui.shadcn.com/r/styles/{style}/{name}.json (implicit default) |
@diceui |
https://diceui.com/r/{name}.json |
@react-bits |
https://reactbits.dev/r/{name}.json |
Install with the CLI directly:
yarn dlx shadcn@latest add button card # @shadcn (implicit)
yarn dlx shadcn@latest add @diceui/combobox # DiceUI
yarn dlx shadcn@latest add @react-bits/FadeContent-TS-TW # React Bits — pick the -TS-TW variant
React Bits component names are case-sensitive and variant-suffixed (-TS-TW, -TS-CSS, -JS-TW, -JS-CSS). This project is TypeScript + Tailwind, so always use -TS-TW.
When you add a component, prefer shadcn first, then DiceUI, then React Bits, then hand-rolled — and keep the project's per-section folder structure (src/components/<project>/...) for project-specific compositions. Generic UI primitives belong under src/components/ui/ per the components.json alias.
After installing from @diceui or @react-bits, read the added files and rewrite any hardcoded imports (e.g. @/components/ui/...) that don't match the project's actual aliases. The CLI only rewrites imports for its own UI files.
Theme tokens & styling discipline
The point of the .theme-<project> system (see "Routing & per-project architecture") is that components are theme-agnostic and the theme owns the palette. Components should not reach into the raw Tailwind palette.
- Use theme tokens, not raw Tailwind colors. In project-scoped components, prefer
bg-background,text-foreground,text-primary,text-primary-foreground,bg-secondary,bg-accent,border-border,ring-ring, etc. Do not usestone-*,amber-*,yellow-*,slate-*, or any other concrete palette class — those bypass the theme and silently break when a sibling project adds a second.theme-*class. - Brand yellow →
primary(oraccentfor the darker active/pressed state). In the Steins;Gate dark theme,--accentis the darker variant of--primary, so the standard pressed-state pattern is: primary buttons gobg-primary→bg-accent, outlined buttons gobg-background→bg-secondary. Matches shadcn conventions. - Never hardcode hex/rgb for theme colors. No inline
style={{ color: "#ffc700" }}and no arbitrary classes likebg-[#1c1917]. If you need the Nixie yellow specifically, usevar(--color-nixie-base)(already declared insteins-gate.css); if you need it as a Tailwind utility, add a--color-*mapping toglobal.css@theme inlinefirst. - Don't fight the theme's marker classes. Classes like
.title,.frame,.sihouette,.nav-link,.social-linkare defined inside.theme-steins-gate { … }and already set color, animation, and hover behavior. Layeringtext-white/30,style={{ color: … }}, or anothertext-*utility on top either overrides the theme (wrong) or gets silently overridden by it (dead code). Use the marker class and let the theme decide. - Adding a new project theme = supply new token values, not new utility mappings. The token-to-utility mapping in
global.css@theme inlineis shared; each project's CSS file just provides concrete--<token>values under its.theme-<project>class.
Design source: design/
design/steins-gate.pen is a Pencil (https://pencil.dev) source file containing layout scaffolding for the Steins;Gate section. Treat it as directional, not contractual — the .pen is a rough page-shape sketch, not a strict spec. Designers can change it.
.penfiles are just json files, but don't open them withRead/Grep/catdirectly. Use the pencil MCP server tools (pencilserver is declared in.mcp.json; tools includeopen_document,batch_get,get_screenshot, etc.).- Always read the notes inside the .pen file before implementing a section — that's where the designer's intent lives. The visual scaffold alone is not enough.
- If the pencil MCP server is not running locally (Pencil extension not installed), say so and proceed from the existing implementation; do not invent design intent.
- Never copy exact pixel values from
.peninto Tailwind arbitrary classes (e.g.max-w-[1416px],h-[620px],px-[80px]). The .pen is directional — its frame widths and spacings are sketches sized to the designer's canvas, not contracts. Use standard Tailwind tokens (max-w-7xl,max-w-3xl,h-16,px-6 lg:px-8,gap-8,py-24, etc.) or theme tokens. Exception: actual image intrinsic dimensions on<img width/height>attributes (those describe the file, not the design).
File & naming conventions
- File names:
kebab-case— components, pages, layouts, styles, assets. E.g.landing-page.tsx,steins-gate.astro,steins-gate.css. - Within TS/React code, follow standard identifier casing:
PascalCasecomponents/types,camelCasefunctions/variables/hooks (use*),UPPER_SNAKE_CASEconstants. - Import via the
@/*alias (tsconfig.jsonpaths) — do not use long relative paths. - The repo is in the middle of moving off Svelte; new interactive UI should use React unless there's a concrete reason otherwise.
Where things live
src/data/— static content modules (<project>-content.ts) that hold copy, nav config, CTAs, image src lists, social links, etc. Components import from@/data/<project>-content. Treat these as the single source of truth for per-project content; do not inline strings in components.src/lib/— true utilities only (cn, formatters, generic helpers). Anything project-specific or content-shaped belongs insrc/data/, not here.src/components/<project>/— project-scoped compositions, grouped by section (hero/,navbar/, ...). Project-internal primitives go insrc/components/<project>/ui/.src/components/ui/— generic shadcn primitives only (percomponents.json).
Documentation lookup
Always reach for an MCP docs source first when querying docs for any library, framework, SDK, API, CLI tool, or cloud service — even well-known ones like React, Astro, Tailwind, or shadcn. Use it before web search, and before guessing from training data, which may be out of date.
Order of preference for docs:
astro-docsMCP — for Astro 6 specifics. Astro's release cadence outpaces training data, so check this first for anything Astro-shaped.shadcnMCP — for component registry search / view / add across@shadcn,@diceui,@react-bits(this is registry data, not prose docs).context7— default for any other library/framework/API. Resolve the library id, then query. Use this before web search for React, Tailwind, Cloudflare Workers, Wrangler, etc.- Web search — last resort, when MCP docs sources don't cover the question.
Do not use context7 for: refactoring, debugging business logic, code review, or general programming concepts — those don't need docs lookup.
MCP servers wired in this repo (.mcp.json)
astro-docs— Astro documentation (http). Use it for Astro 6 specifics; the model's training data may pre-date this release.shadcn— the shadcn MCP server (npx shadcn@latest mcp). It readscomponents.jsonregistriesand exposes search / view / add across all declared registries (@shadcn,@diceui,@react-bits). One server, three sources. Init/refresh withnpx shadcn@latest mcp init --client claude.
context7 is available as a plugin MCP (not in this repo's .mcp.json) — see the "Documentation lookup" section above for ordering.
When a new tool/library category is introduced (e.g. another component registry), wire its MCP server into .mcp.json and document it here so future sessions know it exists.
Things to know
wrangler typesruns before every script; ifworker-configuration.d.tsis dirty/out-of-date, runyarn devonce to regenerate before debugging type errors.tsconfig.jsonexcludeslegacy/, so the legacy implementation does not block builds — but it is also not under type-checking. Do not migrate code by importing fromlegacy/; copy what you need intosrc/and adapt it.- The Steins;Gate theme has a custom Nixie-tube glow keyframe (
bifrost-glow,bifrost-glow-all,bifrost-typinginsrc/styles/themes/steins-gate.css) and three custom BONX fonts loaded from/public/fonts. These are part of the established visual language — reuse them rather than inventing new glow effects.