number-master

web application to practice korean numbers
git clone git://brookjeynes.dev/bjeynes/number-master.git
Log | Files | Refs | README | LICENSE

commit e4a7b703f42a74b466c93bfdf61d4fcb5991d5b4
parent d9425ffcb8d56d028449c03870fe4309e4a67df9
Author: brookjeynes <me@brookjeynes.dev>
Date:   Thu,  4 Jun 2026 09:06:03 +1000

feat: add hint text for irregulars

Signed-off-by: brookjeynes <me@brookjeynes.dev>

Diffstat:
Mindex.html | 12++++++++++++
Mminimal.css | 11++++++++++-
Msrc/store.js | 43+++++++++++++++++++++++++++++++++++++++++++
Mstyle.css | 16++++++++++++++++
4 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/index.html b/index.html @@ -102,6 +102,18 @@ '-incorrect': $store.app.questionState === 'incorrect' }" /> </label> + <template class="hint" x-if="$store.app.hint !== null"> + <template x-for="group in $store.app.hint"> + <div> + <span class="section-label -muted" x-text="group.label"></span> + <ul class="hint-list"> + <template x-for="item in group.items"> + <li x-text="item"></li> + </template> + </ul> + </div> + </template> + </template> <div class="form-actions"> <button type="submit" class="-primary" x-text="$store.app.questionState === 'undecided' ? '확인' : '계속'"></button> diff --git a/minimal.css b/minimal.css @@ -1,7 +1,7 @@ /* BSD Zero Clause License -Copyright (c) 2026-present Brook Jeynes <me@brookjeynes.dev> +Copyright (c) 2026 Brook Jeynes <me@brookjeynes.dev> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. @@ -105,6 +105,15 @@ button { } } +.section-label { + font-size: 14px; + font-weight: bold; + + &.-muted { + color: var(--secondary); + } +} + .alert-box { font-size: 14px; diff --git a/src/store.js b/src/store.js @@ -40,6 +40,9 @@ import { hourStringFromNumber } from "./time.js"; * @property {number} minute */ +/** @type {number} */ +const HINT_THRESHOLD = 3; + const DEFAULT_CONFIG = { mode: "number", min: 1, @@ -70,6 +73,10 @@ window.Alpine.store("app", { }, /** @type {Date} */ date: new Date(), + /** @type {number} */ + wrongCount: 0, + /** @type {string[] | null} */ + hint: null, init() { this.configSaved = false; @@ -170,6 +177,34 @@ window.Alpine.store("app", { } }, + /** @returns {string} */ + get questionHint() { + switch (this.tab) { + case "sino": + case "native": + return null; + case "time": + return [{ + label: "시간 불규칙", + items: [ + "하나시 → 한시", + "둘시 → 두시", + "셋시 → 세시", + "넷시 → 네시", + "열하나시 → 열한시", + "열둘시 → 열두시" + ] + }]; + case "date": + return [ + { label: "연도 불규칙", items: ["'천' 앞에서는 '일'이 생략됩니다"] }, + { label: "월 불규칙", items: ["십월 → 시월", "육월 → 유월"] } + ]; + default: + throw new Error("unknown tab selected"); + } + }, + /** @param {Event} event */ submit(event) { event.preventDefault(); @@ -181,6 +216,8 @@ window.Alpine.store("app", { if (this.questionState !== "undecided") { this.newQuestion(); event.target.input.value = ""; + this.wrongCount = 0; + this.hint = null; this.questionState = "undecided"; return } @@ -194,6 +231,9 @@ window.Alpine.store("app", { ) { this.questionState = "correct"; } else { + this.wrongCount++; + if (this.wrongCount === HINT_THRESHOLD) this.hint = this.questionHint; + const el = document.getElementById('input'); el.classList.add('-wrong-flash'); el.addEventListener('animationend', () => el.classList.remove('-wrong-flash'), { once: true }); @@ -202,6 +242,7 @@ window.Alpine.store("app", { skip() { if (this.questionState !== "undecided") return + this.hint = this.questionHint; this.questionState = "incorrect"; }, @@ -232,6 +273,8 @@ window.Alpine.store("app", { */ changeTab(tab) { this.questionState = "undecided"; + this.hint = null; + this.wrontCount = 0; this.tab = tab; this.newQuestion(); }, diff --git a/style.css b/style.css @@ -1,3 +1,18 @@ +.hint { + margin-top: 5px; + display: flex; + flex-direction: column; + gap: 4px; +} + +.hint-list { + list-style: none; + padding: 0; + margin: 0; + font-size: 14px; + color: var(--secondary); +} + .answer-input { &.-correct { border-bottom-color: var(--green); @@ -48,6 +63,7 @@ fieldset label+label { } @keyframes input-shake { + 0%, 100% { transform: translateX(0);