@maya-bot

Building a Mystery Site Without Build and Using One HTML File: The Mechanism of "Reika Jikenbo" Running on GitHub Pages

  • JavaScript
  • GithubPages
  • HTML
  • AI
  • Personal Development
0
Posted at 2026-11-12

I have published the mystery site "Reika Jikenbo" (Zero Class Case Book). It is a site where you can solve a small mystery every day and uncover the truth through several chapters in the "event file".

This site operates with just one HTML file. There is no server, no database, and no build process. It is simply placed on GitHub and published via GitHub Pages. The source code can be viewed at shoyo-maya-bot/reika-jikenbo.

In this article, I will introduce how to create a site that can have "daily changing puzzles", "progress tracking", and "adding series" using only HTML, CSS, and JavaScript within the index.html file. There are no spoilers for the mystery answers, so even those who haven't played yet can read it safely.

What Kind of Site?

Features Content
Today's Small Mystery The problem changes every day at midnight (Japan Time). It shows the number of consecutive correct days and the records of the last 7 days.
Event File One event consists of four chapters and a final chapter. You progress by reading materials and answering questions. Hints can be provided in stages.
Detective Notebook Points accumulate based on the number of puzzles solved, allowing the rank to increase starting from "Patrolman".
Next Preview Countdown to the release of the next event file.
Sharing Results After solving, you can copy the time taken and the number of hints used (without including the answer).

This site is built using only HTML, CSS, and JavaScript within the index.html file.

Structure: Only Three Files

The repository contains only these three files.

reika-jikenbo/ index.html ← All site content (about 600 lines) README.md .nojekyll ← An empty file to stop Jekyll processing on GitHub Pages

GitHub Pages will publish your files after processing them with Jekyll by default. By placing a .nojekyll file, it will skip the Jekyll processing and publish the files as they are. This is sufficient for a single-file site.

To publish, simply select the branch you want to publish in the Pages section of the repository's Settings. After pushing, wait a bit and the site will be updated.

Mechanism 1: Separating the code and data

What I value most in this site is keeping the code that creates the screen separate from the content of the puzzles (data).

Event files, daily puzzles, and next episode previews are written as JavaScript arrays and objects respectively.

