DEV Community

Cover image for Building a Vanilla TypeScript Japanese Flashcards App with Antigravity AI
A0mineTV
A0mineTV

Posted on

Building a Vanilla TypeScript Japanese Flashcards App with Antigravity AI

In this post, I want to share my experience building a robust Japanese Flashcards application using Vanilla TypeScript, HTML, and CSS, all while pair-programming with Antigravity, an advanced AI coding assistant from Google DeepMind.

The Goal

The objective was straightforward but strict: create a lightweight, browser-based application to help learn Japanese vocabulary.
The Constraints:

  • No Frameworks: No React, Vue, or Angular. Just pure DOM manipulation.
  • No Bundlers: No Webpack or Vite configuration hell. Just the TypeScript compiler (tsc).
  • Type Safety: Strict TypeScript usage (no any).

The Architecture

Since we weren't using a framework, we had to structure the app carefully to keep it maintainable.

1. Project Structure

We opted for a simple separation of source and distribution:

/src
  ├── data.ts        # Vocabulary data and types
  ├── flashcards.ts  # Main game logic and DOM manipulation
/dist                # Compiled JavaScript (ES Modules)
index.html           # Entry point
styles.css           # Global styles
tsconfig.json        # TypeScript configuration
Enter fullscreen mode Exit fullscreen mode

2. Data Modeling

We defined clear interfaces to structure our data. This made extending the app with new "Decks" (categories) extremely easy later on.

// src/data.ts
export type Flashcard = {
    jp: string;      // Kanji/Kana
    romaji?: string; // Pronunciation help
    fr: string;      // French translation
};

export type Deck = {
    id: string;
    name: string;
    cards: Flashcard[];
};
Enter fullscreen mode Exit fullscreen mode

3. State Management

Without Redux or Context API, how do we manage state? Simple: Global variables within our module scope. Since flashcards.ts is a module, these variables aren't polluting the global window scope, but are accessible to all our internal functions.

// Application State
let currentDeck: Flashcard[] = [];
let currentIndex = 0;
let currentMode: 'review' | 'quiz' = 'review';
let quizScore = 0;
Enter fullscreen mode Exit fullscreen mode

Key Features & Implementation Details

The Dual-Mode System

The app features two distinct modes that share the same data but offer different interactions:

  1. Review Mode: The classic spaced-repetition style. You see the front, click to reveal, and self-assess.
  2. Quiz Mode: A gamified multiple-choice mode.

We implemented a updateModeUI() function that toggles visibility of specific DOM elements (like the "Reveal" button vs. the "Quiz Options" container) based on the currentMode.

Dynamic Quiz Generation

One of the coolest parts is how the quiz options are generated. We don't hardcode wrong answers. Instead, we dynamically pick "distractors" from the current deck at runtime.

function showQuizCard(correctCard: Flashcard) {
    // 1. Filter out the correct card to get a pool of wrong answers
    const otherCards = currentDeck.filter(c => c.jp !== correctCard.jp);

    // 2. Shuffle and pick 3 distractors
    const distractors = shuffle(otherCards).slice(0, 3);

    // 3. Combine with the correct answer and shuffle again
    const options = shuffle([correctCard, ...distractors]);

    // 4. Render buttons
    quizOptions.innerHTML = '';
    options.forEach(option => {
        // ... create button logic
    });
}
Enter fullscreen mode Exit fullscreen mode

The UI/UX

We didn't neglect the design. We used CSS Variables for consistent theming (Primary colors, Success/Error states) and Flexbox/Grid for layout.
We recently refactored the app to have a Menu View and a Game View.

  • Menu View: Displays a grid of available decks (Basics, Food, Travel, etc.).
  • Game View: The actual flashcard interface.

This required abstracting our initialization logic so we could load different datasets on the fly without reloading the page.

How Antigravity Helped

Working with Antigravity felt like having a senior developer constantly available to pair program.

  • Refactoring: When I asked to move from a simple dropdown to a visual card-based menu, Antigravity handled the HTML restructuring and CSS additions in one go.
  • Debugging: We hit a "Maximum call stack size exceeded" error when I tried to implement a custom logger that recursively called itself. Antigravity spotted the recursion immediately and suggested a fix using a proxy pattern for console.log.
  • Strictness: It respected my rule of keeping code comments in English, even though we conversed in French.

Source Code

You can find the full source code for this project on GitHub:
https://github.com/VincentCapek/flashhcards

Conclusion

This project was a great refresher on the fundamentals of Web Development. It proves that you don't always need a complex toolchain to build something useful and performant.

The result is a snappy, useful tool for my Japanese studies that I can easily extend with more vocabulary in the future !

Top comments (0)