Back to Blog

Text Styles App in egui — RichText, Color32 & Formatting, Ep16

Sandy LaneSandy Lane

Video: Text Styles App in egui — RichText, Color32 & Formatting, Ep16 by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

RichText::new("text").size(28.0).strong().color(Color32::WHITE) — the chained styling builder for every text widget.

We have used RichText already (Episode 13 for fonts, Episode 11 for headings). Today we look at it on its own and walk through every styling method. Color, size, weight, italics, underline, strikethrough, monospace — all from one builder.

What we are building

A text styles showcase. A central panel scrolls through four sections — Headings, Body, Highlights, Warnings — each demonstrating different RichText combinations. A live font-size slider in the top panel changes everything at once. A sidebar of checkboxes hides individual sections.

RichText basics

egui::RichText::new("Hello")
  .size(20.0)
  .strong()
  .color(egui::Color32::WHITE)

Builder pattern. Each method returns a new RichText; chain them. When you have the styled text you want, pass it to ui.label, ui.button, or any widget that accepts Into<RichText>.

Same RichText::new("Hello") builds either a label or a button title — the styling applies to either. There is no separate "styled label" widget; styling lives on the text, not the widget.

The styling methods

Every method called inline in this episode's code:

  • .size(f32) — font size in points. Overrides the inherited default.
  • .strong() — bold weight.
  • .italics() — italic style.
  • .underline() — underline.
  • .strikethrough() — strikethrough.
  • .monospace() — switch to the monospace font family.
  • .color(Color32) — text color.
  • .background_color(Color32) — highlight background.

You can chain any combination:

RichText::new("Important")
  .size(20.0)
  .strong()
  .color(Color32::from_rgb(255, 100, 100))
  .underline()

Bold, red, underlined, 20pt. Each method composes; order does not matter.

Color32 constants and from_rgb

Color32::WHITE
Color32::from_rgb(166, 227, 161)

Color32 has a handful of named constants (WHITE, BLACK, RED, GREEN, BLUE, YELLOW, TRANSPARENT, etc.) for common colors. For everything else, Color32::from_rgb(r, g, b) takes three u8 values (0..=255).

For variants with alpha, use Color32::from_rgba_premultiplied(r, g, b, a) — note the premultiplied part: the RGB channels should already be multiplied by alpha. For most uses, named constants and from_rgb are enough.

Conditional rendering with checkboxes

if self.show_headings {
  ui.label(RichText::new("Heading Styles").size(self.font_size + 16.0).strong());
  // ... other heading widgets
}

if self.show_body {
  // ... body section
}

Each demo section is wrapped in an if based on a checkbox. Toggle the checkbox and the section appears or disappears. This is the immediate-mode show/hide we have used since Episode 4.

Slider for global size

ui.add(egui::Slider::new(&mut self.font_size, 12.0..=40.0));

The font size slider in the top panel writes to self.font_size. Every text widget below reads self.font_size (often with offsets like + 16.0 for headings) so changing the slider re-renders every label at the new size.

This is one of the strengths of immediate mode: a single source of truth (the slider value) controls every widget, and the propagation is just normal variable reads.

ctx.set_pixels_per_point

ctx.set_pixels_per_point(1.5);

Sets the global UI scale factor. 1.0 is the OS default; 1.5 makes everything 50% larger; 2.0 doubles the size. Useful for high-DPI displays or for screen recording where you want the UI to be very visible.

set_pixels_per_point affects layout sizes, text sizes, padding — everything. Setting it once at the top of update is the typical pattern. For per-component scaling, use RichText.size or a more granular approach.

Heading vs RichText

ui.heading(RichText::new("Title").color(Color32::ORANGE));

ui.heading is a shortcut: it accepts Into<RichText> and applies the heading text style. So you can pass a styled RichText to heading and get both the heading style and your custom color.

Equivalent ways to write a styled heading:

ui.heading(RichText::new("Title").color(Color32::ORANGE));
ui.label(RichText::new("Title").heading().color(Color32::ORANGE));

Both work; pick the one that reads more clearly.

Running it

cargo run. The window opens with all four demo sections visible. Drag the font-size slider — every label resizes together. Toggle the checkboxes — sections disappear and reappear.

The sample colors (greens, blues, oranges, reds) come from a Catppuccin-flavoured palette. The point is to show you what's possible; a real app would pick semantic colors based on its design system.

Patterns worth stealing

A common production pattern is to define color and style helpers per app:

pub mod theme {
  use eframe::egui::{Color32, RichText};
  pub fn success(text: &str) -> RichText {
    RichText::new(text).color(Color32::from_rgb(100, 200, 100)).strong()
  }
  pub fn warning(text: &str) -> RichText {
    RichText::new(text).color(Color32::from_rgb(250, 179, 135)).strong()
  }
}

// Then in update:
ui.label(theme::success("Done"));
ui.label(theme::warning("Disk low"));

Centralises the visual language. Change the success color in one place; the whole app updates.

Common mistakes

Storing RichText in a struct field. It is cheap to construct; build it where you display it. Storing it leads to stale styling when the underlying value changes.

Using Color32::from_rgb for floating-point math. Use Rgba::from_rgb with floats, then convert. Or use Color32 constants/colors and skip the math.

Mixing pixels_per_point with explicit sizes. When you scale globally, your custom sizes scale too. Be deliberate about which sizes are absolute and which scale.

Reaching for a markdown library. egui does not parse markdown. Either use RichText chains or look at egui_commonmark for a separate markdown renderer.

What's next

Next episode: images. Loading and displaying image files with egui::Image and the egui_extras image loader. We will build a small gallery: thumbnails on the left, a zoomable preview on the right.

Recap

RichText::new(text) plus chained .size, .strong, .italics, .underline, .strikethrough, .monospace, .color, .background_color — every text style. Pass to ui.label or ui.heading. Use ctx.set_pixels_per_point for global scaling. Centralise visual language in a theme module.

Next episode: images. See you in the next one.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.