Styling¶
Click Extra ships its own Style class as a drop-in replacement for cloup.Style (which itself wraps click.style). The runtime contract — calling the instance to apply styling, equality, hashing, with_() — is identical to cloup’s; everything below is purely additive ergonomics.
The class lands automatically when you do from click_extra import Style: __init__.py re-exports it after from cloup import * so the click-extra version takes precedence.
[31m[1mhello[0m
Hex-string color shorthand¶
Style.fg and Style.bg accept "#rrggbb" or "#rgb" shorthand strings alongside Click’s named colors and RGB tuples. The shorthand is converted to an RGB tuple on construction:
[38;2;241;250;140msample[0m
[38;2;170;187;204msample[0m
[38;2;241;250;140msample[0m
[93msample[0m
REPL-friendly __repr__ and __str__¶
The compact __repr__ hides None and falsy attributes and renders RGB tuples as #rrggbb. The __str__ returns the styled word "sample" so print(style) and debugger inspectors visualize what the style does, not just its fields:
Style(fg=#f1fa8c, bold, italic)
[38;2;241;250;140m[1m[3msample[0m
Composition operator a | b¶
The | operator merges two styles. The right operand wins on conflicts; None fields on either side don’t override the other side’s set fields. The reflected __ror__ makes mixing with cloup.Style (or any compatible subclass) symmetric:
Style(fg='magenta', bold, italic)
Style(fg='cyan', bold, italic)
cascade(base) — fill gaps from a base style¶
Where | lets the right operand always win, cascade keeps the instance’s set values and only fills its None fields from base. Useful for theme-inheritance patterns where a derived style should keep its overrides and inherit the rest:
Style(fg='magenta', bold, italic)
TOML/JSON round-trip with to_dict / from_dict¶
Style.to_dict() emits a plain mapping containing only the set fields, with RGB tuples flattened to #rrggbb strings so the output round-trips through TOML/JSON/YAML untouched. Style.from_dict() is the symmetric loader and rejects unknown keys with TypeError so typos surface immediately:
{'fg': '#f1fa8c', 'bold': True}
{"fg": "#f1fa8c", "bold": true}
True
TypeError('Unknown Style field(s): colour')
to_css() — CSS declaration list¶
Renders the style as a semicolon-separated CSS declaration list, suitable for inline style="..." attributes on HTML spans. Used by AnsiHtmlFormatter and the theme palette swatches:
color: #f1fa8c; font-weight: bold; font-style: italic
color: #ff5555; text-decoration: underline line-through
color: cyan; opacity: 0.6
The mapping is:
Attribute |
CSS declaration |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
underline, overline and strikethrough collapse into a single text-decoration declaration when more than one is set.
from_ansi() — parse ANSI SGR escapes¶
Given one or more consecutive ANSI SGR escapes (the \x1b[...m sequences Click emits), rebuild a Style instance. Supports the standard 8/16-color codes (30–37, 40–47, 90–97, 100–107), the 38;5;n / 48;5;n 256-color extension, and the 38;2;r;g;b / 48;2;r;g;b 24-bit extension. Reset codes (0) are ignored. Multiple back-to-back escapes (as Click emits when combining colors with attributes) are merged into a single Style:
Style(fg='red', bold)
Style(fg='red', bold)
Style(fg=#f1fa8c)
Style(fg=226)
from_ansi is the inverse of calling the style: parsing the output of Style(fg="red", bold=True)("text") recovers the same style.
contrast_ratio(other) — WCAG accessibility check¶
Returns the WCAG 2.x contrast ratio between this style’s foreground and another style’s foreground. Result is in [1, 21]: 1 = identical colors (no contrast), 21 = maximum contrast (black on white). WCAG AA requires 4.5+ for normal text and 3.0+ for large text; AAA wants 7.0+ and 4.5+ respectively.
9.04
1.23
4.08
Click Extra uses this internally to gate the WCAG legibility floor and AA Large compliance for branded themes.
Equality and hash¶
Style overrides cloup’s __eq__ and __hash__ to skip the lazily-populated _style_kwargs cache, so two otherwise-identical styles compare equal whether or not either has been called yet:
True
True
True
click_extra.styling API¶
classDiagram
Style <|-- Style
Drop-in replacement for cloup.Style with extra features.
The module name mirrors cloup.styling, the upstream module that hosts
the original Style class. Click Extra’s Style is a subclass that
keeps cloup’s runtime contract (calling, equality, hashing, with_())
intact and adds:
A compact, single-line
__repr__that hidesNoneand falsy attributes and renders RGB tuples as#rrggbbhex.Hex-string color shorthand:
Style(fg="#f1fa8c")works alongsideStyle(fg=(241, 250, 140)).A
__str__that returns the styled word"sample"so REPL prints and debuggers visualize what the style does, not just its fields.A composition operator
a | bthat merges two styles, with the right operand winning on conflicts.A
Style.cascade()method that fills the style’sNonefields from a base style without overriding any value already set.Style.to_dict()/Style.from_dict()for round-tripping styles through TOML/JSON/YAML.Style.to_css()for emitting CSS-equivalent declarations: useful for HTML renderings of help screens.Style.from_ansi()for parsing an ANSI SGR escape sequence back into aStyleinstance.Style.contrast_ratio()returning the WCAG contrast ratio between two foreground colors. Useful for theme designers checking accessibility.
- click_extra.styling.fields_to_dict(instance, *, encode=<function <lambda>>, keep=<function <lambda>>)[source]¶
Serialize a dataclass instance to a dict of set fields.
Walks every field via
dataclasses.fields(), skips the internal_style_kwargscache, applies keep to decide which fields are written (default: every non-default field), and passes the surviving values through encode (default: identity).- Parameters:
instance (
Any) – the dataclass to serialize.encode (
Callable[[Any,Any],Any]) – callable(field, value) -> encoded_valueapplied to every kept value. Use to convert RGB tuples to#rrggbbstrings, nested dataclasses to dicts, etc.keep (
Callable[[Any,Any],bool]) – callable(field, value) -> booldeciding whether the field is emitted. Default keeps everything that differs from the field’s declared default.
- Return type:
- click_extra.styling.dict_to_fields(cls, data, *, decode=<function <lambda>>)[source]¶
Validate data’s keys against cls’s dataclass fields and decode them.
Returns a kwargs dict ready to splat into
cls(**kwargs). RaisesTypeErrorlisting every unknown key, so callers can build a constructor call without an extra pre-validation pass.- Parameters:
- Return type:
- click_extra.styling.cascade_fields(base, overlay, *, is_set=<function <lambda>>)[source]¶
Layer overlay’s set fields on top of base, returning a merged kwargs dict.
Walks both instances’ fields and produces a dict suitable for
type(base)(**kwargs)(ordataclasses.replace(base, **kwargs)). The slot-level analogue ofStyle.cascade’s attribute-level merge.- Parameters:
base (
Any) – the underlying instance whose fields fill any gaps.overlay (
Any) – the instance whose set fields win on conflicts.is_set (
Callable[[Any,Any],bool]) – callable(field, value) -> booldistinguishing “set” from “unset” fields. Default treats a value equal to the field default as unset.
- Return type:
- class click_extra.styling.Style(fg=None, bg=None, bold=None, dim=None, underline=None, overline=None, italic=None, blink=None, reverse=None, strikethrough=None, text_transform=None)[source]¶
Bases:
Stylecloup.Stylewith extra ergonomics.See the module docstring for the full list of additions. The runtime contract (calling the instance to apply styling, equality, hashing,
with_()) is otherwise identical tocloup.Style.- fg: str | tuple[int, int, int] | int | None = None¶
Foreground color: named ANSI string,
#rrggbbhex, RGB tuple, or palette index.
- bg: str | tuple[int, int, int] | int | None = None¶
Background color: named ANSI string,
#rrggbbhex, RGB tuple, or palette index.
- cascade(base)[source]¶
Return a copy with
Nonefields filled in from base.The instance’s own non-
Nonevalues always win:cascadeonly fills gaps. Useful for theme inheritance:derived.cascade(parent)keepsderived’s overrides and inherits the rest fromparent.- Return type:
- to_dict()[source]¶
Serialize to a plain dict with only the set fields.
RGB tuples are emitted as
#rrggbbstrings so the result round-trips through TOML/JSON/YAML untouched. Pair withfrom_dict()to rebuild aStyle.
- classmethod from_dict(data)[source]¶
Build a
Stylefrom the plain dict produced byto_dict().Validates that every key in data names a known
Stylefield and raisesTypeErrorotherwise. Pair withto_dict()to round-trip through TOML/JSON/YAML.- Return type:
- to_css()[source]¶
Render the style as a semicolon-separated CSS declaration list.
Style(fg="#f1fa8c", bold=True).to_css()returns"color: #f1fa8c; font-weight: bold". Suitable for inlinestyle="..."attributes on HTML spans.- Return type:
- classmethod from_ansi(escape)[source]¶
Parse one or more consecutive ANSI SGR escapes into a
Style.Supports the standard 8/16-color codes (30–37, 40–47, 90–97, 100–107), the
38;5;n/48;5;n256-color extension, and the38;2;r;g;b/48;2;r;g;b24-bit extension. Reset codes (0) are ignored. Multiple back-to-back escapes (as click emits when combining colors with attributes:\x1b[31m\x1b[1m) are merged into a singleStyle.- Return type:
- contrast_ratio(other)[source]¶
Return the WCAG 2.x contrast ratio between this fg and other’s fg.
Result is in
[1, 21]: 1 = identical colors (no contrast), 21 = maximum contrast (black on white). WCAG AA requires 4.5+ for normal text, 3.0+ for large text; AAA wants 7.0+ and 4.5+ respectively.- Return type: