Files
open-design/plugins/_official/examples/fs-electric-studio/example.html
T
eb749841b4 fix(home): surface community slide decks on Home with letterbox-free 16:9 previews (#4350)
* fix(plugins): surface slides decks on Home instead of marketplace sources

The 23 community slides/deck plugins from #4127 were registered as a
restricted community marketplace source, so they showed up as installable
"available sources" cards (受限 + Install) instead of on Home. Relocate them
to plugins/_official/examples so they ship bundled — appearing in the Home
Community gallery and the deck example-prompt rail, exactly like the existing
zhangzara / swiss / guizang decks — and drop their community-registry entries.

- Move 23 plugins/community/<slug> → plugins/_official/examples/<slug>
  (upstream LICENSE / author / homepage / "Based on" attribution preserved)
- Rename manifest name community-<slug> → example-<slug> (bundled convention)
- Remove the 23 entries from plugins/registry/community/open-design-marketplace.json
  (the 3 pre-existing community entries stay)
- HomeHero: uncap the deck example-prompt rail (other chips keep the curated 18)
  so every bundled slide template is reachable straight from "All"
- curatedPriority: pin the 23 slides to the front of both the deck example rail
  and the Community Slides shelf

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(home): drop provenance tail from deck seeds & fix deck preview letterbox

Two polish fixes for the slide/deck plugins now surfaced on Home:

1. Example-prompt seed: strip a trailing source-attribution sentence
   ("…。移植自 foo/bar 的 baz 模板。" / "Based on …") from the composer seed so
   the prompt doesn't end on provenance boilerplate. Conservative — only fires
   when the final sentence opens with an attribution marker and real text
   precedes it; attribution woven mid-description (followed by a use-case
   sentence) and the gallery/detail descriptions are left intact. Latin '.'
   only ends a sentence at whitespace/EOL so "STYLE_PRESETS.md" / "github.com/…"
   don't mis-split.

2. Deck gallery preview: decks ship a fixed 16:9 stage that scales to its
   viewport, so the tall scroll-preview frame letterboxed it and showed a dark
   --stage-bg band above/below the slide. Tag deck cards with data-od-mode and
   give them a 16:9 frame the iframe fills natively (no transform, no pan), so
   the stage sits edge-to-edge with no band.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(home): deck preview frame to 16:9 so no white strip below the slide

The deck preview override targeted the inner html-frame while the gallery frame
kept its fixed 300px height, leaving a white strip under the 16:9 slide. Make
the gallery frame itself 16:9 for deck cards so the stage fills it exactly —
title bar + slide, nothing left over.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(decks): hide the page-counter chrome on all 23 community slide decks

The five upstream deck families each render a different page-counter ("1 / 10")
— a center pill (.deck-controls), an id="counter" badge, a fixed .deck-counter,
or a .count span inside a Shadow-DOM rail — so the home gallery thumbnails
showed it inconsistently. Per maintainer request, drop it everywhere rather
than unify: inject a display:none rule for the light-DOM counters and inline-
hide the Shadow-DOM .count span. Keyboard/touch navigation is untouched; only
the visual counter is removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(home): fix deck letterbox band in the example-prompt rail tiles too

The earlier 16:9 deck-preview fix only covered the Community gallery cards; the
hero example-prompt rail tiles render the deck in a 4:3 (1440×1080) preview and
still showed a dark --stage-bg band on top. Tag deck preset tiles with
data-od-mode and give them a 16:9 preview the iframe fills natively, matching
the gallery treatment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(home): remove the ↗ open-external button from community gallery cards

The ↗ only rendered when a tile's preview was an HTML source, so it
appeared on some cards (most decks, a few prototypes) and not others
(images, srcless tiles) — inconsistent chrome across the shelf. Drop it
and the whole onOpenExternal pass-through (PluginCard → PluginsHomeSection
→ HomeView) plus the now-dead .plugins-home__gallery-open styles.

* fix(home): make the community search clear (×) button visible

The clear button uses the shared <Button> component, whose CSS-module
class set padding 6/12 and crushed the 12px × glyph to zero width inside
the 26px button (a blank grey square). The .plugins-home__search-clear
reset only out-specified the global `button {}` element rule, not the
component class — scope it under .plugins-home__search so the padding:0 /
20x20 sizing actually wins.

* fix(home): align deck example-prompt tile title row with other tiles

Deck preset tiles used a 16:9 `auto` preview row, so their card was
shorter than the fixed-150px sibling tiles; the flex rail stretched them
back up and the slack leaked into the title row, making deck titles ~6px
taller. Keep deck tiles on the standard 150px preview cell and center the
native 16:9 stage inside it (neutral margin, no dark band), so card and
title-row heights match prototype/HyperFrames/image tiles.

* fix(deck-bridge): mirror the visible class so reveal animations fire on navigation

Reveal-animation decks (the frontend-slides family) gate staggered
entrances on a separate `.slide.visible` class that the deck's own show()
adds alongside `.active`. The host bridge's setActive() flipped only the
active class, so bridge-driven page turns showed slide chrome but left
every .reveal child at opacity:0 — the body rendered blank. Mirror
`.visible` in lock-step with the active slide (only for decks that use it,
a no-op elsewhere). Adds a red→green regression test.

* fix(home): unpin the bare Frontend Slides template from the slides shelf

example-frontend-slides is the family-root template with a generic cover;
pinned first it read as filler leading the Slides shelf and deck chip.
Drop it from PINNED_SLIDE_PLUGIN_IDS so it falls to the uncurated tail
while the styled variants (Creative Voltage, Electric Studio, …) keep the
lead.

---------

Co-authored-by: free666799 <293857035+free666799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 09:32:15 +00:00

843 lines
35 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Open Design · Electric Studio Deck</title>
<!-- Fonts: Electric Studio is all-Manrope — 800 for display, 400/500 for body -->
<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=Manrope:wght@400;500;800&display=swap" rel="stylesheet">
<style>
/* ===========================================
CSS CUSTOM PROPERTIES (THEME)
Electric Studio preset — bold split panels,
white over cobalt #4361ee, all Manrope.
=========================================== */
:root {
--bg-dark: #0a0a0a;
--bg-white: #ffffff;
--accent-blue: #4361ee;
--text-dark: #0a0a0a;
--text-light: #ffffff;
--text-dark-soft: rgba(10, 10, 10, 0.58);
--text-light-soft: rgba(255, 255, 255, 0.72);
--line-dark: rgba(10, 10, 10, 0.14);
--line-light: rgba(255, 255, 255, 0.28);
--blue-soft: rgba(67, 97, 238, 0.10);
--stage-bg: var(--bg-dark);
--slide-bg: var(--bg-white);
/* Typography — authored at the 1920×1080 stage size */
--font: 'Manrope', sans-serif;
--title-size: 152px;
--h2-size: 88px;
--lede-size: 33px;
--body-size: 27px;
/* Spacing — authored at the 1920×1080 stage size */
--slide-pad-x: 110px;
--edge-bar-h: 14px;
/* Animation */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--duration-normal: 0.7s;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
/* ===========================================
FIXED 16:9 STAGE: MANDATORY BASE STYLES
(full contents of viewport-base.css)
Slides are authored at 1920×1080 and scaled as a whole.
=========================================== */
html,
body {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
background: var(--stage-bg, #000);
}
.deck-viewport {
position: fixed;
inset: 0;
overflow: hidden;
background: var(--stage-bg, #000);
}
/* Fixed 16:9 design canvas. JavaScript sets transform: translate(...) scale(...). */
.deck-stage {
position: absolute;
left: 0;
top: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
transform-origin: 0 0;
background: var(--slide-bg, #fff);
}
/* Slides stack inside the fixed stage. Content laid out at 1920×1080, never reflowed per device. */
.slide {
position: absolute;
inset: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
display: block;
visibility: hidden;
opacity: 0;
pointer-events: none;
background: var(--slide-bg, #fff);
}
.slide.active,
.slide.visible {
visibility: visible;
opacity: 1;
pointer-events: auto;
z-index: 1;
}
img, video, canvas, svg {
max-width: 100%;
max-height: 100%;
}
/* Presentation chrome stays outside the slide design system */
.deck-controls {
position: fixed;
left: 50%;
bottom: 22px;
transform: translateX(-50%);
z-index: 1000;
}
@media print {
html, body { width: 1920px; height: auto; overflow: visible; background: #fff; }
.deck-viewport { position: static; overflow: visible; background: #fff; }
.deck-stage { position: static; width: auto; height: auto; transform: none !important; background: none; }
.slide {
position: relative;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
pointer-events: auto !important;
width: 1920px;
height: 1080px;
break-after: page;
page-break-after: always;
}
.slide:last-child { break-after: auto; page-break-after: auto; }
.deck-controls { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.2s !important;
}
}
/* ===========================================
SLIDE SHELL — Electric Studio split panels
White panel above, cobalt panel below, a dark
edge bar on the seam, brand marks in corners.
=========================================== */
.slide {
font-family: var(--font);
color: var(--text-dark);
transition: opacity 0.55s var(--ease-out-expo);
}
.split {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
}
.panel { position: relative; overflow: hidden; }
.panel-white { background: var(--bg-white); color: var(--text-dark); }
.panel-blue { background: var(--accent-blue); color: var(--text-light); }
.panel-pad { padding: 0 var(--slide-pad-x); }
/* Signature device: accent bar on the panel edge (seam between panels) */
.edge-bar { flex: none; height: var(--edge-bar-h); background: var(--bg-dark); }
.edge-bar.light { background: var(--bg-white); }
/* Brand marks in the four corners — Electric Studio chrome */
.mark {
position: absolute;
z-index: 6;
font-size: 19px;
font-weight: 800;
letter-spacing: 0.22em;
text-transform: uppercase;
display: flex;
align-items: center;
gap: 14px;
}
.mark-tl { top: 52px; left: 64px; }
.mark-tr { top: 52px; right: 64px; }
.mark-bl { bottom: 48px; left: 64px; }
.mark-br { bottom: 48px; right: 64px; }
.mark.dark { color: var(--text-dark); }
.mark.dark .soft { color: var(--text-dark-soft); }
.mark.light { color: var(--text-light); }
.mark.light .soft { color: var(--text-light-soft); }
.mark .chip {
width: 16px;
height: 16px;
background: var(--accent-blue);
display: inline-block;
}
.mark.light .chip { background: var(--bg-white); }
/* ===========================================
TYPOGRAPHY
=========================================== */
.display {
font-weight: 800;
font-size: var(--title-size);
line-height: 0.96;
letter-spacing: -0.035em;
}
.h2 {
font-weight: 800;
font-size: var(--h2-size);
line-height: 1.0;
letter-spacing: -0.025em;
}
.kicker {
font-size: 22px;
font-weight: 800;
letter-spacing: 0.34em;
text-transform: uppercase;
color: var(--accent-blue);
margin-bottom: 28px;
}
.panel-blue .kicker { color: var(--text-light); }
.lede {
font-size: var(--lede-size);
line-height: 1.5;
font-weight: 400;
/* Body copy on cobalt uses full --text-light for WCAG AA contrast;
soft alpha stays reserved for bold uppercase chrome (marks, hints). */
color: var(--text-light);
max-width: 56ch;
}
.blue { color: var(--accent-blue); }
/* ===========================================
ANIMATIONS — trigger via .visible on the slide
=========================================== */
.reveal {
opacity: 0;
transform: translateY(36px);
transition: opacity var(--duration-normal) var(--ease-out-expo),
transform var(--duration-normal) var(--ease-out-expo);
}
.slide.visible .reveal { opacity: 1; transform: translateY(0); }
.reveal:nth-child(1) { transition-delay: 0.08s; }
.reveal:nth-child(2) { transition-delay: 0.18s; }
.reveal:nth-child(3) { transition-delay: 0.28s; }
.reveal:nth-child(4) { transition-delay: 0.38s; }
.reveal:nth-child(5) { transition-delay: 0.48s; }
.reveal:nth-child(6) { transition-delay: 0.58s; }
/* ===========================================
S1 · TITLE — 50/50 split, statement type on white
=========================================== */
.s-title .panel-white { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; padding-bottom: 64px; }
.s-title .panel-blue { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.s-title .meta-row {
display: flex;
gap: 56px;
margin-top: 52px;
font-size: 22px;
font-weight: 800;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--text-light);
}
.s-title .meta-row .soft { color: var(--text-light-soft); font-weight: 500; }
/* ===========================================
S2 · AGENDA — heading on white, numbered list on blue
=========================================== */
.s-agenda .panel-white { flex: none; height: 348px; display: flex; align-items: flex-end; padding-bottom: 56px; }
.s-agenda .panel-blue { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.agenda-rows { display: flex; flex-direction: column; }
.agenda-row {
display: flex;
align-items: baseline;
gap: 56px;
padding: 30px 0;
border-top: 1px solid var(--line-light);
font-size: 42px;
font-weight: 500;
}
.agenda-row:last-child { border-bottom: 1px solid var(--line-light); }
.agenda-row .idx { font-weight: 800; font-size: 26px; min-width: 86px; letter-spacing: 0.1em; }
.agenda-row .hint {
margin-left: auto;
font-size: 21px;
font-weight: 800;
color: var(--text-light-soft);
letter-spacing: 0.14em;
text-transform: uppercase;
}
/* ===========================================
S3 · SECTION DIVIDER — full cobalt, outlined giant number
=========================================== */
.s-divider .panel-white { flex: none; height: 150px; display: flex; align-items: center; }
.s-divider .panel-blue { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.divider-no {
font-weight: 800;
font-size: 430px;
line-height: 0.86;
letter-spacing: -0.04em;
color: transparent;
-webkit-text-stroke: 3px var(--bg-white);
}
.divider-label { margin-top: 30px; font-size: 84px; font-weight: 800; letter-spacing: -0.02em; }
/* ===========================================
S4 · BULLETS — proofs on white, takeaway on blue
=========================================== */
.s-bullets .panel-white { flex: 1; padding-top: 108px; }
.s-bullets .panel-blue { flex: none; height: 240px; display: flex; align-items: center; }
.points { margin-top: 44px; display: grid; gap: 34px; max-width: 1420px; }
.point { display: flex; gap: 38px; align-items: flex-start; }
.point .tick {
flex: none;
width: 54px;
height: 54px;
background: var(--blue-soft);
border: 2px solid var(--accent-blue);
display: grid;
place-items: center;
}
.point .tick svg { width: 24px; height: 24px; }
.point h3 { font-size: 33px; font-weight: 800; margin-bottom: 8px; letter-spacing: -0.01em; }
.point p { font-size: 25px; line-height: 1.5; font-weight: 400; color: var(--text-dark-soft); }
.takeaway { font-size: 30px; font-weight: 500; line-height: 1.4; max-width: 60ch; }
.takeaway strong { font-weight: 800; }
/* ===========================================
S5 · BIG STAT — oversized cobalt number on white
=========================================== */
.s-stat .panel-white { flex: 1; display: flex; align-items: center; }
.s-stat .panel-blue { flex: none; height: 300px; display: flex; align-items: center; }
.stat-num {
font-weight: 800;
font-size: 400px;
line-height: 0.9;
letter-spacing: -0.05em;
color: var(--accent-blue);
}
.stat-num .unit { font-size: 150px; letter-spacing: -0.02em; }
.stat-side { max-width: 980px; display: flex; gap: 64px; align-items: baseline; }
.stat-side h3 { font-size: 40px; font-weight: 800; flex: none; }
.stat-side p { font-size: 26px; line-height: 1.5; color: var(--text-light); }
/* ===========================================
S6 · QUOTE HERO — typography as the visual
=========================================== */
.s-quote .panel-white { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.s-quote .panel-blue { flex: none; height: 220px; display: flex; align-items: center; }
.quote-mark {
font-weight: 800;
font-size: 220px;
line-height: 0.55;
color: var(--accent-blue);
}
.quote-text {
font-size: 92px;
font-weight: 800;
line-height: 1.08;
letter-spacing: -0.03em;
max-width: 1560px;
margin-top: 44px;
}
.quote-attr {
font-size: 24px;
font-weight: 800;
letter-spacing: 0.2em;
text-transform: uppercase;
}
.quote-attr .soft { color: var(--text-light); font-weight: 500; margin-left: 24px; letter-spacing: 0.14em; }
/* ===========================================
S7 · COMPARISON — the split itself is the contrast
=========================================== */
.s-compare .panel { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.cmp-head { display: flex; align-items: baseline; gap: 40px; margin-bottom: 30px; }
.cmp-head h3 { font-size: 46px; font-weight: 800; letter-spacing: -0.01em; }
.cmp-head .tag {
font-size: 19px;
font-weight: 800;
letter-spacing: 0.24em;
text-transform: uppercase;
padding: 8px 18px;
}
.panel-white .cmp-head .tag { border: 2px solid var(--line-dark); color: var(--text-dark-soft); }
.panel-blue .cmp-head .tag { border: 2px solid var(--line-light); color: var(--text-light); }
.cmp-list { list-style: none; display: flex; gap: 70px; }
.cmp-list li { font-size: 25px; line-height: 1.45; max-width: 360px; padding-top: 20px; position: relative; }
.panel-white .cmp-list li { color: var(--text-dark-soft); }
.panel-blue .cmp-list li { color: var(--text-light); font-weight: 500; }
.cmp-list li::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 44px;
height: 4px;
background: var(--accent-blue);
}
.panel-blue .cmp-list li::before { background: var(--bg-white); }
/* ===========================================
S8 · PRINCIPLE GRID — 2×2 cards on cobalt
=========================================== */
.s-grid .panel-white { flex: none; height: 300px; display: flex; align-items: flex-end; padding-bottom: 52px; }
.s-grid .panel-blue { flex: 1; display: flex; align-items: center; }
.grid4 { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; width: 100%; }
.gcard { border: 2px solid var(--line-light); padding: 42px 48px; }
.gcard .gno { font-weight: 800; font-size: 24px; letter-spacing: 0.18em; margin-bottom: 18px; }
.gcard h3 { font-size: 31px; font-weight: 800; margin-bottom: 10px; letter-spacing: -0.01em; }
.gcard p { font-size: 25px; line-height: 1.45; color: var(--text-light); }
/* ===========================================
S9 · BAR CHART — pure CSS, cobalt bars on white
=========================================== */
.s-chart .panel-white { flex: 1; padding-top: 140px; display: flex; flex-direction: column; }
.s-chart .panel-blue { flex: none; height: 200px; display: flex; align-items: center; }
.chart { flex: 1; display: flex; align-items: flex-end; gap: 70px; padding: 80px 0 64px; }
.bar-group { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; gap: 18px; }
.bar {
background: var(--blue-soft);
border: 2px solid var(--accent-blue);
position: relative;
transform-origin: bottom;
transform: scaleY(0);
transition: transform 0.9s var(--ease-out-expo);
}
.bar.solid { background: var(--accent-blue); }
.slide.visible .bar { transform: scaleY(1); }
.slide.visible .bar-group:nth-child(1) .bar { transition-delay: 0.15s; }
.slide.visible .bar-group:nth-child(2) .bar { transition-delay: 0.25s; }
.slide.visible .bar-group:nth-child(3) .bar { transition-delay: 0.35s; }
.slide.visible .bar-group:nth-child(4) .bar { transition-delay: 0.45s; }
.bar-val {
position: absolute;
top: -52px;
left: 0;
right: 0;
text-align: center;
font-weight: 800;
font-size: 30px;
color: var(--text-dark);
}
.bar-label {
text-align: center;
font-size: 21px;
font-weight: 500;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--text-dark-soft);
}
/* ===========================================
S10 · CLOSING — cobalt dominant, statement type
=========================================== */
.s-close .panel-white { flex: none; height: 150px; display: flex; align-items: center; }
.s-close .panel-blue { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.close-links {
display: flex;
gap: 64px;
margin-top: 72px;
font-size: 24px;
font-weight: 800;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.close-links .lk { display: flex; align-items: center; gap: 16px; color: var(--text-light); }
.close-links svg { width: 30px; height: 30px; flex: none; }
/* ===========================================
DECK CHROME — page counter (outside the stage)
=========================================== */
.deck-controls {
font-family: var(--font);
font-size: 14px;
font-weight: 500;
letter-spacing: 0.22em;
color: rgba(255, 255, 255, 0.72);
background: rgba(10, 10, 10, 0.65);
border: 1px solid rgba(255, 255, 255, 0.16);
padding: 8px 18px;
border-radius: 999px;
user-select: none;
}
.deck-controls .cur { color: var(--text-light); font-weight: 800; }
/* ===========================================
REDUCED MOTION - strict degrade (supplements
the base block): no slide-up, no bar scale,
no stagger delays; content appears in place.
=========================================== */
@media (prefers-reduced-motion: reduce) {
.reveal { transform: none; transition-delay: 0s !important; }
.bar,
.slide.visible .bar { transform: none; transition-delay: 0s !important; }
}
</style>
<style id="od-no-pager">.deck-controls,#counter,.deck-counter{display:none!important}</style>
</head>
<body>
<div class="deck-viewport">
<main class="deck-stage" id="deckStage" aria-label="Slide deck">
<!-- === SLIDE 1: TITLE === -->
<section class="slide s-title active visible">
<div class="split">
<div class="panel panel-white panel-pad">
<p class="kicker reveal">Product Story · 2026</p>
<h1 class="display reveal">Open<br>Design<span class="blue">.</span></h1>
</div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<p class="lede reveal">The open-source, local, agent-driven design platform. From idea to prototypes, web pages, slides, and HTML video — the full workflow, on your own machine.</p>
<div class="meta-row reveal"><span>Apache-2.0</span><span class="soft">/</span><span>Runs Locally</span><span class="soft">/</span><span>BYOK</span></div>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">No Sign-up</span></div>
<div class="mark mark-bl light"><span class="soft">Electric Studio</span></div>
<div class="mark mark-br light">01 / 10</div>
</section>
<!-- === SLIDE 2: AGENDA === -->
<section class="slide s-agenda">
<div class="split">
<div class="panel panel-white panel-pad">
<h2 class="h2 reveal">What's inside<span class="blue">.</span></h2>
</div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<div class="agenda-rows">
<div class="agenda-row reveal"><span class="idx">01</span>Why Open Design exists<span class="hint">Why</span></div>
<div class="agenda-row reveal"><span class="idx">02</span>The platform, by the numbers<span class="hint">Numbers</span></div>
<div class="agenda-row reveal"><span class="idx">03</span>Capabilities &amp; ecosystem<span class="hint">Capabilities</span></div>
<div class="agenda-row reveal"><span class="idx">04</span>Getting started<span class="hint">CTA</span></div>
</div>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">Agenda</span></div>
<div class="mark mark-bl light"><span class="soft">Product Story 2026</span></div>
<div class="mark mark-br light">02 / 10</div>
</section>
<!-- === SLIDE 3: SECTION DIVIDER === -->
<section class="slide s-divider">
<div class="split">
<div class="panel panel-white panel-pad"></div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<div class="divider-no reveal">01</div>
<div class="divider-label reveal">Why Open Design</div>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">Section</span></div>
<div class="mark mark-bl light"><span class="soft">Product Story 2026</span></div>
<div class="mark mark-br light">03 / 10</div>
</section>
<!-- === SLIDE 4: BULLETS === -->
<section class="slide s-bullets">
<div class="split">
<div class="panel panel-white panel-pad">
<p class="kicker reveal">Three principles</p>
<h2 class="h2 reveal" style="font-size: 66px;">Open, local,<br>agent-driven</h2>
<div class="points">
<div class="point reveal">
<span class="tick"><svg viewBox="0 0 24 24" fill="none" stroke="#4361ee" stroke-width="3"><path d="M4 12l5 5L20 6"/></svg></span>
<div><h3>Desktop-native</h3><p>Design happens on the desktop. Local files, Figma exports, and code repos are directly readable; agents get full terminal power.</p></div>
</div>
<div class="point reveal">
<span class="tick"><svg viewBox="0 0 24 24" fill="none" stroke="#4361ee" stroke-width="3"><path d="M4 12l5 5L20 6"/></svg></span>
<div><h3>Plug in agents, don't rebuild them</h3><p>Claude Code, Codex, and Cursor on your machine are already powerful. Open Design wires them into a complete design workflow.</p></div>
</div>
<div class="point reveal">
<span class="tick"><svg viewBox="0 0 24 24" fill="none" stroke="#4361ee" stroke-width="3"><path d="M4 12l5 5L20 6"/></svg></span>
<div><h3>It learns your taste</h3><p>Every choice becomes design systems, preferences, and memory, so the next generation lands closer to what you want.</p></div>
</div>
</div>
</div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<p class="takeaway reveal">In April 2026, Claude Design proved an LLM can truly design — <strong>Open Design sets that capability free.</strong></p>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">Why</span></div>
<div class="mark mark-br light">04 / 10</div>
</section>
<!-- === SLIDE 5: BIG STAT === -->
<section class="slide s-stat">
<div class="split">
<div class="panel panel-white panel-pad">
<div class="stat-num reveal">60K<span class="unit">+ stars</span></div>
</div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<div class="stat-side reveal">
<h3>on GitHub</h3>
<p>Open source under Apache-2.0 — built with 300+ contributors, 217+ plugins, 129 design systems, and 21 coding agents plugged in.</p>
</div>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">By the numbers</span></div>
<div class="mark mark-br light">05 / 10</div>
</section>
<!-- === SLIDE 6: QUOTE HERO === -->
<section class="slide s-quote">
<div class="split">
<div class="panel panel-white panel-pad">
<div class="quote-mark reveal">&ldquo;</div>
<p class="quote-text reveal">Design is finally under <span class="blue">version control.</span></p>
</div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<p class="quote-attr reveal">Indie developer<span class="soft">On file-system semantics &amp; git-diffable design artifacts</span></p>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">Community</span></div>
<div class="mark mark-br light">06 / 10</div>
</section>
<!-- === SLIDE 7: COMPARISON — split is the contrast === -->
<section class="slide s-compare">
<div class="split">
<div class="panel panel-white panel-pad">
<div class="cmp-head reveal"><h3>Claude Design</h3><span class="tag">Closed</span></div>
<ul class="cmp-list reveal">
<li>Closed-source, paid, and cloud-only</li>
<li>Locked to one model — no agent choice</li>
<li>No self-hosting, no BYOK</li>
</ul>
</div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<div class="cmp-head reveal"><h3>Open Design</h3><span class="tag">Open</span></div>
<ul class="cmp-list reveal">
<li>Apache-2.0 — fork, self-host, commercial use allowed</li>
<li>21 coding agents, auto-detected, zero config</li>
<li>Full BYOK — monthly cost from $500 to ~$73, -80%</li>
</ul>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">Why</span></div>
<div class="mark mark-br light">07 / 10</div>
</section>
<!-- === SLIDE 8: PRINCIPLE GRID === -->
<section class="slide s-grid">
<div class="split">
<div class="panel panel-white panel-pad">
<h2 class="h2 reveal" style="font-size: 64px;">Core capabilities<span class="blue">.</span></h2>
</div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<div class="grid4">
<div class="gcard reveal"><div class="gno">A</div><h3>HTML video pipeline</h3><p>Programmatic video via HyperFrames — HTML in, MP4 out, for marketing shorts and product demos.</p></div>
<div class="gcard reveal"><div class="gno">B</div><h3>129 design systems</h3><p>Import from GitHub, Figma, or local files; markdown systems keep brand tokens from drifting.</p></div>
<div class="gcard reveal"><div class="gno">C</div><h3>Plugin ecosystem</h3><p>217+ plugins covering prototypes, slides, posters, and dashboards — and growing.</p></div>
<div class="gcard reveal"><div class="gno">D</div><h3>Full-stack BYOK</h3><p>Anthropic, OpenAI, Azure, and Google endpoints, plus 14 media providers — your keys, your bill.</p></div>
</div>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">Capabilities</span></div>
<div class="mark mark-br light">08 / 10</div>
</section>
<!-- === SLIDE 9: BAR CHART === -->
<section class="slide s-chart">
<div class="split">
<div class="panel panel-white panel-pad">
<p class="kicker reveal">217+ plugins · demo split</p>
<h2 class="h2 reveal" style="font-size: 60px;">Where the plugins land</h2>
<div class="chart">
<div class="bar-group reveal">
<div class="bar" style="height: 53%;"><span class="bar-val">43</span></div>
<div class="bar-label">Dashboards</div>
</div>
<div class="bar-group reveal">
<div class="bar solid" style="height: 84%;"><span class="bar-val">68</span></div>
<div class="bar-label">Slides &amp; decks</div>
</div>
<div class="bar-group reveal">
<div class="bar solid" style="height: 70%;"><span class="bar-val">57</span></div>
<div class="bar-label">Prototypes</div>
</div>
<div class="bar-group reveal">
<div class="bar" style="height: 60%;"><span class="bar-val">49</span></div>
<div class="bar-label">Posters &amp; image</div>
</div>
</div>
</div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<p class="takeaway reveal"><strong>217+ plugins, one ecosystem</strong> — every artifact type covered, from decks to dashboards (demo data).</p>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">By the numbers</span></div>
<div class="mark mark-br light">09 / 10</div>
</section>
<!-- === SLIDE 10: CLOSING === -->
<section class="slide s-close">
<div class="split">
<div class="panel panel-white panel-pad"></div>
<div class="edge-bar"></div>
<div class="panel panel-blue panel-pad">
<h2 class="display reveal" style="color: var(--text-light);">Open. Local.<br>Agent-native<span style="color: var(--bg-dark);">.</span></h2>
<div class="close-links reveal">
<span class="lk"><svg viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2"><path d="M3 8l9 6 9-6M4 19h16a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1z"/></svg>Download Open Design</span>
<span class="lk"><svg viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.5 3.8 5.6 3.8 9S14.5 18.5 12 21c-2.5-2.5-3.8-5.6-3.8-9S9.5 5.5 12 3z"/></svg>github.com/nexu-io/open-design</span>
</div>
</div>
</div>
<div class="mark mark-tl dark"><span class="chip"></span>Open Design</div>
<div class="mark mark-tr dark"><span class="soft">Thank you</span></div>
<div class="mark mark-bl light"><span class="soft">Product Story 2026</span></div>
<div class="mark mark-br light">10 / 10</div>
</section>
</main>
</div>
<!-- Page counter lives outside the scaled stage -->
<div class="deck-controls" aria-hidden="true"><span class="cur" id="pageCur">1</span>&nbsp;/&nbsp;<span id="pageTotal">10</span></div>
<script>
/* ===========================================
SLIDE PRESENTATION CONTROLLER
Fixed 1920×1080 stage, scaled uniformly to the viewport.
Navigation: ← → Space PageUp/PageDown Home/End, wheel,
touch swipe. Current slide is mirrored into the URL hash.
=========================================== */
(function () {
'use strict';
var STAGE_W = 1920;
var STAGE_H = 1080;
var stage = document.getElementById('deckStage');
var slides = Array.prototype.slice.call(document.querySelectorAll('.slide'));
var pageCur = document.getElementById('pageCur');
var pageTotal = document.getElementById('pageTotal');
var current = 0;
var wheelLockUntil = 0;
pageTotal.textContent = String(slides.length);
/* --- Stage scaling: one transform, letterbox as needed --- */
function fitStage() {
var factor = Math.min(window.innerWidth / STAGE_W, window.innerHeight / STAGE_H);
var x = (window.innerWidth - STAGE_W * factor) / 2;
var y = (window.innerHeight - STAGE_H * factor) / 2;
stage.style.transform = 'translate(' + x + 'px, ' + y + 'px) scale(' + factor + ')';
}
/* --- Hash routing: #/3 means slide index 3 (0-based) --- */
function indexFromHash() {
var m = /^#\/(\d+)$/.exec(window.location.hash);
if (!m) return null;
var idx = parseInt(m[1], 10);
return isNaN(idx) ? null : idx;
}
function show(index, skipHash) {
current = Math.max(0, Math.min(index, slides.length - 1));
slides.forEach(function (slide, i) {
slide.classList.toggle('active', i === current);
slide.classList.toggle('visible', i === current);
});
pageCur.textContent = String(current + 1);
if (!skipHash) {
var target = '#/' + current;
if (window.location.hash !== target) {
window.history.replaceState(null, '', target);
}
}
}
function next() { show(current + 1); }
function prev() { show(current - 1); }
/* --- Keyboard --- */
document.addEventListener('keydown', function (e) {
if (e.target && e.target.getAttribute && e.target.getAttribute('contenteditable')) return;
switch (e.key) {
case 'ArrowRight':
case 'ArrowDown':
case ' ':
case 'PageDown':
e.preventDefault(); next(); break;
case 'ArrowLeft':
case 'ArrowUp':
case 'PageUp':
e.preventDefault(); prev(); break;
case 'Home':
e.preventDefault(); show(0); break;
case 'End':
e.preventDefault(); show(slides.length - 1); break;
}
});
/* --- Mouse wheel (debounced) --- */
document.addEventListener('wheel', function (e) {
var now = Date.now();
if (now < wheelLockUntil) return;
if (Math.abs(e.deltaY) < 12) return;
wheelLockUntil = now + 650;
if (e.deltaY > 0) { next(); } else { prev(); }
}, { passive: true });
/* --- Touch swipe --- */
var touchX = null;
document.addEventListener('touchstart', function (e) {
if (e.touches.length === 1) touchX = e.touches[0].clientX;
}, { passive: true });
document.addEventListener('touchend', function (e) {
if (touchX === null) return;
var dx = e.changedTouches[0].clientX - touchX;
touchX = null;
if (Math.abs(dx) < 40) return;
if (dx < 0) { next(); } else { prev(); }
}, { passive: true });
/* --- Hash navigation (back/forward, deep links) --- */
window.addEventListener('hashchange', function () {
var idx = indexFromHash();
if (idx !== null && idx !== current) show(idx, true);
});
window.addEventListener('resize', fitStage);
fitStage();
var initial = indexFromHash();
show(initial !== null ? initial : 0, initial === null);
})();
</script>
</body>
</html>