Camada Dois: Gradientes, Sombreamento e Fontes
Aqui está o que aprendi construindo isso: tokens de cor sozinhas dão a você paletas, não temas.
Troque 22 cores e o aplicativo parece recolorido. Não se sente diferente. A personalidade de um tema vive em tudo ao redor das cores: quão arredondados os cantos são, se as cartas têm sombras suaves ou brilhos neon, que fonte os números renderizam.
Então há uma segunda camada, mais grossa. Cada tema também define um objeto ThemeComponentStyles que descreve partes nomeadas da interface do usuário: totalCard, categoryCard, tabBar, modal, input, progressTrack e assim por diante. Esta é uma fatia cortada da versão 2077:
// apps/mobile/lib/theme-styles.ts
const CYBER_STYLES: ThemeComponentStyles = {
baseFont: 'Orbitron_700Bold',
background: {
gradientColors: ['#0D0F1A', '#1A1B23'],
backgroundImage: require('@/assets/digital-nomad-2077-bg.webp'),
backgroundImageOpacity: 0.28,
},
totalCard: {
gradientColors: ['rgba(13,15,26,0.95)', 'rgba(13,15,26,0.95)'],
borderColor: '#00FFFF',
borderWidth: 2,
shadow: {
shadowColor: '#00FFFF',
shadowOpacity: 0.3,
shadowRadius: 12,
shadowOffset: { width: 0, height: 4 },
elevation: 4,
},
titleColor: '#FF00FF',
valueColor: '#00FFFF',
titleFont: 'Orbitron_700Bold',
valueFont: 'Orbitron_700Bold',
},
tabActive: {
gradientColors: ['#FF00FF', '#00FFFF'],
textColor: '#ffffff',
shadow: {
shadowColor: '#FF00FF',
shadowOpacity: 0.4,
shadowRadius: 20,
shadowOffset: { width: 0, height: 0 },
elevation: 8,
},
},
radii: { card: 6, dialog: 6, input: 4, pill: 2 },
// ...o restante das partes nomeadas cortado
};É aqui que o cyberpunk realmente acontece. A sombra na aba ativa tem deslocamento zero e raio de 20px, o que em um fundo escuro lê como um brilho magenta, não uma sombra de queda. A fonte base para todo o tema é Orbitron. As pílulas são quase quadradas (pill: 2) porque chips arredondados sentiriam errado neste mundo.
Agora compare as respostas do tema Imigrante às mesmas perguntas:
// apps/mobile/lib/theme-styles.ts
const ESTILOS_TEMA: ThemeComponentStyles = {
totalCard: {
gradientColors: ['#F8F8F8', '#F0F0F0'],
borderColor: '#C0C0C0',
borderWidth: 1,
titleColor: '#1F1F1F',
valueColor: '#1F1F1F',
titleStyle: {
textTransform: 'uppercase',
letterSpacing: 0.5,
fontWeight: '700',
},
},
progressFill: {
backgroundColor: '#4A4A4A',
borderRadius: 0,
},
monoFont: 'JetBrainsMono_400Regular',
radii: { card: 4, dialog: 4, input: 0, pill: 0 },
// ...trimmed
};Tudo aqui é retangular. A “gradiente” quase não é uma gradiente, a barra de progresso é um retângulo plano e os títulos vão para maiúsculas com espaçamento entre as letras. Os títulos das cartas de categoria são renderizados em JetBrains Mono e o log de transações também fica monoespaçado através de uma monoFont que apenas as linhas de transação leem. Não há propriedade de sombra alguma, porque concreto não brilha.
Ambas as fontes são carregadas uma vez na raiz do aplicativo com useFonts das Google Fonts do Expo, e a partir daí elas acompanham o tema como qualquer outro token. Um tema que deseja uma fonte personalizada apenas a nomeia; um tema que não quer deixa baseFont indefinido e os componentes recuam para a fonte do sistema.
Esse é o padrão: gradientes, sombras, fontes e raio são dados do tema, não decisões de componentes.

