
Kami Deck
- 139 installs
- 83.7k repo stars
- Updated August 5, 2026
- nexu-io/open-design
Generate Kami-styled HTML presentation decks for internal reviews, partner pitches, and design-system-aligned storytelling.
About
Open-design Kami deck skill outputs HTML presentations using the Kami visual language—consistent tokens, slide templates, and narrative structure—for aligned pitches and design reviews from nexu-io/open-design.
- Kami design variant
- HTML slide decks
- Brand-consistent storytelling
- Partner pitch ready
- Open-design family
Kami Deck by the numbers
- 139 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #1,032 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/nexu-io/open-design --skill kami-deckAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 139 |
|---|---|
| repo stars | ★ 83.7k |
| Last updated | August 5, 2026 |
| Repository | nexu-io/open-design ↗ |
What it does
Generate Kami-styled HTML presentation decks for internal reviews, partner pitches, and design-system-aligned storytelling.
Files
kami-deck
Sister skill to `kami-landing`. Produces a single self-contained HTML file: a horizontal magazine-style swipe deck in the kami (紙 / 纸) design system — print rhythm, ink-blue accent, serif at one weight, no italic, no cool grays.
The navigation model is intentionally borrowed from the `guizang-ppt` skill — ←/→ arrow keys, wheel / swipe, ESC for the overview grid. The aesthetic stays kami: parchment content slides, ink-blue cover and chapter slides, serif everywhere.
Design system source of truth:
`design-systems/kami/DESIGN.md`.
Read it before shipping. Tokens, type rules, and forbidden colors
all live there. Slide-specific scale ratios (macro × 1.6,
letter-spacing × 0.6 vs. print) are documented in §3 "Hierarchy"
and §5 "Layout Principles · Slides".
What you get
- N viewport-sized slides (6-15 is the sweet spot) laid out
horizontally on one transformed flex track.
- Cover and chapter slides flip background to ink-blue
(#1B365D) with ivory text — the only place dark theme is used.
- Content / stats / quote / CTA slides stay on parchment
(#f5f4ed) with serif at weight 500.
- Per-slide chrome strip: brand mark · deck title · live slide
counter (01 / 09).
- Tabular-nums on every counter, metric, page number.
- Coral-free — kami's accent is ink-blue. Progress bar and dot
nav are ink-blue too.
- Keyboard / wheel / touch nav, ESC overview grid, dot indicator.
- Multilingual stack — EN / zh-CN / ja, set on
:rootvia
the language parameter.
Slide types
| Kind | Background | Use it for |
|---|---|---|
cover | ink-blue | Title plate at the start. Centered serif title + tagline. |
chapter | ink-blue | Roman/Arabic numeral chapter divider. |
content | parchment | Section number + title + body + optional bullets. |
stats | parchment | 3-4 metric cells (value · label · sub). |
quote | parchment | Pull quote with ink-blue left rule + author signature. |
cta | parchment | Closing pitch + 1-2 buttons. |
end | ink-blue | Mega serif kicker word + colophon footer. |
A typical 11-slide deck:
1. cover — ink-blue title plate
2. chapter — "01 / Why now"
3. content — manifesto
4. content — capabilities + bullets
5. stats — 4 numbers
6. chapter — "02 / How it feels"
7. content — method
8. content — selected work
9. quote — testimonial
10. cta — primary action
11. end — ink-blue kickerWorkflow
1. Gather the brief
Ask in two rounds (don't dump the whole list at once):
1. Identity round — name, mark, tagline, location, edition, language. 2. Content round — for each slide, kind + the typed fields.
2. Pick the language stack
Same as `kami-landing`: EN → Charter, zh-CN → TsangerJinKai02 / Source Han Serif, ja → YuMincho. JA also overrides --olive to #4d4c48 because YuMincho strokes are thinner.
3. Write index.html
Output a single file with all CSS inline. Mirror the structure of `example.html`. Use only the tokens from design-systems/kami/DESIGN.md.
The runtime script (keyboard / wheel / touch nav, dot indicator, progress bar, ESC overview) should match the model documented in `open-design-landing-deck/scripts/compose.ts`. Do not reuse the open-design-landing-deck CSS; the visual language is different.
4. Self-check
- [ ] All cover / chapter / end slides use ink-blue background
(#1B365D) with ivory text. All other slides are on parchment.
- [ ] Ink-blue covers ≤ 5% of any parchment slide's surface.
- [ ] Slide titles use serif weight 500 only. No italic.
- [ ] All numeric stacks (counter, metrics, page numbers) carry
font-variant-numeric: tabular-nums.
- [ ] Press
→/Space/ scroll. Smoothly slides one viewport
to the right; dot nav advances; the ink-blue progress bar ticks forward.
- [ ] Press
Esc. Overview grid appears with scaled thumbnails. - [ ] Resize to 1080px and 640px. Cover / content collapse to a
single column; dot nav still works.
- [ ] Lighthouse: contrast AA, font-display swap, no layout shift.
Boundaries
- Do not introduce a second accent color. Pick ink-blue or
pick nothing.
- Do not use italic anywhere — emphasis swaps to ink-blue.
- Do not use
rgba()for tag fills; pre-blend over parchment
and use solid hex from the table in design-systems/kami/DESIGN.md §2.
- Do not add a router. This is a single-file artifact.
- Do not reuse Atelier Zero collage imagery (the open-design-landing
visual system). Kami is gradient-free, image-light, and hierarchy is carried by type.
See also
- `kami-landing` — long-form one-pager sister skill.
- `design-systems/kami/DESIGN.md` — token spec.
- `open-design-landing-deck` — same
horizontal swipe nav model, different visual language (Atelier Zero).
- Upstream: `tw93/kami` — original
Claude skill (MIT). Kami's slides.py template documents the macro × 1.6 / micro × 0.6 ratios this skill applies.
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<title>Open Design · kami deck — Vol. 01 / Issue Nº 26</title>
<meta name='description' content='Open Design as a kami slide deck. Warm parchment with ink-blue cover and chapter slides, serif at one weight, no italic.' />
<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=Source+Serif+4:wght@400;500&family=Source+Sans+3:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap' rel='stylesheet' />
<style>
/*
* kami-deck — single-file slide deck stylesheet.
*
* Kami token language (warm parchment, ink-blue accent, serif at one
* weight, no italic) — but laid out as a horizontal swipe deck. Macro
* tokens are scaled × ~1.6 vs. print baseline; letter-spacing scaled
* × ~0.6 (per design-systems/kami/DESIGN.md §3).
*
* Navigation model is borrowed from skills/guizang-ppt — same flex
* track + transform translateX, same key/wheel/touch handlers.
*/
:root {
/* surface */
--parchment: #f5f4ed;
--ivory: #faf9f5;
--warm-sand: #e8e6dc;
--deep-dark: #141413;
/* brand (single chromatic accent) */
--brand: #1B365D;
--brand-light: #2D5A8A;
/* text */
--near-black: #141413;
--dark-warm: #3d3d3a;
--olive: #504e49;
--stone: #6b6a64;
/* border */
--border: #e8e6dc;
--border-soft: #e5e3d8;
/* tag tints (solid hex, NEVER rgba) */
--tag-08: #EEF2F7;
--tag-14: #E4ECF5;
--tag-22: #D0DCE9;
/* type */
--serif: 'Source Serif 4', Charter, Georgia, Palatino, 'Times New Roman', serif;
--sans: 'Source Sans 3', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--mono: 'JetBrains Mono', 'SF Mono', 'Fira Code', Consolas, Monaco, monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
width: 100%; height: 100%;
overflow: hidden;
background: var(--parchment);
color: var(--near-black);
font-family: var(--serif);
font-weight: 400;
-webkit-font-smoothing: antialiased;
}
strong { font-weight: 500; }
/* ---------- deck flex track ---------- */
#deck {
position: fixed; inset: 0;
height: 100vh;
display: flex;
flex-wrap: nowrap;
transition: transform 0.9s cubic-bezier(0.77, 0, 0.175, 1);
z-index: 5;
will-change: transform;
}
.slide {
width: 100vw; height: 100vh;
flex: 0 0 100vw;
position: relative;
padding: 80px 96px;
display: flex;
flex-direction: column;
overflow: hidden;
background: var(--parchment);
color: var(--near-black);
}
.slide.dark {
background: var(--brand);
color: var(--ivory);
}
.slide-inner {
max-width: 1280px;
margin: 0 auto;
width: 100%;
height: 100%;
display: grid;
align-content: center;
gap: 24px;
position: relative;
min-height: 0;
}
/* ---------- per-slide chrome strip ---------- */
.slide-chrome {
position: absolute;
top: 28px; left: 0; right: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 96px;
font-family: var(--sans);
font-size: 11px;
font-weight: 500;
letter-spacing: 0.6px;
text-transform: uppercase;
color: var(--stone);
z-index: 4;
pointer-events: none;
}
.slide.dark .slide-chrome { color: rgba(250, 249, 245, 0.55); }
.slide-chrome b { color: var(--near-black); font-weight: 500; }
.slide.dark .slide-chrome b { color: var(--ivory); }
.slide-chrome .left { display: inline-flex; align-items: center; gap: 12px; }
.slide-chrome .right {
display: inline-flex; align-items: center; gap: 12px;
font-variant-numeric: tabular-nums;
}
.slide-chrome .mark {
width: 22px; height: 22px;
border-radius: 50%;
border: 1px solid currentColor;
display: inline-flex;
align-items: center;
justify-content: center;
font-family: var(--serif);
font-weight: 500;
font-size: 11px;
letter-spacing: 0;
opacity: 0.85;
}
.slide-foot {
position: absolute;
bottom: 28px; left: 0; right: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 96px;
font-family: var(--mono);
font-size: 10.5px;
letter-spacing: 0.4px;
color: var(--stone);
font-variant-numeric: tabular-nums;
z-index: 4;
pointer-events: none;
}
.slide.dark .slide-foot { color: rgba(250, 249, 245, 0.55); }
.slide-foot .counter {
font-family: var(--mono);
letter-spacing: 0.04em;
color: var(--near-black);
background: var(--ivory);
padding: 4px 8px;
border: 1px solid var(--border);
border-radius: 4px;
}
.slide.dark .slide-foot .counter {
color: var(--ivory);
background: rgba(250, 249, 245, 0.08);
border-color: rgba(250, 249, 245, 0.22);
}
/* ---------- progress bar ---------- */
.deck-progress {
position: fixed;
left: 0; right: 0; bottom: 0;
height: 2px;
background: rgba(27, 54, 93, 0.08);
z-index: 30;
}
.deck-progress .bar {
height: 100%;
background: var(--brand);
width: 0%;
transition: width 0.6s cubic-bezier(0.77, 0, 0.175, 1);
}
/* ---------- dot nav ---------- */
#nav {
position: fixed;
left: 50%;
bottom: 40px;
transform: translateX(-50%);
z-index: 30;
display: flex;
gap: 9px;
padding: 8px 14px;
border-radius: 999px;
background: rgba(245, 244, 237, 0.78);
border: 1px solid var(--border);
backdrop-filter: blur(8px);
}
#nav .dot {
width: 7px; height: 7px;
border-radius: 50%;
background: rgba(27, 54, 93, 0.22);
cursor: pointer;
transition: all 0.3s ease;
border: 0;
padding: 0;
}
#nav .dot:hover {
background: rgba(27, 54, 93, 0.5);
transform: scale(1.15);
}
#nav .dot.active {
background: var(--brand);
width: 22px;
border-radius: 999px;
}
#hint {
position: fixed;
bottom: 36px; right: 28px;
z-index: 30;
font-family: var(--mono);
font-size: 10px;
letter-spacing: 0.4px;
text-transform: uppercase;
color: var(--stone);
opacity: 0.75;
}
/* ---------- COVER slide (dark) ---------- */
.s-cover .slide-inner {
grid-template-columns: 1fr;
text-align: left;
align-content: center;
gap: 28px;
max-width: 980px;
}
.s-cover .eyebrow {
font-family: var(--sans);
font-size: 12px;
font-weight: 500;
letter-spacing: 1.2px;
text-transform: uppercase;
color: rgba(250, 249, 245, 0.65);
}
.s-cover h1 {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(60px, 7vw, 110px);
line-height: 1.05;
letter-spacing: -1.2px;
color: var(--ivory);
margin: 0;
}
.s-cover h1 .hl { color: #B5C8DC; }
.s-cover .tagline {
font-family: var(--serif);
font-weight: 500;
font-size: 21px;
color: rgba(250, 249, 245, 0.75);
max-width: 50ch;
line-height: 1.45;
}
.s-cover .meta {
margin-top: 20px;
display: flex;
align-items: center;
gap: 20px;
font-family: var(--mono);
font-size: 11px;
letter-spacing: 0.4px;
text-transform: uppercase;
color: rgba(250, 249, 245, 0.6);
font-variant-numeric: tabular-nums;
}
.s-cover .meta .rule {
width: 56px; height: 1px;
background: rgba(250, 249, 245, 0.4);
display: inline-block;
}
/* ---------- CHAPTER divider (dark) ---------- */
.s-chapter .slide-inner {
grid-template-columns: 1fr;
text-align: center;
align-content: center;
gap: 36px;
}
.s-chapter .num {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(64px, 8vw, 130px);
color: rgba(250, 249, 245, 0.55);
letter-spacing: -0.5px;
line-height: 1;
font-variant-numeric: tabular-nums;
}
.s-chapter h2 {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(48px, 5.4vw, 84px);
line-height: 1.1;
letter-spacing: -0.6px;
color: var(--ivory);
margin: 0 auto;
max-width: 22ch;
}
.s-chapter .lede {
font-family: var(--serif);
font-weight: 500;
font-size: 19px;
color: rgba(250, 249, 245, 0.7);
max-width: 42ch;
margin: 0 auto;
line-height: 1.5;
letter-spacing: 0.05em;
}
/* ---------- CONTENT slide ---------- */
.s-content .slide-inner {
grid-template-columns: 1fr 2.4fr;
gap: 56px;
align-content: center;
}
.s-content.layout-full .slide-inner { grid-template-columns: 1fr; max-width: 980px; }
.s-content .head { display: flex; flex-direction: column; gap: 16px; }
.s-content .num {
font-family: var(--serif);
font-weight: 500;
font-size: 14px;
letter-spacing: 0.4px;
color: var(--brand);
font-variant-numeric: tabular-nums;
}
.s-content h2 {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(36px, 4vw, 56px);
line-height: 1.1;
letter-spacing: 0.4px;
color: var(--near-black);
margin: 0;
max-width: 14ch;
}
.s-content .lede {
font-family: var(--serif);
font-weight: 500;
font-size: 15px;
color: var(--olive);
max-width: 28ch;
line-height: 1.5;
}
.s-content .body { display: flex; flex-direction: column; gap: 18px; }
.s-content .body p {
font-family: var(--serif);
font-weight: 400;
font-size: 17px;
line-height: 1.55;
color: var(--dark-warm);
max-width: 64ch;
}
.s-content .body p strong { color: var(--near-black); font-weight: 500; }
.s-content .body code {
font-family: var(--mono);
font-size: 14px;
color: var(--brand);
background: var(--tag-08);
padding: 1px 6px;
border-radius: 3px;
}
.s-content ul.dash {
list-style: none; padding: 0; margin: 4px 0 0;
display: flex; flex-direction: column; gap: 10px;
}
.s-content ul.dash li {
position: relative; padding-left: 18px;
font-family: var(--serif);
font-weight: 400;
font-size: 16px;
line-height: 1.55;
color: var(--dark-warm);
}
.s-content ul.dash li::before {
content: '\2013';
position: absolute; left: 0;
color: var(--brand);
}
.s-content .body .tag-row { margin-top: 4px; }
.s-content .tag {
display: inline-block;
font-family: var(--sans);
font-size: 12px;
font-weight: 500;
padding: 2px 8px;
border-radius: 4px;
color: var(--brand);
background: var(--tag-14);
letter-spacing: 0.4px;
}
/* ---------- STATS slide ---------- */
.s-stats .slide-inner { grid-template-columns: 1fr; gap: 48px; }
.s-stats .head { display: flex; flex-direction: column; gap: 18px; }
.s-stats .num {
font-family: var(--serif);
font-weight: 500;
font-size: 14px;
letter-spacing: 0.4px;
color: var(--brand);
font-variant-numeric: tabular-nums;
}
.s-stats h2 {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(36px, 4.2vw, 60px);
line-height: 1.1;
letter-spacing: 0.4px;
color: var(--near-black);
max-width: 22ch;
margin: 0;
}
.s-stats .grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 0;
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
}
.s-stats .stat {
padding: 32px 24px 28px;
display: flex;
flex-direction: column;
gap: 8px;
border-right: 1px solid var(--border-soft);
}
.s-stats .stat:last-child { border-right: 0; }
.s-stats .stat .v {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(48px, 6vw, 88px);
line-height: 1;
letter-spacing: -0.8px;
color: var(--brand);
font-variant-numeric: tabular-nums;
}
.s-stats .stat .l {
font-family: var(--serif);
font-weight: 500;
font-size: 15px;
color: var(--near-black);
margin-top: 8px;
}
.s-stats .stat .s {
font-family: var(--serif);
font-weight: 400;
font-size: 13px;
color: var(--olive);
line-height: 1.5;
max-width: 28ch;
}
.s-stats .caption {
font-family: var(--mono);
font-size: 11px;
color: var(--stone);
letter-spacing: 0.4px;
font-variant-numeric: tabular-nums;
}
/* ---------- QUOTE slide ---------- */
.s-quote .slide-inner {
grid-template-columns: 1fr;
max-width: 1080px;
gap: 36px;
align-content: center;
}
.s-quote blockquote {
border-left: 2px solid var(--brand);
padding: 6px 0 6px 28px;
font-family: var(--serif);
font-weight: 500;
font-size: clamp(28px, 3.4vw, 42px);
line-height: 1.35;
color: var(--near-black);
margin: 0;
letter-spacing: 0.05em;
max-width: 36ch;
}
.s-quote .author {
display: flex;
align-items: center;
gap: 16px;
margin-top: 12px;
}
.s-quote .author .glyph {
width: 44px; height: 44px;
border-radius: 50%;
background: var(--brand); color: var(--ivory);
font-family: var(--serif); font-weight: 500;
font-size: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
text-transform: uppercase;
letter-spacing: 0;
}
.s-quote .author p {
font-family: var(--serif);
font-weight: 500;
font-size: 14px;
color: var(--near-black);
}
.s-quote .author p span {
display: block;
color: var(--olive);
font-weight: 400;
margin-top: 2px;
}
/* ---------- CTA slide ---------- */
.s-cta .slide-inner {
grid-template-columns: 1fr;
max-width: 980px;
gap: 28px;
align-content: center;
text-align: left;
}
.s-cta .eyebrow {
font-family: var(--sans);
font-size: 12px; font-weight: 500;
letter-spacing: 1.2px;
text-transform: uppercase;
color: var(--brand);
}
.s-cta h2 {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(48px, 5.4vw, 88px);
line-height: 1.05;
letter-spacing: -0.6px;
color: var(--near-black);
margin: 0;
}
.s-cta .body {
font-family: var(--serif);
font-weight: 400;
font-size: 17px;
color: var(--dark-warm);
max-width: 50ch;
line-height: 1.55;
}
.s-cta .actions {
display: inline-flex;
gap: 12px;
margin-top: 12px;
align-items: center;
flex-wrap: wrap;
}
.s-cta .btn {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--sans);
font-size: 13px;
font-weight: 500;
letter-spacing: 0.4px;
padding: 10px 16px;
border-radius: 8px;
border: 0;
cursor: pointer;
text-decoration: none;
}
.s-cta .btn-primary {
background: var(--brand);
color: var(--ivory);
box-shadow: 0 0 0 1px var(--brand);
}
.s-cta .btn-primary:hover { background: var(--brand-light); box-shadow: 0 0 0 1px var(--brand-light); }
.s-cta .btn-ghost {
background: transparent;
color: var(--brand);
box-shadow: 0 0 0 1px var(--brand);
}
.s-cta .btn-ghost:hover { background: var(--tag-08); }
/* ---------- END slide (dark) ---------- */
.s-end .slide-inner {
grid-template-columns: 1fr;
align-content: end;
padding-bottom: 24px;
text-align: left;
gap: 18px;
max-width: none;
}
.s-end .word {
font-family: var(--serif);
font-weight: 500;
font-size: clamp(96px, 16vw, 240px);
line-height: 1;
letter-spacing: -1.2px;
color: var(--ivory);
white-space: nowrap;
overflow-x: hidden;
}
.s-end .word .hl { color: #B5C8DC; }
.s-end .colophon {
border-top: 1px solid rgba(250, 249, 245, 0.22);
padding-top: 22px;
font-family: var(--sans);
font-size: 11px;
font-weight: 500;
letter-spacing: 1.2px;
text-transform: uppercase;
color: rgba(250, 249, 245, 0.65);
font-variant-numeric: tabular-nums;
}
/* ---------- ESC overview ---------- */
#overview {
position: fixed; inset: 0;
z-index: 100;
background: rgba(245, 244, 237, 0.96);
backdrop-filter: blur(12px);
display: none;
overflow-y: auto;
padding: 60px 56px;
}
#overview .ov-head {
display: flex; justify-content: space-between; align-items: baseline;
margin-bottom: 32px;
font-family: var(--sans); font-size: 11px;
letter-spacing: 1.2px; text-transform: uppercase;
color: var(--stone);
font-variant-numeric: tabular-nums;
}
#overview .ov-head b { color: var(--near-black); font-weight: 500; }
#overview .ov-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 22px;
max-width: 1280px;
margin: 0 auto;
}
#overview .ov-card {
cursor: pointer;
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--border);
transition: border-color 0.2s, transform 0.2s;
background: var(--ivory);
}
#overview .ov-card:hover { border-color: var(--brand); transform: translateY(-2px); }
#overview .ov-card.active { border-color: var(--brand); border-width: 2px; }
#overview .ov-thumb {
width: 100%;
aspect-ratio: 16 / 10;
overflow: hidden;
position: relative;
pointer-events: none;
background: var(--parchment);
}
#overview .ov-thumb .clone {
width: 100vw; height: 100vh;
transform: scale(0.18);
transform-origin: top left;
position: absolute;
top: 0; left: 0;
pointer-events: none;
}
#overview .ov-label {
padding: 8px 12px;
font-family: var(--mono); font-size: 10px;
letter-spacing: 0.4px; text-transform: uppercase;
color: var(--stone);
display: flex; justify-content: space-between; align-items: center;
font-variant-numeric: tabular-nums;
}
#overview .ov-label b { color: var(--near-black); font-weight: 500; }
/* ---------- responsive ---------- */
@media (max-width: 1080px) {
.slide { padding: 64px 56px; }
.slide-chrome, .slide-foot { padding: 0 56px; }
.s-content .slide-inner { grid-template-columns: 1fr; gap: 28px; }
}
@media (max-width: 640px) {
.slide { padding: 44px 28px; }
.slide-chrome, .slide-foot { padding: 0 28px; font-size: 9.5px; letter-spacing: 0.6px; }
#hint { display: none; }
}
</style>
</head>
<body>
<div id='deck'>
<!-- ===== 01 · COVER (dark) ===== -->
<section class='slide s-cover dark' data-slide-kind='cover'>
<div class='slide-chrome'>
<span class='left'><span class='mark'>kami</span> <b>Open Design</b> · Vol. 01 / Issue Nº 26</span>
<span class='right'>Open Design · kami deck</span>
</div>
<div class='slide-inner'>
<span class='eyebrow'>Open-source design studio · Nº 01</span>
<h1>Designing intelligence on warm <span class='hl'>paper</span>.</h1>
<p class='tagline'>The open-source studio for editorial documents and slide decks — typeset by your own coding agent.</p>
<div class='meta'>
<span>Berlin · 52.5200° N · 13.4050° E</span>
<span class='rule'></span>
<span>MMXXVI · Apache-2.0</span>
</div>
</div>
<div class='slide-foot'>
<span>Berlin · MMXXVI</span>
<span class='counter'>01 / 09</span>
</div>
</section>
<!-- ===== 02 · CHAPTER (dark) ===== -->
<section class='slide s-chapter dark' data-slide-kind='chapter'>
<div class='slide-chrome'>
<span class='left'><span class='mark'>kami</span> <b>Open Design</b> · Vol. 01 / Issue Nº 26</span>
<span class='right'>Open Design · kami deck</span>
</div>
<div class='slide-inner'>
<p class='num'>01</p>
<h2>Why design needs another tool.</h2>
<p class='lede'>Because the strongest agents already live on your laptop — and they deserve a real workflow, not a chat window.</p>
</div>
<div class='slide-foot'>
<span>Berlin · MMXXVI</span>
<span class='counter'>02 / 09</span>
</div>
</section>
<!-- ===== 03 · CONTENT ===== -->
<section class='slide s-content' data-slide-kind='content'>
<div class='slide-chrome'>
<span class='left'><span class='mark'>kami</span> <b>Open Design</b> · Vol. 01 / Issue Nº 26</span>
<span class='right'>Open Design · kami deck</span>
</div>
<div class='slide-inner'>
<div class='head'>
<p class='num'>01.1</p>
<h2>What it is.</h2>
<p class='lede'>A local-first design studio for the agent you already trust.</p>
</div>
<div class='body'>
<p>
Open Design is the <strong>open-source alternative to Anthropic's Claude Design</strong>. It runs on your laptop. Your agent reads a folder of <code>SKILL.md</code> files and a folder of <code>DESIGN.md</code> systems, then produces real files — landing pages, decks, white papers, dashboards.
</p>
<ul class='dash'>
<li>Files, not opaque prompts — every skill is a folder of Markdown.</li>
<li>Deterministic visual directions, not random generation.</li>
<li>Sandboxed iframe preview, real <code>cwd</code>, exportable artifacts.</li>
</ul>
<div class='tag-row'>
<span class='tag'>Apache-2.0</span>
<span class='tag'>Local-first</span>
<span class='tag'>BYOK</span>
</div>
</div>
</div>
<div class='slide-foot'>
<span>Berlin · MMXXVI</span>
<span class='counter'>03 / 09</span>
</div>
</section>
<!-- ===== 04 · CONTENT ===== -->
<section class='slide s-content' data-slide-kind='content'>
<div class='slide-chrome'>
<span class='left'><span class='mark'>kami</span> <b>Open Design</b> · Vol. 01 / Issue Nº 26</span>
<span class='right'>Open Design · kami deck</span>
</div>
<div class='slide-inner'>
<div class='head'>
<p class='num'>01.2</p>
<h2>How it feels.</h2>
<p class='lede'>Editorial discipline, not chat-window improvisation.</p>
</div>
<div class='body'>
<p>
A new project starts with a 30-second question form: brand, audience, scale, language. The agent picks one of five visual directions, locks the type stack, and writes the artifact to disk. <strong>You can read every file it touched.</strong>
</p>
<p>
Every iteration is reviewed in a sandboxed iframe with comment-mode anchors on every editable element. Re-runs are deterministic — same brief, same output.
</p>
<ul class='dash'>
<li>Brief → 30s question form locks brand + audience + scale.</li>
<li>Direction → 5 visual directions in OKLch + locked type stack.</li>
<li>Artifact → real file on disk, sandboxed preview, comment anchors.</li>
</ul>
</div>
</div>
<div class='slide-foot'>
<span>Berlin · MMXXVI</span>
<span class='counter'>04 / 09</span>
</div>
</section>
<!-- ===== 05 · STATS ===== -->
<section class='slide s-stats' data-slide-kind='stats'>
<div class='slide-chrome'>
<span class='left'><span class='mark'>kami</span> <b>Open Design</b> · Vol. 01 / Issue Nº 26</span>
<span class='right'>Open Design · kami deck</span>
</div>
<div class='slide-inner'>
<div class='head'>
<p class='num'>01.3</p>
<h2>By the numbers.</h2>
</div>
<div class='grid'>
<div class='stat'>
<div class='v'>31</div>
<div class='l'>Skills</div>
<div class='s'>file-based, shippable today, drop-in compatible.</div>
</div>
<div class='stat'>
<div class='v'>72</div>
<div class='l'>Design systems</div>
<div class='s'>portable DESIGN.md tokens — Linear, Vercel, Stripe, kami…</div>
</div>
<div class='stat'>
<div class='v'>12</div>
<div class='l'>Agent CLIs</div>
<div class='s'>auto-detected on your $PATH; switch backends instantly.</div>
</div>
<div class='stat'>
<div class='v'>3</div>
<div class='l'>Commands</div>
<div class='s'>from <code>git clone</code> to first artifact, locally.</div>
</div>
</div>
<p class='caption'>Open Design v0.2.0 · Apache-2.0 · MMXXVI · figures as of Issue Nº 26.</p>
</div>
<div class='slide-foot'>
<span>Berlin · MMXXVI</span>
<span class='counter'>05 / 09</span>
</div>
</section>
<!-- ===== 06 · CHAPTER (dark) ===== -->
<section class='slide s-chapter dark' data-slide-kind='chapter'>
<div class='slide-chrome'>
<span class='left'><span class='mark'>kami</span> <b>Open Design</b> · Vol. 01 / Issue Nº 26</span>
<span class='right'>Open Design · kami deck</span>
</div>
<div class='slide-inner'>
<p class='num'>02</p>
<h2>What ships next.</h2>
<p class='lede'>Q2 2026 — packaging, multi-tenant tokens, daemon hardening. The roadmap is public.</p>
</div>
<div class='slide-foot'>
<span>Berlin · MMXXVI</span>
<span class='counter'>06 / 09</span>
</div>
</section>
<!-- ===== 07 · QUOTE ===== -->
<section class='slide s-quote' data-slide-kind='quote'>
<div class='slide-chrome'>
<span class='left'><span class='mark'>kami</span> <b>Open Design</b> · Vol. 01 / Issue Nº 26</span>
<span class='right'>Open Design · kami deck</span>
</div>
<div class='slide-inner'>
<blockquote>
Open Design helped us turn vague AI ideas into a visual system that felt sharp, believable, and genuinely new — without ever opening a chat window.
</blockquote>
<div class='author'>
<span class='glyph'>m</span>
<p>
Mina Kovac
<span>Creative Director · North Form, Berlin</span>
</p>
</div>
</div>
<div class='slide-foot'>
<span>Berlin · MMXXVI</span>
<span class='counter'>07 / 09</span>
</div>
</section>
<!-- ===== 08 · CTA ===== -->
<section class='slide s-cta' data-slide-kind='cta'>
<div class='slide-chrome'>
<span class='left'><span class='mark'>kami</span> <b>Open Design</b> · Vol. 01 / Issue Nº 26</span>
<span class='right'>Open Design · kami deck</span>
</div>
<div class='slide-inner'>
<span class='eyebrow'>Start a conversation · Nº 03</span>
<h2>Let's build something open and visually unforgettable.</h2>
<p class='body'>
Star the repo on GitHub, drop into the issues, or run <code>pnpm tools-dev</code> tonight. Three commands and the loop is yours.
</p>
<div class='actions'>
<a class='btn btn-primary' href='https://github.com/nexu-io/open-design'>Star on GitHub</a>
<a class='btn btn-ghost' href='https://github.com/nexu-io/open-design/issues'>Open an issue</a>
</div>
</div>
<div class='slide-foot'>
<span>Berlin · MMXXVI</span>
<span class='counter'>08 / 09</span>
</div>
</section>
<!-- ===== 09 · END (dark) ===== -->
<section class='slide s-end dark' data-slide-kind='end'>
<div class='slide-chrome'>
<span class='left'><span class='mark'>kami</span> <b>Open Design</b> · Vol. 01 / Issue Nº 26</span>
<span class='right'>Open Design · kami deck</span>
</div>
<div class='slide-inner'>
<div class='word'>Open <span class='hl'>Design.</span></div>
<p class='colophon'>Apache-2.0 · MMXXVI · Berlin · 52.5200° N · 13.4050° E · Composed in kami</p>
</div>
<div class='slide-foot'>
<span>Berlin · MMXXVI</span>
<span class='counter'>09 / 09</span>
</div>
</section>
</div>
<div id='nav'></div>
<div id='hint'>← / → · esc · swipe</div>
<div class='deck-progress'><div class='bar'></div></div>
<script>
(function () {
var deck = document.getElementById('deck');
if (!deck) return;
var slides = Array.prototype.slice.call(deck.querySelectorAll('.slide'));
var nav = document.getElementById('nav');
var bar = document.querySelector('.deck-progress .bar');
var total = slides.length;
var idx = 0, lock = false;
deck.style.width = (total * 100) + 'vw';
slides.forEach(function (s, i) {
var b = document.createElement('button');
b.className = 'dot';
b.dataset.i = i;
b.setAttribute('aria-label', 'Slide ' + (i + 1));
b.onclick = function () { go(i); };
nav.appendChild(b);
});
/* Unthrottled state update. The interaction throttle (`lock`) only
guards wheel/key/touch so a fast input burst doesn't overshoot the
transition; host- and observer-driven sync must bypass it, otherwise
a host message or restoreInitialSlide that lands inside the 700ms
window after go(0) silently no-ops and the deck stays on slide 1
while the host counter advances. */
function applySlide(n) {
idx = Math.max(0, Math.min(total - 1, n));
deck.style.transform = 'translateX(' + (-idx * 100) + 'vw)';
/* load-bearing: .slide.active is read by Open Design's host bridge
(src/runtime/srcdoc.ts findActiveByClass) to drive the slide
counter. No CSS targets it — do not remove. */
slides.forEach(function (s, i) { s.classList.toggle('active', i === idx); });
nav.querySelectorAll('.dot').forEach(function (d, i) {
d.classList.toggle('active', i === idx);
});
if (bar) bar.style.width = (((idx + 1) / total) * 100) + '%';
}
function go(n) {
if (lock) return;
applySlide(n);
lock = true;
setTimeout(function () { lock = false; }, 700);
}
/* ESC overview */
var overviewOn = false;
var ov = document.createElement('div');
ov.id = 'overview';
document.body.appendChild(ov);
function buildOverview() {
ov.innerHTML = '';
var head = document.createElement('div');
head.className = 'ov-head';
head.innerHTML = '<span><b>Slide overview</b> · esc to close</span><span>' +
String(idx + 1).padStart(2, '0') + ' / ' + String(total).padStart(2, '0') + '</span>';
ov.appendChild(head);
var grid = document.createElement('div');
grid.className = 'ov-grid';
slides.forEach(function (s, i) {
var card = document.createElement('div');
card.className = 'ov-card' + (i === idx ? ' active' : '');
var thumb = document.createElement('div');
thumb.className = 'ov-thumb';
var clone = s.cloneNode(true);
clone.className = clone.className + ' clone';
clone.style.transform = 'scale(0.18)';
thumb.appendChild(clone);
var label = document.createElement('div');
label.className = 'ov-label';
label.innerHTML = '<b>' + String(i + 1).padStart(2, '0') + '</b><span>' +
(s.dataset.slideKind || '') + '</span>';
card.appendChild(thumb);
card.appendChild(label);
card.onclick = function () { toggleOverview(); go(i); };
grid.appendChild(card);
});
ov.appendChild(grid);
}
function toggleOverview() {
overviewOn = !overviewOn;
if (overviewOn) { buildOverview(); ov.style.display = 'block'; }
else { ov.style.display = 'none'; }
}
addEventListener('keydown', function (e) {
if (e.key === 'Escape') { e.preventDefault(); toggleOverview(); return; }
if (overviewOn) return;
if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ' || e.key === 'ArrowDown') {
e.preventDefault(); go(idx + 1);
} else if (e.key === 'ArrowLeft' || e.key === 'PageUp' || e.key === 'ArrowUp') {
e.preventDefault(); go(idx - 1);
} else if (e.key === 'Home') {
e.preventDefault(); go(0);
} else if (e.key === 'End') {
e.preventDefault(); go(total - 1);
}
});
var wheelTO = null, wheelAcc = 0;
addEventListener('wheel', function (e) {
if (overviewOn) return;
wheelAcc += e.deltaY + e.deltaX;
if (Math.abs(wheelAcc) > 60) {
go(idx + (wheelAcc > 0 ? 1 : -1));
wheelAcc = 0;
}
clearTimeout(wheelTO);
wheelTO = setTimeout(function () { wheelAcc = 0; }, 150);
}, { passive: true });
var tx = 0, ty = 0;
addEventListener('touchstart', function (e) {
tx = e.touches[0].clientX; ty = e.touches[0].clientY;
}, { passive: true });
addEventListener('touchend', function (e) {
if (overviewOn) return;
var dx = e.changedTouches[0].clientX - tx;
var dy = e.changedTouches[0].clientY - ty;
if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy)) {
go(idx + (dx < 0 ? 1 : -1));
}
}, { passive: true });
/* Host-driven navigation: Open Design's host bridge classifies this deck
as class-driven (because go() toggles .slide.active) but the visible
slide is moved by deck.style.transform, which the bridge can't drive.
Two cooperating handlers keep the deck in sync with the host:
1. An od:slide message listener routes host nav through go() and
calls stopImmediatePropagation() so the bridge's own listener
(registered after this one) doesn't run a second time and
overshoot by re-reading the freshly-toggled .active class.
2. A MutationObserver on each slide watches .active and pulls the
deck transform onto the active index for class changes that
don't come through a message — chiefly the bridge's
restoreInitialSlide path, which calls setActive() directly. */
addEventListener('message', function (e) {
var data = e && e.data;
if (!data || data.type !== 'od:slide') return;
if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation();
if (data.action === 'go' && typeof data.index === 'number') applySlide(data.index);
else if (data.action === 'next') applySlide(idx + 1);
else if (data.action === 'prev') applySlide(idx - 1);
else if (data.action === 'first') applySlide(0);
else if (data.action === 'last') applySlide(total - 1);
});
if (typeof MutationObserver !== 'undefined') {
var syncFromActiveClass = function () {
for (var i = 0; i < slides.length; i++) {
if (slides[i].classList && slides[i].classList.contains('active') && i !== idx) {
applySlide(i);
return;
}
}
};
var mo = new MutationObserver(syncFromActiveClass);
slides.forEach(function (s) { mo.observe(s, { attributes: true, attributeFilter: ['class'] }); });
}
applySlide(0);
})();
</script>
</body>
</html>
kami-deck
Sister skill to `kami-landing`. Produces a single self-contained HTML file: a horizontal magazine-style swipe deck in the kami (紙 / 纸) design system — print rhythm, ink-blue accent, serif at one weight, no italic, no cool grays.
Read first — agent contract, schema, and self-check live in
`SKILL.md`. This README is the human quick-start.
What you get
- N viewport-sized slides laid out horizontally on a transformed
flex track.
- Cover / chapter / end slides flip background to ink-blue
(#1B365D) with ivory text. All other slides stay on parchment (#f5f4ed) with serif at weight 500.
- Per-slide chrome strip: brand mark · deck title · live
slide counter (01 / 09).
- Tabular-nums on every counter, metric, and date.
- Ink-blue progress bar at the bottom that fills as you advance.
- Dot indicator near the bottom; click to jump.
- ESC overview grid with scaled thumbnails.
- Keyboard / wheel / touch nav — same model as
guizang-ppt. - Multilingual stack — EN / zh-CN / ja, set on
:rootvia the
language parameter.
30-second tour
The skill is "agent-driven, no script": there's no compose.ts. The agent reads SKILL.md, gathers the brief, then writes out/index.html directly using the tokens from `design-systems/kami/DESIGN.md` and the layout primitives in `example.html`.
To preview the canonical Open Design instance:
open example.htmlTo start a fresh project:
1. Open the skill in your agent (Claude · Cursor · Codex · …). 2. Answer two rounds of brief questions (identity + content). 3. Write the file. Done.
Files
skills/kami-deck/
├── SKILL.md # ← agent contract (read this first)
├── README.md # ← you are here
└── example.html # canonical Open Design rendering (9 slides)Boundaries
- No second accent color. No italic. No cool blue-grays. No hard
drop shadows.
- One self-contained HTML file. No router, no external JS bundle.
- Cover / chapter / end slides only — no other slide kind goes dark.
- Tag fills must be solid hex (kami's print invariant), not
rgba().
See also
- `kami-landing` — long-form one-pager sister.
- `design-systems/kami/DESIGN.md` — token spec.
- `open-design-landing-deck` — same
swipe nav model, different visual language (Atelier Zero).
- Upstream: `tw93/kami` — original
Claude skill (MIT) the design system adapts.