card-maker/index.html
chris fb03a99f2c Initial commit — Card Studio printable card generator
Features: quarter-fold and flat 4-up layouts, 8 themes, 12 presets,
font picker, text alignment, text size sliders, background/accent/text
colour pickers, message page (left/right) toggle, A4/Letter paper size,
tabbed sidebar UI, responsive mobile layout, localStorage persistence,
and Beach Party Balloons branding support.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 21:33:49 -04:00

213 lines
9.4 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>Card Studio — Printable Cards</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=Caveat:wght@400;700&family=Cinzel:wght@400;700&family=Dancing+Script:wght@400;700&family=Fredoka:wght@400;600&family=Lora:ital,wght@0,400;0,700;1,400&family=Montserrat:wght@400;700;800&family=Pacifico&family=Playfair+Display:ital,wght@0,400;0,700;0,800;1,400&family=Quicksand:wght@400;600;700&family=Raleway:ital,wght@0,400;0,700;1,400&family=Press+Start+2P&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="topbar no-print">
<div class="brand">
<h1>Card Studio</h1>
<span class="tag">printable cards</span>
</div>
<button class="btn primary" id="printBtn">Print card</button>
</header>
<div class="layout">
<aside class="controls no-print">
<div class="tabs" role="tablist" id="tabBar">
<button class="tab active" data-tab="design" role="tab" aria-selected="true">Design</button>
<button class="tab" data-tab="text" role="tab" aria-selected="false">Text</button>
<button class="tab" data-tab="options" role="tab" aria-selected="false">Options</button>
</div>
<!-- ── Design ─────────────────────────────────────── -->
<div class="tab-panel active" data-tab="design">
<div class="group">
<span class="lbl">Paper size</span>
<div class="seg" id="paperSeg" role="group" aria-label="Paper size">
<button data-paper="letter" aria-pressed="true">Letter<br><span style="font-weight:400;font-size:11px;">8.5 × 11 in</span></button>
<button data-paper="a4" aria-pressed="false">A4<br><span style="font-weight:400;font-size:11px;">210 × 297 mm</span></button>
</div>
</div>
<div class="group">
<span class="lbl">Card style</span>
<div class="seg" id="styleSeg" role="group" aria-label="Card style">
<button data-style="quarter" aria-pressed="true">Quarter-fold<br>(small)</button>
<button data-style="flat" aria-pressed="false">Flat ×4</button>
</div>
<div class="note" id="styleNote" style="margin-top:10px;">
<b>Quarter-fold:</b> one portrait sheet, folded twice, makes a small card — front, back, and message all on one sheet.
</div>
</div>
<div class="group">
<span class="lbl">Theme</span>
<div class="swatches" id="swatches"></div>
</div>
<div class="group">
<span class="lbl">Typography</span>
<div class="field">
<label for="fFont">Font</label>
<select id="fFont" style="width:100%;font:inherit;font-size:14px;padding:8px;border-radius:8px;border:1px solid var(--line);background:#fcfbf8;">
<option value="">(Theme default)</option>
<optgroup label="Serif (Elegant)">
<option value="'Playfair Display', serif">Playfair Display</option>
<option value="'Lora', serif">Lora</option>
<option value="'Cinzel', serif">Cinzel (Classical)</option>
</optgroup>
<optgroup label="Sans-Serif (Modern)">
<option value="'Montserrat', sans-serif">Montserrat</option>
<option value="'Raleway', sans-serif">Raleway</option>
<option value="'Quicksand', sans-serif">Quicksand (Soft)</option>
</optgroup>
<optgroup label="Script &amp; Handwritten">
<option value="'Dancing Script', cursive">Dancing Script</option>
<option value="'Pacifico', cursive">Pacifico</option>
<option value="'Caveat', cursive">Caveat</option>
</optgroup>
<optgroup label="Playful">
<option value="'Fredoka', sans-serif">Fredoka</option>
</optgroup>
<optgroup label="Pixel &amp; Retro">
<option value="'Press Start 2P', monospace">Press Start 2P</option>
</optgroup>
</select>
</div>
<div class="field">
<label>Alignment</label>
<div class="seg" id="alignSeg" role="group" aria-label="Text alignment">
<button data-align="left">Left</button>
<button data-align="center" aria-pressed="true">Center</button>
<button data-align="right">Right</button>
</div>
</div>
<div class="field">
<label for="fTitleScale">Title size <span class="slider-val" id="fTitleScaleVal">100%</span></label>
<input type="range" id="fTitleScale" min="50" max="200" step="5" value="100">
</div>
<div class="field" style="margin-bottom:0">
<label for="fBodyScale">Body size <span class="slider-val" id="fBodyScaleVal">100%</span></label>
<input type="range" id="fBodyScale" min="50" max="200" step="5" value="100">
</div>
</div>
<div class="group" style="margin-bottom:0">
<span class="lbl">Colors</span>
<div class="colorrow">
<label class="ci">Accent <input type="color" id="cAccent"></label>
<label class="ci">Text <input type="color" id="cText"></label>
<label class="ci">Background <input type="color" id="cBg"></label>
<button class="reset-link" id="resetColors" type="button">Reset to theme</button>
</div>
</div>
</div><!-- /design -->
<!-- ── Text ───────────────────────────────────────── -->
<div class="tab-panel" data-tab="text">
<div class="group">
<span class="lbl">Quick start</span>
<div class="presets" id="presets"></div>
</div>
<div class="group" style="margin-bottom:0">
<span class="lbl">Text</span>
<div class="field">
<label for="fTitle">Front title</label>
<input id="fTitle" type="text" maxlength="60" value="Happy Birthday">
</div>
<div class="field">
<label for="fSub">Front subtitle</label>
<input id="fSub" type="text" maxlength="80" value="Wishing you a wonderful day">
</div>
<div class="field">
<label for="fMsg" id="msgLabel">Message</label>
<textarea id="fMsg" maxlength="400">Hope your day is filled with joy, laughter, and a little extra cake.
With love,</textarea>
<div class="hint" id="msgHint">Printed inside the quarter-fold card, or on each flat card.</div>
</div>
<div class="field" id="msgSideWrap">
<label>Message page</label>
<div class="seg" id="msgSideSeg" role="group" aria-label="Message page">
<button data-msgside="left">Left page</button>
<button data-msgside="right" aria-pressed="true">Right page</button>
</div>
</div>
<div class="field" style="margin-bottom:0">
<label for="fBack">Back footer <span style="color:var(--muted);font-weight:400;">(optional)</span></label>
<input id="fBack" type="text" maxlength="50" value="">
</div>
</div>
</div><!-- /text -->
<!-- ── Options ────────────────────────────────────── -->
<div class="tab-panel" data-tab="options">
<div class="group">
<span class="lbl">Branding</span>
<label class="opt">
<input type="checkbox" id="cLogo" checked>
<span>Include the Beach Party Balloons logo
<span class="sub">Loads from beachpartyballoons.com — needs internet.</span>
</span>
</label>
<div id="logoPosWrap">
<span class="lbl" style="margin-bottom:6px;">Logo position</span>
<div class="seg" id="logoSeg" role="group" aria-label="Logo position">
<button data-pos="back" aria-pressed="true">Back</button>
<button data-pos="front" aria-pressed="false">Front</button>
</div>
</div>
</div>
<div class="group">
<span class="lbl">Print guides</span>
<label class="opt">
<input type="checkbox" id="cGuide" checked>
<span id="guideOptLabel">Show fold lines when printing
<span class="sub" id="guideOptSub">A faint dashed crease to fold against.</span>
</span>
</label>
</div>
<div class="note" style="margin-bottom:0">
<b>Printing tips.</b> In the print dialog set margins to <b>None</b> and enable <b>Background graphics</b> so colors show. Match the paper size in your print dialog to the Design tab selection.
</div>
<!-- kept in DOM for JS compatibility, never visible -->
<div id="optGroup" style="display:none"><input type="checkbox" id="cInside"></div>
<div id="printGroup" style="display:none">
<input type="checkbox" id="cDouble">
<div id="sideSelector"><div class="seg" id="sideSeg"><button data-side="outside" aria-pressed="true">Outside</button><button data-side="inside" aria-pressed="false">Inside</button></div></div>
</div>
</div><!-- /options -->
</aside>
<div class="previewOuter">
<div class="previewWrap" id="previewWrap">
<div id="sheets"></div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>