Ícones no SolidJS
Ainda não existe um pacote dedicado ao Solid, e ele não faz falta. O elemento <glyph-fox> funciona em templates do Solid, e um componente de poucas linhas sobre o glyphfox/core oferece ícones reativos, compatíveis com tree shaking, que renderizam no servidor.
Instalação
Instale o pacote principal. Tudo nesta página usa pontos de entrada que já vêm nele: glyphfox/web para o elemento, e glyphfox/core com glyphfox/icons/NAME para o componente.
npm install glyphfox
| Aspecto | Elemento glyph-fox | Componente Glyph |
|---|---|---|
| Renderiza | No navegador, quando o elemento faz o upgrade. | No servidor e no navegador. |
| Envia | O elemento, o motor dele e os ícones que você registra. | O renderizador e os módulos de ícone que você importa. |
| Movimento | Morph a cada troca de nome, gatilhos de hover e de clique, estados. | Movimento em CSS dentro do SVG: presets e morphs no hover. |
| Ideal para | Ícones que reagem ao usuário. | A maioria dos ícones de um app. |
O elemento glyph-fox
Registre o elemento uma vez, no seu arquivo de entrada. O glyphfox/web registra todos os ícones; para deixar no bundle só os ícones que você listar, use glyphfox/web/element com registerIcons, como mostra o guia de web components.
import { render } from 'solid-js/web'; import 'glyphfox/web'; import App from './App'; render(() => <App />, document.getElementById('root'));
Atributos estáticos são compilados no template, então chegam ao elemento como atributos e funcionam do jeito que estão escritos:
export function Toolbar() { return ( <div class="toolbar"> <glyph-fox name="settings" size="20" weight="1.5" /> <glyph-fox name="bell" animate="shake" trigger="hover" /> </div> ); }
Com valores dinâmicos é diferente. Em custom elements, o Solid atribui expressões como propriedades, e o <glyph-fox> não tem propriedades: ele lê atributos. Coloque o namespace attr: antes das props dinâmicas para que o Solid defina o atributo em vez da propriedade.
import { createSignal } from 'solid-js'; export function MenuButton() { const [open, setOpen] = createSignal(false); return ( <button type="button" aria-expanded={open()} aria-label={open() ? 'Close menu' : 'Open menu'} onClick={() => setOpen(!open())} > <glyph-fox attr:name={open() ? 'x' : 'menu'} morph duration="300" /> </button> ); }
O atributo morph faz o elemento animar toda troca de name, então cada alternância do signal toca o morph de menu para fechar, ajustado à mão. Eventos não precisam de nada especial: onClick em um elemento pai funciona normalmente, e gatilhos como trigger="click" são tratados dentro do elemento.
TypeScript
Os tipos JSX do Solid não conhecem o glyph-fox. Declare-o uma vez em um arquivo de declaração e liste os atributos que você define via attr: em ExplicitAttributes, a interface que o Solid lê para esse namespace.
import 'solid-js'; type GlyphFoxAttributes = { name?: string; size?: string | number; color?: string; weight?: string | number; variant?: 'outline' | 'duotone' | 'solid'; animate?: 'draw' | 'pop' | 'wiggle' | 'spin' | 'pulse' | 'bounce' | 'shake'; trigger?: 'mount' | 'hover' | 'click' | 'state'; morph?: boolean | string; 'morph-to'?: string; duration?: string | number; }; declare module 'solid-js' { namespace JSX { interface IntrinsicElements { 'glyph-fox': JSX.HTMLAttributes<HTMLElement> & GlyphFoxAttributes; } interface ExplicitAttributes { name: string; size: string | number; active: boolean | string; } } }
Um pequeno componente Glyph
Para ícones que não precisam da interação embutida no elemento, renderize o SVG você mesmo. O renderToString de glyphfox/core transforma dados de ícone em marcação com todas as props compartilhadas, e cada módulo glyphfox/icons/NAME guarda os dados de um ícone, então o bundle leva só o que você importa.
import { splitProps } from 'solid-js'; import { renderToString } from 'glyphfox/core'; export function Glyph(props) { const [local, options] = splitProps(props, ['icon', 'class']); return ( <span class={local.class} style={{ display: 'inline-flex', 'line-height': '0' }} innerHTML={renderToString(local.icon, options)} /> ); }
import { Glyph } from './Glyph'; import home from 'glyphfox/icons/home'; import bell from 'glyphfox/icons/bell'; export function Nav(props) { return ( <nav> <a href="/"> <Glyph icon={home} size={20} /> Home </a> <button type="button" aria-label="Notifications"> <Glyph icon={bell} animate="shake" trigger="state" active={props.unread > 0} /> </button> </nav> ); }
O efeito por trás de innerHTML rastreia todas as props que o renderToString lê, então uma mudança em active, size ou icon reescreve a marcação. A marcação é substituída, não interpolada. Para um morph entre dois ícones, use o elemento ou um morph CSS no hover com morphTo:
import { Glyph } from './Glyph'; import arrowRight from 'glyphfox/icons/arrow-right'; import chevronRight from 'glyphfox/icons/chevron-right'; export const NextLink = () => ( <a href="/next" class="next"> Next <Glyph icon={arrowRight} morphTo={chevronRight} trigger="hover" /> </a> );
Com glyphfox/core, morphTo recebe dados de ícone, não um nome: só o toSvg do ponto de entrada principal resolve nomes. Um nome passado aqui é ignorado, e o ícone fica parado. O morph CSS acontece enquanto o ponteiro está sobre o svg ou sobre o pai direto dele, que neste componente é o span que o envolve.
A sintaxe de nomes (file+check@tr, cloud&lock) também é resolvida pelo toSvg. Com o core, use no lugar dela as props e os helpers correspondentes: modifier e modifierCorner para selos, fuse para fusões.
import { fuse } from 'glyphfox/core'; import { Glyph } from './Glyph'; import file from 'glyphfox/icons/file'; import cloud from 'glyphfox/icons/cloud'; import lock from 'glyphfox/icons/lock'; const secureCloud = fuse([cloud, lock], { layout: 'overlap' }); export const Status = () => ( <p> <Glyph icon={file} modifier="check" modifierCorner="top-right" title="Saved" /> <Glyph icon={secureCloud} size={32} title="Encrypted backup" /> </p> );
SolidStart e renderização no servidor
O componente Glyph renderiza no servidor como qualquer outra marcação: o SolidStart escreve o SVG no HTML, então os ícones fazem parte da primeira pintura e o layout não se mexe quando a página é hidratada.
O elemento é um recurso do navegador. Ele faz o upgrade depois que o script dele roda e não usa shadow DOM declarativo, então o HTML do servidor contém a tag vazia. Reserve o espaço dele com CSS ou deixe no componente Glyph os ícones que precisam aparecer de imediato. Os prós e contras de cada framework estão em renderização no servidor.
glyph-fox:not(:defined) { display: inline-block; width: 24px; height: 24px; }
Importar glyphfox/web no servidor não causa problema: o elemento verifica se customElements existe e não faz nada sem ele. Mesmo assim, importe-o a partir de código do cliente, como src/entry-client.tsx no SolidStart, para que o elemento e os ícones dele fiquem fora do bundle do servidor.
Props
O componente Glyph repassa as props para o renderToString, então aceita as opções compartilhadas com os nomes em camelCase. No elemento, as mesmas props são atributos em kebab-case, listados no guia de web components.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
size | number, string | 24 | Largura e altura; números são pixels. |
color | string | currentColor | Cor principal. |
secondaryColor | string | color | Cor da camada duotone e do preenchimento sólido. |
secondaryOpacity | number | 0.3 / 1 | Opacidade da camada duotone ou do preenchimento sólido. |
weight | number | 2 | Traço de 1 a 3 na grade de 24, com compensação óptica abaixo de 24 px. |
absoluteWeight | boolean | false | Mantém o traço com weight pixels em qualquer tamanho. |
corners | round, sharp | round | Junções e pontas redondas ou retas. |
variant | outline, duotone, solid | outline | Eixo de estilo; o sólido vem das formas fechadas. |
rotate | number | 0 | Rotação em graus. |
flipX | boolean | false | Espelha na horizontal. |
flipY | boolean | false | Espelha na vertical. |
mirrorRtl | boolean | false | Espelha em layouts da direita para a esquerda. |
animate | draw, pop, wiggle, spin, pulse, bounce, shake | nenhum | Animação de estado vivo. |
trigger | mount, hover, click, state | mount | Quando a animação ou o morph acontece. |
active | boolean | false | O estado para o gatilho state. |
duration | number (ms) | por animação | Duração da animação ou do morph. |
delay | number (ms) | 0 | Atraso da animação ou do morph. |
loop | boolean, number | false | Repete para sempre ou um número de vezes (spin e pulse repetem por padrão). |
modifier | plus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dot | nenhum | Selo com espaço de recorte automático. |
modifierCorner | top-left, top-right, bottom-left, bottom-right | bottom-right | Canto do selo. |
morphTo | name, data | nenhum | Destino do morph; o gatilho decide quando aparece. |
title | string | nenhum | Nome acessível: title, role img e aria-label. |
decorative | boolean | true sem title | Esconde o ícone das tecnologias assistivas. |
Armadilhas comuns
- Props dinâmicas sem attr:
name={...}noglyph-foxdefine uma propriedade que o elemento ignora, e o ícone renderiza vazio. Escrevaattr:name={...}. Strings estáticas funcionam sem o prefixo.- Um nome em morphTo
- O
renderToStringdo core precisa de dados de ícone emmorphTo. Uma string é ignorada. Importe o ícone de destino e passe os dados dele. - Animações de montagem tocam de novo
- Cada mudança de prop substitui o
innerHTML, então um preset com o padrãotrigger="mount"toca de novo. Para animações ligadas a um estado, usetrigger="state"comactive. - Todos os ícones sem querer
import { toSvg } from 'glyphfox'traz todos os ícones para o bundle, porque um nome pode apontar para qualquer um deles. Em código do cliente, use o core com um módulo por ícone; deixe otoSvgpara código só de servidor.- O elemento no servidor
- O HTML do servidor fica com um
<glyph-fox>vazio até o script rodar. Reserve o tamanho dele ou renderize com o componente Glyph os ícones que precisam estar visíveis no HTML do servidor. - O span que envolve o svg
- O componente Glyph envolve o svg em um span: o gap de flex e de grid se aplica ao span, e
classvai para o span, não para o svg. Defina tamanhos pelas props em vez de usar CSS no span.
Perguntas frequentes
Existe um pacote glyphfox para Solid?
Ainda não. As duas abordagens desta página usam o que o pacote glyphfox já traz hoje: o custom element e o renderizador do core, que é a base dos pacotes de React, Vue e Svelte.
Os ícones se atualizam quando um signal muda?
Sim. Com o elemento, faça o binding via attr: e o elemento se redesenha a cada mudança de atributo, com morph quando o atributo morph está definido. Com o componente Glyph, o efeito por trás de innerHTML rastreia as props que o renderizador lê e reescreve a marcação.