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:
- Primary Text: Highest contrast, used for headings and key information. Often near-black or dark gray.
- Secondary Text: Medium contrast, used for descriptions and metadata. Often medium gray.
- Accent Text: High saturation, used for links and buttons. Ensure this color also meets contrast requirements against its background.
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.