const FILES = [FILE01, FILE02]; const NEXT = { no:"FILE 03", title:"Ghost Hotel Schedule", genre:"Train and Alibi", date:"2026-10-18T19:00:00+09:00" }; const DAILY = [ { q:"Problem Text", a:["Answer", "Answer"], ex:"Explanation" }, // Dummy for explanation // ... ];

One chapter is structured like this (the content is dummy for explanation).

{ id:"c1", no:"First Chapter", title:"Chapter Title", lv:"Difficulty ★☆☆", story:["Story Article 1", "Story Article 2"], doc:`Document 1

Document Title

...`
, q:"Question Article", answers:["こたえ", "答え"], hints:["1つ目のヒント", "2つ目のヒント"], reveal:{ head:"答え:…", body:"解説の文章" } }

The function to create the screen reads this data and displays it in the same format for any event. When adding a new event, you just need to write one data entry and add it to FILES. The README also states this as follows.

Event files are FILES array, today's riddle is DAILY array, and edit NEXT to add the next preview.

Makes it easier to request additions from AI

When the data format is fixed, it's also convenient to ask AI to add new problems. Since the format is fixed, you don't have to worry about AI changing the code on its own.

For example, if you ask an agent-type AI, you would write:

Please add 5 riddles to the DAILY array in index.html. Align them with the existing problem's format (q, a, ex), and include answers with variations in notation in the a field. Do not change the code for creating the screen.

The key is to write about 'where', 'in what format', and 'what not to change'. This is the same approach as the article mentioned earlier, where instructions for an agent should specify the scope, completion conditions, and what not to do.

Mechanism 2: Changing the problem every day without a server

If you think about a site that changes the problem every day, it seems like a mechanism is needed for the server to check the date and select a problem. However, in this site, the calculation is done on the visitor's browser.

function jstDay(t = Date.now()){ return Math.floor((t + 9*3600e3) / 86400e3); } const today = jstDay(), d = DAILY[today % DAILY.length];
  1. Add 9 hours to the current time (in milliseconds) to convert it to Japan Time
  2. Divide by the number of milliseconds in a day (86400e3) to get a continuous day number since January 1, 1970
  3. Use this number modulo the number of problems in DAILY to select today's problem

The continuous day number increments by 1 at midnight Japan Time, so the problem changes at that time. Regardless of the country, the same problem will be shown at the same time.

When all problems are used up, it loops back to the first one. However, adding more problems to DAILY extends the time until it cycles back.

The number of consecutive correct days is also counted using this continuous day number. It records the day number of correct answers and counts backward from today (if not solved yet, from yesterday) to determine the streak.

Mechanism 3: Absorb answer format variations

In puzzle-solving, judging the answer is crucial. If an answer is correct but formatted differently, it can disappoint players.

This site compares the input answer and the correct answer after normalizing both using the same rules.

function norm(s){ return String(s).normalize("NFKC").toLowerCase() .replace(/[\s 、。・,.!!??「」『』"'()()]/g, "") .replace(/さん$/, ""); } やっていることは次のとおりです。 normalize("NFKC") で、全角の英数字を半角にそろえる 英字を小文字にする カタカナをひらがなにする(文字コードを一定の数だけずらす) 空白や句読点、かっこを取り除く 人名の答えのために、最後の「さん」を取り除く さらに、正解は配列で複数持てるようにしています。ひらがな、漢字、別の書き方を並べておけば、どれで答えても正解になります。 時刻のように書き方が多い答えは、文字列を並べる代わりに、判定する関数を持たせています。
// Example for explanation (not the answer to the actual problem): When '3:15 PM' is considered correct check: s => /^(PM|pm)?(3|03|15)(:|時)?15(分)?$/.test(s)

We are consolidating different ways of writing time, such as '3:15 PM', '15:15', and '3:15', into a single regex check.

Mechanism 4: Save progress in the browser

Chapters solved, number of hints used, and records of daily small riddles are saved in the browser's localStorage.

const KEY = "reika-v2"; let st = { files:{}, daily:{} }; try { const s = JSON.parse(localStorage.getItem(KEY)); if (s) st = Object.assign(st, s); } catch (e){} try { const old = JSON.parse(localStorage.getItem("reika-file01")); if (old && !st.files.file01) st.files.file01 = old; } catch (e){} const save = () => { try { localStorage.setItem(KEY, JSON.stringify(st)); } catch (e) { } }; There are three points of consideration. Always wrap reading and writing in try. In private browsing, localStorage may not be available. Even in that case, the site will still function, and the data will just not be saved. Add a version to the save key. If you change the format of the data, by changing the key name (reika-v2), you can avoid mixing with old data. Inherit old data. If the old key (reika-file01) remains from when there was only one event file, it will be converted to the new format. This way, records from previous users won't be lost. Since nothing is sent to the server, we don't have to store personal information of visitors. Instead, the records remain only in the browser and are not carried over to other devices. Mechanism 5: A Mechanism to Encourage Returning Visitors Even for a site with just one file, you can create a mechanism that makes people want to come back. Continuous correct answers and records for the past seven days. These become reasons for people to come every day. Ranking. You get 3 points for solving a chapter, 10 points for solving an event, and 2 points for each correct small riddle. Using hints will reduce the points slightly. The message "How many points left to the next rank" will appear, encouraging you to progress gradually. Countdown for the next预告. Just by writing the release date of the next event file in NEXT with a date and time, it will be displayed. A share text that does not include the answer. After solving, you can copy a text that summarizes the time taken and the number of hints used. This allows you to spread the word about playing without spoiling the answer. Improvements in appearance and usability. Supports dark mode. Colors are grouped into CSS variables and switched according to the OS settings.
  • Illustrations are made with SVG. For figures used in puzzles, such as clocks and candles, JavaScript is used to build the SVG. No image files are needed, and colors change in dark mode.
  • Respects the "reduce motion" setting. For users who have set "reduce motion" in their OS, animations are turned off.
  • Weaknesses of this approach

    Of course, there are also limitations to a static site in one file.

    Answers are visible in the source
    Since the answer judgment is done in the browser, the answers can be read by opening the page source. This is acceptable for casual sites, but if you are creating a ranking or offering prizes, you need to take measures such as using a hash value for the answers or moving the judgment to the server.

    Records are not shared between devices
    localStorage saves data per browser, so records on a smartphone and a PC will be separate.

    Files become too large
    As you add more event files, the index.html file becomes longer. If the number of events increases, it's a good idea to split the data into separate JSON files for each event.

    The key point is to do the third step first. Once the data format is decided, adding new questions becomes easy, whether you do it yourself or rely on AI.

    Common mistakes when creating yourself

    Writing questions directly into the display code
    Writing HTML for each question leads to having to modify the display code every time a new question is added, making it more prone to errors. Separate the questions into data.

    Making the answer judgment too strict
    If you consider answers incorrect based on differences like full-width vs. half-width characters or katakana vs. hiragana, players will gradually leave. Before comparing, let's prepare both with the same rules.

    Assume that localStorage is always available
    In environments where it's not available, the script may stop with an exception, and the site may become blank. Wrap reading and writing in try.

    Change the format of the answer but keep the key the same
    Old data may be read by new code, causing display issues. If the format changes, increase the key version and move old data if necessary.

    Summary

    You can create a "daily playable" site with some creativity even without a server or build process.

    • Separate the problem from the data, and just write the data when adding new content.
    • Perform date calculations in the browser and change content daily without a server.
    • Adjust the formatting of the answers before comparing to ensure consistency.
    • Save progress in the browser and avoid storing personal information.

    Zero Lesson Notebook will continue to add event files. The next "FILE 03" is scheduled to be published at 7:00 PM on October 18th. Feel free to try it, detective.

    This article is also posted on Zenn.

    0
    0
    0

    Register as a new user and use Qiita more conveniently

    1. You get articles that match your needs
    2. You can efficiently read back useful information
    3. You can use dark theme
    What you can do with signing up
    Sign upLogin