Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1주차] 권혜인 미션 제출합니다 #6

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9438c31
🎉 [ init ] 개발 뼈대 설정
hae2ni Sep 5, 2024
8c8b28a
💄 [ design ] style.css, reset.css 설정
hae2ni Sep 5, 2024
98cfce3
💄 design : header design 추가
hae2ni Sep 5, 2024
5a25e9c
⚡️ feat : js로 오늘의 날짜 불러오기
hae2ni Sep 5, 2024
d2c887c
💄 design : new task 버튼 css 추가
hae2ni Sep 5, 2024
da3d2c5
💄 design : 뼈대 구성
hae2ni Sep 5, 2024
e24af32
💄 design : media query 추가
hae2ni Sep 5, 2024
e04b568
✨ feat : 오늘의 날짜 함수
hae2ni Sep 5, 2024
a06a97f
✨ feat : header에 날짜 나타내는 함수 실행
hae2ni Sep 5, 2024
6330567
💄 design : input css 추가
hae2ni Sep 5, 2024
629db7a
🔥 remove : 파일 삭제
hae2ni Sep 5, 2024
17220a4
✨ feat : dom 불러오기
hae2ni Sep 5, 2024
afabf04
✨ feat : newTask onClick 함수 구현
hae2ni Sep 5, 2024
6ce1fdc
🚚 etc : camelCase로 변경
hae2ni Sep 5, 2024
55c60b6
✨ feat : 할일 input dom 추가
hae2ni Sep 5, 2024
1882cd9
✨ feat: onClick 함수 불러오기
hae2ni Sep 5, 2024
b0b05b7
✨ feat: 남아있는 할일 체크박스 구현
hae2ni Sep 5, 2024
59a2ef9
💄 design : 체크박스 css 추가
hae2ni Sep 5, 2024
24cd2dd
✨ feat: 확인 버튼 이벤트함수추가
hae2ni Sep 5, 2024
b44d056
💄 design: ul 태그 추가
hae2ni Sep 5, 2024
1a41831
🚚 rename: 파일 이름 변경
hae2ni Sep 5, 2024
e6742d2
✨ feat: 상수형 dom 추가
hae2ni Sep 5, 2024
04f1721
🐛 fix: import 오류 해결
hae2ni Sep 5, 2024
07eadba
💄 design: design 추가
hae2ni Sep 5, 2024
3783da8
🚚 rename: 함수이름변경
hae2ni Sep 5, 2024
0b089f5
💄 design: pretandard 추가
hae2ni Sep 5, 2024
9998c26
✨ feat: checkbox checked유무에 따른 취소선
hae2ni Sep 5, 2024
eb80030
💄 design: pretendard
hae2ni Sep 6, 2024
611b55c
🐛 fix: date 하루 에러 제거
hae2ni Sep 6, 2024
921ce4c
♻️ refact: 재활용할 수 있도록 함수 변경
hae2ni Sep 6, 2024
9a979a0
♻️ refact: refact code
hae2ni Sep 6, 2024
6ed61a4
✨feat: 함수 localStorage에 저장, 삭제 구현
hae2ni Sep 6, 2024
90c5eeb
🚑 hotfix: localstorage에 저장된 후 새로고침했을 때 빈 배열이 나타나는 오류 해결 (for..in: 모든 …
hae2ni Sep 6, 2024
36d9c41
✨ feat: showTask main에 추가
hae2ni Sep 6, 2024
467cbbe
✨ feat: append method 추가
hae2ni Sep 6, 2024
f1265ad
⚰️ removedead: 쓸모없는 코드 삭제
hae2ni Sep 6, 2024
3bd3c64
🚀 deploy: 브랜치 변경 반영되라
hae2ni Sep 6, 2024
396380d
🚑 🚀 hotfix: 경로로 인한 배포 오류 수정
hae2ni Sep 6, 2024
b757d85
🎨 etc: 각주
hae2ni Sep 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vanilla Todo</title>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="./style/todo.css" />
<title>혜인이의 TODO LIST</title>
</head>

<body>
<div class="container"></div>
<header>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

시멘틱 태그의 장점으로 코드 가독성 및 유지보수가 용이하며 검색 엔진에 최적화된다는 것이 있는데 혜인 님의 html은 장점을 잘 살려 시멘틱 태그를 활용해 주신 듯 합니다!

태그를 이용해 섹션을 나누신 것 👍🏻👍🏻 전 이 태그를 까먹고 있었습니다 ㅠㅠ

<h1 id="todayDateId"></h1>
</header>
<main>
<div class="mainWrapper">
<section class="todoListWrapper">
<ul class="todosWrapper"></ul>
</section>
<button class="newTaskBtn">New Task</button>
</div>
</main>
</body>
<script src="script.js"></script>
<script type="module" src="./js/main.js"></script>
</html>
8 changes: 8 additions & 0 deletions js/constants/date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//헤더에 해당되는 날짜 상수화 파일입니다.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 상수를 따로 모듈화 해서 여러 곳에서의 재사용 성을 높이고 유지 보수를 쉽게 한 것은 매우 좋은 것 같습니다!!! 👍🏻👍🏻


// 오늘 날짜 가져오기
const currentDate = new Date();

//각각의 달, 일 가져오기
export const todayMonth = currentDate.getMonth() + 1;
export const todayDAY = currentDate.getDate();
Comment on lines +7 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 todyaDAY만 컨벤션에 어긋나 있네요!
그리고 날짜를 다룰 때는 date를 일, day를 요일로 쓰는 경우가 많아서 혼동할 가능성이 있을 것 같아요.
또, today+month나 today+day라는 조합이 어색해 보여요. 저라면 currentDatecurrentDateObject로 바꾸고 달, 일을 currentMonth, currentDate라고 쓸 것 같아요!

11 changes: 11 additions & 0 deletions js/constants/document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//html에 있는 dom 요소들 상수화

//header dom
export const headerToday = document.getElementById("todayDateId");

//할일 dom
export const todolistWrapper = document.querySelector(".todoListWrapper");
export const todosWrapper = document.querySelector(".todosWrapper");

//할일 추가 button
export const newTaskBtn = document.querySelector(".newTaskBtn");
17 changes: 17 additions & 0 deletions js/constants/newTask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//new Task를 눌렀을 때 나타나는 task input
const newTodoInputEmoji = document.createElement("p");
newTodoInputEmoji.textContent = "✨";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기에서 textContent를 써주신 부분이 좋았어요👍👍 저는 innerText를 썼는데, 검색해보니 innerText는 스타일 재계산이 일어나지만, textContent는 스타일 재계산이 일어나지 않으므로 간단한 텍스트 업데이트에는 textContent가 더 성능이 좋다고 합니다! 저두 textContent로 바꿔야겠어요🫶🫶


let todoInput = document.createElement("input");
todoInput.className = "todoInput";
todoInput.type = "text";

const confirmBtn = document.createElement("button");
confirmBtn.className = "confirmBtn";
confirmBtn.textContent = "확인";

let newTodoInputContainer = document.createElement("div");
newTodoInputContainer.className = "newTodoInputContainer";
newTodoInputContainer.append(newTodoInputEmoji, todoInput, confirmBtn);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래에는 appendChild를 쓰신 부분이 있던데 append와 appendChild의 차이점 (append는 노드 객체와 문자열 모두 추가할 수 있는 반면, appendChild는 노드 객체만 추가 가능하다 & append는 한번에 여러개의 자식 노드를 추가할 수 있지만, appendChild는 한번에 한 개만 가능하다.) 을 잘 이해하고 계신 듯 합니다! 👍🏻

저는 기존에 appendChild만 알고 있었고 그것만 사용했는데... 제가 수정이 필요할 것 같네용 😂


export { todoInput, confirmBtn, newTodoInputContainer };
7 changes: 7 additions & 0 deletions js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { headerInsertDate } from "./utils/header.js";
import { newTaskFn } from "./utils/makeNewTask.js";
import { showTask } from "./utils/showTask.js";

newTaskFn();
headerInsertDate();
showTask();
48 changes: 48 additions & 0 deletions js/utils/handleNewTasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { todosWrapper } from "../constants/document.js";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 기능별 함수를 모듈화 해서 사용하신 것도 위의 상수 모듈화와 동일하게 유지보수에 강점을 가지고 있는 것 같습니다 👍🏻

import { handleDeleteStoreTask } from "./storeTask.js";

//element 추가 함수
export function handleNewTask(todoId, todoValue) {
let doneBtnContainer = document.createElement("div");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 보기에는 doneBtnContainer 객체의 property가 수정되는 일은 있지만, 다른 객체로 재할당되는 일은 없는 것 같아요. 아래의 doneBtn, doneBtnLabel 등의 객체들도 const로 선언하셔도 될 것 같아요!

doneBtnContainer.className = "doneBtnContainer";

let doneBtn = document.createElement("input");
doneBtn.id = todoId;
doneBtn.type = "checkbox";

let doneBtnLabel = document.createElement("label");
doneBtnLabel.className = "doneBtnLabel";
doneBtnLabel.htmlFor = doneBtn.id;

doneBtnContainer.append(doneBtn, doneBtnLabel);

let deleteBtn = document.createElement("button");
deleteBtn.className = "deleteBtn";
deleteBtn.type = "button";
deleteBtn.textContent = "🗑️";
//삭제
deleteBtn.addEventListener("click", () => {
onetodoWrapper.remove();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

삭제 버튼 클릭 시 onetodoWrapper.remove()를 호출하셨는데, onetodoWrapper 변수가 선언되기 전에 사용하신 것을 보아 JS의 호이스팅을 이용하신 것 같습니당
그러나 let, const 변수는 선언 전에 사용하려고 하면 참조 오류가 발생한다고 알고 있습니다 그러니 변수를 선언한 후 사용하도록 위치를 조정하는 것은 어떨까요?!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+) 추가로, todo 삭제 시 alert나 'confirm('이 할 일을 삭제하시겠습니까?')' 등을 사용해 사용자에게 알리는 것은 어떨까용?!

이 점은 저도 수정하려고 합니다! 저는 단순히 alert만으로 삭제한다는 것을 알려주기만 하고 '취소' 선택지를 두지 않았었는데 confirm을 사용해 사용자가 선택할 수 있게 하면 더 좋을 것 같습니다!! 👍🏻👍🏻

handleDeleteStoreTask(todoId);
});

let onetodoWrapper = document.createElement("li");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 onetodoWrapper라는 변수 이름을 이해하기 어려웠어요🥹 one + todo + wrapper의 조합이라고 생각하고 코멘트 드리겠습니다...!
보통 단일 아이템은 todoItem, todoListItem 등으로 사용하고(wrapper라기보다는 그냥 아이템 그 자체인 것 같아서 wrapper는 뗐어요), 이 li 아이템들을 모은 ultodoList정도로 사용하는 것 같아요. 이 부분은 혜인님 취향에 맞게 찾아보시고 사용하면 좋을 것 같아요!

onetodoWrapper.className = "onetodoWrapper";

let todoContent = document.createElement("p");
todoContent.className = "todoContent";

onetodoWrapper.append(doneBtnContainer, todoContent, deleteBtn);

//할일 입력됨
todoContent.textContent = todoValue;

doneBtn.addEventListener("change", () => {
if (doneBtn.checked) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혜인 님의 todolist를 사용해 보았는데 todo를 하나 생성하고 완료 표시를 누른 뒤 새로 고침을 하면 체크 표시가 초기화 되더라고요 😂😂

완료 상태 또한 localStorage에 저장해서 새로 고침되어도 상태가 유지될 수 있게 하는 방식은 어떨까요?! 그러기 위해서는 아이디와 값만을 저장하는 것이 아니라 배열 형태로 list를 저장하는 것을 추천 드립니다...!!
(더 좋은 방법이 있는 것 같다면 공유 부탁 드립니다 👍🏻)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


진짜
생각도
못한
이슈네여
ㅋㅎㅋㅎ,,,,
진짜 완료 표시 local저장 생각지도 못했던,,, 아 감사합니다!!하하하하,,,

todoContent.style.textDecoration = "line-through";
} else {
todoContent.style.textDecoration = "none";
}
});
todosWrapper.append(onetodoWrapper);
}
7 changes: 7 additions & 0 deletions js/utils/header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { todayDAY, todayMonth } from "../constants/date.js";
import { headerToday } from "../constants/document.js";

