Skip to content
Start free — No card required
Brand & Site Kit · Version 2.1 · Pinwheel identity

Have fun teaching,
on brand every time.

A warm, playful design system for Have Fun Teaching — built for the pre‑K–6th grade teachers and homeschool moms (women 20–45) who use us every day. Built around the new pinwheel brandmark: four sails, four subjects, one motion. Every color in this kit is sampled straight out of the logo.

🎨 Warm, playful, trustworthy 🍩 Pill‑first UI ♿ WCAG‑AA 2M+ teachers & families
Assets & logos
00 — Who we design for

Designed for the teacher who does it all

Our audience is women aged 20–45 who teach little kids — Pre‑K through 6th grade: classroom teachers and homeschool moms, often juggling many students, lesson plans and limited time. She wants resources that feel warm, joyful and reassuring — colorful enough to delight her kids, simple enough to use at 9pm. V2.1 leans into rounded shapes, warm cream, generous whitespace, and one obvious next step on every screen. Trust cues (2M+ teachers, 20+ years, free samples) sit on the call‑to‑action, not near it — see 05b — Conversion patterns, which is where this audience actually gets served.

Pre‑K–6th teachers Homeschool moms Women 20–45 Time‑poor, heart‑rich
00b — The grade-span system

How the brand grows up

There is one brand, not three. But the same ingredients — color, shape, type, tone — are dialed by grade band. Younger runs warmer, rounder and more colorful; older runs cooler, tighter and more editorial. The house default is the Elementary setting — warm, rounded and friendly — because that’s where most of what we make lives (Pre-K–5th). Push cooler and tighter only as the grades climb.

Elementary School House default
Pre-K – 5th
Letter B Tracing Fun!
Print it
Color: full sail brights
Shape: round, pill buttons
Type: Fredoka leads
Voice: playful, friendly
Middle School
6th – 8th
Cell Structure Lab & Worksheet
Add to cart
Color: navy-forward, thin accents
Shape: 8px, tags stay pills
Type: Source Serif headlines
Voice: plain, respectful
High School
9th – 12th
Argumentative Essay Rubric
Add to cart
Color: navy / ink / cream
Shape: 6px, minimal
Type: Source Serif, tight
CTA stays pink — every band
Warmer · more color · rounderCooler · less color · tighter
The mark never changes. The pinwheel, wordmark and favicon are identical in every band — that’s what holds a Pre-K printable and an AP study guide together as one brand. Only the world around the logo shifts.
Color intensity is the loudest dial. Elementary leans on the bright sails as fields; High School uses the exact same hexes as hairline accents on navy and cream. Same palette, opposite volume.
Shape carries age. Full pills read youngest. As the grade climbs, the corner radius tightens from 999px pills to a calm 6px — soft, but not bouncy.
Type does the quiet work. Fredoka signals play, so it recedes as grades rise; Source Serif 4 signals rigor, so it leads. Both stay in every band — the ratio is what moves.
02 — Color

Color system

Every brand color is now sampled straight out of the logo. Navy #1B437D is the anchor (it’s the wordmark). The four pinwheel sails — yellow, green, blue, pink — are the whole accent system and map one‑to‑one onto the four core subjects. Warm cream neutrals keep it friendly, never clinical. Orange is the one extended color: it isn’t in the logo, it exists only to make the primary CTA the loudest thing on the page. Click any swatch to copy its hex.

Anchor — navy & blue
Navy 900
#12305A
Footers, hero depth.
Navy · PRIMARY
#1B437D
The wordmark. Headings, nav, bands.
Blue · sail
#0758A1
Links, buttons, math / STEM.
Blue 400
#5A93DA
Focus rings, interactive.
Blue 100
#D5E4F5
Tints, icon tiles.
Blue 50
#EFF4FB
Cool section wash.
The four sails (accents & curriculum categories)
Yellow · sail
#FBB915
Writing / phonics. Badges, stars (dark text).
Green · sail
#34B26A
Reading. Success, “free” tags.
Blue · sail
#0758A1
Math / STEM. Links.
Coral · display
#F26E5F
Ink text only. White fails at 2.9 : 1.
Pink · sail
#F0619E
Science & songs. Signature accent.
Pink 600
#D64C86
Pink hover / active.
Extended surfaces — not in the logo, all white‑text safe
CTA Pink · THE button
#D6337B
The one big button. White text, always.
CTA Pink 600
#B62A67
CTA hover / active.
Purple
#7A3FA8
Music & sound — Songs. Playful, never a warning.
Purple 700
#633389
Purple hover / active.
Red
#CC2E2E
Watch & play — Videos. Not an error color.
Red 700
#A82525
Red hover / active.
Orange
#D2470B
Summer School, urgency, warning. White text.
Orange 600
#B03C09
Orange hover / active.
Green 600
#2A9358
Green hover / active.

Red and purple are full brand colors, not one‑offs. Purple #7A3FA8 (6.8 : 1 on white) is the music & sound family — it lets Songs be joyful again instead of the bronze compromise. Red #CC2E2E (5.3 : 1) is watch & playVideos. Two rules keep them honest: red is never the error color (error stays the softer #C9584E, and errors are always paired with an icon + text), and neither one ever becomes a CTA — pink is the only button.

Coral & Orange — one color, two weights
Coral · display
Ink text on coral
5.6 : 1 ✓ AA

#F26E5F — tints, illustration, section washes, badges and flags with dark text. It is the warm, friendly, not-burnt face of the brand.

✕ Never white text. 2.9 : 1 — fails AA text (4.5) and the 3.0 floor for large text and UI. Not a button. Not a tag.

Orange · surface
White text on orange
4.5 : 1 ✓ AA

#D2470B — buttons, tags, bands, the Summer School format, warning states. Hover #B03C09 (6.0 : 1).

This is the brightest orange that exists at 4.5 : 1. Anything more vivid cannot carry white text — and anything darker stops being orange and turns brown. It sits exactly on the wall.

Why not one color? Because white text and “bright orange” are mutually exclusive — the same wall that made yellow impossible. Rather than compromise the color or break the contrast rule, we keep both: coral is the look, orange is the surface. Same hue family, so they read as one brand color at two weights. Retired in the process: burnt orange #B85C10 (brown, not orange) and #C0410E (5° from the new orange — indistinguishable).
Clean neutrals & backgrounds
Ink
#231F20
Body & headings text.
Slate
#5A6B7D
Secondary text.
Muted
#93A0AD
Meta, captions.
Cream 50
#FFFCF6
Card / stage background.
Cream 100
#FBF5EC
Soft page background.
Cream 200
#EEE6DA
Borders, section fills.
Semantic (feedback states)
Success
#2A9358
Saved, confirmed. (Sail green, darkened for AA.)
Warning
#FBB915
Attention needed (dark text).
Error
#C9584E
Problems, required fields.
Info
#0758A1
Tips & neutral notices. (Sail blue.)
02b — Category Product ID Tags

Category Product ID Tags

The little uppercase pill on every product card — Worksheet, Video, Song — is the fastest way a teacher scans /resources/. It’s also the card’s top border and its grade label, so each format owns one color end to end.

This is the canonical set. Every one of the 17 formats in the live Format filter owns exactly one color — no defaults, no fallbacks, no two formats sharing a hue. The colors sit in seven families, so she learns six or seven ideas, not seventeen. Click any hex to copy.

