Урок 18. Частые ошибки
⚡ Кратко
Забытый cn(), замена примитива на div, обёртки
вместо правки, потерянные data-slot, злоупотребление
!.
Ошибка 1. className не доходит до элемента
Как ломается:
<Button className="w-full"> не растягивает кнопку.
❌ Пропущен className
function Button({ variant, size, ...props }) {
return <button className={cn(buttonVariants({ variant, size }))} {...props} />
}✅ className участвует в слиянии
function Button({ className, variant, size, ...props }) {
return <button className={cn(buttonVariants({ variant, size, className }))} {...props} />
}Почему коварно: в первом варианте className
попадёт в ...props и перезапишет весь атрибут целиком — кнопка
потеряет вообще все базовые стили.
Ошибка 2. Замена примитива на обычный тег
Как ломается: визуально то же самое, но пропадает поведение: диалог не закрывается по Esc, меню не ходит по стрелкам, фокус уходит на страницу под окном.
Как правильно: менять оформление — сколько угодно. Менять слой поведения — только если вы готовы реализовать его сами и протестировать с клавиатурой и скринридером.
Ошибка 3. Обёртка вместо правки
❌ Обёртка поверх компонента
function BrandButton(props) {
return (
<div className="[&_button]:bg-brand [&_button]:text-white">
<Button {...props} />
</div>
)
}✅ Вариант внутри компонента
// в buttonVariants
brand: "bg-brand text-brand-foreground hover:opacity-90"Почему: обёртка добавляет лишний узел в DOM, ломает
раскладку (лишний div во flex-контейнере) и делает поведение
непредсказуемым. Файл ваш — правьте его.
Ошибка 4. Потерянные data-slot при рефакторинге
Как ломается: карточка перестаёт
убирать нижний отступ при наличии футера, потому что селектор
has-data-[slot=card-footer] больше ничего не находит.
Как правильно: относитесь к data-slot
как к части контракта компонента. Переименовали — проверьте все селекторы,
которые на него ссылаются.
Ошибка 5. Злоупотребление модификатором !
❌ Пробиваем всё подряд
<Button className="bg-emerald-600! px-8! text-white!">Кнопка</Button>✅ Обычные классы: cn() и так их применит
<Button className="bg-emerald-600 px-8 text-white">Кнопка</Button>Почему: если cn() настроен правильно,
конфликтующие базовые классы просто удаляются. Появление !
обычно означает, что где-то потерялся cn().
Ошибка 6. Копирование компонента вместо варианта
Понадобилась кнопка с другим фоном — и в проекте появляется
button-2.tsx. Через полгода их пять, и правка фокуса
не доезжает до четырёх из них. Новый внешний вид — это вариант,
а не новый компонент.