fix: full Apple HIG mobile + SEO audit - all issues resolved
MOBILE RESPONSIVENESS (Apple HIG): - HeroSection: typography scaled 7xl→4xl sm:5xl md:6xl lg:7xl, responsive px/py, rounded-2xl, active:scale-95 - Cart quantity buttons: h-8→h-11 w-8→w-11 (44px touch target), rounded-xl, active:scale-95 - StorefrontFooter newsletter: responsive w-full sm:w-52 lg:w-64, aria-label, larger touch targets - StorefrontHeader mobile nav: padding px-6→px-4 py-6→py-5 - AdminTable: overflow-x-auto + min-w-[600px] for horizontal scroll - AdminOrdersPanel: same table overflow fix - AdminLayout: mobile px-4 sm:px-6 py-6 sm:py-10 - AdminFilterTabs: responsive text/text sizes - AdminSidebar hamburger: h-10→h-11 w-10→w-11 (44px touch target) - DashboardClient: grid gap-3 sm:gap-4, responsive stat text - OrderEditForm: grid-cols-1 sm:grid-cols-2 (was 2, breaks on mobile) - BillingClient: min-h-[44px] on select/button - ProductsClient: h-32 sm:h-40 responsive image height - StopCard: line-clamp-2 instead of line-clamp-1 on location - CommunicationsPage: tabs wrapped in overflow-x-auto - Checkout page: grid breakpoint md not lg - Tuxedo page: SectionHeader mobile-first, feature grid grid-cols-1 sm:2, label visible - Tuxedo stats: text-3xl sm:text-4xl SEO METADATA: - Root layout: viewport export, full OG/Twitter, metadataBase, keywords, robots - Tuxedo layout: complete OG + Twitter + canonical + keywords - Indian River layout: complete OG + Twitter + canonical + keywords - Tuxedo/IRD FAQ pages: new layout.tsx with full metadata + FAQPage JSON-LD schema - Tuxedo/IRD Contact pages: new layout.tsx with full metadata - Pricing page: expanded metadata with OG/Twitter - Contact page: refactored to layout+ClientPage structure - Sitemap: updated with dynamic stop URLs, async function SCHEMA + STRUCTURED DATA: - FAQPage JSON-LD on FAQ pages - BreadcrumbList JSON-LD on storefront layouts - BreadcrumbNav component created (Apple HIG compliant) BUG FIXES: - Indian River: replaced raw <img> with Next.js Image component - Indian River: verified single H1 (others are h2) - Stop card: location line-clamp-2 for better readability TYPE CHECK: all pass
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
|
||||
export type BreadcrumbItem = {
|
||||
label: string;
|
||||
href?: string;
|
||||
};
|
||||
|
||||
export type BreadcrumbNavProps = {
|
||||
/** Array of breadcrumb items. Last item is the current page (no href). */
|
||||
items: BreadcrumbItem[];
|
||||
/** Optional brand accent for styling: 'green' (Tuxedo) or 'blue' (Indian River) */
|
||||
brandAccent?: "green" | "blue";
|
||||
/** Optional additional CSS classes */
|
||||
className?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Breadcrumb navigation component following Apple HIG.
|
||||
* Uses semantic <nav> with aria-label and ordered list for accessibility.
|
||||
*/
|
||||
export default function BreadcrumbNav({
|
||||
items,
|
||||
brandAccent = "green",
|
||||
className = "",
|
||||
}: BreadcrumbNavProps) {
|
||||
if (!items || items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const accentColor = brandAccent === "blue"
|
||||
? "text-blue-600 hover:text-blue-700"
|
||||
: "text-emerald-600 hover:text-emerald-700";
|
||||
|
||||
const separatorColor = "text-stone-400";
|
||||
|
||||
return (
|
||||
<nav
|
||||
aria-label="Breadcrumb"
|
||||
className={`w-full ${className}`}
|
||||
>
|
||||
<ol className="flex items-center flex-wrap gap-2 text-sm">
|
||||
{items.map((item, index) => {
|
||||
const isLast = index === items.length - 1;
|
||||
|
||||
return (
|
||||
<li
|
||||
key={index}
|
||||
className="flex items-center"
|
||||
>
|
||||
{index > 0 && (
|
||||
<span
|
||||
className={`mx-2 ${separatorColor}`}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
className="h-3.5 w-3.5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M8.25 4.5l7.5 7.5-7.5 7.5"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{isLast ? (
|
||||
<span
|
||||
className="font-medium text-stone-700"
|
||||
aria-current="page"
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
) : item.href ? (
|
||||
<Link
|
||||
href={item.href}
|
||||
className={`font-medium transition-colors ${accentColor}`}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
) : (
|
||||
<span className={`font-medium ${accentColor}`}>
|
||||
{item.label}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export default function StopCard({
|
||||
<h3 className="text-2xl font-black text-stone-950 leading-tight tracking-tight">
|
||||
{city}, {state}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-stone-500 leading-relaxed line-clamp-1">{location}</p>
|
||||
<p className="mt-1 text-xs sm:text-sm text-stone-500 leading-relaxed line-clamp-2">{location}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ export default function StorefrontHeader({
|
||||
|
||||
{/* Mobile nav */}
|
||||
{menuOpen && (
|
||||
<div className="border-t border-stone-100 bg-white px-6 py-6 md:hidden">
|
||||
<div className="border-t border-stone-100 bg-white px-4 py-5 md:hidden">
|
||||
<nav className="flex flex-col gap-1 text-sm font-medium text-stone-600">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
|
||||
Reference in New Issue
Block a user