Як створити мобільне меню з burger-кнопкою без jQuery

Для створення мобільного меню у WordPress зовсім не обов’язково підключати jQuery або сторонні JavaScript-бібліотеки.

Сучасного Vanilla JavaScript цілком достатньо, щоб реалізувати:

  • відкриття та закриття меню;
  • анімацію burger-кнопки;
  • блокування прокрутки сторінки;
  • закриття меню клавішею Escape;
  • закриття після переходу за посиланням;
  • коректні aria-* атрибути для accessibility.

У цій статті створимо просте мобільне меню для власної WordPress-теми.

1. Виводимо меню в header.php

Припустимо, що у functions.php вже зареєстрована позиція меню:

function wp_hunter_setup() {

	register_nav_menus(
		array(
			'primary' => __( 'Primary Menu', 'wp-hunter' ),
		)
	);

}
add_action( 'after_setup_theme', 'wp_hunter_setup' );

Тепер у header.php перевіримо, чи призначене меню, та додамо burger-кнопку:

<?php if ( has_nav_menu( 'primary' ) ) : ?>

	<button
		class="menu-toggle"
		type="button"
		aria-expanded="false"
		aria-controls="primary-navigation"
		aria-label="<?php esc_attr_e( 'Open menu', 'wp-hunter' ); ?>"
	>
		<span class="menu-toggle__line"></span>
		<span class="menu-toggle__line"></span>
		<span class="menu-toggle__line"></span>
	</button>

	<nav
		id="primary-navigation"
		class="primary-navigation"
		aria-label="<?php esc_attr_e( 'Primary Navigation', 'wp-hunter' ); ?>"
	>
		<?php
		wp_nav_menu(
			array(
				'theme_location' => 'primary',
				'container'      => false,
				'menu_class'     => 'primary-menu',
				'fallback_cb'    => false,
			)
		);
		?>
	</nav>

<?php endif; ?>

Тут важливими є два атрибути:

aria-expanded="false"

показує, чи відкрите меню.

А:

aria-controls="primary-navigation"

вказує, яким елементом керує кнопка.

ID:

id="primary-navigation"

відповідно встановлений для <nav>.

2. Створюємо burger-кнопку

Всередині кнопки ми використали три звичайні <span>:

<span class="menu-toggle__line"></span>
<span class="menu-toggle__line"></span>
<span class="menu-toggle__line"></span>

Саме з них за допомогою CSS створимо burger.

.menu-toggle {
	display: none;
	width: 44px;
	height: 44px;
	padding: 10px;
	border: 0;
	background: transparent;
	cursor: pointer;
}

.menu-toggle__line {
	display: block;
	width: 24px;
	height: 2px;
	margin: 5px auto;
	background: currentColor;
	transition:
		transform 0.3s ease,
		opacity 0.3s ease;
}

На desktop кнопка прихована:

display: none;

Показувати її будемо лише на мобільних пристроях.

3. Анімація burger → close

Коли меню відкрите, JavaScript змінюватиме:

aria-expanded="false"

на:

aria-expanded="true"

Це дозволяє керувати анімацією без додаткових CSS-класів:

.menu-toggle[aria-expanded="true"] .menu-toggle__line:nth-child(1) {
	transform: translateY(7px) rotate(45deg);
}

.menu-toggle[aria-expanded="true"] .menu-toggle__line:nth-child(2) {
	opacity: 0;
}

.menu-toggle[aria-expanded="true"] .menu-toggle__line:nth-child(3) {
	transform: translateY(-7px) rotate(-45deg);
}

Три лінії burger-кнопки плавно перетворяться на хрестик.

4. Стилі мобільного меню

Тепер створимо саме мобільне меню.

Наприклад, нехай воно виїжджає справа:

@media (max-width: 991px) {

	.menu-toggle {
		display: block;
		position: relative;
		z-index: 1001;
	}

	.primary-navigation {
		position: fixed;
		top: 0;
		right: 0;
		bottom: 0;
		width: min(400px, 100%);
		padding: 100px 30px 40px;
		background: #fff;

		transform: translateX(100%);
		visibility: hidden;
		transition:
			transform 0.35s ease,
			visibility 0.35s;
		z-index: 1000;
	}

	body.menu-open .primary-navigation {
		transform: translateX(0);
		visibility: visible;
	}

	.primary-menu {
		display: flex;
		flex-direction: column;
		gap: 20px;
		margin: 0;
		padding: 0;
		list-style: none;
	}

	.primary-menu a {
		display: block;
		text-decoration: none;
	}

	body.menu-open {
		overflow: hidden;
	}
}

