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

# Віджет Ayrshare Connect

> Дозвольте вашим користувачам зв'язувати соціальні акаунти зсередини вашої власної панелі керування — один тег script і наші кнопки, вбудовані у вашу сторінку.

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} />

Віджет Ayrshare Connect розміщує наші кнопки зв'язування **всередині вашої власної панелі
керування**. Ви завантажуєте один скрипт, ставите слот там, де мережа має бути у вашому макеті, і
ми рендеримо там кнопку, яка вже показує, чи підключено акаунт. Ваш користувач натискає її і
зв'язує акаунт, не залишаючи вашої сторінки — щонайбільше одне спливаюче вікно, і то власне вікно
мережі.

Ви не пишете обробку спливаючих вікон, OAuth-колбеки, оновлення сесії чи логіку для кожної мережі.
Коли мережа щось змінює на своєму боці, виправлення з'являється всередині наших фреймів у момент
нашого деплою; ви нічого не редеплоїте.

## Яка поверхня вам потрібна

| Поверхня | Ваш користувач бачить | White-labelling | Вибирайте, коли |
| - | - | - | - |
| **Віджет — вбудовані фрейми** ([`mount()`](#mount-a-slot)) | наші кнопки просто у вашому власному макеті; жодного спливаючого вікна до власного вікна мережі | **Найсильніший.** Ваша сторінка, ваші шрифти й кольори, і ваш користувач ніколи її не залишає | у вас є панель керування з рядком для кожної мережі, і ви хочете, щоб зв'язування відбувалося на місці. Max Pack. |
| **Віджет — ваша власна кнопка** ([`popup()`](#your-own-button)) | вашу кнопку, а потім одне спливаюче вікно для мережі | **Сильний.** Спливаюче вікно наше, але воно коротке й успадковує ваш вигляд | ви хочете власну кнопку й стилізацію та жодних фреймів у вашому макеті. Max Pack. |
| **Хостована сторінка зв'язування** ([як саме](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | сторінку, яку хостимо ми, з вашим логотипом, кольорами та власним CSS | **Найслабший.** Це наша сторінка, і ваш користувач залишає вашу, щоб нею скористатися | ви хочете одне посилання, яке можна роздати, або проводите онбординг електронною поштою. Нічого не потрібно будувати, Max Pack не потрібен. |

<div className="my-8">
  <Frame caption="Вбудовані фрейми: наші плитки, відрендерені всередині вашого власного макета — один слот на мережу або один слот на кілька мереж.">
    <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="Клієнтська панель керування з плитками Ayrshare Connect для Instagram, TikTok і LinkedIn, вбудованими в картку" width="2400" height="1120" data-path="images/multiple-users/connect-widget-frames.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="Ваша власна кнопка: ви рендерите кнопку, а одне коротке наше спливаюче вікно опрацьовує мережу.">
    <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="Клієнтська панель керування з власними кнопками Connect і спливаючим вікном Ayrshare з екраном передачі Facebook" width="2880" height="1317" data-path="images/multiple-users/connect-widget-popup.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="Хостована сторінка зв'язування: сторінка, яку хостимо ми, з вашим логотипом і кольорами, заради якої ваш користувач залишає ваш застосунок.">
    <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="Хостована сторінка соціального зв'язування, що показує всі доступні мережі" width="2336" height="1906" data-path="images/multiple-users/connect-widget-hosted.webp" />
  </Frame>
</div>

<Note>
  Два рядки з віджетом — це **одна інтеграція**, а не дві. Один `init` дає обидва варіанти:
  монтуйте фрейми там, де мають бути наші кнопки, і викликайте `popup()` з власної кнопки всюди
  інде. Вони ділять одну сесію і звітують через ті самі обробники.

  [Прямий режим](/docs/multiple-users/connect-direct-mode) (Direct Mode) — це те саме спливаюче вікно,
  але **без** нашого скрипта — для сторінки із суворою Content-Security-Policy, сторінки з
  серверним рендерингом або нативного застосунку. Там ви відкриваєте спливаюче вікно й стежите за
  ним самостійно.
</Note>

## Додайте скрипт

Зафіксуйте версію та її хеш або слідкуйте за каналом без нього. Ніколи не робіть обох одразу —
атрибут `integrity` на рухомому URL перестає працювати з нашим наступним релізом, бо файл, який
він називає, легітимно змінився.

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

| Шлях | Кешування | `integrity` |
| - | - | - |
| `/ayrshare-connect/<version>/widget.js` | незмінний, один рік | **так** — зафіксуйте його |
| `/ayrshare-connect/v1/widget.js` | п'ять хвилин | ні |
| `/ayrshare-connect/latest/widget.js` | п'ять хвилин | ні |

**`v1` — канал, який варто рекомендувати.** Він підхоплює виправлення, але ніколи не перетинає
злам сумісності. `latest` перетинає мажорні версії за визначенням, тож він зрештою підсуне вашій
сторінці версію, поведінку якої ви не переглядали.

Хеш кожної версії опубліковано в
[`manifest.json`](https://app.ayrshare.com/ayrshare-connect/manifest.json), який також називає, що
саме зараз обслуговує кожен канал:

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

Кожен бандл також починається з коментаря з назвою власної версії — це найшвидший спосіб сказати
нам, що насправді виконує сторінка:

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

### Content-Security-Policy

Якщо ваша сторінка надсилає Content-Security-Policy, їй потрібні **два** записи, обидва з хостом,
з якого ви завантажуєте скрипт:

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

Це весь список. Вам **не потрібен запис `connect-src`** для нас — наші фрейми звертаються до
нашого API зсередини себе, а не з вашої сторінки — і **жодного запису для спливаючих вікон**,
які є вікнами верхнього рівня, що не підпадають під вашу політику. Обидва записи перевірено
вимірюванням: вилучення `script-src` блокує скрипт, а вилучення `frame-src` блокує фрейм.

## Запустіть екземпляр

`session` — єдиний обов'язковий параметр. Він викликається **один раз на екземпляр**, а не один
раз на кожен mount, і має повернути відповідь вашого бекенду на
[Створення Link Session](/docs/apis/profiles/create-link-session) з `mode: "connect"` —
`{ sessionId, token, expiresAt }` — точно такою, якою вона надійшла.

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

Сесія віджета **не** називає `network` — вона авторизує кожну мережу, яку дозволяє ваш акаунт, а
які з них з'являться, вирішується для кожного mount, на вашій сторінці. Вона несе `origin`, і саме
він робить рендеринг фреймів взагалі можливим: фрейм звіряє сторінку, що його вбудувала, з цим
значенням і відмовляється рендеритися будь-де інде.

<Warning>
  Створюйте сесію на своєму сервері. Виклик потребує вашого API Key, а повернений токен авторизує
  вашого користувача в його User Profile — ставтеся до нього як до пароля.
</Warning>

Ми викликаємо `session` знову до закінчення строку дії токена, тож віджет продовжує працювати на
сторінці, залишеній відкритою на весь день. Виклик, який завершується відмовою або не повертає
токен, повторюється ще двічі — через 0,5 с, потім через 1 с — перш ніж ми здаємося і генеруємо
`error`. Тож одне оновлення коштує щонайбільше трьох викликів вашого ендпоінта.

| Параметр | За замовчуванням | Що робить |
| - | - | - |
| `session` | — | **Обов'язковий.** Повертає сесію зв'язування в режимі connect. |
| `appearance` | наші стандартні значення | Токени дизайну, застосовані всередині кожного фрейма. Див. [Зовнішній вигляд](#appearance). |
| `css` | немає | Рядок CSS, застосований всередині кожного фрейма. Див. [Власний CSS](#custom-css). |
| `maxHeight` | `600` | Наскільки високим може стати фрейм, перш ніж він почне прокручуватися всередині. |

<h2 id="mount-a-slot">
  Змонтуйте слот
</h2>

Один `mount` на слот. Запитуйте одну мережу, кілька або все, що дозволяє сесія — гранулярність за
вами, тож слот може бути одним рядком у наявній таблиці або однією панеллю, що вміщує все.

```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` приймає CSS-селектор або елемент і повертає `{ unmount, element }`. Він кидає виняток,
якщо ціль нічому не відповідає — це майже завжди слот, якого ще не існує, тож монтуйте після того,
як ваша розмітка опиниться в документі.

Кожен mount — це один iframe. Він повідомляє нам власну висоту, і ми змінюємо його розмір
відповідно, тож ваш макет перебудовується разом зі зміною нашого вмісту; після `maxHeight` фрейм
прокручується всередині замість того, щоб вилазити за межі вашої сторінки. **Найвужчий слот, який
ми підтримуємо, — 300px.**

Ключі мереж — власні ключі Ayrshare, і альтернативні написання теж працюють: `instagram` та
`instagramapi` обидва означають `instagramApi`, а `x` означає `twitter`. Ключ, який не є мережею,
не рендерить жодної плитки.

<h2 id="your-own-button">
  Ваша власна кнопка
</h2>

Клієнт, який радше використає власну кнопку, ніж один із наших фреймів, викликає натомість
`popup`. Він запускає той самий потік, у тій самій сесії, і звітує через ті самі обробники.

<Warning>
  **Викликайте його безпосередньо всередині обробника кліку, без жодного await перед ним.**
  Браузер дозволяє спливаюче вікно, лише поки він ще опрацьовує клік вашого користувача, і цей
  дозвіл не переживає `await`. Втім, чекати й нема чого — сесію було створено під час `init`.
</Warning>

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

`popup` повертає `{ close(), network }` і **завжди** повертає handle — навіть після заблокованого
спливаючого вікна, де `close()` нічого не робить — тож вашому коду ніколи не доводиться
перевіряти його на null перед викликом `close()`.

Тут працює кожна мережа, включно з тими, які фрейм завершує всередині власної панелі: Facebook
показує свій екран-пояснення передачі у спливаючому вікні, Bluesky і X показують свою форму
облікових даних, а LinkedIn, Pinterest, YouTube і Google Business виходять до мережі й
повертаються.

Він синхронно кидає виняток для трьох речей, які є помилками програмування — відсутній `network`,
знищений екземпляр або сесія, яка ще не розв'язалася. Заблоковане спливаюче вікно **не** одна з
них: воно генерує `error` з `reason: "popupBlocked"`, бо ваш користувач не зробив нічого поганого.

Одночасно відкрите щонайбільше одне спливаюче вікно. Другий виклик закриває перше й повідомляє на
ньому `cancelled` з `reason: "superseded"`. Спливаюче вікно, відкрите одним із наших **фреймів**, —
інша річ, і його ніколи не чіпають, тож ваша кнопка не може скасувати потік, що виконується
всередині змонтованого слота.

<Note>
  Чи може сесія зв'язати мережу — це відповідь **сервера**, а не скрипта. Сесія, обмежена Bluesky,
  яку попросили про LinkedIn, отримує відмову, відрендерену у спливаючому вікні й повідомлену як
  `error`. Скрипт перевіряє лише те, що мережу взагалі назвали.
</Note>

## React

Скрипт не залежить від фреймворків, тож React не потребує від нас нічого особливого — але чотири
речі про його життєвий цикл варто зробити правильно з першого разу.

Завантажуйте скрипт **один раз**, поза деревом ваших компонентів. У Next.js це `next/script` у
кореневому layout; у Vite або Create React App це тег в `index.html`. Завантаження у кожному
компоненті перезапускає його при кожному монтуванні.

```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>
  **Ніколи не використовуйте екземпляр повторно після `destroy()`.** Знищений екземпляр
  залишається знищеним — `popup()` на ньому кидає виняток, а `mount()` його не поверне. Створіть
  новий екземпляр у наступному запуску ефекту, що й робить код вище.
</Warning>

Два наслідки цього патерну, і жоден із них не є багом:

* **У розробці ви побачите, що колбек session спрацьовує двічі.** Strict Mode у React виконує
  ефекти в порядку mount → unmount → mount, тож екземпляр створюється, знищується і створюється
  знову. Наведене вище очищення робить це безпечним; це коштує одного додаткового виклику вашого
  бекенду в dev і жодного в production.
* **Тримайте залежності ефекту стабільними.** Літерал масиву, переданий прямо в `mount` з рендера
  батьківського компонента, — це щоразу нове значення, тож ефект, який від нього залежить,
  розбирає віджет і перебудовує його при кожному рендері. Мемоізуйте його або тримайте константою,
  як вище.

## Події

Підписуйтеся через `on`, який повертає функцію відписки. Обробники отримують payload події та
mount, з якого вона надійшла; `off(name, 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
```

Десять подій. Кожна несе `network`, крім `ready` і крім того одного різновиду `error`, який
взагалі не про мережу — див. [`error` має два джерела](#error-has-two-sources) нижче.

| Подія | Спрацьовує, коли | Також несе |
| - | - | - |
| `ready` | фрейм змонтовано і він готовий | — |
| `click` | ваш користувач натиснув мережу, **у будь-якому напрямку** | `action`: `"connect"` або `"unlink"` |
| `started` | спроба зв'язування триває | — |
| `selection` | ваш користувач дійшов до пікера або форми | `step` |
| `success` | акаунт підключено **і збережено** | `displayName` (пропускається, коли невідомий), `refId` |
| `unlinked` | підключений акаунт видалено, і видалення збережено | — |
| `error` | спроба зазнала невдачі | `code`, `message`, `reason` |
| `cancelled` | ваш користувач відмовився | `reason` |
| `closed` | спливаюче вікно цієї спроби закрилося | — |
| `state` | стан акаунта мережі, при монтуванні та при кожній зміні | `state`, `since` |

**Чотири з них — завершення** — `success`, `unlinked`, `error` і `cancelled` — і рівно одне
надходить на кожну спробу. `closed` — це повідомлення життєвого циклу, яке йде слідом за
завершенням, а не є ним.

`click` спрацьовує **до** початку будь-якої роботи зі зв'язування, тож він повідомляє про клік,
який заблоковане спливаюче вікно або мертва сесія згодом відхилить. Це подія для вашої власної
аналітики; `started` — та, що означає, що спроба справді виконується.

### Причини

| Подія | `reason` | Означає |
| - | - | - |
| `error` | `popupBlocked` | браузер відмовився відкрити спливаюче вікно |
| `cancelled` | `popupClosed` | ваш користувач закрив вікно вручну |
| `cancelled` | `scopesDenied` | ваш користувач відхилив дозвіл, який запитала мережа |
| `cancelled` | `userCancelled` | ваш користувач відмовився, або ваш код викликав `handle.close()` |
| `cancelled` | `superseded` | другий виклик `popup()` замінив цю спробу |

<h3 id="error-has-two-sources">
  `error` має два джерела
</h3>

Лише одне з них — невдале зв'язування, і вони несуть різні поля.

* **Помилка зв'язування** несе `network`, `code` і mount, з якого вона надійшла.
* **Помилка сесії** — ми не змогли створити або оновити вашу сесію — несе лише `message`, бо в
  той момент нічого не зв'язувалося.

Деструктуруйте обережно: `code` і `network` — `undefined` у другому різновиді.

### `state` позбавляє вас опитування

`state` — це канал даних, а не звіт про спробу. Кожен фрейм генерує одну подію на мережу при
монтуванні, з поточним станом цієї мережі й міткою часу `since`, від якої вона його тримає, і ще
одну щоразу, коли стан змінюється — **включно зі змінами, що походять з нашого боку**, наприклад
коли токен помирає у стан «потрібне повторне зв'язування». Тож ви можете керувати всім своїм UI з
віджета, нічого не опитуючи.

Значення — той самий перелік, який повертає
[`GET /profiles` з `include=state`](/docs/apis/profiles/get-profiles): `linked`, `unlinked`,
`identityVerificationRequired`, `restricted`, `rateLimited`, `suspended`.

## Від'єднання

Наші плитки не лише зв'язують, а й від'єднують. Ваш користувач натискає підключену мережу,
підтверджує, і акаунт видаляється:

1. Спрацьовує `click` з `action: "unlink"`.
2. Спрацьовує `unlinked`, щойно видалення збережено.

Від'єднання, яке **зазнало невдачі**, повідомляє `error`, а те, від якого ваш користувач
відмовився на кроці підтвердження, повідомляє `cancelled`. Окремої події «від'єднання не вдалося»
немає.

<h2 id="appearance">
  Зовнішній вигляд
</h2>

Таблиця стилів клієнта не може дістатися всередину крос-доменного фрейма, тож стилізація
подорожує як дані, які ми застосовуємо всередині нього. Передайте `appearance` в `init` як
користувацькі властивості CSS; кожна, якої ми не отримали, зберігає наше стандартне значення.

```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>
  **Задавайте кольори парами.** Токен фону без токена переднього плану поруч — єдиний спосіб
  отримати щось нечитабельне: задайте `--ayr-connect-surface-bg` темним значенням самим по собі, і
  наш стандартний `--ayr-connect-surface-fg` усе ще буде темно-синім. Ніщо не може вивести другу
  половину за вас.
</Warning>

Без жодного `appearance` кожен токен тримає своє стандартне значення, і фрейм виглядає так:

<div className="my-8">
  <Frame caption="Стандартні токени: білі поверхні, темно-синій текст, акцент індиго, радіус 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="Три плитки Ayrshare Connect зі стандартним виглядом" width="920" height="528" data-path="images/multiple-users/connect-widget-appearance-default.webp" />
  </Frame>
</div>

Передайте жменьку токенів — і той самий фрейм набуває вашої палітри. Цей приклад робить поверхні
блідо-блакитними, поглиблює текст і акцент відповідно й трохи більше заокруглює кути:

```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="Ті самі три плитки після застосування наведених вище токенів.">
    <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="Три плитки Ayrshare Connect, перестилізовані з блідо-блакитними поверхнями й глибшим синім акцентом" width="920" height="574" data-path="images/multiple-users/connect-widget-appearance-themed.webp" />
  </Frame>
</div>

**Є одна палітра й жодного світлого/темного пресета.** Ніщо не орієнтується на
`prefers-color-scheme` — навмисно: тема вашої сторінки може не збігатися з операційною системою
вашого користувача, і media query мовчки переголосувала б кольори, які ви обрали. Темна панель
керування отримує тему через темні значення.

Цей список токенів — підтримуваний контракт, який ми зберігаємо між версіями.

### Токени

| Токен | За замовчуванням |
| - | - |
| `--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` |

Значення, яке не є валідним CSS для свого токена, **ігнорується з попередженням у вашій
консолі**, а не застосовується. Це важливіше, ніж звучить: `8` без одиниць виміру для
`--ayr-connect-spacing` — цілком безневинний рядок, який зробив би недійсним кожне обчислення, що
його читає, і зруйнував би макет без жодної помилки будь-де. Давайте довжинам одиниці виміру.

### Ваша власна назва на екрані передачі

Перш ніж передати вашого користувача мережі, ми показуємо короткий екран із назвою того, через
кого він підключається. Два гачки дозволяють зробити його вашим власним, і обидва стабільні між
версіями.

`--ayr-connect-partner-name` задає підпис. Це єдиний токен, значення якого — **текст**, тож його
треба брати в лапки як рядок CSS — значення без лапок недійсне і не рендерить взагалі нічого:

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

Логотип — не токен. Ми постачаємо знак як позиціонований елемент із заданим розміром, а ви
заповнюєте його через `background-image` у `css`, як вище — токен, який міг би завантажити
зображення зсередини нашого документа, ми не приймаємо, тож запит походить із правила, яке
написали ви, а не зі значення, яке ви нам передали.

<Note>
  `[data-ayr-connect-partner-mark]` і `[data-ayr-connect-partner-name]` — **виняток** із
  застереження про власний CSS нижче: ці два селектори є частиною контракту, і ми зберігаємо їх
  між версіями.
</Note>

<h3 id="custom-css">
  Власний CSS
</h3>

`css` приймає рядок, застосовуваний всередині кожного фрейма, для випадків, які токени не
покривають.

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

<Warning>
  **Власний CSS не підтримується між версіями.** Його селектори орієнтуються на нашу внутрішню
  розмітку, яка змінюється між релізами — правило, що працює сьогодні, може мовчки перестати
  збігатися після будь-якого оновлення. Контракт токенів вище — та частина, яку ми зберігаємо.
  Зафіксуйте версію, якщо ви залежите від власного CSS.
</Warning>

Спливаючі вікна успадковують `appearance` і `css` екземпляра точно так само, як фрейми, тож ваш
користувач не спостерігає, як наші нейтральні стандартні стилі з'являються посеред потоку.

## Варто знати перед випуском

<AccordionGroup>
  <Accordion title="Спливаюче вікно з простроченою сесією повідомляє cancelled, а не error">
    Спливаючому вікну, відкритому через `popup()`, не можна сказати, чому токен було відхилено —
    щоб це зробити, воно мало б довіритися origin, який ще не валідувало, чого наша модель безпеки
    не дозволяє. Тож спливаюче вікно з мертвим токеном закривається і проявляється як `cancelled`
    з `reason: "popupClosed"`, а не `error`.

    На практиці це рідкість: спливаюче вікно, відкрите до тихого оновлення, продовжує працювати,
    бо валідувало свій токен під час відкриття. Якщо ви бачите незрозумілі результати
    `popupClosed`, перевірте, що ваш ендпоінт `session` повертає свіжу сесію.
  </Accordion>

  <Accordion title="Одна сесія на екземпляр, а не на кожен mount">
    Чотирнадцять mount ділять один токен і коштують один виклик вашого бекенду, а не чотирнадцять.
    Якщо вам потрібні слоти з різними межами — інший `allowedSocial` на деяких із них — запустіть
    другий `init` із власною сесією, а не очікуйте, що mount її звузить.
  </Accordion>

  <Accordion title="Фрейми рендеряться лише на origin, який ви задекларували">
    Кожна сесія несе `origin`, на якому працює ваша сторінка, і фрейм звіряє сторінку, що його
    вбудувала, з цим значенням, перш ніж щось рендерити. Фрейм, вбудований деінде, залишається
    порожнім і не надсилає жодних подій. Немає allowlist для реєстрації і нічого налаштовувати —
    надішліть правильний `origin` під час створення сесії.
  </Accordion>

  <Accordion title="Висота опрацьовується за вас і не є подією">
    Фрейми повідомляють свою висоту скрипту, і скрипт змінює їхній розмір. Ваш макет просто
    перебудовується. Немає події resize, на яку можна підписатися, і нічого вимірювати на вашому
    боці.
  </Accordion>
</AccordionGroup>

## Вимоги

<ul className="custom-bullets">
  <li>
    **[Max Pack](/docs/additional/maxpack)**. Сесія віджета — це сесія в режимі connect, і її створення
    без Max Pack повертає `code: 504`. Зверніться до підтримки, якщо вам потрібно ввімкнути режим
    connect на акаунті без нього.
  </li>

  <li>
    **`origin`** на кожній сесії — точний origin, на якому працює ваша сторінка. Його пропуск
    повертає `code: 505`; значення, яке не є origin `https`, власною схемою або
    `http://localhost`, повертає `code: 506`.
  </li>

  <li>
    **Без `network`** на сесії. Саме цей параметр робить сесію натомість
    [прямим режимом](/docs/multiple-users/connect-direct-mode), а URL сесії прямого режиму — не те, чого
    очікує скрипт.
  </li>
</ul>

Кожен наведений вище код є в довіднику
[Помилки Link Session](/docs/errors/errors-ayrshare#link-session-errors), з точним повідомленням, яке
повертає API, і тим, що з цим робити.


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