My kid forgot plant charts in 2 days. So I built tiny JSON-driven flashcards instead of printing more paper.
I am not a teacher. I am a builder who runs a kids learning site.
Last month my 5-year-old forgot mint vs tulsi and called every tree mango tree. Charts failed. So I thought like a dev: what if data + UI + print + play solve this?
I built a tiny no-dependency kit: plants.json + index.html + print CSS. No React, no build step. Loads in ~1.2s on mobile.
Here is what I did, what broke, and code you can reuse.
Why Paper Lists Failed and Data Helped
Paper has no interaction. Sunflower and marigold both look yellow on paper. Mint and tulsi both look green.
I needed structure: name, type, smell, touch, one fact. Once data was clean, UI became easy. Same JSON drives screen cards, quiz, and print sheet.
Lesson: fix data first, design later.
1. Simple JSON for 15 Starter Plants
I kept only 5 fields. No over-engineering.
[
{
"name": "Mint",
"type": "herb",
"sense": "smell",
"fact": "Smells fresh, grows fast in water",
"image": "/images/mint.png"
},
{
"name": "Money Plant",
"type": "climber",
"sense": "touch",
"fact": "Roots visible in glass bottle",
"image": "/images/money-plant.png"
},
{
"name": "Sunflower",
"type": "flower",
"sense": "sight",
"fact": "Turns toward the sun",
"image": "/images/sunflower.png"
}
]
Why this works for kids: sense field decides activity. Smell cards for kitchen game, touch cards for balcony, sight cards for outdoor. Code drives pedagogy.
I started with 7: mint, tulsi, money plant, aloe, spider plant, sunflower, hibiscus. Then added mango, neem, banana and veggies up to 15.
2. Vanilla JS Flip and Quiz No Framework
I did not want npm for a preschool tool. Plain JS is enough.
<div id="card-grid"></div>
<script>
async function loadCards() {
const res = await fetch('./data/plants.json');
const plants = await res.json();
const grid = document.getElementById('card-grid');
grid.innerHTML = plants.map(p => `
<button class="card" data-type="${p.type}">
<img src="${p.image}" alt="${p.name} for kids" loading="lazy" />
<h3>${p.name}</h3>
<small>${p.type} - ${p.sense}</small>
<p>${p.fact}</p>
</button>
`).join('');
}
loadCards();
</script>
Features I added in 60 lines:
- filter by type: herb / flower / tree
- smell vs touch mode toggle
- click-to-flip for fact
- shuffle button - kids love shuffle
Performance tip that helped Core Web Vitals: loading="lazy" + width/height on img to fix CLS + under 40KB total JS. LCP went from 3.1s to 1.2s.
For botany accuracy reference I cross-checked growth notes with Kew Gardens.
3. Print CSS Parents Actually Use
Most parents still print. So @media print mattered more than dark mode.
@media print {
button { break-inside: avoid; border: 1px solid #333; }
#toolbar, .no-print { display: none; }
#card-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
}
Flow: screen for play, print for fridge. Kid draws on printed card back. That drawing page fixed memory better than any re-read.
We tested this flow against the live plant learning page I built for testing with real pictures and one-line facts. I used it only as content reference for facts, not as iframe. decoupling content and app kept both fast.
3 Mistakes I Made
1. Too many cards day one: I shipped 25 cards. Kid confused climbers vs creepers. Cut to 7. One concept per day wins.
2. No image sizes: First version had CLS 0.28. Fixed with fixed aspect-ratio + dimensions. CLS 0.03 now.
3. Quiz as test: Tell 5 names fast failed. Changed to Find the plant that smells - play, not exam. Engagement 3x.
If you build for kids: big tap targets 48px+, high contrast, one action per screen, offline-friendly. They tap everything.
Full code + PDF is in my repo linked in my profile. Author bio: I'm Shivam, I build simple learning tools for preschool kids at The Tiny Tutors. I share only what I test at home.
Disclosure: I run The Tiny Tutors, linked here as live demo companion to this open-source tutorial, not as promo. This post is based on my own build.

Top comments (0)