Files
vercel__flags/apps/docs/components/custom/framework-switcher.tsx
christopherkindl 97c8060917 [docs] Sync docs with @vercel/geistdocs 1.2.0 + 1.2.1 (#364)
* Sync docs template with @vercel/geistdocs 1.2.0 + 1.2.1

Pulls in template updates via `@vercel/geistdocs@1.2.1 update --sync`:

- 1.2.0: new NavbarLogo (oss product switcher variant), geist theme
  tokens + icons, updated docs layout, MobileDocsBar, redesigned navbar
  and mobile menu
- 1.2.1: CommandPrompt UI component, IconCheck / IconCopy fallback
  icons, supporting CSS tokens and @utility rules (material-small,
  text-label-13, text-label-14-mono, text-label-16-mono,
  --ds-shadow-border-small light/dark)

Home page intentionally left untouched because flags-sdk uses a custom
home/[code] routing structure. package.json left untouched to preserve
workspace/catalog references and app-specific deps (flags,
@vercel/toolbar, newer Next).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Cap footer max-w and use LogoFlagsSdk in navbar

- Cap the footer inner row at max-w-[1448px] so it aligns with the
  navbar on very wide screens
- Render the Flags SDK wordmark (LogoFlagsSdk) directly in the navbar
  via the oss NavbarLogo trigger; drop the duplicate entry from the
  product switcher dropdown

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Use bg-background-200 for docs container

Matches the navbar background so docs pages (principles, frameworks,
providers, etc.) no longer sit on a lighter surface than the header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Use CommandPrompt humans/agents switcher in hero CTA

Replaces the Get Started button + CopySnippet in the home hero with
the CommandPrompt switcher. Triggers and surface are aligned to the
left via `items-start` on the root so they line up with the hero
heading instead of being centered.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Use full npm install command in hero CommandPrompt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Default hero CommandPrompt to the agents tab

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Bump navbar Flags SDK logo to height 22

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Use MobileDocsBar for docs mobile navigation

Disables the default Fumadocs TOC popover and renders the
MobileDocsBar (sidebar trigger + TOC sheet) above the docs title,
matching the geistdocs template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Override fumadocs primary at :root so prose links render gray-1000

Matches the workflow docs tweak — broadens the fd-primary override
from #nd-toc to :root so in-content links pick up the design-system
foreground color instead of the default blue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Use new --ds-shadow-menu on hero "Try the Flags SDK" card

- Update --ds-shadow-menu to the 4-layer geist menu shadow
- Apply it to the flags-toggle card via `shadow-(--ds-shadow-menu)`;
  drop the stock `shadow-md` and redundant border

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Use LogoFlagsSdk as the app Logo; add AI SDK to product switcher

- Replace the inline triangle SVG + "Flags SDK" text in geistdocs.tsx
  with `<LogoFlagsSdk height={22} />`, so navbar & template consumers
  get the real wordmark
- Revert navbar.tsx to import `Logo` from `@/geistdocs` (single source
  of truth) — the direct LogoFlagsSdk import is no longer needed
- Add `LogoAiSdk` to OSS_PRODUCT_LINKS at the top of the list
  (`ai-sdk.dev`)
- Give LogoAiSdk the same `color` prop pattern as the other SDK logos
- Formatter cleanup in app/[lang]/layout.tsx

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Restore FrameworkSwitcher and theme the Next.js icon

- Re-mount <FrameworkSwitcher /> at the top of the desktop sidebar and
  the mobile Sheet (lost during the 1.2.x template sync)
- Rewrite the Next.js icon to mirror LogoIconNextSvg from
  @vercel/geistcn-assets: circle uses currentColor + text-gray-1000 so
  it auto-inverts per theme; gradient stops for the "N" use
  var(--ds-background-100) so the highlight stays readable in light
  and dark

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Docs: active nav state, geist shadow, restored framework icon + TOC offset

- DesktopMenu: mark nav items active when pathname falls under the
  item's section (match on first three path segments, ignore locale
  prefix); add `data-[active]:text-gray-1000` to the selector
