В разделе content вашего файла tailwind.config.js вы настраиваете пути ко всем вашим HTML-шаблонам, компонентам JavaScript и любым другим исходным файлам, которые содержат имена классов Tailwind.

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{html,js}',
    './components/**/*.{html,js}',
  ],
  // ...
}

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


Настройка исходных путей

Tailwind CSS работает, сканируя все ваши компоненты HTML, JavaScript и любые другие файлы шаблонов на предмет имен классов, а затем генерируя все соответствующие CSS для этих стилей.

Чтобы Tailwind мог сгенерировать весь необходимый вам CSS, он должен знать о каждом отдельном файле в вашем проекте, который содержит любые имена классов Tailwind.

Сконфигурируйте пути ко всем вашим файлам содержимого в разделе content вашего файла конфигурации:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{html,js}',
    './components/**/*.{html,js}'
  ],
  // ...
}

Пути настроены как glob patterns, что упрощает сопоставление всех файлов содержимого в вашем проекте без тонкой настройки:

  • Используйте * для соответствия чему угодно, кроме косой черты и скрытых файлов
  • Используйте **, чтобы сопоставить ноль или более каталогов
  • Используйте разделенные запятыми значения между {} для сопоставления со списком параметров

Tailwind использует скрытую библиотеку fast-glob — ознакомьтесь с их документацией, чтобы узнать о других поддерживаемых функциях шаблонов.

Пути относятся к корню вашего проекта, а не к вашему файлу tailwind.config.js, поэтому, если ваш файл tailwind.config.js находится в произвольном месте, вы все равно должны указывать свои пути относительно корня вашего проекта.

Рекомендации по шаблону

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

Если вы используете действительно широкий шаблон, подобный этому, Tailwind даже просканирует node_modules на предмет содержимого, которое, вероятно, не то, что вам нужно:

Не используйте слишком широкие шаблоны

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './**/*.{html,js}',
  ],
  // ...
}

Если у вас есть какие-либо файлы, которые вам нужно просканировать, которые находятся в корне вашего проекта (часто это файл index.html), перечислите этот файл отдельно, чтобы другие ваши шаблоны могли быть более конкретными:

Будьте конкретны с вашими шаблонами контента

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './components/**/*.{html,js}',
    './pages/**/*.{html,js}',
    './index.html',
  ],
  // ...
}

Некоторые фреймворки скрывают свою основную точку входа HTML в другом месте, чем остальные ваши шаблоны (часто public/index.html), поэтому, если вы добавляете классы Tailwind в этот файл, убедитесь, что он также включен в вашу конфигурацию:

Не забудьте указать точку входа HTML, если применимо

tailwind.config.js
module.exports = {
  content: [
    './public/index.html',
    './src/**/*.{html,js}',
  ],
  // ...
}

Если у вас есть какие-либо файлы JavaScript, которые манипулируют вашим HTML для добавления классов, убедитесь, что вы также включили их:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    // ...
    './src/**/*.js',
  ],
  // ...
}
src/spaghetti.js
// ...
menuButton.addEventListener('click', function () {
  let classList = document.getElementById('nav').classList
  classList.toggle('hidden')
  classList.toggle('block')
})
// ...

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

Никогда не включайте файлы CSS в конфигурацию вашего контента

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './src/**/*.css',
  ],
  // ...
}

Детальное определение класса

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

Например, вот некоторый HTML-код, в котором каждая строка потенциального имени класса отдельно выделена:

<div class="md:flex">
  <div class="md:flex-shrink-0">
    <img class="rounded-lg md:w-56" src="/img/shopping.jpg" alt="Woman paying for a purchase">
  </div>
  <div class="mt-4 md:mt-0 md:ml-6">
    <div class="uppercase tracking-wide text-sm text-indigo-600 font-bold">
      Marketing
    </div>
    <a href="/get-started" class="block mt-1 text-lg leading-tight font-semibold text-gray-900 hover:underline">
      Finding customers for your new business
    </a>
    <p class="mt-2 text-gray-600">
      Getting a new business off the ground is a lot of hard work.
      Here are five ideas you can use to find your first customers.
    </p>
  </div>
</div>

