diff --git a/app.js b/app.js new file mode 100644 index 0000000..3e4096d --- /dev/null +++ b/app.js @@ -0,0 +1,197 @@ +const stylePresets = [ + { + name: "Vibrant", + gradient: ["#4c5cff", "#ff5b7c"], + textShadow: "0 10px 20px rgba(15, 23, 42, 0.4)", + }, + { + name: "Neon", + gradient: ["#00f5d4", "#9b5cff"], + textShadow: "0 8px 18px rgba(0, 0, 0, 0.35)", + }, + { + name: "Cinematic", + gradient: ["#111827", "#ef4444"], + textShadow: "0 6px 20px rgba(0, 0, 0, 0.55)", + }, +]; + +const palettes = [ + { name: "Cosmic", colors: ["#4c5cff", "#ff5b7c", "#ffffff"] }, + { name: "Sunset", colors: ["#ff8c42", "#ff3c6f", "#ffd66b"] }, + { name: "Ocean", colors: ["#0077ff", "#00c2ff", "#e0f7ff"] }, +]; + +const alignments = ["Left", "Center", "Right"]; + +const state = { + style: stylePresets[0], + palette: palettes[0], + alignment: alignments[0], +}; + +const previewCanvas = document.getElementById("previewCanvas"); +const previewText = document.getElementById("previewText"); +const previewTitle = document.getElementById("previewTitle"); +const previewSubtitle = document.getElementById("previewSubtitle"); +const previewKicker = document.getElementById("previewKicker"); +const styleBadge = document.getElementById("styleBadge"); +const swatches = document.getElementById("swatches"); + +const styleChips = document.getElementById("styleChips"); +const alignChips = document.getElementById("alignChips"); +const paletteOptions = document.getElementById("paletteOptions"); + +const videoTitle = document.getElementById("videoTitle"); +const videoHook = document.getElementById("videoHook"); +const videoSubtitle = document.getElementById("videoSubtitle"); + +const generateBtn = document.getElementById("generateBtn"); +const shuffleBtn = document.getElementById("shuffleBtn"); +const resetBtn = document.getElementById("resetBtn"); + +const createChip = (label, group, onSelect) => { + const chip = document.createElement("button"); + chip.type = "button"; + chip.className = "chip"; + chip.textContent = label; + chip.addEventListener("click", () => onSelect(label, chip)); + group.appendChild(chip); + return chip; +}; + +const updatePreview = () => { + const [first, second] = state.style.gradient; + previewCanvas.style.background = `linear-gradient(135deg, ${first}, ${second})`; + previewText.style.textAlign = state.alignment.toLowerCase(); + previewText.style.textShadow = state.style.textShadow; + styleBadge.textContent = state.style.name; + + swatches.innerHTML = ""; + state.palette.colors.forEach((color) => { + const swatch = document.createElement("span"); + swatch.className = "swatch"; + swatch.style.background = color; + swatches.appendChild(swatch); + }); +}; + +const setActiveChip = (group, activeChip) => { + group.querySelectorAll(".chip").forEach((chip) => chip.classList.remove("active")); + activeChip.classList.add("active"); +}; + +stylePresets.forEach((preset, index) => { + const chip = createChip(preset.name, styleChips, (label, clicked) => { + state.style = stylePresets.find((item) => item.name === label); + setActiveChip(styleChips, clicked); + updatePreview(); + }); + if (index === 0) { + chip.classList.add("active"); + } +}); + +alignments.forEach((alignment, index) => { + const chip = createChip(alignment, alignChips, (label, clicked) => { + state.alignment = label; + setActiveChip(alignChips, clicked); + updatePreview(); + }); + if (index === 0) { + chip.classList.add("active"); + } +}); + +palettes.forEach((palette, index) => { + const card = document.createElement("button"); + card.type = "button"; + card.className = "palette-option"; + + const samples = document.createElement("div"); + samples.className = "palette-samples"; + palette.colors.forEach((color) => { + const dot = document.createElement("span"); + dot.style.background = color; + samples.appendChild(dot); + }); + + const label = document.createElement("span"); + label.textContent = palette.name; + + card.appendChild(samples); + card.appendChild(label); + card.addEventListener("click", () => { + state.palette = palette; + paletteOptions + .querySelectorAll(".palette-option") + .forEach((option) => option.classList.remove("active")); + card.classList.add("active"); + updatePreview(); + }); + + if (index === 0) { + card.classList.add("active"); + } + + paletteOptions.appendChild(card); +}); + +const syncInputs = () => { + previewTitle.textContent = videoTitle.value.trim() || "Your video title"; + previewKicker.textContent = videoHook.value.trim() || "Hook"; + previewSubtitle.textContent = videoSubtitle.value.trim() || "Supporting line"; +}; + +const randomItem = (items) => items[Math.floor(Math.random() * items.length)]; + +const shuffle = () => { + state.style = randomItem(stylePresets); + state.palette = randomItem(palettes); + state.alignment = randomItem(alignments); + + styleChips + .querySelectorAll(".chip") + .forEach((chip) => chip.classList.toggle("active", chip.textContent === state.style.name)); + alignChips + .querySelectorAll(".chip") + .forEach((chip) => chip.classList.toggle("active", chip.textContent === state.alignment)); + paletteOptions + .querySelectorAll(".palette-option") + .forEach((option) => option.classList.toggle("active", option.textContent === state.palette.name)); + + updatePreview(); +}; + +const resetForm = () => { + videoTitle.value = "Create Thumbnails That Pop"; + videoHook.value = "AI Hacks"; + videoSubtitle.value = "Boost clicks with smart layouts"; + state.style = stylePresets[0]; + state.palette = palettes[0]; + state.alignment = alignments[0]; + shuffle(); + syncInputs(); +}; + +videoTitle.addEventListener("input", syncInputs); +videoHook.addEventListener("input", syncInputs); +videoSubtitle.addEventListener("input", syncInputs); + +const form = document.getElementById("thumbnailForm"); +form.addEventListener("submit", (event) => { + event.preventDefault(); + syncInputs(); + updatePreview(); +}); + +generateBtn.addEventListener("click", () => { + syncInputs(); + updatePreview(); +}); + +shuffleBtn.addEventListener("click", shuffle); +resetBtn.addEventListener("click", resetForm); + +syncInputs(); +updatePreview(); diff --git a/index.html b/index.html new file mode 100644 index 0000000..5a72e83 --- /dev/null +++ b/index.html @@ -0,0 +1,128 @@ + + + + + + AI YouTube Thumbnail Generator + + + +
+
+
+

