<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 19:57:06 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="AePB2zj3sWgWLo2WpHgVzOpEY6fiX9JrWC39rEk5yBbuMgrsyokcCKBtqQ6od-W7Kk1ODepoLMnlzWf4Gz4veA";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Создание пользовательского компонента | Bootstrap 5: Продвинутый уровень</title>
<meta name="description" content="Создание пользовательского компонента / Bootstrap 5: Продвинутый уровень: Разработаем пользовательский компонент цитат с использованием возможностей Bootstrap">
<link rel="canonical" href="https://ru.hexlet.io/courses/bootstrap-advanced/lessons/creating-a-component/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Создание пользовательского компонента">
<meta property="og:title" content="Bootstrap 5: Продвинутый уровень">
<meta property="og:description" content="Создание пользовательского компонента / Bootstrap 5: Продвинутый уровень: Разработаем пользовательский компонент цитат с использованием возможностей Bootstrap">
<meta property="og:url" content="https://ru.hexlet.io/courses/bootstrap-advanced/lessons/creating-a-component/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="M609UfC_w_47jhwLcRg6l3F6xXmAArsbXPnqFLAwPZzcfPZmAsFuno3NOJN9F8rgsXPo04g1RbnhGXBA4jfa8g" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDAzNCwicHVyIjoiYmxvYl9pZCJ9fQ==--ba516ea9573bdfcd1d21e2aa0fff8818561828f2/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Typing-bro.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDA0MCwicHVyIjoiYmxvYl9pZCJ9fQ==--137ae65fe79f41b3d5270b8a3b65b38b1621c65b/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Code%20typing-bro.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T19:57:05.943Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"q817bK_JP4u8jFZ7HdvApaKN_c4Zmby1dDeFdjiZDF5EHLBbXbeS6wrPcuMR1DDSYoTQZBGuQhfJ1x8iap7rMA","topics":[{"id":90740,"title":"по поводу видео - извините конечно , но информативности в 2-х часовом видео минут на 5\n\nно главное даже не это а то как подается материал, вы начинаете применять стили на свой вариант верстки - это прекрасно , но я как студент у которого будет своя верстка хочу понять саму суть этих классов , откуда их брать, как я должен додумать где какой класс применить , проще говоря вы в этом видео даете рыбу а не удочку , но мне на рыбалке ваша рыба даром не нужна -надеюсь так понятно.","plain_title":"по поводу видео - извините конечно , но информативности в 2-х часовом видео минут на 5 но главное даже не это а то как подается материал, вы начинаете применять стили на свой вариант верстки - это прекрасно , но я как студент у которого будет своя верстка хочу понять саму суть этих классов , откуда их брать, как я должен додумать где какой класс применить , проще говоря вы в этом видео даете рыбу а не удочку , но мне на рыбалке ваша рыба даром не нужна -надеюсь так понятно. ","creator":{"public_name":"Den Drop","id":501541,"is_tutor":false},"comments":[{"creator":{"public_name":"Olga Pejenkova","id":364375,"is_tutor":true},"id":180052,"body":"**Den Drop**, \n\nвидео это дополнительный материал, который дополняет теорию и практику курса. Основная его задача - дать представление, рассказать и показать, какие подходы и способы работы существуют, то есть оно не предполагает необходимости повторять все шаги точь-в-точь. Но, опираясь на примеры из видео, вы можете в своем локальном проекте поэкспериментировать самостоятельно с разными концепциями, о которых рассказывается в теории этого урока. \n","topic_id":90740},{"creator":{"public_name":"Den Drop","id":501541,"is_tutor":false},"id":180055,"body":"в своем сообщении я передал вам \"боль клиента\" , вы вместо того чтобы эту боль решить - давите сильнее\n\n\"дополнительный материал\" - это прекрасно, но повторюсь в нем \"дополнительного\" на 5 минут а идет оно 2 часа - наверное стоит подумать над значимостью такого материала\n\n\"Но, опираясь на примеры из видео\" в этом и вся проблема! я не хочу и не смогу в реальности опираться на единственный пример. И понятно что все разобрать невозможно- но можно показать КЛЮЧ как разбираться самому КАК и ГДЕ искать то что пригодится в разных ситуациях\n\nповторюсь- вы даете готовую рыбу, а не удочку - в этом видео","topic_id":90740}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Создание пользовательского компонента","entity_url":null,"active":true}},{"id":90738,"title":"--#{$prefix}citation-padding: #{$citation-padding};\n\nпоясните что за prefix у всех значений , откуда он берется и где задается \n\nне очень понятно почему если мы работаем с scss переменная обозначена через VAR \n\n color: var(--#{$prefix}citation-color);\n\nдалее с цветами - почему все свойства идут с -- \n\n--bs-citation-color: var(--bs-primary-text-emphasis);\n\nне понятно зачем так , можно хотябы по одной строчке в каждом блоке разьяснять досканально что откуда и зачем\n\n","plain_title":"--#{$prefix}citation-padding: #{$citation-padding}; поясните что за prefix у всех значений , откуда он берется и где задается не очень понятно почему если мы работаем с scss переменная обозначена через VAR color: var(--#{$prefix}citation-color); далее с цветами - почему все свойства идут с -- --bs-citation-color: var(--bs-primary-text-emphasis); не понятно зачем так , можно хотябы по одной строчке в каждом блоке разьяснять досканально что откуда и зачем ","creator":{"public_name":"Den Drop","id":501541,"is_tutor":false},"comments":[{"creator":{"public_name":"Olga Pejenkova","id":364375,"is_tutor":true},"id":180051,"body":"**Den Drop**, \n\n>--#{$prefix}citation-padding: #{$citation-padding};\nпоясните что за prefix у всех значений , откуда он берется и где задается\n\n`prefix` - это переменная, которая задана в настройках бутстрап, в файле [_variables.scss](https://github.com/twbs/bootstrap/blob/fc8b03bee3929822c5418177be0c56d1745337fd/scss/_variables.scss#L393). Все CSS переменные в Bootstrap начинаются с префикса `bs-`. Префикс нужен для того, чтобы отделить CSS переменные бутстрап от других переменных, и тем самым избежать конфликта. \n\n>не очень понятно почему если мы работаем с scss переменная обозначена через VAR\n> color: var(--#{$prefix}citation-color);\n\nЗдесь меняются именно CSS переменные, которые будут доступны в браузере. Более подробно про переменные в [этом уроке](https://ru.hexlet.io/courses/css-content/lessons/variables/theory_unit).\n\n\n> далее с цветами - почему все свойства идут с --\n--bs-citation-color: var(--bs-primary-text-emphasis);\nне понятно зачем так , можно хотябы по одной строчке в каждом блоке разьяснять досканально что откуда и зачем\n\nc помощью `--` обозначаются [кастомные свойства](https://developer.mozilla.org/ru/docs/Web/CSS/--*) или сss-переменные. Например,\n\n```css\n:root {\n --primary-color: #fff;\n}\n\ndiv {\n color: var(--primary-color);\n}\n```\nВ примере выше мы определили переменную `--primary-color` в селекторе `:root`. Затем мы используем эту переменную для установки цвета `div`-a при помощи функции [var()](https://developer.mozilla.org/ru/docs/Web/CSS/var).","topic_id":90738}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Создание пользовательского компонента","entity_url":null,"active":true}},{"id":96853,"title":"Здравствуйте! Просидел за этим уроком часа 3, пытаясь понять, почему у меня ничего не работает (спойлер - оно так до конца и не заработало). В целом хотелось бы от урока большей \"внятности\", потому что, как по мне, сейчас он похож на транскрипт видео-урока, только вот без самого видео. Ну а о том, что весь урок построен на концепции \"посмотри как сделано - скопируй себе\", без объяснения, что, зачем и почему вставляется в каждую строку, хотя бы комментариями в коде первого элемента нашего компонента (*прям какие-то флешбеки с курса про React и его хуки*) - уже писали ниже. Возможно, видео из доп.материалов и старое-доброе \"потыкай в каждую строчку - посмотри на что в итоге повлияет\" поможет чуть лучше разобраться в этой теме.\n\nМогу предложить несколько рекомендаций по улучшению урока (в моём видении). Например, для начала можно указать, что базовую разметку для компонента надо самим создать в файле `index.pug`, благо это не так сложно. Следующее - в разделе про создание кавычек явная ошибка в адресах у `@import`, потому что буквально в предыдущем разделе с подключением Bootstrap и компонента Citation пути были другие. Далее - для каждого элемента нашего компонента код дописывается в файл `citation.scss`, что меня по началу ввело в заблуждение, потому что обычно в уроках указывался код для каждого элемента отдельно, а потом в конце урока сводился в один общий код. Тогда не будет необходимости отслеживать, что именно во внушительной \"простыни\" кода относится к конкретному элементу (пусть на это и непрозрачно намекают все эти `-before` и `-footer` в названии свойств). Ну и в качестве вишенки на торте - у меня всё равно после выполнения команды `build` в браузере на компоненте не применились стили Bootstrap ))","plain_title":"Здравствуйте! Просидел за этим уроком часа 3, пытаясь понять, почему у меня ничего не работает (спойлер - оно так до конца и не заработало). В целом хотелось бы от урока большей \"внятности\", потому что, как по мне, сейчас он похож на транскрипт видео-урока, только вот без самого видео. Ну а о том, что весь урок построен на концепции \"посмотри как сделано - скопируй себе\", без объяснения, что, зачем и почему вставляется в каждую строку, хотя бы комментариями в коде первого элемента нашего компонента (прям какие-то флешбеки с курса про React и его хуки) - уже писали ниже. Возможно, видео из доп.материалов и старое-доброе \"потыкай в каждую строчку - посмотри на что в итоге повлияет\" поможет чуть лучше разобраться в этой теме. Могу предложить несколько рекомендаций по улучшению урока (в моём видении). Например, для начала можно указать, что базовую разметку для компонента надо самим создать в файле index.pug, благо это не так сложно. Следующее - в разделе про создание кавычек явная ошибка в адресах у @import, потому что буквально в предыдущем разделе с подключением Bootstrap и компонента Citation пути были другие. Далее - для каждого элемента нашего компонента код дописывается в файл citation.scss, что меня по началу ввело в заблуждение, потому что обычно в уроках указывался код для каждого элемента отдельно, а потом в конце урока сводился в один общий код. Тогда не будет необходимости отслеживать, что именно во внушительной \"простыни\" кода относится к конкретному элементу (пусть на это и непрозрачно намекают все эти -before и -footer в названии свойств). Ну и в качестве вишенки на торте - у меня всё равно после выполнения команды build в браузере на компоненте не применились стили Bootstrap )) ","creator":{"public_name":"Андрей Луговов","id":517209,"is_tutor":false},"comments":[{"creator":{"public_name":"Olga Pejenkova","id":364375,"is_tutor":true},"id":187866,"body":"**Андрей Луговов**, \n\nСпасибо, передавала вашу обратную связь нашим методистам.","topic_id":96853}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Создание пользовательского компонента","entity_url":null,"active":true}},{"id":101240,"title":"Здравствуйте! Решил пройти добавленные тесты и споткнулся на 2-м вопросе. Исключил из ответов \"*Определение фиксированных размеров*\" и \"*Использование внешних библиотек*\", но решение неправильное. У ChatGPT мнение такое же, как и у меня. Что не так?! )","plain_title":"Здравствуйте! Решил пройти добавленные тесты и споткнулся на 2-м вопросе. Исключил из ответов \"Определение фиксированных размеров\" и \"Использование внешних библиотек\", но решение неправильное. У ChatGPT мнение такое же, как и у меня. Что не так?! ) ","creator":{"public_name":"Андрей Луговов","id":517209,"is_tutor":false},"comments":[{"creator":{"public_name":"Elena Gromova","id":548102,"is_tutor":true},"id":193259,"body":"**Андрей Луговов**, здравствуйте! Ответ есть в уроке)","topic_id":101240},{"creator":{"public_name":"Андрей Луговов","id":517209,"is_tutor":false},"id":193286,"body":"**Elena Gromova**, спасибо, проглядел )","topic_id":101240}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Создание пользовательского компонента","entity_url":null,"active":true}},{"id":101608,"title":"День добрый! Прощу помощи, не могу разобраться с данным заданием, ощущение что зашёл в тупик. Мне уже удалось в первый раз практически решить данное упражнение, но линтер никак не хотел пропускать решение со стандартными значениями которые были указаны в задаче, Не совсем понимаю в правильном ли я направлении двигаюсь и правильна ли сама стилистика написания стилей.\n\nВот второй вариант решения:\nhttps://ru.hexlet.io/code_reviews/1498618","plain_title":"День добрый! Прощу помощи, не могу разобраться с данным заданием, ощущение что зашёл в тупик. Мне уже удалось в первый раз практически решить данное упражнение, но линтер никак не хотел пропускать решение со стандартными значениями которые были указаны в задаче, Не совсем понимаю в правильном ли я направлении двигаюсь и правильна ли сама стилистика написания стилей. Вот второй вариант решения: https://ru.hexlet.io/code_reviews/1498618 ","creator":{"public_name":"Stepan Korshukov","id":278478,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":193693,"body":"**Stepan Korshukov**, возможно это из-за цвета, так как цвета действительно различаются. Обычно, если тесты не проходят, значит не все параметры соблюдены и задание нельзя считать выполненным. Бывают редкие ситуации, когда по ошибке тесты работают не так, как ожидалось или неверное описание задания. Но такие вещи мы стараемся быстро исправлять.","topic_id":101608},{"creator":{"public_name":"Stepan Korshukov","id":278478,"is_tutor":false},"id":193705,"body":"**Ivan Gagarinov**, \nЗдравствуйте ещё раз!\n\nВнимательно пробежался по теории ещё раз, решил использовать тот же подход с созданием цвета background, что был и в теории, через использование специальных версии, где фон осветляется, а текст становится темнее. Но у меня всё равно не получается достить идетичного цвета как на примере из теста. Я уже не знаю что тут можно еще сделать, можно хотя бы какой-то намёк получить на что мне стоить обратить внимание, потому что я уже не знаю где еще несостыковки в моём решении.\n\nВот моё последнее решение:\nhttps://ru.hexlet.io/code_reviews/1498618\n\nСпасибо=)","topic_id":101608},{"creator":{"public_name":"Stepan Korshukov","id":278478,"is_tutor":false},"id":193758,"body":"Добрый день! Я как-то с трудом справился с заданием, так как описание не соответствовало решению. В задании явно указаны цвета, которые следует использовать в качестве переменных:\n\nЦвета: основной серый (#343a40), белый (#ffffff), светло-серый для текста (#6c757d), зелёный для успешных комментариев (#28a745), красный для опасных комментариев (#dc3545), голубой для информационных комментариев (#17a2b8), жёлтый для предупреждающих комментариев (#ffc107).\n\nЭто ввело меня в заблуждение и заставило потратить много времени на поиск ошибки, хотя задание по сути было не слишком сложным. Решение учителя предполагает использование стандартных цветов без их переопределения. Возможно, я что-то неправильно понял, но, если я прав, вам стоит обновить описание задания, чтобы не вводить в заблуждение студентов, которые в будущем будут пытаться пройти этот трэк.","topic_id":101608},{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":193716,"body":"**Stepan Korshukov**, вы можете попросить помощи [в нашем комьюнити в телеграме](https://t.me/hexletcommunity). Там могут помочь другие ребята, кто уже прошел упражнение. Здесь, в обсуждениях, мы не делаем ревью ваших решений. Мы можем подсказать по выводу тестов или если что-то не понятно по тексту. Но поиск ошибок в вашем решении требует некоторых ресурсов. По сути для этого нужно продебажить ваше решение, провести ревью. Без этого сложно направить в нужную сторону. И для этого у нас предполагается обучение с наставником. Одна из задач наставника как раз проводить такую работу и помогать вам в решении. Но так как вы учитесь без наставника, вам придется самостоятельно искать ошибки в своих решениях или просить помощи у сообщества.","topic_id":101608},{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":193795,"body":"**Stepan Korshukov**, здравствуйте! Спасибо за фидбек! Добавил уточнение в описание.","topic_id":101608},{"creator":{"public_name":"Ivan Gagarinov","id":75907,"is_tutor":true},"id":193664,"body":"**Stepan Korshukov**, здравствуйте. Тесты проверяют только отображение страницы сравнивая ее с эталоном. После прогона тестами, в папке тестов будет директория со скриншотами, где можно посмотреть разницу между тем, что ожидалось, что в итоге было. Линтер никак не запрещает прохождение упражнения, обычно лучше его ошибки исправить, но можно и пропустить.","topic_id":101608},{"creator":{"public_name":"Stepan Korshukov","id":278478,"is_tutor":false},"id":193678,"body":"У меня складывается ощущение, что тесты не проходят по причине того, что цвета background не совпадают, других причин я не вижу.","topic_id":101608},{"creator":{"public_name":"Stepan Korshukov","id":278478,"is_tutor":false},"id":193675,"body":"**Ivan Gagarinov**, Это понятно, но что что мне делать если все параметры из задания соблюдены, а тесты всё равно не проходят. В целом этот урок не очень понятен, даже после просмотра видео прикреплённого к дополнительным материалам. Я понял основную суть работы по созданию собственных компонентов, но описание задания крайне запутанное, зачем сначала обьявлять переменные, потом их значения передавать в другие переменные. Я уже закончил этот трэк, но мучаюсь с данным заданием поскольку не могу пройти тест(((\n\nВот моё решение:\n\nhttps://ru.hexlet.io/code_reviews/1498618\n\nУже все перепробовал, даже выставил статичные значения, все равно не проходит тесты.","topic_id":101608}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Создание пользовательского компонента","entity_url":null,"active":true}},{"id":103054,"title":"Добрый день,\n\nкакие значения внутренних отступов должны быть для комментария(.comment)??","plain_title":"Добрый день, какие значения внутренних отступов должны быть для комментария(.comment)?? ","creator":{"public_name":"Sasha Vatamanyuk","id":241312,"is_tutor":false},"comments":[{"creator":{"public_name":"Elena Gromova","id":548102,"is_tutor":true},"id":195437,"body":"**Sasha Vatamanyuk**, добрый день! 1.5rem 1.5rem. Добавим в описание","topic_id":103054}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Создание пользовательского компонента","entity_url":null,"active":true}},{"id":103018,"title":"Здравствуйте,\n\nтекст в футере компонента(comment-footer) выровнен по левой стороне по умолчанию, а в эталонном скриншоте текст выровнен по правой стороне, это ошибка/неточность ?? \n\n просто в условии к заданию об этом ничего не пишет и сложилось впечатление неполного технического задания..\\\nи ещё https://ibb.co/kH7X69X, что за зернистость в скриншоте сравнения и откуда она появилась?? смотрю актуальный скриншот и ожидаемый и я не вижу этой разницы – в следствии чего непонятно что неправильно и что нужно исправить для удачного прохождения теста.","plain_title":"Здравствуйте, текст в футере компонента(comment-footer) выровнен по левой стороне по умолчанию, а в эталонном скриншоте текст выровнен по правой стороне, это ошибка/неточность ?? просто в условии к заданию об этом ничего не пишет и сложилось впечатление неполного технического задания..\\ и ещё https://ibb.co/kH7X69X, что за зернистость в скриншоте сравнения и откуда она появилась?? смотрю актуальный скриншот и ожидаемый и я не вижу этой разницы – в следствии чего непонятно что неправильно и что нужно исправить для удачного прохождения теста. ","creator":{"public_name":"Sasha Vatamanyuk","id":241312,"is_tutor":false},"comments":[{"creator":{"public_name":"Elena Gromova","id":548102,"is_tutor":true},"id":195419,"body":"**Sasha Vatamanyuk**, здравствуйте! Это нормально смотреть в скриншот - comment-footer действительно должен быть выровнен по правой стороне. Добавила в описание итоговое изображение так же. По поводу второго - сходу сложно подсказать, рекомендую обратиться к наставнику, либо прислать свое решение в обсуждения для обсуждения с другими студентами.","topic_id":103018},{"creator":{"public_name":"Sasha Vatamanyuk","id":241312,"is_tutor":false},"id":195468,"body":"добрый день! напишите, пожалуйста, точные hex-значения цветов: .***comment-body*** в каждом *.comment*, если применять цвета с задания без переопределения – разброс ещё больше будет..","topic_id":103018},{"creator":{"public_name":"Sasha Vatamanyuk","id":241312,"is_tutor":false},"id":195461,"body":"здравствуйте! К сожалению, не имею возможности обратиться к наставнику..одно из крайних ревью : https://ru.hexlet.io/code_reviews/1561207 и вот так ситуация улучшилась, но всё равно есть разница в 21 пикселель и тест не проходит https://ibb.co/RHyw362 \\\nтакже измените, пожалуйста, у себя html на этот:\n```\n<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Комментарии</title>\n <script type=\"module\" src=\"./index.js\"></script>\n </head>\n <body>\n <div class=\"container\">\n <div class=\"comment comment-success\" style=\"background-color: #84e8ba;\">\n <div class=\"comment-header\" style=\"color: #000500;\">Комментарий от пользователя</div>\n <div class=\"comment-body\" style=\"color: #000c00;\">Это успешный комментарий. Он выделяется зеленым цветом.</div>\n <div class=\"comment-footer\" style=\"padding-top: 2px;\">10 минут назад</div>\n </div>\n <div class=\"comment comment-danger\" style=\"padding-top: 24px; background-color: #f9e3e6;\">\n <div class=\"comment-header\" style=\"color: #300507;\">Комментарий от администратора</div>\n <div class=\"comment-body\" style=\"color: #46131a;\">Это комментарий сигнализирующий об ошибке. Он выделяется красным цветом.</div>\n <div class=\"comment-footer\" style=\"padding-top: 2px;\">5 минут назад</div>\n </div>\n <div class=\"comment comment-info\" style=\"padding-top: 25px; background-color: #cdf3fc;\">\n <div class=\"comment-header\" style=\"color: #022612;\">Комментарий от модератора</div>\n <div class=\"comment-body\" style=\"color: #022612;\">Это информационный комментарий. Он выделяется синим цветом.</div>\n <div class=\"comment-footer\">2 минуты назад</div>\n </div>\n <div class=\"comment comment-warning\" style=\"padding-top: 27px; background-color: #fff4d6;\">\n <div class=\"comment-header\" style=\"color: #2c2100;\">Комментарий от гостя</div>\n <div class=\"comment-body\" style=\"color: #3b2f08;\">Это предупреждающий комментарий. Он выделяется желтым цветом.</div>\n <div class=\"comment-footer\">1 минуту назад</div>\n </div>\n </div>\n </body>\n</html>\n```","topic_id":103018}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Создание пользовательского компонента","entity_url":null,"active":true}},{"id":103186,"title":"Здравствуйте,\\\nудалось мне всё таки решить и разобраться почему тест не проходил и что за зернистость была на скриншоте сравнения – проблема была в разных цветах, поэтому методом тыка взял пипетку и там, где была зернистость брал значения цветов и изменял цвета в своём решении, возможно, кому-то поможет пройти тест без просмотра решения учителя.. \\\n Также в решении учителя исправьте 14 строчку на ***$comment-padding: map.get($spacers, 4)***, излишне и нелогично повторять те же значения, 18 строчку на ***$comment-box-shadow: 0 0.5rem 1rem rgb(0 0 0 / 15%)***, потому что линтер будет ругаться на неправильную нотацию (https://stylelint.io/user-guide/rules/color-function-notation/), ну и уже молчу о неполном техническом задании – о значениях внутренностей компонента в описании задания ни слова..\\\n Почему-то учитель в теории к уроку для бутстрап переменных использовал их специальный префикс, а в своём решении — нет..\\\nТакже моветон и нелогично переопределять стандартные значения переменных бутстрап spacer, spacers на те же значения.. Если в решении учителя 62 строчку не изменить на ***background-color: color.adjust($color, $lightness: 40%);***, 63 строчку не изменить на ***color: color.adjust($color, $lightness: -40%);*** – линтер опять же будет ругаться(нужно подключить ещё модуль sass:color)\n\nскажите, пожалуйста, в курсе фронтенд-разработчик тоже так много неточностей ?? проходил ли курс фронтенд-разработчик проверку и утверждение Кириллом??","plain_title":"Здравствуйте,\\ удалось мне всё таки решить и разобраться почему тест не проходил и что за зернистость была на скриншоте сравнения – проблема была в разных цветах, поэтому методом тыка взял пипетку и там, где была зернистость брал значения цветов и изменял цвета в своём решении, возможно, кому-то поможет пройти тест без просмотра решения учителя.. \\ Также в решении учителя исправьте 14 строчку на $comment-padding: map.get($spacers, 4), излишне и нелогично повторять те же значения, 18 строчку на $comment-box-shadow: 0 0.5rem 1rem rgb(0 0 0 / 15%), потому что линтер будет ругаться на неправильную нотацию (https://stylelint.io/user-guide/rules/color-function-notation/), ну и уже молчу о неполном техническом задании – о значениях внутренностей компонента в описании задания ни слова..\\ Почему-то учитель в теории к уроку для бутстрап переменных использовал их специальный префикс, а в своём решении — нет..\\ Также моветон и нелогично переопределять стандартные значения переменных бутстрап spacer, spacers на те же значения.. Если в решении учителя 62 строчку не изменить на background-color: color.adjust($color, $lightness: 40%);, 63 строчку не изменить на color: color.adjust($color, $lightness: -40%); – линтер опять же будет ругаться(нужно подключить ещё модуль sass:color) скажите, пожалуйста, в курсе фронтенд-разработчик тоже так много неточностей ?? проходил ли курс фронтенд-разработчик проверку и утверждение Кириллом?? ","creator":{"public_name":"Sasha Vatamanyuk","id":241312,"is_tutor":false},"comments":[{"creator":{"public_name":"Elena Gromova","id":548102,"is_tutor":true},"id":195715,"body":"**Sasha Vatamanyuk**, спасибо за ответ","topic_id":103186},{"creator":{"public_name":"Elena Gromova","id":548102,"is_tutor":true},"id":195606,"body":"**Sasha Vatamanyuk**, здравствуйте! \nОчень жаль, что возникли подобные трудности с этим заданием. Передам автору для улучшения. \nЕдинственное, уточните пожалуйста по поводу цветов. В описании указано, какие цвета использовать для каких комментариев. Какой именно информации по цветам вам не хватило для решения без пипетки?\nТакже будет полезно, если расскажете какой информации про внутренние значения комментариев не хватило в техническом задании","topic_id":103186},{"creator":{"public_name":"Sasha Vatamanyuk","id":241312,"is_tutor":false},"id":195609,"body":"Здравствуйте,\n\n> Единственное, уточните пожалуйста по поводу цветов. В описании указано, какие цвета использовать для каких комментариев.\n\nпересмотрел свой ход выполнения задания и по памяти и скриншотам могу сказать, что в первом комментарии фон зеленый почему-то не соответствовал(https://imgbb.com/kH7X69X) и была небольшая зернистость и так же немного зернистости было в comment-header, comment-body (https://imgbb.com/9tyJkXz, это уже после пипетки и разница была только 21 пиксель).\n\n> Также будет полезно, если расскажете какой информации про внутренние значения комментариев не хватило в техническом задании \n\nв ТЗ не было информации о ***margin-bottom*** для comment-header, comment-body \\\nтакже о ***line-height*** для comment-body \\\n***font-size*** , ***color*** для comment-footer\n","topic_id":103186}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Создание пользовательского компонента","entity_url":null,"active":true}},{"id":103331,"title":"Добрый день! Используя стандартные цвета без их переопределения зеленый фон очень отличается от эталонного, остальные при этом идеально совпадают. Так же цвета текста сильно сильно светлее (только серый у футера норм), хотя использовала -text-emphasis как в теории. И кстати в теории зеленый фон цитаты такой же как получается у меня, но он почему-то не подходит. Чтобы подогнать цвета взяла пипеткой точные цвета с эталонного скриншота и задала их. Задание все равно не проходит тесты, потому что не совпадает 24 пикселя (которые раскиданы именно по тексту боди и хедеров) и по ощущениям это просто артефакты изображения. При этом человеческий взгляд этих различий не видит вообще. Худшее задание из всех курсов.","plain_title":"Добрый день! Используя стандартные цвета без их переопределения зеленый фон очень отличается от эталонного, остальные при этом идеально совпадают. Так же цвета текста сильно сильно светлее (только серый у футера норм), хотя использовала -text-emphasis как в теории. И кстати в теории зеленый фон цитаты такой же как получается у меня, но он почему-то не подходит. Чтобы подогнать цвета взяла пипеткой точные цвета с эталонного скриншота и задала их. Задание все равно не проходит тесты, потому что не совпадает 24 пикселя (которые раскиданы именно по тексту боди и хедеров) и по ощущениям это просто артефакты изображения. При этом человеческий взгляд этих различий не видит вообще. Худшее задание из всех курсов. ","creator":{"public_name":"Hanna","id":840005,"is_tutor":false},"comments":[{"creator":{"public_name":"Elena Gromova","id":548102,"is_tutor":true},"id":195782,"body":"**Hanna**, добрый день! Спасибо за обратную связь. Подумаем, что можно сделать","topic_id":103331}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Создание пользовательского компонента","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":3379,"slug":"bootstrap_advanced_creating_a_component_exercise","name":null,"state":"active","kind":"exercise","language":"html","locale":"ru","has_web_view":true,"has_test_view":false,"reviewable":true,"readme":"Вам необходимо написать пользовательский компонент для отображения комментариев с использованием. Этот компонент должен поддерживать 4 типа комментариев: *успешный*, *предупреждающий*, *информационный* и *сигнализирующий об ошибке*. Для задания стилей разных типов комментариев используйте директиву `@each`.\n\n\n\n## scss/component/_comment.scss\n\n### Определите необходимые переменные:\n\n* Переменная для базового отступа: `1rem`.\n* Массив для отступов: `0`, `0.25`, `0.5`, `1`, `1.5` и `3` базовых отступа.\n* Цвета. Используйте стандартные цвета без переопределения: основной серый (`#343a40`), белый (`#ffffff`), светло-серый для текста (`#6c757d`), зелёный для успешных комментариев (`#28a745`), красный для опасных комментариев (`#dc3545`), голубой для информационных комментариев (`#17a2b8`), жёлтый для предупреждающих комментариев (`#ffc107`).\n* Радиус границ: `0.3rem`.\n* Тень блока: `0 0.5rem 1rem rgba(0, 0, 0, 0.15)`.\n\n### Создайте базовые стили для компонента комментариев:\n\n* Внешний отступ: 1.5rem 0.\n* Внутренний отступ: переменная `--comment-padding` (определяется из массива отступов, должно получиться 1.5rem 1.5rem).\n* Цвет текста: переменная `--comment-color` (основной серый).\n* Размер шрифта: переменная `--comment-font-size` (1rem).\n* Цвет фона: переменная `--comment-bg` (белый).\n* Радиус границ: переменная `--comment-border-radius` (0.3rem).\n* Тень блока: берем значение из `--comment-box-shadow`.\n* Левая граница: 5px solid, цвет берется из переменной `--comment-border-color` (по умолчанию `#007bff`).\n* Заголовок (comment-header): отступ снизу 0.75rem, шрифт bold.\n* Тело (comment-body): отступ снизу 0.75rem, межстрочное расстрояние 1.5.\n* Футер (comment-footer): размер шрифта 0.875rem, цвет текста светло-серый.\n","prepared_readme":"Вам необходимо написать пользовательский компонент для отображения комментариев с использованием. Этот компонент должен поддерживать 4 типа комментариев: *успешный*, *предупреждающий*, *информационный* и *сигнализирующий об ошибке*. Для задания стилей разных типов комментариев используйте директиву `@each`.\n\n\n\n## scss/component/_comment.scss\n\n### Определите необходимые переменные:\n\n* Переменная для базового отступа: `1rem`.\n* Массив для отступов: `0`, `0.25`, `0.5`, `1`, `1.5` и `3` базовых отступа.\n* Цвета. Используйте стандартные цвета без переопределения: основной серый (`#343a40`), белый (`#ffffff`), светло-серый для текста (`#6c757d`), зелёный для успешных комментариев (`#28a745`), красный для опасных комментариев (`#dc3545`), голубой для информационных комментариев (`#17a2b8`), жёлтый для предупреждающих комментариев (`#ffc107`).\n* Радиус границ: `0.3rem`.\n* Тень блока: `0 0.5rem 1rem rgba(0, 0, 0, 0.15)`.\n\n### Создайте базовые стили для компонента комментариев:\n\n* Внешний отступ: 1.5rem 0.\n* Внутренний отступ: переменная `--comment-padding` (определяется из массива отступов, должно получиться 1.5rem 1.5rem).\n* Цвет текста: переменная `--comment-color` (основной серый).\n* Размер шрифта: переменная `--comment-font-size` (1rem).\n* Цвет фона: переменная `--comment-bg` (белый).\n* Радиус границ: переменная `--comment-border-radius` (0.3rem).\n* Тень блока: берем значение из `--comment-box-shadow`.\n* Левая граница: 5px solid, цвет берется из переменной `--comment-border-color` (по умолчанию `#007bff`).\n* Заголовок (comment-header): отступ снизу 0.75rem, шрифт bold.\n* Тело (comment-body): отступ снизу 0.75rem, межстрочное расстрояние 1.5.\n* Футер (comment-footer): размер шрифта 0.875rem, цвет текста светло-серый.\n","has_solution":true,"entity_name":"Создание пользовательского компонента"},"units":[{"id":9185,"name":"theory","url":"/courses/bootstrap-advanced/lessons/creating-a-component/theory_unit"},{"id":11715,"name":"quiz","url":"/courses/bootstrap-advanced/lessons/creating-a-component/quiz_unit"},{"id":11762,"name":"exercise","url":"/courses/bootstrap-advanced/lessons/creating-a-component/exercise_unit"}],"links":[{"id":426100,"name":"Разработка пользовательских компонентов и утилит на Bootstrap [Хекслет]","url":"https://www.youtube.com/watch?v=EdnHwJw9xro"}],"ordered_units":[{"id":9185,"name":"theory","url":"/courses/bootstrap-advanced/lessons/creating-a-component/theory_unit"},{"id":11715,"name":"quiz","url":"/courses/bootstrap-advanced/lessons/creating-a-component/quiz_unit"},{"id":11762,"name":"exercise","url":"/courses/bootstrap-advanced/lessons/creating-a-component/exercise_unit"}],"id":4120,"slug":"creating-a-component","state":"approved","name":"Создание пользовательского компонента","course_order":400,"goal":"Разработаем пользовательский компонент цитат с использованием возможностей Bootstrap","self_study":"Создайте свой компонент и подключите его в проект. Скопируйте компонент из урока или придумайте свой. В шаблоне страницы добавьте верстку, которая будет использовать новый компонент. Убедитесь, что все отрисовывается как ожидалось.\n","theory_video_provider":null,"theory_video_uid":null,"theory":"При разработке нового сайта может не хватить стандартных компонентов. В этом случае можно пойти двумя путями:\n\n* Переработать дизайн и подстроиться под готовые компоненты фреймворка\n* Создать новые компоненты под свои нужды\n\nБлагодаря функциям, переменным и миксинам в Bootstrap создать новый компонент будет не сложно. Главное — понимать принципы работы Bootstrap и использовать все возможности.\n\nВ этом уроке мы создадим компонент цитаты. Он впишется в любой блог или новостное издание, когда нужно подчеркнуть мысль внутри статьи.\n\nВнешний вид компонента и его финальную верстку можно увидеть в [CodePen](https://codepen.io/hexlet/embed/Vwaqmxe?height=265&theme-id=light&default-tab=result&editable=true).\n\nПроцесс создания компонента разделим на несколько этапов:\n\n1. Определение структуры компонентов\n2. Верстка базовой структуры\n3. Верстка классов-оформления для придания компоненту различных цветовых схем\n\n## Структура компонента\n\nПеред созданием стилей нужно определить, какие элементы будут использоваться в компоненте, и где будут нужны модификаторы. Структурно можно разбить компонент на следующие составляющие:\n\n* Главная обертка. Компонент называется _citation_. Класс будет выбран соответствующий. Блоки цитат должны иметь различные варианты оформления в виде изменения фона и цвета текста. В качестве цветов будут использованы значения из переменной `$theme-colors`\n* Обертка для текста. Это блок с телом цитаты. Хоть данная обертка может не всегда быть нужна, как в случае с одной строкой текста, но при сложных цитатах желательно иметь свою обертку. В качестве класса можно выбрать класс _.citation-body_\n* Обертка для автора цитаты. Помимо имени автора она создает границу сверху для разделения частей компонента. Данная область может иметь и ссылки на цитаты, поэтому блок можно назвать _.citation-footer_ без привязки к автору\n\n## Верстка базовой структуры\n\nСоздадим одну базовую разметку для компонента:\n\n```html\n<blockquote class=\"citation citation-success\">\n <div class=\"citation-body\">Основная задача не в том, чтобы найти ошибку. А в том, чтобы найти условия, при которых код продолжает свою работу</div>\n <div class=\"citation-footer\">\n <cite>Кирилл Мокевнин</cite>\n </div>\n</blockquote>\n```\n\nСейчас выбрана семантичная верстка для цитат, но компонент должен работать при любой структуре. Это значит, что в стилях не должно быть привязки к тегам.\n\nВторой этап создания компонента — создание файловой структуры. В Bootstrap каждый компонент располагается в отдельном файле. Также необходимо создать отдельный файл, где будет подключение Bootstrap и самого компонента. Таким файлом будет _main.scss_.\n\nОбщий вид файловой структуры:\n\n```bash\nbootstrap-project/\n├── app/\n│ ├── scss/\n│ │ ├── main.scss\n│ │ └── component/\n│ │ └── _citation.scss\n| └── index.pug\n├── build/\n├── node_modules/\n├── package-lock.json\n├── package.json\n└── gulpfile.js\n```\n\nВ файле main.scss_ подключим Bootstrap и компонент Citation:\n\n```scss\n@import \"../../node_modules/bootstrap/scss/bootstrap\";\n@import \"./component/citation\";\n```\n\nТеперь можно перейти к верстке блока цитат. В Bootstrap существуют свои стили для цитат и для этого есть четыре переменные в файле __variables.scss_. Их можно использовать во время разработки компонента. Это поможет сохранить преемственность нашего компонента и стандартных стилей цитат.\n\n```scss\n$blockquote-margin-y: $spacer !default;\n$blockquote-font-size: $font-size-base * 1.25 !default;\n$blockquote-footer-color: $gray-600 !default;\n$blockquote-footer-font-size: $small-font-size !default;\n```\n\nДля создания отступов воспользуемся массивом `$spacers`, который имеет следующую структуру:\n\n```scss\n$spacer: 1rem !default;\n$spacers: (\n 0: 0,\n 1: $spacer * .25,\n 2: $spacer * .5,\n 3: $spacer,\n 4: $spacer * 1.5,\n 5: $spacer * 3,\n) !default;\n```\n\nВсе эти значения помогут создать структуру блока _.citation_:\n\n```scss\n@use \"sass:map\";\n\n$citation-padding: map.get($spacers, 5) map.get($spacers, 4) map.get($spacers, 3);\n$citation-color: $blockquote-footer-color;\n$citation-bg: transparent;\n$citation-font-size: $blockquote-font-size;\n$citation-border-radius: $border-radius;\n\n.citation {\n --#{$prefix}citation-padding: #{$citation-padding};\n --#{$prefix}citation-color: #{$citation-color};\n --#{$prefix}citation-bg: #{$citation-bg};\n --#{$prefix}citation-font-size: #{$citation-font-size};\n --#{$prefix}citation-border-radius: #{$citation-border-radius};\n\n position: relative;\n\n display: block;\n\n margin: 0;\n padding: var(--#{$prefix}citation-padding);\n\n color: var(--#{$prefix}citation-color);\n font-size: var(--#{$prefix}citation-font-size);\n\n background-color: var(--#{$prefix}citation-bg);\n\n border-radius: var(--#{$prefix}citation-border-radius);\n @include box-shadow($box-shadow);\n}\n```\n\nВ этом примере основные свойства, такие как отступы, цвет, размеры вынесены в CSS-переменные. Этот подход появился в последних версиях Bootstrap и используется для создания тем. Главным триггером появления такого подхода стала возможность использования светлой и темной темы.\n\n### Создание кавычек в компоненте\n\nДля создания кавычек воспользуемся псевдоэлементом `::before`, который создаст новый элемент сразу после открытия тега с классом `citation`. Внутри псевдоэлемента используем абсолютно позиционированный элемент со шрифтом «Times New Roman». Для этого создадим переменную `$font-family-serif` в файле _main.scss_:\n\n```scss\n$enable-shadows: true; // включаем миксин box-shadow\n$font-family-serif: \"Times New Roman\", Times, serif;\n\n@import \"../node_modules/bootstrap/scss/bootstrap\";\n@import \"./citation\";\n```\n\nТеперь все готово для создания псевдоэлемента:\n\n```scss\n@use \"sass:map\";\n\n$citation-padding: map.get($spacers, 5) map.get($spacers, 4) map.get($spacers, 3);\n$citation-color: $blockquote-footer-color;\n$citation-bg: transparent;\n$citation-font-size: $blockquote-font-size;\n$citation-border-radius: $border-radius;\n\n$citation-before-top: map.get($spacers, 4) * -1;\n$citation-before-font-size: map.get($display-font-sizes, 1);\n$citation-before-font-family: $font-family-serif;\n\n.citation {\n --#{$prefix}citation-padding: #{$citation-padding};\n --#{$prefix}citation-color: #{$citation-color};\n --#{$prefix}citation-bg: #{$citation-bg};\n --#{$prefix}citation-font-size: #{$citation-font-size};\n --#{$prefix}citation-border-radius: #{$citation-border-radius};\n --#{$prefix}citation-before-top: #{$citation-before-top};\n --#{$prefix}citation-before-font-size: #{$citation-before-font-size};\n --#{$prefix}citation-before-font-family: #{$citation-before-font-family};\n\n position: relative;\n\n display: block;\n\n margin: 0;\n padding: var(--#{$prefix}citation-padding);\n\n color: var(--#{$prefix}citation-color);\n font-size: var(--#{$prefix}citation-font-size);\n\n background-color: var(--#{$prefix}citation-bg);\n\n border-radius: var(--#{$prefix}citation-border-radius);\n @include box-shadow($box-shadow);\n\n &::before {\n position: absolute;\n top: var(--#{$prefix}citation-before-top);\n\n font-size: var(--#{$prefix}citation-before-font-size);\n font-family: var(--#{$prefix}citation-before-font-family);\n\n content: \"“\";\n }\n}\n```\n\n### Создание футера компонента\n\nОсновная верстка контейнера для цитат готова. Теперь можно перейти к созданию стилей для `.citation-footer`. Можно обратить внимание, что пропущены стили для `.citation-body`. Это сделано по причине того, что все необходимые стили будут унаследованы от родительского блока `.citation`. В будущем эту обертку можно использовать для указания классов-утилит Bootstrap или создания новых модификаторов.\n\nЗадача футера — указать наклонное начертание шрифта и установить границу сверху вместе со всеми отступами. Так как автор цитаты всегда указывается шрифтом меньшим, чем сама цитата, воспользуемся переменной `$blockquote-footer-font-size`:\n\n```scss\n@use \"sass:map\";\n\n$citation-padding: map.get($spacers, 5) map.get($spacers, 4) map.get($spacers, 3);\n$citation-color: $blockquote-footer-color;\n$citation-bg: transparent;\n$citation-font-size: $blockquote-font-size;\n$citation-border-radius: $border-radius;\n\n$citation-before-top: map.get($spacers, 4) * -1;\n$citation-before-font-size: map.get($display-font-sizes, 1);\n$citation-before-font-family: $font-family-serif;\n\n$citation-footer-margin: $spacer 0 0;\n$citation-footer-padding: map.get($spacers, 2) 0 0;\n$citation-footer-font-size: $blockquote-footer-font-size;\n$citation-footer-border: 1px solid;\n\n.citation {\n --#{$prefix}citation-padding: #{$citation-padding};\n --#{$prefix}citation-color: #{$citation-color};\n --#{$prefix}citation-bg: #{$citation-bg};\n --#{$prefix}citation-font-size: #{$citation-font-size};\n --#{$prefix}citation-border-radius: #{$citation-border-radius};\n --#{$prefix}citation-before-top: #{$citation-before-top};\n --#{$prefix}citation-before-font-size: #{$citation-before-font-size};\n --#{$prefix}citation-before-font-family: #{$citation-before-font-family};\n --#{$prefix}citation-footer-margin: #{$citation-footer-margin};\n --#{$prefix}citation-footer-padding: #{$citation-footer-padding};\n --#{$prefix}citation-footer-font-size: #{$citation-footer-font-size};\n --#{$prefix}citation-footer-border: #{$citation-footer-border};\n\n position: relative;\n\n display: block;\n\n margin: 0;\n padding: var(--#{$prefix}citation-padding);\n\n color: var(--#{$prefix}citation-color);\n font-size: var(--#{$prefix}citation-font-size);\n\n background-color: var(--#{$prefix}citation-bg);\n\n border-radius: var(--#{$prefix}citation-border-radius);\n @include box-shadow($box-shadow);\n\n &::before {\n position: absolute;\n top: var(--#{$prefix}citation-before-top);\n\n font-size: var(--#{$prefix}citation-before-font-size);\n font-family: var(--#{$prefix}citation-before-font-family);\n\n content: \"“\";\n }\n\n & > &-footer {\n margin: var(--#{$prefix}citation-footer-margin);\n padding: var(--#{$prefix}citation-footer-padding);\n\n font-size: var(--#{$prefix}citation-footer-font-size);\n font-style: italic;\n\n border-top: var(--#{$prefix}citation-footer-border);\n }\n}\n```\n\nОсновная верстка компонента закончена. Если воспользоваться созданным компонентом, то получим [следующий блок](https://codepen.io/hexlet/embed/XWdopOj?height=353&theme-id=light&default-tab=result&editable=true).\n\n## Создание классов-модификаторов цветового оформления\n\nПоследняя задача — создание классов-модификаторов, которые добавят фоновый цвет для цитат. Помимо этого эти классы должны переопределить цвет текста на белый для создания правильного контраста.\n\nС массивом `$theme-colors` возможно пройтись по всем цветам и добавить свойство `background-color`. Массив `$theme-colors` имеет следующие значения:\n\n```scss\n$theme-colors: (\n \"primary\": $primary,\n \"secondary\": $secondary,\n \"success\": $success,\n \"info\": $info,\n \"warning\": $warning,\n \"danger\": $danger,\n \"light\": $light,\n \"dark\": $dark\n) !default;\n```\n\nТак как это ассоциативный массив, то для его перебора можно воспользоваться функцией `@each`:\n\n```scss\n@each $color, $value in $theme-colors {\n .citation-#{$color} {\n --#{$prefix}citation-color: var(--#{$prefix}#{$color}-text-emphasis);\n --#{$prefix}citation-bg: var(--#{$prefix}#{$color}-bg-subtle);\n }\n}\n```\n\nЗдесь мы меняем только заданные переменные для замены цвета фона и текста. Для «красоты» были использованы не просто цвета, а их специальные версии, где фон осветляется, а текст становится темнее. Такой же подход используется в компоненте Alert.\n\nВ результате компиляции появятся следующие стили:\n\n```css\n.citation-primary {\n --bs-citation-color: var(--bs-primary-text-emphasis);\n --bs-citation-bg: var(--bs-primary-bg-subtle);\n}\n\n.citation-secondary {\n --bs-citation-color: var(--bs-secondary-text-emphasis);\n --bs-citation-bg: var(--bs-secondary-bg-subtle);\n}\n\n.citation-success {\n --bs-citation-color: var(--bs-success-text-emphasis);\n --bs-citation-bg: var(--bs-success-bg-subtle);\n}\n\n.citation-info {\n --bs-citation-color: var(--bs-info-text-emphasis);\n --bs-citation-bg: var(--bs-info-bg-subtle);\n}\n\n.citation-warning {\n --bs-citation-color: var(--bs-warning-text-emphasis);\n --bs-citation-bg: var(--bs-warning-bg-subtle);\n}\n\n.citation-danger {\n --bs-citation-color: var(--bs-danger-text-emphasis);\n --bs-citation-bg: var(--bs-danger-bg-subtle);\n}\n\n.citation-light {\n --bs-citation-color: var(--bs-light-text-emphasis);\n --bs-citation-bg: var(--bs-light-bg-subtle);\n}\n\n.citation-dark {\n --bs-citation-color: var(--bs-dark-text-emphasis);\n --bs-citation-bg: var(--bs-dark-bg-subtle);\n}\n```\n\nТак как все цвета берутся из массива, то смена цветовой темы всего проекта приведет и к смене цветов этого компонента.\n\nРазработка компонента закончена. Его всегда можно доработать, создав дополнительные классы-модификаторы или дополнительно адаптировав под различные разрешения экрана. Принцип создания этих дополнений не будет отличаться от того, что было рассмотрено в этом уроке.\n\n## Выводы\n\nВ этом уроке мы изучили, как создаются пользовательские компоненты с помощью Bootstrap. Чтобы связать новый компонент с «внутренностями» фреймворка следует использовать:\n\n* Переменные Bootstrap, которые находятся в файле _\\_variables.scss_\n* Функции и миксины фреймворка\n* CSS-переменные\n\nПри таком подходе смена настроек фреймворка или его обновление затронет и ваши созданные компоненты. Это поможет держать систему в однообразии.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":9327,"name":"theory","url":"/courses/bootstrap-advanced/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":9327,"name":"theory","url":"/courses/bootstrap-advanced/lessons/intro/theory_unit"}],"id":4175,"slug":"intro","state":"approved","name":"Введение","course_order":100,"goal":"Познакомимся с планом курса по разработке на Bootstrap","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"В этом курсе вы продолжите изучать фреймворк Bootstrap. В этот раз речь пойдет про более глубокое погружение. Вы уже умеете использовать готовые утилиты и компоненты, а на этом курсе научитесь создавать свои.\n\nВ курсе мы рассмотрим:\n\n* Подключение Bootstrap к проекту в виде npm-пакета\n* Создание своих компонентов на основе готовых переменных и миксинов\n* Создание утилит при помощи Utilities API\n* Структуру цветовых схем Bootstrap: откуда берутся цвета и как формируется вся цветовая схема\n* Темную тему в Bootstrap\n\n## Практика\n\nВ большинстве уроков есть практики с возможностью перехода в веб-доступ. Старайтесь не только выполнять задания, но и экспериментировать с кодом. Так вы подробнее разберетесь во всех возможностях, которые узнали в уроке.\n\nВ течение курса вы будете встречать самостоятельные задания. При необходимости вы можете попрактиковаться в задачах, представленных в заданиях. Также в них будут примеры кода, в которые можно подглядывать или просто изучать.\n\n## Документация\n\nУ фреймворка Bootstrap одна из лучших документаций в своем роде. В ней вы можете подробно изучить все возможные компоненты и утилиты. Документация оперативно обновляется при выходе каждой новой версии фреймворка. Советуем держать ее всегда при себе.\n\n## GitHub\n\nПомимо документации, Bootstrap можно найти на сервисе GitHub. Фреймворк открыт для пользователей, поэтому вы можете изучить код и обнаружить множество функций и миксинов. Все это пригодится, когда вы начнете создавать свои компоненты.\n"},"id":369,"slug":"bootstrap-advanced","challenges_count":3,"name":"Bootstrap 5: Продвинутый уровень","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы глубже погрузитесь в работу с фреймворком Bootstrap. Вы узнаете о том, как создавать свои компоненты и утилиты. В итоге вы научитесь профессионально использовать фреймворк и его возможности по созданию новых компонентов. Разберетесь, из чего состоит Bootstrap и как подключать только нужные его части в проекте.","kind":"advanced","updated_at":"2026-01-23T16:12:02.833Z","language":"html","duration_cache":22140,"skills":["Устанавливать Bootstrap в свой проект","Создавать свои компоненты","Добавлять, обновлять и изменять встроенные утилиты"],"keywords":[],"lessons_count":7,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTAzNzEsInB1ciI6ImJsb2JfaWQifX0=--27461ae3a86fd2eab681cb3c811d4a3c7290d6a7/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJqcGciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--39ba06fa99226096df9fc6bb31f84e1d29ea98e9/image.png"},"recommendedLandings":[{"stack":{"id":19,"slug":"layout-designer","title":"Профессиональная верстка","audience":"for_beginners","start_type":"anytime","pricing_model":"purchase","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":1700,"duration_in_months":5},"id":26,"slug":"professional-layout","title":"Профессиональная верстка","subtitle":"Навык адаптивной вёрстки с современными подходами для корректного отображения сайтов на любых устройствах и разрешениях","subtitle_for_lists":"Адаптивная вёрстка для отображения на любых устройствах ","locale":"ru","current":true,"duration_in_months_text":"5 месяцев","stack_slug":"layout-designer","price_text":"от 3 900 ₽","duration_text":"5 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDAzNCwicHVyIjoiYmxvYl9pZCJ9fQ==--ba516ea9573bdfcd1d21e2aa0fff8818561828f2/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Typing-bro.png"},{"stack":{"id":42,"slug":"layout-designer-bootstrap","title":"Bootstrap","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":400,"duration_in_months":2},"id":72,"slug":"layout-designer-bootstrap","title":"Bootstrap","subtitle":"Навык работы с SASS, Bootstrap 5 и Gulp для ускорения, упрощения и оптимизации процесса вёрстки и разработки сайтов","subtitle_for_lists":"Изучите навык для работы с Bootstrap 5, SASS и Gulp для быстрой вёрстки","locale":"ru","current":true,"duration_in_months_text":"2 месяца","stack_slug":"layout-designer-bootstrap","price_text":"от 3 900 ₽","duration_text":"2 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDA0MCwicHVyIjoiYmxvYl9pZCJ9fQ==--137ae65fe79f41b3d5270b8a3b65b38b1621c65b/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Code%20typing-bro.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/bootstrap-advanced/lessons/creating-a-component/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Bootstrap 5: Продвинутый уровень</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Создание пользовательского компонента</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Создание пользовательского компонента","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"Bootstrap 5: Продвинутый уровень"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>При разработке нового сайта может не хватить стандартных компонентов. В этом случае можно пойти двумя путями:</p>
<ul>
<li>Переработать дизайн и подстроиться под готовые компоненты фреймворка</li>
<li>Создать новые компоненты под свои нужды</li>
</ul>
<p>Благодаря функциям, переменным и миксинам в Bootstrap создать новый компонент будет не сложно. Главное — понимать принципы работы Bootstrap и использовать все возможности.</p>
<p>В этом уроке мы создадим компонент цитаты. Он впишется в любой блог или новостное издание, когда нужно подчеркнуть мысль внутри статьи.</p>
<p>Внешний вид компонента и его финальную верстку можно увидеть в <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://codepen.io/hexlet/embed/Vwaqmxe?height=265&theme-id=light&default-tab=result&editable=true" rel="noopener noreferrer" target="_blank">CodePen</a>.</p>
<p>Процесс создания компонента разделим на несколько этапов:</p>
<ol>
<li>Определение структуры компонентов</li>
<li>Верстка базовой структуры</li>
<li>Верстка классов-оформления для придания компоненту различных цветовых схем</li>
</ol>
<h2 id="heading-2-1">Структура компонента</h2>
<p>Перед созданием стилей нужно определить, какие элементы будут использоваться в компоненте, и где будут нужны модификаторы. Структурно можно разбить компонент на следующие составляющие:</p>
<ul>
<li>Главная обертка. Компонент называется <em>citation</em>. Класс будет выбран соответствующий. Блоки цитат должны иметь различные варианты оформления в виде изменения фона и цвета текста. В качестве цветов будут использованы значения из переменной <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$theme-colors</code></li>
<li>Обертка для текста. Это блок с телом цитаты. Хоть данная обертка может не всегда быть нужна, как в случае с одной строкой текста, но при сложных цитатах желательно иметь свою обертку. В качестве класса можно выбрать класс <em>.citation-body</em></li>
<li>Обертка для автора цитаты. Помимо имени автора она создает границу сверху для разделения частей компонента. Данная область может иметь и ссылки на цитаты, поэтому блок можно назвать <em>.citation-footer</em> без привязки к автору</li>
</ul>
<h2 id="heading-2-2">Верстка базовой структуры</h2>
<p>Создадим одну базовую разметку для компонента:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><blockquote class="citation citation-success">
<div class="citation-body">Основная задача не в том, чтобы найти ошибку. А в том, чтобы найти условия, при которых код продолжает свою работу</div>
<div class="citation-footer">
<cite>Кирилл Мокевнин</cite>
</div>
</blockquote></code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Сейчас выбрана семантичная верстка для цитат, но компонент должен работать при любой структуре. Это значит, что в стилях не должно быть привязки к тегам.</p>
<p>Второй этап создания компонента — создание файловой структуры. В Bootstrap каждый компонент располагается в отдельном файле. Также необходимо создать отдельный файл, где будет подключение Bootstrap и самого компонента. Таким файлом будет <em>main.scss</em>.</p>
<p>Общий вид файловой структуры:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">bootstrap-project/
├── app/
│ ├── scss/
│ │ ├── main.scss
│ │ └── component/
│ │ └── _citation.scss
| └── index.pug
├── build/
├── node_modules/
├── package-lock.json
├── package.json
└── gulpfile.js</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>В файле main.scss_ подключим Bootstrap и компонент Citation:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">@import "../../node_modules/bootstrap/scss/bootstrap";
@import "./component/citation";</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Теперь можно перейти к верстке блока цитат. В Bootstrap существуют свои стили для цитат и для этого есть четыре переменные в файле _<em>variables.scss</em>. Их можно использовать во время разработки компонента. Это поможет сохранить преемственность нашего компонента и стандартных стилей цитат.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$blockquote-margin-y: $spacer !default;
$blockquote-font-size: $font-size-base * 1.25 !default;
$blockquote-footer-color: $gray-600 !default;
$blockquote-footer-font-size: $small-font-size !default;</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Для создания отступов воспользуемся массивом <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$spacers</code>, который имеет следующую структуру:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$spacer: 1rem !default;
$spacers: (
0: 0,
1: $spacer * .25,
2: $spacer * .5,
3: $spacer,
4: $spacer * 1.5,
5: $spacer * 3,
) !default;</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Все эти значения помогут создать структуру блока <em>.citation</em>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">@use "sass:map";
$citation-padding: map.get($spacers, 5) map.get($spacers, 4) map.get($spacers, 3);
$citation-color: $blockquote-footer-color;
$citation-bg: transparent;
$citation-font-size: $blockquote-font-size;
$citation-border-radius: $border-radius;
.citation {
--#{$prefix}citation-padding: #{$citation-padding};
--#{$prefix}citation-color: #{$citation-color};
--#{$prefix}citation-bg: #{$citation-bg};
--#{$prefix}citation-font-size: #{$citation-font-size};
--#{$prefix}citation-border-radius: #{$citation-border-radius};
position: relative;
display: block;
margin: 0;
padding: var(--#{$prefix}citation-padding);
color: var(--#{$prefix}citation-color);
font-size: var(--#{$prefix}citation-font-size);
background-color: var(--#{$prefix}citation-bg);
border-radius: var(--#{$prefix}citation-border-radius);
@include box-shadow($box-shadow);
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>В этом примере основные свойства, такие как отступы, цвет, размеры вынесены в CSS-переменные. Этот подход появился в последних версиях Bootstrap и используется для создания тем. Главным триггером появления такого подхода стала возможность использования светлой и темной темы.</p>
<h3 id="heading-3-3">Создание кавычек в компоненте</h3>
<p>Для создания кавычек воспользуемся псевдоэлементом <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">::before</code>, который создаст новый элемент сразу после открытия тега с классом <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">citation</code>. Внутри псевдоэлемента используем абсолютно позиционированный элемент со шрифтом «Times New Roman». Для этого создадим переменную <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$font-family-serif</code> в файле <em>main.scss</em>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$enable-shadows: true; // включаем миксин box-shadow
$font-family-serif: "Times New Roman", Times, serif;
@import "../node_modules/bootstrap/scss/bootstrap";
@import "./citation";</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Теперь все готово для создания псевдоэлемента:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">@use "sass:map";
$citation-padding: map.get($spacers, 5) map.get($spacers, 4) map.get($spacers, 3);
$citation-color: $blockquote-footer-color;
$citation-bg: transparent;
$citation-font-size: $blockquote-font-size;
$citation-border-radius: $border-radius;
$citation-before-top: map.get($spacers, 4) * -1;
$citation-before-font-size: map.get($display-font-sizes, 1);
$citation-before-font-family: $font-family-serif;
.citation {
--#{$prefix}citation-padding: #{$citation-padding};
--#{$prefix}citation-color: #{$citation-color};
--#{$prefix}citation-bg: #{$citation-bg};
--#{$prefix}citation-font-size: #{$citation-font-size};
--#{$prefix}citation-border-radius: #{$citation-border-radius};
--#{$prefix}citation-before-top: #{$citation-before-top};
--#{$prefix}citation-before-font-size: #{$citation-before-font-size};
--#{$prefix}citation-before-font-family: #{$citation-before-font-family};
position: relative;
display: block;
margin: 0;
padding: var(--#{$prefix}citation-padding);
color: var(--#{$prefix}citation-color);
font-size: var(--#{$prefix}citation-font-size);
background-color: var(--#{$prefix}citation-bg);
border-radius: var(--#{$prefix}citation-border-radius);
@include box-shadow($box-shadow);
&::before {
position: absolute;
top: var(--#{$prefix}citation-before-top);
font-size: var(--#{$prefix}citation-before-font-size);
font-family: var(--#{$prefix}citation-before-font-family);
content: "“";
}
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<h3 id="heading-3-4">Создание футера компонента</h3>
<p>Основная верстка контейнера для цитат готова. Теперь можно перейти к созданию стилей для <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">.citation-footer</code>. Можно обратить внимание, что пропущены стили для <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">.citation-body</code>. Это сделано по причине того, что все необходимые стили будут унаследованы от родительского блока <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">.citation</code>. В будущем эту обертку можно использовать для указания классов-утилит Bootstrap или создания новых модификаторов.</p>
<p>Задача футера — указать наклонное начертание шрифта и установить границу сверху вместе со всеми отступами. Так как автор цитаты всегда указывается шрифтом меньшим, чем сама цитата, воспользуемся переменной <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$blockquote-footer-font-size</code>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">@use "sass:map";
$citation-padding: map.get($spacers, 5) map.get($spacers, 4) map.get($spacers, 3);
$citation-color: $blockquote-footer-color;
$citation-bg: transparent;
$citation-font-size: $blockquote-font-size;
$citation-border-radius: $border-radius;
$citation-before-top: map.get($spacers, 4) * -1;
$citation-before-font-size: map.get($display-font-sizes, 1);
$citation-before-font-family: $font-family-serif;
$citation-footer-margin: $spacer 0 0;
$citation-footer-padding: map.get($spacers, 2) 0 0;
$citation-footer-font-size: $blockquote-footer-font-size;
$citation-footer-border: 1px solid;
.citation {
--#{$prefix}citation-padding: #{$citation-padding};
--#{$prefix}citation-color: #{$citation-color};
--#{$prefix}citation-bg: #{$citation-bg};
--#{$prefix}citation-font-size: #{$citation-font-size};
--#{$prefix}citation-border-radius: #{$citation-border-radius};
--#{$prefix}citation-before-top: #{$citation-before-top};
--#{$prefix}citation-before-font-size: #{$citation-before-font-size};
--#{$prefix}citation-before-font-family: #{$citation-before-font-family};
--#{$prefix}citation-footer-margin: #{$citation-footer-margin};
--#{$prefix}citation-footer-padding: #{$citation-footer-padding};
--#{$prefix}citation-footer-font-size: #{$citation-footer-font-size};
--#{$prefix}citation-footer-border: #{$citation-footer-border};
position: relative;
display: block;
margin: 0;
padding: var(--#{$prefix}citation-padding);
color: var(--#{$prefix}citation-color);
font-size: var(--#{$prefix}citation-font-size);
background-color: var(--#{$prefix}citation-bg);
border-radius: var(--#{$prefix}citation-border-radius);
@include box-shadow($box-shadow);
&::before {
position: absolute;
top: var(--#{$prefix}citation-before-top);
font-size: var(--#{$prefix}citation-before-font-size);
font-family: var(--#{$prefix}citation-before-font-family);
content: "“";
}
& > &-footer {
margin: var(--#{$prefix}citation-footer-margin);
padding: var(--#{$prefix}citation-footer-padding);
font-size: var(--#{$prefix}citation-footer-font-size);
font-style: italic;
border-top: var(--#{$prefix}citation-footer-border);
}
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Основная верстка компонента закончена. Если воспользоваться созданным компонентом, то получим <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://codepen.io/hexlet/embed/XWdopOj?height=353&theme-id=light&default-tab=result&editable=true" rel="noopener noreferrer" target="_blank">следующий блок</a>.</p>
<h2 id="heading-2-5">Создание классов-модификаторов цветового оформления</h2>
<p>Последняя задача — создание классов-модификаторов, которые добавят фоновый цвет для цитат. Помимо этого эти классы должны переопределить цвет текста на белый для создания правильного контраста.</p>
<p>С массивом <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$theme-colors</code> возможно пройтись по всем цветам и добавить свойство <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">background-color</code>. Массив <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$theme-colors</code> имеет следующие значения:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">$theme-colors: (
"primary": $primary,
"secondary": $secondary,
"success": $success,
"info": $info,
"warning": $warning,
"danger": $danger,
"light": $light,
"dark": $dark
) !default;</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Так как это ассоциативный массив, то для его перебора можно воспользоваться функцией <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">@each</code>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">@each $color, $value in $theme-colors {
.citation-#{$color} {
--#{$prefix}citation-color: var(--#{$prefix}#{$color}-text-emphasis);
--#{$prefix}citation-bg: var(--#{$prefix}#{$color}-bg-subtle);
}
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Здесь мы меняем только заданные переменные для замены цвета фона и текста. Для «красоты» были использованы не просто цвета, а их специальные версии, где фон осветляется, а текст становится темнее. Такой же подход используется в компоненте Alert.</p>
<p>В результате компиляции появятся следующие стили:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">.citation-primary {
--bs-citation-color: var(--bs-primary-text-emphasis);
--bs-citation-bg: var(--bs-primary-bg-subtle);
}
.citation-secondary {
--bs-citation-color: var(--bs-secondary-text-emphasis);
--bs-citation-bg: var(--bs-secondary-bg-subtle);
}
.citation-success {
--bs-citation-color: var(--bs-success-text-emphasis);
--bs-citation-bg: var(--bs-success-bg-subtle);
}
.citation-info {
--bs-citation-color: var(--bs-info-text-emphasis);
--bs-citation-bg: var(--bs-info-bg-subtle);
}
.citation-warning {
--bs-citation-color: var(--bs-warning-text-emphasis);
--bs-citation-bg: var(--bs-warning-bg-subtle);
}
.citation-danger {
--bs-citation-color: var(--bs-danger-text-emphasis);
--bs-citation-bg: var(--bs-danger-bg-subtle);
}
.citation-light {
--bs-citation-color: var(--bs-light-text-emphasis);
--bs-citation-bg: var(--bs-light-bg-subtle);
}
.citation-dark {
--bs-citation-color: var(--bs-dark-text-emphasis);
--bs-citation-bg: var(--bs-dark-bg-subtle);
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Так как все цвета берутся из массива, то смена цветовой темы всего проекта приведет и к смене цветов этого компонента.</p>
<p>Разработка компонента закончена. Его всегда можно доработать, создав дополнительные классы-модификаторы или дополнительно адаптировав под различные разрешения экрана. Принцип создания этих дополнений не будет отличаться от того, что было рассмотрено в этом уроке.</p>
<h2 id="heading-2-6">Выводы</h2>
<p>В этом уроке мы изучили, как создаются пользовательские компоненты с помощью Bootstrap. Чтобы связать новый компонент с «внутренностями» фреймворка следует использовать:</p>
<ul>
<li>Переменные Bootstrap, которые находятся в файле <em>_variables.scss</em></li>
<li>Функции и миксины фреймворка</li>
<li>CSS-переменные</li>
</ul>
<p>При таком подходе смена настроек фреймворка или его обновление затронет и ваши созданные компоненты. Это поможет держать систему в однообразии.</p></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/professional-layout?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">5 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Профессиональная верстка</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Адаптивная вёрстка для отображения на любых устройствах </p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDAzNCwicHVyIjoiYmxvYl9pZCJ9fQ==--ba516ea9573bdfcd1d21e2aa0fff8818561828f2/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Typing-bro.png" alt="Профессиональная верстка" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/layout-designer-bootstrap?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">2 месяца</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Bootstrap</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите навык для работы с Bootstrap 5, SASS и Gulp для быстрой вёрстки</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NDA0MCwicHVyIjoiYmxvYl9pZCJ9fQ==--137ae65fe79f41b3d5270b8a3b65b38b1621c65b/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Code%20typing-bro.png" alt="Bootstrap" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/bootstrap-advanced/lessons/creating-a-component/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 7</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/bootstrap-advanced/lessons/creating-a-component/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>