Components

Caspian components are just Python functions. They encapsulate logic, styles, and structure. Start simple with basic strings, or scale up to fully type-safe, reactive UI elements.

1. The Basics

A standard component handles prop merging and returns an HTML string. Use merge_classes to ensure Tailwind classes don't conflict.

Developer Speed Tip

Don't write boilerplate. If you have the VS Code Extension installed, just type:

caspcom + Tab

It automatically generates the standard structure seen below.

src/components/Container.py
from casp.html_attrs import get_attributes, merge_classes
from casp.component_decorator import component

@component
def Container(**props):
    # 1. Extract 'class' to merge it safely
    incoming_class = props.pop("class", "")
    final_class = merge_classes("mx-auto max-w-7xl px-4", incoming_class)

    # 2. Extract children (inner HTML)
    children = props.pop("children", "")

    # 3. Generate remaining attributes (id, data-*, etc.)
    attributes = get_attributes({
        "class": final_class
    }, props)

    return f'<div {attributes}>{children}</div>'

2. Type-Safe Props

Want TypeScript-like validation? Use Python's Literal type. Your editor will autocomplete variants, and linter will catch typos.

src/components/ui/Button.py
from typing import Literal, Any
from casp.component_decorator import component

# Define strict types for autocomplete
ButtonVariant = Literal["default", "destructive", "outline"]
ButtonSize = Literal["default", "sm", "lg"]

@component
def Button(
    children: Any = "",
    # Editor will now suggest: "default" | "destructive" | "outline"
    variant: ButtonVariant = "default", 
    size: ButtonSize = "default",
    **props,
) -> str:
    # ... implementation (merging classes based on variant/size) ...
    return f"<button {attrs}>{children}</button>"

3. HTML Templates & Reactivity

For complex layouts or reactive state, separate your code into an HTML file. Use html_file to bridge them.

Counter.py
from casp.component_decorator import component, html_file

@component
def Counter(**props):
    return html_file("Counter.html", **props)
Counter.html
<div>
  <h3>[[label]]</h3> <!-- Prop -->
  <button onclick="inc()">
     {count} <!-- State -->
  </button>
</div>