// header.js
import ToggleSwitch from './ToggleSwitch';
// ...
<ToggleSwitch
toggleTheme={toggleTheme}
isChecked={isChecked}
id="mode"
ariaLabel="dark mode toggle"
/>;
A toggle is just a checkbox with some CSS magic applied to it. 🧙♂️
The jsx for the component consists of a div (Toggle), an input with type="checkbox", and a span (Slider). On the checkbox input, toggleTheme is assigned to onChange and isChecked is assigned to checked.
import React from 'react';
import styled from '@emotion/styled';
export default function ToggleSwitch({
toggleTheme,
isChecked,
ariaLabel,
id,
}) {
return (
<Toggle>
<Input
type="checkbox"
onChange={toggleTheme}
checked={isChecked}
id={id}
aria-label={ariaLabel}
/>
<Slider />
</Toggle>
);
}
As you can see I'm using CSS-in-JS 🙀 via the @emotion/styled library. If you are familiar with styled-components, it works almost exactly the same but the package is slightly smaller and apparently more performant (I haven't actually tested it, so what do I know?). Emotion also gives you the option of using css props to style components, which can be useful in certain situations.
To use styled-components, you simple rename your HTML tags to whatever makes sense semantically, and then define which native HTML elements your new tags should use with the CSS for each element inside back-ticks. The API supports nesting similar to SCSS, and the styles are SCOPED TO THE COMPONENT!
Personally, I love how semantic and simple this makes my JSX markup. No more jamming 14 different classnames onto every element ala Bootstrap or writing disgusting classnames like "header__toggle-switch--dark-mode" ala BEM.
const Toggle = styled.div`
position: relative;
&:after {
content: '☀️';
font-size: 18px;
position: absolute;
top: 7px;
left: 37px;
}
&:before {
content: '🌙';
font-size: 18px;
position: absolute;
top: 7px;
left: 6px;
z-index: 1;
}
`;
const Input = styled.input`
position: absolute;
left: 0;
top: 0;
z-index: 5;
opacity: 0;
cursor: pointer;
&:hover + span:after {
box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.2);
}
&:checked + span {
background: #4a5b90;
&:after {
transform: translate3d(32px, 0, 0);
}
}
`;
const Slider = styled.span`
position: relative;
display: block;
height: 32px;
width: 64px;
border-radius: 32px;
transition: 0.25s ease-in-out;
background: #3a9df8;
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.15);
&:after {
content: '';
position: absolute;
border-radius: 100%;
top: 0;
left: 0;
z-index: 2;
background: #fff;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
transition: 0.25s ease-in-out;
width: 32px;
height: 32px;
}
`;
The CSS variable can then be assigned to any element and the colors will change upon clicking the toggle switch.
The variables can be assigned globally...
/* layout.css */
body {
margin: 0;
line-height: 1;
background: #f0f0f0;
background: var(--color-bg);
color: #994cc3;
color: var(--color-text);
transition: background 0.8s, color 0.8s;
}
and inside components using CSS-in-JS.
// header.js
const MenuButton = styled.button`
background: none;
border: none;
padding: 0;
margin: 0;
.hamburger {
fill: #994cc3;
fill: var(--color-text);
transition: fill 0.5s;
height: 40px;
}
@media screen and (min-width: ${bpMed}) {
display: none;
}
`;
Thanks for reading! I hope you found this informative.
Next up, I will be documenting how I created the flickering Neon SVG animation.