@aurea-uds/native 0.8.5 → 0.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/inputs.js CHANGED
@@ -40,6 +40,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
40
40
  import * as React from "react";
41
41
  import { Animated, KeyboardAvoidingView as KeyboardAvoidingViewRN, Modal, Platform, Pressable, ScrollView, TextInput, View, } from "react-native";
42
42
  import { comOpacidade, criarFolha } from "./estilos.js";
43
+ import { FilaRolante } from "./rolagem.js";
43
44
  import { IconButton } from "./actions.js";
44
45
  import { Icon } from "./icon.js";
45
46
  import { useReduceMotion } from "./movimento.js";
@@ -415,30 +416,30 @@ export function SegmentedControl({ items, value, onChange, label, disabled, styl
415
416
  const s = folha(t);
416
417
  const campo = useCampo();
417
418
  const inativo = disabled ?? campo?.disabled;
418
- return (_jsx(View, { accessibilityRole: "radiogroup", accessibilityLabel: label ?? campo?.label, style: [s.segmentada, inativo && s.desabilitado, style], ...rest, children: items.map((it) => {
419
- const ativo = it.value === value;
420
- return (_jsxs(Pressable, { onPress: inativo ? undefined : () => onChange?.(it.value), disabled: inativo, accessibilityRole: "radio", accessibilityState: { checked: ativo, disabled: !!inativo }, accessibilityLabel: typeof it.label === "string" ? it.label : undefined, style: [s.segmento, ativo && s.segmentoAtivo], children: [typeof it.label === "string"
421
- ? _jsx(Text, { size: "sm", weight: ativo ? 600 : 400,
422
- // 🔴 ERA `foreground`, E ISSO QUEBRAVA A LINGUAGEM DE SELECIONADO.
423
- // Achado pelo consumidor em 17/09/2026. A web pinta o escolhido com a
424
- // MARCA (`aurea.css:937-938`), e o comentário de cima daquela regra diz
425
- // que nav ativo, aba ativa, item de lista e botão ligado mostram o mesmo
426
- // sinal — ordem do Victor, 24/07. O `SegmentedControl` daqui discordava
427
- // da web **e do `BottomNav` deste mesmo pacote**, que já usa este token.
428
- //
429
- // ⚠ **`primaryEmphasis` e NÃO `primary`, e a razão está no CSS:** *"sem
430
- // fundo, a marca vira TEXTO, e o amarelo puro"* não se lê no claro
431
- // (`aurea.css:358`). É a mesma lição que a `0.8.3` pagou no `Button`.
432
- // No escuro os dois valem o mesmo amarelo; no claro este aprofunda.
433
- //
434
- // ⚠ **Uma divergência declarada, não escondida:** para o `.segmented` a
435
- // web usa uma mistura própria (`primary 45% + foreground`) em vez do
436
- // token, enquanto para o `.bottom-nav` usa o token. São dois âmbares
437
- // parecidos para a MESMA linguagem, e isso é incoerência DA WEB. Escolhi
438
- // o token, que deixa os dois componentes daqui iguais entre si.
439
- style: { color: ativo ? t.color.primaryEmphasis : t.color.mutedForeground }, children: it.label })
440
- : it.label, ativo && _jsx(View, { style: s.fioDoSegmento })] }, it.value));
441
- }) }));
419
+ return (_jsx(FilaRolante, { children: _jsx(View, { accessibilityRole: "radiogroup", accessibilityLabel: label ?? campo?.label, style: [s.segmentada, inativo && s.desabilitado, style], ...rest, children: items.map((it) => {
420
+ const ativo = it.value === value;
421
+ return (_jsxs(Pressable, { onPress: inativo ? undefined : () => onChange?.(it.value), disabled: inativo, accessibilityRole: "radio", accessibilityState: { checked: ativo, disabled: !!inativo }, accessibilityLabel: typeof it.label === "string" ? it.label : undefined, style: [s.segmento, ativo && s.segmentoAtivo], children: [typeof it.label === "string"
422
+ ? _jsx(Text, { size: "sm", weight: ativo ? 600 : 400,
423
+ // 🔴 ERA `foreground`, E ISSO QUEBRAVA A LINGUAGEM DE SELECIONADO.
424
+ // Achado pelo consumidor em 17/09/2026. A web pinta o escolhido com a
425
+ // MARCA (`aurea.css:937-938`), e o comentário de cima daquela regra diz
426
+ // que nav ativo, aba ativa, item de lista e botão ligado mostram o mesmo
427
+ // sinal — ordem do Victor, 24/07. O `SegmentedControl` daqui discordava
428
+ // da web **e do `BottomNav` deste mesmo pacote**, que já usa este token.
429
+ //
430
+ // ⚠ **`primaryEmphasis` e NÃO `primary`, e a razão está no CSS:** *"sem
431
+ // fundo, a marca vira TEXTO, e o amarelo puro"* não se lê no claro
432
+ // (`aurea.css:358`). É a mesma lição que a `0.8.3` pagou no `Button`.
433
+ // No escuro os dois valem o mesmo amarelo; no claro este aprofunda.
434
+ //
435
+ // ⚠ **Uma divergência declarada, não escondida:** para o `.segmented` a
436
+ // web usa uma mistura própria (`primary 45% + foreground`) em vez do
437
+ // token, enquanto para o `.bottom-nav` usa o token. São dois âmbares
438
+ // parecidos para a MESMA linguagem, e isso é incoerência DA WEB. Escolhi
439
+ // o token, que deixa os dois componentes daqui iguais entre si.
440
+ style: { color: ativo ? t.color.primaryEmphasis : t.color.mutedForeground }, children: it.label })
441
+ : it.label, ativo && _jsx(View, { style: s.fioDoSegmento })] }, it.value));
442
+ }) }) }));
442
443
  }
443
444
  /**
444
445
  * A escolha de um de muitos.
package/dist/layout.d.ts CHANGED
@@ -55,11 +55,34 @@ export interface SeparatorProps extends ViewProps {
55
55
  * comportamento certo.
56
56
  */
57
57
  export declare function Separator({ orientation, label, style, ...rest }: SeparatorProps): React.JSX.Element;
58
- export type AureaCardVariant = "base" | "raised" | "interactive" | "inset" | "selected" | "danger";
59
- export interface CardProps extends ViewProps {
60
- variant?: AureaCardVariant;
58
+ export type AureaCardVariant = "base" | "raised" | "interactive" | "inset" | "selected" | "danger" | "brand";
59
+ interface CardBase extends ViewProps {
61
60
  children?: React.ReactNode;
62
61
  }
62
+ /**
63
+ * 🔴 **O `brand` EXIGE `action`, e isso é de propósito — a regra é do TIPO, não de documento.**
64
+ *
65
+ * O cartão na cor da marca entrou em 17/09/2026 e é a **primeira superfície grande preenchida
66
+ * com o amarelo** neste sistema. Até aqui ele só pintava coisa pequena: o ponto do contador, a
67
+ * pílula do passo atual, o link de pular para o conteúdo. Um cartão amarelo é precedente, e
68
+ * precedente sem trava vira decoração — outras telas pedem o mesmo e o amarelo deixa de
69
+ * significar "aja aqui".
70
+ *
71
+ * A regra combinada foi *"o cartão amarelo é só para pedir uma ação da pessoa"*. **Escrita num
72
+ * documento, ela apagaria** — este repositório tem registro disso em cada arquivo, e a §"Afirmação
73
+ * velha" do `CLAUDE.md` existe por causa desse desgaste. Então ela mora no tipo: sem `action`, o
74
+ * `brand` não compila. É a mesma escolha que faz o `label` do `IconButton` ser obrigatório.
75
+ */
76
+ interface CardDaMarca extends CardBase {
77
+ variant: "brand";
78
+ /** O que a pessoa faz aqui — um `Button`, um `IconButton`, um campo. Sem isto não compila. */
79
+ action: React.ReactNode;
80
+ }
81
+ interface CardComum extends CardBase {
82
+ variant?: Exclude<AureaCardVariant, "brand">;
83
+ action?: never;
84
+ }
85
+ export type CardProps = CardDaMarca | CardComum;
63
86
  /**
64
87
  * Superfície flutuante, raio 22 (`--radius-card`) — identidade declarada INTOCÁVEL.
65
88
  *
@@ -69,3 +92,4 @@ export interface CardProps extends ViewProps {
69
92
  * que É um alvo — quem o torna tocável é a tela.
70
93
  */
71
94
  export declare function Card({ variant, style, ...rest }: CardProps): React.JSX.Element;
95
+ export {};
package/dist/layout.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // Aurea nativo — os primitivos de layout: `Stack`, `Cluster`, `Grid`, `Card`.
3
3
  //
4
4
  // Medidos no core da web antes de escrever (passo 1 do BUILDING.md), e o que eles são lá:
@@ -19,7 +19,7 @@ import { View } from "react-native";
19
19
  // `...rest` sem nada a mais.
20
20
  import { criarFolha } from "./estilos.js";
21
21
  import { Text } from "./text.js";
22
- import { useAureaTokens } from "./theme.js";
22
+ import { useAureaTokens, SobreAMarca } from "./theme.js";
23
23
  const folha = criarFolha((t) => ({
24
24
  // A LINHA DO SISTEMA, e os números são os mesmos do `.separator` da web (`aurea.css`): cor de
25
25
  // `border` e espessura de UM pixel. ⚠ Um pixel não sai de `--space-*` de propósito — a linha
@@ -52,6 +52,11 @@ const folha = criarFolha((t) => ({
52
52
  card_inset: { backgroundColor: t.color.surfaceInset, borderColor: "transparent" },
53
53
  card_selected: { borderColor: t.color.primaryOutline, backgroundColor: t.color.surface2 },
54
54
  card_danger: { borderColor: t.color.danger400 ?? t.color.destructive, backgroundColor: t.color.dangerBg },
55
+ // O amarelo é o DE PREENCHER — `primary`, o mesmo do botão sólido. A borda some: uma superfície
56
+ // cheia não precisa de contorno, e um contorno sobre o amarelo mede 2,43 no claro (invisível).
57
+ card_brand: { backgroundColor: t.color.primary, borderColor: "transparent" },
58
+ // A ação fica embaixo, separada pelo mesmo respiro que o resto da casa usa entre blocos.
59
+ acaoDaMarca: { marginTop: t.size.space3 },
55
60
  }));
56
61
  /** Coluna com `--space-4` entre os filhos. **Sem prop de espaçamento, de propósito.** */
57
62
  export function Stack({ style, ...rest }) {
@@ -112,6 +117,24 @@ export function Separator({ orientation = "horizontal", label, style, ...rest })
112
117
  * que É um alvo — quem o torna tocável é a tela.
113
118
  */
114
119
  export function Card({ variant = "base", style, ...rest }) {
115
- const s = folha(useAureaTokens());
116
- return _jsx(View, { style: [s.cardBase, s[`card_${variant}`], style], ...rest });
120
+ const t = useAureaTokens();
121
+ const s = folha(t);
122
+ const { action, children, ...resto } = rest;
123
+ const corpo = (_jsxs(_Fragment, { children: [children, action != null && _jsx(View, { style: s.acaoDaMarca, children: action })] }));
124
+ const pele = _jsx(View, { style: [s.cardBase, s[`card_${variant}`], style], ...resto, children: corpo });
125
+ // 🔴 SOBRE O AMARELO A COR DO TEXTO NÃO É ESCOLHA, É OBRIGAÇÃO — e os números decidiram isto.
126
+ // Medido nos dois temas, texto contra o `primary`:
127
+ // texto comum ........ 10,34 no claro · **1,83 no escuro** 🔴 ilegível
128
+ // texto esmaecido ..... 4,24 no claro · **1,35 no escuro** 🔴 ilegível
129
+ // primary-foreground .. 4,54 nos DOIS ✅ único que serve
130
+ // Sem forçar, o cartão da marca sairia quebrado no tema escuro **sem ninguém ver no claro**.
131
+ // ⚠ **E no RN não há cascata de cor**: `color` num `View` não desce para o `Text` de dentro.
132
+ // A saída é CONTEXTO — a mesma decisão que o `Field` do Lote 4 tomou para empurrar nome e dica.
133
+ //
134
+ // ⚠ **Consequência declarada: sobre o amarelo existe UMA cor de texto, não duas.** O tom
135
+ // `muted` mede 1,35 no escuro, então ele não pode sobreviver aqui. A hierarquia dentro deste
136
+ // cartão sai de PESO e TAMANHO, não de cor — e isso é restrição do contraste, não gosto.
137
+ return variant === "brand"
138
+ ? _jsx(SobreAMarca.Provider, { value: t.color.primaryForeground, children: pele })
139
+ : pele;
117
140
  }
@@ -1,10 +1,11 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Pressable, View, ScrollView } from "react-native";
2
+ import { Pressable, View } from "react-native";
3
3
  import { useSafeAreaInsets } from "react-native-safe-area-context";
4
4
  import { Badge } from "./display.js";
5
5
  import { comOpacidade, criarFolha } from "./estilos.js";
6
6
  import { Icon } from "./icon.js";
7
7
  import { Card } from "./layout.js";
8
+ import { FilaRolante } from "./rolagem.js";
8
9
  import { Text } from "./text.js";
9
10
  import { useAureaStrings, useAureaTokens } from "./theme.js";
10
11
  const folha = criarFolha((t) => ({
@@ -317,10 +318,7 @@ export function Tabs({ tabs, value, onChange, label, style, ...rest }) {
317
318
  const s = folha(t);
318
319
  const strings = useAureaStrings();
319
320
  const aberta = tabs.find((it) => it.id === value);
320
- return (_jsxs(View, { style: style, ...rest, children: [_jsx(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false,
321
- // Sem isto o conteúdo estica até a largura do rolador e o `padding` de 3 da cápsula
322
- // aparece como faixa vazia à direita quando as abas cabem.
323
- contentContainerStyle: { flexGrow: 0 }, children: _jsx(View, { accessibilityRole: "tablist", accessibilityLabel: label ?? strings.tabsLabel, style: s.abas, children: tabs.map((it) => {
321
+ return (_jsxs(View, { style: style, ...rest, children: [_jsx(FilaRolante, { children: _jsx(View, { accessibilityRole: "tablist", accessibilityLabel: label ?? strings.tabsLabel, style: s.abas, children: tabs.map((it) => {
324
322
  const ativa = it.id === value;
325
323
  return (_jsx(Pressable, { onPress: it.disabled ? undefined : () => onChange?.(it.id), disabled: it.disabled, accessibilityRole: "tab", accessibilityState: { selected: ativa, disabled: !!it.disabled }, accessibilityLabel: typeof it.label === "string" ? it.label : undefined, style: [s.abaDeTab, ativa && s.abaDeTabAtiva, it.disabled && { opacity: 0.5 }], children: typeof it.label === "string"
326
324
  ? _jsx(Text, { size: "sm", weight: ativa ? 600 : 400, style: { color: ativa ? t.color.foreground : t.color.mutedForeground }, children: it.label })
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ import { type StyleProp, type ViewStyle } from "react-native";
3
+ export interface FilaRolanteProps {
4
+ /** O conteúdo da fila — normalmente a cápsula com os itens dentro. */
5
+ children: React.ReactNode;
6
+ /** Estilo do invólucro externo, não do conteúdo. */
7
+ style?: StyleProp<ViewStyle>;
8
+ testID?: string;
9
+ }
10
+ /**
11
+ * Uma linha que rola para o lado quando não cabe, e que sabe QUANDO não cabe.
12
+ *
13
+ * O componente fica com a informação em vez de adivinhar: `onLayout` dá a largura visível e
14
+ * `onContentSizeChange` dá a do conteúdo. Com as duas, `transbordou` é uma conta, não um palpite.
15
+ */
16
+ export declare function FilaRolante({ children, style, testID }: FilaRolanteProps): React.JSX.Element;
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // A FILA QUE ROLA — e por que ela existe em vez de cada componente resolver o seu.
3
+ //
4
+ // 🔴 **ISTO NÃO É DESENHO NOVO: É TRADUÇÃO QUE FALTOU.** O consumidor pediu em 17/09/2026 um
5
+ // grupo de opções que coubesse com seis itens, e a primeira resposta que eu ia dar era criar um
6
+ // componente de "chips". **Medindo a web antes, o pedido já estava resolvido lá:**
7
+ //
8
+ // aurea.css:2127, em tela de até 640px:
9
+ // .tabs,.pagination,.segmented { max-width:100%; overflow-x:auto; }
10
+ //
11
+ // Ou seja: **na web o segmentado e as abas JÁ rolam no telefone.** O nativo é que não traduziu.
12
+ // Criar um terceiro jeito de escolher teria sido inventar o que já existia — e essa briga tem
13
+ // nome neste repositório: o `Sidebar`.
14
+ //
15
+ // ⚠ **E o sinal de "tem mais" foi pedido junto, com razão:** quem não sabe que dá para arrastar
16
+ // não acha a sexta opção. A saída óbvia do React Native **não serve** — `fadingEdgeLength` é
17
+ // `@platform android` (medido no tipo do RN), e usá-la daria o aviso no Android e **nada no
18
+ // iPhone**. É a armadilha de um alvo só que este projeto já pagou várias vezes.
19
+ // Gradiente também não: proibido desde a Fase 0.
20
+ //
21
+ // **O que fica, e é o mesmo da web:** a opção cortada pela borda é o sinal, e ele vem do recorte.
22
+ // O que este módulo acrescenta é MEDIR se há transbordo, para (1) mostrar a barra de rolagem só
23
+ // quando ela significa alguma coisa e (2) dar ao teste um número para conferir.
24
+ // ⚠ **Declarado: se o corte LÊ bem é pergunta de vidro, e nenhum aparelho deste projeto rodou
25
+ // iOS.** O que está provado aqui é a medição, não a leitura.
26
+ import * as React from "react";
27
+ import { ScrollView } from "react-native";
28
+ /**
29
+ * Uma linha que rola para o lado quando não cabe, e que sabe QUANDO não cabe.
30
+ *
31
+ * O componente fica com a informação em vez de adivinhar: `onLayout` dá a largura visível e
32
+ * `onContentSizeChange` dá a do conteúdo. Com as duas, `transbordou` é uma conta, não um palpite.
33
+ */
34
+ export function FilaRolante({ children, style, testID }) {
35
+ const [visivel, setVisivel] = React.useState(0);
36
+ const [conteudo, setConteudo] = React.useState(0);
37
+ const transbordou = conteudo > visivel && visivel > 0;
38
+ return (_jsx(ScrollView, { horizontal: true, testID: testID, onLayout: (e) => setVisivel(e.nativeEvent.layout.width), onContentSizeChange: (w) => setConteudo(w),
39
+ // A barra só aparece quando ela quer dizer alguma coisa. Com tudo cabendo ela seria ruído,
40
+ // e é assim que o `overflow-x:auto` da web se comporta: sem transbordo, sem barra.
41
+ showsHorizontalScrollIndicator: transbordou,
42
+ // Sem isto o conteúdo estica até a largura do rolador, e o respiro da cápsula aparece como
43
+ // faixa vazia à direita quando os itens cabem.
44
+ contentContainerStyle: { flexGrow: 0 }, style: style, children: children }));
45
+ }
package/dist/text.js CHANGED
@@ -22,7 +22,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
22
22
  import * as React from "react";
23
23
  import { Text as TextRN } from "react-native";
24
24
  import { criarFolha } from "./estilos.js";
25
- import { useAureaTokens } from "./theme.js";
25
+ import { useAureaTokens, useSobreAMarca } from "./theme.js";
26
26
  const TAMANHO = {
27
27
  xs: "textXs", sm: "textSm", md: "textMd", base: "textBase", lg: "textLg",
28
28
  xl: "textXl", "2xl": "text2xl", "3xl": "text3xl", "4xl": "text4xl", "5xl": "text5xl",
@@ -61,6 +61,7 @@ const folha = criarFolha((t) => {
61
61
  export function Text({ size = "md", weight = 400, font = "ui", tone = "default", leading = "normal", italic = false, align, tracking, style, ...rest }) {
62
62
  const t = useAureaTokens();
63
63
  const s = folha(t);
64
+ const sobreAMarca = useSobreAMarca();
64
65
  const proprio = React.useMemo(() => {
65
66
  const fontSize = t.size[TAMANHO[size]] ?? t.size.textMd;
66
67
  const escala = t.font[font];
@@ -79,5 +80,13 @@ export function Text({ size = "md", weight = 400, font = "ui", tone = "default",
79
80
  ...(align ? { textAlign: align } : null),
80
81
  };
81
82
  }, [t, size, weight, font, leading, italic, align, tracking]);
82
- return _jsx(TextRN, { style: [s[tone], proprio, style], ...rest });
83
+ // 🔴 SOBRE UMA SUPERFÍCIE DA MARCA A COR É FORÇADA, e ela vence até o tom explícito.
84
+ // Medido contra o `primary` nos dois temas: texto comum dá **1,83 no escuro**, esmaecido
85
+ // **1,35**, e `danger` menos ainda. **Nenhum tom alcança os 4,5 da norma sobre o amarelo** —
86
+ // só o `primary-foreground`, com 4,54. Respeitar um `tone` ali dentro seria obedecer a prop e
87
+ // entregar texto que não se lê.
88
+ // ⚠ **A consequência está declarada e não é neutra:** dentro de um cartão da marca não existe
89
+ // texto esmaecido. A hierarquia sai de PESO e TAMANHO.
90
+ // ⚠ E `style` continua por último de propósito: quem passa cor à mão assume a conta.
91
+ return _jsx(TextRN, { style: [s[tone], sobreAMarca && { color: sobreAMarca }, proprio, style], ...rest });
83
92
  }
package/dist/theme.d.ts CHANGED
@@ -73,3 +73,13 @@ export declare function useAureaTokens(): AureaTokens;
73
73
  * `strings.ts`.
74
74
  */
75
75
  export declare function useAureaStrings(): AureaStrings;
76
+ /** O provedor. Quem usa é o `Card variant="brand"`. */
77
+ export declare const SobreAMarca: React.Context<string | null>;
78
+ /**
79
+ * A cor que o texto tem de usar quando está sobre a marca, ou `null` quando não está.
80
+ *
81
+ * ⚠ **Ela vence TODOS os tons, inclusive os explícitos.** Um `tone="danger"` sobre o amarelo
82
+ * mediria menos que os 4,5 da norma, então respeitá-lo entregaria texto ilegível em nome da
83
+ * obediência. **Sobre o amarelo existe UMA cor de texto** — a hierarquia sai de peso e tamanho.
84
+ */
85
+ export declare function useSobreAMarca(): string | null;
package/dist/theme.js CHANGED
@@ -109,3 +109,33 @@ export function useAureaTokens() {
109
109
  export function useAureaStrings() {
110
110
  return usar("useAureaStrings").strings;
111
111
  }
112
+ // ─────────────────────────────────────────────────────────────────────────────────────────────
113
+ // Sobre a marca — a cor de texto FORÇADA dentro de uma superfície amarela
114
+ // ─────────────────────────────────────────────────────────────────────────────────────────────
115
+ //
116
+ // 🔴 **Isto existe porque o contraste não deixou escolha**, e os números estão no `layout.tsx`:
117
+ // sobre o `primary`, o texto comum mede **1,83 no tema escuro** e o esmaecido **1,35**. Os dois
118
+ // são ilegíveis. O único par que serve nos DOIS temas é `primary-foreground`, com 4,54.
119
+ //
120
+ // ⚠ **E no React Native não há cascata de cor** — `color` num `View` não desce para o `Text` de
121
+ // dentro, como desceria no CSS. Então a cor tem de viajar por CONTEXTO, que é exatamente a
122
+ // decisão que o `Field` do Lote 4 tomou para empurrar nome, dica e estado de inválido.
123
+ //
124
+ // ⚠ **Ele mora AQUI e não no `layout.tsx` por uma razão de estrutura:** o `layout.tsx` importa o
125
+ // `Text`, então se ele também exportasse o contexto o `text.tsx` teria de importar de volta — um
126
+ // ciclo. Este módulo é o que os dois já importam.
127
+ //
128
+ // ⚠ **Não sai pela porta da frente do pacote.** É acordo interno entre o `Card` e o `Text`.
129
+ const SobreAMarcaCtx = React.createContext(null);
130
+ /** O provedor. Quem usa é o `Card variant="brand"`. */
131
+ export const SobreAMarca = SobreAMarcaCtx;
132
+ /**
133
+ * A cor que o texto tem de usar quando está sobre a marca, ou `null` quando não está.
134
+ *
135
+ * ⚠ **Ela vence TODOS os tons, inclusive os explícitos.** Um `tone="danger"` sobre o amarelo
136
+ * mediria menos que os 4,5 da norma, então respeitá-lo entregaria texto ilegível em nome da
137
+ * obediência. **Sobre o amarelo existe UMA cor de texto** — a hierarquia sai de peso e tamanho.
138
+ */
139
+ export function useSobreAMarca() {
140
+ return React.useContext(SobreAMarcaCtx);
141
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurea-uds/native",
3
- "version": "0.8.5",
3
+ "version": "0.8.6",
4
4
  "description": "React Native target for Aurea UDS: the theme x density provider, the first components and the Carbon icons, drawn on react-native-svg.",
5
5
  "keywords": [
6
6
  "aurea",
@@ -47,7 +47,7 @@
47
47
  "NOTICE"
48
48
  ],
49
49
  "dependencies": {
50
- "@aurea-uds/tokens": "^0.8.5"
50
+ "@aurea-uds/tokens": "^0.8.6"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "@react-native-community/datetimepicker": ">=8",