//각 날짜 불러오기
export function headerInsertDate() {
headerToday.append(todayMonth, "월", " ", todayDAY, "일");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기에서는 텍스트를 하나하나 append해주는 방식으로 날짜를 알려주셨는데, 저라면 템플릿 리터럴을 사용해서 이렇게 작성해볼 것 같아요!

headerToday.textContent = `${todayMonth}${todayDAY}일`

템플릿 리터럴을 이용하면 어디에 변수가 들어갈 지 알기 쉬워서 좋은 것 같아요😘

}
29 changes: 29 additions & 0 deletions js/utils/makeNewTask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { newTaskBtn, todolistWrapper } from "../constants/document.js";
import { confirmBtn, newTodoInputContainer } from "../constants/newTask.js";
import { handleNewTask } from "./handleNewTasks.js";
import { todoInput } from "../constants/newTask.js";
import { handleStoreTask } from "./storeTask.js";

//NewTask 버튼 눌렀을 때
function onClickNewTaskBtn() {
newTaskBtn.addEventListener("click", () => {
todolistWrapper.appendChild(newTodoInputContainer);
});
}

//확인 눌렀을 때
function onClickConfirmBtn() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 todoInput.value에 대한 유효성 검사가 없어서 빈 입력값도 저장되고 있습니다 😂

