fix(web): mobile-responsive hero, marketing site, docs and dashboard
All checks were successful
Deploy to Production / deploy (push) Successful in 1m13s

- Hero h1 was a fixed text-[44px] — overflowed narrow phones. Now
  text-[30px] sm:text-[40px] md:text-[56px].
- Hero grid children get min-w-0 so the code blocks' overflow-x-auto
  actually constrains instead of widening the page.
- Marketing nav: the inline links were hidden below md with no fallback.
  Added a hamburger MobileMenu; "Sign in" collapses into it on the
  smallest screens.
- Section vertical padding is now responsive (py-14 sm:py-20).
- globals.css: overflow-x: clip on <html> as a safety net.
- docs: the 240px sidebar is hidden below lg, article gets min-w-0.
- dashboard header: nav labels collapse to icons on small screens.

Verified: next build passes (40/40 pages).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marco Sadjadi 2026-05-21 23:25:26 +02:00
parent 2e5bf5b44b
commit 88c7262a08
6 changed files with 80 additions and 24 deletions

View File

@ -1,15 +1,15 @@
import Link from 'next/link';
import { Logo } from '@/components/logo'; import { Logo } from '@/components/logo';
import { LayoutGrid, Server, Settings, FileClock, Package } from 'lucide-react'; import { FileClock, LayoutGrid, Package, Server, Settings } from 'lucide-react';
import Link from 'next/link';
export default function DashboardLayout({ children }: { children: React.ReactNode }) { export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return ( return (
<div className="flex min-h-screen flex-col"> <div className="flex min-h-screen flex-col">
<header className="sticky top-0 z-50 border-b border-[--color-border] bg-[--color-bg]/85 backdrop-blur-md"> <header className="sticky top-0 z-50 border-b border-[--color-border] bg-[--color-bg]/85 backdrop-blur-md">
<div className="mx-auto flex h-12 max-w-7xl items-center justify-between px-6"> <div className="mx-auto flex h-12 max-w-7xl items-center justify-between gap-2 px-4 sm:px-6">
<div className="flex items-center gap-6"> <div className="flex min-w-0 items-center gap-2 sm:gap-6">
<Logo /> <Logo />
<nav className="flex items-center gap-1"> <nav className="flex items-center gap-0.5 sm:gap-1">
<NavLink href="/dashboard" icon={<LayoutGrid size={13} />}> <NavLink href="/dashboard" icon={<LayoutGrid size={13} />}>
Overview Overview
</NavLink> </NavLink>
@ -55,7 +55,7 @@ function NavLink({
className="inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-[12.5px] text-[--color-fg-muted] transition-colors hover:bg-[--color-bg-subtle] hover:text-[--color-fg]" className="inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-[12.5px] text-[--color-fg-muted] transition-colors hover:bg-[--color-bg-subtle] hover:text-[--color-fg]"
> >
{icon} {icon}
{children} <span className="hidden sm:inline">{children}</span>
</Link> </Link>
); );
} }

View File

@ -1,11 +1,12 @@
import Link from 'next/link';
import { Logo } from '@/components/logo'; import { Logo } from '@/components/logo';
import { MarketingMobileMenu } from '@/components/marketing-mobile-menu';
import Link from 'next/link';
export default function MarketingLayout({ children }: { children: React.ReactNode }) { export default function MarketingLayout({ children }: { children: React.ReactNode }) {
return ( return (
<div className="flex min-h-screen flex-col"> <div className="flex min-h-screen flex-col">
<header className="sticky top-0 z-50 border-b border-[--color-border] bg-[--color-bg]/80 backdrop-blur-md"> <header className="sticky top-0 z-50 border-b border-[--color-border] bg-[--color-bg]/80 backdrop-blur-md">
<div className="mx-auto flex h-12 max-w-6xl items-center justify-between px-6"> <div className="mx-auto flex h-12 max-w-6xl items-center justify-between px-5 sm:px-6">
<div className="flex items-center gap-6"> <div className="flex items-center gap-6">
<Logo /> <Logo />
<nav className="hidden items-center gap-5 text-[13px] text-[--color-fg-muted] md:flex"> <nav className="hidden items-center gap-5 text-[13px] text-[--color-fg-muted] md:flex">
@ -26,10 +27,10 @@ export default function MarketingLayout({ children }: { children: React.ReactNod
</Link> </Link>
</nav> </nav>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-1.5 sm:gap-2">
<Link <Link
href="/login" href="/login"
className="rounded-md px-3 py-1.5 text-[13px] text-[--color-fg-muted] transition-colors hover:text-[--color-fg]" className="hidden rounded-md px-3 py-1.5 text-[13px] text-[--color-fg-muted] transition-colors hover:text-[--color-fg] sm:block"
> >
Sign in Sign in
</Link> </Link>
@ -39,13 +40,17 @@ export default function MarketingLayout({ children }: { children: React.ReactNod
> >
Start building Start building
</Link> </Link>
<MarketingMobileMenu />
</div> </div>
</div> </div>
</header> </header>
<main className="flex-1">{children}</main> <main className="flex-1">{children}</main>
<footer className="border-t border-[--color-border] py-8"> <footer className="border-t border-[--color-border] py-8">
<div className="mx-auto flex max-w-6xl flex-col gap-4 px-6 text-[12px] text-[--color-fg-subtle] md:flex-row md:items-center md:justify-between"> <div className="mx-auto flex max-w-6xl flex-col gap-4 px-6 text-[12px] text-[--color-fg-subtle] md:flex-row md:items-center md:justify-between">
<Link href="/status" className="flex items-center gap-2 transition-colors hover:text-[--color-fg]"> <Link
href="/status"
className="flex items-center gap-2 transition-colors hover:text-[--color-fg]"
>
<span className="size-1.5 animate-pulse rounded-full bg-emerald-400" /> <span className="size-1.5 animate-pulse rounded-full bg-emerald-400" />
<span>System status</span> <span>System status</span>
</Link> </Link>

View File

@ -85,12 +85,12 @@ export default function Landing() {
<> <>
{/* Hero */} {/* Hero */}
<section className="relative border-b border-[--color-border]"> <section className="relative border-b border-[--color-border]">
<div className="mx-auto grid max-w-6xl gap-12 px-6 py-20 md:grid-cols-[1.05fr_1fr] md:items-center md:py-28"> <div className="mx-auto grid max-w-6xl gap-10 px-6 py-14 sm:py-20 md:grid-cols-[1.05fr_1fr] md:items-center md:gap-12 md:py-28">
<div> <div className="min-w-0">
<span className="mono inline-block rounded-full border border-[--color-border] bg-[--color-bg-elevated] px-2.5 py-0.5 text-[11px] tracking-wide text-[--color-fg-muted]"> <span className="mono inline-block rounded-full border border-[--color-border] bg-[--color-bg-elevated] px-2.5 py-0.5 text-[11px] tracking-wide text-[--color-fg-muted]">
v0.1 updated 2026-05-20 v0.1 updated 2026-05-20
</span> </span>
<h1 className="mt-6 text-balance text-[44px] font-semibold leading-[1.05] tracking-tight md:text-[56px]"> <h1 className="mt-6 text-balance text-[30px] font-semibold leading-[1.06] tracking-tight sm:text-[40px] md:text-[56px]">
Describe your tool. Describe your tool.
<br /> <br />
We host the server. We host the server.
@ -131,7 +131,7 @@ export default function Landing() {
</div> </div>
</div> </div>
<div className="relative"> <div className="relative min-w-0">
<div className="absolute -inset-px rounded-lg border border-[--color-border-strong]" /> <div className="absolute -inset-px rounded-lg border border-[--color-border-strong]" />
<div className="space-y-3"> <div className="space-y-3">
<CodeBlock label="prompt.txt" code={PROMPT_EXAMPLE} /> <CodeBlock label="prompt.txt" code={PROMPT_EXAMPLE} />
@ -143,7 +143,7 @@ export default function Landing() {
</section> </section>
{/* How it works */} {/* How it works */}
<section id="how" className="border-b border-[--color-border] py-20"> <section id="how" className="border-b border-[--color-border] py-14 sm:py-20">
<div className="mx-auto max-w-6xl px-6"> <div className="mx-auto max-w-6xl px-6">
<div className="mb-12 max-w-2xl"> <div className="mb-12 max-w-2xl">
<h2 className="text-[28px] font-semibold tracking-tight">How it works</h2> <h2 className="text-[28px] font-semibold tracking-tight">How it works</h2>
@ -182,7 +182,7 @@ export default function Landing() {
</section> </section>
{/* Works with */} {/* Works with */}
<section className="border-b border-[--color-border] py-16"> <section className="border-b border-[--color-border] py-12 sm:py-16">
<div className="mx-auto max-w-6xl px-6"> <div className="mx-auto max-w-6xl px-6">
<h2 className="text-center text-[13px] uppercase tracking-[0.18em] text-[--color-fg-subtle]"> <h2 className="text-center text-[13px] uppercase tracking-[0.18em] text-[--color-fg-subtle]">
Works with the clients you already use Works with the clients you already use
@ -199,7 +199,7 @@ export default function Landing() {
</section> </section>
{/* Examples */} {/* Examples */}
<section className="border-b border-[--color-border] py-20"> <section className="border-b border-[--color-border] py-14 sm:py-20">
<div className="mx-auto max-w-6xl px-6"> <div className="mx-auto max-w-6xl px-6">
<div className="mb-10 max-w-2xl"> <div className="mb-10 max-w-2xl">
<h2 className="text-[28px] font-semibold tracking-tight"> <h2 className="text-[28px] font-semibold tracking-tight">
@ -226,7 +226,7 @@ export default function Landing() {
</section> </section>
{/* Marketplace */} {/* Marketplace */}
<section className="border-b border-[--color-border] py-20"> <section className="border-b border-[--color-border] py-14 sm:py-20">
<div className="mx-auto max-w-6xl px-6"> <div className="mx-auto max-w-6xl px-6">
<div className="mb-10 flex flex-wrap items-end justify-between gap-4"> <div className="mb-10 flex flex-wrap items-end justify-between gap-4">
<div className="max-w-2xl"> <div className="max-w-2xl">
@ -257,7 +257,7 @@ export default function Landing() {
</section> </section>
{/* Pricing */} {/* Pricing */}
<section id="pricing" className="border-b border-[--color-border] py-20"> <section id="pricing" className="border-b border-[--color-border] py-14 sm:py-20">
<div className="mx-auto max-w-6xl px-6"> <div className="mx-auto max-w-6xl px-6">
<div className="mb-10 max-w-2xl"> <div className="mb-10 max-w-2xl">
<h2 className="text-[28px] font-semibold tracking-tight">Pricing</h2> <h2 className="text-[28px] font-semibold tracking-tight">Pricing</h2>
@ -290,7 +290,7 @@ export default function Landing() {
</section> </section>
{/* FAQ */} {/* FAQ */}
<section className="py-20"> <section className="py-14 sm:py-20">
<JsonLd data={faqJsonLd()} /> <JsonLd data={faqJsonLd()} />
<div className="mx-auto max-w-6xl px-6"> <div className="mx-auto max-w-6xl px-6">
<h2 className="text-[28px] font-semibold tracking-tight">FAQ</h2> <h2 className="text-[28px] font-semibold tracking-tight">FAQ</h2>

View File

@ -62,8 +62,8 @@ export default function DocsLayout({ children }: { children: React.ReactNode })
</nav> </nav>
</div> </div>
</header> </header>
<div className="mx-auto flex w-full max-w-6xl flex-1 gap-12 px-6 py-10"> <div className="mx-auto flex w-full max-w-6xl flex-1 gap-8 px-5 py-8 sm:px-6 sm:py-10 lg:gap-12">
<aside className="w-[240px] shrink-0"> <aside className="hidden w-[240px] shrink-0 lg:block">
<nav className="sticky top-20 space-y-5"> <nav className="sticky top-20 space-y-5">
{SECTIONS.map((section) => ( {SECTIONS.map((section) => (
<div key={section.heading}> <div key={section.heading}>
@ -86,7 +86,7 @@ export default function DocsLayout({ children }: { children: React.ReactNode })
))} ))}
</nav> </nav>
</aside> </aside>
<article className="prose prose-invert max-w-2xl flex-1">{children}</article> <article className="prose prose-invert min-w-0 max-w-2xl flex-1">{children}</article>
</div> </div>
</div> </div>
); );

View File

@ -30,6 +30,10 @@
background: var(--color-bg); background: var(--color-bg);
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
/* Safety net against a stray wide element causing horizontal scroll on
mobile. `clip` (not `hidden`) does not create a scroll container, so it
leaves position: sticky intact. */
overflow-x: clip;
} }
body { body {
background: var(--color-bg); background: var(--color-bg);

View File

@ -0,0 +1,47 @@
'use client';
import { Menu, X } from 'lucide-react';
import Link from 'next/link';
import { useState } from 'react';
const LINKS = [
{ href: '/#how', label: 'How it works' },
{ href: '/templates', label: 'Templates' },
{ href: '/pricing', label: 'Pricing' },
{ href: '/docs', label: 'Docs' },
{ href: '/changelog', label: 'Changelog' },
];
/** Hamburger menu shown below the md breakpoint, where the inline nav is hidden. */
export function MarketingMobileMenu() {
const [open, setOpen] = useState(false);
return (
<div className="md:hidden">
<button
type="button"
aria-label={open ? 'Close menu' : 'Open menu'}
aria-expanded={open}
onClick={() => setOpen((v) => !v)}
className="flex size-8 items-center justify-center rounded-md text-[--color-fg-muted] transition-colors hover:text-[--color-fg]"
>
{open ? <X size={18} /> : <Menu size={18} />}
</button>
{open && (
<div className="absolute inset-x-0 top-12 border-b border-[--color-border] bg-[--color-bg]/95 backdrop-blur-md">
<nav className="mx-auto flex max-w-6xl flex-col px-6">
{LINKS.map((l) => (
<Link
key={l.href}
href={l.href}
onClick={() => setOpen(false)}
className="border-b border-[--color-border] py-3.5 text-[14px] text-[--color-fg-muted] transition-colors last:border-0 hover:text-[--color-fg]"
>
{l.label}
</Link>
))}
</nav>
</div>
)}
</div>
);
}