React에서 다크 모드 전환

portfolio site를 다시 만들 때 디자인에 재미를 느끼고 싶었고 어둡고 밝은 모드가 적합하다는 것을 알았습니다. 나는 이 프로젝트에서 발견한 많은 것들을 즐겼지만, 내가 가장 좋아하는 것은 색상이 변하는 SVG입니다. 이 튜토리얼은 React에 익숙하다고 가정하고 v17.0.1 및 기능 구성 요소를 사용하고 있습니다.

먼저 기본 레이아웃을 만들었습니다. 다음으로 darklight 색 구성표를 조합했습니다. 약간의 시행착오가 필요했지만 sufficient contrast에 대한 모든 조합을 테스트하고 배치를 실험한 후 6개의 CSS 변수가 필요하다는 것을 알았습니다. 변수 이름이 어두운 테마의 컨텍스트에서 의미가 있기 때문에 "어두운 우선"개발을 사용했다고 말할 수 있습니다. 밝은 테마는 변형이 적지만 필요합니다--button-border. 여기서 --accent는 배경과 같은 색상이 됩니다.

.theme-dark {
  --dark-text: #292929;
  --light-text: #F9F8F8;  
  --dark-background: #2F4550;
  --light-background: #586F7C;
  --accent: #B8DBD9;
  --button-border: #B8DBD9;
}



.theme-light {
  --dark-text: #5E4B56;
  --light-text: #5E4B56;
  --dark-background: #DBE7E4;
  --light-background: #EDDCD2;
  --accent: #DBE7E4;
  --button-border: #5E4B56;
}


그런 다음 기본 레이아웃에 색상을 적용하기 시작했습니다.

html, #root {
  background-color: var(--dark-background);
  color: var(--dark-text);
}

nav {
  background-color: var(--dark-background);
  color: var(--light-text);
}

.main-container {
  background-color: var(--light-background);
}


팝하고 싶은 콘텐츠 섹션의 배경도 --accent 로 설정했습니다. --dark-text는 어두운 테마의 모든 배경에서 작업했을텐데 섹션 제목을 --light-text로 설정하여 더 눈에 띄게 했습니다.

Musthaq Ahamad'sbasic theme switcher tutorial를 찾았고 이를 기능적인 React 구성 요소에 적용하기 시작했습니다.
테마를 변경하고 테마 기본 설정에 대한 localStorage를 확인하는 기능을 themes.js 라는 파일에 넣었습니다.

function setTheme(themeName) {
    localStorage.setItem('theme', themeName);
    document.documentElement.className = themeName;
}

function keepTheme() {
  if (localStorage.getItem('theme')) {
    if (localStorage.getItem('theme') === 'theme-dark') {
      setTheme('theme-dark');
    } else if (localStorage.getItem('theme') === 'theme-light') {
      setTheme('theme-light')
    }
  } else {
    setTheme('theme-dark')
  }
}

module.exports = {
  setTheme,
  keepTheme
}


App.js 파일에서 내 keepTheme()useEffect() 를 추가했습니다.

import { keepTheme } from './utils/themes';

function App() {
  useEffect(() => {
      keepTheme();
  })
}


다음으로 내 탐색 모음 구성 요소에 토글 구성 요소를 추가했습니다. Chris Bongers’ Tutorial 을 기반으로 Katia De Juan’s Dribbble 토글 스타일을 지정했습니다. 그럼 나adjusted the size and flipped it to default to dark mode . 이 토글이 너무 귀여워서 죽을 수도 있지만 이 튜토리얼은 모든 <button> 또는 클릭 가능한 <input> 에서 작동합니다. 먼저 기본 JSX, 로컬 상태 및 localStorage에서 가져온 테마를 저장할 변수를 설정합니다.

import React, { useEffect, useState } from 'react';
import '../styles/toggle.css';
import { setTheme } from '../utils/themes';

