DS

Layout

Patterns de mise en page utilises dans Speedcube Master : containers, grilles de cartes, sidebar, navigation mobile et zones PWA.

Structure de page

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>
  );
}

Grille de cartes

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>

Sidebar + Contenu principal

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>
  );
}

Navigation mobile (bottom nav)

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>
  );
}

Safe areas PWA

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>

Regles d'espacement

p-6Padding minimum des cartes et sections
gap-4 / gap-6Espacement entre les cartes dans une grille
space-y-6Espacement vertical entre les sections d'une page
max-w-6xlLargeur maximale du container principal
px-4 sm:px-6Padding horizontal responsive du container