AI YouTube Studio

+

AI YouTube Thumbnail Generator

+

+ Craft attention-grabbing thumbnails in minutes with smart presets, bold typography, and + creative layouts. +

+
+ + +
+
    +
  • + 1,280×720 + Optimized size +
  • +
  • + 3 + Style modes +
  • +
  • + Instant + Preview +
  • +
+
+
+
+ Live preview + Vibrant +
+
+
+

AI Hacks

+

Create Thumbnails That Pop

+

Boost clicks with smart layouts

+
+
+ +
+
+ +
+
+

Describe your video

+

Tell the AI what your video is about, choose a style, and customize the layout.

+
+ + + + + + + +
+ + +
+
+
+ +
+

AI creative tips

+
+
+

Faces & focus

+

Add a bold subject area for a face or product cut-out.

+
+
+

Contrast

+

Pick a background palette with strong contrast to make text pop.

+
+
+

Short hooks

+

Keep the hook under 3 words to create curiosity.

+
+
+

Story cues

+

Use arrows or rings around important focus points.

+
+
+
+
+
+ + + + diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..3dac07e --- /dev/null +++ b/styles.css @@ -0,0 +1,333 @@ +:root { + color-scheme: light; + font-family: "Inter", "Segoe UI", system-ui, sans-serif; + --bg: #f5f6fb; + --card: #ffffff; + --text: #0f172a; + --muted: #5b647c; + --primary: #4c5cff; + --primary-dark: #2a39d6; + --accent: #ff5b7c; + --border: #e4e7f5; + --shadow: 0 20px 50px rgba(19, 22, 45, 0.08); +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + background: var(--bg); + color: var(--text); + min-height: 100vh; +} + +.page { + max-width: 1200px; + margin: 0 auto; + padding: 40px 24px 80px; +} + +.hero { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 32px; + align-items: center; + margin-bottom: 50px; +} + +.eyebrow { + text-transform: uppercase; + letter-spacing: 0.16em; + font-size: 0.75rem; + color: var(--primary-dark); + font-weight: 600; + margin-bottom: 12px; +} + +h1 { + font-size: clamp(2.2rem, 3.2vw, 3.4rem); + line-height: 1.1; + margin-bottom: 18px; +} + +.subtitle { + font-size: 1.05rem; + color: var(--muted); + margin-bottom: 24px; + max-width: 480px; +} + +.hero-actions { + display: flex; + gap: 16px; + margin-bottom: 24px; + flex-wrap: wrap; +} + +.primary, +.ghost { + border: none; + border-radius: 999px; + padding: 12px 22px; + font-weight: 600; + cursor: pointer; + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.primary { + background: var(--primary); + color: #fff; + box-shadow: 0 10px 20px rgba(76, 92, 255, 0.3); +} + +.primary:hover { + transform: translateY(-2px); +} + +.ghost { + background: transparent; + border: 1px solid var(--border); + color: var(--text); +} + +.hero-metrics { + list-style: none; + display: flex; + gap: 24px; + flex-wrap: wrap; + color: var(--muted); + font-size: 0.9rem; +} + +.hero-metrics strong { + display: block; + color: var(--text); + font-size: 1.1rem; + margin-bottom: 4px; +} + +.preview-card { + background: var(--card); + padding: 20px; + border-radius: 28px; + box-shadow: var(--shadow); +} + +.preview-header, +.preview-footer { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.85rem; + color: var(--muted); + margin-bottom: 12px; +} + +.badge { + background: rgba(76, 92, 255, 0.1); + color: var(--primary-dark); + padding: 4px 10px; + border-radius: 999px; + font-weight: 600; +} + +.preview-canvas { + background: linear-gradient(135deg, #4c5cff, #ff5b7c); + border-radius: 22px; + height: 260px; + padding: 24px; + position: relative; + overflow: hidden; + display: flex; + align-items: flex-end; + color: #fff; + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); +} + +.preview-canvas::after { + content: ""; + position: absolute; + inset: 16px; + border: 2px dashed rgba(255, 255, 255, 0.25); + border-radius: 18px; +} + +.preview-text { + position: relative; + z-index: 1; +} + +.preview-kicker { + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: 0.75rem; + margin-bottom: 10px; +} + +.preview-text h2 { + font-size: 1.6rem; + margin-bottom: 12px; +} + +.preview-subtitle { + font-size: 0.95rem; + opacity: 0.85; +} + +.swatches { + display: flex; + gap: 6px; +} + +.swatch { + width: 18px; + height: 18px; + border-radius: 50%; + border: 2px solid #fff; + box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.1); +} + +.content { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 32px; +} + +.form-card { + background: var(--card); + padding: 28px; + border-radius: 24px; + box-shadow: var(--shadow); +} + +.form-card h2 { + margin-bottom: 8px; +} + +.form-card p { + color: var(--muted); + margin-bottom: 20px; +} + +form { + display: grid; + gap: 16px; +} + +label { + display: grid; + gap: 8px; + font-weight: 600; + font-size: 0.9rem; +} + +input, +select { + border: 1px solid var(--border); + border-radius: 12px; + padding: 10px 12px; + font-size: 0.95rem; + font-family: inherit; +} + +.chip-group { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.chip { + padding: 8px 14px; + border-radius: 999px; + border: 1px solid var(--border); + background: transparent; + cursor: pointer; + font-size: 0.85rem; + color: var(--muted); + transition: all 0.2s ease; +} + +.chip.active { + border-color: var(--primary); + background: rgba(76, 92, 255, 0.12); + color: var(--primary-dark); + font-weight: 600; +} + +.palette { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 12px; +} + +.palette-option { + border: 1px solid var(--border); + border-radius: 14px; + padding: 10px; + cursor: pointer; + display: grid; + gap: 8px; + font-size: 0.85rem; + color: var(--muted); +} + +.palette-option.active { + border-color: var(--primary); + color: var(--text); + background: rgba(76, 92, 255, 0.08); +} + +.palette-samples { + display: flex; + gap: 6px; +} + +.palette-samples span { + width: 20px; + height: 20px; + border-radius: 50%; + display: inline-block; +} + +.form-actions { + display: flex; + gap: 12px; + flex-wrap: wrap; +} + +.tips h2 { + margin-bottom: 16px; +} + +.tip-grid { + display: grid; + gap: 16px; +} + +.tip-grid article { + background: var(--card); + padding: 20px; + border-radius: 18px; + box-shadow: var(--shadow); +} + +.tip-grid h3 { + margin-bottom: 8px; +} + +.tip-grid p { + color: var(--muted); +} + +@media (max-width: 720px) { + .preview-canvas { + height: 220px; + } + + .preview-text h2 { + font-size: 1.4rem; + } +}