Мы не ограничиваем наш поиск только атрибутами class="...", потому что вы можете использовать классы где угодно, например, в некотором JavaScript для переключения меню:

spaghetti.js
<script>
  menuButton.addEventListener('click', function () {
    let classList = document.getElementById('nav').classList
    classList.toggle('hidden')
    classList.toggle('block')
  })
</script>

Используя этот очень простой подход, Tailwind чрезвычайно надежно работает с любым языком программирования, например, с JSX:

Button.jsx
const sizes = {
  md: 'px-4 py-2 rounded-md text-base',
  lg: 'px-5 py-3 rounded-lg text-lg',
}

const colors = {
  indigo: 'bg-indigo-500 hover:bg-indigo-600 text-white',
  cyan: 'bg-cyan-600 hover:bg-cyan-700 text-white',
}

export default function Button({ color, size, children }) {
  let colorClasses = colors[color]
  let sizeClasses = sizes[size]

  return (
    <button type="button" className={`font-bold ${sizeClasses} ${colorClasses}`}>
      {children}
    </button>
  )
}

Имена динамических классов

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

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

Не создавайте имена классов динамически

<div class="text-{{ error ? 'red' : 'green' }}-600"></div>

В приведенном выше примере строки text-red-600 и text-green-600 не существуют, поэтому Tailwind не будет генерировать эти классы.

Вместо этого убедитесь, что все имена классов, которые вы используете, существуют полностью:

Всегда используйте полные имена классов

<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>

Если вы используете библиотеку компонентов, такую как React или Vue, это означает, что вы не должны использовать свойства для динамического создания классов:

Не используйте свойства для динамического построения имен классов

function Button({ color, children }) {
  return (
    <button className={`bg-${color}-600 hover:bg-${color}-500 ...`}>
      {children}
    </button>
  )
}

Вместо этого сопоставьте свойства с полными именами классов, которые статически обнаруживаются во время сборки:

Всегда сопоставляйте свойства со статическими именами классов

function Button({ color, children }) {
  const colorVariants = {
    blue: 'bg-blue-600 hover:bg-blue-500',
    red: 'bg-red-600 hover:bg-red-500',
  }

  return (
    <button className={`${colorVariants[color]} ...`}>
      {children}
    </button>
  )
}

Это дает дополнительное преимущество, позволяя сопоставлять разные значения свойств с разными цветовыми оттенками, например:

function Button({ color, children }) {
  const colorVariants = {
    blue: 'bg-blue-600 hover:bg-blue-500 text-white',
    red: 'bg-red-500 hover:bg-red-400 text-white',
    yellow: 'bg-yellow-300 hover:bg-yellow-400 text-black',
  }

  return (
    <button className={`${colorVariants[color]} ...`}>
      {children}
    </button>
  )
}

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

Работа со сторонними библиотеками

Если вы работаете с какими-либо сторонними библиотеками (например, Select2) и оформляете эту библиотеку с помощью собственного пользовательского CSS, мы рекомендуем писать эти стили без использования функции Tailwind @layer:

main.css
@tailwind base;
@tailwind components;

.select2-dropdown {
  @apply rounded-b-lg shadow-md;
}
.select2-search {
  @apply border border-gray-300 rounded;
}
.select2-results__group {
  @apply text-lg font-bold text-gray-900;
}
/* ... */

@tailwind utilities;

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

Если вы создали свой собственный многоразовый набор компонентов, стилизованных под Tailwind, и импортируете их в несколько проектов, обязательно сконфигурируйте Tailwind для сканирования этих компонентов на предмет имен классов:

tailwind.config.js
module.exports = {
  content: [
    './components/**/*.{html,js}',
    './pages/**/*.{html,js}',
    './node_modules/@my-company/tailwind-components/**/*.js',
  ],
  // ...
}

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

Если вы работаете в монорепозитории с рабочими пространствами, вам может понадобиться использовать require.resolve, чтобы убедиться, что Tailwind может видеть ваши файлы содержимого:

tailwind.config.js
  const path = require('path');

  module.exports = {
    content: [
      './components/**/*.{html,js}',
      './pages/**/*.{html,js}',
>     path.join(path.dirname(require.resolve('@my-company/tailwind-components')), '**/*.js'),
    ],
    // ...
  }