Ключовим тут є клас:

body.menu-open

Коли він з’являється на <body>, меню стає видимим.

Також:

body.menu-open {
	overflow: hidden;
}

блокує прокрутку сторінки під відкритим мобільним меню.

5. JavaScript без jQuery

Створимо файл:

/assets/js/navigation.js

і додамо:

document.addEventListener('DOMContentLoaded', () => {

	const toggle = document.querySelector('.menu-toggle');
	const navigation = document.querySelector('.primary-navigation');

	if (!toggle || !navigation) {
		return;
	}

	const openMenu = () => {
		document.body.classList.add('menu-open');

		toggle.setAttribute('aria-expanded', 'true');
		toggle.setAttribute('aria-label', 'Close menu');
	};

	const closeMenu = () => {
		document.body.classList.remove('menu-open');

		toggle.setAttribute('aria-expanded', 'false');
		toggle.setAttribute('aria-label', 'Open menu');
	};

	const toggleMenu = () => {
		const isOpen = toggle.getAttribute('aria-expanded') === 'true';

		if (isOpen) {
			closeMenu();
		} else {
			openMenu();
		}
	};

	toggle.addEventListener('click', toggleMenu);

});

Все.

Для базового мобільного меню нам не потрібні ні jQuery, ні Bootstrap, ні будь-які інші залежності.

6. Закриваємо меню клавішею Escape

Додамо можливість закрити меню клавішею Escape.

Це особливо важливо для користувачів, які працюють із сайтом за допомогою клавіатури.

document.addEventListener('keydown', (event) => {

	if (event.key === 'Escape') {
		closeMenu();
	}

});

Тепер натискання:

Esc

закриє мобільне меню.

7. Закриваємо меню після кліку на посилання

На мобільних пристроях після вибору пункту навігації меню також бажано закривати.

Додаємо:

navigation.addEventListener('click', (event) => {

	if (event.target.closest('a')) {
		closeMenu();
	}

});

Це особливо корисно, якщо меню містить anchor-посилання:

#about
#services
#contacts

Користувач натискає пункт — меню закривається, а браузер переходить до потрібної секції.

8. Закриваємо меню при кліку поза ним

Також можна закривати меню, якщо користувач натиснув у будь-якому місці за межами навігації.

document.addEventListener('click', (event) => {

	const clickedInsideMenu = navigation.contains(event.target);
	const clickedToggle = toggle.contains(event.target);

	if (!clickedInsideMenu && !clickedToggle) {
		closeMenu();
	}

});

При цьому клік на самому меню або burger-кнопці не викликатиме закриття.

9. Враховуємо зміну розміру браузера

Є ще один невеликий нюанс.

Наприклад, користувач відкрив мобільне меню, а потім збільшив ширину браузера до desktop.

На <body> все ще залишиться:

class="menu-open"

Тому краще автоматично скинути стан меню при переході на desktop.

const desktopMedia = window.matchMedia('(min-width: 992px)');

desktopMedia.addEventListener('change', (event) => {

	if (event.matches) {
		closeMenu();
	}

});

Тепер при переході вище 992px мобільне меню автоматично повернеться у початковий стан.

10. Повний JavaScript

У результаті наш navigation.js виглядатиме так:

document.addEventListener('DOMContentLoaded', () => {

	const toggle = document.querySelector('.menu-toggle');
	const navigation = document.querySelector('.primary-navigation');

	if (!toggle || !navigation) {
		return;
	}

	const openMenu = () => {
		document.body.classList.add('menu-open');

		toggle.setAttribute('aria-expanded', 'true');
		toggle.setAttribute('aria-label', 'Close menu');
	};

	const closeMenu = () => {
		document.body.classList.remove('menu-open');

		toggle.setAttribute('aria-expanded', 'false');
		toggle.setAttribute('aria-label', 'Open menu');
	};

	const toggleMenu = () => {
		const isOpen = toggle.getAttribute('aria-expanded') === 'true';

		isOpen ? closeMenu() : openMenu();
	};

	toggle.addEventListener('click', toggleMenu);

	document.addEventListener('keydown', (event) => {

		if (event.key === 'Escape') {
			closeMenu();
		}

	});

	navigation.addEventListener('click', (event) => {

		if (event.target.closest('a')) {
			closeMenu();
		}

	});

	document.addEventListener('click', (event) => {

		const clickedInsideMenu = navigation.contains(event.target);
		const clickedToggle = toggle.contains(event.target);

		if (!clickedInsideMenu && !clickedToggle) {
			closeMenu();
		}

	});

	const desktopMedia = window.matchMedia('(min-width: 992px)');

	desktopMedia.addEventListener('change', (event) => {

		if (event.matches) {
			closeMenu();
		}

	});

});

