Frameworks

Icônes dans Astro

Astro rend les composants en HTML sur le serveur ou au moment du build, ce qui convient bien aux icônes SVG : appelez la fonction de rendu dans le frontmatter, et la page reçoit du SVG inline simple, sans JavaScript côté client. Les îles et l'élément personnalisé prennent en charge les icônes qui doivent réagir.

Installation

Installez le paquet. Les icônes statiques ne nécessitent aucune intégration Astro, car glyphfox est constitué de modules ES standards qui s'exécutent dans le frontmatter. Les îles construites avec glyphfox/react, glyphfox/vue ou glyphfox/svelte nécessitent l'intégration Astro correspondante.

terminal
npm install glyphfox
# only for islands
npx astro add react

Les icônes dans le frontmatter

Le code du frontmatter s'exécute sur le serveur, ou au moment du build pour les pages statiques. toSvg renvoie la chaîne SVG de n'importe quel nom d'icône, et set:html l'écrit dans la page sans l'échapper :

src/pages/index.astro
---
import { toSvg } from 'glyphfox';

const search = toSvg('search', { size: 20 });
---
<label class="search">
  <Fragment set:html={search} />
  <input type="search" name="q" aria-label="Search the docs" />
</label>

toSvg accepte la syntaxe complète des noms, si bien que badges, fusions et styles tiennent en une seule chaîne : toSvg('file+check@tr'), toSvg('cloud&lock:overlap'), toSvg('heart/solid').

Importer glyphfox charge toutes les icônes, mais uniquement sur le serveur ou pendant le build. Rien de tout cela n'atteint le navigateur, car le frontmatter ne s'y exécute jamais.

Un composant Icon.astro

Encapsulez l'appel dans un composant pour que les pages passent des props plutôt que des options. Le SVG est inséré directement dans le parent, sans élément conteneur :

src/components/Icon.astro
---
import { toSvg, type SvgOptions } from 'glyphfox';

interface Props extends SvgOptions {
  name: string;
}

const { name, ...options } = Astro.props;
const svg = toSvg(name, options);
---
<Fragment set:html={svg} />
src/components/Nav.astro
---
import Icon from './Icon.astro';
---
<nav class="nav">
  <a href="/"><Icon name="home" size={20} /> Home</a>
  <a href="/docs/"><Icon name="book-open" size={20} weight={1.5} /> Docs</a>
  <a href="/new/"><Icon name="file+plus" size={20} title="New file" /></a>
</nav>

