Vladislav Moiseev 27cb891083 Add lwF3
2025-04-01 23:44:31 +04:00
..
2025-04-01 23:44:31 +04:00

Лабораторная работа №F3 - Стилизация веб-приложения (React)

Цель работы

  • Освоение принципов стилизации элементов при помощи конструкций CSS.
  • Освоение использования компонентов готовых дизайн-систем в React.

Ход работы

  1. Подключить к проекту дизайн-систему.
  2. Для компонентов, которые в работе F2 разрабатывались вручную, найти аналоги.
  3. Начать использовать компоненты из дизайн-системы.
  4. Добавить собственные стили при помощи CSS-модулей, чтобы вёрстка сайта была похожа на макет из работы F1.
  5. При необходимости добавить изображения и фон.

Для любителей хардкора пункты 1-3 можно пропустить, и всё делать самому.

Подключение дизайн-системы

В nodejs есть стандартный менеджер пакетов NPM. По аналогии с NuGet в DotNet и Maven в Java.

Обычно подключение дизайн-системы, как и любых других библиотек, происходит через указанный выше NPM командой npm install. Её необходимо запускать в той директории, где располагается файл package.json. После успешной установки библиотеки (или "зависимости" для нашего приложения) изменятся файлы package.json и package-lock.json. В этих файлах описывается базовая информация о проекте и закрепление конкретных зависимостей сответственно.

Что за закрепление зависимостей? Всё дело в том, что в package.json обычно указаны диапазоны зависимостей (например, "zustand": "^4.5.5", то есть "любая версия от 4.5.5 строго до 5.0.0"). В package-lock.json указывается конкретная версия, которая должна удовлетворять требованиям и быть использована при работе нашего приложения.

Пример команды подключения consta design:

npm install @consta/uikit @consta/icons @consta/header

Это команда установит компоненты из Consta UI и Consta Header, а также иконки из Consta Icons.

Собственная стилизация через CSS-модули

В классическом документе HTML с подключёнными CSS все стили для всех элементов описываются глобально, а элементы, для которых и нужно определить какие-то стили, выбираются через "селекторы".

Пример селектора:

  • .text-danger - все элементы, у которых определён аттрибут class="<...> text-danger <...>", то есть хотя бы один класс - это text-danger;
  • .text.text-danger - все элементы, у которых определены сразу два класса: и text, и text-danger;
  • p,h1 - этот селектор выбирает все абзацы (<p>это абзац</p>) и все заголовки первого уровня (если что, их 6 уровней);
  • a:hover - для ссылок (<a href="https://ulstu.ru/">Политех!</a>), на которые пользователь навёл мышь.

Затем для каждого селектора объявляется набор правил стилизации, например:

.text {
    font-size: 16px; /* Размер шрифта */
}

.text.text-danger {
    color: #ff0000; /* Красный цвет текста */
    background-color: #ff9999; /* Розовый - цвет фона */ 
}

h1 {
    border: 1px solid black; /* Стиль границ элемента */
    border-radius: 10px; /* Радиус скругления границ */
}

Этот подход существует уже как несколько десятков лет, но есть нюанс. Если сайт (веб-приложение, система) очень сложная и некоторые стили ещё и подключаются снаружи (например, та же дизайн-система), то можно легко схлопотать конфликт стилей.

Представим, что для примера выше где-то другой разработчик определил следующее:

.text {
    font-size: 14pt;
}

И что получится? Размер шрифта должен быть 16px или 14pt?

Чтобы частично решить эту проблему, было предложено множество подходов.

Один из них: БЭМ (Блок, Элемент, Модификатор). Он описывает правила, как должны называться классы, чтобы не было конфликта стилей, как выше.

Другой подход - использование модулей CSS. При этом подходе для каждого компонента создаётся отдельный файл %component_name%.module.css, где можно указывать стили для конкретных элементов компонента.

Импортируется такой набор стилей как import styles from './%component_name%.module.css', далее можно обращаться к переменной styles. Это - объект, в котором каждое поле - это название класса из CSS-модуля.

Предположим CSS-модуль для карточки (Card.module.css) описан таким образом:

.header { /* Стили заголовка */ }
.text { /* Стили текста. Да, тут нет комментариев // */ }
.footer { /* Стили подвала */ }

Тогда если импортировать этот модуль в Card.tsx как import styles from './Card.module.css', то в объекте styles будут следующие строки:

  • styles.header - для className, связанного с заголовком;
  • styles.text - для className, связанного с основным текстом;
  • styles.footer - для className, связанного с подвалом.

Пример описания компонента Card.tsx может быть таким:

import { ReactNode, PropsWithChildren } from 'react';
import styles from './Card.module.css';

interface Props {
    header: string;
    footer: ReactNode;
}

export const Card = (props: PropsWithChildren<Props>) => (
    <div>
        <h3 className={styles.header}>{props.header}</h3>
        <div className={styles.body}>{props.children}<div>
        <footer className={styles.footer}>{props.footer}</footer>
    </div>
);

Вся мощь CSS-модулей - это уникальные названия селекторов. Предположим, есть 2 CSS-модуля, и в обоих определён селектор .header. Тогда при компиляции нашего приложения для одного из компонентов итоговое название класса, которое "увидит" браузер, может быть _header_mdccx_73, а для другого - _header_mdccx_78.

Добавление изображений

Как добавлять статические файлы (к которым относятся документы, картинки, и т.д.) для vite описаны в этой документации.

Если кратко, есть несколько путей:

  • Скопировать файлы с изображениями в папку public и использовать в качестве ссылки на фон или изображение просто имя файла, например, <img src="img.png" alt="" />.
  • Скопировать файлы с изображениями рядом с компонентом и импортировать их как import imgBg from './img.png'. Тогда в переменную imgBg будет помещён сгенерированный url, который надо использовать по назначению, например, <img src={imgBg} alt="" />.
  • Скопировать файлы с изображениями рядом с CSS-модулем и использовать значение свойства по типу url('./img.png'). Например, .text{ background: url('./bg.png'); }.