Использование относительных путей

По умолчанию Tailwind разрешает неабсолютные пути к содержимому относительно текущего рабочего каталога, а не файла tailwind.config.js. Это может привести к неожиданным результатам, если вы запустите Tailwind из другого каталога.

Чтобы всегда разрешать пути относительно файла tailwind.config.js, используйте нотацию объекта для вашей конфигурации content и установите для свойства relative значение true:

tailwind.config.js
module.exports = {
  content: {
    relative: true,
    files: [
      './pages/**/*.{html,js}',
      './components/**/*.{html,js}',
    ],
  },
  // ...
}

Скорее всего, это станет поведением по умолчанию в следующей основной версии фреймворка.

Настройка необработанного контента

Если по какой-либо причине вам нужно настроить Tailwind на сканирование необработанного содержимого, а не содержимого файла, используйте объект с ключом raw вместо пути:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{html,js}',
    './components/**/*.{html,js}',
    { raw: '<div class="font-bold">', extension: 'html' },
  ],
  // ...
}

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


Классы безопасных списков

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

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

Если вам нужно убедиться, что Tailwind генерирует определенные имена классов, которых нет в ваших файлах содержимого, используйте опцию safelist:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{html,js}',
    './components/**/*.{html,js}',
  ],
  safelist: [
    'bg-red-500',
    'text-3xl',
    'lg:text-4xl',
  ]
  // ...
}

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

Использование регулярных выражений

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

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{html,js}',
    './components/**/*.{html,js}',
  ],
  safelist: [
    'text-2xl',
    'text-3xl',
    {
      pattern: /bg-(red|green|blue)-(100|200|300)/,
    },
  ],
  // ...
}

Шаблоны могут совпадать только с базовыми именами утилит, такими как /bg-red-.+/, и не будут совпадать, если шаблон включает модификатор варианта, такой как /hover:bg-red-.+/.

Если вы хотите заставить Tailwind генерировать варианты для любых совпадающих классов, включите их, используя опцию variants:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{html,js}',
    './components/**/*.{html,js}',
  ],
  safelist: [
    'text-2xl',
    'text-3xl',
    {
      pattern: /bg-(red|green|blue)-(100|200|300)/,
      variants: ['lg', 'hover', 'focus', 'lg:hover'],
    },
  ],
  // ...
}

Отказ от классов

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

Например, этот HTML-код по-прежнему будет генерировать класс container, даже если этот класс на самом деле не используется:

<div class="text-lg leading-8 text-gray-600">
  Каждый индивидуальный бассейн, который мы проектируем, начинается как подержанный транспортный контейнер,
  модернизируется с использованием самых современных технологий и отделки,
  чтобы превратить его в красивый и функциональный способ развлекать ваших гостей в течение всего лета.
</div>

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

В этих ситуациях вы можете использовать параметр blocklist, чтобы указать Tailwind игнорировать определенные классы, которые он обнаруживает в вашем контенте:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{html,js}',
    './components/**/*.{html,js}',
  ],
  blocklist: [
    'container',
    'collapse',
  ],
  // ...
}

Параметр blocklist влияет только на CSS, который будет создан Tailwind, а не на пользовательский CSS, который вы создали сами или импортируете из другой библиотеки.

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


Преобразование исходных файлов

Если вы создаете контент в формате, который компилируется в HTML (например, Markdown), часто имеет смысл скомпилировать этот контент в HTML перед сканированием его на предмет имен классов.

Используйте опцию content.transform для преобразования любого контента, соответствующего определенному расширению файла, перед извлечением классов:

tailwind.config.js
const remark = require('remark')

module.exports = {
  content: {
    files: ['./src/**/*.{html,md}'],
    transform: {
      md: (content) => {
        return remark().process(content)
      }
    }
  },
  // ...
}

При использовании content.transform, вам необходимо указать исходные пути, используя content.files вместо массива верхнего уровня в разделе content.


Настройка логики извлечения

