Skip to content

rebuild#1001

Open
quantizor wants to merge 10 commits intomainfrom
feat/blog-section
Open

rebuild#1001
quantizor wants to merge 10 commits intomainfrom
feat/blog-section

Conversation

@quantizor
Copy link
Copy Markdown
Contributor

No description provided.

A ground-up rebuild of styled-components.com on top of the prior site.

Infrastructure
- Migrate from Yarn 4 to pnpm 10
- Husky pre-commit hooks via lint-staged
- Stop tracking tsconfig.tsbuildinfo
- Sitemap, robots.txt, 404 page, error boundary
- llms.txt at the project root for crawlers and AI agents
- Asset proxy route for badge images

Design system
- OKLCH color tokens for consistent perceptual lightness across light/dark
- Theme toggle with light / dark / auto and CSS-first FOUC prevention
- Typography system with display/sans/mono families
- Global styles
- Custom logo concepts component

Navigation
- Sidebar-first navigation with scroll-spy section highlighting
- Sidebar fold/unfold for narrower viewports
- Breadcrumbs and per-page navigation
- Mobile navbar with collapsing sections

Homepage
- Redesigned hero with celebration effect and live editor
- Showcase strip and "as seen at" company logos
- Latest blog post card on the index

Blog
- Full blog section with Medium content migration
- Post listing with year grouping, individual post pages, sidebar
- 16 archived posts plus the decade retrospective

Cleanup
- Remove dead components (BlmBanner, Loading, NavLinks, SeoHead, WithIsScrolled)
- Drop snapshot-only test files
- Drop dead utils (escape, fonts, pathnameToTitle, stripIndent)
Comprehensive update of the documentation corpus to cover styled-components
v6.4. Adds new API references, expands existing guides, and tightens version
labels and language to match actual shipping behavior.

New API references
- createTheme: full reference for the v6.4 CSS-variable theming utility,
  including dark mode patterns and Shadow DOM usage
- stylisPluginRSC: documented in StyleSheetManager and the SSR section
- CSP nonce sources documented in Security with all five supply methods
- attrs improvements: optional-prop typing covered in the basics section

RSC and SSR coverage
- React Server Components section in the SSR guide with do/don't tables
- Child-index selector caveat with the universal :first-of-type fix
- StyleSheetManager-in-RSC behavior documented at the API level
- Theming with CSS custom properties for RSC environments

