Demo
Setup
Create app
npx degit sveltejs/template svelte-todo-app
cd into the folder-
cd svelte-todo-app
Install dependencies
npm install # npm
yarn install # yarn
Start app
npm run dev # npm
yarn dev # yarn
Cleanup
I don't want the default stylings, so I will replace the stylings in globals.css
with this-
* {
margin: 0;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
Building the container
In App. svelte
let's create the starter code for our app.
<script>
</script>
<main class="container">
</main>
<style>
.container {
display: flex;
flex-direction: column;
align-items: center;
min-height: 90vh;
background: #222e50
url(https://images.unsplash.com/photo-1579546929518-9e396f3cc809?ixid=MXwxMjA3fDB8MHxzZWFyY2h8N3x8Z3JhZGllbnR8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60)
no-repeat;
background-size: cover;
padding-top: 10vh;
}
</style>
Building the form to submit Todos
Inside our main container, add a form component with input and button-
<main class="container">
<div>
<form on:submit|preventDefault={addTodo}>
<input
bind:value={newItem}
type="task"
class="todos__input"
placeholder="Enter Todo"
/>
<button class="todos__button">+</button>
</form>
</div>
</main>
We need to create the following things now-
A variable to store the input value
A
addTodo
function for adding the todos.
So in the script tag add the following-
let newItem = "";
let todoList = [];
function addTodo() {
if (newItem !== "") {
todoList = [
...todoList,
{
task: newItem,
completed: false,
},
];
newItem = "";
}
console.log(todoList);
}
Styling
Now let's style our submit button and the input. Inside the styles, tag add this-
.todos__input {
background-color: inherit;
border: none;
box-shadow: none;
text-decoration: none;
font-size: 1.2rem;
border-bottom: 1px solid black;
margin-top: 15px;
outline: none;
width: 500px;
}
.todos__button {
background-color: inherit;
border: none;
box-shadow: none;
font-size: 1.2rem;
cursor: pointer;
}
If we now add an item, it will add the item to the list and console log it.
Rendering the todos
In React like we have a map function, we do it through #each
in Svelte
{#each todoList as item, index}
<div class="todo">
<span class="todo__text">{item.task}</span>
</div>
{/each}
Styling the todos
.todo {
display: flex;
padding: 20px;
border-radius: 20px;
box-shadow: 0 0 15px rgb(0 0 0 / 20%);
background-color: hsla(0, 0%, 100%, 0.2);
-webkit-backdrop-filter: blur(25px);
backdrop-filter: blur(25px);
width: inherit;
margin-top: 15px;
font-size: 1.2rem;
justify-content: space-between;
align-items: center;
}
This will give the todos a glassmorphic look 🤩
Adding a header for the todos
The to-do list and the inputs look kinda clumsy, so let's add a header there-
After the form component add this h2 tag-
<h2 class="todos__listHeader">Todos</h2>
The stylings for this header-
.todos__listHeader {
text-align: center;
padding: 20px;
border-radius: 20px;
box-shadow: 0 0 15px rgb(0 0 0 / 20%);
background-color: hsla(0, 0%, 100%, 0.2);
-webkit-backdrop-filter: blur(25px);
backdrop-filter: blur(25px);
margin: 15px 0px 25px 0px;
font-size: 1.2rem;
}
Now we have got a pretty good separation there.
Creating the complete and delete functionality
We are going to use icons for delete and complete, so let's get the icons first.
- Create
Icons. svelte
file in the src directory. Add the following piece of code, for the icons. Feel free to change the icons :) -
<script>
export let name;
export let width = "1.5rem";
export let height = "1.5rem";
export let focusable = false;
let icons = [
{
box: 24,
name: "check-mark",
svg: `<svg focusable="false" viewBox="0 0 24 24" aria-hidden="true"><path d="M19.77 4.93l1.4 1.4L8.43 19.07l-5.6-5.6 1.4-1.4 4.2 4.2L19.77 4.93m0-2.83L8.43 13.44l-4.2-4.2L0 13.47l8.43 8.43L24 6.33 19.77 2.1z"></path></svg>`,
},
{
box: 32,
name: "delete",
svg: `<svg focusable="false" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM8 9h8v10H8V9zm7.5-5l-1-1h-5l-1 1H5v2h14V4h-3.5z"></path></svg>`,
},
];
let displayIcon = icons.find((e) => e.name === name);
</script>
<svg
class={$$props.class}
{focusable}
{width}
{height}
viewBox="0 0 {displayIcon.box} {displayIcon.box}">{@html displayIcon.svg}</svg
>
Show the icons
Inside the todo, I will add a div with two buttons that have the icons as follows-
{#each todoList as item, index}
<div class="todo">
<span class="todo__text">{item.task}</span>
<div class="icons">
<button
class="icon__button"
on:click={() => (item.completed = !item.completed)}
>
<Icons name="check-mark" class="icon" />
</button>
<button class="icon__button" on:click={() => removeFromList(index)}>
<Icons name="delete" class="icon" />
</button>
</div>
</div>
{/each}
Import the icons like this-
import Icons from "./Icons.svelte";
Creating the delete function-
function removeFromList(index) {
todoList.splice(index, 1);
todoList = todoList;
}
Styling the buttons
Add the following styles to get a beautiful icon button-
.icon__button {
background-color: transparent;
border: none;
box-shadow: none;
font-size: 1.2rem;
cursor: pointer;
color: rgba(0, 0, 0, 0.54);
}
.icon {
background: rgba(0, 0, 0, 0.54);
}
Striking the Text
Add this optional class to the item. task span, so if the item is completed then it will add the class-
<span
class={`todo__text ${item.completed ? "todo__checked--strike" : ""}`}
>{item.task}</span
>
Now, we need to add the styles to strike it-
.todo__checked--strike {
text-decoration: line-through;
}
This function takes an argument of index
and splices the todoList to remove the item.
We have successfully built a to-do app in Svelte! 🥳🎉
To extend your knowledge about svelte check out this video by James Q Quick where he will show you how to create a todo app with Sveltekit and tailwind CSS!