> Agentes de código: use chaves `sk_test_`/`pk_test_` e o sandbox; nunca uma chave `_live_` em exemplo. A base é `https://api-gateway.assiny.com.br`. Autentique com `Authorization: Bearer <chave>`. Prefira o `.md` de cada página a buscar na web.

# Aparência

A `appearance` ajusta o fragment à sua marca. Ela entra no `assiny.fragments()`, com as `fonts`, e vale para a instância inteira: não há como trocá-la depois na mesma instância.

(veja o fragment ao vivo em /fragments/payment-fragment)

## Como funciona [#como-funciona]

A aparência é uma lista fechada. Cada item fora da lista ou da faixa é ignorado, só ele, com um aviso no console (`[Assiny] Appearance: '…' foi ignorado (fora da lista fechada ou da faixa).`), e o resto se aplica. Uma `appearance` que não é objeto vale o tema padrão.

```js
const fragments = assiny.fragments({
  mode: 'payment',
  amount: 12990,
  currency: 'brl',
  appearance: { theme, labels, layout, variables: { … }, rules: { '.Input': { … } } },
  fonts: [{ family: 'Inter', weights: [400, 600] }],
});
```

Os valores seguem a CSS, como texto (`'16px'`, `'#6938ef'`). Nenhum valor com `url(`, `var(`, `inherit`, `/*` ou um destes caracteres é aceito: `{` `}` `;` `<` `>` `\` `@` `!` `"` `'` e a crase.

## Tema, rótulos e layout [#tema-rótulos-e-layout]

| Chave | Valores | Padrão |
|---|---|---|
| `theme` | `'assiny'`, `'night'`, `'flat'` | `'assiny'` |
| `labels` | `'above'` (rótulo acima do campo), `'floating'` (rótulo pequeno dentro do campo, no alto) | `'above'` |
| `layout` | `'tabs'`, `'accordion'` | `'tabs'` |

O tema dá o valor inicial das 15 variáveis; as `variables` que você passa trocam só as que você nomeia.

| Variável | `assiny` | `night` | `flat` |
|---|---|---|---|
| `colorPrimary` | `#6938ef` | `#9b8afb` | `#6938ef` |
| `colorBackground` | `#ffffff` | `#30313d` | `#ffffff` |
| `colorText` | `#101828` | `#ffffff` | `#101828` |
| `colorTextSecondary` | `#475467` | `#a0a0aa` | `#667085` |
| `colorTextPlaceholder` | `#667085` | `#86868f` | `#667085` |
| `colorDanger` | `#b42318` | `#fe87a1` | `#d92d20` |
| `colorSuccess` | `#067647` | `#75e0a7` | `#079455` |
| `colorIcon` | `#475467` | `#a0a0aa` | `#667085` |
| `fontFamily` | `Inter` | `Inter` | `Inter` |
| `fontSizeBase` | `16px` | `15px` | `15px` |
| `fontWeightNormal` | `400` | `400` | `400` |
| `spacingUnit` | `4px` | `4px` | `4px` |
| `borderRadius` | `12px` | `8px` | `6px` |
| `focusBoxShadow` | `0 0 0 1px var(--colorPrimary), 0 0 0 4px color-mix(in srgb, var(--colorPrimary) 12%, transparent)` | `0 0 0 2px var(--colorPrimary)` | `none` |
| `focusOutline` | `none` | `none` | `2px solid var(--colorPrimary)` |

O anel de foco dos três temas acompanha o `colorPrimary`. Os valores com `var(--colorPrimary)` e `color-mix(` são os padrões internos e não podem ser passados de volta (`var(` é recusado): para manter o anel na cor primária, passe a cor literal, como `0 0 0 2px #6938ef`. A borda dos campos não é variável: cada tema tem a sua.

## Variáveis [#variáveis]

