diff --git a/src/components/builder/SimpleChoiceBuilder.tsx b/src/components/builder/SimpleChoiceBuilder.tsx new file mode 100644 index 00000000..b848a344 --- /dev/null +++ b/src/components/builder/SimpleChoiceBuilder.tsx @@ -0,0 +1,150 @@ +import React, { useMemo, useState } from 'react'; + +const POSTER_TITLES = [ + 'Midnight Signal', + 'The Last Orbit', + 'Glass City', + 'Northern Bloom', + 'Static Hearts', + 'Velvet Run', + 'Solar Drift', + 'Hidden Coast', + 'Neon Valley', + 'Quiet Thunder', + 'Paper Moons', + 'Arcade Summer', +]; + +const PALETTES = [ + ['#24111f', '#7c2d12', '#f97316'], + ['#08111f', '#1d4ed8', '#38bdf8'], + ['#111827', '#581c87', '#e879f9'], + ['#10140f', '#166534', '#86efac'], + ['#190b0b', '#991b1b', '#fca5a5'], + ['#111112', '#334155', '#f8fafc'], +]; + +const DEFAULT_CHOICES = ['Movie night', 'Series binge', 'Anime marathon', 'Documentary deep dive']; + +const svgToDataUrl = (svg: string) => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`; + +const SimpleChoiceBuilder: React.FC = () => { + const [question, setQuestion] = useState('What should we watch tonight?'); + const [choices, setChoices] = useState(DEFAULT_CHOICES); + const [selectedChoice, setSelectedChoice] = useState(0); + + const posters = useMemo( + () => + Array.from({ length: 36 }, (_, index) => { + const palette = PALETTES[index % PALETTES.length]; + const title = POSTER_TITLES[index % POSTER_TITLES.length]; + const svg = `${title}POSTERIUM PICK`; + return { alt: `${title} poster tile`, src: svgToDataUrl(svg) }; + }), + [] + ); + + const updateChoice = (index: number, value: string) => { + setChoices((current) => + current.map((choice, choiceIndex) => (choiceIndex === index ? value : choice)) + ); + }; + + const addChoice = () => { + setChoices((current) => [...current, `Choice ${current.length + 1}`]); + }; + + const removeChoice = (index: number) => { + setChoices((current) => current.filter((_, choiceIndex) => choiceIndex !== index)); + setSelectedChoice((current) => Math.max(0, Math.min(current, choices.length - 2))); + }; + + const safeChoices = choices.filter((choice) => choice.trim()); + const winningChoice = safeChoices[selectedChoice] ?? safeChoices[0] ?? 'Your pick'; + + return ( +
+ +
+
+
+
P
+

Guided Mode

+

Choice Posters

+

+ Create a shareable poster from one simple multiple-choice question. +

+ +
+ + setQuestion(event.target.value)} + placeholder="Ask a question" + /> + +
+ {choices.map((choice, index) => ( +
+ + updateChoice(index, event.target.value)} + /> + {choices.length > 2 && ( + + )} +
+ ))} +
+ + +
+ +
+ Poster Preview +

{question || 'Your question'}

+
+ {safeChoices.map((choice, index) => ( +

+ {String.fromCharCode(65 + index)} {choice} +

+ ))} +
+
{winningChoice}
+
+
+
+
+ ); +}; + +export default SimpleChoiceBuilder; diff --git a/src/pages/build/simple.astro b/src/pages/build/simple.astro new file mode 100644 index 00000000..1bcdd9d4 --- /dev/null +++ b/src/pages/build/simple.astro @@ -0,0 +1,31 @@ +--- +import BaseLayout from '@/layouts/BaseLayout.astro'; +import SimpleChoiceBuilder from '@/components/builder/SimpleChoiceBuilder'; +import { SITE_CONFIG } from '@/lib/config'; +import type { PageSEOMetadata } from '@/lib/seo'; +import '@/styles/global.css'; + +const canonical = `${SITE_CONFIG.baseUrl}/build/simple`; + +const seo: PageSEOMetadata = { + title: 'Guided Choice Poster Builder | Posterium', + description: + 'Create a choice poster from a simple multiple-choice question with Posterium guided mode.', + keywords: + 'choice poster builder, multiple choice poster, guided poster creator, simple poster builder, Posterium guided mode', + canonical, + ogImage: SITE_CONFIG.ogImageUrl, + ogImageAlt: 'Posterium guided choice poster builder', + robots: 'index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1', + breadcrumbs: [ + { name: 'Home', url: SITE_CONFIG.baseUrl }, + { name: 'Poster Builder', url: `${SITE_CONFIG.baseUrl}/build` }, + { name: 'Guided Choice Poster Builder', url: canonical }, + ], + jsonLd: [], +}; +--- + + + + diff --git a/src/styles/global.css b/src/styles/global.css index e6e1bc28..adaf18de 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -546,3 +546,280 @@ padding-left: 3px; } } + +.simple-choice-page { + min-height: 100vh; + overflow: hidden; + background: #0a0a0a; + color: #fff; + font-family: + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + sans-serif; +} + +.simple-choice-bg { + position: fixed; + inset: 0; + z-index: 0; + overflow: hidden; + opacity: 0.35; +} + +.simple-choice-grid { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 8px; + padding: 8px; + animation: simple-choice-scroll 70s linear infinite; +} + +.simple-choice-grid img { + display: block; + width: 100%; + aspect-ratio: 2 / 3; + border-radius: 6px; + background: #1a1a1a; + object-fit: cover; +} + +.simple-choice-overlay { + position: fixed; + inset: 0; + z-index: 1; + background: radial-gradient( + ellipse at center, + rgba(10, 10, 10, 0.4) 0%, + rgba(10, 10, 10, 0.95) 70% + ); +} + +.simple-choice-center { + position: relative; + z-index: 2; + display: flex; + min-height: 100vh; + align-items: center; + justify-content: center; + padding: 2rem; +} + +.simple-choice-card { + width: min(100%, 460px); + max-height: calc(100vh - 4rem); + overflow: auto; + padding: 2rem; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 24px; + background: rgba(10, 10, 10, 0.72); + box-shadow: 0 24px 80px rgba(0, 0, 0, 0.55); + text-align: center; + backdrop-filter: blur(18px); +} + +.simple-choice-mark { + display: inline-grid; + width: 80px; + height: 80px; + margin-bottom: 1rem; + place-items: center; + border-radius: 16px; + background: linear-gradient(135deg, #fff, #9ca3af); + color: #111; + font-size: 2.4rem; + font-weight: 900; +} + +.simple-choice-kicker { + margin: 0 0 0.35rem; + color: #e5e7eb; + font-size: 0.76rem; + font-weight: 800; + letter-spacing: 0.16em; + text-transform: uppercase; +} + +.simple-choice-card h1 { + margin: 0 0 0.5rem; + font-size: clamp(2rem, 7vw, 3rem); + font-weight: 900; + line-height: 0.95; +} + +.simple-choice-subtitle { + margin: 0 auto 1rem; + color: #aaa; + font-size: 0.95rem; + line-height: 1.5; +} + +.simple-choice-builder { + display: grid; + gap: 0.65rem; + margin-top: 1.25rem; + text-align: left; +} + +.simple-choice-label { + color: #d4d4d4; + font-size: 0.75rem; + font-weight: 800; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.simple-choice-builder input { + width: 100%; + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 10px; + background: rgba(255, 255, 255, 0.08); + color: #fff; + font: inherit; + outline: none; + padding: 0.78rem 0.9rem; +} + +.simple-choice-builder input:focus { + border-color: rgba(255, 255, 255, 0.72); + box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.08); +} + +.simple-choice-list { + display: grid; + gap: 0.5rem; +} + +.simple-choice-row { + display: grid; + grid-template-columns: auto 1fr auto; + gap: 0.5rem; + align-items: center; +} + +.simple-choice-row button, +.simple-choice-secondary { + border: 0; + cursor: pointer; + font: inherit; + font-weight: 800; + transition: + transform 0.2s ease, + background 0.2s ease; +} + +.simple-choice-row button:first-child { + width: 42px; + height: 42px; + border-radius: 10px; + background: rgba(255, 255, 255, 0.16); + color: #fff; +} + +.simple-choice-row button:first-child.is-selected { + background: #fff; + color: #111; +} + +.simple-choice-remove { + width: 34px; + height: 34px; + border-radius: 999px; + background: rgba(248, 113, 113, 0.2); + color: #fecaca; +} + +.simple-choice-secondary { + display: inline-block; + justify-self: start; + margin-top: 0.1rem; + padding: 0.75rem 1.1rem; + border-radius: 8px; + background: #fff; + color: #111; +} + +.simple-choice-secondary:hover, +.simple-choice-row button:hover { + transform: scale(1.04); +} + +.simple-choice-preview { + margin-top: 1.25rem; + padding: 1.1rem; + border-radius: 18px; + background: linear-gradient(160deg, #f8fafc, #cbd5e1 48%, #0f172a 49%, #111 100%); + color: #111; + text-align: left; +} + +.simple-choice-preview span { + font-size: 0.68rem; + font-weight: 900; + letter-spacing: 0.16em; + text-transform: uppercase; +} + +.simple-choice-preview h2 { + margin: 0.45rem 0 0.85rem; + font-size: 1.55rem; + line-height: 1.05; +} + +.simple-choice-options { + display: grid; + gap: 0.4rem; +} + +.simple-choice-options p { + margin: 0; + padding: 0.55rem 0.65rem; + border-radius: 10px; + background: rgba(255, 255, 255, 0.74); + color: #111; + font-size: 0.9rem; +} + +.simple-choice-options strong { + margin-right: 0.4rem; +} + +.simple-choice-options .is-winner { + background: #111; + color: #fff; +} + +.simple-choice-preview footer { + margin-top: 1rem; + color: #fff; + font-size: 0.8rem; + font-weight: 900; + letter-spacing: 0.12em; + text-align: right; + text-transform: uppercase; +} + +@keyframes simple-choice-scroll { + from { + transform: translateY(0); + } + to { + transform: translateY(-50%); + } +} + +@media (max-width: 768px) { + .simple-choice-grid { + grid-template-columns: repeat(3, 1fr); + } + + .simple-choice-center { + padding: 1rem; + } + + .simple-choice-card { + max-height: calc(100vh - 2rem); + padding: 1.2rem; + } +}