SemanticUI FastHTML Cards
Caution: I’ve learned better patterns since I wrote this. Leaving this here for posterity.
```python
from fasthtml.common import *
```
I pasted the code for [Semantic UI's group of button cards](https://fomantic-ui.com/views/card.html#buttons) into [HTML to XT](https://h2x-production-16c0.up.railway.app/) and got:
```python
Div(
Div(
Div(
Div('Elliot Fu', cls='header'),
Div('Elliot Fu is a film-maker from New York.', cls='description'),
cls='content'
),
Div(
I(cls='add icon'),
'Add Friend',
cls='ui button'
),
cls='card'
),
Div(
Div(
Div('Veronika Ossi', cls='header'),
Div('Veronika Ossi is a set designer living in New York who enjoys kittens, music, and partying.', cls='description'),
cls='content'
),
Div(
I(cls='add icon'),
'Add Friend',
cls='ui button'
),
cls='card'
),
Div(
Div(
Div('Jenny Hess', cls='header'),
Div('Jenny is a student studying Media Management at the New School', cls='description'),
cls='content'
),
Div(
I(cls='add icon'),
'Add Friend',
cls='ui button'
),
cls='card'
),
cls='ui cards'
)
```
Extracting the data into a Python list of tuples:
```python
cards_data = [
('Elliot Fu', 'Elliot Fu is a film-maker from New York.'),
('Veronika Ossi', 'Veronika Ossi is a set designer living in New York who enjoys kittens, music, and partying.'),
('Jenny Hess', 'Jenny is a student studying Media Management at the New School')
]
```
We could hardcode the Semantic UI tree structure in like this...
```python
def Card(name, description):
return Div(
Div(
Div(name, cls='header'),
Div(description, cls='description'),
cls='content'
),
Div(
I(cls='add icon'),
'Add Friend',
cls='ui button'
),
cls='card'
)
```
```python
cards = [Card(name, description) for name, description in cards_data]
cards
```
```python
result = Div(*cards, cls='ui cards')
result
```
But it might be nice to separate card text/values from how it is rendered as an XT, and offer different rendering options
```python
@dataclass
class Card():
title: str
image: str
description: str
# button_links: list of text, link pairs maybe
def __xt__(self, uiframework='semanticui'):
if uiframework == 'semanticui':
...
elif uiframework == 'frankenui':
...
elif uiframework == 'bootstrap':
...
else:
raise ValueError(f"Unknown uiframework {uiframework}")
```
This would be hard to maintain, though. I feel like a system to allow devs to make their own CSS framework plugins for XT would be nice.
Daniel suggested using dataclasses like in his blog.
Claude 3.5 Sonnet suggested renderers for each framework could look like:
```python
from typing import List, Tuple, Optional, Callable, Dict
# Global registry for renderers
renderer_registry: Dict[str, Callable] = {}
def register_renderer(framework: str):
"""Decorator to register a renderer for a specific framework."""
def decorator(func: Callable):
renderer_registry[framework] = func
return func
return decorator
```
```python
from dataclasses import dataclass
@dataclass
class Card:
title: str
description: str
image: Optional[str] = None
button_links: List[Tuple[str, str]] = ()
def __xt__(self, uiframework='semanticui'):
if uiframework not in renderer_registry:
raise ValueError(f"No renderer registered for framework: {uiframework}")
return renderer_registry[uiframework](self)
```
```python
@register_renderer('semanticui')
def render_semanticui(card: Card):
content = [
Div(card.title, cls='header'),
Div(card.description, cls='description')
]
if card.image:
content.insert(0, Div(Img(src=card.image, cls='ui image'), cls='image'))
buttons = [
A(text, href=link, cls='ui button')
for text, link in card.button_links
]
return Div(
Div(*content, cls='content'),
*buttons,
cls='ui card'
)
```
Let's try rendering out a simple card with this
```python
hannah_card = Card('Hannah', 'Hannah is a girl who likes to dance.', image='hannah.jpg', button_links=[('Add Friend', '#')])
hannah_card.__xt__()
```
Note: I've learned better patterns since I wrote this. Leaving this here for posterity.