Для звичайного мобільного меню цього більш ніж достатньо.

11. Підключаємо JavaScript у WordPress

Не варто вставляти:

<script src="..."></script>

безпосередньо в header.php або footer.php.

WordPress має власний механізм підключення JavaScript-файлів — wp_enqueue_script().

Додаємо у functions.php:

function wp_hunter_scripts() {

	wp_enqueue_script(
		'wp-hunter-navigation',
		get_template_directory_uri() . '/assets/js/navigation.js',
		array(),
		wp_get_theme()->get( 'Version' ),
		array(
			'strategy'  => 'defer',
			'in_footer' => true,
		)
	);

}
add_action( 'wp_enqueue_scripts', 'wp_hunter_scripts' );

Наш скрипт не використовує jQuery, тому список залежностей порожній:

array()

Тобто WordPress не буде завантажувати jQuery заради роботи мобільного меню.

12. Повна структура

У результаті структура теми може виглядати так:

your-theme/
│
├── assets/
│   ├── css/
│   │   └── main.css
│   │
│   └── js/
│       └── navigation.js
│
├── functions.php
├── header.php
├── footer.php
├── index.php
└── style.css

У functions.php:

register_nav_menus();

реєструємо позицію меню.

У header.php:

wp_nav_menu();

виводимо навігацію та burger-кнопку.

У:

navigation.js

керуємо відкриттям та закриттям меню.

А CSS відповідає за зовнішній вигляд та анімацію.

Чому краще без jQuery?

Колись для такої задачі часто використовували:

$('.menu-toggle').on('click', function() {
	$('.menu').toggleClass('active');
});

Але сьогодні для настільки простої логіки jQuery не потрібен.

Vanilla JavaScript вже має все необхідне:

document.querySelector()
addEventListener()
classList.add()
classList.remove()
classList.toggle()
closest()
matchMedia()

У результаті ми не створюємо зайву залежність і залишаємо код теми простішим.

Не забуваємо про accessibility

Burger-кнопка повинна бути саме:

<button>

а не:

<div>

або:

<span>

Кнопка вже має правильну семантику та доступна для керування клавіатурою.

Також використовуємо:

aria-expanded="false"

щоб повідомляти стан меню.

Коли меню відкривається:

aria-expanded="true"

А через:

aria-controls="primary-navigation"

зв’язуємо кнопку безпосередньо з навігацією.

Підсумок

Для створення сучасного мобільного меню у WordPress не потрібен jQuery.

Достатньо:

wp_nav_menu()
+
CSS
+
Vanilla JavaScript

У нашому прикладі burger-кнопка:

  • відкриває та закриває меню;
  • анімується у хрестик;
  • змінює aria-expanded;
  • блокує прокрутку сторінки;
  • закривається через Escape;
  • закривається після переходу за посиланням;
  • закривається при кліку поза меню;
  • автоматично скидається при переході на desktop.

Для більшості власних WordPress-тем цього достатньо, а JavaScript залишається невеликим, зрозумілим і без сторонніх залежностей.

CSS @starting-style: анімація появи елементів без JavaScript

CSS нарешті дозволяє анімувати першу появу елемента: знайомимося з @starting-style

У CSS давно є transition, але в нього була одна неприємна особливість: перехід не запускався під час першої появи елемента на сторінці.

Наприклад, якщо ми додавали повідомлення, модальне вікно, картку товару або інший елемент у DOM через JavaScript, браузер одразу показував його у фінальному стані.

Щоб отримати плавну появу, розробникам доводилося використовувати додаткові класи, requestAnimationFrame(), setTimeout() або примусове читання layout.

Тепер це можна зробити значно простіше.

Для цього в CSS з’явилося правило:

@starting-style

Воно дозволяє описати початковий стан елемента перед його першою появою, після чого звичайний transition виконає плавний перехід до фінального стану.

