npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

safira-ui

v2.4.0

Published

Biblioteca CSS-first, acessível e progressiva para a web e React.

Readme

Safira UI

Web nativa. Interface clara.

Componentes com identidade visual, HTML semântico e CSS estático. Use em páginas simples ou adicione wrappers React quando eles realmente reduzirem trabalho.

License: MIT Node.js Version CSS first Accessibility


Menos dependência. Mais significado.

A Safira começa onde a web já funciona. O browser cuida da semântica e dos comportamentos nativos; o CSS cria uma identidade coerente; React entra como uma camada opcional de conveniência.

O resultado é uma biblioteca que reduz cerimônia sem esconder a plataforma. Você consegue ler o HTML, personalizar a cascata e entender o que chegará às pessoas e às tecnologias assistivas.

| Princípio | O que significa na prática | | ------------------------ | ---------------------------------------------------------------------------------------- | | CSS-first | O pacote visual é um arquivo CSS estático, sem injeção de estilos ou runtime. | | HTML nativo | Componentes mantêm elementos, semântica e comportamentos oferecidos pelo browser. | | API pequena | O consumidor informa intenção; a biblioteca automatiza IDs, relações e defaults seguros. | | Acessível por padrão | Foco, teclado, contraste, movimento reduzido e cores forçadas fazem parte do núcleo. | | Progressiva | Funciona em HTML puro e oferece wrappers opcionais e tipados para React. |

O diferencial em uma frase

A Safira agiliza a construção da interface sem transformar HTML, CSS e acessibilidade em detalhes internos.

Sua linguagem visual segue um minimalismo técnico: paleta contida, espaço para leitura, tipografia funcional e movimento usado apenas para explicar mudanças. As decisões completas estão em visual-identity.md.

Instalação

Requisitos

  • Node.js 20.19 ou superior;
  • um projeto web com npm;
  • React 18 ou superior apenas se você quiser usar os wrappers React.

React não é obrigatório. O CSS e as classes da Safira podem ser usados em HTML puro, Vite ou qualquer outro bundler que aceite importação de CSS.

1. Adicione o pacote

Na raiz do seu projeto, execute:

npm install safira-ui

2. Carregue os estilos

Importe o CSS uma única vez no arquivo de entrada da aplicação, normalmente main.ts, main.tsx, app.ts ou app.tsx:

import "safira-ui/styles.css";

Em um projeto Vite com React, o início do src/main.tsx pode ficar assim:

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "safira-ui/styles.css";
import { App } from "./App";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

Carregue a Safira antes do CSS da aplicação. Assim, classes próprias e utilitários do Tailwind têm prioridade sobre a pré-estilização da biblioteca:

import "safira-ui/styles.css";
import "./app.css";

Com Tailwind CSS v4, app.css pode começar normalmente com:

@import "tailwindcss";

3. Escolha a forma de uso

Para utilizar apenas o CSS, aplique as classes sf-* no HTML:

<button class="sf-button">Minha primeira ação</button>

Para utilizar a integração React, importe os componentes pelo submódulo safira-ui/react:

import { Button } from "safira-ui/react";

export function App() {
  return <Button>Minha primeira ação</Button>;
}

Se o botão aparecer com o estilo da Safira e apresentar um contorno visível ao receber foco pela tecla Tab, a instalação está funcionando.

Primeiros objetivos para quem está começando

Não é necessário conhecer todos os componentes de uma vez. Esta é a sequência recomendada para aprender a biblioteca construindo uma interface real:

| Objetivo | O que praticar | Resultado esperado | | ----------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------ | | 1. Estilizar HTML nativo | Use sf-button, variantes e atributos data-*. | Entender a API CSS sem depender de React. | | 2. Organizar o layout | Combine Stack, Cluster e Card. | Criar espaçamento consistente sem CSS repetitivo. | | 3. Montar um formulário | Use Field, labels, descrições e erros. | Construir entradas compreensíveis por teclado e leitor de tela. | | 4. Comunicar estados | Adicione Toast, Badge, loading e disabled. | Informar sucesso, aviso e erro sem depender apenas de cor. | | 5. Personalizar a marca | Sobrescreva tokens --sf-*. | Adaptar cores, fontes e raios sem alterar componentes. | | 6. Validar acessibilidade | Navegue com Tab, aplique zoom e teste tema escuro. | Confirmar que a experiência continua utilizável em diferentes condições. |