입력 값이 비어있는 경우 할 일이 추가되지 않도록

if (todoInput.value.trim() === "") {
    alert("할 일을 입력하세요!");
    return;

이렇게 입력 값이 비었으면 함수가 종료되도록 수정하는 것은 어떨까요?!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+) 확인 버튼을 클릭해야만 새로운 todo가 생기는 것이 아니라 enter 키를 눌렀을 때도 새로운 todo가 생기도록 추가하면 어떨까요!?

제가 혜인 님의 todolist를 테스트하면서 습관적으로 enter을 치고 있더라고요 😂😂 저와 같은 사용자를 위해 enter로도 todo가 추가되도록 todoInput 요소에 keydown 이벤트 리스너를 추가하여 엔터 키를 감지하고, 엔터 키가 눌렸을 때 새로운 todo를 추가하는 함수를 호출하는 방식을 추천 드립니다...!!! (더 나은 방식이 있다면 채택 후 공유 부탁드립니다 👍🏻👍🏻👍🏻)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그러네요! 제가 사용자 입장에서 생각할 시간이 많이 부족했던 것 같습니다ㅠㅠ! 생각도 못한 부분들 짚어주셔서 넘넘 감사합니다!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 input, button 태그를 form으로 감싼 다음 click 대신에 submit 이벤트를 이용합니다!
대신에 submit이 일어나면 브라우저가 새로고침되기 때문에 이를 원하지 않으면 event.preventDefault() 메소드를 호출해주셔야 해요!

