fix(web): mobile-responsive hero, marketing site, docs and dashboard
All checks were successful
Deploy to Production / deploy (push) Successful in 1m13s
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:
parent
2e5bf5b44b
commit
88c7262a08
@ -1,15 +1,15 @@
|
||||
import Link from 'next/link';
|
||||
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 }) {
|
||||
return (
|
||||
<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">
|
||||
<div className="mx-auto flex h-12 max-w-7xl items-center justify-between px-6">
|
||||
<div className="flex items-center gap-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 min-w-0 items-center gap-2 sm:gap-6">
|
||||
<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} />}>
|
||||
Overview
|
||||
</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]"
|
||||
>
|
||||
{icon}
|
||||
{children}
|
||||
<span className="hidden sm:inline">{children}</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import Link from 'next/link';
|
||||
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 }) {
|
||||
return (
|
||||
<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">
|
||||
<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">
|
||||
<Logo />
|
||||
<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>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-1.5 sm:gap-2">
|
||||
<Link
|
||||
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
|
||||
</Link>
|
||||
@ -39,13 +40,17 @@ export default function MarketingLayout({ children }: { children: React.ReactNod
|
||||
>
|
||||
Start building
|
||||
</Link>
|
||||
<MarketingMobileMenu />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main className="flex-1">{children}</main>
|
||||
<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">
|
||||
<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>System status</span>
|
||||
</Link>
|
||||
|
||||
@ -85,12 +85,12 @@ export default function Landing() {
|
||||
<>
|
||||
{/* Hero */}
|
||||
<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>
|
||||
<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 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]">
|
||||
v0.1 — updated 2026-05-20
|
||||
</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.
|
||||
<br />
|
||||
We host the server.
|
||||
@ -131,7 +131,7 @@ export default function Landing() {
|
||||
</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="space-y-3">
|
||||
<CodeBlock label="prompt.txt" code={PROMPT_EXAMPLE} />
|
||||
@ -143,7 +143,7 @@ export default function Landing() {
|
||||
</section>
|
||||
|
||||
{/* 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="mb-12 max-w-2xl">
|
||||
<h2 className="text-[28px] font-semibold tracking-tight">How it works</h2>
|
||||
@ -182,7 +182,7 @@ export default function Landing() {
|
||||
</section>
|
||||
|
||||
{/* 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">
|
||||
<h2 className="text-center text-[13px] uppercase tracking-[0.18em] text-[--color-fg-subtle]">
|
||||
Works with the clients you already use
|
||||
@ -199,7 +199,7 @@ export default function Landing() {
|
||||
</section>
|
||||
|
||||
{/* 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="mb-10 max-w-2xl">
|
||||
<h2 className="text-[28px] font-semibold tracking-tight">
|
||||
@ -226,7 +226,7 @@ export default function Landing() {
|
||||
</section>
|
||||
|
||||
{/* 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="mb-10 flex flex-wrap items-end justify-between gap-4">
|
||||
<div className="max-w-2xl">
|
||||
@ -257,7 +257,7 @@ export default function Landing() {
|
||||
</section>
|
||||
|
||||
{/* 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="mb-10 max-w-2xl">
|
||||
<h2 className="text-[28px] font-semibold tracking-tight">Pricing</h2>
|
||||
@ -290,7 +290,7 @@ export default function Landing() {
|
||||
</section>
|
||||
|
||||
{/* FAQ */}
|
||||
<section className="py-20">
|
||||
<section className="py-14 sm:py-20">
|
||||
<JsonLd data={faqJsonLd()} />
|
||||
<div className="mx-auto max-w-6xl px-6">
|
||||
<h2 className="text-[28px] font-semibold tracking-tight">FAQ</h2>
|
||||
|
||||
@ -62,8 +62,8 @@ export default function DocsLayout({ children }: { children: React.ReactNode })
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<div className="mx-auto flex w-full max-w-6xl flex-1 gap-12 px-6 py-10">
|
||||
<aside className="w-[240px] shrink-0">
|
||||
<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="hidden w-[240px] shrink-0 lg:block">
|
||||
<nav className="sticky top-20 space-y-5">
|
||||
{SECTIONS.map((section) => (
|
||||
<div key={section.heading}>
|
||||
@ -86,7 +86,7 @@ export default function DocsLayout({ children }: { children: React.ReactNode })
|
||||
))}
|
||||
</nav>
|
||||
</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>
|
||||
);
|
||||
|
||||
@ -30,6 +30,10 @@
|
||||
background: var(--color-bg);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
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 {
|
||||
background: var(--color-bg);
|
||||
|
||||
47
apps/web/components/marketing-mobile-menu.tsx
Normal file
47
apps/web/components/marketing-mobile-menu.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user