Primeiro exercício: uma ação acessível

Comece com um botão nativo e explore somente duas variações:

<div class="sf-cluster">
  <button class="sf-button">Salvar alterações</button>
  <button class="sf-button" data-variant="secondary">Cancelar</button>
</div>

Teste este exercício sem mouse:

  1. pressione Tab até chegar ao primeiro botão;
  2. confirme que o foco está claramente visível;
  3. pressione Enter ou Espaço para ativá-lo;
  4. aumente o zoom do navegador e confirme que os controles continuam legíveis.

Segundo exercício: um formulário com React

Depois, deixe a Safira cuidar das relações entre label, ajuda e campo:

import { Button, Field, Stack } from "safira-ui/react";

export function Entrar() {
  return (
    <form>
      <Stack>
        <Field
          label="E-mail"
          type="email"
          autoComplete="email"
          description="Use o endereço cadastrado na sua conta."
          required
        />
        <Button type="submit">Entrar</Button>
      </Stack>
    </form>
  );
}

Ao concluir esses dois exercícios, você já terá aprendido o fluxo principal: importar o CSS, usar HTML semântico, compor layouts e aproveitar a automação opcional do React.

Comece com HTML

Não é necessário inicializar JavaScript, provider ou sistema de temas:

<button class="sf-button">Continuar</button>

<button class="sf-button" data-variant="secondary">Voltar</button>

<details class="sf-accordion">
  <summary class="sf-accordion__summary">Como funciona?</summary>
  <div class="sf-accordion__content">O próprio browser controla esta interação.</div>
</details>

Para páginas sem bundler, use o arquivo compilado dist/safira.css distribuído pelo pacote.

Use a mesma base no React

Os wrappers React não usam CSS-in-JS. Eles renderizam HTML nativo com as mesmas classes públicas:

import "safira-ui/styles.css";
import { Button, Field, Stack } from "safira-ui/react";

export function Cadastro() {
  return (
    <form>
      <Stack>
        <Field label="E-mail" type="email" description="Usaremos apenas para contato." required />

        <Button type="submit">Criar conta</Button>
      </Stack>
    </form>
  );
}

Neste exemplo, Field cria e conecta automaticamente o id, o label, a descrição e um eventual erro por meio de aria-describedby.

Componentes

| Componente | Elemento ou recurso nativo | Responsabilidade | | ------------- | -------------------------- | ------------------------------------------------------------ | | Button | <button> | Variantes, tamanhos e carregamento acessível. | | Field | <label> + <input> | Nome acessível, ajuda, erro e validação. | | Toast | <div> | Notificação visual, anúncio acessível e fechamento opcional. | | ToastRegion | <div> | Posicionamento e empilhamento visual de notificações. | | Badge | <span> | Estado textual compacto. | | Card | <article> | Agrupamento semântico de conteúdo. | | Stack | <div> | Composição vertical com escala de espaçamento. | | Cluster | <div> | Composição horizontal responsiva. | | Grid | <div> | Grade fixa ou fluida com espaçamento por tokens. | | Image | <img> | Imagem responsiva com fontes de alta resolução. | | Accordion | <details> + <summary> | Disclosure sem JavaScript da biblioteca. | | CodeBlock | <pre> + <code> | Exemplos HTML/TS com realce, cópia e expansão acessível. | | Modal | <dialog> | Diálogo modal, foco nativo e fechamento acessível. | | Popover | Popover API | Abertura, fechamento e light dismiss declarativos. | | SkipLink | <a> | Salto direto para o conteúdo principal. |

Cada componente visual também pode ser usado diretamente por sua classe sf-*.

Toast visual

Toast cuida da apresentação e da semântica de uma notificação, sem impor provider, fila global ou temporizador. O consumidor mantém o controle sobre quando a mensagem aparece e desaparece:

