Skip to main content
Kolekti home
Kolekti will be at Team '26! Booth number revealed
Read more
17 Confluence tools, ready-made in HTML
Share on socials

17 Confluence tools, ready-made in HTML

A headshot of Simon Kirrane
Simon Kirrane
Published: 28 April 2026
9 min read
A colourful, collage-style image of some example creations with the HTML macro on a Confluence page, including a delivery progress bar and a doughnut chart
A headshot of Simon Kirrane
Simon Kirrane
Published: 28 April 2026
9 min read
Jump to section
- Preview
- Customising the delivery progress bar
- Preview
- Customising the live search and filter directory
- Preview
- Customising the holographic 3D multi-display
- Preview
- Customising the colour gradient banner header
- Preview
- Customising the masonry image gallery
- Preview
- Customising the release countdown timer
- Preview
- Customising the data doughnut chart
- Preview
- Customising the animated data doughnut chart
- Preview
- Customising the glass-effect panel card
- Preview
- Customising the slide and fill action card
- Preview
- Customising the border slide and lift card
- Preview
- Customising the icon scale card
- Preview
- Customising the solid colour fill card
- Preview
- Customising the rotating gradient border card
- Preview
- Customising the rotating light border card
- Preview
- Customising the ambient glow effect card
- Preview
- Customising the light sweep card

With the HTML macro, you can ‘vibe code’ your way to perfect Confluence pages. Here are some examples we’ve created for you to try.

Getting a Confluence page to look and behave exactly how you want can sometimes mean there’s an element of compromise. Whether that’s thanks to the rigid layouts, or due to a miscommunication between you and the developer you requested a custom chart from. But now, Mosaic and its HTML macro give you the opportunity to create bespoke, interactive components in minutes.

Mosaic is designed to make your content look incredible with minimal effort and the HTML macro increases the levels of creative freedom you can exploit. To give you a taste of that freedom we’ve created 17 tools and widgets for you to try in the HTML macro right now. Just scroll to the element that appeals to you here and then copy the lines of code into the tabs in the macro editor: HTML goes into HTML, CSS into CSS, and JavaScript (JS) into the JS tab. Just be aware that JS is only available to users with the Advanced Edition of Mosaic, so if you are in Standard some of these tools will not copy across to your macro. Once you have created it, you can then follow the instructions on customising the tool to suit your needs.

You can use the HTML macro for high-impact, custom elements, while using the standard formatting macros for the body of your content. This gives you the best of both worlds: when you need to update a line of text quickly, you can do it instantly in a formatting macro without having to hunt through lines of code to find the right string. Meanwhile, the HTML macro can handle complex and custom headers, charts, and interactive tools to keep your day-to-day edits seamless. Or if you are creating pages that don’t need updating, you can create whole pages using HTML that feature multiple elements and tools.

And the best part about the HTML macro is that you don’t have to be a cybersecurity expert to use it. We’ve built these tools to work in harmony with Atlassian’s native security measures. Our built-in safety features complement Atlassian’s own sanitisation protocols, ensuring that your custom widgets are as secure as they are stylish. So you can innovate while your IT department gets the peace of mind they require.

If you want to create your own widgets or pages, then why not try our vibe coding prompt and see how easy it is to bring your vision to life in minutes?

The delivery progress bar

A horizontal progress indicator that animates on load. Useful for tracking objectives, sprint goals, and quarterly targets.
A gif of a delivery progress bar filling up with colour to 75%, made with an HTML macro from Mosaic in Confluence
<div class="progress-wrapper">
<div class="progress-item">
<div class="progress-header">
<span class="progress-label">Q3 Delivery Target</span>
<span class="progress-value">75%</span>
</div>
<div class="progress-track">
<div class="progress-fill" style="width: 75%; "></div>
</div>
</div>
</div>
.progress-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 20px;
max-width: 500px;
}
.progress-header {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 8px;
}
.progress-label {
font-size: 14px;
font-weight: 600;
color: #0f172a;
}
.progress-value {
font-size: 14px;
font-weight: 700;
color: #6366f1;
}
.progress-track {
background: #e2e8f0;
border-radius: 100px;
height: 10px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
border-radius: 100px;
animation: fillUp 1.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
transform-origin: left;
}
@keyframes fillUp {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(1);
}
}
How to customise the delivery progress bar
Customising the progress bar requires making a quick update in both the HTML tab (for the data and text) and the CSS tab (for your brand colours).
To change the data, you need to update two things in your HTML: the visible text that the user reads, and the inline width style that tells the bar how far to fill up.

Find this section in your HTML:
<div class="progress-header">
<span class="progress-label">Q3 Delivery Target</span> <span class="progress-value">75%</span> </div>
<div class="progress-track">
<div class="progress-fill" style="width: 75%;"></div> </div>


Example of an updated version
If your new target is "Project Alpha" and it is 90% complete, your HTML should look like this:

<div class="progress-header">
<span class="progress-label">Project Alpha Completion</span> 
<span class="progress-value">90%</span>
</div>
<div class="progress-track">
<div class="progress-fill" style="width: 90%;"></div>
</div>

By default, the progress bar uses a left-to-right gradient. You can change this to match your corporate branding.