- CodeBlock: restore the `#next` / `#svelte` title-suffix parser so
  framework icons render in code block headers again
- FrameworkSwitcher: apply `--ds-shadow-menu` to the popover content
- geistdocs.css: push `#nd-toc` over with `md:pl-8`

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 19:18:06 +02:00

206 lines
7.5 KiB
TypeScript

'use client';
import { ChevronDownIcon } from 'lucide-react';
import { usePathname, useRouter } from 'next/navigation';
import { type ReactNode, useState } from 'react';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import { Button } from '../ui/button';
type Tab = {
title: string;
url: string;
icon: ReactNode;
description: string;
};
const tabs: Tab[] = [
{
title: 'Next.js',
url: '/docs/frameworks/next',
icon: (
<svg
className="text-gray-1000"
fill="none"
height="16"
strokeLinejoin="round"
viewBox="0 0 16 16"
width="16"
>
<circle
cx="8"
cy="8"
r="7.375"
fill="currentColor"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.25"
/>
<path
d="M10.63 11V5"
stroke="url(#logo-next__b)"
strokeMiterlimit="1.414"
strokeWidth="1.25"
/>
<path
clipRule="evenodd"
d="M6 5H4.75v6H6V6.97l6.36 7.74q.52-.35 1-.76z"
fill="url(#logo-next__c)"
fillRule="evenodd"
/>
<defs>
<linearGradient
gradientUnits="userSpaceOnUse"
id="logo-next__b"
x1="11.13"
x2="11.13"
y1="5"
y2="11"
>
<stop style={{ stopColor: 'var(--ds-background-100)' }} />
<stop
offset=".609"
stopOpacity=".57"
style={{ stopColor: 'var(--ds-background-100)' }}
/>
<stop
offset=".797"
stopOpacity="0"
style={{ stopColor: 'var(--ds-background-100)' }}
/>
<stop
offset="1"
stopOpacity="0"
style={{ stopColor: 'var(--ds-background-100)' }}
/>
</linearGradient>
<linearGradient
gradientUnits="userSpaceOnUse"
id="logo-next__c"
x1="9.702"
x2="12.791"
y1="10.474"
y2="14.414"
>
<stop style={{ stopColor: 'var(--ds-background-100)' }} />
<stop
offset=".604"
stopOpacity="0"
style={{ stopColor: 'var(--ds-background-100)' }}
/>
<stop
offset="1"
stopOpacity="0"
style={{ stopColor: 'var(--ds-background-100)' }}
/>
</linearGradient>
</defs>
</svg>
),
description: 'Flags SDK for Next.js',
},
{
title: 'SvelteKit',
url: '/docs/frameworks/sveltekit',
icon: (
<svg
className="grayscale"
height="16"
strokeLinejoin="round"
viewBox="0 0 16 16"
width="16"
style={{ color: 'currentcolor' }}
>
<path
d="M13.7974 2.11525C12.3195 -0.0135593 9.37709 -0.637288 7.26183 0.705085L3.53302 3.09153C2.51607 3.72881 1.81098 4.77288 1.60759 5.95254C1.43132 6.94237 1.58048 7.95932 2.05505 8.84068C1.72963 9.32881 1.51268 9.87119 1.41776 10.4407C1.20082 11.6475 1.48556 12.8949 2.19065 13.8847C3.68217 16.0136 6.61098 16.6373 8.72624 15.2949L12.4551 12.922C13.472 12.2847 14.1771 11.2407 14.3805 10.061C14.5567 9.07119 14.4076 8.05424 13.933 7.17288C14.2584 6.68475 14.4754 6.14237 14.5703 5.57288C14.8008 4.35254 14.5161 3.10508 13.7974 2.11525Z"
fill="#FF3E00"
/>
<path
d="M6.8958 14.0881C5.68902 14.4 4.428 13.9254 3.72292 12.9085C3.28902 12.3119 3.12631 11.5661 3.24834 10.8339C3.27546 10.7119 3.30258 10.6034 3.3297 10.4814L3.3975 10.2644L3.58733 10.4C4.03478 10.7254 4.52292 10.9695 5.05173 11.1322L5.18733 11.1729L5.17377 11.3085C5.16021 11.4983 5.21445 11.7017 5.32292 11.8644C5.53987 12.1763 5.91953 12.3254 6.28563 12.2305C6.36699 12.2034 6.44834 12.1763 6.51614 12.1356L10.2314 9.76271C10.4212 9.64068 10.5433 9.46441 10.5839 9.24746C10.6246 9.03051 10.5704 8.8 10.4483 8.62373C10.2314 8.31186 9.85173 8.17627 9.48563 8.27119C9.40428 8.29831 9.32292 8.32542 9.25512 8.3661L7.83139 9.27458C7.60089 9.42373 7.34326 9.5322 7.07207 9.6C5.86529 9.91187 4.60428 9.43729 3.89919 8.42034C3.47885 7.82373 3.30258 7.07797 3.43817 6.34576C3.56021 5.64068 3.99411 5.00339 4.60428 4.62373L8.33309 2.25085C8.5636 2.1017 8.82123 1.99322 9.09241 1.91186C10.2992 1.6 11.5602 2.07458 12.2653 3.09153C12.6992 3.68814 12.8619 4.4339 12.7399 5.1661C12.7128 5.28814 12.6856 5.39661 12.645 5.51864L12.5772 5.73559L12.3873 5.6C11.9399 5.27458 11.4517 5.03051 10.9229 4.8678L10.7873 4.82712L10.8009 4.69153C10.8144 4.50169 10.7602 4.29831 10.6517 4.13559C10.4348 3.82373 10.0551 3.68814 9.68902 3.78305C9.60767 3.81017 9.52631 3.83729 9.45851 3.87797L5.74326 6.25085C5.55343 6.37288 5.43139 6.54915 5.39072 6.7661C5.35004 6.98305 5.40428 7.21356 5.52631 7.38983C5.74326 7.7017 6.12292 7.83729 6.48902 7.74237C6.57038 7.71525 6.65173 7.68814 6.71953 7.64746L8.14326 6.73898C8.37377 6.58983 8.63139 6.48136 8.90258 6.4C10.1094 6.08814 11.3704 6.56271 12.0755 7.57966C12.5094 8.17627 12.6721 8.92203 12.55 9.65424C12.428 10.3593 11.9941 10.9966 11.3839 11.3763L7.65512 13.7492C7.42461 13.8983 7.16699 14.0068 6.8958 14.0881Z"
fill="white"
/>
<defs>
<clipPath id="clip0_53_108_svelte">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>
),
description: 'Flags SDK for SvelteKit',
},
];
export const FrameworkSwitcher = () => {
const pathname = usePathname();
const router = useRouter();
const [open, setOpen] = useState(false);
if (tabs.length < 2 || !pathname.startsWith('/docs/frameworks/')) {
return null;
}
const activeTab = tabs.find((tab) => pathname.startsWith(tab.url));
if (!activeTab) {
return null;
}
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
type="button"
variant="ghost"
className="h-auto text-left shadow-none mb-4 gap-4"
>
{activeTab.icon ? (
<div className="size-4">{activeTab.icon}</div>
) : null}
<div className="min-w-0 flex-1">
<div className="truncate font-medium text-sm">
{activeTab.title}
</div>
{activeTab.description ? (
<div className="truncate text-muted-foreground text-xs">
{activeTab.description}
</div>
) : null}
</div>
<ChevronDownIcon className="size-4 shrink-0 text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent
align="start"
className="w-(--radix-popover-trigger-width) p-1"
>
{tabs.map((tab) => (
<Button
key={tab.url}
type="button"
className="h-auto text-left shadow-none gap-4 w-full justify-start"
variant="ghost"
onClick={() => {
router.push(tab.url);
setOpen(false);
}}
>
{tab.icon ? <div className="size-4">{tab.icon}</div> : null}
<div className="min-w-0">
<div className="truncate font-medium text-sm">{tab.title}</div>
{tab.description ? (
<div className="truncate text-muted-foreground text-xs">
{tab.description}
</div>
) : null}
</div>
</Button>
))}
</PopoverContent>
</Popover>
);
};