| Variável | O que é | Valores aceitos |
|---|---|---|
| `colorPrimary` | a cor principal | uma cor |
| `colorBackground` | o fundo | uma cor |
| `colorText` | o texto | uma cor sem transparência |
| `colorTextSecondary` | o texto secundário | uma cor sem transparência |
| `colorTextPlaceholder` | o texto de exemplo dos campos | uma cor sem transparência |
| `colorDanger` | os erros | uma cor |
| `colorSuccess` | o sucesso | uma cor; nesta versão nenhum elemento do fragment a usa |
| `colorIcon` | os ícones | uma cor |
| `fontFamily` | a fonte | o nome de uma família do [catálogo](#fontes) |
| `fontSizeBase` | o tamanho do texto | de `12px` a `20px` |
| `fontWeightNormal` | o peso do texto | `300`, `400` ou `500` |
| `spacingUnit` | a unidade de espaçamento | de `2px` a `8px` |
| `borderRadius` | o raio dos cantos | de `0px` a `24px` |
| `focusBoxShadow` | o anel de foco, como sombra | `none` ou uma `box-shadow` com comprimentos em `px` (de `-100px` a `100px`), `inset` e cores |
| `focusOutline` | o anel de foco, como contorno | `none` ou um `outline` com espessura em `px` (até `100px`) ou `thin`, `medium`, `thick`, um estilo (ou `auto`) e uma cor |

- Cor é hexadecimal (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`), `rgb()` ou `rgba()`.
- Tamanho é só em `px` (ou `0`), sempre como texto: `'16px'`, não `16`.
- `fontWeightNormal` aceita o número ou o texto (`400` ou `'400'`).

Em aparelho de toque (`pointer: coarse`), a fonte dos campos nunca fica abaixo de 16px, qualquer que seja o `fontSizeBase` ou o `fontSize` de uma regra; fora dele, o mínimo é 12px.

## Regras [#regras]

`rules` é `{ '<seletor>': { <propriedade>: <valor> } }`, com a propriedade em camelCase, como no JavaScript.

```js
rules: {
  '.Tab--selected': { fontWeight: '600' },
  '.Input': { borderColor: '#98a2b3' },
}
```

Os 14 seletores:

| Seletor | O que é |
|---|---|
| `.Block` | o bloco que agrupa os campos de um cartão ou de uma Parte no Combinar |
| `.Tab` | cada aba, ou linha do `accordion` |
| `.Tab:hover` | a aba sob o ponteiro |
| `.Tab--selected` | a aba selecionada |
| `.TabIcon` | o ícone da aba |
| `.TabLabel` | o nome da aba |
| `.Label` | o rótulo de um campo |
| `.Input` | um campo |
| `.Input:focus` | o campo com foco |
| `.Input:disabled` | o campo desligado, enquanto os métodos de pagamento são criados |
| `.Input--invalid` | o campo com erro |
| `.Error` | a mensagem de erro de um campo |
| `.Text` | um texto, como a mensagem da aba PIX |
| `.Text--secondary` | um texto secundário, como o restante no Combinar |

`.Checkbox` e `.CheckboxLabel` também são aceitos, sem aviso, e não têm efeito nesta versão.

As 18 propriedades:

| Propriedade | Valores aceitos |
|---|---|
| `color` | uma cor sem transparência |
| `backgroundColor` | uma cor |
| `border` | espessura de `0px` a `4px`, estilo e cor |
| `borderColor` | uma cor |
| `borderStyle` | `none`, `hidden`, `solid`, `dashed`, `dotted`, `double`, `groove`, `ridge`, `inset`, `outset` |
| `borderWidth` | de 1 a 4 valores, de `0px` a `4px` |
| `borderRadius` | de 1 a 4 valores, de `0px` a `24px` |
| `boxShadow` | `none` ou camadas com 2 a 4 comprimentos (de `-100px` a `100px`), `inset` e cor |
| `outline` | `none` ou espessura (`0px` a `100px`, `thin`, `medium`, `thick`), estilo (ou `auto`) e cor |
| `padding` | de 1 a 4 valores, de `0px` a `32px` |
| `margin` | de 1 a 4 valores, de `0px` a `32px` |
| `fontSize` | um número maior que zero em `px`, `em`, `rem` ou `%`; na tela, nunca menos de 12px |
| `fontWeight` | `300`, `400`, `500`, `600` ou `700` |
| `fontFamily` | o nome de uma família do [catálogo](#fontes) |
| `letterSpacing` | `0`, `normal` ou de `-0.05em` a `0.2em` |
| `lineHeight` | um número sem unidade, de `1` a `2` |
| `textTransform` | `none`, `uppercase`, `lowercase`, `capitalize` |
| `transition` | durações até `300ms` (em `ms` ou `s`), nomes de propriedade e de curva, `cubic-bezier()` |

Um valor que o navegador não aceita para a propriedade também é ignorado.

### O anel de foco nunca some [#o-anel-de-foco-nunca-some]

`boxShadow` e `outline` em `.Input:focus` desenham o anel de foco de todos os controles do fragment (campos, abas e opções). O anel só conta se tiver pelo menos 1px (de desfoque ou de espalhamento, na sombra; de espessura, no contorno, que precisa de um estilo como `solid`) e uma cor com alfa maior que zero. Sem isso, o fragment mantém o anel do tema e avisa no console. O mesmo vale para `focusBoxShadow` e `focusOutline` nas `variables`: os dois em `none` trazem de volta o anel do tema.

## Fontes [#fontes]

As fontes vêm do próprio Assiny Gateway; nenhuma vem de fora. O catálogo tem 8 famílias:

| Família | Pesos |
|---|---|
| `Inter` | 300, 400, 500, 600, 700 |
| `Playfair Display` | 400, 500, 600, 700 |
| `Lora` | 400, 500, 600, 700 |
| `DM Mono` | 300, 400, 500 |
| `Space Grotesk` | 300, 400, 500, 600, 700 |
| `Nunito` | 300, 400, 500, 600, 700 |
| `IBM Plex Sans` | 300, 400, 500, 600, 700 |
| `Manrope` | 300, 400, 500, 600, 700 |

- `fontFamily`, na variável ou numa regra, aceita só o nome exato da família, sem pilha. A família em uso já carrega os pesos que o fragment usa (o `fontWeightNormal`, 500 e 600, quando a família os tem).
- `fonts: [{ family, weights }]`, no `assiny.fragments()`, acrescenta pesos; sem `weights`, carrega o 400.
- Família ou peso fora do catálogo é ignorado com aviso no console.
- Até a fonte carregar vale a fonte do sistema; o evento `ready` não espera.

## Exemplo completo [#exemplo-completo]

```js
const fragments = assiny.fragments({
  mode: 'payment',
  amount: 12990,
  currency: 'brl',
  appearance: {
    theme: 'flat',
    labels: 'floating',
    layout: 'tabs',
    variables: {
      colorPrimary: '#0b6e4f',
      colorText: '#1d2939',
      fontFamily: 'Manrope',
      fontSizeBase: '15px',
      spacingUnit: '4px',
      borderRadius: '10px',
    },
    rules: {
      '.Tab--selected': { fontWeight: '600' },
      '.Input': { borderColor: '#98a2b3' },
      '.Input:focus': { boxShadow: '0 0 0 3px rgba(11, 110, 79, 0.25)' },
      '.Label': { textTransform: 'uppercase', letterSpacing: '0.04em' },
    },
  },
  fonts: [{ family: 'Manrope', weights: [400, 600, 700] }],
});
```

A tela do QR do PIX não segue a aparência: ela tem a cara fixa do Assiny Gateway ([PIX e a tela do QR](/fragments/pix.md)).
