Recurso
Base

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

Tema atual: neutral

Criar um tema customizado

Estenda um tema publicado, sobrescreva os tokens desejados e registre via customThemes.

sunset

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

tsx
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
<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.

css
:root[data-theme="cm-neutral"] {
  --color-primary: #7c3aed;
}

Temas publicados e helpers de CSS

tsx
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.

css
: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.

css
.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.

tsx
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.