Основные концепции
Управление дублированием и создание многоразовых абстракций.
Tailwind поощряет рабочий процесс сначала утилиты, когда проекты реализуются с использованием только низкоуровневых служебных классов. Это мощный способ избежать преждевременной абстракции и связанных с ней болевых точек.
Но, конечно, по мере роста проекта вы неизбежно будете повторять стандартные комбинации утилит для воссоздания одного и того же дизайна во многих разных местах.
Например, в приведенном ниже шаблоне вы можете видеть, что служебные классы для каждого изображения аватара повторяются пять раз:
<div>
<div class="flex items-center space-x-2 text-base">
<h4 class="font-semibold text-slate-900">Контрибьюторы</h4>
<span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700">204</span>
</div>
<div class="mt-3 flex -space-x-2 overflow-hidden">
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80" alt=""/>
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
</div>
<div class="mt-3 text-sm font-medium">
<a href="#" class="text-blue-500">+ 198 других</a>
</div>
</div>
Не паникуйте! В этом руководстве вы узнаете о различных стратегиях повторного использования стилей в вашем проекте, а также о лучших практиках, когда использовать каждый из них.
В большинстве случаев подобное дублирование даже не является реальной проблемой, потому что все это находится в одном месте или даже не существует на самом деле, потому что вы выполняете итерацию по массиву элементов и пишете разметку только один раз.
Если стили, которые вам нужно повторно использовать, нужно повторно использовать только в одном файле, редактирование с несколькими курсорами и циклы - самый простой способ справиться с любым дублированием.
Если дублирование локализовано в группе элементов в одном файле, самый простой способ справиться с ним — использовать редактирование несколькими курсорами для быстрого выбора и редактирования списка классов для каждого элемента одновременно:
<nav class="flex justify-center space-x-4">
<a href="/dashboard" class="font-medium px-3 py-2 text-slate-700 rounded-lg hover:bg-slate-100 hover:text-slate-900">Главная</a>
<a href="/team" class="font-medium px-3 py-2 text-slate-700 rounded-lg hover:bg-slate-100 hover:text-slate-900">Команда</a>
<a href="/projects" class="font-medium px-3 py-2 text-slate-700 rounded-lg hover:bg-slate-100 hover:text-slate-900">Проекты</a>
<a href="/reports" class="font-medium px-3 py-2 text-slate-700 rounded-lg hover:bg-slate-100 hover:text-slate-900">Отчеты</a>
</nav>
Вы будете удивлены, узнав, как часто это оказывается лучшим решением. Если вы можете быстро редактировать все дублированные списки классов одновременно, нет никакой пользы от введения какой-либо дополнительной абстракции.
Прежде чем вы предположите, что вам нужно будет извлечь компонент или создать собственный класс для чего-либо, убедитесь, что вы фактически используете его более одного раза в своем шаблоне.
Часто элемент дизайна, который появляется более одного раза на отображаемой странице, фактически создается только один раз, потому что фактическая разметка отображается в цикле.
Например, дубликаты аватаров в начале этого руководства почти наверняка будут отрисованы в цикле в реальном проекте:
<div>
<div class="flex items-center space-x-2 text-base">
<h4 class="font-semibold text-slate-900">Контрибьюторы</h4>
<span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700">204</span>
</div>
<div class="mt-3 flex -space-x-2 overflow-hidden">
{#each Контрибьюторы as user}
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="{user.avatarUrl}" alt="{user.handle}"/>
{/each}
</div>
<div class="mt-3 text-sm font-medium">
<a href="#" class="text-blue-500">+ 198 других</a>
</div>
</div>
Вы можете даже переписать пример навигации, используя loop
или map
, если хотите:
<nav className="flex sm:justify-center space-x-4">
{[
['Home', '/dashboard'],
['Team', '/team'],
['Projects', '/projects'],
['Reports', '/reports'],
].map(([title, url]) => (
<a href={url} className="rounded-lg px-3 py-2 text-slate-700 font-medium hover:bg-slate-100 hover:text-slate-900">{title}</a>
))}
</nav>
Когда элементы отображаются в цикле, подобном этому, фактический список классов записывается только один раз, поэтому проблема дублирования отсутствует.
Если вам нужно повторно использовать некоторые стили в нескольких файлах, лучшая стратегия - создать компонент, если вы используете интерфейсную структуру, такую как React, Svelte или Vue, или частичный шаблон, если вы используете язык шаблонов, например Blade, ERB, Twig или Nunjucks.
<template>
<div>
<img class="rounded" :src="img" :alt="imgAlt">
<div class="mt-2">
<div>
<div class="text-xs text-slate-600 uppercase font-bold tracking-wider">{{ eyebrow }}</div>
<div class="font-bold text-slate-700 leading-snug">
<a :href="url" class="hover:underline">{{ title }}</a>
</div>
<div class="mt-2 text-sm text-slate-600">{{ pricing }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['img', 'imgAlt', 'eyebrow', 'title', 'pricing', 'url']
}
</script>
Теперь вы можете использовать этот компонент в любом количестве мест, сохраняя при этом единый источник достоверной информации о стилях, чтобы их можно было легко обновлять вместе в одном месте.
Если компонент не является одним элементом HTML, информация, необходимая для его определения, не может быть записана только в CSS. Для всего, даже отдаленно сложного, структура HTML так же важна, как и CSS.
Не полагайтесь на классы CSS для извлечения сложных компонентов
У вас есть новое сообщение!
<!-- Даже с настраиваемым CSS вам все равно нужно дублировать эту HTML-структуру -->
<div class="chat-notification">
<div class="chat-notification-logo-wrapper">
<img class="chat-notification-logo" src="/img/logo.svg" alt="ChitChat Logo">
</div>
<div class="chat-notification-content">
<div class="chat-notification-title">ChitChat</div>
<p class="chat-notification-message">У вас есть новое сообщение!</p>
</div>
</div>
<style>
.chat-notification { /* ... */ }
.chat-notification-logo-wrapper { /* ... */ }
.chat-notification-logo { /* ... */ }
.chat-notification-content { /* ... */ }
.chat-notification-title { /* ... */ }
.chat-notification-message { /* ... */ }
</style>
Даже если вы создаете классы для различных элементов в таком компоненте, вам все равно придется дублировать HTML каждый раз, когда вы хотите использовать этот компонент. Конечно, вы можете обновить размер шрифта для каждого экземпляра в одном месте, но что, если вам нужно превратить заголовок в ссылку?
Компоненты и части шаблона решают эту проблему намного лучше, чем абстракции только с помощью CSS, потому что компонент может инкапсулировать HTML и стили. Изменить размер шрифта для каждого экземпляра так же просто, как и с помощью CSS, но теперь вы также можете превратить все заголовки в ссылки в одном месте.
Создайте частичный шаблон или компонент JavaScript
У вас есть новое сообщение!
function Notification({ imageUrl, imageAlt, title, message }) {
return (
<div className="p-6 max-w-sm mx-auto bg-white rounded-xl shadow-md flex items-center space-x-4">
<div className="shrink-0">
<img className="h-12 w-12" src={imageUrl.src} alt={imageAlt}>
</div>
<div>
<div className="text-xl font-medium text-black">{title}</div>
<p className="text-slate-500">{message}</p>
</div>
</div>
)
}
Когда вы создаете компоненты и части шаблона, подобные этому, нет причин использовать что-либо, кроме служебных классов, потому что у вас уже есть единственный источник истинности для стилей.
Если вы используете традиционный язык шаблонов, такой как ERB или Twig, создание партиала шаблона для чего-то такого маленького, как кнопка, может показаться излишним по сравнению с простым классом CSS, таким как btn
.
Хотя настоятельно рекомендуется создавать правильные части шаблона для более сложных компонентов, вы можете использовать директиву Tailwind @apply
для извлечения повторяющихся служебных шаблонов в пользовательские классы CSS, когда часть шаблона кажется неуклюжей.
Вот как может выглядеть класс btn-primary
при использовании @apply
для его составления из существующих утилит:
<!-- Before extracting a custom class -->
<button class="py-2 px-5 bg-violet-500 text-white font-semibold rounded-full shadow-md hover:bg-violet-700 focus:outline-none focus:ring focus:ring-violet-400 focus:ring-opacity-75">
Сохранить изменения
</button>
<!-- После извлечения настраиваемого класса -->
<button class="btn-primary">
Сохранить изменения
</button>
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn-primary {
@apply py-2 px-5 bg-violet-500 text-white font-semibold rounded-full shadow-md hover:bg-violet-700 focus:outline-none focus:ring focus:ring-violet-400 focus:ring-opacity-75;
}
}
Узнайте больше о @apply
и @layer
в документации Функции и директивы.
Что бы вы ни делали, не используйте @apply
только для того, чтобы все выглядело “чище”. Да, HTML-шаблоны, заваленные классами Tailwind, довольно уродливы. Вносить изменения в проект с множеством пользовательских стилей CSS еще хуже.
Если вы начнете использовать @apply
для всего, вы просто снова напишете CSS и отбросите все преимущества рабочего процесса и удобства обслуживания, которые дает Tailwind, например:
Если вы собираетесь использовать @apply
, используйте его для очень маленьких, многоразовых вещей, таких как кнопки и элементы управления формы - и даже тогда, только если вы не используете фреймворк, такой как React, где компонент был бы лучшим выбором.