Miro / Whiteboard
Infinite canvas aesthetics — sticky notes, hand-drawn connectors, collaborative markers, and the feeling of a shared whiteboard.
MCP
Code
:root {
--wb-bg: #f5f5f0;
--wb-yellow: #ffed46;
--wb-pink: #ffc0cb;
--wb-blue: #b3e5fc;
--wb-green: #c8e6c9;
--wb-coral: #ffccbc;
--wb-text: #222;
--wb-text-muted: #555;
--font-hand: "Kalam", cursive;
--font-ui: "Inter", system-ui, sans-serif;
--sticky-shadow: 3px 4px 12px rgba(0, 0, 0, 0.15), 0 1px 3px rgba(0, 0, 0, 0.08);
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-ui);
background: var(--wb-bg);
min-height: 100vh;
overflow: auto;
}
/* ── Canvas ── */
.canvas {
position: relative;
min-height: 100vh;
padding: 40px 24px 80px;
/* Dot grid whiteboard surface */
background-image: radial-gradient(circle, #c8c8c0 1px, transparent 1px);
background-size: 30px 30px;
}
/* ── Frame label ── */
.frame-label {
font-family: var(--font-ui);
font-size: 12px;
font-weight: 600;
color: var(--wb-text-muted);
letter-spacing: 0.04em;
border: 2px solid rgba(0, 0, 0, 0.15);
border-radius: 6px;
display: inline-block;
padding: 6px 14px;
margin-bottom: 36px;
background: rgba(255, 255, 255, 0.7);
}
/* ── SVG connectors ── */
.connectors {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
overflow: visible;
}
/* ── Sticky note base ── */
.sticky {
position: relative;
padding: 20px;
box-shadow: var(--sticky-shadow);
border-radius: 2px;
z-index: 2;
}
.sticky--yellow {
background: var(--wb-yellow);
}
.sticky--pink {
background: var(--wb-pink);
}
.sticky--blue {
background: var(--wb-blue);
}
.sticky--green {
background: var(--wb-green);
}
.sticky--coral {
background: var(--wb-coral);
}
/* Push-pin decoration */
.sticky__pin {
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
width: 12px;
height: 12px;
background: radial-gradient(circle at 40% 35%, #f44 60%, #a00);
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
/* ── Layout clusters ── */
.sticky-cluster {
display: flex;
flex-wrap: wrap;
gap: 24px;
margin-bottom: 40px;
max-width: 700px;
}
.profile-sticky {
width: 260px;
min-height: 140px;
}
.stats-sticky {
width: 280px;
min-height: 100px;
}
.controls-cluster {
display: flex;
flex-wrap: wrap;
gap: 24px;
margin-bottom: 40px;
max-width: 800px;
}
.buttons-sticky {
width: 220px;
}
.input-sticky {
width: 260px;
}
.tags-sticky {
width: 240px;
}
/* ── Profile card content ── */
.profile-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.avatar-sticky {
flex-shrink: 0;
border-radius: 4px;
overflow: hidden;
box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.1);
}
.profile-name {
font-family: var(--font-hand);
font-size: 19px;
font-weight: 700;
color: var(--wb-text);
line-height: 1.2;
}
.profile-role {
font-family: var(--font-hand);
font-size: 14px;
color: var(--wb-text-muted);
margin-top: 2px;
}
.sticky-note-label {
font-family: var(--font-hand);
font-size: 15px;
color: var(--wb-text-muted);
border-top: 1.5px dashed rgba(0, 0, 0, 0.15);
padding-top: 10px;
margin-top: 4px;
}
/* ── Stats sticky ── */
.sticky-heading {
font-family: var(--font-hand);
font-size: 15px;
font-weight: 700;
color: var(--wb-text-muted);
margin-bottom: 12px;
letter-spacing: 0.02em;
}
.stat-row {
display: flex;
gap: 16px;
flex-wrap: wrap;
}
.stat-item {
font-family: var(--font-hand);
font-size: 20px;
font-weight: 700;
color: var(--wb-text);
display: flex;
flex-direction: column;
gap: 0;
}
.stat-item small {
font-size: 12px;
font-weight: 400;
color: var(--wb-text-muted);
}
/* ── Buttons ── */
.button-col {
display: flex;
flex-direction: column;
gap: 10px;
}
.btn {
font-family: var(--font-hand);
font-size: 16px;
font-weight: 700;
padding: 9px 16px;
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease;
text-align: left;
}
.btn--marker {
background: white;
color: var(--wb-text);
border: 3px solid var(--wb-text);
border-radius: 4px;
box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.12);
}
.btn--marker:hover {
transform: translate(-1px, -1px);
box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.15);
}
.btn--blue-sticky {
background: var(--wb-blue);
color: #01579b;
border: 3px solid rgba(1, 87, 155, 0.3);
border-radius: 4px;
box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.1);
}
.btn--blue-sticky:hover {
transform: translate(-1px, -1px);
box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.12);
}
.btn--ghost-marker {
background: transparent;
color: var(--wb-text-muted);
border: none;
font-family: var(--font-hand);
font-size: 16px;
text-decoration: underline;
text-underline-offset: 3px;
padding: 8px 4px;
}
.btn--ghost-marker:hover {
color: var(--wb-text);
}
/* ── Textarea input ── */
.board-input {
font-family: var(--font-hand);
font-size: 15px;
width: 100%;
padding: 8px 10px;
border: none;
border-bottom: 2px solid rgba(0, 0, 0, 0.2);
background: transparent;
color: var(--wb-text);
outline: none;
resize: none;
line-height: 1.5;
}
.board-input::placeholder {
color: rgba(0, 0, 0, 0.4);
font-style: italic;
}
.board-input:focus {
border-bottom-color: rgba(0, 0, 0, 0.5);
}
/* ── Badges ── */
.badge-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.badge {
font-family: var(--font-hand);
font-size: 13px;
font-weight: 700;
padding: 4px 12px;
border-radius: 3px;
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.badge:hover {
transform: rotate(-2deg) scale(1.08);
}
.badge--yellow {
background: #fff176;
color: #f57f17;
border: 1.5px solid rgba(245, 127, 23, 0.3);
}
.badge--blue {
background: #81d4fa;
color: #01579b;
border: 1.5px solid rgba(1, 87, 155, 0.2);
}
.badge--pink {
background: #f48fb1;
color: #880e4f;
border: 1.5px solid rgba(136, 14, 79, 0.2);
}
.badge--green {
background: #a5d6a7;
color: #1b5e20;
border: 1.5px solid rgba(27, 94, 32, 0.2);
}
/* ── Draggable sticky ── */
.draggable-sticky {
position: absolute;
top: 320px;
right: 48px;
width: 200px;
cursor: grab;
user-select: none;
z-index: 10;
transition: box-shadow 0.15s ease;
}
.draggable-sticky:active {
cursor: grabbing;
box-shadow: 8px 12px 28px rgba(0, 0, 0, 0.25), 0 2px 6px rgba(0, 0, 0, 0.12);
}
.drag-label {
font-family: var(--font-hand);
font-size: 18px;
font-weight: 700;
color: var(--wb-text);
margin-bottom: 8px;
}
.drag-sub {
font-family: var(--font-hand);
font-size: 14px;
color: var(--wb-text-muted);
line-height: 1.4;
}/* Miro / Whiteboard — Interactive JS */
(function () {
"use strict";
// ── Draggable sticky note ──
const draggable = document.getElementById("draggableSticky");
if (draggable) {
let isDragging = false;
let startX = 0;
let startY = 0;
let origLeft = 0;
let origTop = 0;
function getPos() {
const style = window.getComputedStyle(draggable);
const rect = draggable.getBoundingClientRect();
const canvas = document.getElementById("canvas").getBoundingClientRect();
return {
left: rect.left - canvas.left,
top: rect.top - canvas.top,
};
}
draggable.addEventListener("mousedown", (e) => {
e.preventDefault();
isDragging = true;
const pos = getPos();
origLeft = pos.left;
origTop = pos.top;
startX = e.clientX;
startY = e.clientY;
draggable.style.position = "absolute";
draggable.style.left = origLeft + "px";
draggable.style.top = origTop + "px";
draggable.style.right = "auto";
draggable.style.transform = "rotate(-3deg) scale(1.03)";
draggable.style.zIndex = "100";
draggable.style.transition = "none";
});
document.addEventListener("mousemove", (e) => {
if (!isDragging) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
draggable.style.left = origLeft + dx + "px";
draggable.style.top = origTop + dy + "px";
// Update connectors while dragging
updateConnectors();
});
document.addEventListener("mouseup", () => {
if (!isDragging) return;
isDragging = false;
draggable.style.transform = "rotate(-3deg)";
draggable.style.zIndex = "10";
draggable.style.transition = "box-shadow 0.15s ease";
});
// Touch support
draggable.addEventListener(
"touchstart",
(e) => {
const touch = e.touches[0];
const pos = getPos();
origLeft = pos.left;
origTop = pos.top;
startX = touch.clientX;
startY = touch.clientY;
isDragging = true;
draggable.style.position = "absolute";
draggable.style.left = origLeft + "px";
draggable.style.top = origTop + "px";
draggable.style.right = "auto";
draggable.style.transition = "none";
e.preventDefault();
},
{ passive: false }
);
document.addEventListener(
"touchmove",
(e) => {
if (!isDragging) return;
const touch = e.touches[0];
const dx = touch.clientX - startX;
const dy = touch.clientY - startY;
draggable.style.left = origLeft + dx + "px";
draggable.style.top = origTop + dy + "px";
},
{ passive: true }
);
document.addEventListener("touchend", () => {
isDragging = false;
});
}
// ── SVG Connector lines ──
function getCenterRelativeToCanvas(el) {
const canvas = document.getElementById("canvas");
if (!canvas || !el) return { x: 0, y: 0 };
const canvasRect = canvas.getBoundingClientRect();
const elRect = el.getBoundingClientRect();
return {
x: elRect.left - canvasRect.left + elRect.width / 2,
y: elRect.top - canvasRect.top + elRect.height / 2,
};
}
function drawCurvedPath(x1, y1, x2, y2) {
const cx1 = x1 + (x2 - x1) * 0.4;
const cy1 = y1;
const cx2 = x1 + (x2 - x1) * 0.6;
const cy2 = y2;
return `M ${x1} ${y1} C ${cx1} ${cy1}, ${cx2} ${cy2}, ${x2} ${y2}`;
}
function updateConnectors() {
const conn1 = document.getElementById("conn1");
const conn2 = document.getElementById("conn2");
const profileCluster = document.getElementById("profileCluster");
const controlsCluster = document.getElementById("controlsCluster");
const draggableEl = document.getElementById("draggableSticky");
if (conn1 && profileCluster && controlsCluster) {
const p1 = getCenterRelativeToCanvas(profileCluster);
const p2 = getCenterRelativeToCanvas(controlsCluster);
conn1.setAttribute("d", drawCurvedPath(p1.x + 100, p1.y, p2.x - 80, p2.y));
}
if (conn2 && controlsCluster && draggableEl) {
const p1 = getCenterRelativeToCanvas(controlsCluster);
const p2 = getCenterRelativeToCanvas(draggableEl);
conn2.setAttribute("d", drawCurvedPath(p1.x + 80, p1.y, p2.x - 60, p2.y));
}
}
// Initial draw + update on resize
updateConnectors();
window.addEventListener("resize", updateConnectors);
// ── Sticker hover pop ──
document.querySelectorAll(".sticky").forEach((sticky) => {
if (sticky.id === "draggableSticky") return;
sticky.addEventListener("mouseenter", () => {
const rot = parseFloat(sticky.style.transform.replace(/[^-\d.]/g, "") || "0");
sticky.style.transition =
"transform 0.2s cubic-bezier(0.34,1.56,0.64,1), box-shadow 0.2s ease";
sticky.style.transform = `rotate(${rot * 0.5}deg) scale(1.03)`;
sticky.style.boxShadow = "6px 8px 20px rgba(0,0,0,.2)";
sticky.style.zIndex = "5";
});
sticky.addEventListener("mouseleave", () => {
const rot = sticky.dataset.origRot || sticky.style.transform;
sticky.style.transform = sticky.dataset.origTransform || sticky.style.transform;
sticky.style.boxShadow = "";
sticky.style.zIndex = "2";
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Kalam:wght@400;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
<title>Miro / Whiteboard — Design Style</title>
</head>
<body>
<!-- Whiteboard canvas area -->
<div class="canvas" id="canvas">
<!-- Board title label (like a Miro frame label) -->
<div class="frame-label">📋 Design System — Whiteboard Style</div>
<!-- SVG connector lines -->
<svg class="connectors" id="connectors" aria-hidden="true">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#555" opacity="0.6"/>
</marker>
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#1976D2" opacity="0.7"/>
</marker>
</defs>
<!-- These paths will be updated by JS to connect elements -->
<path class="connector-line" id="conn1" d="" stroke="#555" stroke-width="2" fill="none" marker-end="url(#arrow)" opacity="0.5" stroke-dasharray="6,4"/>
<path class="connector-line" id="conn2" d="" stroke="#1976D2" stroke-width="2" fill="none" marker-end="url(#arrow-blue)" opacity="0.45" stroke-dasharray="6,4"/>
</svg>
<!-- Profile sticky note cluster -->
<div class="sticky-cluster" id="profileCluster">
<!-- Main profile sticky note -->
<div class="sticky sticky--yellow profile-sticky" style="transform: rotate(-1.5deg)">
<div class="sticky__pin" aria-hidden="true"></div>
<div class="profile-row">
<!-- SVG avatar on a sticky -->
<div class="avatar-sticky">
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
<rect width="48" height="48" rx="4" fill="#FFF9C4"/>
<circle cx="24" cy="18" r="9" fill="#FFA000"/>
<path d="M6 44c0-9.941 7.163-18 18-18s18 8.059 18 18" fill="#FFA000" opacity="0.6"/>
<circle cx="20" cy="18" r="2" fill="white"/>
<circle cx="28" cy="18" r="2" fill="white"/>
<path d="M19 24 Q24 28 29 24" stroke="white" stroke-width="2" stroke-linecap="round" fill="none"/>
</svg>
</div>
<div>
<p class="profile-name">Sam Boards</p>
<p class="profile-role">Product Designer</p>
</div>
</div>
<div class="sticky-note-label">👋 Hi! I'm Sam</div>
</div>
<!-- Stats as individual stickies -->
<div class="sticky sticky--blue stats-sticky" style="transform: rotate(2deg)" id="statSticky">
<div class="sticky__pin" aria-hidden="true"></div>
<p class="sticky-heading">📊 Stats</p>
<div class="stat-row">
<span class="stat-item">126 <small>boards</small></span>
<span class="stat-item">4.9 <small>rating</small></span>
<span class="stat-item">8.4k <small>followers</small></span>
</div>
</div>
</div>
<!-- Controls sticky cluster -->
<div class="controls-cluster" id="controlsCluster">
<!-- Buttons sticky note -->
<div class="sticky sticky--pink buttons-sticky" style="transform: rotate(1deg)">
<div class="sticky__pin" aria-hidden="true"></div>
<p class="sticky-heading">🖱️ Actions</p>
<div class="button-col">
<button class="btn btn--marker">✓ Confirm</button>
<button class="btn btn--blue-sticky">✦ Explore</button>
<button class="btn btn--ghost-marker">→ View more</button>
</div>
</div>
<!-- Input sticky note -->
<div class="sticky sticky--green input-sticky" style="transform: rotate(-1.5deg)">
<div class="sticky__pin" aria-hidden="true"></div>
<p class="sticky-heading">💬 Add a note</p>
<textarea class="board-input" id="boardInput" placeholder="Write your idea here..." rows="3"></textarea>
</div>
<!-- Badges / flags sticky note -->
<div class="sticky sticky--yellow tags-sticky" style="transform: rotate(1.5deg)">
<div class="sticky__pin" aria-hidden="true"></div>
<p class="sticky-heading">🏷️ Tags</p>
<div class="badge-row">
<span class="badge badge--yellow">Miro</span>
<span class="badge badge--blue">Canvas</span>
<span class="badge badge--pink">Collab</span>
<span class="badge badge--green">Sticky</span>
</div>
</div>
</div>
<!-- Draggable sticky note -->
<div class="sticky sticky--coral draggable-sticky" id="draggableSticky" style="transform: rotate(-3deg)">
<div class="sticky__pin" aria-hidden="true"></div>
<p class="drag-label">✋ Drag me!</p>
<p class="drag-sub">This sticky note is moveable around the board.</p>
</div>
</div>
<script src="script.js"></script>
</body>
</html>Miro / Whiteboard
The whiteboard aesthetic captures the collaborative energy of digital canvas tools like Miro, FigJam, and Mural. Everything feels placed by hand on a physical surface — sticky notes cluster at slight angles, elements have imperfect margins and shadows, and SVG connector lines join elements like drawn arrows. The off-white background with a subtle dot grid perfectly mimics the surface of a premium whiteboard.
Sticky notes are the core building block: colorful squares (#FFED46 yellow, #FFC0CB pink, #B3E5FC blue, #C8E6C9 green) in slightly rotated clusters, each with the characteristic soft drop shadow and thick marker-weight content. Typography mixes ’Kalam’(a handwriting font) for note content with’Inter’` for UI chrome, creating the authentic mix of hand-written notes and digital interface.
The interactive signature of this style is draggability — users can grab a sticky note and drag it around the canvas, repositioning it as they would on a real board. SVG connector lines drawn between elements complete the spatial composition metaphor.
Key characteristics
- Off-white
#F5F5F0dot-grid background simulating a whiteboard surface - Colorful sticky notes with
rotate()transforms and soft box-shadows 'Kalam'handwriting font for note content- SVG connector lines with arrowheads drawn between card elements
- One fully draggable sticky note via mouse/touch events
- Marker-style button with imperfect hand-drawn shadow
When to use it
The Miro style is ideal for collaborative tools, design process documentation, project planning interfaces, retrospective boards, and brainstorming applications. Its casual, tactile quality lowers the perceived formality of the interface, encouraging participation and experimentation.