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:
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);