오늘은 styled components를 활용하여 웹페이지 상에서 자주 사용되는 UI를 구현하는 스프린트를 진행하였다. React와 CSS의 문법과 기능을 복습하면서 새로운 작성 방식을 익힐 수 있었다.
import { useState } from 'react'; import styled from 'styled-components'; export const ModalContainer = styled.div` text-align: center; margin: 125px auto; width: 100%; height: 100%; `; export const ModalBackdrop = styled.div` position: fixed; left: 0; top: 0; width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.8); `; export const ModalBtn = styled.button` background-color: #4000c7; text-decoration: none; border: none; padding: 20px; color: white; border-radius: 30px; cursor: grab; `; export const ModalView = styled.div.attrs(props => ({ role: 'dialog' }))` width: 320px; padding: 1.5rem; background: white; border-radius: 2px; margin: 0; `; export const Modal = () => { const [isOpen, setIsOpen] = useState(false); const openModalHandler = () => { setIsOpen(!isOpen) }; return ( <> <ModalContainer onClick={openModalHandler}> <ModalBtn onClick={openModalHandler}> {isOpen ? "Opened!" : "Open Modal"} </ModalBtn> {isOpen ? <ModalBackdrop onClick={openModalHandler}> <ModalView> <div onClick={openModalHandler}> × </div> Hello CodeStates! </ModalView> </ModalBackdrop>: ""} </ModalContainer> </> ); };
import { useState } from 'react'; import styled from 'styled-components'; const TabMenu = styled.ul` background-color: #dcdcdc; color: rgba(73, 73, 73, 0.5); font-weight: bold; display: flex; flex-direction: row; justify-items: center; align-items: center; list-style: none; margin-bottom: 7rem; border-radius: 10px; .submenu { ${'' /* 기본 Tabmenu 에 대한 CSS를 구현합니다. */} padding-top: 0.8rem; padding-bottom: 0.8rem; padding-left: 0.9rem; text-align: center; padding-right: 0.9rem; cursor: pointer; border-radius: 10px; } .focused { ${'' /* 선택된 Tabmenu 에만 적용되는 CSS를 구현합니다. */} color: #ffffff; background-color: #4900CE; } & div.desc { text-align: center; } `; const Desc = styled.div` text-align: center; `; export const Tab = () => { // TIP: Tab Menu 중 현재 어떤 Tab이 선택되어 있는지 확인하기 위한 // currentTab 상태와 currentTab을 갱신하는 함수가 존재해야 하고, 초기값은 0 입니다. const [currentTab, setCurrentTab] = useState(0); const menuArr = [ { name: 'Tab1', content: 'Tab menu ONE' }, { name: 'Tab2', content: 'Tab menu TWO' }, { name: 'Tab3', content: 'Tab menu THREE' }, ]; const selectMenuHandler = (index) => { setCurrentTab(index); }; return ( <> <div> <TabMenu> {menuArr.map((menu, idx) => { return ( <li key={idx} className={idx === currentTab ? 'submenu focused' : 'submenu' } onClick={() => selectMenuHandler(idx)}> {menu.name} </li> ); })} </TabMenu> <Desc> <p>{menuArr[currentTab].content}</p> </Desc> </div> </> ); };
import { useState } from 'react'; import styled from 'styled-components'; export const TagsInput = styled.div` margin: 8rem auto; display: flex; align-items: flex-start; flex-wrap: wrap; min-height: 48px; width: 480px; padding: 0 8px; border: 1px solid rgb(214, 216, 218); border-radius: 6px; ul { display: flex; flex-wrap: wrap; padding: 0; margin: 8px 0 0 0; .tag { width: auto; height: 32px; display: flex; align-items: center; justify-content: center; color: #fff; padding: 0 8px; font-size: 14px; list-style: none; border-radius: 6px; margin: 0 8px 8px 0; background: #4000c7; .tag-close-icon { display: block; width: 16px; height: 16px; line-height: 16px; text-align: center; font-size: 14px; margin-left: 8px; color: #4000c7; border-radius: 50%; background: #fff; cursor: pointer; } } } input { flex: 1; border: none; height: 46px; font-size: 14px; padding: 4px 0 0 0; :focus { outline: transparent; } } &:focus-within { border: 1px solid #4000c7; } `; export const Tag = () => { const initialTags = ['CodeStates', 'kimcoding']; const [tags, setTags] = useState(initialTags); const removeTags = (indexToRemove) => { // TODO : 태그를 삭제하는 메소드를 완성하세요. setTags(tags.filter(tag => indexToRemove !== tags.indexOf(tag))); }; const addTags = (event) => { if (!tags.includes(event.target.value) && event.target.value !== '') { setTags([...tags, event.target.value]) event.target.value = ''; } } return ( <> <TagsInput> <ul id='tags'> {tags.map((tag, index) => ( <li key={index} className='tag'> <span className='tag-title'>{tag}</span> <span className='tag-close-icon' onClick={() => removeTags(index)}>× </span> </li> ))} </ul> <input className='tag-input' type='text' onKeyUp={ (event) => event.key === 'Enter' ? addTags(event) : '' } placeholder='Press enter to add tags' /> </TagsInput> </> ); };
import { useState } from 'react'; import { isCompositeComponent } from 'react-dom/test-utils'; import styled from 'styled-components'; const ToggleContainer = styled.div` position: relative; margin-top: 8rem; left: 47%; cursor: pointer; .toggle-container { width: 50px; height: 24px; border-radius: 30px; background-color: #8b8b8b; &.toggle--checked { background-color: #4900CE; } } .toggle-circle { position: absolute; top: 1px; left: 1px; width: 22px; height: 22px; border-radius: 50%; background-color: #ffffff; &.toggle--checked { left: 27px } } `; const Desc = styled.div` text-align: center; `; export const Toggle = () => { const [isOn, setisOn] = useState(false); const toggleHandler = () => { setisOn(!isOn) }; return ( <> <ToggleContainer onClick={toggleHandler}> <div className={`toggle-container ${isOn ? "toggle--checked" : ""}`}/> <div className={`toggle-circle ${isOn ? "toggle--checked" : ""}`}/> </ToggleContainer> <Desc>{isOn ? "Toggle Switch ON":"Toggle Switch OFF"}</Desc> </> ); };
styled components는 React와 CSS의 기본적인 문법을 기반으로 해서 기능 구현을 위해 코드를 작성하는 것은 어렵지 않았으나 스프린트를 진행하면서 CSS에 대한 공부가 부족하다는 것을 느낄 수 있었다. 만약 프론트엔드 포지션으로 일하게 된다면 CSS는 필수적이고 백엔드 포지션이라도 기본적인 내용은 알아야 하기 때문에 따로 공부해야겠다고 느꼈다.