import { useState } from "react";
import { Toast, ToastRegion } from "safira-ui/react";

export function Notifications() {
  const [visible, setVisible] = useState(true);

  return visible ? (
    <ToastRegion position="top-end">
      <Toast title="Perfil salvo" tone="success" onDismiss={() => setVisible(false)}>
        Suas alterações já estão disponíveis.
      </Toast>
    </ToastRegion>
  ) : null;
}

Quando onDismiss é informado, o toast exibe uma barra regressiva e desaparece após 5 segundos. Altere esse tempo com duration={8000} ou use duration={0} para desativar o fechamento automático. O progresso e o temporizador pausam enquanto o usuário mantém o ponteiro ou o foco dentro da notificação.

Os tons info, success e warning usam role="status"; danger usa role="alert". Use live={false} quando o conteúdo já tiver sido anunciado por outro meio. Em HTML puro, combine sf-toast-region, sf-toast, data-position e data-tone; JavaScript só é necessário para ações como fechar ou remover automaticamente.

Accordion com uma única tag

O consumidor utiliza somente Accordion; a Safira cria internamente os elementos nativos <details> e <summary>. className e styles são objetos que personalizam cada parte do componente:

const [open, setOpen] = useState(false);

<Accordion
  summary="Como funciona?"
  open={open}
  disabled={false}
  animationDuration={300}
  className={{
    container: "minha-borda",
    button: "meu-resumo",
    chevron: "meu-chevron",
    content: "meu-conteudo",
  }}
  styles={{
    container: { borderRadius: "1rem" },
    button: { fontWeight: 800 },
    chevron: { color: "rebeccapurple" },
    content: { padding: "1rem" },
  }}
  onChange={(event) => setOpen(event.open)}
  onOpen={(event) => console.log("Abriu", event)}
  onClose={(event) => console.log("Fechou", event)}
>
  Conteúdo do accordion
</Accordion>;

Quando open é informado, o componente é controlado e espera que onChange atualize o estado. Sem open, <details> controla a abertura nativamente. animationDuration é medida em milissegundos e aceita qualquer número igual ou maior que zero.

Blocos de código reutilizáveis

CodeBlock apresenta exemplos em HTML, TypeScript e terminal com realce de sintaxe, abas acessíveis, botão SVG para copiar e um accordion fechado por padrão:

import { CodeBlock } from "safira-ui/react";

<CodeBlock
  html={'<button class="sf-button">Salvar</button>'}
  ts={`import { Button } from "safira-ui/react";

<Button>Salvar</Button>`}
/>;

Use defaultLanguage="ts" para iniciar em TypeScript, terminal="npm install safira-ui" para comandos, defaultExpanded para começar aberto e animationDuration={300} para ajustar a transição. Defina collapsible={false} quando o código deve permanecer visível sem accordion. Os eventos onCodeCopy e onLanguageChange permitem integrar métricas ou feedback próprios. O onCopy nativo continua disponível. Se somente um formato for informado, o componente mostra apenas aquele formato.

Grid responsiva

Use colunas fixas para estruturas controladas ou minItemWidth para permitir que o CSS escolha quantos itens cabem em cada linha:

import { Card, Grid } from "safira-ui/react";

<Grid columns={3} minItemWidth="14rem" gap={4}>
  <Card>Primeiro</Card>
  <Card>Segundo</Card>
  <Card>Terceiro</Card>
</Grid>;

Quando minItemWidth é informado, a Grid usa auto-fit e se adapta sem JavaScript. columnGap e rowGap podem sobrescrever o gap em cada eixo. Em HTML puro, use sf-grid, data-fluid="true" e a variável --sf-grid-min-item.

Imagens nítidas e responsivas

Image torna alt obrigatório e gera um srcSet a partir de fontes reais. O navegador escolhe a resolução apropriada para o tamanho renderizado e para a densidade da tela:

import { Image } from "safira-ui/react";

<Image
  src="foto-640.webp"
  alt="Equipe reunida"
  sources={[
    { src: "foto-640.webp", width: 640 },
    { src: "foto-1280.webp", width: 1280 },
  ]}
  sizes="(max-width: 40rem) 100vw, 40rem"
  aspectRatio="16 / 9"
  fit="cover"
