투두리스트 완료 기능을 만들어보자! 일단 완료 버튼은 이거로 만들어보기로 했다.
만들려고 했던 기능에서 몇 개를 빼고 완성했다😂
나중에 지금 못만든 기능을 만드는 법을 알게 되면 추가하자.
// TodoItem.js
<label>
<input type="checkbox"/>
<div>
<FontAwesomeIcon icon={faCheck} color="#1a202c" className={styles.checkIcon} />
</div>
</label>
/* TodoItem.module.css */
input[type="checkbox"] {
display: none;
}
label div {
display: flex;
justify-content: center;
align-items: center;
width: 35px;
height: 35px;
background-color: #1a202c;
border: 3px solid rgb(73, 220, 73);
border-radius: 50%;
cursor: pointer;
}
input[type="checkbox"]:checked +div {
background-color: rgb(73, 220, 73);
}
.text {
margin-left: 20px;
}
.checkIcon {
width: 0%;
}
input[type="checkbox"]:checked +div .checkIcon{
transition: width 0.2s ease;
width: 100%;
}
<input type="checkbox"></input>
를 TodoItem 컴포넌트에 추가하였다. 그리고 체크박스와 텍스트를 왼쪽으로 몰기 위해 둘을 div로 묶었고, display: flex
를 주어 가로로 정렬시켰다. (기본이 flex-direction: row
이기 때문에 생략 가능하다고 함)
// TodoItem.js
return (
<div className={styles.textAndDeteteBtn}>
<div className={styles.checkboxAndText}>
<input type="checkbox"></input>
<div className={styles.text}>{props.todo.text}</div>
</div>
{isDeleteClicked ?
<div>
<button className={styles.delteOkIcon} onClick={() => onDelete(props.todo.id)}><FontAwesomeIcon icon={faCheck} size="2x" color="white"/></button>
<button className={styles.delteNoIcon} onClick={cancelDelete}><FontAwesomeIcon icon={faXmark} size="2x" color="white"/></button>
</div>
: <button className={styles.delteIcon} onClick={openDelete}><FontAwesomeIcon icon={faTrashCan} size="2x" color="white"/></button>
}
</div>
);
/* TodoItem.module.css */
.checkboxAndText {
display: flex;
}
그랬더니 이렇게 텍스트가 중앙에 있지 않고 약간 위쪽에 정렬되는 것이다!
이유는, align-items
속성이 기본적으로 stretch
로 설정되어 있기 때문인데, 이를 center
로 설정해주면 해결된다.
label
만들기checkbox 타입을 가진 input 아래에 for로 input과 결합한 label을 만들었다. label은 폼의 양식에 이름을 붙이는 태그이다.
label을 클릭하면 결합된 요소도 클릭이 되기 때문에 이를 이용하여 체크박스를 디자인 할 것이다.
html은 이렇게 만들었다. 이제 기존에 체크박스가 있던 input 요소는 display: none
으로 안보이게 만들고, label에서 새로운 체크박스 모양을 만들고 디자인하여 보여줄 것이다.
// TodoItem.js
<input type="checkbox" id="checkboxId" />
<label for="checkboxId"></label>
/* TodoItem.module.css */
input[type="checkbox"] {
display: none;
}
label {
display: inline-block;
width: 35px;
height: 35px;
background-color: #1a202c;
border: 3px solid rgb(73, 220, 73);
border-radius: 50%;
cursor: pointer;
}
input[type="checkbox"]:checked + label {
background-color: rgb(73, 220, 73);
}
type이 checkbox인 input의 display를 none으로 설정하여 기존의 네모난 체크박스를 숨겼다.
label을 디자인했다.
type이 checkbox인 input이 체크되었을 때 input에 인접한 label의 색을 바꾼다.
💥 여기서 +
가 왜 들어가는지 몰라서 지워봤는데, 속성이 적용되지 않았다. 왜냐하면 input + label
은 input에 인접한 label이라는 뜻인데 반해 input label
은 input 안에 있는 label이라는 뜻이기 때문이다. 코드를 보면 알 수 있지만 label은 input에 인접하여있지 안에 위치하고 있지 않다.
<input type="checkbox" id="checkboxId" />
<label for="checkboxId"></label>
하지만 이렇게 하면 다른 투두의 체크박스를 클릭해도 오직 맨 위에 있는 체크박스만 변경이 된다. 이 이유는 적용되는 id가 같기 때문이다. 따라서 다른 방법을 써보자.
label 안에 input을 쓰고, 그 안에 있는 div로 버튼을 만들고 디자인 할 것이다.
// TodoItem.js
<label>
<input type="checkbox" />
<div />
</label>
/* TodoItem.module.css */
input[type="checkbox"] {
display: none;
}
label div {
display: inline-block;
width: 35px;
height: 35px;
background-color: #1a202c;
border: 3px solid rgb(73, 220, 73);
border-radius: 50%;
cursor: pointer;
}
label input[type="checkbox"]:checked + div {
background-color: rgb(73, 220, 73);
}
label 안에 있는 div로 버튼을 디자인했다.
label 안에 있는 input이 체크되면 input과 인접한 div의 배경색을 바꿨다.
:has()
사용하기 (성공):has()
는 최근에 추가된 문법이라고 한다. input이 체크되면 label의 속성을 변경할 수 있다.
// TodoItem.js
<label>
<input type="checkbox" />
</label>
/* TodoItem.module.css */
input[type="checkbox"] {
display: none;
}
label {
display: inline-block;
width: 35px;
height: 35px;
background-color: #1a202c;
border: 3px solid rgb(73, 220, 73);
border-radius: 50%;
cursor: pointer;
}
label:has(input[type="checkbox"]:checked) {
background-color: rgb(73, 220, 73);
}
먼저 체크 박스를 누르면 나타나는 체크 아이콘을 삽입해보자. FontAwesome에서 아이콘을 가져왔다. 리액트에서 FontAwesome을 사용하는 방법은 지난번 포스팅에 정리해놨다.
<label>
<input type="checkbox" />
<div>
<FontAwesomeIcon icon={faCheck} color="white" />
</div>
</label>
label div
에 flexbox를 설정해서 아래의 코드를 추가해주었다.
display: flex;
justify-content: center;
align-items: center;
isChecked라는 state를 만들어 체크됨과 체크되지 않는 상태를 기록하였고, 이 상태에 따라 체크 표시를 화면에 표시하거나 표시하지 않았다.
// TodoItem.js
const [isChecked, setIsChecked] = useState(false);
<label>
<input type="checkbox" onClick={() => setIsChecked(!isChecked)} />
<div>
{isChecked && <FontAwesomeIcon icon={faCheck} color="white" />}
</div>
</label>
하지만 체크 버튼을 누르면 서서히 나타나게 변경하고 싶었다. 그래서 체크버튼이 눌리기 전에는 크기가 0%로 보이지 않다가, 누르면 서서히 커지게 했다. (state인 isChecked를 없애고 input[type="checkbox"]:checked
를 사용했다)
/* TodoItem.module.css */
.checkIcon {
width: 0%;
}
input[type="checkbox"]:checked +div .checkIcon{
transition: width 0.2s ease;
width: 100%;
}
0.2초간 서서히 확대했다!
체크한 투두의 글자 색을 회색으로 바꾸어 완료된 투두라는 것을 더 확실히 하고 싶었다. 그래서 state로 isChecked를 추가하여 만들었다.(이걸 추가하지 않고 css로만 하는 방법을 모르겠다)
기존의 코드에 아래의 코드를 추가했다.
const [isChecked, setIsChecked] = useState(false);
<div className={isChecked? `${styles.text} ${styles.checked}` : `${styles.text}`}>{props.todo.text}</div>
.text.checked {
color: rgb(107, 107, 107);
}
완성했다!
체크박스를 label
로 커스텀 하는 법을 알게 되어 유익했다!
(처음에는 이미지로 체크박스를 만들려고 했으나 그렇게 되면 tab
버튼으로 체크 박스를 선택할 수 없고 시각장애인이 이것이 체크박스인 것을 알 수 없는 등의 문제가 있다고 하여 커스텀하게 됨)
그리고 더 세련되게 만들고 싶었지만 여기서 마무리해서 아쉽다 👀
완료 기능을 만들기 위해 구글에서 예시를 찾아봤는데, Beautiful CSS checkboxes examples 이곳에서 많은 데모와 코드를 볼 수 있어서 유용했다.
다음 포스팅에서는 수정 기능을 만들어보자!