Skip to content

Rebuild website on Primer Brand design prototype - #2703

Open
aaronpowell wants to merge 37 commits into
mainfrom
aaronpowell-cautious-lamp
Open

Rebuild website on Primer Brand design prototype#2703
aaronpowell wants to merge 37 commits into
mainfrom
aaronpowell-cautious-lamp

Conversation

@aaronpowell

Copy link
Copy Markdown
Contributor

Pull Request Checklist

  • I have read and followed the CONTRIBUTING.md guidelines.
  • I have read and followed the Guidance for submissions involving paid services.
  • My contribution adds a new instruction, prompt, agent, skill, workflow, or canvas extension file in the correct directory.
  • The file follows the required naming convention.
  • The content is clearly structured and follows the example format.
  • I have tested my instructions, prompt, agent, skill, workflow, or canvas extension with GitHub Copilot.
  • I have run npm start and verified that README.md is up to date.
  • I am targeting the main branch for this pull request.

Description

This rebuilds the website/ app on the Brand Engineering design prototype, replacing the previous Starlight-based UI with components and styling ported directly from the prototype (Astro 7 + React + @primer/react-brand). The prototype is treated as the design authority: catalogs, detail pages, the home page, navigation, search, and the renamed "Playbook" (formerly Learning Hub) all now use the prototype's components as-is, with dynamic data (agent/skill/plugin/contributor counts, search index, catalog listings) injected at build time rather than reinvented.

Scope covered by this PR:

  • All catalog pages (agents, instructions, skills, plugins, extensions) with filtering, sorting, and search
  • Resource detail pages, including the split-button install actions (VS Code / Copilot app deep link / CLI copy, depending on resource type)
  • Home page, top navigation, contributors page, and site-wide search
  • Playbook (renamed Learning Hub) index, articles, and Cookbook recipes, migrated to the prototype's article layout and Shiki-based code block styling
  • A large set of incremental fixes and polish items found through hands-on review of the running site: pagination/scrolling on catalog grids, a fixed-position footer overlapping detail-page content, dark mode contrast and theming bugs (article text color, link color, missing ThemeProvider on the Playbook layout), softened scrollbars on filter/sidebar lists, a broken contributor count that reverted to zero after hydration, and an Astro 7 markdown API deprecation warning

Notable implementation details

  • Contributor count: read once from .all-contributorsrc at build time and inlined via vite.define, since several page shells are client:load hydrated and can't safely read the filesystem in the browser. A missing/malformed manifest now fails production builds instead of silently rendering 0.
  • Markdown pipeline: migrated off the deprecated markdown.remarkPlugins option to a markdown.processor: unified({...}) config per Astro 7's guidance, preserving GFM, SmartyPants, and the GitHub-admonitions-to-directives conversion used by mirrored Playbook content.
  • Plugin install UX: the plugin detail page now defaults to a ghapp://plugins/install?source=<id>@awesome-copilot deep link into the Copilot app (mirroring the existing VS Code deep-link pattern on agent pages), with "copy CLI install command" available as a secondary action in the same split-button menu.
  • Verified with an axe-core accessibility scan (zero violations across catalogs, detail pages, home, and Playbook articles in both light and dark mode) and a manual keyboard-navigation pass (skip link, tab order, and focus visibility all confirmed working).

Type of Contribution

  • New instruction file.
  • New prompt file.
  • New agent file.
  • New plugin.
  • New skill file.
  • New agentic workflow.
  • New canvas extension.
  • Update to existing instruction, prompt, agent, plugin, skill, workflow, or canvas extension.
  • Other (please specify):

Additional Notes

This PR does not yet cover every page that exists on the current site; pages without a corresponding design prototype were built following the existing site's UX as a functional guide, using the prototype's components as the visual/interaction authority. npm run build passes (1084 pages) and npm run plugin:validate / npm run skill:validate are unaffected, as this PR only touches website/.


By submitting this pull request, I confirm that my contribution abides by the Code of Conduct and will be licensed under the MIT License.

aaronpowell and others added 18 commits August 14, 2026 17:32
Replace the Astro + Starlight site with the Brand Engineering design
prototype, ported component-for-component onto plain Astro + React
islands. The prototype is treated as the authority on markup and
styling; dynamic data is injected into its components rather than the
components being reinterpreted.

Framework:
- Remove @astrojs/starlight entirely, along with its document shell,
  search, footer and language selector. BaseLayout.astro now owns the
  document head, CSP, social meta and analytics.
