From 395571bd89498fce46d37f3853cf718387fd0d9a Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Mon, 27 Dec 2021 11:20:26 +0100 Subject: chore: add a theme toggle Dark theme is not implemented yet. --- src/assets/images/icon-moon.svg | 36 ++++++++++ src/assets/images/icon-sun.svg | 36 ++++++++++ src/assets/images/icon-theme.svg | 79 ++++++++++++++++++++++ src/components/Buttons/ButtonTheme/ButtonTheme.tsx | 40 +++++++++++ src/components/Buttons/Buttons.module.scss | 7 +- src/components/Buttons/index.tsx | 3 +- src/components/Form/Form.module.scss | 4 ++ src/components/Icons/Moon/Moon.module.scss | 8 +++ src/components/Icons/Moon/Moon.tsx | 15 ++++ src/components/Icons/Sun/Sun.module.scss | 8 +++ src/components/Icons/Sun/Sun.tsx | 15 ++++ src/components/Icons/Theme/Theme.module.scss | 43 ++++++++++++ src/components/Icons/Theme/Theme.tsx | 54 +++++++++++++++ src/components/Icons/index.tsx | 6 ++ src/components/ThemeToggle/ThemeToggle.module.scss | 50 ++++++++++++++ src/components/ThemeToggle/ThemeToggle.tsx | 34 ++++++++++ src/components/Toolbar/Toolbar.module.scss | 6 +- src/components/Toolbar/Toolbar.tsx | 22 +++++- 18 files changed, 457 insertions(+), 9 deletions(-) create mode 100644 src/assets/images/icon-moon.svg create mode 100644 src/assets/images/icon-sun.svg create mode 100644 src/assets/images/icon-theme.svg create mode 100644 src/components/Buttons/ButtonTheme/ButtonTheme.tsx create mode 100644 src/components/Icons/Moon/Moon.module.scss create mode 100644 src/components/Icons/Moon/Moon.tsx create mode 100644 src/components/Icons/Sun/Sun.module.scss create mode 100644 src/components/Icons/Sun/Sun.tsx create mode 100644 src/components/Icons/Theme/Theme.module.scss create mode 100644 src/components/Icons/Theme/Theme.tsx create mode 100644 src/components/ThemeToggle/ThemeToggle.module.scss create mode 100644 src/components/ThemeToggle/ThemeToggle.tsx (limited to 'src') diff --git a/src/assets/images/icon-moon.svg b/src/assets/images/icon-moon.svg new file mode 100644 index 0000000..b84c531 --- /dev/null +++ b/src/assets/images/icon-moon.svg @@ -0,0 +1,36 @@ + + + + + + diff --git a/src/assets/images/icon-sun.svg b/src/assets/images/icon-sun.svg new file mode 100644 index 0000000..0e570c3 --- /dev/null +++ b/src/assets/images/icon-sun.svg @@ -0,0 +1,36 @@ + + + + + + diff --git a/src/assets/images/icon-theme.svg b/src/assets/images/icon-theme.svg new file mode 100644 index 0000000..aa2510e --- /dev/null +++ b/src/assets/images/icon-theme.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + diff --git a/src/components/Buttons/ButtonTheme/ButtonTheme.tsx b/src/components/Buttons/ButtonTheme/ButtonTheme.tsx new file mode 100644 index 0000000..afbac89 --- /dev/null +++ b/src/components/Buttons/ButtonTheme/ButtonTheme.tsx @@ -0,0 +1,40 @@ +import { CloseIcon, ThemeIcon } from '@components/Icons'; +import { t } from '@lingui/macro'; +import { SetStateAction } from 'react'; +import styles from '../Buttons.module.scss'; + +const ButtonTheme = ({ + isActivated, + setIsActivated, +}: { + isActivated: boolean; + setIsActivated: (value: SetStateAction) => void; +}) => { + const btnClasses = isActivated + ? `${styles.theme} ${styles['theme--opened']}` + : styles.theme; + + return ( + + ); +}; + +export default ButtonTheme; diff --git a/src/components/Buttons/Buttons.module.scss b/src/components/Buttons/Buttons.module.scss index fb93d5a..9f046d2 100644 --- a/src/components/Buttons/Buttons.module.scss +++ b/src/components/Buttons/Buttons.module.scss @@ -2,7 +2,6 @@ .btn { display: block; - margin: auto; border: none; font-size: var(--font-size-md); } @@ -81,7 +80,8 @@ } } -.search { +.search, +.theme { display: block; width: var(--btn-size); height: var(--btn-size); @@ -125,7 +125,8 @@ z-index: 10; } -.search--opened { +.search--opened, +.theme--opened { .icon { transform: rotateY(180deg); } diff --git a/src/components/Buttons/index.tsx b/src/components/Buttons/index.tsx index aa1291e..53d4e91 100644 --- a/src/components/Buttons/index.tsx +++ b/src/components/Buttons/index.tsx @@ -1,5 +1,6 @@ import Button from './Button/Button'; import ButtonSearch from './ButtonSearch/ButtonSearch'; import ButtonSubmit from './ButtonSubmit/ButtonSubmit'; +import ButtonTheme from './ButtonTheme/ButtonTheme'; -export { Button, ButtonSearch, ButtonSubmit }; +export { Button, ButtonSearch, ButtonSubmit, ButtonTheme }; diff --git a/src/components/Form/Form.module.scss b/src/components/Form/Form.module.scss index 891db19..25fce19 100644 --- a/src/components/Form/Form.module.scss +++ b/src/components/Form/Form.module.scss @@ -7,6 +7,10 @@ display: flex; flex-flow: row nowrap; } + + &--theme { + line-height: 1; + } } .item { diff --git a/src/components/Icons/Moon/Moon.module.scss b/src/components/Icons/Moon/Moon.module.scss new file mode 100644 index 0000000..5912973 --- /dev/null +++ b/src/components/Icons/Moon/Moon.module.scss @@ -0,0 +1,8 @@ +@use "@styles/abstracts/functions" as fun; + +.moon { + fill: var(--color-primary-lighter); + stroke: var(--color-border); + stroke-width: 4; + width: var(--icon-size, #{fun.convert-px(25)}); +} diff --git a/src/components/Icons/Moon/Moon.tsx b/src/components/Icons/Moon/Moon.tsx new file mode 100644 index 0000000..b2e5f7b --- /dev/null +++ b/src/components/Icons/Moon/Moon.tsx @@ -0,0 +1,15 @@ +import styles from './Moon.module.scss'; + +const MoonIcon = () => { + return ( + + + + ); +}; + +export default MoonIcon; diff --git a/src/components/Icons/Sun/Sun.module.scss b/src/components/Icons/Sun/Sun.module.scss new file mode 100644 index 0000000..da2de89 --- /dev/null +++ b/src/components/Icons/Sun/Sun.module.scss @@ -0,0 +1,8 @@ +@use "@styles/abstracts/functions" as fun; + +.sun { + fill: var(--color-primary-lighter); + stroke: var(--color-border); + stroke-width: 4; + width: var(--icon-size, #{fun.convert-px(25)}); +} diff --git a/src/components/Icons/Sun/Sun.tsx b/src/components/Icons/Sun/Sun.tsx new file mode 100644 index 0000000..a4f5a11 --- /dev/null +++ b/src/components/Icons/Sun/Sun.tsx @@ -0,0 +1,15 @@ +import styles from './Sun.module.scss'; + +const SunIcon = () => { + return ( + + + + ); +}; + +export default SunIcon; diff --git a/src/components/Icons/Theme/Theme.module.scss b/src/components/Icons/Theme/Theme.module.scss new file mode 100644 index 0000000..e0ea24c --- /dev/null +++ b/src/components/Icons/Theme/Theme.module.scss @@ -0,0 +1,43 @@ +@use "@styles/abstracts/functions" as fun; + +.icon { + display: block; + margin: auto; + width: var(--icon-size, #{fun.convert-px(40)}); +} + +.roller { + fill: var(--color-primary-lighter); + stroke: var(--color-border); + stroke-width: 3; +} + +.guard { + fill: var(--color-primary); + stroke: var(--color-border); + stroke-width: 2; +} + +.handle { + fill: var(--color-primary-lighter); + stroke: var(--color-border); + stroke-width: 3; +} + +.bar { + fill: var(--color-bg); + stroke: var(--color-border); + stroke-width: 3; +} + +.colors { + fill: var(--color-primary-lighter); + stroke: var(--color-border); + stroke-width: 2; +} + +.palette { + fill: var(--color-bg); + stroke: var(--color-border); + stroke-width: 3; +} diff --git a/src/components/Icons/Theme/Theme.tsx b/src/components/Icons/Theme/Theme.tsx new file mode 100644 index 0000000..6b6c8d0 --- /dev/null +++ b/src/components/Icons/Theme/Theme.tsx @@ -0,0 +1,54 @@ +import styles from './Theme.module.scss'; + +const ThemeIcon = () => { + return ( + + + + + + + + + + + + + ); +}; + +export default ThemeIcon; diff --git a/src/components/Icons/index.tsx b/src/components/Icons/index.tsx index b32c3d3..9144619 100644 --- a/src/components/Icons/index.tsx +++ b/src/components/Icons/index.tsx @@ -4,7 +4,10 @@ import ContactIcon from './Contact/Contact'; import CVIcon from './CV/CV'; import HamburgerIcon from './Hamburger/Hamburger'; import HomeIcon from './Home/Home'; +import MoonIcon from './Moon/Moon'; import SearchIcon from './Search/Search'; +import SunIcon from './Sun/Sun'; +import ThemeIcon from './Theme/Theme'; export { BlogIcon, @@ -13,5 +16,8 @@ export { CVIcon, HamburgerIcon, HomeIcon, + MoonIcon, SearchIcon, + SunIcon, + ThemeIcon, }; diff --git a/src/components/ThemeToggle/ThemeToggle.module.scss b/src/components/ThemeToggle/ThemeToggle.module.scss new file mode 100644 index 0000000..c780b96 --- /dev/null +++ b/src/components/ThemeToggle/ThemeToggle.module.scss @@ -0,0 +1,50 @@ +@use "@styles/abstracts/functions" as fun; + +.label { + --icon-size: #{fun.convert-px(25)}; + --toggle-width: #{fun.convert-px(45)}; + --toggle-height: calc(var(--toggle-width) / 2); + + display: inline-flex; + align-items: center; +} + +.toggle { + display: inline-flex; + align-items: center; + width: var(--toggle-width); + height: var(--toggle-height); + background: var(--color-shadow-lighter); + border: fun.convert-px(1) solid var(--color-primary); + border-radius: fun.convert-px(32); + box-shadow: inset 0 0 2px 0 var(--color-shadow); + margin: 0 var(--spacing-2xs); + position: relative; + + &::after { + content: ""; + display: block; + width: calc(var(--toggle-width) / 2); + height: calc(var(--toggle-width) / 2); + background: var(--color-primary-lighter); + border: fun.convert-px(1) solid var(--color-primary); + border-radius: 50%; + box-shadow: inset 0 0 fun.convert-px(1) fun.convert-px(1) + var(--color-shadow-light), + 0 0 fun.convert-px(2) fun.convert-px(1) var(--color-shadow-lighter); + position: absolute; + left: fun.convert-px(-2); + transition: all 0.3s ease-in-out 0s; + } +} + +.checkbox { + position: absolute; + opacity: 0; + + &:checked ~ .label { + .toggle::after { + left: calc(100% - (var(--toggle-width) / 2) + #{fun.convert-px(2)}); + } + } +} diff --git a/src/components/ThemeToggle/ThemeToggle.tsx b/src/components/ThemeToggle/ThemeToggle.tsx new file mode 100644 index 0000000..015201b --- /dev/null +++ b/src/components/ThemeToggle/ThemeToggle.tsx @@ -0,0 +1,34 @@ +import { Form } from '@components/Form'; +import { MoonIcon, SunIcon } from '@components/Icons'; +import { t } from '@lingui/macro'; +import { FormEvent, useState } from 'react'; +import styles from './ThemeToggle.module.scss'; + +const ThemeToggle = () => { + const [isDarkTheme, setIsDarkTheme] = useState(false); + + const onSubmit = (e: FormEvent) => { + e.preventDefault(); + }; + + return ( +
+ setIsDarkTheme(!isDarkTheme)} + /> + +
+ ); +}; + +export default ThemeToggle; diff --git a/src/components/Toolbar/Toolbar.module.scss b/src/components/Toolbar/Toolbar.module.scss index 527a342..1814b99 100644 --- a/src/components/Toolbar/Toolbar.module.scss +++ b/src/components/Toolbar/Toolbar.module.scss @@ -28,7 +28,7 @@ --toolbar-size: auto; justify-content: flex-end; - gap: var(--spacing-md); + gap: var(--spacing-sm); width: auto; background: inherit; box-shadow: none; @@ -66,12 +66,12 @@ width: fun.convert-px(500); left: unset; right: unset; - top: 150%; + top: 120%; bottom: unset; background: var(--color-bg-opacity); box-shadow: fun.convert-px(2) fun.convert-px(2) fun.convert-px(3) fun.convert-px(1) var(--color-shadow); - transform-origin: 100% -200%; + transform-origin: 50% -200%; transition: all 0.8s ease-in-out 0s; &--closed { diff --git a/src/components/Toolbar/Toolbar.tsx b/src/components/Toolbar/Toolbar.tsx index fdab76a..615dfd5 100644 --- a/src/components/Toolbar/Toolbar.tsx +++ b/src/components/Toolbar/Toolbar.tsx @@ -1,21 +1,36 @@ import { ButtonSearch } from '@components/Buttons'; import MainNav from '@components/MainNav/MainNav'; import SearchForm from '@components/SearchForm/SearchForm'; +import ThemeToggle from '@components/ThemeToggle/ThemeToggle'; import { useEffect, useState } from 'react'; import styles from './Toolbar.module.scss'; const Toolbar = () => { const [isNavOpened, setIsNavOpened] = useState(false); const [isSearchOpened, setIsSearchOpened] = useState(false); + const [isThemeOpened, setIsThemeOpened] = useState(false); useEffect(() => { - if (isNavOpened) setIsSearchOpened(false); + if (isNavOpened) { + setIsSearchOpened(false); + setIsThemeOpened(false); + } }, [isNavOpened]); useEffect(() => { - if (isSearchOpened) setIsNavOpened(false); + if (isSearchOpened) { + setIsNavOpened(false); + setIsThemeOpened(false); + } }, [isSearchOpened]); + useEffect(() => { + if (isThemeOpened) { + setIsNavOpened(false); + setIsSearchOpened(false); + } + }, [isThemeOpened]); + const searchClasses = `${styles.search} ${ isSearchOpened ? styles['search--opened'] : styles['search--closed'] }`; @@ -30,6 +45,9 @@ const Toolbar = () => {
+
+ +
); }; -- cgit v1.2.3