Website expansion: docs IA, examples, blog, legal pages, and UX refresh#36
Website expansion: docs IA, examples, blog, legal pages, and UX refresh#36
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR expands the Next.js website to support a larger documentation/info architecture (docs, examples, blog, legal pages), plus navigation/search/SEO updates and a landing-page UX refresh.
Changes:
- Expanded site IA: new docs sections, examples hub, blog posts, and legal/policy pages (privacy/terms/consent/contact/license).
- Navigation + discovery updates: header/footer/sidebar links, search index expansion, dynamic sitemap, and
llms.txt. - UX/accessibility improvements: mobile docs drawer focus-trap work, new hero install tabs, and updated styling + OpenGraph/Twitter images.
Reviewed changes
Copilot reviewed 47 out of 47 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| website/src/components/Sidebar.tsx | Adds new docs links + mobile drawer focus management |
| website/src/components/SearchHighlight.tsx | Improves highlight cleanup and effect teardown |
| website/src/components/Search.tsx | Expands search index + refines modal open/reset + highlighting |
| website/src/components/HeroInstallTabs.tsx | Adds tabbed install UI for homepage hero |
| website/src/components/Header.tsx | Updates nav links and header styling |
| website/src/components/Footer.tsx | Expands footer IA (legal/docs/examples) + layout tweaks |
| website/src/components/ConsentPreferencesManager.tsx | Adds consent preference manager UI (localStorage-backed) |
| website/src/components/ConsentBanner.tsx | Adds consent banner + persistence |
| website/src/app/use-cases/page.tsx | Adds top-level “Use Cases” hub page |
| website/src/app/twitter-image.tsx | Adds Twitter card image route (re-exports OG image) |
| website/src/app/terms/page.tsx | Adds Terms of Use page |
| website/src/app/sitemap.ts | Adds dynamic sitemap route for expanded site |
| website/src/app/roadmap/page.tsx | Updates roadmap copy and stats |
| website/src/app/privacy/page.tsx | Adds Privacy Policy page |
| website/src/app/page.tsx | Replaces hero one-liner with HeroInstallTabs |
| website/src/app/opengraph-image.tsx | Adds OG image generator route |
| website/src/app/llms.txt/route.ts | Adds llms.txt route for LLM/GEO indexing hints |
| website/src/app/license/page.tsx | Adds license page that reads repo root LICENSE |
| website/src/app/licence/page.tsx | Adds UK spelling alias redirect to /license |
| website/src/app/layout.tsx | Adds structured data, robots metadata, OG/Twitter images, consent banner |
| website/src/app/integrations/page.tsx | Adds Integrations landing page |
| website/src/app/globals.css | Refreshes “glass” styling variables and classes |
| website/src/app/examples/workflows/page.tsx | Adds workflow playbooks page |
| website/src/app/examples/usages/page.tsx | Adds usage cookbook page |
| website/src/app/examples/page.tsx | Adds examples hub page |
| website/src/app/donate/page.tsx | Adds donation/support page |
| website/src/app/docs/use-cases/page.tsx | Adds docs-scoped use-cases page (currently duplicative) |
| website/src/app/docs/troubleshooting/page.tsx | Adds troubleshooting guide page |
| website/src/app/docs/roadmap/page.tsx | Changes docs roadmap route to permanent redirect |
| website/src/app/docs/integrations/page.tsx | Adds docs integrations redirect to top-level integrations |
| website/src/app/docs/installation/page.tsx | Updates installation docs (Go version + changelog link) |
| website/src/app/docs/getting-started/page.tsx | Updates prerequisites + links to installation page |
| website/src/app/docs/faq/page.tsx | Adds FAQ page |
| website/src/app/docs/contributing/page.tsx | Adds contributing guide page |
| website/src/app/docs/ci-cd/page.tsx | Adds CI/CD recipes page |
| website/src/app/docs/changelog/page.tsx | Adds docs changelog page (currently out of date vs repo changelog) |
| website/src/app/contact/page.tsx | Adds contact/support page |
| website/src/app/consent/page.tsx | Adds consent preferences page and embeds manager |
| website/src/app/blog/use-case-story/page.tsx | Adds blog case study post |
| website/src/app/blog/tool-ranking/page.tsx | Adds blog ranking post |
| website/src/app/blog/tool-comparison/page.tsx | Adds blog comparison post |
| website/src/app/blog/technical-deep-dive/page.tsx | Adds blog technical deep dive post |
| website/src/app/blog/page.tsx | Adds blog index page |
| website/src/app/blog/migration-playbook/page.tsx | Adds migration playbook post |
| website/src/app/blog/developer-workflow/page.tsx | Adds developer workflow post |
| website/public/sitemap.xml | Removes static sitemap file in favor of dynamic sitemap route |
| website/public/robots.txt | Keeps sitemap reference and adds llms.txt hint comment |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const focusable = mobileDrawerRef.current?.querySelectorAll<HTMLElement>( | ||
| 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])' | ||
| ); | ||
| const first = focusable?.[0] ?? closeButtonRef.current; | ||
| const last = focusable?.[focusable.length - 1] ?? closeButtonRef.current; | ||
| first?.focus(); |
There was a problem hiding this comment.
focusable?.[focusable.length - 1] still evaluates focusable.length even when focusable is possibly undefined from the optional chain, which will fail TypeScript type-checking (and can be fragile). Compute last only after confirming focusable is defined (e.g., via a conditional) or derive last inside the if (!focusable || focusable.length === 0) guard.
| const closeMobileMenu = () => { | ||
| setMobileOpen(false); | ||
| requestAnimationFrame(() => { | ||
| openButtonRef.current?.focus(); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
closeMobileMenu always shifts focus back to the (mobile) open-menu button. Because the same handler is attached to all sidebar links, clicking a desktop sidebar link will also trigger this focus change and can move keyboard focus to a hidden element (lg:hidden), which is an accessibility/UX regression. Consider only restoring focus when the mobile drawer was actually open (e.g., gate on mobileOpen or pass a flag from the mobile-only interactions).
| <h3 className="text-sm font-semibold">Consent Preference</h3> | ||
| <p className="text-sm text-[var(--color-text-secondary)] leading-relaxed"> | ||
| When you interact with the consent banner, your choice is stored in browser <code>localStorage</code> under the key <code>xenvsync-consent-v1</code>. This is a single boolean value that prevents the banner from re-appearing on subsequent visits. It does not identify you or leave your browser. | ||
| </p> |
There was a problem hiding this comment.
The privacy policy claims the consent preference stored in localStorage is “a single boolean value”, but the implementation stores a JSON object with { choice, updatedAt } (see ConsentBanner/ConsentPreferencesManager). Update this text to accurately describe what is stored so the policy matches actual behavior.
| export const metadata = { | ||
| title: "Changelog - xenvsync", | ||
| description: | ||
| "Full release history for xenvsync — security fixes, new commands, vault format changes, and upgrade guidance from v1.0.0 through v1.12.0.", | ||
| openGraph: { | ||
| title: "Changelog - xenvsync", | ||
| description: "Release history and upgrade guidance for xenvsync.", | ||
| url: "https://xenvsync.softexforge.io/docs/changelog", | ||
| }, | ||
| alternates: { canonical: "https://xenvsync.softexforge.io/docs/changelog" }, | ||
| }; | ||
|
|
||
| const releases = [ | ||
| { | ||
| version: "v1.12.0", | ||
| date: "2026-04-01", | ||
| tag: "Latest", | ||
| tagColor: "text-emerald-400", | ||
| sections: [ |
There was a problem hiding this comment.
The docs changelog is already behind the repository changelog: it lists v1.12.0 as “Latest” and metadata says “through v1.12.0”, but the repo root CHANGELOG.md has v1.13.0 dated 2026-04-02. This page should either include v1.13.0 (and mark it latest) or be generated/synced from CHANGELOG.md to avoid drifting release info.
| export const metadata = { | ||
| title: "Use Cases - xenvsync Docs", | ||
| description: | ||
| "How xenvsync fits into real workflows: solo developer local setup, startup team secret sharing, enterprise CI/CD pipelines, open-source maintainer credential isolation, and Docker/container workflows.", | ||
| openGraph: { | ||
| title: "Use Cases - xenvsync", | ||
| description: "Real-world xenvsync workflows by team size and delivery model.", | ||
| url: "https://xenvsync.softexforge.io/docs/use-cases", | ||
| }, | ||
| alternates: { canonical: "https://xenvsync.softexforge.io/docs/use-cases" }, | ||
| }; |
There was a problem hiding this comment.
This page introduces a full /docs/use-cases article while the sidebar/search/sitemap point users to the canonical top-level /use-cases route. Unless you intentionally want two separate pages, this duplication is likely to drift and can create SEO duplicate-content issues. Consider making /docs/use-cases a redirect to /use-cases (similar to /docs/integrations and /docs/roadmap) or otherwise ensure it’s linked and clearly differentiated.
| <div | ||
| role="tablist" | ||
| aria-label="Install instructions" | ||
| className="mb-3 p-1 rounded-xl bg-[color:rgba(12,12,20,0.9)] border border-[var(--color-border-bright)] inline-flex flex-wrap justify-center gap-1" | ||
| > | ||
| {installTargets.map((target) => { | ||
| const isActive = target.id === activeTarget.id; | ||
| return ( | ||
| <button | ||
| key={target.id} | ||
| id={`install-tab-${target.id}`} | ||
| role="tab" | ||
| aria-selected={isActive} | ||
| aria-controls="install-tab-panel" | ||
| onClick={() => { | ||
| if (target.id !== activeId) { | ||
| setActiveId(target.id); | ||
| } | ||
| }} |
There was a problem hiding this comment.
HeroInstallTabs applies ARIA tabs roles (tablist/tab/tabpanel) but doesn’t implement the expected keyboard interaction model (arrow-key navigation) or manage tab focus (tabIndex=0/-1 for active/inactive tabs). Either add the missing keyboard handling / roving tabIndex, or consider using simpler semantics (e.g., a segmented button group) to avoid misleading assistive technologies.
Summary
This PR delivers the website/docs update as a dedicated follow-up to the tool release PR.
Commit 1
Commit 2
Validation
Notes