One rule: white text, always. Every tag on this page is dark enough to carry white at 11px — minimum 4.5 : 1, no exceptions, no per‑tag overrides. That’s what makes the system predictable: a new format needs one color, not a color and a text decision.
Media has its own two colors. Songs is brand purple #7A3FA8 (6.8 : 1) and Videos is brand red #CC2E2E (5.3 : 1). Sound and screen are the two things on this site she plays rather than prints, and they are the only purple and the only red on the grid — spotted in a single pass. Burnt orange stays warm-adjacent for Summer School, the one format that means “hurry.”
Red is a category color, not an alarm. Error stays the softer #C9584E and never appears as a tag; red never appears as a message. Neither purple nor red is ever a button — pink is the only CTA on the site.
Green family — printable practice
The green sail. What she prints and hands out.
FormatTagHexWhite text
WorksheetsWorksheet#1F85514.6 : 1 ✓
Graphic OrganizersOrganizer#0F7A635.3 : 1 ✓
Blue family — check & screen
The blue sail. Measuring learning, and anything that lives on a device.
FormatTagHexWhite text
AssessmentsAssessment#0758A17.2 : 1 ✓
Digital LearningDigital#2A72B55.0 : 1 ✓
Google ClassroomClassroom#1D6A945.9 : 1 ✓
Navy family — the big purchases
The wordmark navy. Substantial, multi-resource buys — this is where the money is, so it looks like it.
FormatTagHexWhite text
WorkbooksWorkbook#1B437D9.8 : 1 ✓
CurriculumCurriculum#12305A13.2 : 1 ✓
BundlesBundle#3F6FB55.1 : 1 ✓
Pink family — hands-on & quick
The pink sail, deepened until white text holds. The fun stuff — and the loudest tag on the grid.
FormatTagHexWhite text
ActivitiesActivity#D6337B4.6 : 1 ✓
Micro LessonsMicro Lesson#9E3C746.3 : 1 ✓
Purple family — music & sound
Brand purple. Anything she plays out loud. Purple is new to the brand and owns this family alone — nothing else on the grid is purple, so a song is spotted in one pass.
FormatTagHexWhite text
SongsSong#7A3FA86.8 : 1 ✓
Warm family — watch & hurry
Brand red for screen media; burnt orange for the one format that means “the clock is ticking.”
FormatTagHexWhite text
VideosVideo#CC2E2E5.3 : 1 ✓
Summer SchoolSummer#D2470B5.3 : 1 ✓
Slate family — utility & planning
Deliberately quiet. These are tools, not treats — they shouldn’t out-shout a song or an activity.
FormatTagHexWhite text
Flash CardsFlash Card#5A6B7D5.5 : 1 ✓
Lesson PlansLesson Plan#3E4C5A8.8 : 1 ✓
Common Core LessonsCommon Core#6174884.8 : 1 ✓
Relief PacksRelief Pack#7A64595.5 : 1 ✓
One color, three places — the pill background, the card’s 3px top border, and the grade label above the title all use the same --accent. Set it once per category.
Every format has a tag now — nothing falls back to a default. New format? Give it a color inside one of the seven families, dark enough for white text. Never a brand-new hue.
Six families, not seventeen colors — she isn’t learning a 17-color legend. She’s learning “green = printable, navy = the big one, pink = fun.” The hue does the work; the label does the rest.

The tags on real cards live in one place now. This section defines the seventeen colours; 05d — HFT Search and Filter is where they get used, on the grid and list cards a teacher actually meets.

CSS — drop-in replacement
Resource card + category tag CSS307 lines · click to expand
/* =========================================================================
   HFT Resource Card + Category ID Tags — Brand Kit V2.1 (Pinwheel)
   Card anatomy is unchanged from the live /resources/ card:
   full-bleed header band > thumbnail > title > price > full-width button.
   Only the colors, fonts and button are new.
   RULE: white text on every band and every button. No exceptions.
   ========================================================================= */
:root{
  /* Category accents — all clear 4.5:1 against #fff at 11px */
  --cat-worksheets:            #1F8551;
  --cat-graphic-organizers:    #0F7A63;
  --cat-assessments:           #0758A1;
  --cat-digital-learning:      #2A72B5;
  --cat-google-classroom:      #1D6A94;
  --cat-workbooks:             #1B437D;
  --cat-curriculum:            #12305A;
  --cat-bundles:               #3F6FB5;
  --cat-activities:            #D6337B;
  --cat-micro-lessons:         #9E3C74;
  --cat-songs:                 #7A3FA8;
  --cat-videos:                #CC2E2E;
  --cat-summer-school:         #D2470B;
  --cat-flash-cards:           #5A6B7D;
  --cat-lesson-plans:          #3E4C5A;
  --cat-common-core-lessons:   #617488;
  --cat-relief-packs:          #7A6459;

  /* Card + CTA */
  --cta:#D6337B; --cta-hover:#B62A67;
  --navy:#1B437D; --ink:#231F20; --cream-50:#FFFCF6; --cream-200:#EEE6DA;
  --font-display:'Fredoka',sans-serif;
  --font-body:'Nunito Sans',sans-serif;
  --font-serif:'Source Serif 4',Georgia,serif;
}

/* ---- Per-category: one variable + one label ---- */
li.product.product_cat-worksheets          { --accent:var(--cat-worksheets); }
li.product.product_cat-worksheets           a.woocommerce-loop-product__link::before{ content:"Worksheet"; }
li.product.product_cat-graphic-organizers  { --accent:var(--cat-graphic-organizers); }
li.product.product_cat-graphic-organizers   a.woocommerce-loop-product__link::before{ content:"Organizer"; }
li.product.product_cat-assessments         { --accent:var(--cat-assessments); }
li.product.product_cat-assessments          a.woocommerce-loop-product__link::before{ content:"Assessment"; }
li.product.product_cat-digital-learning    { --accent:var(--cat-digital-learning); }
li.product.product_cat-digital-learning     a.woocommerce-loop-product__link::before{ content:"Digital"; }
li.product.product_cat-google-classroom    { --accent:var(--cat-google-classroom); }
li.product.product_cat-google-classroom     a.woocommerce-loop-product__link::before{ content:"Classroom"; }
li.product.product_cat-workbooks           { --accent:var(--cat-workbooks); }
li.product.product_cat-workbooks            a.woocommerce-loop-product__link::before{ content:"Workbook"; }
li.product.product_cat-curriculum          { --accent:var(--cat-curriculum); }
li.product.product_cat-curriculum           a.woocommerce-loop-product__link::before{ content:"Curriculum"; }
li.product.product_cat-bundles             { --accent:var(--cat-bundles); }
li.product.product_cat-bundles              a.woocommerce-loop-product__link::before{ content:"Bundle"; }
li.product.product_cat-activities          { --accent:var(--cat-activities); }
li.product.product_cat-activities           a.woocommerce-loop-product__link::before{ content:"Activity"; }
li.product.product_cat-micro-lessons       { --accent:var(--cat-micro-lessons); }
li.product.product_cat-micro-lessons        a.woocommerce-loop-product__link::before{ content:"Micro Lesson"; }
li.product.product_cat-songs               { --accent:var(--cat-songs); }
li.product.product_cat-songs                a.woocommerce-loop-product__link::before{ content:"Song"; }
li.product.product_cat-videos              { --accent:var(--cat-videos); }
li.product.product_cat-videos               a.woocommerce-loop-product__link::before{ content:"Video"; }
li.product.product_cat-summer-school       { --accent:var(--cat-summer-school); }
li.product.product_cat-summer-school        a.woocommerce-loop-product__link::before{ content:"Summer"; }
li.product.product_cat-flash-cards         { --accent:var(--cat-flash-cards); }
li.product.product_cat-flash-cards          a.woocommerce-loop-product__link::before{ content:"Flash Card"; }
li.product.product_cat-lesson-plans        { --accent:var(--cat-lesson-plans); }
li.product.product_cat-lesson-plans         a.woocommerce-loop-product__link::before{ content:"Lesson Plan"; }
li.product.product_cat-common-core-lessons { --accent:var(--cat-common-core-lessons); }
li.product.product_cat-common-core-lessons  a.woocommerce-loop-product__link::before{ content:"Common Core"; }
li.product.product_cat-relief-packs        { --accent:var(--cat-relief-packs); }
li.product.product_cat-relief-packs         a.woocommerce-loop-product__link::before{ content:"Relief Pack"; }

