Skip to content

Commit 404c92e

Browse files
Merge branch 'feature/theme'
2 parents 9b7b447 + 85f65fb commit 404c92e

File tree

7 files changed

+173
-62
lines changed

7 files changed

+173
-62
lines changed

README.md

Lines changed: 42 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,54 @@
1-
# todo-react-app
2-
# React + TypeScript + Vite
1+
# MyTodo – Simple app to list things to do
32

4-
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
3+
A small yet complete to‑do application built with React and TypeScript. It showcases how to combine `useReducer` for state management, Context API for a light/dark theme, and `localStorage` for persistence.
54

6-
Currently, two official plugins are available:
5+
## Preview
76

8-
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
9-
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
7+
![MyTodo preview](./public/preview1.png)
8+
![MyTodo preview](./public/preview2.png)
109

11-
## Expanding the ESLint configuration
10+
## Features
11+
- Add, toggle (done/undone), and delete tasks
12+
- Automatic persistence in `localStorage`
13+
- Light/Dark theme via `ThemeContext` and a toggle button
14+
- Strict TypeScript typing (Task model, reducer actions, Theme)
15+
- Simple and responsive UI
1216

13-
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
17+
## Tech Stack
18+
- React 18 + Vite
19+
- TypeScript
20+
- Context API (theme) + `useReducer` (tasks)
21+
- `localStorage` (persistence)
1422

15-
```js
16-
export default tseslint.config([
17-
globalIgnores(['dist']),
18-
{
19-
files: ['**/*.{ts,tsx}'],
20-
extends: [
21-
// Other configs...
23+
## Installation
2224

23-
// Remove tseslint.configs.recommended and replace with this
24-
...tseslint.configs.recommendedTypeChecked,
25-
// Alternatively, use this for stricter rules
26-
...tseslint.configs.strictTypeChecked,
27-
// Optionally, add this for stylistic rules
28-
...tseslint.configs.stylisticTypeChecked,
25+
```bash
26+
# Clone the repository
27+
git clone https://github.com/eren-the-coder/todo-react-app.git
28+
cd todo-react-app
2929

