Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions apps/web/src/components/mobile-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client'

import { type ReactNode, useEffect, useRef } from 'react'

export function MobileMenu({ children }: { readonly children: ReactNode }) {
const details = useRef<HTMLDetailsElement>(null)

useEffect(() => {
function onKey(event: KeyboardEvent) {
if (event.key === 'Escape' && details.current?.open) {
details.current.open = false
details.current.querySelector('summary')?.focus()
}
}
document.addEventListener('keydown', onKey)
return () => document.removeEventListener('keydown', onKey)
}, [])

return (
<details className="mobile-menu md:hidden" ref={details}>
<summary aria-label="Menu" className="mobile-menu-button">
<span aria-hidden className="mobile-menu-bars">
<span />
<span />
<span />
</span>
</summary>
<div
className="mobile-menu-panel"
onClickCapture={(event) => {
if (event.target instanceof Element && event.target.closest('a') && details.current) {
details.current.open = false
}
}}
>
{children}
</div>
</details>
)
}
61 changes: 56 additions & 5 deletions apps/web/src/components/site-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import { quickstartHref } from '../docs/registry'
import { DemoLink } from './demo-link'
import { DocsSearch } from './docs-search'
import { Logomark } from './logomark'
import { MobileMenu } from './mobile-menu'
import { ThemeToggle } from './theme-toggle'

const item = 'text-micro font-medium text-fg-muted transition-colors hover:text-fg'

export function SiteHeader() {
const startHref = quickstartHref()

return (
<header className="sticky top-0 z-40 border-b border-border bg-canvas/85 backdrop-blur-lg">
<div aria-hidden className="top-beam" />
Expand All @@ -26,7 +29,7 @@ export function SiteHeader() {
Product
</Link>

<div className="menu hidden sm:block">
<div className="menu hidden md:block">
<Link href={audienceIndexHref} className={`${item} inline-flex items-center gap-1`}>
Who it&rsquo;s for
<span aria-hidden className="menu-caret">
Expand All @@ -49,24 +52,72 @@ export function SiteHeader() {
</ul>
</div>

<Link href="/docs" className={item}>
<Link href="/docs" className={`hidden ${item} md:inline`}>
Docs
</Link>
<DemoLink className={item}>Demo</DemoLink>
<DemoLink className={`hidden ${item} md:inline`}>Demo</DemoLink>
<Link href={about.href} className={`hidden ${item} lg:inline`}>
About
</Link>
<a href={site.repository} className={`hidden ${item} sm:inline`}>
<a href={site.repository} className={`hidden ${item} md:inline`}>
GitHub
</a>
<DocsSearch />
<ThemeToggle />
<Link
href={quickstartHref()}
href={startHref}
className="hidden rounded-[var(--radius-control)] bg-accent px-3 py-1.5 text-micro font-medium text-accent-contrast transition-colors hover:bg-accent-hover lg:inline-block"
>
Get started
</Link>

<MobileMenu>
<ul className="mobile-menu-list">
<li>
<Link className="mobile-menu-link" href="/">
Product
</Link>
</li>
<li>
<Link className="mobile-menu-link" href={audienceIndexHref}>
Who it&rsquo;s for
</Link>
<ul className="mobile-menu-sublist">
{audiences.map((audience) => (
<li key={audience.slug}>
<Link className="mobile-menu-link" href={audienceHref(audience.slug)}>
{audience.name}
</Link>
</li>
))}
</ul>
</li>
<li>
<Link className="mobile-menu-link" href="/docs">
Docs
</Link>
</li>
<li>
<DemoLink className="mobile-menu-link">Demo</DemoLink>
</li>
<li>
<Link className="mobile-menu-link" href={about.href}>
About
</Link>
</li>
<li>
<a className="mobile-menu-link" href={site.repository}>
GitHub
</a>
</li>
</ul>
<Link className="btn btn-primary mt-4 w-full" href={startHref}>
Get started
<span aria-hidden className="btn-arrow">
</span>
</Link>
</MobileMenu>
</nav>
</div>
</header>
Expand Down
100 changes: 100 additions & 0 deletions apps/web/src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,106 @@ body {
font-size: 0.72rem;
}

.mobile-menu-button {
display: grid;
place-items: center;
width: 2.25rem;
height: 2.25rem;
border: 1px solid var(--border);
border-radius: var(--radius-control);
background: var(--raised);
color: var(--fg);
cursor: pointer;
list-style: none;
transition:
border-color 0.15s ease,
background-color 0.15s ease;
}

.mobile-menu-button::-webkit-details-marker {
display: none;
}

.mobile-menu-button:hover {
border-color: var(--border-strong);
background: var(--surface);
}

.mobile-menu-bars {
display: grid;
gap: 0.28rem;
width: 1rem;
}

.mobile-menu-bars > span {
display: block;
height: 1.5px;
border-radius: 999px;
background: currentColor;
transition:
transform 0.15s ease,
opacity 0.15s ease;
}

.mobile-menu[open] .mobile-menu-bars > span:nth-child(1) {
transform: translateY(0.37rem) rotate(45deg);
}

.mobile-menu[open] .mobile-menu-bars > span:nth-child(2) {
opacity: 0;
}

.mobile-menu[open] .mobile-menu-bars > span:nth-child(3) {
transform: translateY(-0.37rem) rotate(-45deg);
}

.mobile-menu-panel {
position: absolute;
inset: 100% 0 auto;
max-height: calc(100dvh - 4rem);
overflow-y: auto;
padding: 0.75rem 1.25rem 1.25rem;
border-bottom: 1px solid var(--border);
background: var(--canvas);
box-shadow: var(--lift-lg);
}

.mobile-menu-list {
display: flex;
flex-direction: column;
}

.mobile-menu-list > li + li {
border-top: 1px solid var(--border);
}

.mobile-menu-link {
display: block;
padding: 0.8rem 0;
font-size: var(--step-0);
font-weight: 500;
color: var(--fg);
}

.mobile-menu-sublist {
display: grid;
gap: 0;
margin: -0.25rem 0 0.5rem;
padding-left: 1rem;
border-left: 2px solid var(--border);
}

.mobile-menu-sublist .mobile-menu-link {
padding: 0.45rem 0;
font-size: var(--step--1);
font-weight: 400;
color: var(--fg-muted);
}

.mobile-menu-sublist .mobile-menu-link:hover {
color: var(--fg);
}

.kbd {
border: 1px solid var(--border);
border-radius: 4px;
Expand Down