Migration guide
- New "Notable changes in v6.4" section covering createTheme,
  stylisPluginRSC, StyleSheetManager-in-RSC, CSP nonce, attrs changes,
  faster re-renders, the Metro/Expo nanoid fix, and the IE11 build target
  removal (with provenance to #3333 and #4292)
- Browser support FAQ reframed: v6 has not officially supported IE11
  since the August 2021 v6 planning issue; v6.4 just aligns the compile
  target

Dark mode
- Step-by-step dark mode guide using createTheme + the css partial
  + GlobalStyle overrides + the localStorage anti-flash script

Performance
- One-line callout that v6.4 re-renders skip style work entirely when
  props and theme are unchanged
- Reframed implementation-detail wording (e.g. "hash recomputed every
  render") to observable cause-and-effect

llms.txt
- Full rewrite covering RSC, createTheme, stylisPluginRSC, attrs
  changes, CSP nonce, faster re-renders, RN fixes, and IE11 framing

Cleanup of pre-existing inaccuracies
- Removed implementation-detail leaks from API reference docs
- Fixed invalid quoted CSS values in the homepage getting-started example
- Tightened "v6.3.0+" labels to be precise about what shipped where
Replace Prettier 2.8.8 with the oxc toolchain. oxfmt 0.44.0 is 100%
Prettier v3.8 conformant for JS/TS and ~30x faster. oxlint 1.59.0 adds
linting (none was previously configured).

Tooling
- prettier devDep removed; oxfmt + oxlint added (pinned)
- .prettierrc replaced with .oxfmtrc.json (migrated via oxfmt --migrate=prettier)
- .oxlintrc.json with the correctness-focused plugin set:
  typescript, unicorn, oxc, react, nextjs, jsx-a11y, import, promise, jest
- categories: correctness=error, suspicious=warn
- Three rules disabled as noise:
  - react/react-in-jsx-scope (obsolete since the React 17 JSX transform)
  - import/no-named-as-default (would flag the canonical
    `import styled from 'styled-components'` idiom)
  - import/no-unassigned-import (flags CSS side-effect imports)
- Scripts: `format`, `format:check`, `lint`
- lint-staged runs jest + oxfmt + oxlint on staged JS/TS

Codebase changes surfaced and resolved by the new tooling
- Reformat across 14 files (Prettier 2 -> Prettier 3.8 idioms)
- Removed dead `description` plumbing from BlogListPage / BlogPostPage /
  blog/[slug]/page.tsx -> DocsLayout (SEO descriptions live in
  generateMetadata, the prop was never rendered)
- Added explicit keys to array-rendered JSX in elementToText.spec.tsx
- CaptureScroll: collapsed two useCallbacks into one effect with a
  captured `node` const, fixing the ref-in-cleanup pitfall
- useScrollSpy: extracted `idsKey = ids.join(',')` outside the effect,
  re-derived `localIds` inside, gave the linter a single string dep
- Migrated raw <img> in Slider/Navigation and the showcase page to
  next/image with `fill` and explicit `sizes`
- NavButton in Slider/Navigation: removed dead role/tabIndex (the
  parent Next.js Link is the actual interactive element)
- Renamed shadowed variables in ThemeToggle, companies-manifest,
  app/docs/page.tsx
- Replaced two `Array#sort()` calls with `Array#toSorted()` for
  immutability
- Added `return null` to the docsearch import().then() handler
- Removed unused imports (`css`, `font`) and unused catch binding
- Bumped markdown-to-jsx 9.7.11 -> 9.7.14: the published 9.7.11 dist
  was missing the .d.ts/.d.cts entry-point declarations referenced by
  the exports map, breaking TS7016 in consuming pages
…tion

The plugin's rule count has changed since this line was written and will
change again. Describe the qualitative shape of the plugin instead of
quoting a number that goes stale and erodes trust.
Bumps dev dependencies to versions where the previously-vulnerable
transitive deps (handlebars, minimatch, picomatch, micromatch, yaml,
brace-expansion) are either resolved naturally or coverable by overrides.

Direct devDep upgrades:
- husky 8.0.3 -> 9.1.7 (drops the husky.sh shebang and the duplicate
  postinstall hook; uses `prepare: husky` per the v9 migration guide)
- lint-staged 13.2.2 -> 16.4.0
- jest 29.5.0 -> 30.3.0
- @types/jest 29.5.1 -> 30.0.0
- jest-environment-jsdom -> 30 (was already 30.2.0; now floats to 30.x)
- ts-jest 29.1.0 -> 29.4.9

These upgrades alone resolved 10 of the 18 vulnerabilities.

For the remaining 8 (transitive deps that the upgraded packages still
pull at vulnerable versions), pnpm.overrides forces safe versions:
- brace-expansion ^1.1.13
- minimatch ^3.1.5
- picomatch@<3 ^2.3.2 (for the 2.x consumers in jest-haste-map and
  jest-message-util)
- picomatch@>=4 <5 ^4.0.4 (for the 4.x consumers in fast-glob and
  jest-environment-jsdom)

Picomatch needs the scoped overrides because both the 2.x and 4.x lines
are simultaneously consumed by different parts of the dep tree, and a
single top-level override would force one or the other and break
semver expectations on the side that didn't match.

`pnpm audit`: 0 vulnerabilities. tsc, oxlint, oxfmt --check, jest, and
build all pass.
Bug fixes
- ThemeToggle: cycling never reached `auto` for users on system-dark.
  `getTheme()` was reading the html `dark` class, but the inline
  themeScript adds that class for both "explicit dark" and "auto +
  system-dark", so the two cases were indistinguishable. Read
  localStorage directly instead.
- CaptureScroll: module-level `wheel` listener was non-passive,
  forcing the browser to wait on it for every scroll event app-wide.
  Marked `{ passive: true }`.
- PlatonicLogo: the morph interval ran forever in the global navbar
  even when the tab was hidden. Now pauses on `visibilitychange`.
- Asset proxy: `size.svg` was hardcoded to a stale "12.4 kB" badge URL.
  Removed the entry and the corresponding badge from the homepage.

Dead code
- Deleted components/Nav/BlogSidebarMenu.tsx (115 lines, no consumers)
- Removed unused $active prop from TopLink and CategoryLink in
  SidebarMenus
- Removed empty `FooterLink = styled(Link)` wrapper in Footer

Reuse
- app/not-found.tsx: dropped local titleToDash, imported the existing
  utils/titleToDash
- ThemeToggle: replaced 60 lines of hand-rolled SVG icons with
  LightMode / DarkMode / BrightnessAuto from @styled-icons/material
- DotSeparator: extracted shared `<DotSeparator>` component, replaces
  byte-identical Sep/Separator definitions in BlogListPage and BlogMeta
- HomepageBadges: collapsed four near-identical anchor blocks into a
  BADGES array + .map
- Breadcrumbs: derive DOCS_CATEGORY_LABELS from app/docs.json instead
  of maintaining a parallel hand-keyed table; resolve blog post titles
  via postBySlug

Net: -156 lines.
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
styled-components Error Error Apr 8, 2026 2:19pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 7, 2026

⚠️ No Changeset found

Latest commit: da835d4

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

…ient component

The blog pages used a `createGlobalStyle` rendered inside the
BlogPostPage client component to set max-width and image styles on the
content wrapper. With JS disabled the layout was stable (the rule
landed in the SSR registry), but on hydration the styled-components
client runtime took over and the brief SSR-style → client-style handoff
caused visible text reflow as the rule re-applied.

These rules don't depend on any runtime values, so they don't need
styled-components at all. Moved them to a static `app/blog/blog.css`
imported via a new `app/blog/layout.tsx` route layout. The CSS is in
the page from first paint via Next.js's CSS pipeline, and there's no
SSR-to-client style handoff to flicker.
Blog section
- Year-as-poster list layout with sticky spine, warm blogAccent color
- Split BlogMeta into byline (top) and colophon (bottom)
- Magazine HR treatment scoped to blog routes
- Drop 100ch content cap so blog matches docs width

Layout
- Add theme.layout.gutterFluid (clamp(2rem, 5vw, 5rem)) token
- Navbar and docs/blog/hero content share the same fluid gutter
- HeroContent styled component replaces plain .hero-content class
- Update markdown-to-jsx to 9.7.15 (restores releases page)

Syntax highlighting
- Unify all code blocks through HighlightedCode component — static
  CodeBlock, interactive LiveEdit, and HomepageHeroEditor share the
  same annotation pipeline and token rendering
- Extract codeTextMixin so all code blocks share font weight, colors,
  family, and letter-spacing
- CSS grammar extension: tokenize CSS value keywords (inline-block,
  ease-in-out, all, etc.) as constants
- TSX grammar extension: re-tokenize __html template literals as JS
  so dangerouslySetInnerHTML script content gets full highlighting
- TSX grammar extension: recognize styled.div<Generic> template
  literals so their CSS content still renders with CSS sub-grammar
- JSX tag depth annotator: 4-level complementary palette (cyan,
  blue, amber, teal) stamped on tag fragments only, not embedded
  JS inside attribute expressions
- Property access chain depth: stamp deep class on 2nd+ member so
  the leaf of props.theme.fg renders distinctly
- Optional chaining fix: stamp property-access on identifiers after
  ?. so the depth counter continues through them
- Function machinery group: attr-name, arrow, and parameter share
  brand pink with function calls
- Operators removed from theme dict so arrow wins cascade
- Code palette matches PlatonicLogo brightness profile in dark mode
  (L 0.82, high chroma across all chromatic tokens)
- Warm-tinted neutrals (hue 60) so code text feels inky not disabled
- codeProperty and codePropertyDeep for chain members
- Medium font weight in light mode, light weight in dark mode
Navigation alignment
- Navbar ContentZone mirrors Layout.Content's 120ch + auto-margin box
  model so desktop items align with content edges at wide viewports
- MobileNavbar pads to the content gutter minus each button's icon inset
  so hamburger and theme toggle icons land on the content edge
- Extract theme.layout.contentWidth (120ch) for reuse between Content
  and ContentZone

styled-components
- Bump to 6.4.0-prerelease.14 (from .9)
- GlobalStyles dark color overrides now use theme.vars.color[key]
  instead of hand-rolled --sc-color-${key} strings — names track the
  createTheme prefix automatically

llms.txt corrections
- ThemeProvider must receive the raw theme object, not the createTheme
  output (passing the output produces self-referential var(--x, fallback)
  CSS). Added empirical wiring example.
- theme.GlobalStyle renders nothing without a ThemeProvider ancestor
- Dark mode example uses theme.vars for bare property names
- Added gotchas: @Property for animatable tokens, Next.js 16 <style>
  precedence + href for streaming dedup, dual class + data-theme signal
  for DocSearch-style integrations
- Cut mid-prerelease narration and decorative bold emphasis

AGENTS.md
- New "Read first" section directs agents at public/llms.txt before
  touching createTheme, ThemeProvider, createGlobalStyle, and related
  APIs — trust the doc over training-data assumptions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant