Skip to content

Commit ef0eb39

Browse files
committed
fix: mobile responsiveness
1 parent 177358d commit ef0eb39

File tree

3 files changed

+101
-18
lines changed

3 files changed

+101
-18
lines changed

src/components/Navbar.tsx

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { clsx } from "clsx";
2+
import { Menu, X } from "lucide-react";
3+
import { useState } from "react";
24
import { Link, useLocation } from "react-router-dom";
35
import logoDark from "../assets/logo-dark.png";
46
import logoLight from "../assets/logo-light.png";
@@ -14,23 +16,26 @@ export function Navbar({ sticky = false, maxWidth = false }: NavbarProps) {
1416
const location = useLocation();
1517
const { resolvedTheme } = useTheme();
1618
const isDocsActive = location.pathname.startsWith("/docs");
19+
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
1720

1821
return (
1922
<header
2023
className={clsx(
21-
"border-b border-[var(--color-border)] bg-[var(--color-bg-primary)] px-6 py-3",
24+
"border-b border-[var(--color-border)] bg-[var(--color-bg-primary)] px-4 py-3 sm:px-6",
2225
sticky && "sticky top-0 z-50",
2326
)}
2427
>
2528
<div className={clsx("flex items-center justify-between", maxWidth && "mx-auto max-w-6xl")}>
26-
<Link to="/" className="flex items-center">
29+
<Link to="/" className="flex shrink-0 items-center">
2730
<img
2831
src={resolvedTheme === "dark" ? logoDark : logoLight}
2932
alt="ooxml.dev"
3033
className="h-6"
3134
/>
3235
</Link>
33-
<nav className="flex items-center gap-4">
36+
37+
{/* Desktop navigation */}
38+
<nav className="hidden items-center gap-4 sm:flex">
3439
<NavLink to="/docs" active={isDocsActive}>
3540
Reference
3641
</NavLink>
@@ -44,7 +49,37 @@ export function Navbar({ sticky = false, maxWidth = false }: NavbarProps) {
4449
</NavLink>
4550
<ThemeToggle />
4651
</nav>
52+
53+
{/* Mobile menu button */}
54+
<div className="flex items-center gap-2 sm:hidden">
55+
<ThemeToggle />
56+
<button
57+
type="button"
58+
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
59+
className="rounded-lg p-2 text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-tertiary)] hover:text-[var(--color-text-primary)]"
60+
aria-label="Toggle menu"
61+
>
62+
{mobileMenuOpen ? <X size={20} /> : <Menu size={20} />}
63+
</button>
64+
</div>
4765
</div>
66+
67+
{/* Mobile menu */}
68+
{mobileMenuOpen && (
69+
<nav className="mt-3 flex flex-col gap-1 border-t border-[var(--color-border)] pt-3 sm:hidden">
70+
<NavLink to="/docs" active={isDocsActive} onClick={() => setMobileMenuOpen(false)}>
71+
Reference
72+
</NavLink>
73+
<NavLink to="#" active={false} disabled>
74+
<span className="relative pr-6">
75+
Playground
76+
<span className="absolute -top-2 -right-1 rounded bg-[var(--color-accent)]/15 px-1 py-0.5 text-[8px] font-medium text-[var(--color-accent)]">
77+
soon
78+
</span>
79+
</span>
80+
</NavLink>
81+
</nav>
82+
)}
4883
</header>
4984
);
5085
}
@@ -54,11 +89,13 @@ function NavLink({
5489
active,
5590
disabled,
5691
children,
92+
onClick,
5793
}: {
5894
to: string;
5995
active: boolean;
6096
disabled?: boolean;
6197
children: React.ReactNode;
98+
onClick?: () => void;
6299
}) {
63100
const className = clsx(
64101
"rounded px-3 py-1.5 text-sm transition",
@@ -82,7 +119,7 @@ function NavLink({
82119
}
83120

84121
return (
85-
<Link to={to} className={className}>
122+
<Link to={to} className={className} onClick={onClick}>
86123
{children}
87124
</Link>
88125
);

src/pages/Home.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,22 @@ export function Home() {
88
<Navbar maxWidth />
99

1010
{/* Hero */}
11-
<main className="mx-auto max-w-4xl px-6 py-24 text-center">
11+
<main className="mx-auto max-w-4xl px-4 py-16 text-center sm:px-6 sm:py-24">
1212
<p className="mb-4 text-sm font-medium text-[var(--color-accent)]">ECMA-376 / ISO 29500</p>
13-
<h1 className="mb-6 text-5xl font-bold tracking-tight">ooxml.dev</h1>
14-
<p className="mb-8 text-xl text-[var(--color-text-secondary)]">
13+
<h1 className="mb-6 text-4xl font-bold tracking-tight sm:text-5xl">ooxml.dev</h1>
14+
<p className="mb-8 text-lg text-[var(--color-text-secondary)] sm:text-xl">
1515
The OOXML spec, explained by people who actually implemented it.
16-
<br />
16+
<br className="hidden sm:block" />
17+
<span className="sm:hidden"> </span>
1718
Interactive examples, real-world gotchas, and live previews.
1819
</p>
1920
<div className="flex justify-center gap-4">
2021
<Link
2122
to="/docs"
22-
className="rounded-lg bg-[var(--color-accent)] px-6 py-3 font-medium text-white transition hover:bg-[var(--color-accent-hover)]"
23+
className="rounded-lg bg-[var(--color-accent)] px-5 py-2.5 font-medium text-white transition hover:bg-[var(--color-accent-hover)] sm:px-6 sm:py-3"
2324
>
2425
Browse Reference
2526
</Link>
26-
{/* <a
27-
href="#"
28-
className="rounded-lg border border-[var(--color-border)] px-6 py-3 font-medium transition hover:bg-[var(--color-bg-secondary)]"
29-
>
30-
Open Playground
31-
</a> */}
3227
</div>
3328
</main>
3429

src/pages/docs/Layout.tsx

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,67 @@
11
import { clsx } from "clsx";
2+
import { ChevronDown } from "lucide-react";
3+
import { useState } from "react";
24
import { Link, Outlet, useLocation } from "react-router-dom";
35
import { Footer } from "../../components/Footer";
46
import { Navbar } from "../../components/Navbar";
57

8+
const NAV_ITEMS = [
9+
{ to: "/docs", label: "Introduction", path: "index" },
10+
{ to: "/docs/paragraphs", label: "Paragraphs", path: "paragraphs" },
11+
{ to: "/docs/tables", label: "Tables", path: "tables" },
12+
{ to: "/docs/styles", label: "Styles", path: "styles", disabled: true },
13+
{ to: "/docs/creating-documents", label: "Creating Documents", path: "creating-documents" },
14+
{ to: "/docs/common-gotchas", label: "Common Gotchas", path: "common-gotchas" },
15+
];
16+
617
export function DocsLayout() {
718
const location = useLocation();
819
const currentPath = location.pathname.replace("/docs/", "").replace("/docs", "index");
20+
const [navOpen, setNavOpen] = useState(false);
21+
22+
const currentPage = NAV_ITEMS.find((item) => item.path === currentPath);
923

1024
return (
1125
<div className="min-h-screen bg-[var(--color-bg-primary)]">
1226
<Navbar sticky />
1327

28+
{/* Mobile collapsible nav */}
29+
<div className="border-b border-[var(--color-border)] md:hidden">
30+
<button
31+
type="button"
32+
onClick={() => setNavOpen(!navOpen)}
33+
className="flex w-full items-center justify-between px-4 py-3"
34+
>
35+
<span className="text-sm font-medium">{currentPage?.label || "Navigation"}</span>
36+
<ChevronDown
37+
size={16}
38+
className={clsx(
39+
"text-[var(--color-text-muted)] transition-transform",
40+
navOpen && "rotate-180",
41+
)}
42+
/>
43+
</button>
44+
{navOpen && (
45+
<nav className="border-t border-[var(--color-border)] px-4 py-2">
46+
<ul className="space-y-1">
47+
{NAV_ITEMS.map((item) => (
48+
<SidebarLink
49+
key={item.to}
50+
to={item.to}
51+
label={item.label}
52+
active={currentPath === item.path}
53+
disabled={item.disabled}
54+
onClick={() => setNavOpen(false)}
55+
/>
56+
))}
57+
</ul>
58+
</nav>
59+
)}
60+
</div>
61+
1462
<div className="flex">
15-
{/* Sidebar */}
16-
<aside className="sticky top-[57px] h-[calc(100vh-57px)] w-64 overflow-y-auto border-r border-[var(--color-border)] bg-[var(--color-bg-secondary)] p-4">
63+
{/* Desktop Sidebar */}
64+
<aside className="sticky top-[57px] hidden h-[calc(100vh-57px)] w-64 overflow-y-auto border-r border-[var(--color-border)] bg-[var(--color-bg-secondary)] p-4 md:block">
1765
<nav className="space-y-6">
1866
{/* Getting Started */}
1967
<div>
@@ -68,7 +116,7 @@ export function DocsLayout() {
68116
</aside>
69117

70118
{/* Main content */}
71-
<main className="flex-1 px-8 py-8">
119+
<main className="min-w-0 flex-1 px-4 py-6 sm:px-8 sm:py-8">
72120
<div className="mx-auto max-w-3xl">
73121
<Outlet />
74122
</div>
@@ -85,11 +133,13 @@ function SidebarLink({
85133
label,
86134
active,
87135
disabled,
136+
onClick,
88137
}: {
89138
to: string;
90139
label: string;
91140
active: boolean;
92141
disabled?: boolean;
142+
onClick?: () => void;
93143
}) {
94144
if (disabled) {
95145
return (
@@ -110,6 +160,7 @@ function SidebarLink({
110160
<li>
111161
<Link
112162
to={to}
163+
onClick={onClick}
113164
className={clsx(
114165
"block rounded px-3 py-1.5 text-sm transition",
115166
active

0 commit comments

Comments
 (0)