Оглавление
- Введение и предварительные условия
- Базовая установка
- Создание бургера
- Создание бокового меню
- Финальные штрихи
Введение и предварительные условия
Итак, для сегодняшнего урока я решил сделать кое-что на React. Если вы хотите следовать за мной, я рекомендую вам иметь некоторые знания о React, иначе некоторые вещи могут показаться вам непонятными. И, как всегда, будет YouTube-версия этого руководства, вы можете найти ее здесь 🙂
Вот что мы создаем сегодня:
Базовая установка
Итак, я решил сделать забавную вещь: у всех будет один и тот же сайт, но с разными цветовыми темами.
Вы откроете этот сайт. По сути, это блог, демонстрирующий 40 различных цветовых тем (я всегда использую этот сайт).
Теперь вам нужно открыть в Google генератор случайных чисел и покрутить его. Какое бы число вы ни получили, это и будет ваша цветовая тема. Вот что получилось у меня:
Запомните свою цветовую тему!
Теперь откройте папку, в которой будет находиться ваш код, запустите проект React и загрузите следующие пакеты:
npx create-react-app frontend
cd frontend
yarn add hamburger-react react-focus-lock react-icons styled-components
После этого откройте редактор кода и сохраните/создайте следующие файлы:
Быстрое примечание: Другие файлы self-created
не понадобятся, поэтому смело delete
их, также мы не будем использовать простой CSS
, мы будем стилизовать наши компоненты с помощью styled-components
.
Теперь измените код внутри App.js
на:
import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./global";
import { theme } from "./theme";
function App() {
return (
<ThemeProvider theme={theme}>
<GlobalStyles />
<div class="main-text">
<h1>Hello World</h1>
</div>
</ThemeProvider>
);
}
export default App;
И код внутри index.js
на:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Теперь внутри theme.js
определите вашу тему:
export const theme = {
primaryDark: "#2B3252",
primaryLight: "#EF5455",
primaryHover: "#FAD744",
mobile: "576px",
};
Здесь вы можете использовать свою цветовую тему. Для primaryDark
используйте более темный цвет в вашей теме. А для primaryLight
используйте более светлый цвет. Если в вашей теме нет третьего цвета, используйте #FFFFFF
или #000000
для primaryHover
.
А теперь внутри global.js
определите основные правила CSS:
import { createGlobalStyle } from "styled-components";
export const GlobalStyles = createGlobalStyle`
html, body {
margin: 0;
padding: 0;
}
*, *::after, *::before {
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
background: ${({ theme }) => theme.primaryDark};
color: ${({ theme }) => theme.primaryLight};
height: 100vh;
line-height: 1.6;
font-family: "Roboto", sans-serif;
font-size: 1.2rem;
font-weight: normal;
}
h1 {
font-size: 2rem;
text-align: center;
text-transform: uppercase;
}
div {
text-align: center;
}
small {
display: block;
}
a {
color: ${({ theme }) => theme.primaryHover};
text-decoration: none;
}
.main-text {
margin-left: 20rem;
margin-right: 20rem;
}
`;
Если у вас есть вопросы о том, как мы определяем CSS с помощью styled-components
, задавайте их в комментариях!
Вот что у нас осталось:
Создание бургера
По адресу ./components/Burger
создайте следующие файлы:
Внутри index.js
напишите:
export { default } from "./Burger";
Внутри Burger.styled.js
напишите:
import styled from "styled-components";
export const StyledBurger = styled.button`
position: absolute;
top: 5%;
left: 2rem;
display: flex;
flex-direction: column;
justify-content: space-around;
width: 2rem;
height: 2rem;
background: transparent;
border: none;
cursor: pointer;
padding: 0;
z-index: 10;
span {
width: 2rem;
height: 0.25rem;
background: ${({ theme, open }) =>
open ? theme.primaryDark : theme.primaryLight};
border-radius: 10px;
transition: all 0.3s linear;
position: relative;
transform-origin: 1px;
:first-child {
transform: ${({ open }) => (open ? "rotate(45deg)" : "rotate(0)")};
}
:nth-child(2) {
opacity: ${({ open }) => (open ? "0" : "1")};
transform: ${({ open }) => (open ? "translateX(20px)" : "translateX(0)")};
}
:nth-child(3) {
transform: ${({ open }) => (open ? "rotate(-45deg)" : "rotate(0)")};
}
}
`;
Внутри Burger.js
мы будем использовать hamburger-react
, который мы установили ранее. Это пакет, созданный специально для меню гамбургера. Вы можете узнать больше о нем на официальном сайте.
Теперь добавьте следующий код внутри Burger.js
:
import React from "react";
import { bool, func } from "prop-types";
import { Twirl as Hamburger } from "hamburger-react";
import { StyledBurger } from "./Burger.styled";
const Burger = ({ open, setOpen, ...props }) => {
const isExpanded = open ? true : false;
return (
<StyledBurger>
<Hamburger
toggled={open}
toggle={setOpen}
size={32}
direction="right"
duration={0.4}
distance="lg"
aria-expanded={isExpanded}
color={open ? "#2B3252" : "#EF5455"}
></Hamburger>
</StyledBurger>
);
};
Burger.propTypes = {
open: bool.isRequired,
setOpen: func.isRequired,
};
export default Burger;
Быстрое замечание: в атрибуте color
в <Hamburger>
используйте эту формулу (так как мы используем разные темы):
Мы почти закончили! Внутри ./components/index.js
добавьте:
export { default as Burger } from "./Burger";
И внутри App.js
напишите:
import React, { useState } from "react";
import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./global";
import { theme } from "./theme";
import { Burger } from "./components";
function App() {
const [open, setOpen] = useState(false);
return (
<ThemeProvider theme={theme}>
<GlobalStyles />
<Burger open={open} setOpen={setOpen} aria-controls={menuId} />
<div class="main-text">
<h1>Hello World!</h1>
</div>
</ThemeProvider>
);
}
export default App;
Теперь, если вы запустите ваше приложение, вы увидите иконку гамбургера
в левом верхнем углу 🙂
Создание бокового меню
Так же, как и Burger
в папке Menu
, создайте:
Внутри index.js
добавьте:
export { default } from "./Menu";
Внутри Menu.styled.js
добавить:
import styled from "styled-components";
export const StyledMenu = styled.nav`
display: flex;
flex-direction: column;
justify-content: center;
background: ${({ theme }) => theme.primaryLight};
transform: ${({ open }) => (open ? "translateX(0)" : "translateX(-100%)")};
height: 100vh;
text-align: left;
padding: 2rem;
position: absolute;
top: 0;
left: 0;
transition: transform 0.3s ease-in-out;
@media (max-width: ${({ theme }) => theme.mobile}) {
width: 100%;
}
a {
font-size: 2rem;
text-transform: uppercase;
padding: 2rem 0;
font-weight: bold;
letter-spacing: 0.5rem;
color: ${({ theme }) => theme.primaryDark};
text-decoration: none;
transition: color 0.6s linear;
@media (max-width: ${({ theme }) => theme.mobile}) {
font-size: 1.5rem;
text-align: center;
}
&:hover {
color: ${({ theme }) => theme.primaryHover};
}
}
.icon {
margin-right: 10px;
}
`;
И, наконец, внутри Menu.js
добавьте:
import React from "react";
import { bool } from "prop-types";
import { AiOutlineFileText } from "react-icons/ai";
import { RiPriceTag3Line, RiContactsBookLine } from "react-icons/ri";
import { StyledMenu } from "./Menu.styled";
const Menu = ({ open, ...props }) => {
const isHidden = open ? true : false;
const tabIndex = isHidden ? 0 : -1;
return (
<StyledMenu open={open} aria-hidden={!isHidden} {...props}>
<a href="#" tabIndex={tabIndex}>
<span aria-hidden="true">
<AiOutlineFileText className="icon" />
</span>
About us
</a>
<a href="#" tabIndex={tabIndex}>
<span aria-hidden="true">
<RiPriceTag3Line className="icon" />
</span>
Pricing
</a>
<a href="#" tabIndex={tabIndex}>
<span aria-hidden="true">
<RiContactsBookLine className="icon" />
</span>
Contact
</a>
</StyledMenu>
);
};
Menu.propTypes = {
open: bool.isRequired,
};
export default Menu;
И не забудьте внутри ./components/index.js
добавить:
...
export { default as Menu } from "./Menu";
И в App.js
добавить:
import React, { useState, useRef } from "react";
import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./global";
import { theme } from "./theme";
import FocusLock from "react-focus-lock";
import { useOnClickOutside } from "./hooks";
import { Burger, Menu } from "./components";
function App() {
const [open, setOpen] = useState(false);
const node = useRef();
const menuId = "main-menu";
useOnClickOutside(node, () => setOpen(false));
return (
<ThemeProvider theme={theme}>
<GlobalStyles />
<Burger open={open} setOpen={setOpen} aria-controls={menuId} />
<Menu open={open} setOpen={setOpen} id={menuId} />
<div class="main-text">
<h1>Hello World</h1>
</div>
</ThemeProvider>
);
}
export default App;
И, мы закончили! Однако есть еще пара штрихов, которые мы могли бы добавить. Если вы не хотите этого делать, ничего страшного, боковая навигация прекрасно работает и так 🙂
Завершающие штрихи
Итак, я говорю о том, что если вы щелкните за пределами боковой навигации, она не закроется. Это нормально, если вы хотите, чтобы пользователь нажал на иконку X
, чтобы закрыть его. Однако мне это не нравится 🙂
Внутри hooks.js
(который мы создали в начале) добавьте:
import { useEffect } from "react";
export const useOnClickOutside = (ref, handler) => {
useEffect(() => {
const listener = (event) => {
if (!ref.current || ref.current.contains(event.target)) {
return;
}
handler(event);
};
document.addEventListener("mousedown", listener);
return () => {
document.removeEventListener("mousedown", listener);
};
}, [ref, handler]);
};
И измените код в App.js
на:
import React, { useState, useRef } from "react";
import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./global";
import { theme } from "./theme";
import FocusLock from "react-focus-lock";
import { useOnClickOutside } from "./hooks";
import { Burger, Menu } from "./components";
function App() {
const [open, setOpen] = useState(false);
const node = useRef();
const menuId = "main-menu";
useOnClickOutside(node, () => setOpen(false));
return (
<ThemeProvider theme={theme}>
<GlobalStyles />
<div ref={node}>
<FocusLock disabled={!open}>
<Burger open={open} setOpen={setOpen} aria-controls={menuId} />
<Menu open={open} setOpen={setOpen} id={menuId} />
</FocusLock>
</div>
<div class="main-text">
<h1>Hello World!</h1>
</div>
</ThemeProvider>
);
}
export default App;
Обратите внимание, здесь мы использовали несколько вещей. Во-первых, мы использовали useRef
, который является хуком React. Вы можете прочитать больше о useRef
здесь. И мы использовали react-focus-lock
. Вот официальная (я думаю) документация.
Теперь, мы закончили!
Ссылка на страницу github.
Надеюсь, вам понравилось. Если вам понравилось, не забудьте следовать за мной.
Ссылки на мои социальные сети:

24YoungMamba (Young Mamba) — GitHub
Я делаю сайты : )Я могу создать ваш сайт. 24YoungMamba имеет один доступный репозиторий. Следите за их кодом на GitHub.


Young Mamba — Medium
Читайте статьи от Young Mamba на Medium. Я делаю сайты👨💻. Каждый день Young Mamba и тысячи других авторов читают, пишут и делятся важными историями на Medium.

Ошибка жидкости: внутренняя