diff --git a/App.js b/App.js
new file mode 100644
index 0000000..ac98622
--- /dev/null
+++ b/App.js
@@ -0,0 +1,26 @@
+import {useState}from 'react';
+
+function App() {
+ const [time, setTime] = useState(1);
+ const handleClick = () => {
+ let newTime;
+ if (time >= 22){
+ newTime =1;
+ }else {
+ newTime = time +1;
+ }
+ setTime(newTime);
+ };
+
+ console.log(' 업데이트');
+
+
+ return (
+
+ 현재 시각 : {time}시
+ Update
+
+ );
+}
+
+export default App;
\ No newline at end of file
diff --git a/bg-desktop-dark.jpg b/bg-desktop-dark.jpg
new file mode 100644
index 0000000..394ebb9
Binary files /dev/null and b/bg-desktop-dark.jpg differ
diff --git a/bg-desktop-light.jpg b/bg-desktop-light.jpg
new file mode 100644
index 0000000..1b5f3bb
Binary files /dev/null and b/bg-desktop-light.jpg differ
diff --git a/bg-mobile-dark.jpg b/bg-mobile-dark.jpg
new file mode 100644
index 0000000..3285a35
Binary files /dev/null and b/bg-mobile-dark.jpg differ
diff --git a/bg-mobile-light.jpg b/bg-mobile-light.jpg
new file mode 100644
index 0000000..9df5c53
Binary files /dev/null and b/bg-mobile-light.jpg differ
diff --git a/favicon-32x32.png b/favicon-32x32.png
new file mode 100644
index 0000000..1e2df7f
Binary files /dev/null and b/favicon-32x32.png differ
diff --git a/html/.vscode/Oval.png b/html/.vscode/Oval.png
new file mode 100644
index 0000000..f9266fb
Binary files /dev/null and b/html/.vscode/Oval.png differ
diff --git a/html/.vscode/Oval2.png b/html/.vscode/Oval2.png
new file mode 100644
index 0000000..bdb9980
Binary files /dev/null and b/html/.vscode/Oval2.png differ
diff --git a/html/.vscode/Oval3.png b/html/.vscode/Oval3.png
new file mode 100644
index 0000000..061e86b
Binary files /dev/null and b/html/.vscode/Oval3.png differ
diff --git a/html/.vscode/assets/Oval.png b/html/.vscode/assets/Oval.png
new file mode 100644
index 0000000..f9266fb
Binary files /dev/null and b/html/.vscode/assets/Oval.png differ
diff --git a/html/.vscode/assets/Oval2.png b/html/.vscode/assets/Oval2.png
new file mode 100644
index 0000000..bdb9980
Binary files /dev/null and b/html/.vscode/assets/Oval2.png differ
diff --git a/html/.vscode/assets/Oval3.png b/html/.vscode/assets/Oval3.png
new file mode 100644
index 0000000..061e86b
Binary files /dev/null and b/html/.vscode/assets/Oval3.png differ
diff --git a/html/.vscode/assets/dark.jpg b/html/.vscode/assets/dark.jpg
new file mode 100644
index 0000000..394ebb9
Binary files /dev/null and b/html/.vscode/assets/dark.jpg differ
diff --git a/html/.vscode/assets/light.jpg b/html/.vscode/assets/light.jpg
new file mode 100644
index 0000000..1b5f3bb
Binary files /dev/null and b/html/.vscode/assets/light.jpg differ
diff --git a/html/.vscode/assets/stars.png b/html/.vscode/assets/stars.png
new file mode 100644
index 0000000..85bdfd0
Binary files /dev/null and b/html/.vscode/assets/stars.png differ
diff --git a/html/.vscode/assets/top-image.png b/html/.vscode/assets/top-image.png
new file mode 100644
index 0000000..07a03a8
Binary files /dev/null and b/html/.vscode/assets/top-image.png differ
diff --git a/html/.vscode/layout.css b/html/.vscode/layout.css
new file mode 100644
index 0000000..dc3b110
--- /dev/null
+++ b/html/.vscode/layout.css
@@ -0,0 +1,244 @@
+.background {
+ display: flex;
+ width: 100vw;
+ height: 100vh;
+ background: #fff;
+ position: relative;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+body {
+ font-family: 'League Spartan', sans-serif;
+ margin: 0; /* 기본 여백 제거 */
+}
+
+.path {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ max-width: 80vw; /* 최대 너비를 화면의 80%로 제한 */
+ max-height: 80vh; /* 최대 높이를 화면의 80%로 제한 */
+ width: auto; /* 자동 너비 조정 */
+ height: auto; /* 자동 높이 조정 */
+}
+
+.path2 {
+ position: absolute;
+ top: 0;
+ left: 0;
+ max-width: 80vw; /* 최대 너비를 화면의 80%로 제한 */
+ max-height: 80vh; /* 최대 높이를 화면의 80%로 제한 */
+ width: auto; /* 자동 너비 조정 */
+ height: auto; /* 자동 높이 조정 */
+}
+
+.title-container {
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ top: 50px;
+ left: 165px;
+ width: 445px;
+ color: #512051;
+ margin-bottom: 20px;
+}
+
+.title {
+ font-size: 61px;
+ font-weight: 800;
+ line-height: 48px;
+ letter-spacing: -2px;
+ margin-bottom: 15px;
+}
+
+.subtitle {
+ color: #927B91;
+ font-size: 24px;
+ font-weight: 500;
+ margin-bottom: 30px;
+ line-height: 25px;
+}
+
+.container2 {
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ top: 118px;
+ left: 700px;
+ width: 445px;
+ gap: 20px;
+ color: #512051;
+}
+
+.box_s {
+ display: flex;
+ align-items: center;
+ background: #F7F2F7;
+ border-radius: 8px;
+ width: 100%;
+ height: 56px;
+ gap: 32.45px;
+}
+
+.star {
+ margin-left: 32px;
+ height: auto;
+}
+
+.container {
+ display: flex;
+ flex-direction: row;
+ position: absolute;
+ top: 434px;
+ align-items: center;
+ width: calc(100% - 330px);
+ gap: 20px;
+}
+
+.box_B {
+ display: flex;
+ flex-direction: column;
+ width: 350px;
+ height: 225px;
+ background: #512051;
+ border-radius: 8px;
+ padding: 20px;
+ color: white;
+}
+.box_B_content {
+ display: flex;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+.img {
+ width: 50px;
+ height: 50px;
+ margin-left: 32PX;
+ border-radius: 50%;
+ margin-top: 30px;
+ margin-right: 23px;
+}
+
+.b_title_subtitle {
+ display: flex;
+ flex-direction: column;
+ margin-top: 30px;
+
+}
+.b_title {
+ font-size: 17px;
+ font-weight: 700;
+ margin-bottom: 4px;
+}
+
+.bolder {
+ font-weight: bold;
+}
+
+.b_subtitle {
+ font-size: 17px;
+ color: #EE69A4;
+ font-weight: 400;
+}
+
+.b_text {
+ margin : 22px;
+ width: 85%;
+ font-size: 16.5px;
+ font-weight: 500;
+ line-height: 22px;
+
+}
+
+/* 미디어 쿼리 추가로 반응형 디자인 설정 */
+@media (max-width: 375px) {
+ body {
+
+ margin: 0; /* 기본 여백 제거 */
+ }
+ .title-container {
+ align-items: center;
+ text-align: center;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+ .title{
+ font-size: 60px;
+ }
+ .subtitle {
+ margin-bottom: 39px;
+ font-size: 19px;
+ width : 335px;
+ }
+ .path {
+ top: 1401.396;
+ }
+
+ .container2 {
+ position: relative;
+ top: 10px;
+ left: auto;
+ width: 85%;
+ height : 25%;
+ margin-top: 337px;
+ margin-bottom: 237px;
+ gap: 20px;
+ align-items: center;
+
+ }
+ .container2 span{
+ margin-top : -20px;
+ }
+
+ .star {
+ margin-top : 16px;
+ margin-right : 16px;
+
+ }
+
+ .container {
+ flex-direction: column;
+ align-content: center;
+ margin-top: 260px;
+ width: 78%;
+ height : 25%;
+
+ }
+ .box_s{
+ display : flex;
+ flex-direction: column;
+ align-items: center;
+ height : 95px;
+ }
+
+ .box_B {
+ height: 23vh;
+ width: 77vw;
+ margin-bottom: 0px;
+ align-content: center;
+ padding-left: 12px;
+ padding-right: 12px;
+
+ }
+ .img{
+ margin-top : 15px;
+ margin-left : 10px;
+ }
+ .b_title_subtitle{
+ margin-top : 15px;
+ }
+ .b_title{
+ margin-bottom: 8px;
+ }
+ .b_text{
+ margin-top : 10px;
+ font-size: 16px;
+ width : 93%;
+ align-items: center;
+ margin-left : 10px;
+ }
+}
+
diff --git a/html/.vscode/layout.html b/html/.vscode/layout.html
new file mode 100644
index 0000000..38dbba7
--- /dev/null
+++ b/html/.vscode/layout.html
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+ layout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
10,000+ of our users love our products.
+ We only provide great products combined with excellent customer service. See what our satisfied customers are saying about our services.
+
+
+
+
+
+
Rated 5 Stars in Reviews
+
+
+
+
Rated 5 Stars in Report Guru
+
+
+
+
Rated 5 Stars in BestTech
+
+
+
+
+
+
+
+
+
Colton Smith
+
Verified Buyer
+
+
+
“We needed the same printed design as the one we had ordered a week prior. Not only did they find the original order, but we also received it in time. Excellent!”
+
+
+
+
+
+
Irene Roberts
+
Verified Buyer
+
+
+
“Customer service is always excellent and very quick turn around. Completely delighted with the simplicity of the purchase and the speed of delivery.”
+
+
+
+
+
+
Anne Wallace
+
Verified Buyer
+
+
+
“Put an order with this company and can only praise them for the very high standard. Will definitely use them again and recommend to everyone!”
+
+
+
+
+
diff --git a/html/.vscode/recipe.css b/html/.vscode/recipe.css
new file mode 100644
index 0000000..9b68a9f
--- /dev/null
+++ b/html/.vscode/recipe.css
@@ -0,0 +1,133 @@
+.container {
+ display: flex;
+ max-width: 1440px;
+ padding: var(--spacing-1600, 128px) var(--spacing-600, 48px);
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ background: #F3E5D7;
+ margin: auto;
+}
+.white_box {
+ display: flex;
+ width: 736px;
+ padding: 40px;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 10px;
+ flex-shrink: 0;
+ border-radius: 24px;
+ background: #FFF;
+}
+img {
+ height: 300px;
+ align-self: stretch;
+ border-radius: 12px;
+}
+.headtitle {
+ color: #312E2C;
+ font-family: 'Young Serif', serif;
+ font-size: 40px;
+ line-height: 100%;
+ align-self: stretch;
+ padding-top: 40px;
+ padding-bottom: 20px;
+}
+.subtitle {
+ color: #5F564D;
+ font-family: 'Outfit', sans-serif;
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 150%;
+ align-self: stretch;
+ padding-bottom: 30px;
+}
+.pink_box {
+ font-family: 'Young Serif', serif;
+ display: flex;
+ padding: 24px;
+ flex-direction: column;
+ align-items: flex-start;
+ border-radius: 12px;
+ background: #FFF7FB;
+ width: 92%;
+ height: 150px;
+}
+.title_pink {
+ align-self: stretch;
+ color: #7A284E;
+ font-family: 'Outfit', sans-serif;
+ font-size: 20px;
+ font-weight: 600;
+ line-height: 100%;
+}
+.Red_title {
+ align-self: stretch;
+ color: #854632;
+ font-family: 'Young Serif', serif;
+ font-size: 28px;
+ line-height: 100%;
+ padding-top: 30px;
+ padding-bottom: 10px;
+}
+.content {
+ line-height: 150%;
+ font-size: var(--font-size-desktop-text-preset-4, 18px);
+ color: #5F564D;
+ margin-bottom: 16px;
+ font-family: 'Outfit', sans-serif;
+ font-weight: normal;
+}
+.nutrition {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ color: #5F564D;
+ font-family: 'Outfit', sans-serif;
+ font-size: 16px;
+ font-weight: 400;
+ width: 100%;
+ line-height: 150%;
+}
+.nutrition-info {
+ flex: 1;
+ text-align: left;
+ margin-left: 5%;
+ padding-right: 20px;
+}
+.nutrition-item {
+ color: #854632;
+ font-family: 'Outfit', sans-serif;
+ font-weight: 700;
+ text-align: left;
+ margin-left: auto;
+ margin-right: 45%;
+ min-width: 60px;
+}
+.bolder {
+ font-weight: bolder;
+ color: #5F564D;
+ margin-bottom: 10px;
+}
+.line {
+ width: 720px;
+ height: 1px;
+ align-self: stretch;
+ background: #D8D3CD;
+ margin: 8px 0px;
+}
+.bullet {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ line-height: 150%;
+ margin-left: -30px;
+}
+.bullet-icon {
+ width: 4px;
+ height: 4px;
+ flex-shrink: 0;
+ border-radius: 50%;
+ background: #854632;
+ margin-top: -14px;
+}
diff --git a/html/.vscode/recipe.html b/html/.vscode/recipe.html
new file mode 100644
index 0000000..c5cd3bb
--- /dev/null
+++ b/html/.vscode/recipe.html
@@ -0,0 +1,107 @@
+
+
+
+
+
+ Recipe
+
+
+
+
+
+
+
+
Simple Omelette Recipe
+
An easy and quick dish, perfect for any meal. This classic omelette combines beaten eggs cooked to perfection, optionally filled with your choice of cheese, vegetables, or meats.
+
+
Ingredients
+
+
+
+ 2-3 large eggs
+
+
+
+ Salt, to taste
+
+
+
+ Pepper, to taste
+
+
+
+ 1 tablespoon of butter or oil
+
+
+
+ Optional fillings: cheese, diced vegetables, cooked meats, herbs
+
+
+
+
Instructions
+
+
+
+ 1.
+ Beat the eggs: In a bowl, beat the eggs with a pinch of salt and pepper until they are well mixed. You can add a tablespoon of water or milk for a fluffier texture.
+
+
+ 2.
+ Heat the pan: Place a non-stick frying pan over medium heat and add butter or oil.
+
+
+ 3.
+ Cook the omelette: Once the butter is melted and bubbling, pour in the eggs. Tilt the pan to ensure the eggs evenly coat the surface.
+
+
+ 4.
+ Add fillings (optional): When the eggs begin to set at the edges but are still slightly runny in the middle, sprinkle your chosen fillings over one half of the omelette.
+
+
+ 5.
+ Fold and serve: Carefully fold the omelette in half and cook for an additional minute. Slide it onto a plate and serve warm.
+
+
+
+
+
Nutrition
+
The table below shows nutritional values per serving without the additional fillings.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/html/.vscode/stars.png b/html/.vscode/stars.png
new file mode 100644
index 0000000..85bdfd0
Binary files /dev/null and b/html/.vscode/stars.png differ
diff --git a/html/.vscode/todo.css b/html/.vscode/todo.css
new file mode 100644
index 0000000..19d7e2e
--- /dev/null
+++ b/html/.vscode/todo.css
@@ -0,0 +1,277 @@
+/* General Styles */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ font-family: 'Outfit', sans-serif;
+}
+
+body {
+ min-height: 100vh; /* 화면의 최소 높이를 설정하여 스크롤이 가능하게 함 */
+ background: #FAFAFA;
+ display: flex;
+ flex-direction: column; /* 세로로 정렬 */
+ justify-content: flex-start; /* 내용이 화면 위에 정렬되도록 */
+ align-items: center;
+ overflow: auto; /* 전체 페이지가 스크롤 되도록 설정 */
+}
+
+/* 배경 이미지 */
+.background-image {
+ position: absolute;
+ top: 0;
+ background: url('assets/light.jpg') lightgray no-repeat;
+ background-position: center;
+ flex-shrink: 0;
+ z-index: -1;
+}
+
+/* task */
+.todo-app {
+ width: 540px;
+ flex-shrink: 0;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ margin-top: 24px;
+ margin-bottom : 49px;
+ padding-top: 0;
+ display: flex;
+ flex-direction: column;
+ height: auto; /* 내용에 맞게 높이를 자동으로 조정 */
+ max-height: calc(100vh - 80px); /* 최대 높이를 화면 높이에 맞게 설정 */
+ overflow-y: auto; /* 내용이 넘칠 경우 스크롤 가능 */
+}
+
+.container {
+ margin: 0;
+ margin-top: 50px;
+ padding: 0;
+ width: 540px;
+ justify-content: space-between;
+}
+
+/* 제목과 테마 토글을 같은 줄에 배치하기 위해 .title-container 생성 */
+.title-container {
+ flex-direction: row;
+ width: 541px;
+ height: 48px;
+ flex-shrink: 0;
+ justify-content: space-between;
+ align-items: center;
+ padding-top: 0; /* 패딩 제거 */
+ display: flex;
+ flex-shrink: 0;
+ margin-bottom: 40px;
+}
+
+.title {
+ color: #FFF;
+ font-family: "Josefin Sans";
+ font-size: 40px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: 15px;
+}
+
+/* theme toggle 버튼을 title의 오른쪽에 24px 간격으로 배치 */
+.theme-toggle {
+ width: 25.113px;
+ height: 26px;
+ flex-shrink: 0;
+ top : 11px;
+ fill: #FFF;
+}
+
+#new-task {
+ width: 100%;
+ padding: 1rem;
+ border: 1px solid hsl(30, 54%, 90%);
+ border-radius: 8px;
+ margin-top: 1rem;
+ color: #393A4B;
+ font-family: "Josefin Sans";
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.25px;
+}
+
+/* Container for the tasks */
+#taskList {
+ display: flex;
+ flex-direction: column;
+ gap: 10px; /* Adjust the space between tasks */
+ padding: 10px;
+ max-height: none; /* Ensure that the height expands based on content */
+ overflow: visible; /* Allow the container to grow */
+ margin-top: 1rem; /* task 항목 위에 여백 추가 */
+}
+
+.task {
+ display: flex;
+ align-items: center;
+ padding: 0.75rem;
+ border-bottom: 1px solid #E3E4F1;
+ font-size: 18px;
+}
+
+.task-completed {
+ text-decoration: line-through;
+ color: #D1D2DA;
+ font-family: "Josefin Sans";
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.25px;
+}
+
+.task-controls {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-left : 24px;
+ margin-bottom : 20px;
+ margin-right : 24px;
+ margin-top: 16px;
+}
+
+#remaining-tasks {
+ color: #9495A5;
+ font-family: "Josefin Sans";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+.filter-btn {
+ color: #9495A5;
+ font-family: "Josefin Sans";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+ background: none;
+ border: none;
+ cursor: pointer;
+}
+
+.filter-btn.active {
+ color: #3A7CFD;
+ font-weight: bold;
+}
+
+.filter-btn:hover {
+ color: #3A7CFD;
+}
+
+#clear-completed {
+ color: #9495A5;
+ background: none;
+ border: none;
+ cursor: pointer;
+}
+
+#clear-completed:hover {
+ color: #3A7CFD;
+}
+
+.filter {
+ width : 166px;
+ justify-content: space-between;
+}
+
+/* 하단 텍스트 */
+.footer {
+ font-size: 14px;
+ color: #999;
+ text-align: center;
+ margin-top: 24px;
+ padding: 20px;
+ background-color: #FAFAFA;
+ width: 100%;
+}
+
+/* Dark Mode */
+
+/* 전체 배경 */
+body.dark-mode {
+ background: #171823;
+}
+
+body.dark-mode .background-image {
+ background: url('assets/dark.jpg') lightgray no-repeat;
+}
+
+/* create a new todo .. box */
+body.dark-mode #new-task {
+ border-radius: 5px;
+ background: #25273D;
+ box-shadow: 0px 35px 50px -15px rgba(0, 0, 0, 0.50);
+ border:0px;
+ color: #C8CBE7;
+}
+
+/* task 박스 */
+body.dark-mode .todo-app {
+ border-radius: 5px;
+ background: #25273D;
+ box-shadow: 0px 35px 50px -15px rgba(0, 0, 0, 0.50);
+}
+
+body.dark-mode .task {
+ color: #C8CBE7;
+ font-family: "Josefin Sans";
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.25px;
+ border-bottom: 1px solid #393A4B;
+}
+
+/* Hover Effects */
+.task:hover .delete-icon {
+ display: inline;
+ width: 18px;
+ height: 18px;
+ flex-shrink: 0;
+}
+
+/* 삭제 아이콘 숨기기.hover 안되었을 때 */
+.delete-icon {
+ display: none;
+ cursor: pointer;
+ margin-left: auto;
+}
+
+input {
+ margin-right: 24px;
+}
+
+input:hover {
+ flex-shrink: 0;
+}
+
+.custom-checkbox {
+ appearance: none;
+ width: 24px;
+ height: 24px;
+ background-image: url('assets/circle1.png'); /* 기본 이미지 */
+ background-size: cover;
+ cursor: pointer;
+}
+
+.custom-checkbox:hover {
+ background-image: url('assets/circle2.png'); /* 호버 이미지 */
+}
+
+.custom-checkbox:checked {
+ background-image: url('assets/circle3.png'); /* 클릭(체크됨) 이미지 */
+}
diff --git a/html/.vscode/todo.html b/html/.vscode/todo.html
new file mode 100644
index 0000000..5f505be
--- /dev/null
+++ b/html/.vscode/todo.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+ Todo App
+
+
+
+
+
+
+
+
+
TODO
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
0 items left
+
+ All
+ Active
+ Completed
+
+
Clear Completed
+
+
+
+
+
+
+
+
diff --git a/html/.vscode/todo.js b/html/.vscode/todo.js
new file mode 100644
index 0000000..ecc4830
--- /dev/null
+++ b/html/.vscode/todo.js
@@ -0,0 +1,133 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const newTaskInput = document.getElementById('new-task');
+ const taskList = document.getElementById('task-list');
+ const remainingTasks = document.getElementById('remaining-tasks');
+ const filterButtons = document.querySelectorAll('.filter-btn');
+ const clearCompletedButton = document.getElementById('clear-completed');
+ const themeToggle = document.querySelector('.theme-toggle');
+ const lightModeIcon = document.querySelector('.light-mode-icon');
+ const darkModeIcon = document.querySelector('.dark-mode-icon');
+
+ let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
+ let darkMode = JSON.parse(localStorage.getItem('darkMode')) || false;
+
+ const updateTaskList = () => {
+ taskList.innerHTML = '';
+ tasks.forEach((task, index) => {
+ const taskItem = document.createElement('li');
+ taskItem.classList.add('task');
+ taskItem.innerHTML = `
+
+ ${task.text}
+
+
+
+`;
+ taskList.appendChild(taskItem);
+
+ const checkbox = taskItem.querySelector('input[type="checkbox"]');
+ const taskText = taskItem.querySelector('span');
+ checkbox.addEventListener('click', () => toggleTaskCompletion(index));
+ taskText.addEventListener('click', () => toggleTaskCompletion(index));
+
+ const deleteIcon = taskItem.querySelector('.delete-icon');
+ deleteIcon.addEventListener('click', () => deleteTask(index));
+ });
+
+ const remainingCount = tasks.filter(task => !task.completed).length;
+ remainingTasks.textContent = `${remainingCount} item${remainingCount !== 1 ? 's' : ''} left`;
+ localStorage.setItem('tasks', JSON.stringify(tasks));
+ };
+
+ const toggleTaskCompletion = (index) => {
+ tasks[index].completed = !tasks[index].completed;
+ updateTaskList();
+ };
+
+ const deleteTask = (index) => {
+ tasks.splice(index, 1);
+ updateTaskList();
+ };
+
+ const addTask = (e) => {
+ if (e.key === 'Enter' && newTaskInput.value.trim() !== '') {
+ tasks.push({
+ text: newTaskInput.value,
+ completed: false
+ });
+ newTaskInput.value = '';
+ updateTaskList();
+ }
+ };
+
+ const toggleTheme = () => {
+ darkMode = !darkMode;
+ localStorage.setItem('darkMode', JSON.stringify(darkMode));
+ document.body.classList.toggle('dark-mode', darkMode);
+
+ if (darkMode) {
+ lightModeIcon.style.display = 'none';
+ darkModeIcon.style.display = 'inline';
+ } else {
+ lightModeIcon.style.display = 'inline';
+ darkModeIcon.style.display = 'none';
+ }
+ };
+
+ const filterTasks = (filter) => {
+ filterButtons.forEach(btn => btn.classList.remove('active'));
+ const activeFilterButton = document.getElementById(`filter-${filter}`);
+ activeFilterButton.classList.add('active');
+
+ const filteredTasks = tasks.filter(task => {
+ if (filter === 'all') return true;
+ if (filter === 'active') return !task.completed;
+ if (filter === 'completed') return task.completed;
+ });
+
+ taskList.innerHTML = '';
+ filteredTasks.forEach((task, index) => {
+ const taskItem = document.createElement('li');
+ taskItem.classList.add('task');
+ taskItem.innerHTML = `
+
+ ${task.text}
+
+
+
+ `;
+ taskList.appendChild(taskItem);
+
+ const checkbox = taskItem.querySelector('input[type="checkbox"]');
+ const taskText = taskItem.querySelector('span');
+ checkbox.addEventListener('click', () => toggleTaskCompletion(index));
+ taskText.addEventListener('click', () => toggleTaskCompletion(index));
+
+ const deleteIcon = taskItem.querySelector('.delete-icon');
+ deleteIcon.addEventListener('click', () => deleteTask(index));
+ });
+ };
+
+ const clearCompleted = () => {
+ tasks = tasks.filter(task => !task.completed);
+ updateTaskList();
+ };
+
+ newTaskInput.addEventListener('keydown', addTask);
+ clearCompletedButton.addEventListener('click', clearCompleted);
+ themeToggle.addEventListener('click', toggleTheme);
+ document.getElementById('filter-all').addEventListener('click', () => filterTasks('all'));
+ document.getElementById('filter-active').addEventListener('click', () => filterTasks('active'));
+ document.getElementById('filter-completed').addEventListener('click', () => filterTasks('completed'));
+
+ if (darkMode) {
+ document.body.classList.add('dark-mode');
+ lightModeIcon.style.display = 'none';
+ darkModeIcon.style.display = 'inline';
+ } else {
+ lightModeIcon.style.display = 'inline';
+ darkModeIcon.style.display = 'none';
+ }
+
+ updateTaskList();
+});
diff --git a/html/.vscode/todo/images/bg-desktop-dark.jpg b/html/.vscode/todo/images/bg-desktop-dark.jpg
new file mode 100644
index 0000000..394ebb9
Binary files /dev/null and b/html/.vscode/todo/images/bg-desktop-dark.jpg differ
diff --git a/html/.vscode/todo/images/bg-desktop-light.jpg b/html/.vscode/todo/images/bg-desktop-light.jpg
new file mode 100644
index 0000000..1b5f3bb
Binary files /dev/null and b/html/.vscode/todo/images/bg-desktop-light.jpg differ
diff --git a/html/.vscode/todo/images/bg-mobile-dark.jpg b/html/.vscode/todo/images/bg-mobile-dark.jpg
new file mode 100644
index 0000000..3285a35
Binary files /dev/null and b/html/.vscode/todo/images/bg-mobile-dark.jpg differ
diff --git a/html/.vscode/todo/images/bg-mobile-light.jpg b/html/.vscode/todo/images/bg-mobile-light.jpg
new file mode 100644
index 0000000..9df5c53
Binary files /dev/null and b/html/.vscode/todo/images/bg-mobile-light.jpg differ
diff --git a/html/.vscode/todo/images/favicon-32x32.png b/html/.vscode/todo/images/favicon-32x32.png
new file mode 100644
index 0000000..1e2df7f
Binary files /dev/null and b/html/.vscode/todo/images/favicon-32x32.png differ
diff --git a/html/.vscode/todo/images/icon-check.svg b/html/.vscode/todo/images/icon-check.svg
new file mode 100644
index 0000000..61e7384
--- /dev/null
+++ b/html/.vscode/todo/images/icon-check.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/html/.vscode/todo/images/icon-cross.svg b/html/.vscode/todo/images/icon-cross.svg
new file mode 100644
index 0000000..cdf9c7c
--- /dev/null
+++ b/html/.vscode/todo/images/icon-cross.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/html/.vscode/todo/images/icon-moon.svg b/html/.vscode/todo/images/icon-moon.svg
new file mode 100644
index 0000000..60c2ace
--- /dev/null
+++ b/html/.vscode/todo/images/icon-moon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/html/.vscode/todo/images/icon-sun.svg b/html/.vscode/todo/images/icon-sun.svg
new file mode 100644
index 0000000..24f69f3
--- /dev/null
+++ b/html/.vscode/todo/images/icon-sun.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/html/.vscode/todo/index.html b/html/.vscode/todo/index.html
new file mode 100644
index 0000000..f739204
--- /dev/null
+++ b/html/.vscode/todo/index.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+ Frontend Mentor | Todo app
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
0 items left
+
+ All
+ Active
+ Completed
+
+
+ Clear Completed
+
+
+
+
+
+
+
diff --git a/html/.vscode/todo/script.js b/html/.vscode/todo/script.js
new file mode 100644
index 0000000..192cfba
--- /dev/null
+++ b/html/.vscode/todo/script.js
@@ -0,0 +1,417 @@
+// 주요 DOM 요소 가져오기
+const themeToggle = document.querySelector('.toggle'); // 테마 전환 버튼
+const themeIcon = document.getElementById('icon'); // 테마 아이콘
+const background = document.getElementById('background-image'); // 배경 이미지
+const newTodoInput = document.getElementById('input-text'); // 새로운 할 일 입력 필드
+const mainContainer = document.querySelector('.main-container'); // 할 일 목록이 표시될 메인 컨테이너
+const itemsLeft = document.querySelector('.remain-item'); // 남은 할 일 수 표시
+const clearCompletedButton = document.querySelector('.clear'); // 완료된 할 일 삭제 버튼
+const filterButtons = document.querySelectorAll('.filter-button'); // 필터 버튼들
+const addTodoOval = document.querySelector('.oval'); // 할 일 추가 버튼의 Oval
+const inputContainer = document.querySelector('.input-container'); // 입력 컨테이너
+const bottomContainer = document.querySelector('.bottom-container'); // 하단 컨테이너
+
+
+// let todos = [];
+let todos = JSON.parse(localStorage.getItem("todos")) || [];
+let draggedIndex = null;
+
+// Oval 상태 로컬 스토리지에서 가져오기
+let ovalState = localStorage.getItem('ovalState') || 'light';
+
+// 기본, hover, 완료 상태 SVG
+const lightOvalSVG = `
+
+
+ `;
+
+const darkOvalSVG = `
+
+
+ `;
+
+const clickedOval = `
+
+
+
+
+
+
+
+
+
+
+ `;
+
+const hoverOval = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+
+// Oval 업데이트 함수
+function updateAddTodoOval() {
+ const isDark = document.body.classList.contains('dark');
+ addTodoOval.innerHTML = isDark ? darkOvalSVG : lightOvalSVG;
+}
+
+// 테마 전환 함수에서 line과 텍스트 색상 업데이트 추가
+const toggleTheme = () => {
+ document.body.classList.toggle('dark');
+ const isDark = document.body.classList.contains('dark');
+
+ // 아이콘과 배경 이미지 변경
+ themeIcon.src = isDark ? 'images/icon-sun.svg' : 'images/icon-moon.svg';
+ background.style.backgroundImage = isDark
+ ? 'url(images/bg-desktop-dark.jpg)'
+ : 'url(images/bg-desktop-light.jpg)';
+
+ // 낮/밤 모드에 따른 배경색 변경
+ document.body.style.background = isDark ? '#171823' : '#FAFAFA';
+
+ // 주요 컨테이너 배경색
+ const containerBgColor = isDark ? '#25273D' : '#FFFFFF';
+ inputContainer.style.background = containerBgColor;
+ newTodoInput.style.background = containerBgColor;
+ mainContainer.style.background = containerBgColor;
+ bottomContainer.style.background = containerBgColor;
+
+ // line 및 텍스트 색상 업데이트
+ document.querySelectorAll('.line').forEach(line => {
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ });
+ document.querySelectorAll('.todo-text').forEach(text => {
+ text.style.color = isDark ? '#C8CBE7' : '#494C6B';
+ });
+
+ // 로컬 스토리지에 테마 상태 저장
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
+
+ // Oval 상태 업데이트
+ ovalState = isDark ? 'dark' : 'light';
+ localStorage.setItem('ovalState', ovalState);
+ updateAddTodoOval();
+ updateTodoList();
+};
+
+// 초기 로딩 시 테마 및 할 일 설정
+document.addEventListener('DOMContentLoaded', function () {
+ const savedTheme = localStorage.getItem('theme');
+ const isDark = savedTheme === 'dark';
+
+ if (isDark) {
+ document.body.classList.add('dark');
+ themeIcon.src = 'images/icon-sun.svg';
+ background.style.backgroundImage = 'url(images/bg-desktop-dark.jpg)';
+ document.body.style.background = '#171823';
+ const containerBgColor = '#25273D';
+ inputContainer.style.background = containerBgColor;
+ newTodoInput.style.background = containerBgColor;
+ mainContainer.style.background = containerBgColor;
+ bottomContainer.style.background = containerBgColor;
+ } else {
+ document.body.classList.remove('dark');
+ themeIcon.src = 'images/icon-moon.svg';
+ background.style.backgroundImage = 'url(images/bg-desktop-light.jpg)';
+ document.body.style.background = '#FAFAFA';
+ const containerBgColor = '#FFFFFF';
+ inputContainer.style.background = containerBgColor;
+ newTodoInput.style.background = containerBgColor;
+ mainContainer.style.background = containerBgColor;
+ bottomContainer.style.background = containerBgColor;
+
+ }
+
+ updateAddTodoOval();
+
+ const allButton = document.getElementById('all');
+ allButton.classList.add('active');
+ filterTodos('all');
+
+ const savedTodos = JSON.parse(localStorage.getItem('todos'));
+ if (savedTodos) {
+ todos = savedTodos;
+ updateAddTodoOval();
+ updateTodoList();
+ }
+});
+
+// 테마 토글 버튼 이벤트
+themeToggle.addEventListener('click', toggleTheme);
+
+// 새로운 할 일 추가
+newTodoInput.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter' && newTodoInput.value.trim()) {
+ addTodo(newTodoInput.value);
+ newTodoInput.value = '';
+ }
+});
+
+// 할 일 추가 함수
+function addTodo(text) {
+ const todo = { text, completed: false };
+ todos.push(todos);
+ updateTodoList();
+ saveTodosToLocalStorage();
+ localStorage.setItem('todos', JSON.stringify(todos));
+}
+
+function handleDragStart(event) {
+ draggedIndex = event.target.getAttribute('data-index');
+ event.dataTransfer.effectAllowed = "move";
+}
+
+function handleDragOver(event) {
+ event.preventDefault(); // 드롭을 허용하기 위해 필요
+ event.dataTransfer.dropEffect = "move";
+}
+
+function handleDrop(event) {
+ event.preventDefault();
+ const targetIndex = event.target.closest('.todo-item').getAttribute('data-index');
+
+ // todos 배열을 드래그한 항목과 드롭한 대상의 위치에 맞게 재정렬
+ if (draggedIndex !== null && targetIndex !== null && draggedIndex !== targetIndex) {
+ const movedItem = todos.splice(draggedIndex, 1)[0];
+ todos.splice(targetIndex, 0, movedItem);
+
+ updateTodoList();
+ saveTodosToLocalStorage();
+ }
+ draggedIndex = null;
+}
+
+// updateTodoList 함수에서 새로 생성되는 항목의 색상 설정
+function updateTodoList() {
+ mainContainer.innerHTML = ''; // 기존 목록 초기화
+ const isDark = document.body.classList.contains('dark');
+
+ todos.forEach((todo, index) => {
+ const todoItem = document.createElement('div');
+ todoItem.className = 'todo-item';
+ todoItem.draggable = true;
+ todoItem.setAttribute('data-index', index);
+
+
+ todoItem.innerHTML = `
+
+ ${todo.completed ? clickedOval : isDark ? darkOvalSVG : lightOvalSVG}
+
+
+ ${todo.text}
+
+
+ `;
+
+ const oval = todoItem.querySelector('.oval');
+ const todoText = todoItem.querySelector('.todo-text');
+
+ if (todo.completed) {
+ todoText.style.textDecoration = 'line-through';
+ todoText.style.color = isDark ? '#4D5067' : '#9495A5';
+ oval.innerHTML = clickedOval;
+ }
+
+ oval.addEventListener('click', () => toggleComplete(index));
+ todoText.addEventListener('click', () => toggleComplete(index));
+
+ mainContainer.appendChild(todoItem);
+
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ mainContainer.appendChild(line);
+ });
+}
+
+
+// 할 일 목록 업데이트 함수 (드래그 앤 드롭 통합)
+function updateTodoList() {
+ mainContainer.innerHTML = ''; // 기존 목록 초기화
+ const isDark = document.body.classList.contains('dark');
+
+ todos.forEach((todo, index) => {
+ const todoItem = document.createElement('div');
+ todoItem.className = 'todo-item';
+ todoItem.draggable = true; // 드래그 가능하도록 설정
+ todoItem.setAttribute('data-index', index);
+
+ todoItem.innerHTML = `
+
+ ${todo.completed ? clickedOval : isDark ? darkOvalSVG : lightOvalSVG}
+
+
+ ${todo.text}
+
+
+ `;
+
+ const oval = todoItem.querySelector('.oval');
+ const todoText = todoItem.querySelector('.todo-text');
+ const cancelButton = todoItem.querySelector('.cross-line');
+
+ // 완료된 할 일 텍스트 스타일 적용
+ if (todo.completed) {
+ todoText.style.textDecoration = 'line-through';
+ todoText.style.color = isDark ? '#4D5067' : '#9495A5';
+ oval.innerHTML = clickedOval;
+ }
+
+ // 드래그 앤 드롭 이벤트 추가
+ todoItem.addEventListener('dragstart', handleDragStart);
+ todoItem.addEventListener('dragover', handleDragOver);
+ todoItem.addEventListener('drop', handleDrop);
+
+ oval.addEventListener('click', () => toggleComplete(index));
+ todoText.addEventListener('click', () => toggleComplete(index));
+ cancelButton.addEventListener('click', (e) => {
+ e.stopPropagation();
+ deleteTodo(index);
+ });
+
+ mainContainer.appendChild(todoItem);
+
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ mainContainer.appendChild(line);
+ });
+
+ updateItemsLeft();
+}
+
+
+// 할 일 삭제 함수
+function deleteTodo(index) {
+ todos.splice(index, 1);
+ updateTodoList();
+ saveTodosToLocalStorage();
+}
+
+// 완료 상태 토글 함수
+function toggleComplete(index) {
+ todos[index].completed = !todos[index].completed;
+ updateTodoList();
+ saveTodosToLocalStorage();
+}
+
+// 남은 할 일 수 업데이트
+function updateItemsLeft() {
+ const remainingItems = todos.filter(todo => !todo.completed).length;
+ itemsLeft.textContent = `${remainingItems} items left`;
+}
+
+// 필터 버튼 이벤트
+filterButtons.forEach(button => {
+ button.addEventListener('click', () => {
+ filterButtons.forEach(btn => btn.classList.remove('active'));
+ button.classList.add('active');
+ filterTodos(button.dataset.filter);
+ });
+});
+
+// 필터링 함수에서 완료된 항목 스타일 수정
+function filterTodos(filter) {
+ mainContainer.innerHTML = '';
+ const isDark = document.body.classList.contains('dark');
+ const filteredTodos = filter === 'all' ? todos :
+ todos.filter(todo => filter === 'active' ? !todo.completed : todo.completed);
+
+ filteredTodos.forEach((todo, index) => {
+ const todoItem = document.createElement('div');
+ todoItem.className = 'todo-item';
+ todoItem.innerHTML = `
+
+ ${todo.completed ? clickedOval : isDark ? darkOvalSVG : lightOvalSVG}
+
+
+ ${todo.text}
+
+ `;
+
+ const oval = todoItem.querySelector('.oval');
+ const todoText = todoItem.querySelector('.todo-text');
+
+ if (todo.completed) {
+ todoText.style.textDecoration = 'line-through';
+ todoText.style.color = isDark ? '#4D5067' : '#9495A5';
+ }
+
+ oval.addEventListener('click', () => toggleComplete(index));
+ todoText.addEventListener('click', () => toggleComplete(index));
+
+ mainContainer.appendChild(todoItem);
+
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ mainContainer.appendChild(line);
+ });
+
+ updateItemsLeft();
+}
+
+
+// 완료된 할 일 삭제
+clearCompletedButton.addEventListener('click', () => {
+ todos = todos.filter(todo => !todo.completed);
+ updateTodoList();
+ saveTodosToLocalStorage();
+});
+
+// 로컬 스토리지 저장
+function saveTodosToLocalStorage() {
+ localStorage.setItem('todos', JSON.stringify(todos));
+}
+
+// 필터 버튼 스타일 설정
+
+// 필터 버튼 이벤트
+filterButtons.forEach(button => {
+ button.addEventListener('click', () => {
+ filterButtons.forEach(btn => btn.classList.remove('active'));
+ button.classList.add('active');
+ filterTodos(button.dataset.filter);
+ });
+});
+
+function applyDarkModeShadow() {
+ if (document.body.classList.contains('dark')) {
+ mainContainer.style.boxShadow = '0px 35px 50px -15px rgba(0, 0, 0, 0.50)';
+ bottomContainer.style.boxShadow = '0px 35px 50px -15px rgba(0, 0, 0, 0.50)';
+ } else {
+ mainContainer.style.boxShadow = '0px 35px 50px -15px rgba(194, 195, 214, 0.50)';
+ bottomContainer.style.boxShadow = '0px 35px 50px -15px rgba(194, 195, 214, 0.50)';
+ }
+}
+
+ clear.bottom.filter-button(button => {
+ clear.bottom.filter-button.addEventListener('mouseover', () => {
+ clear.bottom.filter-button.classList.add('active');
+ });
+ clear.bottom.filter-button.addEventListener('mouseout', () => {
+ clear.bottom.filter-button.classList.remove('active');
+ });
+});
\ No newline at end of file
diff --git a/html/.vscode/todo/style.css b/html/.vscode/todo/style.css
new file mode 100644
index 0000000..19472c2
--- /dev/null
+++ b/html/.vscode/todo/style.css
@@ -0,0 +1,286 @@
+.attribution { font-size: 11px; text-align: center; }
+
+body {
+ width: 100%;
+ background: #fafafa;
+ font-family: "Josefin Sans", sans-serif;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+}
+
+#background-image {
+ position:absolute;
+ background-image: url("images/bg-desktop-light.jpg");
+ background-position: center;
+ top: 0;
+ left: 0px;
+ width: 100%;
+ height: 300px;
+ z-index: -1;
+ background-repeat: no-repeat;
+ background-size: cover;
+}
+
+.header-container{
+ width: 541px;
+ height: 48px;
+ flex-shrink: 0;
+ margin-top: 70px;
+ display: flex;
+ justify-content: space-between;
+}
+
+.header{
+ color: #FFF;
+ font-size: 40px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: 15px;
+ margin: 0;
+}
+
+
+.input-container{
+ max-width: 540px;
+ width: 90%;
+ height: 64px;
+ margin-top: 40px;
+ flex-shrink: 0;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+}
+
+.oval{
+ width: 24px;
+ height: 24px;
+ fill: #FFF;
+}
+
+.todo-item .oval:hover circle{
+ stroke:#5df;
+}
+
+
+
+
+#input-text{
+ color : #393A4B;
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.25px;
+ border: none;
+ outline: none;
+}
+
+.input-box{
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ gap: 24px;
+ margin-left: 24px;
+}
+
+.main-container{
+ max-width: 540px;
+ width: 90%;
+ min-height: 0px;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ margin-top: 24px;
+}
+
+.bottom-container{
+ max-width: 540px;
+ width: 90%;
+ max-height: 439px;
+ min-height: 50px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: auto;
+ margin-bottom: 16px;
+ padding: 0 24px;
+ box-sizing: border-box;
+}
+
+.toggle {
+ background: none; /* 배경을 없애거나 단색으로 설정 */
+ border: none; /* 테두리 없애기 */
+ box-shadow: none; /* 그림자 없애기 */
+ padding: 0; /* 기본 여백 없애기 */
+ outline: none; /* 클릭 시 외곽선 없애기 */
+ cursor: pointer; /* 포인터 변경 */
+}
+
+.interaction-container{
+ width: 116px;
+ height: 14px;
+ display: inline-flex;
+ padding-bottom: 4px;
+ justify-content: center;
+ align-items: flex-start;
+ gap: 19px;
+ color: #9495A5;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: -0.194px;
+ margin-left: 40px;
+}
+
+.bottom{
+ color: #9495A5;
+ text-align: right;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+
+
+.footer{
+ margin-top: 49px;
+ color: #9495A5;
+ text-align: center;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+.todo-item{
+ display: flex;
+ width: 492px;
+ height: 24px;
+ flex-shrink: 0;
+ align-items: center;
+ margin-top: 20px;
+}
+
+.todo-text{
+ margin-left: 24px;
+ color: #494C6B;
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.25px;
+}
+
+.line{
+ margin-top: 20px;
+ width: 540px;
+ height: 1px;
+ flex-shrink: 0;
+ background: #E3E4F1;
+}
+
+.cross-line{
+ margin-left: auto;
+ display: none;
+}
+
+/* todo-item에 마우스를 올렸을 때만 cross-line 보이기 */
+.todo-item:hover .cross-line {
+ display: block;
+}
+
+
+
+.filter-button{
+ border: none;
+ outline: none;
+ background: none;
+ color: #9495A5;
+ font-family: "Josefin Sans";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+.clear{
+ color: #9495A5;
+ text-align: right;
+ font-family: "Josefin Sans";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+/* .filter-button:hover {
+ color: #E3E4F1;
+ text-align: right;
+ font-family: "Josefin Sans";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+} */
+
+.filter-button.active{
+ color: #3A7CFD;
+ font-weight: 700;
+
+}
+/* dark-shadow 클래스를 다크 모드 전용으로 box-shadow를 적용하는 용도로 추가 */
+.dark-shadow {
+ box-shadow: 0px 35px 50px -15px rgba(0, 0, 0, 0.5);
+}
+
+
+/* Dark mode 시 main-container와 bottom-container의 box-shadow */
+body.dark .main-container,
+body.dark .bottom-container {
+ box-shadow: 0px 35px 50px -15px rgba(0, 0, 0, 0.50);
+}
+
+/* clear bottom filter-button의 hover 스타일 */
+.clear.bottom.filter-button:hover {
+ color: #494C6B;
+ font-weight: 400;
+
+}
+.clear.bottom.filter-button {
+ color: #9495A5;
+ font-weight: 400;
+
+}
+
+body.dark.clear.bottom.filter-button {
+ color: #5B5E7E;
+ font-weight: 400;
+
+}
+body.dark.clear:hover {
+ color: #E3E4F1;
+ font-weight: 400;
+
+}
\ No newline at end of file
diff --git a/icon-check.svg b/icon-check.svg
new file mode 100644
index 0000000..61e7384
--- /dev/null
+++ b/icon-check.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/icon-cross.svg b/icon-cross.svg
new file mode 100644
index 0000000..cdf9c7c
--- /dev/null
+++ b/icon-cross.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/icon-moon.svg b/icon-moon.svg
new file mode 100644
index 0000000..60c2ace
--- /dev/null
+++ b/icon-moon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/icon-sun.svg b/icon-sun.svg
new file mode 100644
index 0000000..24f69f3
--- /dev/null
+++ b/icon-sun.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..e52708f
--- /dev/null
+++ b/index.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+ Frontend Mentor | Todo app
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${todo.completed ? clickedOval : (isDark ? darkOvalSVG : lightOvalSVG)}
+
+
+ ${todo.text}
+
+
+
+
0 items left
+
+ All
+ Active
+ Completed
+
+
+ Clear Completed
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..cd64f3b
--- /dev/null
+++ b/script.js
@@ -0,0 +1,458 @@
+// 주요 DOM 요소 가져오기
+const themeToggle = document.querySelector('.toggle'); // 테마 전환 버튼
+const themeIcon = document.getElementById('icon'); // 테마 아이콘
+const background = document.getElementById('background-image'); // 배경 이미지
+const newTodoInput = document.getElementById('input-text'); // 새로운 할 일 입력 필드
+const mainContainer = document.querySelector('.main-container'); // 할 일 목록이 표시될 메인 컨테이너
+const itemsLeft = document.querySelector('.remain-item'); // 남은 할 일 수 표시
+const clearCompletedButton = document.querySelector('.clear'); // 완료된 할 일 삭제 버튼
+const filterButtons = document.querySelectorAll('.filter-button'); // 필터 버튼들
+const addTodoOval = document.querySelector('.oval'); // 할 일 추가 버튼의 Oval
+const inputContainer = document.querySelector('.input-container'); // 입력 컨테이너
+const bottomContainer = document.querySelector('.bottom-container'); // 하단 컨테이너
+
+
+let todos = [];
+
+let draggedIndex = null;
+
+// Oval 상태 로컬 스토리지에서 가져오기
+let ovalState = localStorage.getItem('ovalState') || 'light';
+
+// 기본, hover, 완료 상태 SVG
+const lightOvalSVG = `
+
+
+ `;
+
+const darkOvalSVG = `
+
+
+ `;
+
+const clickedOval = `
+
+
+
+
+
+
+
+
+
+
+ `;
+
+const hoverOval = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ //다크모드 그림자
+function applyDarkModeShadow() {
+ if (document.body.classList.contains('dark')) {
+ mainContainer.style.boxShadow = '0px 35px 50px -15px rgba(0, 0, 0, 0.50)';
+ bottomContainer.style.boxShadow = '0px 35px 50px -15px rgba(0, 0, 0, 0.50)';
+ } else {
+ mainContainer.style.boxShadow = '0px 35px 50px -15px rgba(194, 195, 214, 0.50)';
+ bottomContainer.style.boxShadow = '0px 35px 50px -15px rgba(194, 195, 214, 0.50)';
+ }
+}
+
+// Oval 업데이트 함수
+function updateAddTodoOval() {
+ const isDark = document.body.classList.contains('dark');
+ addTodoOval.innerHTML = isDark ? darkOvalSVG : lightOvalSVG;
+}
+
+// 테마 전환 함수에서 line과 텍스트 색상 업데이트 추가
+const toggleTheme = () => {
+ document.body.classList.toggle('dark');
+ const isDark = document.body.classList.contains('dark');
+
+ // 아이콘과 배경 이미지 변경
+ themeIcon.src = isDark ? 'images/icon-sun.svg' : 'images/icon-moon.svg';
+ background.style.backgroundImage = isDark
+ ? 'url(images/bg-desktop-dark.jpg)'
+ : 'url(images/bg-desktop-light.jpg)';
+
+ // 낮/밤 모드에 따른 배경색 변경
+ document.body.style.background = isDark ? '#171823' : '#FAFAFA';
+
+ // 주요 컨테이너 배경색
+ const containerBgColor = isDark ? '#25273D' : '#FFFFFF';
+ inputContainer.style.background = containerBgColor;
+ newTodoInput.style.background = containerBgColor;
+ mainContainer.style.background = containerBgColor;
+ bottomContainer.style.background = containerBgColor;
+
+ // line 및 텍스트 색상 업데이트
+ document.querySelectorAll('.line').forEach(line => {
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ });
+ document.querySelectorAll('.todo-text').forEach(text => {
+ text.style.color = isDark ? '#C8CBE7' : '#494C6B';
+ });
+
+ // 로컬 스토리지에 테마 상태 저장
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
+
+ // Oval 상태 업데이트
+ ovalState = isDark ? 'dark' : 'light';
+ localStorage.setItem('ovalState', ovalState);
+ updateAddTodoOval();
+ updateTodoList();
+};
+
+
+// 초기 로딩 시 테마 및 할 일 설정
+document.addEventListener('DOMContentLoaded', function () {
+ applyDarkModeShadow();
+ const savedTheme = localStorage.getItem('theme');
+ const isDark = savedTheme === 'dark';
+
+ const filterButtons = document.querySelectorAll('[data-filter]');
+ filterButtons.forEach(button => {
+ button.addEventListener('click', function() {
+ const filter = this.dataset.filter;
+
+ // 필터를 클릭할 때마다 로컬 스토리지에 저장
+ localStorage.setItem('filter', filter);
+
+ // 필터링된 할 일 목록을 화면에 갱신
+ filterTodos(filter);
+
+ // 선택된 필터 버튼에 active, hover 클래스를 추가하여 스타일 적용
+ filterButtons.forEach(btn => btn.classList.remove('active', 'hover'));
+ this.classList.add('active', 'hover');
+ });
+ });
+ const clearCompletedButton = document.querySelector('#clear-completed'); // clear completed 버튼 선택
+ if (clearCompletedButton) {
+ clearCompletedButton.addEventListener('click', function() {
+ // 완료된 할 일을 삭제하는 함수 호출
+ clearCompletedTodos();
+ });
+ }
+
+ if (isDark) {
+ document.body.classList.add('dark');
+ themeIcon.src = 'images/icon-sun.svg';
+ background.style.backgroundImage = 'url(images/bg-desktop-dark.jpg)';
+ document.body.style.background = '#171823';
+ const containerBgColor = '#25273D';
+ inputContainer.style.background = containerBgColor;
+ newTodoInput.style.background = containerBgColor;
+ mainContainer.style.background = containerBgColor;
+ bottomContainer.style.background = containerBgColor;
+ } else {
+ document.body.classList.remove('dark');
+ themeIcon.src = 'images/icon-moon.svg';
+ background.style.backgroundImage = 'url(images/bg-desktop-light.jpg)';
+ document.body.style.background = '#FAFAFA';
+ const containerBgColor = '#FFFFFF';
+ inputContainer.style.background = containerBgColor;
+ newTodoInput.style.background = containerBgColor;
+ mainContainer.style.background = containerBgColor;
+ bottomContainer.style.background = containerBgColor;
+ }
+
+ updateAddTodoOval();
+
+ // 로컬 스토리지에서 필터 상태 가져오기 (기본값: 'all')
+ const savedFilter = localStorage.getItem('all') || 'all';
+ const activeButton = document.querySelector(`[data-filter='${savedFilter}']`);
+ if (activeButton) {
+ // 선택된 필터 버튼에 active, hover 클래스를 추가하여 스타일 적용
+ activeButton.classList.add('active', 'hover');
+ }
+
+ // 해당 필터에 맞게 할 일 목록 필터링
+ filterTodos(savedFilter); // 필터링 함수 호출
+
+ // 로컬 스토리지에서 할 일 목록 가져오기
+ const savedTodos = JSON.parse(localStorage.getItem('todos'));
+ if (savedTodos) {
+ todos = savedTodos; // 로컬 스토리지에서 가져온 todos 배열로 갱신
+
+ // 필터 상태에 맞게 할 일 목록을 필터링하고 갱신
+ filterTodos(savedFilter); // 필터링된 상태로 할 일 목록 갱신
+ updateTodoList(); // 필터링된 할 일 목록을 화면에 표시
+ updateAddTodoOval(); // Oval 업데이트 함수 호출
+ }
+ applyDarkModeShadow(); // 테마에 맞는 그림자 적용
+});
+
+// 테마 토글 버튼 이벤트
+themeToggle.addEventListener('click', toggleTheme);
+
+// 새로운 할 일 추가
+newTodoInput.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter' && newTodoInput.value.trim()) {
+ addTodo(newTodoInput.value);
+ newTodoInput.value = '';
+ }
+});
+
+// 할 일 추가 함수
+function addTodo(text) {
+ const todo = { text, completed: false };
+ todos.push(todo);
+ updateTodoList();
+ saveTodosToLocalStorage();
+}
+
+function handleDragStart(event) {
+ draggedIndex = event.target.getAttribute('data-index');
+ event.dataTransfer.effectAllowed = "move";
+}
+
+function handleDragOver(event) {
+ event.preventDefault(); // 드롭을 허용하기 위해 필요
+ event.dataTransfer.dropEffect = "move";
+}
+
+function handleDrop(event) {
+ event.preventDefault();
+ const targetIndex = event.target.closest('.todo-item').getAttribute('data-index');
+
+ // todos 배열을 드래그한 항목과 드롭한 대상의 위치에 맞게 재정렬
+ if (draggedIndex !== null && targetIndex !== null && draggedIndex !== targetIndex) {
+ const movedItem = todos.splice(draggedIndex, 1)[0];
+ todos.splice(targetIndex, 0, movedItem);
+
+ updateTodoList();
+ saveTodosToLocalStorage();
+ }
+ draggedIndex = null;
+}
+
+
+
+// updateTodoList 함수에서 새로 생성되는 항목의 색상 설정
+function updateTodoList() {
+ mainContainer.innerHTML = ''; // 기존 목록 초기화
+ const isDark = document.body.classList.contains('dark');
+
+ todos.forEach((todo, index) => {
+ const todoItem = document.createElement('div');
+ todoItem.className = 'todo-item';
+ todoItem.draggable = true;
+ todoItem.setAttribute('data-index', index);
+
+
+ todoItem.innerHTML = `
+
+ ${todo.completed ? clickedOval : isDark ? darkOvalSVG : lightOvalSVG}
+
+
+ ${todo.text}
+
+
+ `;
+
+ const oval = todoItem.querySelector('.oval');
+ const todoText = todoItem.querySelector('.todo-text');
+
+ if (todo.completed) {
+ todoText.style.textDecoration = 'line-through';
+ todoText.style.color = isDark ? '#4D5067' : '#9495A5';
+ oval.innerHTML = clickedOval;
+ }
+
+ oval.addEventListener('click', () => toggleComplete(index));
+ todoText.addEventListener('click', () => toggleComplete(index));
+
+ mainContainer.appendChild(todoItem);
+
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ mainContainer.appendChild(line);
+ });
+}
+
+
+// 할 일 목록 업데이트 함수 (드래그 앤 드롭 통합)
+function updateTodoList() {
+ mainContainer.innerHTML = ''; // 기존 목록 초기화
+ const isDark = document.body.classList.contains('dark');
+
+ todos.forEach((todo, index) => {
+ const todoItem = document.createElement('div');
+ todoItem.className = 'todo-item';
+ todoItem.draggable = true; // 드래그 가능하도록 설정
+ todoItem.setAttribute('data-index', index);
+
+ todoItem.innerHTML = `
+
+ ${todo.completed ? clickedOval : isDark ? darkOvalSVG : lightOvalSVG}
+
+
+ ${todo.text}
+
+
+ `;
+
+ const oval = todoItem.querySelector('.oval');
+ const todoText = todoItem.querySelector('.todo-text');
+ const cancelButton = todoItem.querySelector('.cross-line');
+
+ // 완료된 할 일 텍스트 스타일 적용
+ if (todo.completed) {
+ todoText.style.textDecoration = 'line-through';
+ todoText.style.color = isDark ? '#4D5067' : '#9495A5';
+ oval.innerHTML = clickedOval;
+ }
+
+ // 드래그 앤 드롭 이벤트 추가
+ todoItem.addEventListener('dragstart', handleDragStart);
+ todoItem.addEventListener('dragover', handleDragOver);
+ todoItem.addEventListener('drop', handleDrop);
+
+ oval.addEventListener('click', () => toggleComplete(index));
+ todoText.addEventListener('click', () => toggleComplete(index));
+ cancelButton.addEventListener('click', (e) => {
+ e.stopPropagation();
+ deleteTodo(index);
+ });
+
+ mainContainer.appendChild(todoItem);
+
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ mainContainer.appendChild(line);
+ });
+
+ updateItemsLeft();
+}
+
+
+// 할 일 삭제 함수
+function deleteTodo(index) {
+ todos.splice(index, 1);
+ updateTodoList();
+ saveTodosToLocalStorage();
+}
+
+// 완료 상태 토글 함수
+function toggleComplete(index) {
+ todos[index].completed = !todos[index].completed;
+ updateTodoList();
+ saveTodosToLocalStorage();
+}
+
+// 남은 할 일 수 업데이트
+function updateItemsLeft() {
+ const remainingItems = todos.filter(todo => !todo.completed).length;
+ itemsLeft.textContent = `${remainingItems} items left`;
+}
+
+// 필터링 함수에서 완료된 항목 스타일 수정
+
+// 필터링 함수에서 완료된 항목 스타일 수정
+function filterTodos(filter) {
+ mainContainer.innerHTML = ''; // 기존 항목을 제거
+ const isDark = document.body.classList.contains('dark');
+
+ // 필터링된 할 일 항목을 가져옵니다.
+ const filteredTodos = filter === 'all' ? todos :
+ todos.filter(todo => filter === 'active' ? !todo.completed : todo.completed);
+
+ // 필터링된 항목을 표시합니다.
+ filteredTodos.forEach((todo, index) => {
+ const todoItem = document.createElement('div');
+ todoItem.className = 'todo-item';
+ todoItem.draggable = true;
+ todoItem.setAttribute('data-index', index); // data-index로 인덱스를 추가
+
+ todoItem.innerHTML = `
+
+ ${todo.completed ? clickedOval : isDark ? darkOvalSVG : lightOvalSVG}
+
+
+ ${todo.text}
+
+ `;
+
+ const oval = todoItem.querySelector('.oval');
+ const todoText = todoItem.querySelector('.todo-text');
+
+ // 완료된 항목은 스타일을 변경
+ if (todo.completed) {
+ todoText.style.textDecoration = 'line-through';
+ todoText.style.color = isDark ? '#4D5067' : '#9495A5';
+ oval.innerHTML = clickedOval;
+ }
+
+ oval.addEventListener('click', () => toggleComplete(index));
+ todoText.addEventListener('click', () => toggleComplete(index));
+
+ // mainContainer에 todo 항목 추가
+ mainContainer.appendChild(todoItem);
+
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ mainContainer.appendChild(line);
+ });
+
+ updateItemsLeft(); // 남은 할 일 수 업데이트
+}
+
+
+function renderTodos() {
+ // 기존 필터된 todos 배열을 기반으로 필터링된 todo만 렌더링
+ const filteredTodos = todos.filter(todo => {
+ if (currentFilter === 'active') {
+ return !todo.completed;
+ } else if (currentFilter === 'completed') {
+ return todo.completed;
+ } else {
+ return true; // 'all' 필터는 모든 항목을 보여줌
+ }
+ });
+
+ // 필터링된 todos를 화면에 렌더링
+ mainContainer.innerHTML = ''; // 기존 내용 초기화
+}
+
+
+// 완료된 할 일 삭제
+clearCompletedButton.addEventListener('click', () => {
+ todos = todos.filter(todo => !todo.completed);
+ updateTodoList();
+ renderTodos();
+ saveTodosToLocalStorage();
+
+});
+
+// 로컬 스토리지 저장
+function saveTodosToLocalStorage() {
+ localStorage.setItem('todos', JSON.stringify(todos));
+}
+
+
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..618bd77
--- /dev/null
+++ b/style.css
@@ -0,0 +1,271 @@
+.attribution { font-size: 11px; text-align: center; }
+
+body {
+ width: 100%;
+ background: #fafafa;
+ font-family: "Josefin Sans", sans-serif;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+}
+
+#background-image {
+ position:absolute;
+ background-image: url("images/bg-desktop-light.jpg");
+ background-position: center;
+ top: 0;
+ left: 0px;
+ width: 100%;
+ height: 300px;
+ z-index: -1;
+ background-repeat: no-repeat;
+ background-size: cover;
+}
+
+.header-container{
+ width: 541px;
+ height: 48px;
+ flex-shrink: 0;
+ margin-top: 70px;
+ display: flex;
+ justify-content: space-between;
+}
+
+.header{
+ color: #FFF;
+ font-size: 40px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: 15px;
+ margin: 0;
+}
+
+
+.input-container{
+ max-width: 540px;
+ width: 90%;
+ height: 64px;
+ margin-top: 40px;
+ flex-shrink: 0;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+}
+
+.oval{
+ width: 24px;
+ height: 24px;
+ fill: #FFF;
+}
+
+.todo-item .oval:hover circle{
+ stroke:#5df;
+}
+
+
+
+
+#input-text{
+ color : #393A4B;
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.25px;
+ border: none;
+ outline: none;
+}
+
+.input-box{
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ gap: 24px;
+ margin-left: 24px;
+}
+
+.main-container{
+ max-width: 540px;
+ width: 90%;
+ min-height: 0px;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ margin-top: 24px;
+}
+
+.bottom-container{
+ max-width: 540px;
+ width: 90%;
+ max-height: 439px;
+ min-height: 50px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: auto;
+ margin-bottom: 16px;
+ padding: 0 24px;
+ box-sizing: border-box;
+}
+body.dark.bottom-container{
+ max-width: 540px;
+ width: 90%;
+ max-height: 439px;
+ min-height: 50px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(0, 0, 0, 0.50);
+ justify-content: space-between;
+ align-items: center;
+ margin-top: auto;
+ margin-bottom: 16px;
+ padding: 0 24px;
+ box-sizing: border-box;
+}
+body.dark.main-container{
+ max-width: 540px;
+ width: 90%;
+ min-height: 0px;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(0, 0, 0, 0.50);
+ margin-top: 24px;
+}
+
+.toggle {
+ background: none; /* 배경을 없애거나 단색으로 설정 */
+ border: none; /* 테두리 없애기 */
+ box-shadow: none; /* 그림자 없애기 */
+ padding: 0; /* 기본 여백 없애기 */
+ outline: none; /* 클릭 시 외곽선 없애기 */
+ cursor: pointer; /* 포인터 변경 */
+}
+
+.interaction-container{
+ width: 116px;
+ height: 14px;
+ display: inline-flex;
+ padding-bottom: 4px;
+ justify-content: center;
+ align-items: flex-start;
+ gap: 19px;
+ color: #9495A5;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: -0.194px;
+ margin-left: 40px;
+}
+
+.bottom{
+ color: #9495A5;
+ text-align: right;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+
+
+.footer{
+ margin-top: 49px;
+ color: #9495A5;
+ text-align: center;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+.todo-item{
+ display: flex;
+ width: 492px;
+ height: 24px;
+ flex-shrink: 0;
+ align-items: center;
+ margin-top: 20px;
+}
+
+.todo-text{
+ margin-left: 24px;
+ color: #494C6B;
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.25px;
+}
+
+.line{
+ margin-top: 20px;
+ width: 540px;
+ height: 1px;
+ flex-shrink: 0;
+ background: #E3E4F1;
+}
+
+.cross-line{
+ margin-left: auto;
+ display: none;
+}
+
+/* todo-item에 마우스를 올렸을 때만 cross-line 보이기 */
+.todo-item:hover .cross-line {
+ display: block;
+}
+
+
+
+.filter-button{
+ border: none;
+ outline: none;
+ background: none;
+ color: #9495A5;
+ font-family: "Josefin Sans";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+.clear{
+ color: #9495A5;
+ text-align: right;
+ font-family: "Josefin Sans";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+
+.filter-button.active{
+ color: #3A7CFD;
+}
\ No newline at end of file
diff --git a/todo_images/bg-desktop-dark.jpg b/todo_images/bg-desktop-dark.jpg
new file mode 100644
index 0000000..394ebb9
Binary files /dev/null and b/todo_images/bg-desktop-dark.jpg differ
diff --git a/todo_images/bg-desktop-light.jpg b/todo_images/bg-desktop-light.jpg
new file mode 100644
index 0000000..1b5f3bb
Binary files /dev/null and b/todo_images/bg-desktop-light.jpg differ
diff --git a/todo_images/bg-mobile-dark.jpg b/todo_images/bg-mobile-dark.jpg
new file mode 100644
index 0000000..3285a35
Binary files /dev/null and b/todo_images/bg-mobile-dark.jpg differ
diff --git a/todo_images/bg-mobile-light.jpg b/todo_images/bg-mobile-light.jpg
new file mode 100644
index 0000000..9df5c53
Binary files /dev/null and b/todo_images/bg-mobile-light.jpg differ
diff --git a/todo_images/favicon-32x32.png b/todo_images/favicon-32x32.png
new file mode 100644
index 0000000..1e2df7f
Binary files /dev/null and b/todo_images/favicon-32x32.png differ
diff --git a/todo_images/icon-check.svg b/todo_images/icon-check.svg
new file mode 100644
index 0000000..61e7384
--- /dev/null
+++ b/todo_images/icon-check.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/todo_images/icon-cross.svg b/todo_images/icon-cross.svg
new file mode 100644
index 0000000..cdf9c7c
--- /dev/null
+++ b/todo_images/icon-cross.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/todo_images/icon-moon.svg b/todo_images/icon-moon.svg
new file mode 100644
index 0000000..60c2ace
--- /dev/null
+++ b/todo_images/icon-moon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/todo_images/icon-sun.svg b/todo_images/icon-sun.svg
new file mode 100644
index 0000000..24f69f3
--- /dev/null
+++ b/todo_images/icon-sun.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/todo_index.html.html b/todo_index.html.html
new file mode 100644
index 0000000..2769508
--- /dev/null
+++ b/todo_index.html.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+ Frontend Mentor | Todo app
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${todo.completed ? clickedOval : (isDark ? darkOvalSVG : lightOvalSVG)}
+
+
+ ${todo.text}
+
+
+
+
0 items left
+
+ All
+ Active
+ Completed
+
+
+ Clear Completed
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/todo_script.js b/todo_script.js
new file mode 100644
index 0000000..a57aaea
--- /dev/null
+++ b/todo_script.js
@@ -0,0 +1,458 @@
+
+// 주요 DOM 요소 가져오기
+const themeToggle = document.querySelector('.toggle'); // 테마 전환 버튼
+const themeIcon = document.getElementById('icon'); // 테마 아이콘
+const background = document.getElementById('background-image'); // 배경 이미지
+const newTodoInput = document.getElementById('input-text'); // 새로운 할 일 입력 필드
+const mainContainer = document.querySelector('.main-container'); // 할 일 목록이 표시될 메인 컨테이너
+const itemsLeft = document.querySelector('.remain-item'); // 남은 할 일 수 표시
+const clearCompletedButton = document.querySelector('.clear'); // 완료된 할 일 삭제 버튼
+const filterButtons = document.querySelectorAll('.filter-button'); // 필터 버튼들
+const addTodoOval = document.querySelector('.oval'); // 할 일 추가 버튼의 Oval
+const inputContainer = document.querySelector('.input-container'); // 입력 컨테이너
+const bottomContainer = document.querySelector('.bottom-container'); // 하단 컨테이너
+
+
+let todos = [];
+
+let draggedIndex = null;
+
+// Oval 상태 로컬 스토리지에서 가져오기
+let ovalState = localStorage.getItem('ovalState') || 'light';
+
+// 기본, hover, 완료 상태 SVG
+const lightOvalSVG = `
+
+
+ `;
+
+const darkOvalSVG = `
+
+
+ `;
+
+const clickedOval = `
+
+
+
+
+
+
+
+
+
+
+ `;
+
+const hoverOval = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ //다크모드 그림자
+function applyDarkModeShadow() {
+ if (document.body.classList.contains('dark')) {
+ mainContainer.style.boxShadow = '0px 35px 50px -15px rgba(0, 0, 0, 0.50)';
+ bottomContainer.style.boxShadow = '0px 35px 50px -15px rgba(0, 0, 0, 0.50)';
+ } else {
+ mainContainer.style.boxShadow = '0px 35px 50px -15px rgba(194, 195, 214, 0.50)';
+ bottomContainer.style.boxShadow = '0px 35px 50px -15px rgba(194, 195, 214, 0.50)';
+ }
+}
+
+// Oval 업데이트 함수
+function updateAddTodoOval() {
+ const isDark = document.body.classList.contains('dark');
+ addTodoOval.innerHTML = isDark ? darkOvalSVG : lightOvalSVG;
+}
+
+// 테마 전환 함수에서 line과 텍스트 색상 업데이트 추가
+const toggleTheme = () => {
+ document.body.classList.toggle('dark');
+ const isDark = document.body.classList.contains('dark');
+
+ // 아이콘과 배경 이미지 변경
+ themeIcon.src = isDark ? 'images/icon-sun.svg' : 'images/icon-moon.svg';
+ background.style.backgroundImage = isDark
+ ? 'url(images/bg-desktop-dark.jpg)'
+ : 'url(images/bg-desktop-light.jpg)';
+
+ // 낮/밤 모드에 따른 배경색 변경
+ document.body.style.background = isDark ? '#171823' : '#FAFAFA';
+
+ // 주요 컨테이너 배경색
+ const containerBgColor = isDark ? '#25273D' : '#FFFFFF';
+ inputContainer.style.background = containerBgColor;
+ newTodoInput.style.background = containerBgColor;
+ mainContainer.style.background = containerBgColor;
+ bottomContainer.style.background = containerBgColor;
+
+ // line 및 텍스트 색상 업데이트
+ document.querySelectorAll('.line').forEach(line => {
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ });
+ document.querySelectorAll('.todo-text').forEach(text => {
+ text.style.color = isDark ? '#C8CBE7' : '#494C6B';
+ });
+
+ // 로컬 스토리지에 테마 상태 저장
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
+
+ // Oval 상태 업데이트
+ ovalState = isDark ? 'dark' : 'light';
+ localStorage.setItem('ovalState', ovalState);
+ updateAddTodoOval();
+ updateTodoList();
+};
+
+
+// 초기 로딩 시 테마 및 할 일 설정
+document.addEventListener('DOMContentLoaded', function () {
+ applyDarkModeShadow();
+ const savedTheme = localStorage.getItem('theme');
+ const isDark = savedTheme === 'dark';
+
+ const filterButtons = document.querySelectorAll('[data-filter]');
+ filterButtons.forEach(button => {
+ button.addEventListener('click', function() {
+ const filter = this.dataset.filter;
+
+ // 필터를 클릭할 때마다 로컬 스토리지에 저장
+ localStorage.setItem('filter', filter);
+
+ // 필터링된 할 일 목록을 화면에 갱신
+ filterTodos(filter);
+
+ // 선택된 필터 버튼에 active, hover 클래스를 추가하여 스타일 적용
+ filterButtons.forEach(btn => btn.classList.remove('active', 'hover'));
+ this.classList.add('active', 'hover');
+ });
+ });
+ const clearCompletedButton = document.querySelector('#clear-completed'); // clear completed 버튼 선택
+ if (clearCompletedButton) {
+ clearCompletedButton.addEventListener('click', function() {
+ // 완료된 할 일을 삭제하는 함수 호출
+ clearCompletedTodos();
+ });
+ }
+
+ if (isDark) {
+ document.body.classList.add('dark');
+ themeIcon.src = 'images/icon-sun.svg';
+ background.style.backgroundImage = 'url(images/bg-desktop-dark.jpg)';
+ document.body.style.background = '#171823';
+ const containerBgColor = '#25273D';
+ inputContainer.style.background = containerBgColor;
+ newTodoInput.style.background = containerBgColor;
+ mainContainer.style.background = containerBgColor;
+ bottomContainer.style.background = containerBgColor;
+ } else {
+ document.body.classList.remove('dark');
+ themeIcon.src = 'images/icon-moon.svg';
+ background.style.backgroundImage = 'url(images/bg-desktop-light.jpg)';
+ document.body.style.background = '#FAFAFA';
+ const containerBgColor = '#FFFFFF';
+ inputContainer.style.background = containerBgColor;
+ newTodoInput.style.background = containerBgColor;
+ mainContainer.style.background = containerBgColor;
+ bottomContainer.style.background = containerBgColor;
+ }
+
+ updateAddTodoOval();
+
+ // 로컬 스토리지에서 필터 상태 가져오기 (기본값: 'all')
+ const savedFilter = localStorage.getItem('all') || 'all';
+ const activeButton = document.querySelector(`[data-filter='${savedFilter}']`);
+ if (activeButton) {
+ // 선택된 필터 버튼에 active, hover 클래스를 추가하여 스타일 적용
+ activeButton.classList.add('active', 'hover');
+ }
+
+ // 해당 필터에 맞게 할 일 목록 필터링
+ filterTodos(savedFilter); // 필터링 함수 호출
+
+ // 로컬 스토리지에서 할 일 목록 가져오기
+ const savedTodos = JSON.parse(localStorage.getItem('todos'));
+ if (savedTodos) {
+ todos = savedTodos; // 로컬 스토리지에서 가져온 todos 배열로 갱신
+
+ // 필터 상태에 맞게 할 일 목록을 필터링하고 갱신
+ filterTodos(savedFilter); // 필터링된 상태로 할 일 목록 갱신
+ updateTodoList(); // 필터링된 할 일 목록을 화면에 표시
+ updateAddTodoOval(); // Oval 업데이트 함수 호출
+ }
+ applyDarkModeShadow(); // 테마에 맞는 그림자 적용
+});
+
+// 테마 토글 버튼 이벤트
+themeToggle.addEventListener('click', toggleTheme);
+
+// 새로운 할 일 추가
+newTodoInput.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter' && newTodoInput.value.trim()) {
+ addTodo(newTodoInput.value);
+ newTodoInput.value = '';
+ }
+});
+
+// 할 일 추가 함수
+function addTodo(text) {
+ const todo = { text, completed: false };
+ todos.push(todo);
+ updateTodoList();
+ saveTodosToLocalStorage();
+}
+
+function handleDragStart(event) {
+ draggedIndex = event.target.getAttribute('data-index');
+ event.dataTransfer.effectAllowed = "move";
+}
+
+function handleDragOver(event) {
+ event.preventDefault(); // 드롭을 허용하기 위해 필요
+ event.dataTransfer.dropEffect = "move";
+}
+
+function handleDrop(event) {
+ event.preventDefault();
+ const targetIndex = event.target.closest('.todo-item').getAttribute('data-index');
+
+ // todos 배열을 드래그한 항목과 드롭한 대상의 위치에 맞게 재정렬
+ if (draggedIndex !== null && targetIndex !== null && draggedIndex !== targetIndex) {
+ const movedItem = todos.splice(draggedIndex, 1)[0];
+ todos.splice(targetIndex, 0, movedItem);
+
+ updateTodoList();
+ saveTodosToLocalStorage();
+ }
+ draggedIndex = null;
+}
+
+
+
+// updateTodoList 함수에서 새로 생성되는 항목의 색상 설정
+function updateTodoList() {
+ mainContainer.innerHTML = ''; // 기존 목록 초기화
+ const isDark = document.body.classList.contains('dark');
+
+ todos.forEach((todo, index) => {
+ const todoItem = document.createElement('div');
+ todoItem.className = 'todo-item';
+ todoItem.draggable = true;
+ todoItem.setAttribute('data-index', index);
+
+
+ todoItem.innerHTML = `
+
+ ${todo.completed ? clickedOval : isDark ? darkOvalSVG : lightOvalSVG}
+
+
+ ${todo.text}
+
+
+ `;
+
+ const oval = todoItem.querySelector('.oval');
+ const todoText = todoItem.querySelector('.todo-text');
+
+ if (todo.completed) {
+ todoText.style.textDecoration = 'line-through';
+ todoText.style.color = isDark ? '#4D5067' : '#9495A5';
+ oval.innerHTML = clickedOval;
+ }
+
+ oval.addEventListener('click', () => toggleComplete(index));
+ todoText.addEventListener('click', () => toggleComplete(index));
+
+ mainContainer.appendChild(todoItem);
+
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ mainContainer.appendChild(line);
+ });
+}
+
+
+// 할 일 목록 업데이트 함수 (드래그 앤 드롭 통합)
+function updateTodoList() {
+ mainContainer.innerHTML = ''; // 기존 목록 초기화
+ const isDark = document.body.classList.contains('dark');
+
+ todos.forEach((todo, index) => {
+ const todoItem = document.createElement('div');
+ todoItem.className = 'todo-item';
+ todoItem.draggable = true; // 드래그 가능하도록 설정
+ todoItem.setAttribute('data-index', index);
+
+ todoItem.innerHTML = `
+
+ ${todo.completed ? clickedOval : isDark ? darkOvalSVG : lightOvalSVG}
+
+
+ ${todo.text}
+
+
+ `;
+
+ const oval = todoItem.querySelector('.oval');
+ const todoText = todoItem.querySelector('.todo-text');
+ const cancelButton = todoItem.querySelector('.cross-line');
+
+ // 완료된 할 일 텍스트 스타일 적용
+ if (todo.completed) {
+ todoText.style.textDecoration = 'line-through';
+ todoText.style.color = isDark ? '#4D5067' : '#9495A5';
+ oval.innerHTML = clickedOval;
+ }
+
+ // 드래그 앤 드롭 이벤트 추가
+ todoItem.addEventListener('dragstart', handleDragStart);
+ todoItem.addEventListener('dragover', handleDragOver);
+ todoItem.addEventListener('drop', handleDrop);
+
+ oval.addEventListener('click', () => toggleComplete(index));
+ todoText.addEventListener('click', () => toggleComplete(index));
+ cancelButton.addEventListener('click', (e) => {
+ e.stopPropagation();
+ deleteTodo(index);
+ });
+
+ mainContainer.appendChild(todoItem);
+
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ mainContainer.appendChild(line);
+ });
+
+ updateItemsLeft();
+}
+
+
+// 할 일 삭제 함수
+function deleteTodo(index) {
+ todos.splice(index, 1);
+ updateTodoList();
+ saveTodosToLocalStorage();
+}
+
+// 완료 상태 토글 함수
+function toggleComplete(index) {
+ todos[index].completed = !todos[index].completed;
+ updateTodoList();
+ saveTodosToLocalStorage();
+}
+
+// 남은 할 일 수 업데이트
+function updateItemsLeft() {
+ const remainingItems = todos.filter(todo => !todo.completed).length;
+ itemsLeft.textContent = `${remainingItems} items left`;
+}
+
+// 필터링 함수에서 완료된 항목 스타일 수정
+
+// 필터링 함수에서 완료된 항목 스타일 수정
+function filterTodos(filter) {
+ mainContainer.innerHTML = ''; // 기존 항목을 제거
+ const isDark = document.body.classList.contains('dark');
+
+ // 필터링된 할 일 항목을 가져옵니다.
+ const filteredTodos = filter === 'all' ? todos :
+ todos.filter(todo => filter === 'active' ? !todo.completed : todo.completed);
+
+ // 필터링된 항목을 표시합니다.
+ filteredTodos.forEach((todo, index) => {
+ const todoItem = document.createElement('div');
+ todoItem.className = 'todo-item';
+ todoItem.draggable = true;
+ todoItem.setAttribute('data-index', index); // data-index로 인덱스를 추가
+
+ todoItem.innerHTML = `
+
+ ${todo.completed ? clickedOval : isDark ? darkOvalSVG : lightOvalSVG}
+
+
+ ${todo.text}
+
+ `;
+
+ const oval = todoItem.querySelector('.oval');
+ const todoText = todoItem.querySelector('.todo-text');
+
+ // 완료된 항목은 스타일을 변경
+ if (todo.completed) {
+ todoText.style.textDecoration = 'line-through';
+ todoText.style.color = isDark ? '#4D5067' : '#9495A5';
+ oval.innerHTML = clickedOval;
+ }
+
+ oval.addEventListener('click', () => toggleComplete(index));
+ todoText.addEventListener('click', () => toggleComplete(index));
+
+ // mainContainer에 todo 항목 추가
+ mainContainer.appendChild(todoItem);
+
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.style.background = isDark ? '#393A4B' : '#E3E4F1';
+ mainContainer.appendChild(line);
+ });
+
+ updateItemsLeft(); // 남은 할 일 수 업데이트
+}
+
+
+function renderTodos() {
+ // 기존 필터된 todos 배열을 기반으로 필터링된 todo만 렌더링
+ const filteredTodos = todos.filter(todo => {
+ if (currentFilter === 'active') {
+ return !todo.completed;
+ } else if (currentFilter === 'completed') {
+ return todo.completed;
+ } else {
+ return true; // 'all' 필터는 모든 항목을 보여줌
+ }
+ });
+
+ // 필터링된 todos를 화면에 렌더링
+ mainContainer.innerHTML = ''; // 기존 내용 초기화
+}
+
+
+// 완료된 할 일 삭제
+clearCompletedButton.addEventListener('click', () => {
+ todos = todos.filter(todo => !todo.completed);
+ updateTodoList();
+ renderTodos();
+ saveTodosToLocalStorage();
+
+});
+
+// 로컬 스토리지 저장
+function saveTodosToLocalStorage() {
+ localStorage.setItem('todos', JSON.stringify(todos));
+}
+
diff --git a/todo_sytle.css b/todo_sytle.css
new file mode 100644
index 0000000..3b57bf9
--- /dev/null
+++ b/todo_sytle.css
@@ -0,0 +1,272 @@
+
+.attribution { font-size: 11px; text-align: center; }
+
+body {
+ width: 100%;
+ background: #fafafa;
+ font-family: "Josefin Sans", sans-serif;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+}
+
+#background-image {
+ position:absolute;
+ background-image: url("images/bg-desktop-light.jpg");
+ background-position: center;
+ top: 0;
+ left: 0px;
+ width: 100%;
+ height: 300px;
+ z-index: -1;
+ background-repeat: no-repeat;
+ background-size: cover;
+}
+
+.header-container{
+ width: 541px;
+ height: 48px;
+ flex-shrink: 0;
+ margin-top: 70px;
+ display: flex;
+ justify-content: space-between;
+}
+
+.header{
+ color: #FFF;
+ font-size: 40px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: 15px;
+ margin: 0;
+}
+
+
+.input-container{
+ max-width: 540px;
+ width: 90%;
+ height: 64px;
+ margin-top: 40px;
+ flex-shrink: 0;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+}
+
+.oval{
+ width: 24px;
+ height: 24px;
+ fill: #FFF;
+}
+
+.todo-item .oval:hover circle{
+ stroke:#5df;
+}
+
+
+
+
+#input-text{
+ color : #393A4B;
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.25px;
+ border: none;
+ outline: none;
+}
+
+.input-box{
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ gap: 24px;
+ margin-left: 24px;
+}
+
+.main-container{
+ max-width: 540px;
+ width: 90%;
+ min-height: 0px;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ margin-top: 24px;
+}
+
+.bottom-container{
+ max-width: 540px;
+ width: 90%;
+ max-height: 439px;
+ min-height: 50px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(194, 195, 214, 0.50);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: auto;
+ margin-bottom: 16px;
+ padding: 0 24px;
+ box-sizing: border-box;
+}
+body.dark.bottom-container{
+ max-width: 540px;
+ width: 90%;
+ max-height: 439px;
+ min-height: 50px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(0, 0, 0, 0.50);
+ justify-content: space-between;
+ align-items: center;
+ margin-top: auto;
+ margin-bottom: 16px;
+ padding: 0 24px;
+ box-sizing: border-box;
+}
+body.dark.main-container{
+ max-width: 540px;
+ width: 90%;
+ min-height: 0px;
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border-radius: 5px;
+ background: #FFF;
+ box-shadow: 0px 35px 50px -15px rgba(0, 0, 0, 0.50);
+ margin-top: 24px;
+}
+
+.toggle {
+ background: none; /* 배경을 없애거나 단색으로 설정 */
+ border: none; /* 테두리 없애기 */
+ box-shadow: none; /* 그림자 없애기 */
+ padding: 0; /* 기본 여백 없애기 */
+ outline: none; /* 클릭 시 외곽선 없애기 */
+ cursor: pointer; /* 포인터 변경 */
+}
+
+.interaction-container{
+ width: 116px;
+ height: 14px;
+ display: inline-flex;
+ padding-bottom: 4px;
+ justify-content: center;
+ align-items: flex-start;
+ gap: 19px;
+ color: #9495A5;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: -0.194px;
+ margin-left: 40px;
+}
+
+.bottom{
+ color: #9495A5;
+ text-align: right;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+
+
+.footer{
+ margin-top: 49px;
+ color: #9495A5;
+ text-align: center;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+.todo-item{
+ display: flex;
+ width: 492px;
+ height: 24px;
+ flex-shrink: 0;
+ align-items: center;
+ margin-top: 20px;
+}
+
+.todo-text{
+ margin-left: 24px;
+ color: #494C6B;
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.25px;
+}
+
+.line{
+ margin-top: 20px;
+ width: 540px;
+ height: 1px;
+ flex-shrink: 0;
+ background: #E3E4F1;
+}
+
+.cross-line{
+ margin-left: auto;
+ display: none;
+}
+
+/* todo-item에 마우스를 올렸을 때만 cross-line 보이기 */
+.todo-item:hover .cross-line {
+ display: block;
+}
+
+
+
+.filter-button{
+ border: none;
+ outline: none;
+ background: none;
+ color: #9495A5;
+ font-family: "Josefin Sans";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+.clear{
+ color: #9495A5;
+ text-align: right;
+ font-family: "Josefin Sans";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -0.194px;
+}
+
+
+.filter-button.active{
+ color: #3A7CFD;
+}
\ No newline at end of file