DIY — 3D Print Model Page
A Thingiverse/Printables-style 3D model detail page for a fictional parametric cable organizer clip, rendered in a blueprint-and-safety-orange maker aesthetic. It pairs a swappable SVG gallery (iso render, dimensioned side profile, exploded view, in-use scene) with a download rail featuring fake progress, like/collect/follow toggles, a CC license badge, remix lineage, a mono print-settings spec table, and Description/Print Settings/Files/Makes tabs plus related models.
MCP
Code
:root {
--blueprint: #0f2a4a;
--blueprint-d: #0a1e36;
--blueprint-l: #16385f;
--grid: rgba(140, 180, 220, 0.14);
--grid-major: rgba(140, 180, 220, 0.26);
--orange: #ff6b35;
--orange-d: #e0521f;
--orange-soft: #ffe3d6;
--kraft: #d9c7a7;
--kraft-l: #efe6d4;
--ink: #1c2733;
--ink-2: #3d4a57;
--muted: #6b7885;
--bg: #f4f1ea;
--white: #ffffff;
--line: rgba(28, 39, 51, 0.12);
--line-2: rgba(28, 39, 51, 0.2);
--ok: #2f9e6f;
--warn: #d98a2b;
--danger: #d4503e;
--r-sm: 6px;
--r-md: 12px;
--r-lg: 18px;
--shadow-sm: 0 1px 2px rgba(28, 39, 51, 0.06), 0 2px 8px rgba(28, 39, 51, 0.05);
--shadow-md: 0 4px 14px rgba(28, 39, 51, 0.1);
--mono: "JetBrains Mono", ui-monospace, monospace;
}
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a { color: inherit; }
h1, h2, h3, p, ul, ol, figure { margin: 0; }
button { font: inherit; cursor: pointer; }
/* ---------- Top bar ---------- */
.topbar {
background: var(--blueprint-d);
color: var(--kraft-l);
border-bottom: 2px solid var(--orange);
}
.topbar-inner {
max-width: 1180px;
margin: 0 auto;
padding: 0.7rem 1.25rem;
display: flex;
align-items: center;
gap: 1.5rem;
}
.brand {
display: inline-flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
}
.brand-mark {
display: grid;
place-items: center;
width: 30px;
height: 30px;
border-radius: var(--r-sm);
background: var(--orange);
color: var(--blueprint-d);
font-size: 1.1rem;
font-weight: 800;
}
.brand-name {
font-weight: 800;
letter-spacing: 0.14em;
font-size: 0.95rem;
}
.brand-name em { font-style: normal; color: var(--orange); }
.topnav { display: flex; gap: 0.25rem; flex: 1; }
.topnav-link {
text-decoration: none;
font-size: 0.85rem;
font-weight: 600;
letter-spacing: 0.04em;
padding: 0.4rem 0.75rem;
border-radius: var(--r-sm);
color: rgba(239, 230, 212, 0.75);
transition: color 0.15s, background 0.15s;
}
.topnav-link:hover { color: var(--white); background: rgba(255, 255, 255, 0.07); }
.topnav-link.is-active { color: var(--orange); }
.topbar-actions { display: flex; align-items: center; gap: 0.75rem; }
.avatar {
display: grid;
place-items: center;
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--kraft);
color: var(--ink);
font-family: var(--mono);
font-size: 0.72rem;
font-weight: 700;
}
/* ---------- Buttons ---------- */
.btn {
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.1rem;
border-radius: var(--r-sm);
border: 1px solid transparent;
font-weight: 700;
letter-spacing: 0.03em;
padding: 0.6rem 1.1rem;
transition: background 0.15s, transform 0.1s, border-color 0.15s, box-shadow 0.15s;
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
.btn-primary {
background: var(--orange);
color: var(--white);
box-shadow: 0 2px 0 var(--orange-d), var(--shadow-sm);
}
.btn-primary:hover { background: var(--orange-d); }
.btn-outline {
background: transparent;
border-color: var(--line-2);
color: var(--ink-2);
}
.btn-outline:hover { border-color: var(--orange); color: var(--orange-d); }
.btn-ghost { background: transparent; color: var(--kraft-l); border-color: rgba(239, 230, 212, 0.35); }
.btn-ghost:hover { border-color: var(--orange); color: var(--orange); }
.btn-sm { padding: 0.35rem 0.75rem; font-size: 0.8rem; }
.btn-block { width: 100%; }
/* ---------- Badges ---------- */
.badge {
display: inline-flex;
align-items: center;
gap: 0.3rem;
border-radius: 999px;
padding: 0.2rem 0.65rem;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.badge-mono {
font-family: var(--mono);
background: var(--blueprint);
color: var(--orange-soft);
border-radius: var(--r-sm);
}
.badge-orange { background: var(--orange-soft); color: var(--orange-d); }
.badge-kraft { background: var(--kraft-l); color: var(--ink-2); border: 1px solid var(--line); }
.badge-license {
font-family: var(--mono);
background: var(--kraft-l);
color: var(--ink-2);
border: 1px dashed var(--line-2);
border-radius: var(--r-sm);
text-transform: none;
}
/* ---------- Breadcrumb ---------- */
.crumbs { max-width: 1180px; margin: 0 auto; padding: 0.9rem 1.25rem 0; }
.crumbs ol {
list-style: none;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
font-size: 0.78rem;
font-family: var(--mono);
color: var(--muted);
}
.crumbs li + li::before { content: "/"; margin-right: 0.35rem; color: var(--line-2); }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--orange-d); text-decoration: underline; }
.crumbs [aria-current] { color: var(--ink-2); font-weight: 600; }
/* ---------- Page / title ---------- */
.page { max-width: 1180px; margin: 0 auto; padding: 1rem 1.25rem 3rem; }
.title-block {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 2rem;
padding-bottom: 1.25rem;
}
.title-badges { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.6rem; }
.model-title {
font-size: clamp(1.5rem, 3.4vw, 2.2rem);
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.03em;
line-height: 1.15;
}
.model-sub { color: var(--ink-2); max-width: 56ch; margin-top: 0.5rem; }
.maker-row { display: flex; align-items: center; gap: 0.7rem; margin-top: 1rem; }
.maker-avatar {
display: grid;
place-items: center;
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--blueprint);
color: var(--orange-soft);
font-family: var(--mono);
font-size: 0.8rem;
font-weight: 700;
flex: none;
}
.maker-meta { display: flex; flex-direction: column; }
.maker-name { font-weight: 700; text-decoration: none; font-size: 0.92rem; }
.maker-name:hover { color: var(--orange-d); }
.maker-handle { color: var(--muted); font-weight: 500; font-family: var(--mono); font-size: 0.78rem; }
.maker-sub { font-size: 0.75rem; color: var(--muted); }
.follow-btn.is-on { background: var(--blueprint); color: var(--kraft-l); border-color: var(--blueprint); }
.stat-strip {
list-style: none;
padding: 0.9rem 1.1rem;
margin: 0;
display: grid;
grid-template-columns: repeat(4, auto);
gap: 1.4rem;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
box-shadow: var(--shadow-sm);
}
.stat-strip li { display: flex; flex-direction: column; align-items: center; min-width: 64px; }
.stat-strip strong { font-family: var(--mono); font-size: 1.05rem; font-weight: 700; }
.stat-strip span { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); }
/* ---------- Layout grid ---------- */
.detail-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) 320px;
gap: 1.5rem;
align-items: start;
}
/* ---------- Gallery ---------- */
.gallery-main {
position: relative;
border-radius: var(--r-lg);
overflow: hidden;
background-color: var(--blueprint);
background-image:
linear-gradient(var(--grid) 1px, transparent 1px),
linear-gradient(90deg, var(--grid) 1px, transparent 1px),
linear-gradient(var(--grid-major) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-major) 1px, transparent 1px);
background-size: 8px 8px, 8px 8px, 80px 80px, 80px 80px;
box-shadow: var(--shadow-md);
border: 1px solid var(--blueprint-l);
}
.gallery-view { display: none; }
.gallery-view.is-active { display: block; animation: viewIn 0.3s ease; }
@keyframes viewIn {
from { opacity: 0; transform: scale(0.985); }
to { opacity: 1; transform: scale(1); }
}
.gallery-view svg { display: block; width: 100%; height: auto; }
.gallery-caption {
position: absolute;
left: 0.9rem;
right: 0.9rem;
bottom: 0.8rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.gallery-caption .badge-mono { background: rgba(10, 30, 54, 0.85); border: 1px solid var(--grid-major); }
.gallery-hint {
font-family: var(--mono);
font-size: 0.68rem;
color: rgba(239, 230, 212, 0.6);
letter-spacing: 0.06em;
}
.thumb-strip { display: flex; gap: 0.6rem; margin-top: 0.75rem; }
.thumb {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.35rem;
background: var(--white);
border: 2px solid var(--line);
border-radius: var(--r-md);
padding: 0.55rem 0.4rem 0.45rem;
transition: border-color 0.15s, box-shadow 0.15s, transform 0.12s;
}
.thumb:hover { border-color: var(--orange); transform: translateY(-2px); }
.thumb:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
.thumb.is-active { border-color: var(--orange); box-shadow: 0 0 0 3px var(--orange-soft); }
.thumb-txt {
font-family: var(--mono);
font-size: 0.68rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--muted);
}
.thumb.is-active .thumb-txt { color: var(--orange-d); }
.thumb-art {
width: 100%;
height: 44px;
border-radius: var(--r-sm);
background-color: var(--blueprint);
background-image:
linear-gradient(var(--grid) 1px, transparent 1px),
linear-gradient(90deg, var(--grid) 1px, transparent 1px);
background-size: 6px 6px;
position: relative;
overflow: hidden;
}
.thumb-art::after {
content: "";
position: absolute;
inset: 0;
margin: auto;
background: var(--orange);
}
.thumb-iso::after { width: 22px; height: 18px; clip-path: polygon(50% 0, 100% 28%, 100% 72%, 50% 100%, 0 72%, 0 28%); }
.thumb-profile::after { width: 20px; height: 22px; background: transparent; border: 3px solid var(--orange-soft); border-right-color: transparent; border-radius: 5px; }
.thumb-exploded::after { width: 20px; height: 6px; box-shadow: 0 -9px 0 var(--orange-soft), 0 9px 0 var(--orange-soft); }
.thumb-inuse::after { width: 26px; height: 8px; background: var(--kraft); box-shadow: 0 -6px 0 -1px var(--orange); }
/* ---------- Tabs ---------- */
.tabs-card {
margin-top: 1.5rem;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow-sm);
overflow: hidden;
}
.tabbar {
display: flex;
border-bottom: 1px solid var(--line);
background: var(--kraft-l);
overflow-x: auto;
}
.tab {
appearance: none;
border: 0;
background: none;
padding: 0.85rem 1.1rem;
font-weight: 700;
font-size: 0.85rem;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--muted);
border-bottom: 3px solid transparent;
white-space: nowrap;
transition: color 0.15s, border-color 0.15s;
}
.tab:hover { color: var(--ink); }
.tab:focus-visible { outline: 2px solid var(--orange); outline-offset: -2px; }
.tab.is-active { color: var(--ink); border-bottom-color: var(--orange); }
.tab-count {
font-family: var(--mono);
font-size: 0.68rem;
background: var(--blueprint);
color: var(--orange-soft);
border-radius: 999px;
padding: 0.05rem 0.45rem;
margin-left: 0.25rem;
}
.tabpanel { display: none; padding: 1.4rem 1.5rem 1.6rem; }
.tabpanel.is-active { display: block; animation: viewIn 0.25s ease; }
.panel-h {
font-size: 0.8rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--ink-2);
margin-bottom: 0.8rem;
}
.tabpanel p { color: var(--ink-2); margin-bottom: 0.9rem; }
.tabpanel code {
font-family: var(--mono);
font-size: 0.82em;
background: var(--kraft-l);
border: 1px solid var(--line);
border-radius: 4px;
padding: 0.08em 0.35em;
}
.feature-list { list-style: none; padding: 0; display: grid; gap: 0.45rem; margin-bottom: 1.1rem; }
.feature-list li { display: flex; gap: 0.6rem; align-items: baseline; color: var(--ink-2); }
.tick { color: var(--ok); font-weight: 800; }
.cutout-note {
border: 2px dashed var(--line-2);
border-radius: var(--r-md);
background: var(--kraft-l);
padding: 0.85rem 1rem;
font-size: 0.88rem;
color: var(--ink-2);
}
.cutout-note strong { color: var(--orange-d); text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.78rem; }
.panel-foot { font-size: 0.82rem; color: var(--muted); margin: 0.9rem 0 0; }
/* ---------- Spec tables ---------- */
.spec-table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 0.8rem; }
.spec-table th, .spec-table td { text-align: left; padding: 0.45rem 0.5rem; border-bottom: 1px dashed var(--line); vertical-align: top; }
.spec-table tr:last-child th, .spec-table tr:last-child td { border-bottom: 0; }
.spec-table th { color: var(--muted); font-weight: 600; white-space: nowrap; }
.spec-table td { color: var(--ink); font-weight: 600; }
.spec-table td.ok { color: var(--ok); }
.spec-table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-md); }
.spec-table-wide { min-width: 540px; }
.spec-table-wide th, .spec-table-wide td { padding: 0.55rem 0.8rem; border-bottom: 1px solid var(--line); }
.spec-table-wide tr:last-child th, .spec-table-wide tr:last-child td { border-bottom: 0; }
/* ---------- Files ---------- */
.file-list { list-style: none; padding: 0; display: grid; gap: 0.55rem; }
.file-row {
display: flex;
align-items: center;
gap: 0.85rem;
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 0.65rem 0.75rem;
transition: border-color 0.15s, box-shadow 0.15s;
}
.file-row:hover { border-color: var(--orange); box-shadow: var(--shadow-sm); }
.file-icon {
flex: none;
display: grid;
place-items: center;
width: 46px;
height: 40px;
border-radius: var(--r-sm);
background: var(--blueprint);
color: var(--orange-soft);
font-family: var(--mono);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.05em;
}
.file-icon-alt { background: var(--orange); color: var(--white); }
.file-icon-src { background: var(--kraft); color: var(--ink); }
.file-info { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.file-name { font-family: var(--mono); font-weight: 700; font-size: 0.85rem; overflow-wrap: anywhere; }
.file-meta { font-size: 0.74rem; color: var(--muted); }
.file-dl {
flex: none;
width: 38px;
height: 38px;
border-radius: 50%;
border: 2px solid var(--line-2);
background: var(--white);
color: var(--ink-2);
font-size: 1.05rem;
font-weight: 800;
transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.1s;
}
.file-dl:hover { background: var(--orange); border-color: var(--orange); color: var(--white); }
.file-dl:active { transform: scale(0.94); }
.file-dl:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
.file-dl.is-done { background: var(--ok); border-color: var(--ok); color: var(--white); }
/* ---------- Makes ---------- */
.makes-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.9rem; margin-bottom: 1rem; }
.make-card { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; transition: box-shadow 0.15s, transform 0.12s; }
.make-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.make-art {
height: 90px;
display: grid;
place-items: center;
background-image:
linear-gradient(var(--grid) 1px, transparent 1px),
linear-gradient(90deg, var(--grid) 1px, transparent 1px);
background-size: 8px 8px;
}
.make-art span {
font-family: var(--mono);
font-weight: 700;
font-size: 0.75rem;
letter-spacing: 0.12em;
color: var(--white);
background: rgba(10, 30, 54, 0.55);
padding: 0.2rem 0.6rem;
border-radius: 4px;
}
.make-a { background-color: #23364a; }
.make-b { background-color: #2f7f6d; }
.make-c { background-color: #6b4a2f; }
.make-d { background-color: #c9481a; }
.make-body { display: flex; flex-direction: column; gap: 0.15rem; padding: 0.6rem 0.75rem 0.7rem; }
.make-user { font-family: var(--mono); font-size: 0.75rem; font-weight: 700; color: var(--orange-d); }
.make-note { font-size: 0.8rem; color: var(--ink-2); }
/* ---------- Right rail ---------- */
.rail { display: grid; gap: 1.1rem; }
.rail-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 1.1rem 1.15rem;
box-shadow: var(--shadow-sm);
}
.rail-h {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.78rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--ink-2);
margin-bottom: 0.7rem;
}
.rail-h-ico { color: var(--orange); }
.dl-price { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.8rem; }
.dl-free { font-family: var(--mono); font-weight: 700; font-size: 1.15rem; color: var(--ok); letter-spacing: 0.08em; }
.dl-label { font-size: 0.95rem; }
.dl-size { font-family: var(--mono); font-size: 0.68rem; font-weight: 400; opacity: 0.85; }
.dl-progress { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.7rem; }
.dl-progress-track { flex: 1; height: 8px; border-radius: 999px; background: var(--kraft-l); border: 1px solid var(--line); overflow: hidden; }
.dl-progress-bar { height: 100%; width: 0%; background: var(--orange); border-radius: 999px; transition: width 0.15s linear; }
.dl-progress-txt { font-family: var(--mono); font-size: 0.72rem; font-weight: 700; min-width: 3.2em; text-align: right; }
.rail-actions { display: flex; gap: 0.5rem; margin-top: 0.9rem; }
.chip-btn {
flex: 1;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.35rem;
border: 1px solid var(--line-2);
background: var(--white);
border-radius: 999px;
padding: 0.42rem 0.55rem;
font-size: 0.78rem;
font-weight: 700;
color: var(--ink-2);
transition: border-color 0.15s, background 0.15s, color 0.15s, transform 0.1s;
}
.chip-btn:hover { border-color: var(--orange); color: var(--orange-d); }
.chip-btn:active { transform: scale(0.97); }
.chip-btn:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
.chip-btn[aria-pressed="true"] { background: var(--orange-soft); border-color: var(--orange); color: var(--orange-d); }
.chip-ico { font-size: 0.9rem; }
.license-note { font-size: 0.76rem; color: var(--muted); margin: 0.85rem 0 0; }
/* Lineage */
.lineage { list-style: none; padding: 0; margin: 0 0 0.9rem; position: relative; }
.lineage::before {
content: "";
position: absolute;
left: 6px;
top: 8px;
bottom: 8px;
width: 2px;
background: repeating-linear-gradient(var(--line-2) 0 4px, transparent 4px 8px);
}
.lineage li { display: flex; gap: 0.75rem; padding: 0.35rem 0; position: relative; }
.lineage-dot {
flex: none;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--white);
border: 3px solid var(--muted);
margin-top: 4px;
z-index: 1;
}
.lineage li.is-current .lineage-dot { border-color: var(--orange); background: var(--orange-soft); }
.lineage-body { display: flex; flex-direction: column; font-size: 0.84rem; }
.lineage-body a { text-decoration: none; font-weight: 600; }
.lineage-body a:hover { color: var(--orange-d); text-decoration: underline; }
.lineage-by { font-size: 0.72rem; color: var(--muted); font-family: var(--mono); }
/* ---------- Related ---------- */
.related { margin-top: 2.4rem; }
.related-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 0.9rem; }
.section-h {
font-size: 1rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.1em;
}
.related-more { font-size: 0.82rem; font-weight: 700; color: var(--orange-d); text-decoration: none; }
.related-more:hover { text-decoration: underline; }
.related-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }
.rel-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
overflow: hidden;
transition: box-shadow 0.15s, transform 0.12s;
cursor: pointer;
}
.rel-card:hover { box-shadow: var(--shadow-md); transform: translateY(-3px); }
.rel-art {
height: 96px;
position: relative;
background-image:
linear-gradient(var(--grid) 1px, transparent 1px),
linear-gradient(90deg, var(--grid) 1px, transparent 1px);
background-size: 8px 8px;
}
.rel-a { background-color: var(--blueprint); }
.rel-b { background-color: var(--blueprint-l); }
.rel-c { background-color: #14413a; }
.rel-d { background-color: #5b3b1e; }
.rel-tag {
position: absolute;
left: 0.6rem;
bottom: 0.5rem;
font-family: var(--mono);
font-size: 0.64rem;
font-weight: 700;
letter-spacing: 0.08em;
color: var(--orange-soft);
background: rgba(10, 30, 54, 0.6);
padding: 0.12rem 0.45rem;
border-radius: 4px;
}
.rel-title { font-size: 0.88rem; font-weight: 700; padding: 0.6rem 0.75rem 0.15rem; }
.rel-meta { display: block; font-size: 0.74rem; color: var(--muted); font-family: var(--mono); padding: 0 0.75rem 0.7rem; }
/* ---------- Footer ---------- */
.foot {
background: var(--blueprint-d);
color: rgba(239, 230, 212, 0.55);
text-align: center;
padding: 1.1rem;
border-top: 2px solid var(--orange);
}
.foot-mono { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.14em; }
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 1.4rem;
transform: translate(-50%, 20px);
background: var(--blueprint-d);
color: var(--kraft-l);
font-family: var(--mono);
font-size: 0.8rem;
font-weight: 600;
padding: 0.65rem 1.1rem;
border-radius: var(--r-sm);
border-left: 4px solid var(--orange);
box-shadow: var(--shadow-md);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s, transform 0.25s;
max-width: min(92vw, 480px);
z-index: 50;
}
.toast.is-show { opacity: 1; transform: translate(-50%, 0); }
/* ---------- Responsive ---------- */
@media (max-width: 900px) {
.detail-grid { grid-template-columns: 1fr; }
.title-block { flex-direction: column; align-items: stretch; }
.stat-strip { grid-template-columns: repeat(4, 1fr); gap: 0.5rem; }
.related-row { grid-template-columns: repeat(2, 1fr); }
.topnav { display: none; }
}
@media (max-width: 520px) {
.page { padding: 0.75rem 0.85rem 2.2rem; }
.crumbs { padding: 0.7rem 0.85rem 0; }
.topbar-inner { padding: 0.6rem 0.85rem; gap: 0.75rem; }
.stat-strip { grid-template-columns: repeat(2, 1fr); row-gap: 0.8rem; }
.makes-grid { grid-template-columns: 1fr; }
.related-row { grid-template-columns: 1fr; }
.thumb-strip { flex-wrap: wrap; }
.thumb { flex: 1 1 40%; }
.tabpanel { padding: 1.1rem 1rem 1.3rem; }
.gallery-caption { flex-direction: column; align-items: flex-start; gap: 0.3rem; }
.gallery-hint { display: none; }
.rail-actions { flex-wrap: wrap; }
.chip-btn { flex: 1 1 45%; }
}/* MakerForge — 3D print model page interactions (vanilla JS, illustrative data) */
(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer = null;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
}, 2600);
}
/* Generic data-toast buttons */
document.querySelectorAll("[data-toast]").forEach(function (btn) {
btn.addEventListener("click", function () {
toast(btn.getAttribute("data-toast"));
});
});
/* ---------- Gallery: thumbnail → main view ---------- */
var thumbs = Array.prototype.slice.call(document.querySelectorAll("[data-thumb]"));
var views = document.querySelectorAll(".gallery-view");
var viewLabel = document.getElementById("viewLabel");
function activateView(id) {
var idx = 0;
thumbs.forEach(function (t, i) {
var on = t.getAttribute("data-thumb") === id;
t.classList.toggle("is-active", on);
t.setAttribute("aria-selected", String(on));
if (on) idx = i;
});
views.forEach(function (v) {
v.classList.toggle("is-active", v.getAttribute("data-view") === id);
});
var label = thumbs[idx].getAttribute("data-label");
viewLabel.textContent = "VIEW " + (idx + 1) + "/" + thumbs.length + " — " + label;
}
thumbs.forEach(function (t, i) {
t.addEventListener("click", function () {
activateView(t.getAttribute("data-thumb"));
});
t.addEventListener("keydown", function (e) {
var next = null;
if (e.key === "ArrowRight") next = thumbs[(i + 1) % thumbs.length];
if (e.key === "ArrowLeft") next = thumbs[(i - 1 + thumbs.length) % thumbs.length];
if (next) {
e.preventDefault();
next.focus();
activateView(next.getAttribute("data-thumb"));
}
});
});
/* ---------- Tabs ---------- */
var tabs = Array.prototype.slice.call(document.querySelectorAll(".tab[data-tab]"));
var panels = document.querySelectorAll(".tabpanel");
function activateTab(id) {
tabs.forEach(function (t) {
var on = t.getAttribute("data-tab") === id;
t.classList.toggle("is-active", on);
t.setAttribute("aria-selected", String(on));
});
panels.forEach(function (p) {
var on = p.id === "panel-" + id;
p.classList.toggle("is-active", on);
p.hidden = !on;
});
}
tabs.forEach(function (t, i) {
t.addEventListener("click", function () {
activateTab(t.getAttribute("data-tab"));
});
t.addEventListener("keydown", function (e) {
var next = null;
if (e.key === "ArrowRight") next = tabs[(i + 1) % tabs.length];
if (e.key === "ArrowLeft") next = tabs[(i - 1 + tabs.length) % tabs.length];
if (next) {
e.preventDefault();
next.focus();
activateTab(next.getAttribute("data-tab"));
}
});
});
/* ---------- Download all: fake progress + toast ---------- */
var dlBtn = document.getElementById("downloadBtn");
var dlProgress = document.getElementById("dlProgress");
var dlBar = document.getElementById("dlBar");
var dlPct = document.getElementById("dlPct");
var dlStat = document.querySelector('[data-stat="downloads"]');
var downloading = false;
dlBtn.addEventListener("click", function () {
if (downloading) return;
downloading = true;
dlBtn.disabled = true;
dlBtn.style.opacity = "0.7";
dlProgress.hidden = false;
var pct = 0;
toast("Preparing MDL-4471 bundle… (illustrative)");
var timer = setInterval(function () {
pct += 4 + Math.random() * 9;
if (pct >= 100) {
pct = 100;
clearInterval(timer);
dlBar.style.width = "100%";
dlPct.textContent = "100%";
toast("cable_organizer_clip_v2.3.zip — 6.3 MB downloaded");
bumpStat(dlStat);
setTimeout(function () {
dlProgress.hidden = true;
dlBar.style.width = "0%";
dlPct.textContent = "0%";
dlBtn.disabled = false;
dlBtn.style.opacity = "";
downloading = false;
}, 1400);
return;
}
dlBar.style.width = pct + "%";
dlPct.textContent = Math.round(pct) + "%";
}, 120);
});
function bumpStat(el, delta) {
if (!el) return;
var n = parseInt(el.textContent.replace(/[^\d]/g, ""), 10) + (delta === undefined ? 1 : delta);
el.textContent = n.toLocaleString("en-US");
}
/* ---------- Per-file download buttons ---------- */
document.querySelectorAll(".file-dl").forEach(function (btn) {
btn.addEventListener("click", function () {
var name = btn.getAttribute("data-file");
btn.classList.add("is-done");
btn.textContent = "✓";
toast(name + " downloaded (illustrative)");
setTimeout(function () {
btn.classList.remove("is-done");
btn.textContent = "↓";
}, 2000);
});
});
/* ---------- Like toggle ---------- */
var likeBtn = document.querySelector("[data-like]");
var likeCount = document.querySelector("[data-like-count]");
var likeStat = document.querySelector('[data-stat="likes"]');
likeBtn.addEventListener("click", function () {
var on = likeBtn.getAttribute("aria-pressed") === "true";
likeBtn.setAttribute("aria-pressed", String(!on));
var delta = on ? -1 : 1;
bumpStat(likeCount, delta);
bumpStat(likeStat, delta);
toast(on ? "Removed from your likes" : "Added to your likes ♥");
});
/* ---------- Collect toggle ---------- */
var collectBtn = document.querySelector("[data-collect]");
var collectLabel = document.querySelector("[data-collect-label]");
collectBtn.addEventListener("click", function () {
var on = collectBtn.getAttribute("aria-pressed") === "true";
collectBtn.setAttribute("aria-pressed", String(!on));
collectLabel.textContent = on ? "Collect" : "Collected";
toast(on ? "Removed from “Desk Setup” collection" : "Saved to “Desk Setup” collection");
});
/* ---------- Follow toggle ---------- */
var followBtn = document.querySelector("[data-follow]");
followBtn.addEventListener("click", function () {
var on = followBtn.getAttribute("aria-pressed") === "true";
followBtn.setAttribute("aria-pressed", String(!on));
followBtn.classList.toggle("is-on", !on);
followBtn.textContent = on ? "+ Follow" : "Following";
toast(on ? "Unfollowed @hexlab" : "Now following @hexlab");
});
/* ---------- Related cards ---------- */
document.querySelectorAll(".rel-card").forEach(function (card) {
card.addEventListener("click", function () {
var title = card.querySelector(".rel-title").textContent;
toast("Opening “" + title + "”… (illustrative)");
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Parametric Cable Organizer Clip — MakerForge</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=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Top bar -->
<header class="topbar">
<div class="topbar-inner">
<a class="brand" href="#" aria-label="MakerForge home">
<span class="brand-mark" aria-hidden="true">⌬</span>
<span class="brand-name">MAKER<em>FORGE</em></span>
</a>
<nav class="topnav" aria-label="Primary">
<a href="#" class="topnav-link">Explore</a>
<a href="#" class="topnav-link is-active">Models</a>
<a href="#" class="topnav-link">Contests</a>
<a href="#" class="topnav-link">Groups</a>
</nav>
<div class="topbar-actions">
<button class="btn btn-ghost btn-sm" type="button" data-toast="Upload flow is illustrative only">Upload</button>
<span class="avatar" aria-label="Signed in as WrenchDaily">WD</span>
</div>
</div>
</header>
<!-- Breadcrumb -->
<nav class="crumbs" aria-label="Breadcrumb">
<ol>
<li><a href="#">Models</a></li>
<li><a href="#">Household</a></li>
<li><a href="#">Organization</a></li>
<li aria-current="page">Parametric Cable Organizer Clip</li>
</ol>
</nav>
<main class="page">
<!-- Title block -->
<section class="title-block">
<div class="title-main">
<div class="title-badges">
<span class="badge badge-mono">MDL-4471</span>
<span class="badge badge-orange">Featured</span>
<span class="badge badge-kraft">Parametric</span>
</div>
<h1 class="model-title">Parametric Cable Organizer Clip</h1>
<p class="model-sub">
Snap-fit desk clip for 2–8 mm cables. Fully parametric — regenerate for any desk edge
from 12 to 40 mm thick. No supports, prints in under an hour.
</p>
<div class="maker-row">
<span class="maker-avatar" aria-hidden="true">HL</span>
<div class="maker-meta">
<a href="#" class="maker-name">Hattie Lindqvist <span class="maker-handle">@hexlab</span></a>
<span class="maker-sub">Published Mar 14, 2026 · Updated Jul 02, 2026</span>
</div>
<button class="btn btn-outline btn-sm follow-btn" type="button" data-follow aria-pressed="false">+ Follow</button>
</div>
</div>
<ul class="stat-strip" aria-label="Model statistics">
<li><strong data-stat="downloads">12,384</strong><span>Downloads</span></li>
<li><strong data-stat="likes">2,913</strong><span>Likes</span></li>
<li><strong data-stat="makes">147</strong><span>Makes</span></li>
<li><strong data-stat="remixes">36</strong><span>Remixes</span></li>
</ul>
</section>
<!-- Gallery + rail -->
<div class="detail-grid">
<section class="gallery" aria-label="Model gallery">
<figure class="gallery-main" id="galleryMain">
<div class="gallery-view is-active" data-view="iso" role="img" aria-label="Isometric render of the cable clip">
<svg viewBox="0 0 640 420" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
<defs>
<linearGradient id="gTop" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#ff8b5b"/><stop offset="1" stop-color="#ff6b35"/>
</linearGradient>
<linearGradient id="gLeft" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#e0521f"/><stop offset="1" stop-color="#b93f13"/>
</linearGradient>
<linearGradient id="gRight" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#c9481a"/><stop offset="1" stop-color="#9c360f"/>
</linearGradient>
</defs>
<!-- build plate -->
<g opacity="0.9">
<polygon points="320,340 560,250 320,160 80,250" fill="#16385f" stroke="rgba(140,180,220,0.35)" stroke-width="1.5"/>
<g stroke="rgba(140,180,220,0.18)" stroke-width="1">
<line x1="140" y1="272.5" x2="380" y2="182.5"/><line x1="200" y1="295" x2="440" y2="205"/>
<line x1="260" y1="317.5" x2="500" y2="227.5"/><line x1="260" y1="182.5" x2="500" y2="272.5"/>
<line x1="200" y1="205" x2="440" y2="295"/><line x1="140" y1="227.5" x2="380" y2="317.5"/>
</g>
</g>
<!-- clip body: C-shape iso -->
<g>
<!-- back column -->
<polygon points="250,120 310,98 310,238 250,260" fill="url(#gLeft)"/>
<polygon points="310,98 370,120 370,260 310,238" fill="url(#gRight)"/>
<polygon points="250,120 310,98 370,120 310,142" fill="url(#gTop)"/>
<!-- lower jaw -->
<polygon points="250,238 310,216 430,262 370,284" fill="url(#gTop)"/>
<polygon points="250,238 370,284 370,308 250,262" fill="url(#gLeft)"/>
<polygon points="370,284 430,262 430,286 370,308" fill="url(#gRight)"/>
<!-- upper jaw -->
<polygon points="250,142 310,120 430,166 370,188" fill="url(#gTop)"/>
<polygon points="250,142 370,188 370,206 250,160" fill="url(#gLeft)"/>
<polygon points="370,188 430,166 430,184 370,206" fill="url(#gRight)"/>
<!-- cable held in jaw -->
<ellipse cx="398" cy="238" rx="20" ry="13" fill="#1c2733"/>
<ellipse cx="398" cy="234" rx="20" ry="13" fill="#3d4a57"/>
<ellipse cx="398" cy="234" rx="11" ry="7" fill="#6b7885"/>
</g>
<!-- dimension callout -->
<g font-family="JetBrains Mono, monospace" font-size="13" fill="#ffe3d6">
<line x1="460" y1="150" x2="520" y2="120" stroke="#ffe3d6" stroke-width="1" stroke-dasharray="4 3"/>
<text x="524" y="118">Ø 6.0 mm</text>
<line x1="240" y1="290" x2="180" y2="330" stroke="#ffe3d6" stroke-width="1" stroke-dasharray="4 3"/>
<text x="90" y="346">w = 18 mm</text>
</g>
</svg>
</div>
<div class="gallery-view" data-view="profile" role="img" aria-label="Side profile with dimensions">
<svg viewBox="0 0 640 420" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
<!-- side profile: C clip -->
<g stroke="#ffe3d6" stroke-width="3" fill="none" stroke-linejoin="round">
<path d="M240 110 h180 a18 18 0 0 1 18 18 v20 a14 14 0 0 1 -14 14 h-130 v96 h130 a14 14 0 0 1 14 14 v20 a18 18 0 0 1 -18 18 h-180 a26 26 0 0 1 -26 -26 v-148 a26 26 0 0 1 26 -26 z"/>
</g>
<circle cx="452" cy="200" r="26" fill="none" stroke="#ff6b35" stroke-width="3" stroke-dasharray="6 5"/>
<g font-family="JetBrains Mono, monospace" font-size="13" fill="#8cb4dc">
<line x1="214" y1="90" x2="438" y2="90" stroke="#8cb4dc" stroke-width="1"/>
<line x1="214" y1="84" x2="214" y2="96" stroke="#8cb4dc" stroke-width="1"/>
<line x1="438" y1="84" x2="438" y2="96" stroke="#8cb4dc" stroke-width="1"/>
<text x="290" y="80">42.0 mm</text>
<line x1="190" y1="110" x2="190" y2="310" stroke="#8cb4dc" stroke-width="1"/>
<line x1="184" y1="110" x2="196" y2="110" stroke="#8cb4dc" stroke-width="1"/>
<line x1="184" y1="310" x2="196" y2="310" stroke="#8cb4dc" stroke-width="1"/>
<text x="118" y="215">31.5 mm</text>
<text x="492" y="204" fill="#ff6b35">R6 snap</text>
<text x="240" y="352" fill="#8cb4dc">SECTION A-A · SCALE 2:1 · WALL 2.4 mm</text>
</g>
</svg>
</div>
<div class="gallery-view" data-view="exploded" role="img" aria-label="Exploded view of the two-part clip">
<svg viewBox="0 0 640 420" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
<g stroke-linejoin="round">
<!-- top jaw floating -->
<g>
<polygon points="230,86 300,62 440,112 370,136" fill="#ff8b5b"/>
<polygon points="230,86 370,136 370,158 230,108" fill="#e0521f"/>
<polygon points="370,136 440,112 440,134 370,158" fill="#c9481a"/>
</g>
<line x1="330" y1="170" x2="330" y2="220" stroke="#8cb4dc" stroke-width="2" stroke-dasharray="5 5"/>
<!-- base -->
<g>
<polygon points="230,236 300,212 440,262 370,286" fill="#ff8b5b"/>
<polygon points="230,236 370,286 370,326 230,276" fill="#e0521f"/>
<polygon points="370,286 440,262 440,302 370,326" fill="#c9481a"/>
</g>
<!-- pin -->
<g>
<rect x="500" y="150" width="14" height="110" rx="7" fill="#efe6d4"/>
<line x1="507" y1="140" x2="507" y2="120" stroke="#8cb4dc" stroke-width="2" stroke-dasharray="5 5"/>
</g>
</g>
<g font-family="JetBrains Mono, monospace" font-size="13" fill="#ffe3d6">
<text x="120" y="60">[1] jaw_top.stl</text>
<text x="120" y="250">[2] clip_base.stl</text>
<text x="470" y="300">[3] hinge_pin.stl</text>
</g>
</svg>
</div>
<div class="gallery-view" data-view="inuse" role="img" aria-label="Clip in use on a desk edge">
<svg viewBox="0 0 640 420" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
<!-- desk edge -->
<rect x="60" y="180" width="520" height="60" rx="6" fill="#d9c7a7"/>
<rect x="60" y="180" width="520" height="12" rx="6" fill="#efe6d4"/>
<!-- three clips -->
<g fill="#ff6b35">
<path d="M170 168 h44 v84 h-44 a12 12 0 0 1 -12 -12 v-60 a12 12 0 0 1 12 -12 z"/>
<path d="M310 168 h44 v84 h-44 a12 12 0 0 1 -12 -12 v-60 a12 12 0 0 1 12 -12 z"/>
<path d="M450 168 h44 v84 h-44 a12 12 0 0 1 -12 -12 v-60 a12 12 0 0 1 12 -12 z"/>
</g>
<!-- cables sagging between clips -->
<g fill="none" stroke-width="6" stroke-linecap="round">
<path d="M192 160 C 250 120, 270 120, 332 160" stroke="#3d4a57"/>
<path d="M332 160 C 390 122, 410 122, 472 160" stroke="#3d4a57"/>
<path d="M192 152 C 252 106, 272 106, 332 152" stroke="#6b7885"/>
<path d="M332 152 C 392 108, 412 108, 472 152" stroke="#6b7885"/>
</g>
<g font-family="JetBrains Mono, monospace" font-size="13" fill="#8cb4dc">
<text x="120" y="320">3× clips @ 140 mm spacing · desk 25 mm</text>
<line x1="192" y1="270" x2="332" y2="270" stroke="#8cb4dc" stroke-width="1"/>
<line x1="192" y1="264" x2="192" y2="276" stroke="#8cb4dc" stroke-width="1"/>
<line x1="332" y1="264" x2="332" y2="276" stroke="#8cb4dc" stroke-width="1"/>
<text x="228" y="292">140 mm</text>
</g>
</svg>
</div>
<figcaption class="gallery-caption">
<span class="badge badge-mono" id="viewLabel">VIEW 1/4 — ISO RENDER</span>
<span class="gallery-hint">PLA · 0.2 mm · rendered on MakerForge</span>
</figcaption>
</figure>
<div class="thumb-strip" role="tablist" aria-label="Gallery views">
<button class="thumb is-active" role="tab" aria-selected="true" data-thumb="iso" data-label="ISO RENDER">
<span class="thumb-art thumb-iso" aria-hidden="true"></span><span class="thumb-txt">Iso</span>
</button>
<button class="thumb" role="tab" aria-selected="false" data-thumb="profile" data-label="SIDE PROFILE">
<span class="thumb-art thumb-profile" aria-hidden="true"></span><span class="thumb-txt">Profile</span>
</button>
<button class="thumb" role="tab" aria-selected="false" data-thumb="exploded" data-label="EXPLODED">
<span class="thumb-art thumb-exploded" aria-hidden="true"></span><span class="thumb-txt">Exploded</span>
</button>
<button class="thumb" role="tab" aria-selected="false" data-thumb="inuse" data-label="IN USE">
<span class="thumb-art thumb-inuse" aria-hidden="true"></span><span class="thumb-txt">In use</span>
</button>
</div>
<!-- Tabs -->
<section class="tabs-card" aria-label="Model details">
<div class="tabbar" role="tablist" aria-label="Model information tabs">
<button class="tab is-active" role="tab" id="tab-description" aria-selected="true" aria-controls="panel-description" data-tab="description">Description</button>
<button class="tab" role="tab" id="tab-settings" aria-selected="false" aria-controls="panel-settings" data-tab="settings">Print Settings</button>
<button class="tab" role="tab" id="tab-files" aria-selected="false" aria-controls="panel-files" data-tab="files">Files <span class="tab-count">4</span></button>
<button class="tab" role="tab" id="tab-makes" aria-selected="false" aria-controls="panel-makes" data-tab="makes">Makes <span class="tab-count">147</span></button>
</div>
<div class="tabpanel is-active" role="tabpanel" id="panel-description" aria-labelledby="tab-description">
<h2 class="panel-h">About this model</h2>
<p>
This clip started as a fix for the rat's nest behind my monitor arm and turned into a
rabbit hole of snap-fit tuning. The jaw uses a living hinge with a 0.45 mm flex wall,
so cables click in one-handed and stay put — no adhesive, no screws, no desk damage.
</p>
<p>
Everything is parametric. Open <code>cable_clip.scad</code> and change three numbers:
<code>desk_thickness</code>, <code>cable_slots</code>, and <code>slot_diameter</code>.
The default STL fits a 25 mm desktop with three slots (4, 6 and 8 mm).
</p>
<ul class="feature-list">
<li><span class="tick" aria-hidden="true">✓</span> Prints flat, zero supports, zero bridging over 8 mm</li>
<li><span class="tick" aria-hidden="true">✓</span> Tested to 1.8 kg pull-out force in PETG</li>
<li><span class="tick" aria-hidden="true">✓</span> Grip pads printed in TPU (optional file included)</li>
<li><span class="tick" aria-hidden="true">✓</span> Tolerances tuned for 0.4 mm nozzles — no post-processing</li>
</ul>
<div class="cutout-note">
<strong>Remix note</strong> — v2.3 merges @spindleworks' wider jaw and fixes the hinge
crack reported on cold PLA. If your first layer squishes more than 0.05 mm, scale
X/Y to 100.5% for a snug desk fit.
</div>
</div>
<div class="tabpanel" role="tabpanel" id="panel-settings" aria-labelledby="tab-settings" hidden>
<h2 class="panel-h">Recommended slicer profile</h2>
<p>Profile exported from OrcaSlicer 2.1 — bundled as <code>profile_0.2mm_pla.json</code> in the Files tab.</p>
<div class="spec-table-wrap">
<table class="spec-table spec-table-wide">
<tbody>
<tr><th scope="row">Perimeters</th><td>3 walls</td><th scope="row">Top / bottom</th><td>4 / 3 layers</td></tr>
<tr><th scope="row">Infill pattern</th><td>Gyroid</td><th scope="row">Seam</th><td>Rear, aligned</td></tr>
<tr><th scope="row">First layer</th><td>0.25 mm @ 20 mm/s</td><th scope="row">Speed</th><td>120 mm/s outer 60</td></tr>
<tr><th scope="row">Nozzle temp</th><td>210 °C (PLA)</td><th scope="row">Bed temp</th><td>60 °C, textured PEI</td></tr>
<tr><th scope="row">Cooling</th><td>100% after L3</td><th scope="row">Brim</th><td>None needed</td></tr>
</tbody>
</table>
</div>
<p class="panel-foot">PETG: drop cooling to 40% and add one extra wall for the hinge. TPU grip pads: 0.25 mm layers, 15 mm/s, direct drive only.</p>
</div>
<div class="tabpanel" role="tabpanel" id="panel-files" aria-labelledby="tab-files" hidden>
<h2 class="panel-h">Model files</h2>
<ul class="file-list">
<li class="file-row">
<span class="file-icon" aria-hidden="true">STL</span>
<span class="file-info">
<span class="file-name">clip_base_25mm.stl</span>
<span class="file-meta">1.8 MB · 24,882 triangles · updated Jul 02</span>
</span>
<button class="file-dl" type="button" data-file="clip_base_25mm.stl" aria-label="Download clip_base_25mm.stl">↓</button>
</li>
<li class="file-row">
<span class="file-icon" aria-hidden="true">STL</span>
<span class="file-info">
<span class="file-name">jaw_top_3slot.stl</span>
<span class="file-meta">1.1 MB · 15,204 triangles · updated Jul 02</span>
</span>
<button class="file-dl" type="button" data-file="jaw_top_3slot.stl" aria-label="Download jaw_top_3slot.stl">↓</button>
</li>
<li class="file-row">
<span class="file-icon file-icon-alt" aria-hidden="true">3MF</span>
<span class="file-info">
<span class="file-name">cable_clip_plate_x3.3mf</span>
<span class="file-meta">3.4 MB · pre-arranged plate of 3 · updated Jun 18</span>
</span>
<button class="file-dl" type="button" data-file="cable_clip_plate_x3.3mf" aria-label="Download cable_clip_plate_x3.3mf">↓</button>
</li>
<li class="file-row">
<span class="file-icon file-icon-src" aria-hidden="true">SCAD</span>
<span class="file-info">
<span class="file-name">cable_clip.scad</span>
<span class="file-meta">14 KB · parametric source · updated Jul 02</span>
</span>
<button class="file-dl" type="button" data-file="cable_clip.scad" aria-label="Download cable_clip.scad">↓</button>
</li>
</ul>
</div>
<div class="tabpanel" role="tabpanel" id="panel-makes" aria-labelledby="tab-makes" hidden>
<h2 class="panel-h">Community makes</h2>
<div class="makes-grid">
<article class="make-card">
<div class="make-art make-a" aria-hidden="true"><span>PETG</span></div>
<div class="make-body">
<span class="make-user">@spindleworks</span>
<span class="make-note">Galaxy black PETG, 5-slot remix on a standing desk.</span>
</div>
</article>
<article class="make-card">
<div class="make-art make-b" aria-hidden="true"><span>PLA</span></div>
<div class="make-body">
<span class="make-user">@printsofjune</span>
<span class="make-note">Matte mint PLA — printed six, scaled 100.5% for IKEA top.</span>
</div>
</article>
<article class="make-card">
<div class="make-art make-c" aria-hidden="true"><span>ASA</span></div>
<div class="make-body">
<span class="make-user">@garage_ohm</span>
<span class="make-note">ASA for the garage bench. Survives the summer heat fine.</span>
</div>
</article>
<article class="make-card">
<div class="make-art make-d" aria-hidden="true"><span>PLA+</span></div>
<div class="make-body">
<span class="make-user">@ninavoltage</span>
<span class="make-note">Safety orange PLA+ with TPU pads. Hinge feels great.</span>
</div>
</article>
</div>
<button class="btn btn-outline btn-block" type="button" data-toast="147 makes — full gallery is illustrative">View all 147 makes</button>
</div>
</section>
</section>
<!-- Right rail -->
<aside class="rail" aria-label="Download and print info">
<div class="rail-card dl-card">
<div class="dl-price">
<span class="dl-free">FREE</span>
<span class="badge badge-license" title="Creative Commons Attribution-ShareAlike 4.0">CC BY-SA 4.0</span>
</div>
<button class="btn btn-primary btn-block" type="button" id="downloadBtn">
<span class="dl-label">Download all files</span>
<span class="dl-size">ZIP · 6.3 MB</span>
</button>
<div class="dl-progress" id="dlProgress" hidden>
<div class="dl-progress-track"><div class="dl-progress-bar" id="dlBar"></div></div>
<span class="dl-progress-txt" id="dlPct">0%</span>
</div>
<div class="rail-actions">
<button class="chip-btn" type="button" data-like aria-pressed="false">
<span class="chip-ico" aria-hidden="true">♥</span> <span data-like-count>2,913</span>
</button>
<button class="chip-btn" type="button" data-collect aria-pressed="false">
<span class="chip-ico" aria-hidden="true">▣</span> <span data-collect-label>Collect</span>
</button>
<button class="chip-btn" type="button" data-toast="Share link copied (pretend)">
<span class="chip-ico" aria-hidden="true">⇗</span> Share
</button>
</div>
<p class="license-note">
Free for personal & commercial use. Remixes must keep the same license and credit
<strong>@hexlab</strong>.
</p>
</div>
<div class="rail-card spec-card">
<h2 class="rail-h"><span class="rail-h-ico" aria-hidden="true">⚙</span> Print settings</h2>
<table class="spec-table">
<tbody>
<tr><th scope="row">printer</th><td>Voxelsmith A2 (tested)</td></tr>
<tr><th scope="row">layer_h</th><td>0.20 mm</td></tr>
<tr><th scope="row">infill</th><td>25 % gyroid</td></tr>
<tr><th scope="row">supports</th><td class="ok">none</td></tr>
<tr><th scope="row">filament</th><td>PLA · 14 g · 4.7 m</td></tr>
<tr><th scope="row">print_time</th><td>0 h 52 m / clip</td></tr>
<tr><th scope="row">nozzle</th><td>0.4 mm</td></tr>
</tbody>
</table>
</div>
<div class="rail-card lineage-card">
<h2 class="rail-h"><span class="rail-h-ico" aria-hidden="true">⑂</span> Remix lineage</h2>
<ol class="lineage">
<li>
<span class="lineage-dot" aria-hidden="true"></span>
<span class="lineage-body"><a href="#">Simple Desk Clip</a> <span class="lineage-by">by @benchmarkbet · 2024</span></span>
</li>
<li>
<span class="lineage-dot" aria-hidden="true"></span>
<span class="lineage-body"><a href="#">Snap-Fit Cable Guide</a> <span class="lineage-by">by @spindleworks · 2025</span></span>
</li>
<li class="is-current">
<span class="lineage-dot" aria-hidden="true"></span>
<span class="lineage-body"><strong>This model (v2.3)</strong> <span class="lineage-by">by @hexlab · 2026</span></span>
</li>
</ol>
<button class="btn btn-outline btn-block btn-sm" type="button" data-toast="Remix workspace is illustrative">⑂ Remix this model</button>
</div>
</aside>
</div>
<!-- Related -->
<section class="related" aria-label="Related models">
<div class="related-head">
<h2 class="section-h">Related models</h2>
<a class="related-more" href="#">Browse organization →</a>
</div>
<div class="related-row">
<article class="rel-card">
<div class="rel-art rel-a" aria-hidden="true"><span class="rel-tag">MDL-4102</span></div>
<h3 class="rel-title">Headphone Hook, Screwless</h3>
<span class="rel-meta">@ninavoltage · 8.2k ↓</span>
</article>
<article class="rel-card">
<div class="rel-art rel-b" aria-hidden="true"><span class="rel-tag">MDL-3987</span></div>
<h3 class="rel-title">Under-Desk Power Strip Cradle</h3>
<span class="rel-meta">@garage_ohm · 5.9k ↓</span>
</article>
<article class="rel-card">
<div class="rel-art rel-c" aria-hidden="true"><span class="rel-tag">MDL-4260</span></div>
<h3 class="rel-title">SD Card Bandolier (Parametric)</h3>
<span class="rel-meta">@hexlab · 11.4k ↓</span>
</article>
<article class="rel-card">
<div class="rel-art rel-d" aria-hidden="true"><span class="rel-tag">MDL-4415</span></div>
<h3 class="rel-title">Modular Pegboard Bins v3</h3>
<span class="rel-meta">@printsofjune · 19.7k ↓</span>
</article>
</div>
</section>
</main>
<footer class="foot">
<span class="foot-mono">MAKERFORGE // MDL-4471 // ALL DIMENSIONS IN MM UNLESS NOTED</span>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>3D Print Model Page
A full model detail page in the style of Thingiverse or Printables, built around a fictional “Parametric Cable Organizer Clip”. The hero gallery sits on the signature navy blueprint grid and cycles through four hand-drawn SVG views — an isometric render on a build plate, a dimensioned side profile, an exploded parts diagram, and an in-use desk scene — via a keyboard-navigable thumbnail strip that updates a mono view-counter badge.
The right rail carries the commerce-free essentials: a safety-orange “Download all files” button that runs a fake progress bar, fires toasts, and bumps the download counter; like and collect toggles with live counts; a CC BY-SA license badge; a print-settings spec table set entirely in JetBrains Mono (printer, layer height, infill, supports, filament grams and meters, print time); and a dashed remix-lineage timeline tracing the model back through two ancestors.
Below the gallery, a four-way tab switcher covers Description (with a dashed “remix note” callout), a wide slicer-profile table, a Files tab listing .stl/.3mf/.scad rows with sizes and per-file download buttons that flip to checkmarks, and a Makes tab showing a small grid of community prints in different filaments. A related-models row and a stenciled footer close out the page.
Illustrative UI only — always follow real safety guidance when working with tools, electronics, or repairs.