Split Screen Morph
Deux panneaux flex morphent entre 50/50, 70/30 et panneau unique — un bouton ajoute une classe de mode, la transition CSS cubic-bezier gère l'animation sans une ligne de JS d'animation.
Vous aurez accès à la démo interactive avec un compte gratuit.
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Cartes comprend 41 effets, dont 6 gratuits. Découvrir la catégorie →
3 exemples d'usage
Comment ça marche
Le conteneur .split-screen-wrapper est un flex row avec overflow: hidden. Chaque panneau part sur flex: 1 1 0% (50/50 par défaut). La classe .mode-70-30 surcharge les ratios en flex: 7 1 0% et flex: 3 1 0% (7 + 3 = 10, soit 70 % et 30 %). La classe .mode-single collapse le panneau B en flex: 0 1 0% avec min-width: 0; overflow: hidden; font-size: 0 — le navigateur l'écrase à zéro largeur, son contenu disparaît proprement.
Toute la fluidité vient d'une seule règle : transition: .6s cubic-bezier(.4, 0, .2, 1) sur .split-panel. Le navigateur interpole la valeur flex-grow entre les états (CSS Transitions Level 1, propriété interpolable). La courbe cubic-bezier (easing standard Material Design) produit une accélération initiale puis un ralentissement marqué — le mouvement paraît naturel. Aucune boucle JS, aucun requestAnimationFrame — zéro CPU hors de la transition.
setSplitMode(btn, mode) fait trois choses : elle retire .active de tous les .morph-toggle du parent (btn.parentElement.querySelectorAll), l'ajoute au bouton cliqué, puis vide les deux classes de mode du wrapper avant d'en appliquer une nouvelle. L'appel à document.getElementById('splitDemo') est en dur — une seule instance par page avec le code tel quel (voir FAQ pour la multi-instance).
Accessibilité
- prefers-reduced-motion absent : la transition flex s'anime quel que soit le réglage système. Pour la production, ajouter
@media (prefers-reduced-motion: reduce) { .split-panel { transition: none } }. - Les boutons sont des
<button>natifs aveconclickinline — navigables au clavier (Tab + Entrée ou Espace) sans ajout. - Aucun
aria-pressedsur les toggles : l'état actif est purement visuel (classe.active). Ajouteraria-pressed="true/false"danssetSplitModepour les lecteurs d'écran. - Pas de
roleni d'aria-labelsur.split-screen-wrapper— les panneaux doivent porter leurs propres landmarks ou étiquettes si le contenu est significatif. - Contraste des labels 'Panel A / Panel B' en blanc sur dégradé violet (#6366f1 → #818cf8) : ratio ≥ 4,5:1 (WCAG AA atteint).
Compatibilité navigateur
Nécessite CSS Flexbox et CSS Transitions — supportés nativement dans tous les navigateurs modernes depuis 2016. Zéro dépendance externe.
Sans CSS Transitions (IE 9 et inférieur), les panneaux changent de largeur instantanément — aucune erreur JS, le changement de mode reste fonctionnel.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="demo-preview">
<div class="morph-toggle-group">
<button class="morph-toggle active" onclick="setSplitMode(this, '50-50')">50/50</button>
<button class="morph-toggle" onclick="setSplitMode(this, '70-30')">70/30</button>
<button class="morph-toggle" onclick="setSplitMode(this, 'single')">Seul</button>
</div>
<div class="split-screen-wrapper" id="splitDemo">
<div class="split-panel split-panel-a">Panneau A</div>
<div class="split-panel split-panel-b">Panneau B</div>
</div>
</div>
HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
| transition sur .split-panel | .6s cubic-bezier(.4, 0, .2, 1) | Durée et courbe de l'animation. Exemple : .35s ease pour une UI réactive, .9s cubic-bezier(.34,1.56,.64,1) pour un rebond élastique. |
| --primary (var CSS) | #6366f1 | Couleur des boutons toggle (bordure, fond actif, texte actif). Remplacez par la couleur primaire de votre projet. |
| flex sur .mode-70-30 .split-panel-a | 7 | Part du grand panneau en mode asymétrique. 6 = 60/40, 8 = 80/20 — adapter la valeur de .split-panel-b en conséquence. |
| flex sur .mode-70-30 .split-panel-b | 3 | Part du petit panneau. La somme doit être cohérente avec .split-panel-a (ex. 8+2, 7+3, 6+4). |
| gap sur .split-screen-wrapper | 4px | Gouttière entre les deux panneaux. 0 pour un joint net, 12px pour un espacement aéré. |
| border-radius sur .split-screen-wrapper | 8px | Arrondi du conteneur. 0 pour plein bleed, 16px pour une carte ou une modale. |
| setSplitMode(btn, mode) | — | Appel externe depuis n'importe quel code : déclenche un changement de mode depuis un scroll, un timer ou une interaction clavier sans toucher aux boutons intégrés. |
FAQ
.split-screen-wrapper.mode-30-70 .split-panel-a { flex: 3 1 0% } et .mode-30-70 .split-panel-b { flex: 7 1 0% }. Ajoutez un bouton avec onclick="setSplitMode(this, '30-70')", puis étendez la condition dans setSplitMode : '30-70' === t ? o.classList.add('mode-30-70') : ….<div class="split-panel-a">Panel A</div> par n'importe quel HTML. Ajoutez min-width: 0 sur les panneaux si le contenu est rigide (tableaux, éditeurs mono-espace) pour autoriser la réduction en dessous de sa taille naturelle — la propriété est déjà présente sur .mode-single .split-panel-b et sert de modèle.setSplitMode cible getElementById('splitDemo') en dur. Pour plusieurs instances, renommez chaque wrapper (splitDemo2, etc.) et créez une variante setSplitModeById(btn, mode, id) qui reçoit l'id en paramètre, ou lisez l'id depuis un attribut data-target sur le bouton (btn.dataset.target).