/>;

Para o caso simples de uma imagem 2x, use highResolutionSrc. A Safira não inventa detalhes ausentes em um arquivo pequeno: a melhoria de nitidez acontece porque o componente entrega ao browser uma fonte maior. loading="lazy" e decoding="async" são os defaults e podem ser sobrescritos para imagens prioritárias.

Modal nativo e acessível

Modal utiliza <dialog> e mantém abertura modal, foco, Escape e retorno ao elemento acionador sob responsabilidade do browser. O título é obrigatório para garantir um nome acessível:

import { Button, Modal } from "safira-ui/react";

<Modal
  id="confirmar-publicacao"
  label="Publicar"
  title="Confirmar publicação"
  description="O conteúdo ficará visível para todas as pessoas."
  actions={<Button>Confirmar</Button>}
  onOpenChange={(open) => console.log(open)}
>
  Revise as informações antes de continuar.
</Modal>;

Use open para controle externo, defaultOpen para iniciar aberto e closeOnBackdrop={false} quando o fluxo exigir uma decisão explícita. className e styles aceitam as partes trigger, dialog, header, title, description, content, actions e close.

Em HTML, a abertura pode permanecer declarativa com commandfor e command="show-modal"; o fechamento funciona com um formulário method="dialog".

Personalização com className, Tailwind e unstyled

As variantes da Safira fornecem identidade visual, não bloqueiam o consumidor. Os wrappers preservam className, style, id, atributos data-*, eventos e demais atributos nativos:

<Button id="salvar-perfil" className="rounded-full bg-violet-600 px-8 hover:bg-violet-700" data-origin="perfil">
  Salvar
</Button>

Quando a aparência da Safira não for desejada, use unstyled. Essa variante remove fundo, borda, raio, peso, tamanho mínimo, padding e transições visuais, mas preserva o elemento nativo, foco visível, estado desabilitado, loading e composição dos ícones:

<Button variant="unstyled" className="rounded-md bg-emerald-600 px-4 py-2 font-semibold text-white">
  Publicar
</Button>

Em HTML, o mesmo contrato usa data-variant="unstyled":

<button class="sf-button minha-acao" data-variant="unstyled">Publicar</button>

As classes do consumidor vencem por cascade quando o CSS da Safira é carregado primeiro. Propriedades inline em style sempre têm prioridade. Defaults semânticos e guardrails acessíveis continuam intencionais: por exemplo, Button usa type="button" por padrão e loading mantém o controle desabilitado e expõe aria-busy.

Posicionamento do Popover

Informe o lado preferido com placement. A Safira usa CSS Anchor Positioning para manter o popover junto ao botão e permite que o browser inverta o lado quando o conteúdo ultrapassaria a área visível:

<Popover id="acoes-do-perfil" label="Abrir ações" placement="bottom" title="Ações do perfil">
  Conteúdo do popover
</Popover>

Os valores aceitos são top, right, bottom e left. O valor é uma preferência, não uma posição rígida: por exemplo, um popover configurado como bottom poderá abrir acima quando estiver próximo ao final da viewport.

Em HTML puro, use o mesmo contrato declarativo:

<button class="sf-button" popovertarget="acoes">Abrir ações</button>
<div id="acoes" class="sf-popover" data-placement="bottom" popover>Conteúdo do popover</div>

Progressive UI

A ordem de decisão da Safira é simples:

  1. HTML nativo para estrutura, semântica e comportamento.
  2. CSS para aparência, layout, estados e movimento.
  3. JavaScript apenas quando a plataforma não entrega a interação necessária.
  4. React como conveniência opcional, nunca como fundação visual.

Isso mantém o CSS utilizável em qualquer stack e evita enviar comportamento duplicado ao browser.

Temas e tokens

O site também possui um guia dedicado na rota /theme, com configuração para React, Next.js e Vite.

Ative o tema escuro com um atributo:

<html data-theme="dark"></html>

Todos os tokens públicos usam custom properties --sf-*. Personalize-os globalmente ou dentro de um escopo:

