fitcard-ui-react-native
v2.0.4
Published
Componentes React Native do design system Grafit, com suporte a temas por marca, alto contraste e Liquid Glass.
Maintainers
Readme
fitcard-ui-react-native
Biblioteca de componentes React Native do design system Grafit, compartilhada por todos os apps.
- 7 marcas:
fitcard,fitmoby,euro,loop,link,neo,prime. - 4 modos: claro, escuro, claro alto contraste e escuro alto contraste.
- Liquid Glass (iOS 26) injetado pelo app, com fallback opaco em todas as outras plataformas.
- Zero dependências nativas próprias: tudo que é nativo é
peerDependency, então os apps não precisam deresolutions.
Começando agora? Siga o Getting Started. Vindo da v1? Veja o guia de migração. Publicando uma versão? Siga o PUBLISHING.md · histórico no CHANGELOG.
Sumário
- Instalação
- Tema
- Aparências: tone, variant e size
- Liquid Glass
- Componentes
- Utilitários
- Dependências e versões duplicadas
- Desenvolvimento
Instalação
yarn add fitcard-ui-react-native
yarn add styled-components phosphor-react-native react-native-svg \
react-native-safe-area-context react-native-reanimated react-native-worklets \
react-native-gesture-handler @react-native-community/datetimepicker| Peer | Versão mínima |
|---|---|
| react | 19.0 |
| react-native | 0.78 (testado na 0.87.1) |
| styled-components | 6 |
| phosphor-react-native | 3 |
| react-native-svg | 15 |
| react-native-safe-area-context | 4.10 |
| react-native-reanimated | 4.1 |
| react-native-worklets | 0.5 |
| react-native-gesture-handler | 2.20 (com o GestureHandlerRootView na raiz do app) |
| @react-native-community/datetimepicker | 7 |
Fontes Inter: adicione ./node_modules/fitcard-ui-react-native/assets/fonts aos assets do react-native.config.js do app e rode npx react-native-asset. O passo a passo completo está no Getting Started.
<SafeAreaProvider>
<GrafitProvider brand="link" colorScheme="system" glass={glass}>
<ToastProvider>
<App />
</ToastProvider>
</GrafitProvider>
</SafeAreaProvider>Tema
GrafitProvider
Configura o tema e o ThemeProvider do styled-components (os styled do app recebem o mesmo tema).
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| brand | GrafitBrand | 'fitcard' | Marca (tema) do Grafit. Padrão: fitcard. |
| colorScheme | GrafitColorScheme \| 'system' | 'system' | Esquema de cor. system acompanha o aparelho. Padrão: system. |
| highContrast | boolean | false | Ativa os modos de alto contraste (lightHC/darkHC). |
| overrides | GrafitThemeOverrides | | Sobrescritas pontuais de tokens (cores, strokes e extensões da lib). |
| theme | GrafitTheme | | Tema já montado (ex.: createGrafitTheme). Quando informado, ignora brand, colorScheme e highContrast. |
| glass | LiquidGlassAdapter \| null | | Liquid Glass do app. Ex.: { View: LiquidGlassView, isSupported: isLiquidGlassSupported }. |
| children * | ReactNode | | |
Memoize overrides e glass; objetos novos a cada render recriam o tema.
Ícones e tamanho do bundle
Importe ícones do Phosphor pelo caminho do ícone, não pelo índice do pacote:
import { BellIcon } from 'phosphor-react-native/src/icons/Bell'; // ✅ só este ícone
import { BellIcon } from 'phosphor-react-native'; // ❌ inclui os ~1.500 íconesO Metro não remove código não usado. Um único import pelo índice coloca o Phosphor inteiro no bundle: medimos 5,3 MB, 72% do bundle de produção do app de exemplo. A lib já importa ícone por ícone e, sem o índice, o Phosphor cai para 50 KB.
Por isso o GrafitProvider não configura o IconContext do Phosphor; ele só existe no índice do pacote. Os componentes da lib já aplicam weight="bold" e as cores do tema. Para ícones do app, prefira o Icon da lib (<Icon icon={BellIcon} color="primary" />).
Lendo o tema
const theme = useGrafitTheme();
theme.color.primary; // cor da marca no modo atual
theme.color.onPrimary_soft; // conteúdo sobre primary_soft
theme.spacing.x200; // 16
theme.radius.full; // 999
theme.font.label_md; // { fontFamily, fontSize, lineHeight }
theme.stroke.input; // { width, style, color }
theme.transition.natural_fast; // { duration, delay, timingFunction }
theme.scheme; // 'light' | 'dark'
theme.mode; // 'light' | 'dark' | 'lightHC' | 'darkHC'useTheme() é um alias de useGrafitTheme(). O tema também expõe colors e fonts no formato da v1 (derivados do Grafit, @deprecated) para apps em migração.
Helpers para styled-components
| Helper | Gera |
|---|---|
| typography(theme.font.label_md) | font-family, font-size e line-height do papel |
| inputTypography(theme.font.paragraph_md) | Igual, sem line-height (TextInput de uma linha) |
| strokeBorder(theme.stroke.input) | border-width, border-style e border-color |
| toEasing(theme.easing.natural) | Easing.bezier do Reanimated |
| toTimingConfig(theme.transition.enter) | { duration, easing } para withTiming |
| typographyStyle(theme.font.label_md) | Objeto de estilo (fora do styled) |
export const Title = styled.Text`
${({ theme }) => typography(theme.font.header_sm)}
color: ${({ theme }) => theme.color.foreground_high};
`;Overrides
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| color | Partial<GrafitThemedTokens['color']> | | |
| stroke | { [Key in GrafitStrokeToken]?: Partial<GrafitStroke> } | | |
| fontFamily | Partial<Record<GrafitFontWeightName, string>> | | Troca a família de fonte por peso (ex.: app que ainda não embarcou a Inter). |
| extension | Partial<GrafitLibraryExtension> | | |
createGrafitTheme({ brand, scheme, highContrast, overrides }) monta o mesmo tema fora do React (testes, Storybook, theme do provider).
Aparências: tone, variant e size
Todos os componentes interativos e de feedback falam a mesma língua:
| Prop | Valores | Uso |
|---|---|---|
| tone | primary, secondary, success, warning, danger, neutral | Cor/intenção |
| variant | solid, soft, strong, outline, ghost | Peso visual |
| size | sm, md, lg | Altura (40/48/56), padding, papel tipográfico e tamanho do ícone |
| variant | Fundo | Conteúdo |
|---|---|---|
| solid | color.{tone} | color.on{Tone} |
| soft | color.{tone}_soft | color.on{Tone}_soft |
| strong | color.{tone}_strong | color.on{Tone}_strong |
| outline | sem fundo + stroke.button | color.on{Tone}_soft |
| ghost | sem fundo | color.on{Tone}_soft |
neutralusa as superfícies:surface_medium(solid),surface_low(soft),surface_high(strong), com conteúdoforeground_high.- Desabilitado é sempre
surface_mediumcomforeground_low. - O resolvedor é exportado (
resolveToneTokens(tone, variant)), para componentes do app seguirem a mesma tabela.
Liquid Glass
A lib não importa nenhum módulo nativo de vidro. O app entrega a view:
import { LiquidGlassView, isLiquidGlassSupported } from '@callstack/liquid-glass';
const glass = useMemo(
() => ({ View: LiquidGlassView, isSupported: isLiquidGlassSupported, enabled: true, intensity: 'regular' }),
[],
);| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| View * | ComponentType<LiquidGlassViewLikeProps> | | |
| isSupported * | boolean | | |
| enabled | boolean | | Liga/desliga o vidro no app inteiro (ex.: preferência do usuário). Padrão: true. |
| intensity | GlassIntensity | | Intensidade do vidro: material + opacidade da tinta surface_high. Padrão: regular. |
Quais componentes viram vidro
O vidro só fica bom quando há conteúdo passando por trás. Vidro sobre fundo liso ou vidro sobre vidro fica estranho. Por isso, só superfícies flutuantes são elegíveis:
| Componente | Vidro | Comportamento |
|---|---|---|
| NavBar, CustomNavBar | ✅ automático | Flutua sobre o conteúdo (type="secondary" vira cápsula) |
| AnimatedHeader (AnimatedScrollablePage, PagedList) | ✅ automático | Cabeçalho que aparece ao rolar |
| PageFooter | ✅ automático | Cápsula com a ação principal acima da safe area |
| FloatingTabBar | ✅ automático | Barra e indicador de vidro |
| BottomSheet, Sheet | ✅ automático | Folha sobre a tela |
| Toast | ✅ automático | Pílula flutuante |
| IconButton, Stencil | ☑️ opt-in (glass) | Para uso sobre mapa, foto ou lista |
| PhotoSlot | interno | Legenda e botão de remover sobre a foto |
| Botões de texto, campos, cards, listas, menus, tags, avisos, estados, containers | ❌ | Ficam no fluxo do conteúdo, sobre fundo liso |
- Todos os componentes funcionam com o vidro ligado no app; só os elegíveis mudam de aparência.
glass={false}desliga o vidro só naquele componente.- Leitura:
readabledeixa o vidro quase opaco (materialregularcom fundo sólidosurface_high, ignorando aintensitydo provider) para textos que precisam ser lidos sobre qualquer fundo. Já vem ligado noToast, noSheete noBottomSheet(passereadable={false}para o vidro normal); nos demais é opt-in. Sem vidro nativo não muda nada, porque o fallback já é opaco. - Sem suporte (Android ou iOS anterior ao 26): o componente vira uma superfície opaca
surface_lowcom bordastroke_medium, mantendo o layout flutuante. A tela é a mesma em todas as plataformas. - Recuo do conteúdo: barras flutuantes informam a altura por
onHeightChange; repasse paracontentInsetTop/contentInsetBottomdoScrollablePage.
Glass e useGlass
Para superfícies de vidro próprias do app:
const { isGlass, isNativeGlass } = useGlass(); // padrão do provider, ou useGlass(false)
<Glass interactive fallbackColor="background" style={styles.bubble}>
<Text>Sobre o mapa</Text>
</Glass>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| fallbackColor | Extract<GrafitColorToken, 'background' \| 'surface_low' \| 'surface_medium' \| 'surface_high'> | 'surface_low' | Cor opaca usada quando o Liquid Glass não está disponível. |
| interactive | boolean | false | Vidro reage ao toque (efeito interativo do iOS 26). |
| readable | boolean | false | Vidro quase opaco para texto que precisa ser lido sobre qualquer fundo (ex.: toast, sheet). Ignora a intensidade do GrafitProvider. |
| style | StyleProp<ViewStyle> | | |
| children | ReactNode | | |
Também aceita as props de ViewProps.
Componentes
Convenções de todas as props:
- Ícones são componentes do Phosphor:
icon={BellIcon}. O peso é semprebold. - Cores são nomes de token:
color="foreground_low",iconColor="primary". Nunca hex. - Props marcadas como Obsoleto existem para compatibilidade com a v1.
*na tabela = obrigatória.
Navegação
NavBar
Barra de topo com título, voltar e ações. Com vidro, flutua sobre o conteúdo.
<NavBar
title="Pedido #1234"
subtitle={<StatusPill tone="success">Pago</StatusPill>}
onBack={navigation.goBack}
rightComponent={<NavBar.Action icon={ShareIcon} onPress={share} accessibilityLabel="Compartilhar" />}
onHeightChange={setHeaderHeight}
>
<ProgressBar progress={60} showValue={false} size="sm" />
</NavBar>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title | string | | |
| subtitle | ReactNode | | |
| onBack | () => void | | Atalho para um NavBar.BackAction à esquerda. |
| leftComponent | ReactNode | | |
| rightComponent | ReactNode | | |
| children | ReactNode | | Conteúdo abaixo da linha do título (ex.: ProgressBar, busca). |
| glass | boolean | | Liquid Glass. A barra passa a flutuar sobre o conteúdo; use onHeightChange para recuar a tela. Padrão: segue o GrafitProvider. |
| readable | boolean | | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
| type | 'primary' \| 'secondary' | 'primary' | primary (ponta a ponta) ou secondary (cápsula flutuante). |
| flat | boolean | true | Remove a divisória inferior da barra opaca. Padrão: true. |
| safeArea | boolean | true | |
| onHeightChange | (height: number) => void | | |
| textStyle | StyleProp<TextStyle> | | |
| containerStyle | StyleProp<ViewStyle> | | |
NavBar.Action (= NavBarAction) e NavBar.BackAction (= NavBarBackAction):
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| icon * | IconComponent | | |
| onPress | () => void | | |
| color | GrafitColorToken | 'foreground_high' | |
| size | number | | |
| accessibilityLabel | string | | |
| disabled | boolean | false | |
| testID | string | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| onPress | () => void | | |
| color | GrafitColorToken | 'foreground_high' | |
| size | number | | |
| accessibilityLabel | string | 'Voltar' | |
| testID | string | | |
CustomNavBar
Barra com três slots livres, para busca, logo ou seletor no centro.
<CustomNavBar
leftComponent={<NavBar.BackAction onPress={goBack} />}
middleComponent={<TextInput size="sm" placeholder="Buscar" leftIcon={MagnifyingGlassIcon} />}
/>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| leftComponent | ReactNode | | |
| middleComponent | ReactNode | | |
| rightComponent | ReactNode | | |
| children | ReactNode | | |
| glass | boolean | | |
| readable | boolean | | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
| type | 'primary' \| 'secondary' | 'primary' | |
| flat | boolean | false | |
| safeArea | boolean | true | |
| onHeightChange | (height: number) => void | | |
| containerStyle | StyleProp<ViewStyle> | | |
AnimatedHeader
Cabeçalho que recebe uma opacidade animada (o AnimatedScrollablePage e o PagedList já o controlam).
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| opacity | Animated.AnimatedInterpolation<number> \| Animated.Value | | |
| title | string | | |
| glass | boolean | | |
| readable | boolean | false | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
| safeArea | boolean | true | Recua o título para fora da status bar e da Dynamic Island quando o cabeçalho encosta no topo da tela. Mede a posição na janela, então não soma o recuo se o pai já aplica a safe area. Padrão: true. |
| style | StyleProp<ViewStyle> | | |
PageFooter
Rodapé fixo com a ação principal. Com vidro, vira uma cápsula flutuante acima da safe area.
<PageFooter onHeightChange={setFooterHeight}>
<Button text="Confirmar pagamento" onPress={pay} />
<Button text="Cancelar" variant="ghost" onPress={cancel} />
</PageFooter>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| glass | boolean | | Liquid Glass: cápsula flutuante acima da safe area, sobre o conteúdo que rola. Padrão: segue o GrafitProvider. |
| readable | boolean | false | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
| safeArea | boolean | true | |
| onHeightChange | (height: number) => void | | |
| style | StyleProp<ViewStyle> | | |
| children * | ReactNode | | |
FloatingTabBar
Tab bar em cápsula flutuante, compatível com o tabBar do @react-navigation/bottom-tabs. Ao tocar em qualquer ponto da barra, o indicador vai até o dedo, acompanha o arraste (a aba sob ele já fica destacada) e encaixa na aba mais próxima ao soltar, como no Oficinas e no Credenciado. O gesto roda na thread de UI com o react-native-gesture-handler (peer), então o app precisa do GestureHandlerRootView na raiz. O leitor de tela seleciona as abas pela ação activate. tabBarBadge aparece como badge.
<Tab.Navigator tabBar={props => <FloatingTabBar {...props} />} screenOptions={{ headerShown: false }}>
<Tab.Screen name="Home" component={Home} options={{ title: 'Início', tabBarIcon: ({ color, size }) => <HouseIcon color={color} size={size} /> }} />
</Tab.Navigator>
// no fim de cada tela das abas
<FloatingTabBarSafeArea />
// ou em listas
const { contentBottomInset } = useFloatingTabBarInsets();| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| state * | { index: number; routes: TabRoute[] } | | |
| descriptors * | Record<string, { options: TabOptions }> | | |
| navigation * | TabNavigation | | |
| glass | boolean | | Liquid Glass. Padrão: segue o GrafitProvider. |
| readable | boolean | false | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
StatusBar
Status bar que acompanha o modo do tema (light-content no escuro, dark-content no claro). Aceita as props do StatusBar do React Native.
<StatusBar />Containers e layout
PageWrapper
Container raiz de toda tela (flex: 1, fundo background).
<PageWrapper safeArea edges={['top']}>{/* … */}</PageWrapper>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| safeArea | boolean | false | Respeita a safe area (todas as bordas ou as informadas em edges). |
| edges | Edge[] | | |
| background | Extract<GrafitColorToken, 'background' \| 'canvas' \| 'surface_low'> | 'background' | Fundo da página. Padrão: background. |
| children | ReactNode | | |
Também aceita as props de ViewProps.
ScrollablePage
ScrollView da página, com padding lateral x300, gap x200 entre os filhos e recuos para barras flutuantes. Aceita ref e as props de ScrollView.
<ScrollablePage contentInsetTop={headerHeight} refreshControl={<RefreshControl refreshing={loading} onRefresh={reload} />}>
<Card title="Saldo" />
<Card title="Extrato" />
</ScrollablePage>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| children | ReactNode | | |
Também aceita as props de ScrollViewProps, PageInsets.
AnimatedScrollablePage
ScrollablePage com um AnimatedHeader que aparece ao rolar.
<AnimatedScrollablePage title="Configurações">{/* … */}</AnimatedScrollablePage>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title | string | | Título do cabeçalho que aparece ao rolar. |
| glass | boolean | | |
| readable | boolean | | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
| containerStyle | StyleProp<ViewStyle> | | |
| children | ReactNode | | |
Também aceita as props de ScrollViewProps, PageInsets.
PagedList
FlatList com paginação infinita: chama onEndReached uma vez por página, enquanto page < totalPages.
<PagedList
data={orders}
page={page}
totalPages={totalPages}
onEndReached={() => fetchPage(page + 1)}
keyExtractor={order => order.id}
renderItem={({ item }) => <ListItem title={item.title} description={item.date} onPress={() => open(item)} />}
ListEmptyComponent={<EmptyState icon={ReceiptIcon} title="Nenhum pedido" />}
/>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| data * | Item[] | | |
| page * | number | | Página atual (começando em 1). |
| totalPages * | number | | |
| onEndReached | () => void | | Chamado ao chegar no fim da lista enquanto houver páginas. |
| useHeader | boolean | false | Mostra um AnimatedHeader que aparece ao rolar. |
| title | string | | |
| glass | boolean | | |
| readable | boolean | | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
Também aceita as props de Omit<FlatListProps<Item>, 'data'>.
SectionCardHeader, SectionCardItem, SectionCardBox
Seções agrupadas em cards, no estilo de ajustes do iOS. Itens consecutivos formam um único card com divisórias.
<SectionCardHeader title="Veículo" right={<TextButton onPress={edit}>Editar</TextButton>} />
<SectionCardItem first>
<ListItem title="Placa" rightComponent={<Stencil>abc1d23</Stencil>} divider={false} />
</SectionCardItem>
<SectionCardItem last>
<ListItem title="Modelo" description="Onix 1.0" divider={false} />
</SectionCardItem>
<SectionCardBox>
<Paragraph>Conteúdo livre dentro de um card de seção.</Paragraph>
</SectionCardBox>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title * | string | | |
| right | ReactNode | | |
| style | StyleProp<ViewStyle> | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| first | boolean | false | Primeiro item do grupo: arredonda o topo e remove a divisória. |
| last | boolean | false | Último item do grupo: arredonda a base. |
| inset | boolean | true | Recuo lateral de spacing.x200. Padrão: true. |
| style | StyleProp<ViewStyle> | | |
| children | ReactNode | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| inset | boolean | true | |
| style | StyleProp<ViewStyle> | | |
| children | ReactNode | | |
Spacer
<Spacer size="x300" /> // token
<Spacer height={headerHeight} /> // altura medida em runtime| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| size | GrafitSpacingToken | | Token de spacing para a altura. |
| height | number | | Altura em pontos (ex.: altura medida de um NavBar de vidro). |
| width | number | 0 | |
Divider e SectionBand
<Divider gap="x200" />
<Divider vertical />
<SectionBand /> // faixa `canvas` de ponta a ponta entre blocos| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| gap | GrafitSpacingToken | | Espaço antes e depois da linha (token de spacing). |
| vertical | boolean | false | |
| style | StyleProp<ViewStyle> | | |
Sheet
Folha inferior com altura pelo conteúdo, alça, título e descrição. Sobe com o teclado, fecha pelo fundo e pelo botão voltar do Android, e é animada com transition.enter/exit.
<Sheet visible={visible} onClose={close} title="Filtrar" description="Escolha o período">
<SegmentedControl options={PERIODS} selected={period} onChange={setPeriod} />
<Button text="Aplicar" onPress={apply} />
</Sheet>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| visible * | boolean | | |
| onClose * | () => void | | |
| title | string | | |
| description | string | | |
| dismissible | boolean | true | Permite fechar pelo fundo e pelo botão voltar. Padrão: true. |
| glass | boolean | | Liquid Glass no painel. Padrão: segue o GrafitProvider. |
| readable | boolean | true | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: true. |
| closeAccessibilityLabel | string | 'Fechar' | |
| children | ReactNode | | |
BottomSheet e BottomSheetContainer
Folha de altura fixa embutida na tela, sem Modal, para conteúdo persistente sobre um mapa, por exemplo. Para diálogos, prefira Sheet.
<BottomSheet visible={visible} height={320} onBackdropPress={close}>
<BottomSheetContainer>{/* … */}</BottomSheetContainer>
</BottomSheet>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| visible * | boolean | | |
| height * | number | | Altura do conteúdo (a safe area inferior é somada). |
| showBackdrop | boolean | true | |
| onBackdropPress | () => void | | |
| glass | boolean | | Liquid Glass na folha. Padrão: segue o GrafitProvider. |
| readable | boolean | true | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: true. |
| style | StyleProp<ViewStyle> | | |
| backdropStyle | StyleProp<ViewStyle> | | |
| backdropAccessibilityLabel | string | 'Fechar' | |
| children | ReactNode | | |
RefreshControl
RefreshControl com as cores do tema; aceita as props do RefreshControl do React Native.
<ScrollablePage refreshControl={<RefreshControl refreshing={loading} onRefresh={reload} />} />Texto, ícones e identificadores
Text e presets
variant é o papel tipográfico do Grafit e color é o token. O peso vem do papel; nunca use fontWeight.
<Text variant="display_md" color="primary">R$ 1.250,00</Text>
<H1>Título de página</H1> // header_lg
<H2>Seção principal</H2> // header_md
<H3>Subseção</H3> // header_sm
<Paragraph>Texto corrido.</Paragraph> // paragraph_md, foreground_medium
<Description>Texto auxiliar.</Description> // paragraph_sm, foreground_low
<Medium>Rótulo curto</Medium> // label_sm, foreground_medium
<Caption>Legenda</Caption> // caption_lg, foreground_low| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| variant | GrafitFontToken | 'paragraph_md' | Papel tipográfico do Grafit. Padrão: paragraph_md. |
| color | GrafitColorToken | 'foreground_high' | Token de cor do texto. Padrão: foreground_high. |
| align | 'auto' \| 'left' \| 'right' \| 'center' \| 'justify' | 'auto' | |
| children | ReactNode | | |
Também aceita as props de NativeTextProps.
Icon
<Icon icon={BellIcon} size={24} color="danger" />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| icon * | IconComponent | | Ícone do Phosphor, ex.: import { BellIcon } from 'phosphor-react-native'. |
| size | number | | Tamanho em pontos. Padrão: spacing.x250 (20). |
| color | GrafitColorToken | 'foreground_high' | Token de cor. Padrão: foreground_high. |
Também aceita as props de Omit<PhosphorIconProps, 'color' | 'weight' | 'size'>.
IconTile
Ícone sobre um quadrado tonal.
<IconTile icon={GasPumpIcon} tone="warning" />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| icon * | IconComponent | | |
| tone | GrafitTone | 'primary' | Cor. Padrão: primary. |
| variant | Extract<GrafitVariant, 'solid' \| 'soft' \| 'strong'> | 'soft' | Aparência. Padrão: soft. |
| size | number | | Lado do quadrado. Padrão: spacing.x500 (40). |
| iconSize | number | | |
| style | StyleProp<ViewStyle> | | |
Avatar
Foto ou iniciais (primeira e última palavra do nome).
<Avatar name="Maria da Silva" />
<Avatar source={{ uri: user.photo }} size={64} ring />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| name | string | '' | Nome usado para as iniciais (primeira e última palavra). |
| source | ImageSourcePropType | | |
| size | number | | Diâmetro. Padrão: spacing.x600 (48). |
| tone | AvatarTone | 'accent' | |
| ring | boolean | false | Contorno fino na cor do conteúdo. |
| style | StyleProp<ViewStyle> | | |
Stencil
Identificador em "carimbo": placa, número de OS, código.
<Stencil>abc1d23</Stencil> // ABC1D23
<Stencil variant="os" tone="accent">4821</Stencil> // #4821
<Stencil glass size="lg">abc1d23</Stencil> // sobre uma foto| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| children * | string \| number | | |
| variant | StencilVariant | 'plate' | plate (placa, em maiúsculas) ou os (número com #). |
| tone | StencilTone | 'default' | inverse é para uso sobre fundo primary. |
| size | GrafitSize | 'md' | |
| glass | boolean | false | Vidro sobre foto ou mapa. Opt-in, padrão false. |
| readable | boolean | false | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
| style | StyleProp<ViewStyle> | | |
Botões e seleção
Button
<Button text="Salvar" onPress={save} />
<Button text="Cancelar" variant="outline" tone="neutral" onPress={cancel} />
<Button text="Excluir" tone="danger" variant="soft" leftIcon={TrashIcon} onPress={remove} />
<Button text="Enviando" loading onPress={send} />
<Button text="Filtro" size="sm" shape="pill" fullWidth={false} onPress={openFilter} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| text * | string | | Texto do botão. |
| onPress * | () => void | | |
| variant | GrafitVariant | | Aparência: solid (padrão), soft, strong, outline ou ghost. |
| tone | GrafitTone | 'primary' | Cor: primary (padrão), secondary, success, warning, danger ou neutral. |
| size | GrafitSize | 'md' | |
| shape | ButtonShape | 'rounded' | |
| fullWidth | boolean | true | Ocupa toda a largura disponível. Padrão: true. |
| leftIcon | IconComponent | | |
| rightIcon | IconComponent | | |
| loading | boolean | false | |
| disabled | boolean | false | |
| light | boolean | false | Obsoleto. Use variant="soft". |
| containerStyle | StyleProp<ViewStyle> | | |
| textStyle | StyleProp<TextStyle> | | |
| accessibilityLabel | string | | |
| testID | string | | |
IconButton
<IconButton icon={PlusIcon} onPress={add} accessibilityLabel="Adicionar" />
<IconButton icon={CrosshairIcon} glass onPress={centerMap} accessibilityLabel="Centralizar" />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| icon * | IconComponent | | |
| onPress * | () => void | | |
| variant | GrafitVariant | | Aparência. Padrão: soft. |
| tone | GrafitTone | 'primary' | |
| size | GrafitSize | 'md' | |
| disabled | boolean | false | |
| glass | boolean | | Botão flutuante de vidro, para uso sobre listas, mapas ou fotos. Opt-in, padrão false: botões dentro do conteúdo não viram vidro. |
| readable | boolean | false | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
| noBackground | boolean | false | Obsoleto. Use variant="ghost". |
| accessibilityLabel | string | | |
| style | StyleProp<ViewStyle> | | |
| testID | string | | |
TextButton
<TextButton onPress={forgot}>Esqueci minha senha</TextButton>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| onPress | () => void | | |
| tone | GrafitTone | 'primary' | |
| color | GrafitColorToken | | Token de cor que substitui o tone (ex.: o on* do fundo). |
| size | GrafitSize | 'md' | |
| disabled | boolean | false | |
| bold | boolean | | Obsoleto. O peso agora vem do papel tipográfico label. |
| style | StyleProp<TextStyle> | | |
| accessibilityLabel | string | | |
| testID | string | | |
| children | ReactNode | | |
TagButton e OptionList
Pílula selecionável e uma lista horizontal rolável delas.
<OptionList
options={[{ text: 'Todos', value: 'all' }, { text: 'Pagos', value: 'paid' }, { text: 'Pendentes', value: 'pending' }]}
selected={selectedIndex}
onChange={setSelectedIndex}
/>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| text * | string | | |
| onPress * | () => void | | |
| active | boolean | false | Selecionado: usa activeVariant no tone. |
| tone | GrafitTone | 'primary' | |
| activeVariant | GrafitVariant | 'solid' | Aparência quando ativo. Padrão: solid. |
| inactiveVariant | GrafitVariant | 'soft' | Aparência quando inativo. Padrão: soft em neutral. |
| icon | IconComponent | | |
| disabled | boolean | false | |
| containerStyle | StyleProp<ViewStyle> | | |
| textStyle | StyleProp<TextStyle> | | |
| testID | string | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| options * | Option[] | | |
| onChange * | (itemIndex: number) => void | | Chamado com o índice da opção escolhida. |
| selected | number | | Índice da opção selecionada. |
| keyExtractor | (option: Option) => string \| number | defaultKeyExtractor | |
| tone | GrafitTone | 'primary' | |
| optionContainerStyle | StyleProp<ViewStyle> | | |
| optionTextStyle | StyleProp<TextStyle> | | |
Também aceita as props de Omit<ScrollViewProps, 'horizontal'>.
SegmentedControl
De 2 a 4 opções mutuamente exclusivas, com indicador deslizante animado. Para muitas opções, use OptionList.
<SegmentedControl
options={[{ label: 'Dia', value: 'day' }, { label: 'Semana', value: 'week' }, { label: 'Mês', value: 'month' }]}
selected={period}
onChange={setPeriod}
/>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| options * | SegmentedControlOption<Value>[] | | |
| selected * | Value | | |
| onChange * | (value: Value) => void | | |
| style | StyleProp<ViewStyle> | | |
ChoiceChip e ColorDot
Opção em pílula com check. ColorDot mostra uma cor cadastrada pelo cliente. É dado, não tema: nunca use essa cor como fundo de texto.
{colors.map(option => (
<ChoiceChip key={option.id} label={option.name} color={option.hex} selected={option.id === value} onPress={() => setValue(option.id)} />
))}| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| label * | string | | |
| selected * | boolean | | |
| onPress * | () => void | | |
| color | string | | Mostra um ColorDot com a cor informada. |
| role | 'radio' \| 'checkbox' | 'radio' | radio (escolha única, padrão) ou checkbox (múltipla). |
| disabled | boolean | false | |
| style | StyleProp<ViewStyle> | | |
SelectableCard
Card marcável (checkbox), com ícone opcional. Em grades de 2 colunas, passe style={{ width: '48%' }}.
<SelectableCard title="Combustível" icon={GasPumpIcon} selected={selected.fuel} onPress={() => toggle('fuel')} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title * | string | | |
| onPress * | () => void | | |
| selected | boolean | false | |
| icon | IconComponent | | |
| disabled | boolean | false | |
| style | StyleProp<ViewStyle> | | Defina a largura aqui (ex.: { width: '48%' } numa grade de 2 colunas). |
Switch
Switch nativo com as cores do Grafit; aceita as props do Switch do React Native.
<ListItem title="Notificações" rightComponent={<Switch value={enabled} onValueChange={setEnabled} />} />Formulários
Todos os campos compartilham a mesma caixa: fundo surface_low, borda stroke.input, anel de foco stroke.input_focus externo (sem deslocar o layout), borda danger no erro e estado desabilitado visual (surface_medium + foreground_low). error aceita boolean ou a mensagem.
Field e PasswordField
Atalho para label + campo + erro + ajuda. É o jeito recomendado de montar formulários.
<Field label="E-mail" required keyboardType="email-address" value={email} onChangeText={setEmail} error={errors.email} hint="Usaremos para enviar o comprovante" />
<Field label="Valor" icon={CurrencyDollarIcon} right={<Text variant="label_sm" color="foreground_low">R$</Text>} />
<PasswordField label="Senha" value={password} onChangeText={setPassword} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| label | string | | |
| required | boolean | false | |
| error | string | | Mensagem de erro abaixo do campo. |
| hint | string | | Texto de ajuda abaixo do campo quando não há erro. |
| icon | TextInputProps['leftIcon'] | | Ícone à esquerda. |
| right | TextInputProps['rightAccessory'] | | Conteúdo à direita (mesmo que rightAccessory). |
| style | StyleProp<ViewStyle> | | Estilo do grupo (label + campo + mensagens). |
Também aceita as props de Omit<TextInputProps, 'error' | 'leftIcon'>.
FormGroup
Label, erro e ajuda em volta de qualquer campo. Repassa error e accessibilityLabel ao filho.
<FormGroup label="Cidade" required error={errors.city}>
<SearchablePicker items={cities} selected={city} onChange={setCity} modalTitle="Cidade" />
</FormGroup>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| label | string | | |
| error | string | | Mensagem de erro. Também é repassada como error para o campo filho. |
| helperText | string | | Texto de ajuda exibido abaixo do campo quando não há erro. |
| required | boolean | false | |
| style | StyleProp<ViewStyle> | | |
| children * | ReactNode | | |
TextInput e TextArea
<TextInput placeholder="Buscar" leftIcon={MagnifyingGlassIcon} showClearButton value={query} onChangeText={setQuery} />
<TextInput placeholder="Senha" secureTextEntry />
<TextArea placeholder="Observações" value={notes} onChangeText={setNotes} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| disabled | boolean | false | |
| error | boolean \| string | false | Estado de erro. Aceita a mensagem do FormGroup. |
| size | FieldSize | 'md' | sm ou md (padrão). small/normal continuam aceitos. |
| showClearButton | boolean | false | |
| onClear | () => void | | |
| leftIcon | IconComponent | | |
| rightAccessory | ReactNode | | Conteúdo à direita do campo (ex.: botão, unidade, ícone de ação). |
| containerStyle | StyleProp<ViewStyle> | | |
| clearAccessibilityLabel | string | 'Limpar campo' | |
| toggleSecretAccessibilityLabel | string | 'Mostrar ou ocultar senha' | |
Também aceita as props de Omit<NativeTextInputProps, 'editable'>.
TextArea aceita as mesmas props, exceto multiline, secureTextEntry e showClearButton.
MaskedTextInput
Campo com máscara (react-native-mask-input).
import { Masks } from 'react-native-mask-input';
<MaskedTextInput mask={Masks.BRL_CPF} value={cpf} onChangeText={(masked, raw) => setCpf(raw)} keyboardType="number-pad" />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| mask | Mask | | |
| value | string | | |
| defaultValue | string | '' | |
| onChangeText | (formatted: string, extracted?: string) => void | | |
| disabled | boolean | false | |
| error | boolean \| string | false | |
| size | FieldSize | 'md' | |
| containerStyle | StyleProp<ViewStyle> | | |
Também aceita as props de Omit<NativeTextInputProps, 'editable' | 'onChangeText' | 'value' | 'defaultValue'>.
InputCurrency
<InputCurrency value={amount} onChangeValue={setAmount} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| value | number \| null | 0 | |
| onChangeValue * | (value: number) => void | | |
| onChangeText | (text: string) => void | | |
| precision | number | 2 | |
| prefix | string | 'R$ ' | |
| delimiter | string | '.' | |
| separator | string | ',' | |
| maxValue | number | DEFAULT_MAX_VALUE | |
| minValue | number | 0 | |
| disabled | boolean | false | |
| error | boolean \| string | false | |
| size | FieldSize | 'md' | |
| style | StyleProp<ViewStyle> | | Obsoleto. Use containerStyle. |
| containerStyle | StyleProp<ViewStyle> | | |
Também aceita as props de Omit<TextInputProps, 'value' | 'editable' | 'onChangeText' | 'defaultValue'>.
Picker
Seleção única numa folha de opções, com busca opcional.
<Picker
items={banks}
selectedValue={bankCode}
onValueChange={setBankCode}
keyExtractor={bank => bank.code}
labelExtractor={bank => bank.name}
valueExtractor={bank => bank.code}
placeholder="Banco"
searchable
/>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| items * | Item[] | | |
| selectedValue | string | | |
| onValueChange * | (itemValue: string) => void | | |
| keyExtractor * | (item: Item) => string | | |
| labelExtractor * | (item: Item) => string | | |
| valueExtractor * | (item: Item) => string | | |
| placeholder | string | 'Selecione uma opção' | |
| title | string | | Título da folha de opções. Padrão: o placeholder. |
| searchable | boolean | false | |
| disabled | boolean | false | |
| loading | boolean | false | |
| error | boolean \| string | false | |
| size | FieldSize | 'md' | |
| style | StyleProp<ViewStyle> | | |
| testID | string | | |
SearchablePicker
Seleção única com busca (sem acento e sem diferença de maiúsculas). Os itens têm o formato { text, value }, configurável por textKey/valueKey.
<SearchablePicker items={cities} selected={cityId} onChange={setCityId} modalTitle="Escolha a cidade" placeholder="Cidade" />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| items * | SearchablePickerItem[] | | |
| selected | string \| number \| null | | Valor (propriedade valueKey) do item selecionado. |
| onChange * | (selected: string \| number) => void | | |
| modalTitle * | string | | |
| keyExtractor | (item: SearchablePickerItem, index: number) => string | | |
| placeholder | string | 'Selecione uma opção' | |
| valueKey | string | 'value' | |
| textKey | string | 'text' | |
| searchPlaceholder | string | 'Buscar' | |
| listTitle | string | | |
| renderBottomContent | ReactNode | | |
| loading | boolean | false | |
| disabled | boolean | false | |
| error | boolean \| string | false | |
| size | FieldSize | 'md' | |
| type | 'primary' \| 'secondary' | 'primary' | primary (campo padrão) ou secondary (campo pill maior). |
| backgroundColor | string | | Obsoleto. O fundo vem do tema (surface_low). |
| style | StyleProp<ViewStyle> | | |
| testID | string | | |
MultiSelect
Seleção múltipla com busca e limite (ao atingir o máximo, substitui a seleção mais antiga).
<MultiSelect items={services} selectedItems={selected} onClose={setSelected} modalTitle="Serviços" maxSelectableItems={3} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| items * | MultiSelectItem[] | | |
| onClose * | (selected: MultiSelectSelection) => void | | Chamado ao fechar a folha, com os itens selecionados (valor → texto). |
| onChange | (selected: MultiSelectSelection) => void | | Chamado a cada alteração da seleção. |
| modalTitle * | string | | |
| keyExtractor | (item: MultiSelectItem, index: number) => string | | |
| maxSelectableItems | number | | Ao atingir o máximo, a seleção mais antiga é substituída. |
| placeholder | string | 'Selecione' | |
| buttonTitle | string | 'Confirmar' | |
| valueKey | string | 'value' | |
| textKey | string | 'text' | |
| searchPlaceholder | string | 'Buscar' | |
| selectedItems | MultiSelectSelection | | |
| disabled | boolean | false | |
| error | boolean \| string | false | |
| size | FieldSize | 'md' | |
| style | StyleProp<ViewStyle> | | |
| testID | string | | |
DateTimePicker
Data, hora ou data+hora:
- iOS: folha com o picker inline; a data só é aplicada no "Confirmar".
- Android: diálogos nativos; no modo
datetime, abre a data e depois a hora.
<DateTimePicker date={date} onDateChange={setDate} placeholder="Data de vencimento" minimumDate={new Date()} />
<DateTimePicker mode="time" date={time} onDateChange={setTime} customFormat="HH'h'mm" />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| date | Date \| null | | Data selecionada. Vazio mostra o placeholder. |
| onChange | (event: DateTimePickerEvent, date?: Date) => void | | Assinatura da v1: recebe o evento nativo e a data escolhida. |
| onDateChange | (date: Date) => void | | Recebe apenas a data confirmada. |
| mode | PickerMode | 'date' | |
| placeholder | string | 'Selecione' | |
| defaultDate | string \| Date | | Data exibida quando date está vazio. |
| customFormat | string | | Formato com tokens dd MM MMM yyyy HH mm. O padrão depende do mode. |
| formatValue | (date: Date) => string | | |
| minimumDate | Date | | |
| maximumDate | Date | | |
| display | IOSDisplay | 'inline' | |
| IOSHeaderTitle | string | 'Selecione uma data' | |
| confirmText | string | 'Confirmar' | |
| cancelText | string | 'Fechar' | |
| locale | string | 'pt-BR' | |
| disabled | boolean | false | |
| error | boolean \| string | false | |
| size | FieldSize | 'md' | |
| containerStyle | StyleProp<ViewStyle> | | |
| testID | string | | |
PhotoSlot
Espaço de foto: vazio (tracejado, para adicionar) ou preenchido (com legenda e botão de remover em vidro).
<PhotoSlot icon={CameraIcon} label="Frente do veículo" hint="Toque para fotografar" uri={photos.front} onAdd={takeFront} onOpen={preview} onRemove={removeFront} />
<PhotoSlot icon={CameraIcon} compact uri={photo} onAdd={take} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| icon * | IconComponent | | Ícone do slot vazio (ex.: CameraIcon). |
| label | string | | |
| hint | string | | |
| uri | string | | URI da foto. Sem URI, mostra o slot vazio. |
| compact | boolean | false | Versão quadrada pequena para grades. |
| onAdd | () => void | | |
| onOpen | () => void | | |
| onRemove | () => void | | |
| readable | boolean | false | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: false. |
| style | StyleProp<ViewStyle> | | |
Listas e menus
ListItem
Linha com título, descrição e slots laterais.
<ListItem
leftComponent={<Avatar name={client.name} size={40} />}
title={client.name}
description={client.document}
rightComponent={<StatusPill tone="success">Ativo</StatusPill>}
onPress={() => open(client)}
/>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title | string | | |
| description | string | | |
| leftComponent | ReactNode | | |
| rightComponent | ReactNode | | |
| onPress | () => void | | |
| divider | boolean | true | Divisória inferior. Padrão: true. |
| titleNumberOfLines | number | 1 | |
| descriptionNumberOfLines | number | 2 | |
| containerStyle | StyleProp<ViewStyle> | | |
| leftContentStyle | StyleProp<ViewStyle> | | |
| rightContentStyle | StyleProp<ViewStyle> | | |
| titleStyle | StyleProp<TextStyle> | | |
| descriptionStyle | StyleProp<TextStyle> | | |
| accessibilityLabel | string | | |
| testID | string | | |
ListMenu, MenuListItem, MenuListSubitem e MenuSeparator
Menu lateral ou de configurações, com seções e itens expansíveis.
<ListMenu
defaultActiveIndex={1}
items={[
{ type: 'separator', text: 'Conta' },
{ type: 'menu-item', text: 'Perfil', icon: UserIcon, onPress: openProfile },
{ type: 'menu-item', text: 'Relatórios', icon: ChartBarIcon, subItems: [{ text: 'Mensal', onPress: openMonthly }] },
]}
/>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| text * | string | | |
| type * | 'separator' \| 'menu-item' | | |
| icon | IconComponent | | |
| subItems | Pick<MenuItem, 'text' \| 'onPress'>[] | | |
| onPress | () => void | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title * | string | | |
| icon | IconComponent | | |
| iconColor | GrafitColorToken | 'foreground_medium' | |
| onPress | () => void | | |
| active | boolean | false | |
| children | ReactNode | | Subitens (MenuListSubitem); o item vira um acordeão. |
| defaultExpanded | boolean | false | |
| containerStyle | StyleProp<ViewStyle> | | |
| textStyle | StyleProp<TextStyle> | | |
| testID | string | | |
Feedback e estados
Card
<Card title="Saldo disponível" icon={WalletIcon} rightContent={<Tag text="Novo" tone="success" />} onPress={openBalance}>
<Text variant="display_sm">R$ 1.250,00</Text>
</Card>
<Card variant="filled" title="Sem borda" />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title | string | | |
| icon | IconComponent | | |
| iconColor | GrafitColorToken | 'primary' | |
| rightContent | ReactNode | | Conteúdo à direita do cabeçalho. |
| onPress | () => void | | Torna o card inteiro clicável. |
| variant | CardVariant | | outlined (padrão), filled ou elevated. Elevação vem das camadas de surface. |
| mode | LegacyCardMode | | Obsoleto. Use variant: bordered → outlined, shadowed → elevated. |
| style | StyleProp<ViewStyle> | | |
| accessibilityLabel | string | | |
| testID | string | | |
| children | ReactNode | | |
Tag e StatusPill
Tag é um rótulo com ícone opcional; StatusPill é um status compacto com ponto colorido.
<Tag text="Combustível" icon={GasPumpIcon} tone="secondary" />
<StatusPill tone="warning">Aguardando aprovação</StatusPill>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| text | string | | |
| icon | IconComponent | | |
| tone | GrafitTone | 'primary' | Cor. Padrão: primary. |
| variant | GrafitVariant | | Aparência. Padrão: soft. |
| mode | TagShape | 'rounded' | Formato: rounded (pill, padrão) ou squared. |
| light | boolean | true | Obsoleto. Use variant="solid" para a tag preenchida. |
| containerStyle | StyleProp<ViewStyle> | | |
| testID | string | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| children * | string | | |
| tone | GrafitTone | 'neutral' | Cor do status. Padrão: neutral. |
| style | StyleProp<ViewStyle> | | |
| testID | string | | |
Notice, NoticeBar e Disclaimer
| Componente | Quando usar |
|---|---|
| Notice | Aviso com título e descrição, opcionalmente clicável (seta) |
| NoticeBar | Mensagem curta de uma linha com botão de fechar |
| Disclaimer | Bloco informativo com título, descrição e conteúdo livre |
<Notice tone="warning" title="Documento pendente" description="Envie a CNH para liberar o cartão." onPress={openDocs} />
<NoticeBar type="success" message="Cartão desbloqueado." onRightButtonClick={dismiss} />
<Disclaimer tone="neutral" title="Importante" description="O limite é renovado todo dia 1º." icon={InfoIcon} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title * | string | | |
| description | string | | |
| tone | GrafitTone | 'primary' | Cor. Padrão: primary. |
| icon | IconComponent | | |
| onPress | () => void | | Torna o aviso clicável e mostra uma seta. |
| style | StyleProp<ViewStyle> | | |
| testID | string | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| message * | string | | |
| type | NoticeType | 'information' | Preset de cor e ícone. Padrão: information. |
| tone | GrafitTone | | Sobrescreve a cor do preset. |
| variant | GrafitVariant | 'soft' | Aparência. Padrão: soft. |
| icon | IconComponent | | |
| showRightButton | boolean | true | |
| rightButtonIcon | IconComponent | XIcon | |
| onRightButtonClick | () => void | | |
| rightButtonAccessibilityLabel | string | 'Fechar aviso' | |
| style | StyleProp<ViewStyle> | | |
| testID | string | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title | string | | |
| description | string | | |
| icon | IconComponent | | |
| tone | GrafitTone | | Cor. Padrão: neutral. |
| variant | GrafitVariant | | Aparência. Padrão: soft. |
| selectColor | LegacyColor | | Obsoleto. Use tone. |
| stateView | LegacyStateView | | Obsoleto. Use variant. |
| style | StyleProp<ViewStyle> | | |
| testID | string | | |
| children | ReactNode | | |
ProgressBar e PageProgressIndicator
<ProgressBar progress={72} label="Perfil completo" /> // fica success em 100%
<PageProgressIndicator numberOfPages={4} current={2} /> // fluxo em etapas| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| progress * | number | | Progresso de 0 a 100. |
| size | GrafitSize | 'md' | |
| color | GrafitColorToken | | Cor de preenchimento. Padrão: primary, e success ao completar. |
| label | string | | |
| showValue | boolean | true | Mostra o percentual. Padrão: true. |
| style | StyleProp<ViewStyle> | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| numberOfPages * | number | | |
| current | number | | Etapa atual, começando em 1. |
| currentPage | number | | Obsoleto. Use current. |
| style | StyleProp<ViewStyle> | | |
StepCard
Onboarding em passos, com paginação; o último passo chama onClose.
<StepCard steps={[{ title: 'Bem-vindo', description: '…' }, { title: 'Pronto', description: '…' }]} onClose={finish} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| steps * | StepCardStep[] | | |
| onClose | () => void | | Chamado ao concluir o último passo. |
| previousButtonText | string | 'Voltar' | |
| nextButtonText | string | 'Próximo' | |
| finishButtonText | string | 'Concluir' | |
| style | StyleProp<ViewStyle> | | |
Timeline
<Timeline
events={[
{ title: 'Pedido criado', description: '10/10 às 09:12', status: 'completed' },
{ title: 'Pagamento recusado', status: 'failed' },
{ title: 'Aguardando nova tentativa', status: 'pending' },
]}
/>| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| title * | string | | |
| description | string | | |
| status * | TimelineStatus | | |
Skeleton e SkeletonRow
Placeholder pulsante (easing.linear + duration.loop).
{loading ? [1, 2, 3].map(key => <SkeletonRow key={key} />) : items.map(renderItem)}
<Skeleton width="60%" height={24} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| width | DimensionValue | '100%' | Largura em pontos ou percentual. Padrão: 100%. |
| height | number | | Altura. Padrão: spacing.x200. |
| radius | number | | Raio. Padrão: radius.x100. |
| style | StyleProp<ViewStyle> | | |
EmptyState, StateView e ErrorState
| Componente | Quando usar |
|---|---|
| EmptyState | Lista ou seção sem conteúdo (ícone ou ilustração, texto e ação) |
| StateView | Estado ilustrado de tela cheia (emblema com anéis, ícones satélite e até 2 ações) |
| ErrorState | StateView de erro; o texto por tipo de erro vem do app via resolveCopy |
<EmptyState icon={ReceiptIcon} title="Nenhum pedido" description="Seus pedidos aparecem aqui." buttonText="Fazer pedido" onButtonPress={create} />
<StateView
icon={WifiSlashIcon}
tone="warning"
eyebrow="Offline"
title="Sem conexão"
description="Verifique sua internet."
satellites={[CloudIcon, BellIcon]}
primaryAction={{ text: 'Tentar novamente', onPress: retry }}
/>
<ErrorState error={error} resolveCopy={getErrorStateCopy} onRetry={refetch} />| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| icon | IconComponent | | |
| iconTone | GrafitTone | 'neutral' | |
| imageSource | ImageSourcePropType | | Ilustração no lugar do ícone. |
| title | string | | |
| description | string | | |
| buttonText | string | | |
| onButtonPress | () => void | | |
| style | StyleProp<ViewStyle> | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| icon * | IconComponent | | |
| title * | string | | |
| tone | StateViewTone | 'primary' | |
| eyebrow | string | | Texto curto acima do título (ex.: "Sem conexão"). |
| description | string | | |
| context | string | | Pílula de contexto abaixo da descrição (ex.: código do erro). |
| satellites | IconComponent[] | [] | Até 3 ícones decorativos ao redor do emblema. |
| primaryAction | StateViewAction | | |
| secondaryAction | StateViewAction | | |
| style | StyleProp<ViewStyle> | | |
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| error | unknown | | Erro original, repassado para resolveCopy. |
| resolveCopy | (error: unknown) => ErrorStateCopy | | Converte o erro no texto da tela (ex.: o getErrorStateCopy do app). |
| title | string | | Sobrescreve o título vindo da cópia. |
| onRetry | () => void | | |
| retryText | string | 'Tentar novamente' | |
| style | StyleProp<ViewStyle> | | |
Toast e useToast
const { show, hide } = useToast();
show({ message: 'Pagamento aprovado', type: 'success' });
show({ message: 'Sem conexão', type: 'danger', buttonText: 'Tentar', onButtonPress: retry });
show({ message: 'Item removido', type: 'neutral', placement: 'bottom' });O toast aparece no topo, abaixo da status bar. Para mudar a posição no app inteiro, use <ToastProvider placement="bottom">; o placement de cada show sobrescreve o do provider. Embaixo, ele fica acima da safe area e pode cobrir rodapés e tab bars. Com Liquid Glass, o toast usa sempre o vidro readable.
A duração padrão é de 50 ms por caractere, entre duration.ambient (1,5 s) e duration.toast (5 s). Mostrar outro toast reinicia o tempo. O leitor de tela anuncia a mensagem.
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
| message * | string | | |
| type | ToastType | | |
| visible | boolean | | |
| duration | number | | Tempo visível em ms. Padrão: 50ms por caractere, entre duration.ambient e duration.toast. |
| buttonText | string | | |
| onButtonPress | () => void | | |
| glass | boolean | | Liquid Glass. Padrão: segue o GrafitProvider. |
| placement | ToastPlacement | | Onde o toast aparece: top (abaixo da status bar) ou bottom (acima da safe area). Padrão: top. |
| readable | boolean | | Vidro quase opaco para facilitar a leitura (ver Glass). Padrão: true. |
Utilitários
| Export | Uso |
|---|---|
| createGrafitTheme(options) | Tema fora do React |
| resolveToneTokens(tone, variant) | Tokens de fundo/conteúdo da tabela de aparências |
| useGlass(glass?) | { isGlass, isNativeGlass } para componentes próprios |
| formatDate(date, pattern) | Datas pt-BR com tokens dd MM MMM MMMM yyyy HH mm ss; literais entre aspas simples |
| getInitials(name) | Iniciais usadas pelo Avatar |
| grafitThemes, grafitShared | Tokens crus (gerados a partir do DTCG) |
Dependências e versões duplicadas
- Toda dependência nativa ou singleton é
peerDependency.dependenciessó tem JS puro (react-native-mask-input,react-native-currency-input). - O
prepublishOnlyrodascripts/check-dependencies.js, que bloqueia o publish se um módulo nativo ou singleton entrar emdependencies, direta ou transitivamente. - Os tipos publicados (
.d.ts) não referenciam tipos privados do React Native, então funcionam do RN 0.78 ao 0.87.
Ao atualizar para a v2, remova os resolutions de react-native-svg, react-native-safe-area-context, react-native-gesture-handler, react-native-device-info e styled-components que existiam por causa da lib.
Desenvolvimento
Requer Node ≥ 22.11 (exigência do React Native 0.87 do app de exemplo).
yarn install
yarn typecheck # tsc strict (lib + exemplo)
yarn lint
yarn test # tema + render de todos os componentes em várias marcas/modos, com e sem vidro
yarn docs # regenera as tabelas de props deste README a partir dos tipos
yarn tokens # regenera src/theme/grafit/tokens.ts a partir de .claude/dtcg/dtcg
yarn check:peers # garante que nada nativo/singleton vaze para dependencies
yarn verify # todas as checagens acima (rode antes de publicar)
yarn prepare # build (bob): lib/commonjs, lib/module, lib/typescript
yarn start | yarn ios | yarn android # app de exemplo (roda dentro de example/)lib/é gerado nopreparee não é versionado.O app de exemplo fica inteiro em
example/(App.tsx,android/,ios/, Metro e Babel) e usa onode_modulesda raiz, sem instalação própria. No iOS, rodecd example/ios && bundle install && bundle exec pod install. Ao trocar a versão de uma dependência nativa nasdevDependencies, atualize tambémexample/package.json, que o autolinking lê.Componente novo: crie a pasta com
index.tsx+styles.ts, exporte emsrc/index.ts, adicione aoShowcasedesrc/__tests__/components.test.tsxe documente aqui com um marcador<!-- props:NomeProps -->.Publicação no NPM
npm publish