docs(mockups): static HTML wireframes for Library scaling concepts
CI / test (push) Has been cancelled

Renders the five design concepts from
_bmad-output/planning-artifacts/library-many-frames-design-ideas.md as
standalone HTML so Matt can compare them in a browser without spinning
up the SPA. Mockups live at /mockups/library/ and reuse the project's
design tokens for visual consistency:

  index.html   landing page with concept links
  current.html the chip-explosion state we're shipping today
  concept-a.html  photo + status badge → DevicePicker sheet (recommended)
  concept-b.html  device-first tab
  concept-c.html  multi-select bulk action bar
  concept-d.html  device chip filter + photo dots

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 15:08:18 -04:00
parent a511b89564
commit 9854688a49
8 changed files with 1102 additions and 0 deletions
+196
View File
@@ -0,0 +1,196 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Concept 🅐 — Photo + badge</title>
<link rel="stylesheet" href="shared.css">
<style>
.grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); padding: var(--space-4); }
.item { display: flex; flex-direction: column; gap: var(--space-1); }
.item__photo { position: relative; }
.item__photo .corner-lock {
position: absolute; top: var(--space-2); right: var(--space-2);
background: var(--color-lock); color: white; border-radius: var(--radius-full);
width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
font-size: 14px; box-shadow: 0 1px 2px rgba(0,0,0,0.25);
}
.item__status {
display: flex; align-items: center; gap: var(--space-2);
padding: var(--space-1) var(--space-1);
font-size: var(--text-xs);
color: var(--color-text-muted);
}
.item__status b { color: var(--color-text); font-weight: 600; }
.item__status .lock { color: #6b5210; }
.item__row {
display: flex; align-items: center; justify-content: space-between;
padding: 2px var(--space-1);
}
.item__manage {
background: transparent; border: 0;
color: var(--color-primary); font-size: var(--text-xs); font-weight: 600;
}
/* Sheet mock — pinned bottom panel showing the DevicePicker reuse */
.sheet {
position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
width: 100%; max-width: 420px;
background: var(--color-surface);
border-top: 1px solid var(--color-border);
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
box-shadow: 0 -8px 24px rgba(58,46,34,0.18);
padding: var(--space-4);
transform: translate(-50%, 0);
transition: transform var(--duration-fast);
}
.sheet__handle {
width: 36px; height: 4px; background: var(--color-border);
border-radius: 2px; margin: 0 auto var(--space-4);
}
.sheet__title {
font-weight: 700; margin: 0 0 var(--space-3); font-size: var(--text-md);
}
.sheet__row {
display: flex; align-items: center; justify-content: space-between;
padding: var(--space-3) 0; border-bottom: 1px solid var(--color-border);
}
.sheet__row:last-child { border-bottom: 0; }
.sheet__device { font-weight: 600; }
.sheet__device small { color: var(--color-text-muted); font-weight: 400; }
.toggle {
width: 44px; height: 26px; background: var(--color-border);
border-radius: var(--radius-full); position: relative; transition: background 0.15s;
border: 0;
}
.toggle::after {
content: ''; position: absolute; top: 3px; left: 3px;
width: 20px; height: 20px; border-radius: 50%; background: white;
transition: left 0.15s;
}
.toggle--on { background: var(--color-primary); }
.toggle--on::after { left: 21px; }
.lock-toggle {
background: transparent; border: 1px solid var(--color-border);
color: var(--color-text-muted); padding: 4px 10px;
border-radius: var(--radius-full); font-size: var(--text-xs);
}
.lock-toggle--on { background: var(--color-lock); color: white; border-color: var(--color-lock); }
.sheet__done {
background: var(--color-primary); color: var(--color-primary-fg);
width: 100%; border: 0; padding: 12px;
border-radius: var(--radius-full); font-weight: 700;
margin-top: var(--space-4);
}
</style>
</head>
<body>
<div class="frame">
<header class="topbar">
<h1>Library</h1>
<div class="topbar__tabs">
<button class="topbar__tab topbar__tab--active">My Photos</button>
<button class="topbar__tab">Shared</button>
</div>
</header>
<div class="note">
<b>🅐</b> Each photo card is just the image + a one-line status. The lock
is a corner badge on the photo (lock-icon). Tap any photo to open the
bottom sheet with device approvals + per-device lock — same UI we already
use for upload.
</div>
<div class="grid">
<div class="item">
<div class="item__photo">
<div class="photo photo--p1"></div>
<div class="corner-lock" title="Locked on Living Room">🔒</div>
</div>
<div class="item__row">
<span class="item__status"><b>3</b>/5 frames</span>
<button class="item__manage">Manage ▸</button>
</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p2"></div>
</div>
<div class="item__row">
<span class="item__status"><b>2</b>/5 frames</span>
<button class="item__manage">Manage ▸</button>
</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p3"></div>
</div>
<div class="item__row">
<span class="item__status"><b>5</b>/5 frames</span>
<button class="item__manage">Manage ▸</button>
</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p4"></div>
<div class="corner-lock" title="Locked on Mom's Place">🔒</div>
</div>
<div class="item__row">
<span class="item__status"><b>2</b>/5 frames</span>
<button class="item__manage">Manage ▸</button>
</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p5"></div>
</div>
<div class="item__row">
<span class="item__status"><b>2</b>/5 frames</span>
<button class="item__manage">Manage ▸</button>
</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p6"></div>
</div>
<div class="item__row">
<span class="item__status"><b>3</b>/5 frames</span>
<button class="item__manage">Manage ▸</button>
</div>
</div>
</div>
<!-- Sheet mock (always visible to demo reuse) -->
<div class="sheet">
<div class="sheet__handle"></div>
<div class="sheet__title">Manage frames for this photo</div>
<div class="sheet__row">
<div class="sheet__device">Living Room<br><small>Online · last sync 2m ago</small></div>
<button class="lock-toggle lock-toggle--on">🔒 Locked</button>
<button class="toggle toggle--on" aria-label="Approved"></button>
</div>
<div class="sheet__row">
<div class="sheet__device">Kitchen<br><small>Online · last sync 5m ago</small></div>
<button class="lock-toggle">🔓 Rotate</button>
<button class="toggle toggle--on" aria-label="Approved"></button>
</div>
<div class="sheet__row">
<div class="sheet__device">Bedroom<br><small>Online · last sync 1m ago</small></div>
<span></span>
<button class="toggle" aria-label="Not approved"></button>
</div>
<div class="sheet__row">
<div class="sheet__device">Mom's Place<br><small>Online · last sync 3m ago</small></div>
<button class="lock-toggle">🔓 Rotate</button>
<button class="toggle toggle--on" aria-label="Approved"></button>
</div>
<div class="sheet__row">
<div class="sheet__device">Cabin<br><small>Offline · last seen 2h ago</small></div>
<span></span>
<button class="toggle" aria-label="Not approved"></button>
</div>
<button class="sheet__done">Done</button>
</div>
</div>
</body>
</html>
+172
View File
@@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Concept 🅑 — Device-first</title>
<link rel="stylesheet" href="shared.css">
<style>
.scope-tabs {
display: flex; gap: var(--space-2); padding: 0 var(--space-4) var(--space-3);
border-bottom: 1px solid var(--color-border);
}
.scope-tabs button {
background: transparent; border: 0;
padding: var(--space-2) var(--space-3);
font-weight: 600; font-size: var(--text-sm);
color: var(--color-text-muted);
border-bottom: 2px solid transparent;
}
.scope-tabs button.active {
color: var(--color-text); border-bottom-color: var(--color-primary);
}
.frame-row {
padding: var(--space-4);
border-bottom: 1px solid var(--color-border);
}
.frame-row__header {
display: flex; justify-content: space-between; align-items: baseline;
margin-bottom: var(--space-3);
}
.frame-row__name { font-weight: 700; font-size: var(--text-md); }
.frame-row__meta { font-size: var(--text-xs); color: var(--color-text-muted); }
.frame-row__meta .lock-meta { color: #6b5210; font-weight: 600; }
.frame-row__strip {
display: grid; grid-template-columns: repeat(4, 1fr);
gap: var(--space-2);
}
.strip-photo { position: relative; }
.strip-photo .locked-flag {
position: absolute; top: 4px; right: 4px;
background: var(--color-lock); color: white; border-radius: var(--radius-full);
width: 20px; height: 20px; font-size: 11px;
display: flex; align-items: center; justify-content: center;
}
.strip-photo--add {
border: 2px dashed var(--color-border);
border-radius: var(--radius-sm);
aspect-ratio: 1/1;
display: flex; align-items: center; justify-content: center;
color: var(--color-text-muted);
font-size: 24px;
}
.frame-row__count {
margin-top: var(--space-2);
font-size: var(--text-xs); color: var(--color-text-muted);
}
</style>
</head>
<body>
<div class="frame">
<header class="topbar">
<h1>Library</h1>
<div class="topbar__tabs">
<button class="topbar__tab topbar__tab--active">My Photos</button>
<button class="topbar__tab">Shared</button>
</div>
</header>
<div class="scope-tabs">
<button>By Photo</button>
<button class="active">By Frame</button>
</div>
<div class="note">
<b>🅑</b> Group photos by frame, not the other way around. Each row is a
frame — name, status, locked photo, scroll of approved photos, and a
big <b>+</b> button to add more. Answers "what's on Grandma's frame?"
in one glance.
</div>
<div class="frame-row">
<div class="frame-row__header">
<div>
<div class="frame-row__name">Living Room</div>
<div class="frame-row__meta">8 photos · <span class="lock-meta">🔒 Birthday cake</span></div>
</div>
<button class="chip chip--device">Settings</button>
</div>
<div class="frame-row__strip">
<div class="strip-photo"><div class="photo photo--p1"></div><div class="locked-flag">🔒</div></div>
<div class="strip-photo"><div class="photo photo--p2"></div></div>
<div class="strip-photo"><div class="photo photo--p3"></div></div>
<div class="strip-photo strip-photo--add">+</div>
</div>
<div class="frame-row__count">+ 5 more</div>
</div>
<div class="frame-row">
<div class="frame-row__header">
<div>
<div class="frame-row__name">Kitchen</div>
<div class="frame-row__meta">12 photos · rotating</div>
</div>
<button class="chip chip--device">Settings</button>
</div>
<div class="frame-row__strip">
<div class="strip-photo"><div class="photo photo--p4"></div></div>
<div class="strip-photo"><div class="photo photo--p5"></div></div>
<div class="strip-photo"><div class="photo photo--p6"></div></div>
<div class="strip-photo strip-photo--add">+</div>
</div>
<div class="frame-row__count">+ 9 more</div>
</div>
<div class="frame-row">
<div class="frame-row__header">
<div>
<div class="frame-row__name">Bedroom</div>
<div class="frame-row__meta">3 photos · rotating</div>
</div>
<button class="chip chip--device">Settings</button>
</div>
<div class="frame-row__strip">
<div class="strip-photo"><div class="photo photo--p7"></div></div>
<div class="strip-photo"><div class="photo photo--p8"></div></div>
<div class="strip-photo"><div class="photo photo--p2"></div></div>
<div class="strip-photo strip-photo--add">+</div>
</div>
</div>
<div class="frame-row">
<div class="frame-row__header">
<div>
<div class="frame-row__name">Mom's Place</div>
<div class="frame-row__meta">6 photos · <span class="lock-meta">🔒 Family dinner</span></div>
</div>
<button class="chip chip--device">Settings</button>
</div>
<div class="frame-row__strip">
<div class="strip-photo"><div class="photo photo--p4"></div><div class="locked-flag">🔒</div></div>
<div class="strip-photo"><div class="photo photo--p1"></div></div>
<div class="strip-photo"><div class="photo photo--p9"></div></div>
<div class="strip-photo strip-photo--add">+</div>
</div>
<div class="frame-row__count">+ 3 more</div>
</div>
<div class="frame-row">
<div class="frame-row__header">
<div>
<div class="frame-row__name">Cabin</div>
<div class="frame-row__meta">2 photos · offline 2h</div>
</div>
<button class="chip chip--device">Settings</button>
</div>
<div class="frame-row__strip">
<div class="strip-photo"><div class="photo photo--p5"></div></div>
<div class="strip-photo"><div class="photo photo--p10"></div></div>
<div class="strip-photo strip-photo--add">+</div>
</div>
</div>
<nav class="bottomnav">
<div class="bottomnav__item">🏠<span>Home</span></div>
<div class="bottomnav__item bottomnav__item--active">📷<span>Library</span></div>
<div class="bottomnav__item">⚙️<span>Settings</span></div>
</nav>
</div>
</body>
</html>
+131
View File
@@ -0,0 +1,131 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Concept 🅒 — Multi-select bulk</title>
<link rel="stylesheet" href="shared.css">
<style>
.selection-bar {
position: sticky; top: 0; z-index: 6;
background: var(--color-primary); color: var(--color-primary-fg);
padding: var(--space-3) var(--space-4);
display: flex; align-items: center; justify-content: space-between;
}
.selection-bar__count { font-weight: 700; }
.selection-bar__actions { display: flex; gap: var(--space-2); }
.selection-bar__btn {
background: rgba(255,255,255,0.15); color: white; border: 0;
padding: 6px 12px; border-radius: var(--radius-full);
font-size: var(--text-sm); font-weight: 600;
}
.selection-bar__btn--primary { background: white; color: var(--color-primary); }
.grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); padding: var(--space-4); }
.item { position: relative; }
.item__photo { position: relative; }
.item__check {
position: absolute; top: var(--space-2); left: var(--space-2);
width: 26px; height: 26px;
border-radius: 50%;
background: rgba(255,255,255,0.85);
border: 2px solid white;
display: flex; align-items: center; justify-content: center;
font-weight: 700; color: var(--color-primary);
box-shadow: 0 1px 4px rgba(0,0,0,0.25);
}
.item--selected .item__photo { outline: 3px solid var(--color-primary); outline-offset: -3px; border-radius: var(--radius-sm); }
.item--selected .item__check { background: var(--color-primary); color: white; border-color: white; }
.item__status {
font-size: var(--text-xs);
color: var(--color-text-muted);
padding: var(--space-1) var(--space-1);
}
</style>
</head>
<body>
<div class="frame">
<div class="selection-bar">
<span class="selection-bar__count">3 selected</span>
<div class="selection-bar__actions">
<button class="selection-bar__btn selection-bar__btn--primary">Send to ▸</button>
<button class="selection-bar__btn">🔒</button>
<button class="selection-bar__btn">🗑</button>
<button class="selection-bar__btn"></button>
</div>
</div>
<div class="note">
<b>🅒</b> Long-press any photo to enter selection mode (this mockup is
pre-entered). Top bar morphs to action bar. Tick multiple photos, hit
"Send to ▸" — opens the DevicePicker once for the batch. J1 ("send to
all my frames") becomes two gestures instead of N × 5.
</div>
<div class="grid">
<div class="item item--selected">
<div class="item__photo">
<div class="photo photo--p1"></div>
<div class="item__check"></div>
</div>
<div class="item__status">3/5 frames</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p2"></div>
<div class="item__check"></div>
</div>
<div class="item__status">2/5 frames</div>
</div>
<div class="item item--selected">
<div class="item__photo">
<div class="photo photo--p3"></div>
<div class="item__check"></div>
</div>
<div class="item__status">5/5 frames</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p4"></div>
<div class="item__check"></div>
</div>
<div class="item__status">2/5 frames</div>
</div>
<div class="item item--selected">
<div class="item__photo">
<div class="photo photo--p5"></div>
<div class="item__check"></div>
</div>
<div class="item__status">2/5 frames</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p6"></div>
<div class="item__check"></div>
</div>
<div class="item__status">3/5 frames</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p7"></div>
<div class="item__check"></div>
</div>
<div class="item__status">1/5 frames</div>
</div>
<div class="item">
<div class="item__photo">
<div class="photo photo--p8"></div>
<div class="item__check"></div>
</div>
<div class="item__status">4/5 frames</div>
</div>
</div>
<nav class="bottomnav">
<div class="bottomnav__item">🏠<span>Home</span></div>
<div class="bottomnav__item bottomnav__item--active">📷<span>Library</span></div>
<div class="bottomnav__item">⚙️<span>Settings</span></div>
</nav>
</div>
</body>
</html>
+146
View File
@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Concept 🅓 — Chip filter + dots</title>
<link rel="stylesheet" href="shared.css">
<style>
.filter-row {
overflow-x: auto;
display: flex; gap: var(--space-2);
padding: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--color-border);
background: var(--color-surface);
}
.filter-row::-webkit-scrollbar { display: none; }
.filter-row button { white-space: nowrap; }
.grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); padding: var(--space-4); }
.item__row {
display: flex; align-items: center; justify-content: space-between;
padding: var(--space-1);
}
.item__dots {
display: flex; gap: 4px;
}
.dot {
width: 18px; height: 18px; border-radius: 50%;
display: inline-flex; align-items: center; justify-content: center;
font-size: 9px; font-weight: 700;
background: var(--color-surface-2); color: var(--color-text-muted);
border: 1px solid var(--color-border);
}
.dot--on { background: var(--color-primary); color: white; border-color: var(--color-primary); }
.dot--lock { background: var(--color-lock); color: white; border-color: var(--color-lock); }
</style>
</head>
<body>
<div class="frame">
<header class="topbar">
<h1>Library</h1>
<div class="topbar__tabs">
<button class="topbar__tab topbar__tab--active">My Photos</button>
<button class="topbar__tab">Shared</button>
</div>
</header>
<div class="filter-row">
<button class="chip chip--on">All</button>
<button class="chip">Living Room</button>
<button class="chip">Kitchen</button>
<button class="chip">Bedroom</button>
<button class="chip">Mom's Place</button>
<button class="chip">Cabin</button>
</div>
<div class="note">
<b>🅓</b> A horizontally-scrolling chip bar at the top filters the grid
by frame. Each photo shows little colour dots representing the frames it's
on (initials, lock-flag for the locked frame). Tap a photo to manage in
a sheet. Works at any N because the dots can scroll-truncate.
</div>
<div class="grid">
<div class="item">
<div class="photo photo--p1"></div>
<div class="item__row">
<div class="item__dots">
<span class="dot dot--on dot--lock" title="Locked on Living Room">LR</span>
<span class="dot dot--on">KT</span>
<span class="dot dot--on">MP</span>
<span class="dot">BR</span>
<span class="dot">CA</span>
</div>
</div>
</div>
<div class="item">
<div class="photo photo--p2"></div>
<div class="item__row">
<div class="item__dots">
<span class="dot dot--on dot--lock">LR</span>
<span class="dot dot--on">BR</span>
<span class="dot">KT</span>
<span class="dot">MP</span>
<span class="dot">CA</span>
</div>
</div>
</div>
<div class="item">
<div class="photo photo--p3"></div>
<div class="item__row">
<div class="item__dots">
<span class="dot dot--on">LR</span>
<span class="dot dot--on">KT</span>
<span class="dot dot--on">BR</span>
<span class="dot dot--on">MP</span>
<span class="dot dot--on">CA</span>
</div>
</div>
</div>
<div class="item">
<div class="photo photo--p4"></div>
<div class="item__row">
<div class="item__dots">
<span class="dot dot--on dot--lock">MP</span>
<span class="dot dot--on">KT</span>
<span class="dot">LR</span>
<span class="dot">BR</span>
<span class="dot">CA</span>
</div>
</div>
</div>
<div class="item">
<div class="photo photo--p5"></div>
<div class="item__row">
<div class="item__dots">
<span class="dot dot--on">LR</span>
<span class="dot dot--on">CA</span>
<span class="dot">KT</span>
<span class="dot">BR</span>
<span class="dot">MP</span>
</div>
</div>
</div>
<div class="item">
<div class="photo photo--p6"></div>
<div class="item__row">
<div class="item__dots">
<span class="dot dot--on">LR</span>
<span class="dot dot--on">KT</span>
<span class="dot dot--on">BR</span>
<span class="dot">MP</span>
<span class="dot">CA</span>
</div>
</div>
</div>
</div>
<nav class="bottomnav">
<div class="bottomnav__item">🏠<span>Home</span></div>
<div class="bottomnav__item bottomnav__item--active">📷<span>Library</span></div>
<div class="bottomnav__item">⚙️<span>Settings</span></div>
</nav>
</div>
</body>
</html>
+129
View File
@@ -0,0 +1,129 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Library — current state</title>
<link rel="stylesheet" href="shared.css">
<style>
.grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); padding: var(--space-4); }
.item { display: flex; flex-direction: column; gap: var(--space-2); }
.approvals, .locks { display: flex; flex-wrap: wrap; gap: 4px; }
</style>
</head>
<body>
<div class="frame">
<header class="topbar">
<h1>Library</h1>
<div class="topbar__tabs">
<button class="topbar__tab topbar__tab--active">My Photos</button>
<button class="topbar__tab">Shared</button>
</div>
</header>
<div class="note">
<b>Pain point:</b> each photo card stacks two rows of 5 chips
(approve · lock). At 10 photos × 5 frames that's 100 buttons on screen.
</div>
<div class="grid">
<!-- one item × 10 -->
<div class="item">
<div class="photo photo--p1"></div>
<div class="approvals">
<button class="chip chip--device chip--on">Living Room</button>
<button class="chip chip--device chip--on">Kitchen</button>
<button class="chip chip--device">Bedroom</button>
<button class="chip chip--device chip--on">Mom's Place</button>
<button class="chip chip--device">Cabin</button>
</div>
<div class="locks">
<button class="chip chip--lock chip--device">🔒 Living Room</button>
<button class="chip chip--lock chip--device">🔒 Kitchen</button>
<button class="chip chip--lock chip--device">🔒 Mom's Place</button>
</div>
</div>
<div class="item">
<div class="photo photo--p2"></div>
<div class="approvals">
<button class="chip chip--device chip--on">Living Room</button>
<button class="chip chip--device">Kitchen</button>
<button class="chip chip--device chip--on">Bedroom</button>
<button class="chip chip--device">Mom's Place</button>
<button class="chip chip--device">Cabin</button>
</div>
<div class="locks">
<button class="chip chip--lock chip--device chip--lock-on">🔒 Living Room</button>
<button class="chip chip--lock chip--device">🔒 Bedroom</button>
</div>
</div>
<div class="item">
<div class="photo photo--p3"></div>
<div class="approvals">
<button class="chip chip--device chip--on">Living Room</button>
<button class="chip chip--device chip--on">Kitchen</button>
<button class="chip chip--device chip--on">Bedroom</button>
<button class="chip chip--device chip--on">Mom's Place</button>
<button class="chip chip--device chip--on">Cabin</button>
</div>
<div class="locks">
<button class="chip chip--lock chip--device">🔒 Living Room</button>
<button class="chip chip--lock chip--device">🔒 Kitchen</button>
<button class="chip chip--lock chip--device">🔒 Bedroom</button>
<button class="chip chip--lock chip--device">🔒 Mom's Place</button>
<button class="chip chip--lock chip--device">🔒 Cabin</button>
</div>
</div>
<div class="item">
<div class="photo photo--p4"></div>
<div class="approvals">
<button class="chip chip--device">Living Room</button>
<button class="chip chip--device chip--on">Kitchen</button>
<button class="chip chip--device">Bedroom</button>
<button class="chip chip--device chip--on">Mom's Place</button>
<button class="chip chip--device">Cabin</button>
</div>
<div class="locks">
<button class="chip chip--lock chip--device">🔒 Kitchen</button>
<button class="chip chip--lock chip--device">🔒 Mom's Place</button>
</div>
</div>
<div class="item">
<div class="photo photo--p5"></div>
<div class="approvals">
<button class="chip chip--device chip--on">Living Room</button>
<button class="chip chip--device">Kitchen</button>
<button class="chip chip--device">Bedroom</button>
<button class="chip chip--device">Mom's Place</button>
<button class="chip chip--device chip--on">Cabin</button>
</div>
<div class="locks">
<button class="chip chip--lock chip--device">🔒 Living Room</button>
<button class="chip chip--lock chip--device">🔒 Cabin</button>
</div>
</div>
<div class="item">
<div class="photo photo--p6"></div>
<div class="approvals">
<button class="chip chip--device chip--on">Living Room</button>
<button class="chip chip--device chip--on">Kitchen</button>
<button class="chip chip--device chip--on">Bedroom</button>
<button class="chip chip--device">Mom's Place</button>
<button class="chip chip--device">Cabin</button>
</div>
<div class="locks">
<button class="chip chip--lock chip--device">🔒 Living Room</button>
<button class="chip chip--lock chip--device">🔒 Kitchen</button>
<button class="chip chip--lock chip--device">🔒 Bedroom</button>
</div>
</div>
</div>
<nav class="bottomnav">
<div class="bottomnav__item">🏠<span>Home</span></div>
<div class="bottomnav__item bottomnav__item--active">📷<span>Library</span></div>
<div class="bottomnav__item">⚙️<span>Settings</span></div>
</nav>
</div>
</body>
</html>
+60
View File
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Library — design concepts</title>
<link rel="stylesheet" href="shared.css">
<style>
body { padding: var(--space-6); }
.wrap { max-width: 720px; margin: 0 auto; }
h1 { font-size: var(--text-2xl); margin: 0 0 var(--space-2); }
p { color: var(--color-text-muted); line-height: 1.5; }
ul.concepts { list-style: none; padding: 0; margin: var(--space-6) 0 0; display: grid; gap: var(--space-3); }
ul.concepts a {
display: block; padding: var(--space-4); border-radius: var(--radius-md);
background: var(--color-surface); border: 1px solid var(--color-border);
text-decoration: none; color: inherit;
}
ul.concepts a:hover { border-color: var(--color-primary); }
ul.concepts h2 { margin: 0 0 var(--space-1); font-size: var(--text-lg); }
ul.concepts small { color: var(--color-text-muted); }
</style>
</head>
<body>
<div class="wrap">
<h1>Library page — scaling to many frames</h1>
<p>Static HTML wireframes for the five concepts in the BMAD design memo. Each
uses the same design tokens as the real Vue app, fake content set at five
devices and ten photos so chip-count pain is visible.</p>
<ul class="concepts">
<li><a href="current.html">
<h2>🚫 Current state (for reference)</h2>
<small>What's shipping today. Five chip-buttons per photo × twice
(approve + lock) at N=5 frames.</small>
</a></li>
<li><a href="concept-a.html">
<h2>🅐 Photo card + status badge → DevicePicker sheet (recommended)</h2>
<small>Strip the card to image + one-line summary. Tap photo opens the
existing DevicePicker bottom sheet.</small>
</a></li>
<li><a href="concept-b.html">
<h2>🅑 Device-first tab</h2>
<small><code>My Photos</code> / <code>By Frame</code> toggle. By-Frame
groups photos by device row.</small>
</a></li>
<li><a href="concept-c.html">
<h2>🅒 Multi-select + bulk action bar</h2>
<small>Long-press a photo to enter selection mode. Top bar morphs to
Send-to/Lock/Delete.</small>
</a></li>
<li><a href="concept-d.html">
<h2>🅓 Device chip filter + photo dots</h2>
<small>Top row of device chips filters the grid. Each photo shows tiny
dots for the other frames it's on.</small>
</a></li>
</ul>
</div>
</body>
</html>
+109
View File
@@ -0,0 +1,109 @@
/* Design tokens cloned from frontend/src/styles/global.scss so mockups
feel native without the full Vue app shell. */
:root {
--text-xs: 11px; --text-sm: 13px; --text-base: 15px; --text-md: 17px;
--text-lg: 20px; --text-xl: 24px; --text-2xl: 28px;
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
--space-5: 20px; --space-6: 24px; --space-8: 32px;
--radius-sm: 6px; --radius-md: 12px; --radius-lg: 20px; --radius-full: 9999px;
--color-bg: #fdf6ee; --color-surface: #fff9f2; --color-surface-2: #f5ead8;
--color-border: #e8d9c4; --color-text: #3a2e22; --color-text-muted: #8a7060;
--color-primary: #c97c3a; --color-primary-fg: #ffffff;
--color-secondary: #e8d9c4; --color-destructive: #c0392b;
--color-lock: #c49a20;
--shadow-card: 0 2px 4px rgba(58, 46, 34, 0.06);
}
* { box-sizing: border-box; }
body {
margin: 0; padding: 0; background: var(--color-bg); color: var(--color-text);
font-family: -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
font-size: var(--text-base);
}
button { font-family: inherit; cursor: pointer; }
/* Mac-friendly viewport — show the mobile width centered on a wide screen */
.frame {
max-width: 420px;
margin: 0 auto;
min-height: 100dvh;
background: var(--color-bg);
border-left: 1px solid var(--color-border);
border-right: 1px solid var(--color-border);
position: relative;
}
/* Topbar */
.topbar {
position: sticky; top: 0; z-index: 5;
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
padding: var(--space-4) var(--space-4) var(--space-2);
}
.topbar h1 { font-size: var(--text-xl); margin: 0; font-weight: 700; }
.topbar__tabs { display: flex; gap: var(--space-2); margin-top: var(--space-3); }
.topbar__tab {
background: transparent; border: 0;
padding: var(--space-2) var(--space-3);
font-size: var(--text-sm); font-weight: 600;
color: var(--color-text-muted);
border-radius: var(--radius-sm);
}
.topbar__tab--active { color: var(--color-text); background: var(--color-surface-2); }
/* Generic chip / button */
.chip {
display: inline-flex; align-items: center; gap: 4px;
padding: 5px 10px;
border-radius: var(--radius-full);
background: var(--color-surface-2);
border: 1px solid var(--color-border);
font-size: var(--text-xs); font-weight: 600;
color: var(--color-text-muted);
}
.chip--on { background: var(--color-primary); color: var(--color-primary-fg); border-color: var(--color-primary); }
.chip--lock { background: #fff4d1; border-color: var(--color-lock); color: #6b5210; }
.chip--lock-on { background: var(--color-lock); color: white; border-color: var(--color-lock); }
.chip--device { padding: 3px 8px; font-size: var(--text-xs); }
/* Photo placeholder — colored gradient so each one is visually distinct */
.photo {
display: block; width: 100%; aspect-ratio: 1/1;
border-radius: var(--radius-sm);
background: linear-gradient(135deg, #d4a37e, #8c5a3a);
position: relative; overflow: hidden;
}
.photo--p1 { background: linear-gradient(135deg, #c97c3a, #6b3e1e); }
.photo--p2 { background: linear-gradient(135deg, #3a8a6e, #1f4d3d); }
.photo--p3 { background: linear-gradient(135deg, #8c6fc1, #4c3a82); }
.photo--p4 { background: linear-gradient(135deg, #e0a85c, #a5602c); }
.photo--p5 { background: linear-gradient(135deg, #5e9bb0, #2f5f72); }
.photo--p6 { background: linear-gradient(135deg, #d4a37e, #8c5a3a); }
.photo--p7 { background: linear-gradient(135deg, #6f8c5e, #3e5630); }
.photo--p8 { background: linear-gradient(135deg, #c1715a, #6e3a2d); }
.photo--p9 { background: linear-gradient(135deg, #a08fb1, #62506f); }
.photo--p10{ background: linear-gradient(135deg, #d3a87b, #84583a); }
/* Portrait-aspect variant for the photo simulating a portrait crop */
.photo--portrait { aspect-ratio: 3/4; }
/* Footer nav (decorative, just to look like the real PWA) */
.bottomnav {
position: sticky; bottom: 0;
background: var(--color-surface);
border-top: 1px solid var(--color-border);
padding: var(--space-3); display: flex; gap: var(--space-4);
justify-content: space-around; font-size: var(--text-xs);
color: var(--color-text-muted);
}
.bottomnav__item { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.bottomnav__item--active { color: var(--color-primary); }
/* Mockup note panel */
.note {
background: #fff5dc;
border: 1px dashed var(--color-lock);
border-radius: var(--radius-sm);
padding: var(--space-3); margin: var(--space-3) var(--space-4);
font-size: var(--text-sm); color: #6b5210;
}
.note b { color: #3a2e22; }