Frameworks

Icons in Lit und Web Components

glyphfox liefert ein Custom Element, <glyph-fox>, das den Icon-Namen und alle gemeinsamen Props als Attribute entgegennimmt. Es funktioniert überall, wo HTML funktioniert, und passt ohne Wrapper in Lit-Templates.

Installation

Installieren Sie das Paket und registrieren Sie das Element einmalig im Einstiegsmodul Ihrer App. Der Import von glyphfox/web registriert als Seiteneffekt alle Icons und definiert <glyph-fox>, weitere Schritte sind nicht nötig.

terminal
npm install glyphfox
main.js
import 'glyphfox/web';

Um nur die Icons auszuliefern, die Sie verwenden, importieren Sie das Element aus glyphfox/web/element, registrieren die Icon-Daten aus glyphfox/icons/NAME und definieren das Element selbst. registerIcons stammt aus glyphfox/core, das seine Registry mit dem Element teilt.

icons.js
import { defineGlyphFox } from 'glyphfox/web/element';
import { registerIcons } from 'glyphfox/core';
import menu from 'glyphfox/icons/menu';
import x from 'glyphfox/icons/x';
import bell from 'glyphfox/icons/bell';
import search from 'glyphfox/icons/search';

registerIcons([menu, x, bell, search]);
defineGlyphFox();
EinstiegspunktWas ausgeliefert wirdGröße (esbuild, minifiziert)
glyphfox/webDas Element, die Engine und alle Icons, beim Import registriert.etwa 209 KB, 59 KB gzip
glyphfox/web/elementDas Element und die Engine; die Icons registrieren Sie selbst.etwa 77 KB, 28 KB gzip mit einem Icon, plus einige hundert Byte pro Icon

Ein Name, den Sie nicht registriert haben, wird bei Bedarf nachgeladen. webpack und esbuild machen daraus einen kleinen Chunk pro Icon, ein Produktions-Build mit Vite lässt den Import innerhalb von node_modules dagegen unverändert, und das Icon bleibt leer (siehe den Leitfaden zum Tree-Shaking). Registrieren Sie bei glyphfox/web/element jedes Icon, das Sie verwenden; die Icons werden dann außerdem schon im ersten Frame gezeichnet, ohne Netzwerk-Roundtrip.

Das Element verwenden

Das Element rendert ein Inline-SVG in seinen Shadow-Root. name akzeptiert die vollständige Namenssyntax, sodass Modifier-Badges, Fusionen und Stile keine zusätzliche API benötigen. Alle gemeinsamen Props sind Attribute in Kebab-Case.

index.html
<glyph-fox name="home"></glyph-fox>
<glyph-fox name="file+check" size="32" weight="1.5"></glyph-fox>
<glyph-fox name="cloud&lock" layout="overlap"></glyph-fox>
<glyph-fox name="heart/solid" color="#e11d48"></glyph-fox>
<glyph-fox name="bell" animate="shake" trigger="hover"></glyph-fox>

Das Element beobachtet seine Attribute. Ändern Sie eines mit setAttribute oder über ein beliebiges Template, das Attribute schreibt, und das Icon wird mit dem neuen Wert neu gezeichnet.

Lit-Templates

Binden Sie Werte in Lit über Attribut-Bindings. Das Element liest Attribute und hat keine passenden JavaScript-Properties, daher setzt ein Property-Binding wie .name=${...} ein Feld, das das Element nie ausliest, und das Icon bleibt unverändert.

app-toolbar.js
import { LitElement, html, css } from 'lit';
import 'glyphfox/web';

export class AppToolbar extends LitElement {
  static properties = {
    open: { type: Boolean },
    unread: { type: Number },
  };

  static styles = css`
    button { display: inline-flex; align-items: center; gap: 8px; color: inherit; }
    glyph-fox { color: var(--toolbar-icon, currentColor); }
  `;

  constructor() {
    super();
    this.open = false;
    this.unread = 0;
  }

  render() {
    return html`
      <button type="button" aria-expanded=${this.open} @click=${() => (this.open = !this.open)}>
        <glyph-fox name=${this.open ? 'x' : 'menu'} morph duration="300"></glyph-fox>
        <span>Menu</span>
      </button>
      <glyph-fox
        name="bell"
        animate="shake"
        trigger="state"
        ?active=${this.unread > 0}
        title=${this.unread ? `${this.unread} new notifications` : 'No new notifications'}
      ></glyph-fox>
    `;
  }
}

customElements.define('app-toolbar', AppToolbar);

