This is how I built Mugen Quiz, an April Fool's day project.

This is how I built Mugen Quiz, an April Fool's day project.

On the. day before April Fool's day, I got a silly idea for the April Fool's day project. It's similar to Akinator except it only determines your favorite language, has only Yes and No, and never ends. I tried to do it simple way. Let's take a look at my project. The code is on this repo..

Project structure

Screen Shot 2565-04-02 at 21.32.09.png

The project is so simple to the point that I don't even create a folder for components. The page directory is for the home page and quiz page. The app.tsx file contains logic for this project. The data directory contains questions for the quiz.

I use CSS file for styling as there is no benefit using complicated system such as CSS Module or CSS-in-JS. Tailwind is a good alternative choice.

Source of app.tsx

import { Logo } from './logo'
import Router, { route, Route } from 'preact-router'
import { h } from 'preact'
import { useState } from 'preact/hooks'
import { Index } from './pages/index'
import { Quiz, QuizProps } from './pages/quiz'
import { questions } from './data/questions'

function getQuestion() {
  return questions[Math.floor(Math.random() * questions.length)]
}

export function App() {
  const [questionNumber, setQuestionNumber] = useState(1)
  const [started, setStarted] = useState(false)
  const [currentQuestion, setCurrentQuestion] = useState<string>(getQuestion())

  const handleNext = () => {
    setStarted(_ => true)
    setQuestionNumber(n => n + 1)
    setCurrentQuestion(oldQuestion => {
      let newQuestion: string
      do {
        newQuestion = getQuestion()
      } while (newQuestion == oldQuestion)
      return newQuestion
    })
    route(`/questions/${questionNumber}`)
  }
  return (
    <>
      <Router>
        <Route component={() => <Index onNext={handleNext} />} path="/" />
        <Route component={({ questionNumber }: Partial<QuizProps>) => <Quiz onNext={handleNext} questionNumber={questionNumber} currentQuestion={currentQuestion} started={started} />} path="/questions/:questionNumber" />
      </Router>
    </>
  )
}

Note that I use preact-router. It render components inside base on the path prop. It's not quite friendly for TypeScript because I would need to define the component to accept path props or have global definitions similar to how css props work. I don't know how to do it yet. Preact router provides Route component so I can use the router in TypeScript friendly way.

There is nothing interesting in Index page component. Let's take a look at Quiz page.

Source code of quiz.tsx

import { route } from "preact-router"
import { useEffect } from "preact/hooks"

export interface QuizProps {
    onNext: () => void,
    questionNumber: number | undefined,
    currentQuestion: string,
    started: boolean,
}

export function Quiz({ onNext, questionNumber, currentQuestion, started }: QuizProps) {
    useEffect(() => {
        if (!started) {
            // Go home. You haven't visit home yet.
            route("/", true)
        }
    }, [])
    return (
        <>
            <h1>Question {questionNumber}</h1>
            <p>{currentQuestion}</p>
            <button onClick={onNext}>Yes</button>
            <button onClick={onNext}>No</button>
        </>
    )
}

And that's the prank, no matter what you clicked, you only get another random question. Nothing is saved. Nothing is used to determine your favorite language.

This projected fooled my friends to think that the quiz will eventually end. I want this prank to be obvious so I put joke questions such as "What is your favorite TV show?" there.

Conclusion

This is a simple project created in a simple way. There is no need for CSS-in-JS, state management or advanced build tools configuration although I used Vite to simplify development setup. It can even be simplified more such as don't use any JavaScript framework.

I hope this post can be a example for anyone who have no idea what to build yet. I am looking forward to see your projects.