Tema, densidade e tokens
Os tokens de todos os temas embutidos são publicados estaticamente dentro de styles.css (blocos :root[data-theme="..."] na layer cm.tokens) — nenhum setup é necessário para renderizar com o tema padrão cm-neutral. Trocar de tema é só mudar o atributo data-theme do <html>: o CmThemeProvider faz isso em runtime (e persiste em localStorage); em Next, o CmThemeScript no <head> aplica o tema salvo antes do primeiro paint, sem flash no SSR.
Toggle e menu de tema
Criar um tema customizado
Estenda um tema publicado, sobrescreva os tokens desejados e registre via customThemes.
Tema customizado
primary, accent e os raios vêm do tema novo; o resto herda do cm-neutral.
Densidade via hook
Densidade atual: default
Snippets
Trechos prontos para colar no projeto.
Layout raiz no Next
import "cosmemilton-ui/styles.css";
import { CmThemeProvider, CmThemeScript } from "cosmemilton-ui/theme";
export default function RootLayout({ children }) {
return (
<html lang="pt-BR" suppressHydrationWarning>
<head>
<CmThemeScript defaultThemeName="cm-neutral" />
</head>
<body>
<CmThemeProvider defaultThemeName="cm-neutral">
{children}
</CmThemeProvider>
</body>
</html>
);
}Tema fixo sem JavaScript
Como os tokens já estão no CSS, qualquer tema embutido pode ser fixado direto no atributo — útil para páginas estáticas ou apps sem troca de tema.
<html data-theme="cm-dark">Sobrescrevendo tokens de um tema via CSS
O provider não escreve mais estilos inline no <html>, então CSS comum fora de @layer sempre vence os blocos da biblioteca.
:root[data-theme="cm-neutral"] {
--color-primary: #7c3aed;
}Temas publicados e helpers de CSS
import {
themes,
extendThemes,
themeToCSSVars,
themeToCSSBlock,
themeSelector,
} from "cosmemilton-ui/theme";
const available = Object.keys(themes);
const registry = extendThemes([customTheme]);
themeSelector(themes["cm-blue"]);
// ':root[data-theme="cm-blue"]'
themeToCSSVars(themes["cm-blue"]);
// { "--color-primary": "...", ... }
themeToCSSBlock(themes["cm-blue"]);
// ':root[data-theme="cm-blue"] { --color-primary: ...; }'Catálogo de design tokens
Variáveis publicadas estaticamente em styles.css (layer cm.tokens): o bloco :root traz o padrão cm-neutral e cada tema embutido tem um bloco :root[data-theme="..."] trocando esses valores.
:root {
/* Cores — cada cor de conteúdo tem um par "-foreground" para contraste */
--color-background: #f8fafc;
--color-foreground: #20242a;
--color-muted: #eef1f4;
--color-muted-foreground: #69717d;
--color-card: #ffffff;
--color-card-foreground: #20242a;
--color-popover: #ffffff;
--color-popover-foreground: #20242a;
--color-primary: #334155;
--color-primary-foreground: #ffffff;
--color-secondary: #d9e0e8;
--color-secondary-foreground: #20242a;
--color-accent: #0f766e;
--color-accent-foreground: #ffffff;
--color-success: #2f855a;
--color-success-foreground: #ffffff;
--color-warning: #b7791f;
--color-warning-foreground: #1f2933;
--color-danger: #c2413a;
--color-danger-foreground: #ffffff;
--color-info: #2563a8;
--color-info-foreground: #ffffff;
--color-border: #d8dee6;
--color-input: #cfd6df;
--color-ring: #475569;
--color-selection: #dbeafe;
--color-selection-foreground: #1e293b;
--color-overlay: rgba(32, 36, 42, 0.48);
/* Tipografia */
--font-family: var(--font-geist-sans, 'Inter', sans-serif);
--font-mono: var(--font-geist-mono, 'JetBrains Mono', monospace);
--font-base: 16px;
--font-scale: 1.18;
/* Raios de borda */
--radius-xs: 0.125rem;
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.625rem;
--radius-full: 9999px;
/* Sombras (elevação) */
--shadow-xs: 0 1px 2px 0 rgba(15, 23, 42, 0.05);
--shadow-sm: 0 1px 3px 0 rgba(15, 23, 42, 0.08);
--shadow-md: 0 8px 24px -18px rgba(15, 23, 42, 0.28);
--shadow-lg: 0 18px 44px -28px rgba(15, 23, 42, 0.32);
--shadow-xl: 0 28px 70px -38px rgba(15, 23, 42, 0.38);
/* Espaçamento (gaps, paddings, margins) */
--space-none: 0;
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 0.75rem;
--space-lg: 1rem;
--space-xl: 1.5rem;
--space-2xl: 2rem;
--space-3xl: 3rem;
/* Camadas de empilhamento (z-index dos overlays) */
--z-base: 0;
--z-sticky: 20;
--z-docked: 30;
--z-overlay: 300;
--z-modal: 301;
--z-dropdown: 500;
--z-toast: 9999;
--z-tooltip: 10000;
/* Movimento (durações e curvas) */
--motion-duration-fast: .15s;
--motion-duration-base: .2s;
--motion-duration-slow: .3s;
--motion-ease-standard: ease;
--motion-ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
/* Breakpoints */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
/* Densidade do modo "default" (também existem --density-compact-* e --density-comfortable-*) */
--density-default-control-height: 2.5rem;
--density-default-control-padding-x: 0.875rem;
--density-default-control-padding-y: 0.5rem;
--density-default-gap: 0.5rem;
--density-default-icon-size: 1.125rem;
/* Superfícies semânticas (derivadas das cores) */
--layer-base: var(--color-background);
--layer-surface: var(--color-card);
--layer-elevated: color-mix(in srgb, var(--color-card) 96%, var(--color-background));
--layer-floating: var(--color-popover);
--layer-overlay: var(--color-overlay);
}Consumindo os tokens no seu CSS
Use os tokens em vez de valores fixos para herdar tema, dark mode e densidade automaticamente.
.minha-secao {
background: var(--color-card);
color: var(--color-card-foreground);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: var(--space-lg);
box-shadow: var(--shadow-md);
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
}
.minha-secao:focus-visible {
outline: 2px solid var(--color-ring);
outline-offset: 2px;
}Sobrescrevendo tokens em um escopo
Tokens são variáveis CSS herdáveis: redefina qualquer um em um escopo — usando os próprios primitivos da lib — para personalizar sem sair do sistema.
import { CmButton } from "cosmemilton-ui/client";
import { CmStack } from "cosmemilton-ui/server";
// Tudo dentro desta seção herda o primary roxo e o raio maior.
// CmStack as="section" vira um <section> semântico, sem HTML cru.
<CmStack as="section" style={{ "--color-primary": "#7c3aed", "--radius-md": "1rem" } as React.CSSProperties}>
<CmButton tone="primary">Herda os tokens deste escopo</CmButton>
</CmStack>Notas
Temas publicados: cm-neutral, cm-school, cm-dark, cm-orange, cm-red, cm-blue, cm-green, cm-violet, cm-midnight, cm-rose e cm-aurora.
Densidades públicas: default, comfortable e compact.
Como os tokens dos temas embutidos já vêm em styles.css, o CmThemeScript injeta apenas alguns bytes por página: ele lê o tema salvo no localStorage e seta data-theme antes do primeiro paint (não serializa mais os tokens de todos os temas).
CmThemeScript aceita a prop nonce, repassada ao <script>/<style> inline — necessário em apps cuja Content-Security-Policy não permite 'unsafe-inline'.
Temas custom (que não existem no CSS estático) são emitidos uma vez como um <style id="cm-theme-custom"> pelo CmThemeScript e/ou pelo CmThemeProvider, com seletor de especificidade maior que os blocos embutidos — um tema custom pode inclusive sobrescrever o nome de um tema publicado.
themeToCSSVars(theme) gera as variáveis a partir do objeto do tema; nomes camelCase viram kebab-case (mutedForeground → --color-muted-foreground). themeToCSSBlock(theme) rende o bloco CSS completo (tokens + color-scheme) e themeSelector(theme) devolve o seletor :root[data-theme="..."].
Famílias de tokens: --color-*, --font-*, --radius-*, --shadow-*, --space-*, --z-*, --motion-duration-*/--motion-ease-*, --breakpoint-*, --density-<modo>-* e --layer-*.
Cada cor de conteúdo acompanha um par -foreground para garantir contraste sobre a superfície.
Por serem variáveis CSS herdáveis, os tokens podem ser redefinidos em qualquer escopo (inclusive via style inline) sem recompilar nada.
Para criar um tema próprio, monte um ThemeConfig — o mais simples é dar spread em um tema publicado (themes["cm-neutral"]) e sobrescrever só os tokens desejados, já que colors, typography, radii e shadows são obrigatórios e completos.
Registre o(s) tema(s) com a prop customThemes do CmThemeProvider (aceita ThemeConfig[] ou um ThemeRegistry); extendThemes faz o merge com os publicados e os temas custom aparecem no CmThemeMenu junto dos demais.