Tailwind поощряет рабочий процесс сначала утилиты, когда проекты реализуются с использованием только низкоуровневых служебных классов. Это мощный способ избежать преждевременной абстракции и связанных с ней болевых точек.

Но, конечно, по мере роста проекта вы неизбежно будете повторять стандартные комбинации утилит для воссоздания одного и того же дизайна во многих разных местах.

Например, в приведенном ниже шаблоне вы можете видеть, что служебные классы для каждого изображения аватара повторяются пять раз:

Контрибьюторы

204
<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>

Вы будете удивлены, узнав, как часто это оказывается лучшим решением. Если вы можете быстро редактировать все дублированные списки классов одновременно, нет никакой пользы от введения какой-либо дополнительной абстракции.

Циклы

Прежде чем вы предположите, что вам нужно будет извлечь компонент или создать собственный класс для чего-либо, убедитесь, что вы фактически используете его более одного раза в своем шаблоне.

Часто элемент дизайна, который появляется более одного раза на отображаемой странице, фактически создается только один раз, потому что фактическая разметка отображается в цикле.

Например, дубликаты аватаров в начале этого руководства почти наверняка будут отрисованы в цикле в реальном проекте:

Контрибьюторы

204
<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.

Пляж
Частная вилла
$299 долларов за ночь
VacationCard.vue
<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>

Теперь вы можете использовать этот компонент в любом количестве мест, сохраняя при этом единый источник достоверной информации о стилях, чтобы их можно было легко обновлять вместе в одном месте.

По сравнению с абстракциями CSS

Если компонент не является одним элементом HTML, информация, необходимая для его определения, не может быть записана только в CSS. Для всего, даже отдаленно сложного, структура HTML так же важна, как и CSS.

Не полагайтесь на классы CSS для извлечения сложных компонентов

ChitChat

У вас есть новое сообщение!

<!-- Даже с настраиваемым 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">
    <h4 class="chat-notification-title">ChitChat</h4>
    <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

ChitChat

У вас есть новое сообщение!

Notification.jsx
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} alt={imageAlt}>
      </div>
      <div>
        <div className="text-xl font-medium text-black">{title}</div>
        <p className="text-slate-500">{message}</p>
      </div>
    </div>
  )
}

Когда вы создаете компоненты и части шаблона, подобные этому, нет причин использовать что-либо, кроме служебных классов, потому что у вас уже есть единственный источник истинности для стилей.


Извлечение классов с помощью @apply

Если вы используете традиционный язык шаблонов, такой как ERB или Twig, создание партиала шаблона для чего-то такого маленького, как кнопка, может показаться излишним по сравнению с простым классом CSS, таким как btn.

Хотя настоятельно рекомендуется создавать правильные части шаблона для более сложных компонентов, вы можете использовать директиву Tailwind @apply для извлечения повторяющихся служебных шаблонов в пользовательские классы CSS, когда часть шаблона кажется неуклюжей.

Вот как может выглядеть класс btn-primary при использовании @apply для его составления из существующих утилит:

HTML
<!-- Перед извлечением настраиваемого класса -->
<button class="py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75">
  Сохранить изменения
</button>

<!-- После извлечения настраиваемого класса -->
<button class="btn-primary">
  Сохранить изменения
</button>
CSS
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn-primary {
    @apply py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75;
  }
}

Узнайте больше о @apply и @layer в документации Функции и директивы.

Как избежать преждевременного абстрагирования

Что бы вы ни делали, не используйте @apply только для того, чтобы все выглядело “чище”. Да, HTML-шаблоны, заваленные классами Tailwind, довольно уродливы. Вносить изменения в проект с множеством пользовательских стилей CSS еще хуже.

Если вы начнете использовать @apply для всего, вы просто снова напишете CSS и отбросите все преимущества рабочего процесса и удобства обслуживания, которые дает Tailwind, например:

  • Вы должны постоянно придумывать названия классов — ничто не замедлит вас и не истощит вашу энергию, как придумывание имени класса для чего-то, что не заслуживает названия.
  • Вам нужно переключаться между несколькими файлами, чтобы вносить изменения — это намного больше убивает рабочий процесс, чем вы могли подумать, прежде чем собирать все вместе.
  • Смена стилей страшнее — CSS глобален, уверены ли вы, что можете изменить значение минимальной ширины в этом классе, не нарушая чего-либо в другой части сайта?
  • Ваш пакет CSS будет больше — уф.

Если вы собираетесь использовать @apply, используйте его для очень маленьких, многоразовых вещей, таких как кнопки и элементы управления формы - и даже тогда, только если вы не используете фреймворк, такой как React, где компонент был бы лучшим выбором.