- Add @primer/react-brand and @astrojs/react. Alias the package to its
  ESM build in vite.resolve, since the default CJS entrypoint breaks
  named-export detection during SSR, and mark it noExternal so its
  stylesheet imports resolve.
- Promote pagefind to an explicit devDependency; it was previously
  pulled in transitively by Starlight.

Pages: home, the five catalogs, the five detail routes, contributors,
Playbook index and articles, and the cookbook are all rendered by
ported prototype components inside a shared PageShell.

Detail pages share a DetailChassis (hero, breadcrumbs, sticky TOC with
scroll-spy, prev/next) while keeping what makes each type distinct: a
file switcher over bundled skill assets, an included-items grid and
external provenance for plugins, and a hero-scale preview for
extensions.

i18n: resolve translated Playbook entries via Astro.currentLocale
inside the shared article route instead of separate [locale] routes.
The explicit routes collided with the i18n fallback routes, so
translated articles were being shadowed by their English originals and
900 nonsensical double-locale pages were emitted.

Search: TopNavSearch now queries the Pagefind index client-side in
addition to the static resource index, merging on href and degrading
to the static index in dev, where no index has been built.

Also delete the superseded vanilla-TS page renderers and Astro
partials, which the ported React components fully replace.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Fixes the three axe violations surfaced by the a11y audit after the
Primer Brand redesign:

- aria-prohibited-attr: `aria-label` was set on roleless `div`s in
  InstructionDetail and PluginDetail. The "Applies to" list now uses
  list/listitem roles; plugin provenance uses a group role.
- scrollable-region-focusable: the install command `code` element
  overflows horizontally but was not keyboard reachable. Added
  tabIndex to all three render sites.
- color-contrast: the Playbook "New" label used the brand's
  success-fg on success-subtle, reaching only 4.09:1 in light mode.
  Stepped one down the same green ramp for 6.14:1. Scoped to light
  mode; dark mode already passed and its green-7 is near-black.

Also corrects the stale route list in the audit script: /hooks/,
/workflows/ and /tools/ have never existed as pages.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Two issues surfaced by review of the redesigned site:

Catalog facets rendered every option. The prototype's filter groups were
built from small hardcoded arrays, but real data produces 193 tool options
on /agents/ and 245 "Applies to" values on /instructions/. The sidebar grew
to ~10,000px and stretched the whole catalog row, pushing the (already
present) pagination control far below the fold so it read as missing.

Adopt the prototype's own solution for this, which it had already applied to
the extensions page: collapse groups past 10 options behind a "Show N more"
toggle, and cap .filterOptions with an internal scroll area. Ported verbatim
to the agents, instructions, skills, and plugins catalogs.

Detail pages scroll inside .scrollHost rather than the document, but the
footer came from PageShell, outside that element, so it stayed pinned over
the content instead of appearing at the end. The prototype renders its
footer inside the scroll host; PageShell now takes a renderFooter flag so
DetailChassis can do the same. LearningArticleLayout already did this.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Injected markdown inherited color from body, which sits outside the
ThemeProvider and always resolved the light-mode token, making body
copy unreadable in dark mode.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Resource detail pages (agent, instruction, skill, plugin, extension) are
not articles, and their markdown headings do not form a meaningful
outline. Playbook and cookbook articles keep their TOC.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Raw markdown-injected <a> elements previously fell back to the browser's
default blue/purple link colours, which are harsh against the dark-mode
background. Route them through --brand-color-text-link-rest (and the
pressed/hover token) instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
…mode works

LearningArticleLayout replaces PageShell for Playbook articles but never
wrapped itself in a ThemeProvider, so its useTheme() call always fell back
to Primer's light default regardless of the site's actual theme preference.
Split the component into a thin ThemeProvider wrapper plus the existing
implementation (now LearningArticleLayoutBody), matching the pattern already
used by PageShell.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Astro's default Shiki config bakes in the fixed 'github-dark' theme's
literal colors, ignoring the site's actual light/dark mode - this made
plain markdown-fenced code blocks (as opposed to the prototype's own
SyntaxHighlightedCode component) always render a hardcoded dark box
regardless of theme.

Switch shikiConfig to the 'css-variables' theme so highlighted tokens
resolve through --astro-code-* custom properties instead, then map
those to the same brand color tokens the prototype's codeBlock uses
(canvas-subtle background, border-muted border, brand text/link/accent
colors for tokens). Raw markdown code blocks now match the prototype's
bordered, canvas-subtle surface in both color modes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
The contributor badge rendered 0 on Playbook, Cookbook, home and custom
pages, and reverted to 0 on hydration everywhere else.

