opencd: murdock prototype — opencd.css + three templates (jewel-case, leaflet, back-tray)
Prototype build incorporating Amy's gate review findings: - Custom --cd-surface-* tokens (oklch), not Open Props - Custom --cd-grid-* tokens via gray-3/gray-2 - --cd-scale: 1 = default 2x display scale, all dims calc() - --cd-font-label using font-neo-grotesque for spine legibility Files: - opencd.css (622 lines) — full monolith with variables, reset, components, grid overlays, advisory badge, print styles, responsive containers - templates/jewel-case.html — front jewel case with spine + disc + advisory - templates/leaflet.html — 4-page booklet with page-turn navigation - templates/back-tray.html — back tray with tracklist, dual spines, grid GPG: ABE295FFEB4571F8 — H.M. Murdock <murdock@a-team.dev>
This commit is contained in:
parent
f3d4fc1626
commit
e3dbfa2fd1
4 changed files with 1090 additions and 0 deletions
623
opencd.css
Normal file
623
opencd.css
Normal file
|
|
@ -0,0 +1,623 @@
|
||||||
|
/* ==========================================================================
|
||||||
|
OpenCD — Jewel Case CSS Framework
|
||||||
|
Prototype: single-file system
|
||||||
|
Author: H.M. Murdock <murdock@a-team.dev>
|
||||||
|
Base: Open Props v2 via unpkg
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@import "https://unpkg.com/open-props";
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
1. Custom Properties — CD Dimension & Design Tokens
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* ── Scale ──
|
||||||
|
--cd-scale: 1 → default 2× display scale (280px = 142mm at 2×)
|
||||||
|
Set to 0.5 for 1× (physical mm → px at 96dpi), 2 for 4×, etc.
|
||||||
|
*/
|
||||||
|
--cd-scale: 1;
|
||||||
|
|
||||||
|
/* ── Physical CD Dimensions (at 2× scale) ── */
|
||||||
|
--cd-jewel-width: calc(280px * var(--cd-scale));
|
||||||
|
--cd-jewel-height: calc(245px * var(--cd-scale));
|
||||||
|
--cd-jewel-open-width: calc(560px * var(--cd-scale));
|
||||||
|
--cd-disc-diameter: calc(240px * var(--cd-scale));
|
||||||
|
--cd-disc-hole: calc(30px * var(--cd-scale)); /* Ø15mm × 2 */
|
||||||
|
--cd-leaflet-size: calc(240px * var(--cd-scale));
|
||||||
|
--cd-spine-width: calc(14px * var(--cd-scale));
|
||||||
|
--cd-tray-width: calc(260px * var(--cd-scale));
|
||||||
|
|
||||||
|
/* ── Derived aspect ratios ── */
|
||||||
|
--cd-aspect-jewel: 280 / 245;
|
||||||
|
--cd-aspect-leaflet: 1 / 1;
|
||||||
|
--cd-aspect-disc: 1 / 1;
|
||||||
|
|
||||||
|
/* ── Surface Colors (ASW-style, NOT from Open Props — Amy finding #1) ── */
|
||||||
|
--cd-surface-1: oklch(30% .015 265); /* tray paper base */
|
||||||
|
--cd-surface-2: oklch(35% .015 265); /* tray raised panel */
|
||||||
|
--cd-surface-3: oklch(40% .015 265); /* leaflet inner area */
|
||||||
|
--cd-surface-4: oklch(15% .01 265); /* spine background — darkest */
|
||||||
|
|
||||||
|
/* ── Typography ── */
|
||||||
|
--cd-font-label: var(--font-neo-grotesque); /* Inter/Roboto stack — Amy finding #4 */
|
||||||
|
--cd-font-body: var(--font-serif);
|
||||||
|
--cd-font-mono: var(--font-mono-code, var(--font-mono));
|
||||||
|
--cd-font-size-body: var(--font-size-fluid-1);
|
||||||
|
--cd-font-size-title: var(--font-size-fluid-2);
|
||||||
|
--cd-font-size-display: var(--font-size-fluid-3);
|
||||||
|
|
||||||
|
/* ── Spacing insets ── */
|
||||||
|
--cd-space-inset: var(--size-fluid-1);
|
||||||
|
--cd-space-stack: var(--size-fluid-2);
|
||||||
|
--cd-space-gutter: var(--size-fluid-3);
|
||||||
|
|
||||||
|
/* ── Semantic Text Colors ── */
|
||||||
|
--cd-text-primary: var(--gray-9);
|
||||||
|
--cd-text-secondary: var(--gray-7);
|
||||||
|
--cd-text-muted: var(--gray-5);
|
||||||
|
--cd-text-on-spine: var(--gray-1);
|
||||||
|
|
||||||
|
/* ── Surface / Tray Mappings ── */
|
||||||
|
--cd-tray-bg: var(--cd-surface-1);
|
||||||
|
--cd-tray-bg-raised: var(--cd-surface-2);
|
||||||
|
--cd-tray-bg-sunken: var(--cd-surface-3);
|
||||||
|
--cd-spine-bg: var(--cd-surface-4);
|
||||||
|
|
||||||
|
/* ── Grid Colors (custom — NOT Open Props tokens, Amy finding #2) ── */
|
||||||
|
--cd-grid-color: var(--gray-3);
|
||||||
|
--cd-grid-color-alt: var(--gray-2);
|
||||||
|
--cd-grid-fine: 4px;
|
||||||
|
--cd-grid-coarse: 8px;
|
||||||
|
|
||||||
|
/* ── Advisory Badge ── */
|
||||||
|
--cd-advisory-red: var(--red-6);
|
||||||
|
--cd-advisory-red-dark: var(--red-7);
|
||||||
|
--cd-advisory-bg: var(--red-0);
|
||||||
|
|
||||||
|
/* ── Jewel Case Decorations ── */
|
||||||
|
--cd-jewel-radius: var(--radius-2); /* 5px */
|
||||||
|
--cd-jewel-shadow: var(--shadow-2);
|
||||||
|
--cd-jewel-shadow-raised: var(--shadow-3);
|
||||||
|
--cd-leaflet-radius: var(--radius-3); /* 1rem */
|
||||||
|
|
||||||
|
/* ── Spine Typography ── */
|
||||||
|
--cd-spine-font-xs: var(--font-size-0);
|
||||||
|
--cd-spine-font-sm: var(--font-size-1);
|
||||||
|
--cd-spine-font-md: var(--font-size-2);
|
||||||
|
--cd-spine-font-lg: var(--font-size-3);
|
||||||
|
|
||||||
|
/* ── Motion & Print ── */
|
||||||
|
--cd-transition-duration: 200ms;
|
||||||
|
--cd-ease-flip: cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
--cd-print-jewel-width: 142mm;
|
||||||
|
--cd-print-jewel-height: 125mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
2. Reset
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
3. Jewel Case Container
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.cd-jewel-case {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--cd-spine-width) 1fr;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
width: var(--cd-jewel-width);
|
||||||
|
min-height: var(--cd-jewel-height);
|
||||||
|
background: var(--gray-0);
|
||||||
|
border-radius: var(--cd-jewel-radius);
|
||||||
|
box-shadow: var(--cd-jewel-shadow);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
transition: width var(--cd-transition-duration) var(--ease-3);
|
||||||
|
font-family: var(--cd-font-body);
|
||||||
|
color: var(--cd-text-primary);
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-jewel-case--open {
|
||||||
|
width: var(--cd-jewel-open-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-jewel-case[data-jewel-state="open"] {
|
||||||
|
width: var(--cd-jewel-open-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Internal layout wrapper ── */
|
||||||
|
.cd-jewel-case > .cd-jewel-inner {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1 / -1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: var(--cd-space-inset);
|
||||||
|
gap: var(--cd-space-stack);
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
4. Spine
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.cd-spine {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1 / -1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--cd-spine-bg);
|
||||||
|
color: var(--cd-text-on-spine);
|
||||||
|
font-family: var(--cd-font-label);
|
||||||
|
padding: var(--size-2) var(--size-1);
|
||||||
|
gap: var(--size-2);
|
||||||
|
writing-mode: vertical-rl;
|
||||||
|
text-orientation: mixed;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-spine .spine-label {
|
||||||
|
font-size: var(--cd-spine-font-sm);
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: var(--font-letterspacing-3, 0.1em);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-spine .spine-track {
|
||||||
|
font-size: var(--cd-spine-font-xs);
|
||||||
|
letter-spacing: var(--font-letterspacing-1, 0.05em);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Side spine variant ── */
|
||||||
|
.cd-spine--side {
|
||||||
|
writing-mode: horizontal-tb;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
5. Leaflet Content & Pages
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.leaflet-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--cd-space-stack);
|
||||||
|
max-width: var(--cd-leaflet-size);
|
||||||
|
position: relative;
|
||||||
|
background:
|
||||||
|
repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
transparent 0 calc(var(--cd-grid-coarse) * 3 - 1px),
|
||||||
|
var(--cd-grid-color) calc(var(--cd-grid-coarse) * 3 - 1px) calc(var(--cd-grid-coarse) * 3)
|
||||||
|
),
|
||||||
|
var(--cd-tray-bg);
|
||||||
|
border-radius: var(--cd-leaflet-radius);
|
||||||
|
padding: var(--cd-space-inset);
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-page {
|
||||||
|
aspect-ratio: var(--cd-aspect-leaflet);
|
||||||
|
background: var(--gray-0);
|
||||||
|
border-radius: calc(var(--cd-leaflet-radius) / 2);
|
||||||
|
padding: var(--cd-space-inset);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--size-2);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,.08);
|
||||||
|
transition: transform var(--cd-transition-duration) var(--cd-ease-flip);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-page[data-leaflet-active="true"] {
|
||||||
|
box-shadow: var(--cd-jewel-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-page[data-leaflet-direction="rtl"] {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-page h1,
|
||||||
|
.leaflet-page h2 {
|
||||||
|
font-family: var(--cd-font-label);
|
||||||
|
font-size: var(--cd-font-size-title);
|
||||||
|
color: var(--cd-text-primary);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-page p {
|
||||||
|
font-family: var(--cd-font-body);
|
||||||
|
font-size: var(--cd-font-size-body);
|
||||||
|
color: var(--cd-text-secondary);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-page .leaflet-meta {
|
||||||
|
font-family: var(--cd-font-mono);
|
||||||
|
font-size: var(--cd-spine-font-xs);
|
||||||
|
color: var(--cd-text-muted);
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
6. Disc Art
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.disc-art {
|
||||||
|
width: var(--cd-disc-diameter);
|
||||||
|
aspect-ratio: var(--cd-aspect-disc);
|
||||||
|
border-radius: 50%;
|
||||||
|
background:
|
||||||
|
radial-gradient(
|
||||||
|
circle at 30% 30%,
|
||||||
|
var(--gray-4) 0%,
|
||||||
|
var(--gray-2) 40%,
|
||||||
|
var(--gray-6) 70%,
|
||||||
|
var(--gray-2) 100%
|
||||||
|
);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: inset 0 0 8px rgba(0,0,0,.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.disc-art::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: var(--cd-disc-hole);
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--gray-0);
|
||||||
|
box-shadow: inset 0 0 3px rgba(0,0,0,.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.disc-hole {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(var(--cd-disc-hole) * 0.6);
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: calc(var(--cd-disc-hole) * 0.5);
|
||||||
|
color: var(--gray-4);
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Disc art variants ── */
|
||||||
|
.disc-art--sheen {
|
||||||
|
background:
|
||||||
|
radial-gradient(
|
||||||
|
circle at 25% 25%,
|
||||||
|
var(--gray-4) 0%,
|
||||||
|
var(--gray-2) 35%,
|
||||||
|
var(--gray-5) 50%,
|
||||||
|
transparent 65%
|
||||||
|
),
|
||||||
|
radial-gradient(
|
||||||
|
circle at 75% 75%,
|
||||||
|
var(--gray-7) 0%,
|
||||||
|
var(--gray-3) 40%,
|
||||||
|
transparent 70%
|
||||||
|
),
|
||||||
|
repeating-radial-gradient(
|
||||||
|
circle at 50%,
|
||||||
|
transparent 0 calc(var(--cd-disc-hole) * 0.5),
|
||||||
|
var(--gray-3) calc(var(--cd-disc-hole) * 0.5) calc(var(--cd-disc-hole) * 0.5 + 1px)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
7. Human Advisory Badge
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.human-advisory {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
border: 2px solid #000;
|
||||||
|
font-family: Impact, "Arial Black", var(--cd-font-label), sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0;
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: var(--cd-space-inset);
|
||||||
|
right: var(--cd-space-inset);
|
||||||
|
}
|
||||||
|
|
||||||
|
.human-advisory .advisory-row {
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--size-1) var(--size-3);
|
||||||
|
font-size: var(--cd-spine-font-sm);
|
||||||
|
letter-spacing: var(--font-letterspacing-3, 0.1em);
|
||||||
|
line-height: 1.2;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.human-advisory .advisory-row--black {
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.human-advisory .advisory-row--white {
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.human-advisory .advisory-row--red {
|
||||||
|
background: var(--cd-advisory-red);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Human Advisory variants ── */
|
||||||
|
.human-advisory--red .advisory-row--black {
|
||||||
|
background: var(--cd-advisory-red-dark);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.human-advisory--red .advisory-row--white {
|
||||||
|
background: var(--cd-advisory-red);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
8. Back Tray
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.back-tray {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--cd-spine-width) 1fr var(--cd-spine-width);
|
||||||
|
background: var(--cd-tray-bg);
|
||||||
|
border-top: 1px solid var(--cd-grid-color);
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-tray .tray-credits {
|
||||||
|
grid-column: 2;
|
||||||
|
padding: var(--cd-space-inset);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--cd-space-stack);
|
||||||
|
background: var(--cd-tray-bg-raised);
|
||||||
|
border-radius: 0 0 var(--cd-leaflet-radius) var(--cd-leaflet-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tray-credits h2 {
|
||||||
|
font-family: var(--cd-font-label);
|
||||||
|
font-size: var(--cd-font-size-title);
|
||||||
|
color: var(--cd-text-primary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: var(--font-letterspacing-2, 0.075em);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tray-credits ol {
|
||||||
|
list-style: none;
|
||||||
|
counter-reset: track;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--size-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tray-credits ol li {
|
||||||
|
counter-increment: track;
|
||||||
|
font-family: var(--cd-font-label);
|
||||||
|
font-size: var(--cd-font-size-body);
|
||||||
|
color: var(--cd-text-secondary);
|
||||||
|
padding: var(--size-1) 0;
|
||||||
|
border-bottom: 1px solid var(--cd-grid-color-alt);
|
||||||
|
display: flex;
|
||||||
|
gap: var(--size-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tray-credits ol li::before {
|
||||||
|
content: counter(track, decimal-leading-zero) ".";
|
||||||
|
font-family: var(--cd-font-mono);
|
||||||
|
color: var(--cd-text-muted);
|
||||||
|
min-width: 2.5ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tray-credits .credits-label {
|
||||||
|
font-family: var(--cd-font-mono);
|
||||||
|
font-size: var(--cd-spine-font-xs);
|
||||||
|
color: var(--cd-text-muted);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: var(--font-letterspacing-1, 0.05em);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tray-credits p {
|
||||||
|
font-family: var(--cd-font-body);
|
||||||
|
font-size: var(--cd-font-size-body);
|
||||||
|
color: var(--cd-text-secondary);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Back tray spine labels ── */
|
||||||
|
.back-tray .cd-spine {
|
||||||
|
grid-row: 1;
|
||||||
|
background: var(--cd-spine-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
9. Grid Overlay Utility
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.cd-grid {
|
||||||
|
background-image:
|
||||||
|
repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
transparent 0 calc(var(--cd-grid-coarse) - 1px),
|
||||||
|
var(--cd-grid-color) calc(var(--cd-grid-coarse) - 1px) var(--cd-grid-coarse)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-grid--fine {
|
||||||
|
background-image:
|
||||||
|
repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
transparent 0 calc(var(--cd-grid-fine) - 1px),
|
||||||
|
var(--cd-grid-color) calc(var(--cd-grid-fine) - 1px) var(--cd-grid-fine)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-grid--coarse {
|
||||||
|
background-image:
|
||||||
|
repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
transparent 0 calc(var(--cd-grid-coarse) - 1px),
|
||||||
|
var(--cd-grid-color) calc(var(--cd-grid-coarse) - 1px) var(--cd-grid-coarse)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-grid--crosshatch {
|
||||||
|
background-image:
|
||||||
|
repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
transparent 0 calc(var(--cd-grid-fine) - 1px),
|
||||||
|
var(--cd-grid-color-alt) calc(var(--cd-grid-fine) - 1px) var(--cd-grid-fine)
|
||||||
|
),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent 0 calc(var(--cd-grid-coarse) - 1px),
|
||||||
|
var(--cd-grid-color) calc(var(--cd-grid-coarse) - 1px) var(--cd-grid-coarse)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
10. Open / Closed State Transitions
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.cd-jewel-case .back-tray {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-jewel-case--open .back-tray,
|
||||||
|
.cd-jewel-case[data-jewel-state="open"] .back-tray {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
11. Responsive Breakpoints (Open Props container queries)
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@container (max-width: 350px) {
|
||||||
|
.cd-jewel-case {
|
||||||
|
width: 100% !important;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-spine {
|
||||||
|
writing-mode: horizontal-tb;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: var(--size-1) var(--size-2);
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-jewel-inner {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disc-art {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: var(--cd-disc-diameter);
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-content {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.human-advisory {
|
||||||
|
position: static;
|
||||||
|
margin-top: var(--cd-space-stack);
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (min-width: 351px) and (max-width: 549px) {
|
||||||
|
.cd-jewel-case {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: var(--cd-jewel-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-content {
|
||||||
|
gap: var(--size-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.human-advisory .advisory-row {
|
||||||
|
font-size: var(--cd-spine-font-xs);
|
||||||
|
padding: var(--size-1) var(--size-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
12. Print Styles
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.cd-jewel-case {
|
||||||
|
width: var(--cd-print-jewel-width);
|
||||||
|
height: var(--cd-print-jewel-height);
|
||||||
|
box-shadow: none;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-jewel-case--open {
|
||||||
|
width: calc(var(--cd-print-jewel-width) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-tray {
|
||||||
|
display: grid !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.human-advisory {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disc-art {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-jewel-case,
|
||||||
|
.leaflet-page,
|
||||||
|
.back-tray {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
13. Grain Texture Utility
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.cd-grain {
|
||||||
|
background-image: repeating-radial-gradient(
|
||||||
|
circle at 50% 50%,
|
||||||
|
transparent 0 1px,
|
||||||
|
rgba(0,0,0,.02) 1px 2px,
|
||||||
|
transparent 2px 4px
|
||||||
|
),
|
||||||
|
repeating-radial-gradient(
|
||||||
|
circle at 100% 100%,
|
||||||
|
transparent 0 3px,
|
||||||
|
rgba(0,0,0,.015) 3px 4px,
|
||||||
|
transparent 4px 8px
|
||||||
|
);
|
||||||
|
background-size: 120px 120px, 200px 200px;
|
||||||
|
}
|
||||||
152
templates/back-tray.html
Normal file
152
templates/back-tray.html
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>OpenCD · Back Tray</title>
|
||||||
|
<link rel="stylesheet" href="../opencd.css">
|
||||||
|
<style>
|
||||||
|
/* Demo page chrome */
|
||||||
|
body {
|
||||||
|
min-height: 100dvh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2rem;
|
||||||
|
padding: 2rem;
|
||||||
|
background: oklch(92% .012 85);
|
||||||
|
font-family: var(--font-system-ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls input[type="checkbox"] {
|
||||||
|
accent-color: var(--red-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-footnote {
|
||||||
|
font-size: .75rem;
|
||||||
|
color: var(--gray-6);
|
||||||
|
text-align: center;
|
||||||
|
max-width: 480px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Back-tray specific layout
|
||||||
|
============================================================ */
|
||||||
|
/* Wrap the back-tray in a jewel-case frame */
|
||||||
|
.demo-tray-container {
|
||||||
|
width: var(--cd-jewel-width);
|
||||||
|
border-radius: var(--cd-jewel-radius);
|
||||||
|
box-shadow: var(--cd-jewel-shadow);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================
|
||||||
|
BACK TRAY — Tracklist & Credits
|
||||||
|
============================================================ -->
|
||||||
|
<div class="demo-tray-container">
|
||||||
|
|
||||||
|
<div class="back-tray cd-grid--crosshatch">
|
||||||
|
|
||||||
|
<!-- Left spine -->
|
||||||
|
<nav class="cd-spine cd-spine--side">
|
||||||
|
<span class="spine-label">OPENCD</span>
|
||||||
|
<span class="spine-track">03 · back-tray</span>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Credits / tracklist -->
|
||||||
|
<section class="tray-credits">
|
||||||
|
|
||||||
|
<span class="credits-label">Tracklist</span>
|
||||||
|
<ol>
|
||||||
|
<li>jewel-case</li>
|
||||||
|
<li>leaflet</li>
|
||||||
|
<li>back-tray</li>
|
||||||
|
<li>human-advisory</li>
|
||||||
|
<li>disc-art</li>
|
||||||
|
<li>spine-navigation</li>
|
||||||
|
<li>grid-overlay</li>
|
||||||
|
<li>print-layout</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<span class="credits-label">Credits</span>
|
||||||
|
<p>
|
||||||
|
<strong>Design & Architecture</strong><br>
|
||||||
|
Hannibal · Face
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Engineering & Prototypes</strong><br>
|
||||||
|
Murdock · B.A. Baracus
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Quality & Verification</strong><br>
|
||||||
|
Amy Amanda Allen
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<span class="credits-label">Technical</span>
|
||||||
|
<p>
|
||||||
|
Built on Open Props v2 · Pure CSS framework · No build step required<br>
|
||||||
|
ISO 15727 jewel case dimensions · 2× display scale<br>
|
||||||
|
GPG-signed commits · A-Team design collective
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Right spine -->
|
||||||
|
<nav class="cd-spine cd-spine--side">
|
||||||
|
<span class="spine-label">TRENTUNA</span>
|
||||||
|
<span class="spine-track">MMXXV</span>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Demo controls -->
|
||||||
|
<div class="demo-controls">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="toggle-grid" checked onchange="document.querySelector('.back-tray').classList.toggle('cd-grid--crosshatch', this.checked)">
|
||||||
|
Show grid
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Scale:
|
||||||
|
<select id="scale-select" onchange="setScale(this.value)">
|
||||||
|
<option value="0.5">0.5× (1× physical)</option>
|
||||||
|
<option value="1" selected>1 (default 2×)</option>
|
||||||
|
<option value="1.5">1.5× (3×)</option>
|
||||||
|
<option value="2">2× (4×)</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="demo-footnote">
|
||||||
|
Back tray paper insert (151 × 118 mm physical) with dual spines, tracklist, and production credits · Grid overlay inspired by trentuna.com paper-card aesthetic
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function setScale(scale) {
|
||||||
|
document.documentElement.style.setProperty('--cd-scale', scale);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
131
templates/jewel-case.html
Normal file
131
templates/jewel-case.html
Normal 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.0">
|
||||||
|
<title>OpenCD · Jewel Case</title>
|
||||||
|
<link rel="stylesheet" href="../opencd.css">
|
||||||
|
<style>
|
||||||
|
/* Demo page chrome — not part of OpenCD */
|
||||||
|
body {
|
||||||
|
min-height: 100dvh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2rem;
|
||||||
|
padding: 2rem;
|
||||||
|
background: oklch(92% .012 85);
|
||||||
|
font-family: var(--font-system-ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls input[type="checkbox"] {
|
||||||
|
accent-color: var(--red-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls select {
|
||||||
|
padding: .25rem .5rem;
|
||||||
|
border: 1px solid var(--gray-5);
|
||||||
|
border-radius: var(--radius-2);
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-footnote {
|
||||||
|
font-size: .75rem;
|
||||||
|
color: var(--gray-6);
|
||||||
|
text-align: center;
|
||||||
|
max-width: 480px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================
|
||||||
|
JEWEL CASE — Closed (default)
|
||||||
|
============================================================ -->
|
||||||
|
<main class="cd-jewel-case" id="jewel" data-jewel-state="closed">
|
||||||
|
|
||||||
|
<!-- Spine -->
|
||||||
|
<nav class="cd-spine">
|
||||||
|
<span class="spine-label">OPENCD · TRENTUNA</span>
|
||||||
|
<span class="spine-track">01 · jewel-case</span>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Inner content -->
|
||||||
|
<div class="cd-jewel-inner">
|
||||||
|
|
||||||
|
<!-- Leaflet content area with disc art -->
|
||||||
|
<section class="leaflet-content cd-grid">
|
||||||
|
<article class="leaflet-page" data-leaflet-page="0" data-leaflet-active="true">
|
||||||
|
<h1>Jewel Case</h1>
|
||||||
|
<p>A compact disc (CD) jewel case is a three-piece plastic case designed to hold standard audio CDs, CD-ROMs, and other optical media. This prototype recreates the physical form factor in pure HTML and CSS.</p>
|
||||||
|
<span class="leaflet-meta">142 × 125 mm · 2× scale</span>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Disc art -->
|
||||||
|
<div class="disc-art disc-art--sheen">
|
||||||
|
<span class="disc-hole">◉</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Human Advisory badge -->
|
||||||
|
<aside class="human-advisory human-advisory--red">
|
||||||
|
<span class="advisory-row advisory-row--black">HUMAN</span>
|
||||||
|
<span class="advisory-row advisory-row--white">ADVISORY</span>
|
||||||
|
<span class="advisory-row advisory-row--red">PROTOTYPE CODE</span>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Demo controls -->
|
||||||
|
<div class="demo-controls">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="toggle-open" onchange="toggleOpen(this.checked)">
|
||||||
|
Open case
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Scale:
|
||||||
|
<select id="scale-select" onchange="setScale(this.value)">
|
||||||
|
<option value="0.5">0.5× (1× physical)</option>
|
||||||
|
<option value="1" selected>1 (default 2×)</option>
|
||||||
|
<option value="1.5">1.5× (3×)</option>
|
||||||
|
<option value="2">2× (4×)</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="demo-footnote">
|
||||||
|
An OpenCD prototype by H.M. Murdock · Built with Open Props · Pure CSS, no JS dependency
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function toggleOpen(open) {
|
||||||
|
const jewel = document.getElementById('jewel');
|
||||||
|
jewel.dataset.jewelState = open ? 'open' : 'closed';
|
||||||
|
jewel.classList.toggle('cd-jewel-case--open', open);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setScale(scale) {
|
||||||
|
document.documentElement.style.setProperty('--cd-scale', scale);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
184
templates/leaflet.html
Normal file
184
templates/leaflet.html
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>OpenCD · Leaflet</title>
|
||||||
|
<link rel="stylesheet" href="../opencd.css">
|
||||||
|
<style>
|
||||||
|
/* Demo page chrome */
|
||||||
|
body {
|
||||||
|
min-height: 100dvh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2rem;
|
||||||
|
padding: 2rem;
|
||||||
|
background: oklch(92% .012 85);
|
||||||
|
font-family: var(--font-system-ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: .875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls button {
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
border: 1px solid var(--gray-6);
|
||||||
|
border-radius: var(--radius-2);
|
||||||
|
background: var(--gray-0);
|
||||||
|
font-family: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background var(--cd-transition-duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls button:hover {
|
||||||
|
background: var(--gray-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls button:disabled {
|
||||||
|
opacity: .4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-controls .page-indicator {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: .875rem;
|
||||||
|
color: var(--gray-6);
|
||||||
|
min-width: 8ch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-footnote {
|
||||||
|
font-size: .75rem;
|
||||||
|
color: var(--gray-6);
|
||||||
|
text-align: center;
|
||||||
|
max-width: 480px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Leaflet-specific overrides for multi-page demo
|
||||||
|
============================================================ */
|
||||||
|
.leaflet-content--multi {
|
||||||
|
max-width: calc(var(--cd-leaflet-size) + var(--cd-space-inset) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-pages {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--cd-space-stack);
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-pages .leaflet-page {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-pages .leaflet-page[data-leaflet-active="true"] {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================
|
||||||
|
LEAFLET — Multi-page booklet
|
||||||
|
============================================================ -->
|
||||||
|
<main class="cd-jewel-case" style="width: auto; box-shadow: none; background: transparent;">
|
||||||
|
|
||||||
|
<div class="cd-jewel-inner" style="grid-column:1/-1; gap: var(--cd-space-inset);">
|
||||||
|
|
||||||
|
<section class="leaflet-content leaflet-content--multi cd-grid--crosshatch">
|
||||||
|
|
||||||
|
<div class="leaflet-pages" id="pages">
|
||||||
|
|
||||||
|
<article class="leaflet-page" data-leaflet-page="0" data-leaflet-active="true">
|
||||||
|
<h1>Design Notes</h1>
|
||||||
|
<p>This booklet is a demonstration of the leaflet component — the insert booklet found inside a standard CD jewel case. Each page measures 120 × 120 mm (240px at 2× scale), matching the ISO 15727 standard for CD packaging.</p>
|
||||||
|
<span class="leaflet-meta">Page 1 of 4 · v0.1.0</span>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="leaflet-page" data-leaflet-page="1" data-leaflet-active="false">
|
||||||
|
<h1>Architecture</h1>
|
||||||
|
<p>OpenCD is built on Open Props — a design token framework by Adam Argyle. Every dimension, color, and spacing value maps to a custom property, with fallback values for standalone use. The framework includes:</p>
|
||||||
|
<ul style="margin-left:1rem; color:var(--cd-text-secondary); line-height:1.6;">
|
||||||
|
<li>CD dimension tokens at 2× scale</li>
|
||||||
|
<li>Semantic color aliases on Open Props</li>
|
||||||
|
<li>ASW-inspired surface layers</li>
|
||||||
|
<li>Visible grid overlays (tray paper aesthetic)</li>
|
||||||
|
</ul>
|
||||||
|
<span class="leaflet-meta">Page 2 of 4 · v0.1.0</span>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="leaflet-page" data-leaflet-page="2" data-leaflet-active="false">
|
||||||
|
<h1>Token System</h1>
|
||||||
|
<p>The variable layer defines ~40 custom properties organized into semantic groups:</p>
|
||||||
|
<dl style="margin-top:.5rem; display:grid; gap:.25rem; font-size:var(--size-fluid-1);">
|
||||||
|
<dt style="font-family:var(--font-mono); color:var(--gray-6);">--cd-jewel-width</dt>
|
||||||
|
<dd style="color:var(--gray-7);">280px × scale</dd>
|
||||||
|
<dt style="font-family:var(--font-mono); color:var(--gray-6);">--cd-surface-1</dt>
|
||||||
|
<dd style="color:var(--gray-7);">oklch(30% .015 265)</dd>
|
||||||
|
<dt style="font-family:var(--font-mono); color:var(--gray-6);">--cd-font-label</dt>
|
||||||
|
<dd style="color:var(--gray-7);">font-neo-grotesque</dd>
|
||||||
|
</dl>
|
||||||
|
<span class="leaflet-meta">Page 3 of 4 · v0.1.0</span>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="leaflet-page" data-leaflet-page="3" data-leaflet-active="false">
|
||||||
|
<h1>Credits</h1>
|
||||||
|
<p>OpenCD is a project of the A-Team design collective — a framework for physical-digital design artifacts that bridge the gap between print packaging and web implementations.</p>
|
||||||
|
<p style="margin-top:.5rem;">Design: Hannibal & Face<br>Engineering: Murdock & B.A.<br>Quality: Amy</p>
|
||||||
|
<span class="leaflet-meta">Page 4 of 4 · MMXXV</span>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Human Advisory badge (small) -->
|
||||||
|
<aside class="human-advisory" style="position:static; align-self:flex-end;">
|
||||||
|
<span class="advisory-row advisory-row--black">PROTOTYPE</span>
|
||||||
|
<span class="advisory-row advisory-row--white">OPENCD v0.1</span>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Demo controls -->
|
||||||
|
<div class="demo-controls">
|
||||||
|
<button id="prev-btn" onclick="flipPage(-1)" disabled>← Prev</button>
|
||||||
|
<span class="page-indicator" id="page-indicator">Page 1 / 4</span>
|
||||||
|
<button id="next-btn" onclick="flipPage(1)">Next →</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="demo-footnote">
|
||||||
|
Leaflet booklet demo · 4-page layout at 120 × 120 mm each · Page-turn via inline JS
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let currentPage = 0;
|
||||||
|
const totalPages = 4;
|
||||||
|
|
||||||
|
function flipPage(direction) {
|
||||||
|
const pages = document.querySelectorAll('.leaflet-page');
|
||||||
|
const old = currentPage;
|
||||||
|
|
||||||
|
currentPage = Math.max(0, Math.min(totalPages - 1, currentPage + direction));
|
||||||
|
|
||||||
|
if (old === currentPage) return;
|
||||||
|
|
||||||
|
pages[old].dataset.leafletActive = 'false';
|
||||||
|
pages[currentPage].dataset.leafletActive = 'true';
|
||||||
|
|
||||||
|
document.getElementById('page-indicator').textContent = 'Page ' + (currentPage + 1) + ' / ' + totalPages;
|
||||||
|
document.getElementById('prev-btn').disabled = currentPage === 0;
|
||||||
|
document.getElementById('next-btn').disabled = currentPage === totalPages - 1;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue