> ## 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

> Permite que tus usuarios vinculen cuentas sociales desde dentro de tu propio dashboard, con una sola etiqueta script y nuestros botones incrustados en tu 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} />

El widget Ayrshare Connect coloca nuestros botones de vinculación **dentro de tu propio dashboard**.
Cargas un script, colocas un slot allí donde corresponda una red en tu layout, y nosotros
renderizamos ahí un botón que ya muestra si la cuenta está conectada. Tu usuario hace clic y vincula
la cuenta sin salir de tu página — como mucho una ventana emergente, la de la propia red.

No escribes gestión de ventanas emergentes, ni callbacks de OAuth, ni refresco de sesión, ni lógica
por red. Cuando una red cambia algo en su lado, la corrección llega dentro de nuestros frames en el
momento en que la desplegamos; tú no redespliegas nada.

## Qué superficie quieres

| Superficie | Tu usuario ve | Marca blanca | Elígela cuando |
| - | - | - | - |
| **Widget — frames incrustados** ([`mount()`](#mount-a-slot)) | nuestros botones integrados en tu propio layout; sin ventana emergente hasta la de la propia red | **La más fuerte.** Tu página, tus fuentes y colores, y tu usuario nunca la abandona | tienes un dashboard con una fila por red y quieres que la vinculación ocurra ahí mismo. Max Pack. |
| **Widget — tu propio botón** ([`popup()`](#your-own-button)) | tu botón, y después una única ventana emergente para la red | **Fuerte.** La ventana emergente es nuestra, pero es breve y hereda tu apariencia | quieres tu propio botón y tus estilos, y ningún frame en tu layout. Max Pack. |
| **Página de vinculación alojada** ([cómo hacerlo](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | una página alojada por nosotros, con tu logo, tus colores y tu CSS personalizado | **La más débil.** Es nuestra página, y tu usuario abandona la tuya para usarla | quieres un único enlace que repartir, o incorporas usuarios por correo. Nada que construir, sin Max Pack. |

<div className="my-8">
  <Frame caption="Frames incrustados: nuestras tarjetas renderizadas dentro de tu propio layout, un slot por red o un slot para varias.">
    <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="A customer dashboard with Ayrshare Connect tiles for Instagram, TikTok and LinkedIn embedded in a card" width="2400" height="1120" data-path="images/multiple-users/connect-widget-frames.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="Tu propio botón: tú renderizas el botón, y una breve ventana emergente nuestra se encarga de la red.">
    <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="A customer dashboard with its own Connect buttons and an Ayrshare popup showing the Facebook hand-off screen" width="2880" height="1317" data-path="images/multiple-users/connect-widget-popup.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="Página de vinculación alojada: una página alojada por nosotros, con tu logo y tus colores, que tu usuario abandona tu 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="The hosted social linking page showing every available network" width="2336" height="1906" data-path="images/multiple-users/connect-widget-hosted.webp" />
  </Frame>
</div>

<Note>
  Las dos filas del widget son **una sola integración**, no dos. Un único `init` te da ambas: monta
  frames donde quieras nuestros botones y llama a `popup()` desde tu propio botón en el resto de
  sitios. Comparten una misma sesión y reportan en los mismos handlers.

  El [modo directo](/docs/multiple-users/connect-direct-mode) (Direct Mode) es la misma ventana emergente
  **sin** nuestro script — para una página con una Content-Security-Policy estricta, una página
  renderizada en servidor o una app nativa. Ahí abres y supervisas la ventana emergente tú mismo.
</Note>

## Añade el script

Fija una versión y su hash, o sigue un canal sin él. Nunca ambas cosas — un atributo `integrity` en
una URL que se mueve deja de funcionar en nuestra siguiente versión, porque el archivo que nombra ha
cambiado legítimamente.

```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>
```

| Ruta | Caché | `integrity` |
| - | - | - |
| `/ayrshare-connect/<version>/widget.js` | inmutable, un año | **sí** — fíjalo |
| `/ayrshare-connect/v1/widget.js` | cinco minutos | no |
| `/ayrshare-connect/latest/widget.js` | cinco minutos | no |

**`v1` es el canal recomendable.** Recoge correcciones pero nunca cruza un cambio incompatible.
`latest` cruza versiones mayores por definición, así que en algún momento entregará a tu página una
versión cuyo comportamiento no has revisado.

El hash de cada versión se publica en
[`manifest.json`](https://app.ayrshare.com/ayrshare-connect/manifest.json), que también indica qué
sirve actualmente cada canal:

```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 además empieza con un comentario que indica su propia versión, que es la forma más
rápida de decirnos qué está ejecutando realmente una página:

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

### Content-Security-Policy

Si tu página envía una Content-Security-Policy, necesita **dos** entradas, ambas con el host desde
el que cargas el script:

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

Esa es toda la lista. No necesitas **ninguna entrada `connect-src`** para nosotros — nuestros frames
llegan a nuestra API desde dentro de sí mismos, no desde tu página — y **ninguna entrada para
ventanas emergentes**, que son ventanas de nivel superior que tu política no gobierna. Ambas
entradas están medidas: quitar `script-src` bloquea el script, y quitar `frame-src` bloquea el frame.

## Inicia una instancia

`session` es la única opción obligatoria. Se llama **una vez por instancia**, no una vez por mount, y
debe devolver la respuesta de tu backend a
[Crear una Link Session](/docs/apis/profiles/create-link-session) con `mode: "connect"` —
`{ sessionId, token, expiresAt }` — exactamente como llegó.

```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());
});
```

Una sesión del widget no nombra **ningún** `network` — autoriza todas las redes que tu cuenta
permite, y cuáles aparecen se decide por mount, en tu página. Sí lleva un `origin`, que es lo único
que hace que los frames se rendericen: un frame comprueba la página que lo incrusta contra ese valor
y se niega a renderizarse en cualquier otro sitio.

<Warning>
  Crea la sesión en tu servidor. La llamada necesita tu API Key, y el token que devuelve inicia la
  sesión de tu usuario en su User Profile — trátalo como una contraseña.
</Warning>

Volvemos a llamar a `session` antes de que el token caduque, así que el widget sigue funcionando en
una página que se deja abierta todo el día. Una llamada que rechaza, o que no devuelve ningún token,
se reintenta dos veces más — a los 0,5 s y luego a 1 s — antes de rendirnos y emitir `error`. Así
que un refresco cuesta como mucho tres llamadas a tu endpoint.

| Opción | Por defecto | Hace |
| - | - | - |
| `session` | — | **Obligatoria.** Devuelve una link session en modo connect. |
| `appearance` | nuestros valores por defecto | Design tokens, aplicados dentro de cada frame. Consulta [Apariencia](#appearance). |
| `css` | ninguno | Una cadena CSS aplicada dentro de cada frame. Consulta [CSS personalizado](#custom-css). |
| `maxHeight` | `600` | Cuánto puede crecer un frame antes de que pase a desplazarse internamente. |

<h2 id="mount-a-slot">
  Monta un slot
</h2>

Un `mount` por slot. Pide una red, varias o todo lo que la sesión permita — la granularidad es tuya,
así que un slot puede ser una única fila de una tabla existente o un panel que lo contenga todo.

```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` acepta un selector CSS o un elemento, y devuelve `{ unmount, element }`. Lanza una excepción
si el destino no coincide con nada — que casi siempre es un slot que todavía no existe, así que monta
después de que tu marcado esté en el documento.

Cada mount es un iframe. Nos informa de su propia altura y lo redimensionamos para que coincida, de
modo que tu layout se reajusta cuando nuestro contenido cambia; pasado `maxHeight` el frame se
desplaza internamente en lugar de salirse de tu página. **El slot más estrecho que admitimos es de
300px.**

Las claves de red son las propias de Ayrshare, y las grafías alias también funcionan: `instagram` e
`instagramapi` significan ambas `instagramApi`, y `x` significa `twitter`. Una clave que no es una
red no renderiza ninguna tarjeta.

<h2 id="your-own-button">
  Tu propio botón
</h2>

Un cliente que prefiera usar su propio botón en lugar de uno de nuestros frames llama a `popup` en
su lugar. Ejecuta el mismo flujo, sobre la misma sesión, y reporta en los mismos handlers.

<Warning>
  **Llámalo directamente dentro del manejador de clic, sin nada awaited antes.** Un navegador solo
  permite una ventana emergente mientras todavía está procesando el clic de tu usuario, y ese permiso
  no sobrevive a un `await`. De todos modos no hay nada que esperar — la sesión se creó en `init`.
</Warning>

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

`popup` devuelve `{ close(), network }`, y **siempre** devuelve un handle — incluso tras una ventana
emergente bloqueada, donde `close()` no hace nada — de modo que tu código nunca tiene que comprobar
null antes de llamar a `close()`.

Todas las redes funcionan aquí, incluidas las que un frame termina dentro de su propio panel:
Facebook muestra su pantalla explicativa de traspaso en la ventana emergente, Bluesky y X muestran su
formulario de credenciales, y LinkedIn, Pinterest, YouTube y Google Business salen a la red y
vuelven.

Lanza una excepción síncrona en los tres casos que son errores de programación — ningún `network`,
una instancia destruida, o una sesión que aún no se ha resuelto. Una ventana emergente bloqueada
**no** es uno de ellos: eso emite `error` con `reason: "popupBlocked"`, porque tu usuario no hizo
nada mal.

Como mucho hay una ventana emergente abierta a la vez. Una segunda llamada cierra la primera y
reporta `cancelled` con `reason: "superseded"` sobre ella. Una ventana emergente abierta por uno de
nuestros **frames** es otra cosa y nunca se toca, así que tu botón no puede cancelar un flujo en
curso dentro de un slot montado.

<Note>
  Si la sesión puede vincular una red es una respuesta del **servidor**, no del script. Una sesión
  limitada a Bluesky a la que se le pide LinkedIn recibe un rechazo renderizado en la ventana
  emergente y reportado como `error`. El script solo comprueba que se haya nombrado alguna red.
</Note>

## React

El script no depende de ningún framework, así que React no necesita nada especial de nosotros — pero
hay cuatro cosas de su ciclo de vida que conviene hacer bien a la primera.

Carga el script **una vez**, fuera de tu árbol de componentes. En Next.js eso es `next/script` en tu
layout raíz; en Vite o Create React App es una etiqueta en `index.html`. Cargarlo por componente lo
vuelve a ejecutar en 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(() => {
    // Inside the effect, so the ref is attached: mount() throws if its target
    // does not exist yet, which is exactly what happens if you call it during 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` is one of ten events the widget reports. See Events below for
    // the full list, including `state`, which replaces polling.

    // destroy() takes the frames, the message listener and the timers with it.
    // Without this, a route change leaves all three behind.
    return () => {
      stop();
      connect.destroy();
    };
  }, []);

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

<Warning>
  **Nunca reutilices una instancia después de `destroy()`.** Una instancia destruida se queda
  destruida — `popup()` lanza una excepción sobre ella, y `mount()` no la revivirá. Crea una nueva
  instancia en la siguiente ejecución del efecto, que es lo que hace el código de arriba.
</Warning>

Dos consecuencias de ese patrón, y ninguna es un bug:

* **En desarrollo verás el callback de sesión dispararse dos veces.** El Strict Mode de React
  ejecuta los efectos mount → unmount → mount, así que la instancia se crea, se destruye y se vuelve
  a crear. La limpieza de arriba hace que eso sea seguro; cuesta una llamada extra a tu backend en
  desarrollo y ninguna en producción.
* **Mantén estables las dependencias del efecto.** Un array literal pasado directamente a `mount`
  desde el render de un padre es un valor nuevo cada vez, así que un efecto que dependa de él
  desmonta el widget y lo reconstruye en cada render. Memoízalo, o mantenlo constante como arriba.

## Eventos

Suscríbete con `on`, que devuelve una función para cancelar la suscripción. Los handlers reciben el
payload del evento y el mount del que procede; `off(name, handler)` hace el mismo trabajo cuando
prefieres nombrar el 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
```

Diez eventos. Todos llevan un `network` excepto `ready`, y excepto el único tipo de `error` que no
trata de ninguna red — consulta [`error` tiene dos orígenes](#error-has-two-sources) más abajo.

| Evento | Se dispara cuando | También lleva |
| - | - | - |
| `ready` | un frame está montado y listo | — |
| `click` | tu usuario hizo clic en una red, **en cualquiera de las dos direcciones** | `action`: `"connect"` o `"unlink"` |
| `started` | un intento de vinculación está en marcha | — |
| `selection` | tu usuario llegó a un selector o a un formulario | `step` |
| `success` | la cuenta está conectada **y guardada** | `displayName` (omitido cuando se desconoce), `refId` |
| `unlinked` | se eliminó una cuenta conectada, y la eliminación está guardada | — |
| `error` | el intento falló | `code`, `message`, `reason` |
| `cancelled` | tu usuario se echó atrás | `reason` |
| `closed` | la ventana emergente que usó este intento se ha cerrado | — |
| `state` | el estado de cuenta de una red, al montar y en cada cambio | `state`, `since` |

**Cuatro de ellos son finales** — `success`, `unlinked`, `error` y `cancelled` — y llega exactamente
uno por intento. `closed` es un aviso de ciclo de vida que sigue a un final, no un final en sí.

`click` se dispara **antes** de que empiece cualquier trabajo de vinculación, así que reporta un clic
que una ventana emergente bloqueada o una sesión muerta rechazará después. Es el evento que usar para
tus propias analíticas; `started` es el que significa que un intento está realmente en marcha.

### Razones

| Evento | `reason` | Significa |
| - | - | - |
| `error` | `popupBlocked` | el navegador se negó a abrir la ventana emergente |
| `cancelled` | `popupClosed` | tu usuario cerró la ventana a mano |
| `cancelled` | `scopesDenied` | tu usuario rechazó un permiso que la red pidió |
| `cancelled` | `userCancelled` | tu usuario se echó atrás, o tu código llamó a `handle.close()` |
| `cancelled` | `superseded` | una segunda llamada a `popup()` reemplazó este intento |

<h3 id="error-has-two-sources">
  `error` tiene dos orígenes
</h3>

Solo uno de ellos es un fallo de vinculación, y llevan campos distintos.

* Un **error de vinculación** lleva `network`, `code` y el mount del que procede.
* Un **error de sesión** — no pudimos crear o refrescar tu sesión — lleva solo `message`, porque
  no se estaba vinculando nada en ese momento.

Desestructura a la defensiva: `code` y `network` son `undefined` en el segundo tipo.

### `state` te ahorra el polling

`state` es el canal de datos, más que un informe sobre un intento. Cada frame emite uno por red al
montarse, con el estado actual de esa red y la marca de tiempo `since` desde la que lo mantiene, y
otro cada vez que un estado cambia — **incluidos los cambios que se originan en nuestro lado**, como
un token que muere y pasa a requerir revinculación. Así que puedes alimentar toda tu UI desde el
widget sin hacer polling de nada.

Los valores son el mismo enum que devuelve
[`GET /profiles` con `include=state`](/docs/apis/profiles/get-profiles): `linked`, `unlinked`,
`identityVerificationRequired`, `restricted`, `rateLimited`, `suspended`.

## Desvincular

Nuestras tarjetas desvinculan además de vincular. Tu usuario hace clic en una red conectada,
confirma, y la cuenta se elimina:

1. `click` se dispara con `action: "unlink"`.
2. `unlinked` se dispara una vez que la eliminación está guardada.

Una desvinculación que **falla** reporta `error`, y una de la que tu usuario se echa atrás en el
paso de confirmación reporta `cancelled`. No hay un evento separado de desvinculación fallida.

<h2 id="appearance">
  Apariencia
</h2>

La hoja de estilos de un cliente no puede llegar al interior de un frame cross-origin, así que los
estilos viajan como datos que aplicamos dentro de él. Pasa `appearance` a `init` como propiedades
personalizadas de CSS; cada una que no recibamos conserva nuestro valor por defecto.

```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>
  **Define los colores en pares.** Un token de fondo sin su token de primer plano al lado es la única
  manera de que esto produzca algo ilegible: pon `--ayr-connect-surface-bg` en un valor oscuro por sí
  solo y nuestro `--ayr-connect-surface-fg` por defecto sigue siendo azul marino oscuro. Nada puede
  inferir la otra mitad por ti.
</Warning>

Sin ningún `appearance`, cada token conserva su valor por defecto y un frame tiene este aspecto:

<div className="my-8">
  <Frame caption="Tokens por defecto: superficies blancas, texto azul marino oscuro, acento índigo, radio 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="Three Ayrshare Connect tiles with the default appearance" width="920" height="528" data-path="images/multiple-users/connect-widget-appearance-default.webp" />
  </Frame>
</div>

Pasa un puñado de tokens y el mismo frame adopta tu paleta. Este ejemplo vuelve las superficies azul
pálido, oscurece el texto y el acento para que combinen, y redondea las esquinas un poco más:

```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="Las mismas tres tarjetas después de aplicar los tokens de arriba.">
    <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="Three Ayrshare Connect tiles restyled with pale blue surfaces and a deeper blue accent" width="920" height="574" data-path="images/multiple-users/connect-widget-appearance-themed.webp" />
  </Frame>
</div>

**Hay una sola paleta y ningún preajuste claro/oscuro.** Nada depende de `prefers-color-scheme`, a
propósito — el tema de tu página puede no coincidir con el sistema operativo de tu usuario, y una
media query invalidaría silenciosamente los colores que elegiste. Un dashboard oscuro se tematiza
suministrando valores oscuros.

Esta lista de tokens es un contrato soportado que mantenemos entre versiones.

### Los tokens

| Token | Por defecto |
| - | - |
| `--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` |

Un valor que no sea CSS válido para su token se **ignora, con una advertencia en tu consola**, en
lugar de aplicarse. Eso importa más de lo que parece: un `8` sin unidad para `--ayr-connect-spacing`
es una cadena perfectamente inocente que invalidaría cada cálculo que la lea y colapsaría el layout,
sin ningún error en ninguna parte. Dales unidad a las longitudes.

### Tu propio nombre en la pantalla de traspaso

Antes de entregar tu usuario a una red, mostramos una breve pantalla que indica a través de quién se
está conectando. Dos ganchos te permiten hacerla tuya, y ambos son estables entre versiones.

`--ayr-connect-partner-name` establece la etiqueta. Es el único token cuyo valor es **texto**, así
que tiene que ir entrecomillado como una cadena CSS — un valor sin comillas es inválido y no
renderiza nada en absoluto:

```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') }",
});
```

El logo no es un token. Servimos la marca como un elemento posicionado y dimensionado, y tú lo
rellenas con una `background-image` mediante `css`, como arriba — un token que pudiera cargar una
imagen desde dentro de nuestro documento no es algo que aceptemos, así que la petición procede de una
regla que escribiste tú y no de un valor que nos pasaste.

<Note>
  `[data-ayr-connect-partner-mark]` y `[data-ayr-connect-partner-name]` son la **excepción** a la
  advertencia sobre CSS personalizado de abajo: estos dos selectores forman parte del contrato y los
  mantenemos entre versiones.
</Note>

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

`css` acepta una cadena que se aplica dentro de cada frame, para los casos que los tokens no cubren.

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

<Warning>
  **El CSS personalizado no está soportado entre versiones.** Sus selectores apuntan a nuestro
  marcado interno, que cambia entre releases — una regla que funciona hoy puede dejar de coincidir
  silenciosamente tras cualquier actualización. El contrato de tokens de arriba es la parte que
  mantenemos. Fija una versión si dependes de CSS personalizado.
</Warning>

Las ventanas emergentes heredan el `appearance` y el `css` de la instancia exactamente igual que los
frames, así que tu usuario no ve aparecer nuestros valores neutros por defecto a mitad de un flujo.

## Cosas que conviene saber antes de lanzar

<AccordionGroup>
  <Accordion title="Una ventana emergente cuya sesión ha caducado reporta cancelled, no error">
    A una ventana emergente que abriste con `popup()` no se le puede decir por qué se rechazó un
    token — para hacerlo tendría que confiar en un origen que aún no ha validado, cosa que nuestro
    modelo de seguridad no permite. Así que una ventana emergente con un token muerto se cierra y
    aflora como `cancelled` con `reason: "popupClosed"` en lugar de `error`.

    En la práctica esto es raro: una ventana emergente abierta antes de un refresco silencioso sigue
    funcionando, porque validó su token al abrirse. Si ves resultados `popupClosed` sin explicación,
    comprueba que tu endpoint de `session` esté devolviendo una sesión nueva.
  </Accordion>

  <Accordion title="Una sesión por instancia, no una por mount">
    Catorce mounts comparten un token y cuestan una llamada a tu backend, no catorce. Si quieres
    slots con distinto alcance — un `allowedSocial` distinto en algunos — ejecuta un segundo `init`
    con su propia sesión en lugar de esperar que un mount la acote.
  </Accordion>

  <Accordion title="Los frames solo se renderizan en el origin que declaraste">
    Cada sesión lleva el `origin` en el que corre tu página, y un frame compara la página que lo
    incrusta contra ese valor antes de renderizar nada. Un frame incrustado en otro sitio se queda en
    blanco y no envía eventos. No hay ninguna allowlist que registrar ni nada que configurar — envía
    el `origin` correcto al crear la sesión.
  </Accordion>

  <Accordion title="La altura se gestiona por ti, y no es un evento">
    Los frames informan de su altura al script, y el script los redimensiona. Tu layout simplemente
    se reajusta. No hay ningún evento resize al que suscribirse, ni nada que medir en tu lado.
  </Accordion>
</AccordionGroup>

## Requisitos

<ul className="custom-bullets">
  <li>
    El **[Max Pack](/docs/additional/maxpack)**. Una sesión del widget es una sesión en modo connect, y
    crear una sin el Max Pack devuelve `code: 504`. Contacta con soporte si necesitas habilitar el
    modo connect en una cuenta sin él.
  </li>

  <li>
    Un **`origin`** en cada sesión — el origen exacto en el que corre tu página. Omitirlo devuelve
    `code: 505`; un valor que no sea un origen `https`, un esquema personalizado o `http://localhost`
    devuelve `code: 506`.
  </li>

  <li>
    **Ningún `network`** en la sesión. Ese parámetro es lo que convierte una sesión en
    [modo directo](/docs/multiple-users/connect-direct-mode), y la URL de una sesión en modo directo no es
    lo que el script espera.
  </li>
</ul>

Todos los códigos de arriba están en la referencia de
[Errores de link session](/docs/errors/errors-ayrshare#link-session-errors), con el mensaje exacto que
devuelve la API y qué hacer al respecto.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.