Урок 12. Примеры: интерактив без JavaScript
⚡ Кратко
Шесть интерактивных примеров без единой строки JavaScript: кнопки, карточка-ссылка, поле с иконкой, выбор тарифа, аккордеон и меню.
1. Полный набор состояний кнопки
<div class="flex flex-wrap items-center gap-3">
<button class="rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white transition-colors
hover:bg-slate-700 active:bg-slate-800
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-900
disabled:cursor-not-allowed disabled:bg-slate-300">Обычная</button>
<button class="rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white disabled:cursor-not-allowed disabled:bg-slate-300" disabled>Отключена</button>
<a href="#" class="rounded-md px-4 py-2 text-sm font-medium text-sky-700 underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-sky-600">Ссылка</a>
</div>
2. Карточка-ссылка целиком
<a href="#" class="group block max-w-sm rounded-xl border border-slate-200 bg-white p-4 transition-colors hover:border-slate-300 hover:bg-slate-50
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-600">
<div class="flex items-center justify-between">
<h3 class="text-sm font-semibold text-slate-900">Ежемесячный отчёт</h3>
<span class="text-slate-300 transition-transform group-hover:translate-x-1 group-hover:text-slate-500">→</span>
</div>
<p class="mt-1 text-sm text-slate-600">Кликабельна вся карточка, стрелка реагирует на наведение.</p>
</a>
3. Поле с иконкой и подсветкой обёртки
<div class="flex max-w-sm items-center gap-2 rounded-md border border-slate-300 px-3 py-2 transition-colors
focus-within:border-sky-500 focus-within:ring-2 focus-within:ring-sky-500/30">
<span class="text-slate-400">🔍</span>
<input class="w-full text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none" placeholder="Поиск по документам">
</div>
focus-within позволяет подсветить всю обёртку, когда
фокус попадает в поле внутри. Иначе рамка появлялась бы только у самого
input, а иконка оставалась «снаружи».
4. Выбор тарифа карточками
<div class="grid gap-3 sm:grid-cols-3">
<label class="cursor-pointer rounded-xl border border-slate-200 p-4 transition-colors has-checked:border-indigo-500 has-checked:bg-indigo-50 has-checked:ring-1 has-checked:ring-indigo-500">
<input type="radio" name="tariff" class="sr-only">
<p class="text-sm font-semibold text-slate-900">Старт</p>
<p class="mt-1 text-xs text-slate-600">0 ₽ в месяц</p>
</label>
<label class="cursor-pointer rounded-xl border border-slate-200 p-4 transition-colors has-checked:border-indigo-500 has-checked:bg-indigo-50 has-checked:ring-1 has-checked:ring-indigo-500">
<input type="radio" name="tariff" class="sr-only" checked>
<p class="text-sm font-semibold text-slate-900">Команда</p>
<p class="mt-1 text-xs text-slate-600">990 ₽ в месяц</p>
</label>
<label class="cursor-pointer rounded-xl border border-slate-200 p-4 transition-colors has-checked:border-indigo-500 has-checked:bg-indigo-50 has-checked:ring-1 has-checked:ring-indigo-500">
<input type="radio" name="tariff" class="sr-only">
<p class="text-sm font-semibold text-slate-900">Компания</p>
<p class="mt-1 text-xs text-slate-600">по договору</p>
</label>
</div>
Настоящий input скрыт классом sr-only: он остаётся
доступным для клавиатуры и скринридера, но не виден. Всё оформление —
на карточке через has-checked:.
5. Аккордеон на теге details
Как работает аккордеон без JavaScript? ▾
Тег details хранит состояние сам, а вариант group-open позволяет оформить содержимое и стрелку в зависимости от того, открыт блок или нет.
<details class="group max-w-md rounded-lg border border-slate-200 bg-white p-4">
<summary class="flex cursor-pointer list-none items-center justify-between text-sm font-medium text-slate-900">
Как работает аккордеон без JavaScript?
<span class="text-slate-400 transition-transform group-open:rotate-180">▾</span>
</summary>
<p class="mt-3 text-sm text-slate-600">
Тег details хранит состояние сам, а вариант group-open позволяет оформить
содержимое и стрелку в зависимости от того, открыт блок или нет.
</p>
</details>
6. Меню с состоянием в атрибуте
<div class="max-w-xs rounded-lg border border-slate-200 bg-white p-1">
<button data-state="open" aria-expanded="true"
class="flex w-full items-center justify-between rounded-md px-3 py-2 text-sm text-slate-700
aria-expanded:bg-slate-100 aria-expanded:font-medium aria-expanded:text-slate-900">
Настройки
<span class="text-slate-400 transition-transform data-[state=open]:rotate-180" data-state="open">▾</span>
</button>
<div class="mt-1 space-y-1 ps-3">
<a class="block rounded-md px-3 py-1.5 text-sm text-slate-600 hover:bg-slate-100" href="#">Профиль</a>
<a class="block rounded-md px-3 py-1.5 text-sm text-slate-600 hover:bg-slate-100" href="#">Уведомления</a>
</div>
</div>
Приложение переключает атрибут, а внешний вид описан классами. Это тот же принцип, по которому устроены компоненты shadcn/ui.