aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Buttons/ButtonTheme/ButtonTheme.tsx40
-rw-r--r--src/components/Buttons/Buttons.module.scss7
-rw-r--r--src/components/Buttons/index.tsx3
-rw-r--r--src/components/Form/Form.module.scss4
-rw-r--r--src/components/Icons/Moon/Moon.module.scss8
-rw-r--r--src/components/Icons/Moon/Moon.tsx15
-rw-r--r--src/components/Icons/Sun/Sun.module.scss8
-rw-r--r--src/components/Icons/Sun/Sun.tsx15
-rw-r--r--src/components/Icons/Theme/Theme.module.scss43
-rw-r--r--src/components/Icons/Theme/Theme.tsx54
-rw-r--r--src/components/Icons/index.tsx6
-rw-r--r--src/components/ThemeToggle/ThemeToggle.module.scss50
-rw-r--r--src/components/ThemeToggle/ThemeToggle.tsx34
-rw-r--r--src/components/Toolbar/Toolbar.module.scss6
-rw-r--r--src/components/Toolbar/Toolbar.tsx22
15 files changed, 306 insertions, 9 deletions
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<boolean>) => void;
+}) => {
+ const btnClasses = isActivated
+ ? `${styles.theme} ${styles['theme--opened']}`
+ : styles.theme;
+
+ return (
+ <button
+ className={btnClasses}
+ type="button"
+ onClick={() => setIsActivated(!isActivated)}
+ >
+ <span className={styles.icon}>
+ <span className={styles.front}>
+ <ThemeIcon />
+ </span>
+ <span className={styles.back}>
+ <CloseIcon />
+ </span>
+ </span>
+ {isActivated ? (
+ <span className="screen-reader-text">{t`Close theme options`}</span>
+ ) : (
+ <span className="screen-reader-text">{t`Open theme options`}</span>
+ )}
+ </button>
+ );
+};
+
+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 (
+ <svg
+ className={styles.moon}
+ viewBox="0 0 100 100"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path d="M 51.077315,1.9893942 A 43.319985,43.319985 0 0 1 72.840039,39.563145 43.319985,43.319985 0 0 1 29.520053,82.88313 43.319985,43.319985 0 0 1 5.4309911,75.569042 48.132997,48.132997 0 0 0 46.126047,98 48.132997,48.132997 0 0 0 94.260004,49.867002 48.132997,48.132997 0 0 0 51.077315,1.9893942 Z" />
+ </svg>
+ );
+};
+
+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 (
+ <svg
+ className={styles.sun}
+ viewBox="0 0 100 100"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path d="M 69.398043,50.000437 A 19.399259,19.399204 0 0 1 49.998784,69.399641 19.399259,19.399204 0 0 1 30.599525,50.000437 19.399259,19.399204 0 0 1 49.998784,30.601234 19.399259,19.399204 0 0 1 69.398043,50.000437 Z m 27.699233,1.125154 c 2.657696,0.0679 1.156196,12.061455 -1.435545,11.463959 L 80.113224,59.000697 c -2.589801,-0.597494 -1.625657,-8.345536 1.032041,-8.278609 z m -18.06653,37.251321 c 1.644087,2.091234 -9.030355,8.610337 -10.126414,6.188346 L 62.331863,80.024585 c -1.096058,-2.423931 5.197062,-6.285342 6.839209,-4.194107 z M 38.611418,97.594444 C 38.02653,100.18909 26.24148,95.916413 27.436475,93.54001 l 7.168026,-14.256474 c 1.194024,-2.376403 8.102101,0.151313 7.517214,2.744986 z M 6.1661563,71.834242 C 3.7916868,73.028262 -0.25499873,61.16274 2.3386824,60.577853 L 17.905618,57.067567 c 2.593681,-0.584886 4.894434,6.403678 2.518995,7.598668 z M 6.146757,30.055146 c -2.3764094,-1.194991 4.46571,-11.714209 6.479353,-9.97798 l 12.090589,10.414462 c 2.014613,1.736229 -1.937017,7.926514 -4.314396,6.731524 z M 38.56777,4.2639045 C 37.982883,1.6682911 50.480855,0.41801247 50.415868,3.0766733 L 50.020123,19.028638 c -0.06596,2.657691 -7.357169,3.394862 -7.943027,0.800218 z m 40.403808,9.1622435 c 1.635357,-2.098023 10.437771,6.872168 8.339742,8.506552 l -12.58818,9.805327 c -2.099,1.634383 -7.192276,-3.626682 -5.557888,-5.724706 z M 97.096306,50.69105 c 2.657696,-0.06596 1.164926,12.462047 -1.425846,11.863582 L 80.122924,58.96578 c -2.590771,-0.597496 -1.636327,-7.814 1.021371,-7.879957 z" />
+ </svg>
+ );
+};
+
+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 (
+ <svg
+ className={styles.icon}
+ viewBox="0 0 100 100"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ className={styles.palette}
+ d="m 36.966438,97.25893 c -19.863716,0 -35.96643811,-16.10281 -35.96643811,-35.966628 0,-19.86382 16.13555711,-37.108288 35.96643811,-35.966626 20.456993,1.177707 -4.177725,18.358363 2.330923,24.867041 6.508645,6.50868 33.138234,-14.015906 33.635518,16.06554 0,14.897863 -16.102724,31.000673 -35.966441,31.000673 z"
+ />
+ <path
+ className={`${styles.colors} ${styles.color1}`}
+ d="m 34.363531,35.947138 a 5.5648608,5.5649128 0 0 1 -5.564861,5.564912 5.5648608,5.5649128 0 0 1 -5.564861,-5.564912 5.5648608,5.5649128 0 0 1 5.564861,-5.564913 5.5648608,5.5649128 0 0 1 5.564861,5.564913 z"
+ />
+ <path
+ className={`${styles.colors} ${styles.color2}`}
+ d="m 20.774887,52.836279 a 5.5648608,5.5649128 0 0 1 -5.564861,5.564912 5.5648608,5.5649128 0 0 1 -5.5648609,-5.564912 5.5648608,5.5649128 0 0 1 5.5648609,-5.564913 5.5648608,5.5649128 0 0 1 5.564861,5.564913 z"
+ />
+ <path
+ className={`${styles.colors} ${styles.color3}`}
+ d="m 25.169864,73.456953 a 5.5648608,5.5649128 0 0 1 -5.564861,5.564913 5.5648608,5.5649128 0 0 1 -5.56486,-5.564913 5.5648608,5.5649128 0 0 1 5.56486,-5.564913 5.5648608,5.5649128 0 0 1 5.564861,5.564913 z"
+ />
+ <path
+ className={`${styles.colors} ${styles.color4}`}
+ d="m 45.37022,82.982572 a 5.5648608,5.5649128 0 0 1 -5.564861,5.564913 5.5648608,5.5649128 0 0 1 -5.564861,-5.564913 5.5648608,5.5649128 0 0 1 5.564861,-5.564913 5.5648608,5.5649128 0 0 1 5.564861,5.564913 z"
+ />
+ <path
+ className={`${styles.colors} ${styles.color5}`}
+ d="m 65.337493,72.325422 a 5.5648608,5.5649128 0 0 1 -5.56486,5.564913 5.5648608,5.5649128 0 0 1 -5.564861,-5.564913 5.5648608,5.5649128 0 0 1 5.564861,-5.564913 5.5648608,5.5649128 0 0 1 5.56486,5.564913 z"
+ />
+ <path
+ className={styles.bar}
+ d="m 19.832251,25.296368 1.538355,5.741247 67.390349,-18.057216 3.844973,14.349636 -28.622498,7.669394 c -6.183313,1.656814 -5.744267,1.516427 -4.200251,7.278868 l 3.80049,14.183562 5.738576,-1.537647 -3.80047,-14.183547 28.622508,-7.669383 c 5.765552,-1.544878 5.744722,-1.516352 4.200664,-7.278988 L 94.499944,11.442637 C 92.953635,5.6716712 92.982563,5.6957688 87.2226,7.2391505 Z"
+ />
+ <path
+ className={styles.handle}
+ d="m 73.052595,56.920912 10.091431,37.661729 c 0.127261,0.47495 -0.133494,0.95463 -0.584655,1.07552 l -9.80712,2.627809 c -0.45116,0.12089 -0.916823,-0.164149 -1.044084,-0.639099 L 61.616737,59.985142 c -0.127263,-0.47495 0.133494,-0.954632 0.584654,-1.075519 l 9.80712,-2.62781 c 0.451161,-0.120889 0.916822,0.16415 1.044084,0.639099 z"
+ />
+ <path
+ className={styles.guard}
+ d="m 75.589139,56.451775 -16.995133,4.553832 c -0.906064,0.242779 -1.831036,-0.29154 -2.073928,-1.198024 -0.242892,-0.906485 0.290998,-1.831704 1.197062,-2.074484 l 16.995133,-4.553832 c 0.906064,-0.242779 1.831036,0.29154 2.073928,1.198025 0.242892,0.906484 -0.290998,1.831703 -1.197062,2.074483 z"
+ />
+ <path
+ className={styles.roller}
+ d="M 25.017665,15.867638 77.477694,1.8110162 c 1.94242,-0.5204698 3.925526,0.625572 4.446426,2.5695996 L 85.458379,17.57065 c 0.520901,1.944028 -0.623498,3.928082 -2.565918,4.448552 L 30.432432,36.075824 c -1.94242,0.52047 -3.925526,-0.625572 -4.446426,-2.5696 L 22.451747,20.31619 c -0.520901,-1.944027 0.623498,-3.928082 2.565918,-4.448552 z"
+ />
+ </svg>
+ );
+};
+
+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<boolean>(false);
+
+ const onSubmit = (e: FormEvent) => {
+ e.preventDefault();
+ };
+
+ return (
+ <Form modifier="theme" submitHandler={onSubmit}>
+ <input
+ className={styles.checkbox}
+ type="checkbox"
+ id="dark-theme"
+ name="dark-theme"
+ checked={isDarkTheme}
+ onChange={() => setIsDarkTheme(!isDarkTheme)}
+ />
+ <label htmlFor="dark-theme" className={styles.label}>
+ <span className="screen-reader-text">{t`Activate dark theme`}</span>
+ <SunIcon />
+ <span className={styles.toggle}></span>
+ <MoonIcon />
+ </label>
+ </Form>
+ );
+};
+
+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<boolean>(false);
const [isSearchOpened, setIsSearchOpened] = useState<boolean>(false);
+ const [isThemeOpened, setIsThemeOpened] = useState<boolean>(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 = () => {
<div className={searchClasses}>
<SearchForm isOpened={isSearchOpened} />
</div>
+ <div>
+ <ThemeToggle />
+ </div>
</div>
);
};