CSS Text Color and Alignment
Two of the very first properties every CSS author reaches for are color, which sets the color of an element’s text, and text-align, which controls how that text is positioned horizontally inside its container. They look simple, but both interact with inheritance, the box model, and the difference between inline and block-level boxes in ways that trip up beginners constantly. This lesson covers the full picture: how each property is computed by the browser, every value format you can use, and the mistakes that most often produce "my CSS isn’t working" bug reports.
Overview / How it works
color is an inherited property. When you set it on an element, every descendant that does not have its own color declaration inherits the computed value. This is why setting color once on <body> can style an entire page’s text: the value cascades down the DOM tree unless something more specific overrides it. Internally, the browser resolves whatever color format you used (a keyword, a hex code, a function like rgb()) into a single internal color representation used for painting glyphs during the paint stage of rendering — well after layout has already happened, since color never affects the size or position of a box.
text-align is different: it is also inherited, but it does not paint anything by itself. Instead it is a formatting instruction consumed by the block layout algorithm. It only has an effect on block-level boxes that establish an inline formatting context — that is, elements whose content includes text or inline children, such as <p>, <div>, <li>, or table cells. When the browser lays out the lines of inline content inside such a box, text-align tells it where to position each line within the available width of that box: flush left, flush right, centered, or stretched edge-to-edge (justified). Setting text-align on an inline element like <span> or on the box itself to try to move the box has no effect on the box’s own position — a very common source of confusion covered later in this lesson.
Syntax
selector {
color: value;
text-align: value;
}
| Property | Common values | Inherited? | Affects layout? |
|---|---|---|---|
color |
keyword, hex, rgb(), hsl(), currentColor |
Yes | No (paint only) |
text-align |
left, right, center, justify, start, end |
Yes | Yes (inline layout) |
color value formats
- Keyword —
red,rebeccapurple,transparent. Easy to read, but limited to ~150 named colors. - Hex —
#ff0000or the short form#f00; an 8-digit form like#ff000080adds an alpha channel. - rgb() —
rgb(255 0 0)for opaque, orrgb(255 0 0 / 50%)for 50% opacity. Modern CSS allows the space-separated syntax shown here without commas. - hsl() —
hsl(0 100% 50%)expresses color as hue/saturation/lightness, which is often more intuitive for building color scales by hand. currentColor— a keyword that resolves to the element’s own computedcolorvalue. Useful onborder,box-shadow, orfillso a border automatically matches the text color without repeating a value.
text-align value reference
| Value | Effect |
|---|---|
left |
Lines start at the left edge of the box |
right |
Lines start at the right edge of the box |
center |
Each line is centered within the box’s width |
justify |
Word spacing is stretched so every line except the last fills the full width |
start / end |
Like left/right, but relative to the text direction (logical, so they flip automatically in right-to-left languages) |
Examples
Example 1: Basic color and alignment on a paragraph
p.intro {
color: #2b2b2b;
text-align: left;
}
This applies to a paragraph like <p class="intro">Welcome to the course.</p>.
Result: the paragraph’s text renders in a dark charcoal gray instead of the browser default black, and stays flush against the left edge of its container (the default in left-to-right languages, so this rule is mostly here for explicitness).
This is the simplest possible case: one color value, one alignment value, applied directly to the element that contains the text.
Example 2: Centered heading and a semi-transparent warning message
h2.section-title {
color: hsl(210 70% 40%);
text-align: center;
}
p.warning {
color: rgb(178 34 34 / 90%);
text-align: center;
}
Result: the heading renders in a medium, slightly desaturated blue and is centered horizontally within its own box. The warning paragraph renders in a firebrick red that is very close to fully opaque (90% alpha lets a hint of the background show through), and its text is also centered.
Notice that hsl() and rgb() can both take an optional alpha channel using the / syntax. This is the modern, comma-less function syntax supported by all evergreen browsers.
Example 3: A realistic card component using currentColor and a custom property
:root {
--brand-color: #1a73e8;
}
.card {
border: 2px solid currentColor;
color: var(--brand-color);
padding: 1.5rem;
text-align: justify;
}
.card__title {
color: inherit;
text-align: left;
font-weight: 700;
}
.card__cta {
color: #ffffff;
background-color: var(--brand-color);
text-align: center;
padding: 0.5rem 1rem;
}
This targets a structure like a <div class="card"> containing an <h3 class="card__title"> and a <button class="card__cta">.
Result: the card gets a 2px border painted in the same blue as its text, because currentColor picks up the .card element’s own color: var(--brand-color). Body text inside the card is justified so both edges of each line align. The title explicitly inherits the same blue and stays left-aligned, overriding the justify behavior it would otherwise inherit. The call-to-action button gets solid blue background with white text, centered.
This example shows how currentColor and custom properties reduce repetition: change --brand-color once and both the border and the button background update together.
How it works step by step
- Step 1 — cascade and specificity: the browser collects every
colorandtext-aligndeclaration that could apply to an element, from the user-agent stylesheet, author stylesheets, and inline styles, then keeps the one with the highest specificity (or the one that appears last, if specificity ties). - Step 2 — inheritance: if no rule directly targets an element for
colorortext-align, the browser walks up to the parent and uses its computed value instead. This is why a `` inside a red paragraph is red by default even with zero CSS written for the span itself. - Step 3 — box generation: block-level elements generate a box; if that box’s content is inline (text, ``, ``, etc.) the box establishes an inline formatting context.
- Step 4 — line breaking: the layout engine breaks the inline content into lines based on the available width of the box (its content-box width, after subtracting padding and border).
- Step 5 — alignment: for each line,
text-aligndecides where the line’s box sits within the available width — flush to one edge, centered, or (forjustify) stretched so inter-word spacing fills the line exactly. - Step 6 — paint: only after layout is finalized does the paint stage run, filling each glyph with the resolved
colorvalue. This ordering is why changingcolornever triggers a layout reflow, while changing something likefont-sizedoes.
Common Mistakes
Mistake 1: Using text-align to try to center the box itself
.box {
width: 300px;
text-align: center;
}
This is wrong when the goal is to horizontally center the .box element on the page. text-align only centers the inline content inside the box — it has no effect on where the box itself sits relative to its parent. Beginners often add this rule, see nothing move, and assume CSS is broken.
The fix is to center the box using margins (for a block element with a fixed width) instead:
.box {
width: 300px;
margin: 0 auto;
text-align: left;
}
Mistake 2: A typo’d property name silently doing nothing
span.label {
colour: red;
text-align: center;
}
Browsers silently ignore any declaration they don’t recognize, so colour (the British spelling) simply never applies — no error appears anywhere, which makes this bug easy to miss. In addition, text-align here has no visible effect because <span> is inline by default and does not establish its own block-level box to align content within.
The corrected version uses the correct American-spelled property name and makes the span a block-level box so alignment has something to act on:
span.label {
color: red;
display: inline-block;
text-align: center;
}
Best Practices
- Set a base
coloron<body>or:rootand let inheritance carry it through the page instead of repeating the same color on every element. - Prefer
rgb()/hsl()with an alpha channel over plain hex when you need transparency — it keeps the color and opacity values in one readable declaration. - Use
currentColorfor borders, shadows, and SVG fills that should always match the surrounding text color, so you only maintain one color value per component. - Reach for
start/endinstead ofleft/rightwhen a page might support right-to-left languages, since the logical values flip automatically with text direction. - Always check color contrast against the background (aim for at least a 4.5:1 ratio for body text) — a color that looks fine to you may fail accessibility guidelines for users with low vision.
- Remember
text-alignonly works on block-level boxes with inline content; if it seems to do nothing, check whether the target element isinlineand needsdisplay: inline-blockorblockfirst. - Avoid
text-align: justifyon narrow columns of body text — it can create large, ugly gaps between words when there are few words per line.
Practice Exercises
- Create a rule for a
<blockquote>that sets its text color to a muted gray usinghsl(), and center-aligns its text. Then add a nested<footer>inside the blockquote whose color should be different from the quote text — write the override. - You have a
<div class="alert">withwidth: 400pxthat a teammate tried to center on the page using onlytext-align: center, and it didn’t move. Explain why, and write the corrected CSS that actually centers the box itself while keeping its text left-aligned. - Build a small badge component: a
<span>with a colored background and white text, centered text, using a CSS custom property for the background color so the same variable could be reused for a border elsewhere viacurrentColor.
Summary
colorsets text color and is inherited; it affects paint only, never layout.text-aligncontrols how lines of inline content are positioned inside a block-level box; it does not move the box itself.- Color can be specified as a keyword, hex,
rgb(),hsl(), or via the specialcurrentColorkeyword, all of which support an alpha channel for transparency. text-alignonly has a visible effect on elements that establish an inline formatting context — inline elements needdisplay: inline-blockorblockfirst.- Use
start/endinstead ofleft/rightfor layouts that need to support right-to-left languages. - To center a box (not its text), use margins or a layout method like flexbox/grid — not
text-align.
