Ocean redesign + App Router migration - #52
Open
joelvarty wants to merge 81 commits into
Open
Conversation
…-zone tuning) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… detection Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…down Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ig quarantined, HOSTING.md contract) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…endering bug Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d MCP content Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e 0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t 9 (Phase 0) - next 14.2 -> 16.2.10 (Turbopack build, 8.7s), react 18 -> 19.2.7 - @agility/nextjs 14 -> 16.0.8, @next/third-parties 14 -> 16, @agility/content-fetch to 2.x - serverExternalPackages for applicationinsights (dynamic requires can't bundle) - React 19 types: JSX.Element -> React.JSX.Element (Changelog.tsx) - next lint removed in Next 16: migrate to ESLint 9 flat config (eslint.config.mjs, eslint-config-next/core-web-vitals native flat export); new react-hooks v7 compiler rules demoted to warnings for legacy Pages Router components that the ocean rebuild replaces - tsconfig auto-migrated by Next (moduleResolution: bundler, jsx: react-jsx) Verified: production build passes; homepage, article, and 404/500 prerender; headlessui v1 menus open correctly under React 19 in a real browser with no console errors; lint green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e fronting CDN Standard RFC 9213 header (public, s-maxage=60, stale-while-revalidate=86400) scoped to non-API routes, per the HOSTING.md cache strategy — lets the Netlify proxy (or any RFC-9213-compliant CDN) serve docs pages from its edge instead of proxying every request to origin. Also pin outputFileTracingRoot to this repo. Verified locally: header present on /docs/overview, absent on /docs/api/robots. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ran the official @tailwindcss/upgrade tool: config inlined into styles/globals.css as CSS-first @theme/@Utility (tailwind.config.js removed), PostCSS switched to @tailwindcss/postcss, 19 component files migrated to v4 utility names. Removed dead plugins: nightwind (dark mode was disabled; ocean tokens replace it) and tailwindcss-nested-groups (Changelog's group-scope classes converted to native named groups: group/scope + group-hover/scope:). Verified: build passes; homepage and article pages visually unchanged in browser (header, hero, cards, sidebar tree, prose, callouts, TOC rail); lint green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… script, fonts (Phase 1 / T1) - styles/tokens.css: ocean tokens verbatim from the handoff (neutral scale, light + dark blocks on html[data-theme], radius, elevation, focus-ring pattern) - @custom-variant dark wired to data-theme for Tailwind dark: utilities - Three-state ThemeControl (light/dark/system) in the header; system mode tracks prefers-color-scheme live; persisted to localStorage - No-flash inline script in _document applies persisted theme before paint - Inder (400-only, per handoff gotcha) and Fira Mono added via next/font Per T1's definition of done, no component consumes the tokens yet — the visual reskin lands with the T2 component work. Verified in browser: toggle flips data-theme and persists, system mode resolves correctly, aria-pressed tracks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contentful report assembled from partial verification (their client-rendered docs stall non-browser fetchers — both research agents hung on fetches); unverified items are marked. Synthesis locks in six Phase 3 hub-design decisions and five consensus content investments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s, verified both themes CMS side (instance 67bc73e6-u, all additive): content models FeatureCard (44), LinkCard (45); component models PageHero (46), MediaHero (47), FeatureCardGroup (48, nested cards), ArticleListSection (49, nested items), CodeBlock (50), CalloutBlock (51), ThemeAwareImage (52). Test page 'ocean-test' (pageID 58, staging, hidden from menu/sitemap) composes 5 of them on Main Template. Code side: components/agility-pageModules/ocean/* — tokens-only styling (no hex in components), per-accent card bar/ink CSS (yellow never sits as text on light), ThemeAwareImage light/dark swap classes, OceanLink for ~/ site-root hrefs. Font aliases (--font/--serif/--mono) moved to the <main> wrapper: custom properties resolve var() refs at the declaring node, so :root aliases of next/font vars (defined on main) compute to invalid — and Next 16 forbids next/font in _document, so html-level vars aren't an option. Verified in browser on /docs/ocean-test in dev (preview content): Inder and Fira Mono resolve, hero/code/callout/list render in both themes, theme toggle flips live. Known issue: save_page_model via MCP returns IDs but models never materialize — new page models deferred (existing templates work; see plan). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… link colors Header: surface bg + ocean border, teal nav actives/hovers, tertiary-yellow filled CTA and teal outline CTA (AA in both themes), tokenized mobile menu. Preheader/marketing banner: warm dark neutral (n-900) with tertiary accent. Footer: n-900/n-950 neutrals replacing purples (consistent in both themes). Templates (Main/WithSidebarNav/Fullwidth): page canvas reads --bg/--text. Globals: prose links and list markers to --primary, marketing accents to --tertiary. The chrome is now fully theme-aware; remaining light islands in dark mode are the old CMS-composed hub modules (HeroCategories etc.) that T3 replaces, plus the search input (part of the Cmd-K reskin). Verified in browser in both themes on the homepage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y module tokens
- FeatureCardGroup/ArticleListSection: fetch nested children via
getCustomInitialProps (page fetch uses expandAllContentLinks: false,
so linked lists arrive as {referencename}, same as legacy modules)
- Tokenize kept hub modules for dark mode: ListofLinks (dark band ->
n-900 like the footer), RightOrLeftAlignedLinks, ArticleListing
- custom-shadow now uses --elev-1 instead of purple-tinted rgb
Hub page (pageID 2) recomposed in CMS staging: PageHero + 3 flagship
FeatureCards (primary/secondary/tertiary) + Browse-by-role LinkCards,
keeping Concepts / Guides / APIs / Getting Help.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…no-op, dropdown mangling, page-model phantoms) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…PreviewBar Single floating pill (Agility mark, copy link, edit-in-CMS, options) that expands to an options panel — same design as the marketing site's PreviewBar, on ocean tokens so it themes. Keeps the docs behaviors: exitPreview route, manager deep link, Ctrl/Cmd+Q toggle in live mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Uploaded to CDN at cdn.aglty.io/agility-cms-docs/docs-redesign/ and used as PageHero media on /web-studio, /page-management, /ai (pages 59-61). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nd revalidation webhook Replaces the Pages Router with the App Router architecture modeled on demosite2025 and the marketing site, updated to Next 16 best practices: - app/[locale]/[...slug] catch-all with generateStaticParams over the Agility sitemap (all locales); default locale serves unprefixed URLs via a proxy.ts rewrite (middleware is deprecated in Next 16). Locales come from AGILITY_LOCALES — multi-language is a config change. - Cache Components (cacheComponents: true): lib/cms data layer wraps every Agility fetch in 'use cache' + cacheTag/cacheLife using the reference tag scheme (agility-content/page/sitemap-flat/graphql-*). getAgilityPageProps replaced with our own cached composition. - /api/revalidate webhook: Agility publish events -> revalidateTag(tag, 'max') + revalidatePath for instant invalidation. - draftMode() preview via /api/preview + /api/preview/exit; dev serves staging content; FORCE_PUBLISHED=1 tests published behavior locally. - All getCustomInitialProps modules are now async server components fetching their own data; interactive UIs split into client components (SideBarNavClient, ChangelogClient). Header computes active nav from usePathname; Footer is a cached server component. - JSON-LD (WebSite/TechArticle/BreadcrumbList) emitted in-body per the marketing site's getRichSnippet pattern; generateMetadata replaces HeadSEO with canonical + OG + metaHTML extraction. - All pages/api routes ported to route handlers; Apollo removed from the render path (kept for search indexing); sitemap.xml + robots as handlers. Gotchas fixed along the way (see AGENTS.md): - proxy.ts matcher must list bare '/' explicitly or the home page skips the locale rewrite. - applicationinsights must not initialize during next build — its OTel RandomIdGenerator's Math.random() aborts Cache Components prerenders. - algoliasearch client creation is lazy (host shuffle uses Math.random). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Full stack/routing/caching/data-flow documentation plus the complete CMS model inventory (doc article containers + ocean redesign models) and the Cache Components gotchas discovered during migration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-mode coverage Tokenizes every remaining legacy surface so PUBLISHED content renders ocean-themed without any content changes: - HeroCategories: purple band + white overlapping cards -> ocean hero + accent-barred category cards (rotating primary/secondary/tertiary) - HeroHeading: purple section bands -> ocean hero on --bg - Sidebar (SideBarNavClient), ArticleNav + feedback, Search input + results, ButtonDropdown, FilterBlock, ToggleSwitch, feedback dialog - Article prose: body/heading/table/blockquote colors, inline + block code on --code-bg, list markers; dark-mode hljs token remap (lightfair is a light-only theme); EditorJS block components - TextBlocksWithImages, SDKsFrameworks, RighOrLeftAlignedImageLinks, Changelog UI Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lopers) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- lib/cms-content/articleMarkdown.ts: serialize DocArticle to clean markdown
(markdownContent near-verbatim with H1 handling; EditorJS blocks converted)
- app/api/article-md/[...slug]: markdown endpoint, reached via new proxy
rewrite /{article-path}.md (path in URL, not query — basePath rewrite
drops added query params)
- app/llms.txt: AI-agent index from the cached published sitemap; flagship
entries appear automatically when those pages publish
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Warning/Info article blocks: yellow-*/blue-* -> ocean callout recipe (matches CalloutBlock color-mix styling in both themes) - Table block: brightPurple + hardcoded #D7E2ED header -> tokens - Dialog overlays: bg-opacity-* is a no-op in Tailwind v4 (scrims rendered solid) -> bg-black/50 - Search results header, Header icons, ToggleSwitch, ArticleNav scroll-spy classes, TextBlocksWithImages dots, image-link tile, footer RenderLink hover -> tokens - Remove dead nprogress (JS was dropped in the App Router migration; only the purple CSS remained) and the legacy purple/gray @theme palette Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SideBarNav interpolated a container's reference name straight into a GraphQL
query. That only worked because every container happened to be alphanumeric —
`ManagementSDK-Articles` produced `{ managementsdk-articles (take: 250) {…} }`,
which is a GraphQL syntax error (hyphens are illegal in field names). The throw
propagated out of the server component, so every article in that section
rendered the error boundary ("This page couldn't load") instead of its content.
- Map the reference name to the field name Agility actually exposes: lowercased
with non-alphanumerics folded to `_` (`managementsdk_articles`). Verified a
no-op for all existing containers, which the read API already lowercases.
- Alias both selections (`articles:` / `sections:`) so response keys are stable
regardless of the container name.
- Catch query failures and fall back to the category-only nav, so a nav problem
can never blank the article body again.
Note: a GraphQL-valid name isn't sufficient on its own — hyphenated containers
also return 0 items from the GraphQL index even though REST lists them. The
Management SDK content was therefore moved to new hyphen-free containers
(ManagementSDKArticles / ManagementSDKSections); see the Phase 3 notes in
docs/content-refresh-plan-2026.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AGENTS.md was already fixed, but two active instruction files still claimed the MCP can't publish: - CLAUDE.md: said "cannot set workflow state, publish, or delete". It can (publish_content/unpublish_content, manage_content_workflow, publish_page/ unpublish_page, delete_content_item) — but save_content_items still writes to Staging, so a save isn't live until published, and publishing is outward-facing. - authoring skill: removed the leftover "the MCP can't publish or delete" step that contradicted the skill's own corrected section. - rebuild-plan §4: logged a new MCP bug (initialize_media_upload double-prefixes the folder path) and noted the dropdown-mangling bug did not recur on a 45-choice DropdownList; AGENTS.md's bug list re-synced to match. - content-refresh-plan: Phase 1/2/4 progress, the bulk publish record, the publish-dependency lesson, and the Phase 3 findings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Management SDK section rendered for the first time today. Documents the diagnosis (hyphenated container names are invalid GraphQL field names AND are not indexed by the GraphQL API at all, while REST lists them fine), the fix (new hyphen-free containers, slugs preserved so URLs don't move), the sidebar hardening, and the remaining topic-merge work with a source-to-target map. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records the 8 published language-tabbed articles (with their JS/.NET source pairs and tab-group counts), the Webhooks article that rescued content the topic merge would have dropped, the QC method (every article re-rendered through the site's unified pipeline to verify label-to-panel parity), and the nav entry. Also logs ~10 places where the JavaScript and .NET SDK docs disagree about the same endpoint — naming, return shapes, parameter contracts, a deprecation conflict, and example page sizes that exceed the documented cap. These were reproduced faithfully in the docs rather than guessed at, and are written up for the SDK teams to verify. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The SDK was documented twice — once under /javascript, once under /dotNet — and both copies stayed published when the consolidated language-tabbed set went live, so 14 URLs now duplicate the canonical articles and compete with them in search. Rather than misuse `archived` (whose banner says "no longer actively maintained", which is untrue of freshly-merged content), add a `superseded` status: - Banner wording follows the status: superseded pages say "This page has moved" and link the canonical guide; archived pages keep the unmaintained wording. - Both statuses are `noindex` — for superseded that's the point, it stops the duplicate competing with its own canonical copy. - URLs stay live, per the §4 convention: bookmarks and external links still work. - 14 exact article paths mapped to their canonical successors. The matcher only matches a whole path or a `path/` prefix, so these had to be per-article — the duplicates sit inside otherwise-current /javascript and /dotNet sections. `getArchivedEntry` is aliased to the new `getNoticeEntry` so existing callers compile unchanged. Verified with 39 assertions over the real matcher, covering the two failure modes that would have been damaging: noindexing the new canonical articles, and dropping /javascript or /dotNet from the nav. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Records the `superseded` retirement of the 14 duplicate Management SDK copies and closes out the phase. Notes the optional follow-up (move the section to top-level /management-sdk with redirects) that was deferred to keep URLs stable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…I specs
Every API claim in the docs was hand-written with nothing tying it to the
contract. A spot check found four wrong claims in minutes: a PAT field
documented as required that isn't, a single-item save documented as returning an
array when the endpoint returns one integer, two parameter names that don't
exist in the spec at all, and examples paging at take:1000/5000 against a
default of 50. This makes that check repeatable.
check_drift.py pulls both specs (Management + Fetch), reads articles through the
preview API so unpublished fixes count, and grades findings:
HIGH endpoints absent from both specs; "X is required" where no schema
marks X required; page sizes past a declared maximum
MEDIUM parameter-shaped identifiers the specs never declare; page sizes far
past the defaults where no maximum is declared
INFO coverage
Grading matters because the cheap heuristics do misfire — SDK Options fields
(retryCount) and non-Agility params (webAppName) are expected false positives,
and SKILL.md lists them so they aren't re-reported as bugs. It also documents
the blind spots honestly: unbackticked identifiers, free-form return-type prose,
and semantic drift are NOT detected, so a clean run means "no mechanical
contradiction found", not "the docs are correct".
Verified by re-discovering the known errors independently, including one the
Pages merge had only flagged as suspicious (getPageTemplateName is absent from
the spec), plus two page-size problems in articles I hadn't examined. Confirms
fixes too: the corrected PAT article no longer flags.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Checked the Phase 3 merge findings against the live Management API spec. It settled several open JS-vs-.NET questions and found four claims we had already published were wrong (all inherited from the pre-merge sources): expiryDate documented as required, a single-item save documented as returning an array, two parameter names that don't exist, and examples paging at 1000/5000. Records what was corrected, what the spec confirmed (the locale-variant query params, OAuth endpoints, PAT creation, the webhook payload), and one useful finding: every ".NET SDK can't do this" gap is an SDK gap, not an API gap — the REST endpoints all exist, so the documented workaround is sound. Also notes the take-limit nuance honestly: the spec declares no maximum, so "exceeds the cap" was itself an unverifiable claim; the real defect was examples assuming a single request returned everything. §6 now lists api-spec-drift alongside audit-framework-docs as the monthly anti-drift pair. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…specs The spec diff had a structural blind spot: it can't see claims the docs make about the SDKs. That's where the real errors were. check_sdk_drift.py reads ground truth from the published packages — @agility/management-sdk (npm .d.ts) and Agility.Management.SDK (NuGet assembly metadata via dnfile) — and flags: - install commands naming the assembly instead of the NuGet package - "not available in the JavaScript/.NET SDK" claims the surface refutes - sections attributed to the wrong SDK via their signature lines - parameter-level "X only" claims contradicted by the other signature - documented methods absent from both SDKs It also prints a verified JS <-> .NET method matrix (--matrix), which is the only trustworthy source for cross-SDK availability tables. Precision was the hard part. Earlier passes over-fired by treating "only documented for X" (a statement about our docs) as an availability claim, and by widening the context window until it swept in unrelated method names. It now scopes to the table cell or the note line, strips "use Y instead" hand-offs, stops the backwards walk at headings, and skips anything ambiguous — a wrong subject means "correcting" a sentence that was right. Validated by a negative control against the pre-fix article wording: all four known errors detected. .NET absence is read from assembly metadata rather than `strings`, because the ECMA-335 string heap stores one name as a suffix of another (PublishContent inside UnPublishContent), so absence looks proven when it isn't. Also fixes the .spec-cache gitignore path, which pointed at the skill directory while the scripts cache at the repo root. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Publishing the corrected Management SDK articles pushed the spec checker from
2 HIGH to 3, so the new findings needed verifying rather than trusting. Two of
the three were the checker's fault:
- "`required` (it's a `settings` entry)" was read as "settings is required".
The pattern for "required ... `field`" matched when `required` was itself
the backticked subject. Added a lookbehind.
- `const base = '.../api/v1/instance'` was reported as an endpoint that
doesn't exist. A strict prefix of real spec paths is a URL building block,
not a claim, so it's now exempt.
Chasing the second uncovered a pre-existing hole in the same check: it tested
known paths with re.search, so any invented suffix on a real route
(.../locales/{id}/obliterate) was silently accepted. Both comparisons are
fullmatch now — the query-string fallback included, where the substring match
was also redundant since the query is already stripped.
test_checks.py pins all of it: 19 cases, both directions, flag-cases using the
verbatim pre-correction article wording. Tuning for precision is exactly how a
checker goes quiet, and "0 HIGH" is worthless without evidence it would still
speak up.
Remaining real finding: dotNetArticles/1405 still claims expiryDate is
required. That's one of the 14 superseded duplicates, noindex'd and bannered but
still serving the pre-correction claim to anyone arriving on a bookmark.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…re it
Two numbers had drifted apart. CSS parks a clicked heading 80px below the
viewport top (scroll-margin-top, clearing the sticky header); the scroll-spy
tested `scrollPos >= heading.offsetTop - 60`. After a click that reduces to
`scrollPos >= scrollPos + 20`, which is never true, so the clicked heading never
counted as reached and the highlight stayed one behind — on every heading except
the first.
The offset now lives in one place, `--heading-scroll-offset`, which the CSS uses
for scroll-margin-top and the component reads via getComputedStyle. They can't
diverge again, which is the actual bug.
Also fixed while in here:
- offsetTop -> getBoundingClientRect().top. offsetTop is relative to the
nearest positioned ancestor, and the article sits inside sticky/positioned
wrappers, so it was never a dependable document coordinate.
- window.onscroll = fn -> addEventListener. Direct assignment silently
replaces any other scroll listener on the page.
- rAF-throttled; every sync reads layout and scroll fires far more often than
that is useful.
- Active state is React state now, so aria-current actually tracks the current
heading. It was previously applied by mutating classList, leaving
aria-current permanently unset for screen readers.
- hashchange listener: anchor clicks don't reliably emit scroll before paint.
- Renders nothing when an article has no headings, instead of an empty
"On this page" heading.
- scroll-margin-top extended to h3/h4, which are valid anchor targets too.
Verified against the reported scenario: with the old rule, clicking headings
1-4 of 5 highlighted 0,1,2,3; with the new rule each click highlights itself,
and free-scrolling still tracks correctly.
Adds docs/screenshots-needed.md — the 7 screenshots the two locale articles want,
with capture standards and the original block ids so images land where the author
intended. Those placeholders blocked publishing for four months; logging them
here means prose ships and the images follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
I read "last modified 2026-03-20" as abandoned, stripped the screenshot placeholders from both articles and published them. They were active drafts and were unpublished the same day. Age is not ownership. The doc now says so at the top, names the owner, and the capture checklist starts with "check with the author" instead of ending with "publish". The screenshot specs are still useful; they are a reference, not approval to touch the articles. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both checkers read articles through the preview Fetch API, which is CDN-cached and lags a save by minutes. It reported an article still carrying edits that had already been reverted, and I relayed that as fact — nearly causing the revert to be done twice. The Management API (get_content_item) is uncached and authoritative. Recorded as a blind spot so a stale read isn't mistaken for current state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… type Jina chose Concept 4 on 2026-08-21 — teal #028D83, blue #295BAC, CTA yellow #FFCB28 — on Stone with Warm bone body copy. The neutrals needed no change: Stone and Warm bone were already this site's defaults at the exact hexes. The three brand hues were still the pre-2026 Ocean values. The teal cannot do both of its jobs with one hex. As body-size copy on dark, #028D83 reads 4.38 / 4.08 / 3.59 on bg / surface / raised — under the 4.5 bar everywhere. So --primary stays her value for fills, rules and rings, and a new --primary-text (#02A196, same hue and saturation 4% lighter) reads 5.58 / 5.19 / 4.57 and carries type and icons. Light needs no split. Every text-(--primary) moved to text-(--primary-text); fills, borders and rings did not. Two deliberate exceptions: FilterBlock's form-checkbox (the forms plugin uses `color` for the checked box, so it is a fill) and the code-tab underline. Blue is a background colour on dark — #295BAC as copy is 2.72:1. Dark feature cards take --secondary-bright for both ink and the accent bar, since the fill value is under even the 3:1 graphics bar on a card. Also adds --on-primary, because the label on a teal fill flips by theme: near-black on dark (4.75:1), white on light (5.29:1). This fixes a pre-existing failure — the mega-menu's active pill was 3.74:1 in light using --on-color. Prose list markers: the .prose li::before rules parsed but never painted (a ::before with no content generates no box), leaving @tailwindcss/typography's cool-grey defaults — counters 3.70:1 on dark, bullets 1.28:1 on light. Replaced with the --tw-prose-* variables the plugin actually reads. Diagram sources recoloured per the same fill/type split, and #76716B (--n-500, not a text token) doing fine-print duty at 3.04–3.70:1 became --faint. Every text element in all three now clears AA. The live CDN copies still need uploading — see the rollout section in docs/brand-palette-2026.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…there too
Two bugs with one root.
The on-this-page nav listed headings from a different page. ArticleNav scraped
document.querySelectorAll("#DynamicArticleDetails h2"), and during an App Router
client transition the outgoing page is still mounted — two of those subtrees
exist at once, both connected and visible — so the query merged both articles.
It also took up to 9 seconds to appear. It was built 100ms after hydration, so
it inherited however long the page took to hydrate, and DynamicArticleDetails
was "use client". That meant the whole render path ran twice: the server
produced the HTML, then the browser re-ran unified/remark/rehype over the
markdown and re-ran highlight.js over every code block to produce byte-identical
output, before hydrating ~1,000 highlighted nodes. Measured on the dev server,
the TOC appeared after 102ms on a 4KB article and 8,916ms on a 74KB one with 10
code blocks. Until hydration finished nothing on the page was interactive — nav,
search, theme toggle, all of it. The nav was just the visible symptom.
Headings are now derived on the server (lib/docs/renderArticleBody.ts, shared
with the article module via React cache() so the pipeline runs once per request)
and passed to ArticleNav as a prop, so the nav is in the SSR HTML and there is
nothing to scrape. Only the scroll-spy needs the client.
The body is now a server component. The three things that genuinely need the
browser are separate: ClassicModeSwitch (both bodies arrive as rendered server
nodes, so toggling is a visibility change), ArticleBodyEnhancer (code tabs and
inline-script re-execution — it takes a target id rather than wrapping the body,
so the article is not serialised a second time into the RSC payload), and the
scroll-spy. Two other modules were "use client" for no good reason and dragged
highlight.js in with them: BlockEditor had no hooks or state at all, and
ocean/CodeBlock highlighted in an effect — its copy button is now CopyCodeButton.
On /docs/developers/content-fetch-api: TOC 8,916ms -> 0ms, client JS
13,252KB -> 11,227KB (dev, unminified), 32 -> 27 chunks, and zero chunks
containing highlight.js or the remark/rehype pipeline. Verified: highlighting,
the Classic toggle, scroll-spy tracking, no duplicate heading ids across the two
bodies, and both the markdown and blocks paths.
Production is not measured — every number here is from the dev server.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adopts the shell from Agility-Website-Nextjs-2026 (src/components/preview/preview-bar.client.tsx): a round button pinned to the right edge that opens a centred modal with a status pill, rather than the old left-edge vertical pill. Pinned right because the old bar overlapped the sidebar nav — it was clipping "Using Agility for Multiple Sites" and its neighbours at common widths. Keeps the docs-only editor affordances the marketing panel has no need for: Copy link, the Edit-in-CMS deep link, and the Ctrl/Cmd+Q toggle. Styled from the ocean tokens rather than the marketing site's hardcoded greys, so it tracks the brand palette and both themes, and using this site's sharp-corner radius. Status pills are solid where they need attention and fixed in both themes: near-black on yellow for Preview (12.78:1), white on blue for Development (6.59:1), neutral outline for Live. Replaces alert() and confirm() with in-modal UI — Copy link shows a "Link copied" tick, exiting preview is a two-step inline confirm. No Enter Preview action: /api/preview requires a valid agilitypreviewkey, so there is no keyless way in from the UI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's in this branch
App Router migration (Next 16, Cache Components)
app/[locale]/[...slug], default locale unprefixed viaproxy.tsrewrite — pattern from demosite2025)'use cache'+cacheTag+cacheLife) data layer inlib/cms/with instant invalidation via the/api/revalidatewebhook (revalidateTag(tag, 'max'))draftMode()-based preview, JSON-LD (WebSite / TechArticle / BreadcrumbList), per-module server-component data fetchingOcean design system (T1–T2)
styles/tokens.css), class-based dark mode (data-theme), three-state theme control, no-flash head scriptContent phases (T3–T5, staged in CMS — publish only after this deploys)
/web-studio,/page-management,/aiflagship pages; Overview/Editors/Developers section reordersMachine readability (T7)
/docs/llms.txtindex for AI agents.mdto any article URLVerification
npm run buildgreen: 325 static pages, PPR, both themes smoke-testedAGENTS.mdfully rewritten for the new stack; plan + progress indocs/rebuild-plan-2026.mdPublish sequence (after merge + production deploy)
Publish staged pages 2 / 59 / 60 / 61 (+ section landings 3 / 4 / 5) in the Agility UI — the live bundle must know the ocean components before the content goes live.
🤖 Generated with Claude Code