Skip to content

Commit 51f75a3

Browse files
authored
feat: implement dark mode support (#353)
### Summary Add dark mode support to the BootstrapAdminUi bundle using Bootstrap 5.3 color modes. ### Features - Theme toggle button (sun/moon) in the navbar - Persistent preference via localStorage with prefers-color-scheme fallback - Anti-flash inline script applied before page paint - Login page logo adapts to active theme - SCSS refactored to use CSS custom properties (--tblr-*) throughout - ApexCharts re-renders on theme change via MutationObserver ### Changes - Add theme-switcher.js script - Add theme.html.twig anti-flash hookable in #stylesheets (priority 200) - Add theme_switcher.html.twig navbar button hookable - Refactor _body.scss, _sidebar.scss, _tom-select.scss with include color-mode() mixin - Replace hardcoded colors in _accordion.scss, _filters.scss, _form.scss, _loader.scss, _navbar.scss - Update statistics_chart.js to use CSS vars and re-render on theme change - Update login logo template to support both light/dark variants ![Enregistrementdelecran2026-03-14a11 36 42-ezgif com-video-to-gif-converter](https://github.com/user-attachments/assets/66569358-3886-428e-9f5e-14ac2df4ab3b)
2 parents 3948df3 + 02fa52d commit 51f75a3

27 files changed

Lines changed: 169 additions & 56 deletions

File tree

assets/scripts/statistics_chart.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ function renderChart() {
99
return;
1010
}
1111

12+
const styles = getComputedStyle(document.documentElement);
13+
const labelColor = styles.getPropertyValue('--tblr-body-color').trim();
14+
const primaryColor = styles.getPropertyValue('--tblr-primary').trim();
15+
1216
const options = {
13-
colors: ['#32be9f'],
17+
colors: [primaryColor],
1418
fill: {
15-
colors: ['#32be9f']
19+
colors: [primaryColor]
1620
},
1721
series: [{
1822
name: 'talks',
@@ -38,12 +42,17 @@ function renderChart() {
3842
offsetY: -20,
3943
style: {
4044
fontSize: '12px',
41-
colors: ['#304758']
45+
colors: [labelColor]
4246
}
4347
},
4448
xaxis: {
4549
categories: JSON.parse(statisticsChart.dataset.intervals),
4650
position: 'top',
51+
labels: {
52+
style: {
53+
colors: labelColor
54+
}
55+
},
4756
axisBorder: {
4857
show: false
4958
},
@@ -54,8 +63,8 @@ function renderChart() {
5463
fill: {
5564
type: 'gradient',
5665
gradient: {
57-
colorFrom: '#32be9f',
58-
colorTo: '#2a9f83',
66+
colorFrom: primaryColor,
67+
colorTo: primaryColor,
5968
stops: [0, 100],
6069
opacityFrom: 0.4,
6170
opacityTo: 0.5
@@ -83,7 +92,7 @@ function renderChart() {
8392
offsetY: 330,
8493
align: 'center',
8594
style: {
86-
color: '#444'
95+
color: labelColor
8796
}
8897
}
8998
};
@@ -109,4 +118,17 @@ if (element) {
109118
observer.observe(element, {
110119
attributes: true
111120
});
121+
122+
const themeObserver = new MutationObserver(function(mutations) {
123+
mutations.forEach(function(mutation) {
124+
if (mutation.attributeName === 'data-bs-theme') {
125+
chart.destroy();
126+
renderChart();
127+
}
128+
});
129+
});
130+
131+
themeObserver.observe(document.documentElement, {
132+
attributes: true
133+
});
112134
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
3+
export default class extends Controller {
4+
toggle() {
5+
const current = document.documentElement.getAttribute('data-bs-theme') || 'light';
6+
const next = current === 'dark' ? 'light' : 'dark';
7+
localStorage.setItem('sylius-theme', next);
8+
document.documentElement.setAttribute('data-bs-theme', next);
9+
}
10+
}

src/BootstrapAdminUi/assets/styles/_accordion.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
.accordion-item {
1212
.accordion-header {
1313
&:hover {
14-
background: $gray-100;
14+
background: var(--tblr-bg-surface);
1515
}
1616
}
1717
.accordion-body {

src/BootstrapAdminUi/assets/styles/_body.scss

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,52 @@
88
*/
99

1010
* {
11-
--tblr-body-color: #212529;
1211
--tblr-breadcrumb-item-active-color: var(--tblr-gray-500);
1312
--tblr-breadcrumb-divider-color: var(--tblr-gray-300);
14-
--tblr-code-color: #36393B;
15-
--tblr-breadcrumb-link-color: #212529;
1613
--tblr-blue-rgb: 17, 81, 141;
1714
--tblr-green-rgb: 0, 97, 16;
1815
--tblr-pagination-border-width: 0;
1916
}
2017

21-
[data-bs-theme=dark] {
18+
@include color-mode(light) {
19+
--tblr-body-color: #212529;
20+
--tblr-code-color: #36393B;
21+
--tblr-breadcrumb-link-color: #212529;
22+
23+
body {
24+
--bs-body-bg: #{$body-bg};
25+
--bs-tertiary-bg: #{$body-bg};
26+
--bs-body-color: #{$body-color};
27+
}
28+
29+
[data-theme-switch="light"] {
30+
display: none;
31+
}
32+
}
33+
34+
@include color-mode(dark) {
35+
--tblr-body-bg: #0F1623;
2236
--tblr-bg-surface: #1E2433;
37+
38+
.btn {
39+
--tblr-btn-border-color: var(--tblr-border-color);
40+
}
41+
42+
.form-control,
43+
.form-select,
44+
.form-check-input {
45+
border-color: var(--tblr-border-color);
46+
}
47+
48+
.page-link.active,
49+
.active > .page-link {
50+
background-color: var(--tblr-bg-surface);
51+
border-color: var(--tblr-border-color);
52+
}
53+
54+
[data-theme-switch="dark"] {
55+
display: none;
56+
}
2357
}
2458

2559
@font-face {
@@ -29,9 +63,6 @@
2963
}
3064

3165
body {
32-
--bs-body-bg: #{$body-bg};
33-
--bs-tertiary-bg: #{$body-bg};
34-
--bs-body-color: #{$body-color};
3566
font-feature-settings: "cv03", "cv04", "cv11";
3667
}
3768

@@ -71,14 +102,6 @@ a.link-reset {
71102
}
72103
}
73104

74-
html[data-bs-theme="light"] [data-theme-switch="light"] {
75-
display: none;
76-
}
77-
78-
html[data-bs-theme="dark"] [data-theme-switch="dark"] {
79-
display: none;
80-
}
81-
82105
.switch-collapse {
83106
display: none;
84107
}

src/BootstrapAdminUi/assets/styles/_filters.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
justify-content: center;
2929
align-items: center;
3030
border-radius: 3px;
31-
background: $gray-100;
31+
background: var(--tblr-card-bg);
3232

3333
@include media-breakpoint-down(md) {
3434
bottom: -19px;

src/BootstrapAdminUi/assets/styles/_form.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ textarea.form-control {
3636
.list-group-item.active:has(.tab-error) {
3737
border-left-style: solid;
3838
border-left-width: 2px;
39-
border-left-color: #ff0017;
39+
border-left-color: var(--tblr-danger);
4040
}
4141

4242
.form-select:disabled {

src/BootstrapAdminUi/assets/styles/_loader.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
position: absolute;
1313
height: 100%;
1414
width: 100%;
15-
background: rgba(255, 255, 255, 0.9);
15+
background: rgba(var(--tblr-body-bg-rgb, 255, 255, 255), 0.9);
1616
align-items: center;
1717
justify-content: center;
1818
z-index: 100;

src/BootstrapAdminUi/assets/styles/_navbar.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
min-width: 44px;
3737
}
3838
.navbar-collapse .nav-link:focus-visible {
39-
outline: solid 2px rgb(153, 200, 255);
39+
outline: solid 2px var(--tblr-primary);
4040
}
4141
.navbar-plus-badge{
4242
width: 50px;

src/BootstrapAdminUi/assets/styles/_sidebar.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,9 @@
9696
display: none;
9797
}
9898
}
99+
100+
@include color-mode(dark) {
101+
.menu-search .form-control {
102+
border-color: transparent;
103+
}
104+
}

src/BootstrapAdminUi/assets/styles/_tom-select.scss

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,20 @@
1212
.ts-control {
1313
padding: 0.5625rem 2.3rem 0.5625rem 0.8rem;
1414
line-height: 1.4285714286;
15-
background: #fff;
1615
color: var(--tblr-body-color);
1716
border: var(--tblr-border-width) solid var(--tblr-border-color);
1817
border-radius: var(--tblr-border-radius);
1918
box-shadow: var(--tblr-box-shadow-input);
19+
20+
input {
21+
color: var(--tblr-body-color) !important;
22+
}
23+
}
24+
25+
@include color-mode(dark) {
26+
.ts-control .item {
27+
color: var(--tblr-body-color);
28+
}
2029
}
2130

2231
.focus .ts-control {
@@ -26,10 +35,20 @@
2635

2736
.ts-dropdown, .ts-dropdown.form-control, .ts-dropdown.form-select {
2837
padding: 5px;
29-
background: #ffffff;
30-
border: 1px solid #00000017;
38+
background: var(--tblr-bg-surface) !important;
39+
border: 1px solid var(--tblr-border-color);
3140
border-radius: 0.175rem;
32-
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.075);
41+
box-shadow: 0 2px 5px rgba(var(--tblr-body-color-rgb), 0.075);
42+
43+
.option {
44+
color: var(--tblr-body-color);
45+
opacity: 1;
46+
47+
&.active {
48+
background: var(--tblr-primary);
49+
color: var(--tblr-primary-fg);
50+
}
51+
}
3352
}
3453

3554
.ts-wrapper.single .ts-control, .ts-wrapper.single .ts-control input {

0 commit comments

Comments
 (0)