// ============================================================
// SLIDE CANVAS — Presentation Kit
// Vanilla JS Slide Engine + Demo Slides
// ============================================================
// ----------------------------------------------------------
// PRIMITIVE HELPERS — Generate HTML strings
// ----------------------------------------------------------
const TYPE = {
eyebrow: 24, micro: 24, body: 32, bodyLg: 38, small: 26,
kicker: 44, title: 88, titleLg: 120, display: 200, mega: 280,
};
const C = {
orange: '#FF4D00', black: '#000000', white: '#FFFFFF',
white20: 'rgba(255,255,255,0.20)', white10: 'rgba(255,255,255,0.10)',
black20: 'rgba(0,0,0,0.20)', black10: 'rgba(0,0,0,0.10)',
};
function metaBar(left, right, extraStyle = '') {
return `
${left}
${right}
`;
}
function bottomMeta(left, right, fg = C.white) {
return `
${left} ${right}
`;
}
function display(text, size = 'title', color = 'inherit', extraStyle = '') {
return `${text}
`;
}
function bodyText(text, size = 'size-body', extraStyle = '') {
return `${text}
`;
}
function mono(text, size = 'size-eyebrow', extraStyle = '') {
return `${text} `;
}
function idx(n, color = C.orange, size = TYPE.eyebrow) {
return `${String(n).padStart(2,'0')} `;
}
function marquee(text, dir = 'left', bg = C.black, fg = C.white, skew = true, fontSize = '10vw') {
const items = Array(6).fill(text);
const itemsHtml = items.map(t =>
`${t}◆ `
).join('');
return ``;
}
function badge(text, size = 180, fg = C.white, spinning = true) {
const r = size / 2 - 18;
const chars = text;
const repeated = `${chars} · ${chars} · ${chars} · `;
return ``;
}
function arrowSvg(size = 40, color = 'currentColor', rotate = 0) {
return `
`;
}
// ----------------------------------------------------------
// SLIDE DATA
// ----------------------------------------------------------
const slides = [
// ---- SLIDE 0: Title / Cover ----
{
bg: C.orange,
fg: C.black,
html: `
${metaBar('SLIDE CANVAS · 01', 'PRESENTATION KIT')}
${display('SLIDE', 'titleLg', C.black)}
${display('CANVAS', 'mega', C.black)}
${bodyText('A design system for building bold, high-impact presentation slides. Primitives, tokens, and animations — ready to compose.', 'size-bodyLg')}
${badge('SLIDE CANVAS · PRESENTATION KIT · ', 220, C.black, true)}
${bottomMeta('2024 · VANILLA JS', 'DESIGN TOKENS + PRIMITIVES', C.black)}
`
},
// ---- SLIDE 1: Marquee Divider ----
{
bg: C.black,
fg: C.white,
noPad: true,
html: `
${display('DESIGN', 'display', C.white, 'margin-bottom:16px;')}
${display('TOKENS', 'mega', C.orange)}
${marquee('DESIGN TOKENS · TYPE · SPACE · COLOR · FONT', 'left', C.orange, C.black, true, '6vw')}
`
},
// ---- SLIDE 2: Design Tokens Overview ----
{
bg: C.black,
fg: C.white,
html: `
${metaBar('TOKENS · 02', 'TYPOGRAPHY + SPACE + COLOR')}
${idx(1)} ${mono('TYPE SCALE')}
Typography
10-step type scale from eyebrow (24px) to mega (280px). Display faces for impact, body faces for readability.
24
32
44
88
120
${idx(2)} ${mono('SPACING')}
Space
Consistent spacing tokens for padding, gaps, and layout rhythm. Slide padding, item gaps, and title spacing all tokenized.
${idx(3)} ${mono('COLOR')}
Color
Brand orange as the singular accent. Black, white, and transparency steps for layering and depth.
`
},
// ---- SLIDE 3: Stats ----
{
bg: C.black,
fg: C.white,
html: `
${metaBar('IMPACT · 03', 'BY THE NUMBERS')}
Design Coverage 92%
Token Adoption 87%
${bottomMeta('03 · IMPACT', 'DATA-DRIVEN DESIGN')}
`
},
// ---- SLIDE 4: Timeline ----
{
bg: C.black,
fg: C.white,
html: `
${metaBar('ROADMAP · 04', 'EVOLUTION')}
${display('THE', 'kicker')}
${display('ROAD-', 'titleLg')}
${display('MAP', 'titleLg', C.orange)}
${bodyText('A phased approach to building a complete design system for high-impact presentations.', 'size-body')}
PHASE 1
Tokens & Primitives
Define color, type, spacing, and build base UI components.
PHASE 2
Slide Templates
Compose primitives into reusable slide layouts: title, split, data, quote.
PHASE 3
Motion & Transitions
Add entrance animations, slide transitions, and micro-interactions.
PHASE 4
Export & Publish
Generate PDFs, share links, and present fullscreen from the browser.
`
},
// ---- SLIDE 5: Primitives Showcase ----
{
bg: C.orange,
fg: C.black,
html: `
${metaBar('PRIMITIVES · 05', 'BUILDING BLOCKS', 'border-color:currentColor')}
METABAR
SECTION · 05 STATUS
SECTION · 06 META
DISPLAY
KICKER 44
TITLE 88
BIG 120
BADGE
${badge('PRIMITIVES · ', 140, C.black, true)}
${badge('DEMO · ', 110, C.black, true)}
BODY TEXT
Body text at 32px for comfortable reading on large canvases.
Small body at 26px for secondary content and captions.
INDEX + ARROW
01 ${arrowSvg(40, C.black, 0)}
02 ${arrowSvg(40, C.black, 0)}
MONO LABEL
UPPERCASE MONO 24
MICRO LABEL 24
${bottomMeta('05 · PRIMITIVES', '9 COMPONENTS', C.black)}
`
},
// ---- SLIDE 6: Quote ----
{
bg: C.black,
fg: C.white,
html: `
"
DESIGN IS NOT JUST WHAT IT LOOKS LIKE.
— STEVE JOBS · QUOTE SLIDE PRIMITIVE
${bottomMeta('06 · PHILOSOPHY', 'QUOTE PRIMITIVE')}
`
},
// ---- SLIDE 7: Image + Text Split ----
{
bg: C.black,
fg: C.white,
html: `
${metaBar('SHOWCASE · 07', 'IMAGE + TEXT')}
${display('VISUAL', 'titleLg')}
${display('IMPACT', 'titleLg', C.orange)}
${bodyText('Full-bleed images paired with bold typography create slides that command attention and communicate with clarity.', 'size-body')}
${mono('EXPLORE MORE')} ${arrowSvg(24, C.orange, 0)}
${bottomMeta('07 · SHOWCASE', 'SPLIT LAYOUT')}
`
},
// ---- SLIDE 8: Marquee + End ----
{
bg: C.orange,
fg: C.black,
noPad: true,
html: `
${marquee('SLIDE CANVAS · PRESENTATION KIT · DESIGN SYSTEM · ', 'right', C.black, C.orange, true, '8vw')}
${badge('END · SLIDE CANVAS · PRESENTATION KIT · FIN · ', 260, C.black, true)}
${display('THANK YOU', 'titleLg', C.black)}
BUILT WITH DESIGN TOKENS + PRIMITIVES
${marquee('THANK YOU · GRAZIE · MERCI · DANKE · ARIGATO · ', 'left', C.black, C.orange, true, '6vw')}
`
},
];
// ----------------------------------------------------------
// SLIDE ENGINE
// ----------------------------------------------------------
let currentSlide = 0;
let isTransitioning = false;
let overviewOpen = false;
function renderSlides() {
const container = document.getElementById('slide-container');
container.innerHTML = slides.map((s, i) => {
const padClass = s.noPad ? 'no-pad' : '';
return `
${s.html}
`;
}).join('');
}
function goToSlide(n, animate = true) {
if (isTransitioning || n < 0 || n >= slides.length) return;
isTransitioning = true;
const allSlides = document.querySelectorAll('.slide');
const prev = allSlides[currentSlide];
const next = allSlides[n];
if (prev) prev.classList.remove('active');
if (next) next.classList.add('active');
currentSlide = n;
updateIndicator();
setTimeout(() => { isTransitioning = false; }, animate ? 500 : 0);
}
function nextSlide() { goToSlide(currentSlide + 1); }
function prevSlide() { goToSlide(currentSlide - 1); }
function updateIndicator() {
const el = document.getElementById('slide-indicator');
el.textContent = `${String(currentSlide + 1).padStart(2, '0')} / ${String(slides.length).padStart(2, '0')}`;
}
// ----------------------------------------------------------
// OVERVIEW
// ----------------------------------------------------------
function toggleOverview() {
overviewOpen = !overviewOpen;
const modal = document.getElementById('overview-modal');
if (overviewOpen) {
buildOverview();
modal.classList.remove('hidden');
modal.classList.add('flex');
} else {
modal.classList.add('hidden');
modal.classList.remove('flex');
}
}
function buildOverview() {
const grid = document.getElementById('overview-grid');
grid.innerHTML = slides.map((s, i) => {
const padClass = s.noPad ? 'no-pad' : '';
return `
${String(i+1).padStart(2,'0')}
`;
}).join('');
}
function overviewGo(n) {
toggleOverview();
setTimeout(() => goToSlide(n), 200);
}
// ----------------------------------------------------------
// FULLSCREEN
// ----------------------------------------------------------
function toggleFullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(() => {});
} else {
document.exitFullscreen().catch(() => {});
}
}
// ----------------------------------------------------------
// KEYBOARD
// ----------------------------------------------------------
document.addEventListener('keydown', (e) => {
if (overviewOpen) {
if (e.key === 'Escape') toggleOverview();
return;
}
switch (e.key) {
case 'ArrowRight':
case 'ArrowDown':
case ' ':
case 'PageDown':
e.preventDefault();
nextSlide();
break;
case 'ArrowLeft':
case 'ArrowUp':
case 'PageUp':
e.preventDefault();
prevSlide();
break;
case 'Home':
e.preventDefault();
goToSlide(0);
break;
case 'End':
e.preventDefault();
goToSlide(slides.length - 1);
break;
case 'f':
case 'F':
toggleFullscreen();
break;
case 'Escape':
if (document.fullscreenElement) toggleFullscreen();
break;
case 'o':
case 'O':
toggleOverview();
break;
}
});
// Touch support
let touchStartX = 0;
let touchStartY = 0;
document.addEventListener('touchstart', (e) => {
touchStartX = e.changedTouches[0].screenX;
touchStartY = e.changedTouches[0].screenY;
}, { passive: true });
document.addEventListener('touchend', (e) => {
const dx = e.changedTouches[0].screenX - touchStartX;
const dy = e.changedTouches[0].screenY - touchStartY;
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 50) {
if (dx < 0) nextSlide();
else prevSlide();
}
}, { passive: true });
// Hide key hint after first navigation
let hintHidden = false;
function hideHint() {
if (hintHidden) return;
hintHidden = true;
const hint = document.getElementById('key-hint');
if (hint) hint.style.opacity = '0';
}
const origNext = nextSlide;
const origPrev = prevSlide;
// Override to also hide hint
window.nextSlide = function() { hideHint(); origNext(); };
window.prevSlide = function() { hideHint(); origPrev(); };
// ----------------------------------------------------------
// INIT
// ----------------------------------------------------------
function init() {
renderSlides();
updateIndicator();
// Auto-hide hint after 6s
setTimeout(() => { hideHint(); }, 6000);
}
document.addEventListener('DOMContentLoaded', init);