Capítulo 4 de 29

Chapter 4: Detecting Classes in Source Files

Core Idea

Tailwind scans every project file as plain text looking for tokens that look like class names and generates CSS only for the ones it finds — it never parses your source as code, which is why dynamically-constructed class name strings silently fail to generate CSS.

Key Concepts

  • Plain-text scanning: Tailwind doesn't understand JS/JSX/template syntax — it just tokenizes text for anything matching valid class-name characters, generates CSS for tokens that map to a real utility, and discards the rest.
  • Dynamic class names don't work: string concatenation/interpolation like `text-{{ error ? 'red' : 'green' }}-600` never produces the literal strings text-red-600/text-green-600 in the source, so neither gets generated. Same failure mode for template-literal-built classes in JSX (`bg-${color}-600`).
  • Fix — always use complete, static class names: write out every possible full class name somewhere in the source (e.g. a { blue: "bg-blue-600 hover:bg-blue-500", red: "..." } lookup object keyed by a prop), so each string exists literally and is detected.
  • Default scan scope: every project file except .gitignore-matched files, node_modules, binary files (images/video/zip), CSS files, and common lockfiles.
  • @source "<path>": explicitly register an extra path (relative to the stylesheet) to scan — most commonly needed for a node_modules dependency that ships Tailwind classes but is .gitignored by default.
  • source("<path>") on @import: sets the base path for automatic detection (@import "tailwindcss" source("../src");) — useful in monorepos where the build runs from the repo root instead of each project's root.
  • @source not "<path>": excludes a path from scanning (e.g. a legacy components directory known not to use Tailwind).
  • source(none): disables automatic detection entirely so every source must be registered explicitly via @source — useful when a project has multiple Tailwind stylesheets that should each only include what they individually need.
  • @source inline("..."): force-generates specific classes even if they don't literally appear in any scanned file (safelisting); the input supports brace expansion for variants ({hover:,focus:,}underline) and ranges (bg-red-{50,{100..900..100},950}) to generate whole families at once.
  • @source not inline("..."): the inverse — explicitly blocks specific classes (and their variant combinations) from ever being generated, even if detected in source.

Code Examples

// Works: every possible class name is a complete, static string
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>;
}
@import "tailwindcss";
@source "../node_modules/@acmecorp/ui-lib";
@source not "../src/components/legacy";
@source inline("{hover:,focus:,}bg-red-{50,{100..900..100},950}");
  • What it demonstrates: the static-lookup pattern that keeps dynamic-looking class selection detectable, plus explicit source registration/exclusion and safelisting with brace-expansion ranges.

Key Takeaways

  1. Never build a class name via string interpolation — map props to complete, statically-visible class strings instead.
  2. Reach for @source mainly to pull in a pre-built dependency's Tailwind classes that live in node_modules.
  3. @source inline() is the safelist mechanism — needed when a class genuinely can't appear literally in scanned source (e.g. generated server-side from data Tailwind can't see).
  4. source(none) + explicit @source per stylesheet keeps multiple Tailwind entry points in a project from bloating each other's output.

Connects To

  • Adding Custom Styles: arbitrary values (top-[117px]) are detected the same way — as long as the literal bracket syntax appears in source text, not built dynamically.
  • Compatibility: CSS Modules/component <style> blocks are excluded from scanning by the "CSS files" rule, reinforcing why @apply/var() + @reference is the pattern there instead of relying on class detection.