І найважливіше — сьогодні це вже не експериментальна можливість. @starting-style входить до Baseline 2024, а глобальна підтримка браузерами становить приблизно 90,65%.


Яку проблему вирішує @starting-style

Розглянемо простий приклад.

У нас є елемент:

<div class="notice">
    Налаштування успішно збережено
</div>

І ми хочемо, щоб під час появи він плавно піднімався знизу:

.notice {
    opacity: 1;
    transform: translateY(0);

    transition:
        opacity 0.3s ease,
        transform 0.3s ease;
}

Логічно було б припустити, що браузер сам виконає перехід від якогось початкового стану.

Але проблема в тому, що коли елемент тільки з’явився, попереднього стану в нього немає.

Браузер бачить одразу:

opacity: 1;
transform: translateY(0);

Тому переходити фактично немає звідки.

Саме тут і потрібен @starting-style.


Найпростіший приклад @starting-style

Додамо початковий стан:

.notice {
    opacity: 1;
    transform: translateY(0);

    transition:
        opacity 0.3s ease,
        transform 0.3s ease;
}

@starting-style {
    .notice {
        opacity: 0;
        transform: translateY(20px);
    }
}

Тепер під час першої появи .notice браузер фактично бачить три речі:

Початковий стан
opacity: 0
translateY(20px)

        ↓ transition

Фінальний стан
opacity: 1
translateY(0)

І все це працює без JavaScript-коду, який відповідає за запуск самої анімації.

JavaScript усе ще може додати елемент у DOM, але йому більше не потрібно вручну створювати проміжний стан.


Вкладений синтаксис

Є ще один варіант запису.

@starting-style можна вкладати безпосередньо в CSS-селектор:

.notice {
    opacity: 1;
    transform: translateY(0);

    transition:
        opacity 0.3s ease,
        transform 0.3s ease;

    @starting-style {
        opacity: 0;
        transform: translateY(20px);
    }
}

Особисто мені такий варіант подобається більше.

В одному місці ми одразу бачимо:

  • фінальний стан;
  • transition;
  • початковий стан.

Для компонентного CSS це особливо зручно.


Як ми робили це раніше

До появи @starting-style типовий код виглядав приблизно так.

CSS:

.notice {
    opacity: 0;
    transform: translateY(20px);

    transition:
        opacity 0.3s ease,
        transform 0.3s ease;
}

.notice.is-visible {
    opacity: 1;
    transform: translateY(0);
}

JavaScript:

const notice = document.createElement('div');

notice.className = 'notice';
notice.textContent = 'Налаштування успішно збережено';

document.body.appendChild(notice);

requestAnimationFrame(() => {
    notice.classList.add('is-visible');
});

Навіщо тут requestAnimationFrame()?

Тому що браузеру потрібно було спочатку відрендерити:

opacity: 0;

а вже потім отримати:

opacity: 1;

Інакше він міг застосувати обидва стани в одному циклі рендерингу — і transition знову не запускався.

З @starting-style це спрощується.

JavaScript:

const notice = document.createElement('div');

notice.className = 'notice';
notice.textContent = 'Налаштування успішно збережено';

document.body.appendChild(notice);

CSS:

.notice {
    opacity: 1;
    transform: translateY(0);

    transition:
        opacity 0.3s ease,
        transform 0.3s ease;

    @starting-style {
        opacity: 0;
        transform: translateY(20px);
    }
}

Готово.

JavaScript займається логікою.

CSS — анімацією.

Саме так і повинно бути.


Де це реально корисно

@starting-style особливо добре підходить для елементів, які з’являються динамічно.

Наприклад:

AJAX-повідомлення

.ajax-message {
    opacity: 1;
    transform: translateY(0);

    transition: 0.3s ease;

    @starting-style {
        opacity: 0;
        transform: translateY(-10px);
    }
}

Це можуть бути повідомлення:

Товар додано до кошика

Форму успішно відправлено

Налаштування збережено

Сталася помилка

Toast notifications

.toast {
    opacity: 1;
    transform: translateX(0);

    transition:
        opacity 0.25s ease,
        transform 0.25s ease;

    @starting-style {
        opacity: 0;
        transform: translateX(30px);
    }
}

Під час додавання .toast у DOM він автоматично плавно з’явиться справа.


Картки, які додаються через AJAX

Наприклад, WooCommerce підвантажує товари без перезавантаження сторінки.

