### Stylish CLI example with RichHelpConfiguration Source: https://ewels.github.io/rich-click/latest/blog/2024/04/30/version-1.8 A complete example showing how to style option and command panels with RichHelpConfiguration. It sets panel boxes, borders, titles, and colors, then defines a CLI with subcommands. ```python import rich_click as click help_config = click.RichHelpConfiguration( style_options_panel_box="SIMPLE_HEAD", style_options_table_box="SIMPLE_HEAD", style_commands_panel_box="SIMPLE_HEAD", style_commands_table_box="SIMPLE_HEAD", style_options_panel_border="bold", options_panel_title="[u]Options[/]", use_rich_markup=True, style_commands_panel_border="bold", commands_panel_title="[u]Commands[/]", style_option="green", style_usage="", style_usage_command="", style_argument="green", style_switch="dim green", style_command="green", style_metavar="dim", ) @click.group("app") @click.option("--env-file", "-e", type=click.Path(), help=".env file") @click.rich_config(help_config=help_config) def cli(): """ CLI for `app` This `app` lets you do cool things. """ @cli.command("db") def deploy(): """Database commands for app""" @cli.command("deploy") def deploy(): """Deploy app""" @cli.command("self") def self(): """Manage app""" if __name__ == "__main__": cli() ``` -------------------------------- ### Install term-background with uv Source: https://ewels.github.io/rich-click/latest/blog/2024/11/13/three-pre-made-styles Installation command for the term-background package using uv. ```bash uv add term-background ``` -------------------------------- ### Install term-background with rye Source: https://ewels.github.io/rich-click/latest/blog/2024/11/13/three-pre-made-styles Installation command for the term-background package using rye. ```bash rye add term-background rye sync ``` -------------------------------- ### Install rich-click Source: https://ewels.github.io/rich-click/latest Install the package using pip. ```bash pip install rich-click ``` -------------------------------- ### Simple Example with rich-click Source: https://ewels.github.io/rich-click/latest Replace the standard click import with rich_click to automatically format help output. ```python import rich_click as click @click.command() @click.option("--count", default=1, help="Number of greetings.") @click.option("--name", prompt="Your name", help="The person to greet.") def hello(count, name): """Simple program that greets NAME for a total of COUNT times.""" for _ in range(count): click.echo(f"Hello, {name}!") if __name__ == '__main__': hello() ``` -------------------------------- ### Set up local development environment Source: https://ewels.github.io/rich-click/latest/contributing Commands to pin the Python version, create a virtual environment, sync dependencies, and install git hooks. ```bash uv python pin 3.14 uv venv .venv source .venv/bin/activate uv sync --all-groups prek install ``` -------------------------------- ### Install term-background with pip Source: https://ewels.github.io/rich-click/latest/blog/2024/11/13/three-pre-made-styles Installation command for the term-background package using pip. ```bash pip install term-background ``` -------------------------------- ### Sample rich-click help output Source: https://ewels.github.io/rich-click/latest/editor Example output of a rich-click help screen showing styled options and commands panels. ```text Usage: docs [OPTIONS] FOO COMMAND [ARGS]... Help text for CLI Second line of help text. ╭─ Options ────────────────────────────────────────────────────────────╮ │ --bar -b TEXT Lorem ipsum [default: (someval)] │ │ * --baz [a|b|c] Choose wisely [required] │ │ --help Show this message and exit. │ ╰──────────────────────────────────────────────────────────────────────╯ ╭─ Commands ───────────────────────────────────────────────────────────╮ │ subcommand Help text for subcommand │ ╰──────────────────────────────────────────────────────────────────────╯ ``` -------------------------------- ### CLI help output for wildcard command groups Source: https://ewels.github.io/rich-click/latest/blog/2024/04/30/version-1.8 Example output showing how the wildcard configuration affects the help display for specific sub-commands. ```bash $ python foo.py x c --help Usage: foo.py x c [OPTIONS] ╭─ foo ──────────────────────────────────────────────────────────────────────────────╮ │ --flag1 TEXT │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭─ Options ──────────────────────────────────────────────────────────────────────────╮ │ --flag2 TEXT │ │ --help Show this message and exit. │ ╰────────────────────────────────────────────────────────────────────────────────────╯ $ python foo.py x b --help Usage: foo.py x b [OPTIONS] ╭─ Options ───────────────────────────────────────────────────────────────────────────╮ │ --flag1 TEXT │ │ --flag2 TEXT │ │ --help Show this message and exit. │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ``` -------------------------------- ### Example Typer CLI with rich markup and help panels Source: https://ewels.github.io/rich-click/latest/blog/2025/09/16/version-1.9 Example Typer CLI that uses rich-click's rich markup in help texts and rich_help_panel to group commands. Requires rich-click>=1.9 and typer>=0.15. Run with `rich-click your-cli-tool` to apply themes. ```python # /// script # dependencies = ["rich-click>=1.9", "typer>=0.15"] # /// # This example comes from the Typer docs: # https://typer.tiangolo.com/tutorial/commands/help/#help-panels-for-commands import typer app = typer.Typer(rich_markup_mode="rich") @app.command() def create(username: str): """[green]Create[/green] a new user. :sparkles:""" print(f"Creating user: {username}") @app.command() def delete(username: str): """[red]Delete[/red] a user. :fire:""" print(f"Deleting user: {username}") @app.command(rich_help_panel="Utils and Configs") def config(configuration: str): """[blue]Configure[/blue] the system. :wrench:""" print(f"Configuring the system with: {configuration}") @app.command(rich_help_panel="Utils and Configs") def sync(): """[blue]Synchronize[/blue] the system or something fancy like that. :recycle:""" print("Syncing the system") @app.command(rich_help_panel="Help and Others") def help(): """Get [yellow]help[/yellow] with the system. :question:""" print("Opening help portal...") @app.command(rich_help_panel="Help and Others") def report(): """[yellow]Report[/yellow] an issue. :bug:""" print("Please open a new issue online, not a direct message") if __name__ == "__main__": app() ``` -------------------------------- ### Install dependencies for Gradient art header Source: https://ewels.github.io/rich-click/latest/blog/2024/11/13/three-pre-made-styles Install the required packages using pip, uv, or rye. Requires rich-click version 1.8.4 or higher. ```bash pip install term-background art rich-gradient ``` ```bash uv add term-background art rich-gradient ``` ```bash rye add term-background rye add art rye add rich-gradient rye sync ``` -------------------------------- ### Fix 1b: theme via RichHelpConfiguration object Source: https://ewels.github.io/rich-click/latest/documentation/configuration This example shows how to apply the 'green2-nu' theme by passing a RichHelpConfiguration object with theme and style_option to @click.rich_config. ```python import rich_click as click @click.command() @click.rich_config(click.RichHelpConfiguration(theme="green2-nu", style_option="magenta")) def my_command(): """Help text here.""" ``` -------------------------------- ### Fix 1c: theme via global config variables Source: https://ewels.github.io/rich-click/latest/documentation/configuration This example shows how to apply the 'green2-nu' theme by setting global config variables rc.THEME and rc.STYLE_OPTION directly. ```python import rich_click as click import rich_click.rich_click as rc rc.THEME = "green2-nu" rc.STYLE_OPTION = "magenta" @click.command() def my_command(): """Help text here.""" ``` -------------------------------- ### Implement Gradient art header in rich-click Source: https://ewels.github.io/rich-click/latest/blog/2024/11/13/three-pre-made-styles Example script using Gradient and text2art to create a dynamic header. The colors adjust based on whether the terminal background is dark or light. ```python # /// script # dependencies = [ # "rich-click>=1.8.4", # "term-background", # "art", # "rich-gradient", # ] # /// import rich_click as click from art import text2art from rich_gradient import Gradient from term_background import is_dark_background if is_dark_background(): primary_color = "#99ff00" secondary_color = "#ffdd00" else: primary_color = "#77bb22" secondary_color = "#aa9922" help_config = click.RichHelpConfiguration( style_options_panel_box="SIMPLE", style_commands_panel_box="SIMPLE", style_option=f"bold {secondary_color}", style_argument=secondary_color, style_command=primary_color, options_panel_title="[u]Options[/]", commands_panel_title="[u]Commands[/]", style_switch=secondary_color, style_metavar="bold", header_text=Gradient(text2art("My App"), colors=[primary_color, secondary_color]), style_metavar_separator="", style_usage=f"bold {primary_color}", style_usage_command="", style_helptext_first_line="bold", style_helptext="", style_option_default="", style_required_short="bold", style_required_long="", style_options_panel_border=f"bold {secondary_color}", style_commands_panel_border=f"bold {primary_color}", text_markup="markdown", max_width=120 ) @click.group("app") @click.option("--env-file", "-e", type=click.Path(), help=".env file") @click.rich_config(help_config=help_config) def cli(): """ CLI for `app` This `app` lets you do cool things. """ @cli.command("db") def deploy(): """Database commands for app""" @cli.command("deploy") def deploy(): """Deploy app""" @cli.command("self") def self(): """Manage app""" if __name__ == "__main__": cli() ``` -------------------------------- ### Benchmark rich-click 1.8 vs 1.7 with argparse, click, typer Source: https://ewels.github.io/rich-click/latest/blog/2024/04/30/version-1.8 Benchmark script that creates hello_click.py, hello_rich_click.py, hello_argparse.py, and hello_typer.py, then measures startup time and peak memory for each with and without clearing .pyc files. It installs rich-click 1.8.0 and 1.7 to compare versions, and uses mprof for memory profiling. Requires uv, gdate, bc, and memory-profiler. ```bash #!/bin/bash set -eo pipefail export VIRTUAL_ENV=.venv_benchmarks export PY_VERSION=3.12 speed_trials=40 mprof_trials=10 ############################################################################### uv venv "${VIRTUAL_ENV}" --python "${PY_VERSION}" cat < hello_click.py import click @click.command() @click.option("--name", default="World", help="Name to greet.") def hello(name): """Greet someone.""" print(f"Hello, {name}!") if name == "Daniel": import time time.sleep(0.2) if __name__ == "__main__": hello() EOF cat < hello_rich_click.py import rich_click as click @click.command() @click.option("--name", default="World", help="Name to greet.") def hello(name): """Greet someone.""" print(f"Hello, {name}!") if name == "Daniel": import time time.sleep(0.2) if __name__ == "__main__": hello() EOF cat < hello_argparse.py import argparse def main(): parser = argparse.ArgumentParser(description="Greet someone.") parser.add_argument("--name", default="World", help="Name to greet.") args = parser.parse_args() print(f"Hello, {args.name}!") if args.name == "Daniel": import time time.sleep(0.2) if __name__ == '__main__': main() EOF cat < hello_typer.py import typer from typing_extensions import Annotated def main(name: Annotated[str, typer.Option(help="Name to greet.")] = "World"): """Greet someone.""" print(f"Hello, {name}!") if name == "Daniel": import time time.sleep(0.3) if __name__ == '__main__': main() EOF chmod +x hello_argparse.py chmod +x hello_click.py chmod +x hello_rich_click.py chmod +x hello_typer.py ################################################################################ function get_times { total_time=0 filename="${1}" clear_pyc_files="${2}" # Run once to compile pyc files "${VIRTUAL_ENV}/bin/python" "${filename}" --name Phil >/dev/null for (( i=0; i < speed_trials; i++ )) do if [ "${clear_pyc_files}" = "true" ]; then find "${VIRTUAL_ENV}/lib/python${PY_VERSION}/site-packages/" -name '*.pyc' -delete fi start_time=$(gdate +%s.%N) "${VIRTUAL_ENV}/bin/python" "${filename}" --name Phil >/dev/null end_time=$(gdate +%s.%N) elapsed=$(echo "$end_time - $start_time" | bc) total_time=$(echo "$total_time + $elapsed" | bc) done average_time=$(echo "$total_time / $speed_trials" | bc -l) echo "Average time for ${filename} with clear_pyc_files=${clear_pyc_files}: $average_time seconds" } function get_mprof { total_mib=0 filename="${1}" clear_pyc_files="${2}" # Run once to compile pyc files "${VIRTUAL_ENV}/bin/python" "${filename}" --name Phil >/dev/null for (( i=0; i < mprof_trials; i++ )) do if [ "${clear_pyc_files}" = "true" ]; then find "${VIRTUAL_ENV}/lib/python${PY_VERSION}/site-packages/" -name '*.pyc' -delete fi "${VIRTUAL_ENV}/bin/mprof" run "${VIRTUAL_ENV}/bin/python" "${filename}" --name Daniel >/dev/null output=$("${VIRTUAL_ENV}/bin/mprof" peak) mprof_file=$(echo "$output" | grep 'mprofile' | awk '{print $1}') memory_usage=$(echo "$output" | grep 'mprofile' | awk '{print $2}' | bc) total_time=$(echo "$total_mib + $memory_usage" | bc) rm "${mprof_file}" done average_memory_usage=$(echo "$memory_usage / $mprof_trials" | bc -l) echo "Average MiB consumed for ${filename} with clear_pyc_files=${clear_pyc_files}: $memory_usage MiB" } ################################################################################ # Times uv pip install --no-binary :all: "rich-click==1.8.0" get_times hello_argparse.py true get_times hello_click.py true get_times hello_rich_click.py true get_times hello_argparse.py false get_times hello_click.py false get_times hello_rich_click.py false uv pip install --no-binary :all: "rich-click==1.7" get_times hello_rich_click.py true get_times hello_rich_click.py false ################################################################################ # Memory profiling uv pip install memory-profiler uv pip install --no-binary :all: "rich-click==1.8.0" get_mprof hello_argparse.py true get_mprof hello_click.py true get_mprof hello_rich_click.py true get_mprof hello_argparse.py false get_mprof hello_click.py false get_mprof hello_rich_click.py false uv pip install --no-binary :all: "rich-click==1.7" get_mprof hello_rich_click.py true get_mprof hello_rich_click.py false ``` -------------------------------- ### Implement dark/light mode aware rich-click configuration Source: https://ewels.github.io/rich-click/latest/blog/2024/11/13/three-pre-made-styles A complete example showing how to use is_dark_background to toggle color variables and apply them to a RichHelpConfiguration object for a CLI application. ```python # /// script # dependencies = [ # "rich-click", # "term-background", # ] # /// import rich_click as click from term_background import is_dark_background if is_dark_background(): primary_color = "#99bbf2" secondary_color = "#c099f2" tertiary_color = "#d1d43d" else: primary_color = "#2b569c" secondary_color = "#5c2b9c" tertiary_color = "#c4b74f" help_config = click.RichHelpConfiguration( style_option=f"bold {secondary_color}", style_argument=f"bold {tertiary_color}", style_command=f"bold {primary_color}", style_switch=f"bold {secondary_color}", style_metavar=tertiary_color, style_metavar_separator="", style_usage=f"bold {primary_color}", style_usage_command="", style_helptext_first_line="bold", style_helptext="", style_option_default=tertiary_color, style_required_short="bold red", style_required_long="red", style_options_panel_border=f"dim {secondary_color}", style_commands_panel_border=f"dim {primary_color}", text_markup="markdown", max_width=120 ) @click.group("app") @click.option("--env-file", "-e", type=click.Path(), help=".env file") @click.rich_config(help_config=help_config) def cli(): """ CLI for `app` This `app` lets you do cool things. """ @cli.command("db") def deploy(): """Database commands for app""" @cli.command("deploy") def deploy(): """Deploy app""" @cli.command("self") def self(): """Manage app""" if __name__ == "__main__": cli() ``` -------------------------------- ### Use @rich_config with RichHelpConfiguration to set style_option Source: https://ewels.github.io/rich-click/latest/documentation/configuration Initializes a new RichHelpConfiguration with style_option set to 'bold red' and passes it to the CLI via the @rich_config decorator. RichHelpConfiguration() starts from default configuration and is unaware of global configuration. ```python import rich_click as click @click.command() @click.rich_config(help_config=click.RichHelpConfiguration(style_option="bold red")) def cli(): """Help text here.""" ``` -------------------------------- ### Boxless one-color CLI style with RichHelpConfiguration Source: https://ewels.github.io/rich-click/latest/blog/2024/11/13/three-pre-made-styles Configure a boxless, one-color CLI style using RichHelpConfiguration. Set style_options_panel_box and style_commands_panel_box to "SIMPLE_HEAD" to remove the box, and use blue for options, arguments, and commands. The example also sets text_markup to "markdown" and max_width to 100. ```python # /// script # dependencies = [ # "rich-click", # ] # /// import rich_click as click help_config = click.RichHelpConfiguration( style_options_panel_box="SIMPLE_HEAD", style_commands_panel_box="SIMPLE_HEAD", style_options_panel_border="bold", options_panel_title="[u]Options[/]", style_commands_panel_border="bold", commands_panel_title="[u]Commands[/]", style_option="blue", style_usage="", style_usage_command="", style_argument="blue", style_switch="bold blue", style_command="blue", style_metavar="dim", text_markup="markdown", max_width=100 ) @click.group("app") @click.option("--env-file", "-e", type=click.Path(), help=".env file") @click.rich_config(help_config=help_config) def cli(): """ CLI for `app` This `app` lets you do cool things. """ @cli.command("deploy") def deploy(): """Database commands for app""" @cli.command("deploy") def deploy(): """Deploy app""" @cli.command("self") def self(): """Manage app""" if __name__ == "__main__": cli() ``` -------------------------------- ### Fix 1a: theme via dict in rich_config Source: https://ewels.github.io/rich-click/latest/documentation/configuration This example shows how to apply the 'green2-nu' theme by passing a dict to @click.rich_config, which merges into the global config. The theme and style_option are both set. ```python import rich_click as click @click.command() @click.rich_config({"theme": "green2-nu", "style_option": "magenta"}) def my_command(): """Help text here.""" ``` -------------------------------- ### Fix 2: theme via global config with dict merge Source: https://ewels.github.io/rich-click/latest/documentation/configuration This example shows that setting rc.THEME globally works when a dict is passed to @click.rich_config, because dicts get merged into the global config. The theme is applied while style_option is set via the dict. ```python import rich_click as click import rich_click.rich_click as rc # This works because of the config rule that dicts get merged # into the global config rc.THEME = "green2-nu" @click.command() @click.rich_config({"style_option": "magenta"}) def my_command(): """Help text here.""" ``` -------------------------------- ### Bad example: theme not applied due to config resolution rule Source: https://ewels.github.io/rich-click/latest/documentation/configuration This example shows that setting rc.THEME globally does not affect a RichHelpConfiguration object created with style_option, because the config rule states that RichHelpConfiguration() will not pull from globals. The theme 'green2-nu' will NOT be applied. ```python import rich_click as click import rich_click.rich_click as rc # The following line will NOT do anything because the RichHelpConfiguration() below # completely ignores the the global config. rc.THEME = "green2-nu" # This will NOT have the green2-nu theme. help_config = click.RichHelpConfiguration(style_option="magenta") @click.command() @click.rich_config(help_config=help_config) def my_command(): """Help text here.""" ``` -------------------------------- ### Organize CLI with option and command panels Source: https://ewels.github.io/rich-click/latest/documentation/comparison_of_click_and_rich_click Use click.option_panel and click.command_panel to group related options and commands under specific headers in the help output. ```python import rich_click as click @click.group() @click.option("--environment", help="Environment") @click.option("--log-level", help="Log level") @click.option_panel("Config", options=["--environment", "--log-level"], help="Global runtime configuration") @click.command_panel("Admin Commands", commands=["user", "resource", "self"], help="Commands available to administrators") def cli(environment, log_level): """My application""" ... ``` -------------------------------- ### Set theme with RICH_CLICK_THEME and run CLI help Source: https://ewels.github.io/rich-click/latest/documentation/themes Set the RICH_CLICK_THEME environment variable to a theme name and run the CLI help command. The theme names shown are nord-nu, dracula-modern, cargo-slim, and forest-robo. ```bash export RICH_CLICK_THEME=nord-nu python docs/code_snippets/themes/cli.py --help ``` ```bash export RICH_CLICK_THEME=dracula-modern python docs/code_snippets/themes/cli.py --help ``` ```bash export RICH_CLICK_THEME=cargo-slim python docs/code_snippets/themes/cli.py --help ``` ```bash export RICH_CLICK_THEME=forest-robo python docs/code_snippets/themes/cli.py --help ``` -------------------------------- ### Set plain-slim theme Source: https://ewels.github.io/rich-click/latest/documentation/themes Applies the plain-slim style for conventional CLI help text rendering. ```bash export RICH_CLICK_THEME=plain-slim python docs/code_snippets/themes/cli.py --help ``` -------------------------------- ### Configure rich-click styles with RichHelpConfiguration Source: https://ewels.github.io/rich-click/latest/editor Global configuration using RichHelpConfiguration to set style options for rich-click help output. ```python import rich_click as click help_config = click.RichHelpConfiguration( style_option="bold cyan", style_argument="bold cyan", style_command="bold cyan", style_switch="bold green", style_metavar="bold yellow", style_metavar_separator="dim", style_usage="bold yellow", style_usage_command="bold", style_helptext_first_line="", style_helptext="dim", style_option_default="dim", style_required_short="red", style_required_long="dim red", style_options_panel_border="dim", style_commands_panel_border="dim" ) @click.group("my-command") @click.argument("foo") @click.option("--bar", "-b", help="Lorem ipsum", show_default="someval") @click.option("--baz", required=True, help="Choose wisely", type=click.Choice(["a", "b", "c"])) @click.rich_config(help_config=help_config) def cli(foo, bar): """ Help text for CLI Second line of help text. """ @cli.command("subcommand") def subcommand(foo, bar): """Help text for subcommand""" if __name__ == "__main__": cli() ``` -------------------------------- ### Side-by-side: Click vs Rich implementation of greet command Source: https://ewels.github.io/rich-click/latest/documentation/comparison_of_click_and_rich_click Comparison of Click and Rich implementations for a greet command using echo and interactive elements. The Click version uses click.prompt, click.confirm, and click.echo with click.style; the Rich version uses Prompt.ask, Confirm.ask, and rich.print with Rich markup. ```python import rich_click as click @click.command("greet") def greet(): name = click.prompt(click.style("What is your name?", fg="blue")) if not click.confirm(click.style("Are you sure?", fg="blue")): click.echo(click.style("Aborting", fg="red")) return click.echo(click.style(f"Hello, {name}!", fg="green")) if __name__ == "__main__": greet() ``` ```python import rich_click as click import rich from rich.prompt import Confirm, Prompt @click.command("greet") def greet(): name = Prompt.ask("[blue]What is your name?[/]") if not Confirm.ask("[blue]Are you sure?[/]"): rich.print("[red]Aborting[/]") return rich.print(f"[green]Hello, {name}![/]") if __name__ == "__main__": greet() ``` -------------------------------- ### Load global config into RichHelpConfiguration Source: https://ewels.github.io/rich-click/latest/documentation/configuration Use RichHelpConfiguration.load_from_globals() to create a help config that inherits the global settings. The resulting help_config will have style_option = "bold red". ```python import rich_click as click import rich_click.rich_click as rc rc.STYLE_OPTION = "bold red" # The help config will have `style_option = "bold red"`. help_config = click.RichHelpConfiguration.load_from_globals() @click.command() @click.rich_config(help_config=help_config) def my_command(): """Help text here.""" ``` -------------------------------- ### Override Typer CLI with a rich-click theme Source: https://ewels.github.io/rich-click/latest/documentation/rich_click_cli This example demonstrates a Typer application using rich-click themes by setting rich_markup_mode to 'rich'. It includes various commands organized into help panels. ```python # /// script # dependencies = ["rich-click>=1.9", "typer>=0.15"] # /// # This example comes from the Typer docs: # https://typer.tiangolo.com/tutorial/commands/help/#help-panels-for-commands import typer app = typer.Typer(rich_markup_mode="rich") @app.command() def create(username: str): """[green]Create[/green] a new user. :sparkles:""" print(f"Creating user: {username}") @app.command() def delete(username: str): """[red]Delete[/red] a user. :fire:""" print(f"Deleting user: {username}") @app.command(rich_help_panel="Utils and Configs") def config(configuration: str): """[blue]Configure[/blue] the system. :wrench:""" print(f"Configuring the system with: {configuration}") @app.command(rich_help_panel="Utils and Configs") def sync(): """[blue]Synchronize[/blue] the system or something fancy like that. :recycle:""" print("Syncing the system") @app.command(rich_help_panel="Help and Others") def help(): """Get [yellow]help[/yellow] with the system. :question:""" print("Opening help portal...") @app.command(rich_help_panel="Help and Others") def report(): """[yellow]Report[/yellow] an issue. :bug:""" print("Please open a new issue online, not a direct message") if __name__ == "__main__": app() ``` -------------------------------- ### Configure rich-click styles with global style variables Source: https://ewels.github.io/rich-click/latest/editor Alternative global configuration using rich_click.STYLE_* variables to set styles for rich-click help output. ```python import rich_click as click click.rich_click.STYLE_OPTION = "bold cyan" click.rich_click.STYLE_ARGUMENT = "bold cyan" click.rich_click.STYLE_COMMAND = "bold cyan" click.rich_click.STYLE_SWITCH = "bold green" click.rich_click.STYLE_METAVAR = "bold yellow" click.rich_click.STYLE_METAVAR_SEPARATOR = "dim" click.rich_click.STYLE_USAGE = "bold yellow" click.rich_click.STYLE_USAGE_COMMAND = "bold" click.rich_click.STYLE_HELPTEXT_FIRST_LINE = "" click.rich_click.STYLE_HELPTEXT = "dim" click.rich_click.STYLE_OPTION_DEFAULT = "dim" click.rich_click.STYLE_REQUIRED_SHORT = "red" click.rich_click.STYLE_REQUIRED_LONG = "dim red" click.rich_click.STYLE_OPTIONS_PANEL_BORDER = "dim" click.rich_click.STYLE_COMMANDS_PANEL_BORDER = "dim" @click.group("my-command") @click.argument("foo") @click.option("--bar", "-b", help="Lorem ipsum", show_default="someval") @click.option("--baz", required=True, help="Choose wisely", type=click.Choice(["a", "b", "c"])) def cli(foo, bar): """ Help text for CLI Second line of help text. """ @cli.command("subcommand") def subcommand(foo, bar): """Help text for subcommand""" if __name__ == "__main__": cli() ``` -------------------------------- ### Define option panels with @click.option_panel() Source: https://ewels.github.io/rich-click/latest/documentation/panels/overview Defines option panels using the @click.option_panel() decorator, grouping options into named panels. The example sets a custom panel border style via @click.rich_config(). ```python # /// script # dependencies = ["rich-click>=1.9"] # /// import rich_click as click @click.command() @click.option("--src", help="Source") @click.option("--dest", help="Destination") @click.option("--env", help="Environment") @click.option("--log-level", help="Log level") @click.version_option("1.2.3") @click.option_panel("Main", options=["--src", "--dest"]) @click.option_panel("Extra", options=["--env", "--log-level", "--help", "--version"]) @click.rich_config({"style_options_panel_border": "dim blue"}) def move_item(src, dest, env, log_level): """Move an item from a src location to a dest location""" pass if __name__ == "__main__": move_item() ``` -------------------------------- ### Mix overridable and immutable configuration options Source: https://ewels.github.io/rich-click/latest/documentation/configuration Demonstrates how to make specific options overridable by a theme while keeping others immutable by defining them in a separate RichHelpConfiguration object. ```python import rich_click as click # All of these config options can be overridden theme = click.RichHelpConfiguration( style_option="bold red", style_command="bold green" ).to_theme() # Here, `style_metavar` will not be overridden by the RICH_CLICK_THEME env var. config = click.RichHelpConfiguration( theme=theme, style_metavar="magenta", ) @click.group("cli") @click.rich_config(config) def cli(): """My CLI help text""" ``` -------------------------------- ### Configure column types with option_panel and rich_config Source: https://ewels.github.io/rich-click/latest/documentation/panels/advanced Example showing how to set column_types for option panels and customize style options. Uses rich-click>=1.9. The column_types list order determines column order in the table. ```python # /// script # dependencies = ["rich-click>=1.9"] # /// import rich_click as click @click.command() @click.option("--src", "-s", help="Source", type=click.STRING, panel="Main") @click.option("--dest", "-d", help="Destination", type=click.STRING, panel="Main") @click.option("--env", "-e", help="Environment", panel="Extra") @click.option("--quiet/--no-quiet", "-q/-Q", help="Quiet logging", panel="Extra") @click.help_option("--help", "-h", panel="Extra") @click.option_panel("Main", column_types=["opt_short", "opt_long", "metavar", "help"]) @click.option_panel("Extra", column_types=["help", "metavar", "opt_primary", "opt_secondary"]) @click.rich_config({ "delimiter_comma": ", ", "style_option_negative": "bold magenta", "style_switch_negative": "bold blue", }) def move_item(src, dest, env, quiet): """Move an item from a src location to a dest location""" pass if __name__ == "__main__": move_item() ``` -------------------------------- ### Configure panels via option panel parameter Source: https://ewels.github.io/rich-click/latest/documentation/panels/overview Configures panels directly within each option using the panel parameter. This produces the same output as the @click.option_panel() decorator approach, and panels are created on the fly if not defined separately. ```python # /// script # dependencies = ["rich-click>=1.9"] # /// import rich_click as click @click.command() @click.option("--src", panel="Main", help="Source") @click.option("--dest", panel="Main", help="Destination") @click.option("--env", panel="Extra", help="Environment") @click.option("--log-level", panel="Extra", help="Log level") @click.help_option(panel="Extra") @click.version_option("1.2.3", panel="Extra") @click.rich_config({"style_options_panel_border": "dim blue"}) def move_item(src, dest, env, log_level): """Move an item from a src location to a dest location""" pass if __name__ == "__main__": move_item() ``` -------------------------------- ### Reorder help sections and remove metavar column Source: https://ewels.github.io/rich-click/latest/documentation/panels/advanced Example showing how to remove the metavar column and append it to help text by configuring options_table_column_types and options_table_help_sections. Uses rich-click>=1.9. Demonstrates reordering help text elements. ```python # /// script # dependencies = ["rich-click>=1.9"] # /// import rich_click as click @click.command() @click.argument("src") @click.argument("dest") @click.option("--env", help="Environment", default="dev", show_default=True, envvar="ENV", show_envvar=True, required=True) @click.option("--log-level", help="Log level", default="INFO", show_default=True, envvar="LOG_LEVEL", show_envvar=True, deprecated=True) @click.option_panel("Arguments") @click.option_panel("Options") @click.rich_config({ "options_table_column_types": ["opt_long", "opt_short", "help"], "options_table_help_sections": ["metavar", "required", "help", "default", "envvar"] }) def move_item(src, dest, env, log_level): """Move an item from a src location to a dest location""" pass if __name__ == "__main__": move_item() ``` -------------------------------- ### Inherit parent help config with dict in @click.rich_config() Source: https://ewels.github.io/rich-click/latest/blog/2024/04/30/version-1.8 Child commands inherit the parent's help config as a baseline and only update fields specified in the dict. In this example, my_group has style_option="red" and my_subcommand has both style_option="red" and style_argument="green". ```python @click.group @click.rich_config(help_config={"style_option": "red"}) def my_group(): ... @my_group.command @click.rich_config(help_config={"style_argument": "green"}) def my_subcommand(): ... ``` -------------------------------- ### Define arguments and options in Click Source: https://ewels.github.io/rich-click/latest/documentation/introduction_to_click Demonstrates using @click.argument and @click.option to handle positional arguments and optional flags with default values and types. ```python # docs/code_snippets/introduction_to_click/hello_v2.py import click @click.command() @click.argument("name") @click.option("--times", "-t", default=1, type=click.INT, show_default=True, help="Number of times to print the greeting.") @click.option("--say-goodbye", is_flag=True, default=False, help="After saying hello, say goodbye.") def hello(name, times, say_goodbye): """Prints 'hello, [name]!' into the terminal N times.""" for t in range(times): print(f"Hello, {name}!") if say_goodbye: print("Goodbye!") if __name__ == "__main__": hello() ``` -------------------------------- ### Set theme via environment variable for quartz-box Source: https://ewels.github.io/rich-click/latest/documentation/themes Use the RICH_CLICK_THEME environment variable to set the quartz-box theme and run the CLI help command. ```bash export RICH_CLICK_THEME=quartz-box python docs/code_snippets/themes/cli.py --help ``` -------------------------------- ### rich-click newline handling in help text Source: https://ewels.github.io/rich-click/latest/blog/2025/09/16/version-1.9 Example of rich-click 1.9's newline handling in command help text. The docstring demonstrates that double newlines are preserved, single newlines are usually collapsed, indented lines with 4+ spaces are preserved, unordered lists are preserved, and block quotes are preserved. Run the script to see the help output. ```python # /// script # dependencies = ["rich-click>=1.9"] # /// import rich_click as click @click.command("newline-control") def cli(): """Newline control Double newlines are always preserved. But single newlines are usually not. There are however a few situations where we preserve single newlines. Indented lines with 4+ spaces are preserved: ```python from foo import bar \n\ bar.action() ``` Unordered lists are preserved: - like - this also: * newlines within unordered lists are collapsed down. * pretty neat! Last but not least, we preserve: > block > quotes """ if __name__ == "__main__": cli() ``` -------------------------------- ### Set plain-box theme Source: https://ewels.github.io/rich-click/latest/documentation/themes Applies a theme with no style at all. ```bash export RICH_CLICK_THEME=plain-box python docs/code_snippets/themes/cli.py --help ``` -------------------------------- ### Configure complex command groups with wildcards Source: https://ewels.github.io/rich-click/latest/blog/2024/04/30/version-1.8 Demonstrates using wildcards in OPTION_GROUPS to apply specific option groupings across multiple sub-commands. ```python # foo.py import rich_click as click click.rich_click.OPTION_GROUPS = { "cli * c": [ { "name": "foo", "options": ["--flag1"] } ] } @click.group def cli(): ... @cli.group("x") @click.option("--flag1") @click.option("--flag2") def x(flag1, flag2): ... @x.command("a") @click.option("--flag1") @click.option("--flag2") def xa(flag1, flag2): ... @x.command("b") @click.option("--flag1") @click.option("--flag2") def xb(flag1, flag2): ... @x.command("c") @click.option("--flag1") @click.option("--flag2") def xc(flag1, flag2): ... @cli.group("y") @click.option("--flag1") @click.option("--flag2") def y(flag1, flag2): ... @y.command("a") @click.option("--flag1") @click.option("--flag2") def ya(flag1, flag2): ... @y.command("b") @click.option("--flag1") @click.option("--flag2") def yb(flag1, flag2): ... @y.command("c") @click.option("--flag1") @click.option("--flag2") def yc(flag1, flag2): ... @cli.group("z") @click.option("--flag1") @click.option("--flag2") def z(flag1, flag2): ... @z.command("a") @click.option("--flag1") @click.option("--flag2") def za(flag1, flag2): ... @z.command("b") @click.option("--flag1") @click.option("--flag2") def zb(flag1, flag2): ... @z.command("c") @click.option("--flag1") @click.option("--flag2") def zc(flag1, flag2): ... cli() ``` -------------------------------- ### Configure option tables with rich_config Source: https://ewels.github.io/rich-click/latest/blog/2025/09/16/version-1.9 Demonstrates using options_table_column_types and options_table_help_sections to define the layout of the help output, replacing deprecated metavars configuration options. ```python # /// script # dependencies = ["rich-click>=1.9"] # /// import rich_click as click @click.command() @click.argument("src") @click.argument("dest") @click.option("--env", help="Environment", default="dev", show_default=True, envvar="ENV", show_envvar=True, required=True) @click.option("--log-level", help="Log level", default="INFO", show_default=True, envvar="LOG_LEVEL", show_envvar=True, deprecated=True) @click.option_panel("Arguments") @click.option_panel("Options") @click.rich_config({ "options_table_column_types": ["opt_long", "opt_short", "help"], "options_table_help_sections": ["metavar", "required", "help", "default", "envvar"] }) def move_item(src, dest, env, log_level): """Move an item from a src location to a dest location""" pass if __name__ == "__main__": move_item() ``` -------------------------------- ### Set theme via environment variable for quartz2-box Source: https://ewels.github.io/rich-click/latest/documentation/themes Use the RICH_CLICK_THEME environment variable to set the quartz2-box theme and run the CLI help command. ```bash export RICH_CLICK_THEME=quartz2-box python docs/code_snippets/themes/cli.py --help ``` -------------------------------- ### Dump RichHelpConfiguration to global config Source: https://ewels.github.io/rich-click/latest/documentation/configuration Use RichHelpConfiguration().dump_to_globals() to write a help config into the global config. This is mostly for internal use; the CLI will then use the dumped style_option. ```python import rich_click as click import rich_click.rich_click as rc help_config = click.RichHelpConfiguration(style_option="bold red") help_config.dump_to_globals() # The CLI will have `style_option = "bold red"` since it pulls from the globals. @click.command() def my_command(): """Help text here.""" ``` -------------------------------- ### Handling --help option: Fix (method 1) Source: https://ewels.github.io/rich-click/latest/documentation/panels/tips Explicitly define the panels and include --help in the options list for the target panel. ```python # /// script # dependencies = ["rich-click>=1.9"] # /// import rich_click as click # Explicitly define the panels and place --help in Extra: @click.command() @click.option("--src", panel="Main") @click.option("--dest", panel="Main") @click.option("--env", panel="Extra") @click.option("--log-level", panel="Extra") @click.option_panel("Main") @click.option_panel("Extra", options=["--env", "--log-level", "--help"]) def move_item(src, dest, env, log_level): """Move an item from a src location to a dest location""" pass if __name__ == "__main__": move_item() ``` -------------------------------- ### Show arguments panel with show_arguments config Source: https://ewels.github.io/rich-click/latest/documentation/panels/overview Shows how to give positional arguments their own panel by setting the show_arguments config option to True in @click.rich_config(). ```python # /// script # dependencies = ["rich-click>=1.9"] # /// import rich_click as click @click.command() @click.argument("src") @click.argument("dest") @click.option("--env", help="Environment") @click.option("--log-level", help="Log level") @click.rich_config({"show_arguments": True}) def move_item(src, dest, env, log_level): """Move an item from a src location to a dest location""" pass if __name__ == "__main__": move_item() ``` -------------------------------- ### Apply configuration as a theme Source: https://ewels.github.io/rich-click/latest/documentation/configuration Converts a RichHelpConfiguration object into a theme using to_theme, allowing it to be overridden by user settings. ```python import rich_click as click config = click.RichHelpConfiguration() theme = config.to_theme() @click.group("cli") # This can be overridden. @click.rich_config({"theme": theme}) def cli(): """My CLI help text""" ``` -------------------------------- ### Use RICH_CLICK_THEME with rich-click CLI Source: https://ewels.github.io/rich-click/latest/documentation/themes Applies a theme when wrapping a base Click CLI with the rich-click CLI. The first uses the rich-click command directly, the second uses uv to run it with the required packages. ```bash export RICH_CLICK_THEME=star-modern rich-click flask --help ``` ```bash export RICH_CLICK_THEME=star-modern uv run --with=flask --with=rich-click \ rich-click flask --help ``` -------------------------------- ### Use rich-click CLI tool Source: https://ewels.github.io/rich-click/latest Prefix any existing Click-based command with rich-click to format its help output. ```bash $ rich-click awesometool --help Usage: awesometool [OPTIONS] ..more richified output below.. ``` -------------------------------- ### Define command panels with @click.command_panel() Source: https://ewels.github.io/rich-click/latest/documentation/panels/overview Defines panels for sub-commands using @click.command_panel() on a group. Each panel groups a list of command names, and can include a help string for the panel. ```python # /// script # dependencies = ["rich-click>=1.9"] # /// import rich_click as click @click.group() @click.command_panel("Items", commands=["move-item", "update-item"]) @click.command_panel("Users", commands=["create-user", "update-user"], help="User management commands") def cli(): """CLI""" pass @cli.command() def move_item(): """Move an item""" pass @cli.command() def update_item(): """Update an item""" pass @cli.command() def create_user(): """Create a user""" pass @cli.command() def update_user(): """Update a user""" pass if __name__ == "__main__": cli() ```