Skip to content

Commit 4b4f416

Browse files
committed
feat: add toggle to switch light/dark/auto theme for toolbar
1 parent 11a9921 commit 4b4f416

6 files changed

Lines changed: 163 additions & 37 deletions

File tree

src/view/frontend/web/css/toolbar/_footer.css

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313
padding-top: 6px;
1414
}
1515

16-
/* ── Theme toggle ────────────────────────────────────────────────────────── */
16+
/* ── Footer toolbar row (credit + theme + export) ─────────────────────────── */
1717

18-
.mageforge-footer-theme-row {
18+
.mageforge-footer-toolbar-row {
1919
display: flex;
2020
align-items: center;
2121
justify-content: space-between;
2222
padding: 0 6px 6px 16px;
2323
gap: 8px;
2424
}
2525

26-
.mageforge-footer-theme-label {
26+
.mageforge-footer-toolbar-label {
2727
font-family: var(--mageforge-font-family);
2828
font-size: 10px;
2929
font-weight: 500;
@@ -33,7 +33,7 @@
3333
padding: 3px 6px 3px 8px;
3434
}
3535

36-
.mageforge-theme-toggle {
36+
.mageforge-segmented-toggle {
3737
display: flex;
3838
align-items: center;
3939
gap: 2px;
@@ -42,7 +42,7 @@
4242
padding: 2px;
4343
}
4444

45-
.mageforge-theme-btn {
45+
.mageforge-segmented-btn {
4646
padding: 3px 8px;
4747
border-radius: 4px;
4848
border: none;
@@ -56,23 +56,23 @@
5656
line-height: 1;
5757
}
5858

59-
.mageforge-theme-btn:hover {
59+
.mageforge-segmented-btn:hover {
6060
color: var(--mageforge-color-white);
6161
background: rgba(var(--mageforge-color-white-rgb), 0.08);
6262
}
6363

64-
.mageforge-theme-btn--active,
65-
.mageforge-theme-btn[aria-pressed="true"] {
64+
.mageforge-segmented-btn--active,
65+
.mageforge-segmented-btn[aria-pressed="true"] {
6666
background: var(--mageforge-color-blue);
6767
color: var(--mageforge-color-white);
6868
}
6969

70-
.mageforge-theme-btn:focus-visible {
70+
.mageforge-segmented-btn:focus-visible {
7171
outline: 2px solid var(--mageforge-color-blue);
7272
outline-offset: 1px;
7373
}
7474

75-
.mageforge-theme-btn:disabled {
75+
.mageforge-segmented-btn:disabled {
7676
opacity: 0.35;
7777
cursor: not-allowed;
7878
pointer-events: none;

src/view/frontend/web/css/toolbar/_menu.css

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,57 @@
122122
outline-offset: 2px;
123123
}
124124

125+
/* ============================================================================
126+
Menu Header Actions (theme toggle + close)
127+
========================================================================== */
128+
129+
.mageforge-toolbar-menu-actions {
130+
display: flex;
131+
align-items: center;
132+
gap: 6px;
133+
}
134+
135+
.mageforge-theme-icon-group {
136+
display: flex;
137+
align-items: center;
138+
gap: 2px;
139+
background: rgba(var(--mageforge-color-white-rgb), 0.06);
140+
border-radius: 6px;
141+
padding: 2px;
142+
}
143+
144+
.mageforge-theme-icon-btn {
145+
display: flex;
146+
align-items: center;
147+
justify-content: center;
148+
width: 24px;
149+
height: 24px;
150+
padding: 0;
151+
border: none;
152+
border-radius: 4px;
153+
background: transparent;
154+
color: var(--mageforge-color-slate-400);
155+
cursor: pointer;
156+
transition: all 0.15s ease;
157+
line-height: 0;
158+
}
159+
160+
.mageforge-theme-icon-btn:hover {
161+
color: var(--mageforge-color-white);
162+
background: rgba(var(--mageforge-color-white-rgb), 0.08);
163+
}
164+
165+
.mageforge-theme-icon-btn--active,
166+
.mageforge-theme-icon-btn[aria-pressed="true"] {
167+
background: var(--mageforge-color-blue);
168+
color: var(--mageforge-color-white);
169+
}
170+
171+
.mageforge-theme-icon-btn:focus-visible {
172+
outline: 2px solid var(--mageforge-color-blue);
173+
outline-offset: 2px;
174+
}
175+
125176
/* ============================================================================
126177
Menu Items
127178
========================================================================== */

src/view/frontend/web/css/toolbar/_themes.css

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@
5959
}
6060

6161
/* Theme toggle pill: replace hardcoded white alpha with dark */
62-
.mageforge-toolbar[data-theme="light"] .mageforge-theme-toggle {
62+
.mageforge-toolbar[data-theme="light"] .mageforge-segmented-toggle,
63+
.mageforge-toolbar[data-theme="light"] .mageforge-theme-icon-group {
6364
background: rgba(var(--mageforge-color-black-rgb), 0.06);
6465
}
6566

66-
.mageforge-toolbar[data-theme="light"] .mageforge-theme-btn:hover {
67+
.mageforge-toolbar[data-theme="light"] .mageforge-segmented-btn:hover,
68+
.mageforge-toolbar[data-theme="light"] .mageforge-theme-icon-btn:hover {
6769
background: rgba(var(--mageforge-color-black-rgb), 0.08);
6870
}
6971

@@ -130,11 +132,13 @@
130132
background: rgba(var(--mageforge-color-teal-rgb), 0.14);
131133
}
132134

133-
.mageforge-toolbar[data-theme="auto"] .mageforge-theme-toggle {
135+
.mageforge-toolbar[data-theme="auto"] .mageforge-segmented-toggle,
136+
.mageforge-toolbar[data-theme="auto"] .mageforge-theme-icon-group {
134137
background: rgba(var(--mageforge-color-black-rgb), 0.06);
135138
}
136139

137-
.mageforge-toolbar[data-theme="auto"] .mageforge-theme-btn:hover {
140+
.mageforge-toolbar[data-theme="auto"] .mageforge-segmented-btn:hover,
141+
.mageforge-toolbar[data-theme="auto"] .mageforge-theme-icon-btn:hover {
138142
background: rgba(var(--mageforge-color-black-rgb), 0.08);
139143
}
140144

src/view/frontend/web/js/toolbar/ui/build.js

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import {
2424
createLogoSvg,
2525
generateId,
2626
ICON_HOME,
27+
ICON_THEME_DARK,
28+
ICON_THEME_LIGHT,
29+
ICON_THEME_AUTO,
2730
GROUP_ICONS,
2831
GAUGE_ARC_LENGTH,
2932
SCORE_RING_CIRCUMFERENCE,
@@ -93,7 +96,7 @@ export const buildMethods = {
9396
},
9497

9598
/**
96-
* Sticky title bar: logo + name + close button.
99+
* Sticky title bar: logo + name + theme toggle + close button.
97100
*
98101
* @returns {HTMLDivElement}
99102
*/
@@ -105,15 +108,54 @@ export const buildMethods = {
105108
<div>${createLogoSvg("#E5622A")}</div>
106109
<span class="mageforge-toolbar-menu-title-text">MageForge</span>
107110
</div>
108-
<button type="button" class="mageforge-toolbar-menu-close" title="Close & deactivate all" aria-label="Close & deactivate all">
109-
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"></path></svg>
110-
</button>
111+
<div class="mageforge-toolbar-menu-actions"></div>
111112
`;
112-
header.querySelector(".mageforge-toolbar-menu-close").onclick = (e) => {
113+
114+
const actions = header.querySelector(".mageforge-toolbar-menu-actions");
115+
116+
// Theme toggle
117+
const themeGroup = document.createElement("div");
118+
themeGroup.className = "mageforge-theme-icon-group";
119+
themeGroup.setAttribute("role", "group");
120+
themeGroup.setAttribute("aria-label", "Colour theme");
121+
[
122+
["dark", ICON_THEME_DARK, "Dark theme"],
123+
["auto", ICON_THEME_AUTO, "Auto theme"],
124+
["light", ICON_THEME_LIGHT, "Light theme"],
125+
].forEach(([value, icon, label]) => {
126+
const btn = document.createElement("button");
127+
btn.type = "button";
128+
btn.className = "mageforge-theme-icon-btn";
129+
btn.dataset.themeValue = value;
130+
btn.innerHTML = icon;
131+
btn.setAttribute("aria-label", label);
132+
btn.title = label;
133+
btn.setAttribute("aria-pressed", "false");
134+
btn.onclick = (e) => {
135+
e.stopPropagation();
136+
this.setTheme(/** @type {'dark'|'auto'|'light'} */ (value));
137+
};
138+
themeGroup.appendChild(btn);
139+
});
140+
actions.appendChild(themeGroup);
141+
this._themeToggleGroup = themeGroup;
142+
143+
// Close button
144+
const closeBtn = document.createElement("button");
145+
closeBtn.type = "button";
146+
closeBtn.className = "mageforge-toolbar-menu-close";
147+
closeBtn.title = "Close & deactivate all";
148+
closeBtn.setAttribute("aria-label", "Close & deactivate all");
149+
closeBtn.innerHTML =
150+
'<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"></path></svg>';
151+
closeBtn.onclick = (e) => {
113152
e.stopPropagation();
114153
this.deactivateAllAudits();
115154
this.closeMenu();
116155
};
156+
actions.appendChild(closeBtn);
157+
158+
this._updateThemeToggle();
117159
return header;
118160
},
119161

@@ -718,24 +760,34 @@ export const buildMethods = {
718760
const footer = document.createElement("div");
719761
footer.className = "mageforge-toolbar-menu-footer";
720762

721-
// Export format row ──────────────────────────────────────────────────
722-
const exportRow = document.createElement("div");
723-
exportRow.className = "mageforge-footer-theme-row";
724-
this._exportBtnRow = exportRow;
725-
const exportGroup = document.createElement("div");
726-
exportGroup.className = "mageforge-theme-toggle";
763+
// Footer toolbar row: credit + theme toggle + export formats
764+
const footerRow = document.createElement("div");
765+
footerRow.className = "mageforge-footer-toolbar-row";
766+
767+
// Credit line
768+
const credit = document.createElement("div");
769+
credit.className = "mageforge-toolbar-menu-credit";
770+
credit.innerHTML =
771+
'Built with <span class="mageforge-toolbar-menu-credit-heart">\u2764</span> by <a href="https://github.com/OpenForgeProject/mageforge" target="_blank" rel="noopener noreferrer" class="mageforge-toolbar-menu-credit-link">MageForge</a>';
772+
footerRow.appendChild(credit);
773+
774+
// Export format toggle
775+
this._exportBtnRow = document.createElement("div");
776+
this._exportBtnRow.className = "mageforge-segmented-toggle";
777+
this._exportBtnRow.setAttribute("role", "group");
778+
this._exportBtnRow.setAttribute("aria-label", "Export format");
727779
const exportLabel = document.createElement("span");
728-
exportLabel.className = "mageforge-footer-theme-label";
780+
exportLabel.className = "mageforge-footer-toolbar-label";
729781
exportLabel.textContent = "Export";
730-
exportGroup.appendChild(exportLabel);
782+
this._exportBtnRow.appendChild(exportLabel);
731783
[
732784
["json", "JSON"],
733785
["md", "MD"],
734786
["txt", "TXT"],
735787
].forEach(([fmt, label]) => {
736788
const btn = document.createElement("button");
737789
btn.type = "button";
738-
btn.className = "mageforge-theme-btn mageforge-export-btn--disabled";
790+
btn.className = "mageforge-segmented-btn mageforge-export-btn--disabled";
739791
btn.dataset.exportFormat = fmt;
740792
btn.textContent = label;
741793
btn.disabled = true;
@@ -745,17 +797,11 @@ export const buildMethods = {
745797
e.stopPropagation();
746798
this.exportFindings(fmt);
747799
};
748-
exportGroup.appendChild(btn);
800+
this._exportBtnRow.appendChild(btn);
749801
});
750-
exportRow.appendChild(exportGroup);
751-
footer.appendChild(exportRow);
802+
footerRow.appendChild(this._exportBtnRow);
752803

753-
// Credit line (left side of export row) ─────────────────────────────
754-
const credit = document.createElement("div");
755-
credit.className = "mageforge-toolbar-menu-credit";
756-
credit.innerHTML =
757-
'Built with <span class="mageforge-toolbar-menu-credit-heart">\u2764</span> by <a href="https://github.com/OpenForgeProject/mageforge" target="_blank" rel="noopener noreferrer" class="mageforge-toolbar-menu-credit-link">MageForge</a>';
758-
exportRow.insertBefore(credit, exportRow.firstChild);
804+
footer.appendChild(footerRow);
759805

760806
// Populate nav action bar for the initially active tab (home).
761807
// footerActionBar was already created in _buildTabNav().

src/view/frontend/web/js/toolbar/ui/constants.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ export const LOGO_SVG_PATH =
66
export const ICON_HOME =
77
'<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>';
88

9+
export const ICON_THEME_DARK =
10+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
11+
12+
export const ICON_THEME_LIGHT =
13+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';
14+
15+
export const ICON_THEME_AUTO =
16+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M12 2a10 10 0 0 1 0 20v-20z"></path></svg>';
17+
918
export const GROUP_ICONS = {
1019
wcag: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z"></path><circle cx="12" cy="12" r="3"></circle></svg>',
1120
"html-quality":

src/view/frontend/web/js/toolbar/ui/controls.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export const controls = {
9696
this.runAllButton = null;
9797
this.resetButton = null;
9898
this._exportBtnRow = null;
99+
this._themeToggleGroup = null;
99100
this.menuOpen = false;
100101
},
101102

@@ -110,5 +111,20 @@ export const controls = {
110111
localStorage.setItem("mageforge-theme", theme);
111112
} catch (_) {}
112113
if (this.container) this.container.setAttribute("data-theme", theme);
114+
this._updateThemeToggle();
115+
},
116+
117+
/**
118+
* Reflect the current theme in the header icon toggle buttons.
119+
*/
120+
_updateThemeToggle() {
121+
if (!this._themeToggleGroup) return;
122+
this._themeToggleGroup
123+
.querySelectorAll(".mageforge-theme-icon-btn")
124+
.forEach((btn) => {
125+
const active = btn.dataset.themeValue === this.currentTheme;
126+
btn.classList.toggle("mageforge-theme-icon-btn--active", active);
127+
btn.setAttribute("aria-pressed", String(active));
128+
});
113129
},
114130
};

0 commit comments

Comments
 (0)