.minha-marca {
  --sf-color-primary: #6d28d9;
  --sf-color-primary-hover: #5b21b6;
  --sf-radius-md: 0.25rem;
  --sf-font-sans: "Minha Fonte", system-ui, sans-serif;
}

Acessibilidade

A Safira inclui no núcleo:

  • navegação completa por teclado;
  • foco visível com :focus-visible;
  • nomes, descrições e erros associados aos controles;
  • suporte a prefers-reduced-motion;
  • estilos para forced-colors e alto contraste;
  • estados importantes que não dependem apenas de cor;
  • controles com áreas de interação confortáveis;
  • HTML semântico antes de ARIA customizado.

A biblioteca fornece uma base segura, mas o produto final ainda precisa cuidar da ordem dos headings, textos, nomes das ações, fluxo de foco e testes com pessoas e tecnologias assistivas.

Documentação legível por pessoas e IA

O site foi estruturado para reduzir contexto implícito:

  • cada página possui um propósito e um h1 claro;
  • cada componente apresenta finalidade, elemento nativo, exemplo HTML, exemplo TypeScript e propriedades;
  • código permanece como texto real e copiável;
  • metadados descrevem pacote, versão, licença e entrypoints;
  • llms.txt oferece um resumo estático para agentes e indexadores;
  • visual-identity.md registra voz, semântica visual e critérios editoriais.

Conteúdo IA-friendly não substitui acessibilidade nem revisão humana. A mesma informação precisa continuar compreensível por teclado, leitor de tela, zoom, texto ampliado e leitura linear.

Estrutura do projeto

safira-ui/
├── src/
│   ├── styles/index.css    # tokens, reset e componentes CSS
│   ├── react/              # componentes com implementação, tipos e testes colocados juntos
│   └── test/               # configuração dos testes
├── docs/                   # documentação e laboratório em Vite
├── scripts/                # tarefas de build reproduzíveis
├── dist/                   # pacote compilado, gerado pelo build
└── site-dist/              # site estático, gerado pelo build

Desenvolvimento

Requer Node.js 20.19 ou superior.

npm install
npm run dev

O servidor local abre a documentação e o laboratório de componentes.

Antes de enviar uma alteração, execute:

npm run check

Esse comando valida lint, TypeScript, testes, pacote da biblioteca e site da documentação.

Scripts úteis

| Comando | Resultado | | -------------------- | ----------------------------------------------------------- | | npm run dev | Inicia a documentação em modo de desenvolvimento. | | npm run lint | Verifica as regras estáticas. | | npm run typecheck | Valida os tipos sem emitir arquivos. | | npm test | Executa os testes com Vitest. | | npm run build | Gera CSS, ESM, CommonJS e declarações. | | npm run build:docs | Gera o site estático em site-dist. | | npm run check | Executa toda a esteira de qualidade. | | npm run 1 | Abre o login web do npm. | | npm run 2 | Confirma qual conta npm está autenticada. | | npm run 3 | Valida o projeto e simula o conteúdo do pacote. | | npm run 4 | Publica no npm a versão atual registrada no package.json. |

Publicação no npm

Atualize a versão, revise as alterações e faça o commit antes da publicação. Depois execute os quatro passos em ordem:

npm run 1
npm run 2
npm run 3
npm run 4

O terceiro passo executa a esteira completa e mostra exatamente quais arquivos entrarão no pacote. Confira nome, versão e conteúdo antes de seguir para a publicação. Códigos de autenticação devem ser informados diretamente no terminal, nunca em chats ou arquivos.

Para publicar uma versão de pré-lançamento com outra tag:

npm run 4 -- --tag next

Saída do pacote

  • dist/safira.css — núcleo visual estático;
  • dist/react.js — wrappers React em ESM;
  • dist/react.cjs — wrappers React em CommonJS;
  • dist/**/*.d.ts — declarações TypeScript.

Contribuição

Leia AGENTS.md antes de alterar componentes, tokens ou configuração de build. Para conteúdo, campanhas e documentação, preserve também as decisões de visual-identity.md.

Licença

Distribuído sob a licença MIT.

CSS primeiro. Pessoas sempre.