<div id="root">
</div>
:root {
--text-color: #333;
--bg-color: #fff;
--header-bg-color: #eee;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #333;
--text-color: #fff;
--header-bg-color: #222;
}
}
body.dark-theme {
--bg-color: #333;
--text-color: #ddd;
--header-bg-color: #222;
}
body.light-theme {
--bg-color: #fff;
--text-color: #333;
--header-bg-color: #eee;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: .5s;
margin: 0;
}
.header {
background-color: var(--header-bg-color);
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1em;
padding: 1em;
}
.container {
height: 1000px;
padding: 1em;
}
const {
useState,
useRef,
useCallback,
useEffect,
useLayoutEffect,
forwardRef
} = React;
const useDarkModeButton = () => {
const [isDarkMode, setIsDarkMode] = useState(false)
const checkboxElement = useRef(null)
const handleChange = useCallback(e => {
const btn = e.target
const body = document.body
if(btn.checked === true) {
body.classList.remove("light-theme")
body.classList.add("dark-theme")
Cookies.set('darkMode', 'on')
//setIsDarkMode(true)
}else{
body.classList.remove("dark-theme")
body.classList.add("light-theme")
Cookies.remove('darkMode')
//setIsDarkMode(false)
}
})
useLayoutEffect(() => {
const darkModeCookie = Cookies.get('darkMode')
const body = document.body
const checkbox = checkboxElement.current
if(darkModeCookie){
//body.classList.remove('light-theme');
body.classList.add('dark-theme');
checkbox.checked = true
//setIsDarkMode(true)
}else{
//body.classList.remove('dark-theme');
//body.classList.add('light-theme');
//checkbox.checked = false
//setIsDarkMode(false)
}
}, [])
useEffect(() => {
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const darkModeOn = darkModeMediaQuery.matches
const body = document.body
const checkbox = checkboxElement.current
darkModeMediaQuery.addListener((e) => {
const darkModeOn = e.matches;
if (darkModeOn) {
body.classList.remove('light-theme');
//body.classList.add('dark-theme');
Cookies.set('darkMode', 'on')
checkbox.checked = true
//setIsDarkMode(true)
} else {
body.classList.remove('dark-theme');
//body.classList.add('light-theme');
Cookies.remove('darkMode')
checkbox.checked = false
//setIsDarkMode(false)
}
});
}, [])
return [
isDarkMode,
checkboxElement,
handleChange
]
}
const StyledIcon = styled.span`
color: ${({ color }) => color};
font-size: ${({ size }) => size};
`
const Icon = ({ name, color, size }) => (
<StyledIcon className="icon" color={color} size={size}>
<i class={`fas fa-${name}`}></i>
</StyledIcon>
)
const StyledToggleSwitchButton = styled.div`
& input {
display: none;
&:checked + label {
background-color: ${({onColor}) => onColor};
&::before {
//order: 2;
left: 2em;
}
}
}
& label {
background-color: ${({offColor}) => offColor};
border-radius: 2em;
//border: 2px solid var(--text-color);
cursor: pointer;
//display: block;
display: flex;
align-items: center;
font-size: ${({ size }) => size};2.4em;
justify-content: space-around;
height: 2em;
position: relative;
transition: .2s;// 本体の色
width: 3.75em;
&::before {
background-color: #fff;
border-radius: 100%;
content: '';
display: inline-block;
height: 1.5em;
position: absolute;
left: 0.25em;
transition: .2s ease-out;
width: 1.5em;
z-index: 2;
}
&::after {
background-color: red;
border-radius: 100%;
content: '';
display: inline-block;
height: 1.5em;
position: absolute;
right: .25em;
visibility: hidden;
width: 1.5em;
z-index: 2;
}
& .icons {
border-radius: 2em;
display: flex;
align-items: center;
justify-content: space-around;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1;
}
}
`
const ToggleSwitchButton = forwardRef(({ className, handleChange, offColor, onColor, size }, ref) => (
<StyledToggleSwitchButton
className={className}
offColor={offColor}
onColor={onColor}
size={size}
>
<input id="btn-mode" type="checkbox" onChange={handleChange} ref={ref} />
<label htmlFor="btn-mode">
</label>
</StyledToggleSwitchButton>
))
const DarkModeSwitchButton = forwardRef(({ className, handleChange }, ref) => (
<StyledDarkModeSwitchButton className={className}>
<input id="btn-mode" type="checkbox" onChange={handleChange} ref={ref} />
<label htmlFor="btn-mode">
<div className="icons">
<Icon className="icon icon-light" name="sun" />
<Icon className="icon icon-dark" name="moon" />
</div>
</label>
</StyledDarkModeSwitchButton>
))
const App = () => {
const [
isDarkMode,
checkboxElement,
handleChangeDarkMode
] = useDarkModeButton()
return (
<div>
<header className="header">
<div>LOGO</div>
<ToggleSwitchButton
className="toggle-switch-button"
handleChange={handleChangeDarkMode}
ref={checkboxElement}
offColor="#ccc"
onColor="#383896"
size="1em"
/>
</header>
<div className="container">
<div>example</div>
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
View Compiled
This Pen doesn't use any external CSS resources.