Shadwire

Buscar na documentação

Busque nas páginas da documentação e dos componentes por título e por conteúdo.

Começar
Introdução
Instalação
shadwire.json
Theming
Dark mode
Ferramentas
CLI
Agent skill
Registry
llms.txt
Guias
Composição
Estilização
Formulários
Ícones
Acessibilidade
Localização
Blocks
Visão geral
Componentes
Visão geral
Accordion
Alert
Alert Dialog
Aspect Ratio
Avatar
Badge
Breadcrumb
Button
Button Group
Calendar
Card
Carousel
Chart
Checkbox
Collapsible
Combobox
Command
Context Menu
Data Table
Date Picker
Dialog
Drawer
Dropdown Menu
Empty
Field
Hover Card
Icon
Input
Input Group
Input OTP
Item
Kbd
Label
Menubar
Native Select
Navigation Menu
Pagination
Popover
Progress
Radio Group
Resizable
Scroll Area
Select
Separator
Sheet
Sidebar
Skeleton
Slider
Sonner
Spinner
Switch
Table
Tabs
Textarea
Toggle
Toggle Group
Tooltip

Dark mode

Uma única classe no <html> muda o site inteiro para o modo escuro, sem que os componentes precisem saber disso.

O shadwire.css liga a variante dark do Tailwind a uma classe:

@custom-variant dark (&:is(.dark *));

A variante dark vale quando algum ancestral tem a classe .dark, então você troca de tema colocando ou tirando essa classe do <html>. Os componentes só usam tokens, e todo token tem um valor claro e um escuro, então o dark mode não exige nenhum código nos componentes.

O toggle

Este controller Stimulus de cinco linhas é o que alterna o tema deste site:

// app/javascript/controllers/theme_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  toggle() {
    const isDark = document.documentElement.classList.toggle("dark")
    localStorage.setItem("theme", isDark ? "dark" : "light")
  }

E o botão. A variante dark decide qual dos dois ícones aparece:

<%= ui_button(size: :icon, variant: :ghost,
              data: { controller: "theme", action: "theme#toggle" },
              aria: { label: "Toggle theme" }) do %>
  <%= ui_icon("moon", class: "dark:hidden") %>
  <%= ui_icon("sun", class: "hidden dark:block") %>
<% end %>

dark:hidden e hidden dark:block escolhem qual elemento fica visível, e não a cor dele. Esse é um bom uso do prefixo dark:; usá-lo para cores não é, como explicado mais abaixo.

Evitando o flash

Se a classe só for aplicada depois que o JavaScript do app carregar, quem escolheu o tema escuro vê a tela piscar em branco a cada página. Para evitar isso, coloque um script inline no <head>, antes de qualquer folha de estilo, para ele rodar antes de a página ser desenhada:

<script>
  (() => {
    const stored = localStorage.getItem("theme")
    const dark = stored
      ? stored === "dark"
      : window.matchMedia("(prefers-color-scheme: dark)").matches
    document.documentElement.classList.toggle("dark", dark)
  })()

Ele olha primeiro a preferência salva e só depois a configuração do sistema. Assim, quem usa o sistema no modo escuro pode escolher o tema claro, e a escolha é lembrada.

Não use dark: para cores

Uma cor escura escrita à mão repete uma decisão que os tokens já tomam, e deixa de combinar com o tema assim que alguém mexe nos tokens:

<%# Wrong: light and dark colours written by hand %>
<%= ui_card(class: "bg-white text-gray-900 dark:bg-gray-900 dark:text-white") do %>
<%# Right: the tokens already handle both modes %>
<%= ui_card do %>

Se você precisa de uma cor que os tokens não cobrem, adicione um token. Você define um valor claro e um escuro para ele, e ele continua funcionando quando o tema mudar.

Testando os dois modos

Confira suas telas nos dois modos. Quando algo parece errado no modo escuro, raramente o problema é o valor da cor. Com mais frequência, dois utilitários com a mesma especificidade estão brigando, ou falta um utilitário que deveria vir em par: a lista de classes parece certa, mas o CSS compilado não está. Olhe o CSS gerado.