Two causes:
- Shells that bypass PageShell (LearningArticleLayout, PlaybookIndex,
  PlaybookArticleBody, CookbookIndex, HomePage, TopNav, Custom) defaulted
  contributorsTotal to 0 instead of the site-data value.
- site-data read .all-contributorsrc with node:fs at module scope. Those
  shells are client:load hydrated, so the read threw in the browser and
  the count reset to 0 after hydration.

The count is now read once in astro.config.mjs and inlined through
vite.define as __CONTRIBUTORS_TOTAL__, so it is a literal in both the
server render and the client bundle.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
The filter option lists only set scrollbar-width: thin, so they rendered
the platform default scrollbar. They now use the same muted, transparent
track treatment as the article sidebar, which resolves through
--brand-color-border-muted in both colour modes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Plugin detail pages exposed only a copyable CLI command. They now lead
with a ghapp://plugins/install deep link in the same split-button
ActionMenu the other detail pages use, keeping the CLI command available
as a Copy action in the menu.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Astro 7 deprecates markdown.remarkPlugins in favour of passing a
unified() processor from @astrojs/markdown-remark. Moves the GitHub
admonitions plugin into markdown.processor, clearing the startup
deprecation warning. shikiConfig stays at the markdown level as it is
not part of UnifiedProcessorOptions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Silently falling back to 0 is how the contributor badge regressed before,
so a missing or malformed .all-contributorsrc now throws in production
builds and warns in dev instead of shipping a wrong count.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Copilot AI balanced review requested due to automatic review settings August 18, 2026 05:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@github-actions github-actions Bot added new-submission PR adds at least one new contribution website-update PR touches website content or code labels Aug 18, 2026
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

🟡 Contributor Reputation Check: MEDIUM risk

Check Risk
Profile MEDIUM
Credential audit NONE

Maintainers: please review this contributor before merging.
See the workflow run for full details.
Automated check powered by AGT.

@github-actions github-actions Bot added the needs-review:MEDIUM Contributor reputation check flagged MEDIUM risk label Aug 18, 2026
Comment thread website/src/components/brand/DetailChassis.tsx Fixed
Comment thread website/src/components/brand/DetailChassis.tsx Fixed
Comment thread website/src/components/brand/SyntaxHighlightedCode.tsx Fixed
Comment thread website/src/components/brand/pagefindSearch.ts Fixed
- Fix real typos flagged by codespell: 'Couldn&apos;t' -> 'Couldn't'
  (plain apostrophe, matching convention elsewhere in JSX) and
  'Unparseable' -> 'Unparsable' in catalogFilters.ts
- DetailChassis.tsx: replace sequential HTML entity unescaping with a
  single-pass replace to avoid double-unescape/injection risk flagged
  by CodeQL
- SyntaxHighlightedCode.tsx: make the markup HTML comment regex match
  newlines so multi-line comments cannot break out of the token
  (Bad HTML filtering regexp)
- pagefindSearch.ts: strip HTML tags in a loop until stable so nested/
  malformed markup can't survive a single-pass strip (Incomplete
  multi-character sanitization)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Copilot AI review requested due to automatic review settings August 19, 2026 01:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

Comment thread website/src/components/brand/DetailChassis.tsx Fixed
@aaronpowell aaronpowell self-assigned this Aug 19, 2026
Copilot AI review requested due to automatic review settings August 27, 2026 04:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (14)

Previously missed (3) — in code that hasn't changed since the last review.

website/src/components/brand/ExtensionsCatalog.tsx:384

  • Generated in-repo extension records use installUrl for the GitHub source tree, not a Copilot deep link (eng/generate-website-data.mjs:1234-1260). Consequently, this “Open in Copilot app” button opens GitHub. Build the ghapp://plugins/install?source=... URL from pluginName, as the detail route does.
    website/src/components/brand/AgentsCatalog.tsx:151
  • The generated model facet includes (none), but agents without a model produce an empty models array, so selecting (none) always returns zero matches. Treat that sentinel as models.length === 0.
    website/src/components/brand/PageShell.tsx:59
  • Because this provider is created inside PageShell, useTheme() calls in parent components such as AgentsCatalog, InstructionsCatalog, and PluginDetail execute outside it. Their data-mode={colorMode} values therefore cannot receive the resolved dark mode, so the dark-only grid/menu overrides do not activate. Hoist the provider above the hydrated page component or move those hooks into descendants rendered under this provider.