function Toggle() {
  const [togClass, setTogClass] = useState('dark');
  let theme = localStorage.getItem('theme');
  return (
        <div className="container--toggle">
           <input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} />
            <label htmlFor="toggle" className="toggle--label">
                <span className="toggle--label-background"></span>
            </label>
        </div>
    )
}

export default Toggle;


사용자가 토글을 클릭하면 페이지의 테마가 변경되고 토글이 함께 변경되기를 원합니다. 로컬 상태에서 가져온 setTheme() 함수와 setTogClass()를 handleOnClick 함수에 추가했습니다. 위의 JSX에서 토글의 클릭 가능한 부분으로 전달되는 위치를 볼 수 있습니다.

const handleOnClick = () => {
  if (localStorage.getItem('theme') === 'theme-dark') {
      setTheme('theme-light');
      setTogClass('light')
  } else {
      setTheme('theme-dark');
      setTogClass('dark')
  }
}


이 구성 요소useEffect()를 사용하여 로컬 상태 togClass가 항상 올바른 테마로 로드되는지 확인했습니다.

useEffect(() => {
    if (localStorage.getItem('theme') === 'theme-dark') {
        setTogClass('dark')
    } else if (localStorage.getItem('theme') === 'theme-light') {
        setTogClass('light')
    }
}, [theme])


내 토글이 확인란이기 때문에 어두운 테마는 선택되지 않은(달) 상태를 표시하고 밝은 테마는 선택(태양) 상태를 표시해야 합니다. defaultChecked 이 원하는 대로 작동하지 않아 체크되지 않은 <input>를 다음 조건부 렌더링(조건부 연산자)으로 대체했습니다.

{
    togClass === "light" ?
    <input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} checked />
    :
    <input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} />
}

<button> 를 사용한 경우 이와 같은 조건부 렌더링을 사용하여 <button> 태그 내에서 className 속성을 변경하고 동일한 효과를 얻을 수 있습니다.

토글 구성 요소의 코드는 다음과 같습니다.

import React, { useEffect, useState } from 'react';
import '../styles/toggle.css';
import { setTheme } from '../utils/themes';

function Toggle() {
    const [togClass, setTogClass] = useState('dark');
    let theme = localStorage.getItem('theme');

    const handleOnClick = () => {
        if (localStorage.getItem('theme') === 'theme-dark') {
            setTheme('theme-light');
            setTogClass('light')
        } else {
            setTheme('theme-dark');
            setTogClass('dark')
        }
    }

    useEffect(() => {
        if (localStorage.getItem('theme') === 'theme-dark') {
            setTogClass('dark')
        } else if (localStorage.getItem('theme') === 'theme-light') {
            setTogClass('light')
        }
    }, [theme])

    return (
        <div className="container--toggle">
            {
                togClass === "light" ?
                <input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} checked />
                :
                <input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} />
            }
            <label htmlFor="toggle" className="toggle--label">
                <span className="toggle--label-background"></span>
            </label>
        </div>
    )
}


업데이트



이 구성 요소의 논리를 리팩토링하고 액세스 가능하게 만든 방법을 보려면 .

마지막으로 제가 가장 좋아하는 부분은 SVG 색상 전환입니다! CSS 변수는 SVG 코드에서도 작동합니다!

DEVICON 에서 Github 및 Chrome 아이콘에 대한 SVG 코드를 받았습니다. Github 아이콘의 경우 <g> 에서 하나의 채우기 속성만 변경해야 했습니다.

<g fill="var(--dark-text)">


Chrome 아이콘에는 <circle><path>에 채우기 속성이 있습니다.

<circle fill="var(--dark-text)" cx="63.624" cy="64.474" r="22.634"></circle><path fill="var(--dark-text)" ...>


결과는 다음과 같습니다.



결론



관련 코드를 모두 포함하려고 했지만 Github repository 에서 내 사이트의 전체 코드를 볼 수도 있습니다. 이 글이 마음에 드셨거나 질문이 있으시면 댓글을 남겨주세요! 또한 이 튜토리얼에 따라 구축된 모든 것을 보고 싶습니다.

좋은 웹페이지 즐겨찾기