confirmBtn.addEventListener("click", (e) => {
let todoId = Math.random();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고유 id 생성을 위해 Math.random()을 사용하셨는데, 이 방식은 완전히 고유성을 보장하지 않기 때문에 같은 ID가 생성될 가능성이 (아주 낮지만) 존재하며, 이런 경우 기능이 의도한 대로 작동하지 않을 수 있다고 알고 있습니다!

UUID나 Date.now()를 활용하는 방식 등 더 고유성을 보장하는 방법을 사용하는 것으로 개선하는 것은 어떨까요?!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고유 id 생성을 위해 Math.random()을 사용하셨는데, 이 방식은 완전히 고유성을 보장하지 않기 때문에 같은 ID가 생성될 가능성이 (아주 낮지만) 존재하며, 이런 경우 기능이 의도한 대로 작동하지 않을 수 있다고 알고 있습니다!

UUID나 Date.now()를 활용하는 방식 등 더 고유성을 보장하는 방법을 사용하는 것으로 개선하는 것은 어떨까요?!

의견감사합니당

handleNewTask(todoId, todoInput.value);
handleStoreTask(todoId, todoInput.value);
todoInput.value = "";
});
}

function newTaskFn() {
onClickNewTaskBtn();
onClickConfirmBtn();
}

export { newTaskFn };
13 changes: 13 additions & 0 deletions js/utils/showTask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { handleNewTask } from "./handleNewTasks.js";

