I've been working with FastHTML and I have to say, I rather enjoy HTML-markup-as-objects.

It works pretty seamlessly and you get something like this:

    @app.post('/part/add')
    def create_part(part_number: str, name: str, description: str):
        p = Part(part_number=part_number, name=name, description=description)
        p.save()
        return (p,
            Input(placeholder="part number", id="part_number", hx_swap_oob='true'),
            Input(placeholder="description", id="description", hx_swap_oob='true'),
            Input(placeholder="name", id="name", hx_swap_oob='true'))
Note, I'm not using FastHTML's built-in database interface. I prefer a little less implicit than the strict FastHTML style.