-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #216 from dwreeves/enhance-docs
[Draft] Enhance documentation with accessibility and comparison pages
- Loading branch information
Showing
15 changed files
with
461 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# /// script | ||
# dependencies = [ | ||
# "rich", | ||
# ] | ||
from rich.console import Console | ||
from rich.box import SIMPLE | ||
from rich.table import Table | ||
|
||
console = Console(color_system="truecolor") | ||
table = Table(title="ANSI Colors", box=SIMPLE) | ||
colors = ["black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"] | ||
table.add_column("Color", style="bold") | ||
|
||
for variant in ["Normal", "Dim", "Bright", "Dim +\nBright"]: | ||
table.add_column(variant, style="bold") | ||
|
||
for color in colors: | ||
table.add_row( | ||
color, | ||
*[ | ||
f"[{style}{color}]██████[/{style}{color}]" | ||
for style in ["", "dim ", "bright_", "dim bright_"] | ||
] | ||
) | ||
|
||
console.print(table) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Accessibility | ||
|
||
This page goes over information relevant to both developers and users relating to accessibility considerations. | ||
|
||
Accessibility is very important to consider when developing applications. | ||
Colorblindness impacts roughly 4% of the general population, meaning that if your application gets even a small user base, there is a high probability that it is being used by someone with colorblindness. | ||
|
||
Fortunately, there are ways as both developers and users to address these accessibility concerns, as detailed on this page. | ||
|
||
## For users | ||
|
||
If you are a user of a **rich-click** CLI, there are a few options you have to improve accessibility for yourself. | ||
|
||
### 1. Use the `NO_COLOR` environment variable | ||
|
||
Rich uses the `NO_COLOR` standard ([more information here](https://no-color.org/)), giving rich-click built-in capability to allow the user to suppress color. | ||
|
||
So, to run any rich-click CLI program without colour, you can do: | ||
|
||
```shell | ||
export NO_COLOR=1 # Set environment variable in shell | ||
python cli.py # Run CLI tool | ||
|
||
# ... Or run as a single line: | ||
NO_COLOR=1 python cli.py | ||
``` | ||
|
||
In order to set this environment variable automatically every time you use the terminal, you can add it to your `~/.bashrc` (if using bash) or `~/.zshrc` (if using zsh): | ||
|
||
=== "bash" | ||
```shell | ||
echo "export NO_COLOR=1" >> ~/.bashrc | ||
``` | ||
|
||
=== "zsh" | ||
```shell | ||
echo "export NO_COLOR=1" >> ~/.zshrc | ||
``` | ||
|
||
!!!tip | ||
Note that other programs may also respect `NO_COLOR`, so it could have other effects! | ||
|
||
### 2. Configure your terminal's 4-bit ANSI colors | ||
|
||
The 4-bit ANSI color system is a set of 16 different colors implemented in every terminal. This is the most common way to set colors. | ||
These colors are **not** deterministic; different terminals use slightly different hex values for the ANSI colors. [Wikipedia has a full breakdown of all the variations in these colors](https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit) | ||
|
||
Most modern terminals have the ability to customize all of these colors in the terminals' settings. | ||
If you are having difficulty distinguishing colors, it is recommended that you adjust these settings. | ||
|
||
!!! note | ||
This will only work for CLIs that utilize the 4-bit ANSI color system. | ||
CLIs that utilize hex values or other color systems will not be impacted by your terminal's ANSI color settings. | ||
|
||
## For developers | ||
|
||
If you would like to make your CLI more accessible for others, there are a few rules of thumb you can follow: | ||
|
||
### 1. Use Rich features over Click features | ||
|
||
There are some Click features that rich-click doesn't override such as print statements and interactive prompts (see [Comparison of Click and rich-click](comparison_of_click_and_rich_click.md#click-features-that-rich-click-does-not-override)). | ||
|
||
In these cases, we recommend using native Rich functionality so that your end users can benefit from `NO_COLOR`, which Click does not support. | ||
|
||
So, for example: | ||
|
||
- `#!python Confirm.ask("[red]Are you sure?[/]")` is more accessible because it works with `NO_COLOR`. | ||
- `#!python click.confirm(click.echo("Are you sure?", fg="red"))` is less accessible because it cannot be overridden by `NO_COLOR`. | ||
|
||
### 2. Use 4-bit ANSI colors | ||
|
||
The 4-bit ANSI color system is a set of 16 different colors implemented in effectively every terminal, and they are the most common way to set colors. | ||
These colors are **not** deterministic; different terminals use slightly different hex values for the ANSI colors. [Wikipedia has a full breakdown of all the variations in these colors](https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit) | ||
|
||
!!! note | ||
**rich-click**'s logo references the ANSI colors! 😁 | ||
|
||
There are 16 total ANSI colors: 8 base ANSI colors, with each one having a "bright" variant: | ||
|
||
- `black`, `bright_black` | ||
- `red`, `bright_red` | ||
- `green`, `bright_green` | ||
- `yellow`, `bright_yellow` | ||
- `blue`, `bright_blue` | ||
- `magenta`, `bright_magenta` | ||
- `cyan`, `bright_cyan` | ||
- `white`, `bright_white` | ||
|
||
Additionally, each one of these can be modified with `dim`, which in modern terminals just applies a change to the opacity of the color, giving developers a total of 32 different colors that can be shown. | ||
|
||
Below is a quick script that renders all of these colors: | ||
|
||
```python | ||
{!code_snippets/accessibility/colors.py!} | ||
``` | ||
|
||
<!-- RICH-CODEX | ||
working_dir: docs/code_snippets/accessibility | ||
hide_command: true | ||
terminal_width: 48 | ||
--> | ||
![`python colors.py`](../images/code_snippets/accessibility/colors.svg){.screenshot} | ||
|
||
(The colors you see when running this locally will differ from the colors in the image.) | ||
|
||
The fact that the colors are not deterministic is a _benefit_ for accessibility; it means, for example, a user can customize their terminal so that the ANSI "red" is more suitable for them. | ||
Nearly every modern terminal allows for this sort of customization. | ||
|
||
This means that developers looking to create a more accessible experience should prefer ANSI colors. | ||
|
||
So, for example: | ||
|
||
- `#!python RichHelpConfiguration(style_option="red")` is more accessible because users can configure the hex value of this red. | ||
- `#!python RichHelpConfiguration(style_option="#FF0000")` is less accessible because it is not configurable by the end user. |
111 changes: 111 additions & 0 deletions
111
docs/documentation/comparison_of_click_and_rich_click.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# Comparison of Click and rich-click | ||
|
||
**rich-click** is a [shim](https://en.wikipedia.org/wiki/Shim_(computing)) around Click, | ||
meaning its API is designed to mirror the Click API and intercept some of the calls to slightly different functions. | ||
|
||
Everything available via `#!python import click` is also available via `#!python import rich_click as click`. | ||
|
||
**rich-click** is designed to keep the additional API surface introduced on top of Click as lightweight as possible. | ||
|
||
## Click features that rich-click overrides | ||
|
||
The only things that **rich-click** explicitly overrides in the high-level API are the decorators: | ||
|
||
- `click.command()` | ||
- `click.group()` | ||
|
||
The only change to these decorators is that by default, their `cls=` parameters point to the **rich-click** implementations of `Command` (i.e. `RichCommand`) and `Group` (i.e. `RichGroup`). | ||
|
||
!!! note | ||
There is also a thin wrapper around `pass_context()` to cast the `click.Context` type in the function signature to `click.RichContext` to assist with static type-checking with MyPy. Aside from different typing, there are no substantive changes to the `pass_context()` decorator. | ||
|
||
## Click features that rich-click does _not_ override | ||
|
||
### Base Click command classes | ||
|
||
You can still access the base Click classes by their original names: | ||
|
||
```python | ||
from rich_click import Command, Group, Context | ||
``` | ||
|
||
The above are the same as importing from `click`. | ||
|
||
**rich-click**'s subclasses all have the word Rich in front of them! | ||
|
||
```python | ||
from rich_click import RichCommand, RichGroup, RichContext | ||
``` | ||
|
||
### Echo and interactive elements | ||
|
||
**rich-click** deliberately does _not_ enrich certain Click features: | ||
|
||
```python | ||
click.echo() | ||
click.echo_via_pager() | ||
click.confirm() | ||
click.prompt() | ||
``` | ||
|
||
You are free to use these functions and they are available via `#!python import rich_click as click`, | ||
but Rich's markup will not work with these functions because these functions are just the base Click implementations, | ||
without any changes. | ||
|
||
This is a deliberate decision that we are unlikely to change in the future. | ||
We do not want to maintain a more spread-out API surface, and we encourage users to become comfortable using Rich directly; it's a great library and it's worth learning a little bit about it! | ||
If you'd like Rich markup for your echos and interactive elements, then you can: | ||
|
||
| Click Function | Rich Replacement | Rich Documentation | | ||
|---------------|------------------|---------------| | ||
| `click.echo()` | `rich.print()` | [Quick start](https://rich.readthedocs.io/en/stable/introduction.html#quick-start) | | ||
| `click.echo_via_pager()` | `rich.Console().pager()` | [Console](https://rich.readthedocs.io/en/stable/console.html#paging) | | ||
| `click.confirm()` | `rich.prompt.Confirm.ask()` | [Prompt](https://rich.readthedocs.io/en/stable/prompt.html) | | ||
| `click.prompt()` | `rich.prompt.Prompt.ask()` | [Prompt](https://rich.readthedocs.io/en/stable/prompt.html) | | ||
|
||
Below is a side-by-side comparison of Click and Rich implementations of echos and interactive elements in **rich-click**: | ||
|
||
=== "Click" | ||
|
||
```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() | ||
``` | ||
|
||
=== "Rich" | ||
|
||
```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() | ||
``` | ||
|
||
## Additional rich-click features | ||
|
||
- **rich-click** has a configuration object, `RichHelpConfiguration()`, that allows for control over how **rich-click** help text renders, so you are not just locked into the defaults. More information about this is described in [the **Configuration** docs](configuration.md). | ||
- **rich-click** comes with a CLI tool that allows you to convert regular Click CLIs into **rich-click** CLIs, and also lets you render your **rich-click** CLI help text as HTML and SVG. More information about this is described in [the **rich-click CLI** docs](rich_click_cli.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.