Skip to content

Gradient Rule Reference

Bases: Rule

A Rule with a gradient background.

Parameters:

Name Type Description Default
title Optional[str]

The text to display as the title.

required
title_style StyleType

The style to apply to the title text. Defaults to NULL_STYLE.

NULL_STYLE
colors List[ColorType]

A list of color strings for the gradient. Defaults to empty list.

None
thickness int

Thickness level of the rule (0 to 3). Defaults to 2.

2
style StyleType

The style of the rule line. Defaults to NULL_STYLE.

NULL_STYLE
rainbow bool

If True, use a rainbow gradient regardless of colors. Defaults to False.

False
hues int

Number of hues in the gradient if colors are not provided. Defaults to 10.

10
end str

End character after the rule. Defaults to newline.

'\n'
align AlignMethod

Alignment of the rule. Defaults to "center".

'center'
Source code in src/rich_gradient/rule.py
class GradientRule(Rule):
    """A Rule with a gradient background.

    Args:
        title (Optional[str]): The text to display as the title.
        title_style (StyleType, optional): The style to apply to the title text. Defaults to NULL_STYLE.
        colors (List[ColorType], optional): A list of color strings for the gradient. Defaults to empty list.
        thickness (int, optional): Thickness level of the rule (0 to 3). Defaults to 2.
        style (StyleType, optional): The style of the rule line. Defaults to NULL_STYLE.
        rainbow (bool, optional): If True, use a rainbow gradient regardless of colors. Defaults to False.
        hues (int, optional): Number of hues in the gradient if colors are not provided. Defaults to 10.
        end (str, optional): End character after the rule. Defaults to newline.
        align (AlignMethod, optional): Alignment of the rule. Defaults to "center".
    """

    # @snoop()
    def __init__(
        self,
        title: Optional[str],
        title_style: StyleType = NULL_STYLE,
        colors: Optional[List[ColorInputType]] = None,
        thickness: int = 2,
        style: StyleType = NULL_STYLE,
        rainbow: bool = False,
        hues: int = 10,
        end: str = "\n",
        align: AlignMethod = "center",
    ) -> None:
        # Validate thickness input
        if thickness < 0 or thickness > 3:
            raise ValueError(
                f"Invalid thickness: {thickness}. Thickness must be between 0 and 3."
            )
        # Validate type
        if title is not None and not isinstance(title, str):
            raise TypeError(f"title must be str, got {type(title).__name__}")

        if not isinstance(title_style, (str, Style)):
            raise TypeError(
                f"title_style must be str or Style, got {type(title_style).__name__}"
            )
        if not isinstance(style, (str, Style)):
            raise TypeError(f"style must be str or Style, got {type(style).__name__}")
        # Determine character based on thickness
        self.characters = CHARACTER_MAP.get(thickness, "━")
        # Parse and store the title style
        self.title_style = Style.parse(str(title_style))
        # Initialize the base Rule with provided parameters
        super().__init__(
            title=title or "",
            characters=self.characters,
            style=Style.parse(str(style)),
            end=end,
            align=align,
        )
        # Parse and store the gradient colors
        self.colors = self._parse_colors(
            colors if colors is not None else [], rainbow, hues
        )

    # @snoop(watch=["title_style", "style"])
    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        """Render the gradient rule.

        Args:
            console (Console): The console to render to.
            options (ConsoleOptions): The console options.

        Yields:
            RenderResult: The rendered segments of the gradient rule.
        """
        # Prepare a base rule with no style to extract segments
        base_rule = Rule(
            title=self.title or "",
            characters=self.characters,
            style=NULL_STYLE,
            end=self.end,
            align=cast(AlignMethod, self.align),
        )
        # Render the base rule to get segments
        rule_segments = console.render(base_rule, options=options)
        # Concatenate segment texts to form the full rule text
        rule_text = "".join(seg.text for seg in rule_segments)

        # If no title style, render the gradient text directly
        if self.title_style == NULL_STYLE:
            gradient_rule = Text(rule_text, colors=self.colors)
            yield from console.render(gradient_rule, options)
            return
        # Create gradient text for the rule
        gradient_rule = Text(rule_text, colors=self.colors)

        # Extract the title string for highlighting
        title = self.title.plain if isinstance(self.title, Text) else str(self.title)

        # Apply the title style highlight after gradient generation
        if title and self.title_style != NULL_STYLE:
            gradient_rule.highlight_words([title], style=self.title_style)

        # Yield the styled gradient text
        yield from console.render(gradient_rule, options)

    def _parse_colors(
        self,
        colors: Sequence[ColorInputType],
        rainbow: bool,
        hues: int,
    ) -> List[str]:
        """Parse colors for the gradient.

        Args:
            colors (List[ColorType]): A list of color strings.
            rainbow (bool): If True, use a rainbow gradient.
            hues (int): Number of hues in the gradient.
        Raises:
            ValueError: If any color is not a valid string.
            ColorParseError: If a color string cannot be parsed.

        Returns:
            List[str]: A list of hex color strings for the gradient.
        """
        # Use full rainbow spectrum if rainbow flag is set, or if insufficient colors
        if rainbow:
            return Spectrum(hues).hex
        _colors: List[str] = []
        if len(colors) < 2:
            raise ValueError(
                "At least two colors are required for a gradient. "
                "Please provide a list of at least two color strings."
            )
        for color in colors:
            # Validate color is a string
            if not isinstance(color, str):
                raise ValueError(
                    f"Invalid color: {color}. Please provide a valid color string."
                )
            try:
                # Convert color string to hex format
                _colors.append(Color.parse(color).get_truecolor().hex)
            except ColorParseError as ce:
                raise ColorParseError(
                    f"Invalid color: {color}. Please provide a valid color string."
                ) from ce
        return _colors