website/src/components/brand/SkillsCatalog.tsx:135

  • skill.files is generated as an array of file records, but this component types it as a number and passes the array to fileBucketOf. Numeric comparisons then produce no match and the fallback classifies multi-file skills as “1–2 files.” Type files as the generated array shape and pass skill.files.length.
    website/src/components/brand/SkillsCatalog.tsx:49
  • This URL uses GitHub’s /blob/ route for a directory, so the catalog’s source button leads to a 404. Skill folders need the /tree/ route (or a /blob/ link to SKILL.md).
    website/src/components/brand/searchIndex.ts:120
  • staticPages is defined for the fallback index but never added here. When Pagefind is unavailable (notably under astro dev, which is an explicitly supported fallback), searches for Home, Playbook, Contributors, or catalog landing pages return no static result.
    website/src/components/brand/navigation.ts:17
  • The rebuild drops the existing Hooks, Workflows, and Tools destinations (and their routes are absent), so established /hooks/, /workflows/, /tools/, and detail URLs now 404. This conflicts with the PR note that pages without a prototype were retained using the existing UX. Restore those resource routes/navigation or explicitly treat this as a breaking scope change.
    website/src/components/brand/SkillsCatalog.tsx:125
  • The success state and “Install command copied!” announcement are set even when Clipboard API access throws. Only set copied after writeText resolves, otherwise users are told a command was copied when their clipboard is unchanged.
    website/src/components/brand/ExtensionsCatalog.tsx:98
  • This marks the action as copied and triggers the success toast even when Clipboard API support is absent or writeText rejects. Await the write and only call setCopied/onCopied after it succeeds.
    website/src/components/brand/PluginsCatalog.tsx:125
  • The previous catalog exposed “Recently Updated,” but this state is permanently initialized to az and its setter is discarded, making the newest branch unreachable. This catalog therefore no longer provides the sorting behavior claimed in the PR description.
    website/src/components/brand/InstructionsCatalog.tsx:114
  • The sort mode can never change because the state setter is discarded and no sort control is rendered, so the newest path is unreachable. This removes the prior “Recently Updated” sorting behavior despite the PR description stating catalogs support sorting.
    website/src/components/brand/AgentsCatalog.tsx:160
  • This hardcodes alphabetical ordering and the new catalog has no sort state or control, removing the previous “Recently Updated” option. That contradicts the stated catalog sorting scope.
    website/src/components/brand/ExtensionsCatalog.tsx:193
  • This hardcodes alphabetical ordering and no sort control is rendered, so the previous “Recently Updated” behavior is lost. That contradicts the PR’s claim that catalog pages support sorting.
    website/src/components/brand/SkillsCatalog.tsx:141
  • This hardcodes alphabetical ordering and no sort control is rendered, so the previous “Recently Updated” behavior is lost. That contradicts the PR’s claim that catalog pages support sorting.
  • Files reviewed: 73/164 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Most of the site is English-only, but the language selector rendered
unconditionally on every page even though only the
learning-hub/copilot-workshops/app track has mirrored translations.

Add hasTranslations() to playbook-routes.ts, thread a showLanguageSelect
prop through PageShell, TopNav and LearningArticleLayout, and compute
it from the article's englishId in learning-hub/[...slug].astro so only
translated Playbook articles show the selector.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Copilot AI review requested due to automatic review settings August 27, 2026 05:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (15)

Previously missed (3) — in code that hasn't changed since the last review.

website/src/components/brand/AgentsCatalog.tsx:428

  • Changing pages only swaps the six cards while the viewport remains at the pagination control below the grid, so users do not see the newly selected page. Scroll the catalog back into view after updating the page (and respect reduced-motion preferences).
    website/src/components/brand/SkillsCatalog.tsx:355
  • Changing pages only swaps the six cards while the viewport remains at the pagination control below the grid, so users do not see the newly selected page. Scroll the catalog back into view after updating the page (and respect reduced-motion preferences).
    website/src/components/brand/InstructionsCatalog.tsx:565
  • Changing pages only swaps the six cards while the viewport remains at the pagination control below the grid, so users do not see the newly selected page. Scroll the catalog back into view after updating the page (and respect reduced-motion preferences).

