This commit is contained in:
Camille Islasse 2026-06-25 07:47:09 +02:00 committed by GitHub
commit af6719c01e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 284 additions and 63 deletions

View file

@ -81,6 +81,12 @@
"webpackMode": "lazy",
"fetch": "lazy",
"enabled": true
},
"theme-switcher": {
"main": "controllers/ThemeSwitcherController.js",
"webpackMode": "lazy",
"fetch": "lazy",
"enabled": true
}
}
},

View file

@ -0,0 +1,23 @@
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import {Controller} from '@hotwired/stimulus';
export default class extends Controller {
toggle() {
const current = document.documentElement.getAttribute('data-bs-theme') || 'light';
const next = current === 'dark' ? 'light' : 'dark';
try {
localStorage.setItem('sylius-admin-theme', next);
} catch {
// storage unavailable (private mode, quota exceeded)
}
document.documentElement.setAttribute('data-bs-theme', next);
}
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -72,6 +72,12 @@
"webpackMode": "lazy",
"fetch": "lazy",
"enabled": true
},
"theme-switcher": {
"main": "controllers/ThemeSwitcherController.js",
"webpackMode": "lazy",
"fetch": "lazy",
"enabled": true
}
}
},

View file

@ -18,10 +18,20 @@ function renderChart() {
return;
}
const styles = getComputedStyle(document.documentElement);
const labelColor = styles.getPropertyValue('--tblr-body-color').trim();
const primaryColor = styles.getPropertyValue('--tblr-primary').trim();
const primaryDarken = styles.getPropertyValue('--sylius-primary-darken').trim();
const secondaryColor = styles.getPropertyValue('--tblr-info').trim();
const currentTheme = document.documentElement.getAttribute('data-bs-theme') || 'light';
const options = {
colors: ['#32be9f', '#066fd1'],
theme: {
mode: currentTheme
},
colors: [primaryColor, secondaryColor],
fill: {
colors: ['#32be9f']
colors: [primaryColor]
},
series: [{
name: 'Sales',
@ -31,6 +41,7 @@ function renderChart() {
data: JSON.parse(statisticsChart.dataset.paidOrdersCount)
}],
chart: {
background: 'transparent',
toolbar: {
show: false
},
@ -41,13 +52,18 @@ function renderChart() {
bar: {
borderRadius: 4,
dataLabels: {
position: 'top' // top, center, bottom
position: 'top'
}
}
},
xaxis: {
categories: JSON.parse(statisticsChart.dataset.intervals),
position: 'top',
labels: {
style: {
colors: labelColor
}
},
axisBorder: {
show: false
},
@ -58,8 +74,8 @@ function renderChart() {
fill: {
type: 'gradient',
gradient: {
colorFrom: '#32be9f',
colorTo: '#2a9f83',
colorFrom: primaryColor,
colorTo: primaryDarken,
stops: [0, 100],
opacityFrom: 0.4,
opacityTo: 0.5
@ -78,15 +94,20 @@ function renderChart() {
show: false
},
labels: {
style: {
colors: labelColor
},
formatter(val) {
const { currency } = statisticsChart.dataset;
return `${currency}${val}`;
}
}
}, {
opposite: true,
labels: {
style: {
colors: labelColor
},
formatter(val) {
return val;
}
@ -97,7 +118,7 @@ function renderChart() {
offsetY: 330,
align: 'center',
style: {
color: '#444'
color: labelColor
}
}
};
@ -114,7 +135,10 @@ if (element) {
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'data-sales' || mutation.attributeName === 'data-intervals') {
chart.destroy();
if (chart) {
chart.destroy();
chart = null;
}
renderChart();
}
});
@ -123,4 +147,25 @@ if (element) {
observer.observe(element, {
attributes: true
});
const themeObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'data-bs-theme') {
if (!document.querySelector('#statistics-chart')) {
themeObserver.disconnect();
return;
}
if (chart) {
chart.destroy();
chart = null;
}
renderChart();
}
});
});
themeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-bs-theme']
});
}

View file

