Theme
Version
GitHub PyPI Discord

Provide and inject

Use this pattern when many components inside one part of the page need the same value, such as a theme, and passing it through every component in between would be tedious. <c-provide> makes the value available to everything inside it, and each component reads it with self.inject().

"""A button that reads its look from a provided theme."""

from typing import Any

from citry import Component

class ThemedButton(Component):
    """A button with no theme prop.

    It injects the nearest provided theme instead.
    """

    class Kwargs:
        text: str

    class Slots:
        pass

    def template_data(
        self,
        kwargs: Kwargs,
        slots: Slots,
    ) -> dict[str, Any]:
        theme = self.inject("theme")
        return {
            "text": kwargs.text,
            "label": theme.label,
            "style": f"background: {theme.accent};",
        }

    template = """
      <button class="themed-btn" c-style="style">
        {{ label }}: {{ text }}
      </button>
    """

    css = """
      .themed-btn {
        padding: 0.5rem 1rem;
        margin: 0.25rem;
        border: none;
        border-radius: 6px;
        color: #ffffff;
        font-family: system-ui, sans-serif;
        font-size: 0.95rem;
        cursor: pointer;
      }
    """

ThemedButton has no theme input. It calls self.inject("theme") and reads accent and label from the result. The inner <c-provide> uses the same key, so the button inside it gets the Forest theme, while the buttons outside it get Ocean. A component always reads the nearest value.

For defaults, errors, and providing values from Python, see Provide and inject.