본문 바로가기

styled-components

ThemeProvider 사용하기 (2) - 토글 버튼 만들어서 theme 토글하기

이 포스트는 Udemy 사이트에 올라 온 Tom Phillips의 "React styled-components v5 (2021 edition)" 강의를 듣고 정리한 것입니다.

 

🔗링크

https://www.udemy.com/course/react-styled-components/

 

React styled components v5 (2021 edition)

Ditch CSS stylesheets! Learn CSS in JS to quickly and cleanly style React components with the styled components library

www.udemy.com

 

 

 

 

일단 토글 버튼 만들기🌀

 

👩‍💻 전체 코드

import styled from "styled-components";

const ToggleWrapper = styled.div`
	width: 50px;
	min-width: 50px;
	height: 25px;
	border-radius: 25px;
	border: 1px solid #666;
	margin: auto;
	display: flex;
	background-image: linear-gradient(
		to bottom,
		${(props) => props.theme.primaryColor},
		${(props) => props.theme.secondaryColor}
	);
`;

const Notch = styled.div`
	height: 21px;
	width: 21px;
	border: 1px solid #666;
	margin-top: 1px;
	background: white;
	border-radius: 50%;
	transform: translate(${(props) => (props.isActive ? "26px" : "1px")});
	transition: transform 0.1s linear;
`;

export function Toggle({ isActive }) {
	return (
		<ToggleWrapper>
			<Notch isActive={isActive} />
		</ToggleWrapper>
	);
}

 

 

 

💻 현재 상태

 

 

theme이 커질테니 themes 폴더를 따로 만들어서 분리해내기 🌀

📂 src > themes > light.js

const theme = {
	id: "light",
	primaryColor: "#f8049c",
	secondaryColor: " #fdd54f",
};

export default theme;

 

 

 

📂 src > themes > dark.js

const theme = {
	id: "dark",
	primaryColor: "black",
	secondaryColor: " midnightblue",
};

export default theme;

 

 

📂 src > components > App.js

import { useState } from "react";
import LogIn from "components/pages/LogIn";
import Home from "components/pages/Home";
import { createGlobalStyle, ThemeProvider } from "styled-components";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import LightTheme from "themes/light";
import DarkTheme from "themes/dark";

const GlobalStyle = createGlobalStyle`
	body{
		background-color: white;
		color:black;
		min-height:100vh;
		margin:0;
		font-family: 'Kaushan Script', cursive;
	}
`;

function App() {
	const [theme, setTheme] = useState(LightTheme);
	return (
		<ThemeProvider
			theme={{
				...theme,
				setTheme: () => {
					setTheme((theme) =>
						theme.id === "light" ? DarkTheme : LightTheme
					);
				},
			}}
		>
			<GlobalStyle />
			<BrowserRouter>
				<Switch>
					<Route path="/login">
						<LogIn />
					</Route>
					<Route path="/">
						<Home />
					</Route>
				</Switch>
			</BrowserRouter>
		</ThemeProvider>
	);
}
export default App;

 

 

 

 

 

 

본격적으로 토글 기능 만들기 🌀

📂 src > components > common > Header.js

import { useState, useContext } from "react";
import styled, { ThemeContext } from "styled-components";
import { Link as ReactRouterDomLink, useLocation } from "react-router-dom";
import { Toggle } from "./Toggle";

const HeaderWrapper = styled.header`
	box-sizing: border-box;
	display: flex;
	height: 60px;
	width: 100%;
	padding: 0 16px;
	position: fixed;
	top: 0;
	background-image: linear-gradient(
		to right,
		${(props) => props.theme.primaryColor},
		${(props) => props.theme.secondaryColor}
	);
	border-bottom: 3px solid #fdd54f;
`;
const Menu = styled.nav`
	display: ${(props) => (props.open ? "block" : "none")};
	font-family: "Open Sans";
	position: absolute;
	width: 100%;
	top: 60px;
	left: 0;
	padding: 8px;
	box-sizing: border-box;
	border-bottom: 3px solid ${(props) => props.theme.secondaryColor};
	background: white;
	@media (min-width: 768px) {
		display: flex;
		margin: auto 0 auto auto;
		background: none;
		position: relative;
		top: initial;
		left: initial;
		width: initial;
		border-bottom: none;
	}
`;
const Link = ({ isActive, children, ...props }) => {
	return <ReactRouterDomLink {...props}>{children}</ReactRouterDomLink>;
};
const StyledLink = styled(Link)`
	box-sizing: border-box;
	display: block;
	padding: 4px 8px;
	margin: 0 auto;
	text-align: center;
	color: black;
	font-weight: ${(props) => (props.isActive ? "bold" : "normal")};
`;
const MobileMenuIcon = styled.div`
	margin: auto 0 auto auto;
	width: 25px;
	min-width: 25px;
	padding: 5px;
	> div {
		height: 3px;
		background: black;
		margin: 5px 0;
		width: 100%;
	}
	@media (min-width: 768px) {
		display: none;
	}
`;
export default function Header() {
	const { pathname } = useLocation();
	const [menuOpen, setMenuOpen] = useState(false);
	const { id, setTheme } = useContext(ThemeContext);

	return (
		<HeaderWrapper>
			<MobileMenuIcon onClick={() => setMenuOpen(!menuOpen)}>
				<div />
				<div />
				<div />
			</MobileMenuIcon>
			<Menu open={menuOpen}>
				<StyledLink to="/" isActive={pathname === "/"}>
					Home
				</StyledLink>
				<StyledLink to="/login" isActive={pathname === "/login"}>
					LogIn
				</StyledLink>
				<Toggle isActive={id === "dark"} onToggle={setTheme} />
			</Menu>
		</HeaderWrapper>
	);
}

 

 

 

 

📂 src > components > common > Toggle.js

import styled from "styled-components";
const ToggleWrapper = styled.div`
	width: 50px;
	min-width: 50px;
	height: 25px;
	border-radius: 25px;
	border: 1px solid #666;
	margin: auto;
	display: flex;
	background-image: linear-gradient(
		to bottom,
		${(props) => props.theme.primaryColor},
		${(props) => props.theme.secondaryColor}
	);
`;
const Notch = styled.div`
	height: 21px;
	width: 21px;
	border: 1px solid #666;
	margin-top: 1px;
	background: white;
	border-radius: 50%;
	transform: translate(${(props) => (props.isActive ? "26px" : "1px")});
	transition: transform 0.1s linear;
`;

export function Toggle({ isActive, onToggle }) {
	return (
		<ToggleWrapper onClick={onToggle}>
			<Notch isActive={isActive} />
		</ToggleWrapper>
	);
}