Skip to content
Yamuno
Browse

Supporting 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.

Guides for HTML Macro+ for Confluence

Stuck on something this page does not cover? Ask the developers.