Используйте опцию extract, чтобы переопределить логику, которую Tailwind использует для определения имен классов для определенных расширений файлов:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: {
    files: ['./src/**/*.{html,wtf}'],
    extract: {
      wtf: (content) => {
        return content.match(/[^<>"'`\s]*/g)
      }
    }
  },
  // ...
}

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

Как и при преобразовании, при использовании content.extract, вам необходимо указать исходные пути, используя content.files вместо массива верхнего уровня в разделе content.


Устранение неполадок

Классы не генерируются

Если Tailwind не генерирует классы, убедитесь, что ваша конфигурация content верна и соответствует всем правильным исходным файлам.

Распространенной ошибкой является отсутствие расширения файла, например, если вы используете jsx вместо js для ваших компонентов React:

tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{html,js}',
    './src/**/*.{html,js,jsx}'
  ],
  // ...
}

Или создать новую папку в середине проекта, которая изначально не была охвачена, и забыть добавить ее в свою конфигурацию:

tailwind.config.js
module.exports = {
  content: [
    './pages/**/*.{html,js}',
    './components/**/*.{html,js}',
    './util/**/*.{html,js}'
  ],
  // ...
}

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

Не создавайте имена классов динамически

<div class="text-{{ error ? 'red' : 'green' }}-600"></div>

Убедитесь, что вы всегда используете полные имена классов в своем коде:

Всегда используйте полные имена классов

<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>

Прочтите нашу документацию по именам динамических классов для получения более подробной информации.

Стили перестраиваются в бесконечном цикле

Если кажется, что ваш CSS перестраивается в бесконечном цикле, есть большая вероятность, что ваш инструмент сборки не поддерживает параметр glob при регистрации зависимостей PostCSS.

Многие инструменты сборки (например, webpack) не поддерживают эту опцию, и в результате мы можем сказать им только смотреть определенные файлы или все каталоги. Например, мы не можем указать webpack только смотреть файлы *.html в каталоге.

Это означает, что если создание вашего CSS приводит к изменению любых файлов в этих каталогах, будет запущена перестройка, даже если измененный файл не соответствует расширению в вашем glob.

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    // С некоторыми инструментами сборки ваш CSS будет перестраиваться
    // каждый раз, когда *любой* файл в `src` изменяется.
    './src/**/*.{html,js}',
  ],
  // ...
}

Итак, если вы следите за изменениями в src/**/*.html, но записываете выходной файл CSS в src/css/styles.css, вы получите бесконечный цикл перестройки с использованием некоторых инструментов.

В идеале мы могли бы предупредить вас об этом в консоли, но многие инструменты прекрасно это поддерживают (включая наш собственный инструмент CLI), и у нас нет надежного способа определить, какой инструмент сборки вы используете.

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

tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{html,js}',
    './src/pages/**/*.{html,js}',
    './src/components/**/*.{html,js}',
    './src/layouts/**/*.{html,js}',
    './src/index.html',
  ],
  // ...
}

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

Если вы абсолютно не можете изменить конфигурацию содержимого или структуру каталогов, лучше всего скомпилировать CSS отдельно с помощью инструмента, который имеет полную поддержку glob. Мы рекомендуем использовать Tailwind CLI, который представляет собой быстрый, простой, специально созданный инструмент для компиляции вашего CSS с помощью Tailwind.

Это просто не работает должным образом

Если у вас возникли странные, трудно описываемые проблемы с выходными данными или что-то просто не кажется, что они вообще работают, есть большая вероятность, что это связано с тем, что ваш инструмент сборки не поддерживает сообщения о зависимостях PostCSS должным образом (или вообще). Одним из известных примеров этого в настоящее время является Stencil.

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

Вы можете использовать такие пакеты, как npm-run-all или concurrently, чтобы скомпилировать ваш CSS вместе с вашей обычной командой разработки, добавив несколько скриптов в ваш проект, например:

// package.json
{
  // ...
  "scripts": {
    "start": "concurrently \"npm run start:css\" \"react-scripts start\"",
    "start:css": "tailwindcss -o src/tailwind.css --watch",
    "build": "npm run build:css && react-scripts build",
    "build:css": "NODE_ENV=production tailwindcss -o src/tailwind.css -m",
  },
}

В любом случае обязательно проверьте наличие существующей проблемы или откройте новую, чтобы мы могли выяснить проблему и попытаться улучшить совместимость с любым инструментом, который вы используете.