compone
Builds Python components using the compone framework for type-safe HTML/XML/RSS generation. Use when working with compone, creating Python components, generating markup in Python, or building framework-agnostic component libraries.
Install
npx skills add https://github.com/kissgyorgy/coding-agents/tree/master/skills/compone
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install kissgyorgy-coding-agents@llmmart
git clone https://github.com/kissgyorgy/coding-agents.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole kissgyorgy/coding-agents collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Compone - Python Component Framework
Helps developers create type-safe, reusable components using compone, a modern Python framework for generating markup (HTML, XML, RSS) with React-like patterns.
ALWAYS read core-concepts.md for basic usage.
- For integration with web frameworks, read
frameworks.md. - For HTML generation and patterns, read
html.md - For other formats like XML, RSS, SVG and others, read
other-formats.md - For more examples when writing complex components, read
other-formats.md - When writing tests for Components, read
testing.md
When to Use Compone
- Building framework-agnostic component libraries
- Type-safe HTML generation in Python
- Colocating markup with Python logic
- Generating XML, RSS, or other markup formats
- Creating reusable UI patterns across projects
- Teams preferring Python over template languages
Best Practices
- Type all props: Use type hints for better IDE support and static type checking
- Single responsibility: Keep components focused on one concern
- Composition over complexity: Build complex UIs from simple components
- Descriptive names: Use clear component and prop names
- Default values: Provide sensible defaults for optional props
- Framework agnostic: Don't tie components to specific web frameworks
Official Documentation
- Website: https://compone.kissgyorgy.me/
- GitHub: https://github.com/kissgyorgy/compone
- PyPI: https://pypi.org/project/compone/
Files (coding-agents)
-
core-concepts.md 7.3 KB
# Core Concepts Basic features of compone, creating components, using attributes. ## Importing ```python # HTML components from compone import Component, html # XML components from compone import Component, xml ``` ## Basic Component Syntax Components are created using the `@Component` decorator: ```python from compone import Component, html @Component def Button(text: str, variant: str = "primary", children=None): return html.Button(class_=f"btn btn-{variant}")[ text, children, ] ``` **Key points:** - Use `@Component` decorator on functions - Type hint all parameters (required for type safety) - `children` parameter receives nested content - Return markup using bracket notation for simple components - Use context manager syntax for complex components with inline code ## Nesting Components Components are called like functions and can be nested: ```python # Simple usage Button("Click me") # With children using bracket notation Button("Submit")["Save changes"] # Nested components html.Form[ html.Label["Name:"], html.Input(type="text", name="username"), Button("Submit", variant="success"), ] ``` ### Children Handling The `children` parameter captures nested content: ```python @Component def Card(title: str, children=None): return html.Div(class_="card")[ html.H2[title], html.Div(class_="card-body")[children], ] Card("My Card")[ html.P["First paragraph"], html.P["Second paragraph"], ] ``` ### Bracket Notation You can nest elements with bracket notation ```python html.Div[ html.H1["Title"], html.P["Paragraph text"], ] ``` - `html.TagName[children]` creates elements, `children` is passed as children keyword argument - Multiple children separated by commas - Strings, other elements, and components can be nested - Use parentheses for attributes: `html.Div(class_="container")[...]` ### Context manager notation You can nest elements with context manager notation too: ```python from compone import Component, html @Component def ComplexComponent(text: str, variant: str = "primary", children=None): with html.Div(class_="container") as div: with html.Span(class_="italic") as span: span += html.Button(class_=f"btn btn-{variant}")[ text, children, ] return div ``` You can mix and match Context Manager Notation and Bracket Notation, use the inline append operator `+=` to append elements as the children of the context manager object. They render the same way, for example, this components render the same output as the `ComplexComponent`: ```python @Component def ComplexComponent(text: str, variant: str = "primary", children=None): return html.Div(class_="container")[ html.Span(class_="italic")[ html.Button(class_=f"btn btn-{variant}")[ text, children, ] ] ] ``` ## Element Attributes ### Basic Attributes Pass attributes as keyword arguments: ```python html.Div(id="container", class_="wrapper") html.A(href="/page", title="Link title") html.Img(src="image.jpg", alt="Description") ``` ### Hyphenated Attributes Use underscores for hyphens in attribute names: ```python html.Div(data_id="123") # becomes data-id html.Meta(http_equiv="refresh") # becomes http-equiv html.Button(aria_label="Close") # becomes aria-label ``` ### Boolean Attributes Pass boolean values directly: ```python html.Input(type="checkbox", checked=True) html.Button(disabled=True) html.Script(async_=True, defer=True) ``` Attributes with `False` value will not be rendered. ### Python keyword Attributes Python keywords require trailing underscore: ```python html.Label(for_="input-id") # for is reserved html.Div(class_="my-class") # class is reserved ``` Attributes which are not valid Python argument names, can still be passed: ```python html.Label(**{"invalid:python*-variable": "value"}) # <label invalid:python*-variable="value"></label> ``` ### Default Values ```python @Component def Button( text: str, variant: str = "primary", size: str = "md", disabled: bool = False, children=None ): return html.Button( class_=f"btn btn-{variant} btn-{size}", disabled=disabled )[text, children] ``` ### Required vs Optional Attributes ```python from typing import Optional @Component def Article( title: str, # Required content: str, # Required author: Optional[str] = None, # Optional tags: list[str] = [], # Optional with default children=None ): return html.Article[ html.H1[title], html.P(class_="author")[f"By {author}"] if author else None, html.Div[content], html.Div(class_="tags")[ [html.Span[tag] for tag in tags] ] if tags else None, children, ] ``` ## Self-Closing Elements Elements that don't have closing tags use parentheses for attributes only: ```python html.Img(src="image.jpg", alt="Description") html.Input(type="text", name="username") html.Br() html.Hr() html.Meta(charset="utf-8") html.Link(rel="stylesheet", href="style.css") ``` ## Type Safety Leverage Python type hints for IDE support. ALWAYS type hint ALL arguments: ```python from typing import Literal @Component def Alert( message: str, level: Literal["info", "warning", "error"], dismissible: bool = False, children=None ): return html.Div( class_=f"alert alert-{level}", role="alert" )[ message, children, html.Button("×") if dismissible else None, ] # IDE will autocomplete and validate level parameter Alert("Something went wrong", level="error", dismissible=True) ``` ## Conditional Rendering Use None with conditional expressions to ignore element rendering: ```python @Component def Message(text: str, show_icon: bool = True, children=None): return html.Div[ html.I(class_="icon") if show_icon else None, html.Span[text], children, ] ``` ## List Rendering Use Python list comprehensions to render repeating elements: ```python @Component def TodoList(items: list[str], children=None): return html.Ul[ [html.Li[item] for item in items], children, ] # Usage TodoList(["Task 1", "Task 2", "Task 3"]) ``` ### Caching Static Components ```python from functools import lru_cache @lru_cache(maxsize=128) @Component def StaticHeader(site_name: str): return html.Header[ html.H1[site_name], html.Nav[ html.A(href="/")["Home"], html.A(href="/about")["About"], ], ] # Subsequent calls with same args use cached result header1 = StaticHeader("My Site") header2 = StaticHeader("My Site") # Returns cached ``` ### Prerendering Components When a component can be completely static, render them at import time and use contant variables: ```python # Compute expensive strings once FOOTER_HTML = str(html.Footer[ html.P["© 2024 Company"], html.Nav[ html.A(href="/terms")["Terms"], html.A(href="/privacy")["Privacy"], ], ]) @Component def Page(title: str, children): return html.Html[ html.Head[html.Title[title]], html.Body[ children, FOOTER_HTML, # Reuse precomputed string ], ] ``` -
examples.md 13.7 KB
## Component Libraries ### UI Component Library ```python # ui_library.py from compone import Component, html from typing import Literal, Optional @Component def Container( max_width: Literal["sm", "md", "lg", "xl"] = "lg", children=None ): widths = {"sm": "640px", "md": "768px", "lg": "1024px", "xl": "1280px"} return html.Div( class_="container", style=f"max-width: {widths[max_width]}; margin: 0 auto; padding: 0 1rem;" )[children] @Component def Button( text: str, variant: Literal["primary", "secondary", "danger"] = "primary", size: Literal["sm", "md", "lg"] = "md", disabled: bool = False, children=None ): return html.Button( class_=f"btn btn-{variant} btn-{size}", disabled=disabled )[text, children] @Component def Card( title: Optional[str] = None, footer: Optional[str] = None, children=None ): return html.Div(class_="card")[ html.Div(class_="card-header")[html.H3[title]] if title else None, html.Div(class_="card-body")[children], html.Div(class_="card-footer")[footer] if footer else None, ] @Component def Grid( cols: int = 2, gap: int = 4, children=None ): return html.Div( class_="grid", style=f"display: grid; grid-template-columns: repeat({cols}, 1fr); gap: {gap}rem;" )[children] @Component def Alert( message: str, level: Literal["info", "warning", "error", "success"] = "info", dismissible: bool = False, children=None ): icons = { "info": "ℹ", "warning": "⚠", "error": "✖", "success": "✓", } return html.Div( class_=f"alert alert-{level}", role="alert" )[ html.Span(class_="alert-icon")[icons[level]], html.Span[message, children], html.Button( class_="alert-close", type="button", aria_label="Close" )["×"] if dismissible else None, ] @Component def Badge( text: str, variant: Literal["primary", "secondary", "success", "danger"] = "primary", ): return html.Span(class_=f"badge badge-{variant}")[text] @Component def List( items: list[str], ordered: bool = False, children=None ): ListTag = html.Ol if ordered else html.Ul return ListTag[ [html.Li[item] for item in items], children, ] # Usage example if __name__ == "__main__": page = Container(max_width="lg")[ html.H1["Component Library Demo"], Alert("This is an info alert", level="info", dismissible=True), Grid(cols=3, gap=2)[ Card(title="Card 1", footer="Footer 1")[ html.P["Card content here"], Button("Click me", variant="primary"), ], Card(title="Card 2", footer="Footer 2")[ html.P["More content"], Button("Action", variant="secondary"), ], Card(title="Card 3")[ html.P["Another card"], Badge("New", variant="success"), ], ], List(["Item 1", "Item 2", "Item 3"], ordered=True), ] print(str(page)) ``` ### Form Component Library ```python # forms.py from compone import Component, html from typing import Optional, Literal @Component def FormGroup( label: str, name: str, help_text: Optional[str] = None, error: Optional[str] = None, required: bool = False, children=None ): return html.Div(class_="form-group")[ html.Label(for_=name, class_="form-label")[ label, html.Span(class_="required")["*"] if required else None, ], children, html.Small(class_="form-help")[help_text] if help_text else None, html.Div(class_="form-error")[error] if error else None, ] @Component def TextInput( name: str, type: Literal["text", "email", "password", "tel", "url"] = "text", value: str = "", placeholder: str = "", required: bool = False, disabled: bool = False, ): return html.Input( type=type, id=name, name=name, value=value, placeholder=placeholder, required=required, disabled=disabled, class_="form-input" ) @Component def TextArea( name: str, value: str = "", rows: int = 4, placeholder: str = "", required: bool = False, ): return html.Textarea( id=name, name=name, rows=rows, placeholder=placeholder, required=required, class_="form-textarea" )[value] @Component def Select( name: str, options: list[tuple[str, str]], # [(value, label), ...] selected: str = "", required: bool = False, ): return html.Select( id=name, name=name, required=required, class_="form-select" )[ [html.Option( value=value, selected=(value == selected) )[label] for value, label in options] ] @Component def Checkbox( name: str, label: str, checked: bool = False, value: str = "1", ): return html.Div(class_="form-checkbox")[ html.Input( type="checkbox", id=name, name=name, value=value, checked=checked ), html.Label(for_=name)[label], ] @Component def RadioGroup( name: str, options: list[tuple[str, str]], # [(value, label), ...] selected: str = "", ): return html.Div(class_="form-radio-group")[ [html.Div(class_="form-radio")[ html.Input( type="radio", id=f"{name}_{value}", name=name, value=value, checked=(value == selected) ), html.Label(for_=f"{name}_{value}")[label], ] for value, label in options] ] # Usage registration_form = html.Form(method="post", action="/register")[ FormGroup( label="Email", name="email", required=True, help_text="We'll never share your email" )[ TextInput(name="email", type="email", required=True) ], FormGroup( label="Password", name="password", required=True, help_text="At least 8 characters" )[ TextInput(name="password", type="password", required=True) ], FormGroup( label="Country", name="country", required=True )[ Select( name="country", options=[ ("us", "United States"), ("uk", "United Kingdom"), ("ca", "Canada"), ], required=True ) ], FormGroup( label="Bio", name="bio" )[ TextArea(name="bio", placeholder="Tell us about yourself") ], Checkbox(name="newsletter", label="Subscribe to newsletter"), html.Button(type="submit", class_="btn-primary")["Register"], ] ``` ## Type-Safe Development ### Type-Safe Component Props ```python from compone import Component, html from typing import Literal, TypedDict, Optional from datetime import datetime class User(TypedDict): id: int name: str email: str avatar_url: Optional[str] role: Literal["admin", "user", "guest"] class Post(TypedDict): id: int title: str content: str author: User created_at: datetime tags: list[str] @Component def UserAvatar( user: User, size: Literal["sm", "md", "lg"] = "md", ): sizes = {"sm": "32", "md": "48", "lg": "64"} return html.Div(class_=f"avatar avatar-{size}")[ html.Img( src=user["avatar_url"] or "/default-avatar.png", alt=user["name"], width=sizes[size], height=sizes[size] ) if user.get("avatar_url") else html.Div(class_="avatar-placeholder")[ user["name"][0].upper() ] ] @Component def UserBadge(user: User): badge_colors = { "admin": "red", "user": "blue", "guest": "gray", } return html.Span( class_=f"badge badge-{badge_colors[user['role']]}" )[user["role"].title()] @Component def PostCard(post: Post, show_author: bool = True): return html.Article(class_="post-card")[ html.Header[ html.H2[html.A(href=f"/posts/{post['id']}")[post["title"]]], html.Div(class_="post-meta")[ UserAvatar(post["author"], size="sm") if show_author else None, html.Span[post["author"]["name"]] if show_author else None, html.Time(datetime=post["created_at"].isoformat())[ post["created_at"].strftime("%B %d, %Y") ], ], ], html.Div(class_="post-content")[post["content"][:200] + "..."], html.Footer[ html.Div(class_="post-tags")[ [html.Span(class_="tag")[f"#{tag}"] for tag in post["tags"]] ], html.A(href=f"/posts/{post['id']}")["Read more →"], ], ] # Usage with type checking user: User = { "id": 1, "name": "John Doe", "email": "john@example.com", "avatar_url": "https://example.com/avatar.jpg", "role": "admin", # IDE will autocomplete and validate } post: Post = { "id": 1, "title": "My First Post", "content": "This is the content of my first post...", "author": user, "created_at": datetime.now(), "tags": ["python", "web", "compone"], } # Type errors caught by IDE: # PostCard(post, show_author="yes") # Error: bool expected, got str # user["role"] = "superuser" # Error: Literal type violation ``` ### Generic Components with TypeVars ```python from typing import TypeVar, Generic, Callable from compone import Component, html T = TypeVar('T') @Component def DataList( items: list[T], render_item: Callable[[T], any], empty_message: str = "No items to display", children=None ): if not items: return html.P(class_="empty-state")[empty_message] return html.Div(class_="data-list")[ [html.Div(class_="data-list-item")[render_item(item)] for item in items], children, ] # Usage with type inference users = [ {"name": "Alice", "email": "alice@example.com"}, {"name": "Bob", "email": "bob@example.com"}, ] user_list = DataList( items=users, render_item=lambda u: html.Div[ html.Strong[u["name"]], html.Span[u["email"]], ], empty_message="No users found" ) ``` ## Real-World Examples ### Complete Blog with Admin Panel ```python from flask import Flask, request, redirect, session from compone import Component, html from typing import Optional app = Flask(__name__) app.secret_key = "dev" # Mock database posts_db = [] users_db = {"admin": "password"} @Component def AdminLayout(title: str, children): return html.Html[ html.Head[ html.Meta(charset="utf-8"), html.Title[f"{title} - Admin"], html.Link(rel="stylesheet", href="/static/admin.css"), ], html.Body[ html.Header[ html.H1["Blog Admin"], html.Nav[ html.A(href="/admin")["Dashboard"], html.A(href="/admin/posts")["Posts"], html.A(href="/logout")["Logout"], ], ], html.Main[children], ], ] @Component def PostEditor( post_id: Optional[int] = None, title: str = "", content: str = "", ): action = f"/admin/posts/{post_id}/edit" if post_id else "/admin/posts/create" return html.Form(action=action, method="post")[ html.Div[ html.Label(for_="title")["Title"], html.Input(type="text", id="title", name="title", value=title, required=True), ], html.Div[ html.Label(for_="content")["Content"], html.Textarea(id="content", name="content", rows=10, required=True)[content], ], html.Button(type="submit")["Save Post"], ] @app.route("/admin/posts") def admin_posts(): if "user" not in session: return redirect("/login") return str(AdminLayout("Posts")[ html.H2["All Posts"], html.A(href="/admin/posts/new", class_="btn")["New Post"], html.Table[ html.Thead[ html.Tr[ html.Th["Title"], html.Th["Created"], html.Th["Actions"], ] ], html.Tbody[ [html.Tr[ html.Td[post["title"]], html.Td[post["created_at"]], html.Td[ html.A(href=f"/admin/posts/{i}/edit")["Edit"], html.Form( action=f"/admin/posts/{i}/delete", method="post", style="display:inline" )[ html.Button(type="submit")["Delete"] ], ], ] for i, post in enumerate(posts_db)] ] if posts_db else html.Tbody[ html.Tr[html.Td(colspan="3")["No posts yet"]] ] ], ]) @app.route("/admin/posts/new") def new_post(): if "user" not in session: return redirect("/login") return str(AdminLayout("New Post")[ html.H2["Create New Post"], PostEditor(), ]) @app.route("/admin/posts/create", methods=["POST"]) def create_post(): if "user" not in session: return redirect("/login") posts_db.append({ "title": request.form["title"], "content": request.form["content"], "created_at": datetime.now().strftime("%Y-%m-%d %H:%M"), }) return redirect("/admin/posts") ``` This comprehensive examples file covers all the requested use cases with practical, working code examples. -
frameworks.md 6.8 KB
# Web Framework Integration Compone components return strings that work with any Python web framework: ```python # Flask @app.route("/") def index(): return str(HomePage()["Welcome"]) # FastAPI @app.get("/") def index(): return HTMLResponse(HomePage()["Welcome"]) # Django def index(request): return HttpResponse(HomePage()["Welcome"]) ``` ## Flask Blog Application ```python from flask import Flask, request from compone import Component, html app = Flask(__name__) @Component def Layout(title: str, children): return html.Html[ html.Head[ html.Meta(charset="utf-8"), html.Title[title], html.Link(rel="stylesheet", href="/static/style.css"), ], html.Body[ html.Header[ html.H1["My Blog"], html.Nav[ html.A(href="/")["Home"], html.A(href="/about")["About"], ], ], html.Main[children], html.Footer[ html.P["© 2024 My Blog"], ], ], ] @Component def PostCard(title: str, excerpt: str, date: str, url: str): return html.Article(class_="post-card")[ html.H2[html.A(href=url)[title]], html.Time(datetime=date)[date], html.P[excerpt], html.A(href=url, class_="read-more")["Read more →"], ] @Component def PostList(posts: list, children=None): return html.Div(class_="post-list")[ [PostCard( title=post["title"], excerpt=post["excerpt"], date=post["date"], url=f"/post/{post['id']}" ) for post in posts], children, ] @app.route("/") def index(): posts = [ {"id": 1, "title": "First Post", "excerpt": "This is my first post...", "date": "2024-01-01"}, {"id": 2, "title": "Second Post", "excerpt": "Another great post...", "date": "2024-01-05"}, ] return str(Layout("Home")[PostList(posts)]) @app.route("/post/<int:post_id>") def post_detail(post_id): post = { "title": f"Post {post_id}", "content": "Full post content here...", "date": "2024-01-01", } return str(Layout(post["title"])[ html.Article[ html.H1[post["title"]], html.Time(datetime=post["date"])[post["date"]], html.Div(class_="content")[post["content"]], ] ]) if __name__ == "__main__": app.run(debug=True) ``` ## FastAPI App with HTML Views ```python from fastapi import FastAPI, Form, Request from fastapi.responses import HTMLResponse, RedirectResponse from compone import Component, html from typing import Optional app = FastAPI() @Component def Page(title: str, children): return html.Html[ html.Head[ html.Meta(charset="utf-8"), html.Meta(name="viewport", content="width=device-width, initial-scale=1"), html.Title[title], html.Link(rel="stylesheet", href="https://cdn.simplecss.org/simple.min.css"), ], html.Body[children], ] @Component def Form(action: str, method: str = "post", children=None): return html.Form(action=action, method=method)[children] @Component def TextField( name: str, label: str, type: str = "text", required: bool = False, value: str = "", ): return html.Div[ html.Label(for_=name)[label], html.Input( type=type, id=name, name=name, value=value, required=required, ), ] @Component def Button(text: str, type: str = "submit", children=None): return html.Button(type=type)[text, children] # In-memory database tasks = [] @app.get("/", response_class=HTMLResponse) async def task_list(): return str(Page("Todo List")[ html.H1["My Tasks"], html.Ul[ [html.Li[ task["title"], html.Form(action=f"/tasks/{i}/delete", method="post", style="display:inline")[ Button("Delete", type="submit") ] ] for i, task in enumerate(tasks)] ] if tasks else html.P["No tasks yet."], html.H2["Add Task"], Form(action="/tasks/create")[ TextField("title", "Task", required=True), Button("Add Task"), ], ]) @app.post("/tasks/create", response_class=RedirectResponse) async def create_task(title: str = Form(...)): tasks.append({"title": title, "completed": False}) return RedirectResponse(url="/", status_code=303) @app.post("/tasks/{task_id}/delete", response_class=RedirectResponse) async def delete_task(task_id: int): if 0 <= task_id < len(tasks): tasks.pop(task_id) return RedirectResponse(url="/", status_code=303) ``` ## Django View with Components ```python # components.py from compone import Component, html from typing import Optional @Component def BaseLayout(title: str, children): return html.Html[ html.Head[ html.Meta(charset="utf-8"), html.Title[f"{title} - My Django Site"], html.Link(rel="stylesheet", href="/static/css/style.css"), ], html.Body[ html.Header[ html.Nav[ html.A(href="/")["Home"], html.A(href="/products/")["Products"], html.A(href="/contact/")["Contact"], ], ], html.Main[children], ], ] @Component def ProductCard(name: str, price: float, image_url: str, product_id: int): return html.Div(class_="product-card")[ html.Img(src=image_url, alt=name), html.H3[name], html.P(class_="price")[f"${price:.2f}"], html.A(href=f"/products/{product_id}/", class_="btn")["View Details"], ] # views.py from django.http import HttpResponse from .components import BaseLayout, ProductCard from .models import Product def product_list(request): products = Product.objects.all() content = BaseLayout("Products")[ html.H1["Our Products"], html.Div(class_="product-grid")[ [ProductCard( name=p.name, price=p.price, image_url=p.image.url, product_id=p.id ) for p in products] ], ] return HttpResponse(str(content)) def product_detail(request, product_id): product = Product.objects.get(id=product_id) content = BaseLayout(product.name)[ html.Article[ html.H1[product.name], html.Img(src=product.image.url, alt=product.name), html.P(class_="price")[f"${product.price:.2f}"], html.Div(class_="description")[product.description], html.Form(action=f"/cart/add/{product.id}/", method="post")[ html.Button(type="submit")["Add to Cart"], ], ], ] return HttpResponse(str(content)) ``` -
html.md 5.6 KB
# HTML Module The `html` module provides all standard HTML5 elements. ## Structure Elements ```python html.Html[...] # <html> html.Head[...] # <head> html.Body[...] # <body> html.Div[...] # <div> html.Span[...] # <span> html.Main[...] # <main> html.Header[...] # <header> html.Footer[...] # <footer> html.Nav[...] # <nav> html.Section[...] # <section> html.Article[...] # <article> html.Aside[...] # <aside> ``` ## Text Elements ```python html.H1[...] # <h1> html.H2[...] # <h2> html.H3[...] # <h3> html.H4[...] # <h4> html.H5[...] # <h5> html.H6[...] # <h6> html.P[...] # <p> html.A[...] # <a> html.Strong[...] # <strong> html.Em[...] # <em> html.Code[...] # <code> html.Pre[...] # <pre> html.Blockquote[...] # <blockquote> html.Small[...] # <small> html.Mark[...] # <mark> html.Del[...] # <del> html.Ins[...] # <ins> html.Sub[...] # <sub> html.Sup[...] # <sup> ``` ## List Elements ```python html.Ul[...] # <ul> html.Ol[...] # <ol> html.Li[...] # <li> html.Dl[...] # <dl> html.Dt[...] # <dt> html.Dd[...] # <dd> ``` ## Table Elements ```python html.Table[...] # <table> html.Thead[...] # <thead> html.Tbody[...] # <tbody> html.Tfoot[...] # <tfoot> html.Tr[...] # <tr> html.Th[...] # <th> html.Td[...] # <td> html.Caption[...] # <caption> html.Colgroup[...] # <colgroup> html.Col[...] # <col> ``` ## Form Elements ```python html.Form[...] # <form> html.Input(...) # <input> (self-closing) html.Textarea[...] # <textarea> html.Button[...] # <button> html.Select[...] # <select> html.Option[...] # <option> html.Label[...] # <label> html.Fieldset[...] # <fieldset> html.Legend[...] # <legend> html.Datalist[...] # <datalist> html.Output[...] # <output> html.Progress(...) # <progress> html.Meter(...) # <meter> ``` ## Media Elements ```python html.Img(...) # <img> (self-closing) html.Video[...] # <video> html.Audio[...] # <audio> html.Source(...) # <source> (self-closing) html.Track(...) # <track> (self-closing) html.Picture[...] # <picture> html.Svg[...] # <svg> html.Canvas[...] # <canvas> ``` ## Interactive Elements ```python html.Details[...] # <details> html.Summary[...] # <summary> html.Dialog[...] # <dialog> ``` ## Metadata Elements ```python html.Title[...] # <title> html.Meta(...) # <meta> (self-closing) html.Link(...) # <link> (self-closing) html.Style[...] # <style> html.Script[...] # <script> html.Noscript[...] # <noscript> html.Base(...) # <base> (self-closing) ``` ## Other Elements ```python html.Br(...) # <br> (self-closing) html.Hr(...) # <hr> (self-closing) html.Iframe[...] # <iframe> html.Embed(...) # <embed> (self-closing) html.Object[...] # <object> html.Param(...) # <param> (self-closing) html.Time[...] # <time> html.Data[...] # <data> html.Abbr[...] # <abbr> html.Address[...] # <address> html.Cite[...] # <cite> html.Kbd[...] # <kbd> html.Samp[...] # <samp> html.Var[...] # <var> html.Wbr(...) # <wbr> (self-closing) ``` ## Component Composition Build complex components from simpler ones: ```python @Component def Button(text: str, variant: str = "primary", children=None): return html.Button(class_=f"btn btn-{variant}")[text, children] @Component def IconButton(icon: str, text: str, children=None): return Button(text)[ html.I(class_=f"icon-{icon}"), children, ] @Component def Modal(title: str, show_close: bool = True, children=None): return html.Div(class_="modal")[ html.Div(class_="modal-header")[ html.H2[title], IconButton("close", "Close") if show_close else None, ], html.Div(class_="modal-body")[children], ] ``` ## Modifying Element Attributes Elements have `.append()` and `.replace()` methods to derive new elements with modified attributes. Access current attributes with `.props`. ### `.append()` — Add to existing attributes Appends values to existing attributes (space-separated for `class`): ```python el = html.Input(type="text", class_="base") el2 = el.append(class_="extra") str(el2) # <input type="text" class="base extra" /> ``` ### `.replace()` — Override attributes Replaces attribute values entirely: ```python el = html.Div(class_="old", id="box") el2 = el.replace(class_="new") str(el2) # <div class="new" id="box"></div> ``` ### `.props` — Read current attributes ```python el = html.Div(class_="card", id="main") el.props # {'class_': ['card'], 'id': 'main'} ``` Note: `class_` values are stored as lists (since multiple classes are common). These methods return **new elements** — the original is not mutated. ## Escaping and Raw HTML Compone automatically escapes strings for security. For raw HTML: ```python from compone import safe @Component def RawContent(html_string: str, children=None): # WARNING: Only use with trusted content return html.Div[safe(html_string), children] ``` -
other-formats.md 6.1 KB
# Non-HTML Formats Compone supports XML, RSS, and custom markup: ```python from compone import xml @Component def RSSItem(title: str, link: str, description: str): return xml.Item[ xml.Title[title], xml.Link[link], xml.Description[description], ] # Generate RSS feed feed = xml.Rss(version="2.0")[ xml.Channel[ xml.Title["My Blog"], RSSItem("Post 1", "https://...", "Description"), RSSItem("Post 2", "https://...", "Description"), ] ] ``` ## XML Module The `xml` module provides XML element creation. ### Generic XML Elements ```python from compone import xml # Create any XML element by attribute access xml.Root[...] xml.Item[...] xml.CustomTag[...] ``` ### Common XML Formats #### RSS Feed Example ```python xml.Rss(version="2.0")[ xml.Channel[ xml.Title["Feed Title"], xml.Link["https://example.com"], xml.Description["Feed description"], xml.Item[ xml.Title["Item title"], xml.Link["https://example.com/item"], xml.Description["Item description"], xml.PubDate["Mon, 01 Jan 2024 00:00:00 GMT"], ], ] ] ``` #### Atom Feed Example ```python xml.Feed(xmlns="http://www.w3.org/2005/Atom")[ xml.Title["Feed Title"], xml.Link(href="https://example.com"), xml.Updated["2024-01-01T00:00:00Z"], xml.Entry[ xml.Title["Entry title"], xml.Link(href="https://example.com/entry"), xml.Updated["2024-01-01T00:00:00Z"], xml.Summary["Entry summary"], ], ] ``` #### Sitemap Example ```python xml.Urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")[ xml.Url[ xml.Loc["https://example.com/page1"], xml.Lastmod["2024-01-01"], xml.Changefreq["weekly"], xml.Priority["0.8"], ], ] ``` ## RSS Feed Generator ```python from compone import Component, xml from datetime import datetime from typing import TypedDict class RSSItem(TypedDict): title: str link: str description: str pub_date: datetime author: str guid: str @Component def RSS_Item(item: RSSItem): return xml.Item[ xml.Title[item["title"]], xml.Link[item["link"]], xml.Description[item["description"]], xml.PubDate[item["pub_date"].strftime("%a, %d %b %Y %H:%M:%S %z")], xml.Author[item["author"]], xml.Guid(isPermaLink="true")[item["guid"]], ] @Component def RSS_Feed( title: str, link: str, description: str, items: list[RSSItem], ): return xml.Rss(version="2.0", xmlns_atom="http://www.w3.org/2005/Atom")[ xml.Channel[ xml.Title[title], xml.Link[link], xml.Description[description], xml.Language["en-us"], xml.LastBuildDate[datetime.now().strftime("%a, %d %b %Y %H:%M:%S %z")], xml.Atom_link( href=f"{link}/rss.xml", rel="self", type="application/rss+xml" ), [RSS_Item(item) for item in items], ] ] # Usage blog_items = [ { "title": "First Blog Post", "link": "https://example.com/posts/1", "description": "This is my first blog post about Python", "pub_date": datetime(2024, 1, 1, 12, 0, 0), "author": "john@example.com (John Doe)", "guid": "https://example.com/posts/1", }, { "title": "Second Blog Post", "link": "https://example.com/posts/2", "description": "Another great post about web development", "pub_date": datetime(2024, 1, 5, 14, 30, 0), "author": "john@example.com (John Doe)", "guid": "https://example.com/posts/2", }, ] feed = RSS_Feed( title="My Blog", link="https://example.com", description="A blog about Python and web development", items=blog_items ) print('<?xml version="1.0" encoding="UTF-8"?>') print(str(feed)) ``` ## Sitemap Generator ```python from compone import Component, xml from datetime import datetime from typing import Literal ChangeFreq = Literal["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"] @Component def URL( loc: str, lastmod: datetime, changefreq: ChangeFreq = "weekly", priority: float = 0.5, ): return xml.Url[ xml.Loc[loc], xml.Lastmod[lastmod.strftime("%Y-%m-%d")], xml.Changefreq[changefreq], xml.Priority[f"{priority:.1f}"], ] @Component def Sitemap(urls: list[dict]): return xml.Urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")[ [URL( loc=url["loc"], lastmod=url["lastmod"], changefreq=url.get("changefreq", "weekly"), priority=url.get("priority", 0.5), ) for url in urls] ] # Usage pages = [ { "loc": "https://example.com/", "lastmod": datetime(2024, 1, 1), "changefreq": "daily", "priority": 1.0, }, { "loc": "https://example.com/about", "lastmod": datetime(2024, 1, 1), "changefreq": "monthly", "priority": 0.8, }, { "loc": "https://example.com/blog", "lastmod": datetime(2024, 1, 10), "changefreq": "weekly", "priority": 0.9, }, ] sitemap = Sitemap(pages) print('<?xml version="1.0" encoding="UTF-8"?>') print(str(sitemap)) ``` ## SVG Generation ```python from compone import Component from compone import xml # SVG uses xml module @Component def Circle(cx: int, cy: int, r: int, fill: str = "black"): return xml.Circle(cx=str(cx), cy=str(cy), r=str(r), fill=fill) @Component def Rect(x: int, y: int, width: int, height: int, fill: str = "black"): return xml.Rect( x=str(x), y=str(y), width=str(width), height=str(height), fill=fill ) @Component def SVG(width: int, height: int, children): return xml.Svg( width=str(width), height=str(height), xmlns="http://www.w3.org/2000/svg" )[children] # Create a simple graphic graphic = SVG(200, 200)[ Rect(0, 0, 200, 200, fill="#f0f0f0"), Circle(100, 100, 50, fill="#ff6b6b"), Circle(70, 80, 10, fill="white"), Circle(130, 80, 10, fill="white"), ] print(str(graphic)) ``` -
SKILL.md 1.8 KB
--- name: compone description: Builds Python components using the compone framework for type-safe HTML/XML/RSS generation. Use when working with compone, creating Python components, generating markup in Python, or building framework-agnostic component libraries. allowed-tools: Read, Write, Edit, Grep, Glob, Bash, WebFetch --- # Compone - Python Component Framework Helps developers create type-safe, reusable components using compone, a modern Python framework for generating markup (HTML, XML, RSS) with React-like patterns. ALWAYS read `core-concepts.md` for basic usage. - For integration with web frameworks, read [`frameworks.md`](frameworks.md). - For HTML generation and patterns, read [`html.md`](html.md) - For other formats like XML, RSS, SVG and others, read [`other-formats.md`](other-formats.md) - For more examples when writing complex components, read [`other-formats.md`](examples.md) - When writing tests for Components, read [`testing.md`](testing.md) ## When to Use Compone - Building framework-agnostic component libraries - Type-safe HTML generation in Python - Colocating markup with Python logic - Generating XML, RSS, or other markup formats - Creating reusable UI patterns across projects - Teams preferring Python over template languages ## Best Practices 1. **Type all props**: Use type hints for better IDE support and static type checking 2. **Single responsibility**: Keep components focused on one concern 3. **Composition over complexity**: Build complex UIs from simple components 4. **Descriptive names**: Use clear component and prop names 5. **Default values**: Provide sensible defaults for optional props 6. **Framework agnostic**: Don't tie components to specific web frameworks ## Official Documentation - Website: https://compone.kissgyorgy.me/ - GitHub: https://github.com/kissgyorgy/compone - PyPI: https://pypi.org/project/compone/ -
testing.md 817 B
# Testing compone Compoents Always assert on the full output when testing a Component. ```python import pytest from compone import Component, html @Component def Greeting(name: str, formal: bool = False): greeting = f"Hello, {name}" if not formal else f"Greetings, {name}" return html.Div[html.H1[greeting]] def test_greeting_informal(): result = str(Greeting("Alice")) assert result == "<div><h1>Hello, Alice</h1></div>" def test_greeting_formal(): result = str(Greeting("Bob", formal=True)) assert result == "<div><h1>Greetings, Bob</h1></div>" ``` ### Attribute Validation Testing ```python def test_progress_bar_validation(): with pytest.raises(ValueError): ProgressBar(value=150, max_value=100) with pytest.raises(ValueError): ProgressBar(value=-10) ```
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.