@zevra/ui 0.39.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/DECISIONS.md +75 -0
  2. package/dist/clients-data.d.ts +8 -0
  3. package/dist/clients-data.d.ts.map +1 -0
  4. package/dist/clients-data.js +16 -0
  5. package/dist/clients-data.js.map +1 -0
  6. package/dist/clients.d.ts +49 -0
  7. package/dist/clients.d.ts.map +1 -0
  8. package/dist/clients.js +82 -0
  9. package/dist/clients.js.map +1 -0
  10. package/dist/css-inline.d.ts.map +1 -1
  11. package/dist/css-inline.js +1 -1
  12. package/dist/css-inline.js.map +1 -1
  13. package/dist/overlays.d.ts.map +1 -1
  14. package/dist/overlays.js +14 -2
  15. package/dist/overlays.js.map +1 -1
  16. package/logos/clients/beside-avocats.webp +0 -0
  17. package/logos/clients/blnk.svg +57 -0
  18. package/logos/clients/bremens-avocats.webp +0 -0
  19. package/logos/clients/clesame.webp +0 -0
  20. package/logos/clients/dairia-avocats.webp +0 -0
  21. package/logos/clients/index.json +38 -0
  22. package/logos/clients/karila.svg +35 -0
  23. package/logos/clients/legal-data-space.svg +1 -0
  24. package/logos/clients/orange.svg +4 -0
  25. package/logos/clients/source/beside-avocats.avif +0 -0
  26. package/logos/clients/source/blnk.svg +57 -0
  27. package/logos/clients/source/bremens-avocats.webp +0 -0
  28. package/logos/clients/source/clesame.webp +0 -0
  29. package/logos/clients/source/dairia-avocats.webp +0 -0
  30. package/logos/clients/source/karila.svg +35 -0
  31. package/logos/clients/source/legal-data-space.svg +1 -0
  32. package/logos/clients/source/orange.svg +4 -0
  33. package/logos/clients/source/wj-avocats.webp +0 -0
  34. package/logos/clients/wj-avocats.webp +0 -0
  35. package/package.json +6 -2
  36. package/react/clients-data.ts +27 -0
  37. package/react/clients.tsx +121 -0
  38. package/react/css-inline.ts +1 -1
  39. package/react/overlays.tsx +15 -2
  40. package/src/layout.css +89 -0
@@ -40,6 +40,19 @@ export function useOverlay({ ouvert, onClose, panneau, verrouille = true }: UseO
40
40
  // utilisateur au clavier perd sa place dans la page.
41
41
  const origine = useRef<HTMLElement | null>(null)
42
42
 
43
+ // ⚠️ `onClose` NE FIGURE PAS dans les dépendances de l'effet, et ce n'est pas
44
+ // un oubli. L'effet ne se contente pas d'écouter Échap : il mémorise le
45
+ // nœud focalisé, verrouille le défilement et donne le focus au premier
46
+ // élément du panneau. Le rejouer, ce n'est donc pas re-souscrire, c'est
47
+ // ROUVRIR l'enrobage. Or `onClose` est presque toujours une fonction
48
+ // anonyme, recréée à chaque rendu du parent : le focus repartait sur le
49
+ // premier bouton du panneau à chaque rendu de la page derrière, et un champ
50
+ // qui s'enregistre tout seul devenait impossible à remplir (la note d'une
51
+ // pièce dans Tésope, 30/09/2026). L'effet lit ici la dernière fonction
52
+ // connue ; ses dépendances ne portent que l'état.
53
+ const fermer = useRef(onClose)
54
+ fermer.current = onClose
55
+
43
56
  useEffect(() => {
44
57
  if (!ouvert) return
45
58
  const cible = panneau?.current
@@ -62,7 +75,7 @@ export function useOverlay({ ouvert, onClose, panneau, verrouille = true }: UseO
62
75
  function onKey(e: KeyboardEvent) {
63
76
  if (e.key === 'Escape') {
64
77
  e.stopPropagation()
65
- onClose()
78
+ fermer.current()
66
79
  return
67
80
  }
68
81
  if (e.key !== 'Tab' || !cible) return
@@ -89,7 +102,7 @@ export function useOverlay({ ouvert, onClose, panneau, verrouille = true }: UseO
89
102
  if (overflowPrecedent !== null) docu.body.style.overflow = overflowPrecedent
90
103
  origine.current?.focus?.()
91
104
  }
92
- }, [ouvert, onClose, panneau, verrouille])
105
+ }, [ouvert, panneau, verrouille])
93
106
  }
94
107
 
95
108
  /* ─── Voile ────────────────────────────────────────────────────────── */
