Building a Custom Help Highlighter
Typer uses the Rich library to produce beautiful, colorized help screens. This is achieved through Highlighters, which use regular expressions to identify specific patterns in your CLI help text—like options, switches, and metavars—and apply styles to them.
In this tutorial, you will learn how to extend the built-in highlighter classes in typer.rich_utils to create a custom visual style for your CLI, including adding new highlighting rules for your own keywords.
Prerequisites
To follow this tutorial, you need typer installed with Rich support:
pip install "typer[all]"
Step 1: Understanding the Base Highlighters
Typer defines three core highlighter classes in typer/rich_utils.py. Each inherits from Rich's RegexHighlighter and defines a list of highlights containing regex patterns with named groups. These named groups (e.g., ?P<option>) map directly to style names in a Rich Theme.
OptionHighlighter: The primary highlighter for general help text. It identifies standard options (--help), short switches (-h), metavars in angle brackets (<arg>), and the "Usage:" prefix.NegativeOptionHighlighter: Specifically for "negative" or secondary options (e.g.,--no-force).MetavarHighlighter: Used to style the separators in metavars, such as brackets ([]), angle brackets (<>), and pipes (|).
Step 2: Create a Custom Highlighter
Suppose you want your CLI help to automatically highlight your project's name whenever it appears. You can achieve this by extending OptionHighlighter.
Create a file named custom_help.py:
from typer.rich_utils import OptionHighlighter
class BrandHighlighter(OptionHighlighter):
"""Highlights standard options plus our custom brand name."""
highlights = OptionHighlighter.highlights + [
r"(?P<brand>MyAwesomeCLI)",
]
By appending to OptionHighlighter.highlights, you keep all the default Typer highlighting (like --options and -switches) while adding a new named group called brand.
Step 3: Define Custom Styles with a Theme
Highlighters only identify the text; a Rich Theme determines the colors. You need to map your new brand group to a style and optionally override Typer's default styles.
Typer's default styles are defined as constants in typer.rich_utils. You can use these or provide your own:
from rich.theme import Theme
from typer.rich_utils import STYLE_OPTION, STYLE_SWITCH
# Define a theme that includes our new 'brand' style
# and overrides the default 'option' style
custom_theme = Theme({
"brand": "bold reverse magenta",
"option": "bold yellow",
"switch": "green",
})
Step 4: Apply the Highlighter Manually
You can use your custom highlighter to style any Rich Text object. This is useful if you are building custom panels or headers for your CLI.
from rich.console import Console
from rich.text import Text
# Initialize a console with our theme and highlighter
console = Console(theme=custom_theme, highlighter=BrandHighlighter())
# Create text and print it
text = Text("Welcome to MyAwesomeCLI! Use --help to see all options.")
console.print(text)
When you run this, "MyAwesomeCLI" will appear in bold reverse magenta, and any strings matching the option/switch patterns will use your custom yellow and green styles.
Step 5: Customizing Metavar Separators
The MetavarHighlighter is unique because it is applied specifically to the "Metavar" column in the options table. It targets the metavar_sep group.
If you want to make your separators stand out instead of being "dim" (the Typer default), you can modify the theme:
from typer.rich_utils import MetavarHighlighter, STYLE_METAVAR
# MetavarHighlighter targets 'metavar_sep'
metavar_theme = Theme({
"metavar": STYLE_METAVAR, # The text inside: 'bold yellow'
"metavar_sep": "bold white", # The brackets/pipes: 'bold white'
})
mh = MetavarHighlighter()
console = Console(theme=metavar_theme)
# Manually highlighting a metavar string
text = Text("[OPTIONS]|<ARGS>")
console.print(mh(text))
Step 6: Global Style Overrides
If you want to change the visual style of your entire Typer app without creating new classes, you can modify the global style constants in typer.rich_utils before your app runs. Typer's internal _get_rich_console function reads these constants every time it generates help.
import typer.rich_utils
# Change the default colors for all help screens
typer.rich_utils.STYLE_OPTION = "bold cyan"
typer.rich_utils.STYLE_SWITCH = "bold magenta"
typer.rich_utils.STYLE_METAVAR = "underline yellow"
app = typer.Typer()
@app.command()
def main(name: str = typer.Option(..., help="The name to greet")):
"""
The MyAwesomeCLI tool.
"""
print(f"Hello {name}")
if __name__ == "__main__":
app()
Complete Working Result
By combining these techniques, you can create a highly customized help experience. Here is how the MetavarHighlighter is tested within the project (tests/test_rich_utils.py), demonstrating the verification of style application:
from typer.rich_utils import (
STYLE_METAVAR_SEPARATOR,
Text,
_get_rich_console,
metavar_highlighter,
)
def test_custom_style_application():
console = _get_rich_console()
text = Text("[ARGS]...")
# Apply the built-in metavar highlighter
highlighted = metavar_highlighter(text)
# Verify the style at the first character (the '[')
opening_bracket_style = highlighted.get_style_at_offset(console, 0)
assert str(opening_bracket_style) == STYLE_METAVAR_SEPARATOR
Next Steps
- Explore
typer.rich_utils.NegativeOptionHighlighterto style "no-" flags differently. - Use
rich_markup_mode="rich"in yourtyper.Typerapp to use these styles directly within your docstrings using tags like[option]--my-option[/option].