export function showTask() {
if (localStorage.length > 0) {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
if (value) {
handleNewTask(key, value);
}
}
Comment on lines +3 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 우선 저는 이렇게 localStorage <= 0일 때의 동작이 정의되지 않은 경우,
if (localStorage.length === 0) {
  return;
}
for (let i = 0; i < localStorage.length; i++) {
  ...
}

처럼 early return을 하려고 해요! 코드의 depth를 줄일 수 있어서 과도한 들여쓰기를 피할 수 있거든요!! 물론 상황에 따라서 장단점이 있으니 한번 알아보시면 좋을 것 같아요!!

  1. 지금은 for문을 이용해서 반복문을 사용하고 계신데요, 물론 for문도 좋지만, 저는 forEach가 어떤 일을 하는지 더 명확하게 알아볼 수 있다고 생각해요. 이 부분도 한번 고려해보세요!
Object.keys(localStorage).forEach(key => {
  const value = localStorage.getItem(key);
  if (value) {
    handleNewTask(key, value);
  }
});

}
}
7 changes: 7 additions & 0 deletions js/utils/storeTask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function handleStoreTask(id, value) {
window.localStorage.setItem(id, value);
}

export function handleDeleteStoreTask(id) {
window.localStorage.removeItem(id);
}
1 change: 0 additions & 1 deletion script.js

This file was deleted.

1 change: 0 additions & 1 deletion style.css

This file was deleted.

151 changes: 151 additions & 0 deletions style/checkbox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
.doneBtnContainer {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

css 파일도 역할별로 모듈화 하니 유지보수에 정말 좋을 것 같습니다!! 👍🏻

저는 하나의 css 파일에 모든 디자인 요소를 모아두는 바람에 항상 ctrl F를 사용했었는데 ㅠㅠ 다음에는 저도 모듈화에 좀 더 힘을 써야겠다고 느껴지네용 👍🏻👍🏻

box-sizing: border-box;
--background-color: #fff;
--checkbox-height: 10px;
}

@-moz-keyframes dothabottomcheck-19 {
0% {
height: 0;
}
100% {
height: calc(var(--checkbox-height) / 2);
}
}

@-webkit-keyframes dothabottomcheck-19 {
0% {
height: 0;
}
100% {
height: calc(var(--checkbox-height) / 2);
}
}

@keyframes dothabottomcheck-19 {
0% {
height: 0;
}
100% {
height: calc(var(--checkbox-height) / 2);
}
}

@keyframes dothatopcheck-19 {
0% {
height: 0;
}
50% {
height: 0;
}
100% {
height: calc(var(--checkbox-height) * 1.2);
}
}