package/src/layout.css CHANGED
@@ -650,6 +650,95 @@
650
650
  opacity: 0.72;
651
651
  }
652
652
 
653
+ /* ─── Bandeau des clients (logos) ──────────────────────────────────────
654
+ * La façade est `BandeauClients` (@zevra/ui/react/clients), qui porte la
655
+ * liste, les logos inlinés et leurs hauteurs optiques. Repris de zevra.tech
656
+ * (ClientLogoBanner.astro, 14/10/2026), mesures comprises.
657
+ *
658
+ * ÉCHELLE. Chaque logo pose sa hauteur optique en points (--zv-clients-h) ;
659
+ * la rangée fixe la valeur du point (--zv-clients-u). Toute la bande change
660
+ * d'échelle d'un même facteur sans toucher aux rapports optiques. Fluide
661
+ * plutôt que par paliers : ×1,1 à 768 px, ×1,27 à 1 024, ×1,5 dès ~1 366
662
+ * (« on est fier de nos clients », 25/09/2026). La plus longue ligne doit
663
+ * tenir dans 624 px à 768 px, et finir avant le rail de lecture à 1 024.
664
+ *
665
+ * DÉCOUPE. Le logo n'est pas une image, c'est un MASQUE posé sur un aplat :
666
+ * la couleur devient une propriété, exacte, que le survol change. Sans
667
+ * masque (navigateur ancien), l'image s'affiche en fond, aplatie sur
668
+ * l'encre. */
669
+
670
+ .zv-clients {
671
+ max-width: 1120px;
672
+ margin: 0 auto;
673
+ display: flex;
674
+ flex-direction: column;
675
+ align-items: center;
676
+ gap: 40px;
677
+ }
678
+
679
+ .zv-clients__rangee {
680
+ display: flex;
681
+ flex-wrap: wrap;
682
+ align-items: center;
683
+ justify-content: center;
684
+ /* Verticalement, 18 px de chaque côté de la coupure (hauteur nulle, sur
685
+ * sa propre ligne) font 36 px entre les deux lignes. */
686
+ gap: 18px clamp(26px, 3.6vw, 60px);
687
+ --zv-clients-u: clamp(1.1px, calc(0.586px + 0.0669vw), 1.5px);
688
+ }
689
+
690
+ /* La coupure écrite : un élément vide pleine largeur force le retour. */
691
+ .zv-clients__saut {
692
+ flex-basis: 100%;
693
+ height: 0;
694
+ }
695
+
696
+ .zv-clients__logo {
697
+ /* Posées en ligne par la façade, un logo à la fois ; ces valeurs ne
698
+ * servent que de repli. */
699
+ --zv-clients-h: 30;
700
+ --zv-clients-ratio: 1;
701
+ --zv-clients-src: none;
702
+ display: block;
703
+ height: calc(var(--zv-clients-h) * var(--zv-clients-u));
704
+ aspect-ratio: var(--zv-clients-ratio);
705
+ background: var(--zv-clients-src) center / contain no-repeat;
706
+ filter: brightness(0);
707
+ opacity: 0.74;
708
+ }
709
+
710
+ @supports ((mask-image: url("")) or (-webkit-mask-image: url(""))) {
711
+ .zv-clients__logo {
712
+ background: var(--ink-2);
713
+ filter: none;
714
+ opacity: 1;
715
+ -webkit-mask: var(--zv-clients-src) center / contain no-repeat;
716
+ mask: var(--zv-clients-src) center / contain no-repeat;
717
+ transition: background-color 200ms cubic-bezier(0.2, 0.7, 0.2, 1);
718
+ }
719
+
720
+ .zv-clients__logo:hover {
721
+ background: var(--accent);
722
+ }
723
+ }
724
+
725
+ @media (max-width: 767px) {
726
+ .zv-clients {
727
+ gap: 18px;
728
+ }
729
+
730
+ /* Sur téléphone la rangée se replie d'elle-même : la gouttière se
731
+ * resserre pour que trois lignes ne poussent pas la suite hors de vue. */
732
+ .zv-clients__rangee {
733
+ gap: 22px 34px;
734
+ --zv-clients-u: 0.78px;
735
+ }
736
+
737
+ .zv-clients__saut {
738
+ display: none;
739
+ }
740
+ }
741
+
653
742
  /* ─── Carrousel ─────────────────────────────────────────────────────────
654
743
  * HORS RELEVÉ (25/08/2026, voir DECISIONS.md). Défilement natif
655
744
  * scroll-snap, AUCUN JavaScript : pas de flèches (elles exigeraient un