feat: added keyboard accessibility

This commit is contained in:
Blaster4385 2024-05-15 22:55:13 +05:30 committed by Blaster4385
parent 189ef9ef81
commit 731453588b
17 changed files with 598 additions and 152 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,24 @@
import React, { useEffect } from "react";
import styles from "./AlertToast.module.css";
const AlertToast = ({ openAlertToast, setOpenAlertToast }) => {
useEffect(() => {
const timer = setTimeout(() => {
setOpenAlertToast(false);
}, 3000);
return () => clearTimeout(timer);
}, [openAlertToast, setOpenAlertToast]);
return (
<div
className={`${styles.background} ${openAlertToast ? styles.active : ""}`}
>
<div className={styles.container}>
<p className={styles.container__title}>Please enter some text!</p>
</div>
</div>
);
};
export default AlertToast;

View file

@ -0,0 +1,29 @@
.background {
position: fixed;
bottom: 40px;
left: 40px;
z-index: 1000;
transition: opacity 0.3s ease;
opacity: 0;
}
.background.active {
opacity: 1;
}
.active {
display: flex;
align-items: center;
justify-content: center;
}
.container {
background-color: var(--color-light);
color: var(--color-dark);
padding: 10px 20px;
border-radius: 5px;
}
.container__title {
margin: 0;
}

View file

@ -1,35 +1,110 @@
import React, { useEffect, useState, useRef } from "react";
import { SUPPORTED_LANGUAGES } from "../../utils/constants";
import styles from "./CustomSelect.module.css";
const CustomSelect = ({ options, onSelect }) => {
const CustomSelect = ({ onSelect, textAreaRef, isModalOpen }) => {
const [isOpen, setIsOpen] = useState(false);
const [options, setOptions] = useState(SUPPORTED_LANGUAGES);
const [selectedOption, setSelectedOption] = useState(
options.length > 0 ? options[0] : null,
);
const [focusedOptionIndex, setFocusedOptionIndex] = useState(0);
const selectRef = useRef(null);
const searchRef = useRef(null);
const optionsRef = useRef([]);
const toggleDropdown = () => {
setIsOpen(!isOpen);
};
const handleOptionClick = (option) => {
const handleOptionClick = (option, index) => {
setSelectedOption(option);
onSelect(option.value);
setFocusedOptionIndex(index);
setIsOpen(false);
setOptions(SUPPORTED_LANGUAGES);
if (textAreaRef.current) {
textAreaRef.current.focus();
}
};
const handleClickOutside = (event) => {
if (selectRef.current && !selectRef.current.contains(event.target)) {
setIsOpen(false);
setOptions(SUPPORTED_LANGUAGES);
if (textAreaRef.current) {
textAreaRef.current.focus();
}
}
};
const handleChange = (e) => {
const searchVal = e.target.value;
const filteredOptions =
searchVal.length > 0
? SUPPORTED_LANGUAGES.filter((option) =>
option.label.toLowerCase().includes(searchVal.toLowerCase()),
)
: SUPPORTED_LANGUAGES;
setOptions(filteredOptions);
setFocusedOptionIndex(0);
};
const handleKeyDown = (event) => {
if (isOpen) {
switch (event.key) {
case "ArrowDown":
setFocusedOptionIndex((prevIndex) => {
const newIndex =
prevIndex < options.length - 1 ? prevIndex + 1 : prevIndex;
optionsRef.current[newIndex].scrollIntoView({ block: "nearest" });
return newIndex;
});
break;
case "ArrowUp":
setFocusedOptionIndex((prevIndex) => {
const newIndex = prevIndex > 0 ? prevIndex - 1 : prevIndex;
optionsRef.current[newIndex].scrollIntoView({ block: "nearest" });
return newIndex;
});
break;
case "Enter":
event.preventDefault();
handleOptionClick(options[focusedOptionIndex], focusedOptionIndex);
break;
case "Escape":
setIsOpen(false);
setOptions(SUPPORTED_LANGUAGES);
if (textAreaRef.current) {
textAreaRef.current.focus();
}
break;
default:
break;
}
} else if (!isModalOpen && event.ctrlKey && event.key === "l") {
event.preventDefault();
setIsOpen(true);
}
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("keydown", handleKeyDown);
};
}, []);
}, [isOpen, options, focusedOptionIndex]);
useEffect(() => {
if (isOpen && searchRef.current) {
searchRef.current.focus();
}
}, [isOpen]);
return (
<div className={styles.select} ref={selectRef}>
@ -45,16 +120,26 @@ const CustomSelect = ({ options, onSelect }) => {
)}
</div>
{isOpen && (
<div className={styles.options}>
{options.map((option) => (
<div
key={option.value}
className={styles.option}
onClick={() => handleOptionClick(option)}
>
{option.label}
</div>
))}
<div className={styles.options__container}>
<input
onChange={handleChange}
className={styles.options__search}
ref={searchRef}
placeholder="Search..."
/>
<div className={styles.options}>
{options.map((option, index) => (
<div
key={option.value}
className={`${styles.option} ${index === focusedOptionIndex ? styles.focused : ""}`}
onClick={() => handleOptionClick(option, index)}
tabIndex={0}
ref={(el) => (optionsRef.current[index] = el)}
>
{option.label}
</div>
))}
</div>
</div>
)}
</div>

