Note: CLAUDE.md is a symlink to this file. Edit AGENTS.md directly.
styled-components documentation website. Next.js, MDX, styled-components.
Read first
Before editing anything that touches styled-components APIs (createTheme, ThemeProvider, createGlobalStyle, ServerStyleSheet, StyleSheetManager, stylisPluginRSC), read public/llms.txt. It's the battle-tested v6.4 usage guide with real gotchas that contradict training data.
llms.txt is a first-class doc; when MDX docs change (API, SSR, theming, gotchas), update llms.txt in the same pass. Behavior, not internals.
Principles
- Verify before building: read the file before editing.
- Wire completely: new docs pages need the MDX file, the page component, and a
docs.jsonentry. - The SSR registry (
lib/registry.tsx) is load-bearing. Removing it causes FOUC. - Live code blocks (
```react) run in a scoped editor. Hooks must beReact.useState(), no imports. - Isolate
'use client'as deeply as possible. Homepage is a server component with client islands.
Commands
pnpm install: installnpx next build: do NOT run while the dev server is activenpx jest -c .jest.config.js: testsnode scripts/capture-screenshots.mjs <slug> <url> [...]: refresh showcase thumbnails (drives headless Chrome via CDP, strips consent/chat overlays)node scripts/verify-showcase.mjs: gate for additions tocompanies-manifest.tsx
Tokens / fonts
utils/theme.tsis the single source forvar(--sc-*). Dark overrides inGlobalStyles.tsx.- Display Figtree, body Inter, mono Google Sans Code; injected by next/font as
--font-sans/--font-display/--font-mono. next/ogroutes cannot use those helpers; fonts come from committed TTFs underassets/fonts/og/(utils/readOgFont.ts), matching Next.js opengraph-image docs.test/utils/opengraphImageFonts.spec.tsguards against CDN font URLs in anyopengraph-image.tsx.- Three palette tiers:
lightPalette,darkPalette(CVD-optimized; see theme.ts comments for seeds and pipeline) and the ring colors fromutils/logoPalette.ts.theme.palette[N]auto-switches light/dark. Don't hand-pick oklch; derive from palette indices. Accent variants = L/C shift on the same hue, never adjacent indices. - OKLCH hue 0° is pink/magenta, not red. See
logoPalette.tsfor the offset that places red at step 0.
Theme switching
- Three states (light/dark/auto). Raw
<script>in<head>before stylesheets sets the class. data-theme="dark"synced for DocSearch. Toggle icon shows the current mode.
Z-index: 10 (celebration/code), 20 (content/hero), 30 (sidebar), 40 (navbar).
Gotchas (things you cannot discover by reading the code alone)
docs.jsontitles become URL hashes viatitleToDash. Mismatches break sidebar links.- Live editor
scope.tsderives component IDs from tag/component names. Counters cause hydration mismatches. ${ClientComponent} &selector interpolation invokes.toString(), tripping RSC's client-reference guard. Any styled component using this pattern must stay'use client'. CurrentlyCodeBlock.tsx(uses${Note} &).- Import code mixins (
codeTextMixin,editorMixin) fromcomponents/codeMixins.ts, notLiveEdit. Importing fromLiveEditpullsreact-live-runner+sucraseinto every consumer's client bundle. mix-blend-modeandfilteron children ofpreserve-3dflatten 3D. Use alpha /color-mixinstead.PlatonicLogo.tsxfaces must NOT usebackface-visibility: hidden. Per-face axis-angle interpolation during morph can briefly flip a normal and cull mid-animation.preserve-3dz-sorts back faces naturally; a tiny per-face outward bias breaks z-degeneracy.CelebrationEffect.tsxparticles areReact.memo'd;onAnimationEndis a stableuseCallbackthat reads IDs fromdata-*attributes. Don't close over IDs in per-item arrow functions; it defeats memoization.- Nav/sidebar widths use
sidebarWidthfromutils/sizes.tsin plain px; don'trem()them. utils/rem.tsuses a legacy 18px base. Prefer token spacing vars.- Borders use opaque
color-mix(in oklch, text 8%, surface), not alpha. - Releases page uses
markdown-to-jsx, not MDX. - Blog posts are assembled at build time by
utils/blog.server.tsfrom MDXmetaexports. No JSON index. - Blog Bluesky comments are opt-in via
blueskyPostUrlin MDXmeta. Auto-discovery is intentionally disabled (Bluesky closed publicsearchPosts).BlogComments.tsxoverrides hashed CSS-Module selectors via[class*='_name_']matches so overrides survive package upgrades. - CSS compatibility matrix (
utils/cssCompat.ts) v6/v7 columns describe combined-platform native behavior. If iOS stock = yes but Android stock = no, v7 should bepartial, notno.prUrlsis keyed per column; only set entries where status isnoand an upstream PR exists. - Public-facing docs (
sections/,public/llms.txt, blog posts) describe user-observable behavior only: what is supported, what isn't, what to author. Skip mechanism (how the polyfill maps, what prop it lifts, internal field names, parser / handler / registry mechanics, ABI prefixes, dev-warn IDs, sentinel names). - The compatibility matrix (
utils/cssCompat.ts) follows the same no-mechanism rule, with one narrow exception: when explaining version skew, an entry may name a public stock-RN prop (numberOfLines,trackColor) instead of the CSS-side surface, because the matrix exists to compare authoring across versions. Everything else (parser, handler, registry, ABI prefixes, dev-warn IDs, sentinel names) is still off-limits, and caveats are gotchas the author needs to act on, not internals.
Prose rules (mirrored from ~/code/styled-components/AGENTS.md)
- American English in all prose: color, behavior, honor, recognize, serialize, center, organize, etc. Keep original spelling only inside verbatim quotes.
- Avoid em-dashes. Use the full family of punctuation marks (colon, semicolon, parentheses, period).
- Don't paste CSS spec normative text into consumer-facing docs. Link to the relevant
drafts.csswg.orgsection and summarize user-observable behavior. Spec§n.nanchors and verbatim quotes belong next to test assertions, not in prose docs (this includes the compatibility matrix). - Don't name specific AI coding assistants. "An AI coding assistant" is the neutral phrasing.