Animation et micro-interactions

Basculement d’onglets

Composant d’onglets avec navigation par touches fléchées et animation de changement de contenu.

HTMLCSSJavaScriptTabs
Après activation de JavaScript, ce cas peut être modifié, exécuté et enregistré. Code source fixe ci-dessous :
<!doctype html>
<html lang="fr" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Basculement d’onglets</title>
  <style>
    * { box-sizing: border-box; }
    body {
      min-height: 100vh;
      margin: 0;
      display: grid;
      place-items: center;
      padding: 22px;
      color: #eff4ff;
      font-family: "Futura", "Microsoft YaHei", sans-serif;
      background: #101119;
    }
    .window { width: min(820px, 100%); overflow: hidden; border: 1px solid #343747; border-radius: 22px; background: #181a24; box-shadow: 0 30px 80px rgba(0,0,0,.42); }
    .titlebar { display: flex; align-items: center; gap: 8px; padding: 16px 20px; border-bottom: 1px solid #343747; }
    .dot { width: 11px; height: 11px; border-radius: 50%; background: #ff6b67; }
    .dot:nth-child(2) { background: #f7c95c; }
    .dot:nth-child(3) { background: #61d88c; }
    .titlebar strong { margin-left: auto; color: #7d829a; font-size: 11px; letter-spacing: .16em; }
    .layout { display: grid; grid-template-columns: 210px 1fr; min-height: 440px; }
    [role="tablist"] { padding: 26px 14px; border-right: 1px solid #343747; background: #141620; }
    [role="tab"] { width: 100%; display: flex; gap: 12px; align-items: center; padding: 13px 14px; border: 0; border-radius: 12px; color: #9197ad; text-align: left; font: inherit; font-weight: 700; background: transparent; cursor: pointer; }
    [role="tab"] + [role="tab"] { margin-top: 8px; }
    [role="tab"][aria-selected="true"] { color: #fff; background: #292d3e; box-shadow: inset 3px 0 #67e8b8; }
    [role="tab"]:focus-visible { outline: 2px solid #67e8b8; outline-offset: 2px; }
    .tab-icon { display: grid; width: 30px; height: 30px; place-items: center; border: 1px solid #41465c; border-radius: 9px; }
    .content { padding: clamp(28px, 6vw, 54px); }
    [role="tabpanel"] { animation: reveal .32s ease both; }
    [role="tabpanel"][hidden] { display: none; }
    .label { color: #67e8b8; font-size: 12px; font-weight: 800; letter-spacing: .16em; }
    h1 { margin: 14px 0 16px; font: 700 clamp(34px, 6vw, 54px)/1.02 Georgia, "STSong", serif; }
    p { color: #a5aac0; line-height: 1.75; }
    .metric-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 28px; }
    .metric { padding: 18px; border: 1px solid #343747; border-radius: 14px; background: #1e2130; }
    .metric strong { display: block; margin-bottom: 5px; color: #fff; font-size: 24px; }
    .metric span { color: #7f859d; font-size: 12px; }
    @keyframes reveal { from { opacity: 0; transform: translateY(10px); } }
    @media (max-width: 650px) { .layout { grid-template-columns: 1fr; } [role="tablist"] { display: flex; overflow-x: auto; padding: 12px; border-right: 0; border-bottom: 1px solid #343747; } [role="tab"] { min-width: max-content; } [role="tab"] + [role="tab"] { margin: 0 0 0 6px; } .metric-grid { grid-template-columns: 1fr; } }
    @media (prefers-reduced-motion: reduce) { [role="tabpanel"] { animation: none; } }
  </style>
</head>
<body>
  <main class="window">
    <div class="titlebar"><i class="dot"></i><i class="dot"></i><i class="dot"></i><strong>PROJECT ATLAS</strong></div>
    <div class="layout">
      <div role="tablist" aria-label="Panneau du projet">
        <button id="tab-overview" role="tab" aria-selected="true" aria-controls="panel-overview"><span class="tab-icon">⌁</span>Aperçu</button>
        <button id="tab-progress" role="tab" aria-selected="false" aria-controls="panel-progress" tabindex="-1"><span class="tab-icon">↗</span>Progression</button>
        <button id="tab-team" role="tab" aria-selected="false" aria-controls="panel-team" tabindex="-1"><span class="tab-icon">◎</span>Équipe</button>
      </div>
      <section class="content">
        <div id="panel-overview" role="tabpanel" aria-labelledby="tab-overview"><span class="label">01 / OVERVIEW</span><h1>Tout est sur la bonne voie</h1><p>Atlas est un plan de lancement de produit destiné aux petites équipes. Nous nous concentrons sur la finalisation de l’expérience principale et sur la préparation du prochain cycle de tests utilisateurs.</p><div class="metric-grid"><div class="metric"><strong>82%</strong><span>Progression globale</span></div><div class="metric"><strong>14</strong><span>Tâches de cette semaine</span></div><div class="metric"><strong>6</strong><span>Membres de la collaboration</span></div></div></div>
        <div id="panel-progress" role="tabpanel" aria-labelledby="tab-progress" hidden><span class="label">02 / PROGRESS</span><h1>Plus que 12 jours avant la publication</h1><p>Le système de design et les principaux flux sont déjà terminés. Les tests de régression sur plusieurs appareils sont en cours, et les derniers problèmes d’accessibilité sont en cours de traitement.</p><div class="metric-grid"><div class="metric"><strong>31</strong><span>Terminé</span></div><div class="metric"><strong>7</strong><span>En cours</span></div><div class="metric"><strong>3</strong><span>À confirmer</span></div></div></div>
        <div id="panel-team" role="tabpanel" aria-labelledby="tab-team" hidden><span class="label">03 / TEAM</span><h1>Six personnes, une œuvre</h1><p>Le produit, le design et le développement maintiennent ensemble la même liste de publication, chaque décision est traçable et chaque avancement est clairement visible.</p><div class="metric-grid"><div class="metric"><strong>2</strong><span>Designer</span></div><div class="metric"><strong>3</strong><span>Développeur</span></div><div class="metric"><strong>1</strong><span>Chef de produit</span></div></div></div>
      </section>
    </div>
  </main>
  <script>
    const tabs = [...document.querySelectorAll('[role="tab"]')];
    function selectTab(tab) {
      tabs.forEach(item => {
        const selected = item === tab;
        item.setAttribute('aria-selected', String(selected));
        item.tabIndex = selected ? 0 : -1;
        document.getElementById(item.getAttribute('aria-controls')).hidden = !selected;
      });
      tab.focus();
    }
    tabs.forEach((tab, index) => {
      tab.addEventListener('click', () => selectTab(tab));
      tab.addEventListener('keydown', event => {
        const direction = event.key === 'ArrowRight' || event.key === 'ArrowDown' ? 1 : event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -1 : 0;
        if (direction) { event.preventDefault(); selectTab(tabs[(index + direction + tabs.length) % tabs.length]); }
      });
    });
  </script>
</body>
</html>
Commentaires sur la pageVous avez découvert un problème ou avez une suggestion d’amélioration ?
Les fonctions de retour nécessitent l’utilisation de la version en ligne

Veuillez activer JavaScript dans la version en ligne avant de soumettre ; les fonctionnalités locales de l’outil ou du cas ne sont pas affectées.

Aller vers la version en ligne pour envoyer des commentaires