Документація

Стилі, скрипти та зображення теми

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

На цій сторінці

Файли оформлення лежать у теці assets/ усередині теми і віддаються браузеру за адресою /theme-assets/ім'я-теми/…. Ця адреса завжди доступна в шаблоні:

<img src="{$theme.assets}/img/logo.svg" alt="{$storefront.name}">

Файли за межами assets/ браузеру не віддаються. Шаблони, маніфест і переклади через цю адресу не прочитати — це захист, а не недоробка.

Три файли, які магазин просить завжди

На кожній сторінці магазин підключає три файли вашої теми:

assets/vendor/fontawesome/all.min.css
assets/css/theme.css
assets/js/theme.js

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

Підключення своїх файлів

{block name="page_assets"}
    {asset_css file="css/product.css"}
    {asset_js file="js/product.js" defer=true}
{/block}
Параметр Що робить
file шлях усередині assets/ вашої теми
inline вставити вміст прямо в сторінку, а не посиланням
key ім'я, за яким файл не підключиться двічі
head / footer куди виводити
решта їдуть атрибутами в <link> або <script>

Усе, що не назване, стає атрибутом: defer=true, async=true, media="print", type="module".

Той самий файл, підключений двічі, виводиться один раз. Якщо той самий файл лежить під різними іменами — задайте їм спільний key, і підключиться тільки перший.

Виведення зібраного списку

{assets_head}      у <head>
{assets_footer}    перед </body>

У ці два місця виводяться і ваші файли, і ті, що магазин підключає сам, і ті, що додають доповнення. Без них вітрина залишиться без оформлення і без поведінки.

Підключати файл можна в будь-якому місці шаблону — у шапці, у підвалі, усередині {include} картки товару. Магазин друкує обидва списки останніми, коли сторінку зібрано повністю, тому стиль, оголошений у підвалі, однаково опиниться в <head>. Так само підключають свої файли доповнення: чат у підвалі, позначку акції в картці, маску телефону в шапці.

До ядра 2.86.0 було інакше: файл, оголошений після {assets_head}, у шапку не потрапляв, і блок page_assets мусив стояти раніше. Старі каркаси з таким порядком працюють як і раніше — переставляти нічого не потрібно.

Склеювання: один файл замість двадцяти

Магазин склеює стилі й скрипти сторінки в стиснені файли і віддає їх із /storage/bundles/. Покупець із телефона платить за кожен запит до сервера, а сторінка теми з доповненнями просить півтора-два десятки файлів. На головній штатної теми CartiX замість 23 файлів тепер вантажаться 4.

Склейок два набори — так само, як Бітрікс відокремлює файли ядра й шаблону від файлів сторінки:

Набір Що в ньому Як кешується
спільний стилі й скрипт теми, значки, кошик, живий пошук однаковий на всіх сторінках — браузер завантажує його один раз
сторінки файли цієї сторінки й доповнень на ній свій у кожного виду сторінки, але маленький

Без наборів кожна сторінка отримувала б свою склейку з усією темою всередині, і покупець завантажував би стиль теми заново на головній, у каталозі та в картці.

Скрипти доповнень склеюються окремо від скриптів теми й магазину. В одному файлі помилка в чужому скрипті зупинила б усе, що йде за ним, і криве доповнення вимкнуло б оформлення замовлення. Стилі так не ламаються, тому їх клеять разом.

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

Що потрапляє в склейку, а що ні

Склеюються сусідні файли вашого магазину, підключені через {asset_css} і {asset_js}:

  • стиль без атрибутів (або з media="all");
  • скрипт рівно з defer=true.

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

