Ícones no Vue 3
Cada ícone do glyphfox é um componente Vue 3 com as mesmas props de todos os outros pacotes do glyphfox. Este guia cobre a instalação, as props nos templates, ícones por nome, morph, registro global, TypeScript e os erros que deixam um ícone vazio.
Instalação
npm install glyphfox
O glyphfox não tem dependências. O Vue 3.2 ou mais recente é uma peer dependency opcional, necessária só quando você importa glyphfox/vue. O mesmo pacote também traz os builds para React, Svelte e web component, então não há mais nada para instalar.
Cada ícone é um componente com o nome do ícone em PascalCase e o sufixo Icon: arrow-right vira ArrowRightIcon, x vira XIcon. O explorador de ícones mostra o nome de cada ícone e copia o trecho de código Vue para você.
Use os componentes
<script setup> import { HomeIcon, SearchIcon, BellIcon } from 'glyphfox/vue'; defineProps({ unread: { type: Number, default: 0 } }); </script> <template> <nav class="toolbar"> <a href="/" aria-label="Home"><HomeIcon /></a> <SearchIcon :size="20" :weight="1.5" class="muted" /> <BellIcon variant="duotone" secondary-color="#ff7a1a" :modifier="unread ? 'dot' : undefined" /> </nav> </template>
As props são declaradas em camelCase, e o Vue aceita as duas grafias nos templates: secondary-color e secondaryColor chegam à mesma prop. Para números, use bind, como em :size="20"; um size="20" simples também funciona, porque size, weight, rotate, duration e as outras props numéricas aceitam strings.
Os componentes não declaram class, style nem listeners, então o Vue os repassa para o <svg> raiz. As suas classes são mescladas com as classes embutidas glyphfox glyphfox-NAME, e @click, aria-*, data-* e id vão parar no próprio svg.
Os traços usam currentColor, então um ícone assume o color do CSS do elemento pai e acompanha estados de hover e temas sem esforço nenhum. A prop color define a cor no svg quando você precisa de uma cor fixa. O svg recebe os atributos width e height a partir de size (24 por padrão); width e height definidos no CSS por uma classe têm prioridade sobre eles.
Peso, cantos e estilo
Três props mudam o próprio desenho, e não a caixa em volta dele: weight define o traço de 1 a 3, corners alterna entre junções redondas e retas, e variant escolhe outline, duotone ou solid. Abaixo de 24 px o traço fica um pouco mais grosso para que ícones pequenos não pareçam apagados; absolute-weight mantém exatamente o traço que você pediu.
<script setup> import { FolderIcon } from 'glyphfox/vue'; </script> <template> <FolderIcon :weight="1" /> <FolderIcon :weight="3" corners="sharp" /> <FolderIcon variant="duotone" secondary-color="#ff7a1a" /> <FolderIcon variant="solid" /> <FolderIcon :size="16" :weight="1.5" absolute-weight /> </template>
O estilo sólido é gerado no build a partir das formas fechadas de cada ícone, com as linhas internas recortadas como espaço negativo. Ícones sem formas fechadas mantêm o contorno quando você pede solid.
Ícones por nome, modificadores e fusão
Icon renderiza qualquer spec da sintaxe de nomes: um nome simples, um selo modificador (file+check, com @tl, @tr, @bl ou @br para o canto), uma fusão (cloud&lock:overlap) ou um estilo (heart/solid). Fusion recebe uma lista de nomes e um layout. Os dois são úteis quando o ícone vem de dados, como a definição de um menu, um campo de status ou um CMS.
Componentes como FileIcon trazem os próprios dados. Um nome dentro de uma string não tem nenhum import por trás, então registre os dados uma vez com registerIcons. O registro é compartilhado por Icon, Fusion, MorphIcon e toda prop morph-to, então um único módulo cobre o app inteiro. Os selos modificadores são desenhados pelo motor e não precisam de registro.
import { registerIcons } from 'glyphfox/vue'; import file from 'glyphfox/icons/file'; import cloud from 'glyphfox/icons/cloud'; import lock from 'glyphfox/icons/lock'; import menu from 'glyphfox/icons/menu'; import x from 'glyphfox/icons/x'; import pause from 'glyphfox/icons/pause'; // Every name used in a string: Icon, Fusion, MorphIcon and morph-to. registerIcons([file, cloud, lock, menu, x, pause]);
import { createApp } from 'vue'; import App from './App.vue'; import './icons.js'; createApp(App).mount('#app');
<script setup> import { Icon, Fusion } from 'glyphfox/vue'; defineProps({ saved: Boolean }); </script> <template> <Icon :name="saved ? 'file+check' : 'file'" /> <Icon name="file+alert@tr" variant="duotone" /> <Fusion :icons="['cloud', 'lock']" layout="overlap" :gap="1" /> </template>
Uma fusão é um ícone de verdade: ela segue weight, variant e todas as outras props e, onde as partes se sobrepõem, a de trás é recortada em volta da da frente. Os layouts são overlap, side, stack, grid, orbit, badge e inside; scale e offsets fazem o ajuste fino das partes.
Morph entre ícones
Há três formas de fazer morph. MorphIcon anima sempre que o name muda. Qualquer componente de ícone aceita morph-to com um gatilho (mount, hover, click ou state). E o composable useMorph devolve os frames animados para a sua própria renderização.
<script setup> import { ref } from 'vue'; import { MorphIcon } from 'glyphfox/vue'; const open = ref(false); </script> <template> <button type="button" aria-label="Menu" :aria-expanded="open" @click="open = !open"> <MorphIcon :name="open ? 'x' : 'menu'" :duration="300" /> </button> </template>
<script setup> import { HeartIcon, PlayIcon } from 'glyphfox/vue'; defineProps({ playing: Boolean }); </script> <template> <!-- toggles on every click --> <HeartIcon morph-to="heart/solid" trigger="click" /> <!-- shows pause while playing is true --> <PlayIcon morph-to="pause" trigger="state" :active="playing" /> </template>
useMorph aceita uma ref, um getter ou um valor simples e devolve um frame computado { icon, variant } que anima sempre que a spec muda. Passe o frame para Icon pela prop icon ou entregue ao seu próprio renderizador.
<script setup> import { ref } from 'vue'; import { Icon, useMorph } from 'glyphfox/vue'; const playing = ref(false); const frame = useMorph(() => (playing.value ? 'pause' : 'play'), { duration: 250 }); </script> <template> <button type="button" aria-label="Play" :aria-pressed="playing" @click="playing = !playing"> <Icon v-if="frame" :icon="frame.icon" :variant="frame.variant" /> </button> </template>
Pares ajustados à mão deixam as transições mais comuns com cara de design: menu para x, play para pause, plus para check, sun para moon, eye para eye-off, bell para bell-ring, heart para heart/solid, copy para check, lock para unlock e wifi para wifi-off. Qualquer outro par também se transforma, com um pareamento automático. O guia de ícones animados cobre durações, atrasos e movimento reduzido.
Estados vivos
A prop animate adiciona um preset que entende o ícone: draw desenha os traços na ordem do desenho, shake balança a partir do topo, e pop, wiggle, spin, pulse e bounce fazem o que o nome diz. A animação é uma pequena folha de estilo dentro do svg, então não precisa de nenhum import de CSS e é desativada para visitantes que preferem movimento reduzido.
<script setup> import { BellIcon, LoaderCircleIcon, SparklesIcon } from 'glyphfox/vue'; defineProps({ hasNews: Boolean, loading: Boolean }); </script> <template> <BellIcon animate="shake" trigger="state" :active="hasNews" /> <LoaderCircleIcon v-if="loading" animate="spin" /> <SparklesIcon animate="draw" trigger="hover" /> </template>
Registro global
Importar os ícones onde você os usa mantém cada bundle pequeno. Quando um design system precisa de um conjunto fixo em todos os templates, registre esses componentes uma vez com app.component. Dê ao Icon um nome distinto, como GfIcon, para que ele não entre em conflito com outro componente global.
import { createApp } from 'vue'; import { HomeIcon, SearchIcon, SettingsIcon, Icon } from 'glyphfox/vue'; import App from './App.vue'; import './icons.js'; const app = createApp(App); app.component('HomeIcon', HomeIcon); app.component('SearchIcon', SearchIcon); app.component('SettingsIcon', SettingsIcon); app.component('GfIcon', Icon); app.mount('#app');
Com TypeScript, declare os componentes globais para que os templates sejam verificados:
import type { HomeIcon, SearchIcon, SettingsIcon, Icon } from 'glyphfox/vue'; declare module 'vue' { interface GlobalComponents { HomeIcon: typeof HomeIcon; SearchIcon: typeof SearchIcon; SettingsIcon: typeof SettingsIcon; GfIcon: typeof Icon; } } export {};
TypeScript
Todo componente é tipado como GlyphComponent com as props compartilhadas, então os editores completam variant, modifier, animate e o resto. Nos tipos do Vue, as specs de ícone são strings simples, porque a inferência de props do Vue não consegue expandir a união completa de nomes de ícones. Quando você guarda nomes no seu próprio código, use o tipo IconName de glyphfox para pegar erros de digitação em tempo de compilação.
import type { IconName } from 'glyphfox'; export const nav: { label: string; to: string; icon: IconName }[] = [ { label: 'Home', to: '/', icon: 'home' }, { label: 'Search', to: '/search', icon: 'search' }, { label: 'Settings', to: '/settings', icon: 'settings' }, ];
Imports profundos e tamanho do bundle
Imports nomeados de glyphfox/vue passam por tree shaking: os módulos de ícone são marcados como livres de efeitos colaterais e cada componente é criado com uma chamada pure. Num build de produção do Vite 8 (sem contar o próprio Vue), um componente de ícone mais o runtime compartilhado somam cerca de 53 KB minificados, 20 KB com gzip, e mais nove ícones acrescentam menos de 1 KB com gzip. O runtime traz o renderizador e os motores de morph, fusão e modificadores, e é pago uma única vez.
<script setup> // One module, for tools that do not tree shake. import { FoxIcon } from 'glyphfox/vue/icons/fox'; </script> <template> <FoxIcon :size="32" variant="duotone" title="glyphfox" /> </template>
O guia de tree shaking explica o que mantém o conjunto fora do seu bundle e o que o traz de volta.
Props
Todos os componentes de ícone, além de Icon, Fusion e MorphIcon, aceitam as props compartilhadas. Nos templates você pode escrever o atributo em kebab-case da segunda coluna.
| Prop | Nos templates | Tipo | Padrão | Descrição |
|---|---|---|---|---|
size | size | number, string | 24 | Largura e altura; números são pixels. |
color | color | string | currentColor | Cor principal. |
secondaryColor | secondary-color | string | color | Cor da camada duotone e do preenchimento sólido. |
secondaryOpacity | secondary-opacity | number | 0.3 / 1 | Opacidade da camada duotone ou do preenchimento sólido. |
weight | weight | number | 2 | Traço de 1 a 3 na grade de 24, com compensação óptica abaixo de 24 px. |
absoluteWeight | absolute-weight | boolean | false | Mantém o traço com weight pixels em qualquer tamanho. |
corners | corners | round, sharp | round | Junções e pontas redondas ou retas. |
variant | variant | outline, duotone, solid | outline | Eixo de estilo; o sólido vem das formas fechadas. |
rotate | rotate | number | 0 | Rotação em graus. |
flipX | flip-x | boolean | false | Espelha na horizontal. |
flipY | flip-y | boolean | false | Espelha na vertical. |
mirrorRtl | mirror-rtl | boolean | false | Espelha em layouts da direita para a esquerda. |
animate | animate | draw, pop, wiggle, spin, pulse, bounce, shake | nenhum | Animação de estado vivo. |
trigger | trigger | mount, hover, click, state | mount | Quando a animação ou o morph acontece. |
active | active | boolean | false | O estado para o gatilho state. |
duration | duration | number (ms) | por animação | Duração da animação ou do morph. |
delay | delay | number (ms) | 0 | Atraso da animação ou do morph. |
loop | loop | boolean, number | false | Repete para sempre ou um número de vezes (spin e pulse repetem por padrão). |
modifier | modifier | plus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dot | nenhum | Selo com espaço de recorte automático. |
modifierCorner | modifier-corner | top-left, top-right, bottom-left, bottom-right | bottom-right | Canto do selo. |
morphTo | morph-to | name, data | nenhum | Destino do morph; o gatilho decide quando aparece. |
title | title | string | nenhum | Nome acessível: title, role img e aria-label. |
decorative | decorative | boolean | true sem title | Esconde o ícone das tecnologias assistivas. |
Icon acrescenta name, icon (dados de ícone) e gap. Fusion acrescenta icons, layout, gap, scale e offsets. MorphIcon acrescenta name e icon. Um slot padrão é renderizado dentro do svg.
Armadilhas comuns
- Um ícone usado por nome fica vazio em produção
- O servidor de desenvolvimento faz o pre-bundle do pacote inteiro, então todo nome é resolvido enquanto você desenvolve. Um build de produção mantém só o que você importa, e o Vite não inclui no bundle o carregador sob demanda que fica em node_modules: nomes não registrados em
Icon,Fusion,MorphIconoumorph-torenderizam um svg vazio. Registre esses nomes comregisterIcons, como emsrc/icons.jsacima. - Botões só com ícone e sem nome acessível
- Os ícones são decorativos por padrão e recebem
aria-hidden="true". Por isso, um botão que contém só um ícone fica sem nome acessível. Coloquearia-labelno botão ou dê umtitleao ícone. Veja mais padrões no guia de acessibilidade. - morph-to dispara assim que a página carrega
- O gatilho padrão é mount: o ícone se transforma no destino logo depois de montado. Quando o destino é o estado que você quer mostrar logo de cara, use
trigger="state"com:active, que já começa no estado certo, sem animação. - Props booleanas escritas como strings
active="false"passa a string "false". O glyphfox lê isso como false, mas o Vue emite um aviso sobre o tipo da prop. Escreva as props booleanas sem valor (flip-x) ou use bind (:active="playing").- Imports com asterisco
import * as icons from 'glyphfox/vue'seguido deicons[name]mantém todos os ícones no bundle, porque o bundler não tem como saber quais nomes você lê. Em vez disso, monte um pequeno mapa com os componentes de que você precisa.
Perguntas frequentes
O glyphfox funciona com Vue 2?
Os componentes precisam do Vue 3.2 ou mais recente. Num app Vue 2, use o web component <glyph-fox>, que aceita as mesmas props como atributos; adicione glyph-fox a Vue.config.ignoredElements para o Vue não mexer na tag. Veja o guia de web components.
Posso usar os componentes com a Options API?
Sim. Eles são componentes comuns: liste-os em components: { HomeIcon } e use no template. useMorph é um composable, então o lugar dele é dentro de setup().
Meus próprios ícones SVG podem ter as mesmas props?
Sim. createIcon({ name, body, tags, categories }), de glyphfox/vue, transforma dados de ícone em um componente, em que body é o markup interno de um ícone de traço de 24 por 24. Antes, passe o seu SVG pelo Fox-ify no estúdio, para que ele siga as regras de grade, traço e margem das quais o peso e os outros eixos dependem.