
How to Build a Custom Status Dashboard in Confluence
Project status pages in Confluence often end up as paragraphs of text that nobody reads. A wall of prose with a last-updated date from three weeks ago doesn't tell anyone how the project is doing.
A visual dashboard with RAG indicators, progress percentages and days until key milestones is faster to read and easier to keep current. This guide walks through building one with HTML Macro+ for Confluence.
Do you need a custom dashboard? Confluence Cloud already has a Status element: type /status to add a colored lozenge such as ON TRACK or BLOCKED inside text or a table. For a simple status table, that is often enough. The approach below is for when you want a larger, more visual overview at the top of the page.
What We're Building
A status dashboard with four components:
- RAG status cards: one per workstream, color-coded by health
- Progress bars: percentage complete for each initiative
- Milestone countdown: days until the next key date
- Metric tiles: key numbers at a glance
Each component is a separate HTML Macro+ macro on the page, so you can use all four or only the ones you need. To insert one, edit the page, type /html and select HTML Macro+ for Confluence. Each example below is split into the editor's HTML, CSS and JavaScript tabs. Keeping styles in the CSS tab means updating the dashboard only touches the HTML.
Component 1: RAG Status Cards
A row of status cards, one per workstream: green for on track, amber for at risk, red for blocked.
HTML
<div class="rag-row">
<div class="rag-card green">
<div class="rag-name">Backend API</div>
<div class="rag-state"><span class="dot"></span>On Track</div>
<div class="rag-note">Sprint 3 of 4 complete</div>
</div>
<div class="rag-card amber">
<div class="rag-name">Mobile App</div>
<div class="rag-state"><span class="dot"></span>At Risk</div>
<div class="rag-note">Design review delayed 3 days</div>
</div>
<div class="rag-card red">
<div class="rag-name">Integrations</div>
<div class="rag-state"><span class="dot"></span>Blocked</div>
<div class="rag-note">Waiting on vendor API access</div>
</div>
</div>
CSS
.rag-row { display: flex; gap: 16px; flex-wrap: wrap; font-family: sans-serif; }
.rag-card { flex: 1; min-width: 180px; padding: 20px; border-radius: 12px; border: 1px solid; }
.rag-name { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px; }
.rag-state { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 700; color: #1e293b; }
.rag-note { margin-top: 8px; font-size: 13px; color: #64748b; }
.dot { width: 12px; height: 12px; border-radius: 50%; }
.rag-card.green { background: #f0fdf4; border-color: #86efac; }
.rag-card.green .rag-name { color: #15803d; }
.rag-card.green .dot { background: #22c55e; }
.rag-card.amber { background: #fffbeb; border-color: #fcd34d; }
.rag-card.amber .rag-name { color: #92400e; }
.rag-card.amber .dot { background: #f59e0b; }
.rag-card.red { background: #fef2f2; border-color: #fca5a5; }
.rag-card.red .rag-name { color: #b91c1c; }
.rag-card.red .dot { background: #ef4444; }
To update a status: change the card's class to green, amber or red, then update the label and note text. The CSS handles all the colors.
Component 2: Progress Bars
A filled bar and a percentage label for each initiative. The percentage lives in one data-percent attribute, and a few lines of JavaScript set the bar width and label from it.
HTML
<div class="progress-list">
<div class="progress green" data-percent="75">
<div class="progress-head"><span>Backend API</span><span class="progress-value"></span></div>
<div class="progress-track"><div class="progress-fill"></div></div>
</div>
<div class="progress amber" data-percent="48">
<div class="progress-head"><span>Mobile App</span><span class="progress-value"></span></div>
<div class="progress-track"><div class="progress-fill"></div></div>
</div>
<div class="progress red" data-percent="20">
<div class="progress-head"><span>Integrations</span><span class="progress-value"></span></div>
<div class="progress-track"><div class="progress-fill"></div></div>
</div>
</div>
CSS
.progress-list { font-family: sans-serif; max-width: 560px; }
.progress { margin-bottom: 18px; }
.progress-head { display: flex; justify-content: space-between; margin-bottom: 6px; font-size: 14px; font-weight: 600; color: #1e293b; }
.progress-value { font-weight: 700; }
.progress-track { background: #e2e8f0; border-radius: 99px; height: 8px; }
.progress-fill { height: 8px; border-radius: 99px; width: 0; }
.progress.green .progress-fill { background: #22c55e; }
.progress.green .progress-value { color: #15803d; }
.progress.amber .progress-fill { background: #f59e0b; }
.progress.amber .progress-value { color: #92400e; }
.progress.red .progress-fill { background: #ef4444; }
.progress.red .progress-value { color: #b91c1c; }
JavaScript
document.querySelectorAll(".progress").forEach((item) => {
const percent = Math.max(0, Math.min(100, Number(item.dataset.percent) || 0));
item.querySelector(".progress-fill").style.width = percent + "%";
item.querySelector(".progress-value").textContent = percent + "%";
});
To update: change data-percent and, if the health changed, the green, amber or red class so the bar matches Component 1.
Component 3: Milestone Countdown
A countdown to the next key date. The date is set once, in the data-target attribute, and the script shows both the date and the time remaining.
HTML
<div class="countdown" data-target="2027-03-01T09:00:00">
<div>
<div class="countdown-label">Launch Date</div>
<div class="countdown-date"></div>
</div>
<div class="countdown-units">
<div><div class="countdown-num" id="cd-days">--</div><div class="countdown-unit">Days</div></div>
<div class="countdown-sep">:</div>
<div><div class="countdown-num" id="cd-hours">--</div><div class="countdown-unit">Hours</div></div>
</div>
</div>
CSS
.countdown {
font-family: sans-serif;
display: inline-flex;
align-items: center;
gap: 24px;
padding: 20px 28px;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 14px;
}
.countdown-label { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: #64748b; margin-bottom: 4px; }
.countdown-date { font-size: 14px; color: #1e293b; font-weight: 500; }
.countdown-units { display: flex; gap: 16px; text-align: center; }
.countdown-num { font-size: 40px; font-weight: 800; color: #1e293b; line-height: 1; }
.countdown-unit { font-size: 11px; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.06em; margin-top: 3px; }
.countdown-sep { font-size: 32px; color: #cbd5e1; font-weight: 300; padding-top: 4px; }
JavaScript
const box = document.querySelector(".countdown");
const target = new Date(box.dataset.target);
box.querySelector(".countdown-date").textContent = target.toLocaleDateString(undefined, {
year: "numeric", month: "long", day: "numeric",
});
function tick() {
const diff = Math.max(0, target - new Date());
document.getElementById("cd-days").textContent = Math.floor(diff / 86400000);
document.getElementById("cd-hours").textContent = Math.floor((diff % 86400000) / 3600000);
}
tick();
setInterval(tick, 60000);
The date above is only an example. Set data-target to your own milestone, and update it when you reach the milestone, otherwise the countdown sits at zero.
Component 4: Metric Tiles
Key numbers at a glance: issues closed, open bugs, blocked items, team size or any other project metric.
HTML
<div class="tiles">
<div class="tile accent"><div class="tile-num">34</div><div class="tile-label">Issues Closed</div></div>
<div class="tile green"><div class="tile-num">12</div><div class="tile-label">Open Bugs</div></div>
<div class="tile amber"><div class="tile-num">3</div><div class="tile-label">Blocked Items</div></div>
<div class="tile"><div class="tile-num">8</div><div class="tile-label">Team Members</div></div>
</div>
CSS
.tiles { display: flex; gap: 16px; flex-wrap: wrap; font-family: sans-serif; }
.tile { flex: 1; min-width: 140px; padding: 20px; border-radius: 12px; background: #fff; border: 1px solid #e2e8f0; text-align: center; }
.tile-num { font-size: 36px; font-weight: 800; color: #1e293b; }
.tile-label { font-size: 13px; color: #64748b; margin-top: 4px; }
.tile.accent .tile-num { color: #6366f1; }
.tile.green .tile-num { color: #22c55e; }
.tile.amber .tile-num { color: #f59e0b; }
.tile.red .tile-num { color: #ef4444; }
To update: change the number and label. Add or change the green, amber or red class if you want the color to reflect the metric's health.
Putting It Together
On the Confluence page, stack the four macros vertically:
- RAG status cards at the top
- Metric tiles below
- Progress bars in the middle
- Milestone countdown at the bottom
Above the dashboard, add a "Last updated" line using Confluence's date element: type // in the editor to open the date picker. Readers can then see at a glance whether the data is current.
Weekly update workflow: at the start of each week, edit the page and update the class and text on each card, the data-percent values and the metric numbers. It takes a few minutes, and the page shows project health clearly to anyone who opens it. If a change goes wrong, the macro's History button keeps the last 10 saved versions so you can restore one (version history).
All four components are self-contained, so they work even when your admin has set the app to block all external content. For more examples, see 5 things you can build with HTML Macro+ and how to make pie, bar and line charts in Confluence. The HTML editor docs explain how the three tabs work together.
Install HTML Macro+ for Confluence, free on the Atlassian Marketplace