@ -13,7 +13,7 @@
line-height: 28px;
&:hover {
background: $gray-100;
background: var(--tblr-bg-surface);
}
&:not(.collapsed) {

View file

@ -16,3 +16,9 @@
--tblr-avatar-size: 3rem;
}
}
@include color-mode(dark) {
.sylius.avatar {
background-color: var(--tblr-bg-surface);
}
}

View file

@ -8,24 +8,67 @@
*/
* {
--tblr-body-color: #212529;
--tblr-breadcrumb-item-active-color: var(--tblr-gray-500);
--tblr-breadcrumb-divider-color: var(--tblr-gray-300);
--tblr-breadcrumb-link-color: #212529;
--tblr-code-color: #36393B;
--tblr-blue-rgb: 17, 81, 141;
--tblr-green-rgb: 0, 97, 16;
--tblr-pagination-border-width: 0;
--sylius-primary-darken: #{$primary-darken};
}
[data-bs-theme=dark] {
--tblr-bg-surface: #1E2433;
@include color-mode(light) {
--tblr-body-color: #212529;
--tblr-code-color: #36393B;
--tblr-breadcrumb-link-color: #212529;
body {
--bs-body-bg: #{$body-bg};
--bs-tertiary-bg: #{$body-bg};
--bs-body-color: #{$body-color};
}
[data-theme-switch="light"] {
display: none;
}
}
body {
--bs-body-bg: #{$body-bg};
--bs-tertiary-bg: #{$body-bg};
--bs-body-color: #{$body-color};
@include color-mode(dark) {
--tblr-body-bg: #{$dark-body-bg};
--tblr-body-bg-rgb: #{red($dark-body-bg)}, #{green($dark-body-bg)}, #{blue($dark-body-bg)};
--tblr-bg-surface: #{$dark-bg-surface};
.btn {
--tblr-btn-border-color: var(--tblr-border-color);
}
.form-control,
.form-select,
.form-check-input,
.input-group-text {
border-color: var(--tblr-border-color);
}
.page-link.active,
.active > .page-link {
background-color: var(--tblr-bg-surface);
border-color: var(--tblr-border-color);
}
[data-theme-switch="dark"] {
display: none;
}
.btn-secondary {
--tblr-btn-bg: var(--tblr-bg-surface);
--tblr-btn-border-color: var(--tblr-border-color);
--tblr-btn-hover-bg: var(--tblr-border-color);
--tblr-btn-color: var(--tblr-body-color);
--tblr-btn-hover-color: var(--tblr-body-color);
}
.empty-img img {
filter: invert(0.9) hue-rotate(180deg);
}
}
a {
@ -64,13 +107,6 @@ a.link-reset {
}
}
html[data-bs-theme="light"] [data-theme-switch="light"] {
display: none;
}
html[data-bs-theme="dark"] [data-theme-switch="dark"] {
display: none;
}
.switch-collapse {
display: none;

View file

@ -28,7 +28,7 @@
justify-content: center;
align-items: center;
border-radius: 3px;
background: $gray-100;
background: var(--tblr-card-bg);
@include media-breakpoint-down(md) {
bottom: -19px;

View file

@ -36,7 +36,7 @@ textarea.form-control {
.list-group-item.active:has(.tab-error) {
border-left-style: solid;
border-left-width: 2px;
border-left-color: #ff0017;
border-left-color: var(--tblr-danger);
}
.form-select:disabled {

View file

@ -16,7 +16,7 @@
.infinite-tree-content {
.infinite-tree-title {
cursor: default;
color: $body-color;
color: var(--tblr-body-color);
@include text-truncate-flex-child;
}
@ -28,7 +28,7 @@
@include text-truncate-flex;
&:hover {
background: #F7F8FB;
background: var(--tblr-bg-surface);
border: none;
}
@ -92,7 +92,33 @@
}
.form-check-input[type=checkbox]:indeterminate {
background-color: #c7c7c7;
border-color: #afafaf;
background-color: var(--tblr-gray-400);
border-color: var(--tblr-gray-500);
}
}
@include color-mode(dark) {
.infinite-tree-content {
.form-check-input[type=checkbox]:indeterminate {
background-color: var(--tblr-gray-600);
border-color: var(--tblr-gray-500);
}
.infinite-tree-item:hover {
background: rgba(var(--tblr-emphasis-color-rgb), 0.08);
}
.infinite-tree-title {
color: var(--tblr-body-color);
}
.infinite-tree-toggler {
&.infinite-tree-open::before,
&.infinite-tree-closed::before,
&.infinite-tree-leaf::before {
filter: invert(1);
}
}
}
}

View file

@ -17,7 +17,7 @@
padding-bottom: var(--tblr-page-padding-y);
&.is-sticky {
background: #fff;
background: var(--tblr-body-bg);
border-bottom: var(--tblr-border-width) solid var(--tblr-border-color);
box-shadow: var(--tblr-shadow-card);
}

View file

@ -12,7 +12,7 @@
position: absolute;
height: 100%;
width: 100%;
background: rgba(255, 255, 255, 0.9);
background: rgba(var(--tblr-body-bg-rgb, 255, 255, 255), 0.9);
align-items: center;
justify-content: center;
z-index: 100;

View file

@ -19,6 +19,13 @@
.navbar-brand-image {
height: 3rem;
color: $logo-text-color;
}
@include color-mode(dark) {
.navbar-brand-image {
color: $white;
}
}
.navbar-collapse a.nav-link,
@ -35,7 +42,7 @@
min-width: 44px;
}
.navbar-collapse .nav-link:focus-visible {
outline: solid 2px rgb(153, 200, 255);
outline: solid 2px var(--tblr-primary);
}
.navbar-plus-badge{
width: 50px;

View file

@ -99,3 +99,9 @@
display: none;
}
}
@include color-mode(dark) {
.menu-search .form-control {
border-color: transparent;
}
}

View file

@ -12,11 +12,20 @@
.ts-control {
padding: 0.5625rem 2.3rem 0.5625rem 0.8rem;
line-height: 1.4285714286;
background: #fff;
color: var(--tblr-body-color);
border: var(--tblr-border-width) solid var(--tblr-border-color);
border-radius: var(--tblr-border-radius);
box-shadow: var(--tblr-box-shadow-input);
input {
color: var(--tblr-body-color);
}
}
@include color-mode(dark) {
.ts-control .item {
color: var(--tblr-body-color);
}
}
.focus .ts-control {
@ -26,10 +35,20 @@
.ts-dropdown, .ts-dropdown.form-control, .ts-dropdown.form-select {
padding: 5px;
background: #ffffff;
border: 1px solid #00000017;
background: var(--tblr-bg-surface);
border: 1px solid var(--tblr-border-color);
border-radius: 0.175rem;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.075);
box-shadow: 0 2px 5px rgba(var(--tblr-body-color-rgb), 0.075);
.option {
color: var(--tblr-body-color);
opacity: 1;
&.active {
background: var(--tblr-primary);
color: var(--tblr-primary-fg);
}
}
}
.ts-wrapper.single .ts-control, .ts-wrapper.single .ts-control input {

View file

@ -78,3 +78,13 @@ $h1-line-height: 2.1rem;
// Page
$page-title-font-size: var(--#{$prefix}font-size-h1);
// Dark mode
$dark-body-bg: #0F1623 !default;
$dark-bg-surface: #1E2433 !default;
// Chart
$primary-darken: mix(black, $primary, 15%) !default;
// Logo
$logo-text-color: #131718 !default;

View file

@ -19,6 +19,9 @@ sylius_twig_hooks:
props:
template: '@SyliusAdmin/shared/components/navbar/notifications.html.twig'
priority: 100
theme_switcher:
template: '@SyliusAdmin/shared/crud/common/navbar/items/theme_switcher.html.twig'
priority: 50
user_dropdown:
template: '@SyliusAdmin/shared/crud/common/navbar/items/user.html.twig'
priority: 0

View file

@ -3,8 +3,11 @@ sylius_twig_hooks:
'sylius_admin.base#metatags':
metatags:
template: '@SyliusAdmin/shared/layout/base/metatags.html.twig'
priority: 0
priority: 100
theme:
template: '@SyliusAdmin/shared/layout/base/theme.html.twig'
priority: 50
'sylius_admin.base#stylesheets':
styles:
template: '@SyliusAdmin/shared/layout/base/stylesheets.html.twig'

View file

@ -121,6 +121,7 @@ sylius:
to_approve: 'to approve'
to_process: 'to process'
to_ship: 'to ship'
toggle_theme: 'Toggle theme'
view_in_store: 'View in store'
work_in_progress: Work in progress
empty_date: empty

View file

@ -17,7 +17,7 @@
</button>
</h2>
<div id="translation-{{ locale }}" class="accordion-collapse collapse" data-bs-parent="#catalog-promotion-translations">
<div class="accordion-body bg-gray-400">
<div class="accordion-body bg-body-tertiary">
<div class="py-3 px-5">
<div class="card mb-3">
<div class="card-body">

View file

@ -6,5 +6,5 @@
{% if data.originalPrice %}
{{ money.format(data.originalPrice, currencies[channel_code]) }}
{% else %}
<span class="gray text">-</span>
<span class="text-muted">-</span>
{% endif %}

View file

@ -23,7 +23,7 @@
<div class="card-body">
<div class="row align-items-center">
<div class="col-auto">
<span class="bg-gray text-muted avatar">
<span class="bg-secondary-lt text-secondary avatar">
{{ ux_icon('tabler:shopping-bag') }}
</span>
</div>
@ -40,7 +40,7 @@
<div class="card-body">
<div class="row align-items-center">
<div class="col-auto">
<span class="bg-gray text-muted avatar">
<span class="bg-secondary-lt text-secondary avatar">
{{ ux_icon('tabler:coins') }}
</span>
</div>
@ -57,7 +57,7 @@
<div class="card-body">
<div class="row align-items-center">
<div class="col-auto">
<span class="bg-gray text-muted avatar">
<span class="bg-secondary-lt text-secondary avatar">
{{ ux_icon('tabler:slash') }}
</span>
</div>

View file

@ -8,7 +8,7 @@
<div class="card-body">
<div class="row align-items-center">
<div class="col-auto">
<span class="bg-gray text-black-50 avatar">
<span class="bg-secondary-lt text-secondary avatar">
{{ ux_icon('tabler:slash') }}
</span>
</div>

View file

@ -8,7 +8,7 @@
<div class="card-body">
<div class="row align-items-center">
<div class="col-auto">
<span class="bg-gray text-black-50 avatar">
<span class="bg-secondary-lt text-secondary avatar">
{{ ux_icon('tabler:users') }}
</span>
</div>

View file

@ -8,7 +8,7 @@
<div class="card-body">
<div class="row align-items-center">
<div class="col-auto">
<span class="bg-gray text-black-50 avatar">
<span class="bg-secondary-lt text-secondary avatar">
{{ ux_icon('tabler:shopping-bag') }}
</span>
</div>

View file

@ -9,7 +9,7 @@
<div class="card-body">
<div class="row align-items-center">
<div class="col-auto">
<span class="bg-gray text-black-50 avatar">
<span class="bg-secondary-lt text-secondary avatar">
{{ ux_icon('tabler:coins') }}
</span>
</div>

View file

@ -10,8 +10,8 @@
</div>
</div>
{% else %}
<span class="ui red label">
<i class="remove icon"></i>
<span class="badge bg-danger-lt text-danger">
{{ ux_icon('tabler:x', {'class': 'icon'}) }}
{{ 'sylius.ui.not_tracked'|trans }}
</span>
{% endif %}

View file

@ -1,6 +1,6 @@
<div class="col-12 col-lg-3">
<div id="side-nav" class="sticky-md-top" style="top: 7.6rem; z-index: 990;">
<div class="list-group bg-white mb-5" role="tablist">
<div class="list-group bg-body mb-5" role="tablist">
{% hook 'side_navigation' %}
</div>
</div>

View file

@ -2,7 +2,7 @@
{% set attributes = hookable_metadata.context.attributes %}
<div id="translation-non-translatable" class="accordion-collapse collapse" data-bs-parent="#attribute-translations">
<div class="accordion-body bg-gray-400">
<div class="accordion-body bg-body-tertiary">
<div class="py-3 px-5">
<div class="card mb-3">
<div class="card-header">

View file

@ -4,7 +4,7 @@
{% set loop = hookable_metadata.context.loop %}
<div id="translation-{{ locale_code }}-attributes" class="accordion-collapse collapse {% if loop.first %}show{% endif %}" data-bs-parent="#attribute-translations">
<div class="accordion-body bg-gray-100">
<div class="accordion-body bg-body-tertiary">
<div class="card mb-3 mx-md-5">
<div class="card-header">
<div class="card-title">{{ 'sylius.ui.content'|trans }}</div>

View file

@ -15,7 +15,7 @@
<tr {{ sylius_test_html_attribute('variant', "%s"|format(variant.code)) }}>
{% hook 'configurable_product#tab' with { variant, product } %}
</tr>
<tr class="bg-gray-100">
<tr class="bg-body-tertiary">
<td colspan="9" class="p-0 border-0">
<div class="px-3 px-md-5 py-3 collapse" id="product-variant-details-{{ variant.code }}">
{% hook 'configurable_product#details' with { variant } %}

View file

@ -15,7 +15,7 @@
<tr>
{% hook 'simple_product#tab' with { variant, product } %}
</tr>
<tr class="bg-gray-100">
<tr class="bg-body-tertiary">
<td colspan="9" class="p-0 border-0">
<div class="py-3 px-5">
{% hook 'simple_product#details' with { variant } %}

View file

@ -1,6 +1,6 @@
{% set product = hookable_metadata.context.resource %}
<div class="bg-white mb-3">
<div class="bg-body mb-3">
<div class="accordion" id="media" >
<div class="accordion-item">
<h2 class="accordion-header">

View file

@ -21,7 +21,7 @@
</button>
</h2>
<div id="translation-{{ locale }}" class="accordion-collapse collapse" data-bs-parent="#product-translations">
<div class="accordion-body bg-gray-100">
<div class="accordion-body bg-body-tertiary">
<div class="card mb-3 mx-md-5">
<div class="card-header">
<div class="card-title">{{ 'sylius.ui.content'|trans }}</div>

View file

@ -1,5 +1,5 @@
<div class="card-body">
<div class="alert alert-important alert-info d-flex flex-column">
<div class="alert alert-info d-flex flex-column">
{% hook 'info' %}
</div>
</div>

View file

@ -1,6 +1,6 @@
<div class="col-12 col-lg-3">
<div id="side-nav" class="sticky-md-top" style="top: 7.6rem;">
<div class="list-group bg-white mb-5" role="tablist">
<div class="list-group bg-body mb-5" role="tablist">
{% hook 'side_navigation' %}
</div>
</div>

View file

@ -1 +1,6 @@
<img src="{{ asset('build/admin/images/sylius-logo-dark-text.png', 'admin') }}" alt="Sylius" class="sylius navbar-brand-image">
<svg aria-hidden="true" width="0" height="0" style="position:absolute">
<symbol fill="none" viewBox="0 0 167 49" id="sylius-admin-logo" xmlns="http://www.w3.org/2000/svg"><path d="M60.944 33.595s2.898 3.422 7.126 3.422c2.659 0 4.872-1.61 4.872-4.228 0-5.958-13.73-5.032-13.73-13.89 0-4.27 3.752-7.611 9.1-7.611 3.02 0 8.096 1.37 8.096 5.235v2.334h-3.707V17.53c0-1.491-2.134-2.577-4.39-2.577-3.059 0-4.992 1.69-4.992 3.823 0 5.758 13.691 4.47 13.691 13.812 0 4.344-3.382 8.096-9.019 8.096a12.712 12.712 0 01-9.423-4.226l2.376-2.863zM83.45 45.272c1.58 0 2.818-1.128 3.584-2.939l.927-2.215-6.967-16.146c-.28-.646-.644-.806-1.288-.806h-.484v-3.341h2.295c1.65 0 2.255.442 2.899 2.053l4.63 11.474c.29.804.532 1.624.725 2.457h.08c.171-.834.4-1.655.685-2.457l4.228-11.473c.604-1.612 1.329-2.054 2.98-2.054h2.336v3.341h-.525c-.642 0-1.006.16-1.288.806l-8.053 19.89c-1.248 3.14-3.705 4.792-6.562 4.792a6.75 6.75 0 01-4.791-2.014l1.61-2.818c0 .04 1.247 1.45 2.98 1.45zm20.532-29.267c0-.604-.322-.886-.885-.886h-1.65V11.77h3.864c1.811 0 2.567.765 2.567 2.578V35.97c0 .604.322.886.887.886h1.649V40.2h-3.855c-1.811 0-2.567-.767-2.567-2.578l-.01-21.616zm12.19 8.048c0-.605-.322-.887-.886-.887h-1.651v-3.341h3.827c1.811 0 2.567.764 2.567 2.567V35.97c0 .604.322.886.887.886h1.651V40.2h-3.818c-1.812 0-2.567-.767-2.567-2.578l-.01-13.568zm.081-12.282h3.502v4.067h-3.502v-4.066zm11.268 12.282c0-.605-.322-.887-.886-.887h-1.651v-3.341h3.825c1.852 0 2.616.764 2.616 2.496v9.662c0 2.82.565 5.074 3.786 5.074 4.189 0 6.643-3.664 6.643-7.69v-9.542h3.906V35.97c0 .604.322.886.887.886h1.649V40.2h-3.752c-1.73 0-2.577-.806-2.577-2.256v-.764c0-.646.041-1.167.041-1.167h-.081c-.837 2.012-3.454 4.678-7.48 4.678-4.592 0-6.926-2.415-6.926-7.81v-8.827zm25.319 10.429s2.416 3.019 6.162 3.019c1.892 0 3.543-.847 3.543-2.619 0-3.663-11.113-3.26-11.113-9.822 0-3.906 3.421-5.727 7.57-5.727 2.456 0 6.682.845 6.682 3.95v1.93h-3.503v-1.006c0-1.209-1.811-1.732-3.059-1.732-2.294 0-3.785.808-3.785 2.417 0 3.865 11.114 3.02 11.114 9.825 0 3.623-3.221 5.958-7.491 5.958-5.434 0-8.132-3.583-8.132-3.583l2.012-2.61z" fill="currentColor"/><path d="M6.797 28.69l9.793 19.585 18.075.03 4.374-6.157L6.797 28.689z" fill="#30BA9D"/><path d="M0 30.84l13.166 18.073 21.5.036 4.373-6.762L0 30.84zM15.786 8.823l6.409 4.97 5.794-11.117-1.97-1.26-10.233 7.407z" fill="#1A9F83"/><path d="M34.665 48.949L15.786 8.823 19.825 5.9l21.967 32.027-7.127 11.022zM24.203 2.731L36.5 12.664 27.87 0l-3.667 2.731z" fill="#30BA9D"/></symbol>
</svg>
<svg viewBox="0 0 167 49" class="sylius navbar-brand-image" role="img" aria-label="Sylius" focusable="false">
<use href="#sylius-admin-logo"/>
</svg>

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,6 @@
<div class="nav-item" {{ stimulus_controller('@sylius/admin-bundle/theme-switcher') }}>
<button type="button" class="btn btn-icon btn-ghost-secondary" {{ stimulus_action('@sylius/admin-bundle/theme-switcher', 'toggle') }} aria-label="{{ 'sylius.ui.toggle_theme'|trans }}">
{{ ux_icon('tabler:sun', {'class': 'icon', 'data-theme-switch': 'light'}) }}
{{ ux_icon('tabler:moon', {'class': 'icon', 'data-theme-switch': 'dark'}) }}
</button>
</div>

View file

@ -7,7 +7,7 @@
{% set are_criteria_set = app.request.query.has('criteria') %}
{% if resources.definition.enabledFilters|default([]) is not empty %}
<div class="position-relative z-1 bg-white mb-5">
<div class="position-relative z-1 bg-body mb-5">
{% set content %}
<div class="mb-3">
<form method="get" action="{{ path }}" novalidate {{ sylius_test_html_attribute('filters-form') }}>

View file

@ -0,0 +1,13 @@
<script>
(function() {
let stored = null;
try {
stored = localStorage.getItem('sylius-admin-theme');
} catch {
// storage unavailable (private mode, quota exceeded)
}
const storedTheme = ['light', 'dark'].includes(stored) ? stored : null;
const theme = storedTheme || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-bs-theme', theme);
})();
</script>