I have a million things to do this weekend but instead of doing them I built le petit mot instead. Le Petit Mot (the little word) is a tiny, static, Art Nouveau-themed French learning app. Flashcards, stories, and a daily audio journal, all set in Paris. No backend. No AI in production. Just words.
And why? Well:
- Why not?
- I didn't want to get wrapped up into any other online learning ecosystems.
- I wanted something designed specifically for my trip.
- Did I research formal language learning methodology? Not even a little bit. Not one paper. Not one blog post. I just thought about how I'd want to learn and built that.
- I have Kiro credits burning a hole in my pocket that expire in a few weeks.
What else is a girl supposed to do?
The situation
I'm leaving for Paris in 30 days. First time. I don't speak French. I can say "croissant" but so can everyone. I needed to learn enough to order food, ride the Métro, ask where things are, and not look completely lost in the arrondissements, hopefully.
I thought about Duolingo. I thought about Babbel. I thought about a dozen apps. Then I thought... why hand my learning over to someone else's ecosystem when I could just build what I need?
The UI is a little rough. I know. I wanted something that stood up quickly, not something that won a design award. Function over polish. For now.
What I actually built
Le Petit Mot is a mobile-first web app that teaches basic Parisian French through daily flashcards, story read-alongs, and a narrated audio journal. Every word and story maps to a real scenario I'll encounter in Paris.
The target reading level is a 5-year-old. I'm not trying to read Camus. I'm trying to order a café crème without panicking.
30 days. 5 new words per day. A short story using those words. And a "petit journal de Paris" that narrates a fictional diary of someone's first month in Paris, getting a little more comfortable each day.
The app itself is vanilla HTML, CSS, and JavaScript. No framework, no build step, no backend. All the content lives in a single static JavaScript file. I used Kiro credits for the spec-driven development and Claude for architecture and creative direction, but the thing that ships to your browser has zero AI in it. It's just a webpage serving words.
You can look at all of it: the repo is public.
The design rabbit hole
I wanted it to feel Parisian. I had Art Nouveau Metro entrances and Madeleine storybook illustrations in my head. What I got was simpler than that. But the sage green palette and the ironwork SVGs give it enough personality to not feel like a generic flashcard app.
I went through... let me count... five versions of the ornamental SVG components alone. The first Eiffel Tower looked like a Christmas tree. The iron arch looked like a McDonald's logo with earrings. The divider was a corporate timeline.
I uploaded photos of actual Metro entrances and the Madeleine book cover as design references. The final palette is called "Absinthe Garden": sage green, warm ochre, dusty rose, and Parisian cream. Every color has exactly one job. Green is structure. Ochre is warmth. Rose is "look here." No color does two things.
The Eiffel Tower is a line drawing with curved legs. It took four tries to not look like a tent.
The build process (or: how scope discipline saved my life)
I've been burned enough times by scope creep and mid-project chaos that I've picked up some habits. They're not documented anywhere formal (bad build discipline, I know, I know). But they kept this build from going sideways:
Single PRD, one source of truth. Before I wrote a line of code, I had a 13-section PRD with MUST/STUB/NEVER labels on every feature. If it says NEVER, it's never. No dark mode. No gamification. No grammar drills. No backend.
One file, one responsibility. The app has 10 JS files. audio.js speaks French. progress.js tracks completion. ornaments.js draws Art Nouveau SVGs. curriculum.js holds all the content.
Mock data first, then wire real data. I built the entire UI with Day 1 hardcoded content before generating the other 29 days.
STUB, don't build. If a feature wasn't happening tonight, it got a comment stub with implementation notes. Not a half-built skeleton. Not an empty function. A comment that tells future-me exactly how to pick it up.
I used Kiro as my IDE and it turned my PRD into requirements, design docs, and implementation tasks through its spec-driven development flow. I also set up four agent hooks, and honestly, they were a trip:
- Scope guard: fires before every file write and checks if I'm touching code from an earlier build block. If I am, it blocks the write. This thing was strict. Like, won't-let-you-fix-a-typo-in-the-wrong-file strict.
- QA checkpoint: fires after the agent finishes any task and forces it to list specific things I should verify in the browser before moving on. "It works" is not a passing check. "Tap the Commencer button, verify it navigates to calendar view" is.
-
Security scan: fires on every JS file save and checks for
innerHTMLwith dynamic content,eval(), or hardcoded API keys. - Single responsibility: fires on every JS file save and checks if the file is doing more than one job.
The hooks slowed me down. They really did. Every file save triggered checks. Every task completion triggered a QA list. But they also kept me from veering off the road, which is my biggest weakness on sprint builds. I'd start fixing a color in the ornaments file while I was supposed to be building the flashcard view, and the scope guard would go "nope." Annoying in the moment. Correct every time.
Fair warning though: those hooks cost Kiro credits every time they fire. I burned 13 credits on a CSS-only visual audit because every file write triggered the scope guard and QA hooks. For polish passes, turn them off. Lesson learned.
The whole app was built in one sprint session. Six blocks, each with pass/fail QA criteria I had to check in the browser before moving on.
The "deterministic vs AI" question
So this was new for me. The app has zero AI in production. None. All 30 days of vocabulary, stories, and journals are hardcoded in a static JavaScript file.
I used Claude to help generate the curriculum content and Kiro credits for the development workflow. But the content was validated, edited by me, and committed as plain data. The deployed app is just HTML serving static words.
I'm not going to claim this approach is better than using AI in production. But for this project, for this use case, it works. There's no API latency. No cost per user. No rate limiting. You open it, it works, every time. That felt right for something I want to practice with before the trip.
The question I kept asking myself: did I make this too "dumb"? No AI-powered adaptive learning. No spaced repetition algorithm. No speech recognition to check your pronunciation. No research into second language acquisition theory. I didn't read a single paper on how people actually learn languages. I just thought: flashcards, stories, repetition, context. That's how kids learn. That's how I'll learn.
And something funny happened while I was testing. I actually learned the words. I was debugging the flashcard flip animation and realized I could say "excusez-moi, où est la sortie?" without looking at the screen. My wife says my French accent is good (she might be being nice but I'll take it). Turns out the simplicity might actually be the feature. A 5-year-old doesn't need an algorithm. They need repetition and context.
What surprised me
How fast it came together. The core app was functional in one evening. The content generation (all 30 days of words, stories, and journals) took longer than the code.
How many French words I learned while testing. I was debugging the flashcard flip animation and accidentally memorized all the greetings. QA as education. I didn't plan this. It just happened.
The Kiro hooks. Wow. I set up four of them thinking they'd be helpful guardrails. They were more like a strict project manager who doesn't care about your feelings. The scope guard blocked me from touching files outside my current build block. The QA checkpoint refused to let me move forward without browser verification. They slowed me down and kept me honest. I'm keeping them for every future project (but turning them off for CSS tweaks next time).
That the audio almost didn't work on mobile. Web Speech API on mobile browsers requires a "user gesture unlock." Your first speechSynthesis.speak() call has to happen during a tap event, or the browser silently blocks all future audio. My unlock pattern used an empty string, which iOS Safari treated as a no-op. The fix was changing '' to '.'. One character. Hours of debugging.
That my wife likes my French accent. Unrelated to the build but arguably the most important outcome.
The journal narrative
The part I'm most proud of is the daily journal. It's not just vocabulary practice. It tells a story across 30 days.
Day 1: "Je suis à Paris. Je suis vraiment à Paris!" (I'm in Paris. I'm really in Paris!)
Day 7: "Aujourd'hui, je marche sans carte." (Today, I walk without a map.)
Day 16: "Quelqu'un m'a demandé son chemin. J'ai donné des directions. En français." (Someone asked me for directions. I gave directions. In French.)
Day 27: "Je me sens bien à Paris." (I feel good in Paris.)
Day 30: "Merci, Paris. Merci pour tout. Pour toujours." (Thank you, Paris. Thank you for everything. Forever.)
By Day 30 you've followed someone from "please, Paris, be gentle with me" to "I can order food, take the Métro, and buy cheese without pointing." Same simple vocabulary the whole way through. The words don't get harder. The person gets a little braver each day. Like a kid figuring out their neighborhood.
The stack
- Vanilla HTML / CSS / JS (no framework, no build step)
- Web Speech API for French audio (fr-FR, rate 0.7)
- localStorage for progress tracking
- Vercel free tier for hosting
- Google Fonts (Playfair Display + Nunito)
- SVG Art Nouveau ornaments (hand-coded, not generated)
- Kiro IDE + Kiro credits for spec-driven development and agent hooks
- Claude for architecture, creative direction, and content generation
Total runtime cost: $0.
Total API calls in production: 0.
Kiro credits used: more than I expected, less than I feared.
Would I recommend building your own learning app?
If you have a specific thing you need to learn for a specific reason, and existing tools feel like overkill or too generic? Yeah, build your own thing. It doesn't have to be fancy. It doesn't have to use AI. It just has to teach you what you need to know.
Le Petit Mot won't make me fluent. But in 30 days, I'll be able to walk into a Parisian café, order a croissant and a café crème, understand when someone says "à gauche" instead of "à droite," and maybe, if the fromager is patient, buy a piece of Comté without pointing.
For a first trip to Paris, that feels like enough. Like a little kid who knows enough words to get by. Which is exactly what I was going for.
À bientôt, Paris. Je suis presque prête.
(See you soon, Paris. I'm almost ready.)
AI assisted. Human approved. Powered by NLP.
Top comments (0)