Стилизация чата — цвета и логотип
Приведите внешний вид ссылки для обмена или встроенного виджета к бренду клиента: один цвет, логотип и CSS только в том случае, если брендбук этого требует.
list_sharesconfigure_shareset_pwa_brandingset_custom_domainПринцип — задайте один цвет, а не палитру. Цвета переднего плана вычисляются при сохранении, поэтому все сочетания остаются читаемыми. Выбор собственных цветов текста — это путь к тому, чтобы выпустить кнопку, которую никто не сможет прочитать.
Брендинг принадлежит share, а не агенту: один и тот же агент может быть виджетом под белым лейблом на одном сайте и обычной ссылкой на другом. Поэтому сначала получите токен.
list_shares(agent_name="mortgage-advisor")
# → [{ token: "RuqY…", label: "Website widget", config: { theme_color: "#F7B331", … } }]
configure_share(
agent_name="mortgage-advisor",
token="RuqY…",
theme_color="#F7B331",
logo_url="https://cdn.example.com/brand/mark-512.png",
)Изменяются только переданные поля — остальная часть конфигурации остаётся без изменений.
Переименование share
label — это имя в верхней части страницы чата и заголовок вкладки браузера. Это поле в
configure_share, как и любое другое:
configure_share(agent_name="mortgage-advisor", token="RuqY…", label="Pip — reading buddy")Вам никогда не нужно создавать новый share и удалять старый, чтобы переименовать его. Новый share означает новый токен, поэтому ссылка, уже встроенная на сайт клиента, перестанет работать — видимое имя и адрес — это разные вещи, и после запуска безопасно менять только одно из них.
Второй цвет только если у бренда он есть
У большинства брендов один цвет, и здесь ничего делать не нужно. У некоторых есть основной и акцентный — передайте
второй как highlight_color, и он будет использоваться там, где элемент должен быть заметен, не конкурируя
с основным действием:
configure_share(
agent_name="pip",
token="ENhs…",
theme_color="#0B4230", # primary: buttons, the current storyline step, user bubbles
highlight_color="#D9A922", # highlight: second chart series, citation markers
)Его цвета переднего плана вычисляются при сохранении точно так же, как и у основного, поэтому
гарантия читаемости сохраняется. Не указывайте его, и ничего не изменится — --acc-2 по умолчанию использует основной цвет, что
нужно и требуется каждому бренду с одним цветом, и не требует конфигурации.
Не расширяйте его вручную. Вы можете переопределить наши классы компонентов из custom_css,
но вы об этом пожалеете: эти имена классов — наши внутренние, они меняются, и когда это произойдёт,
ваш брендинг сломается незаметно. Задайте два цвета и позвольте компонентам решить, где каждый из них принадлежит.
Цвет: передайте один, получите четыре
theme_color — это основной цвет бренда, #RGB или #RRGGBB. При сохранении платформа выводит
цвета переднего плана по контрасту WCAG и сохраняет их:
| Выведенный | Используется для |
|---|---|
theme_color_fg | текст на цвете бренда — кнопка отправки, пузыри пользователя, бейдж непрочитанных |
theme_color_text | цвет бренда, используемый как текст, на светлом фоне |
theme_color_text_dark | то же самое, на тёмном фоне |
Светлый цвет бренда поэтому получает тёмный текст на нём, а не белый. #F7B331 с белым текстом
имеет контраст 1.83:1 — ниже даже порогового значения 3:1 для UI-компонентов, что означает, что метка действительно
нечитаема; выведенный тёмный передний план имеет контраст 10.21:1.
Не вычисляйте палитру самостоятельно и не пытайтесь задавать цвета текста — они вычисляются на сервере, а ваши значения перезаписываются. Передача одного цвета — это вся задача.
Очистка его (theme_color="") также сбрасывает выведенные значения и возвращает виджет к
синему по умолчанию. Оставление его незаданным делает то же самое — встроенные токены уже являются парой.
Логотип: соотношение сторон определяет, где его можно использовать
Передайте абсолютный URL. Логотип появляется в двух местах с разными ограничениями:
- Верхняя панель — выровнена по высоте, ширина свободна. Подходит любое соотношение сторон, включая широкий логотип-название.
- Свёрнутый пузырь — квадратный, потому что логотип является формой пузыря. Здесь используются только соотношения между 0.74 и 1.35; широкий логотип-название по умолчанию использует иконку платформы вместо того, чтобы быть сплющенным в узкую полосу, которую невозможно прочитать.
Так что для широкого логотип-названия практический ответ — два файла: логотип-название для шапки, квадратный знак для пузыря. Если у клиента только логотип-название, шапка всё равно покажет их бренд, а пузырь покажет нейтральную иконку — это лучше, чем нечитаемая полоска.
Не обрезайте логотип-название до квадрата. Обрезка оставляет половину слова, что больше не является их товарным знаком.
Когда руководство по бренду переопределяет всё
У некоторых клиентов есть строгая палитра, которая не следует нашей ни в одной теме. custom_css
вставляется после переменных бренда, поэтому простое объявление уже побеждает — не требуется !important.
(Раньше это было необходимо, потому что цвета задавались как встроенные стили. Больше это не так.)
⚠️ Прочтите это перед написанием любого CSS
Вы должны написать два блока:
:rootдля светлой,html[data-theme="dark"]для тёмной.
:rootсовпадает в обеих темах и имеет приоритет над правилами тёмной темы платформы (одинаковая специфичность, и ваш блок идёт позже). Поэтому палитра, написанная только под:root, означает, что переключатель светлой/тёмной темы посетителя ничего не меняет вообще —data-themeпереключается, но за ним не следует переменная.Это уже произошло в продакшене: агент поместил полную тёмную палитру под
:root, страница выглядела отлично, а кнопка темы стала неработающим элементом, который никто не замечал неделями. Страница выглядит правильно, что именно поэтому об этом не сообщают.Если клиенту действительно нужна только одна тема, установите поле
themeshare вlightилиdark. Это скроет переключатель, а не сломает его.
Правильная форма — скопируйте это и замените значения:
/* LIGHT — the light values go here, not the dark ones */
:root{
--acc: #6b21a8; /* brand primary — backgrounds, focus rings */
--acc-fg: #ffffff; /* text on --acc: YOU own the contrast here */
--acc-text-l: #581c87; /* brand colour used as text, light theme */
--bg: #fdfbff;
--surface: #ffffff;
--text: #1e1b2e;
--border: #e6e0f0;
}
/* DARK — required whenever you touched --bg / --surface / --text above */
html[data-theme="dark"]{
--acc: #a855f7;
--acc-fg: #1a0b2e;
--acc-text-d: #c084fc; /* note: -d, the dark-theme variant */
--bg: #120c1d;
--surface: #1c142b;
--text: #f0e9ff;
--border: #32254a;
}Это полный набор, который стоит переопределять — виджет намеренно использует мало цветов.
Какие переменные требуют тёмного блока? Те, которые описывают поверхность, потому что они инвертируются
между темами: --bg, --surface, --text, --border. --acc — это цвет бренда и обычно
остаётся тем же в обеих темах, хотя в примере он сделан ярче для тёмной темы, потому что глубокий фиолетовый на
почти чёрном фоне трудно увидеть.
Ещё две вещи, которые стоит запомнить:
custom_cssне гарантирует читаемость.theme_colorгарантирует. Обращайтесь к CSS только для того, что вывод не может выразить, и оставляйтеtheme_colorустановленным как базовое значение.- Установка
--acc-text-lи--acc-text-dв одно и то же значение лишает их смысла. Они существуют именно для того, чтобы цвет бренда был читаем как текст на противоположных фонах.
configure_share возвращает массив warnings, когда обнаруживает ошибку с единственным блоком :root — прочитайте его.
Консоль показывает то же предупреждение рядом с редактором CSS.
css_url загружает внешнюю таблицу стилей вместо встроенной, для клиентов, которые хранят свой файл темы отдельно.
Интерактивные элементы, управляемые вашими цветами
Каждый акцентный элемент управления в чате читает ту же пару — фон --acc, текст --acc-fg:
| Элемент управления | Селектор (стабильный) |
|---|---|
| Кнопка отправки формы Ask (формы, которые агенты рендерят с блоками ```ask) | .ca-form .casub |
| Строки опций формы Ask | .ca-form label (при наведении: :hover) |
| Выбранная строка формы Ask | .ca-form label:has(input:checked) |
| Сам радио-/чекбокс формы Ask | .ca-form input (через accent-color) |
| Кнопка отправки / пузыри пользователя / бейдж непрочитанных | .composer .send, .m.user .content, .sbadge |
| Кнопки кнопочных вопросов (при наведении) | .qs button |
| Кнопка удержания для разговора во время записи — фон, метка и пульсирующее свечение | .ca-talk.rec |
| Кнопка установки PWA | .pwabar .go, .pwacard .go |
Выбранные строки стилизованы по умолчанию — рамка цвета бренда, оттенок бренда на 10% за строкой,
и нативный элемент управления, окрашенный с помощью accent-color — всё управляется --acc, поэтому установка theme_color
снова является всей задачей. Свечение кнопки записи голоса также выводится из --acc
(через color-mix с альфа-каналом 22%/55%), поэтому оно пульсирует цветом бренда автоматически; переопределяйте его в
custom_css только для намеренно иного оформления:
/* softer, wider recording glow — both themes if you also touch surfaces */
:root .ca-talk.rec{
box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 15%, transparent),
0 0 40px color-mix(in srgb, var(--acc) 40%, transparent);
}Обращайтесь к CSS только тогда, когда руководство по бренду требует иного оформления:
/* stronger selected state, both themes (remember the two-block rule above) */
:root .ca-form label:has(input:checked){
border-color: var(--acc);
background: color-mix(in srgb, var(--acc) 18%, transparent);
font-weight: 600;
}
html[data-theme="dark"] .ca-form label:has(input:checked){
background: color-mix(in srgb, var(--acc) 24%, transparent);
}Правило, которое сохраняет их все читаемыми: всё, что вы красите с помощью --acc, получает свой текст
от --acc-fg — никогда не используйте буквальный цвет. Классическая ошибка — переопределение --acc на светлый
жёлтый бренд в тёмном блоке и оставление белого текста: кнопка формы Ask становится жёлтой на белом тексте с
контрастом 1.8:1, и клиент сообщает «кнопка формы нечитаемая в тёмном режиме». Если вы переопределяете --acc в
custom_css, вы отвечаете за --acc-fg в том же блоке, для обеих тем — или лучше, не используйте CSS
для этого вообще: установите theme_color, и платформа выведет для вас соответствующий --acc-fg.
Сделайте чат устанавливаемым (PWA)
Страницы чата в автономном режиме (/s/… ссылки, QR-коды и псевдонимы ниже) — это устанавливаемые приложения:
посетители могут добавить их на домашний экран, со своей собственной иконкой и именем. Два регулятора, настроенные
для каждого агента — то, что устанавливается на домашний экран, это входная страница одного агента, поэтому каждый агент
это своё собственное приложение со своей собственной иконкой (агент поддержки и агент предварительных продаж устанавливаются как два разных приложения):
set_pwa_branding(
agent="support", # which agent's install branding
icon_source_url="https://cdn.example.com/brand/mark-1024.png", # ONE square master image ≥192×192
install_prompt="banner", # banner (default) | card | off
)- Мастер-изображение загружается один раз, и платформа генерирует весь набор — фавиконку вкладки браузера, иконки установки 192/512 и вариант маскирования для Android. Квадратные мастера не квадратные, поэтому используйте квадратный знак, а не широкий логотип-название.
install_promptуправляет тем, что видят посетители на странице чата:banner— это тонкая закрываемая полоска (Android/Chrome вызывает системный диалог установки; iOS получает «Поделиться → Добавить на домашний экран» инструкцию, так как Apple не предоставляет API),card— более видимая карточка первого посещения,offотключает приглашение. Отклонения запоминаются для каждого посетителя.- Иконки необязательны — без них используются иконки платформы. Вызовите только с именем агента, чтобы прочитать текущую конфигурацию этого агента.
Ссылка, которую вы передаёте человеку
Когда псевдоним арендатора и псевдоним агента установлены, create_share / list_shares возвращают
pretty_url — https://chat.agent4.io/t/<tenant-alias>/<agent-alias>. Это ссылка, которую нужно давать
людям и печатать на материалах: читаемая, запоминающаяся, и она переживает ротацию токена (платформа лениво поддерживает share за ней). Форма /s/<token> остаётся для встраивания и машин.
Нет pretty_url в ответе? Псевдонимы не установлены — исправьте это, а не отправляйте ссылку с токеном: псевдоним агента через create_agent(alias=…) или PUT /agents/{name}/alias; псевдоним арендатора находится
в консоли → Настройки. Установка псевдонима агента — это само действие публикации: страница псевдонима
автоматически создаёт анонимный share при первом посещении.
Собственный домен клиента
Хостинговая страница чата может обслуживаться на домене клиента — https://chat.client.com/ показывает их
брендинговую страницу, сертификат выпускается автоматически.
set_custom_domain(domain="chat.client.com", agent_alias="menu")
# → { status: "pending", cname_target: "endpoint.agent4.io", last_error: "…" }Последовательность имеет значение, и она асинхронна:
- Попросите клиента добавить CNAME с их поддомена на
cname_targetв ответе (endpoint.agent4.io). Только поддомены — домен верхнего уровня (client.com) не может использовать CNAME; пусть они используютchat.client.comили провайдера DNS с выравниванием CNAME. - На Cloudflare запись должна быть только DNS (серое облако). Проксируемые записи разрешаются в
IP-адреса Cloudflare, и проверка не проходит —
last_errorговорит об этом явно, с наблюдаемыми адресами. Это самая частая ошибка; прочитайтеlast_errorперед тем, как гадать. - Проверка автоматически перезапускается каждые 10 минут; вызовите
set_custom_domainснова (те же аргументы) или проверьтеtenant_info(), чтобы увидеть текущий статус.activeозначает живым — первое посещение выпускает сертификат.
Один домен на рабочее пространство. Пустой agent_alias ведёт на брендовую страницу арендатора; установите его, чтобы перейти
напрямую к чату одного агента. Существующие ссылки chat.agent4.io продолжают работать — это дополнительный
вход, а не замена — и идентичность чата зависит от домена: история посетителя на
chat.agent4.io не следует за ним на пользовательский домен.
Требуется план, включающий пользовательские домены (403 = арендатору нужно обновить план).
Модуль поиска разговоров (Spotlight)
Страница в автономном режиме и веб-клиент имеют поиск разговоров ⌘K. Он следует за брендом
автоматически — модальное окно читает те же переменные, что и всё остальное (--surface, --border, --text, --muted, --overlay, --surface-hover, --accent-soft, и пара --acc-text для
выделенных совпадений) — поэтому установка theme_color — это уже и здесь вся задача.
Для руководств по бренду, которым нужно больше, имена классов стабильны, и custom_css имеет приоритет
над стилями самого компонента:
| Часть | Селектор |
|---|---|
| Задний фон | .ca-ss-mask |
| Панель | .ca-ss |
| Поле ввода запроса | .ca-ss input |
| Строка результата / выбранная строка | .ca-ss-item / .ca-ss-item.sel |
| Заголовок результата / фрагмент | .ca-ss-t / .ca-ss-s |
| Выделенное совпадение во фрагменте | .ca-ss-s mark |
| Бейдж «related» (семантические совпадения) | .ca-ss-sem |
| Кнопка поиска в боковой панели | .sb-search |
/* rounder panel, brand-tinted selected row — remember the two-block rule above */
:root{ }
.ca-ss{ border-radius: 20px; }
.ca-ss-item.sel{ background: color-mix(in srgb, var(--acc) 10%, transparent); }
html[data-theme="dark"] .ca-ss-item.sel{ background: color-mix(in srgb, var(--acc) 16%, transparent); }Как и везде: правило двух тем применяется в тот момент, когда вы трогаете цвета поверхности, и простые
объяждения побеждают — без !important.
Элементы форм и нативный выбор даты
Входы формы Ask (.ca-form input.cain) и вход диалога (.ca-in) стилизованы токенами — поверхность,
текст, граница и акцентное кольцо фокуса следуют за темой и вашим theme_color. Системные
диалоги (переименование/подтверждение) разделяют обработку Spotlight: матовый задний фон, дышащее возвышение, свечение бренда в тёмном.
Календарь гибридный. На рабочем столе (мышь) платформа рисует свой календарь — полностью
стилизированный токенами, выбранный день цвета бренда, локализованные названия месяцев/дней недели — стабильные селекторы .ca-dp, .ca-dp-d, .ca-dp-d.sel, .ca-dp-d.today если руководство по бренду требует большего. На сенсорных устройствах нативный
выборщик ОС намеренно сохраняется: мобильное колесо лучше всего, чем что-либо нарисованное на веб-странице, и его панель — это приватный UI ОС, к которому CSS не имеет доступа — там платформа контролирует то, что достижимо (привязанная к теме color-scheme, стилизованное токенами поле ввода, инвертированная иконка календаря) и больше ничего не существует для управления.
Ограничивайте селекторы элементов. Чистый input[type="text"] { … }, написанный для композитора, также
захватывает диалоги платформы и поля форм — вытекающий белый стиль композитора в тёмный диалог переименования — классический симптом, и линтинг CSS теперь отмечает это (broad_element_selector). Напишите .composer textarea или #input вместо этого. Системные диалоги находятся на более высокой специфичности, поэтому случайность больше не попадает — но линтинг говорит вам об этом до того, как вы полагаетесь на случайность.
Также на share
input_mode—text(по умолчанию) илиvoice, который открывает сразу режим удержания для разговора. Требуется бэкенд распознавания речи, включённый на платформе.launcher— всплывающая подсказка пузыря при наведении.theme— принудительноlight/dark, или оставьте пустым, чтобы следовать системной настройке посетителя. Предпочитайте пустое значение: виджет находится внутри страницы клиента и должен соответствовать ей.
Проверка
Откройте chat_url из list_shares и посмотрите на кнопку отправки. Если метка плохо читается, theme_color не установлен, и что-то его переопределяет — проверьте custom_css на наличие жёстко закодированного
color.