font colors
label design
text styling
UI design
typography

Use multiple font colors in a single label

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Multi-color text in one label is a common UI requirement for status badges, highlighted values, and contextual emphasis. Most platforms support this through attributed text models that style specific ranges without splitting content into many separate views. Reliable implementations depend on dynamic range detection, accessibility-safe color choices, and testable style logic.

Core Approach: Style by Text Range

The universal pattern is:

  1. construct full text,
  2. identify target segment ranges,
  3. apply color and optional additional attributes,
  4. render as one label element.

This keeps layout simple and avoids alignment issues that come from multiple adjacent labels.

iOS Example with NSAttributedString

swift
1import UIKit
2
3let label = UILabel()
4let amount = "$125.40"
5let fullText = "Balance: \(amount)"
6
7let styled = NSMutableAttributedString(string: fullText)
8styled.addAttribute(
9    .foregroundColor,
10    value: UIColor.label,
11    range: NSRange(location: 0, length: 9)
12)
13
14if let range = fullText.range(of: amount) {
15    let nsRange = NSRange(range, in: fullText)
16    styled.addAttribute(.foregroundColor, value: UIColor.systemGreen, range: nsRange)
17    styled.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 17), range: nsRange)
18}
19
20label.attributedText = styled

Notice the dynamic range lookup for amount. It is safer than hardcoded indexes when values vary.

Android Example with SpannableString

kotlin
1import android.graphics.Color
2import android.text.SpannableString
3import android.text.style.ForegroundColorSpan
4import android.text.style.StyleSpan
5import android.graphics.Typeface
6
7val status = "SUCCESS"
8val full = "Status: $status"
9val span = SpannableString(full)
10
11span.setSpan(ForegroundColorSpan(Color.DKGRAY), 0, 8, 0)
12val start = full.indexOf(status)
13val end = start + status.length
14
15span.setSpan(ForegroundColorSpan(Color.parseColor("#2E7D32")), start, end, 0)
16span.setSpan(StyleSpan(Typeface.BOLD), start, end, 0)
17
18textView.text = span

For dynamic strings, always compute start and end from content, not fixed positions.

Web Example with Semantic Inline Segments

For browser interfaces, split the sentence into semantic inline spans and style in CSS.

html
1<p class="status-line">
2  <span class="label">Status:</span>
3  <span class="value success">SUCCESS</span>
4</p>
5
6<style>
7.status-line .label { color: #2b2b2b; }
8.status-line .value.success { color: #1f7a3f; font-weight: 700; }
9</style>

This approach is easy to theme and works well with design tokens.

Localization-Safe Styling

Hardcoded character offsets often break when UI text is translated. Instead:

  • style known tokens by substring search,
  • style placeholders after interpolation,
  • keep style rules tied to semantics rather than fixed indices.

Localization-safe iOS pattern:

swift
let template = NSLocalizedString("order_status_format", comment: "")
let status = NSLocalizedString("status_success", comment: "")
let fullText = String(format: template, status)

Then locate status dynamically and apply attributes.

Accessibility and Contrast

Color should not be the only carrier of meaning. Pair color with weight, icon, or explicit text label so users with color-vision differences still understand state.

Also verify contrast ratio in both light and dark modes. A color that looks fine on a white background can fail badly in dark theme.

Practical checks:

  • meet platform contrast guidance for normal text,
  • test dynamic type sizes,
  • verify high-contrast accessibility modes.

Performance in Recyclable Lists

On screens with many rows, avoid rebuilding attributed strings on every bind if content repeats frequently. Cache style computation by value key or precompute formatted text in view-model layers.

Simple memoization idea in Kotlin:

kotlin
1private val cache = mutableMapOf<String, SpannableString>()
2
3fun styledStatus(text: String): SpannableString {
4    return cache.getOrPut(text) {
5        SpannableString(text).apply {
6            // apply spans once
7        }
8    }
9}

Keep caches bounded to avoid memory growth.

Testing Styled Labels

Style regressions are easy to miss visually. Add UI tests or snapshots that verify highlighted segments and expected color tokens. For platform unit tests, assert range detection logic so refactors do not silently style the wrong substring.

Common Pitfalls

  • Hardcoding index ranges that break when text changes or localizes.
  • Relying on color alone to communicate critical status information.
  • Choosing colors with poor contrast in one theme mode.
  • Recomputing heavy attributed styling in large scrolling lists without caching.
  • Mixing business rules and style construction in UI classes without test coverage.

Summary

  • Use attributed or spannable text to apply multiple colors in one label.
  • Compute style ranges dynamically for variable and localized strings.
  • Pair color with secondary emphasis for accessibility.
  • Validate styling across themes, font scales, and contrast modes.
  • Keep formatting logic testable and optimized for list-heavy interfaces.

Course illustration
Course illustration

All Rights Reserved.