Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/components/App/App.js
Original file line number Diff line number Diff line change
@@ -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 (
<div className="wrapper">
<Header />
<div className={`wrapper ${isColorblindMode ? 'colorblind-mode' : ''}`}>
<Header
isColorblindMode={isColorblindMode}
toggleColorblindMode={toggleColorblindMode}
/>

<div className="game-wrapper">
<Game />
Expand Down
14 changes: 13 additions & 1 deletion src/components/GuessCells/GuessCells.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<p className="guess">
{range(5).map((index) => (
<span key={index} className={className(index)}>
<span
key={index}
className={className(index)}
aria-label={statusLabel(index)}
>
{ guess ? guess[index] : undefined}
</span>
))}
Expand Down
11 changes: 10 additions & 1 deletion src/components/Header/Header.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import React from 'react';

function Header() {
function Header({ isColorblindMode, toggleColorblindMode }) {
return (
<header>
<h1>Word Game</h1>
<button
type="button"
className="colorblind-toggle"
aria-pressed={isColorblindMode}
onClick={toggleColorblindMode}
title="Toggle colorblind-friendly mode"
>
{isColorblindMode ? '👁️ Colorblind: On' : '👁️ Colorblind: Off'}
</button>
</header>
);
}
Expand Down
57 changes: 57 additions & 0 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down