diff --git a/src/App.css b/src/App.css
index 69bc7ba..eae8b23 100644
--- a/src/App.css
+++ b/src/App.css
@@ -44,6 +44,10 @@ body {
user-select: none;
}
+table.Game-rows {
+ margin: auto;
+}
+
.Game-keyboard {
display: flex;
flex-direction: column;
@@ -171,7 +175,8 @@ a:active {
font-variant-numeric: tabular-nums;
}
-.Game-sr-feedback {
+.Game-sr-feedback,
+.sr-only {
position: absolute;
left: -10000px;
top: auto;
diff --git a/src/Game.tsx b/src/Game.tsx
index 3770520..eabdea2 100644
--- a/src/Game.tsx
+++ b/src/Game.tsx
@@ -1,10 +1,10 @@
import { useEffect, useState } from "react";
import { Row, RowState } from "./Row";
import dictionary from "./dictionary.json";
-import { Clue, clue } from "./clue";
+import { Clue, clue, describeClue } from "./clue";
import { Keyboard } from "./Keyboard";
import targetList from "./targets.json";
-import { dictionarySet, pick, resetRng, seed } from "./util";
+import { dictionarySet, pick, resetRng, seed, speak } from "./util";
enum GameState {
Playing,
@@ -57,6 +57,7 @@ function Game(props: GameProps) {
if (/^[a-z]$/.test(key)) {
setCurrentGuess((guess) => (guess + key).slice(0, wordLength));
setHint("");
+ setSrStatus("");
} else if (key === "Backspace") {
setCurrentGuess((guess) => guess.slice(0, -1));
setHint("");
@@ -81,7 +82,7 @@ function Game(props: GameProps) {
setGameState(GameState.Lost);
} else {
setHint("");
- setSrStatus("Feedback goes here");
+ speak(describeClue(clue(currentGuess, target)));
}
}
};
@@ -99,7 +100,7 @@ function Game(props: GameProps) {
}, [currentGuess, gameState]);
let letterInfo = new Map();
- const rowDivs = Array(props.maxGuesses)
+ const tableRows = Array(props.maxGuesses)
.fill(undefined)
.map((_, i) => {
const guess = [...guesses, currentGuess][i] ?? "";
@@ -156,7 +157,7 @@ function Game(props: GameProps) {
}}
>
- {rowDivs}
- {hint || `\u00a0`}
-
+
+ {hint || `\u00a0`}
+ {/*
{srStatus}
-
+
*/}
{seed ? (
diff --git a/src/Keyboard.tsx b/src/Keyboard.tsx
index ceb1c24..3413d53 100644
--- a/src/Keyboard.tsx
+++ b/src/Keyboard.tsx
@@ -13,7 +13,7 @@ export function Keyboard(props: KeyboardProps) {
];
return (
-
+
{keyboard.map((row, i) => (
{row.map((label, j) => {
@@ -29,6 +29,7 @@ export function Keyboard(props: KeyboardProps) {
{
props.onKey(label);
diff --git a/src/Row.tsx b/src/Row.tsx
index 342c16d..6c9fa49 100644
--- a/src/Row.tsx
+++ b/src/Row.tsx
@@ -1,4 +1,4 @@
-import { Clue, clueClass, CluedLetter } from "./clue";
+import { Clue, clueClass, CluedLetter, clueWord } from "./clue";
export enum RowState {
LockedIn,
@@ -24,20 +24,22 @@ export function Row(props: RowProps) {
letterClass += " " + clueClass(clue);
}
return (
-
+
{letter}
-
+ |
);
});
let rowClass = "Row";
if (isLockedIn) rowClass += " Row-locked-in";
- return (
-
- {letterDivs}
-
- );
+ return
{letterDivs}
;
}
diff --git a/src/clue.ts b/src/clue.ts
index eeaa94e..c93940f 100644
--- a/src/clue.ts
+++ b/src/clue.ts
@@ -39,3 +39,19 @@ export function clueClass(clue: Clue): string {
return "letter-correct";
}
}
+
+export function clueWord(clue: Clue): string {
+ if (clue === Clue.Absent) {
+ return "no";
+ } else if (clue === Clue.Elsewhere) {
+ return "yellow";
+ } else {
+ return "correct";
+ }
+}
+
+export function describeClue(clue: CluedLetter[]): string {
+ return clue
+ .map(({ letter, clue }) => letter.toUpperCase() + " " + clueWord(clue!))
+ .join(", ");
+}
diff --git a/src/util.ts b/src/util.ts
index eab07fe..62dad2e 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -24,3 +24,24 @@ export function resetRng(): void {
export function pick
(array: Array): T {
return array[Math.floor(array.length * random())];
}
+
+// https://a11y-guidelines.orange.com/en/web/components-examples/make-a-screen-reader-talk/
+export function speak(
+ text: string,
+ priority: "polite" | "assertive" = "assertive"
+) {
+ var el = document.createElement("div");
+ var id = "speak-" + Date.now();
+ el.setAttribute("id", id);
+ el.setAttribute("aria-live", priority || "polite");
+ el.classList.add("sr-only");
+ document.body.appendChild(el);
+
+ window.setTimeout(function () {
+ document.getElementById(id)!.innerHTML = text;
+ }, 100);
+
+ window.setTimeout(function () {
+ document.body.removeChild(document.getElementById(id)!);
+ }, 1000);
+}