Можна зробити:

.product-card {
    opacity: 1;
    transform: translateY(0) scale(1);

    transition:
        opacity 0.4s ease,
        transform 0.4s ease;

    @starting-style {
        opacity: 0;
        transform: translateY(15px) scale(0.98);
    }
}

Після додавання нового елемента в DOM він сам отримає плавну появу.

Без:

element.classList.add('animate');

Без таймерів.

Без requestAnimationFrame().


Popover API + @starting-style

Ще цікавіше @starting-style виглядає разом із сучасним HTML Popover API.

HTML:

<button popovertarget="user-menu">
    Відкрити меню
</button>

<div id="user-menu" popover>
    <a href="#">Профіль</a>
    <a href="#">Налаштування</a>
    <a href="#">Вийти</a>
</div>

CSS:

[popover]:popover-open {
    opacity: 1;
    transform: translateY(0) scale(1);

    transition:
        opacity 0.2s ease,
        transform 0.2s ease;

    @starting-style {
        opacity: 0;
        transform: translateY(-8px) scale(0.96);
    }
}

Тепер popover може плавно з’являтися без JavaScript-анімацій.

MDN окремо відзначає popover та <dialog> як одні з основних сценаріїв використання @starting-style.


А що з display: none?

Ось тут стає ще цікавіше.

Історично display був однією з найбільш незручних властивостей для transition.

Наприклад:

.modal {
    display: none;
    opacity: 0;
}

.modal.active {
    display: block;
    opacity: 1;
}

Ми могли очікувати плавний opacity, але через display: none елемент фактично не існував у layout.

Сучасний CSS поступово вирішує і цю проблему.

Можна використовувати:

.modal {
    transition:
        opacity 0.3s,
        display 0.3s;

    transition-behavior: allow-discrete;
}

У поєднанні з @starting-style це відкриває можливість створювати значно складніші entry/exit transitions без великої кількості JavaScript.

Наприклад:

.modal {
    display: none;
    opacity: 0;
    transform: scale(0.95);

    transition:
        opacity 0.3s,
        transform 0.3s,
        display 0.3s allow-discrete;
}

.modal.is-open {
    display: block;
    opacity: 1;
    transform: scale(1);

    @starting-style {
        opacity: 0;
        transform: scale(0.95);
    }
}

Тут @starting-style описує стан, з якого починається поява.

Важливо розуміти, що @starting-style стосується саме CSS transitions. Для звичайних @keyframes animations він не потрібен.


@starting-style не замінює JavaScript

Тут є важливий нюанс.

Може здатися, що тепер JavaScript для UI-анімацій взагалі не потрібен.

Це не так.

@starting-style не вирішує бізнес-логіку.

Наприклад, JavaScript усе одно може бути потрібен, щоб:

document.body.appendChild(element);

або:

modal.classList.add('is-open');

або щоб отримати дані через AJAX.

Але раніше JS часто був потрібен ще й для технічного запуску анімації:

element.classList.add('initial');

requestAnimationFrame(() => {
    element.classList.add('visible');
});

Ось саме цього коду тепер у багатьох випадках можна позбутися.


Хороший приклад для WordPress

Уявімо WordPress-сайт із кастомною AJAX-формою.

Після успішної відправки ми створюємо повідомлення:

const message = document.createElement('div');

message.className = 'form-success';
message.textContent = 'Дякуємо! Повідомлення відправлено.';

form.appendChild(message);

CSS:

.form-success {
    padding: 16px 20px;
    border-radius: 10px;

    opacity: 1;
    transform: translateY(0);

    transition:
        opacity 0.35s ease,
        transform 0.35s ease;

    @starting-style {
        opacity: 0;
        transform: translateY(15px);
    }
}

JS взагалі не знає, що елемент анімований.

І це хороший поділ відповідальності:

JavaScript
↓
створює елемент

CSS
↓
вирішує, як він виглядає і з'являється

Ще один приклад — mini cart WooCommerce

У WooCommerce часто потрібно показати повідомлення після додавання товару в кошик.

Наприклад:

.woocommerce-message {
    opacity: 1;
    transform: translateY(0);

    transition:
        opacity 0.3s ease,
        transform 0.3s ease;

    @starting-style {
        opacity: 0;
        transform: translateY(-15px);
    }
}

Якщо повідомлення додається в DOM динамічно, браузер отримує стартовий стан автоматично.