@-webkit-keyframes dothatopcheck-19 {
0% {
height: 0;
}
50% {
height: 0;
}
100% {
height: calc(var(--checkbox-height) * 1.2);
}
}

@-moz-keyframes dothatopcheck-19 {
0% {
height: 0;
}
50% {
height: 0;
}
100% {
height: calc(var(--checkbox-height) * 1.2);
}
}

.doneBtnContainer input[type="checkbox"] {
display: none;
}

.doneBtnContainer .doneBtnLabel {
height: var(--checkbox-height);
width: var(--checkbox-height);
background-color: transparent;
border: calc(var(--checkbox-height) * 0.1) solid #000;
border-radius: 3px;
position: relative;
display: inline-block;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-moz-transition: border-color ease 0.2s;
-o-transition: border-color ease 0.2s;
-webkit-transition: border-color ease 0.2s;
transition: border-color ease 0.2s;
cursor: pointer;
}
.doneBtnContainer .doneBtnLabel::before,
.doneBtnContainer .doneBtnLabel::after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
position: absolute;
height: 0;
width: calc(var(--checkbox-height) * 0.2);
background-color: #d88fd8;
display: inline-block;
-moz-transform-origin: left top;
-ms-transform-origin: left top;
-o-transform-origin: left top;
-webkit-transform-origin: left top;
transform-origin: left top;
border-radius: 5px;
content: " ";
-webkit-transition: opacity ease 0.5;
-moz-transition: opacity ease 0.5;
transition: opacity ease 0.5;
}
.doneBtnContainer .doneBtnLabel::before {
top: calc(var(--checkbox-height) * 0.72);
left: calc(var(--checkbox-height) * 0.41);
box-shadow: 0 0 0 calc(var(--checkbox-height) * 0.05) var(--background-color);
-moz-transform: rotate(-135deg);
-ms-transform: rotate(-135deg);
-o-transform: rotate(-135deg);
-webkit-transform: rotate(-135deg);
transform: rotate(-135deg);
}
.doneBtnContainer .doneBtnLabel::after {
top: calc(var(--checkbox-height) * 0.37);
left: calc(var(--checkbox-height) * 0.05);
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}

.doneBtnContainer input[type="checkbox"]:checked + .doneBtnLabel,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

체크 박스 스타일에 많은 신경을 쓰신 것으로 보입니다!! 👏🏻👏🏻 정말 체크할 때마다 체크 표시가 느리게 나타나고 체크 박스 색이 변화하는 것은 시각적으로 정말 매력적인 것 같아요 👍🏻

.doneBtnContainer .doneBtnLabel.checked {
border-color: #d88fd8;
}
.doneBtnContainer input[type="checkbox"]:checked + .doneBtnLabel::after,
.doneBtnContainer .doneBtnLabel.checked::after {
height: calc(var(--checkbox-height) / 2);
-moz-animation: dothabottomcheck-19 0.2s ease 0s forwards;
-o-animation: dothabottomcheck-19 0.2s ease 0s forwards;
-webkit-animation: dothabottomcheck-19 0.2s ease 0s forwards;
animation: dothabottomcheck-19 0.2s ease 0s forwards;
}
.doneBtnContainer input[type="checkbox"]:checked + .doneBtnLabel::before,
.doneBtnContainer .doneBtnLabel.checked::before {
height: calc(var(--checkbox-height) * 1.2);
-moz-animation: dothatopcheck-19 0.4s ease 0s forwards;
-o-animation: dothatopcheck-19 0.4s ease 0s forwards;
-webkit-animation: dothatopcheck-19 0.4s ease 0s forwards;
animation: dothatopcheck-19 0.4s ease 0s forwards;
Comment on lines +136 to +150
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 before, after로 체크표시를 만들고 애니메이션까지 넣는건 처음 봐요!! 짱신기하네용...👍

}
Loading