website/src/components/brand/SkillsCatalog.tsx:49

  • This URL points a directory at GitHub's /blob/ route, which returns a missing-file page. Skills are folders; use /tree/main/skills/... as the detail page already does.
    website/src/components/brand/SkillsCatalog.tsx:135
  • skills.json emits files as an array of file records, but this component declares it as a number and passes the array directly to fileBucketOf. At runtime arrays coerce to NaN (except an empty array), so every populated skill falls back to “1–2 files” and the 3–5/6–10/11+ filters return incorrect results. Type files as the generated file array and bucket skill.files.length.
    website/src/components/brand/searchIndex.ts:120
  • staticPages() is never added to the returned index, despite the adapter promising fixed top-level destinations. When Pagefind is unavailable (including astro dev), searches for Home, Playbook, Contributors, Extensions, or any catalog landing page therefore return no destination. Seed the result list with staticPages(base).
    website/scripts/a11y-audit.mjs:35
  • Removing the only concrete Playbook article route means this audit now exercises the Playbook index and Cookbook index, but never the new PlaybookArticleBody article chassis. Keep a representative article such as /learning-hub/github-copilot-app/ so the claimed article accessibility coverage remains enforced.
    website/src/components/brand/PluginsCatalog.tsx:381
  • Changing pages only swaps the six cards while the viewport remains at the pagination control below the grid, so users do not see the newly selected page. Scroll the catalog back into view after updating the page (and respect reduced-motion preferences).
    website/src/components/brand/ExtensionsCatalog.tsx:444
  • Changing pages only swaps the six cards while the viewport remains at the pagination control below the grid, so users do not see the newly selected page. Scroll the catalog back into view after updating the page (and respect reduced-motion preferences).
    website/src/components/brand/PluginsCatalog.tsx:125
  • sortMode can never change because its setter is discarded and there is no sort control, making the declared newest mode unreachable. This contradicts the PR's catalog-sorting scope and removes the previous “Recently Updated” option; wire a selector to setSortMode and sort by lastUpdated for newest.
    website/src/components/brand/InstructionsCatalog.tsx:114
  • sortMode can never change because its setter is discarded and there is no sort control, so the existing newest branch is unreachable. This contradicts the PR's catalog-sorting scope and removes the previous “Recently Updated” option; wire a selector to setSortMode.
    website/src/components/brand/SkillsCatalog.tsx:141
  • The PR describes sorting on every catalog, but this catalog always applies title order and exposes no sort state or control, so “recently updated” sorting from the previous page is lost. Add a sort selector and switch this comparator between title and lastUpdated.
    website/src/components/brand/ExtensionsCatalog.tsx:193
  • The PR describes sorting on every catalog, but this catalog always applies name order and exposes no sort state or control, so “recently updated” sorting from the previous page is lost. Add a sort selector and switch this comparator between name and lastUpdated.
    website/src/components/brand/AgentsCatalog.tsx:160
  • The PR describes sorting on every catalog, but this catalog always applies title order and exposes no sort state or control, so “recently updated” sorting from the previous page is lost. Add a sort selector and switch this comparator between title and lastUpdated.
    website/src/components/brand/ExtensionsCatalog.tsx:380
  • installUrl comes from external extension metadata and is rendered directly into an anchor. Unlike the previous renderer and the detail route, this path does not run it through sanitizeHttpUrl, so a malformed javascript:/data: value can become an executable catalog action. Sanitize external installUrl/sourceUrl values before rendering and omit the button when sanitization fails.
  • Files reviewed: 73/164 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

The copilot-workshops hands-on tracks (VS Code, CLI, App, Cloud harnesses) existed and rendered correctly at direct URLs, but had no entry point in the new Playbook index UI -- the Articles grid intentionally excludes nested multi-page tracks by design, and the only prior link was buried in body prose.

Adds a 'Workshop' recommended card (following the existing cli-for-beginners precedent) linking to /learning-hub/copilot-workshops/, and registers the route in pageHref.ts.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Copilot AI review requested due to automatic review settings August 27, 2026 06:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (25)

Previously missed (3) — in code that hasn't changed since the last review.

website/src/components/brand/InstructionsCatalog.tsx:565

  • Changing pages leaves the viewport at the bottom pagination control after the card grid is replaced, so the newly selected page appears above the user. Scroll #catalog into view after the state update and respect reduced-motion preferences.
    website/src/components/brand/SkillsCatalog.tsx:355
  • Changing pages leaves the viewport beside the pagination control while the next six cards render above it. This recreates the catalog pagination/scrolling problem called out in the PR description; scroll the catalog into view after changing pages.
    website/src/components/brand/AgentsCatalog.tsx:151
  • The generated model options explicitly include the "(none)" sentinel, but models.includes("(none)") can never match an agent whose model list is empty. Selecting that advertised filter therefore returns zero results; handle the sentinel by matching models.length === 0.

