Interior — Process Timeline
A warm editorial process timeline that walks clients through five studio phases, from discovery to install. Numbered markers ride a thin vertical rule with clay connectors, each step opening a card of duration, deliverables, and a short narrative. Click a marker to expand its detail, track a live progress meter, and toggle a compact overview — all keyboard friendly with visible focus, muted natural tones, and generous serif-led whitespace.
MCP
Code
:root {
--bg: #f6f2ec;
--paper: #fbf9f5;
--ink: #2c2620;
--ink-2: #5a5147;
--muted: #8a8175;
--clay: #b08968;
--clay-d: #8c6a4f;
--walnut: #5c4433;
--sage: #9caf88;
--line: rgba(44, 38, 32, 0.12);
--white: #fff;
--r-sm: 4px;
--r-md: 8px;
--r-lg: 14px;
--shadow: 0 18px 40px -24px rgba(44, 38, 32, 0.4);
}
* {
box-sizing: border-box;
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, sans-serif;
line-height: 1.5;
padding: clamp(1.25rem, 4vw, 3.5rem) 1rem 3rem;
}
.wrap {
max-width: 760px;
margin: 0 auto;
}
/* ---------- Masthead ---------- */
.masthead {
margin-bottom: 2.75rem;
}
.eyebrow {
font-size: 0.72rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--clay-d);
font-weight: 600;
margin: 0 0 0.75rem;
}
.masthead h1 {
font-family: "Cormorant Garamond", Georgia, serif;
font-weight: 500;
font-size: clamp(2.1rem, 6vw, 3.1rem);
line-height: 1.08;
letter-spacing: 0.01em;
margin: 0 0 0.75rem;
color: var(--ink);
}
.lede {
max-width: 46ch;
color: var(--ink-2);
font-size: 1.02rem;
margin: 0 0 1.75rem;
}
/* ---------- Progress ---------- */
.progress {
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 1rem 1.15rem;
box-shadow: var(--shadow);
}
.progress__head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
margin-bottom: 0.7rem;
}
.progress__label {
font-size: 0.9rem;
color: var(--ink-2);
}
.progress__label strong {
color: var(--walnut);
}
.progress__pct {
font-family: "Cormorant Garamond", serif;
font-size: 1.6rem;
font-weight: 600;
color: var(--clay-d);
line-height: 1;
}
.progress__track {
height: 6px;
background: rgba(92, 68, 51, 0.14);
border-radius: 999px;
overflow: hidden;
}
.progress__bar {
height: 100%;
border-radius: 999px;
background: linear-gradient(90deg, var(--clay), var(--walnut));
transition: width 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}
/* ---------- Controls ---------- */
.controls {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
margin-top: 1.25rem;
}
.btn {
font: inherit;
font-weight: 600;
font-size: 0.85rem;
border-radius: 999px;
padding: 0.5rem 1.1rem;
cursor: pointer;
border: 1px solid transparent;
transition: background 0.2s, color 0.2s, border-color 0.2s, transform 0.1s;
}
.btn:active {
transform: translateY(1px);
}
.btn--ghost {
background: transparent;
color: var(--walnut);
border-color: var(--line);
}
.btn--ghost:hover {
background: var(--paper);
border-color: var(--clay);
}
.btn--ghost[aria-pressed="true"] {
background: var(--walnut);
color: var(--paper);
border-color: var(--walnut);
}
:focus-visible {
outline: 2.5px solid var(--clay);
outline-offset: 2px;
}
/* ---------- Timeline ---------- */
.timeline {
list-style: none;
margin: 0;
padding: 0;
position: relative;
}
.timeline::before {
content: "";
position: absolute;
left: 21px;
top: 12px;
bottom: 12px;
width: 2px;
background: linear-gradient(var(--line), var(--line));
z-index: 0;
}
.step {
position: relative;
padding: 0 0 1.15rem 3.75rem;
}
.step:last-child {
padding-bottom: 0;
}
/* connector highlight up to active */
.step--done .connector,
.step--active .connector {
background: var(--clay);
}
.connector {
position: absolute;
left: 21px;
top: 12px;
width: 2px;
height: calc(100% + 1.15rem);
background: transparent;
z-index: 1;
transition: background 0.4s;
}
.step:last-child .connector {
display: none;
}
.marker {
position: absolute;
left: 0;
top: 0;
width: 44px;
height: 44px;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--paper);
border: 2px solid var(--line);
color: var(--muted);
cursor: pointer;
z-index: 2;
padding: 0;
transition: transform 0.2s, border-color 0.2s, background 0.2s, color 0.2s, box-shadow 0.2s;
}
.marker__num {
font-family: "Cormorant Garamond", serif;
font-size: 1.35rem;
font-weight: 600;
line-height: 1;
}
.marker__icon {
display: none;
width: 20px;
height: 20px;
}
.marker:hover {
border-color: var(--clay);
transform: scale(1.06);
}
.step--done .marker {
background: var(--sage);
border-color: var(--sage);
color: var(--white);
}
.step--done .marker .marker__num {
display: none;
}
.step--done .marker .marker__icon {
display: block;
}
.step--active .marker {
background: var(--clay);
border-color: var(--clay);
color: var(--white);
box-shadow: 0 0 0 6px rgba(176, 137, 104, 0.18);
}
/* ---------- Card ---------- */
.card {
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 1.1rem 1.25rem;
transition: border-color 0.2s, box-shadow 0.25s, transform 0.2s;
}
.step--active .card {
border-color: rgba(176, 137, 104, 0.5);
box-shadow: var(--shadow);
}
.card__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
}
.card__title {
font-family: "Cormorant Garamond", serif;
font-weight: 600;
font-size: 1.5rem;
line-height: 1.1;
margin: 0;
color: var(--ink);
}
.card__phase {
display: block;
font-family: "Inter", sans-serif;
font-size: 0.68rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--clay-d);
font-weight: 600;
margin-bottom: 0.2rem;
}
.badge {
flex: none;
font-size: 0.72rem;
font-weight: 600;
color: var(--walnut);
background: rgba(156, 175, 136, 0.22);
border: 1px solid rgba(92, 68, 51, 0.12);
border-radius: 999px;
padding: 0.28rem 0.7rem;
white-space: nowrap;
}
.card__body {
overflow: hidden;
transition: max-height 0.4s ease, opacity 0.3s ease, margin 0.3s ease;
max-height: 0;
opacity: 0;
margin-top: 0;
}
.step--active .card__body {
max-height: 520px;
opacity: 1;
margin-top: 0.85rem;
}
.card__desc {
margin: 0 0 0.9rem;
color: var(--ink-2);
font-size: 0.95rem;
}
.deliverables {
list-style: none;
margin: 0;
padding: 0.9rem 0 0;
border-top: 1px solid var(--line);
display: grid;
gap: 0.5rem;
}
.deliverables li {
display: flex;
align-items: center;
gap: 0.6rem;
font-size: 0.88rem;
color: var(--ink);
}
.deliverables li::before {
content: "";
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--clay);
flex: none;
}
/* ---------- Compact view ---------- */
.timeline.is-compact .step {
padding-bottom: 0.55rem;
}
.timeline.is-compact .card {
padding: 0.7rem 1rem;
}
.timeline.is-compact .card__body,
.timeline.is-compact .badge {
display: none;
}
.timeline.is-compact .card__title {
font-size: 1.15rem;
}
/* ---------- Footer ---------- */
.foot {
margin-top: 2.5rem;
padding-top: 1.25rem;
border-top: 1px solid var(--line);
color: var(--muted);
font-size: 0.82rem;
font-style: italic;
}
.foot p {
margin: 0;
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 1.5rem;
transform: translate(-50%, 130%);
background: var(--walnut);
color: var(--paper);
font-size: 0.85rem;
font-weight: 500;
padding: 0.65rem 1.15rem;
border-radius: 999px;
box-shadow: var(--shadow);
opacity: 0;
pointer-events: none;
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.35s;
z-index: 40;
}
.toast.show {
transform: translate(-50%, 0);
opacity: 1;
}
/* ---------- Responsive ---------- */
@media (max-width: 520px) {
body {
padding: 1.5rem 0.85rem 2.5rem;
}
.progress__head {
flex-direction: column;
gap: 0.35rem;
}
.step {
padding-left: 3.25rem;
}
.timeline::before,
.connector {
left: 18px;
}
.marker {
width: 38px;
height: 38px;
}
.marker__num {
font-size: 1.15rem;
}
.card__head {
flex-direction: column;
gap: 0.5rem;
}
.card__title {
font-size: 1.3rem;
}
}
@media (prefers-reduced-motion: reduce) {
* {
transition-duration: 0.01ms !important;
}
}(function () {
"use strict";
var STEPS = [
{
phase: "Phase 01",
title: "Discovery",
duration: "1–2 weeks",
desc: "We begin with a home visit and a long conversation about how you actually live. We measure the space, photograph every wall, and build a brief around your routines, your budget, and the feeling you want to walk into each day.",
deliverables: ["Site survey & measurements", "Client questionnaire & brief", "Budget framework", "Signed letter of engagement"]
},
{
phase: "Phase 02",
title: "Concept",
duration: "2–3 weeks",
desc: "A direction takes shape. We present a curated mood board, a rough spatial plan, and a material story — warm oak, unlacquered brass, plaster, and linen — so we agree on the mood before a single item is specified.",
deliverables: ["Mood boards & palette", "Preliminary floor plan", "Material direction", "Concept presentation"]
},
{
phase: "Phase 03",
title: "Design Development",
duration: "4–6 weeks",
desc: "We resolve the details. Furniture layouts, joinery drawings, lighting schemes, and finish schedules are drawn to scale and pressure-tested against your budget, then walked through with you room by room.",
deliverables: ["Scaled layouts & elevations", "Lighting & electrical plan", "Joinery & millwork drawings", "Finish & fixture schedule"]
},
{
phase: "Phase 04",
title: "Sourcing",
duration: "4–8 weeks",
desc: "Now we procure. Every piece is ordered, tracked, and quality-checked as it arrives at our receiving warehouse. Custom upholstery and cabinetry are commissioned with our trusted makers and inspected before delivery.",
deliverables: ["Purchase orders placed", "Lead-time tracking", "Warehouse receiving & QC", "Delivery scheduling"]
},
{
phase: "Phase 05",
title: "Installation",
duration: "3–5 days",
desc: "Installation day. Our team places every piece, hangs the art, dresses the beds, and styles the shelves down to the last stack of books — then we do the final walkthrough and hand you the keys to a finished home.",
deliverables: ["White-glove delivery & placement", "Art hanging & styling", "Snag list & touch-ups", "Final walkthrough & handover"]
}
];
var CHECK = '<svg class="marker__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>';
var timeline = document.getElementById("timeline");
var progressBar = document.getElementById("progressBar");
var progressPct = document.getElementById("progressPct");
var phaseNum = document.getElementById("phaseNum");
var phaseName = document.getElementById("phaseName");
var compactBtn = document.getElementById("compactBtn");
var resetBtn = document.getElementById("resetBtn");
var toastEl = document.getElementById("toast");
var active = 0;
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2200);
}
function render() {
STEPS.forEach(function (s, i) {
var li = document.createElement("li");
li.className = "step";
li.dataset.index = String(i);
var deliverables = s.deliverables
.map(function (d) {
return "<li>" + d + "</li>";
})
.join("");
li.innerHTML =
'<span class="connector" aria-hidden="true"></span>' +
'<button class="marker" type="button" aria-expanded="false" aria-controls="body-' + i + '" ' +
'aria-label="Step ' + (i + 1) + ": " + s.title + '">' +
'<span class="marker__num">' + (i + 1) + "</span>" + CHECK +
"</button>" +
'<article class="card">' +
'<div class="card__head">' +
"<div>" +
'<span class="card__phase">' + s.phase + "</span>" +
'<h2 class="card__title">' + s.title + "</h2>" +
"</div>" +
'<span class="badge">' + s.duration + "</span>" +
"</div>" +
'<div class="card__body" id="body-' + i + '">' +
'<p class="card__desc">' + s.desc + "</p>" +
'<ul class="deliverables">' + deliverables + "</ul>" +
"</div>" +
"</article>";
timeline.appendChild(li);
});
timeline.querySelectorAll(".marker").forEach(function (m) {
m.addEventListener("click", function () {
var idx = parseInt(m.closest(".step").dataset.index, 10);
setActive(idx, true);
});
});
}
function setActive(idx, announce) {
active = idx;
var steps = timeline.querySelectorAll(".step");
steps.forEach(function (li, i) {
li.classList.toggle("step--active", i === idx);
li.classList.toggle("step--done", i < idx);
var marker = li.querySelector(".marker");
marker.setAttribute("aria-expanded", i === idx ? "true" : "false");
});
var pct = Math.round(((idx + 1) / STEPS.length) * 100);
progressBar.style.width = pct + "%";
progressPct.textContent = pct + "%";
phaseNum.textContent = idx + 1;
phaseName.textContent = STEPS[idx].title;
if (announce) {
var activeStep = steps[idx];
if (activeStep.scrollIntoView) {
activeStep.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
toast("Phase " + (idx + 1) + " — " + STEPS[idx].title);
}
}
compactBtn.addEventListener("click", function () {
var on = timeline.classList.toggle("is-compact");
compactBtn.setAttribute("aria-pressed", on ? "true" : "false");
compactBtn.textContent = on ? "Detailed view" : "Compact view";
toast(on ? "Compact overview" : "Detailed view");
});
resetBtn.addEventListener("click", function () {
if (timeline.classList.contains("is-compact")) {
timeline.classList.remove("is-compact");
compactBtn.setAttribute("aria-pressed", "false");
compactBtn.textContent = "Compact view";
}
setActive(0, false);
timeline.querySelector(".step").scrollIntoView({ behavior: "smooth", block: "nearest" });
toast("Timeline reset");
});
render();
setActive(0, false);
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Studio Process — Interior Design Timeline</title>
<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=Cormorant+Garamond:wght@400;500;600&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="wrap">
<header class="masthead">
<p class="eyebrow">Atelier Marrow · Interior Design</p>
<h1>How we work, phase by phase</h1>
<p class="lede">A considered path from first conversation to final styling. Explore each stage to see what we deliver, how long it takes, and what to expect from us along the way.</p>
<div class="progress" role="group" aria-label="Overall process progress">
<div class="progress__head">
<span class="progress__label">Phase <strong id="phaseNum">1</strong> of 5 · <span id="phaseName">Discovery</span></span>
<span class="progress__pct" id="progressPct">20%</span>
</div>
<div class="progress__track">
<div class="progress__bar" id="progressBar" style="width:20%"></div>
</div>
</div>
<div class="controls">
<button class="btn btn--ghost" id="compactBtn" aria-pressed="false">Compact view</button>
<button class="btn btn--ghost" id="resetBtn">Reset</button>
</div>
</header>
<ol class="timeline" id="timeline" aria-label="Design process steps">
<!-- steps injected by script.js -->
</ol>
<footer class="foot">
<p>Typical engagement runs 14–20 weeks. Every project is scoped individually — these are our fictional sample durations.</p>
</footer>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A calm, editorial process timeline for an interior design studio. Five numbered phases — Discovery, Concept, Design Development, Sourcing, and Installation — sit along a thin walnut rule, each marked by a clay-toned node and a hairline connector. Every step expands into a card carrying its duration badge, a short paragraph in the studio’s voice, and a checklist of deliverables, so a prospective client can read the whole engagement at a glance.
The component is genuinely interactive: clicking (or pressing Enter/Space on) any marker expands that step and smoothly scrolls it into view, while a slim progress meter and phase counter update to reflect how far along the journey you are. A Compact view toggle collapses the narrative into a tight scannable list for quick reference, and a Reset control returns the timeline to its opening state. A small toast confirms each action.
The layout is responsive down to ~360px, where the vertical spine reflows to a single flowing column. Controls are keyboard usable with clear focus rings, colors meet WCAG AA contrast against the warm paper background, and the display headings use Cormorant Garamond for an unmistakably editorial feel.