Файл Чому окремо
чужа адреса (https://…) його вмісту в магазину немає
стиль з @import @import мусить стояти на початку файлу, усередині склейки браузер його викине
стиль з media="print" тощо склейка застосувалася б до всіх екранів
скрипт без defer, з async, type="module" він виконується в інший момент, і порядок змінився б
скрипт зі своїми атрибутами (data-…) він читає їх зі свого тегу, а в склейці тегу немає

Стиль між двома скриптами (і скрипт між стилями) ланцюжок не розриває: на порядок стилів скрипти не впливають, а defer-скрипти виконуються після розбору сторінки, хоч би коли їх оголосили.

Як зробити свій файл придатним для склеювання

Налаштування скрипту передавайте об'єктом, а не атрибутами тегу. Такий файл склеїти не можна:

{asset_js file="js/slider.js" defer=true data-speed="500"}

А такий можна — налаштування вбудованим скриптом, файл окремо:

{asset_js inline="window.MySlider = { speed: 500 };" key="my-slider" head=true}
{asset_js file="js/slider.js" defer=true}
// js/slider.js
var speed = (window.MySlider || {}).speed || 300;

@import шрифтів винесіть в окремий маленький файл — решта оформлення склеїться.

Скрипт, якому потрібен інший скрипт, підключайте після нього, обидва з defer. Усередині склейки порядок той самий, що в шаблоні.

Якщо тема оновлює частину сторінки сама

Часто тема оновлює шматок сторінки, перечитавши її повністю: кошик після «+», список після фільтра чи «Показати ще». Стилі блоків, яких на сторінці до цього не було (з'явився подарунок, прийшов товар в акції), лежать у <head> перечитаної сторінки, а тема бере з неї лише шматок. Перенесіть їх помічником магазину, і новий блок прийде оформленим:

fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } })
    .then(function (r) { return r.text(); })
    .then(function (html) {
        var doc = new DOMParser().parseFromString(html, 'text/html');
        if (window.CartiX && CartiX.adoptStyles) CartiX.adoptStyles(doc);
        document.querySelector('[data-cart-page]').innerHTML =
            doc.querySelector('[data-cart-page]').innerHTML;
    });

Помічник додає лише стилі, яких на сторінці ще немає, і ставить їх одразу за останнім стилем сторінки. Скрипти він не переносить: у підміненому шматку вони не виконуються, а повторний запуск скриптів сторінки задвоїв би обробники. Так влаштовані кошик і списки штатної теми CartiX та каталог магазину.

Як перевірити

Відкрийте код сторінки (Ctrl+U): у <head> будуть посилання на кшталт /storage/bundles/471ac0cc5ff814eb3afe.css. Якщо файл лишився окремим посиланням, значить, він не підійшов під правила вище, і з таблиці видно чому.

Склеюванню потрібне посилання public/storage (php artisan storage:link; під час встановлення воно створюється само). Якщо посилання немає, магазин не ламає сторінку, а віддає файли окремо, як раніше.

Як вимкнути склеювання

Склеювання вимикають у панелі: Налаштування магазину → Основні налаштування → Швидкість вітрини, перемикач «Склеювати стилі й скрипти». Це знадобиться, коли ви шукаєте помилку у своєму скрипті й хочете бачити його окремим файлом, або коли власна тема чи чуже доповнення не уживаються зі склеюванням.

⚠️ Будьте обережні з цим перемикачем. Він змінює те, як завантажується кожна сторінка вітрини, і після перемикання магазин може працювати неадекватно. Без склеювання вітрина відкривається повільніше, а тема чи доповнення, розраховані на склеювання, можуть залишити блоки без оформлення або кнопки без відповіді. Після перемикання відкрийте вітрину й перевірте головну, картку товару, кошик і оформлення замовлення, а коли закінчите перевірку, увімкніть склеювання знову.

Якщо панель недоступна, склеювання вимикає рядок CARTIX_BUNDLE_ASSETS=false у файлі .env магазину. Він сильніший за перемикач: поки рядок стоїть, панель показує, що склеювання вимкнено на сервері, і перемикач не діє.

Що магазин підключає сам

Тема цього не робить і не повинна дублювати.

Де Що
усюди cart-sync.js — лічильник кошика і позначки «у кошику»
усюди search.css, search.js — живий пошук
сторінки оформлення checkout.css, checkout.js
сторінка оплати pay.js
сторінки товару accessories.css, accessories.js — комплекти
сторінки товару packaging.css, packaging.js — підрахунок упаковками

Своїм checkout.css теми перевизначати вигляд можна і потрібно — він підключається після магазинного. А от свій скрипт оформлення писати не варто: див. Атрибути data-*.

Сторонні бібліотеки — файлами, а не посиланням

Слайдер, повзунок ціни, набір значків кладуться в assets/vendor/ і підключаються звідти:

assets/vendor/
    swiper/
    nouislider/
    fontawesome/