This issue also appears on line 425 of the same file.

website/src/components/brand/searchIndex.ts:132

  • staticPages() is never added to the returned index, so when Pagefind is unavailable (including astro dev) searches for Home, Playbook, or Contributors return no result despite the documented static fallback. Include the fixed destinations in the index.
    website/src/components/brand/AgentsCatalog.tsx:160
  • This always alphabetizes the catalog; there is no sort state or control for the previously available “Recently Updated” ordering. That regresses the advertised sorting behavior, so expose a sort selector and apply daysSince(lastUpdated) when selected.
    website/src/components/brand/InstructionsCatalog.tsx:114
  • sortMode has no setter and is permanently "az", making the existing "newest" branch unreachable and leaving users without the advertised sort control. Add a selector that updates this state.
    website/src/components/brand/ExtensionsCatalog.tsx:193
  • Extensions are unconditionally alphabetized, with no sort state or control for recently updated items. This leaves this catalog without the sorting behavior promised by the PR; add a selector and apply a last-updated comparator.
    website/src/components/brand/SkillsCatalog.tsx:141
  • The rebuilt skills catalog always sorts alphabetically and provides no recently-updated ordering, although sorting is part of the stated catalog functionality and existed before this migration. Add sort state/control and use daysSince for the updated order.
    website/src/components/brand/PluginsCatalog.tsx:125
  • sortMode can never change because its setter is discarded. Moreover, the non-az path does not sort by lastUpdated, so even wiring a control would produce generator order rather than “newest”; implement the comparator and selector together.
    website/scripts/a11y-audit.mjs:32
  • The rebuilt router no longer contains /hooks/, /workflows/, or /tools/ (nor their detail routes), so removing them from this audit hides user-facing 404 regressions for existing resource types. Restore those routes and keep representative audit coverage rather than dropping the checks.
    website/src/components/brand/AgentsCatalog.tsx:428
  • Changing pages only swaps the six rendered cards while leaving the viewport at the pagination control below them, so users do not see the new page without manually scrolling back up. Scroll the catalog into view after updating the page (while respecting reduced motion).
    website/src/components/brand/PluginsCatalog.tsx:381
  • The page transition only updates state, so the viewport remains below the newly rendered plugin cards at the pagination control. Scroll the catalog back into view after selecting a page, using instant scrolling for reduced-motion users.
    website/src/components/brand/ExtensionsCatalog.tsx:444
  • After pagination, the viewport remains at the control below the grid while the selected extension page renders above it. Scroll #catalog into view after setCurrentPage so the pagination/scrolling fix is effective.
    website/src/components/brand/AgentDetail.tsx:98
  • downloadUrl points at raw.githubusercontent.com, so the HTML download attribute is ignored by browsers for this cross-origin URL and this menu item navigates to the raw file instead of downloading it. Fetch the file and download a same-origin Blob (or serve a same-origin download endpoint); the icon action below has the same issue.
    website/src/components/brand/InstructionDetail.tsx:90
  • Because this URL is on raw.githubusercontent.com, browsers ignore the cross-origin download attribute and navigate to the raw text rather than saving a file. Use a fetched Blob/same-origin URL for both this item and the duplicate icon action below.
    website/src/components/brand/SkillsCatalog.tsx:311
  • The raw GitHub URL is cross-origin, so browsers ignore this anchor's download attribute and open SKILL.md rather than saving it. Implement the download through a fetched Blob/same-origin object URL.
    website/src/components/brand/AgentsCatalog.tsx:384
  • The download attribute does not force downloads for this cross-origin raw.githubusercontent.com link, so the card action opens the raw file instead of downloading it. Fetch the content into a Blob and trigger a same-origin object URL, as the existing file-browser flows do.
    website/src/components/brand/InstructionsCatalog.tsx:521
  • This card links cross-origin to raw.githubusercontent.com; browsers ignore download in that case, so clicking the advertised download action navigates away instead of saving the instruction. Use a fetched Blob or same-origin endpoint.
    website/src/components/brand/SkillsCatalog.tsx:45
  • The generated skills data stores files as an array of file records, not a number (generate-website-data.mjs assigns files directly). Because the page casts data to any, this mismatch reaches fileBucketOf(skill.files), where array-to-number comparisons fail and every skill falls back to “1–2 files.” Type this as an array and bucket skill.files.length.
    website/src/components/brand/SkillsCatalog.tsx:121
  • The success toast is shown even when clipboard.writeText rejects, because the catch is ignored and setCopied(true) runs unconditionally. Only enter the copied state after a successful write so the UI does not falsely report success.
    website/src/components/brand/ExtensionsCatalog.tsx:94
  • This reports “Copied” and fires the success toast even when the Clipboard API is unavailable or the write rejects. Await the write and update success state only after it resolves.
    website/src/components/brand/ExtensionDetail.tsx:67
  • The button switches to “Copied” even if the Clipboard API is missing or rejects the write. Make the handler async and update the success state only after writeText resolves.
    website/src/components/brand/SkillsCatalog.tsx:49
  • This is a directory URL, but it uses GitHub's /blob/ route, so the card's source action does not point to the skill folder. Use /tree/main/skills/... as the detail page already does.
    website/src/components/brand/PlaybookArticleBody.tsx:95
  • Each section chunk still contains its original <h2 id="…">, so assigning the same ID to this wrapper produces duplicate IDs for every article heading. Fragment links and scroll tracking can resolve the wrapper instead of the heading; leave the ID on the rendered heading only.
    website/scripts/a11y-audit.mjs:35
  • The audit now covers only the Playbook landing and Cookbook index; removing /learning-hub/github-copilot-app/ means no actual PlaybookArticleBody/LearningArticleLayout article is scanned. Keep a representative article route so the article-specific accessibility claim remains enforced.
  • Files reviewed: 73/164 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

