> ## Documentation Index
> Fetch the complete documentation index at: https://www.ayrshare.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Widget Ayrshare Connect

> Permita que seus usuários vinculem contas sociais de dentro do seu próprio dashboard, com uma tag de script e nossos botões incorporados na sua página.

export const PlansAvailable = ({plans = [], maxPackRequired}) => {
  let displayPlans = plans;
  if (plans && plans.length === 1) {
    const lowerCasePlan = plans[0].toLowerCase();
    if (lowerCasePlan === "business") {
      displayPlans = ["Launch", "Business", "Enterprise"];
    } else if (lowerCasePlan === "premium") {
      displayPlans = ["Premium", "Launch", "Business", "Enterprise"];
    }
  }
  return <Note>
Available on {displayPlans.length === 1 ? "the " : ""}
{displayPlans.join(", ").replace(/\b\w/g, l => l.toUpperCase())}{" "}
{displayPlans.length > 1 ? "plans" : "plan"}.

{maxPackRequired && <span onClick={() => window.open('https://www.ayrshare.com/docs/additional/maxpack', '_self')} className="flex items-center mt-2 cursor-pointer">
 <span className="px-1.5 py-0.5 rounded text-sm" style={{
    backgroundColor: '#C264B6',
    color: 'white',
    fontSize: '12px'
  }}>
   Max Pack required
 </span>
</span>}
</Note>;
};

<PlansAvailable plans={["business"]} maxPackRequired={true} />

O widget Ayrshare Connect coloca nossos botões de vinculação **dentro do seu próprio dashboard**. Você carrega um
script, coloca um slot onde a rede pertence no seu layout, e nós renderizamos ali um botão que
já mostra se a conta está conectada. Seu usuário clica nele e vincula a conta sem
sair da sua página — no máximo um popup, o da própria rede.

Você não escreve nenhum tratamento de popup, nenhum callback de OAuth, nenhum refresh de sessão e nenhuma lógica por rede. Quando
uma rede muda algo do lado dela, a correção chega dentro dos nossos frames no momento em que fazemos o deploy;
você não faz redeploy de nada.

## Qual superfície você quer

