Skip to content

Styling - Estilização

Abordagem

O app usa NativeWind v4 (Tailwind CSS v3 para React Native). Estilização é feita por classes utilitárias (className) nos componentes. Não há SCSS nem Naive UI no mobile (isso é do vue-spa).

CamadaFerramenta
Classes utilitáriasNativeWind v4 / Tailwind v3 (className)
Tokens de corCSS variables (HSL) em global.css
Cor programáticaTHEME[colorScheme] de lib/theme.ts
Variantesclass-variance-authority (cva) + cn()
Dark modedarkMode: 'class' + useColorScheme()

Tokens de cor (CSS variables)

As cores são definidas como CSS variables HSL em global.css, em dois blocos: :root (light) e .dark:root (dark). O tailwind.config.js mapeia cada variável para uma classe (bg-card, text-foreground, border-border, etc.) via hsl(var(--token)).

css
/* global.css */
@layer base {
  :root {
    --background: 44 53% 92%;   /* paper creme — base #f5efde */
    --foreground: 34 18% 15%;
    --card: 42 81% 96%;
    /* ... */
  }
  .dark:root {
    --background: 0 0% 4%;       /* preto neutro, sem viés de hue */
    --foreground: 0 0% 96%;
    --card: 0 0% 10%;
    /* ... */
  }
}

Regra: componentes nunca usam hex/HSL cru. Sempre a classe semântica (bg-card, text-muted-foreground) ou, quando precisar de cor em prop (ícone), o objeto THEME.

Espelho em lib/theme.ts

global.css (classes) e lib/theme.ts (runtime) devem ficar sincronizados com lib/paper-palette.ts, onde #f5efde é a âncora do paper creme (light) e o dark é uma escala de cinza neutra (saturação 0) a partir do preto. THEME[colorScheme] é usado quando a cor vai numa prop JS (cor de ícone Lucide, SystemUI.setBackgroundColorAsync). NAV_THEME adapta os mesmos tokens para o ThemeProvider do @react-navigation/native.

tsx
const { colorScheme } = useColorScheme()
const theme = THEME[colorScheme ?? 'light']

<ChevronRight size={18} color={theme.mutedForeground} />

Temas do mobile (paper creme no light, preto neutro no dark)

O switch de tema continua system | light | dark. O light é o paper creme; o dark é um preto neutro (saturação 0, sem viés azulado) com degraus de elevação bem separados, para que card, popover e input se destaquem do fundo:

TokenLight (paper creme)Dark (preto neutro)L* dark
background#f5efde#0A0A0A4%
card#fefcf6#1A1A1A10%
popover#fefcf6#1F1F1F12%
muted#f0e8d6#24242414%
accent#eae2cc#2E2E2E18%
input#ece5cf#33333320%
border#ece5cf#3D3D3D24%
foreground#342d23#F5F5F596%
muted-foreground#70634c#A8A8A866%

Regra de elevação no dark: cada superfície é um degrau de lightness acima do fundo (background 4% → card 10% → popover 12% → muted 14% → accent 18% → input 20%). Como cards não usam borda (ver design-system.md), o contraste vem exclusivamente desse degrau, nunca reaproveitar o mesmo token para superfícies empilhadas. Campos de formulário no dark usam dark:bg-input sólido (nada de dark:bg-input/30, que dissolve o campo no card).

primary (azul) e secondary (laranja) da marca, e as cores de status (success/warning/destructive/info), são mantidas em ambos os temas.

A web (vue-spa) tem mais variantes (paper, light, paper black, black) e usa a fonte serifada Noto Serif. O mobile não usa fonte customizada — ver Tipografia.


Dark mode

  1. modules/app/stores/settings.ts (Zustand + AsyncStorage) persiste a preferência theme: 'system' | 'light' | 'dark'.
  2. modules/app/hooks/use-settings.ts resolve o tema (se system, usa useColorScheme() do dispositivo) e chama setColorScheme(resolvedTheme) do NativeWind, que alterna a classe .dark.
  3. app/_layout.tsx aplica NAV_THEME[resolvedTheme] no ThemeProvider, ajusta a StatusBar e a cor de fundo do sistema.
tsx
<View className="bg-card">
  <Text className="text-foreground">Título</Text>
  <Text className="text-muted-foreground">Subtítulo</Text>
</View>

Tipografia

O mobile usa a fonte nativa do sistema (San Francisco no iOS, Roboto no Android). Não carregamos fonte customizada (expo-font/useFonts) — diferente da web, que usa Noto Serif. Tamanho/peso vêm das classes Tailwind no componente components/ui/Text (text-base, text-2xl, font-semibold, etc.).


Internacionalização (i18n)

Usa react-i18next (não vue-i18n). Locales em pt-BR/en.

tsx
const { t } = useTranslation()

<Text>{t('templates.list.title')}</Text>