Перейти к содержимому

12. Стилизация

Qwik поддерживает несколько подходов к стилизации: встроенные CSS, CSS Modules, глобальные стили, Tailwind CSS и Vanilla Extract. Каждый подход имеет свои плюсы.

useStylesScoped$ добавляет изолированные CSS стили к компоненту — они применяются только к данному компоненту и его дочерним элементам:

import { component$, useStylesScoped$ } from '@builder.io/qwik';
export const Card = component$(() => {
useStylesScoped$(\`
.card {
background: #1e293b;
border-radius: 12px;
padding: 20px;
}
h2 {
color: #ac7ef4; /* Только h2 внутри этого компонента! */
}
\`);
return (
<div class="card">
<h2>Заголовок карточки</h2>
<p>Контент</p>
</div>
);
});

Qwik автоматически добавляет уникальный атрибут для изоляции:

<div class="card ⭐sx2a3c"> <!-- Уникальный суффикс -->
<h2>Заголовок карточки</h2>
</div>
import { component$, useStyles$ } from '@builder.io/qwik';
export const GlobalLayout = component$(() => {
useStyles$(\`
:root {
--primary: #ac7ef4;
--bg: #0f172a;
}
* { box-sizing: border-box; }
body { background: var(--bg); }
\`);
return <slot />;
});

Qwik поддерживает CSS Modules из коробки:

Button.module.css
.button {
background: #ac7ef4;
color: #0f172a;
border: none;
border-radius: 8px;
padding: 10px 20px;
}
.button.secondary {
background: transparent;
color: #ac7ef4;
border: 1px solid #ac7ef4;
}
import { component$ } from '@builder.io/qwik';
import styles from './Button.module.css';
export const Button = component$<{ variant?: 'primary' | 'secondary' }>((props) => {
return (
<button class={[styles.button, props.variant === 'secondary' && styles.secondary]}>
Click me
</button>
);
});

Tailwind — популярный выбор для Qwik приложений:

Окно терминала
npm run qwik add tailwind

После добавления Tailwind классы работают как обычно:

export const HeroSection = component$(() => {
return (
<section class="min-h-screen bg-slate-950 flex items-center justify-center">
<div class="text-center">
<h1 class="text-5xl font-bold text-purple-400 mb-4">
⚡ Qwik
</h1>
<p class="text-slate-400 text-lg">
Resumable, O(1) загрузка
</p>
</div>
</section>
);
});
// Объект классов
<div class={{
'btn': true,
'btn-primary': variant === 'primary',
'btn-disabled': isDisabled,
}}>
// Массив
<div class={['btn', isActive && 'btn-active', extraClass]}>
// Строка шаблона
<div class={\`btn btn--\${variant}\`}>
export const ThemeButton = component$(() => {
const hue = useSignal(270); // Фиолетовый (Qwik purple)
return (
<div style={{ '--primary-hue': hue.value + 'deg' }}>
<input
type="range"
min={0}
max={360}
value={hue.value}
onInput$={(_, el) => hue.value = +el.value}
/>
<button style={{ background: 'hsl(var(--primary-hue), 70%, 70%)' }}>
Кнопка с динамическим цветом
</button>
</div>
);
});