diff --git a/src/components/App/App.js b/src/components/App/App.js index ce0d236..e3150fe 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -1,10 +1,29 @@ +import React from 'react'; + import Game from '../Game'; import Header from '../Header'; +const COLORBLIND_STORAGE_KEY = 'colorblindMode'; + function App() { + const [isColorblindMode, setIsColorblindMode] = React.useState(() => { + return window.localStorage.getItem(COLORBLIND_STORAGE_KEY) === 'true'; + }); + + function toggleColorblindMode() { + setIsColorblindMode((prev) => { + const next = !prev; + window.localStorage.setItem(COLORBLIND_STORAGE_KEY, next); + return next; + }); + } + return ( -
-
+
+
diff --git a/src/components/GuessCells/GuessCells.js b/src/components/GuessCells/GuessCells.js index 29c99bc..c3b19d4 100644 --- a/src/components/GuessCells/GuessCells.js +++ b/src/components/GuessCells/GuessCells.js @@ -13,10 +13,22 @@ function GuessCells({guess, answer}) { return `cell ${results[index].status}` } + function statusLabel(index) { + if (!results) { + return undefined; + } + + return results[index].status; + } + return (

{range(5).map((index) => ( - + { guess ? guess[index] : undefined} ))} diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index ce7c934..e6387df 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -1,9 +1,18 @@ import React from 'react'; -function Header() { +function Header({ isColorblindMode, toggleColorblindMode }) { return (

Word Game

+
); } diff --git a/src/styles.css b/src/styles.css index f54c1cf..9fdac61 100644 --- a/src/styles.css +++ b/src/styles.css @@ -17,6 +17,21 @@ html { --color-gray-900: hsl(0deg 0% 90%); } +/* + Colorblind-friendly palette. + + Green/yellow is the hardest combination for deuteranopia and + protanopia (red-green colorblindness), the most common forms. + We swap to a blue/orange scheme instead, which stays distinguishable + across all common types of color vision deficiency (including + tritanopia). Tile shapes/symbols are also added below so state never + depends on color perception alone. +*/ +.colorblind-mode { + --color-success: hsl(215deg 90% 45%); + --color-warning: hsl(30deg 100% 45%); +} + /* Custom styles */ @@ -66,10 +81,28 @@ h1 { text-align: center; } +.colorblind-toggle { + align-self: center; + margin-right: 16px; + padding: 6px 12px; + border: 1px solid var(--color-gray-500); + border-radius: 4px; + font-size: 0.85rem; + white-space: nowrap; +} +.colorblind-toggle[aria-pressed="true"] { + border-color: var(--color-success); + color: var(--color-success); +} + @media (max-width: 25rem) { h1 { font-size: 1.25rem; } + .colorblind-toggle { + font-size: 0.7rem; + padding: 4px 8px; + } } .game-wrapper { @@ -140,6 +173,30 @@ h1 { color: white; } +/* + Shape/symbol cues so tile state doesn't depend on color alone. + Only shown in colorblind mode to avoid cluttering the default look. +*/ +.colorblind-mode .cell.correct::after, +.colorblind-mode .cell.misplaced::after { + content: ''; + position: absolute; + bottom: 4px; + right: 4px; + width: 10px; + height: 10px; +} +.colorblind-mode .cell.correct::after { + /* Filled circle for correct */ + border-radius: 50%; + background: white; +} +.colorblind-mode .cell.misplaced::after { + /* Diamond for misplaced */ + background: white; + transform: rotate(45deg); +} + .guess-input-wrapper { display: flex; flex-direction: column;