| Superfície                                                                                                                         | O que seu usuário vê                                                           | White-label                                                                     | Escolha quando                                                                                                  |
| ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| **Widget — frames incorporados** ([`mount()`](#mount-a-slot))                                                                      | nossos botões inline no seu próprio layout; nenhum popup até o da própria rede | **O mais forte.** Sua página, suas fontes e cores, e seu usuário nunca sai dela | você tem um dashboard com uma linha por rede e quer que a vinculação aconteça ali mesmo. Max Pack.              |
| **Widget — seu próprio botão** ([`popup()`](#your-own-button))                                                                     | seu botão, depois um popup para a rede                                         | **Forte.** O popup é nosso, mas é breve e herda sua aparência                   | você quer seu próprio botão e estilo, e nenhum frame no seu layout. Max Pack.                                   |
| **Página de vinculação hospedada** ([como fazer](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | uma página que nós hospedamos, com seu logotipo, cores e CSS personalizado     | **O mais fraco.** É a nossa página, e seu usuário sai da sua para usá-la        | você quer um único link para distribuir, ou está fazendo onboarding por e-mail. Nada a construir, sem Max Pack. |

<div className="my-8">
  <Frame caption="Frames incorporados: nossos tiles renderizados dentro do seu próprio layout, um slot por rede ou um slot para várias.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-frames.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=d6c26228a418421772f753f31b0dfc59" alt="Um dashboard de cliente com tiles do Ayrshare Connect para Instagram, TikTok e LinkedIn incorporados em um card" width="2400" height="1120" data-path="images/multiple-users/connect-widget-frames.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="Seu próprio botão: você renderiza o botão, e um breve popup nosso cuida da rede.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-popup.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=b68021d477703d97a5994ee85d299be6" alt="Um dashboard de cliente com seus próprios botões Connect e um popup da Ayrshare mostrando a tela de transição do Facebook" width="2880" height="1317" data-path="images/multiple-users/connect-widget-popup.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="Página de vinculação hospedada: uma página que nós hospedamos, com seu logotipo e cores, que seu usuário sai do seu app para usar.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-hosted.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=4d22a992f6e70808fd9ad9f065ed3879" alt="A página de vinculação social hospedada mostrando todas as redes disponíveis" width="2336" height="1906" data-path="images/multiple-users/connect-widget-hosted.webp" />
  </Frame>
</div>

<Note>
  As duas linhas do widget são **uma integração**, não duas. Um único `init` dá a você as duas: monte frames
  onde quiser nossos botões e chame `popup()` a partir do seu próprio botão em todos os outros lugares. Elas
  compartilham uma sessão e reportam nos mesmos handlers.

  O [modo direto](/docs/multiple-users/connect-direct-mode) é o mesmo popup **sem** o nosso script — para
  uma página com uma Content-Security-Policy rígida, uma página renderizada no servidor ou um app nativo. Ali
  você mesmo abre e observa o popup.
</Note>

## Adicione o script

Fixe uma versão e seu hash, ou acompanhe um canal sem hash. Nunca os dois — um atributo `integrity`
em uma URL móvel deixa de funcionar no nosso próximo release, porque o arquivo que ela nomeia mudou
legitimamente.

```html Pinned version theme={"system"}
<script
  src="https://app.ayrshare.com/ayrshare-connect/1.2.0/widget.js"
  integrity="sha384-qQ/of8CnYj26nF9RkQ6qk3D1vvzWesu7mpBK6MFK2D0EmmgRJHfbDmRowFBfltmz"
  crossorigin="anonymous"
></script>
```

```html Tracking v1 theme={"system"}
<script src="https://app.ayrshare.com/ayrshare-connect/v1/widget.js" crossorigin="anonymous"></script>
```

| Caminho                                 | Cache            | `integrity`      |
| --------------------------------------- | ---------------- | ---------------- |
| `/ayrshare-connect/<version>/widget.js` | imutável, um ano | **sim** — fixe-o |
| `/ayrshare-connect/v1/widget.js`        | cinco minutos    | não              |
| `/ayrshare-connect/latest/widget.js`    | cinco minutos    | não              |

**`v1` é o canal a recomendar.** Ele recebe correções, mas nunca cruza uma breaking change.
`latest` cruza versões major por definição, então em algum momento entregará à sua página uma versão cujo
comportamento você não revisou.

O hash de cada versão é publicado no
[`manifest.json`](https://app.ayrshare.com/ayrshare-connect/manifest.json), que também nomeia o que
cada canal serve atualmente:

```json manifest.json theme={"system"}
{
  "versions": {
    "1.2.0": { "integrity": "sha384-qQ/of8CnYj26nF9RkQ6qk3D1vvzWesu7mpBK6MFK2D0EmmgRJHfbDmRowFBfltmz" }
  },
  "latest": "1.2.0",
  "channels": { "v1": "1.2.0", "latest": "1.2.0" }
}
```

Cada bundle também abre com um comentário nomeando sua própria versão, que é a forma mais rápida de nos
dizer o que uma página está de fato executando:

```js theme={"system"}
/*! ayrshare-connect 1.2.0 */
```

### Content-Security-Policy

Se a sua página envia uma Content-Security-Policy, ela precisa de **duas** entradas, ambas nomeando o host de
onde você carrega o script:

```
script-src https://app.ayrshare.com;
frame-src  https://app.ayrshare.com;
```

Essa é a lista inteira. Você **não precisa de nenhuma entrada `connect-src`** para nós — nossos frames alcançam nossa API de
dentro deles mesmos, não da sua página — e **nenhuma entrada para popups**, que são janelas de nível superior
que a sua política não governa. As duas entradas foram medidas: remover `script-src` bloqueia o script,
e remover `frame-src` bloqueia o frame.

## Inicie uma instância

`session` é a única opção obrigatória. Ela é chamada **uma vez por instância**, não uma vez por mount, e
deve retornar a resposta do seu backend a
[Criar uma Link Session](/docs/apis/profiles/create-link-session) com `mode: "connect"` —
`{ sessionId, token, expiresAt }` — exatamente como ela voltou.

```javascript Your page theme={"system"}
const connect = AyrshareConnect.init({
  session: () => fetch("/my-api/ayrshare-session").then(r => r.json()),
  appearance: { "--ayr-connect-accent": "#0B7A6C" },
  maxHeight: 800,
});
```

```javascript Your backend theme={"system"}
app.get("/my-api/ayrshare-session", async (req, res) => {
  const response = await fetch("https://api.ayrshare.com/api/profiles/link-sessions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.AYRSHARE_API_KEY}`,
      "Profile-Key": profileKeyFor(req.user),
    },
    body: JSON.stringify({ mode: "connect", origin: "https://app.example.com" }),
  });

  res.json(await response.json());
});
```

Uma sessão de widget não nomeia **nenhum** `network` — ela autoriza todas as redes que sua conta permite, e
quais aparecem é decidido por mount, na sua página. Ela carrega, sim, um `origin`, que é a única
coisa que faz os frames renderizarem: um frame compara a página que o incorpora com esse valor
e se recusa a renderizar em qualquer outro lugar.

<Warning>
  Crie a sessão no seu servidor. A chamada precisa da sua API Key, e o token que ela retorna autentica
  seu usuário no User Profile dele — trate-o como uma senha.
</Warning>

Chamamos `session` novamente antes de o token expirar, então o widget continua funcionando em uma página deixada aberta
o dia todo. Uma chamada que rejeita, ou que não retorna token, é repetida mais duas vezes — após 0.5s e depois 1s —
antes de desistirmos e emitirmos `error`. Então um refresh custa no máximo três chamadas ao seu endpoint.

| Opção        | Padrão         | O que faz                                                                                   |
| ------------ | -------------- | ------------------------------------------------------------------------------------------- |
| `session`    | —              | **Obrigatória.** Retorna uma link session em modo connect.                                  |
| `appearance` | nossos padrões | Design tokens, aplicados dentro de cada frame. Consulte [Aparência](#appearance).           |
| `css`        | nenhum         | Uma string de CSS aplicada dentro de cada frame. Consulte [CSS personalizado](#custom-css). |
| `maxHeight`  | `600`          | Quanto um frame pode crescer antes de passar a rolar internamente.                          |

## Monte um slot

Um `mount` por slot. Peça uma rede, várias ou tudo o que a sessão permite — a
granularidade é sua, então um slot pode ser uma única linha em uma tabela existente ou um painel contendo
tudo.

```javascript theme={"system"}
connect.mount("#instagram-slot", { network: "instagram" });
connect.mount("#some-slot", { networks: ["facebook", "tiktok", "x"] });
connect.mount("#everything");

const row = connect.mount(document.querySelector("#tall"), { maxHeight: 1200 });
row.unmount();
```

`mount` recebe um seletor CSS ou um elemento, e retorna `{ unmount, element }`. Ele lança uma exceção se o
alvo não corresponde a nada — o que quase sempre é um slot que ainda não existe, então monte depois que seu
markup estiver no documento.

Cada mount é um iframe. Ele reporta sua própria altura para nós e nós o redimensionamos para corresponder, então seu layout
se reorganiza conforme nosso conteúdo muda; além de `maxHeight`, o frame rola internamente em vez de escapar da
sua página. **O slot mais estreito que suportamos é 300px.**

As chaves de rede são as próprias da Ayrshare, e as grafias alternativas também funcionam: `instagram` e `instagramapi`
significam ambas `instagramApi`, e `x` significa `twitter`. Uma chave que não é uma rede não renderiza tile algum.

<h2 id="your-own-button">
  Seu próprio botão
</h2>

Um cliente que prefere usar o próprio botão em vez de um dos nossos frames chama `popup` em vez disso. Ele
executa o mesmo fluxo, na mesma sessão, e reporta nos mesmos handlers.

<Warning>
  **Chame-o diretamente dentro do handler de clique, sem nada com await antes dele.** Um navegador só
  permite um popup enquanto ainda está processando o clique do seu usuário, e essa permissão não
  sobrevive a um `await`. De qualquer forma nada precisa de await — a sessão foi criada no `init`.
</Warning>

```javascript theme={"system"}
linkedInButton.addEventListener("click", () => {
  connect.popup({ network: "linkedin" });
});
```

`popup` retorna `{ close(), network }`, e **sempre** retorna um handle — inclusive depois de um popup
bloqueado, quando `close()` não faz nada — para que seu código nunca precise verificar null antes de chamar
`close()`.

Todas as redes funcionam aqui, incluindo as que um frame conclui dentro do próprio painel: o Facebook mostra
sua tela explicativa de transição no popup, Bluesky e X mostram seu formulário de credenciais, e LinkedIn,
Pinterest, YouTube e Google Business vão até a rede e voltam.

Ele lança uma exceção de forma síncrona para as três coisas que são erros de programação — nenhum `network`, uma
instância destruída ou uma sessão que ainda não resolveu. Um popup bloqueado **não** é uma delas:
esse emite `error` com `reason: "popupBlocked"`, porque seu usuário não fez nada de errado.

No máximo um popup fica aberto por vez. Uma segunda chamada fecha o primeiro e reporta `cancelled` com
`reason: "superseded"` nele. Um popup que um dos nossos **frames** abriu é outra coisa e nunca é
tocado, então seu botão não pode cancelar um fluxo em andamento dentro de um slot montado.

<Note>
  Se a sessão pode vincular uma rede é a resposta do **servidor**, não do script. Uma sessão
  restrita ao Bluesky à qual se pede LinkedIn recebe uma recusa renderizada no popup e reportada como
  `error`. O script só verifica que uma rede foi nomeada.
</Note>

## React

O script é livre de frameworks, então o React não precisa de nada especial de nós — mas quatro coisas sobre seu
ciclo de vida valem acertar de primeira.

Carregue o script **uma vez**, fora da sua árvore de componentes. No Next.js isso é `next/script` no seu
layout raiz; no Vite ou Create React App é uma tag no `index.html`. Carregá-lo por componente
o executa novamente a cada mount.

```jsx ConnectAccounts.jsx theme={"system"}
import { useEffect, useRef, useState } from "react";

export function ConnectAccounts() {
  const slot = useRef(null);
  const [linked, setLinked] = useState([]);

  useEffect(() => {
    // Dentro do effect, para que a ref esteja anexada: mount() lança uma exceção se o alvo
    // ainda não existe, que é exatamente o que acontece se você o chamar durante o render.
    const connect = window.AyrshareConnect.init({
      session: () => fetch("/my-api/ayrshare-session").then(r => r.json()),
      appearance: { "--ayr-connect-accent": "#0B7A6C" },
    });

    connect.mount(slot.current, { networks: ["instagram", "tiktok", "x"] });

    const stop = connect.on("success", ({ network }) => {
      setLinked(current => [...current, network]);
    });
    // `success` é um dos dez eventos que o widget reporta. Veja Eventos abaixo para
    // a lista completa, incluindo `state`, que substitui o polling.

    // destroy() leva junto os frames, o listener de message e os timers.
    // Sem isso, uma troca de rota deixa os três para trás.
    return () => {
      stop();
      connect.destroy();
    };
  }, []);

  return <div ref={slot} />;
}
```

<Warning>
  **Nunca reutilize uma instância depois de `destroy()`.** Uma instância destruída permanece destruída — `popup()`
  lança exceção nela, e `mount()` não a trará de volta. Crie uma nova instância na próxima execução do effect,
  que é o que o código acima faz.
</Warning>

Duas consequências desse padrão, e nenhuma delas é um bug:

* **Em desenvolvimento você verá o callback de session disparar duas vezes.** O Strict Mode do React executa os effects
  mount → unmount → mount, então a instância é criada, destruída e criada de novo. O cleanup
  acima torna isso seguro; custa uma chamada extra ao seu backend em dev e nenhuma em produção.
* **Mantenha as dependências do effect estáveis.** Um array literal passado direto para `mount` a partir do
  render de um pai é um valor novo a cada vez, então um effect que depende dele derruba o widget e
  o reconstrói a cada render. Memoize-o, ou mantenha-o constante como acima.

## Eventos

Inscreva-se com `on`, que retorna uma função de cancelamento de inscrição. Os handlers recebem o payload do evento e
o mount de onde ele veio; `off(name, handler)` faz o mesmo trabalho quando você prefere nomear o
handler.

```javascript theme={"system"}
const stop = connect.on("success", ({ network, displayName }, mount) => {
  refreshRow(network, displayName, mount.element);
});
stop();

connect.on("error", ({ network, code, message }) => report(code, message));
connect.destroy(); // every frame, listener and timer
```

Dez eventos. Todos carregam um `network`, exceto `ready` e exceto o único tipo de `error` que
não tem relação com uma rede — consulte [`error` tem duas origens](#error-has-two-sources) abaixo.

| Evento      | Dispara quando                                           | Também carrega                                       |
| ----------- | -------------------------------------------------------- | ---------------------------------------------------- |
| `ready`     | um frame está montado e pronto                           | —                                                    |
| `click`     | seu usuário clicou em uma rede, **em qualquer direção**  | `action`: `"connect"` ou `"unlink"`                  |
| `started`   | uma tentativa de vinculação está em andamento            | —                                                    |
| `selection` | seu usuário chegou a um seletor ou formulário            | `step`                                               |
| `success`   | a conta está conectada **e salva**                       | `displayName` (omitido quando desconhecido), `refId` |
| `unlinked`  | uma conta conectada foi removida, e a remoção está salva | —                                                    |
| `error`     | a tentativa falhou                                       | `code`, `message`, `reason`                          |
| `cancelled` | seu usuário desistiu                                     | `reason`                                             |
| `closed`    | o popup que essa tentativa usou fechou                   | —                                                    |
| `state`     | o estado de conta de uma rede, no mount e a cada mudança | `state`, `since`                                     |

**Quatro deles são desfechos** — `success`, `unlinked`, `error` e `cancelled` — e exatamente um
chega por tentativa. `closed` é um aviso de ciclo de vida que segue um desfecho, em vez de ser um.

`click` dispara **antes** de qualquer trabalho de vinculação começar, então ele reporta um clique que um popup bloqueado ou uma
sessão morta vai em seguida recusar. É o evento a usar para o seu próprio analytics; `started` é o que
significa que uma tentativa está realmente em execução.

### Motivos

| Evento      | `reason`        | Significa                                                   |
| ----------- | --------------- | ----------------------------------------------------------- |
| `error`     | `popupBlocked`  | o navegador se recusou a abrir o popup                      |
| `cancelled` | `popupClosed`   | seu usuário fechou a janela manualmente                     |
| `cancelled` | `scopesDenied`  | seu usuário recusou uma permissão que a rede pediu          |
| `cancelled` | `userCancelled` | seu usuário desistiu, ou seu código chamou `handle.close()` |
| `cancelled` | `superseded`    | uma segunda chamada de `popup()` substituiu essa tentativa  |

<h3 id="error-has-two-sources">
  `error` tem duas origens
</h3>

Apenas uma delas é uma falha de vinculação, e elas carregam campos diferentes.

* Um **erro de vinculação** carrega `network`, `code` e o mount de onde veio.
* Um **erro de sessão** — não conseguimos criar ou renovar sua sessão — carrega apenas `message`,
  porque nada estava sendo vinculado naquele momento.

Desestruture defensivamente: `code` e `network` são `undefined` no segundo tipo.

### `state` poupa você do polling

`state` é o canal de dados, e não um relato sobre uma tentativa. Cada frame emite um por rede quando
é montado, carregando o estado atual daquela rede e o timestamp `since` desde quando ela o mantém, e
outro sempre que um estado muda — **incluindo mudanças que se originam do nosso lado**, como um token
que morre e vira relink obrigatório. Assim você pode dirigir toda a sua UI a partir do widget sem fazer polling de nada.

Os valores são o mesmo enum que
[`GET /profiles` com `include=state`](/docs/apis/profiles/get-profiles) retorna: `linked`, `unlinked`,
`identityVerificationRequired`, `restricted`, `rateLimited`, `suspended`.

## Desvinculação

Nossos tiles desvinculam além de vincular. Seu usuário clica em uma rede conectada, confirma, e a conta é
removida:

1. `click` dispara com `action: "unlink"`.
2. `unlinked` dispara quando a remoção está salva.

Uma desvinculação que **falha** reporta `error`, e uma da qual seu usuário desiste na etapa de confirmação reporta
`cancelled`. Não há um evento separado de falha de desvinculação.

<h2 id="appearance">
  Aparência
</h2>

A folha de estilos de um cliente não consegue alcançar o interior de um frame cross-origin, então o estilo viaja como dados que
aplicamos dentro dele. Passe `appearance` ao `init` como propriedades personalizadas de CSS; cada uma que não
recebermos mantém nosso padrão.

```javascript theme={"system"}
const connect = AyrshareConnect.init({
  session: getSession,
  appearance: {
    "--ayr-connect-font-family": "Inter, system-ui, sans-serif",
    "--ayr-connect-surface-bg": "#111318",
    "--ayr-connect-surface-fg": "#F2F3F7",
    "--ayr-connect-accent": "#0B7A6C",
    "--ayr-connect-accent-fg": "#FFFFFF",
    "--ayr-connect-radius": "12px",
  },
});
```

<Warning>
  **Defina as cores em pares.** Um token de fundo sem um token de primeiro plano ao lado é a única forma de
  fazer isso produzir algo ilegível: defina `--ayr-connect-surface-bg` com um valor escuro sozinho
  e nosso `--ayr-connect-surface-fg` padrão continua sendo azul-marinho escuro. Nada pode inferir a outra metade
  por você.
</Warning>

Sem nenhum `appearance`, todos os tokens mantêm seu padrão e um frame fica assim:

<div className="my-8">
  <Frame caption="Tokens padrão: superfícies brancas, texto azul-marinho escuro, accent índigo, raio de 8px.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-appearance-default.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=62f9ce7e061c8bb58132960b9e270f4a" alt="Três tiles do Ayrshare Connect com a aparência padrão" width="920" height="528" data-path="images/multiple-users/connect-widget-appearance-default.webp" />
  </Frame>
</div>

Passe um punhado de tokens e o mesmo frame assume a sua paleta. Este exemplo torna as
superfícies azul-claras, aprofunda o texto e o accent para combinar, e arredonda um pouco mais os cantos:

```javascript theme={"system"}
const connect = AyrshareConnect.init({
  session: getSession,
  appearance: {
    "--ayr-connect-surface-bg": "#EFF6FF",
    "--ayr-connect-border-color": "#BFDBFE",
    "--ayr-connect-surface-fg": "#0F2A5F",
    "--ayr-connect-surface-fg-muted": "#4A6A9A",
    "--ayr-connect-accent": "#1D4ED8",
    "--ayr-connect-radius": "14px",
    "--ayr-connect-spacing": "10px",
  },
});
```

<div className="my-8">
  <Frame caption="Os mesmos três tiles depois de os tokens acima serem aplicados.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-appearance-themed.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=686d1ad900628a6d4c75dc242f1ab38c" alt="Três tiles do Ayrshare Connect reestilizados com superfícies azul-claras e um accent azul mais profundo" width="920" height="574" data-path="images/multiple-users/connect-widget-appearance-themed.webp" />
  </Frame>
</div>

**Há uma paleta e nenhum preset claro/escuro.** Nada reage a `prefers-color-scheme`, de
propósito — o tema da sua página pode não corresponder ao sistema operacional do seu usuário, e uma media query
silenciosamente derrotaria as cores que você escolheu. Um dashboard escuro é tematizado fornecendo valores escuros.

Esta lista de tokens é um contrato suportado que mantemos entre versões.

### Os tokens

| Token                              | Padrão                                                    |
| ---------------------------------- | --------------------------------------------------------- |
| `--ayr-connect-font-family`        | `Inter, "Segoe UI", system-ui, -apple-system, sans-serif` |
| `--ayr-connect-font-size`          | `16px`                                                    |
| `--ayr-connect-font-size-sm`       | `12px`                                                    |
| `--ayr-connect-label-font-weight`  | `600`                                                     |
| `--ayr-connect-status-font-size`   | `12px`                                                    |
| `--ayr-connect-status-font-weight` | `600`                                                     |
| `--ayr-connect-surface-bg`         | `#FFFFFF`                                                 |
| `--ayr-connect-surface-bg-hover`   | `#F7F7F7`                                                 |
| `--ayr-connect-surface-fg`         | `#010629`                                                 |
| `--ayr-connect-surface-fg-muted`   | `#56596F`                                                 |
| `--ayr-connect-border-color`       | `#DDDEE2`                                                 |
| `--ayr-connect-border-width`       | `1px`                                                     |
| `--ayr-connect-radius`             | `8px`                                                     |
| `--ayr-connect-spacing`            | `8px`                                                     |
| `--ayr-connect-accent`             | `#4553EE`                                                 |
| `--ayr-connect-accent-fg`          | `#FFFFFF`                                                 |
| `--ayr-connect-focus-ring-color`   | `#4553EE`                                                 |
| `--ayr-connect-focus-ring-width`   | `2px`                                                     |
| `--ayr-connect-disabled-fg`        | `#6E7185`                                                 |
| `--ayr-connect-status-radius`      | `4px`                                                     |
| `--ayr-connect-status-success-bg`  | `#EBFFF8`                                                 |
| `--ayr-connect-status-success-fg`  | `#237C5C`                                                 |
| `--ayr-connect-status-warning-bg`  | `#FFF7EF`                                                 |
| `--ayr-connect-status-warning-fg`  | `#702E00`                                                 |
| `--ayr-connect-status-critical-bg` | `#FFE5E1`                                                 |
| `--ayr-connect-status-critical-fg` | `#AD1902`                                                 |
| `--ayr-connect-status-info-bg`     | `#ECF9FF`                                                 |
| `--ayr-connect-status-info-fg`     | `#1F6686`                                                 |
| `--ayr-connect-callout-bg`         | `#FFF7EF`                                                 |
| `--ayr-connect-callout-fg`         | `#702E00`                                                 |
| `--ayr-connect-partner-name`       | `""`                                                      |
| `--ayr-connect-icon-size`          | `32px`                                                    |
| `--ayr-connect-avatar-size`        | `40px`                                                    |

Um valor que não é CSS válido para o seu token é **ignorado, com um aviso no seu console**, em vez de
aplicado. Isso importa mais do que parece: um `8` sem unidade para `--ayr-connect-spacing` é uma
string perfeitamente inocente que invalidaria todo cálculo que a lê e colapsaria o
layout, sem erro em lugar nenhum. Dê unidade aos comprimentos.

### Seu próprio nome na tela de transição

Antes de entregarmos seu usuário a uma rede, mostramos uma tela curta nomeando por meio de quem ele está
conectando. Dois ganchos permitem que você a torne sua, e ambos são estáveis entre versões.

`--ayr-connect-partner-name` define o rótulo. É o único token cujo valor é **texto**, então ele
precisa estar entre aspas como uma string CSS — um valor sem aspas é inválido e não renderiza nada:

```javascript theme={"system"}
AyrshareConnect.init({
  session: getSession,
  appearance: { "--ayr-connect-partner-name": "'Acme Social'" },
  css: "[data-ayr-connect-partner-mark]::after { background-image: url('https://cdn.example.com/mark.svg') }",
});
```

O logotipo não é um token. Nós entregamos a marca como um elemento posicionado e dimensionado e você o preenche com uma
`background-image` via `css`, como acima — um token que pudesse buscar uma imagem de dentro do nosso
documento não é algo que aceitamos, então a requisição vem de uma regra que você escreveu em vez de um
valor que você nos passou.

<Note>
  `[data-ayr-connect-partner-mark]` e `[data-ayr-connect-partner-name]` são a **exceção** à
  ressalva sobre CSS personalizado abaixo: esses dois seletores fazem parte do contrato e nós os mantemos entre
  versões.
</Note>

<h3 id="custom-css">
  CSS personalizado
</h3>

`css` recebe uma string aplicada dentro de cada frame, para os casos que os tokens não cobrem.

```javascript theme={"system"}
AyrshareConnect.init({ session: getSession, css: "button { letter-spacing: 0.01em }" });
```

<Warning>
  **CSS personalizado não é suportado entre versões.** Seus seletores miram nosso markup interno, que
  muda entre releases — uma regra que funciona hoje pode silenciosamente parar de corresponder após qualquer atualização.
  O contrato de tokens acima é a parte que mantemos. Fixe uma versão se você depende de CSS personalizado.
</Warning>

Os popups herdam o `appearance` e o `css` da instância exatamente como os frames, então seu usuário não
vê nossos padrões neutros aparecerem no meio de um fluxo.

## Vale saber antes de lançar

<AccordionGroup>
  <Accordion title="Um popup cuja sessão expirou reporta cancelled, não error">
    Um popup que você abriu com `popup()` não pode ser informado do porquê de um token ter sido recusado — para dizer isso ele
    teria de confiar em uma origem que ainda não validou, o que nosso modelo de segurança não permite. Então um
    popup carregando um token morto fecha e aparece como `cancelled` com `reason: "popupClosed"`
    em vez de `error`.

    Na prática isso é raro: um popup aberto antes de um refresh silencioso continua funcionando, porque ele
    validou seu token quando abriu. Se você vê resultados `popupClosed` inexplicados, verifique se o
    seu endpoint de `session` está retornando uma sessão nova.
  </Accordion>

  <Accordion title="Uma sessão por instância, não uma por mount">
    Catorze mounts compartilham um token e custam uma chamada ao seu backend, não catorze. Se você quer
    slots com escopos diferentes — um `allowedSocial` diferente em alguns deles — execute um segundo `init`
    com a própria sessão dele, em vez de esperar que um mount a restrinja.
  </Accordion>

  <Accordion title="Os frames só renderizam na origem que você declarou">
    Cada sessão carrega o `origin` em que a sua página roda, e um frame compara a página que o incorpora
    com esse valor antes de renderizar qualquer coisa. Um frame incorporado em outro lugar permanece em branco e
    não envia eventos. Não há allowlist para registrar nem nada para configurar — envie o `origin`
    correto ao criar a sessão.
  </Accordion>

  <Accordion title="A altura é tratada para você, e não é um evento">
    Os frames reportam sua altura ao script, e o script os redimensiona. Seu layout simplesmente se reorganiza.
    Não há evento de resize para assinar, e nada para medir do seu lado.
  </Accordion>
</AccordionGroup>

## Requisitos

<ul className="custom-bullets">
  <li>
    O **[Max Pack](/docs/additional/maxpack)**. Uma sessão de widget é uma sessão em modo connect, e
    criar uma sem o Max Pack retorna `code: 504`. Entre em contato com o suporte se você precisar do modo connect
    habilitado em uma conta sem ele.
  </li>

  <li>
    Um **`origin`** em cada sessão — a origem exata em que a sua página roda. Omiti-lo retorna
    `code: 505`; um valor que não é uma origem `https`, um esquema personalizado ou `http://localhost`
    retorna `code: 506`.
  </li>

  <li>
    **Nenhum `network`** na sessão. Esse parâmetro é o que torna uma sessão
    [modo direto](/docs/multiple-users/connect-direct-mode), e a URL de uma sessão em modo direto
    não é o que o script espera.
  </li>
</ul>

Todos os códigos acima estão na referência de
[Erros de Link Session](/docs/errors/errors-ayrshare#link-session-errors), com a mensagem exata que
a API retorna e o que fazer a respeito.