30-
// Other configs...
31-
],
32-
languageOptions: {
33-
parserOptions: {
34-
project: ['./tsconfig.node.json', './tsconfig.app.json'],
35-
tsconfigRootDir: import.meta.dirname,
36-
},
37-
// other options...
38-
},
39-
},
40-
])
30+
# Install dependencies (choose one)
31+
npm install
32+
# or
33+
yarn
34+
# or
35+
pnpm install
4136
```
4237

43-
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
38+
## Development
4439

45-
```js
46-
// eslint.config.js
47-
import reactX from 'eslint-plugin-react-x'
48-
import reactDom from 'eslint-plugin-react-dom'
49-
50-
export default tseslint.config([
51-
globalIgnores(['dist']),
52-
{
53-
files: ['**/*.{ts,tsx}'],
54-
extends: [
55-
// Other configs...
56-
// Enable lint rules for React
57-
reactX.configs['recommended-typescript'],
58-
// Enable lint rules for React DOM
59-
reactDom.configs.recommended,
60-
],
61-
languageOptions: {
62-
parserOptions: {
63-
project: ['./tsconfig.node.json', './tsconfig.app.json'],
64-
tsconfigRootDir: import.meta.dirname,
65-
},
66-
// other options...
67-
},
68-
},
69-
])
40+
```bash
41+
npm run dev
42+
# or: yarn dev
43+
# or: pnpm dev
7044
```
45+
46+
Open the URL shown in the terminal (usually http://localhost:5173).
47+
48+
## Useful Scripts
49+
- `dev` – start the development server
50+
- `build` – generate the production build
51+
- `preview` – preview the production build locally
52+
53+
## Contributing
54+
Issues and pull requests are welcome. Suggestions for improvements (accessibility, tests, UI, etc.) are appreciated because they would help me improve as a young developer.

public/preview1.png

38 KB
Loading

public/preview2.png

37.8 KB
Loading

src/App.css

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,98 @@ li button {
113113
color: red;
114114
padding: 4px;
115115
cursor: pointer;
116+
background: transparent;
116117
}
117118
li button:hover {
118119
border-radius: 4px;
119120
background: rgba(192, 192, 192, 0.548);
121+
}
122+
123+
.header {
124+
display: flex;
125+
justify-content: space-between;
126+
align-items: center;
127+
gap: 1rem;
128+
margin-bottom: 1rem;
129+
}
130+
.header button {
131+
border-radius: .5rem;
132+
border: 1px solid transparent;
133+
background: #f3f4f6;
134+
color: #111827;
135+
padding: 6px 12px;
136+
font-weight: 600;
137+
cursor: pointer;
138+
}
139+
.header button:hover {
140+
background: #e5e7eb;
141+
}
142+
143+
.container.light {
144+
background: #ffffff;
145+
color: #222;
146+
border-color: #d1d5db;
147+
box-shadow: 0 4px 24px #b0b0b0;
148+
}
149+
150+
.container.dark {
151+
background: #111827; /* slate-900 */
152+
color: #e5e7eb; /* gray-200 */
153+
border-color: #374151; /* gray-700 */
154+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
155+
}
156+
157+
.container.dark h1 {
158+
color: #f3f4f6; /* gray-100 */
159+
text-shadow: none;
160+
}
161+
.container.dark h1::after {
162+
background: linear-gradient(90deg, #6b7280, #ffffff);
163+
}
164+
165+
.container.dark .input-group input {
166+
background: #1f2937; /* slate-800 */
167+
border-color: #4b5563; /* gray-600 */
168+
color: #e5e7eb; /* gray-200 */
169+
box-shadow: 0 0 8px rgba(0, 0, 0, 0.35);
170+
}
171+
.container.dark .input-group input:focus {
172+
outline: none;
173+
border-color: #9ca3af; /* gray-400 */
174+
background: #111827; /* slate-900 */
175+
}
176+
177+
.container.dark .input-group button {
178+
background: #e5e7eb; /* gray-200 */
179+
color: #111827; /* slate-900 */
180+
box-shadow: 0 0 8px rgba(0, 0, 0, 0.35);
181+
}
182+
.container.dark .input-group button:hover {
183+
background: #d1d5db; /* gray-300 */
184+
}
185+
186+
.container.dark hr {
187+
box-shadow: 0 0 8px rgba(0, 0, 0, 0.35);
188+
border-top: 1px solid #374151; /* gray-700 */
189+
}
190+
191+
.container.dark li {
192+
background: #1f2937; /* slate-800 */
193+
color: #e5e7eb; /* gray-200 */
194+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
195+
}
196+
.container.dark li button {
197+
color: #f87171; /* red-400 */
198+
}
199+
.container.dark li button:hover {
200+
background: rgba(255, 255, 255, 0.08);
201+
}
202+
203+
.container.dark .header button {
204+
background: #374151; /* gray-700 */
205+
color: #f3f4f6; /* gray-100 */
206+
border-color: #4b5563;
207+
}
208+
.container.dark .header button:hover {
209+
background: #4b5563; /* gray-600 */
120210
}

src/App.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { useEffect, useReducer, useState } from 'react'
1+
import { useContext, useEffect, useReducer, useState } from 'react'
22
import './App.css'
3+
import { ThemeContext } from './Theme';
34

45
const TODOS_STORAGE_KEY = 'todos'
56
interface Task {
@@ -11,6 +12,7 @@ interface Task {
1112
const initialState: Task[] = []
1213

1314
const App = () => {
15+
const { theme, setTheme } = useContext(ThemeContext)
1416
const [text, setText] = useState('');
1517
const [todos, dispatch] = useReducer(reducer, initialState)
1618

@@ -34,8 +36,15 @@ const App = () => {
3436
}
3537

3638
return (
37-
<div className='container'>
38-
<h1>MyTodo</h1>
39+
<div className={`container ${theme}`}>
40+
<div className="header">
41+
<h1>MyTodo</h1>
42+
<button
43+
onClick={() => setTheme(prev => (prev === 'light' ? 'dark' : 'light'))}
44+
>
45+
{theme === 'light' ? 'dark' : 'light'}
46+
</button>
47+
</div>
3948

4049
<div className="input-group">
4150
<input

src/Theme.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { createContext, useState, type ReactNode } from "react"
2+
3+
export type Theme = 'light' | 'dark'
4+
5+
type ThemeContextValue = {
6+
theme: Theme
7+
setTheme: React.Dispatch<React.SetStateAction<Theme>>
8+
}
9+
10+
export const ThemeContext = createContext<ThemeContextValue>({
11+
theme: 'light',
12+
setTheme: () => {},
13+
})
14+
15+
const ThemeProvider = ({ children } : { children: ReactNode }) => {
16+
const [theme, setTheme] = useState<Theme>('light')
17+
18+
return (
19+
<ThemeContext.Provider value={{theme, setTheme}}>
20+
{children}
21+
</ThemeContext.Provider>
22+
)
23+
}
24+
25+
export default ThemeProvider

src/main.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ import { StrictMode } from 'react'
22
import { createRoot } from 'react-dom/client'
33
import './index.css'
44
import App from './App'
5+
import ThemeProvider from './Theme'
56

67
createRoot(document.getElementById('root')!).render(
78
<StrictMode>
8-
<App />
9+
<ThemeProvider>
10+
<App />
11+
</ThemeProvider>
912
</StrictMode>,
1013
)

0 commit comments

Comments
 (0)