Verwenden Sie für Flags Boolean-Bindings: ?morph=${...} und ?active=${...}. Das Element wertet ein leeres Attribut oder "true" als wahr und "false" oder ein fehlendes Attribut als falsch, sodass sich ein Boolean-Binding eins zu eins übertragen lässt.

Binden Sie bei optionalen Werten nothing (oder verwenden Sie die Direktive ifDefined), damit das Attribut entfernt wird, wenn kein Wert vorhanden ist. Ein leeres size-Attribut gibt dem inneren svg-Element eine leere Breite und Höhe, und der Browser greift auf seine Standardgröße für svg-Elemente zurück.

icon-button.js
import { html, nothing } from 'lit';

export const iconButton = ({ name, size, label, onClick }) => html`
  <button type="button" aria-label=${label} @click=${onClick}>
    <glyph-fox name=${name} size=${size ?? nothing}></glyph-fox>
  </button>
`;

Styling über den Shadow-Root hinweg

Das svg liegt im Shadow-Root des Elements, seine Striche verwenden currentColor. Farbe wird über die Shadow-Grenze hinweg vererbt, setzen Sie color also am Element oder an einem beliebigen übergeordneten Element. Der Host hat display: inline-flex mit line-height: 0 und vertical-align: middle und fügt sich dadurch ohne zusätzliches CSS in eine Textzeile ein.

Die Größe kommt aus dem Attribut size, das Breite und Höhe des inneren svg festlegt. CSS-Breite und -Höhe am Host skalieren die Zeichnung nicht. Verwenden Sie size="1em", damit das Icon der Schriftgröße folgt, oder setzen Sie das Attribut aus Ihren eigenen Design-Tokens.

note.html
<p class="note">
  <glyph-fox name="circle-info" size="1em" weight="2.5"></glyph-fox>
  The icon follows the font size of this paragraph.
