This is a solution to the Todo app challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.
Note: Delete this note and update the table of contents based on what sections you keep.
Users should be able to:
- View the optimal layout for the app depending on their device's screen size
- See hover states for all interactive elements on the page
- Add new todos to the list
- Mark todos as complete
- Delete todos from the list
- Filter by all/active/complete todos
- Clear all completed todos
- Toggle light and dark mode
- Bonus: Drag and drop to reorder items on the list
- Solution URL: Add solution URL here
- Live Site URL: Todo App
- Semantic HTML5 markup
- Sass
- Flexbox
- Mobile-first workflow
- React - JS library
- react-beautiful-dnd - a library that allows drag and drop for lists in React
This project helped me to get more familiar with React, especially the useState and useEffect hooks.
This was also my first time using Sass in a React project, which proved to be very straightforward.
For me, the most challenging part of the project was implementing the Drag and Drop feature. Even though it was an optional feature of this challenge, I wanted to give a go and add it to the final app. After some research, I decided to use the existing react-beautiful-dnd library. It provided a simple and effective way to implement this feature.
Another feature I was dreading was the light and dark mode toggle, simply because I had not done it. I broke down this feature into small steps:
- First, I wrote all the styles in the
style.scss
file. Using the light theme as the default, I simply created a.dark
class that overwrites the properties that needed to change when switching themes:
.dark {
background-colour: $clr-dark-neutral-700;
background-image: url("../public/images/bg-mobile-dark.jpg");
colour: $clr-dark-neutral-200;
/* .... */
}
-
Then, I implemented the
ThemeSwitcher
component, which is rendered by the mainApp
component and is responsible to display the appropriate symbol to change the colour palette. -
The
App
component is responsible to handle the changing of the theme and applies the class of the selected theme in thehandleChangeMode
-
After having the basic functionality working, I wanted to somehow persist the user's choice so that when they refresh the page (or return to it) it would still display their desired mode. To achieve this, I made use of JavaScript's
localStorage
property. With this property, we can set a new item with the selected mode, and then retrieve it when the user refreshes the page or opens a new instance of the app.
const [mode, setMode] = useState(null);
// run on load to get the stored theme
useEffect(() => {
const storedMode = localStorage.getItem("mode");
if (storedMode) {
setMode(storedMode);
if (storedMode === "dark") {
document.body.classList.add("dark");
}
} else {
setMode("light");
}
}, []);
// setting the colour mode
function handleChangeMode() {
if (mode === "dark") {
setMode("light");
localStorage.setItem("mode", "light");
} else {
setMode("dark");
localStorage.setItem("mode", "dark");
}
document.body.classList.toggle("dark");
}
- Finally, I wanted to add some more detail to this feature. Previously, I stated that by design the Light Mode was the default one, but these days so many people have their devices set with a dark mode (myself included!), so I thought it would have been a nice touch to have this application detecting the user preference even if they are using the app for the first time. This also required some research, but quickly I found out about the
prefers-colour-scheme
CSS media query, which allows us to define CSS rules depending on the colour-scheme the user has set on their device. However, I wanted a Javascript approach and not a CSS one, because even if the user hasprefers-colour-scheme
set todark
on their device they could have chosen to use the app in light mode, or vice-versa. To check this, I used thewindow.matchMedia
Javascript interface, and use it to check if the user has theprefers-colour-scheme
media query set todark
, and change the theme accordingly.
// run on load to get the stored theme
useEffect(() => {
const storedMode = localStorage.getItem("mode");
if (storedMode) {
setMode(storedMode);
if (storedMode === "dark") {
document.body.classList.add("dark");
}
} else if (storedMode === "light") {
setMode("light");
} else if (
window.matchMedia &&
window.matchMedia("(prefers-colour-scheme: dark)").matches
) {
setMode("dark");
} else {
setMode("light");
}
}, []);
I look forward to keeping using React on my future projects, exploring more of its features and expanding my knowledge of hooks.
For this specific project, I intend to build a back-end using Node and Express and make it a full-stack web application.
-
W3Schools - For general reference and React examples.
-
MDN Web Docs - For general JS reference.
-
Lifting State Up - This article helped me to overcome the problem of sharing data between sibling components.
-
How do I detect dark mode using JavaScript? - This helped me to learn how to detect the user's prefered colour scheme.
-
Beautiful and Accessible Drag and Drop with react-beautiful-dnd - This quick course helped me get started using react-beautiful-dnd.
- Website - coming soon
- Frontend Mentor - @carlaalmeida