
Documentation
HTML Macro+ for ConfluenceBrowse
Documentation
HTML Macro+ for ConfluenceSupporting Dark Mode
When a reader switches Confluence to dark mode, your macro can switch with it instead of showing a white box on a dark page. It takes a few lines of CSS, and works on the page and in the editor's live preview.
The short version: the macro's <html> element has data-theme="light" or data-theme="dark". Style both, and the macro changes as soon as the reader switches theme, without a reload.
Existing macros don't change: nothing happens until your CSS uses data-theme.
Step 1: Put your colors in variables
Define each color once for light mode, then override it for dark:
:root {
--surface: #ffffff;
--text: #172b4d;
--muted: #626f86;
--border: #dfe1e6;
--accent: #0c66e4;
}
[data-theme="dark"] {
--surface: #22272b;
--text: #b6c2cf;
--muted: #8c9bab;
--border: #3b4045;
--accent: #579dff;
color-scheme: dark; /* dark form fields and scrollbars */
}
These are Confluence's own light and dark colors, so the macro blends in with the page.
Step 2: Use the variables everywhere
body { color: var(--text); }
.card {
background: var(--surface);
border: 1px solid var(--border);
}
.card a { color: var(--accent); }
.card small { color: var(--muted); }
That's it for most macros. Switch Confluence's theme (profile picture → Theme) to check.
Common problems
| You see | Cause | Fix |
|---|---|---|
| Black text on the dark page | The macro never sets a text color | Add body { color: var(--text); } |
| A white box around the macro | body or html has a white background |
Remove it; color only your own containers |
| White input fields and scrollbars | Browser defaults | Add color-scheme: dark; under [data-theme="dark"] |
| One element stays light | A hardcoded color, often in a style="" attribute |
Replace it with a variable |
| A dark logo disappears | It was designed for a light background | Swap images (below) |
To show a different logo per theme:
.logo-dark { display: none; }
[data-theme="dark"] .logo-light { display: none; }
[data-theme="dark"] .logo-dark { display: inline; }
Charts, canvas and colors set in JavaScript
CSS can't restyle a <canvas> or colors your script already applied. Read the current theme when the script runs, and redraw when it changes:
function draw(theme) {
// "light" or "dark": redraw your chart with matching colors
}
draw(document.documentElement.dataset.theme);
window.addEventListener('htmlmacro:themechange', (event) => {
draw(event.detail.theme);
});
Tip: read colors from your CSS variables so the chart always matches the CSS:
const text = getComputedStyle(document.documentElement).getPropertyValue('--text').trim();
Full example
A team status card with a badge, a list, a chart and a form. Paste each block into the matching tab, then switch Confluence's theme.
HTML
<div class="card">
<header class="card-header">
<svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm1 15h-2v-6h2v6Zm0-8h-2V7h2v2Z"/>
</svg>
<h2>Platform team status</h2>
<span class="badge badge-ok">All systems normal</span>
</header>
<p class="muted">Current theme: <strong id="theme-name"></strong></p>
<ul class="list">
<li><span class="dot dot-ok"></span> API gateway <small>99.98% uptime</small></li>
<li><span class="dot dot-warn"></span> Search indexing <small>Slower than usual</small></li>
<li><span class="dot dot-ok"></span> File storage <small>99.99% uptime</small></li>
</ul>
<h3>Requests this week</h3>
<canvas id="chart" width="560" height="160"></canvas>
<form class="feedback" id="feedback">
<label for="comment">Something wrong?</label>
<div class="row">
<input id="comment" type="text" placeholder="Tell the team…">
<button type="submit">Send</button>
</div>
<p class="muted" id="thanks" hidden>Thanks, we'll take a look.</p>
</form>
</div>
CSS
:root {
--surface: #ffffff;
--text: #172b4d;
--muted: #626f86;
--border: #dfe1e6;
--accent: #0c66e4;
--accent-text: #ffffff;
--ok: #1f845a;
--ok-bg: #dcfff1;
--warn: #b38600;
}
[data-theme="dark"] {
--surface: #22272b;
--text: #b6c2cf;
--muted: #8c9bab;
--border: #3b4045;
--accent: #579dff;
--accent-text: #1d2125;
--ok: #4bce97;
--ok-bg: #164b35;
--warn: #f5cd47;
color-scheme: dark;
}
body {
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px;
}
.card {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px 20px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
}
.card-header { display: flex; align-items: center; gap: 8px; }
.card-header h2 { flex: 1; margin: 0; font-size: 16px; }
.icon { width: 20px; height: 20px; fill: var(--accent); }
h3 { margin: 4px 0 0; font-size: 13px; }
.muted, small { color: var(--muted); }
.muted { margin: 0; }
.badge { padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: 600; }
.badge-ok { color: var(--ok); background: var(--ok-bg); }
.list { display: flex; flex-direction: column; gap: 6px; margin: 0; padding: 0; list-style: none; }
.list li { display: flex; align-items: center; gap: 8px; }
.list small { margin-left: auto; }
.dot { width: 8px; height: 8px; border-radius: 50%; }
.dot-ok { background: var(--ok); }
.dot-warn { background: var(--warn); }
#chart { width: 100%; height: 160px; }
.feedback { display: flex; flex-direction: column; gap: 6px; }
.feedback .row { display: flex; gap: 8px; }
.feedback input {
flex: 1;
padding: 6px 8px;
border: 1px solid var(--border);
border-radius: 4px;
background: transparent;
color: var(--text);
}
.feedback button {
padding: 6px 14px;
border: 0;
border-radius: 4px;
background: var(--accent);
color: var(--accent-text);
font-weight: 600;
cursor: pointer;
}
JavaScript
const data = [
{ day: 'Mon', value: 120 },
{ day: 'Tue', value: 180 },
{ day: 'Wed', value: 150 },
{ day: 'Thu', value: 210 },
{ day: 'Fri', value: 170 },
];
// Read colors from the CSS variables so the chart matches the theme
function cssColor(name) {
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
}
// A canvas doesn't follow CSS, so redraw it on every theme change
function drawChart() {
const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
const { width, height } = canvas;
const max = Math.max(...data.map((d) => d.value));
const barWidth = width / data.length;
ctx.clearRect(0, 0, width, height);
ctx.font = '12px -apple-system, sans-serif';
ctx.textAlign = 'center';
data.forEach((d, i) => {
const barHeight = (d.value / max) * (height - 40);
const x = i * barWidth + barWidth * 0.2;
const y = height - 20 - barHeight;
ctx.fillStyle = cssColor('--accent');
ctx.fillRect(x, y, barWidth * 0.6, barHeight);
ctx.fillStyle = cssColor('--muted');
ctx.fillText(d.day, x + barWidth * 0.3, height - 5);
ctx.fillStyle = cssColor('--text');
ctx.fillText(String(d.value), x + barWidth * 0.3, y - 5);
});
}
function applyTheme(theme) {
document.getElementById('theme-name').textContent = theme;
drawChart();
}
applyTheme(document.documentElement.dataset.theme || 'light');
window.addEventListener('htmlmacro:themechange', (event) => applyTheme(event.detail.theme));
document.getElementById('feedback').addEventListener('submit', (event) => {
event.preventDefault();
document.getElementById('comment').value = '';
document.getElementById('thanks').hidden = false;
});
Let AI do it
Building with an AI assistant? Click Copy AI prompt at the top of the editor and keep Support Confluence dark mode ticked. The prompt tells the AI to follow the steps on this page. See Build It with AI.
Good to know
- If Confluence is set to match the operating system's theme, the macro follows whichever theme Confluence is showing.
- The theme also applies in the editor's live preview, so you can check both while you edit.
Related
Guides for HTML Macro+ for Confluence
Stuck on something this page does not cover? Ask the developers.