Find this section in your CSS:
.progress-fill {
height: 100%;
/* Change these two hex codes to your brand colours */
background: linear-gradient(90deg, #3b82f6, #8b5cf6); 
border-radius: 100px;
animation: fillUp 1.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
transform-origin: left;
}

To use a custom gradient: Replace #3b82f6 (the starting colour) and #8b5cf6 (the ending colour) with your own hex codes.
To use a solid colour instead: Delete the linear-gradient line entirely and replace it with a standard background colour rule, like this: “background: #FF0000;”.

The live search and filter directory

A searchable grid of cards that filters results in real-time as the user types. Useful for team directories, architecture glossaries, and FAQ hubs.
A live search and filter directory, made with an HTML macro from Mosaic in Confluence.
<div class="mosaic-search-wrapper">
<div class="search-header">
<div class="sh-title">
<h3>Architecture Glossary</h3>
<p>Live filtering powered by Advanced JS DOM Injection.</p>
</div>
<div class="search-input-container"></div>
</div>
<div class="search-grid">
<div class="search-card">
<div class="sc-header">
<h4>Microservices Architecture</h4>
<span class="sc-tag tag-blue">Backend</span>
</div>
<p class="sc-body">
Microservices are an architectural approach where software is composed
of small independent services that communicate over well-defined APIs.
This approach makes applications easier to scale and faster to develop.
</p>
</div>
<div class="search-card">
<div class="sc-header">
<h4>Event-Driven Architecture</h4>
<span class="sc-tag tag-purple">Infrastructure</span>
</div>
<p class="sc-body">
An event-driven architecture uses events to trigger and communicate
between decoupled services. An event is a change in state, or an update.
This paradigm promotes high asynchronous operational capability.
</p>
</div>
<div class="search-card">
<div class="sc-header">
<h4>Zero Trust Security</h4>
<span class="sc-tag tag-green">Security</span>
</div>
<p class="sc-body">
Zero Trust is a security framework requiring all users to be
authenticated and continuously validated before being granted access. We
implement this by removing implicit trust and strictly enforcing
least-privilege access.
</p>
</div>
<div class="search-card">
<div class="sc-header">
<h4>Continuous Integration (CI)</h4>
<span class="sc-tag tag-orange">DevOps</span>
</div>
<p class="sc-body">
Continuous Integration is the practice of automating the integration of
code changes from multiple contributors. Automated tools are used to
assert the new code's correctness, eliminating integration bottlenecks.
</p>
</div>
</div>
<div class="search-empty-state">
<p>No concepts found matching your search.</p>
</div>
</div>
.mosaic-search-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
overflow: hidden;
}
.search-header {
background: #ffffff;
padding: 32px;
border-bottom: 1px solid #e2e8f0;
display: flex;
flex-direction: column;
gap: 24px;
}
.sh-title h3 {
margin: 0 0 8px 0;
font-size: 24px;
color: #0f172a;
}
.sh-title p {
margin: 0;
font-size: 15px;
color: #64748b;
}
.search-input-container input {
width: 100%;
box-sizing: border-box;
background: #ffffff;
border: 2px solid #cbd5e1;
border-radius: 8px;
padding: 16px 20px;
font-size: 16px;
color: #0f172a;
outline: none;
transition: all 0.2s ease;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.02);
font-family: inherit;
}
.search-input-container input:focus {
border-color: #8b5cf6;
box-shadow: 0 0 0 4px rgba(139, 92, 246, 0.15);
}
.search-grid {
padding: 32px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 24px;
}
.search-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.02);
display: flex;
flex-direction: column;
animation: fadeIn 0.3s ease;
}
.sc-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 16px;
margin-bottom: 16px;
border-bottom: 1px solid #f1f5f9;
padding-bottom: 16px;
}
.sc-header h4 {
margin: 0;
font-size: 18px;
color: #0f172a;
line-height: 1.3;
}
.sc-tag {
padding: 4px 10px;
border-radius: 6px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
}
.tag-blue {
background: #eff6ff;
color: #3b82f6;
}
.tag-purple {
background: #f3e8ff;
color: #8b5cf6;
}
.tag-green {
background: #ecfdf5;
color: #10b981;
}
.tag-orange {
background: #fff7ed;
color: #f97316;
}
.sc-body {
margin: 0;
font-size: 14px;
color: #475569;
line-height: 1.7;
}
.search-empty-state {
display: none;
text-align: center;
padding: 60px 0;
color: #64748b;
font-size: 16px;
font-style: italic;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
(function () {
const wrappers = document.querySelectorAll(".mosaic-search-wrapper");
wrappers.forEach(wrapper => {
const container = wrapper.querySelector(".search-input-container");
const cards = wrapper.querySelectorAll(".search-card");
const emptyState = wrapper.querySelector(".search-empty-state");
const realInput = document.createElement("input");
realInput.type = "text";
realInput.placeholder = "Search for a concept (e.g., 'Security' or 'Microservices')...";
container.appendChild(realInput);
realInput.addEventListener("input", e => {
const searchTerm = e.target.value.toLowerCase().trim();
let visibleCount = 0;
cards.forEach(card => {
const text = card.textContent.toLowerCase();
if (text.includes(searchTerm)) {
card.style.display = "flex";
visibleCount++;
} else {
card.style.display = "none";
}
});
if (visibleCount === 0) {
emptyState.style.display = "block";
} else {
emptyState.style.display = "none";
}
});
});
})();
How to customise the live search and filter directory
Because this component uses JS to securely inject the search bar, you will be making edits across all three tabs (HTML, CSS, and JS) to customise it.
Since JS is responsible for building the search box, the placeholder text lives in the JS tab, not the HTML tab.
  1. Open the JS tab.
  2. Look for realInput.placeholder = "..." near the top of the script.
  3. Change the text inside the quotes to match your content:
    E.g. realInput.placeholder = "Search for a team member, department, or role...";
The JS automatically scans all the text inside the grid. You can add as many cards as you want, and the search will immediately know how to filter them.
To add a new card: Copy and paste an entire <div class="search-card">...</div> block and change the text inside.
To remove a card: Simply delete its block.

HTML
<div class="search-card">
<div class="sc-header">
<h4>Your New Title</h4>
<span class="sc-tag tag-blue">Your Tag</span>
</div>
<p class="sc-body">Your custom text goes here.</p>
</div>
The little coloured tags (like the blue Backend tag) are controlled by CSS classes.
To change the text: Just type a new word inside the <span class="sc-tag"> in your HTML.
To use a different colour: Change the class name on the span. The template includes tag-blue, tag-purple, tag-green, and tag-orange.
To create a brand new colour: Go to the CSS tab, find the tag colours, and add your own rule!

/* Add your own brand tag colour here! */
.tag-brand { background: #fee2e2; color: #ef4444; }
When a user clicks into the search box to start typing, it highlights with a purple ring. To change this to your brand's primary colour, open the CSS tab and find the :focus rule.

CSS
.search-input-container input:focus {
/* Change these two hex codes to your brand colour */
border-color: #006d7a; 
box-shadow: 0 0 0 4px rgba(0, 109, 122, 0.15); /* The last number is the opacity */
}

Right now, the grid automatically figures out how many columns to show based on the screen size, with each card being at least 320px wide.

If you want fewer, wider cards (like 2 per row), increase that number to 400px.
If you want more, narrower cards (like 4 per row), decrease it to 250px.
CSS
.search-grid {
/* Change 320px to make the cards wider or narrower */
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}

The holographic 3D multi-display

A 3D spatial, mouse-tracking dashboard that tilts and reacts to the user's mouse position.
A gif of a holographic 3D multi-display orienting around a mouse as if to face it, made with an HTML macro from Mosaic in Confluence.
<div class="holo-wrapper">
<div class="holo-scene">
<div class="holo-floor"></div>
<div class="holo-card layer-bg">
<div class="holo-pulse"></div>
<h4>Global Network Status</h4>
<p>All core routing systems are operating within normal parameters.</p>
</div>
<div class="holo-card layer-mid">
<span class="holo-badge">Live System</span>
<h2>Project Alpha Node</h2>
<div class="holo-stats">
<div class="h-stat">
<span class="h-val">99.9%</span>
<span class="h-lbl">Uptime</span>
</div>
<div class="h-stat">
<span class="h-val">14ms</span>
<span class="h-lbl">Latency</span>
</div>
</div>
</div>
<div class="holo-card layer-fg">
<div class="holo-alert-icon">⚡</div>
<div>
<h4>Active Deployments</h4>
<p>3 clusters updating...</p>
</div>
</div>
</div>
</div>
.holo-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
background: #020617; 
border-radius: 16px;
padding: 60px 20px;
min-height: 500px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
perspective: 1200px;
}
.holo-scene {
position: relative;
width: 100%;
max-width: 600px;
height: 400px;
transform-style: preserve-3d;
transform: rotateX(var(--rotY, 0deg)) rotateY(var(--rotX, 0deg));
transition: transform 0.1s ease-out; 
}
.holo-floor {
position: absolute;
top: 50%;
left: 50%;
width: 200%;
height: 200%;
background-image:
linear-gradient(rgba(56, 189, 248, 0.2) 1px, transparent 1px),
linear-gradient(90deg, rgba(56, 189, 248, 0.2) 1px, transparent 1px);
background-size: 40px 40px;
transform: translate(-50%, -20%) rotateX(75deg) translateZ(-150px);
mask-image: radial-gradient(circle at center, black 10%, transparent 60%);
-webkit-mask-image: radial-gradient(
circle at center,
black 10%,
transparent 60%
);
}
.holo-card {
position: absolute;
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgba(56, 189, 248, 0.3);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border-radius: 16px;
padding: 24px;
color: #f8fafc;
box-shadow:
0 20px 40px rgba(0, 0, 0, 0.5),
inset 0 0 20px rgba(56, 189, 248, 0.05);
}
.layer-bg {
top: 20px;
right: 20px;
width: 260px;
transform: translateZ(-50px); 
border-color: rgba(148, 163, 184, 0.2);
}
.layer-mid {
top: 50%;
left: 50%;
width: 340px;
transform: translate(-50%, -50%) translateZ(40px);
border-color: rgba(56, 189, 248, 0.5);
box-shadow: 0 0 30px rgba(56, 189, 248, 0.15);
}
.layer-fg {
bottom: 20px;
left: 20px;
width: 240px;
display: flex;
gap: 16px;
align-items: center;
transform: translateZ(120px);
background: rgba(139, 92, 246, 0.2);
border-color: rgba(139, 92, 246, 0.5);
}
.holo-badge {
display: inline-block;
padding: 4px 10px;
background: rgba(56, 189, 248, 0.2);
color: #38bdf8;
border-radius: 100px;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 12px;
}
.layer-mid h2 {
margin: 0 0 20px 0;
font-size: 28px;
font-weight: 300;
letter-spacing: -0.5px;
}
.holo-stats {
display: flex;
gap: 24px;
}
.h-stat {
display: flex;
flex-direction: column;
}
.h-val {
font-size: 24px;
font-weight: 700;
color: #38bdf8;
}
.h-lbl {
font-size: 12px;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: 1px;
}
.layer-bg h4 {
margin: 0 0 8px 0;
font-size: 14px;
color: #94a3b8;
}
.layer-bg p {
margin: 0;
font-size: 13px;
color: #64748b;
line-height: 1.5;
}
.holo-alert-icon {
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(139, 92, 246, 0.4);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.layer-fg h4 {
margin: 0 0 4px 0;
font-size: 15px;
}
.layer-fg p {
margin: 0;
font-size: 13px;
color: #c4b5fd;
}
(function () {
const wrappers = document.querySelectorAll(".holo-wrapper");
wrappers.forEach(wrapper => {
const scene = wrapper.querySelector(".holo-scene");
wrapper.addEventListener("mousemove", e => {
const rect = wrapper.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const rotateX = (x - centerX) / centerX * 15;
const rotateY = (y - centerY) / centerY * -15;
scene.style.setProperty("--rotX", `${rotateX}deg`);
scene.style.setProperty("--rotY", `${rotateY}deg`);
});
wrapper.addEventListener("mouseleave", () => {
scene.style.setProperty("--rotX", `0deg`);
scene.style.setProperty("--rotY", `0deg`);
});
});
})();
How to customise the holographic 3D multi-display
The "trick" of the holographic 3D display isn't just the visual effect; it is the deliberate spatial mapping of information. It forces context by placing content in three prioritised dimensions: a quiet background, a primary focal point, and an urgent foreground.
Customise this macro by thinking like an architect, not an editor. Map your data based on priority:
  1. Foreground (layer-fg): Immediate action. Reserved for critical incidents, active deployment alerts, or real-time blockers (DACI decisions). It should demand attention via its purple alert branding (background: rgba(139, 92, 246, 0.2)).
  2. Midground (layer-mid): Primary mission. This is the focal point. It holds the high-value performance data (OKRs, Uptime, Project Health) that the viewer must understand immediately.
  3. Background (layer-bg): Broad context. For low-priority health indicators, general system summaries, or long-form notes that provide foundational knowledge but do not require instant reaction.
The core visual mechanics are defined in the CSS, utilising translateZ to pull elements forward (negative values) or push them back (positive values) on the Z-axis.
1. Defining the depth hierarchy (translateZ)
The "pop" of the display is controlled entirely by the CSS depth.
  • To make a card "float" further away (background), decrease the translateZ value (e.g., -50px to -150px in .layer-bg).
  • To bring a card closer (foreground), increase the translateZ value (e.g., 120px to 200px in .layer-fg).
CSS
/* Example: Pushing the background layer deeper for dramatic context */
.layer-bg {
transform: translateZ(-150px); /* Changed from -50px */
}
2. Customising the grid floor (holo-floor)
The floor provides the spatial anchor. If your dashboard uses a different brand palette than sky blue, adjust the floor grid colours.
Change the sky blue (rgba(56, 189, 248, 0.2)) to your preferred primary status or brand colour.
CSS
/* Customising the floor to a subtle green status grid */
.holo-floor {
background-image:
linear-gradient(rgba(16, 185, 129, 0.15) 1px, transparent 1px), /* Teal/Green */
linear-gradient(90deg, rgba(16, 185, 129, 0.15) 1px, transparent 1px);
}
3. Adjusting the mouse-tracking sensitivity (JS)
The 15 and -15 values in the JS determine how much the scene tilts in response to the mouse.
  • Increase these values (e.g., to 30) for an exaggerated, rapid "holographic" responsiveness.
  • Decrease these values (e.g., to 5) for a slower, more subtle spatial effect appropriate for high-traffic dashboards where excessive motion could become a distraction.
JS
/* JS sensitivity modification: Exaggerated spatial tilt */
const rotateX = (x - centerX) / centerX * 30; // Changed from 15
const rotateY = (y - centerY) / centerY * -30; // Changed from -15

By prioritising information and adjusting depth and responsiveness, you ensure the holographic 3D display is a functional tool for situational awareness, not merely a distracting effect.

The colour gradient banner header

A full-width header block featuring a gradient background and a call-to-action button. Useful for anchoring intranet homepages and driving users to key resources.
A colour gradient banner header with a heading and a button, made with an HTML macro from Mosaic in Confluence.
<div class="ms-component-wrapper">
<section class="ms-hero-banner">
<div class="ms-hero-content">
<div class="ms-hero-text-group">
<h1 class="ms-hero-headline">
Empowering Innovation Across the Enterprise
</h1>
<p class="ms-hero-subheading">
Discover the tools and resources driving our global digital
transformation.
</p>
</div>
<div class="ms-hero-action">
<a href="#" class="ms-hero-cta">Get Started Today</a>
</div>
</div>
</section>
</div>

.ms-component-wrapper .ms-hero-banner {
display: flex;
align-items: center;
justify-content: center;
min-height: 320px;
padding: 40px 24px;
border-radius: 8px;
overflow: hidden;
position: relative;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
background-color: #0a192f;
background-image: 
radial-gradient(circle at 15% 20%, rgba(100, 255, 218, 0.45) 0%, transparent 70%),
radial-gradient(circle at 85% 80%, rgba(189, 147, 249, 0.45) 0%, transparent 70%);
}
.ms-component-wrapper .ms-hero-content {
display: flex;
flex-direction: column;
gap: 32px;
width: 100%;
max-width: 1100px;
z-index: 1;
}
.ms-component-wrapper .ms-hero-text-group {
flex: 1;
}
.ms-component-wrapper .ms-hero-headline {
color: #ffffff;
font-size: 2.5rem;
font-weight: 800;
line-height: 1.1;
margin: 0 0 16px 0;
letter-spacing: -0.02em;
}
.ms-component-wrapper .ms-hero-subheading {
color: rgba(255, 255, 255, 0.85);
font-size: 1.125rem;
line-height: 1.5;
margin: 0;
}
.ms-component-wrapper .ms-hero-action {
display: flex;
align-items: center;
}
.ms-component-wrapper .ms-hero-cta {
display: inline-block;
background-color: #64ffda;
color: #0a192f;
padding: 16px 32px;
border-radius: 4px;
font-weight: 700;
text-decoration: none;
font-size: 1rem;
transition: transform 0.2s ease, box-shadow 0.2s ease, background

window.addEventListener("DOMContentLoaded", function () {
var wrappers = document.querySelectorAll(".ms-component-wrapper");
wrappers.forEach(function (wrapper) {
var cta = wrapper.querySelector(".ms-hero-cta");
if (cta) {
cta.addEventListener("click", function (e) {
console.log("Hero CTA clicked within instance:", wrapper);
});
}
});
});

How to customise the colour gradient banner header
Because we had to hardcode values to survive the sanitiser, you will need to make updates directly within the respective tabs.
Update the <h1> (headline) and <p> (subheading) tags to match your campaign or portal name.

Change the button text inside the <a class="ms-hero-cta"> tag.
Locate the <a href="#"...> tag and replace the # with your target URL.
Background base: Find background-color: #0a192f; under .ms-hero-banner and swap the hex code.
Gradient blobs: Modify the rgba() values within the radial-gradient lines to change the teal and purple mesh effects.
Button: Find background-color: #64ffda; under .ms-hero-cta (and its hover state counterpart below it) to update the button colour.
Adjust min-height: Edit 320px; under .ms-hero-banner if you need the banner to be taller or shorter on the page.

The masonry image gallery

A fluid, interlocking image grid that automatically calculates dimensions to prevent cropping. Useful for displaying team off-sites, product mockups, or event photos.
A masonry image gallery, displaying 9 images stacked together, made with an HTML macro from Mosaic in Confluence.
<div class="ms-component-wrapper">
<div class="ms-gallery-header">
<h2 class="ms-gallery-title">Team Offsite &amp; Events</h2>
<span class="ms-gallery-subtitle">A Look Back at Q3</span>
</div>
<div class="ms-masonry-grid">
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1522071820081-009f0129c71c?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Team workshop">
</div>
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1528605248644-14dd04022da1?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Team dinner">
</div>
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1517048676732-d65bc937f952?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Collaborative meeting">
</div>
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1523240795612-9a054b0db644?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Team laughing">
</div>
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1556761175-b413da4baf72?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Office event">
</div>
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1542744173-8e7e53415bb0?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Strategy session">
</div>
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1511632765486-a01980e01a18?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Social gathering">
</div>
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1515187029135-18ee286d815b?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Business workshop">
</div>
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1552664730-d307ca884978?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Presentation">
</div>
<div class="ms-masonry-item">
<img src="https://images.unsplash.com/photo-1543269865-cbf427effbad?auto=format&amp;fit=crop&amp;w=800&amp;q=80" alt="Outdoor team building">
</div>
</div>
</div>

600;800&display=swap");
.ms-component-wrapper {
font-family:
"Inter",
system-ui,
-apple-system,
sans-serif;
padding: 32px;
background: #ffffff;
border-radius: 16px;
border: 1px solid #e2e8f0;
max-width: 1200px;
margin: 0 auto;
}
.ms-component-wrapper .ms-gallery-header {
margin-bottom: 32px;
text-align: center;
}
.ms-component-wrapper .ms-gallery-title {
margin: 0 0 12px 0;
font-size: 28px;
font-weight: 800;
color: #0f172a;
letter-spacing: -0.5px;
}
.ms-component-wrapper .ms-gallery-subtitle {
font-size: 13px;
font-weight: 700;
color: #64748b;
text-transform: uppercase;
letter-spacing: 1.5px;
background: #f8fafc;
padding: 6px 16px;
border-radius: 100px;
display: inline-block;
}
.ms-component-wrapper .ms-masonry-grid {
column-count: 1;
column-gap: 20px;
width: 100%;
}
@media (min-width: 600px) {
.ms-component-wrapper .ms-masonry-grid {
column-count: 2;
}
}
@media (min-width: 900px) {
.ms-component-wrapper .ms-masonry-grid {
column-count: 3;
}
}
.ms-component-wrapper .ms-masonry-item {
break-inside: avoid;
margin-bottom: 20px;
border-radius: 16px;
overflow: hidden;
position: relative;
background: #f1f5f9;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.05),
0 2px 4px -2px rgba(0, 0, 0, 0.05);
transition:
transform 0.3s ease,
box-shadow 0.3s ease;
}
.ms-component-wrapper .ms-masonry-item:hover {
transform: translateY(-4px);
box-shadow:
0 20px 25px -5px rgba(0, 0, 0, 0.1),
0 8px 10px -6px rgba(0, 0, 0, 0.05);
}
.ms-component-wrapper .ms-masonry-item img {
display: block;
width: 100%;
height: auto;
object-fit: cover;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.ms-component-wrapper .ms-masonry-item:hover img {
transform: scale(1.03);
}

How to customise the masonry image gallery
Change the titles: Find the text Team Offsite & Events inside the <h2 class="ms-gallery-title"> tags and replace it with your own heading. Do the same for the subtitle just below it.
Remove the header entirely: If you just want a pure image grid without a title, simply delete the entire <div class="ms-gallery-header">...</div> block. The grid will naturally slide up to fill the space.
Add more images: To expand the gallery, copy an entire image block (from <div class="ms-masonry-item"> to its closing </div>) and paste it directly beneath the last one. You can add as many as you like; the grid will automatically calculate where to put them.

Update the images:
Replace the placeholder src="..." URLs with the actual links to your company's images.
Change the number of columns: By default, desktop screens show 3 columns. To make the images smaller and fit 4 columns, scroll to the bottom of the CSS tab to find @media (min-width: 900px). Change column-count: 3; to column-count: 4;.
Adjust the image spacing: If you want tighter or wider gaps between your photos, you need to change two numbers. Find .ms-masonry-grid and change column-gap: 20px;. Then, find .ms-masonry-item and change margin-bottom: 20px; to match your new number perfectly.
Change the subtitle colours: To match your company branding, find .ms-gallery-subtitle. Change the color: #64748b; (the text colour) and the background: #f8fafc; (the pill shape colour) to your brand's hex codes.
Tweak the hover zoom: When a user hovers over an image, it slightly zooms in to feel premium. Find .ms-masonry-item:hover img at the very bottom. Change transform: scale(1.03); to scale(1.1); for a dramatic 10% zoom, or scale(1.0); to turn the zoom off entirely.

The release countdown timer

A dynamic countdown clock that displays days, hours, minutes, and seconds until a specified date. Useful for tracking product launches, IT migrations, or company-wide events.
The release countdown timer, displaying numbers labelled days, hours, etc on a dark background, made with an HTML macro from Mosaic in Confluence.
<div class="ms-component-wrapper">
<div class="ms-countdown-card">
<div class="ms-countdown-header">
<h2 class="ms-countdown-title">Product Go-Live</h2>
<span class="ms-countdown-subtitle">Target: September 20th, 2026</span>
</div>
<div class="ms-time-grid">
<div class="ms-time-block">
<span class="ms-time-number ms-val-days">00</span>
<span class="ms-time-label">Days</span>
</div>
<div class="ms-time-divider">:</div>
<div class="ms-time-block">
<span class="ms-time-number ms-val-hours">00</span>
<span class="ms-time-label">Hours</span>
</div>
<div class="ms-time-divider">:</div>
<div class="ms-time-block">
<span class="ms-time-number ms-val-minutes">00</span>
<span class="ms-time-label">Minutes</span>
</div>
<div class="ms-time-divider ms-divider-mobile-hide">:</div>
<div class="ms-time-block">
<span class="ms-time-number ms-val-seconds">00</span>
<span class="ms-time-label">Seconds</span>
</div>
</div>
<div class="ms-completion-message" style="display: none; ">
🎉 The launch window has arrived!
</div>
</div>
</div>

600;800&display=swap");
.ms-component-wrapper {
font-family:
"Inter",
system-ui,
-apple-system,
sans-serif;
padding: 24px;
max-width: 800px;
margin: 0 auto;
}
.ms-component-wrapper .ms-countdown-card {
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
border-radius: 24px;
padding: 40px;
text-align: center;
color: #ffffff;
box-shadow:
0 20px 25px -5px rgba(0, 0, 0, 0.1),
0 8px 10px -6px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.1);
position: relative;
overflow: hidden;
}
.ms-component-wrapper .ms-countdown-card::before {
content: "";
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(
circle,
rgba(56, 189, 248, 0.15) 0%,
transparent 60%
);
z-index: 0;
pointer-events: none;
}
.ms-component-wrapper .ms-countdown-header,
.ms-component-wrapper .ms-time-grid,
.ms-component-wrapper .ms-completion-message {
position: relative;
z-index: 1; 
}
.ms-component-wrapper .ms-countdown-title {
font-size: 32px;
font-weight: 800;
margin: 0 0 12px 0;
letter-spacing: -0.5px;
}
.ms-component-wrapper .ms-countdown-subtitle {
font-size: 14px;
font-weight: 600;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: 1px;
}
.ms-component-wrapper .ms-time-grid {
display: flex;
justify-content: center;
align-items: center;
gap: 16px;
margin-top: 40px;
}
.ms-component-wrapper .ms-time-block {
display: flex;
flex-direction: column;
align-items: center;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 20px 24px;
min-width: 100px;
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.ms-component-wrapper .ms-time-number {
font-size: 48px;
font-weight: 800;
line-height: 1;
color: #38bdf8;
margin-bottom: 8px;
font-variant-numeric: tabular-nums; 
}
.ms-component-wrapper .ms-time-label {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
color: #cbd5e1;
}
.ms-component-wrapper .ms-time-divider {
font-size: 40px;
font-weight: 800;
color: rgba(255, 255, 255, 0.2);
margin-top: -24px; 
}
.ms-component-wrapper .ms-completion-message {
margin-top: 40px;
font-size: 24px;
font-weight: 800;
color: #22c55e;
background: rgba(34, 197, 94, 0.1);
padding: 16px 24px;
border-radius: 12px;
border: 1px solid rgba(34, 197, 94, 0.2);
}
@media (max-width: 600px) {
.ms-component-wrapper .ms-time-grid {
flex-wrap: wrap;
gap: 8px;
}
.ms-component-wrapper .ms-time-block {
min-width: 80px;
padding: 16px;
}
.ms-component-wrapper .ms-time-number {
font-size: 32px;
}
.ms-component-wrapper .ms-time-divider {
display: none;
}
}

function initCountdownTimer() {
var wrappers = document.querySelectorAll(".ms-component-wrapper");
wrappers.forEach(function (wrapper) {
var targetDateString = "2026-09-20T08:00:00";
var targetDate = new Date(targetDateString).getTime();
if (isNaN(targetDate)) {
console.warn("Mosaic Countdown: Invalid date format provided.");
return;
}
var daysEl = wrapper.querySelector(".ms-val-days");
var hoursEl = wrapper.querySelector(".ms-val-hours");
var minutesEl = wrapper.querySelector(".ms-val-minutes");
var secondsEl = wrapper.querySelector(".ms-val-seconds");
var gridEl = wrapper.querySelector(".ms-time-grid");
var completionEl = wrapper.querySelector(".ms-completion-message");
var subtitleEl = wrapper.querySelector(".ms-countdown-subtitle");
if (subtitleEl) {
var dateObj = new Date(targetDateString);
var formattedDate = dateObj.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
subtitleEl.textContent = "TARGET: " + formattedDate.toUpperCase();
}
function padNumber(num) {
return num < 10 ? "0" + num : num;
}
function updateTimer() {
var now = new Date().getTime();
var distance = targetDate - now;
if (distance < 0) {
clearInterval(timerInterval);
if (gridEl) gridEl.style.display = "none";
if (completionEl) completionEl.style.display = "block";
return;
}
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
var hours = Math.floor(distance % (1000 * 60 * 60 * 24) / (1000 * 60 * 60));
var minutes = Math.floor(distance % (1000 * 60 * 60) / (1000 * 60));
var seconds = Math.floor(distance % (1000 * 60) / 1000);
if (daysEl) daysEl.textContent = padNumber(days);
if (hoursEl) hoursEl.textContent = padNumber(hours);
if (minutesEl) minutesEl.textContent = padNumber(minutes);
if (secondsEl) secondsEl.textContent = padNumber(seconds);
}
updateTimer();
var timerInterval = setInterval(updateTimer, 1000);
});
}
if (document.readyState === "loading") {
window.addEventListener("DOMContentLoaded", initCountdownTimer);
} else {
initCountdownTimer();
}

How to customise the release countdown timer
Open the JS tab and locate the targetDateString variable at the top. Change it to your exact launch date using the standard web format: YYYY-MM-DDTHH:MM:SS. For example, to count down to Christmas morning 2026, you would type '2026-12-25T08:00:00'.
In the HTML tab, look for Project Apollo Go-Live and replace it with your project's name. Directly below it, update the Target: text to reflect a human-readable version of your launch date.
By default, when the timer hits zero, the clock disappears and a success message appears. Scroll to the very bottom of the HTML tab to find 🎉 The launch window has arrived!. You can change this text to anything you like (e.g., "The new HR Portal is now live! Click here to login.").
The background: Find .ms-countdown-card and adjust the linear-gradient hex codes to match your company's primary colours.
The numbers: Find .ms-time-number and change the color: #38bdf8; to your preferred highlight colour.

The data doughnut chart

A static, CSS-rendered doughnut chart for data visualisation. Useful for displaying budget allocations, resource distribution, or project phases directly on the page.
The data doughnut chart in green, blue, and grey, made with an HTML macro from Mosaic in Confluence.
<div class="donut-wrapper">
<div class="donut-card">
<h3>Lorem Ipsum Allocation</h3>
<div class="donut-chart-container">
<div class="donut-chart">
<div class="donut-hole">
<span class="donut-number">82%</span>
<span class="donut-label">Completed</span>
</div>
</div>
<ul class="donut-legend">
<li>
<span class="dot" style="background: rgb(99, 102, 241); background-color: rgb(99, 102, 241); "></span> Lorem (45%)
</li>
<li>
<span class="dot" style="background: rgb(16, 185, 129); background-color: rgb(16, 185, 129); "></span> Ipsum (37%)
</li>
<li>
<span class="dot" style="background: rgb(229, 231, 235); background-color: rgb(229, 231, 235); "></span> Dolor (18%)
</li>
</ul>
</div>
</div>
</div>

.donut-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 40px 20px;
display: flex;
justify-content: center;
}
.donut-card {
background: #fff;
padding: 32px;
border-radius: 20px;
border: 1px solid #e1e1e1;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
width: 100%;
max-width: 500px;
}
.donut-card h3 {
font-size: 18px;
color: #000;
margin: 0 0 24px 0;
}
.donut-chart-container {
display: flex;
align-items: center;
gap: 32px;
}
.donut-chart {
width: 140px;
height: 140px;
border-radius: 50%;
background: conic-gradient(#6366f1 0% 45%, #10b981 45% 82%, #e5e7eb 82% 100%);
display: flex;
align-items: center;
justify-content: center;
}
.donut-hole {
width: 100px;
height: 100px;
background: #fff;
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.donut-number {
font-size: 24px;
font-weight: 700;
color: #0f172a;
line-height: 1;
}
.donut-label {
font-size: 11px;
color: #64748b;
text-transform: uppercase;
margin-top: 4px;
}
.donut-legend {
list-style: none;
padding: 0;
margin: 0;
}
.donut-legend li {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: #475569;
margin-bottom: 12px;
}
.donut-legend .dot {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
}
@media (max-width: 480px) {
.donut-chart-container {
flex-direction: column;
}
}
How to customise the data doughnut chart
Because this chart uses zero JS, the "slices" of the pie are drawn using a CSS conic-gradient. To change the data, you have to do a little bit of basic math to set the "start" and "stop" points for each colour.
Example: If your data is 40% Blue, 35% Green, and 25% Grey.
  1. Blue slice: Starts at 0% and ends at 40%.
  2. Green slice: Starts at 40% and ends at 75% (40 + 35 = 75).
  3. Grey slice: Starts at 75% and ends at 100% (75 + 25 = 100).
Think of it like a relay race: where one colour ends, the next colour must begin. All your slices added together must equal 100%.
Let’s say you want to expand the chart from 3 items to 4 items with the following data and custom colours:
  1. Brand blue: 30%
  2. Brand purple: 20%
  3. Brand green: 35%
  4. Brand orange: 15%
To write the CSS, keep a running total of where each slice stops:
  • Blue starts at 0% and ends at 30%.
  • Purple starts at 30% and ends at 50% (30 + 20 = 50).
  • Green starts at 50% and ends at 85% (50 + 35 = 85).
  • Orange starts at 85% and ends at 100% (85 + 15 = 100).
Find the .donut-chart class in your CSS. You can add as many colours as you want, as long as you follow the [Colour] [Start%] [End%], format.
CSS
.donut-chart {
border-radius: 50%;
/* Add your custom hex colours and sequential percentages here */
background: conic-gradient(
#3B82F6 0% 30%, /* Blue: 0 to 30 */
#8B5CF6 30% 50%, /* Purple: 30 to 50 */
#10B981 50% 85%, /* Green: 50 to 85 */
#F59E0B 85% 100% /* Orange: 85 to 100 */
);
}

If you added a 4th colour to the CSS, you must add a 4th item to the legend in your HTML so your users know what the data means!
  1. Change the large centre text (.donut-number).
  2. Add a new <li> for your new data point.
  3. Update the style="background: #..." on each legend dot so it matches the exact hex codes you just used in your CSS.
HTML
<div class="donut-hole">
<span class="donut-number">30%</span>
<span class="donut-label">Blue Segment</span>
</div>
<ul class="donut-legend">
<li><span class="dot" style="background:#3B82F6;"></span> Brand Blue (30%)</li>
<li><span class="dot" style="background:#8B5CF6;"></span> Brand Purple (20%)</li>
<li><span class="dot" style="background:#10B981;"></span> Brand Green (35%)</li>
<li><span class="dot" style="background:#F59E0B;"></span> Brand Orange (15%)</li>
</ul>

The animated data doughnut chart

An animated version of the doughnut chart with a sweeping reveal and live number counter. Useful for displaying key completion metrics and reporting data.
An animated version of the doughnut chart, showing the chart fill up with colour, made with an HTML macro from Mosaic in Confluence.
<div class="animated-donut-wrapper">
<div class="donut-card">
<h3>Lorem Ipsum Allocation</h3>
<div class="donut-chart-container">
<div class="donut-chart">
<div class="donut-sweep"></div>
<div class="donut-hole">
<span class="donut-number"></span>
<span class="donut-label">Completed</span>
</div>
</div>
<ul class="donut-legend">
<li>
<span class="dot" style="background: rgb(59, 130, 246); background-color: rgb(59, 130, 246); "></span> Lorem (45%)
</li>
<li>
<span class="dot" style="background: rgb(16, 185, 129); background-color: rgb(16, 185, 129); "></span> Ipsum (37%)
</li>
<li>
<span class="dot" style="background: rgb(226, 232, 240); background-color: rgb(226, 232, 240); "></span> Dolor (18%)
</li>
</ul>
</div>
</div>
</div>

.animated-donut-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 40px 20px;
display: flex;
justify-content: center;
}
.donut-card {
background: #ffffff;
padding: 32px;
border-radius: 20px;
border: 1px solid #e5e7eb;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
width: 100%;
max-width: 500px;
}
.donut-card h3 {
font-size: 18px;
color: #0f172a;
margin: 0 0 24px 0;
}
.donut-chart-container {
display: flex;
align-items: center;
gap: 32px;
}
.donut-chart {
position: relative;
width: 140px;
height: 140px;
border-radius: 50%;
background: conic-gradient(#3b82f6 0% 45%, #10b981 45% 82%, #e2e8f0 82% 100%);
display: flex;
align-items: center;
justify-content: center;
}
@property --sweep {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
.donut-sweep {
position: absolute;
inset: 0;
border-radius: 50%;
background: conic-gradient(transparent var(--sweep), #ffffff var(--sweep));
animation: chartReveal 1.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes chartReveal {
from {
--sweep: 0deg;
}
to {
--sweep: 360deg;
}
}
.donut-hole {
position: relative;
z-index: 2; 
width: 100px;
height: 100px;
background: #ffffff;
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.02);
}
.donut-label {
font-size: 11px;
color: #64748b;
text-transform: uppercase;
margin-top: 4px;
font-weight: 600;
}
@property --num {
syntax: "<integer>";
initial-value: 0;
inherits: false;
}
.donut-number {
font-size: 24px;
font-weight: 700;
color: #0f172a;
line-height: 1;
counter-reset: percent var(--num);
animation: countUp 1.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.donut-number::after {
content: counter(percent) "%";
}
@keyframes countUp {
from {
--num: 0;
}
to {
--num: 82;
}
.donut-legend {
list-style: none;
padding: 0;
margin: 0;
z-index: 2;
}
.donut-legend li {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: #475569;
margin-bottom: 12px;
}
.donut-legend .dot {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
}
@media (max-width: 480px) {
.donut-chart-container {
flex-direction: column;
}
}

How to customise the animated data doughnut chart
Look for .donut-chart in the CSS and update the conic-gradient hex codes and percentages exactly as you did for the static version.
Look for @keyframes countUp at the bottom of the CSS. Change the to { --num: 82; } rule to match your main data point. If your target is 95%, change it to --num: 95;.
If you change the .donut-card background from #ffffff (white) to a dark colour, you must also change the #ffffff in the .donut-sweep and .donut-hole rules so the sweeping mask blends perfectly with the new card colour!

The interactive card collection


Whether you need a call-to-action, a sleek navigation menu, or a subtle way to highlight critical alerts, these eye-catching cards bring a level of polish to your Confluence pages. They all share the same core functions, acting as a header, breaking up text, introducing something new and so on; simply choose the animation style that best fits your layout.

The glass-effect panel card

A layered information card featuring a frosted-glass depth effect over a shifting background. Useful for framing executive summaries, mission statements, or strategic goals.
The glass-effect panel card on a dark background, made with an HTML macro from Mosaic in Confluence.
HTML
<div class="glass-wrapper">
<div class="glass-blob"></div>
<div class="glass-card">
<span class="glass-icon">✦</span>
<h3 class="glass-title">Lorem Ipsum Dolor</h3>
<p class="glass-text">
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore.
</p>
</div>
</div>

.glass-wrapper {
position: relative;
padding: 60px 20px;
background: #0b0f19;
border-radius: 20px;
overflow: hidden;
display: flex;
justify-content: center;
font-family:
system-ui,
-apple-system,
sans-serif;
}
.glass-blob {
position: absolute;
width: 250px;
height: 250px;
background: linear-gradient(135deg, #6366f1, #a855f7);
border-radius: 50%;
filter: blur(60px);
animation: blobFloat 8s infinite alternate ease-in-out;
}
@keyframes blobFloat {
0% {
transform: translate(-30px, -30px) scale(1);
}
100% {
transform: translate(30px, 30px) scale(1.1);
}
}
.glass-card {
position: relative;
z-index: 2;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 40px;
border-radius: 16px;
max-width: 500px;
text-align: center;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}
.glass-icon {
font-size: 32px;
color: #a855f7;
margin-bottom: 16px;
display: block;
}
.glass-title {
color: #fff;
font-size: 24px;
font-weight: 700;
margin-bottom: 12px;
margin-top: 0;
}
.glass-text {
color: #cbd5e1;
font-size: 15px;
line-height: 1.6;
margin: 0;
}

document.addEventListener('DOMContentLoaded', function () {
const highlighted = document.querySelectorAll('.highlighted');
highlighted.forEach(el => {
el.addEventListener('click', () => {
el.style.color = 'red';
});
});
});

How to customise the glass-effect panel card
Find the .glass-blob rule. The blob is just a blurred circle with a gradient.
CSS
.glass-blob {
/* Swap these two hex codes to change the blob's color */
background: linear-gradient(135deg, #FF5722, #FFC107);
}

Find the .glass-card rule. The background uses an RGBA colour where the last number 0.05 is the opacity (5%). Increase it to 0.1 or 0.2 for a more solid "frosted" look.
CSS
.glass-card {
/* Change 0.05 to 0.15 for more solid white frosting */
background: rgba(255, 255, 255, 0.05); 
/* Change 16px to 8px for less blur, or 24px for more blur */
backdrop-filter: blur(16px); 
}

The slide and fill action card

An interactive display card that floods with a solid colour from left to right on hover, instantly inverting the text colour for maximum contrast.
A gif of the slide and fill action card going from white to black, made with the HTML macro in Confluence.
<div class="hover-wrapper">
<div class="action-card">
<div class="action-icon">🚀</div>
<h3>Lorem Ipsum Process</h3>
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor.
</p>
</div>
</div>

.hover-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 40px 20px;
display: flex;
justify-content: center;
}
.action-card {
position: relative;
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 16px;
padding: 40px 30px;
max-width: 400px;
cursor: pointer;
overflow: hidden;
transition: box-shadow 0.3s ease;
}
.action-card::before {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 0%;
background: #0f172a; 
transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 0;
}
.action-card:hover::before {
width: 100%;
}
.action-card:hover {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.action-card > * {
position: relative;
z-index: 1;
transition: color 0.4s ease;
}
.action-icon {
font-size: 32px;
margin-bottom: 20px;
}
.action-card h3 {
font-size: 20px;
font-weight: 700;
color: #000;
margin: 0 0 12px 0;
}
.action-card p {
font-size: 14px;
color: #64748b;
margin: 0;
line-height: 1.6;
}
.action-card:hover h3,
.action-card:hover p {
color: #ffffff;
}

How to customise the slide and fill action card
Find the .action-card::before rule. This controls the invisible colour block that waits on the left side of the card before you hover over it.
CSS
.action-card::before {
/* Change this to the colour you want the card to become when hovered */
background: #0f172a; 
}

If your fill colour is dark, your text needs to turn white so people can read it. If your fill colour is light, your text should turn dark.
CSS
/* This flips the text colour when the user hovers over the card */
.action-card:hover h3, 
.action-card:hover p {
color: #ffffff; /* Change to #000000 if your fill background is light */
}

The border slide and lift card

A fun interactive card that lifts smoothly off the page on hover while a bright gradient border slides in along the bottom edge and the emoji tilts.
The border slide and lift card, made with the HTML macro in Confluence.
<div class="micro-wrapper">
<div class="micro-card">
<div class="micro-icon">📊</div>
<h3>Lorem Ipsum</h3>
<p>Interactive elements encourage user engagement.</p>
</div>
</div>

.micro-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 40px 20px;
}
.micro-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 30px;
max-width: 350px;
position: relative;
cursor: pointer;
overflow: hidden;
transition:
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.3s ease;
}
.micro-card:hover {
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
}
.micro-icon {
font-size: 32px;
margin-bottom: 16px;
display: inline-block;
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.micro-card:hover .micro-icon {
transform: scale(1.2) rotate(5deg);
}
.micro-card::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
height: 4px;
width: 0%;
background: linear-gradient(90deg, #3b82f6, #a855f7);
transition: width 0.4s ease;
}
.micro-card:hover::after {
width: 100%;
}
.micro-card h3 {
margin: 0 0 8px 0;
font-size: 18px;
color: #0f172a;
}
.micro-card p {
margin: 0;
font-size: 14px;
color: #64748b;
line-height: 1.5;
}

How to customise the border slide and lift card
Find .bs-card::after and alter the background gradient to match your corporate identity.
Delete transform: translateY(-8px); from the .bs-card:hover rule if you only want the border to slide.
In .bs-card:hover .bs-icon, change scale(1.2) to scale(1.5) for a massive zoom, or change rotate(5deg) to rotate(0deg) to stop it from tilting.

The icon scale card

Another eye-catching card that keeps the body completely stationary while the icon tilts and scales up without shifting your surrounding grid layout.
The icon scale card, made with the HTML macro in Confluence.
<div class="scale-wrapper">
<div class="scale-card">
<div class="scale-icon">⭐</div>
<h3>Lorum Ipsum &amp; Dolor</h3>
<p>
A stationary card interaction that won't disrupt tight CSS Grid layouts.
</p>
</div>
</div>

.scale-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 40px 20px;
}
.scale-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 30px;
max-width: 350px;
cursor: pointer;
transition: border-color 0.3s ease;
}
.scale-card:hover {
border-color: #8b5cf6; 
}
.scale-icon {
font-size: 32px;
margin-bottom: 16px;
display: inline-block;
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); 
}
.scale-card:hover .scale-icon {
transform: scale(1.4) rotate(-10deg);
}
.scale-card h3 {
margin: 0 0 8px 0;
font-size: 18px;
color: #0f172a;
}
.scale-card p {
margin: 0;
font-size: 14px;
color: #64748b;
line-height: 1.5;
}

How to customise the icon scale card
The transition: transform rule uses a custom cubic-bezier that forces the icon to scale up slightly past 100% and "bounce" back. Change it to ease for a standard, smooth zoom.
In .scale-card:hover .scale-icon, change rotate(-10deg) to rotate(360deg) to make the icon do a complete flip when hovered!

The solid colour fill card

An impactful card that floods rapidly with a solid block of colour from the bottom up on hover, creating a bold and immediate visual change.
The solid colour fill card, made with the HTML macro, filling up with colour on hover.
<div class="fill-wrapper">
<div class="fill-card">
<div class="fill-icon">🌊</div>
<h3 class="fill-title">Lorem Ipsum</h3>
<p class="fill-text">
Colour flood, a perfect for Call-to-Action blocks or highlighting active states.
</p>
</div>
</div>

.fill-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 40px 20px;
}
.fill-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 30px;
max-width: 350px;
position: relative;
cursor: pointer;
overflow: hidden;
z-index: 1;
}
.fill-card::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 100%;
background: #0ea5e9;
z-index: -1;
transform: scaleY(0);
transform-origin: bottom;
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.fill-card:hover::before {
transform: scaleY(1);
}
.fill-icon {
font-size: 32px;
margin-bottom: 16px;
}
.fill-title {
margin: 0 0 8px 0;
font-size: 18px;
color: #0f172a;
transition: color 0.4s ease;
}
.fill-text {
margin: 0;
font-size: 14px;
color: #077b6;
line-height: 1.5;
transition: color 0.4s ease;
}
.fill-card:hover .fill-title,
.fill-card:hover .fill-text {
color: #ffffff;
}

How to customise the solid colour fill card
In .fill-card::before, change transform-origin: bottom; to transform-origin: top; to make it fill downwards.
Update the blue background: #0ea5e9; to your preferred brand colour. Ensure it is dark enough that white text remains legible on it.

The rotating gradient border card

A static card that features a continuous, seamless conic-gradient border that slowly rotates around the card container.
The rotating gradient border card, made with the HTML macro in Confluence.
<div class="border-anim-wrapper">
<div class="anim-border-card">
<span class="anim-tag">Rotating Gradient</span>
<h3>Lorem Ipsum</h3>
<p>
A conic gradient border that rotates seamlessly using CSS mask-composite.
No JavaScript required.
</p>
</div>
</div>

.border-anim-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 40px 20px;
display: flex;
justify-content: center;
}
@property --angle {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
.anim-border-card {
position: relative;
background: #ffffff;
border-radius: 14px;
padding: 32px 24px;
max-width: 350px;
z-index: 1;
}
.anim-border-card::before {
content: "";
position: absolute;
inset: 0;
border-radius: 14px;
padding: 3px; 
background: conic-gradient(
from var(--angle, 0deg),
#3b82f6,
#8b5cf6,
#f59e0b,
#3b82f6
);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
z-index: -1;
animation: borderSpin 4s linear infinite;
}
@keyframes borderSpin {
to {
--angle: 360deg;
}
}
.anim-tag {
display: inline-block;
padding: 4px 10px;
background: #eff6ff;
color: #3b82f6;
border-radius: 100px;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
margin-bottom: 12px;
letter-spacing: 1px;
}
.anim-border-card h3 {
margin: 0 0 8px 0;
font-size: 18px;
color: #0f172a;
}
.anim-border-card p {
margin: 0;
font-size: 14px;
color: #64748b;
line-height: 1.5;
}

How to customise the rotating gradient border card
Look for background: conic-gradient(...) in the CSS. Swap out the hex codes.
Crucial rule: The first and last colours must be exactly the same (e.g., #3b82f6) so the loop is seamless!
Change padding: 3px; inside the .anim-border-card::before rule.
In animation: borderSpin 4s linear infinite;, lower the 4s to 2s for a faster spin.

The rotating light border card

Creates the illusion of a single, looping beam of coloured light travelling around the edge of the card.
The rotating light border card, showing an illusion of light moving around the border of a card.
<div class="animated-border-wrapper">
<div class="animated-card">
<div class="animated-card-inner">
<span class="animated-tag">SYSTEM STATUS</span>
<h3>Lorem Ipsum Sit Amet</h3>
<p>
Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut
labore et dolore magna.
</p>
</div>
</div>
</div>

.animated-border-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 40px 20px;
display: flex;
justify-content: center;
}
.animated-card {
position: relative;
width: 100%;
max-width: 450px;
border-radius: 16px;
padding: 2px; 
background: #e5e7eb;
overflow: hidden;
display: flex;
}
.animated-card::before {
content: "";
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: conic-gradient(
transparent,
transparent,
transparent,
#6366f1,
#10b981,
transparent
);
animation: borderSpin 4s linear infinite;
}
@keyframes borderSpin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.animated-card-inner {
position: relative;
background: #ffffff;
border-radius: 14px;
padding: 32px;
z-index: 1;
flex: 1;
display: flex;
flex-direction: column;
}
.animated-tag {
font-size: 11px;
font-weight: 700;
color: #6366f1;
letter-spacing: 1px;
display: block;
margin-bottom: 12px;
}
.animated-card-inner h3 {
font-size: 20px;
margin: 0 0 12px 0;
color: #0f172a;
}
.animated-card-inner p {
font-size: 14px;
color: #64748b;
margin: 0;
line-height: 1.6;
}

How to customise the rotating light border card
The effect is created by a rotating background layer that peeks through a thin margin. Use these tips to tweak the movement and colours.
The thickness of the glowing line is controlled by the padding on the .animated-card class.
Subtle: Set padding to 1px or 2px.
Bold: Increase padding to 5px or 8px for a chunky neon look.
Locate the conic-gradient inside the .animated-card::before rule. This list determines which colours "orbit" the card.
The comet effect: Start and end your list with transparent to create a single beam of light that fades in and out.
The full ring: Remove the transparent entries and list 3–4 solid colours to create a continuous, multi-coloured glowing border.
Inside the .animated-card::before rule, find the animation property (e.g., borderSpin 4s linear infinite).
High energy: Change 4s to 1s or 2s for a rapid, high-tech pulse.
Elegant glow: Change 4s to 10s or 15s for a very slow, premium ambient rotation.
To ensure the animation is perfectly smooth, always ensure your gradient has at least two "stops" of the same colour at the start. This prevents the light from appearing to "jump" when the 360-degree rotation resets.

The ambient glow card

An interactive card that sits quietly in dark mode until hovered over, triggering a deep, layered radial shadow that mimics a physical, intensifying glow.
A gif of the ambient glow card, showing a card lift and the border light up when hovered over by the mouse.
<div class="glow-panel-wrapper">
<div class="glow-panel">
<span class="glow-tag">Lorem Ipsum</span>
<h3>Intensifying Glow</h3>
<p>
Layered box-shadow values in the brand color create a realistic aura that
intensifies on hover.
</p>
</div>
</div>

.glow-panel-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 60px 20px;
display: flex;
justify-content: center;
background: #0f172a; 
}
.glow-panel {
background: #1e293b;
border-radius: 14px;
padding: 32px 24px;
max-width: 350px;
cursor: pointer;
box-shadow:
0 0 0 1px rgba(13, 148, 136, 0.2),
0 0 30px rgba(13, 148, 136, 0.15),
0 0 60px rgba(13, 148, 136, 0.05); 
transition:
box-shadow 0.4s ease,
transform 0.4s ease;
}
.glow-panel:hover {
transform: translateY(-4px);
box-shadow:
0 0 0 1px rgba(13, 148, 136, 0.5),
0 0 40px rgba(13, 148, 136, 0.4),
0 0 80px rgba(13, 148, 136, 0.2); 
}
.glow-tag {
display: inline-block;
padding: 4px 10px;
background: rgba(13, 148, 136, 0.15);
color: #2dd4bf;
border-radius: 100px;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
margin-bottom: 12px;
letter-spacing: 1px;
}
.glow-panel h3 {
margin: 0 0 8px 0;
font-size: 18px;
color: #ffffff;
}
.glow-panel p {
margin: 0;
font-size: 14px;
color: #94a3b8;
line-height: 1.5;
}

How to customise the ambient glow effect card
The glow uses RGBA colours. Currently, it uses 13, 148, 136 (teal).
Find the RGB values for your brand colour and replace those three numbers across all the box-shadow rules. Leave the last decimal (e.g., 0.2 or 0.5) exactly as it is—that controls the fading opacity!

The light sweep card

A minimalist static card with a subtle white gradient that sweeps continuously across the card on a loop, mimicking the reflection of light off a polished surface.
A gif of the light sweep card, showing the glossy effect in action.
<div class="shimmer-panel-wrapper">
<div class="shimmer-card">
<span class="shimmer-tag">Light Sweep Animation</span>
<h3>Lorem &amp; Ipsum</h3>
<p>
A light sweeps across the card with a soft white gradient on a
loop, drawing attention to your nugget of information.
</p>
</div>
</div>

.shimmer-panel-wrapper {
font-family:
system-ui,
-apple-system,
sans-serif;
padding: 40px 20px;
display: flex;
justify-content: center;
}
.shimmer-card {
position: relative;
background: linear-gradient(
135deg,
#1e1b4b,
#312e81
); 
border-radius: 14px;
padding: 32px 24px;
max-width: 350px;
overflow: hidden; 
}
.shimmer-card::after {
content: "";
position: absolute;
top: -50%;
left: -60%;
width: 40%;
height: 200%;
background: linear-gradient(
105deg,
transparent 40%,
rgba(255, 255, 255, 0.15) 50%,
transparent 60%
);
animation: shimmerSweep 3s ease-in-out infinite;
z-index: 1; 
pointer-events: none; 
}
@keyframes shimmerSweep {
0% {
left: -60%;
}
100% {
left: 140%;
}
}
.shimmer-card > * {
position: relative;
z-index: 2;
}
.shimmer-tag {
display: inline-block;
padding: 4px 10px;
background: rgba(255, 255, 255, 0.1);
color: #c7d2fe;
border-radius: 100px;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
margin-bottom: 12px;
letter-spacing: 1px;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.shimmer-card h3 {
margin: 0 0 8px 0;
font-size: 18px;
color: #ffffff;
}
.shimmer-card p {
margin: 0;
font-size: 14px;
color: #a5b4fc;
line-height: 1.5;
}

How to customise the light sweep card
In animation: shimmerSweep 3s ease-in-out infinite;, change 3s to 1.5s for a rapid flash, or 6s for a very slow, lazy gleam.
In the .shimmer-card::after rule, change width: 40%; to width: 80%;.
In the linear-gradient, change rgba(255, 255, 255, 0.15) to 0.3 or 0.5 for a much harsher, brighter reflection.
Three Confluence macros appearing from a magician's top hat with a flourish

Your Confluence pages, but better

Give your Confluence pages the love they deserve. Discover Mosaic today and start building pages that users care about.
Written by
A headshot of Simon Kirrane
Simon Kirrane
Senior Content Marketing Manager
With a 20-year career in content marketing, Simon has represented a range of international brands. His current specialism is the future of work and work management. Simon is skilled at launching content pipelines, establishing powerful brands, and crafting innovative content strategies.