View file

@ -10,39 +10,76 @@
.selected__option {
cursor: pointer;
font-weight: 500;
padding: 10px;
border: none;
display: flex;
justify-content: space-between;
}
.options {
.options__container {
position: absolute;
display: flex;
flex-direction: column;
padding: 0.5rem 0;
top: 140%;
left: 0;
height: 400px;
overflow-y: auto;
width: 100%;
height: 400px;
border: none;
border-radius: 10px;
background-color: var(--color-yellow);
z-index: 2;
align-items: center;
}
.options__search {
margin-top: 0.5rem;
padding: 6px;
width: 85%;
border: none;
border-radius: 6px;
background-color: #d79921;
color: var(--color-dark);
&:focus {
outline: none;
}
&::placeholder {
color: #504945;
opacity: 1;
}
&::-ms-input-placeholder {
color: #504945;
}
}
.options {
width: 100%;
height: 400px;
overflow-y: auto;
margin-top: 10px;
}
.option {
padding: 10px;
color: var(--color-dark);
border-bottom: var(--color-dark) 1px solid;
border-bottom: 1px solid var(--color-dark);
cursor: pointer;
transition: all 0.3s ease;
transition: transform 0.3s ease;
&:hover {
scale: 0.9;
transform: scale(0.9);
}
&:last-child {
border-bottom: none;
}
}
.option.focused {
background-color: #d79921;
outline: none;
}
@media screen and (max-width: 480px) {
.select {
width: 8rem;

View file

@ -5,13 +5,13 @@ import React, {
useCallback,
useMemo,
} from "react";
import { Link } from "react-router-dom";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import Prism from "prismjs";
import styles from "./Editor.module.css";
import "../prism-themes/prism-gruvbox-dark.css";
import "../prism-themes/prism-line-numbers.css";
import { URL_REGEX } from "../../utils/constants";
import Header from "../Header/Header";
import {
generateAESKey,
keyToString,
@ -20,6 +20,9 @@ import {
decryptAES,
} from "../../utils/encryption";
import Modal from "../Modal/Modal";
import AlertToast from "../AlertToast/AlertToast";
import CustomSelect from "../CustomSelect/CustomSelect";
import KeyboardModal from "../KeyboardModal/KeyboardModal";
const Editor = () => {
const { id } = useParams();
@ -28,6 +31,8 @@ const Editor = () => {
const [text, setText] = useState("");
const [language, setLanguage] = useState("none");
const [openModal, setOpenModal] = useState(false);
const [openKeyboardModal, setOpenKeyboardModal] = useState(false);
const [openAlertToast, setOpenAlertToast] = useState(false);
const textareaRef = useRef(null);
const lineNumberRef = useRef(null);
const queryParams = useMemo(
@ -48,7 +53,7 @@ const Editor = () => {
const handleSaveClick = useCallback(async () => {
if (!text) {
alert("Please enter some text!");
setOpenAlertToast(true);
return;
}
if (URL_REGEX.test(text)) {
@ -189,14 +194,67 @@ const Editor = () => {
});
}, []);
useEffect(() => {
const handleKeyDown = (event) => {
if (!id && event.ctrlKey && event.key.toLowerCase() === "s") {
event.preventDefault();
handleSaveClick();
}
};
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [handleSaveClick]);
return (
<>
<Header isSelectVisible={!id} onLanguageChange={handleLanguageChange} />
{!id && (
<>
<KeyboardModal
textAreaRef={textareaRef}
isOpen={openKeyboardModal}
setIsOpen={setOpenKeyboardModal}
isModalOpen={openModal}
/>
<AlertToast
openAlertToast={openAlertToast}
setOpenAlertToast={setOpenAlertToast}
/>
</>
)}
<div className={styles.header}>
<Link to="/">
<h1>
<span className={styles.header__mini}>mini</span>bin
</h1>
</Link>
{!id && (
<div className={styles.header__right}>
<CustomSelect
onSelect={handleLanguageChange}
textAreaRef={textareaRef}
isModalOpen={openModal}
/>
<button className={styles.btn__help}>
<img
src="assets/icons/question.png"
className={styles.btn__help__icon}
alt="Help"
onClick={() => setOpenKeyboardModal(true)}
/>
</button>
</div>
)}
</div>
<Modal
openModal={openModal}
setOpenModal={setOpenModal}
onSuccessClick={handleSuccessClick}
onCancelClick={handleCancelClick}
textAreaRef={textareaRef}
/>
<div className={styles.container}>
{!id && (
@ -220,6 +278,7 @@ const Editor = () => {
ref={textareaRef}
placeholder="</> Paste, save, share! (Pasting just a URL will shorten it!)"
value={text}
disabled={openModal}
/>
<pre className="line-numbers" ref={lineNumberRef}>
<code

View file

@ -61,6 +61,25 @@
}
}
.btn__help {
height: 2.75rem;
width: 2.75rem;
background-color: var(--color-yellow);
border: none;
border-radius: 50%;
cursor: pointer;
transition: 0.3s;
z-index: 1;
&:hover {
transform: scale(1.1);
}
}
.btn__help__icon {
height: 1.75rem;
width: 1.75rem;
}
.btn__icon {
height: 3rem;
width: 3rem;
@ -68,6 +87,47 @@
contrast(88%);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0.5rem 2rem;
color: var(--color-light);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.header__right {
display: flex;
align-items: center;
gap: 1rem;
}
.header h1 {
margin: 0;
}
.header a {
text-decoration: none;
color: inherit;
}
.header__mini {
color: var(--color-yellow);
}
@media screen and (max-width: 480px) {
.header {
margin: 0.5rem 1rem;
}
.header h1 {
font-size: 2rem;
}
}
@media screen and (max-width: 768px) {
.btn__save {
bottom: 2rem;
@ -80,4 +140,8 @@
height: 2rem;
width: 2rem;
}
.btn__help {
display: none;
}
}

View file

@ -1,25 +0,0 @@
import React from "react";
import { Link } from "react-router-dom";
import { SUPPORTED_LANGUAGES } from "../../utils/constants";
import styles from "./Header.module.css";
import CustomSelect from "../CustomSelect/CustomSelect";
const Header = ({ isSelectVisible, onLanguageChange }) => {
return (
<div className={styles.header}>
<Link to="/">
<h1>
<span className={styles.header__mini}>mini</span>bin
</h1>
</Link>
{isSelectVisible && (
<CustomSelect
options={SUPPORTED_LANGUAGES}
onSelect={onLanguageChange}
/>
)}
</div>
);
};
export default Header;

View file

@ -1,34 +0,0 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0.5rem 2rem;
color: var(--color-light);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.header h1 {
margin: 0;
}
.header a {
text-decoration: none;
color: inherit;
}
.header__mini {
color: var(--color-yellow);
}
@media screen and (max-width: 480px) {
.header {
margin: 0.5rem 1rem;
}
.header h1 {
font-size: 2rem;
}
}

View file

@ -0,0 +1,71 @@
import React, { useRef, useState, useEffect } from "react";
import styles from "./KeyboardModal.module.css";
const KeyboardModal = ({ textAreaRef, isOpen, setIsOpen, isModalOpen }) => {
const keyboardModalRef = useRef(null);
const handleClickOutside = (event) => {
if (
keyboardModalRef.current &&
!keyboardModalRef.current.contains(event.target)
) {
setIsOpen(false);
}
};
const handleKeyDown = (event) => {
if (!isModalOpen && event.ctrlKey && event.key === "k") {
event.preventDefault();
if (textAreaRef.current) {
textAreaRef.current.blur();
}
setIsOpen(true);
} else if (isOpen && event.key === "Escape") {
setIsOpen(false);
if (textAreaRef.current) {
textAreaRef.current.focus();
}
}
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("keydown", handleKeyDown);
};
}, [isOpen]);
return (
isOpen && (
<div className={`${styles.background} ${styles.active}`}>
<div ref={keyboardModalRef} className={styles.container}>
<button
className={styles.container__close}
onClick={() => setIsOpen(false)}
>
<span>&#10007;</span>
</button>
<p className={styles.container__title}>Keyboard Shortcuts</p>
<div className={styles.container__shortcuts}>
<p>
<span className={styles.container__shortcut__key}>Ctrl + K</span>
Open Keyboard Shortcuts
</p>
<p>
<span className={styles.container__shortcut__key}>Ctrl + L</span>
Open Language Selector
</p>
<p>
<span className={styles.container__shortcut__key}>Ctrl + S</span>
Save Bin
</p>
</div>
</div>
</div>
)
);
};
export default KeyboardModal;

View file

@ -0,0 +1,60 @@
.background {
display: none;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: #00000062;
z-index: 10;
}
.active {
display: flex;
align-items: center;
justify-content: center;
}
.container {
top: 0;
left: 0;
background-color: var(--color-dark);
padding: 30px;
border-radius: 10px;
z-index: 11;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: var(--color-light);
}
.container__title {
margin: 0;
font-size: 1.5rem;
}
.container__shortcuts {
display: flex;
flex-direction: column;
justify-content: center;
margin-top: 10px;
}
.container__shortcut__key {
color: var(--color-yellow);
margin-right: 15px;
}
.container__close {
cursor: pointer;
background: none;
border: none;
margin-left: auto;
margin-bottom: 10px;
color: inherit;
font-size: 2rem;
&:hover {
transform: scale(0.9);
}
}

View file

@ -1,39 +1,79 @@
import React, { useRef, useEffect } from "react";
import styles from "./Modal.module.css";
const Modal = ({ openModal, setOpenModal, onSuccessClick, onCancelClick }) => {
const Modal = ({
openModal,
setOpenModal,
onSuccessClick,
onCancelClick,
textAreaRef,
}) => {
const modalRef = useRef(null);
useEffect(() => {
const handleClickOutside = (event) => {
if (modalRef.current && !modalRef.current.contains(event.target)) {
setOpenModal(false);
if (textAreaRef.current) {
setTimeout(() => {
textAreaRef.current.focus();
}, 0);
}
}
};
const handleKeyDown = (event) => {
if (event.key === "Escape") {
setOpenModal(false);
if (textAreaRef.current) {
setTimeout(() => {
textAreaRef.current.focus();
}, 0);
}
} else if (
(event.key.toLowerCase() === "y" ||
event.key.toLowerCase() === "enter") &&
openModal
) {
onSuccessClick();
event.preventDefault();
setOpenModal(false);
} else if (event.key.toLowerCase() === "n" && openModal) {
onCancelClick();
setOpenModal(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("keydown", handleKeyDown);
};
}, [setOpenModal]);
}, [openModal]);
return (
<div className={`${styles.background} ${openModal && styles.active}`}>
<div ref={modalRef} className={styles.container}>
<button
className={styles.container__close}
onClick={() => setOpenModal(false)}
>
<span>&#10007;</span>
</button>
<p className={styles.container__title}>Encrypt content?</p>
<div className={styles.container__actions}>
<button onClick={onSuccessClick}>Yes</button>
<button onClick={onCancelClick}>No</button>
openModal && (
<div className={`${styles.background} ${openModal && styles.active}`}>
<div ref={modalRef} className={styles.container}>
<button
className={styles.container__close}
onClick={() => setOpenModal(false)}
>
<span>&#10007;</span>
</button>
<p className={styles.container__title}>
Encrypt content?{" "}
<span className={styles.container__title__span}>[Y/n]</span>
</p>
<div className={styles.container__actions}>
<button onClick={onSuccessClick}>Yes</button>
<button onClick={onCancelClick}>No</button>
</div>
</div>
</div>
</div>
)
);
};

View file

@ -42,6 +42,11 @@
}
}
.container__title__span {
font-family: "JetBrains Mono", monospace;
color: var(--color-yellow);
}
.container__actions {
margin-top: 20px;
display: flex;
@ -65,4 +70,3 @@
transform: scale(1.1);
}
}

View file

@ -1,54 +1,87 @@
/* latin-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 100 800;
font-display: swap;
src: url(/assets/fonts/JetBrains_Mono_latin-ext.woff2) format('woff2');
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 100 800;
font-display: swap;
src: url(/assets/fonts/JetBrains_Mono_latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(/assets/fonts/Poppins_latin-ext_400.woff2) format('woff2');
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(/assets/fonts/Poppins_latin_400.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(/assets/fonts/Poppins_latin-ext_700.woff2) format('woff2');
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(/assets/fonts/Poppins_latin_700.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: "JetBrains Mono";
font-style: normal;
font-weight: 100 800;
font-display: swap;
src: url(/assets/fonts/JetBrains_Mono_latin-ext.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "JetBrains Mono";
font-style: normal;
font-weight: 100 800;
font-display: swap;
src: url(/assets/fonts/JetBrains_Mono_latin.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(/assets/fonts/Poppins_latin-ext_400.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(/assets/fonts/Poppins_latin_400.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(/assets/fonts/Poppins_latin-ext_500.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(/assets/fonts/Poppins_latin_500.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(/assets/fonts/Poppins_latin-ext_700.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(/assets/fonts/Poppins_latin_700.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View file

@ -1,6 +1,5 @@
import { Routes, Route } from "react-router-dom";
import styles from "./Home.module.css";
import Header from "../../components/Header/Header";
import Editor from "../../components/Editor/Editor";
const Home = () => {