/* ---- The card ----
   Quiet at rest, accent on hover. The hairline is only there to give a white
   worksheet an edge against a white card; the band carries the colour. Same 1px
   in both states so the grid never reflows on hover. */
li.product{
  background:#fff !important;
  border:1px solid var(--cream-200) !important;
  border-radius:18px !important;
  overflow:hidden !important;
  display:flex !important; flex-direction:column !important;
  padding:0 !important;
  box-shadow:0 2px 10px -6px rgba(31,42,68,.35);
  transition:transform .2s cubic-bezier(.2,.8,.3,1), box-shadow .2s, border-color .2s;
}
/* Hover: lift + the accent border + the page pushes in + the title goes pink. */
li.product:hover{
  transform:translateY(-6px);
  box-shadow:0 24px 44px -22px rgba(18,48,90,.5);
  border-color:var(--accent) !important;
}
li.product:hover img{ transform:scale(1.035); }
li.product:hover .woocommerce-loop-product__title{ color:var(--cta) !important; }

/* 1 · Header band — full-bleed, NOT a floating pill */
li.product a.woocommerce-loop-product__link::before{
  display:block;
  background:var(--accent);
  color:#fff;                       /* always. every category. */
  font-family:var(--font-body);
  font-size:12px; font-weight:800;
  letter-spacing:.09em; text-transform:uppercase;
  padding:13px 18px;
  margin:0;
}

/* 2 · The page — the whole sheet, not a letterbox.
   8.5/11 is US letter. Our product images all measure 0.772–0.773 against
   letter's 0.7727, so this crops nothing and stretches nothing.
   height:auto is required: it makes aspect-ratio authoritative and neutralizes
   the width/height attributes WordPress welds onto every . */
li.product img{
  width:100% !important; height:auto !important;
  aspect-ratio:8.5/11 !important;
  object-fit:cover !important; object-position:top center !important;
  margin:0 !important; border-radius:0 !important;
  border-bottom:1px solid var(--cream-200);
  transition:transform .45s cubic-bezier(.2,.8,.3,1);
}

/* 2b · Media exception — Songs and Videos are 16:9 stills, not pages.
   The box stays 8.5:11 so the grid keeps one card height; `contain` fits the
   frame inside it whole and the accent fills the rest. Never `cover` here —
   cropping a 768×432 still to 8.5:11 throws away ~65% of it. */
li.product.product_cat-songs img,
li.product.product_cat-videos img{
  object-fit:contain !important;
  object-position:center !important;
  background:var(--accent);
  border-bottom-color:var(--accent);
}

/* 3 · Grade + title */
li.product .hft-grade{
  font-family:var(--font-body); font-size:10.5px; font-weight:800;
  letter-spacing:.06em; text-transform:uppercase;
  color:var(--accent); padding:16px 18px 0;
}
li.product .woocommerce-loop-product__title{
  font-family:var(--font-serif) !important; font-weight:600 !important;
  font-size:16px !important; line-height:1.32 !important;
  color:var(--navy) !important;
  padding:4px 18px 0 !important;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  transition:color .18s;
}

