Appearance
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).
| Camada | Ferramenta |
|---|---|
| Classes utilitárias | NativeWind v4 / Tailwind v3 (className) |
| Tokens de cor | CSS variables (HSL) em global.css |
| Cor programática | THEME[colorScheme] de lib/theme.ts |
| Variantes | class-variance-authority (cva) + cn() |
| Dark mode | darkMode: '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:
| Token | Light (paper creme) | Dark (preto neutro) | L* dark |
|---|---|---|---|
background | #f5efde | #0A0A0A | 4% |
card | #fefcf6 | #1A1A1A | 10% |
popover | #fefcf6 | #1F1F1F | 12% |
muted | #f0e8d6 | #242424 | 14% |
accent | #eae2cc | #2E2E2E | 18% |
input | #ece5cf | #333333 | 20% |
border | #ece5cf | #3D3D3D | 24% |
foreground | #342d23 | #F5F5F5 | 96% |
muted-foreground | #70634c | #A8A8A8 | 66% |
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
modules/app/stores/settings.ts(Zustand + AsyncStorage) persiste a preferênciatheme: 'system' | 'light' | 'dark'.modules/app/hooks/use-settings.tsresolve o tema (sesystem, usauseColorScheme()do dispositivo) e chamasetColorScheme(resolvedTheme)do NativeWind, que alterna a classe.dark.app/_layout.tsxaplicaNAV_THEME[resolvedTheme]noThemeProvider, ajusta aStatusBare 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>