diff --git a/src/App.css b/src/App.css index 74b5e05..119112d 100644 --- a/src/App.css +++ b/src/App.css @@ -1,38 +1,3 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } +html.dark { + background-color: rgb(15, 23, 42); } diff --git a/src/App.test.tsx b/src/App.test.tsx index 8bc9bb3..0397eec 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -2,6 +2,22 @@ import React from 'react' import { render, screen } from '@testing-library/react' import App from './App' +beforeEach(() => { + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // deprecated + removeListener: jest.fn(), // deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), + }) +}) + test('renders App component', () => { render() const linkElement = screen.getByText(/Not Wordle/) diff --git a/src/App.tsx b/src/App.tsx index f0816d2..611e5f7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,8 @@ -import { InformationCircleIcon } from '@heroicons/react/outline' -import { ChartBarIcon } from '@heroicons/react/outline' +import { + InformationCircleIcon, + ChartBarIcon, + SunIcon, +} from '@heroicons/react/outline' import { useState, useEffect } from 'react' import { Alert } from './components/alerts/Alert' import { Grid } from './components/grid/Grid' @@ -15,9 +18,15 @@ import { saveGameStateToLocalStorage, } from './lib/localStorage' +import './App.css' + const ALERT_TIME_MS = 2000 function App() { + const prefersDarkMode = window.matchMedia( + '(prefers-color-scheme: dark)' + ).matches + const [currentGuess, setCurrentGuess] = useState('') const [isGameWon, setIsGameWon] = useState(false) const [isInfoModalOpen, setIsInfoModalOpen] = useState(false) @@ -26,6 +35,13 @@ function App() { const [isStatsModalOpen, setIsStatsModalOpen] = useState(false) const [isWordNotFoundAlertOpen, setIsWordNotFoundAlertOpen] = useState(false) const [isGameLost, setIsGameLost] = useState(false) + const [isDarkMode, setIsDarkMode] = useState( + localStorage.getItem('theme') + ? localStorage.getItem('theme') === 'dark' + : prefersDarkMode + ? true + : false + ) const [successAlert, setSuccessAlert] = useState('') const [guesses, setGuesses] = useState(() => { const loaded = loadGameStateFromLocalStorage() @@ -44,6 +60,19 @@ function App() { const [stats, setStats] = useState(() => loadStats()) + useEffect(() => { + if (isDarkMode) { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + }, [isDarkMode]) + + const handleDarkMode = (isDark: boolean) => { + setIsDarkMode(isDark) + localStorage.setItem('theme', isDark ? 'dark' : 'light') + } + useEffect(() => { saveGameStateToLocalStorage({ guesses, solution }) }, [guesses]) @@ -114,13 +143,17 @@ function App() { return (
-

Not Wordle

+

Not Wordle

+ handleDarkMode(!isDarkMode)} + /> setIsInfoModalOpen(true)} /> setIsStatsModalOpen(true)} />
diff --git a/src/components/grid/Cell.tsx b/src/components/grid/Cell.tsx index 0461527..4aa8bdd 100644 --- a/src/components/grid/Cell.tsx +++ b/src/components/grid/Cell.tsx @@ -8,13 +8,16 @@ type Props = { export const Cell = ({ value, status }: Props) => { const classes = classnames( - 'w-14 h-14 border-solid border-2 flex items-center justify-center mx-0.5 text-lg font-bold rounded', + 'w-14 h-14 border-solid border-2 flex items-center justify-center mx-0.5 text-lg font-bold rounded dark:text-white', { - 'bg-white border-slate-200': !status, - 'border-black': value && !status, - 'bg-slate-400 text-white border-slate-400': status === 'absent', + 'bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-600': + !status, + 'border-black dark:border-slate-100': value && !status, + 'bg-slate-400 dark:bg-slate-700 text-white border-slate-400 dark:border-slate-700': + status === 'absent', 'bg-green-500 text-white border-green-500': status === 'correct', - 'bg-yellow-500 text-white border-yellow-500': status === 'present', + 'bg-yellow-500 dark:bg-yellow-700 text-white border-yellow-500 dark:border-yellow-700': + status === 'present', 'cell-animation': !!value, } ) diff --git a/src/components/keyboard/Key.tsx b/src/components/keyboard/Key.tsx index 1a5cbd1..21eb067 100644 --- a/src/components/keyboard/Key.tsx +++ b/src/components/keyboard/Key.tsx @@ -19,13 +19,14 @@ export const Key = ({ onClick, }: Props) => { const classes = classnames( - 'flex items-center justify-center rounded mx-0.5 text-xs font-bold cursor-pointer select-none', + 'flex items-center justify-center rounded mx-0.5 text-xs font-bold cursor-pointer select-none dark:text-white', { - 'bg-slate-200 hover:bg-slate-300 active:bg-slate-400': !status, + 'bg-slate-200 dark:bg-slate-600 hover:bg-slate-300 active:bg-slate-400': + !status, 'bg-slate-400 text-white': status === 'absent', 'bg-green-500 hover:bg-green-600 active:bg-green-700 text-white': status === 'correct', - 'bg-yellow-500 hover:bg-yellow-600 active:bg-yellow-700 text-white': + 'bg-yellow-500 hover:bg-yellow-600 active:bg-yellow-700 dark:bg-yellow-700 text-white': status === 'present', } ) diff --git a/src/components/modals/AboutModal.tsx b/src/components/modals/AboutModal.tsx index 7934c52..1d3345a 100644 --- a/src/components/modals/AboutModal.tsx +++ b/src/components/modals/AboutModal.tsx @@ -8,7 +8,7 @@ type Props = { export const AboutModal = ({ isOpen, handleClose }: Props) => { return ( -

+

This is an open source clone of the game Wordle -{' '} { leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > -

+
handleClose()} />
@@ -57,7 +57,7 @@ export const BaseModal = ({ title, children, isOpen, handleClose }: Props) => {
{title} diff --git a/src/components/modals/InfoModal.tsx b/src/components/modals/InfoModal.tsx index 3fc0f32..4cecba5 100644 --- a/src/components/modals/InfoModal.tsx +++ b/src/components/modals/InfoModal.tsx @@ -9,7 +9,7 @@ type Props = { export const InfoModal = ({ isOpen, handleClose }: Props) => { return ( -

+

Guess the WORDLE in 6 tries. After each guess, the color of the tiles will change to show how close your guess was to the word.

@@ -21,7 +21,7 @@ export const InfoModal = ({ isOpen, handleClose }: Props) => {
-

+

The letter W is in the word and in the correct spot.

@@ -32,7 +32,7 @@ export const InfoModal = ({ isOpen, handleClose }: Props) => {
-

+

The letter L is in the word but in the wrong spot.

@@ -43,7 +43,7 @@ export const InfoModal = ({ isOpen, handleClose }: Props) => {
-

+

The letter U is not in the word in any spot.

diff --git a/src/components/modals/StatsModal.tsx b/src/components/modals/StatsModal.tsx index 770c6cc..0bdf4b1 100644 --- a/src/components/modals/StatsModal.tsx +++ b/src/components/modals/StatsModal.tsx @@ -35,16 +35,16 @@ export const StatsModal = ({ return ( -

+

Guess Distribution

{(isGameLost || isGameWon) && ( -
+
New word in
diff --git a/src/components/stats/Histogram.tsx b/src/components/stats/Histogram.tsx index f496309..ed0c24f 100644 --- a/src/components/stats/Histogram.tsx +++ b/src/components/stats/Histogram.tsx @@ -10,7 +10,7 @@ export const Histogram = ({ gameStats }: Props) => { const maxValue = Math.max(...winDistribution) return ( -
+
{winDistribution.map((value, i) => ( { return ( -
+
{value}
{label}
diff --git a/tailwind.config.js b/tailwind.config.js index 5259866..400a2aa 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,6 +1,7 @@ module.exports = { content: ['./src/**/*.{js,jsx,ts,tsx}'], - theme: { + darkMode: 'class', + theme: { extend: {}, }, plugins: [],