The hands-on workshop tracks (CLI for Beginners, and the copilot-workshops harnesses) had no shared way to discover them from the Articles grid -- each had a recommended card, but no consistent categorization.

Tags both workshop landing pages 'workshop', adds a new 'Workshop' Kind/content-type facet (derived the same way as the existing Terminology/Tutorial/Example kinds), and includes both landing pages in the Articles grid dataset so filtering by Content type > Workshop surfaces exactly the two tracks.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Copilot AI review requested due to automatic review settings August 27, 2026 06:41
@aaronpowell
aaronpowell requested a review from DanWahlin as a code owner August 27, 2026 06:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (4)

Previously missed (2) — in code that hasn't changed since the last review.

website/src/components/brand/ExtensionsCatalog.tsx:380

  • installUrl is the generated GitHub tree URL, not a ghapp:// deep link (the detail route explicitly converts it at pages/extension/[id].astro:111-121). This button therefore opens the repository while announcing “Open in Copilot app.” Build the app URL from pluginName here as the detail route does, and keep installUrl for the source/fallback action.
    website/src/components/brand/PlaybookArticleBody.tsx:81
  • This newly adopted article chassis stops receiving scroll updates below 75rem: its stylesheet changes .scrollHost to overflow: visible (github-copilot-app.module.css:1896-1956), while LearningArticleLayout only reads/listens to that element’s scrollTop. Playbook and Cookbook pages at those widths therefore never update reading progress or expose the back-to-top control. Update the chassis to branch to window scroll metrics/listeners, as the detail-page hook already does.

website/src/components/brand/SkillsCatalog.tsx:135

  • Generated skill records contain files as an array (generate-website-data.mjs:373-402), but this component types it as a number and passes the array directly into numeric bucket comparisons. Non-empty object arrays coerce to NaN, so every skill falls back to “1–2 files” and the other bundled-file filters return no results. Bucket on the array length (and update the item type accordingly).
    website/src/components/brand/searchIndex.ts:120
  • The fixed top-level destinations are defined by staticPages(base) but never added to the returned index. Consequently, when Pagefind is unavailable (including astro dev, the documented fallback case), searches for Home, Playbook, or Contributors cannot return those pages. Seed the result array with the static destinations.
  • Files reviewed: 73/166 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

- P0: html lang now reflects the actually-rendered locale for a page,
  not the requested URL locale (BaseLayout.astro, playbook-routes.ts,
  PlaybookArticle.astro)
- P0: markdown tables get tabindex=0 via enhanceMarkdownA11y, which
  Playbook articles were never running (PlaybookArticle.astro)
- P0: long inline content and breadcrumbs no longer clip on narrow
  viewports (dotnet-upgrade.module.css, github-copilot-app.module.css)
