Patterns de mise en page utilises dans Speedcube Master : containers, grilles de cartes, sidebar, navigation mobile et zones PWA.
Chaque page utilise un container max-w-6xl centre avec un padding horizontal responsive. Le padding vertical minimum est p-6.
// Structure de page standard
export default function MyPage() {
return (
<main className="max-w-6xl mx-auto px-4 sm:px-6 py-6 sm:py-10">
<div className="space-y-6">
{/* Header de page */}
<div>
<h1 className="text-2xl sm:text-3xl font-bold">
Titre de la page
</h1>
<p className="text-muted-foreground mt-1">
Description de la page
</p>
</div>
{/* Contenu */}
<div>
{/* ... */}
</div>
</div>
</main>
);
}Les cartes sont disposees dans une grille responsive qui passe de 1 a 2 puis 3 colonnes. L'espacement standard est gap-4 ou gap-6.
// Grille responsive de cartes
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
<Card>
<CardContent className="pt-6">
<p>Carte 1</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<p>Carte 2</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<p>Carte 3</p>
</CardContent>
</Card>
</div>
// Variante 2 colonnes pour les stats
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{stats.map((stat) => (
<Card key={stat.label}>
<CardContent className="pt-6 text-center">
<p className="text-2xl font-bold font-mono">{stat.value}</p>
<p className="text-xs text-muted-foreground">{stat.label}</p>
</CardContent>
</Card>
))}
</div>Le layout dashboard utilise une sidebar fixe a gauche sur desktop qui se transforme en navigation bottom sur mobile. Le contenu principal occupe l'espace restant avec un scroll independant.
// Layout avec sidebar (dashboard)
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex min-h-screen">
{/* Sidebar - cachee sur mobile */}
<aside className="hidden md:flex w-64 flex-col border-r border-border bg-card">
<div className="p-6">
<h2 className="text-lg font-semibold">Dashboard</h2>
</div>
<nav className="flex-1 px-4 space-y-1">
<SidebarLink href="/dashboard" icon={Home}>
Accueil
</SidebarLink>
<SidebarLink href="/dashboard/stats" icon={BarChart}>
Statistiques
</SidebarLink>
<SidebarLink href="/dashboard/settings" icon={Settings}>
Parametres
</SidebarLink>
</nav>
</aside>
{/* Contenu principal */}
<main className="flex-1 overflow-y-auto">
<div className="max-w-6xl mx-auto px-4 sm:px-6 py-6">
{children}
</div>
</main>
</div>
);
}Sur mobile, la navigation principale est une barre fixe en bas de l'ecran. Elle respecte les safe areas pour les appareils avec encoche ou barre de navigation gestuelle.
// Navigation bottom mobile
function MobileBottomNav() {
const pathname = usePathname();
const links = [
{ href: "/timer", icon: Timer, label: "Timer" },
{ href: "/algos", icon: BookOpen, label: "Algos" },
{ href: "/learning", icon: GraduationCap, label: "Apprendre" },
{ href: "/dashboard", icon: LayoutDashboard, label: "Dashboard" },
];
return (
<nav className="fixed bottom-0 left-0 right-0 z-50 md:hidden
border-t border-border bg-card/95 backdrop-blur-md
pb-[env(safe-area-inset-bottom)]"
>
<div className="flex items-center justify-around h-16">
{links.map((link) => {
const isActive = pathname.startsWith(link.href);
return (
<Link
key={link.href}
href={link.href}
className={cn(
"flex flex-col items-center gap-1 px-3 py-2 text-xs",
"transition-colors duration-150",
isActive
? "text-primary"
: "text-muted-foreground hover:text-foreground"
)}
>
<link.icon className="h-5 w-5" />
<span>{link.label}</span>
</Link>
);
})}
</div>
</nav>
);
}L'application est installee en PWA sur mobile. Les zones de contenu doivent respecter les encoches et barres systeme via les variables CSS env(safe-area-inset-*).
// Configuration du viewport dans le layout racine
<meta
name="viewport"
content="width=device-width, initial-scale=1, viewport-fit=cover"
/>
// Utilisation des safe areas dans le CSS
.main-content {
/* Padding qui respecte les encoches */
padding-top: env(safe-area-inset-top);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
.bottom-nav {
/* Padding bottom pour la barre de navigation gestuelle */
padding-bottom: env(safe-area-inset-bottom);
}
// Avec Tailwind (classes arbitraires)
<div className="pt-[env(safe-area-inset-top)]
pl-[env(safe-area-inset-left)]
pr-[env(safe-area-inset-right)]">
{/* Contenu */}
</div>
<nav className="pb-[env(safe-area-inset-bottom)]">
{/* Navigation */}
</nav>p-6Padding minimum des cartes et sectionsgap-4 / gap-6Espacement entre les cartes dans une grillespace-y-6Espacement vertical entre les sections d'une pagemax-w-6xlLargeur maximale du container principalpx-4 sm:px-6Padding horizontal responsive du container