Storybook — Nursery / Baby Landing
A tender nursery and baby brand landing for the fictional Lulla and Moon, built in soft pastels and cream with ultra-rounded cards and gentle shadows. An airy hero pairs a slowly drifting SVG crib mobile of a moon, cloud and sleepy star with twinkling dots and floating zzz. Bedtime feature cards, a working lullaby player with soft Web Audio chimes, a calm parent testimonial, an easy-read toggle and a quiet email call to action complete the soothing, minimal-motion experience.
MCP
Code
/* ===== Lulla & Moon — Nursery / Baby Landing ===== */
:root {
--bg: #fff8ef;
--bg-2: #fef0f6;
--surface: #ffffff;
--ink: #3a3258;
--ink-soft: #6f678c;
--primary: #ff9eb6; /* soft rose */
--primary-deep: #f47a99;
--secondary: #a8c7ff; /* soft sky */
--accent: #ffd98c; /* warm star */
--lilac: #c9b8ff;
--mint: #bdebd1;
--peach: #ffd2bd;
--r: 26px;
--r-sm: 18px;
--r-pill: 999px;
--shadow-soft: 0 18px 40px -22px rgba(122, 96, 150, 0.45);
--shadow-card: 0 12px 28px -18px rgba(122, 96, 150, 0.5);
--ring: 0 0 0 4px rgba(168, 199, 255, 0.55);
--font-display: "Baloo 2", system-ui, sans-serif;
--font-body: "Nunito", system-ui, -apple-system, sans-serif;
--maxw: 1100px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
font-family: var(--font-body);
background:
radial-gradient(1200px 600px at 85% -10%, var(--bg-2), transparent 60%),
var(--bg);
color: var(--ink);
line-height: 1.55;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
/* Easy-read mode: roomier spacing + heavier body weight */
body.easy-read {
letter-spacing: 0.018em;
word-spacing: 0.09em;
line-height: 1.75;
}
body.easy-read p,
body.easy-read li,
body.easy-read .lede {
font-weight: 600;
}
h1,
h2,
h3 {
font-family: var(--font-display);
line-height: 1.15;
margin: 0;
}
a {
color: inherit;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}
.skip-link {
position: absolute;
left: 12px;
top: -56px;
background: var(--surface);
color: var(--ink);
padding: 10px 16px;
border-radius: var(--r-pill);
box-shadow: var(--shadow-card);
z-index: 50;
transition: top 0.2s ease;
font-weight: 700;
}
.skip-link:focus {
top: 12px;
}
:focus-visible {
outline: none;
box-shadow: var(--ring);
border-radius: var(--r-pill);
}
/* ===== Buttons ===== */
.btn {
--pad-y: 14px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
min-height: 48px;
padding: var(--pad-y) 24px;
border-radius: var(--r-pill);
border: 0;
font-family: var(--font-display);
font-weight: 600;
font-size: 1rem;
cursor: pointer;
text-decoration: none;
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}
.btn:active {
transform: translateY(1px) scale(0.985);
}
.btn--primary {
background: linear-gradient(180deg, var(--primary), var(--primary-deep));
color: #fff;
box-shadow: 0 12px 22px -12px var(--primary-deep);
}
.btn--primary:hover {
transform: translateY(-2px);
box-shadow: 0 16px 26px -12px var(--primary-deep);
}
.btn--soft {
background: var(--surface);
color: var(--ink);
box-shadow: var(--shadow-card);
}
.btn--soft:hover {
transform: translateY(-2px);
}
.btn--ghost {
background: rgba(255, 255, 255, 0.7);
color: var(--ink);
border: 2px solid var(--lilac);
}
.btn--ghost:hover {
background: #fff;
transform: translateY(-2px);
}
.btn--ghost[aria-pressed="true"] {
background: var(--lilac);
color: #fff;
}
.hush-icon {
font-size: 1.15em;
}
/* ===== Header ===== */
.site-header {
max-width: var(--maxw);
margin: 0 auto;
padding: 18px 22px;
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
font-family: var(--font-display);
font-weight: 700;
font-size: 1.25rem;
text-decoration: none;
color: var(--ink);
}
.brand__mark {
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 50%;
background: linear-gradient(160deg, var(--lilac), var(--secondary));
color: var(--accent);
box-shadow: var(--shadow-card);
}
.site-nav {
display: flex;
gap: 6px;
margin-left: auto;
}
.site-nav a {
padding: 10px 16px;
border-radius: var(--r-pill);
text-decoration: none;
color: var(--ink-soft);
font-weight: 700;
transition: background 0.18s ease, color 0.18s ease;
}
.site-nav a:hover {
background: rgba(201, 184, 255, 0.28);
color: var(--ink);
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
}
/* Easy-read toggle */
.toggle {
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 44px;
padding: 6px 14px 6px 8px;
border-radius: var(--r-pill);
border: 2px solid var(--secondary);
background: rgba(255, 255, 255, 0.7);
color: var(--ink);
font-family: var(--font-display);
font-weight: 600;
cursor: pointer;
transition: background 0.18s ease;
}
.toggle__dot {
width: 34px;
height: 20px;
border-radius: var(--r-pill);
background: #e4e9f5;
position: relative;
transition: background 0.2s ease;
}
.toggle__dot::after {
content: "";
position: absolute;
top: 2px;
left: 2px;
width: 16px;
height: 16px;
border-radius: 50%;
background: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: transform 0.2s ease;
}
.toggle[aria-pressed="true"] .toggle__dot {
background: var(--secondary);
}
.toggle[aria-pressed="true"] .toggle__dot::after {
transform: translateX(14px);
}
/* ===== Hero ===== */
.hero {
max-width: var(--maxw);
margin: 18px auto 0;
padding: 28px 22px 64px;
display: grid;
grid-template-columns: 1.05fr 0.95fr;
gap: 42px;
align-items: center;
}
.eyebrow {
display: inline-block;
margin: 0 0 14px;
padding: 7px 16px;
border-radius: var(--r-pill);
background: rgba(255, 217, 140, 0.4);
color: #9a7a2f;
font-weight: 800;
font-size: 0.82rem;
letter-spacing: 0.02em;
text-transform: uppercase;
}
.hero h1 {
font-size: clamp(2.2rem, 5.4vw, 3.5rem);
font-weight: 700;
margin-bottom: 18px;
}
.hl {
color: var(--primary-deep);
position: relative;
white-space: nowrap;
}
.hl::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0.06em;
height: 0.32em;
background: rgba(255, 217, 140, 0.6);
border-radius: var(--r-pill);
z-index: -1;
}
.lede {
font-size: 1.14rem;
color: var(--ink-soft);
max-width: 42ch;
margin: 0 0 26px;
}
.hero__cta {
display: flex;
flex-wrap: wrap;
gap: 14px;
margin-bottom: 28px;
}
.hero__trust {
display: flex;
flex-wrap: wrap;
gap: 26px;
margin: 0;
padding: 0;
list-style: none;
color: var(--ink-soft);
font-size: 0.96rem;
}
.hero__trust strong {
color: var(--ink);
font-family: var(--font-display);
font-size: 1.15rem;
display: block;
}
/* Hero scene */
.hero__scene {
position: relative;
justify-self: center;
width: 100%;
max-width: 420px;
filter: drop-shadow(0 26px 36px rgba(122, 96, 150, 0.28));
}
.scene {
width: 100%;
height: auto;
display: block;
}
.charm {
transform-origin: top center;
animation: drift 7s ease-in-out infinite;
}
.charm--cloud {
animation-duration: 8.5s;
animation-delay: -1.5s;
}
.charm--star {
animation-duration: 6.2s;
animation-delay: -3s;
}
.tw {
animation: twinkle 4s ease-in-out infinite;
}
.tw:nth-child(2n) {
animation-delay: -1.3s;
}
.tw:nth-child(3n) {
animation-delay: -2.4s;
}
.zzz {
position: absolute;
top: 16%;
right: 12%;
font-family: var(--font-display);
font-weight: 700;
color: var(--lilac);
opacity: 0;
animation: float-z 5.5s ease-in-out infinite;
}
.zzz--2 {
right: 8%;
top: 14%;
font-size: 1.4rem;
animation-delay: 1.4s;
}
.zzz--3 {
right: 4%;
top: 12%;
font-size: 1.8rem;
animation-delay: 2.8s;
}
@keyframes drift {
0%, 100% { transform: translateY(0) rotate(-1.5deg); }
50% { transform: translateY(8px) rotate(1.5deg); }
}
@keyframes twinkle {
0%, 100% { opacity: 0.35; }
50% { opacity: 1; }
}
@keyframes float-z {
0% { opacity: 0; transform: translate(0, 0) scale(0.7); }
25% { opacity: 0.9; }
100% { opacity: 0; transform: translate(14px, -34px) scale(1.1); }
}
/* ===== Sections shared ===== */
.section-title {
text-align: center;
font-size: clamp(1.7rem, 3.6vw, 2.4rem);
max-width: 18ch;
margin: 0 auto 8px;
}
.section-sub {
text-align: center;
color: var(--ink-soft);
margin: 0 auto 38px;
max-width: 46ch;
}
/* ===== Features ===== */
.features {
max-width: var(--maxw);
margin: 0 auto;
padding: 40px 22px 24px;
}
.feature-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 22px;
list-style: none;
margin: 0;
padding: 0;
}
.card {
background: var(--surface);
border-radius: var(--r);
padding: 30px 26px;
box-shadow: var(--shadow-card);
border: 1px solid rgba(201, 184, 255, 0.22);
transition: transform 0.22s ease, box-shadow 0.22s ease;
}
.card:hover {
transform: translateY(-6px);
box-shadow: var(--shadow-soft);
}
.card__icon {
display: grid;
place-items: center;
width: 60px;
height: 60px;
border-radius: 20px;
font-size: 1.7rem;
margin-bottom: 16px;
}
.card__icon--peach { background: var(--peach); }
.card__icon--mint { background: var(--mint); }
.card__icon--lilac { background: var(--lilac); }
.card h3 {
font-size: 1.3rem;
margin-bottom: 8px;
}
.card p {
margin: 0;
color: var(--ink-soft);
}
/* ===== Lullaby player ===== */
.lullabies {
max-width: var(--maxw);
margin: 0 auto;
padding: 40px 22px;
}
.lull-card {
background:
linear-gradient(160deg, rgba(201, 184, 255, 0.18), rgba(168, 199, 255, 0.12)),
var(--surface);
border-radius: calc(var(--r) + 6px);
padding: 34px clamp(20px, 4vw, 42px);
box-shadow: var(--shadow-soft);
border: 1px solid rgba(201, 184, 255, 0.3);
}
.lull-card__head h2 {
font-size: clamp(1.5rem, 3vw, 2rem);
margin-bottom: 6px;
}
.lull-card__head p {
color: var(--ink-soft);
margin: 0 0 24px;
}
.lull-list {
list-style: none;
margin: 0 0 22px;
padding: 0;
display: grid;
gap: 12px;
}
.lull {
width: 100%;
display: flex;
align-items: center;
gap: 16px;
padding: 14px 18px;
border-radius: var(--r-sm);
border: 2px solid transparent;
background: rgba(255, 255, 255, 0.78);
cursor: pointer;
text-align: left;
transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.lull:hover {
transform: translateY(-2px);
background: #fff;
}
.lull.is-playing {
border-color: var(--primary);
background: #fff;
}
.lull__art {
display: grid;
place-items: center;
width: 50px;
height: 50px;
border-radius: 16px;
font-size: 1.4rem;
flex: none;
}
.lull__art--1 { background: linear-gradient(160deg, var(--peach), var(--primary)); }
.lull__art--2 { background: linear-gradient(160deg, var(--accent), #ffc46b); }
.lull__art--3 { background: linear-gradient(160deg, var(--lilac), var(--secondary)); }
.lull__text {
display: flex;
flex-direction: column;
}
.lull__name {
font-family: var(--font-display);
font-weight: 600;
font-size: 1.08rem;
}
.lull__meta {
color: var(--ink-soft);
font-size: 0.86rem;
}
.lull__play {
margin-left: auto;
width: 38px;
height: 38px;
border-radius: 50%;
background: var(--bg);
position: relative;
flex: none;
box-shadow: inset 0 0 0 2px rgba(201, 184, 255, 0.5);
}
.lull__play::after {
content: "";
position: absolute;
left: 53%;
top: 50%;
transform: translate(-50%, -50%);
border-style: solid;
border-width: 7px 0 7px 11px;
border-color: transparent transparent transparent var(--primary-deep);
}
.lull.is-playing .lull__play {
background: var(--primary);
box-shadow: inset 0 0 0 2px var(--primary-deep);
}
.lull.is-playing .lull__play::after {
border: none;
width: 4px;
height: 13px;
left: 50%;
background: #fff;
box-shadow: 5px 0 0 #fff;
transform: translate(-65%, -50%);
}
.now-playing {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 18px;
border-radius: var(--r-sm);
background: rgba(255, 255, 255, 0.55);
color: var(--ink-soft);
font-weight: 600;
}
.now-playing__dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: #d8d3e8;
flex: none;
}
.now-playing.is-active .now-playing__dot {
background: var(--primary);
animation: pulse 2.4s ease-in-out infinite;
}
.now-playing.is-active {
color: var(--ink);
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(255, 158, 182, 0.55); }
50% { box-shadow: 0 0 0 8px rgba(255, 158, 182, 0); }
}
/* ===== Testimonial ===== */
.love {
max-width: 760px;
margin: 0 auto;
padding: 50px 22px;
}
.quote {
margin: 0;
background: var(--surface);
border-radius: calc(var(--r) + 4px);
padding: 40px clamp(24px, 5vw, 52px) 32px;
box-shadow: var(--shadow-card);
position: relative;
text-align: center;
border: 1px solid rgba(255, 217, 140, 0.4);
}
.quote__mark {
position: absolute;
top: -22px;
left: 50%;
transform: translateX(-50%);
width: 56px;
height: 56px;
display: grid;
place-items: center;
font-family: var(--font-display);
font-size: 2.6rem;
line-height: 1;
color: #fff;
background: linear-gradient(160deg, var(--primary), var(--primary-deep));
border-radius: 50%;
box-shadow: var(--shadow-card);
padding-top: 12px;
}
.quote blockquote {
margin: 14px 0 26px;
font-family: var(--font-display);
font-weight: 500;
font-size: clamp(1.2rem, 2.6vw, 1.6rem);
color: var(--ink);
}
.quote figcaption {
display: inline-flex;
align-items: center;
gap: 12px;
text-align: left;
}
.quote__avatar {
display: grid;
place-items: center;
width: 48px;
height: 48px;
border-radius: 50%;
background: var(--accent);
font-size: 1.3rem;
}
.quote figcaption strong {
display: block;
font-family: var(--font-display);
}
.quote figcaption small {
color: var(--ink-soft);
}
/* ===== CTA ===== */
.cta {
max-width: var(--maxw);
margin: 0 auto;
padding: 30px 22px 70px;
}
.cta__inner {
background:
radial-gradient(600px 300px at 20% 0%, rgba(255, 217, 140, 0.4), transparent 60%),
linear-gradient(160deg, var(--secondary), var(--lilac));
border-radius: calc(var(--r) + 10px);
padding: clamp(34px, 6vw, 60px);
text-align: center;
color: #fff;
box-shadow: var(--shadow-soft);
}
.cta__inner h2 {
font-size: clamp(1.8rem, 4vw, 2.6rem);
margin-bottom: 8px;
}
.cta__inner > p {
margin: 0 auto 26px;
max-width: 44ch;
font-size: 1.08rem;
color: rgba(255, 255, 255, 0.92);
}
.cta__form {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
max-width: 520px;
margin: 0 auto;
}
.cta__form input {
flex: 1 1 240px;
min-height: 52px;
padding: 0 22px;
border-radius: var(--r-pill);
border: 0;
font-family: var(--font-body);
font-size: 1rem;
background: #fff;
color: var(--ink);
box-shadow: var(--shadow-card);
}
.cta__form input::placeholder {
color: #a59fbb;
}
.cta__form input:focus-visible {
box-shadow: var(--ring);
}
.cta__form input.is-invalid {
box-shadow: 0 0 0 3px rgba(244, 122, 153, 0.9);
}
.cta__error {
flex-basis: 100%;
margin: 4px 0 0;
color: #fff;
font-weight: 700;
background: rgba(244, 122, 153, 0.55);
display: inline-block;
padding: 8px 16px;
border-radius: var(--r-pill);
}
.cta__fine {
margin: 18px 0 0;
font-size: 0.86rem;
color: rgba(255, 255, 255, 0.85);
}
/* ===== Footer ===== */
.site-footer {
max-width: var(--maxw);
margin: 0 auto;
padding: 26px 22px 48px;
text-align: center;
color: var(--ink-soft);
}
.brand--foot {
justify-content: center;
font-size: 1.1rem;
margin: 0 0 6px;
}
.brand--foot .brand__mark {
width: 36px;
height: 36px;
}
.site-footer__note {
margin: 0;
font-size: 0.9rem;
}
/* ===== Toast ===== */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 30px);
background: var(--ink);
color: #fff;
padding: 14px 22px;
border-radius: var(--r-pill);
font-weight: 700;
font-family: var(--font-display);
box-shadow: var(--shadow-soft);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.25s ease;
z-index: 60;
max-width: calc(100vw - 40px);
text-align: center;
}
.toast.show {
opacity: 1;
transform: translate(-50%, 0);
}
/* ===== Responsive ===== */
@media (max-width: 860px) {
.hero {
grid-template-columns: 1fr;
text-align: center;
padding-bottom: 40px;
}
.hero__copy {
order: 2;
}
.hero__scene {
order: 1;
max-width: 320px;
}
.lede {
margin-left: auto;
margin-right: auto;
}
.hero__cta,
.hero__trust {
justify-content: center;
}
.feature-grid {
grid-template-columns: 1fr;
max-width: 460px;
margin: 0 auto;
}
.site-nav {
display: none;
}
}
@media (max-width: 520px) {
.site-header {
justify-content: space-between;
}
.header-actions {
width: 100%;
justify-content: space-between;
}
.toggle__label {
display: none;
}
.toggle {
padding: 6px;
}
.lull {
padding: 12px;
gap: 12px;
}
}
/* ===== Reduced motion ===== */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
.charm,
.tw,
.zzz,
.now-playing.is-active .now-playing__dot {
animation: none !important;
}
.btn,
.card,
.lull,
.toast {
transition: none !important;
}
.zzz {
opacity: 0;
}
}/* ===== Lulla & Moon — nursery landing interactions ===== */
(function () {
"use strict";
var prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2600);
}
/* ---------- Gentle Web Audio chime ---------- */
var audioCtx = null;
var NOTES = { C: 261.63, E: 329.63, G: 392.0, A: 440.0 };
function ensureAudio() {
if (audioCtx) return audioCtx;
var AC = window.AudioContext || window.webkitAudioContext;
if (!AC) return null;
audioCtx = new AC();
return audioCtx;
}
function playChime(noteName) {
var ctx = ensureAudio();
if (!ctx) return;
if (ctx.state === "suspended") ctx.resume();
var base = NOTES[noteName] || NOTES.C;
// soft two-note bell: root + a fifth above, very quiet
[base, base * 1.5].forEach(function (freq, i) {
var osc = ctx.createOscillator();
var gain = ctx.createGain();
osc.type = "sine";
osc.frequency.value = freq;
var t0 = ctx.currentTime + i * 0.12;
gain.gain.setValueAtTime(0.0001, t0);
gain.gain.exponentialRampToValueAtTime(0.07, t0 + 0.06);
gain.gain.exponentialRampToValueAtTime(0.0001, t0 + 1.4);
osc.connect(gain).connect(ctx.destination);
osc.start(t0);
osc.stop(t0 + 1.5);
});
}
/* ---------- Easy-read toggle ---------- */
var calmToggle = document.getElementById("calmToggle");
if (calmToggle) {
calmToggle.addEventListener("click", function () {
var on = document.body.classList.toggle("easy-read");
calmToggle.setAttribute("aria-pressed", String(on));
toast(on ? "Easy-read on — roomier text" : "Easy-read off");
});
}
/* ---------- Hero hush button ---------- */
var hushBtn = document.getElementById("hushBtn");
var hushLabel = document.getElementById("hushLabel");
if (hushBtn) {
hushBtn.addEventListener("click", function () {
var pressed = hushBtn.getAttribute("aria-pressed") === "true";
if (pressed) {
hushBtn.setAttribute("aria-pressed", "false");
if (hushLabel) hushLabel.textContent = "Play a hush";
stopAll();
toast("Shh… all quiet now");
} else {
hushBtn.setAttribute("aria-pressed", "true");
if (hushLabel) hushLabel.textContent = "Quiet please";
playChime("A");
setNowPlaying("Gentle hush — soft white noise drifting");
toast("A soft hush is drifting in");
}
});
}
/* ---------- Lullaby player ---------- */
var lullButtons = Array.prototype.slice.call(
document.querySelectorAll(".lull")
);
var nowPlaying = document.getElementById("nowPlaying");
var nowPlayingText = document.getElementById("nowPlayingText");
var currentBtn = null;
function setNowPlaying(text) {
if (!nowPlaying || !nowPlayingText) return;
nowPlayingText.textContent = text;
nowPlaying.classList.add("is-active");
}
function clearNowPlaying() {
if (!nowPlaying || !nowPlayingText) return;
nowPlayingText.textContent = "Nothing playing — choose a lullaby above.";
nowPlaying.classList.remove("is-active");
}
function stopAll() {
lullButtons.forEach(function (b) {
b.classList.remove("is-playing");
});
currentBtn = null;
clearNowPlaying();
}
lullButtons.forEach(function (btn) {
btn.addEventListener("click", function () {
var nameEl = btn.querySelector(".lull__name");
var name = nameEl ? nameEl.textContent.trim() : "Lullaby";
if (currentBtn === btn) {
// toggle off
btn.classList.remove("is-playing");
currentBtn = null;
clearNowPlaying();
toast("Paused " + name);
return;
}
// switch to this one
lullButtons.forEach(function (b) {
b.classList.remove("is-playing");
});
btn.classList.add("is-playing");
currentBtn = btn;
playChime(btn.getAttribute("data-note"));
var len = btn.getAttribute("data-len") || "";
setNowPlaying("Now playing · " + name + (len ? " · " + len : ""));
toast("Drifting into “" + name + "”");
if (!prefersReducedMotion) {
btn.animate(
[
{ transform: "scale(1)" },
{ transform: "scale(1.015)" },
{ transform: "scale(1)" },
],
{ duration: 320, easing: "ease-out" }
);
}
});
});
/* ---------- Signup form ---------- */
var form = document.getElementById("signupForm");
var emailInput = document.getElementById("email");
var emailError = document.getElementById("emailError");
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function showError(show) {
if (!emailError || !emailInput) return;
emailError.hidden = !show;
emailInput.classList.toggle("is-invalid", show);
emailInput.setAttribute("aria-invalid", show ? "true" : "false");
}
if (form && emailInput) {
emailInput.addEventListener("input", function () {
if (!emailError.hidden && EMAIL_RE.test(emailInput.value.trim())) {
showError(false);
}
});
form.addEventListener("submit", function (e) {
e.preventDefault();
var value = emailInput.value.trim();
if (!EMAIL_RE.test(value)) {
showError(true);
emailInput.focus();
return;
}
showError(false);
form.reset();
toast("Your invite is on its way — sweet dreams ✨");
});
}
/* ---------- Soft reveal on scroll ---------- */
if ("IntersectionObserver" in window && !prefersReducedMotion) {
var revealEls = document.querySelectorAll(".card, .quote, .lull-card");
revealEls.forEach(function (el) {
el.style.opacity = "0";
el.style.transform = "translateY(18px)";
el.style.transition = "opacity .6s ease, transform .6s ease";
});
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.style.opacity = "1";
entry.target.style.transform = "none";
io.unobserve(entry.target);
}
});
},
{ threshold: 0.16 }
);
revealEls.forEach(function (el) {
io.observe(el);
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Lulla & Moon — Gentle nursery for sleepy little ones</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700&family=Nunito:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Skip link -->
<a class="skip-link" href="#main">Skip to content</a>
<!-- ===== Header ===== -->
<header class="site-header" role="banner">
<a class="brand" href="#top" aria-label="Lulla and Moon, home">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 32 32" width="32" height="32">
<path d="M22 6a11 11 0 1 0 4 12 9 9 0 0 1-4-12Z" fill="currentColor" />
<circle cx="9" cy="10" r="1.4" fill="#fff" />
<circle cx="13" cy="6" r="1" fill="#fff" />
</svg>
</span>
<span class="brand__name">Lulla & Moon</span>
</a>
<nav class="site-nav" aria-label="Primary">
<a href="#features">Bedtime</a>
<a href="#lullabies">Lullabies</a>
<a href="#love">Parents</a>
</nav>
<div class="header-actions">
<button class="toggle" id="calmToggle" type="button" aria-pressed="false">
<span class="toggle__dot" aria-hidden="true"></span>
<span class="toggle__label">Easy-read</span>
</button>
<a class="btn btn--soft" href="#cta">Start free</a>
</div>
</header>
<main id="main">
<!-- ===== Hero ===== -->
<section class="hero" id="top" aria-labelledby="hero-title">
<div class="hero__copy">
<p class="eyebrow">For 0–3 year olds • sleep without tears</p>
<h1 id="hero-title">
Soft stories & slow lullabies for
<span class="hl">sleepy little ones</span>
</h1>
<p class="lede">
Lulla & Moon wraps bedtime in gentle voices, dimming colours and
drifting melodies — so the whole house can settle down, sweetly.
</p>
<div class="hero__cta">
<a class="btn btn--primary" href="#cta">Begin the wind-down</a>
<button class="btn btn--ghost" id="hushBtn" type="button" aria-pressed="false">
<span class="hush-icon" aria-hidden="true">♪</span>
<span id="hushLabel">Play a hush</span>
</button>
</div>
<ul class="hero__trust" aria-label="What's inside">
<li><strong>40+</strong> bedtime tales</li>
<li><strong>Slow</strong> auto-dim screen</li>
<li><strong>No</strong> ads, ever</li>
</ul>
</div>
<!-- Soft mobile / stars scene -->
<div class="hero__scene" role="img" aria-label="A gentle crib mobile of a moon, a cloud and a sleepy star drifting beneath a starry sky">
<svg class="scene" viewBox="0 0 360 360" aria-hidden="true">
<defs>
<radialGradient id="sky" cx="50%" cy="30%" r="80%">
<stop offset="0%" stop-color="#fef0f6" />
<stop offset="100%" stop-color="#e9e6ff" />
</radialGradient>
<linearGradient id="moon" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#fff4d6" />
<stop offset="100%" stop-color="#ffd98c" />
</linearGradient>
<linearGradient id="cloud" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="100%" stop-color="#eaf3ff" />
</linearGradient>
</defs>
<circle cx="180" cy="180" r="176" fill="url(#sky)" />
<!-- twinkles -->
<g class="twinkles" fill="#ffd98c">
<circle class="tw" cx="64" cy="70" r="2.4" />
<circle class="tw" cx="300" cy="92" r="3" />
<circle class="tw" cx="120" cy="40" r="1.8" />
<circle class="tw" cx="262" cy="44" r="2.2" />
<circle class="tw" cx="40" cy="160" r="2" />
<circle class="tw" cx="318" cy="200" r="2.6" />
</g>
<!-- mobile bar -->
<rect x="70" y="86" width="220" height="9" rx="4.5" fill="#c9b8ff" />
<circle cx="180" cy="90" r="6" fill="#b3a0f5" />
<!-- moon charm -->
<g class="charm charm--moon">
<line x1="118" y1="95" x2="118" y2="138" stroke="#cdbcff" stroke-width="3" />
<path d="M134 158a22 22 0 1 1 7-24 16 16 0 0 0-7 24Z" fill="url(#moon)" />
<circle cx="120" cy="150" r="2" fill="#f3c879" />
</g>
<!-- cloud charm -->
<g class="charm charm--cloud">
<line x1="180" y1="95" x2="180" y2="150" stroke="#cdbcff" stroke-width="3" />
<path d="M160 176c-9 0-16-7-16-15s7-15 16-15a18 18 0 0 1 34-3 13 13 0 0 1 4 26Z" fill="url(#cloud)" stroke="#dfe9ff" stroke-width="2" />
<circle cx="170" cy="170" r="1.8" fill="#a9b8d6" />
<circle cx="190" cy="170" r="1.8" fill="#a9b8d6" />
<path d="M174 178q6 5 12 0" fill="none" stroke="#a9b8d6" stroke-width="2" stroke-linecap="round" />
</g>
<!-- star charm -->
<g class="charm charm--star">
<line x1="242" y1="95" x2="242" y2="132" stroke="#cdbcff" stroke-width="3" />
<path d="M242 134l6 13 14 1-10 9 3 14-13-7-13 7 3-14-10-9 14-1Z" fill="#ffd98c" stroke="#f3c879" stroke-width="1.5" />
<path d="M238 150q4 3 8 0" fill="none" stroke="#caa55a" stroke-width="1.6" stroke-linecap="round" />
</g>
</svg>
<span class="zzz" aria-hidden="true">z</span>
<span class="zzz zzz--2" aria-hidden="true">z</span>
<span class="zzz zzz--3" aria-hidden="true">z</span>
</div>
</section>
<!-- ===== Feature cards ===== -->
<section class="features" id="features" aria-labelledby="feat-title">
<h2 id="feat-title" class="section-title">A calmer bedtime, step by gentle step</h2>
<p class="section-sub">Three soft moments that ease your little one toward sleep.</p>
<ul class="feature-grid" role="list">
<li class="card">
<span class="card__icon card__icon--peach" aria-hidden="true">🌕</span>
<h3>Wind-down stories</h3>
<p>Short, soothing tales read in a low, slow voice — each one fades to quiet on its own.</p>
</li>
<li class="card">
<span class="card__icon card__icon--mint" aria-hidden="true">🎵</span>
<h3>Drifting lullabies</h3>
<p>Looping melodies that grow softer over twenty minutes, then settle into gentle silence.</p>
</li>
<li class="card">
<span class="card__icon card__icon--lilac" aria-hidden="true">🌙</span>
<h3>Auto-dim screen</h3>
<p>The nightlight warms and darkens with the room, so no bright glow disturbs sleepy eyes.</p>
</li>
</ul>
</section>
<!-- ===== Lullaby player ===== -->
<section class="lullabies" id="lullabies" aria-labelledby="lull-title">
<div class="lull-card">
<div class="lull-card__head">
<h2 id="lull-title">Tonight’s gentle playlist</h2>
<p>Tap a lullaby to set the mood. A soft tone marks each one — volume stays low.</p>
</div>
<ul class="lull-list" role="list" id="lullList">
<li>
<button class="lull" type="button" data-note="C" data-len="3 min">
<span class="lull__art lull__art--1" aria-hidden="true">♪</span>
<span class="lull__text">
<span class="lull__name">Hush, Little Cloud</span>
<span class="lull__meta">Drift • 3 min</span>
</span>
<span class="lull__play" aria-hidden="true"></span>
</button>
</li>
<li>
<button class="lull" type="button" data-note="E" data-len="4 min">
<span class="lull__art lull__art--2" aria-hidden="true">⭐</span>
<span class="lull__text">
<span class="lull__name">Twinkle, Slow and Low</span>
<span class="lull__meta">Drift • 4 min</span>
</span>
<span class="lull__play" aria-hidden="true"></span>
</button>
</li>
<li>
<button class="lull" type="button" data-note="G" data-len="5 min">
<span class="lull__art lull__art--3" aria-hidden="true">🌙</span>
<span class="lull__text">
<span class="lull__name">Moonbeam Cradle</span>
<span class="lull__meta">Drift • 5 min</span>
</span>
<span class="lull__play" aria-hidden="true"></span>
</button>
</li>
</ul>
<div class="now-playing" id="nowPlaying" aria-live="polite">
<span class="now-playing__dot" aria-hidden="true"></span>
<span id="nowPlayingText">Nothing playing — choose a lullaby above.</span>
</div>
</div>
</section>
<!-- ===== Testimonial ===== -->
<section class="love" id="love" aria-labelledby="love-title">
<figure class="quote">
<span class="quote__mark" aria-hidden="true">“</span>
<blockquote id="love-title">
The auto-dim and the soft voice changed our nights. Mara drifts off
before the second story ends — and so do we, honestly.
</blockquote>
<figcaption>
<span class="quote__avatar" aria-hidden="true">🎉</span>
<span>
<strong>Priya & Sam</strong>
<small>Parents of Mara, 14 months</small>
</span>
</figcaption>
</figure>
</section>
<!-- ===== CTA / signup ===== -->
<section class="cta" id="cta" aria-labelledby="cta-title">
<div class="cta__inner">
<h2 id="cta-title">Tuck in tonight, on us</h2>
<p>Two soothing weeks, free. No card, no ads — just quieter evenings.</p>
<form class="cta__form" id="signupForm" novalidate>
<label class="sr-only" for="email">Your email</label>
<input
id="email"
name="email"
type="email"
inputmode="email"
autocomplete="email"
placeholder="you@home.com"
required
/>
<button class="btn btn--primary" type="submit">Send my invite</button>
<p class="cta__error" id="emailError" role="alert" hidden>
Please enter a valid email so we can send your invite.
</p>
</form>
<p class="cta__fine">We send one gentle email. Unsubscribe anytime.</p>
</div>
</section>
</main>
<footer class="site-footer" role="contentinfo">
<p class="brand brand--foot">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 32 32" width="24" height="24">
<path d="M22 6a11 11 0 1 0 4 12 9 9 0 0 1-4-12Z" fill="currentColor" />
</svg>
</span>
Lulla & Moon
</p>
<p class="site-footer__note">A fictional nursery brand for demo purposes. Sweet dreams.</p>
</footer>
<!-- Toast -->
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Nursery / Baby Landing
A landing page for Lulla & Moon, a fictional nursery brand for babies and toddlers. The mood is soft and soothing: a cream-and-pastel palette, ultra-rounded cards, pill buttons and gentle shadows, with lots of whitespace and only the slowest, most tender motion. The airy hero is built around an inline SVG crib mobile — a moon, a cloud and a sleepy star hanging from a bar — that drifts and sways above twinkling stars, with floating “zzz” rising into a soft pastel sky.
Below the hero, three bedtime feature cards explain the wind-down ritual, and a quiet email call-to-action invites families to a free two-week trial. The standout interaction is the lullaby player: tapping any track marks it as playing, updates a live “now playing” status, and rings a soft two-note Web Audio chime — no audio files, no external libraries. A hero “play a hush” button toggles a calming tone, an easy-read toggle opens up letter spacing and body weight for tired eyes, and the signup form validates the email inline before confirming with a gentle toast.
Everything is vanilla HTML, CSS and JavaScript with a single Google Fonts link (Baloo 2 + Nunito),
fully keyboard accessible with visible focus rings, responsive down to about 360px, and respectful
of prefers-reduced-motion (all drifting and twinkling stops).
Illustrative kids’ UI only — fictional stories, characters, and audio.