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

@waler/ui

v3.5.0

Published

Design system Vue 3 da WALER, sobre Reka UI e Tailwind CSS v4

Readme

@waler/ui

Design system Vue 3 da WALER: 31 componentes acessíveis sobre Reka UI, estilizados com Tailwind CSS v4 e pintados pela paleta da marca.

ESM puro, tree-shakeable, tipado. Sem icon set, sem clsx, sem @vueuse/core. O culori só entra pelo subpath @waler/ui/brand, e o TipTap só pelo RichTextEditor: quem importa apenas o RichTextContent não leva editor nenhum no bundle, e o verify:fixture confere isso a cada build.

pnpm add @waler/ui

Requer vue@^3.5 e tailwindcss@^4.1 no projeto.


Setup

/* src/main.css */
@import "tailwindcss";
@import "@waler/ui/theme.css";
@source "../node_modules/@waler/ui/dist";

A linha @source é obrigatória. O Tailwind v4 ignora node_modules na detecção automática de sources. Sem ela os componentes renderizam sem estilo nenhum e sem erro nenhum — a falha é silenciosa, e é o erro de integração mais comum com esta lib.

Se estiver usando pnpm e os estilos não aparecerem, tente a forma que atravessa o store:

@source "../node_modules/.pnpm/**/@waler/ui/dist";

Como o Tailwind escaneia o dist por texto, o CSS gerado cobre todos os componentes do pacote, não só os que você importou. Na prática são utilities que a aplicação normalmente já produz.

Uso

<script setup lang="ts">
import { Button, Dialog, DialogTrigger, DialogContent, DialogTitle } from '@waler/ui'
</script>

<template>
  <Dialog>
    <DialogTrigger as-child>
      <Button>Abrir</Button>
    </DialogTrigger>
    <DialogContent>
      <DialogTitle>Confirmar</DialogTitle>
    </DialogContent>
  </Dialog>
</template>

Tooltip exige um TooltipProvider na raiz do app.

Componentes

| | | | | |---|---|---|---| | Avatar | Badge | Breadcrumb | Button | | Calendar | Card | Checkbox | DatePicker | | Dialog | Drawer | FileUpload | Group | | Input | Kbd | Label | LinkCard | | MenuItem | Popover | Progress | RadioGroup | | RichTextContent | RichTextEditor | SectionHeader | Select | | Steps | Surface | Switch | Textarea | | Timeline | Toaster | Tooltip | |

Input, Textarea, Select, DatePicker, FileUpload e RichTextEditor são a camada de formulário: cada um recebe label, description, error e required e amarra sozinho o aria-describedby e o aria-invalid ao próprio controle. Não há componente de campo separado — o rótulo mora no controle porque é lá que o id nasce.

O Input cobre ainda prefixo e sufixo (props prefix/suffix ou slots de mesmo nome) e os tipos formatados currency, decimal e phone; qualquer outro type passa direto para o DOM.

Todo componente expõe as e asChild quando envolve um primitivo do Reka, e exporta o próprio tipo de props e de variantes.

Texto formatado

RichTextEditor edita e RichTextContent desenha o mesmo documento, o tipo RichTextDoc: o JSON do TipTap na forma canônica, sem atributo padrão e com o link só com href. Guarde o documento como sai do v-model. O renderizador não usa v-html e confere de novo o protocolo de cada link (http, https, mailto e tel); fora disso o link vira texto. O leitor desenha o corpo em 14px, peso 400, linha de 24px e 12px entre parágrafos; o editor escreve no text-body dos outros campos.

<RichTextEditor
  v-model="doc"
  label="Mensagem"
  placeholder="Escreva o comunicado"
  @slash="openInsertMenu"
/>
<RichTextContent :doc="doc" />

O slash sai quando "/" é digitado num parágrafo vazio do topo (dentro de lista ou citação, "/" é texto) e traz uma âncora que o PopoverAnchor aceita em reference. Listas aninham até três níveis, no Tab e no colar.

Superfícies

Três, e só três: branco sólido, vidro e gradiente. Surface é o primitivo que carrega as três, com o fallback opaco de backdrop-filter e o scrim do gradiente já embutidos.

<Surface variant="glass" radius="lg" elevation="card" class="p-6">
  conteúdo
</Surface>

Texto sobre gradiente herda a tinta certa sozinho, via data-wa-surface no elemento raiz da superfície.

O scrim melhora o contraste mas não garante: no fim do gradiente do banner o branco fica em 2.25:1, abaixo de AA. Mantenha texto no primeiro terço do eixo.

Customização

Sobrescreva os tokens semânticos depois de importar o tema. Eles têm prefixo --wa- porque :root é global e last-wins — um --primary sem prefixo colidiria em silêncio com o do seu app:

@import "@waler/ui/theme.css";

:root {
  --wa-primary: #1E3A2F;
  --wa-accent: #2C4A3F;
}

A utility continua sendo bg-primary. O prefixo fica só na camada de valor.

Para ajuste pontual todo componente aceita class, e o tailwind-merge resolve o conflito. Para trocar o elemento renderizado, use as ou asChild:

<Button as-child>
  <RouterLink to="/planos">Ver planos</RouterLink>
</Button>

Cor de tenant

O subpath @waler/ui/brand resolve o caso whitelabel: cada tenant define seis cores, e elas têm de ser recusadas antes do cadastro em vez de degradarem em runtime numa tela que ninguém está olhando.

É TypeScript puro — sem Vue e sem CSS — porque quem o consome é o servidor, e servidor não deve arrastar componente nenhum para validar seis hex.

import { checkBrand, brandToCss, BRAND_TOKENS } from '@waler/ui/brand'

const brand = { '--wa-ink': '#16211D', '--wa-deep': '#1E3A2F', /* ... */ }

const report = checkBrand(brand)
if (!report.ok) {
  // findings diz o par, a razão medida, o mínimo e quais dos seis a causaram
  throw new Error(report.findings.map(f => `${f.pair}: ${f.ratio}`).join('\n'))
}

const css = brandToCss(brand) // bloco :root pronto para servir

BRAND_TOKENS é a lista fechada — --wa-ink, --wa-deep, --wa-soft, --wa-g1, --wa-g2, --wa-g3. Chave fora dela é recusada, não descartada: um tenant cujo token sumiu em silêncio não tem como distinguir isso de um token que a lib ignora.

O valor tem de ser hex sem alpha. #2C4A3F00 é transparente e apagaria o anel de foco do produto inteiro passando por qualquer validação que só olhe formato.

O bloco só repinta aplicado em :root. A camada de valor é global e last-wins por construção — em um nó interno ele não tem efeito. Sirva o CSS por uma tag <link> no index.html, não por JavaScript depois do mount: é o que faz a cor chegar antes do primeiro paint, sem flash da cor errada.

report.bands traz, por par do banner, o maior prefixo [0, t] do gradiente em que o texto ainda passa AA — as bandas do manifesto foram medidas com o gradiente da Waler, e o de cada tenant é outro.

Cor primária não é o verde

--wa-primary é o ink #16211D, não o verde da marca. No handoff da Waler o verde é acento, não ação. Componente que assumir "primário = cor de marca" sai errado.

Licença

MIT © WALER