Storybook — Modern Flat Landing
A bright, modern flat-illustration landing page for a fictional kids' reading app called Tinytales, built in plain HTML, CSS and JavaScript. A bold inline-SVG hero scene pairs with app-store style download buttons, crisp flat feature cards with custom icons, a three-step how-it-works, and a filterable stories shelf where mood chips swap a grid of rated cover cards. A pricing card, validated newsletter form and colorful gradient footer round out a snappy, geometric, solid-color design.
MCP
Code
/* ===== Tokens ===== */
:root{
--bg:#fff8ef;
--surface:#ffffff;
--ink:#2c2350;
--ink-soft:#6a6390;
--primary:#ff8a3d;
--primary-ink:#e26a18;
--secondary:#5ec5d6;
--accent:#ffd23f;
--pink:#ff6f9c;
--green:#7bd389;
--violet:#a07cff;
--line:#efe5d4;
--r-sm:14px;
--r:22px;
--r-lg:30px;
--pill:999px;
--shadow:0 14px 30px -16px rgba(44,35,80,.35);
--shadow-sm:0 8px 18px -12px rgba(44,35,80,.4);
--ring:0 0 0 4px rgba(94,197,214,.45);
--font-head:"Baloo 2",system-ui,-apple-system,"Segoe UI",sans-serif;
--font-body:"Nunito",system-ui,-apple-system,"Segoe UI",sans-serif;
--maxw:1120px;
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
margin:0;
font-family:var(--font-body);
color:var(--ink);
background:
radial-gradient(1200px 540px at 85% -10%, rgba(94,197,214,.18), transparent 60%),
radial-gradient(900px 460px at 0% 0%, rgba(255,138,61,.16), transparent 55%),
var(--bg);
line-height:1.5;
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
text-rendering:optimizeLegibility;
}
h1,h2,h3{font-family:var(--font-head);line-height:1.12;margin:0;color:var(--ink)}
p{margin:0}
ul,ol{margin:0;padding:0;list-style:none}
a{color:inherit;text-decoration:none}
img,svg{display:block;max-width:100%}
button{font:inherit}
.wrap{width:min(var(--maxw),100% - 2.4rem);margin-inline:auto}
.skip-link{
position:absolute;left:50%;top:-60px;transform:translateX(-50%);
background:var(--ink);color:#fff;padding:.6rem 1rem;border-radius:var(--pill);
z-index:60;transition:top .2s;
}
.skip-link:focus{top:10px}
:focus-visible{outline:none;box-shadow:var(--ring);border-radius:var(--pill)}
/* ===== Buttons ===== */
.btn{
--bg:var(--surface);
display:inline-flex;align-items:center;justify-content:center;gap:.45rem;
min-height:48px;padding:.7rem 1.3rem;border:none;border-radius:var(--pill);
font-family:var(--font-head);font-weight:700;font-size:1rem;cursor:pointer;
background:var(--bg);color:var(--ink);
transition:transform .15s ease, box-shadow .2s ease, background .2s ease;
}
.btn:active{transform:translateY(2px) scale(.98)}
.btn-primary{background:var(--primary);color:#fff;box-shadow:0 8px 0 -2px var(--primary-ink)}
.btn-primary:hover{background:#ff9c57}
.btn-ghost{background:#fff;color:var(--ink);box-shadow:inset 0 0 0 2px var(--line)}
.btn-ghost:hover{background:#fffbf4;box-shadow:inset 0 0 0 2px #e7d8bf}
.btn-block{width:100%}
/* ===== Header ===== */
.site-header{position:sticky;top:0;z-index:50;backdrop-filter:saturate(1.2) blur(8px);
background:rgba(255,248,239,.82);border-bottom:1px solid var(--line)}
.header-inner{display:flex;align-items:center;gap:1rem;padding:.7rem 0;min-height:68px}
.brand{display:inline-flex;align-items:center;gap:.55rem;font-family:var(--font-head);font-weight:800}
.brand-mark{display:grid;place-items:center;filter:drop-shadow(0 6px 8px rgba(255,138,61,.35))}
.brand-name{font-size:1.35rem;letter-spacing:.2px}
.main-nav{margin-left:auto}
.main-nav ul{display:flex;gap:.4rem}
.main-nav a{display:inline-block;padding:.5rem .85rem;border-radius:var(--pill);font-weight:700;color:var(--ink-soft);transition:color .15s,background .15s}
.main-nav a:hover{color:var(--ink);background:#fff}
.header-cta{display:flex;gap:.5rem}
.nav-toggle{display:none;flex-direction:column;gap:5px;width:48px;height:48px;align-items:center;justify-content:center;
background:#fff;border:none;border-radius:16px;box-shadow:var(--shadow-sm);cursor:pointer}
.nav-toggle span{width:22px;height:3px;border-radius:3px;background:var(--ink);transition:transform .25s,opacity .2s}
.nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(8px) rotate(45deg)}
.nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
.nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-8px) rotate(-45deg)}
.mobile-nav{display:none;flex-direction:column;gap:.3rem;padding:.6rem 1.2rem 1.1rem}
.mobile-nav a{padding:.8rem 1rem;border-radius:var(--r-sm);font-weight:700;font-family:var(--font-head)}
.mobile-nav a:hover{background:#fff}
.mobile-nav .btn{margin-top:.4rem}
/* ===== Hero ===== */
.hero{position:relative;overflow:hidden;padding:clamp(2rem,6vw,4.5rem) 0 clamp(2.5rem,5vw,4rem)}
.blob{position:absolute;border-radius:50%;filter:blur(6px);opacity:.5;z-index:0}
.blob-a{width:280px;height:280px;background:radial-gradient(circle,var(--accent),transparent 70%);top:-60px;right:-40px}
.blob-b{width:240px;height:240px;background:radial-gradient(circle,var(--pink),transparent 70%);bottom:-80px;left:-60px;opacity:.35}
.hero-inner{position:relative;z-index:1;display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(1.5rem,4vw,3rem);align-items:center}
.eyebrow{display:inline-flex;align-items:center;gap:.5rem;background:#fff;color:var(--primary-ink);
font-weight:800;font-family:var(--font-head);padding:.5rem 1rem;border-radius:var(--pill);box-shadow:var(--shadow-sm);margin-bottom:1.1rem}
.eyebrow-dot{width:10px;height:10px;border-radius:50%;background:var(--green);box-shadow:0 0 0 4px rgba(123,211,137,.3)}
.hero h1{font-size:clamp(2.3rem,6.2vw,4rem);font-weight:800;letter-spacing:-.5px}
.hl{color:var(--primary);position:relative;white-space:nowrap}
.hl::after{content:"";position:absolute;left:-2%;right:-2%;bottom:.06em;height:.34em;background:var(--accent);border-radius:var(--pill);z-index:-1;opacity:.85}
.lede{font-size:clamp(1.05rem,2vw,1.22rem);color:var(--ink-soft);max-width:42ch;margin:1.1rem 0 1.6rem;font-weight:600}
.hero-actions{display:flex;flex-wrap:wrap;gap:.7rem;margin-bottom:1.8rem}
.store-btn{display:inline-flex;align-items:center;gap:.6rem;min-height:56px;padding:.55rem 1.2rem;border-radius:18px;
background:var(--ink);color:#fff;box-shadow:var(--shadow);transition:transform .15s,box-shadow .2s}
.store-btn:hover{transform:translateY(-3px)}
.store-btn:active{transform:translateY(0)}
.store-btn span{display:flex;flex-direction:column;line-height:1.1;text-align:left;font-family:var(--font-head)}
.store-btn small{font-size:.66rem;font-weight:600;opacity:.8;font-family:var(--font-body)}
.store-btn strong{font-size:1.05rem;font-weight:700}
.trust{display:flex;gap:1.4rem;flex-wrap:wrap}
.trust li{display:flex;flex-direction:column}
.trust strong{font-family:var(--font-head);font-size:1.5rem;color:var(--ink)}
.trust span{font-size:.85rem;color:var(--ink-soft);font-weight:700}
/* hero art */
.hero-art{display:grid;place-items:center}
.scene{width:min(100%,460px);filter:drop-shadow(0 24px 40px -22px rgba(44,35,80,.5))}
.book{transform-origin:210px 300px;animation:bookGlow 4s ease-in-out infinite}
.kid{transform-origin:210px 270px;animation:bob 5s ease-in-out infinite}
.owl{transform-origin:92px 206px;animation:bob 4.2s ease-in-out infinite}
.twinkle{transform-origin:196px 70px;animation:twinkle 3s ease-in-out infinite}
.twinkle-2{transform-origin:272px 135px;animation:twinkle 3.4s ease-in-out .6s infinite}
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}
@keyframes bookGlow{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px) scale(1.01)}}
@keyframes twinkle{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.35) rotate(12deg);opacity:.55}}
/* ===== Logos strip ===== */
.logos{padding:1.2rem 0 .4rem;text-align:center}
.logos p{color:var(--ink-soft);font-weight:700;text-transform:uppercase;letter-spacing:.12em;font-size:.78rem;margin-bottom:.8rem}
.logos ul{display:flex;flex-wrap:wrap;gap:.6rem;justify-content:center}
.logos li{background:#fff;border-radius:var(--pill);padding:.55rem 1.1rem;font-weight:800;font-family:var(--font-head);
color:var(--ink);box-shadow:var(--shadow-sm);font-size:.95rem}
/* ===== Section heads ===== */
.section-head{text-align:center;max-width:48ch;margin:0 auto clamp(1.6rem,4vw,2.6rem)}
.kicker{display:inline-block;font-family:var(--font-head);font-weight:800;color:var(--secondary);
text-transform:uppercase;letter-spacing:.14em;font-size:.8rem;margin-bottom:.5rem}
.section-head h2{font-size:clamp(1.7rem,4.4vw,2.6rem);font-weight:800;letter-spacing:-.4px}
.section-sub{color:var(--ink-soft);font-weight:600;margin-top:.7rem;font-size:1.05rem}
/* ===== Features ===== */
.features{padding:clamp(2.5rem,6vw,4.5rem) 0}
.feature-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.1rem}
.feature-card{background:var(--surface);border-radius:var(--r-lg);padding:1.5rem;
box-shadow:var(--shadow);border:1px solid var(--line);position:relative;overflow:hidden;
transition:transform .2s ease, box-shadow .25s ease}
.feature-card::before{content:"";position:absolute;inset:0 0 auto 0;height:8px;background:var(--c)}
.feature-card:hover{transform:translateY(-6px);box-shadow:0 26px 40px -22px rgba(44,35,80,.5)}
.feature-icon{display:grid;place-items:center;width:56px;height:56px;border-radius:18px;
background:var(--c2);color:var(--c);margin:.4rem 0 1rem}
.feature-card h3{font-size:1.25rem;font-weight:700;margin-bottom:.45rem}
.feature-card p{color:var(--ink-soft);font-weight:600}
/* ===== How ===== */
.how{padding:clamp(2rem,5vw,4rem) 0}
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:1.1rem;counter-reset:step}
.step{position:relative;background:var(--surface);border-radius:var(--r-lg);padding:2.2rem 1.4rem 1.5rem;
text-align:center;box-shadow:var(--shadow);border:1px solid var(--line)}
.step-num{position:absolute;top:-22px;left:50%;transform:translateX(-50%);width:48px;height:48px;
display:grid;place-items:center;border-radius:50%;background:var(--s);color:#fff;
font-family:var(--font-head);font-weight:800;font-size:1.3rem;box-shadow:0 8px 18px -8px rgba(44,35,80,.6)}
.step-art{font-size:2.6rem;display:block;margin:.4rem 0 .6rem;line-height:1}
.step h3{font-size:1.3rem;font-weight:700;margin-bottom:.4rem}
.step p{color:var(--ink-soft);font-weight:600}
.steps .step:not(:last-child)::after{content:"";position:absolute;right:-1.1rem;top:50%;width:1.1rem;height:4px;
background:repeating-linear-gradient(90deg,var(--line) 0 6px,transparent 6px 12px)}
/* ===== Stories ===== */
.stories{padding:clamp(2.5rem,6vw,4.5rem) 0}
.mood-row{display:flex;flex-wrap:wrap;gap:.5rem;justify-content:center;margin-bottom:1.8rem}
.chip{min-height:48px;padding:.6rem 1.2rem;border-radius:var(--pill);border:2px solid var(--line);
background:#fff;color:var(--ink);font-family:var(--font-head);font-weight:700;cursor:pointer;
transition:transform .15s,background .15s,border-color .15s,color .15s}
.chip:hover{transform:translateY(-2px);border-color:var(--secondary)}
.chip.is-on{background:var(--ink);color:#fff;border-color:var(--ink)}
.chip:active{transform:translateY(1px)}
.story-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1.1rem}
.story-card{background:#fff;border-radius:var(--r);overflow:hidden;box-shadow:var(--shadow);
border:1px solid var(--line);cursor:pointer;text-align:left;padding:0;
transition:transform .2s ease, box-shadow .25s ease;animation:pop .35s ease backwards}
.story-card:hover{transform:translateY(-6px) rotate(-.6deg);box-shadow:0 26px 40px -22px rgba(44,35,80,.55)}
.story-card:active{transform:translateY(-2px) scale(.99)}
.story-cover{aspect-ratio:4/5;display:grid;place-items:center;font-size:3.4rem;position:relative}
.story-cover .badge{position:absolute;top:.6rem;left:.6rem;background:rgba(255,255,255,.92);color:var(--ink);
font-family:var(--font-head);font-weight:800;font-size:.72rem;padding:.25rem .6rem;border-radius:var(--pill)}
.story-cover .age{position:absolute;bottom:.6rem;right:.6rem;background:rgba(44,35,80,.78);color:#fff;
font-weight:800;font-size:.72rem;padding:.25rem .55rem;border-radius:var(--pill);font-family:var(--font-head)}
.story-meta{padding:.85rem 1rem 1.1rem}
.story-meta h3{font-size:1.1rem;font-weight:700}
.story-meta p{color:var(--ink-soft);font-weight:600;font-size:.88rem;margin-top:.2rem}
.story-meta .stars{color:var(--accent);margin-top:.4rem;font-size:.95rem;letter-spacing:1px}
.story-empty{text-align:center;color:var(--ink-soft);font-weight:700;padding:2rem 0}
@keyframes pop{from{transform:scale(.85);opacity:0}to{transform:scale(1);opacity:1}}
/* ===== Plans ===== */
.plans{padding:clamp(2.5rem,6vw,4.5rem) 0}
.plans-inner{display:grid;grid-template-columns:1.1fr .9fr;gap:clamp(1.4rem,4vw,3rem);align-items:center;
background:linear-gradient(135deg,#fff,#fff6e9);border:1px solid var(--line);border-radius:var(--r-lg);
padding:clamp(1.6rem,4vw,3rem);box-shadow:var(--shadow);position:relative;overflow:hidden}
.plans-inner::before{content:"";position:absolute;width:220px;height:220px;border-radius:50%;
background:radial-gradient(circle,rgba(123,211,137,.35),transparent 70%);top:-70px;right:-50px}
.plans-copy{position:relative}
.plans-copy h2{font-size:clamp(1.7rem,4vw,2.5rem);font-weight:800;margin:.2rem 0 .6rem}
.plans-copy>p{color:var(--ink-soft);font-weight:600;max-width:40ch}
.plan-perks{margin-top:1.2rem;display:grid;gap:.6rem}
.plan-perks li{display:flex;align-items:center;gap:.6rem;font-weight:700;color:var(--ink)}
.plan-perks li::before{content:"✓";display:grid;place-items:center;width:26px;height:26px;border-radius:50%;
background:var(--green);color:#fff;font-weight:900;font-size:.85rem;flex:none}
.price-card{position:relative;background:#fff;border-radius:var(--r-lg);padding:1.8rem;text-align:center;
box-shadow:0 22px 40px -24px rgba(44,35,80,.6);border:2px solid var(--accent)}
.price-tag{font-family:var(--font-head)}
.price-tag .amt{font-size:3.2rem;font-weight:800;color:var(--ink)}
.price-tag .per{font-size:1rem;color:var(--ink-soft);font-weight:700;margin-left:.2rem}
.then{color:var(--ink-soft);font-weight:700;margin:.1rem 0 1.2rem}
.price-card .btn{margin-bottom:.6rem}
.fine{font-size:.85rem;color:var(--ink-soft);font-weight:700;margin-top:.4rem}
/* ===== Footer ===== */
.site-footer{margin-top:1.5rem;background:linear-gradient(180deg,#2c2350,#3a2f63);color:#fff8ef}
.footer-inner{display:grid;grid-template-columns:1.4fr 1.6fr;gap:2rem;padding:clamp(2rem,5vw,3.2rem) 0 2rem}
.footer-brand .brand-name{font-size:1.5rem;color:#fff}
.footer-brand>p{color:#cfc7ea;font-weight:600;margin:.5rem 0 1.2rem;max-width:34ch}
.news label{display:block;font-weight:800;font-family:var(--font-head);margin-bottom:.5rem}
.news-row{display:flex;gap:.5rem;flex-wrap:wrap}
.news-row input{flex:1;min-width:180px;min-height:48px;border:none;border-radius:var(--pill);padding:.7rem 1.1rem;
font:inherit;font-weight:600;background:rgba(255,255,255,.95);color:var(--ink)}
.news-row input::placeholder{color:#9a93b8}
.news-msg{min-height:1.2em;margin-top:.5rem;font-weight:700;color:var(--accent)}
.news-msg.err{color:#ffb3c6}
.footer-cols{display:grid;grid-template-columns:repeat(3,1fr);gap:1.2rem}
.footer-cols h3{font-size:1rem;color:#fff;margin-bottom:.7rem}
.footer-cols a{display:block;color:#cfc7ea;font-weight:600;padding:.3rem 0;transition:color .15s}
.footer-cols a:hover{color:#fff}
.footer-base{border-top:1px solid rgba(255,255,255,.12)}
.footer-base{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.6rem;
padding:1rem 0;max-width:var(--maxw);margin-inline:auto;width:min(var(--maxw),100% - 2.4rem)}
.footer-base p{color:#b8b0dc;font-weight:600;font-size:.9rem}
.footer-emojis{font-size:1.1rem;letter-spacing:.3rem}
/* ===== Toast ===== */
.toast{position:fixed;left:50%;bottom:24px;transform:translate(-50%,140%);z-index:80;
background:var(--ink);color:#fff;font-weight:700;font-family:var(--font-head);
padding:.85rem 1.3rem;border-radius:var(--pill);box-shadow:var(--shadow);
max-width:90vw;text-align:center;transition:transform .35s cubic-bezier(.2,1.4,.4,1)}
.toast.show{transform:translate(-50%,0)}
/* ===== Responsive ===== */
@media (max-width:960px){
.feature-grid{grid-template-columns:repeat(2,1fr)}
.story-grid{grid-template-columns:repeat(2,1fr)}
.plans-inner{grid-template-columns:1fr}
.footer-inner{grid-template-columns:1fr}
}
@media (max-width:820px){
.main-nav,.header-cta{display:none}
.nav-toggle{display:flex;margin-left:auto}
.mobile-nav:not([hidden]){display:flex}
.hero-inner{grid-template-columns:1fr;text-align:center}
.hero-copy{order:2}
.hero-art{order:1}
.eyebrow,.lede{margin-inline:auto}
.hero-actions,.trust{justify-content:center}
.steps{grid-template-columns:1fr;gap:1.8rem}
.steps .step:not(:last-child)::after{display:none}
}
@media (max-width:560px){
.feature-grid{grid-template-columns:1fr}
.story-grid{grid-template-columns:1fr 1fr;gap:.8rem}
.footer-cols{grid-template-columns:repeat(2,1fr)}
.footer-base{justify-content:center;text-align:center}
}
@media (max-width:380px){
.story-grid{grid-template-columns:1fr}
.trust{gap:1rem}
}
/* ===== Reduced motion ===== */
@media (prefers-reduced-motion:reduce){
*{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important}
html{scroll-behavior:auto}
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2600);
}
/* Wire every element that carries a data-toast attribute */
document.querySelectorAll("[data-toast]").forEach(function (el) {
el.addEventListener("click", function (e) {
var href = el.getAttribute("href");
if (href && href.charAt(0) === "#" && href.length > 1) return; // let in-page anchors scroll
if (href === "#" || el.tagName === "BUTTON") e.preventDefault();
toast(el.getAttribute("data-toast"));
});
});
/* ---------- Mobile nav ---------- */
var navToggle = document.querySelector(".nav-toggle");
var mobileNav = document.getElementById("mobile-nav");
if (navToggle && mobileNav) {
navToggle.addEventListener("click", function () {
var open = navToggle.getAttribute("aria-expanded") === "true";
navToggle.setAttribute("aria-expanded", String(!open));
navToggle.setAttribute("aria-label", open ? "Open menu" : "Close menu");
mobileNav.hidden = open;
});
mobileNav.querySelectorAll("a").forEach(function (a) {
a.addEventListener("click", function () {
navToggle.setAttribute("aria-expanded", "false");
navToggle.setAttribute("aria-label", "Open menu");
mobileNav.hidden = true;
});
});
}
/* ---------- Stories: data-driven, filterable grid ---------- */
var STORIES = [
{ title: "Luna & the Sleepy Moon", author: "by R. Okafor", emoji: "🌙", age: "3+", rating: 5, mood: "cozy", bg: "linear-gradient(160deg,#dfe7ff,#bcd0ff)", tag: "New" },
{ title: "Bramble the Brave Cub", author: "by M. Diaz", emoji: "🐻", age: "5+", rating: 5, mood: "brave", bg: "linear-gradient(160deg,#ffe6cf,#ffc79a)", tag: "Top 10" },
{ title: "The Giggling Goblin", author: "by P. Lindqvist", emoji: "👹", age: "4+", rating: 4, mood: "silly", bg: "linear-gradient(160deg,#d8f6dc,#a9e9b3)", tag: "" },
{ title: "Pip Counts the Stars", author: "by A. Mensah", emoji: "⭐", age: "2+", rating: 5, mood: "curious", bg: "linear-gradient(160deg,#fff0c2,#ffe07a)", tag: "" },
{ title: "Captain Marshmallow", author: "by J. Ueno", emoji: "🚀", age: "5+", rating: 4, mood: "brave", bg: "linear-gradient(160deg,#ffd9e5,#ffb3cf)", tag: "New" },
{ title: "Whiskers Won't Nap", author: "by S. Patel", emoji: "🐱", age: "3+", rating: 5, mood: "cozy", bg: "linear-gradient(160deg,#e6ddff,#c9b6ff)", tag: "" },
{ title: "Snorty the Bubble Pig", author: "by L. Romero", emoji: "🐷", age: "4+", rating: 4, mood: "silly", bg: "linear-gradient(160deg,#ffe0ec,#ffc0d8)", tag: "" },
{ title: "Why Is the Sky Blue?", author: "by D. Kowalski", emoji: "🔭", age: "6+", rating: 5, mood: "curious", bg: "linear-gradient(160deg,#d4f0f4,#a9e3ea)", tag: "Top 10" }
];
var grid = document.getElementById("story-grid");
var emptyMsg = document.getElementById("story-empty");
var chips = Array.prototype.slice.call(document.querySelectorAll(".chip"));
var activeMood = "all";
function stars(n) {
return "★★★★★".slice(0, n) + "☆☆☆☆☆".slice(0, 5 - n);
}
function render(mood) {
if (!grid) return;
grid.innerHTML = "";
var list = STORIES.filter(function (s) {
return mood === "all" || s.mood === mood;
});
if (emptyMsg) emptyMsg.hidden = list.length !== 0;
list.forEach(function (s, i) {
var li = document.createElement("li");
var card = document.createElement("button");
card.type = "button";
card.className = "story-card";
card.style.animationDelay = i * 45 + "ms";
card.setAttribute(
"aria-label",
s.title + ", " + s.author + ", ages " + s.age + ", rated " + s.rating + " of 5 stars"
);
card.innerHTML =
'<div class="story-cover" style="background:' + s.bg + '">' +
(s.tag ? '<span class="badge">' + s.tag + "</span>" : "") +
'<span aria-hidden="true">' + s.emoji + "</span>" +
'<span class="age">' + s.age + "</span>" +
"</div>" +
'<div class="story-meta">' +
"<h3>" + s.title + "</h3>" +
"<p>" + s.author + "</p>" +
'<p class="stars" aria-hidden="true">' + stars(s.rating) + "</p>" +
"</div>";
card.addEventListener("click", function () {
toast('Opening "' + s.title + '" — read-along is just a demo. 📖');
});
li.appendChild(card);
grid.appendChild(li);
});
}
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
chips.forEach(function (c) {
c.classList.remove("is-on");
c.setAttribute("aria-pressed", "false");
});
chip.classList.add("is-on");
chip.setAttribute("aria-pressed", "true");
activeMood = chip.getAttribute("data-mood");
render(activeMood);
});
});
render(activeMood);
/* ---------- Newsletter form ---------- */
var form = document.getElementById("news-form");
var msg = document.getElementById("news-msg");
var input = document.getElementById("news-email");
if (form && msg && input) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var val = input.value.trim();
var ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val);
if (!ok) {
msg.textContent = "Oops — please pop in a valid email. 📨";
msg.classList.add("err");
input.focus();
return;
}
msg.classList.remove("err");
msg.textContent = "Yay! A free story is on its way to " + val + " 🎉";
input.value = "";
toast("Subscribed! Check your inbox for a story. 💛");
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Tinytales — read, listen & play</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700;800&family=Nunito:wght@400;600;700;800&display=swap" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- ===== Header ===== -->
<header class="site-header" role="banner">
<div class="wrap header-inner">
<a class="brand" href="#main" aria-label="Tinytales home">
<span class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 48 48" width="40" height="40" focusable="false">
<rect x="2" y="2" width="44" height="44" rx="13" fill="#ff8a3d"/>
<path d="M14 13h9a5 5 0 0 1 5 5v17a4 4 0 0 0-4-4h-10z" fill="#fff8ef"/>
<path d="M34 13h-9a5 5 0 0 0-5 5v17a4 4 0 0 1 4-4h10z" fill="#ffd23f"/>
<circle cx="24" cy="34" r="2.4" fill="#5ec5d6"/>
</svg>
</span>
<span class="brand-name">Tinytales</span>
</a>
<nav class="main-nav" aria-label="Primary">
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#how">How it works</a></li>
<li><a href="#stories">Stories</a></li>
<li><a href="#plans">Plans</a></li>
</ul>
</nav>
<div class="header-cta">
<button class="btn btn-ghost" type="button" data-toast="Welcome back! Sign-in is just a demo.">Log in</button>
<button class="btn btn-primary" type="button" data-toast="Free trial started — happy reading!">Start free</button>
</div>
<button class="nav-toggle" type="button" aria-expanded="false" aria-controls="mobile-nav" aria-label="Open menu">
<span></span><span></span><span></span>
</button>
</div>
<div class="mobile-nav" id="mobile-nav" hidden>
<a href="#features">Features</a>
<a href="#how">How it works</a>
<a href="#stories">Stories</a>
<a href="#plans">Plans</a>
<button class="btn btn-primary" type="button" data-toast="Free trial started — happy reading!">Start free</button>
</div>
</header>
<main id="main">
<!-- ===== Hero ===== -->
<section class="hero" aria-labelledby="hero-title">
<span class="blob blob-a" aria-hidden="true"></span>
<span class="blob blob-b" aria-hidden="true"></span>
<div class="wrap hero-inner">
<div class="hero-copy">
<p class="eyebrow"><span class="eyebrow-dot" aria-hidden="true"></span> 1,200+ stories for ages 2–9</p>
<h1 id="hero-title">Big adventures for <span class="hl">little readers</span>.</h1>
<p class="lede">Tinytales turns bedtime into an adventure with read-along audio,
playful narration and gentle games — all in one cozy, ad-free app.</p>
<div class="hero-actions">
<a class="store-btn" href="#plans" data-toast="App Store link is just a demo.">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path fill="currentColor" d="M16.5 2.1c.1 1-.3 2-1 2.8-.7.8-1.8 1.4-2.9 1.3-.1-1 .4-2 1-2.7.8-.8 2-1.4 2.9-1.4ZM20 17c-.5 1.2-.8 1.7-1.5 2.7-1 1.5-2.4 3.3-4.1 3.3-1.5 0-1.9-1-4-1s-2.6 1-4.1 1c-1.7 0-3-1.6-4-3.1C-.5 16-1 11.4 1.1 9c1-1.2 2.6-2 4.1-2 1.6 0 2.6 1 4 1 1.3 0 2.1-1 4-1 1.4 0 2.8.7 3.8 2-3.3 1.9-2.8 6.6.9 8Z"/></svg>
<span><small>Download on the</small><strong>App Store</strong></span>
</a>
<a class="store-btn" href="#plans" data-toast="Google Play link is just a demo.">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path fill="currentColor" d="M3.6 2.3 13.3 12 3.6 21.7a1 1 0 0 1-.6-.9V3.2a1 1 0 0 1 .6-.9Zm11 8.5L5.9 2.1l10.4 6-1.7 2.7Zm0 2.4 1.7 2.7-10.4 6 8.7-8.7ZM16 12l3.6-2.1c.9.5.9 1.7 0 2.2L16 14.1 14.3 12 16 12Z"/></svg>
<span><small>Get it on</small><strong>Google Play</strong></span>
</a>
</div>
<ul class="trust" aria-label="Why parents love us">
<li><strong>4.9★</strong><span>App rating</span></li>
<li><strong>0 ads</strong><span>Ever, promise</span></li>
<li><strong>Offline</strong><span>Read anywhere</span></li>
</ul>
</div>
<!-- Flat SVG hero illustration -->
<div class="hero-art" aria-hidden="true">
<svg viewBox="0 0 420 380" class="scene" role="img" aria-label="A child reading a glowing storybook with friendly creatures">
<defs>
<clipPath id="moonClip"><circle cx="330" cy="78" r="42"/></clipPath>
</defs>
<!-- sky panel -->
<rect x="10" y="14" width="400" height="300" rx="30" fill="#dff3f6"/>
<!-- hills -->
<path d="M10 230c70-50 150-40 200 0s150 30 200-10v94a30 30 0 0 1-30 30H40a30 30 0 0 1-30-30Z" fill="#7bd389"/>
<path d="M10 264c60-30 130-20 200 10s160 10 200-20v60a30 30 0 0 1-30 30H40a30 30 0 0 1-30-30Z" fill="#5cb87a"/>
<!-- sun/moon -->
<circle cx="330" cy="78" r="42" fill="#ffd23f"/>
<g clip-path="url(#moonClip)"><circle cx="356" cy="64" r="30" fill="#ffe27a"/></g>
<!-- clouds -->
<g fill="#fff8ef">
<ellipse cx="96" cy="70" rx="34" ry="18"/>
<ellipse cx="120" cy="76" rx="24" ry="14"/>
<ellipse cx="76" cy="78" rx="22" ry="13"/>
</g>
<!-- floating stars -->
<g class="twinkle" fill="#ff6f9c">
<path d="M190 60l4 9 9 1-7 6 2 9-8-5-8 5 2-9-7-6 9-1Z"/>
</g>
<g class="twinkle twinkle-2" fill="#ff8a3d">
<path d="M268 130l3 6 6 1-5 4 1 6-5-3-5 3 1-6-5-4 6-1Z"/>
</g>
<!-- glowing book -->
<g class="book">
<ellipse cx="210" cy="312" rx="120" ry="20" fill="#000" opacity=".08"/>
<path d="M120 300c40-22 90-22 90-22s50 0 90 22c-40-6-70 4-90 14-20-10-50-20-90-14Z" fill="#3a8dde"/>
<path d="M120 300c40-30 90-30 90-30v44s-50 0-90 14Z" fill="#fff8ef"/>
<path d="M300 300c-40-30-90-30-90-30v44s50 0 90 14Z" fill="#ffe8d2"/>
<g stroke="#cfd9e6" stroke-width="3" stroke-linecap="round">
<path d="M140 286h54M140 296h54"/>
</g>
<g stroke="#ffd6b8" stroke-width="3" stroke-linecap="round">
<path d="M226 286h54M226 296h54"/>
</g>
<!-- light beam -->
<path d="M210 268l-46-44h92Z" fill="#ffd23f" opacity=".55"/>
</g>
<!-- child -->
<g class="kid">
<ellipse cx="210" cy="318" rx="40" ry="10" fill="#000" opacity=".06"/>
<rect x="178" y="206" width="64" height="68" rx="24" fill="#ff6f9c"/>
<circle cx="210" cy="186" r="30" fill="#ffce9e"/>
<path d="M180 184a30 30 0 0 1 60 0c0-18-12-30-30-30s-30 12-30 30Z" fill="#5a3b2e"/>
<circle cx="201" cy="188" r="3.2" fill="#2c2350"/>
<circle cx="219" cy="188" r="3.2" fill="#2c2350"/>
<path d="M203 198q7 6 14 0" stroke="#2c2350" stroke-width="3" fill="none" stroke-linecap="round"/>
<circle cx="196" cy="196" r="4" fill="#ff6f9c" opacity=".5"/>
<circle cx="224" cy="196" r="4" fill="#ff6f9c" opacity=".5"/>
</g>
<!-- owl friend -->
<g class="owl">
<ellipse cx="92" cy="206" rx="26" ry="30" fill="#a07cff"/>
<circle cx="83" cy="198" r="11" fill="#fff8ef"/>
<circle cx="101" cy="198" r="11" fill="#fff8ef"/>
<circle cx="83" cy="198" r="5" fill="#2c2350"/>
<circle cx="101" cy="198" r="5" fill="#2c2350"/>
<path d="M86 210l6 6 6-6Z" fill="#ffd23f"/>
<path d="M70 186l8-12 4 14ZM114 186l-8-12-4 14Z" fill="#8a63f0"/>
</g>
</svg>
</div>
</div>
</section>
<!-- ===== Logos / marquee ===== -->
<section class="logos" aria-label="Loved by families everywhere">
<p>Trusted by playful families in</p>
<ul>
<li>🏡 12,400 homes</li><li>🏫 380 classrooms</li><li>📚 1,200+ stories</li><li>🌍 9 languages</li>
</ul>
</section>
<!-- ===== Features ===== -->
<section class="features" id="features" aria-labelledby="features-title">
<div class="wrap">
<header class="section-head">
<p class="kicker">Everything in one app</p>
<h2 id="features-title">Made for tiny hands & big imaginations</h2>
<p class="section-sub">Flat, friendly tools that grow with your reader — from first words to first chapter books.</p>
</header>
<ul class="feature-grid">
<li class="feature-card" style="--c:#ff8a3d;--c2:#ffe0c7">
<span class="feature-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="26" height="26"><path fill="currentColor" d="M4 5a2 2 0 0 1 2-2h5v17l-1-1H6a2 2 0 0 0-2 2V5Zm16 0a2 2 0 0 0-2-2h-5v17l1-1h4a2 2 0 0 1 2 2V5Z"/></svg>
</span>
<h3>Read-along audio</h3>
<p>Words light up as a friendly voice narrates, so new readers can follow every line.</p>
</li>
<li class="feature-card" style="--c:#5ec5d6;--c2:#d4f0f4">
<span class="feature-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="26" height="26"><path fill="currentColor" d="M12 3a4 4 0 0 0-4 4v4a4 4 0 0 0 8 0V7a4 4 0 0 0-4-4Zm-7 8a7 7 0 0 0 6 6.9V21H8v2h8v-2h-3v-3.1A7 7 0 0 0 19 11h-2a5 5 0 0 1-10 0H5Z"/></svg>
</span>
<h3>Talk-to-play</h3>
<p>Kids answer riddles out loud and tap shapes to unlock the next page of the tale.</p>
</li>
<li class="feature-card" style="--c:#7bd389;--c2:#dbf3df">
<span class="feature-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="26" height="26"><path fill="currentColor" d="M12 21s-7.5-4.6-9.7-9.2C.6 8.1 2.6 5 6 5c1.9 0 3.3 1 4 2.1C10.7 6 12.1 5 14 5c3.4 0 5.4 3.1 3.7 6.8C19.5 16.4 12 21 12 21Z"/></svg>
</span>
<h3>Cozy & ad-free</h3>
<p>No ads, no autoplay traps — just a calm shelf of stories you both can trust.</p>
</li>
<li class="feature-card" style="--c:#ff6f9c;--c2:#ffd9e5">
<span class="feature-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="26" height="26"><path fill="currentColor" d="M12 2 3 7v6c0 5 3.8 8.4 9 9 5.2-.6 9-4 9-9V7l-9-5Zm-1 13-3-3 1.4-1.4L11 12.2l4.6-4.6L17 9l-6 6Z"/></svg>
</span>
<h3>Parent controls</h3>
<p>Set reading time, pick age bands and follow daily progress from a simple dashboard.</p>
</li>
<li class="feature-card" style="--c:#a07cff;--c2:#e6ddff">
<span class="feature-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="26" height="26"><path fill="currentColor" d="M5 3h14a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-4l-3 3-3-3H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Zm2 5v2h10V8H7Zm0 4v2h7v-2H7Z"/></svg>
</span>
<h3>9 languages</h3>
<p>Swap a story into another language with one tap to grow little bilingual brains.</p>
</li>
<li class="feature-card" style="--c:#ffd23f;--c2:#fff1c2">
<span class="feature-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="26" height="26"><path fill="currentColor" d="M12 2l2.9 6.3 6.8.7-5.1 4.6 1.4 6.7L12 17.8 6 20.3l1.4-6.7L2.3 9l6.8-.7L12 2Z"/></svg>
</span>
<h3>Sticker rewards</h3>
<p>Finish a tale and earn a shiny sticker — a tiny win that keeps the pages turning.</p>
</li>
</ul>
</div>
</section>
<!-- ===== How it works ===== -->
<section class="how" id="how" aria-labelledby="how-title">
<div class="wrap">
<header class="section-head">
<p class="kicker">Three little steps</p>
<h2 id="how-title">From download to dreamland</h2>
</header>
<ol class="steps">
<li class="step" style="--s:#ff8a3d">
<span class="step-num">1</span>
<span class="step-art" aria-hidden="true">📲</span>
<h3>Get the app</h3>
<p>Install Tinytales free on any phone or tablet — no card, no clutter.</p>
</li>
<li class="step" style="--s:#5ec5d6">
<span class="step-num">2</span>
<span class="step-art" aria-hidden="true">🧒</span>
<h3>Pick a reader</h3>
<p>Add your child's name and age and we'll line up just-right stories.</p>
</li>
<li class="step" style="--s:#7bd389">
<span class="step-num">3</span>
<span class="step-art" aria-hidden="true">✨</span>
<h3>Read & play</h3>
<p>Tap a cover, listen along and collect stickers — story time, sorted.</p>
</li>
</ol>
</div>
</section>
<!-- ===== Stories preview (interactive) ===== -->
<section class="stories" id="stories" aria-labelledby="stories-title">
<div class="wrap">
<header class="section-head">
<p class="kicker">Fresh on the shelf</p>
<h2 id="stories-title">Stories your little one will love</h2>
<p class="section-sub">Pick a mood and we'll spin up the perfect bedtime tale.</p>
</header>
<div class="mood-row" role="group" aria-label="Pick a story mood">
<button class="chip is-on" type="button" data-mood="all" aria-pressed="true">All</button>
<button class="chip" type="button" data-mood="cozy" aria-pressed="false">😴 Cozy</button>
<button class="chip" type="button" data-mood="silly" aria-pressed="false">🤪 Silly</button>
<button class="chip" type="button" data-mood="brave" aria-pressed="false">🦸 Brave</button>
<button class="chip" type="button" data-mood="curious" aria-pressed="false">🔭 Curious</button>
</div>
<ul class="story-grid" id="story-grid"><!-- injected by script.js --></ul>
<p class="story-empty" id="story-empty" hidden>No tales in that mood yet — try another!</p>
</div>
</section>
<!-- ===== Plans / CTA ===== -->
<section class="plans" id="plans" aria-labelledby="plans-title">
<div class="wrap plans-inner">
<div class="plans-copy">
<p class="kicker">Start free, stay cozy</p>
<h2 id="plans-title">One little plan, endless stories</h2>
<p>Try every story free for 14 days. Cancel anytime — no late-night fine print.</p>
<ul class="plan-perks">
<li>Unlimited read-along stories</li>
<li>Up to 4 child profiles</li>
<li>Offline downloads & sleep timer</li>
</ul>
</div>
<div class="price-card">
<p class="price-tag"><span class="amt">$0</span><span class="per">/ 14 days</span></p>
<p class="then">then $6.99 / month</p>
<button class="btn btn-primary btn-block" type="button" data-toast="Free trial started — happy reading!">Start free trial</button>
<button class="btn btn-ghost btn-block" type="button" data-toast="No card needed for the demo. 💛">No card needed</button>
<p class="fine">Join 12,400 cozy families.</p>
</div>
</div>
</section>
</main>
<!-- ===== Footer ===== -->
<footer class="site-footer" role="contentinfo">
<div class="wrap footer-inner">
<div class="footer-brand">
<span class="brand-name">Tinytales</span>
<p>Big adventures for little readers. Made with 💛 for bedtime.</p>
<form class="news" id="news-form" novalidate>
<label for="news-email">Get a free story each week</label>
<div class="news-row">
<input id="news-email" type="email" name="email" placeholder="[email protected]" autocomplete="email" required />
<button class="btn btn-primary" type="submit">Send it</button>
</div>
<p class="news-msg" id="news-msg" role="status" aria-live="polite"></p>
</form>
</div>
<nav class="footer-cols" aria-label="Footer">
<div>
<h3>App</h3>
<a href="#features">Features</a>
<a href="#plans">Plans</a>
<a href="#stories">Stories</a>
</div>
<div>
<h3>Company</h3>
<a href="#" data-toast="Just a demo link.">About</a>
<a href="#" data-toast="Just a demo link.">Careers</a>
<a href="#" data-toast="Just a demo link.">Press</a>
</div>
<div>
<h3>Help</h3>
<a href="#" data-toast="Just a demo link.">For parents</a>
<a href="#" data-toast="Just a demo link.">Privacy</a>
<a href="#" data-toast="Just a demo link.">Contact</a>
</div>
</nav>
</div>
<div class="footer-base">
<p>© 2026 Tinytales — a fictional storybook app.</p>
<p class="footer-emojis" aria-hidden="true">🦊 🦉 🌙 ⭐ 🐻</p>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Modern Flat Landing
A contemporary flat-design marketing page for Tinytales, a fictional read-along app for ages 2–9. The hero leads with a bold headline, an accent-highlighted phrase and a hand-built inline-SVG scene — a glowing storybook, a reading child, a perched owl and twinkling stars — beside app-store and Google Play download buttons plus a row of trust stats. Soft color blobs, rounded everything and a Baloo 2 / Nunito type pairing give it a friendly, geometric feel.
Below the fold, six crisp feature cards carry their own flat SVG icons and accent stripes, a three-step “how it works” walks from download to dreamland, and an interactive stories shelf lets you pick a mood (Cozy, Silly, Brave, Curious). The mood chips re-render a responsive grid of rated cover cards with a gentle pop animation; tapping any cover fires an accessible toast. A pricing card, a validated newsletter form with friendly inline messages, and a colorful gradient footer complete the page.
Everything is self-contained vanilla HTML, CSS and JavaScript — no frameworks, images or CDNs beyond the Google Fonts link. Controls are keyboard reachable with visible focus rings, the mobile menu and live regions are wired for screen readers, animations collapse under prefers-reduced-motion, and the layout reflows cleanly from wide desktop down to roughly 360px.
Illustrative kids’ UI only — fictional stories, characters, and audio.