<style>
  .nav :global(.glyphfox) { color: #6b7280; }
  .nav a:hover :global(.glyphfox) { color: #111827; }
</style>

Chaque svg porte les classes glyphfox et glyphfox-NAME. Le balisage inséré avec set:html ne reçoit pas les attributs qu'Astro utilise pour limiter la portée des styles des composants : ciblez-le donc avec :global() à l'intérieur d'un sélecteur parent à portée limitée, comme ci-dessus, ou laissez-le hériter de color grâce à currentColor.

Le lien ci-dessus ne contient qu'une icône, sans texte : celle-ci reçoit donc un title, qui ajoute role="img", aria-label et un <title>, et le lien en tire son nom accessible. Les icônes placées à côté d'un texte visible restent décoratives. D'autres modèles sont présentés dans le guide d'accessibilité.

Animations sans JavaScript

Le SVG peut embarquer son propre CSS, si bien que les états vivants et les morphs au survol se jouent dans du HTML statique, sans aucun script : animate avec le trigger="mount" par défaut ou avec "hover", et morphTo avec trigger="hover", qui réagit au svg et à son parent direct.

src/components/Chapter.astro
---
import Icon from './Icon.astro';
---
<a class="next" href="/chapter-2/">
  Next chapter <Icon name="arrow-right" morphTo="chevron-right" trigger="hover" />
</a>

<p class="status">
  <Icon name="loader" animate="spin" title="Building" /> Building the preview
</p>

Comme Icon.astro n'ajoute aucun conteneur, le svg est un enfant direct du lien, et survoler n'importe quel point du lien fait se transformer la flèche. Chaque preset s'arrête sous prefers-reduced-motion ; les presets et les déclencheurs sont répertoriés dans le guide des icônes animées.

Des îles pour gérer l'état

Les icônes qui suivent un état, comme un bouton de menu qui se transforme en icône de fermeture, ont leur place dans une île. Utilisez le paquet correspondant au framework de l'île et hydratez-la avec une directive client :

src/components/MenuButton.jsx
import { useState } from 'react';
import { MorphIcon, registerIcons } from 'glyphfox/react';
import menu from 'glyphfox/icons/menu';
import x from 'glyphfox/icons/x';

// Names passed to MorphIcon load on demand; registering them
// puts the icon in the server rendered HTML.
registerIcons([menu, x]);

export default function MenuButton() {
  const [open, setOpen] = useState(false);
  return (
    <button
      type="button"
      aria-expanded={open}
      aria-label={open ? 'Close menu' : 'Open menu'}
      onClick={() => setOpen(!open)}
    >
      <MorphIcon name={open ? 'x' : 'menu'} duration={300} />
    </button>
  );
}
src/layouts/Base.astro
---
import MenuButton from '../components/MenuButton.jsx';
---
<header>
  <MenuButton client:load />
</header>

Astro rend d'abord les îles sur le serveur. Les composants d'icônes comme MenuIcon enregistrent leur icône lorsque votre code les utilise, mais les noms passés à MorphIcon, Icon ou morphTo sont chargés à la demande : sans registerIcons, le HTML du serveur contient donc un svg vide jusqu'à l'hydratation de l'île.

Cela fonctionne de la même façon avec glyphfox/vue et avec glyphfox/svelte, qui nécessite Svelte 5. Pour les îles situées sous la ligne de flottaison, client:visible ou client:idle retarde le téléchargement jusqu'à ce qu'il soit nécessaire. Les détails propres à chaque framework se trouvent dans les guides React, Vue et Svelte.

L'élément personnalisé

Sans framework, l'élément <glyph-fox> gère les clics, le survol et les changements de nom. Astro traite et intègre au bundle les balises <script> des composants, et n'inclut chaque script qu'une seule fois par page, même lorsque le composant apparaît plusieurs fois :

src/components/LikeButton.astro
<button type="button" class="like" aria-pressed="false" aria-label="Like">
  <glyph-fox name="heart" morph-to="heart/solid" trigger="state"></glyph-fox>
</button>

<script>
  import { defineGlyphFox } from 'glyphfox/web/element';
  import { registerIcons } from 'glyphfox/core';
  import heart from 'glyphfox/icons/heart';

  registerIcons([heart]);
  defineGlyphFox();

  for (const button of document.querySelectorAll('button.like')) {
    const icon = button.querySelector('glyph-fox');
    button.addEventListener('click', () => {
      const on = button.getAttribute('aria-pressed') !== 'true';
      button.setAttribute('aria-pressed', String(on));
      icon.toggleAttribute('active', on);
    });
  }
</script>

Le bouton pilote l'icône via trigger="state" et l'attribut active. Avec trigger="click", l'élément écouterait les clics sur lui-même, et un clic dans le padding du bouton basculerait aria-pressed sans déclencher le morph de l'icône.

Les éléments personnalisés restent définis pendant la navigation côté client avec le routeur des transitions de vue d'Astro : les icônes des nouvelles pages sont donc mises à niveau d'elles-mêmes. Le code qui attache des écouteurs, comme la boucle ci-dessus, ne s'exécute qu'une fois ; avec le routeur, relancez-le sur l'événement astro:page-load.

Classes Tailwind

Si le projet utilise Tailwind CSS, le plugin glyphfox dessine les icônes sous forme de masques CSS : une autre option sans JavaScript, pratique dans les templates riches en contenu. La configuration et la syntaxe des classes sont décrites dans le guide Tailwind.

src/components/Hero.astro
<h1 class="flex items-center gap-3">
  <span class="gf-rocket text-4xl text-orange-500" aria-hidden="true"></span>
  Launch faster
</h1>

Props

Icon.astro transmet ses props à toSvg : il accepte donc les options communes avec leurs noms en camelCase, ainsi que class et style sous forme de chaînes pour le svg. Contrairement au moteur de rendu du core, morphTo peut ici être un nom, puisque toSvg le résout.

OptionTypePar défautDescription
sizenumber, string24Largeur et hauteur ; les nombres sont en pixels.
colorstringcurrentColorCouleur principale.
secondaryColorstringcolorCouleur du calque duotone et du remplissage plein.
secondaryOpacitynumber0.3 / 1Opacité du calque duotone ou du remplissage plein.
weightnumber2Trait de 1 à 3 sur la grille de 24, avec compensation optique en dessous de 24 px.
absoluteWeightbooleanfalseGarde le trait à weight pixels quelle que soit la taille.
cornersround, sharproundJointures et extrémités arrondies ou vives.
variantoutline, duotone, solidoutlineAxe de style ; solid vient des formes fermées.
rotatenumber0Rotation en degrés.
flipXbooleanfalseMiroir horizontal.
flipYbooleanfalseMiroir vertical.
mirrorRtlbooleanfalseMiroir dans les mises en page de droite à gauche.
animatedraw, pop, wiggle, spin, pulse, bounce, shakeaucunPréréglage d'état vivant.
triggermount, hover, click, statemountMoment où l'animation ou le morph se joue.
activebooleanfalseL'état pour le déclencheur state.
durationnumber (ms)selon le préréglageDurée de l'animation ou du morph.
delaynumber (ms)0Délai de l'animation ou du morph.
loopboolean, numberfalseRépète à l'infini ou un certain nombre de fois (spin et pulse bouclent par défaut).
modifierplus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dotaucunBadge avec espace de découpe automatique.
modifierCornertop-left, top-right, bottom-left, bottom-rightbottom-rightCoin du badge.
morphToname, dataaucunCible du morph ; le déclencheur décide quand elle s'affiche.
titlestringaucunNom accessible : title, role img et aria-label.
decorativebooleantrue sans titleMasque l'icône aux technologies d'assistance.

Pièges courants

Styles à portée limitée et set:html
Les règles du bloc de style d'un composant ne s'appliquent pas au balisage inséré avec set:html, car il lui manque les attributs de portée. Utilisez :global(.glyphfox) à l'intérieur d'un sélecteur à portée limitée, ou faites hériter color.
Les noms dans les îles
MorphIcon, Icon et morphTo chargent les noms à la demande. Enregistrez leurs données avec registerIcons pour que le HTML du serveur soit complet.
Déclencheurs de clic dans les boutons
trigger="click" sur glyph-fox écoute les clics sur l'icône, et les clics dans le padding du bouton ne l'atteignent pas. Utilisez trigger="state" et définissez active depuis le bouton.
Les noms inconnus interrompent le build
toSvg lève une exception pour un nom qui n'existe pas, ce qui fait échouer un build statique et renvoie une erreur en mode serveur. Vérifiez que les noms issus du contenu figurent dans la liste des icônes, ou interceptez l'erreur lorsqu'ils proviennent de données utilisateur.
Bundles serveur sur les runtimes edge
toSvg du point d'entrée principal place toutes les icônes dans le bundle serveur, soit environ 154 Ko minifiés. Là où la taille des fonctions est limitée, utilisez renderToString de glyphfox/core avec un import par icône, et passez les données d'icône à morphTo.
Conteneurs et morphs au survol
Un morph au survol réagit au svg et à son parent direct. Envelopper l'icône dans un élément supplémentaire fait de cet élément le parent : survoler le lien qui l'entoure ne déclenche alors plus le morph.

Questions fréquentes

Les icônes rendues dans le frontmatter embarquent-elles du JavaScript ?

Non. Le frontmatter s'exécute sur le serveur ou au moment du build, et la page reçoit du SVG simple. Les presets et les morphs au survol sont du CSS intégré à ce SVG. Seuls l'élément personnalisé et les îles ajoutent du code client.

Puis-je utiliser des icônes dans du contenu Markdown ou MDX ?

En MDX, importez Icon.astro et utilisez-le comme dans n'importe quel composant. Le Markdown standard ne peut pas importer de composants : rendez donc les icônes dans le layout qui entoure le contenu, ou utilisez les classes Tailwind dans du HTML brut, là où votre Markdown le permet.

Icônes de ce guide