Interior — Lookbook
A warm editorial lookbook for interior design studios: a magazine-style spread mixing hero, portrait, and detail images across an asymmetric mosaic, thin captions with room and material tags, chaptered scroll sections that fade in as you read, and a full-screen lightbox with keyboard navigation, caption panel, and a filmstrip of thumbnails. Serif display type, generous whitespace, and a muted natural palette keep the focus on the interiors.
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;
--serif: "Cormorant Garamond", Georgia, serif;
--sans: "Inter", system-ui, sans-serif;
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
font-family: var(--sans);
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
.kicker {
font-size: 0.72rem;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--clay-d);
font-weight: 600;
margin: 0;
}
/* Scroll progress ------------------------------------------------ */
.scroll-progress {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 3px;
background: transparent;
z-index: 60;
}
.scroll-progress span {
display: block;
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--clay), var(--walnut));
transition: width 0.1s linear;
}
/* Masthead ------------------------------------------------------- */
.masthead {
max-width: 1120px;
margin: 0 auto;
padding: 28px 32px 12px;
}
.masthead__row {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 18px;
border-bottom: 1px solid var(--line);
}
.masthead__mark {
font-family: var(--serif);
font-size: 1.4rem;
font-weight: 600;
letter-spacing: 0.01em;
}
.masthead__nav {
display: flex;
gap: 22px;
}
.masthead__nav a {
font-size: 0.78rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-2);
text-decoration: none;
font-weight: 500;
padding: 4px 2px;
border-bottom: 1px solid transparent;
transition: color 0.2s, border-color 0.2s;
}
.masthead__nav a:hover,
.masthead__nav a:focus-visible {
color: var(--clay-d);
border-color: var(--clay);
outline: none;
}
.masthead__title {
padding: 46px 0 20px;
max-width: 760px;
}
.masthead__title h1 {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(2.6rem, 6vw, 4.6rem);
line-height: 1.02;
margin: 14px 0 18px;
color: var(--ink);
}
.masthead__lede {
font-size: 1.05rem;
color: var(--ink-2);
max-width: 46ch;
margin: 0;
}
/* Lookbook / chapters ------------------------------------------- */
.lookbook {
max-width: 1120px;
margin: 0 auto;
padding: 0 32px 100px;
}
.chapter {
padding: 56px 0;
border-top: 1px solid var(--line);
opacity: 0;
transform: translateY(28px);
transition: opacity 0.8s ease, transform 0.8s ease;
}
.chapter.is-visible {
opacity: 1;
transform: none;
}
.chapter__head {
display: flex;
gap: 20px;
align-items: flex-start;
margin-bottom: 34px;
}
.chapter__num {
font-family: var(--serif);
font-size: 2.4rem;
color: var(--clay);
line-height: 1;
min-width: 56px;
}
.chapter__head h2 {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(1.8rem, 3.4vw, 2.6rem);
margin: 0 0 6px;
}
.chapter__head p {
margin: 0;
color: var(--ink-2);
max-width: 52ch;
font-size: 1rem;
}
/* Spreads (mosaic) ---------------------------------------------- */
.spread {
display: grid;
gap: 22px;
}
.spread--a {
grid-template-columns: 2fr 1fr;
grid-template-areas:
"hero tall"
"wide tall";
}
.spread--a .frame--hero { grid-area: hero; }
.spread--a .frame--tall { grid-area: tall; }
.spread--a .frame--wide { grid-area: wide; }
.spread--b {
grid-template-columns: 1fr 1.6fr;
grid-template-areas:
"tall hero"
"small hero"
"small2 hero";
}
.spread--b .frame--tall { grid-area: tall; }
.spread--b .frame--hero { grid-area: hero; }
.spread--b .frame--small:nth-of-type(3) { grid-area: small; }
.spread--b .frame--small:nth-of-type(4) { grid-area: small2; }
.spread--c {
grid-template-columns: 1.6fr 1fr;
grid-template-areas:
"hero tall"
"wide wide";
}
.spread--c .frame--hero { grid-area: hero; }
.spread--c .frame--tall { grid-area: tall; }
.spread--c .frame--wide { grid-area: wide; }
/* Frames -------------------------------------------------------- */
.frame {
margin: 0;
display: flex;
flex-direction: column;
}
.frame__btn {
border: 0;
padding: 0;
margin: 0;
cursor: pointer;
background: var(--paper);
border-radius: var(--r-md);
overflow: hidden;
position: relative;
display: block;
box-shadow: 0 1px 0 var(--line);
}
.frame__btn::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(44, 38, 32, 0) 55%, rgba(44, 38, 32, 0.28) 100%);
opacity: 0;
transition: opacity 0.35s ease;
}
.frame__btn img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.2, 1), filter 0.5s ease;
filter: saturate(0.94);
}
.frame--hero .frame__btn { min-height: 340px; }
.frame--tall .frame__btn { height: 100%; min-height: 320px; }
.frame--wide .frame__btn { min-height: 240px; }
.frame--small .frame__btn { min-height: 190px; }
.frame__btn:hover img,
.frame__btn:focus-visible img {
transform: scale(1.045);
filter: saturate(1.05);
}
.frame__btn:hover::after,
.frame__btn:focus-visible::after {
opacity: 1;
}
.frame__btn:focus-visible {
outline: 2px solid var(--clay-d);
outline-offset: 3px;
}
figcaption {
padding: 12px 2px 0;
display: flex;
flex-direction: column;
gap: 3px;
border-top: 1px solid var(--line);
margin-top: 12px;
}
.cap-tag {
font-size: 0.66rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--clay-d);
font-weight: 600;
}
.cap-title {
font-family: var(--serif);
font-size: 1.25rem;
font-weight: 500;
color: var(--ink);
}
.cap-note {
font-size: 0.85rem;
color: var(--muted);
}
/* Colophon ------------------------------------------------------ */
.colophon {
margin-top: 56px;
padding-top: 30px;
border-top: 1px solid var(--line);
text-align: center;
}
.colophon p:not(.kicker) {
font-family: var(--serif);
font-size: 1.15rem;
color: var(--ink-2);
max-width: 60ch;
margin: 10px auto 22px;
}
.btn-save {
font-family: var(--sans);
font-size: 0.78rem;
letter-spacing: 0.16em;
text-transform: uppercase;
font-weight: 600;
color: var(--paper);
background: var(--walnut);
border: 0;
padding: 13px 28px;
border-radius: var(--r-sm);
cursor: pointer;
transition: background 0.25s, transform 0.15s;
}
.btn-save:hover {
background: var(--clay-d);
}
.btn-save:active {
transform: translateY(1px);
}
.btn-save:focus-visible {
outline: 2px solid var(--clay);
outline-offset: 3px;
}
/* Lightbox ------------------------------------------------------ */
.lightbox {
position: fixed;
inset: 0;
z-index: 80;
background: rgba(30, 26, 22, 0.92);
backdrop-filter: blur(6px);
display: grid;
grid-template-rows: 1fr auto;
padding: 24px;
opacity: 0;
animation: fadeIn 0.3s ease forwards;
}
.lightbox[hidden] {
display: none;
}
@keyframes fadeIn {
to { opacity: 1; }
}
.lightbox__stage {
display: flex;
align-items: center;
justify-content: center;
min-height: 0;
}
.lightbox__figure {
margin: 0;
display: flex;
gap: 30px;
align-items: flex-end;
max-width: 1000px;
max-height: 100%;
}
.lightbox__figure img {
max-width: min(70vw, 760px);
max-height: 72vh;
object-fit: contain;
border-radius: var(--r-sm);
box-shadow: 0 30px 70px rgba(0, 0, 0, 0.45);
animation: plateIn 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
}
@keyframes plateIn {
from { opacity: 0; transform: scale(0.97); }
to { opacity: 1; transform: none; }
}
.lightbox__cap {
border-top: 0;
border-left: 1px solid rgba(255, 255, 255, 0.22);
padding: 4px 0 4px 22px;
margin: 0;
max-width: 220px;
color: var(--paper);
}
.lightbox__cap .cap-tag { color: var(--clay); }
.lightbox__cap .cap-title { color: var(--paper); font-size: 1.5rem; }
.lightbox__cap .cap-note { color: rgba(251, 249, 245, 0.7); }
.lightbox__count {
margin-top: 16px;
font-size: 0.72rem;
letter-spacing: 0.14em;
color: rgba(251, 249, 245, 0.55);
}
.lightbox__close {
position: absolute;
top: 18px;
right: 22px;
width: 42px;
height: 42px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.25);
background: rgba(0, 0, 0, 0.2);
color: var(--paper);
font-size: 1.5rem;
line-height: 1;
cursor: pointer;
transition: background 0.2s, transform 0.2s;
}
.lightbox__close:hover { background: rgba(255, 255, 255, 0.15); transform: rotate(90deg); }
.lightbox__nav {
position: absolute;
top: 42%;
width: 50px;
height: 50px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.25);
background: rgba(0, 0, 0, 0.25);
color: var(--paper);
font-size: 1.6rem;
line-height: 1;
cursor: pointer;
transition: background 0.2s, transform 0.2s;
}
.lightbox__nav:hover { background: var(--clay-d); }
.lightbox__nav--prev { left: 20px; }
.lightbox__nav--next { right: 20px; }
.lightbox__nav:hover.lightbox__nav--prev { transform: translateX(-3px); }
.lightbox__nav:hover.lightbox__nav--next { transform: translateX(3px); }
.lightbox :focus-visible {
outline: 2px solid var(--clay);
outline-offset: 3px;
}
/* Filmstrip ----------------------------------------------------- */
.filmstrip {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
padding-top: 20px;
margin-top: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.filmstrip button {
width: 66px;
height: 46px;
border: 2px solid transparent;
border-radius: var(--r-sm);
padding: 0;
overflow: hidden;
cursor: pointer;
background: none;
opacity: 0.5;
transition: opacity 0.25s, border-color 0.25s, transform 0.2s;
}
.filmstrip button img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.filmstrip button:hover { opacity: 0.85; transform: translateY(-2px); }
.filmstrip button.is-active {
opacity: 1;
border-color: var(--clay);
}
/* Toast --------------------------------------------------------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translateX(-50%) translateY(20px);
background: var(--ink);
color: var(--paper);
padding: 12px 22px;
border-radius: var(--r-sm);
font-size: 0.85rem;
letter-spacing: 0.02em;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
opacity: 0;
pointer-events: none;
transition: opacity 0.3s, transform 0.3s;
z-index: 90;
}
.toast.is-shown {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* Responsive ---------------------------------------------------- */
@media (max-width: 820px) {
.spread--a,
.spread--b,
.spread--c {
grid-template-columns: 1fr 1fr;
grid-template-areas: none;
}
.spread--a .frame--hero,
.spread--b .frame--hero,
.spread--c .frame--hero {
grid-column: 1 / -1;
}
.spread .frame { grid-area: auto !important; }
.spread--c .frame--wide { grid-column: 1 / -1; }
}
@media (max-width: 520px) {
.masthead,
.lookbook {
padding-left: 20px;
padding-right: 20px;
}
.masthead__nav { gap: 14px; }
.masthead__nav a { font-size: 0.68rem; }
.masthead__title { padding: 30px 0 14px; }
.masthead__lede { font-size: 0.98rem; }
.spread {
grid-template-columns: 1fr !important;
grid-template-areas: none;
gap: 30px;
}
.spread .frame { grid-area: auto !important; }
.chapter__num { font-size: 1.9rem; min-width: 40px; }
.chapter__head { gap: 14px; }
.frame--hero .frame__btn,
.frame--tall .frame__btn { min-height: 260px; }
.lightbox { padding: 16px; }
.lightbox__figure {
flex-direction: column;
align-items: flex-start;
gap: 14px;
}
.lightbox__figure img { max-width: 90vw; max-height: 54vh; }
.lightbox__cap {
border-left: 0;
border-top: 1px solid rgba(255, 255, 255, 0.22);
padding: 12px 0 0;
max-width: none;
}
.lightbox__count { margin-top: 8px; }
.lightbox__nav { top: auto; bottom: 130px; }
.filmstrip button { width: 54px; height: 38px; }
}
@media (prefers-reduced-motion: reduce) {
* { scroll-behavior: auto; }
.chapter { transition: opacity 0.4s ease; transform: none; }
.frame__btn img { transition: none; }
}(function () {
"use strict";
/* --------------------------------------------------------------
* Plate data — derived from the figures in the DOM so the
* lightbox, filmstrip and captions always stay in sync.
* ------------------------------------------------------------ */
var frames = Array.prototype.slice.call(document.querySelectorAll(".frame"));
var plates = frames.map(function (frame) {
var img = frame.querySelector("img");
return {
src: img.getAttribute("src").replace(/w=\d+/, "w=1400"),
thumb: img.getAttribute("src"),
alt: img.getAttribute("alt"),
tag: frame.querySelector(".cap-tag").textContent,
title: frame.querySelector(".cap-title").textContent,
note: frame.querySelector(".cap-note").textContent
};
});
/* --------------------------------------------------------------
* Toast helper
* ------------------------------------------------------------ */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("is-shown");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-shown");
}, 2600);
}
/* --------------------------------------------------------------
* Scroll progress bar
* ------------------------------------------------------------ */
var progressBar = document.getElementById("progressBar");
function updateProgress() {
var h = document.documentElement;
var max = h.scrollHeight - h.clientHeight;
var pct = max > 0 ? (h.scrollTop || document.body.scrollTop) / max : 0;
progressBar.style.width = (pct * 100).toFixed(1) + "%";
}
window.addEventListener("scroll", updateProgress, { passive: true });
window.addEventListener("resize", updateProgress);
updateProgress();
/* --------------------------------------------------------------
* Chapter reveal on scroll
* ------------------------------------------------------------ */
var chapters = document.querySelectorAll("[data-reveal]");
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add("is-visible");
io.unobserve(entry.target);
}
});
},
{ threshold: 0.14 }
);
chapters.forEach(function (c) {
io.observe(c);
});
} else {
chapters.forEach(function (c) {
c.classList.add("is-visible");
});
}
/* --------------------------------------------------------------
* Lightbox
* ------------------------------------------------------------ */
var lightbox = document.getElementById("lightbox");
var lbImg = document.getElementById("lbImg");
var lbTag = document.getElementById("lbTag");
var lbTitle = document.getElementById("lbTitle");
var lbNote = document.getElementById("lbNote");
var lbCount = document.getElementById("lbCount");
var filmstrip = document.getElementById("filmstrip");
var current = 0;
var lastFocused = null;
// Build filmstrip once
plates.forEach(function (plate, i) {
var btn = document.createElement("button");
btn.type = "button";
btn.setAttribute("aria-label", "View " + plate.title);
var img = document.createElement("img");
img.src = plate.thumb;
img.alt = "";
btn.appendChild(img);
btn.addEventListener("click", function () {
show(i);
});
filmstrip.appendChild(btn);
});
var stripBtns = Array.prototype.slice.call(filmstrip.children);
function render() {
var p = plates[current];
lbImg.src = p.src;
lbImg.alt = p.alt;
lbTag.textContent = p.tag;
lbTitle.textContent = p.title;
lbNote.textContent = p.note;
lbCount.textContent = "Plate " + (current + 1) + " of " + plates.length;
stripBtns.forEach(function (b, i) {
b.classList.toggle("is-active", i === current);
});
var active = stripBtns[current];
if (active && active.scrollIntoView) {
active.scrollIntoView({ inline: "center", block: "nearest", behavior: "smooth" });
}
}
function openLightbox(index) {
current = index;
lastFocused = document.activeElement;
lightbox.hidden = false;
render();
document.body.style.overflow = "hidden";
document.getElementById("lbClose").focus();
}
function closeLightbox() {
lightbox.hidden = true;
document.body.style.overflow = "";
if (lastFocused && lastFocused.focus) lastFocused.focus();
}
function show(index) {
current = (index + plates.length) % plates.length;
render();
}
function next() { show(current + 1); }
function prev() { show(current - 1); }
// Wire up frame buttons
frames.forEach(function (frame, i) {
frame.querySelector(".frame__btn").addEventListener("click", function () {
openLightbox(i);
});
});
document.getElementById("lbClose").addEventListener("click", closeLightbox);
document.getElementById("lbNext").addEventListener("click", next);
document.getElementById("lbPrev").addEventListener("click", prev);
// Backdrop click closes (but not clicks on the figure/controls)
lightbox.addEventListener("click", function (e) {
if (e.target === lightbox || e.target.classList.contains("lightbox__stage")) {
closeLightbox();
}
});
// Keyboard controls
document.addEventListener("keydown", function (e) {
if (lightbox.hidden) return;
if (e.key === "Escape") closeLightbox();
else if (e.key === "ArrowRight") next();
else if (e.key === "ArrowLeft") prev();
});
/* --------------------------------------------------------------
* Save action
* ------------------------------------------------------------ */
var saveBtn = document.getElementById("saveBtn");
var saved = false;
saveBtn.addEventListener("click", function () {
saved = !saved;
saveBtn.textContent = saved ? "Saved to your shelf" : "Save this lookbook";
toast(saved ? "Lookbook № 04 saved to your shelf." : "Removed from your shelf.");
});
/* --------------------------------------------------------------
* Smooth-scroll masthead nav + gentle first hint
* ------------------------------------------------------------ */
document.querySelectorAll(".masthead__nav a").forEach(function (a) {
a.addEventListener("click", function (e) {
var target = document.querySelector(a.getAttribute("href"));
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: "smooth", block: "start" });
}
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Atelier Norr — Lookbook № 04</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>
<div class="scroll-progress" aria-hidden="true"><span id="progressBar"></span></div>
<header class="masthead">
<div class="masthead__row">
<span class="masthead__mark">Atelier Norr</span>
<nav class="masthead__nav" aria-label="Lookbook chapters">
<a href="#ch-01">Living</a>
<a href="#ch-02">Kitchen</a>
<a href="#ch-03">Retreat</a>
</nav>
</div>
<div class="masthead__title">
<p class="kicker">Lookbook № 04 · Autumn Interiors</p>
<h1>Rooms that hold<br />the afternoon light</h1>
<p class="masthead__lede">Twelve interiors photographed across three homes — a study in clay, walnut, and unbleached linen. Tap any frame to open the plate.</p>
</div>
</header>
<main class="lookbook" id="lookbook">
<!-- Chapter 01 -->
<section class="chapter" id="ch-01" data-reveal>
<div class="chapter__head">
<span class="chapter__num">01</span>
<div>
<h2>The Living Room</h2>
<p>A south-facing lounge where a plaster fireplace anchors deep, low seating.</p>
</div>
</div>
<div class="spread spread--a">
<figure class="frame frame--hero" data-index="0">
<button class="frame__btn" type="button" aria-label="Open plate: Clay lounge, full view">
<img src="https://images.unsplash.com/photo-1618221195710-dd6b41faaea6?w=1200&q=80&auto=format&fit=crop" alt="Warm living room with clay walls, low linen sofa and arched window" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 01 · Living</span>
<span class="cap-title">Clay & Linen Lounge</span>
<span class="cap-note">Lime-washed walls, boucle seating, oak parquet.</span>
</figcaption>
</figure>
<figure class="frame frame--tall" data-index="1">
<button class="frame__btn" type="button" aria-label="Open plate: Reading corner">
<img src="https://images.unsplash.com/photo-1567016432779-094069958ea5?w=800&q=80&auto=format&fit=crop" alt="Sunlit reading corner with rattan chair and stacked books" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 02 · Detail</span>
<span class="cap-title">The Reading Corner</span>
<span class="cap-note">Rattan lounge chair, brass floor lamp.</span>
</figcaption>
</figure>
<figure class="frame frame--wide" data-index="2">
<button class="frame__btn" type="button" aria-label="Open plate: Shelf still life">
<img src="https://images.unsplash.com/photo-1524758631624-e2822e304c36?w=1000&q=80&auto=format&fit=crop" alt="Styled open shelving with ceramics and dried grasses" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 03 · Still Life</span>
<span class="cap-title">Mantel, Composed</span>
<span class="cap-note">Hand-thrown stoneware, dried pampas.</span>
</figcaption>
</figure>
</div>
</section>
<!-- Chapter 02 -->
<section class="chapter" id="ch-02" data-reveal>
<div class="chapter__head">
<span class="chapter__num">02</span>
<div>
<h2>The Kitchen</h2>
<p>Walnut cabinetry meets a honed marble island under a run of pendant glass.</p>
</div>
</div>
<div class="spread spread--b">
<figure class="frame frame--tall" data-index="3">
<button class="frame__btn" type="button" aria-label="Open plate: Walnut kitchen">
<img src="https://images.unsplash.com/photo-1556911220-bff31c812dba?w=800&q=80&auto=format&fit=crop" alt="Walnut kitchen with marble island and glass pendants" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 04 · Kitchen</span>
<span class="cap-title">Walnut & Marble</span>
<span class="cap-note">Fluted island, aged-brass tapware.</span>
</figcaption>
</figure>
<figure class="frame frame--hero" data-index="4">
<button class="frame__btn" type="button" aria-label="Open plate: Breakfast nook">
<img src="https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=1200&q=80&auto=format&fit=crop" alt="Bright breakfast nook with bench seating and morning light" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 05 · Nook</span>
<span class="cap-title">The Breakfast Bench</span>
<span class="cap-note">Built-in oak seating, checked linen.</span>
</figcaption>
</figure>
<figure class="frame frame--small" data-index="5">
<button class="frame__btn" type="button" aria-label="Open plate: Ceramics detail">
<img src="https://images.unsplash.com/photo-1610701596007-11502861dcfa?w=700&q=80&auto=format&fit=crop" alt="Close-up of ceramic bowls and wooden spoons on a counter" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 06 · Detail</span>
<span class="cap-title">Everyday Ceramics</span>
<span class="cap-note">Matte glaze, olive-wood utensils.</span>
</figcaption>
</figure>
<figure class="frame frame--small" data-index="6">
<button class="frame__btn" type="button" aria-label="Open plate: Pantry corner">
<img src="https://images.unsplash.com/photo-1595514535215-9a5e0e8e394f?w=700&q=80&auto=format&fit=crop" alt="Open pantry shelving with jars and linens" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 07 · Storage</span>
<span class="cap-title">The Open Pantry</span>
<span class="cap-note">Glass jars, waxed-cotton labels.</span>
</figcaption>
</figure>
</div>
</section>
<!-- Chapter 03 -->
<section class="chapter" id="ch-03" data-reveal>
<div class="chapter__head">
<span class="chapter__num">03</span>
<div>
<h2>The Retreat</h2>
<p>A quiet bedroom in sage and sand — layered textiles, soft plaster, no screens.</p>
</div>
</div>
<div class="spread spread--c">
<figure class="frame frame--hero" data-index="7">
<button class="frame__btn" type="button" aria-label="Open plate: Sage bedroom">
<img src="https://images.unsplash.com/photo-1616594039964-ae9021a400a0?w=1200&q=80&auto=format&fit=crop" alt="Calm bedroom with sage walls and layered linen bedding" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 08 · Retreat</span>
<span class="cap-title">Sage & Sand Bedroom</span>
<span class="cap-note">Washed-linen bedding, cane headboard.</span>
</figcaption>
</figure>
<figure class="frame frame--tall" data-index="8">
<button class="frame__btn" type="button" aria-label="Open plate: Bath detail">
<img src="https://images.unsplash.com/photo-1600566752355-35792bedcfea?w=800&q=80&auto=format&fit=crop" alt="Minimal bathroom with stone basin and arched niche" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 09 · Bath</span>
<span class="cap-title">Stone Basin Niche</span>
<span class="cap-note">Travertine vanity, unlacquered brass.</span>
</figcaption>
</figure>
<figure class="frame frame--wide" data-index="9">
<button class="frame__btn" type="button" aria-label="Open plate: Window seat">
<img src="https://images.unsplash.com/photo-1567538096630-e0c55bd6374c?w=1000&q=80&auto=format&fit=crop" alt="Window seat with cushions overlooking a garden" loading="lazy" />
</button>
<figcaption>
<span class="cap-tag">Plate 10 · Corner</span>
<span class="cap-title">The Window Seat</span>
<span class="cap-note">Wool cushions, linen roman shade.</span>
</figcaption>
</figure>
</div>
<div class="colophon">
<p class="kicker">Colophon</p>
<p>Atelier Norr · Lookbook № 04 · Photographed by Mireille Fauve · Styled by Jonas Ek · Printed on uncoated stock, autumn 2026.</p>
<button class="btn-save" type="button" id="saveBtn">Save this lookbook</button>
</div>
</section>
</main>
<!-- Lightbox -->
<div class="lightbox" id="lightbox" role="dialog" aria-modal="true" aria-label="Lookbook plate viewer" hidden>
<button class="lightbox__close" id="lbClose" type="button" aria-label="Close viewer">×</button>
<button class="lightbox__nav lightbox__nav--prev" id="lbPrev" type="button" aria-label="Previous plate">‹</button>
<button class="lightbox__nav lightbox__nav--next" id="lbNext" type="button" aria-label="Next plate">›</button>
<div class="lightbox__stage">
<figure class="lightbox__figure">
<img id="lbImg" src="" alt="" />
<figcaption class="lightbox__cap" id="lbCap">
<span class="cap-tag" id="lbTag"></span>
<span class="cap-title" id="lbTitle"></span>
<span class="cap-note" id="lbNote"></span>
<span class="lightbox__count" id="lbCount"></span>
</figcaption>
</figure>
</div>
<div class="filmstrip" id="filmstrip" aria-label="All plates"></div>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>An editorial lookbook for a fictional interior design studio, laid out like the pages of a print magazine. The spread mixes image sizes — a full-bleed hero, tall portraits, wide panoramas, and small detail shots — arranged in an asymmetric mosaic with thin rules, small-caps kickers, and hanging captions that name each room, its palette, and the key materials. Chapters are broken into scroll sections that fade and rise into view as they enter the viewport, so reading the lookbook feels like turning pages.
Every image is a button. Clicking one opens a full-screen lightbox that dims the page, shows the photo at scale beside an editorial caption panel, and exposes a filmstrip of thumbnails along the bottom. You can move through the collection with the arrow keys, jump via the filmstrip, and close with Escape or the backdrop. A slim progress indicator tracks how far you have scrolled through the lookbook, and a toast confirms actions like saving a spread.
The whole piece is vanilla HTML, CSS, and JavaScript with no dependencies. Imagery uses Unsplash placeholder URLs, focus states are visible for keyboard users, and the layout collapses to a single readable column below 520px while preserving the magazine rhythm.