3061
Шлёпаем формы и красим кнопки... Блог про фронтенд — просто, понятно и с юмором. Всех обнял, приподнял, пошел пилить для вас годноту) Связь: @advertos
Анимированный гамбургер для меню
Давайте сегодня разберем, как сделать простую, но доступную и красивую кнопку для мобильного меню.
HTML:
<button type="button" class="hamburger" aria-label="Открыть меню" aria-expanded="false">
<span class="hamburger__line"></span>
<span class="hamburger__line"></span>
<span class="hamburger__line"></span>
</button>
<button>, а не <div> для семантики. ARIA-атрибуты помогают скринридерам понять назначение кнопки..hamburger {
display: flex;
flex-direction: column;
gap: 6px;
background: transparent;
border: none;
}
.hamburger__line {
width: 28px;
height: 3px;
background: #000;
transition: transform 0.3s, opacity 0.2s;
}.hamburger.active .hamburger__line:nth-child(1) {
transform: translateY(9px) rotate(45deg);
}
.hamburger.active .hamburger__line:nth-child(2) {
opacity: 0;
}
.hamburger.active .hamburger__line:nth-child(3) {
transform: translateY(-9px) rotate(-45deg);
}const hamburger = document.querySelector('.hamburger');
let isOpen = false;
hamburger.addEventListener('click', () => {
isOpen = !isOpen;
hamburger.classList.toggle('active');
hamburger.setAttribute('aria-expanded', isOpen);
hamburger.setAttribute('aria-label', isOpen ? 'Закрыть меню' : 'Открыть меню');
});
Анимация ::marker в списке
Нашел интересный способ анимации маркеров списка. Разберем, как сделать, чтобы при наведении на элемент списка плавно появлялся наш кастомный маркер.
HTML:
<ul>
<li>Первый</li>
<li>Второй</li>
<li>Третий</li>
<li>Четвертый</li>
<li>Пятый</li>
</ul>
li::marker {
content: "🔥 ";
color: transparent;
transition: color 200ms;
}
li:hover::marker {
color: black;
}content: "🔥 "; — ставим наш кастомный маркер.color: transparent; — скрываем его, делая прозрачным.transition: color 300ms; — обеспечиваем плавное появление.color: black; — при ховере, меняем цвет и делаем маркер видимым.::marker браузеры по умолчанию используют текстовый режим рендеринга emoji. Поэтому color: black сработает, и наш маркер будет виден, даже если сам emoji цветной)
Заголовок с анимированным градиентом
Мы уже разбирали, как сделать заголовок с градиентом, давайте теперь разберем, как сделать заголовок с анимированным градиентом.
HTML:
<h1>Формошлёп</h1>
h1 {
background: linear-gradient(90deg, #00f5a0, #00d2ff, #7b61ff, #ff4d6d);
background-size: 200% 100%;
background-clip: text;
color: transparent;
animation: glide 2s linear infinite alternate;
}
@keyframes glide {
0% { background-position: 0% 50%; }
100% { background-position: 100% 50%; }
}background-size: 200% растягивает градиент в два раза, чтобы цвета могли "скользить" по тексту. background-clip: text обрезает фон по контуру текста, а color: transparent делает сам текст прозрачным.@keyframes мы анимируем background-position, перемещая градиент слева направо и обратно. infinite делает цикл бесконечным, alternate заставляет анимацию воспроизводиться в обратном порядке.
Flexbox Defense
Нашел еще одну классную игру-тренажер, похожа на Flexbox Froggy, но эта отлично зайдет тем, кто любит игры в жанре Tower Defense)
Нужно с помощью свойств flexbox правильно располагать башни, чтобы успешно отражать атаки.
Ссылка на игру - http://www.flexboxdefense.com/
Проводим выходные с пользой)
Расширяющийся сайдбар с помощью :has() и Grid
Давайте сегодня разберем еще один интересный пример использования псевдокласса :has(), сделаем сайдбар с плавным раскрытием.
HTML:
<div class="grid">
<nav class="sidebar">…</nav>
<div class="content">…</div>
</div>
.grid {
display: grid;
grid-template-columns: 60px 1fr;
transition: grid-template-columns 400ms;
}60px (узкий сайдбар) и 1fr (основной контент).:has():.grid:has(.sidebar:hover) {
grid-template-columns: clamp(250px, 25%, 400px) 1fr;
}:has(), который меняет ширину колонки с помощью функции clamp() - минимум 250px, максимум 400px, но не более 25% от ширины окна..sidebar .text {
opacity: 0;
transition: opacity 200ms;
}
.grid:has(.sidebar:hover) .sidebar .text {
opacity: 1;
transition-delay: 100ms;
}
Основные способы скрыть элемент в CSS
Иногда при верстке нужно скрыть элемент на странице. В CSS существует несколько способов это сделать — каждый из них имеет свои особенности и сценарии применения. Давайте разберём самые популярные способы.
1. display: none
Самый радикальный способ — элемент полностью удаляется из потока документа.
.hidden {
display: none;
}.invisible {
visibility: hidden;
}.transparent {
opacity: 0;
}.offscreen {
position: absolute;
left: -9999px;
}.clipped {
clip-path: inset(100%);
}.collapsed {
width: 0;
height: 0;
overflow: hidden;
}overflow..scaled {
transform: scale(0);
}
Вкладки с анимацией скольжения
Сегодня давайте рассмотрим еще один классный эффект — плавно скользящие вкладки на CSS.
Основа — радио-кнопки:
<input type="radio" id="radio-1" name="tabs" checked>
<label for="radio-1">Upcoming</label>
name="tabs" группирует их — можно выбрать только одну. checked делает первую активной по умолчанию.input, стилизуем label:input { display: none; }
label {
width: 200px;
height: 54px;
cursor: pointer;
z-index: 2;
}label переключает связанный input. z-index: 2 нужен чтобы текст был поверх фона.<span class="glider"></span>
.glider {
position: absolute;
width: 200px;
height: 54px;
background: #e6eef9;
z-index: 1;
transition: 0.25s ease-out;
}transition сделает движение плавным.glider при клике:#radio-1:checked ~ .glider { transform: translateX(0); }
#radio-2:checked ~ .glider { transform: translateX(100%); }
#radio-3:checked ~ .glider { transform: translateX(200%); }glider сдвигается на 100% — это его собственная ширина (200px). Получается ровно на одну вкладку вправо. Для третьей — на 200% (две вкладки).input:checked + label { color: #185ee0; }input выбран (:checked), селектор + применяет стили к label, который идёт сразу за ним. Так активная вкладка красится.
Сжимающийся хедер при прокрутке
Иногда нужно сделать не просто фиксированный хедер, а такой, чтобы при скролле хедер фиксировался и уменьшался, освобождая место для контента.
Давайте разберём, как это можно реализовать.
HTML:
<header class="header">
<div class="header-content">
<div class="header-logo">
Лого
</div>
<nav class="header-navigation">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Blog</a>
<a href="#">Contact Us</a>
</nav>
</div>
</header>
.header {
position: sticky;
top: -40px;
height: 110px;
}
.header-content {
height: 70px;
position: sticky;
top: 0;
}position: sticky..header имеет высоту 110px и прилипает к позиции top: -40px. Отрицательное значение заставляет контейнер прилипать ВЫШЕ viewport — его верхняя часть уходит за границу экрана..header-content высотой 70px также имеет position: sticky с top: 0. Он прилипает к верхней границе родительского элемента, оставаясь всегда видимым.
<hr> для разделения элементов в <select>
Не многие знают, что обычный тег <hr> можно использовать внутри выпадающих списков <select> для визуального разделения групп опций.
HTML:
<select name="languages">
<option value="" disabled selected>Выберите язык</option>
<option value="js">JavaScript</option>
<option value="ts">TypeScript</option>
<hr>
<option value="python">Python</option>
<option value="java">Java</option>
<hr>
<option value="php">PHP</option>
<option value="csharp">C#</option>
</select>
<optgroup> с label, то можно вполне обойтись <hr>)
Стилизация скроллбара в браузере
Бывает такое, что дизайнеры просят покрасить скролл браузера в свои цвета, я не особый любитель таких затей, но давайте разберем, как в целом это можно делать.
Единого стандарта для стилизации скроллбаров нет. Webkit-браузеры (Chrome, Safari, Edge) используют свои псевдоэлементы, а Firefox использует свойства scrollbar-width и scrollbar-color. В итоге для кроссбраузерной совместимости нужно использовать оба подхода.
Для Firefox:
* {
scrollbar-width: auto; /* auto, thin, none */
scrollbar-color: #5AC2FF #266D98; /* цвет ползунка и трека */
}::-webkit-scrollbar {
width: 12px; /* ширина вертикального скроллбара */
}
::-webkit-scrollbar-track {
background: #266D98; /* фон трека */
}
::-webkit-scrollbar-thumb {
background: #5AC2FF; /* цвет ползунка */
}
::-webkit-scrollbar-thumb:hover {
background: #98D8EC; /* цвет при наведении */
}
Анимация меняющихся слов
Наверняка вы видели, особенно на лендосах такой эффект, когда текст динамически меняется. Это простой, но очень эффектный способ привлечь внимание и показать несколько преимуществ продукта или услуги в одном месте.
Давайте разберём, как реализовать такую анимацию.
HTML:
<div class="container">
<p>Я ❤️</p>
<div class="words">
<span class="word">HTML</span>
<span class="word">CSS</span>
<span class="word">JavaScript</span>
<span class="word">HTML</span>
</div>
</div>
.words {
overflow: hidden;
height: 60px;
}overflow: hidden и height — это основа эффекта. Мы создаем «окошко», через которое видно только одно слово..word {
display: block;
height: 60px;
line-height: 60px;
animation: animate 6s infinite;
}height и line-height по 60px — каждое слово занимает ровно высоту контейнера.animation: animate 6s infinite — запускаем бесконечную анимацию длительностью 6 секунд.@keyframes animate {
0%, 25% {
transform: translateY(0);
}
33%, 58% {
transform: translateY(-100%);
}
66%, 91% {
transform: translateY(-200%);
}
100% {
transform: translateY(-300%);
}
}
Заголовок с обводкой и тенью
Сегодня хочу поделиться простым, но эффектным приёмом, отлично подойдет для заголовков на лендосах или просто для создания визуального акцента на странице.
HTML:
<h1 class="outline-text">ФОРМОШЛЁП</h1>
.outline-text {
color: transparent;
-webkit-text-stroke: 1.5px black;
text-stroke: 1.5px black;
text-shadow: 5px 5px #6C757D;
}color: transparent — делаем основной цвет букв прозрачным.-webkit-text-stroke: 1.5px black — добавляем чёрную обводку толщиной 1.5px.text-shadow: 5px 5px #6C757D — добавляем тень, смещённую на 5px вправо и 5px вниз.
Приводим логотипы к единому стилю
Частая задача в верстке: нужно разместить логотипы партнёров или клиентов, но все они приходят с разными фонами — где-то белый, где-то цветной и т.п. Разберем, как можно быстро привести их к единообразному виду.
Всего две CSS-строки превращают разношёрстную коллекцию в стильную монохромную галерею:
filter: grayscale(100%);
mix-blend-mode: multiply;
filter: grayscale(100%) — преобразует изображение в чёрно-белое. Цветные логотипы становятся серыми, но фон остаётся.mix-blend-mode: multiply — этот режим наложения работает по принципу умножения цветов. Белые области становятся прозрачными, а тёмные части логотипа остаются видимыми. Фон страницы просвечивает сквозь изображение..brands__item img {
width: 200px;
aspect-ratio: 2 / 1;
object-fit: contain;
}aspect-ratio: 2 / 1; — задаёт пропорции 2:1.object-fit: contain; — логотип вписывается в область без обрезки и искажений.
Перемещаем выбранный тег в начало списка без JS
Сегодня разберем интересную фишку на чистом CSS, которая позволяет динамически перемещать выбранный элемент в начало списка.
Ключ к этому эффекту — свойство order внутри Flexbox-контейнера, но давайте по порядку)
HTML:
<label class="tag">
<input type="checkbox">
HTML5/CSS3
</label>
<label class="tag">
<input type="checkbox">
JavaScript
</label>
<label>, что позволяет кликать по всему тегу..tag input {
display: none;
}.tags-wrapper {
display: flex;
flex-wrap: wrap;
gap: 10px;
}.tag:has(:checked) {
background: #007bff;
color: white;
order: -1;
}:has(:checked) определяет, отмечен ли чекбокс внутри тега. Свойство order: -1 перемещает элемент в начало.:has() и Flexbox мы получаем крутой интерактивный интерфейс)
Скрытая кнопка поиска
Давайте сегодня разберем интересный прием с псевдоклассом :placeholder-shown — анимированную кнопку поиска, которая появляется только когда пользователь начинает вводить текст.
HTML:
<form>
<input type="text" placeholder="Поиск...">
<input type="submit" value="Поиск">
</form>
input[type="submit"] {
opacity: 0;
pointer-events: none;
transition: .3s;
}
input:not(:placeholder-shown) + input[type="submit"] {
opacity: 1;
pointer-events: auto;
}opacity: 0pointer-events: none:not(:placeholder-shown) определяет, что поле не пустоеtransition: .3spointer-events: auto
Крутой учебник по JS
Нашел книгу "JavaScript для тупых", необычное название, конечно)
Но суть в том, что это не про IQ, а про то, что стандартная академическая подача материала работает далеко не для всех.
Вот выдержка с первой страницы:
Материал будет подаваться жестко.
Знаешь почему?
Потому что ты пришёл на курс "JavaScript для тупых", следовательно, обычная подача материала тебе не подходит по одной из нижеприведенных причин:
1. ты реально тупой, как угол в 179°, что не вывозишь то, что вывозит большинство адекватных людей;
2. для тебя обычная подача скучна и от этого материал не усваивается, либо усваивается на весьма короткий срок;
3. при обычной подаче не понимаешь, что происходит, и что от тебя требуется и зачем;
4. тебе просто-напросто лень читать огромные мануалы.
Отображаем структуру файлов на веб-странице
Если вам вдруг когда-нибудь понадобится показать структуру проекта на сайте — есть простое и элегантное решение: ASCII-графика.
Всего три символа позволяют построить визуальное дерево файлов:├── — ответвление (не последний элемент)└── — последнее ответвление│ — вертикальная линия (связь с родителем)
Оборачиваем всё в тег <pre> — и готово.
HTML:
<div class="card">
<pre>
project/
├── src/
│ ├── js/
│ │ └── main.js
│ ├── scss/
│ │ └── styles.scss
│ └── index.html
├── package-lock.json
├── package.json
└── webpack.config.js
</pre>
</div>
pre {
font-family: monospace;
font-size: 18px;
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
color: #2d3748;
}
Эффект подсветки ссылок
Давайте разберём интересный hover-эффект для ссылок, при наведении на ссылку текст плавно заполняется золотым фоном, создавая эффект маркера-выделителя.
HTML:
<p>
Lorem Ipsum is simply <a href="#">dummy text</a> of the printing
and typesetting industry.
</p>
a {
color: black;
text-decoration: none;
background: linear-gradient(to bottom, white 50%, gold 50%);
background-size: 100% 200%;
background-position: 0 0;
transition: background-position 400ms ease;
border-bottom: 2px solid gold;
font-weight: 500;
}text-decoration: none — убираем стандартное подчёркивание.linear-gradient(to bottom, white 50%, gold 50%) — создаем градиент, где верхняя половина белая, а нижняя золотая.background-size: 100% 200% — растягиваем фон в два раза по высоте.background-position: 0 0 — изначально показываем только верхнюю (белую) половину.border-bottom: 2px solid gold — золотая линия снизу как индикатор ссылки.a:hover {
background-position: 0 100%;
}transition это происходит плавно.
Шпаргалка по Flexbox
Если все никак не получается запомнить принцип работы и основные свойства Flexbox, то вот вам годная шпаргалка - https://darekkay.com/flexbox-cheatsheet/flexbox-cheatsheet.pdf
Можно даже распечатать, чтоб под рукой всегда была и не гуглить каждый раз)
Ну а тем, кто хочет более подробно — https://yoksel.github.io/flex-cheatsheet/
Это интерактивная шпаргалка, можно кликать, экспериментировать и сразу видеть, как меняется поведение элементов. Отлично подходит для визуалов и тех, кто учится на практике.
Пользуйтесь на здоровье)
Множественные границы через box-shadow
Иногда для дизайна требуется создать элемент с несколькими границами разных цветов. Классическое свойство border позволяет добавить только одну границу, можно напилить дополнительных div-обёрток, но есть элегантное решение — использовать box-shadow.
Свойство box-shadow позволяет задать несколько теней, разделяя их запятыми. Каждая тень может быть использована как отдельная граница.
HTML:
<div class="box">
<p>box-shadow</p>
</div>
.box {
background: white;
border-radius: 20px;
box-shadow:
0 0 0 10px #002700,
0 0 0 20px #005227,
0 0 0 30px #ff7223,
0 0 0 40px #ffd531,
0 0 0 50px #ffff88;
}box-shadow:box-shadow: offset-x offset-y blur-radius spread-radius color;
box-shadow не влияет на макет элемента, то есть не добавляет элементу физическую ширину/высоту.
Эффект "выглядывающего" аватара
Давайте разберём CSS-трюк, который позволяет создать интерактивный аватар с эффектом "выглядывания" изображения за границы круга при наведении курсора.
HTML:
<div class="avatar">
<img src="avatar.png" alt="Аватар" />
</div>
.avatar {
width: 300px;
height: 300px;
border-radius: 50%;
background-color: #ddbe11;
position: relative;
clip-path: inset(-100vw 0 0 0 round 50%);
border: 6px solid #eaeaea;
}position: relative нужен для абсолютного позиционирования изображения внутри.clip-path:inset() - обрезает элемент по заданным отступамround 50% - скругляем форму обрезки до круга.avatar img {
position: absolute;
top: 70%;
left: 60%;
transform: translate(-50%, -50%);
width: 100%;
height: auto;
transition: transform 0.3s ease;
}.avatar:hover img {
transform: translate(-50%, -50%) scale(1.4);
}translate(-50%, -50%) и выходя за пределы круга сверху благодаря нашему хитрому clip-path)
Визуальная шпаргалка по CSS :nth-child
Селектор :nth-child — мощный инструмент в CSS, но его синтаксис часто вызывает вопросы. Давайте разберём основные варианты использования с наглядными примерами.
:nth-child(4) — конкретный элемент
.item:nth-child(4) {
...
}.item:nth-child(n+3) {
...
}.item:nth-child(-n+3) {
...
}.item:nth-child(3n+1) {
...
}.item:nth-child(odd) {
...
}.item:nth-child(even) {
...
}.item:last-child::after {
...
}.item:nth-last-child(2) {
...
}
Распределение элементов с margin: auto в Flexbox
Иногда нужно не просто выровнять элементы, а распределить их в flex-контейнере так, чтобы между ними появилось нужное пространство. С этим отлично справляется margin: auto, давайте разберем, как это работает.
Пример 1: margin-right: auto (прижать к краям)
.container {
display: flex;
}
.element {
margin-right: auto;
}margin-right: auto к элементу, он "захватывает" всё доступное пространство справа. Все элементы после него автоматически сдвигаются вправо.margin-inline: auto (центрирование).container {
display: flex;
}
.element {
margin-inline: auto;
}margin-inline: auto добавляет автоматические отступы слева и справа, центрируя элемент. Остальные элементы распределяются по краям контейнера. margin-right: auto. Если нужна симметрия — margin-inline: auto)
Лоботомированная сова — * + *
Давайте сегодня разберем CSS-паттерн, который автоматически делает отступы между элементами.
Автор этого приёма — Heydon Pickering. Он придумал этот селектор * + * { margin-top: 1em; } как простое, универсальное решение для отступов между элементами и назвал его "Lobotomized Owl".
HTML:
<div class="content">
<h2>Lorem Ipsum</h2>
<p>Lorem ipsum dolor..</p>
<p>Ut enim ad minim veniam..</p>
<img src="image.jpg">
<p>Duis aute irure dolor..</p>
</div>
.content * + * {
margin-top: 24px;
}margin-top..content > * + * {
margin-top: 24px;
}.content. Отступы будут только между основными блоками контента и не будут нарушать макеты вложенных элементов.
Наглядная демонстрация значений width
Давайте разберём, как работают и в чём отличие четырёх значений width — auto, fit-content, max-content и min-content.
width: auto
width: max-content
width: min-content
width: fit-content
max-content, но не может превысить ширину родителя. Если контент слишком длинный, он переносится, а блок сжимается до ширины родителя.max-content — элемент, который никогда не переносится, min-content — ширина самого длинного слова, а fit-content — умный блок, который занимает столько места, сколько нужно, но не выходит за рамки родителя)
Аккордеон со скрывающимся заголовком
Недавно попался на глаза аккордеон без заголовков, это не очень удобно на мой взгляд, но у дизайнеров свое мнение) Давайте разберем, как это реализуется.
HTML:
<details name="accordion">
<summary>Заголовок</summary>
<div class="content">
<p><strong>Заголовок.</strong> Подробное описание...</p>
</div>
</details>
details[open] summary {
display: none;
}<summary> когда аккордеон открыт.name:<details name="accordion">
Префикс в поле ввода телефона
Давайте сегодня разберем, как можно упростить процесс ввода телефона. Вместо того, чтобы заставлять юзера вводить код страны (+7 или 8), мы можем визуально отделить его от основного поля ввода.
HTML:
<div class="card">
<h2>Введите номер телефона</h2>
<div class="input-box">
<span class="prefix">+7</span>
<input type="tel" placeholder="9871234567">
</div>
</div>
.input-box {
display: flex;
border: 1px solid #ddd;
border-radius: 8px;
padding: 0.75rem 1rem;
}input выглядели как одно целое, делаем флекс-контейнер и оформляем его рамкой.input, чтобы поле выглядело цельным:input {
border: none;
}