/* 4 · Price */
li.product .price{
  font-family:var(--font-body) !important; font-weight:900 !important;
  font-size:19px !important; color:var(--ink) !important;
  padding:9px 18px 0 !important;
}
li.product .price .free{ color:#1F8551 !important; }

/* 5 · Add to Cart — full-width sail-yellow pill, navy text (5.6:1) */
li.product a.button, li.product a.button.custom-button{
  display:block !important; width:calc(100% - 36px) !important;
  margin:14px 18px 18px !important;
  text-align:center !important;
  background:var(--cta) !important; color:#fff !important;
  font-family:var(--font-body) !important; font-weight:800 !important;
  font-size:15px !important;
  padding:13px 20px !important; min-height:44px !important;
  border:none !important; border-radius:30px !important;
  transition:background .18s;
}
li.product a.button:hover{ background:var(--cta-hover) !important; }
li.product a.button:focus-visible{
  outline:none !important;
  box-shadow:0 0 0 3px #fff, 0 0 0 6px #5A93DA !important;
}
li.product .added_to_cart{ display:none !important; }   /* hide WooCommerce "View cart" */
/* =========================================================================
   FAVORITES — the heart. Secondary action. Lives in the header band.
   Markup (inject into the band, after the label):
     <button class="hft-fav" aria-pressed="false" aria-label="Save to favorites">
       <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.3 4.6 13a4.6 4.6 0
         0 1 6.5-6.5l.9.9.9-.9A4.6 4.6 0 1 1 19.4 13z"/></svg>
     </button>
   ========================================================================= */
li.product a.woocommerce-loop-product__link::before{
  /* band is now a flex row so the heart can sit at its right edge */
  display:flex; align-items:center; justify-content:space-between;
}
.hft-fav{
  flex:none;
  width:44px; height:44px;              /* full thumb target... */
  margin:-13px -12px -13px 0;           /* ...without growing the band */
  display:flex; align-items:center; justify-content:center;
  background:none; border:none; padding:0; cursor:pointer;
  border-radius:50%;
  transition:background .15s;
}
.hft-fav svg{ width:21px; height:21px; display:block;
  transition:transform .18s cubic-bezier(.3,1.6,.5,1); }
.hft-fav svg path{ fill:none; stroke:#fff; stroke-width:2; opacity:.85; }
.hft-fav:hover{ background:rgba(255,255,255,.16); }
.hft-fav:hover svg{ transform:scale(1.12); }
.hft-fav:active svg{ transform:scale(.92); }
.hft-fav:focus-visible{ outline:none; box-shadow:0 0 0 2px var(--accent), 0 0 0 4px #fff; }

/* Saved. Shape change, not just color — survives colorblindness + grayscale. */
.hft-fav[aria-pressed="true"] svg path{ fill:#fff; stroke:#fff; opacity:1; }
.hft-fav[aria-pressed="true"] svg{ animation:hft-favpop .34s cubic-bezier(.3,1.6,.5,1); }
@keyframes hft-favpop{ 0%{transform:scale(1)} 45%{transform:scale(1.3)} 100%{transform:scale(1)} }

@media (prefers-reduced-motion:reduce){
  .hft-fav svg, .hft-fav[aria-pressed="true"] svg{ transition:none; animation:none; }
}

/* =========================================================================
   FAVORITE CONFIRMATION TOAST
   Markup (once, before </body>):
     <div class="hft-toast" id="hft-favtoast" role="status" aria-live="polite">
       <span class="ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.3
         4.6 13a4.6 4.6 0 0 1 6.5-6.5l.9.9.9-.9A4.6 4.6 0 1 1 19.4 13z"/></svg></span>
       <span class="msg" id="hft-favmsg">Saved to favorites</span>
       <button class="undo" id="hft-favundo">Undo</button>
     </div>
   ========================================================================= */
.hft-toast{
  position:fixed; left:50%; bottom:26px;
  transform:translateX(-50%) translateY(14px);
  display:flex; align-items:center; gap:12px;
  background:#1B437D; color:#fff;                 /* brand navy. never blends in. */
  font-family:var(--font-body); font-weight:800; font-size:14.5px;
  padding:12px 14px 12px 18px;
  border-radius:16px;                 /* SURFACE radius — not a pill. */
  box-shadow:0 18px 40px -16px rgba(18,48,90,.6);
  opacity:0; pointer-events:none;
  transition:opacity .18s ease, transform .22s cubic-bezier(.3,1.4,.5,1);
  z-index:9999; max-width:calc(100vw - 32px);
}
.hft-toast.show{ opacity:1; transform:translateX(-50%) translateY(0); pointer-events:auto; }

/* Echo the icon she just tapped — not a generic check. */
.hft-toast .ic{ flex:none; width:22px; height:22px; border-radius:50%; background:#fff;
  display:flex; align-items:center; justify-content:center; }
.hft-toast .ic svg{ width:13px; height:13px; }
.hft-toast .ic svg path{ fill:var(--cta); stroke:var(--cta); }

.hft-toast .msg{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

/* Undo, not "OK". */
.hft-toast .undo{
  flex:none; background:rgba(255,255,255,.14); color:#fff; border:none; cursor:pointer;
  font-family:var(--font-body); font-weight:800; font-size:13px;
  padding:8px 16px; min-height:36px; border-radius:30px;
  transition:background .15s;
}
.hft-toast .undo:hover{ background:rgba(255,255,255,.26); }
.hft-toast .undo:focus-visible{ outline:none; box-shadow:0 0 0 2px #1B437D, 0 0 0 4px #fff; }

/* Mobile: full-width at the bottom, where her thumb already is. */
@media(max-width:520px){
  .hft-toast{ left:16px; right:16px; bottom:16px; transform:translateY(14px); max-width:none; }
  .hft-toast.show{ transform:translateY(0); }
}
@media(prefers-reduced-motion:reduce){ .hft-toast{ transition:opacity .12s; } }

/* -------------------------------------------------------------------------
   JS — one toast, replaced not queued. Undo reverts the heart.
   Focus NEVER moves into the toast.
   -------------------------------------------------------------------------
(function(){
  var el=document.getElementById('hft-favtoast'),
      msg=document.getElementById('hft-favmsg'),
      undo=document.getElementById('hft-favundo'),
      timer=null, last=null;

  function show(btn, saved){
    last=btn;
    msg.textContent = saved ? 'Saved to favorites' : 'Removed from favorites';
    el.classList.add('show');
    clearTimeout(timer);
    timer=setTimeout(function(){ el.classList.remove('show'); }, 4000);
  }

  undo.addEventListener('click', function(){
    if(!last) return;
    var on = last.getAttribute('aria-pressed')==='true';
    setFav(last, !on, false);            // revert, and don't re-toast
    el.classList.remove('show');
    clearTimeout(timer);
  });

  function setFav(btn, on, toast){
    btn.setAttribute('aria-pressed', String(on));
    btn.setAttribute('aria-label', on ? 'Remove from favorites' : 'Save to favorites');
    // Optimistic: paint first, persist after. On failure, revert quietly — never a modal.
    persist(btn.dataset.productId, on).catch(function(){ 
      btn.setAttribute('aria-pressed', String(!on));
    });
    if(toast) show(btn, on);
  }

  document.addEventListener('click', function(e){
    var btn = e.target.closest('.hft-fav');
    if(!btn) return;
    // Logged out? Let her tap anyway. Fill the heart, save the intent,
    // THEN ask for an account. Never block the tap behind a login wall.
    setFav(btn, btn.getAttribute('aria-pressed')!=='true', true);
  });
})();
------------------------------------------------------------------------- */
Simpler than the live CSS, not more complex: the pill hard-codes color:#fff once and the Songs one-off override goes away entirely.
03 — Typography

Typography

Fredoka leads — it is the logo. The wordmark is Fredoka SemiBold, so every heading on the site is drawn from the same well. Rounded and warm without being childish; the counters stay open at weight. Nunito Sans handles body copy and all UI — soft, highly legible for busy readers on phones. Source Serif 4 is the third voice: it carries product titles, testimonials and long-form reading.

Why Source Serif 4 replaced Fraunces. Fraunces italic is a boutique cue — coffee roasters and wedding stationery. It said “artisanal,” not “trusted for 20 years,” and it earned its place through prettiness rather than a job. Source Serif is a reading face built for screens: sturdy at 14px on a product card, comfortable across a 900-word blog post, and quietly credible under a testimonial. It never competes with Fredoka, because it isn’t trying to be fun. That’s the point — Fredoka is the personality, Source Serif is the proof.
Wordmark spec: Fredoka SemiBold (600), lowercase, tracking −12/1000 em (−1.2%), all navy #1B437D. Fredoka is SIL Open Font License 1.1 — free for commercial use, modifiable, redistributable with OFL.txt. Ship the wordmark as outlines in print; use live text only where Fredoka is installed. Never apply negative tracking to body copy — it hurts legibility below 24px.
Fredoka 700 · Display / hero — clamp 34–50px
Have fun teaching, every day.
Fredoka 600 · Section heading — 30px
Everything you need to teach
Source Serif 4 600 · Product titles & quotes — 24px
“Finally, my evenings back.”
Fredoka 500 · Sub‑heading — 20px
Worksheets, songs, videos & lesson plans
Nunito Sans 400 · Body — 17px / 1.6
Save hours of prep with ready‑to‑teach resources loved by 2 million teachers and homeschool families. Download, print, and teach — no login required to browse.
Type scale
AaDisplay · Fredoka 700 · clamp(34–50)
AaH1 · Fredoka 700 · 34px
AaH2 · Fredoka 600 · 30px
AaSerif · Source Serif 4 · 24px
The quick brown foxBody · Nunito 400 · 17px
UPDATED 2 DAYS AGOMeta · Nunito 800 · 13px
04 — Buttons

Button system

Everything is a pill — soft, tappable, friendly. There are five roles, and the order below is the order of importance on a store:

1 · Buy — sail yellow #FBB915, navy text. Add to Cart, Download, Print it. This is the button the business runs on, so it gets its own colour and never shares one. Tokens: --buy, --buy-hover, --buy-ink. It is the single place a display sail carries type (5.6 : 1) and the only button that is not white-on-colour.
2 · Primary — CTA pink #D6337B. Start free, Sign up, Search, Checkout. Pink asks her to join; yellow is what she taps to get the thing. Two different questions, so two different colours — on a grid of twenty-four cards they cannot be the same one or neither means anything.
3 · Secondary — navy. The reasonable alternative: View samples, Browse, Back. Present, never competing.
4 · Ghost — navy outline on white. For a third option that must exist but should not be chosen often.
5 · Link — pink text, no box. The way out: Skip for now, Cancel, Not today. Always available, never styled to be attractive.

One of each per screen, at most. Two yellows or two pinks on one view and neither is the answer to “what do I do next?”

Buy (sail yellow) · Primary (CTA pink) · Secondary (navy) · Ghost (navy outline) · Link. That is the whole set. There is no orange button and no promo-gold button.

One primary per screen — and here is the answer to “what do I do next?” On a resource, a results grid or a cart, the answer is Add to Cart / Download, so the yellow buy button is the only filled button on the screen. On a marketing page, a signup or a checkout, the answer is the pink primary — Start free, Create account, Place order. Everything else drops to navy, ghost or a link. Two filled buttons of the same colour on one view means one of them is in the wrong role, not that the screen has two answers.
Sizes — three, and only three

Three sizes, with their actual values. Medium is the default — you should have to justify the other two.

SizeClassFontPaddingHeightUse it for
Small.btn.sm13px / 8009px 20px44px ✓Dense UI where the row itself is the real target — filter bars, table rows, chip actions. Never the page’s main ask.
Medium — default.btn15px / 80013px 28px50px ✓Everything ordinary. If you are unsure which size to use, it is this one.
Large.btn.lg17px / 80016px 34px60px ✓Hero and end-of-page close only. One per screen — a second Large cancels the first.
44px is a floor, not a target. Small lands exactly on it — that is deliberate, and it is why Small cannot get any smaller. If a design needs a shorter button, it needs a link (.btnlink), not a fourth size.
Radius does not scale with size. All three are the same --r-pill. A pill that gets rounder as it grows stops reading as the same component; only the box changes.
Horizontal padding scales faster than vertical. 20 → 28 → 34 against 9 → 13 → 16. A big button should get wider more than it gets taller, or it turns into a slab.
The buy button is not in this scale. Add to Cart / Download is always full-width inside its card at 44px minimum — its width is set by the card, so a size class would be meaningless there.
The buy button — every state
Default
Hover
Pressed
Focus (a11y ring)
Disabled

Focus ring = white gap + blue outline, visible on any background. Min touch target 44×44px.

Hover never moves the button. No lift, no nudge, no scale. Hover deepens the fill and the shadow — that’s all. A button that jumps under the cursor is a moving target: it fights a shaky hand or a trackpad, and on a grid the whole layout twitches. Movement belongs to things that are arriving (toasts, menus), not to things waiting to be pressed.
But the press does move it. Every button on the site — Add to Cart, Start free, checkout, Elementor, WooCommerce, form submits — drops to 97% scale on :active while its shadow collapses to almost nothing. The pill feels pushed into the page. 90ms down, 140ms back: fast enough to read as cause‑and‑effect rather than animation. This is the one confirmation she gets on a phone with no hover state at all — the tap has to answer.
So: transform on :active only. Hover transitions background, box-shadow, color, border-color — nothing else. And tags and pills that aren’t buttons never react to a press: a category tag that squishes is lying about being tappable.
Reduced motion: the scale is replaced by a brightness dip. She still gets an answer; she just doesn’t get movement.
05 — Components

Core components

The pieces that show up everywhere and belong to no single page. The resource card — the one component that carries the store — now lives with the search and filter UI it appears inside: 05d — HFT Search and Filter.

Category pills
Reading Math Writing Songs Science New
Testimonial · form · status
★★★★★

“I saved my whole Sunday. My kindergartners are obsessed with the songs and I actually got my evening back.”

J
Jessie L.Kindergarten teacher
Start free — no card needed
Status tags
Active member Save 33% New this week
Component principles
One primary action per view — reduce decision fatigue for busy teachers.
Soft elevation, not hard borders — cards feel approachable and tactile.
Warmth through accents — coral, gold & curriculum colors add joy without losing trust.
Generous whitespace — calm, uncluttered, easy to scan on a phone.
Trust cues stay visible — stars, “2M+ teachers”, free samples near every CTA.
Pills everywhere — buttons, tags and toggles share one rounded language.
05a — Across the span

One card, three grade bands

The same product card, shown as it should feel at three points on the dial. Same structure, same logo, same palette — the color intensity, corner radius, and headline face shift with the audience. The first card (Elementary) is the house default.

Worksheet
✍️
Elementary · Kindergarten

Letter B Tracing & Coloring Fun!

FREE
Print it
Assessment
🔬
Middle · 7th Grade

Cell Structure Lab & Worksheet

$6.97
Add to cart
Lesson Plan
📝
High · 11th Grade

Argumentative Essay Rubric & Checklist

$8.97
Add to cart
Left to right: Elementary (house default; sail green, pill button, Fredoka title) → Middle → High (near-square button, serif title, navy accents). The buy button stays sail yellow in every band; only the radius, headline face and color intensity move. The catalog can mix all three on one page and still read as one store.
05b — Conversion patterns

The five moments that decide the sale

Everything above this section is about the brand. This section is about her — a teacher or homeschool mom, 20–45, on a phone, at 9pm, with tomorrow’s lesson unplanned. She is not browsing. She is solving a problem before she can go to bed. A brand kit that stops at logos and swatches leaves the actual money on the table, so these are the five screens where the decision really happens.

1

The card, at 375px

The product card is the whole store. She meets it 24 at a time, thumb‑scrolling. Tag pill first, then the thumbnail, then a title she can read at 14px. Price and grade visible without tapping.

Why: every tap she has to make to learn something basic is a place she leaves.
2

Proof sits on the button, not near it

The hesitation (“is this actually good?”) happens in the half‑second before the tap. Reassurance placed below the fold, or in a testimonial section further down, arrives after the decision is made.

Why: proof is only useful where the doubt is.
3

Price framed against her real alternative

$39.97/yr competes with “I’ll just make it myself at 11pm.” Frame it in her currency — hours, not dollars. Anchor against the à la carte price she already saw ($6.97 per resource).

Why: she doesn’t compare us to competitors. She compares us to her own free labor.
4

The free path is a door, not a demo

She should be able to hold a real, printable, classroom‑ready resource in her hand before she pays anything. Not a watermarked sample. Not a preview. The actual thing.

Why: the product is the sales pitch. Nothing we write beats her using one tomorrow.
5

Risk gets named and killed

Card required? Auto‑renew? Can I cancel? These are the questions she’s actually asking. Answer them at the button, in plain language, in 12px — never buried in a policy page.

Why: unanswered risk isn’t neutral. It’s a no.

The one‑line test

Every screen answers one question: “What do I do next, and what happens if I do?” If a page can’t answer both in under three seconds, it isn’t finished.

Why: a page that fails this test doesn’t need better copy — it needs the next step named. Apply it to any page before it ships.
Price presentation
Yearly Membership
$39.97
per year · about $3.33 a month
That’s 6 worksheets — for 6,600+.
★★★★★ Trusted by 2M+ teachers
Free for 3 days · cancel anytime · no card charged today
What’s doing the work here
  • $3.33 a month — the real number is annual, but the felt number is monthly.
  • 6 worksheets vs 6,600 — anchors against the price she already saw, not against a competitor.
  • Proof under the button, where the doubt lives — not in a carousel 800px down.
  • “No card charged today” — names the fear out loud instead of hoping she won’t think of it.
  • One pink button. Nothing else on the card is allowed to be pink.
Copy: what we say vs. what she hears
✕ Brand-centred

“Explore our comprehensive library of standards‑aligned educational resources.”

Describes us. Makes her do the translation work into her own life. “Comprehensive” and “library” are words she has to browse, and she has no time to browse.

✓ Her-centred

“Tomorrow’s lesson, ready in five minutes.”

Describes her outcome. Names the deadline she’s actually up against. She doesn’t want a library — she wants to be done and go to sleep.

✕ Feature

“Unlimited downloads & printing”

True, but it’s a spec. It asks her to imagine the benefit herself.

✓ Consequence

“Print it for all 26 kids. Then print it again next year.”

Same fact, but she can see it. Specific numbers beat the word “unlimited” every time.

Write to one person, not a market. “Teachers and homeschool families” is a segment. She is a third‑grade teacher with 26 kids and a laminator that’s out of pouches. Write to her.
Never use urgency we don’t have. Fake countdowns and “only 3 left” on a digital download insult her. She’s been teaching for years; she can smell it. Our urgency is real and it’s hers: the lesson is tomorrow.
Respect the 9pm brain. Short sentences. One decision per screen. No jargon, no cleverness that needs a second read.
Everything here is a hypothesis. This section is design judgment, not evidence. The pink‑vs‑orange CTA, the monthly‑equivalent price, the on‑button proof — each is worth an A/B test, and the test wins over anything written on this page, including this sentence.
05d — HFT Search and Filter

HFT Search and Filter

This is the store. A teacher lands on /resources/ with three or four words in her head, types them, narrows once or twice, and decides — usually inside forty seconds. Everything here serves that: a field, a rail, and a result.

Live implementation: the HFT SAF plugin, rebranded in place. Search is .hft-saf-search, the results wrapper is .hft-cards.hft-view-grid / .hft-view-list, and the view toggle is button.hft-saf-view. Out-of-stock and hidden products drop out of this index automatically.

One card spec, one place. The resource card is defined here and nowhere else on this page. Two copies of a component means two truths, and the second one is always the one someone builds from.
Search first, filter second. She arrives with words, not with a taxonomy. The field is the primary; the rail is the recovery. Never make her build a query out of checkboxes when she could just type it.
Colour carries the taxonomy. Format owns the band (17 colours, 02b), grade owns the grade pill (14 colours), subject owns the subject chip (6 colours). Three axes, three colour families, no overlap.
Search — the field she arrives with

One field, one placeholder written in her words, one pink submit. The placeholder names the three things she actually types — a grade, a subject, or a topic — because “Search…” alone makes her guess what the box will accept.

Search (grade, subject, or topic)
As she types “add”
Adding 8 Song (Lyric Music Video)Song
Two Digit Addition WorksheetWorksheet
Addition Song — DoublesVideo
See all 412 results for “add”
The submit button is pink, and it is the only pink here. Search is the primary action on this page. The card CTAs below are sail yellow precisely so twenty-four of them do not out-shout the one thing at the top.
Suggestions carry their format tag. A row without a tag is a string; a row with one is a decision. She can tell a printable from a video before she commits a click.
Never a dead end. The last row is always “See all N results”. Zero results gets a rephrase suggestion and a “tell us what you need” — those queries are the content roadmap. Full behaviour in 05c.
44px submit, 44px rows. She is on a phone, one-handed, often standing up.
Filter — the rail that narrows

The rail is for the second move, not the first. Grade, then subject, then format — that is the order she thinks in, so that is the order of the groups. Counts sit on every row: depth is the product, and she should feel it before she clicks.

Grade
Subject
Format +
Price +
318 resources 1st Grade × Math × Clear all
Active filters are chips, and the chip is the taxonomy’s colour. “1st Grade” is grade green, “Math” is subject blue — the same colours the cards below use. She learns the legend by using it, not by reading it.
Every chip carries its own ×. Removing one filter must never cost her the other three. “Clear all” sits beside them, quiet and underlined, for when it does.
The count is the first thing on the bar. “318 resources” tells her whether to narrow again or start scanning. A rail with no count is a rail she has to guess at.
The view toggle sits at the far right of the bar and remembers her choice. Grid is the default — she is usually judging by the page, not by the words.
Results — the grid card, five across

She meets this card 24 at a time on a phone, so it does one job: say what this is, show it, price it, and give her one button. The thumbnail is the whole sheet. Not a letterbox crop of the top ~40%, which on a worksheet is the header and the “Name / Date” line — the part that is identical on every worksheet and tells her nothing. It is the whole page at its true 8.5:11. Order, colors, fonts and button are otherwise as they were.

1 · Header bandFull‑bleed, solid category color, white uppercase label. Not a floating pill — the band is the color coding, and it’s the first thing the eye lands on.
2 · The page · newaspect-ratio: 8.5/11. The product image is the whole sheet, uncropped — our images all measure 0.772–0.773 against letter’s 0.7727, so they are pages. She can tell what a worksheet is before she clicks it. That is the entire point of the card.
3 · BorderA quiet cream hairline at rest — it exists to give a white sheet an edge on a white card, nothing more. The band already carries the category colour; the outline doesn’t repeat it. The accent arrives on hover, at the same 1px, so nothing reflows.
4 · HoverFour things, one gesture: the card lifts 6px, the border takes the category colour, the page pushes in 3.5% behind its frame, and the title goes CTA pink. Slow and slight — the card breathing, not a zoom.
5 · TitleSource Serif 4 600, navy, clamped to 2 lines, with a 2‑line floor so rows align. The serif is the “this is a real teaching resource” signal.
6 · PriceNunito 900. Visible without a tap or a hover. FREE is green — the only place green is allowed to shout.
7 · ButtonFull‑width sail-yellow pill, navy text (5.6:1), 44px min height. One per card. It is deliberately not pink: on a grid of 24 cards, 24 pink pills stopped meaning “the action.”
8 · Media exceptionSongs and Videos are 16:9 stills with no page to fill. Cropping a 768×432 frame to 8.5:11 would throw away ~65% of it, so .is-media floats the still at its true ratio on the accent panel. The card keeps its shape; the frame keeps its content.
✕ Rejected — words on the art

Title, price and CTA laid over a gradient scrim on the image, poster‑style. It looks good on a demo grid of hand‑picked worksheets and it fails on the real catalog: white type over unpredictable art, and a scrim that has to be QA’d against the darkest thumbnails forever. Same rule as the heart — the thumbnail is the product, so nothing sits on it.

✕ Rejected — price behind a hover

Revealing price and CTA on hover buys back the height the full page costs. It also hides the two things she’s deciding on, and it means nothing on a phone — where she is. Height is the cheaper thing to spend.

✓ Chosen — page above, words below

Every word sits on white where its contrast is a known token pair. Nothing depends on hover, nothing depends on the image being light enough. Cost, stated plainly: the card is ~37% taller than the 180px version, so roughly 1.9 rows land above a 900px fold instead of ~2.5. That is the trade, and it was made on purpose.

Assessment
Justin Bieber Biography Reading Comprehension Worksheet

Justin Bieber Biography Reading Comprehension Worksheet

$6.97
Worksheet
Third Grade Multiplication and Division Test Practice Worksheet

Third Grade Multiplication and Division Test Practice Worksheet

$6.97
Organizer
Story Map Graphic Organizer

Story Map Graphic Organizer

$6.97
Video
Hexagon Song (Original Music Video)

Hexagon Song (Original Music Video)

$6.97
Song
Adding 8 Song (Lyric Music Video)

Adding 8 Song (Lyric Music Video)

FREE
Results — the list view

The same catalogue, for the other question. Grid answers “which of these looks right?”; list answers “which of these IS right?” — it trades the big page for a description and the full taxonomy on every row. She switches to it once she has already narrowed and is comparing four near-identical worksheets.

Justin Bieber Biography Reading Comprehension Worksheet
Assessment

Justin Bieber Biography Reading Comprehension Worksheet

A one-page biography with five comprehension questions — read it, answer it, done.

5th GradeReading · WritingBiographies
$6.97
Third Grade Multiplication and Division Test Practice Worksheet
Worksheet

Third Grade Multiplication and Division Test Practice Worksheet

Mixed multiplication and division practice in the test format they will actually see.

3rd GradeMathDivision
$6.97
Story Map Graphic Organizer
Organizer

Story Map Graphic Organizer

Beginning, middle, end — the map they fill in after any read-aloud.

PreschoolELA · HolidaysStory Elements
$6.97
Hexagon Song (Original Music Video)
Video

Hexagon Song (Original Music Video)

Two minutes of shapes, sung. Play it twice and they have it.

1st GradeMathShapes
$6.97
Adding 8 Song (Lyric Music Video)
Song

Adding 8 Song (Lyric Music Video)

The +8 facts as a chorus. Free, so she can try one before she buys ten.

KindergartenMathAddition
FREE
Left border is the format accent; chips run grade → subject → skill. Same yellow button, same slate FREE, same 2-line title rule as the grid.
Three chips, one order, every row. Grade, subject, skill. A fixed order means she scans down a column instead of hunting across a row.
Grade and subject are coloured; skill is not. Skill is a 900-value long tail — colouring it would mean inventing 900 colours and teaching none of them. The outline chip says “detail, not axis.”
Chips are links, and they filter. Tapping “Math” on a row adds Math to the rail. The card is a filter surface, not just a result.
The description is two lines, hard. It exists to break a tie between similar titles, not to be read. Three lines and the row stops being scannable.
Subject colours — the 6

The subject axis is deliberately short. Six colours is a legend she can hold; fourteen would just be the grade map again in a different order.

SubjectChipSource hexChip hexWhite textNote
Phonics (incl. Alphabet)Phonics (incl. Alphabet)#CC2E2E#CC2E2E4.71 : 1 ✓source
Reading · WritingReading · Writing#7A3FA8#7A3FA85.66 : 1 ✓source
ELA · HolidaysELA · Holidays#34B26A#247A494.54 : 1 ✓deepened — 2.7 : 1 at source
MathMath#0758A1#0758A16.02 : 1 ✓source
ScienceScience#D2470B#C3420A4.51 : 1 ✓nudged for the alpha
Social Studies (+ Specials)Social Studies (+ Specials)#F0619E#B047744.52 : 1 ✓deepened — 3.0 : 1 at source
Why 92% and not 100%. At full strength the grade pill sat at the same weight as the format band directly above it, and the card read as two competing labels. Backing it off one notch puts format first, grade second — the order she scans in — without giving up a filled pill.
White text, no exceptions. Same rule as the format tags in 02b. A new grade or subject needs a colour dark enough for white at 10px. If it isn’t, deepen it — never switch one pill’s text to navy.
The template is the source. If a worksheet template’s accent changes, the pill follows it. That is the whole point of using the template colours: the pill on the card and the ink on the page are the same decision.
Favorites — the heart

Every card gets one. But a favorite is a secondary, low-commitment action — it must never argue with “Add to Cart.” So it is small, and it lives in the header band — never a 44px outline sitting beside the button at equal weight, which reads as two buttons and no answer.

Worksheet
DefaultWhite outline, 85% opacity. Present, quiet, obviously tappable.
Worksheet
SavedSolid white fill, 100%. Springs on tap — one 340ms pop, no confetti.
Assessment
Any accentWhite works on all 17 bands — same rule as the label.
Song
Even purpleNo per-category heart color to maintain. Ever.
Why the band, and not the thumbnail
✕ Rejected — overlay on the image

The Pinterest/Etsy convention, and wrong here. Our thumbnail is the product. She judges a worksheet by reading it — the top-right corner is where the “Name / Date” fields live. A floating heart chip covers the very thing she’s evaluating, and on a light worksheet scan a white heart disappears entirely.

✕ Rejected — beside the button

It steals horizontal space from the CTA, forcing “Download” to shrink. And at equal size it reads as an equal choice — two buttons, no answer to “what do I do next?”

✓ Chosen — right of the header band

The band’s right side is dead space. The heart is always in the same place across a 24-card scroll, contrasts hard against every accent, never occludes the resource, and gives the CTA its full width back. It costs zero vertical height.

21px icon, 44px target. The visual is small; the tap area is not. Negative margins eat the band padding so the hit box stays full-size without changing the layout. She’s tapping one-handed.
Optimistic, instant, reversible. Fill on tap, no spinner, no page jump. If the save fails, revert quietly. Never a modal — a modal for a favorite is a punishment for engaging.
Logged-out? Let her tap it anyway. Save the intent, fill the heart, then ask for an account — “Save this to your favorites” is the softest account-creation prompt we have, and it arrives at the moment she’s already decided she wants something. Blocking the tap behind a login wall throws that away. See 05b.
Never a number. No “saved 1.2k times” — social proof belongs on the CTA, not on a personal bookmark. This heart means mine, not popular.
Not color alone. Outline vs. fill is a shape change, not just a color change — it survives colorblindness and grayscale print. Backed by aria-pressed and a label that flips to “Remove from favorites.”
Favorite confirmation toast

Tap any heart above — the real thing fires at the bottom of your screen. The toast has one job: confirm the save, and offer the way back. It is brand navy, pinned bottom-centre, and it always offers Undo.

Note the corners. The toast is a 16px rounded rectangle, not a pill — see 06 — Radius encodes function. A pill container wrapping a pill Undo button is two identical shapes nested, and the whole toast starts to look pressable. It isn’t. Surface = 16px, action = pill.

✕ Rejected — the generic toast

Generic dark slate — not a brand color. Floating over the middle of the page, covering the headline she was reading. No undo, so a fat-finger tap is permanent. And “Added to favorites” is passive — it describes the system, not her.

✓ Chosen — the HFT toast

Brand navy pill, Nunito 800, pinned bottom-center where nothing lives. White circle + pink heart — the same heart she just tapped, so the confirmation looks like the action. And an Undo that actually reverts the card.

Saved to favorites
Navy on any background, always. It’s the one element that must never blend in — she needs to find it in her peripheral vision without leaving the card she’s looking at.
Bottom-center, never mid-page. The bottom edge is empty on every screen we own. Mid-page overlays cover the exact content she’s evaluating — and on mobile, the bottom is also where her thumb already is.
Undo, not “OK”. A confirmation she has to dismiss is a tax. A confirmation that offers a way back is a service. Undo restores the heart and closes the toast — no page reload, no navigation.
Echo the icon. The white circle holds the same heart in CTA pink. A green check would say “task complete”; the heart says “this is yours now.” Confirm the thing, not the transaction.
One toast, replaced — never stacked. She’ll favorite six worksheets in a row. Six queued toasts is a wall. Each tap resets the same toast and its 4-second timer.
4 seconds, then gone. Long enough to read and reach Undo, short enough not to nag. It never blocks a tap: the card underneath stays live.
role="status" + aria-live="polite". A screen reader announces the save without stealing focus mid-scroll. Focus stays on the heart she tapped — never move focus into the toast.
It says “Saved,” not “Added.” “Added to favorites” narrates the database. “Saved to favorites” is what she just did. Removing says “Removed from favorites” — same pill, same Undo.
06 — Global tokens

Radius, spacing & elevation

Radius encodes function. In this system a shape is a promise: a pill can be tapped, a rounded rectangle cannot. Everything interactive — buttons, tags, toggles, the Undo — is a full pill. Everything that holds things — cards, panels, the toast — takes the 16px surface radius. Follow that one rule and she never has to learn our UI; she can feel where to press.

Corner radius
10px · sm (inputs, chips)
16px · md · SURFACES
18px · resource card
pill · ACTIONS
This is why the favorites toast is not a pill. A pill container holding a pill Undo button is two identical shapes nested, so the whole toast reads as something you can press. It isn’t. The toast is a surface (16px); the Undo inside it is the action (pill). Same fix applies anywhere a container wraps a button.
Elevation
sm
md
lg
Baseline globals
Content width1080px
Base font17px
Line height1.6
Display fontFredoka
Serif / readingSource Serif 4
Body fontNunito Sans
Card radius16px
Button radiuspill / 30px
Spacing base4pt scale
07 — Accessibility

Built to WCAG AA

Most of our audience browses on a phone, often tired, often at night. Every button and every tag on this site carries white text — one rule, no exceptions, no per‑element overrides. That means every interactive color has to be dark enough to earn it.

White on Navy · 9.8 : 1 ✓ AAA
White on Blue · 7.2 : 1 ✓ AAA
White on CTA Pink · 4.6 : 1 ✓ AA
White on CTA Pink hover · 6.0 : 1 ✓ AA
White on Deep Green · 4.6 : 1 ✓ AA
White on Purple · 6.8 : 1 ✓ AAA
White on Red · 5.3 : 1 ✓ AA
White on Orange · 4.5 : 1 ✓ AA
Ink on Coral · 5.6 : 1 ✓ AA
White on Slate · 5.5 : 1 ✓ AA
Ink on White · 16.3 : 1 ✓ AAA
The bright sails — and coral — are decoration, not surfaces. Pink #F0619E, yellow #FBB915, green #34B26A and coral #F26E5F are for illustration, tints and the logo. They are never a button or a tag background — white text fails on all four (3.0, 1.7, 2.7 and 2.9 : 1). Use their deepened forms.
Coral and orange are one color at two weights. Coral #F26E5F is the bright face you see in illustration and tints; orange #D2470B is the same color deep enough to put white words on. Never white text on coral. Never coral on a button.
Yellow pays the highest price. Dark enough for white text, yellow stops being yellow — which is exactly why Songs is now purple rather than a darkened gold. If a surface must look yellow, it can’t have text on it.
Never rely on color alone — pair every status and category color with a label. The pill has words on it for a reason.
Keep the focus ring on all interactive elements; minimum touch target 44×44px. She’s tapping with one hand and a kid on her hip.
08 — Developer handoff

CSS variables (copy & paste)

Drop these into the theme, Elementor custom CSS, or global settings to roll out V2.1 consistently. Note the split between display colors (the bright sails — decoration only) and surface colors (deep enough to carry white text). Mixing them up is the one way to break accessibility here.

Design token CSS67 lines · click to expand
/* Have Fun Teaching — Design Tokens V2.1 (Pinwheel) */
:root{
  /* ---- SURFACES: anything with white text on it ---- */
  --navy-900:#12305A;  --navy:#1B437D;   /* PRIMARY — the wordmark */
  --blue:#0758A1;
  --cta:#D6337B;       --cta-hover:#B62A67;  /* deepened pink — THE button */
  --green-deep:#1F8551;
  --purple:#7A3FA8;    --purple-700:#633389;  /* music & sound — Songs */
  --red:#CC2E2E;       --red-700:#A82525;     /* watch & play — Videos. NOT the error color */
  --orange-burnt:#D2470B;
  --slate:#5A6B7D;

  /* ---- DISPLAY: illustration, tints, the logo. NEVER a text background ---- */
  --sail-yellow:#FBB915;   /* white text = 1.7:1 — fails hard */
  --sail-green:#34B26A;    /* white text = 2.7:1 — fails */
  --sail-pink:#F0619E;     /* white text = 3.0:1 — fails */
  --sail-blue:#0758A1;     /* the one sail that is also a surface */

  /* ---- Neutrals ---- */
  --ink:#231F20; --muted:#93A0AD;
  --cream-50:#FFFCF6; --cream:#FBF5EC; --cream-200:#EEE6DA; --line:#E9E0D4;

  /* ---- Semantic (all white-text safe) ---- */
  --success:#1F8551; --warning:#D2470B; --error:#C9584E; --info:#0758A1;

  /* ---- Type: three fonts, three jobs ---- */
  --font-display:'Fredoka',sans-serif;              /* headings — it IS the logo */
  --font-body:'Nunito Sans',sans-serif;             /* body + all UI */
  --font-serif:'Source Serif 4',Georgia,serif;      /* product titles, quotes, long-form */

  --r-md:16px; --r-pill:30px; --container:1080px;
}

/* THE primary button. One per screen. White text, always. */
.btn-cta{
  background:var(--cta); color:#fff;
  font-family:var(--font-body); font-weight:800;
  border-radius:var(--r-pill); padding:14px 30px;
  min-height:44px;                    /* thumb target */
  box-shadow:0 12px 26px -14px rgba(214,51,123,.75);
  transition:background .18s, box-shadow .18s;   /* never transform — hover must not move a button */
}
.btn-cta:hover{ background:var(--cta-hover); box-shadow:0 16px 30px -14px rgba(214,51,123,.85); }

/* PRESS — every button, everywhere. transform is allowed on :active ONLY. */
.btn-cta, .btn-navy, .btn-ghost, button, .woocommerce a.button, .elementor-button{
  transition:background .18s, box-shadow .18s, color .18s,
             transform .14s cubic-bezier(.2,.8,.3,1);
}
.btn-cta:active, .btn-navy:active, .btn-ghost:active, button:active,
.woocommerce a.button:active, .elementor-button:active{
  transform:scale(.97);
  box-shadow:0 2px 8px -6px rgba(31,42,68,.6);
  transition-duration:.09s;              /* down fast, back slow */
}
@media (prefers-reduced-motion:reduce){
  .btn-cta:active, button:active{ transform:none; filter:brightness(.92); }
}
.btn-cta:focus-visible{ box-shadow:0 0 0 3px #fff,0 0 0 6px var(--blue); }

/* Secondary */
.btn-navy{ background:var(--navy); color:#fff; border-radius:var(--r-pill); }
.btn-navy:hover{ background:var(--navy-900); }

/* Ghost */
.btn-ghost{ background:#fff; color:var(--navy); border:2px solid var(--navy); border-radius:var(--r-pill); }
.btn-ghost:hover{ background:var(--navy); color:#fff; }
09 — Downloads & assets

Logos & fonts in one place

Every version of the pinwheel identity, as clean SVG (wordmark already converted to outlines — no font needed to render it). Right‑click → Save, or use the links. 2400px PNGs live in /png, favicon sizes in /favicon.

Horizontal lockup

The default logo.

⬇ Full color⬇ Navy⬇ White⬇ Black

Stacked lockup

For squarer spaces.

⬇ Full color⬇ Navy⬇ White⬇ Black

Pinwheel mark

Icon only — avatars, embroidery, corners.

⬇ Full color⬇ Navy⬇ White⬇ Black

App icon & favicon

WordPress Site Icon. 16 / 32 / 48 / 180 / 512.

⬇ Icon (navy)⬇ Icon (cream)⬇ 512px PNG⬇ 32px PNG

Raster (PNG @2400px)

Transparent, for decks & docs.

⬇ Horizontal⬇ Stacked⬇ Mark⬇ Horizontal (white)

Social share image

1200×630 og:image — navy, white lockup, four sails.

⬇ PNG 1200×630⬇ SVG source

Fonts

Wordmark & headings = Fredoka (OFL) · Body & UI = Nunito Sans · Reading = Source Serif 4. Three fonts, three jobs.

⬇ Fredoka⬇ Nunito Sans⬇ Source Serif 4
10 — Take it with you

One file. The whole brand.

Exports this page — every rule, color, token and component — as a single self-contained HTML file you can email to a designer, a freelancer or a printer. No fonts to install, no assets to zip, no login. It opens in any browser and it is always in sync with this page, because it is this page.

Self-contained HTML · Always current · This page is the source of truth
Have Fun Teaching — Brand & Site Kit V2.1 · Pinwheel identity · Warm, playful, WCAG‑AA · Every color sampled from the logo
Copied!
Saved to favorites