Для кастомних WooCommerce-інтерфейсів це може прибрати помітну кількість допоміжного JS.


Які властивості краще анімувати

Як і з будь-якими CSS transitions, найкраще використовувати властивості, які браузер може обробляти ефективно.

Передусім:

opacity
transform

Наприклад:

@starting-style {
    .card {
        opacity: 0;
        transform: translateY(20px);
    }
}

А не намагатися без необхідності анімувати:

width
height
top
left
margin

Особливо якщо таких елементів на сторінці багато.

Для більшості появ UI-компонентів комбінації:

opacity + translate

або:

opacity + scale

цілком достатньо.


Не забуваємо про prefers-reduced-motion

Нові можливості CSS не скасовують accessibility.

Якщо користувач увімкнув у системі зменшення анімацій, це бажано враховувати.

Наприклад:

.card {
    opacity: 1;
    transform: translateY(0);

    transition:
        opacity 0.3s ease,
        transform 0.3s ease;

    @starting-style {
        opacity: 0;
        transform: translateY(20px);
    }
}

@media (prefers-reduced-motion: reduce) {
    .card {
        transition: none;
    }
}

Таким чином ефект залишається для більшості користувачів, але не створює зайвого руху для тих, хто свідомо його вимкнув.


Що буде у старих браузерах?

У цьому і є одна з переваг такого підходу.

Браузер, який не розуміє:

@starting-style

просто не застосує початковий стиль.

Елемент усе одно буде показаний у своєму нормальному фінальному стані.

Тобто замість:

плавної появи

користувач просто побачить:

звичайну появу

Функціональність сайту при цьому не повинна ламатися.

Це хороший приклад progressive enhancement.


Підтримка браузерами

Станом на момент написання цієї статті підтримка вже достатньо хороша для використання в сучасних проєктах.

Can I Use показує приблизно 90,65% глобального покриття.

Підтримуються сучасні версії:

Chrome
Edge
Firefox
Safari
Safari iOS
Samsung Internet

MDN позначає @starting-style як Baseline 2024 — Newly available і вказує, що функція стала доступною в актуальних основних браузерах із серпня 2024 року.

Тому для більшості нових сайтів я вже не бачу причин боятися використовувати її як progressive enhancement.


Важливий нюанс: @starting-style — це не @keyframes

Не варто плутати:

@starting-style

з:

@keyframes

Наприклад, так:

.element {
    animation: fade-in 0.3s ease;
}

@keyframes fade-in {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

і це:

.element {
    opacity: 1;
    transition: opacity 0.3s ease;

    @starting-style {
        opacity: 0;
    }
}

можуть виглядати однаково.

Але концептуально це різні речі.

animation запускає окрему CSS-анімацію.

@starting-style дає початковий стан звичайному transition, коли попереднього стилю в елемента ще не було.

І саме тому ця можливість така корисна для компонентів інтерфейсу.


Підсумок

@starting-style — невелике доповнення до CSS, але воно вирішує проблему, з якою frontend-розробники стикалися роками.

Тепер замість конструкцій на кшталт:

element.classList.add('start');

requestAnimationFrame(() => {
    element.classList.add('visible');
});

у багатьох випадках достатньо:

.element {
    opacity: 1;
    transform: translateY(0);

    transition:
        opacity 0.3s ease,
        transform 0.3s ease;

    @starting-style {
        opacity: 0;
        transform: translateY(20px);
    }
}

І це хороший приклад того, як сучасний CSS поступово забирає на себе завдання, для яких ще кілька років тому доводилося писати JavaScript.

Особливо корисним @starting-style буде для:

  • модальних вікон;
  • popover;
  • toast-повідомлень;
  • AJAX-контенту;
  • форм;
  • WooCommerce-повідомлень;
  • mini cart;
  • динамічних Gutenberg-компонентів;
  • елементів, які додаються до DOM після завантаження сторінки.

Тож @starting-style уже цілком можна додавати до набору інструментів сучасного frontend-розробника.

Якщо підтримка старих браузерів для проєкту не є критичною — використовуйте.

А якщо є — використовуйте все одно як progressive enhancement: у старому браузері користувач просто не побачить анімацію, але сам інтерфейс продовжить працювати.

WP-Hunter

Безпечний вхід до акаунта

Увійдіть, створіть акаунт або відновіть пароль.

З поверненням

Увійдіть, щоб продовжити.