Чому не посиланням на чужий сайт: зовнішня адреса — це чужа доступність, чужа швидкість і чужі правила про дані покупців. Впало зовнішнє джерело — у магазині розсипалася верстка, і власник не може ні полагодити, ні пояснити це покупцеві. Плюс свій файл віддається з правильним типом і з довгим кешем.

Єдиний виняток — reCAPTCHA: її скрипт приходить із сайту Google, і це вибір власника магазину, а не теми.

Кеш браузера: дві пастки

До кожного файлу, підключеного через {asset_css} і {asset_js}, магазин дописує позначку версії: час зміни та розмір файлу. Змінили файл — змінилася позначка, і браузер завантажує новий. Не змінювали — браузер бере файл зі свого кешу й не завантажує його заново на кожній сторінці. Самі файли віддаються з довгим кешем: місяць і «не перевіряти».

Звідси дві пастки, на які наступають усі.

Перша: сторінка прийшла з кешу — у ній заморожена стара позначка. Правка в редакторі «Дизайну» скидає сторінки вітрини сама. Якщо ви змінювали файл через FTP або з консолі, скиньте сторінки вітрини вручну, інакше покупець побачить новий стиль, лише коли сторінка оновиться в кеші.

⚠️ Друга, і вона гірша: файл, підключений вручну, позначки не отримує зовсім.

{* так робити не можна — файл залипне у покупця на місяць *}
<link rel="stylesheet" href="{$theme.assets}/css/my.css">

{* так правильно *}
{asset_css file="css/my.css"}

Через {$theme.assets} підключають картинки і шрифти, а стилі і скрипти — тільки тегами. Файл, написаний вручну, до того ж не потрапляє в склейку й вантажиться окремим запитом.

Картинки

<img src="{$theme.assets}/img/no-photo.svg" alt="">

Картинки теми — це заглушки, значки, фон: те, що належить оформленню. Фотографії товарів приходять у даних і ріжуться тегом {thumb}:

<img src="{thumb src=$product.image size=320}" alt="{$product.name}">

Підвантаження картинок під час прокручування власник вмикає налаштуванням теми — воно приходить у лістингу як image_lazy:

<img src="{thumb src=$product.image size=320}"
     {if $image_lazy}loading="lazy"{/if} alt="{$product.name}">

Шрифти

Кладуться в assets/ і підключаються зі свого ж CSS:

@font-face {
    font-family: 'Inter';
    src: url('../fonts/inter.woff2') format('woff2');
    font-display: swap;
}

Шляхи всередині CSS рахуються від самого файлу стилів — {$theme.assets} там не працює.

Шрифт можна зробити налаштуванням теми ("type": "font"), тоді власник вибирає його в панелі, а тема читає вибір:

<style>:root { --font: {$theme.font_family}, system-ui, sans-serif; }</style>

Кольори налаштуваннями

Зручний прийом: кольори з налаштувань теми виводяться змінними CSS у каркасі, а все інше оформлення спирається на них.

<style>
:root {
    --primary: {$theme.colors.primary|default:'#4f46e5'};
    --accent:  {$theme.colors.accent|default:'#f59e0b'};
    --bg:      {$theme.colors.bg|default:'#ffffff'};
    --text:    {$theme.colors.text|default:'#1f2937'};
    --container: {$theme.container_width|default:1240}px;
}
</style>

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

Світлий фон чи темний

Коли власник задає свій колір фону, темі потрібно зрозуміти, що на ньому читається: світлий логотип чи темний, білі написи чи чорні. Для цього є обробник color_tone: він віддає light для світлого кольору, dark для темного й порожній рядок, якщо колір не розібрано.

<html data-header-tone="{$theme.color_header|color_tone}">
html[data-header-tone="light"] .logo__img--on-dark  { display: none; }
html[data-header-tone="light"] .logo__img--on-light { display: block; }

Обробник розуміє ті самі записи кольору, що й поле color: #hex, rgb() і hsl(). Межа світлого й темного збігається з прийомом для написів oklch(from … clamp(0, (0.68 - l) * 999, 1) 0 0), тому логотип і текст на одному фоні перемикаються одночасно.

Порожня тека не доїде до клієнта

⚠️ У пакет теми складаються файли. Тека без жодного файлу в архів не потрапить: завели assets/css/ порожньою — у клієнта її не буде.

Що ще не потрапляє в пакет і чому — на сторінці Пакування і публікація.

Оновлено 1 жовтня 2026