__rich_console__(console, options)

Render the gradient rule.

Parameters:

Name Type Description Default
console Console

The console to render to.

required
options ConsoleOptions

The console options.

required

Yields:

Name Type Description
RenderResult RenderResult

The rendered segments of the gradient rule.

Source code in src/rich_gradient/rule.py
def __rich_console__(
    self, console: Console, options: ConsoleOptions
) -> RenderResult:
    """Render the gradient rule.

    Args:
        console (Console): The console to render to.
        options (ConsoleOptions): The console options.

    Yields:
        RenderResult: The rendered segments of the gradient rule.
    """
    # Prepare a base rule with no style to extract segments
    base_rule = Rule(
        title=self.title or "",
        characters=self.characters,
        style=NULL_STYLE,
        end=self.end,
        align=cast(AlignMethod, self.align),
    )
    # Render the base rule to get segments
    rule_segments = console.render(base_rule, options=options)
    # Concatenate segment texts to form the full rule text
    rule_text = "".join(seg.text for seg in rule_segments)

    # If no title style, render the gradient text directly
    if self.title_style == NULL_STYLE:
        gradient_rule = Text(rule_text, colors=self.colors)
        yield from console.render(gradient_rule, options)
        return
    # Create gradient text for the rule
    gradient_rule = Text(rule_text, colors=self.colors)

    # Extract the title string for highlighting
    title = self.title.plain if isinstance(self.title, Text) else str(self.title)

    # Apply the title style highlight after gradient generation
    if title and self.title_style != NULL_STYLE:
        gradient_rule.highlight_words([title], style=self.title_style)

    # Yield the styled gradient text
    yield from console.render(gradient_rule, options)

_parse_colors(colors, rainbow, hues)

Parse colors for the gradient.

Parameters:

Name Type Description Default
colors List[ColorType]

A list of color strings.

required
rainbow bool

If True, use a rainbow gradient.

required
hues int

Number of hues in the gradient.

required

Raises: ValueError: If any color is not a valid string. ColorParseError: If a color string cannot be parsed.

Returns:

Type Description
List[str]

List[str]: A list of hex color strings for the gradient.

Source code in src/rich_gradient/rule.py
def _parse_colors(
    self,
    colors: Sequence[ColorInputType],
    rainbow: bool,
    hues: int,
) -> List[str]:
    """Parse colors for the gradient.

    Args:
        colors (List[ColorType]): A list of color strings.
        rainbow (bool): If True, use a rainbow gradient.
        hues (int): Number of hues in the gradient.
    Raises:
        ValueError: If any color is not a valid string.
        ColorParseError: If a color string cannot be parsed.

    Returns:
        List[str]: A list of hex color strings for the gradient.
    """
    # Use full rainbow spectrum if rainbow flag is set, or if insufficient colors
    if rainbow:
        return Spectrum(hues).hex
    _colors: List[str] = []
    if len(colors) < 2:
        raise ValueError(
            "At least two colors are required for a gradient. "
            "Please provide a list of at least two color strings."
        )
    for color in colors:
        # Validate color is a string
        if not isinstance(color, str):
            raise ValueError(
                f"Invalid color: {color}. Please provide a valid color string."
            )
        try:
            # Convert color string to hex format
            _colors.append(Color.parse(color).get_truecolor().hex)
        except ColorParseError as ce:
            raise ColorParseError(
                f"Invalid color: {color}. Please provide a valid color string."
            ) from ce
    return _colors