Skip to content
← Back to portfolio

Project case study · Mobile system

Greek in 30 Days

Offline Spanish→Greek language-learning app with an SM-2 review engine, structured quizzes, XP and streak progression, local reminders, and accessible React Native flows.

React NativeExpoTypeScriptExpo RouterZustand
Greek in 30 Days

The challenge

“Greek in 30 Days,” branded in Spanish as “Griego en 30 Días,” is an offline-first course for Spanish speakers learning Greek. It is the first planned entry in a “30 Days Languages” series. The course covers the Greek alphabet and a 30-day vocabulary curriculum, with pronunciation practice, quizzes, and a review loop that makes daily progress visible and repeatable.

The solution

The course runs locally. Lesson and vocabulary content is bundled as static data, while AsyncStorage keeps progress, review cards, and settings on the device. The learning loop does not depend on a backend, so the installed course works without connectivity. Pronunciation uses expo-speech for on-device text-to-speech. Instead of a set of static screens, the native flow includes onboarding, lesson sections, vocabulary cards, quizzes, progress feedback, and a dedicated review tab.

The app is still in pre-launch. I drafted and structured the curriculum with Claude Code during development. That is a build-time content workflow, not a live in-app AI feature, and the app makes no LLM calls at runtime.

Scroll for more →

What the app includes

A fully offline course

Once installed, the app needs no network connection. Lessons, pronunciation, and progress tracking all run locally with bundled content and AsyncStorage.

Alphabet, vocabulary, and pronunciation

The curriculum is designed for Spanish-speaking learners and covers the Greek alphabet, 360 vocabulary words, and pronunciation drills using text-to-speech. As learners move through each lesson, its vocabulary enters the review system. The lesson flow and review loop share the same progress state.

SM-2 spaced repetition

The review system uses an SM-2 engine. Each card stores its ease factor, interval, repetition count, next-review timestamp, and last-review timestamp. The learner can choose again, hard, good, or easy, and each answer changes the schedule deterministically. Failed recall resets the repetition. Successful recall updates the ease factor and interval with hard or easy modifiers. New cards are staggered if a day’s queue would otherwise flood the review screen.

Jest unit tests cover fresh-card transitions, mature-card interval math, ease-factor bounds, due-date behavior, staggered cards, and all four difficulty grades.

Quizzes that feed progress

Every lesson includes structured quiz data. The native quiz component supports multiple-choice, fill-in-the-blank, and translation questions. It gives immediate correct or incorrect feedback, advances the lesson, and records the best score when a lesson is replayed. Correct answers award XP on the first run, and a perfect score can unlock an achievement.

XP, levels, streaks, and achievements

The persisted Zustand store tracks XP, computed levels, daily streaks, lesson and vocabulary progress, and achievement timestamps. Lesson completion, quiz answers, alphabet practice, and card reviews all count toward progression. Streak updates are idempotent per day, and replaying lessons does not award completion XP again. Milestones cover first completion, streaks, vocabulary, quiz accuracy, alphabet mastery, and completion of the full course. New unlocks appear in an accessible achievement toast.

Onboarding and daily reminders

New installs go through a first-run onboarding route before entering the tab navigator. Finishing or skipping onboarding saves the flag and returns the learner to the course. From the profile screen, learners can opt into a daily practice reminder. The app requests permission, schedules one repeating notification at the chosen local time, and cancels it when the setting is turned off. expo-notifications handles this locally without a remote push-token service.

WCAG-AA accessibility

The color system records WCAG-AA contrast targets for fills with white text, including a 5.13:1 success color, a 4.55:1 large or bold streak color, and difficulty buttons at 5:1 or better. Important controls expose native roles, labels, and states, and Greek text surfaces are marked with their language. VoiceOver and TalkBack announce quiz results, lesson completion, and achievement unlocks. The achievement animation also respects reduced-motion preferences.

AI-assisted content pipeline

I wrote the lesson and vocabulary content with an AI-assisted Claude Code workflow at build time. This is a content-production technique, not an app feature, and the app makes no in-product LLM calls.

Architecture

Frontend: Expo SDK 57, React Native 0.86, React 19.2.3, TypeScript, and Expo Router for file-based navigation.

Progress and review: Zustand with AsyncStorage keeps XP, levels, streaks, achievements, lesson scores, and SM-2 card state on the device. The review tab reads due cards from the same store, so the schedule and UI use one source of truth.

Native capabilities: expo-speech handles on-device pronunciation. expo-notifications handles permission-gated local daily reminders.

Testing and CI: Jest uses the jest-expo preset and a setup file that mocks native-only haptics, storage, speech, and notification modules. This allows the store, algorithm, and data suites to run in Node. GitHub Actions runs frozen pnpm installation, TypeScript typechecking, ESLint, and Jest on pushes and pull requests targeting main.

Status: The app is in development and has no store listing yet. Subscriptions through RevenueCat are planned but not yet implemented.

Have a project in mind?

Tell me what you're working on and where you need help.