React, Vue et JavaScript natif
Comment un même élément fonctionne dans chaque framework, avec les hooks des effets et le rendu serveur.
Un cœur, des adaptateurs
Chaque élément est livré en plusieurs fichiers, installés côte à côte dans components/o-ui/<élément>/ :
| Fichier | Composant | Effet | Primitive |
|---|---|---|---|
<élément>-core.ts : le cœur | oui | oui | oui |
<élément>.tsx : React | le composant | le composant et le hook use<Nom> | les hooks seuls |
<Nom>.vue : le composant Vue | oui | oui | non |
use-<élément>.ts : composable Vue | non | oui | oui |
S'y ajoutent des fichiers internes propres à chaque élément (magnetic-spring.ts…), et, en Vue, un index.ts créé à l'installation. Le cœur contient toute la logique, sans framework ; les adaptateurs relient seulement le cycle de vie du framework au cœur : create…() au montage, update() quand une prop change, destroy() au démontage. Les props ont les mêmes noms et les mêmes valeurs par défaut partout.
Les imports :
- React :
@/components/o-ui/<élément>/<élément>pour le composant et les hooks,@/components/o-ui/<élément>/<élément>-corepour les fonctions du cœur ; - Vue :
@/components/o-ui/<élément>pour tout (composant, composable et cœur).
Les effets : un hook et un composable
Un effet (Magnetic, GlowBorder, ScrollTint) s'applique aussi à votre propre élément, sans l'élément racine que pose son composant. Le hook React use<Nom> et le composable Vue du même nom font le même travail que le composant :
"use client";
import { type ReactNode, useRef } from "react";
import { useGlowBorder } from "@/components/o-ui/glow-border/glow-border";
export function FeaturedCard({ children }: { children: ReactNode }) {
const cardRef = useRef<HTMLElement>(null);
useGlowBorder(cardRef, { intensity: 1.2 });
return (
<article ref={cardRef} className="rounded-2xl border bg-card p-6">
{children}
</article>
);
}- Le hook reçoit une ref ou l'élément ; le composable, un élément, une
refde template ou un getter. Les options sont les mêmes que les props du composant. - Chaque effet garde son contrat, décrit sur sa page. Magnetic reçoit l'élément fixe qui entoure votre bouton (un
<li>, un<span>) : c'est ce champ qui est mesuré, et son premier enfant, ou l'optiontarget, qui se déplace. - Les primitives n'ont pas de composant : leur cœur, des hooks React et des composables Vue. Leur cœur ne crée pas d'instance : il expose
subscribe…, qui renvoie la fonction de désabonnement, ouobserve…etanimate…, qui renvoientupdate()etdestroy().
React et Next.js
Les fichiers React commencent par "use client". Les composants s'utilisent directement dans des Server Components ; les hooks, eux, s'appellent dans un composant client que vous écrivez. Ils sont compatibles avec le mode strict de React et avec le React Compiler. React 19 minimum.
Vue et Nuxt
Les adaptateurs Vue n'agissent que dans onMounted : ils sont sûrs avec le rendu serveur de Nuxt, sans <ClientOnly>. Vue 3.5 minimum.
JavaScript natif (Astro, Svelte, HTML)
Le cœur s'utilise tel quel, avec ses fichiers annexes et les primitives qu'il importe (voir Installation) :
import { createGlowBorder } from "./glow-border-core";
const card = document.querySelector<HTMLElement>("#card");
if (card) {
const glow = createGlowBorder(card, { intensity: 1.2 });
glow.update({ intensity: 1.5 });
glow.destroy(); // removes the listeners and restores the styles
}Rendu serveur
Aucun fichier n'accède à window ou document à l'import. Sur le serveur, create…() renvoie une instance inerte. Le rendu initial est identique côté serveur et côté client, ce qui évite toute erreur d'hydratation.
Les primitives gardent un état par document (la pause commune des animations, la position du pointeur). Côté serveur, n'appelez pas setAnimationsPaused : l'état serait partagé entre les requêtes. Appliquez la préférence du visiteur dans le navigateur.