Hologram
BêtaComposantProjette un texte, une icône ou un logo en hologramme crédible, que le pointeur perturbe.
- Frameworks
- ReactVueVanilla
- Technologies
- WebGLCanvas 2D
- Déclenché par
- PointeurApparition à l'écranContinu
- Mis à jour le
- Construit sur
- FrameLoopPointerTrackerWebGLSurface
Hologram projette un texte, une icône ou un logo comme une lumière translucide : lignes de balayage, légère interférence chromatique, scintillement lent, halo, copies fantômes en profondeur et cône de projection. Au survol, la projection se perturbe autour du pointeur puis se résorbe ; au repos, de rares micro-glitchs la gardent vivante. Le contenu HTML reste dans la page, pour les lecteurs d'écran et en repli. Utilisez-le pour un titre court, un statut ou une marque, sur un fond sombre de préférence ; il ne convient pas au texte courant.
Installation
Installez l'élément et ses dépendances en une commande :
pnpm dlx shadcn@latest add https://o-ui.hd987.dev/r/hologram.jsonHologram n'a aucune dépendance : il dessine avec WebGL2, disponible dans tous les navigateurs récents. Sans WebGL2, le contenu HTML s'affiche à la place, dans la couleur de l'hologramme avec un halo CSS. Les prérequis communs à tous les composants (Tailwind CSS v4, React 19 ou Vue 3.5) sont décrits dans Installation.
Utilisation
Placez Hologram dans un titre : le texte vient de la prop text, et le titre garde sa sémantique. Pour une icône ou un logo, donnez-lui un <svg> inline ou une <img> en enfant.
import { Hologram } from "@/components/o-ui/hologram/hologram";
export function Hero() {
return (
<h1>
<Hologram text="NOW ONLINE" fontSize={120} />
</h1>
);
}<Hologram preset="hud">
<ShieldIcon className="size-16" aria-label="Secured" />
</Hologram>Hologram est un composant client : ces extraits fonctionnent tels quels dans un Server Component de Next.js. Avec un enfant <svg> ou <img>, text est ignoré : c'est l'enfant qui est projeté. Un texte en enfant (<Hologram>Hello {name}</Hologram>) vaut text ; tout autre HTML en enfant n'est pas projeté et s'affiche tel quel.
Quelques règles font la différence :
- Le contenu donne sa taille au composant. En mode texte, la hauteur vient du texte et la largeur du parent ; avec
fit(activé par défaut),fontSizeest une taille maximale et le texte rétrécit quand la largeur manque, sans jamais être agrandi. Avec un enfant, le composant prend la taille de cet enfant : dimensionnez votre<svg>ou votre<img>en CSS. - Écrivez les retours à la ligne dans
text, avec\n. Le texte ne passe jamais à la ligne de lui-même. - Laissez de la place autour. Le halo, les fantômes et les tranches décalées sortent du contenu : environ 50 px autour et jusqu'à 160 px sous le contenu pour le cône de projection. Évitez
overflow: hiddensur le composant ou ses ancêtres proches, et gardez de l'espace avec les éléments voisins. Si le composant touche le bord de la page, posezoverflow-x: clipsur la section pour éviter un défilement horizontal. - Préférez un fond sombre. L'hologramme est une lumière translucide : il reste visible sur fond clair, mais c'est sur un fond sombre qu'il prend tout son relief. Les exemples de cette page forcent le thème sombre sur leur scène avec la classe
dark. - Prévoyez une pause pour toute la page. L'animation est continue : un seul interrupteur suffit à la mettre en pause partout (voir Mettre en pause).
Exemples
Mise en service
Un hero de lancement dans un h1. La séquence d'allumage (intro, 1,2 s par défaut) joue quand le titre entre à l'écran : un balayage révèle le texte, quelques tranches se résolvent, le scintillement se calme. Le bouton appelle replay() par la ref.
Six rendus
Le même mot dans les six presets. cinematic est le rendu par défaut ; les autres changent la couleur, les lignes, le scintillement, l'aberration, le grain, les fantômes et l'instabilité d'un seul coup. Chaque prop fine reste réglable par-dessus.
Statut système
Un statut piloté par l'application, en preset="hud". À chaque étape, la démo change text et appelle disturb() : la projection réagit à la donnée. Le paragraphe porte aria-live="polite" pour annoncer les changements.
Logo et icônes
Un logo multicolore projeté avec ses propres couleurs (colorMode="source") et trois icônes teintées en hud. Un clic ou un tap sur le logo appelle disturb({ x, y }) au point touché ; le bouton lance une salve sur tout.
Personnalisation
Presets et réglages fins
preset choisit un rendu cohérent : cinematic (projection stable, le défaut), hud (interface nette), retro (terminal ambre), broken (signal dégradé), ghost (spectre translucide) et neon (enseigne saturée). Une prop fine donnée explicitement l'emporte sur le preset ; retirez-la pour revenir à la valeur du preset.
<Hologram text="SYSTEM" preset="retro" flicker={0.6} scanlineSize={5} />- Lumière.
coloretcolorEndforment un dégradé vertical, du haut vers le bas ;opacityrègle la translucidité etglowle halo. - Signal.
scanlinesetscanlineSizerèglent les lignes de balayage et la bande qui descend lentement ;flickerle scintillement ;aberrationle désalignement rouge et bleu ;noisele grain. - Volume.
ghostetdepthrèglent les copies fantômes et leur décalage ;beamle cône de projection sous le contenu ;parallaxleur glissement vers le pointeur. - Vie.
instabilityrègle la fréquence des micro-glitchs spontanés (0pour une projection parfaitement stable) ;speedla vitesse de toute l'animation ambiante (0la fige) ;seedla graine des glitchs et du grain.
Perturbation au pointeur
Sur pointeur fin, une énergie monte quand le pointeur entre dans le composant et avec sa vitesse, puis retombe en returnDuration secondes. Dans le rayon radius, les tranches sautent, des lignes se perdent, l'aberration et le grain se renforcent ; le reste du contenu frissonne légèrement. strength règle la force (0 coupe la perturbation) et parallax incline les fantômes vers le pointeur.
Sur un écran tactile, l'hologramme vit mais ne suit pas le doigt. Appelez disturb() sur un tap, un bouton ou un changement de données pour une salve, globale ou centrée sur un point en pixels CSS depuis le coin supérieur gauche du composant.
Couleurs et thème
Les couleurs par défaut sont celles du preset. Toute couleur CSS convient : une variable du thème (var(--primary)) s'adapte d'elle-même, et currentColor suit la couleur du texte environnant.
<Hologram text="Spectrum" color="var(--primary)" colorEnd="var(--primary-foreground)" />En colorMode="source", le contenu garde ses couleurs : un logo multicolore reste reconnaissable, et color ne sert plus qu'au halo, au cône et au repli.
Intégration
Next.js et Nuxt
Le rendu serveur produit le contenu HTML, déjà dans la couleur de l'hologramme ; la projection le remplace visuellement au chargement du script.
- Next.js : Hologram est un composant client, utilisable dans un Server Component. Un fichier qui utilise la
refou un gestionnaire d'événement doit commencer par"use client". - Nuxt : aucun
<ClientOnly>n'est nécessaire. Importez Hologram explicitement : l'import automatique de Nuxt préfixe le nom d'un composant par son dossier. - Taille au chargement. Avec un rendu serveur, le texte apparaît d'abord à
fontSize, puisfitle réduit au chargement du script sur un petit écran.fontSizeest un nombre, pas une valeur responsive : choisissez un compromis entre ordinateur et mobile.
Contrôle programmatique
La ref React reçoit l'élément racine augmenté de disturb() et replay(). En Vue, le composant expose el, disturb et replay.
"use client";
import { useRef } from "react";
import { Hologram, type HologramHandle } from "@/components/o-ui/hologram/hologram";
export function Status({ value }: { value: string }) {
const hologramRef = useRef<HologramHandle>(null);
return (
<>
<Hologram ref={hologramRef} text={value} preset="hud" />
<button type="button" onClick={() => hologramRef.current?.disturb({ strength: 1.2 })}>
Ping
</button>
</>
);
}Mettre en pause toutes les animations
L'hologramme vit sans fin. Pour la WCAG 2.2.2, la page doit permettre de mettre ce mouvement en pause. La pause commune de FrameLoop, installé avec Hologram, fige tous les hologrammes de la page, ceux créés ensuite compris, et les autres animations continues d'O-UI : un seul interrupteur suffit. Placez-le dans l'en-tête, le pied de page ou les réglages du site.
import { setAnimationsPaused } from "@/components/o-ui/frame-loop/frame-loop-core";
setAnimationsPaused(true); // every hologram shows a stable frameLa prop paused fige une seule instance ; la pause commune l'emporte.
États
La racine expose data-state : loading (contenu HTML visible, projection en préparation), active (animée), idle (dessinée, au repos), static (pause ou mouvement réduit), disabled et fallback (sans WebGL, ou contenu qui ne peut pas être projeté). data-source vaut text, svg ou image, et data-disturbed est présent pendant une perturbation : de quoi styler le contenu voisin.
API
| Prop | Type | Défaut | Description |
|---|---|---|---|
text | string | "O-UI" | Texte projeté, 240 caractères au plus ; les retours à la ligne explicites sont conservés. Avec un enfant svg ou img, il est ignoré. |
fontSize | number | 160 | Taille du texte en pixels CSS, de 12 à 512. Avec fit, c'est la taille maximale. |
fontFamily | string | "inherit" | Famille de police CSS du texte ; « inherit » reprend la police environnante. |
fontWeight | number | 700 | Graisse du texte, de 100 à 900. |
letterSpacing | number | 0 | Approche du texte en pixels CSS, de −8 à 32. |
lineHeight | number | 1.1 | Interligne du texte relatif à la taille, de 0,8 à 2. |
align | HologramAlign | "center" | Alignement horizontal des lignes : « left », « center » ou « right ». |
fit | boolean | true | Réduit le texte pour tenir dans la largeur disponible, sans jamais l'agrandir. |
preset | HologramPreset | "cinematic" | Rendu cohérent de base : « cinematic », « hud », « retro », « broken », « ghost » ou « neon ». Chaque prop fine donnée explicitement l'emporte. |
colorMode | HologramColorMode | "tint" | « tint » colore la forme avec color et colorEnd ; « source » conserve les couleurs du contenu. |
color | string | "#6ee7ff" | Couleur en haut de la forme, toute couleur CSS ; elle teinte aussi le halo, les fantômes et le cône. |
colorEnd | string | "#bff6ff" | Couleur en bas de la forme : le dégradé vertical vers l'émetteur. |
opacity | number | 0.85 | Translucidité globale, de 0 à 1. |
glow | number | 0.5 | Halo autour de la forme, de 0 (aucun) à 1 : sa portée et son intensité. |
scanlines | number | 0.35 | Contraste des lignes de balayage et de la bande qui descend, de 0 (aucune) à 1. |
scanlineSize | number | 3 | Pas des lignes de balayage en pixels CSS, de 1 à 12. |
flicker | number | 0.25 | Scintillement lent et irrégulier de la luminosité, de 0 (stable) à 1. |
aberration | number | 1 | Désalignement rouge et bleu au repos en pixels CSS, de 0 à 8 ; une perturbation l'amplifie. |
noise | number | 0.15 | Grain de signal, de 0 (aucun) à 1. |
ghost | number | 0.35 | Opacité des deux copies fantômes derrière la forme, de 0 (aucune) à 1. |
depth | number | 10 | Décalage des copies fantômes en pixels CSS, de 0 à 40 : l'épaisseur perçue. |
beam | number | 0.25 | Cône de projection sous la forme, de 0 (aucun) à 1. |
speed | number | 1 | Vitesse de l'animation ambiante (lignes, scintillement, grain), de 0 (figée) à 3. |
seed | number | 1 | Graine des glitchs spontanés et du grain, un entier de 0 à 1 000 000 : deux instances diffèrent. |
instability | number | 0.3 | Fréquence et force des glitchs spontanés au repos, de 0 (jamais) à 1 ; toujours moins de trois par seconde. |
intro | number | 1.2 | Durée de la séquence d'allumage à la première apparition, en secondes, de 0 (aucune) à 4. |
strength | number | 0.6 | Force de la perturbation autour du pointeur, de 0 (aucune) à 2. |
radius | number | 80 | Rayon de la perturbation autour du pointeur en pixels CSS, de 0 à 500. |
returnDuration | number | 0.6 | Temps de résorption d'une perturbation, en secondes, de 0,1 à 3. |
parallax | number | 0.15 | Glissement des fantômes et du halo vers le pointeur, de 0 (aucun) à 1. |
pixelRatio | number | 1.5 | Densité de pixels maximale du canvas, de 0,5 à 2, jamais au-delà de celle de l'écran. Baissez-la sur un très grand titre. |
paused | boolean | false | Fige une frame stable, sans scintillement, glitch ni grain, et arrête la boucle. |
disabled | boolean | false | Affiche le contenu HTML tel quel et libère le GPU. |
reducedMotion | ReducedMotion | "user" | « user » suit le réglage du système, « always » fige toujours l'hologramme, « never » garde le mouvement (à réserver à un choix explicite de l'utilisateur). |
Les nombres hors plage sont bornés. Une valeur invalide (NaN, mauvais type, mot-clé inconnu) garde la valeur courante. Une couleur est transmise telle quelle au CSS : si le navigateur ne la reconnaît pas, l'hologramme prend currentColor. Une prop retirée revient à la valeur du preset, puis à la valeur par défaut.
Props des adaptateurs. React : className est fusionné avec cn(), les attributs natifs sont transmis à la racine (span), children reçoit le <svg> ou l'<img> à projeter, et ref donne l'élément racine augmenté de disturb() et replay(). Vue : class est fusionné de même, le slot par défaut reçoit le contenu à projeter, et le composant expose el, disturb et replay.
Accessibilité
- Contenu réel. Le texte, le
<svg>ou l'<img>existe dès le rendu serveur et reste dans la page, invisible une fois la projection dessinée. Les lecteurs d'écran le lisent normalement ; donnez à une icône décorativearia-hidden="true"et à une icône porteuse de sens unaria-label, exactement comme sans hologramme. - Canvas décoratif. Le canvas est en
aria-hidden="true"etpointer-events: none: il ne capte ni clic, ni focus. Placez un lien ou un bouton autour de Hologram, jamais dans le<svg>projeté : le contenu projeté est rendu invisible, anneau de focus compris. - Mouvement réduit. Avec
prefers-reduced-motion, l'hologramme reste holographique mais figé : couleur, halo, lignes et fantômes fixes, sans scintillement, glitch, grain animé ni réaction au pointeur ; l'allumage devient un fondu etdisturb()ne fait rien. La bascule du système est prise en compte en direct. - Pas de flash. Au-dessus de 3 Hz, le scintillement reste sous un dixième de la luminance quels que soient les réglages. Les déchirures rapides restent dans un disque de 120 px autour du pointeur ; ailleurs, tranches et lignes perdues changent au plus trois fois par seconde, et les glitchs spontanés aussi (WCAG 2.3.1). Gardez tout de même
flickeretinstabilitymodérés sur un grand titre. - Pause. L'animation continue s'arrête avec la pause commune de la page ou la prop
paused(WCAG 2.2.2). - Contraste élevé et repli. En
forced-colors, sans WebGL2 ou sur un GPU logiciel, le contenu HTML s'affiche dans la couleur de l'hologramme avec un halo CSS.
Performances
- Dépendances : aucune. Le cœur et ses fichiers pèsent environ 16 kB compressés (40 kB minifiés), primitives comprises.
- Un rendu par frame. Le contenu est rasterisé une fois par changement (texte, typographie, police, taille affichée, densité de pixels) dans une texture bornée à 2 millions de pixels ; son halo est calculé au même moment. Chaque frame dessine ensuite un seul triangle plein canvas, sans cible intermédiaire.
- Travail borné. La densité de pixels est plafonnée par
pixelRatio(1,5 par défaut) et le canvas à 4 millions de pixels. Les écouteurs du pointeur n'existent que sur pointeur fin et effet actif. - Sommeil. Hors écran, dans un onglet masqué, en pause ou sous mouvement réduit, rien ne tourne. Loin de l'écran (360 px), le contexte WebGL est libéré et recréé à l'approche : une page peut aligner de nombreux hologrammes sans dépasser la limite de contextes du navigateur.
- Plusieurs instances. Chaque hologramme est un membre de la boucle commune de la page : toutes les mesures passent avant tous les rendus, et une page avec plusieurs éléments O-UI ne demande qu'une frame par rafraîchissement.
Problèmes fréquents
- Le halo ou le cône est coupé. Un ancêtre en
overflow: hiddenrogne le canvas, qui déborde du composant. Retirez-le ou gardez de l'espace. - Le logo est un rectangle plein. En mode
tint, seule la couverture compte : une image JPEG sans transparence est un rectangle. Utilisez un SVG ou une image PNG/WebP avec transparence. - Le logo SVG perd un détail. Le SVG est sérialisé avec ses styles calculés, mais il est rendu seul : une référence externe (
<use href="…">vers un autre fichier, une image distante, une police web) n'est pas disponible. Gardez le SVG autonome. - L'image n'est pas projetée. Une image d'une autre origine sans en-tête CORS ne peut pas être lue : servez-la depuis votre domaine ou avec
crossorigin="anonymous". En attendant, le composant affiche l'image telle quelle (data-state="fallback"). - Les réglages fins semblent sans effet. Vérifiez
preset: une prop fine donnée l'emporte toujours, mais une prop àundefinedrevient à la valeur du preset.
Notes et limites
- Navigateurs. WebGL2 est nécessaire : Chrome, Edge, Firefox, Safari 15 et plus. Sans lui, le contenu HTML coloré s'affiche.
- Un contenu par instance. Avec des enfants, seul le premier
<svg>ou<img>est projeté ; le reste du HTML s'affiche normalement. - Pas de HTML arbitraire. Un bloc de texte mis en forme, un composant ou une vidéo ne peut pas être rasterisé : utilisez
text, un SVG ou une image. - Changement de contenu. Un nouveau texte remplace l'ancien d'un coup ; appelez
disturb()au même moment pour une transition en glitch, comme l'exemple Statut système. Un nouveau logo, ou un SVG modifié sur place, reconstruit l'hologramme sans rejouer l'allumage. Il n'y a pas de transformation progressive entre deux contenus.