Setting the file. One moment. Design Picker · Hyperframes Creative · heygen-com/hyperframes · Skills Docs22.10
Narration
(opens in a new tab)
templates/design-picker.html
HTML·1,432 lines·43 KB
<
script
src
=
"https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"
></
script
>
8 <style>
9 * {
10 margin: 0;
11 padding: 0;
12 box-sizing: border-box;
13 }
14 body {
15 font-family:
16 system-ui,
17 -apple-system,
18 sans-serif;
19 background: #0e0e0e;
20 color: #eee;
21 height: 100vh;
22 overflow: hidden;
23 display: flex;
24 flex-direction: column;
25 font-size: 13px;
26 line-height: 1.4;
27 -webkit-font-smoothing: antialiased;
28 }
29
30 /* ── Phase bar ── */
31 .phase-bar {
32 display: flex;
33 border-bottom: 1px solid #1a1a1a;
34 padding: 0 20px;
35 flex-shrink: 0;
36 background: #111;
37 }
38 .phase-tab {
39 padding: 12px 16px;
40 font-size: 12px;
41 font-weight: 600;
42 color: #555;
43 cursor: pointer;
44 border-bottom: 2px solid transparent;
45 transition: all 0.15s;
46 letter-spacing: 0.02em;
47 }
48 .phase-tab.active {
49 color: #fff;
50 border-bottom-color: #fff;
51 }
52 .phase-tab.disabled {
53 opacity: 0.25;
54 pointer-events: none;
55 }
56
57 /* ── Phases ── */
58 .phase {
59 display: none;
60 flex: 1;
61 overflow: hidden;
62 }
63 .phase.active {
64 display: flex;
65 }
66
67 /* ── Phase 1: Mood boards ── */
68 .mood-grid {
69 display: grid;
70 grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
71 gap: 16px;
72 padding: 20px;
73 overflow-y: auto;
74 flex: 1;
75 align-content: start;
76 }
77 .mood-card {
78 border: 1px solid #1e1e1e;
79 border-radius: 12px;
80 overflow: hidden;
81 cursor: pointer;
82 transition:
83 border-color 0.15s,
84 transform 0.15s,
85 box-shadow 0.15s;
86 background: #141414;
87 display: flex;
88 flex-direction: column;
89 }
90 .mood-card:hover {
91 border-color: #333;
92 transform: translateY(-1px);
93 box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
94 }
95 .mood-card.selected {
96 border-color: #fff;
97 }
98 .mood-preview {
99 height: 500px;
100 overflow: hidden;
101 position: relative;
102 background: #0a0a0a;
103 }
104 .mood-preview > div {
105 transform-origin: top left;
106 }
107 .mood-info {
108 padding: 14px 16px;
109 }
110 .mood-name {
111 font-size: 14px;
112 font-weight: 600;
113 margin-bottom: 3px;
114 }
115 .mood-desc {
116 font-size: 11px;
117 color: #666;
118 line-height: 1.4;
119 display: -webkit-box;
120 -webkit-line-clamp: 2;
121 -webkit-box-orient: vertical;
122 overflow: hidden;
123 }
124 .mood-tokens {
125 display: flex;
126 gap: 4px;
127 margin-top: 8px;
128 flex-wrap: wrap;
129 }
130 .mood-token {
131 font-size: 9px;
132 color: #555;
133 background: #1a1a1a;
134 padding: 2px 7px;
135 border-radius: 3px;
136 letter-spacing: 0.02em;
137 }
138
139 .mood-header {
140 padding: 20px 20px 0;
141 flex-shrink: 0;
142 }
143 .mood-header h2 {
144 font-size: 16px;
145 font-weight: 600;
146 margin-bottom: 2px;
147 }
148 .mood-header p {
149 font-size: 12px;
150 color: #555;
151 }
152 .mood-next {
153 padding: 12px 20px;
154 flex-shrink: 0;
155 display: flex;
156 justify-content: flex-end;
157 }
158 .mood-next-btn {
159 background: #fff;
160 color: #000;
161 border: none;
162 padding: 10px 24px;
163 border-radius: 8px;
164 font-size: 13px;
165 font-weight: 600;
166 cursor: pointer;
167 opacity: 0;
168 transform: translateY(8px);
169 transition: all 0.2s;
170 }
171 .mood-next-btn.visible {
172 opacity: 1;
173 transform: translateY(0);
174 }
175 .mood-next-btn:hover {
176 background: #ddd;
177 }
178
179 /* ── Phase 2: Fine-tune ── */
180 .panel-left {
181 width: 400px;
182 min-width: 400px;
183 height: 100%;
184 overflow-y: auto;
185 border-right: 1px solid #1a1a1a;
186 padding: 0 0 100px;
187 background: #111;
188 }
189 .panel-right {
190 flex: 1;
191 height: 100%;
192 overflow-y: auto;
193 overflow-x: hidden;
194 background: #0e0e0e;
195 }
196 .panel-right * {
197 max-width: 100%;
198 box-sizing: border-box;
199 }
200
201 /* ── Section cards (Neuform-inspired) ── */
202 .section-card {
203 border-bottom: 1px solid #1a1a1a;
204 }
205 .section-header {
206 display: flex;
207 align-items: center;
208 gap: 8px;
209 padding: 14px 16px 6px;
210 }
211 .section-icon {
212 width: 22px;
213 height: 22px;
214 border-radius: 5px;
215 background: rgba(255, 255, 255, 0.04);
216 display: flex;
217 align-items: center;
218 justify-content: center;
219 color: #555;
220 font-size: 11px;
221 flex-shrink: 0;
222 }
223 .section-title {
224 font-size: 11px;
225 font-weight: 600;
226 color: #fff;
227 letter-spacing: 0.01em;
228 }
229 .section-meta {
230 font-size: 10px;
231 color: #444;
232 margin-left: auto;
233 }
234 .section-body {
235 padding: 6px 16px 16px;
236 }
237
238 /* ── Option cards ── */
239 .opt {
240 border: 1px solid #1e1e1e;
241 border-radius: 10px;
242 margin-bottom: 6px;
243 cursor: pointer;
244 transition: border-color 0.12s;
245 overflow: hidden;
246 background: #141414;
247 position: relative;
248 }
249 .opt:hover {
250 border-color: #333;
251 }
252 .opt.selected {
253 border-color: #fff;
254 }
255 .opt-inner {
256 padding: 12px 14px;
257 }
258 .opt-name {
259 font-size: 12px;
260 font-weight: 600;
261 margin-bottom: 2px;
262 }
263 .opt-desc {
264 font-size: 10px;
265 color: #666;
266 line-height: 1.4;
267 }
268 .opt-tag {
269 font-size: 9px;
270 color: #444;
271 margin-top: 4px;
272 text-transform: uppercase;
273 letter-spacing: 0.04em;
274 }
275 .opt-rec {
276 position: absolute;
277 top: 8px;
278 right: 10px;
279 font-size: 8px;
280 font-weight: 700;
281 color: #4ade80;
282 background: rgba(74, 222, 128, 0.08);
283 padding: 2px 6px;
284 border-radius: 3px;
285 text-transform: uppercase;
286 letter-spacing: 0.04em;
287 }
288
289 /* ── Palette option ── */
290 .pal-strip {
291 display: flex;
292 height: 28px;
293 border-radius: 6px;
294 overflow: hidden;
295 margin-bottom: 8px;
296 }
297 .pal-strip > div {
298 flex: 1;
299 }
300 .pal-ramp {
301 display: flex;
302 gap: 2px;
303 margin-top: 4px;
304 }
305 .pal-ramp-swatch {
306 width: 100%;
307 height: 6px;
308 border-radius: 2px;
309 }
310
311 /* ── Type option (specimen) ── */
312 .type-specimen {
313 padding: 14px;
314 border-bottom: 1px solid #1a1a1a;
315 }
316 .type-headline {
317 line-height: 1.1;
318 letter-spacing: -0.02em;
319 margin-bottom: 6px;
320 }
321 .type-body {
322 line-height: 1.5;
323 color: #666;
324 font-size: 11px;
325 }
326 .type-meta {
327 padding: 10px 14px;
328 display: flex;
329 justify-content: space-between;
330 align-items: center;
331 }
332 .type-families {
333 font-size: 9px;
334 color: #444;
335 letter-spacing: 0.02em;
336 }
337 .type-letters {
338 font-size: 9px;
339 color: #333;
340 letter-spacing: 0.08em;
341 overflow: hidden;
342 white-space: nowrap;
343 text-overflow: ellipsis;
344 max-width: 200px;
345 }
346
347 /* ── Chip options ── */
348 .chips {
349 display: flex;
350 gap: 6px;
351 flex-wrap: wrap;
352 }
353 .chip {
354 border: 1px solid #1e1e1e;
355 border-radius: 8px;
356 padding: 8px 12px;
357 cursor: pointer;
358 transition: border-color 0.12s;
359 display: flex;
360 align-items: center;
361 gap: 8px;
362 background: #141414;
363 }
364 .chip:hover {
365 border-color: #333;
366 }
367 .chip.selected {
368 border-color: #fff;
369 }
370 .chip-label {
371 font-size: 11px;
372 font-weight: 500;
373 }
374 .chip-sub {
375 font-size: 9px;
376 color: #555;
377 }
378 .chip-preview {
379 flex-shrink: 0;
380 }
381
382 .theme-chip {
383 flex: 1;
384 min-width: 90px;
385 flex-direction: column;
386 align-items: center;
387 padding: 10px 12px;
388 text-align: center;
389 }
390 .theme-swatch {
391 width: 100%;
392 height: 28px;
393 border-radius: 4px;
394 margin-bottom: 4px;
395 border: 1px solid rgba(255, 255, 255, 0.06);
396 }
397 .corner-box {
398 width: 22px;
399 height: 22px;
400 border: 2px solid #666;
401 }
402 .density-bars {
403 display: flex;
404 flex-direction: column;
405 width: 24px;
406 }
407 .density-bar {
408 background: #666;
409 height: 3px;
410 border-radius: 1px;
411 }
412 .depth-square {
413 width: 22px;
414 height: 22px;
415 background: #2a2a2a;
416 border-radius: 3px;
417 }
418
419 /* ── Easing option ── */
420 .ease-opt {
421 border: 1px solid #1e1e1e;
422 border-radius: 10px;
423 margin-bottom: 6px;
424 cursor: pointer;
425 transition: border-color 0.12s;
426 overflow: hidden;
427 background: #141414;
428 padding: 10px 14px;
429 }
430 .ease-opt:hover {
431 border-color: #333;
432 }
433 .ease-opt.selected {
434 border-color: #fff;
435 }
436 .ease-track {
437 position: relative;
438 height: 8px;
439 background: #1a1a1a;
440 border-radius: 4px;
441 margin-bottom: 8px;
442 overflow: visible;
443 }
444 .ease-dot {
445 position: absolute;
446 top: 50%;
447 transform: translateY(-50%);
448 left: 0;
449 width: 12px;
450 height: 12px;
451 border-radius: 50%;
452 background: #fff;
453 }
454 .ease-label {
455 display: flex;
456 justify-content: space-between;
457 align-items: center;
458 }
459 .ease-name {
460 font-size: 11px;
461 font-weight: 600;
462 }
463 .ease-value {
464 font-size: 9px;
465 color: #444;
466 font-family: "SF Mono", Consolas, monospace;
467 letter-spacing: 0.02em;
468 }
469
470 .preview-empty {
471 display: flex;
472 align-items: center;
473 justify-content: center;
474 height: 100%;
475 color: #2a2a2a;
476 font-size: 13px;
477 }
478
479 /* ── FAB + Overlay ── */
480 .fab {
481 position: fixed;
482 bottom: 20px;
483 right: 32px;
484 transform: translateY(60px);
485 opacity: 0;
486 background: #fff;
487 color: #000;
488 border: none;
489 padding: 12px 24px;
490 border-radius: 10px;
491 font-size: 13px;
492 font-weight: 600;
493 cursor: pointer;
494 box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
495 transition:
496 transform 0.25s ease,
497 opacity 0.25s ease;
498 z-index: 100;
499 }
500 .fab.visible {
501 transform: translateY(0);
502 opacity: 1;
503 }
504 .fab:hover {
505 background: #ddd;
506 }
507
508 .md-overlay {
509 position: fixed;
510 inset: 0;
511 background: rgba(0, 0, 0, 0.85);
512 backdrop-filter: blur(8px);
513 z-index: 200;
514 display: none;
515 align-items: center;
516 justify-content: center;
517 }
518 .md-overlay.visible {
519 display: flex;
520 }
521 .md-card {
522 background: #161616;
523 border: 1px solid #2a2a2a;
524 border-radius: 14px;
525 max-width: 600px;
526 width: 90%;
527 max-height: 80vh;
528 display: flex;
529 flex-direction: column;
530 }
531 .md-header {
532 padding: 16px 20px;
533 border-bottom: 1px solid #1e1e1e;
534 display: flex;
535 justify-content: space-between;
536 align-items: center;
537 }
538 .md-title {
539 font-size: 14px;
540 font-weight: 600;
541 }
542 .md-actions {
543 display: flex;
544 gap: 6px;
545 }
546 .copy-btn {
547 background: #fff;
548 color: #000;
549 border: none;
550 padding: 7px 16px;
551 border-radius: 6px;
552 font-size: 12px;
553 font-weight: 600;
554 cursor: pointer;
555 }
556 .copy-btn:hover {
557 background: #ddd;
558 }
559 .close-btn {
560 background: transparent;
561 color: #666;
562 border: 1px solid #2a2a2a;
563 padding: 7px 14px;
564 border-radius: 6px;
565 font-size: 12px;
566 cursor: pointer;
567 }
568 .close-btn:hover {
569 color: #fff;
570 border-color: #444;
571 }
572 .md-body {
573 padding: 20px;
574 overflow-y: auto;
575 flex: 1;
576 }
577 .md-editor {
578 font-family: "SF Mono", Consolas, monospace;
579 font-size: 12px;
580 line-height: 1.6;
581 color: #aaa;
582 background: transparent;
583 border: none;
584 width: 100%;
585 min-height: 360px;
586 resize: vertical;
587 outline: none;
588 white-space: pre-wrap;
589 }
590
591 /* ── Phase 1 palette bar ── */
592 .pal-bar {
593 display: flex;
594 gap: 8px;
595 padding: 12px 20px 0;
596 flex-shrink: 0;
597 flex-wrap: wrap;
598 align-items: center;
599 }
600 .pal-bar-label {
601 font-size: 10px;
602 color: #444;
603 letter-spacing: 0.03em;
604 text-transform: uppercase;
605 margin-right: 4px;
606 }
607 .pal-bar-chip {
608 display: flex;
609 align-items: center;
610 gap: 6px;
611 padding: 5px 10px;
612 border: 1px solid #1e1e1e;
613 border-radius: 6px;
614 cursor: pointer;
615 transition: border-color 0.12s;
616 background: #141414;
617 }
618 .pal-bar-chip:hover {
619 border-color: #333;
620 }
621 .pal-bar-chip.active {
622 border-color: #fff;
623 }
624 .pal-bar-chip-swatches {
625 display: flex;
626 gap: 2px;
627 }
628 .pal-bar-chip-swatch {
629 width: 14px;
630 height: 14px;
631 border-radius: 3px;
632 }
633 .pal-bar-chip-name {
634 font-size: 10px;
635 font-weight: 500;
636 color: #888;
637 }
638 </style>
639 </head>
640 <body>
641 <div class="phase-bar">
642 <div class="phase-tab active" id="tab-mood" onclick="showPhase('mood')">1. Direction</div>
643 <div class="phase-tab disabled" id="tab-tune" onclick="showPhase('tune')">2. Fine-tune</div>
644 </div>
645
646 <!-- Phase 1: Mood boards -->
647 <div class="phase active" id="phase-mood" style="flex-direction: column">
648 <div class="mood-header">
649 <h2 id="mood-title">Pick a direction</h2>
650 <p id="mood-subtitle">
651 Each option is a complete visual identity. Pick one, then fine-tune.
652 </p>
653 </div>
654 <div class="pal-bar" id="pal-bar"><span class="pal-bar-label">Palette</span></div>
655 <div class="mood-grid" id="mood-grid"></div>
656 <div class="mood-next">
657 <button class="mood-next-btn" id="mood-next-btn" onclick="goToTune()">
658 Customize this direction →
659 </button>
660 </div>
661 </div>
662
663 <!-- Phase 2: Fine-tune -->
664 <div class="phase" id="phase-tune" style="flex-direction: row">
665 <div class="panel-left">
666 <!-- Theme -->
667 <div class="section-card">
668 <div class="section-header">
669 <div class="section-icon">◐</div>
670 <div class="section-title">Theme</div>
671 </div>
672 <div class="section-body"><div class="chips" id="theme-options"></div></div>
673 </div>
674 <!-- Structure -->
675 <div class="section-card">
676 <div class="section-header">
677 <div class="section-icon">▦</div>
678 <div class="section-title">Structure</div>
679 <div class="section-meta" id="arch-meta"></div>
680 </div>
681 <div class="section-body" id="arch-options"></div>
682 </div>
683 <!-- Palette -->
684 <div class="section-card">
685 <div class="section-header">
686 <div class="section-icon">◉</div>
687 <div class="section-title">Color</div>
688 </div>
689 <div class="section-body" id="pal-options"></div>
690 </div>
691 <!-- Typography -->
692 <div class="section-card">
693 <div class="section-header">
694 <div class="section-icon">Aa</div>
695 <div class="section-title">Typography</div>
696 </div>
697 <div class="section-body" id="type-options"></div>
698 </div>
699 <!-- Corners -->
700 <div class="section-card">
701 <div class="section-header">
702 <div class="section-icon">⬔</div>
703 <div class="section-title">Corners</div>
704 </div>
705 <div class="section-body"><div class="chips" id="corner-options"></div></div>
706 </div>
707 <!-- Density -->
708 <div class="section-card">
709 <div class="section-header">
710 <div class="section-icon">≡</div>
711 <div class="section-title">Density</div>
712 </div>
713 <div class="section-body"><div class="chips" id="density-options"></div></div>
714 </div>
715 <!-- Depth -->
716 <div class="section-card">
717 <div class="section-header">
718 <div class="section-icon">◧</div>
719 <div class="section-title">Depth</div>
720 </div>
721 <div class="section-body"><div class="chips" id="depth-options"></div></div>
722 </div>
723 <!-- Motion / Easing -->
724 <div class="section-card">
725 <div class="section-header">
726 <div class="section-icon">⟿</div>
727 <div class="section-title">Motion</div>
728 <div class="section-meta">entry easing</div>
729 </div>
730 <div class="section-body" id="ease-options"></div>
731 </div>
732 </div>
733 <div class="panel-right" id="preview">
734 <div class="preview-empty">Pick a structure to preview</div>
735 </div>
736 </div>
737
738 <button class="fab" id="fab" onclick="showOutput()">Create design.md</button>
739
740 <div class="md-overlay" id="md-overlay">
741 <div class="md-card">
742 <div class="md-header">
743 <div class="md-title">
744 design.md
745 <span style="font-size: 11px; font-weight: 400; color: #555">— edit, then copy</span>
746 </div>
747 <div class="md-actions">
748 <button class="copy-btn" id="copy-btn">Copy</button>
749 <button
750 class="close-btn"
751 onclick="document.getElementById('md-overlay').classList.remove('visible')"
752 >
753 Close
754 </button>
755 </div>
756 </div>
757 <div class="md-body">
758 <textarea class="md-editor" id="md-output" spellcheck="false"></textarea>
759 </div>
760 </div>
761 </div>
762
763 <script>
764 var ARCHITECTURES = __ARCHITECTURES_JSON__;
765 var PALETTES = __PALETTES_JSON__;
766 var TYPEPAIRS = __TYPEPAIRS_JSON__;
767 var MOODBOARDS = __MOODBOARDS_JSON__;
768 var PROMPT = __PROMPT_JSON__;
769 var picks = {
770 theme: null,
771 arch: null,
772 palette: null,
773 type: null,
774 corners: null,
775 density: null,
776 depth: null,
777 easing: null,
778 };
779
780 var EASINGS = [
781 { name: "Confident", value: "power3.out", desc: "Fast start, smooth deceleration" },
782 { name: "Snappy", value: "expo.out", desc: "Very fast start, crisp stop" },
783 { name: "Gentle", value: "sine.inOut", desc: "Soft acceleration and deceleration" },
784 { name: "Bouncy", value: "back.out(1.4)", desc: "Overshoots then settles" },
785 { name: "Elastic", value: "elastic.out(1, 0.5)", desc: "Springs past target and wobbles" },
786 { name: "Sharp", value: "power4.out", desc: "Aggressive start, hard stop" },
787 ];
788
789 var THEMES = [
790 { name: "Dark", id: "dark", bg: "#0e0e0e", fg: "#eee", desc: "Light on dark" },
791 { name: "Light", id: "light", bg: "#f5f3ef", fg: "#1a1a1a", desc: "Dark on light" },
792 { name: "Palette", id: "palette", bg: null, fg: null, desc: "Use palette colors" },
793 ];
794 var CORNERS = [
795 { name: "Sharp", value: "0px" },
796 { name: "Slight", value: "4px" },
797 { name: "Rounded", value: "12px" },
798 ];
799 var DENSITIES = [
800 { name: "Tight", padding: "8-12px", gap: "8-12px", desc: "Compact" },
801 { name: "Normal", padding: "16-24px", gap: "16-20px", desc: "Balanced" },
802 { name: "Generous", padding: "32-48px", gap: "28-40px", desc: "Spacious" },
803 ];
804 var DEPTHS = [
805 { name: "Flat", desc: "No shadows" },
806 { name: "Subtle", desc: "Faint shadows" },
807 { name: "Layered", desc: "Glows + depth" },
808 ];
809
810 // Load fonts
811 var fams = [];
812 TYPEPAIRS.forEach(function (tp) {
813 fams.push(tp.headline.family.replace(/ /g, "+") + ":wght@" + tp.headline.weight);
814 fams.push(tp.body.family.replace(/ /g, "+") + ":wght@" + tp.body.weight);
815 });
816 var lnk = document.createElement("link");
817 lnk.rel = "stylesheet";
818 lnk.href =
819 "https://fonts.googleapis.com/css2?family=" + fams.join("&family=") + "&display=swap";
820 document.head.appendChild(lnk);
821
822 function al(hex, o) {
823 if (!hex || typeof hex !== "string" || hex[0] !== "#") return "rgba(128,128,128," + o + ")";
824 if (hex.length === 4) hex = "#" + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
825 var r = parseInt(hex.slice(1, 3), 16),
826 g = parseInt(hex.slice(3, 5), 16),
827 b = parseInt(hex.slice(5, 7), 16);
828 if (isNaN(r)) r = 128;
829 if (isNaN(g)) g = 128;
830 if (isNaN(b)) b = 128;
831 return "rgba(" + r + "," + g + "," + b + "," + o + ")";
832 }
833
834 // Prompt-aware UI
835 if (PROMPT && PROMPT.title)
836 document.getElementById("mood-title").textContent = "Pick a direction for: " + PROMPT.title;
837
838 // ── Phase 1: Mood boards + Palette bar ──
839 var moodGrid = document.getElementById("mood-grid");
840 var selectedMood = null;
841 var overridePalette = null;
842
843 // Build palette bar
844 var palBar = document.getElementById("pal-bar");
845 PALETTES.forEach(function (p, i) {
846 var chip = document.createElement("div");
847 chip.className = "pal-bar-chip" + (i === 0 ? "" : "");
848 chip.innerHTML =
849 '<div class="pal-bar-chip-swatches"><div class="pal-bar-chip-swatch" style="background:' +
850 p.background +
851 ';border:1px solid rgba(255,255,255,0.08);"></div><div class="pal-bar-chip-swatch" style="background:' +
852 p.accent +
853 ';"></div><div class="pal-bar-chip-swatch" style="background:' +
854 p.foreground +
855 ';border:1px solid rgba(255,255,255,0.08);"></div></div><span class="pal-bar-chip-name">' +
856 p.name +
857 "</span>";
858 chip.onclick = function () {
859 palBar.querySelectorAll(".pal-bar-chip").forEach(function (c) {
860 c.classList.remove("active");
861 });
862 if (overridePalette === i) {
863 overridePalette = null;
864 } else {
865 chip.classList.add("active");
866 overridePalette = i;
867 }
868 renderMoodBoards();
869 };
870 palBar.appendChild(chip);
871 });
872
873 function renderMoodBoards() {
874 var prevSelected = selectedMood;
875 moodGrid.innerHTML = "";
876 MOODBOARDS.forEach(function (mb, i) {
877 var card = document.createElement("div");
878 card.className = "mood-card" + (i === prevSelected ? " selected" : "");
879 var tp = TYPEPAIRS[mb.type_index || 0],
880 arch = ARCHITECTURES[mb.arch_index || 0];
881 var pal =
882 overridePalette !== null ? PALETTES[overridePalette] : PALETTES[mb.palette_index || 0];
883 var thId = mb.theme || "dark",
884 th =
885 THEMES.find(function (t) {
886 return t.id === thId;
887 }) || THEMES[0];
888 var bg = thId === "palette" ? pal.background : th.bg,
889 fg = thId === "palette" ? pal.foreground : th.fg;
890 var tokens = {
891 "{{bg}}": bg,
892 "{{fg}}": fg,
893 "{{ac}}": pal.accent,
894 "{{mt}}": pal.muted || pal.mid || al(fg, 0.4),
895 "{{hf}}": tp.headline.family,
896 "{{hw}}": tp.headline.weight,
897 "{{bf}}": tp.body.family,
898 "{{bw}}": tp.body.weight,
899 "{{cr}}": mb.corners || "4px",
900 "{{pad}}": mb.padding || "20px",
901 "{{gap}}": mb.gap || "16px",
902 "{{shadow}}": mb.shadow || "none",
903 "{{sf}}": al(fg, 0.06),
904 "{{g}}": al(fg, 0.06),
905 "{{fg3}}": al(fg, 0.03),
906 "{{fg6}}": al(fg, 0.06),
907 "{{fg8}}": al(fg, 0.08),
908 "{{fg15}}": al(fg, 0.15),
909 "{{ac3}}": al(pal.accent, 0.03),
910 "{{ac5}}": al(pal.accent, 0.05),
911 "{{ac25}}": al(pal.accent, 0.25),
912 "{{prompt_headline}}": (PROMPT && PROMPT.headline) || "Your Headline",
913 "{{prompt_sub}}": (PROMPT && PROMPT.subline) || "A subline for your product.",
914 };
915 var preview = arch.preview_html || "";
916 Object.keys(tokens).forEach(function (k) {
917 preview = preview.split(k).join(tokens[k]);
918 });
919 var tagHtml = [tp.headline.family, pal.name, CORNERS[mb.corners_index || 1].name]
920 .map(function (t) {
921 return '<span class="mood-token">' + t + "</span>";
922 })
923 .join("");
924 card.innerHTML =
925 '<div class="mood-preview"><div style="transform:scale(0.38);transform-origin:top left;width:263.16%;min-height:263.16%;">' +
926 preview +
927 '</div></div><div class="mood-info"><div class="mood-name">' +
928 mb.name +
929 '</div><div class="mood-desc">' +
930 mb.description +
931 '</div><div class="mood-tokens">' +
932 tagHtml +
933 "</div></div>";
934 card.onclick = function () {
935 moodGrid.querySelectorAll(".mood-card").forEach(function (c) {
936 c.classList.remove("selected");
937 });
938 card.classList.add("selected");
939 selectedMood = i;
940 document.getElementById("mood-next-btn").classList.add("visible");
941 };
942 moodGrid.appendChild(card);
943 });
944 }
945 renderMoodBoards();
946
947 var currentPhase = "mood";
948 function showPhase(name) {
949 currentPhase = name;
950 document.querySelectorAll(".phase").forEach(function (p) {
951 p.classList.remove("active");
952 });
953 document.querySelectorAll(".phase-tab").forEach(function (t) {
954 t.classList.remove("active");
955 });
956 document.getElementById("phase-" + name).classList.add("active");
957 document.getElementById("tab-" + name).classList.add("active");
958 if (name === "mood") document.getElementById("fab").classList.remove("visible");
959 else checkReady();
960 }
961
962 function goToTune() {
963 if (selectedMood === null) return;
964 var mb = MOODBOARDS[selectedMood];
965 var thIdx = THEMES.findIndex(function (t) {
966 return t.id === (mb.theme || "dark");
967 });
968 if (thIdx >= 0) chipSel("theme", thIdx, document.getElementById("theme-options"));
969 if (mb.arch_index != null)
970 sel("arch", mb.arch_index, document.getElementById("arch-options"));
971 var palIdx = overridePalette !== null ? overridePalette : mb.palette_index;
972 if (palIdx != null) sel("palette", palIdx, document.getElementById("pal-options"));
973 if (mb.type_index != null)
974 sel("type", mb.type_index, document.getElementById("type-options"));
975 if (mb.corners_index != null)
976 chipSel("corners", mb.corners_index, document.getElementById("corner-options"));
977 if (mb.density_index != null)
978 chipSel("density", mb.density_index, document.getElementById("density-options"));
979 if (mb.depth_index != null)
980 chipSel("depth", mb.depth_index, document.getElementById("depth-options"));
981 var eIdx = mb.easing_index != null ? mb.easing_index : 0;
982 var eOpts = document.getElementById("ease-options");
983 eOpts.querySelectorAll(".ease-opt").forEach(function (c) {
984 c.classList.remove("selected");
985 });
986 eOpts.children[eIdx].classList.add("selected");
987 picks.easing = eIdx;
988 document.getElementById("tab-tune").classList.remove("disabled");
989 showPhase("tune");
990 }
991
992 // ── Phase 2: Build options ──
993
994 // Architecture cards
995 var archOpts = document.getElementById("arch-options");
996 ARCHITECTURES.forEach(function (a, i) {
997 var el = document.createElement("div");
998 el.className = "opt";
999 el.innerHTML =
1000 '<div class="opt-inner"><div class="opt-name">' +
1001 a.name +
1002 '</div><div class="opt-desc">' +
1003 a.description +
1004 '</div><div class="opt-tag">' +
1005 a.tag +
1006 "</div></div>";
1007 el.onclick = function () {
1008 sel("arch", i, archOpts);
1009 };
1010 archOpts.appendChild(el);
1011 });
1012
1013 // Palette cards with ramp
1014 var palOpts = document.getElementById("pal-options");
1015 PALETTES.forEach(function (p, i) {
1016 var el = document.createElement("div");
1017 el.className = "opt";
1018 el.innerHTML =
1019 '<div class="opt-inner"><div class="pal-strip"><div style="background:' +
1020 p.background +
1021 ';"></div><div style="background:' +
1022 (p.muted || p.mid || "#888") +
1023 ';"></div><div style="background:' +
1024 p.accent +
1025 ';"></div><div style="background:' +
1026 p.foreground +
1027 ';"></div></div><div class="opt-name">' +
1028 p.name +
1029 '</div><div class="opt-desc">' +
1030 p.description +
1031 "</div></div>";
1032 el.onclick = function () {
1033 sel("palette", i, palOpts);
1034 };
1035 palOpts.appendChild(el);
1036 });
1037
1038 // Typography cards with specimen
1039 var typeOpts = document.getElementById("type-options");
1040 TYPEPAIRS.forEach(function (tp, i) {
1041 var letters = "AaBbCcDdEeFfGgHh";
1042 var el = document.createElement("div");
1043 el.className = "opt";
1044 el.innerHTML =
1045 '<div class="type-specimen"><div class="type-headline" style="font-family:\'' +
1046 tp.headline.family +
1047 "',sans-serif;font-weight:" +
1048 tp.headline.weight +
1049 ';font-size:24px;">' +
1050 tp.preview +
1051 '</div><div class="type-body" style="font-family:\'' +
1052 tp.body.family +
1053 "',monospace;font-weight:" +
1054 tp.body.weight +
1055 ';">' +
1056 tp.body_preview +
1057 '</div></div><div class="type-meta"><div class="type-families">' +
1058 tp.headline.family +
1059 " " +
1060 tp.headline.weight +
1061 " · " +
1062 tp.body.family +
1063 " " +
1064 tp.body.weight +
1065 '</div><div class="type-letters" style="font-family:\'' +
1066 tp.headline.family +
1067 "',sans-serif;font-weight:" +
1068 tp.headline.weight +
1069 ';">' +
1070 letters +
1071 "</div></div>";
1072 el.onclick = function () {
1073 sel("type", i, typeOpts);
1074 };
1075 typeOpts.appendChild(el);
1076 });
1077
1078 // Theme chips
1079 var themeOpts = document.getElementById("theme-options");
1080 THEMES.forEach(function (th, i) {
1081 var el = document.createElement("div");
1082 el.className = "chip theme-chip";
1083 el.innerHTML =
1084 '<div class="theme-swatch" style="background:' +
1085 th.bg +
1086 ';"></div><div class="chip-label">' +
1087 th.name +
1088 '</div><div class="chip-sub">' +
1089 th.desc +
1090 "</div>";
1091 el.onclick = function () {
1092 chipSel("theme", i, themeOpts);
1093 };
1094 themeOpts.appendChild(el);
1095 });
1096
1097 // Corner chips
1098 var cornerOpts = document.getElementById("corner-options");
1099 CORNERS.forEach(function (c, i) {
1100 var el = document.createElement("div");
1101 el.className = "chip";
1102 el.innerHTML =
1103 '<div class="chip-preview"><div class="corner-box" style="border-radius:' +
1104 c.value +
1105 ';"></div></div><div><div class="chip-label">' +
1106 c.name +
1107 '</div><div class="chip-sub">' +
1108 c.value +
1109 "</div></div>";
1110 el.onclick = function () {
1111 chipSel("corners", i, cornerOpts);
1112 };
1113 cornerOpts.appendChild(el);
1114 });
1115
1116 // Density chips
1117 var densityOpts = document.getElementById("density-options");
1118 DENSITIES.forEach(function (d, i) {
1119 var gaps = [2, 4, 7][i];
1120 var el = document.createElement("div");
1121 el.className = "chip";
1122 el.innerHTML =
1123 '<div class="chip-preview"><div class="density-bars" style="gap:' +
1124 gaps +
1125 'px;"><div class="density-bar"></div><div class="density-bar"></div><div class="density-bar"></div></div></div><div><div class="chip-label">' +
1126 d.name +
1127 '</div><div class="chip-sub">' +
1128 d.desc +
1129 "</div></div>";
1130 el.onclick = function () {
1131 chipSel("density", i, densityOpts);
1132 };
1133 densityOpts.appendChild(el);
1134 });
1135
1136 // Depth chips
1137 var depthOpts = document.getElementById("depth-options");
1138 DEPTHS.forEach(function (d, i) {
1139 var shadow = [
1140 "none",
1141 "0 2px 8px rgba(255,255,255,0.04)",
1142 "0 0 16px rgba(255,255,255,0.06)",
1143 ][i];
1144 var el = document.createElement("div");
1145 el.className = "chip";
1146 el.innerHTML =
1147 '<div class="chip-preview"><div class="depth-square" style="box-shadow:' +
1148 shadow +
1149 ';"></div></div><div><div class="chip-label">' +
1150 d.name +
1151 '</div><div class="chip-sub">' +
1152 d.desc +
1153 "</div></div>";
1154 el.onclick = function () {
1155 chipSel("depth", i, depthOpts);
1156 };
1157 depthOpts.appendChild(el);
1158 });
1159
1160 // Easing options with GSAP animation
1161 var easeOpts = document.getElementById("ease-options");
1162 EASINGS.forEach(function (e, i) {
1163 var el = document.createElement("div");
1164 el.className = "ease-opt";
1165 var dotId = "ease-dot-" + i;
1166 el.innerHTML =
1167 '<div class="ease-track"><div class="ease-dot" id="' +
1168 dotId +
1169 '"></div></div>' +
1170 '<div class="ease-label"><div class="ease-name">' +
1171 e.name +
1172 '</div><div class="ease-value">' +
1173 e.value +
1174 "</div></div>";
1175 el.onclick = function () {
1176 easeOpts.querySelectorAll(".ease-opt").forEach(function (c) {
1177 c.classList.remove("selected");
1178 });
1179 el.classList.add("selected");
1180 picks.easing = i;
1181 checkReady();
1182 };
1183 easeOpts.appendChild(el);
1184 // Animate the dot with GSAP
1185 gsap.to("#" + dotId, {
1186 left: "calc(100% - 12px)",
1187 duration: 1.2,
1188 ease: e.value,
1189 repeat: -1,
1190 repeatDelay: 0.8,
1191 yoyo: true,
1192 });
1193 });
1194
1195 function chipSel(key, idx, container) {
1196 container.querySelectorAll(".chip").forEach(function (c) {
1197 c.classList.remove("selected");
1198 });
1199 container.children[idx].classList.add("selected");
1200 picks[key] = idx;
1201 checkReady();
1202 renderPreview();
1203 }
1204 function sel(key, idx, container) {
1205 container.querySelectorAll(".opt").forEach(function (c) {
1206 c.classList.remove("selected");
1207 });
1208 container.children[idx].classList.add("selected");
1209 picks[key] = idx;
1210 checkReady();
1211 renderPreview();
1212 }
1213
1214 function checkReady() {
1215 var ready =
1216 picks.theme !== null &&
1217 picks.arch !== null &&
1218 picks.palette !== null &&
1219 picks.type !== null &&
1220 picks.corners !== null &&
1221 picks.density !== null &&
1222 picks.depth !== null &&
1223 picks.easing !== null;
1224 document
1225 .getElementById("fab")
1226 .classList.toggle("visible", ready && currentPhase === "tune");
1227 document.getElementById("md-overlay").classList.remove("visible");
1228 }
1229
1230 function renderPreview() {
1231 var pv = document.getElementById("preview");
1232 if (picks.arch === null) {
1233 pv.innerHTML = '<div class="preview-empty">Pick a structure to preview</div>';
1234 return;
1235 }
1236 var a = ARCHITECTURES[picks.arch];
1237 var th = picks.theme !== null ? THEMES[picks.theme] : THEMES[0];
1238 var p =
1239 picks.palette !== null ? PALETTES[picks.palette] : { accent: "#888", muted: "#555" };
1240 var t =
1241 picks.type !== null
1242 ? TYPEPAIRS[picks.type]
1243 : {
1244 headline: { family: "system-ui", weight: 600 },
1245 body: { family: "system-ui", weight: 400 },
1246 };
1247 var cr = picks.corners !== null ? CORNERS[picks.corners].value : "0px";
1248 var dn = picks.density !== null ? picks.density : 1;
1249 var dp = picks.depth !== null ? picks.depth : 0;
1250 var bg, fg;
1251 if (th.id === "palette" && p.background) {
1252 bg = p.background;
1253 fg = p.foreground;
1254 } else {
1255 bg = th.bg;
1256 fg = th.fg;
1257 }
1258 var ac = p.accent,
1259 mt = p.muted || al(fg, 0.4);
1260 var pad = [12, 20, 36][dn] + "px",
1261 gap = [8, 16, 28][dn] + "px";
1262 var isDark = bg && bg[0] === "#" ? parseInt(bg.slice(1, 3), 16) < 80 : true;
1263 var shCol = isDark ? ac : fg;
1264 var shadow = [
1265 "none",
1266 "0 2px 16px " + al(shCol, isDark ? 0.15 : 0.1),
1267 "0 4px 32px " + al(shCol, isDark ? 0.3 : 0.18),
1268 ][dp];
1269 var tokens = {
1270 "{{bg}}": bg,
1271 "{{fg}}": fg,
1272 "{{ac}}": ac,
1273 "{{mt}}": mt,
1274 "{{sf}}": al(fg, 0.06),
1275 "{{hf}}": t.headline.family,
1276 "{{hw}}": t.headline.weight,
1277 "{{bf}}": t.body.family,
1278 "{{bw}}": t.body.weight,
1279 "{{cr}}": cr,
1280 "{{pad}}": pad,
1281 "{{gap}}": gap,
1282 "{{shadow}}": shadow,
1283 "{{g}}": al(fg, 0.06),
1284 "{{fg3}}": al(fg, 0.03),
1285 "{{fg6}}": al(fg, 0.06),
1286 "{{fg8}}": al(fg, 0.08),
1287 "{{fg15}}": al(fg, 0.15),
1288 "{{ac3}}": al(ac, 0.03),
1289 "{{ac5}}": al(ac, 0.05),
1290 "{{ac25}}": al(ac, 0.25),
1291 "{{prompt_headline}}": (PROMPT && PROMPT.headline) || "Your Headline",
1292 "{{prompt_sub}}": (PROMPT && PROMPT.subline) || "Subline.",
1293 };
1294 var h =
1295 a.preview_html ||
1296 '<div style="background:' +
1297 bg +
1298 ";padding:40px;min-height:100%;display:flex;align-items:center;justify-content:center;color:" +
1299 mt +
1300 ';font-size:14px;">No preview</div>';
1301 Object.keys(tokens).forEach(function (k) {
1302 h = h.split(k).join(tokens[k]);
1303 });
1304 pv.innerHTML = h;
1305 }
1306
1307 // ── Output ──
1308 function showOutput() {
1309 if (
1310 picks.theme === null ||
1311 picks.arch === null ||
1312 picks.palette === null ||
1313 picks.type === null ||
1314 picks.corners === null ||
1315 picks.density === null ||
1316 picks.depth === null
1317 )
1318 return;
1319 var a = ARCHITECTURES[picks.arch],
1320 p = PALETTES[picks.palette],
1321 t = TYPEPAIRS[picks.type];
1322 var th = THEMES[picks.theme],
1323 cr = CORNERS[picks.corners],
1324 dn = DENSITIES[picks.density],
1325 dp = DEPTHS[picks.depth];
1326 var bg = th.id === "palette" && p.background ? p.background : th.bg;
1327 var fg = th.id === "palette" && p.foreground ? p.foreground : th.fg;
1328 var ac = p.accent,
1329 mt = p.muted || p.mid || "#888";
1330 var padVal = { Tight: "8px", Normal: "16px", Generous: "32px" }[dn.name] || "16px";
1331 var gapVal = { Tight: "8px", Normal: "16px", Generous: "28px" }[dn.name] || "16px";
1332
1333 var md = "---\n";
1334 md += "name: " + ((PROMPT && PROMPT.title) || "Untitled") + "\n";
1335 md +=
1336 'colors:\n primary: "' +
1337 bg +
1338 '"\n on-primary: "' +
1339 fg +
1340 '"\n accent: "' +
1341 ac +
1342 '"\n muted: "' +
1343 mt +
1344 '"\n';
1345 md +=
1346 "typography:\n headline:\n fontFamily: " +
1347 t.headline.family +
1348 "\n fontSize: 3.5rem\n fontWeight: " +
1349 t.headline.weight +
1350 "\n letterSpacing: -0.03em\n";
1351 md +=
1352 " body:\n fontFamily: " +
1353 t.body.family +
1354 "\n fontSize: 1rem\n fontWeight: " +
1355 t.body.weight +
1356 "\n lineHeight: 1.5\n";
1357 md += "rounded:\n md: " + cr.value + "\n";
1358 md += "spacing:\n md: " + padVal + "\n lg: " + gapVal + "\n";
1359 md += "---\n\n";
1360 md += "## Overview\n\n" + a.mood + ". " + a.description + ". " + p.description + ".\n\n";
1361 md +=
1362 "## Colors\n\n- **Primary** (`" +
1363 bg +
1364 "`) — Canvas background\n- **On-Primary** (`" +
1365 fg +
1366 "`) — Text and content\n- **Accent** (`" +
1367 ac +
1368 "`) — CTAs, highlights\n- **Muted** (`" +
1369 mt +
1370 "`) — Secondary text\n\n";
1371 md +=
1372 "## Typography\n\n- **Headline** — " +
1373 t.headline.family +
1374 " at weight " +
1375 t.headline.weight +
1376 "\n- **Body** — " +
1377 t.body.family +
1378 " at weight " +
1379 t.body.weight +
1380 "\n\n";
1381 md +=
1382 "## Layout\n\n- Structure: " +
1383 a.name +
1384 " — " +
1385 a.tag +
1386 "\n- Density: " +
1387 dn.name +
1388 " (" +
1389 dn.desc +
1390 ")\n- Corners: " +
1391 cr.name +
1392 " (" +
1393 cr.value +
1394 ")\n\n";
1395 md += "## Elevation\n\n" + dp.name + " — " + dp.desc + ".\n\n";
1396 var easeObj = picks.easing !== null ? EASINGS[picks.easing] : null;
1397 if (easeObj) {
1398 md +=
1399 "## Motion\n\n- **Entry easing:** `" +
1400 easeObj.value +
1401 "` — " +
1402 easeObj.desc +
1403 "\n- **Energy:** " +
1404 easeObj.name +
1405 "\n\n";
1406 }
1407 if (a.components) md += "## Components\n\n" + a.components + "\n\n";
1408 md += "## Do's and Don'ts\n\n";
1409 if (a.dos) {
1410 md += a.dos + "\n";
1411 } else {
1412 md +=
1413 "### Do's\n- Use accent color sparingly for maximum impact\n- Maintain consistent " +
1414 cr.name.toLowerCase() +
1415 " corners\n\n### Don'ts\n- Do not mix multiple accent colors\n- Do not use weights outside the headline/body pair\n";
1416 }
1417
1418 document.getElementById("md-output").value = md;
1419 document.getElementById("md-overlay").classList.add("visible");
1420 }
1421
1422 document.getElementById("copy-btn").addEventListener("click", function () {
1423 navigator.clipboard.writeText(document.getElementById("md-output").value).then(function () {
1424 document.getElementById("copy-btn").textContent = "Copied!";
1425 setTimeout(function () {
1426 document.getElementById("copy-btn").textContent = "Copy";
1427 }, 2000);
1428 });
1429 });
1430 </script>
1431 </body>
1432</html>