- P1: strip the markdown document's own leading H1 so detail pages
  don't render the title twice (detail-page.ts)
- P1: document the hero secondary-CTA hex and InstructionsCatalog
  illustration colors as intentional prototype-fidelity values rather
  than defects (styles.module.css)
- P1: add explicit Heading size props in PlaybookIndex.tsx

Ref: github/brand-experience#458
Copilot AI review requested due to automatic review settings August 27, 2026 07:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (2)

website/src/components/brand/PlaybookArticleBody.tsx:95

  • Each chunk still contains its leading <h2 id="…">, so assigning the same ID to this outer section creates duplicate IDs for every Playbook heading. Remove the wrapper ID (the existing heading remains the TOC/scroll target) or strip the ID from the nested heading.
    website/src/components/brand/searchIndex.ts:132
  • staticPages is defined specifically so main destinations are always searchable, but it is never included in the returned index. Because Pagefind is unavailable in astro dev, searches for Home, Playbook, Contributors, or catalog landing pages then return no fallback result. Prepend the static destinations to the generated records.
  • Files reviewed: 73/167 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

aaronpowell and others added 5 commits August 27, 2026 18:05
Renames PlaybookIndex.tsx -> LearningHubIndex.tsx, PlaybookArticle.astro
-> LearningHubArticle.astro, PlaybookArticleBody.tsx ->
LearningHubArticleBody.tsx, PlaybookIcon.tsx -> LearningHubIcon.tsx,
lib/playbook-routes.ts -> lib/learning-hub-routes.ts, and
lib/playbook-article.ts -> lib/learning-hub-article.ts. Updates all
call sites, UI copy, TopNav's playbookLabel prop, nav/search entries,
and home page CTA data to use Learning Hub terminology. URLs under
/learning-hub/* are unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6
Adopts the prototype's github-copilot-app.tsx directly as a dedicated React
component instead of rendering it through the generic markdown pipeline,
preserving its video carousel, comparison list, learn-more band, and other
bespoke JSX exactly as designed.

- New GithubCopilotApp.tsx under components/brand/learning-hub/, ported
  near-verbatim from the prototype (data-wiring only: pageHref import swap,
  media paths).
- The two large source videos (~23MB, ~32MB) are not committed; the article
  falls back to their poster/webp images with a TODO to host them externally.
- learning-hub-bespoke-articles.ts lists slugs that bypass the generic
  LearningHubArticle.astro pipeline.
- [...slug].astro branches to the bespoke component for "github-copilot-app"
  (English only for now); all other slugs and locales keep the generic path.
Add bespoke Learning Hub article components for the remaining prototype-backed
article pages, wire the cookbook index to the data-driven port, and route the
CLI overview page through the new bespoke component.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80686fef-efe3-4cdd-8cd6-bfa61a5d0af6

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (1)

website/src/components/brand/searchIndex.ts:120

  • staticPages() is never added to the returned index, despite being defined specifically for the top-level destinations. In development, where Pagefind is intentionally unavailable, searches for “Home”, “Contributors”, or a catalog landing page therefore return no page result. Seed the result array with these entries.
  • Files reviewed: 78/181 changed files
  • Comments generated: 6
  • Review effort level: Balanced

id: entry.id,
title: entry.data.title,
description: entry.data.description ?? "",
href: `/${entry.id}/`,
// Scrollable <pre>/<table> blocks in this markdown need to be made keyboard
// focusable the same way detail-page markdown already is (see
// lib/detail-page.ts) — Learning Hub articles were missing this pass entirely.
const html = enhanceMarkdownA11y(entry.rendered?.html ?? "");
const destinations = [
{ label: "Agents", page: "agents" },
{ label: "Instructions", page: "instructions" },
{ label: "Learning Hub", page: "learning-hub-copilot-app" },
Comment on lines +132 to +136
if (window.matchMedia(TWO_COLUMN_QUERY).matches) {
scrollHost.scrollTo({ top: 0, behavior: "smooth" });
} else {
window.scrollTo({ top: 0, behavior: "smooth" });
}
Comment on lines +194 to +196
const scrollToTop = () => {
contentScrollRef.current?.scrollTo({ top: 0, behavior: "smooth" });
};
Comment on lines +43 to +46
export function CookbookIndex({
sections,
searchIndex = [],
contributorsTotal = siteContributorsTotal,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review:MEDIUM Contributor reputation check flagged MEDIUM risk new-submission PR adds at least one new contribution website-update PR touches website content or code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Website redesign

4 participants