Sombras, Elevação & Bordas
Diferenças de plataforma entre sombra iOS e elevação Android.
Busque em todas as páginas da documentação
Diferenças de plataforma entre sombra iOS e elevação Android.
Cartão de receita de referência rápida - pronto para copiar e colar.
import { Platform, StyleSheet, Text, View } from "react-native";
export function ElevatedCard({ title, children }: { title: string; children: React.ReactNode }) {
return (
<View style={styles.shadowWrapper}>
<View style={styles.card}>
<Text style={styles.title}>{title}</Text>
{children}
</View>
</View>
);
}
const styles = StyleSheet.create({
shadowWrapper: {
...Platform.select({
ios: {
shadowColor: "#0f172a",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.12,
shadowRadius: 12,
},
android: { elevation: 4 },
}),
},
card: {
backgroundColor: "#fff",
borderRadius: 16,
padding: 16,
gap: 8,
borderWidth: StyleSheet.hairlineWidth,
borderColor: "#e2e8f0",
overflow: "hidden",
},
title: { fontSize: 17, fontWeight: "600", color: "#0f172a" },
});Quando usar isso: Cards, sheets, botões flutuantes ou linhas de lista que precisam de profundidade - você quer um componente que pareça correto tanto no iOS quanto no Android sem lutar contra sombras cortadas ou elevação ausente.
import { Image, Platform, Pressable, StyleSheet, Text, View } from "react-native";
const AVATAR = require("./assets/avatar.png");
export function ProfileHeader() {
return (
<View style={styles.screen}>
<View style={styles.heroShadow}>
<View style={styles.heroCard}>
<View style={styles.avatarRing}>
<Image source={AVATAR} style={styles.avatar} />
</View>
<Text style={styles.name}>Alex Chen</Text>
<Text style={styles.subtitle}>React Native · Expo SDK 57</Text>
</View>
</View>
<Pressable style={({ pressed }) => [styles.fabShadow, pressed && styles.fabPressed]}>
<View style={styles.fab}>
<Text style={styles.fabLabel}>+</Text>
</View>
</Pressable>
<View style={styles.dividerRow}>
<View style={styles.hairline} />
<Text style={styles.dividerLabel}>Recente</Text>
<View style={styles.hairline} />
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 20, backgroundColor: "#f8fafc", gap: 24 },
heroShadow: {
borderRadius: 20,
...Platform.select({
ios: {
shadowColor: "#000",
shadowOffset: { width: 0, height: 6 },
shadowOpacity: 0.1,
shadowRadius: 16,
},
android: { elevation: 6 },
}),
},
heroCard: {
backgroundColor: "#fff",
borderRadius: 20,
padding: 20,
alignItems: "center",
gap: 8,
borderWidth: StyleSheet.hairlineWidth,
borderColor: "#cbd5e1",
},
avatarRing: {
width: 88,
height: 88,
borderRadius: 44,
borderWidth: 3,
borderColor: "#2563eb",
overflow: "hidden",
...Platform.select({
ios: { borderCurve: "continuous" },
default: {},
}),
},
avatar: { width: "100%", height: "100%" },
name: { fontSize: 22, fontWeight: "700", color: "#0f172a" },
subtitle: { fontSize: 14, color: "#64748b" },
fabShadow: {
alignSelf: "flex-end",
borderRadius: 28,
...Platform.select({
ios: {
shadowColor: "#2563eb",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.35,
shadowRadius: 8,
},
android: { elevation: 8 },
}),
},
fabPressed: { opacity: 0.9, transform: [{ scale: 0.96 }] },
fab: {
width: 56,
height: 56,
borderRadius: 28,
backgroundColor: "#2563eb",
alignItems: "center",
justifyContent: "center",
},
fabLabel: { fontSize: 28, color: "#fff", lineHeight: 32 },
dividerRow: { flexDirection: "row", alignItems: "center", gap: 12 },
hairline: { flex: 1, height: StyleSheet.hairlineWidth, backgroundColor: "#cbd5e1" },
dividerLabel: { fontSize: 12, fontWeight: "600", color: "#94a3b8", letterSpacing: 1 },
});O que isso demonstra:
shadowWrapper externo - o card interno pode usar overflow: "hidden" sem cortar a sombra do iOSPlatform.select combina props shadow* do iOS com elevation do Android em um único StyleSheetStyleSheet.hairlineWidth para bordas e linhas divisórias que permanecem nítidas em todas as densidadesborderCurve: "continuous" no iOS para avatares circulares mais suaves (ignorado no Android)elevation mais alta no Android para um visual de ação flutuanteO iOS renderiza sombras a partir do background e canal alfa da view. Quatro props controlam o efeito:
| Prop | Função | Valor inicial típico |
|---|---|---|
shadowColor | Tonalidade da sombra | "#000" ou cor da marca |
shadowOffset | Deslocamento { width, height } em dp | { width: 0, height: 4 } |
shadowOpacity | Alfa 0–1 | 0.08 – 0.15 para cards |
shadowRadius | Raio de desfoque em dp | 8 – 16 para cards suaves |
// Bloco de sombra exclusivo para iOS
{
shadowColor: "#0f172a",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
}Requisitos no iOS:
backgroundColor opaco ou translúcido na view sombreada - backgrounds transparentes produzem sombras fracas ou invisíveis.overflow: "hidden" corta a sombra - aplique a sombra a um wrapper pai; mantenha overflow: "hidden" na view interna.borderRadius perfeitamente em todas as versões do SO - wrapper + raio interno geralmente correspondem à intenção do design.O Android usa um único número de elevation (dp) para desenhar uma sombra de material e ordenar a view acima dos irmãos.
elevation | Peso visual | Uso típico |
|---|---|---|
0 | Plano | Linhas de lista alinhadas |
2 – 4 | Levantamento sutil | Cards, chips |
6 – 8 | Proeminente | FABs, modais |
12+ | Pesado | Diálogos, bottom sheets |
// Elevação exclusiva para Android
{ elevation: 4, backgroundColor: "#fff" }Requisitos no Android:
backgroundColor é obrigatório - a elevação desenha a partir da forma do background da view.shadowColor, shadowOffset, shadowOpacity e shadowRadius são ignorados.const cardShadow = Platform.select({
ios: {
shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.12,
shadowRadius: 12,
},
android: { elevation: 4 },
default: {},
});
// Espalhe na entrada do StyleSheet
card: {
backgroundColor: "#fff",
borderRadius: 12,
...cardShadow,
},Para tokens de design system, exporte presets de sombra:
export const shadows = {
sm: Platform.select({
ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.06, shadowRadius: 4 },
android: { elevation: 2 },
}),
md: Platform.select({
ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 10 },
android: { elevation: 4 },
}),
} as const;| Prop | Notas |
|---|---|
borderWidth | Uniforme ou por lado via borderTopWidth, etc. |
borderColor | Aplica-se a todos os lados, a menos que substituído por lado |
borderRadius | Arredonda cantos; use props por canto para raios assimétricos |
borderStyle | "solid" (padrão), "dotted", "dashed" - suporte a tracejado varia |
StyleSheet.hairlineWidth | Linha visível mais fina (~1 pixel físico) |
// Borda por lado (comum para separadores de lista)
{
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#e2e8f0",
}
// Máscara arredondada para imagens
{
borderRadius: 12,
overflow: "hidden",
}Somente iOS: borderCurve: "continuous" produz cantos mais suaves em raios grandes - especialmente avatares e pills. O Android ignora essa prop com segurança.
┌─ shadowWrapper (sombra / elevação, borderRadius) ─┐
│ ┌─ card (backgroundColor, overflow: hidden) ─────┐ │
│ │ conteúdo │ │
│ └────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Use um wrapper quando a view interna precisar de overflow: "hidden" para imagens, gradientes ou filhos cortados, mas ainda assim precisar lançar uma sombra no iOS.
import type { ViewStyle } from "react-native";
import { Platform, StyleSheet } from "react-native";
type ShadowPreset = Pick<
ViewStyle,
"shadowColor" | "shadowOffset" | "shadowOpacity" | "shadowRadius" | "elevation"
>;
const presets: Record<"sm" | "md", ShadowPreset | undefined> = {
sm: Platform.select({ ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.06, shadowRadius: 4 }, android: { elevation: 2 } }),
md: Platform.select({ ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 10 }, android: { elevation: 4 } }),
};Platform.select é uma união - espalhar em StyleSheet.create é válido.shadowOffset requer { width: number; height: number } - não uma tupla.borderCurve é específico do iOS; o TypeScript pode incluí-lo em ViewStyle atrás de extensões de plataforma.Sombra invisível no iOS - Falta backgroundColor ou background do card totalmente transparente. Correção: Defina backgroundColor: "#fff" (ou a cor da sua superfície) na view sombreada.
overflow: "hidden" corta a sombra - Comum em cards com imagens arredondadas. Correção: Mova as props de sombra para um wrapper externo; mantenha overflow: "hidden" apenas na view interna.
Props de sombra no Android - shadowColor e similares silenciosamente não fazem nada. Correção: Sempre combine com elevation via Platform.select.
Elevação sem background - O Android não desenha nada. Correção: backgroundColor explícito em cada superfície elevada.
Bordas finas muito grossas - Usar borderWidth: 1 em telas de retina parece pesado. Correção: StyleSheet.hairlineWidth para separadores e contornos de card.
Bordas tracejadas inconsistentes - borderStyle: "dashed" renderiza de forma diferente entre as plataformas. Correção: Use um SVG ou um componente de borda dedicado para tracejados perfeitos em pixels.
Sombra em Text - Text principal não suporta props de sombra de forma confiável para profundidade estilo card. Correção: Envolva o texto em um View que carregue a sombra/elevação.
Elevações concorrentes em pilhas - Múltiplos irmãos elevados podem produzir sombras turvas no Android. Correção: Eleve apenas o elemento flutuante superior; mantenha as linhas de lista planas (elevation: 0).
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
Presets de sombra Platform.select | Cards padrão, FABs, chips | Você precisa de mockups de sombra idênticos pixel-perfeitos em ambos os SOs |
| Wrapper + card interno | Conteúdo arredondado com overflow: "hidden" | Chips planos simples sem corte |
react-native-shadow-2 (comunitário) | Sombras de caixa multicamadas complexas de design | Apps gerenciados pelo Expo evitando dependências nativas extras |
expo-linear-gradient como borda falsa | Bordas gradientes sem SVG | borderColor sólido simples é suficiente |
box-shadow via WebView / Skia | O design exige sombras CSS exatas | Cards e listas nativas normais |
| Apenas bordas planas (sem sombra) | Tabelas de dados densas e listas de configurações | Cards de herói de marketing que precisam de profundidade |
backgroundColor - sombras renderizam a partir da camada de fundo.overflow: "hidden" na mesma view corta a propagação da sombra.shadowOpacity: 0 ou um shadowRadius zero tornam o efeito invisível.backgroundColor, mova a sombra para um wrapper pai e comece com shadowOpacity: 0.1 e shadowRadius: 8.elevation - um único número em pixels independentes de densidade.backgroundColor na mesma view.2 e 6; FABs entre 6 e 12.elevation.Platform.select presets por nível (sm, md, lg) mantêm as equipes consistentes.overflow: "hidden" corta tudo fora da caixa da borda, incluindo o desfoque da sombra.View externo carrega a sombra; View interno tem borderRadius, backgroundColor e overflow: "hidden".borderWidth: 1 para separadores de lista e contornos sutis de card.borderWidth e para height em Views divisórias.borderRadius.elevation se parecer muito pesado.borderCurve: "continuous" junto com borderRadius no iOS.elevation em cada linha multiplica as chamadas de desenho de sombra.Text suporta textShadowColor, textShadowOffset e textShadowRadius para contornos de glifo.shadow* estilo View são para contêineres, não para profundidade de parágrafo.Text em um View elevado.{
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#e2e8f0",
}borderTopWidth, borderLeftColor, etc.borderWidth sozinho define todos os quatro lados.shadowOpacity no iOS - sombras pretas fortes parecem duras.backgroundColor distinto.Animated e Reanimated podem interpolar shadowOpacity e elevation para estados de pressionamento.opacity + scale em vez de animar o raio de desfoque.outline do CSS.borderWidth / borderColor ao focar, ou um View dedicado para anel de foco.shadows.sm, shadows.md, shadows.lg como objetos Platform.select.StyleSheet.create para evitar desvios de copiar e colar.backgroundColor de superfície do seu tema.StyleSheet.create e o modelo mental de estilização do RNPlatform.select e primitivas de layout de card.ios / .androidoverflow: "hidden"Versões da Stack: Esta página foi escrita para React 19.2.3, React Native 0.86.0 e Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026