CColorWell
Get ColorWell

ColorWell/Guides

Change Text Color in Tailwind CSS

Learn how to set text colors in Tailwind CSS using utility classes, custom hex values, and dynamic tokens for consistent design systems.

October 15, 2026 · 4 min read

Change text color in Tailwind CSS by applying utility classes like text-gray-900 for built-in colors, or text-[#1a1a1a] for arbitrary hex values directly in your HTML. For reusable custom colors, define them in your tailwind.config.js file and reference them via their token name.

Tailwind’s default palette covers most needs, but real projects often require specific brand colors or accessible contrast combinations that don’t match the defaults. This guide walks through the three ways to set text color, focusing on how to set up a consistent system that works across light and dark modes.

Why Consistent Text Colors Matter

Text color dictates readability and hierarchy. Using inconsistent hex codes scattered throughout your HTML creates maintenance debt and visual noise. A defined system ensures that headings, body text, and captions always have sufficient contrast against their background. When you define colors once in your configuration, every component inherits that consistency automatically. This approach prevents subtle drift where one button uses #333 and another uses #333333, which can look slightly different on certain screens.

Using Built-in Tailwind Color Utilities

Tailwind ships with a comprehensive default palette. These classes follow a pattern: text-{color}-{shade}. The shade number indicates intensity, with lower numbers being lighter and higher numbers darker. For example, text-gray-500 is a medium gray suitable for secondary text, while text-gray-900 is nearly black for primary headings.

Tailwind requires explicit responsive modifiers to change colors across breakpoints; it does not automatically adapt colors based on screen size without configuration. You can combine these modifiers to set different colors for different screen widths, such as using md:text-blue-500 to apply blue text only on medium screens and larger.

<h1 class="text-gray-900">Main Heading</h1>
<p class="text-gray-600">Supporting text that needs less visual weight.</p>
<span class="text-blue-600">Action link</span>

The default palette includes slate, gray, zinc, neutral, and stone for neutrals, plus red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, and rose. Choosing one neutral family and sticking to it prevents mixed-tone inconsistencies.

Setting Custom Hex Values Directly

Sometimes you need a specific color that isn’t in the default palette, such as a brand-specific accent. Tailwind supports arbitrary values using square brackets. This is useful for one-off tweaks or quick prototypes, but for production apps, named tokens are cleaner.

<p class="text-[#0f172a]">This text uses a specific slate blue hex code.</p>
<span class="text-[rgba(15,23,42,0.7)]">Semi-transparent text for subtle labels.</span>

The syntax is straightforward: text-[value]. The value inside the brackets can be any valid CSS color format, including hex, RGB, RGBA, or HSL. Note that spaces inside the brackets must be removed or handled carefully depending on your build setup; typically, compact formats like rgba(0,0,0,0.5) work best. This method bypasses the configuration file entirely, so if you change the brand color later, you must update every instance manually.

Defining Custom Colors in tailwind.config.js

For scalable projects, define your colors in tailwind.config.js. This creates named tokens that are reusable and easy to update globally. Let’s walk through setting up a 'brand-primary' color and applying it.

First, open your tailwind.config.js. Inside the theme.extend.colors object, add your custom values. We will use a deep blue for headings and a slightly lighter blue for links.

/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          primary: '#0f172a',   // Deep blue for headings
          secondary: '#1e293b', // Lighter blue for body text
          accent: '#3b82f6',    // Bright blue for links
        },
      },
    },
  },
  plugins: [],
}

Now, use these tokens in your HTML. The class name follows the pattern text-{namespace}-{token}.

<h2 class="text-brand-primary">Welcome to Our Service</h2>
<p class="text-brand-secondary">This paragraph uses the secondary brand color for readability.</p>
<a href="#" class="text-brand-accent">Learn more</a>

This setup ensures that if you decide to lighten the primary color, you change one hex code in the config file, and every heading across your site updates automatically. This is far more maintainable than repeating hex codes in HTML classes.

If you are starting from scratch with brand colors, ensuring they harmonize and meet contrast standards can be tedious. Tools like ColorWell can generate harmonized palettes from a single base hex value, verify WCAG contrast ratios instantly, and export the result directly as a Tailwind config block. This saves the manual step of balancing hues and checking accessibility for every text-background pair.

Handling Dark Mode Text Colors

Dark mode requires different color values because light text on dark backgrounds needs adjusted contrast. Tailwind handles this with the dark: prefix. You define the light mode color first, then override it for dark mode.

<p class="text-gray-900 dark:text-gray-100">
  This text is dark gray on light backgrounds and light gray on dark backgrounds.
</p>

When using custom colors from your config, the same logic applies. You can define separate dark-mode variants in your config if needed, but often one set of colors works if chosen carefully. For example, if your brand-primary is #0f172a (very dark), it might be hard to read on a dark gray background. In such cases, define a lighter variant in your config.

colors: {
  brand: {
    primary: '#0f172a',
    'primary-dark': '#e2e8f0', // Lighter version for dark mode backgrounds
  },
},

Then apply it:

<h1 class="text-brand-primary dark:text-brand-primary-dark">Heading</h1>

This explicit control ensures readability in both modes without relying on automatic inversion, which can sometimes wash out colors.

Best Practices for Readable Typography

Choosing text colors is about balance. Too much contrast causes eye strain; too little makes text hard to read. Aim for a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (above 18px or bold 14px).

Use a hierarchy:

Avoid pure black (#000000) on pure white (#ffffff) for long-form reading, as it can cause halation for some users. A very dark gray like #111827 is often softer and equally readable. Tailwind’s gray-900 provides a high-contrast option that is slightly softer than pure black, making it suitable for primary text on light backgrounds.

When building your palette, verify each combination. If you are unsure which shades pair well, generating a palette from your brand color ensures that light and dark variants maintain hue consistency. Perceptual color spaces, such as Oklch, help keep hues stable when lightening or darkening colors, preventing the washed-out look that happens with simple HSL adjustments. This consistency makes your typography look intentional and professional across all components.

Do it in ColorWell

Everything in this guide works in the browser — open the tool and try it on your own input.

Open ColorWell →

Questions people also ask

How do I change text color on hover in Tailwind?

Use the `hover:` variant prefix before your color class, such as `hover:text-blue-600`. This applies the specified color only when the element is hovered, allowing for dynamic interaction states without custom CSS.

Can I use hex codes directly in Tailwind classes?

Yes, you can use arbitrary values by wrapping the hex code in square brackets, like `text-[#1a1a1a]`. This is ideal for one-off adjustments or specific brand colors that aren't defined in your configuration file.

How do I ensure my text color passes WCAG contrast checks?

It depends on the background color and the specific shade chosen, but generally, darker shades like `gray-900` on light backgrounds or lighter shades like `gray-100` on dark backgrounds provide sufficient contrast. You should verify specific combinations using a contrast checker to ensure they meet the required ratio for your audience's needs.

What is the difference between text-gray-900 and text-black?

`text-gray-900` is a very dark gray with a slight tint, while `text-black` is pure black (#000000). Gray shades often feel softer and more readable on screens, whereas pure black can sometimes appear harsh or cause eye strain depending on the display.

More guides