O contrato de estilos
Cada parte pública aceita diretamente seu className, style, ref, manipuladores de eventos, props nativas e atributos data-*. Não há props paralelas como contentClassName ou triggerClassName.
O uiify controla os atributos de identidade e estado. Eles são pontos de integração para estilos: altere o estado por props ou pelo comportamento nativo do HTML, nunca escrevendo data-state. O namespace --uiify-* é reservado para geometria, animação e valores por instância.
<Popover.Root>
<Popover.Trigger className="toolbar-trigger">Open</Popover.Trigger>
<Popover.Content className="toolbar-popover" data-layout="compact">
Actions
</Popover.Content>
</Popover.Root>Nomenclatura
| Ponto de integração | Finalidade | Exemplo |
|---|---|---|
| Marcadores de identidade | Identidade estável do componente, controlada pelo uiify | data-uiify-popover |
| Marcadores de anatomia | Parte pública de um componente composto, controlada pelo uiify | data-part="content" |
| Atributos de estado | Espelho de estado somente leitura para seletores | data-state="open" |
| Propriedades personalizadas | Geometria dinâmica e valores por instância | --uiify-anchor-x |
Cada raiz com estado tem um marcador data-uiify-<component> cujo valor é sua presença. Cada parte composta tem um valor data-part em kebab-case, como trigger, content, item ou label. As props do consumidor não podem substituir esses valores.
CSS puro e Tailwind
[data-uiify-popover][data-part="content"] {
min-inline-size: 12rem;
}
[data-uiify-popover][data-part="content"][data-state="open"] {
opacity: 1;
}
/* Tailwind uses the same attributes. */
<div className="data-[state=open]:opacity-100" />Polimorfismo
as e render substituem o elemento renderizado. Um renderizador personalizado precisa encaminhar as props e a ref recebidas ao elemento DOM; caso contrário, marcadores, estado e estilos deixam de funcionar.
behavior.css é obrigatório
Esta folha de estilos contém as regras que fazem o estado funcionar: posicionamento de janelas flutuantes, dimensionamento de ::details-content, propagação com :has(), posicionamento de campos ocultos e transições com @starting-style. Não tem cores, fontes ou espaçamento.
import "@gunkin/uiify/components/behavior.css";Sem ela, os componentes são renderizados, mas seu estado não funciona. Importe uma vez, em qualquer lugar da aplicação.
Estado é atributo, não classe
Os componentes expõem o estado como atributos data-* no elemento responsável por ele. Assim, o CSS pode selecioná-lo sem JavaScript nem nomes de classes. Cada página de componente lista os atributos que ele emite.
.my-trigger[data-state="open"] { … }
.my-item[data-disabled] { … }
/* Native state works the same way, because the state IS native */
.my-panel:popover-open { … }
details[open] > summary { … }Variantes também são atributos data-*, em vez de consultas de estilo de contêiner, porque um contêiner não pode consultar a si mesmo.
Valores inline, regras na folha de estilos
Os valores por instância chegam como propriedades personalizadas inline; todas as regras ficam em uma folha de estilos estática. Estilos inline não expressam pseudoclasses, pseudoelementos, consultas de contêiner ou @starting-style, e o modelo de estado depende de todos esses recursos.
Propriedades inline chegam com o HTML por streaming sem divergências de hidratação, e uma única folha de estilos é interpretada uma vez e armazenada em cache. Já CSS-in-JS injeta blocos <style> em cada fronteira e não mantém cache entre requisições.
Três formas de aplicar estilos
Seu próprio CSS. Selecione o elemento e seus atributos data-*; não há estilos padrão para sobrescrever.
Tailwind. Os mesmos atributos funcionam como variantes, por exemplo data-[state=open]:rotate-180. As prévias do site mudam para o visual Tailwind na opção Estilo do cabeçalho.
@gunkin/uiify/styles. Uma camada opcional de CSS puro com escalas de tokens OKLCH, temas claro e escuro e um visual para cada componente — um ponto de partida, não uma exigência.
import "@gunkin/uiify/styles/reset";
import "@gunkin/uiify/styles";Ordem da cascata
Tudo que o uiify entrega fica em uma camada nomeada abaixo da sua:
@layer ui.reset, ui.behavior, ui.tokens, ui.theme, app;Sua camada sempre prevalece: as substituições dispensam !important e truques de especificidade e resistem a refatorações da biblioteca.
Cada página de componente lista seus atributos e variáveis CSS, com CSS pronto para copiar e colar para os dois visuais.