diff --git a/README.md b/README.md
index d1c68b5..9b152e8 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
-# Todo
\ No newline at end of file
+GoyoTasks is a clean, minimal, and easy-to-use web-based to-do app that helps you organize your tasks and boost productivity.
+
+Live Demo: https://goyotasks.netlify.app/
diff --git a/index.html b/index.html
index f7ac4e4..73a32c0 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,13 @@
-
Todo
+
+
+
+ Goyo Tasks
diff --git a/package.json b/package.json
index caf6289..1bba4b6 100644
--- a/package.json
+++ b/package.json
@@ -10,18 +10,24 @@
"preview": "vite preview"
},
"dependencies": {
+ "@tailwindcss/vite": "^4.1.18",
"react": "^19.0.0",
- "react-dom": "^19.0.0"
+ "react-dom": "^19.0.0",
+ "react-icons": "^5.5.0",
+ "zustand": "^5.0.10"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
+ "autoprefixer": "^10.4.23",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.18",
"vite": "^6.2.0"
}
}
diff --git a/src/App.jsx b/src/App.jsx
index 5427540..30acc75 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,5 +1,22 @@
+import { AddTodo } from "./components/AddTodo";
+import { TaskCounter } from "./components/TaskCounter";
+import { TodoList } from "./components/TodoList";
+
export const App = () => {
return (
- React Boilerplate
- )
-}
+
+ );
+};
diff --git a/src/assets/goyologo.svg b/src/assets/goyologo.svg
new file mode 100644
index 0000000..96f400e
--- /dev/null
+++ b/src/assets/goyologo.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/components/AddTodo.jsx b/src/components/AddTodo.jsx
new file mode 100644
index 0000000..14aea84
--- /dev/null
+++ b/src/components/AddTodo.jsx
@@ -0,0 +1,35 @@
+import { useState } from "react";
+import useTodoStore from "./store/useTodoStore";
+
+export const AddTodo = () => {
+ const [text, setText] = useState("");
+ const addTask = useTodoStore((state) => state.addTask);
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ addTask(text);
+ setText("");
+ };
+
+ return (
+
+ );
+};
diff --git a/src/components/EmptyState.jsx b/src/components/EmptyState.jsx
new file mode 100644
index 0000000..2a9ece4
--- /dev/null
+++ b/src/components/EmptyState.jsx
@@ -0,0 +1,7 @@
+export const EmptyState = () => {
+ return (
+
+
No tasks yet. Add one above to get started.
+
+ );
+};
diff --git a/src/components/TaskCounter.jsx b/src/components/TaskCounter.jsx
new file mode 100644
index 0000000..b1cff04
--- /dev/null
+++ b/src/components/TaskCounter.jsx
@@ -0,0 +1,13 @@
+import useTodoStore from "./store/useTodoStore";
+
+export const TaskCounter = () => {
+ const tasks = useTodoStore((state) => state.tasks);
+ const completedCount = tasks.filter((task) => task.completed).length;
+ const totalCount = tasks.length;
+
+ return (
+
+ {completedCount} of {totalCount} tasks completed
+
+ );
+};
diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx
new file mode 100644
index 0000000..e0f418d
--- /dev/null
+++ b/src/components/TodoItem.jsx
@@ -0,0 +1,28 @@
+import { FiTrash2 } from "react-icons/fi";
+import useTodoStore from "./store/useTodoStore";
+
+export const TodoItem = ({ task }) => {
+ const toggleTask = useTodoStore((state) => state.toggleTask);
+ const removeTask = useTodoStore((state) => state.removeTask);
+
+ return (
+
+ toggleTask(task.id)}
+ className="w-4 h-4 accent-primary"
+ />
+
+ {task.text}
+
+ removeTask(task.id)}
+ aria-label="Delete task"
+ className="text-text-muted hover:text-red-500 opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity"
+ >
+
+
+
+ );
+};
diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx
new file mode 100644
index 0000000..4faa911
--- /dev/null
+++ b/src/components/TodoList.jsx
@@ -0,0 +1,19 @@
+import useTodoStore from "./store/useTodoStore";
+import { TodoItem } from "./TodoItem";
+import { EmptyState } from "./EmptyState";
+
+export const TodoList = () => {
+ const tasks = useTodoStore((state) => state.tasks);
+
+ if (tasks.length === 0) {
+ return ;
+ }
+
+ return (
+
+ {tasks.map((task) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/store/useTodoStore.js b/src/components/store/useTodoStore.js
new file mode 100644
index 0000000..b038a95
--- /dev/null
+++ b/src/components/store/useTodoStore.js
@@ -0,0 +1,38 @@
+import { create } from "zustand";
+
+const useTodoStore = create((set) => ({
+ tasks: [],
+
+ addTask: (text) => {
+ if (!text.trim()) return;
+
+ set((state) => ({
+ tasks: [
+ {
+ id: Date.now(),
+ text,
+ completed: false,
+ },
+ ...state.tasks,
+ ],
+ }));
+ },
+
+ toggleTask: (id) => {
+ set((state) => ({
+ tasks: state.tasks.map((task) =>
+ task.id === id
+ ? { ...task, completed: !task.completed }
+ : task
+ ),
+ }));
+ },
+
+ removeTask: (id) => {
+ set((state) => ({
+ tasks: state.tasks.filter((task) => task.id !== id),
+ }));
+ },
+}));
+
+export default useTodoStore;
diff --git a/src/index.css b/src/index.css
index f7c0aef..bc985a9 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,3 +1,19 @@
-:root {
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+@import "tailwindcss";
+
+@theme {
+ --color-bg: #F8F7F4;
+ --color-surface: #F1F0EC;
+ --color-border: #DAD8D2;
+ --color-text: #1F2328;
+ --color-text-muted: #6B7075;
+ --color-primary: #4A5B6A;
+ --color-primarymuted: #E4E9ED;
}
+
+@layer base {
+ html, body {
+ font-family: "DM Sans", system-ui, -apple-system, BlinkMacSystemFont,
+ "Segoe UI", sans-serif;
+ }
+}
+
diff --git a/vite.config.js b/vite.config.js
index ba24244..d9e4dfd 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,7 +1,8 @@
+import tailwindcss from '@tailwindcss/vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [react()]
+ plugins: [tailwindcss(), react()]
})