</p>
<glyph-fox name="folder" variant="duotone" color="#0f172a" secondary-color="#ff7a1a"></glyph-fox>
toolbar.css
.toolbar glyph-fox { color: #6b7280; }
.toolbar button:hover glyph-fox { color: #111827; }

Es gibt keine Parts oder Slots zum Stylen: Alles im Inneren wird aus den Props erzeugt. Ändern Sie die Zeichnung über Attribute wie weight, corners, variant und secondary-color, nicht über Selektoren, die in den Shadow-Root hineingreifen sollen.

Ein eigener Tag-Name

defineGlyphFox(tag) definiert das Element unter einem anderen Namen und gibt seine Klasse zurück. So passen Sie es an das Präfix Ihres Designsystems an oder vermeiden einen Konflikt mit einem anderen Element. Mehrfache Aufrufe sind unbedenklich: Ein bereits definiertes Tag wird unverändert zurückgegeben.

icons.js
import { defineGlyphFox } from 'glyphfox/web/element';
import { registerIcons } from 'glyphfox/core';
import home from 'glyphfox/icons/home';
import settings from 'glyphfox/icons/settings';

registerIcons([home, settings]);
defineGlyphFox('acme-icon');
index.html
<acme-icon name="home" size="20"></acme-icon>
<acme-icon name="settings" size="20" weight="1.5"></acme-icon>

Der Import von glyphfox/web definiert außerdem glyph-fox, entscheiden Sie sich daher für einen Einstiegspunkt pro App: glyphfox/web für das Standard-Tag mit allen Icons, glyphfox/web/element, wenn Sie Tag und Icon-Liste selbst wählen.

Auslöser und Morphs

Das Element kümmert sich selbst um die Interaktion. Mit trigger="hover" oder trigger="click" registriert es seine Listener am eigenen Element, mit trigger="state" folgt es dem Attribut active. Das Attribut morph animiert jede Änderung von name, während morph-to immer dann zu einem zweiten Icon morpht, wenn der Auslöser greift.

index.html
<glyph-fox name="menu" morph-to="x" trigger="click"></glyph-fox>
<glyph-fox name="sparkles" animate="draw" trigger="hover"></glyph-fox>
<glyph-fox id="visibility" name="eye" morph></glyph-fox>

<script type="module">
  const eye = document.getElementById('visibility');
  eye.addEventListener('click', () => {
    eye.setAttribute('name', eye.getAttribute('name') === 'eye' ? 'eye-off' : 'eye');
  });
</script>

Hover und Klick werden am Element selbst erfasst. Die CSS-Regeln der Animationen liegen im Shadow-Root, wo sie das Elternelement nicht erreichen, und der Klick-Listener sitzt am Host. Soll ein ganzer Button reagieren, steuern Sie das Icon vom Button aus: Verwenden Sie trigger="state" und setzen Sie active in den Handlern des Buttons.

save-button.html
<button type="button" class="save">
  <glyph-fox name="save" animate="pop" trigger="state"></glyph-fox>
  Save
</button>

<script type="module">
  const button = document.querySelector('.save');
  const icon = button.querySelector('glyph-fox');
  button.addEventListener('pointerenter', () => icon.setAttribute('active', ''));
  button.addEventListener('pointerleave', () => icon.removeAttribute('active'));
</script>

Morphs und Presets berücksichtigen prefers-reduced-motion: Übergänge springen direkt zum Ende, und Animationen werden nicht abgespielt. Die Presets und Auslöser sind unter animierte Icons beschrieben.

Statisches SVG in Lit-Templates

Ändert sich ein Icon nach dem Rendern nie, können Sie auf das Element verzichten und das SVG-Markup einfügen. renderToString aus glyphfox/core rendert Icon-Daten mit den gemeinsamen Props, und Lit fügt den String mit der Direktive unsafeHTML ein. Der Renderer escapt Titel und Attributwerte, daher lässt sich das Markup gefahrlos einfügen.

saved-badge.js
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { renderToString } from 'glyphfox/core';
import circleCheck from 'glyphfox/icons/circle-check';

const check = unsafeHTML(renderToString(circleCheck, { size: 16, color: '#16a34a' }));

export const savedBadge = () => html`<span class="badge">${check} Saved</span>`;

Für die Namenssyntax (file+check@tr, cloud&lock) verwenden Sie toSvg aus glyphfox. Die Funktion löst jeden Namen auf und enthält deshalb alle Icons; lesen Sie den Leitfaden zu Tree-Shaking, bevor Sie sie in einem Client-Bundle einsetzen.

Attribute

Alle gemeinsamen Props sind Attribute in Kebab-Case. Das Element ergänzt vier eigene: name für das Icon, mit der vollständigen Namenssyntax; layout und gap für Fusionen; sowie morph, das Änderungen von name animiert. Zahlen werden aus ihrem Text gelesen, boolesche Werte folgen der oben beschriebenen Regel.

AttributTypStandardBeschreibung
namestringkeinerDie Icon-Angabe: ein Name, name+modifier@corner, a&b:layout oder name/variant.
layoutoverlap, side, stack, grid, orbit, badge, insidekeinerLayout der Fusion, wenn der Name Icons mit & verbindet.
gapnumber1Aussparung einer Fusion, in Rastereinheiten.
morphbooleanfalseAnimiert jede Änderung von name als Morph.
sizenumber, string24Breite und Höhe; Zahlen sind Pixel.
colorstringcurrentColorHauptfarbe.
secondary-colorstringcolorFarbe der Duotone-Ebene und der Solid-Füllung.
secondary-opacitynumber0.3 / 1Deckkraft der Duotone-Ebene oder der Solid-Füllung.
weightnumber2Strichstärke von 1 bis 3 im 24er-Raster, unter 24 px optisch ausgeglichen.
absolute-weightbooleanfalseHält den Strich bei jeder Größe auf weight Pixeln.
cornersround, sharproundAbgerundete oder spitze Verbindungen und Enden.
variantoutline, duotone, solidoutlineStilachse; solid entsteht aus den geschlossenen Formen.
rotatenumber0Drehung in Grad.
flip-xbooleanfalseHorizontal spiegeln.
flip-ybooleanfalseVertikal spiegeln.
mirror-rtlbooleanfalseIn Layouts von rechts nach links spiegeln.
animatedraw, pop, wiggle, spin, pulse, bounce, shakekeinerVoreinstellung für den lebendigen Zustand.
triggermount, hover, click, statemountWann die Animation oder der Morph abläuft.
activebooleanfalseDer Zustand für den Auslöser state.
durationnumber (ms)je nach VoreinstellungDauer der Animation oder des Morphs.
delaynumber (ms)0Verzögerung der Animation oder des Morphs.
loopboolean, numberfalseEndlos oder eine bestimmte Anzahl von Malen wiederholen (spin und pulse laufen standardmäßig in Schleife).
modifierplus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dotkeinerBadge mit automatischer Aussparung.
modifier-cornertop-left, top-right, bottom-left, bottom-rightbottom-rightEcke des Badges.
morph-toname, datakeinerZiel des Morphs; der Auslöser bestimmt, wann es erscheint.
titlestringkeinerZugänglicher Name: title, role img und aria-label.
decorativebooleantrue ohne titleVerbirgt das Icon vor assistiven Technologien.

Barrierefreiheit

Ohne Label ist das Icon dekorativ: Das innere svg erhält aria-hidden="true". Das ist richtig für ein Icon neben sichtbarem Text und für einen Icon-Button, der ein eigenes aria-label trägt.

Um dem Icon selbst einen Namen zu geben, verwenden Sie das Attribut title. Es ergänzt das innere svg um ein <title>, role="img" und aria-label. Verlassen Sie sich nicht auf aria-label am Element: Das Label bleibt am Host, der keine Rolle hat, während das svg darin zu einem Bild ohne Namen wird. title am Host ist zugleich das globale HTML-Attribut, deshalb zeigen Browser es als Tooltip an.

index.html
<!-- decorative: the button carries the name -->
<button type="button" aria-label="Close dialog">
  <glyph-fox name="x"></glyph-fox>
</button>

<!-- meaningful on its own -->
<glyph-fox name="triangle-alert" title="Warning" color="#b45309"></glyph-fox>

Muster für Links, Status-Icons und Kontrast finden Sie im Leitfaden zur Barrierefreiheit.

Serverseitiges Rendering und Layoutverschiebungen

Das Element zeichnet das Icon beim Upgrade im Browser. Serverseitig gerendertes HTML enthält nur das leere Tag, und das Element nutzt kein deklaratives Shadow-DOM, daher erscheint das Icon erst, nachdem das Skript ausgeführt wurde. Reservieren Sie den Platz, damit sich das Layout in diesem Moment nicht verschiebt.

icons.css
glyph-fox:not(:defined) {
  display: inline-block;
  width: 24px;
  height: 24px;
  vertical-align: middle;
}
glyph-fox[size="16"]:not(:defined) {
  width: 16px;
  height: 16px;
}

Muss ein Icon im ersten Paint, in einer E-Mail oder im HTML für Crawler enthalten sein, rendern Sie stattdessen den SVG-String auf dem Server. Die Möglichkeiten für jedes Framework finden Sie unter serverseitiges Rendering.

Stolperfallen

Selbstschließende Tags
Schreiben Sie <glyph-fox name="home"></glyph-fox>. HTML ignoriert den Schrägstrich in <glyph-fox />, das Element bleibt also offen, und der nachfolgende Inhalt wird zu seinen Kindelementen, die der Shadow-Root nicht anzeigt.
Property-Bindings
.name=${...} in Lit und andere Property-Bindings setzen ein JavaScript-Feld, das das Element nicht ausliest. Binden Sie Attribute. Solid braucht aus demselben Grund attr:, siehe den Solid-Leitfaden.
Leere Attribute
Ein leeres size-Attribut rendert ein svg mit leerer Breite und Höhe. Entfernen Sie das Attribut, wenn kein Wert vorhanden ist, in Lit mit nothing oder ifDefined.
Hover und Klick am Elternelement
Hover- und Klick-Auslöser greifen nur am Icon, nicht am umgebenden Button. Verwenden Sie trigger="state" und setzen Sie active vom Elternelement aus.
Attributänderungen spielen Mount-Animationen erneut ab
Das Element tauscht sein inneres svg aus, sobald sich das Markup ändert. Mit einem animate-Preset und dem standardmäßigen trigger="mount" spielt deshalb jede Änderung von color oder size die Animation erneut ab. Verwenden Sie trigger="state" für Icons, deren übrige Attribute sich häufig ändern.
Ein leeres Icon
Ein unbekannter Name rendert nichts und schreibt eine glyphfox:-Warnung in die Konsole. Ein gültiger, aber nicht registrierter Name wird gezeichnet, sobald sein Modul geladen ist; prüfen Sie daher den Netzwerk-Tab, bevor Sie nach einem Tippfehler suchen.

Häufige Fragen

Kann ich das Element in React, Vue oder Angular verwenden?

Ja, es ist ein standardkonformes Custom Element. Dort passen die Framework-Pakete allerdings besser: glyphfox/react, glyphfox/vue und glyphfox-angular bieten typisierte Props und rendern auf dem Server. Das Element empfiehlt sich für Lit, reines HTML und Seiten mit gemischten Stacks. Teilen Sie dem Compiler in Vue-Templates über compilerOptions.isCustomElement mit, dass glyph-fox ein Custom Element ist, sonst versucht Vue, es als Komponente aufzulösen.

Icons in diesem Leitfaden