- Add PLAN.md with 40-step checklist across 10 phases - Add CLAUDE.md with project-specific instructions - Set up nix flake with FastHTML/MonsterUI dependencies - Create Python package skeleton (src/animaltrack) - Vendor FastHTML and MonsterUI documentation - Add Docker build configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7.4 KiB
"""FrankenUI Forms Example built with MonsterUI (original design by ShadCN)"""
from fasthtml.common import * from monsterui.all import * from fasthtml.svg import *
app, rt = fast_app(hdrs=Theme.blue.headers())
def HelpText(c): return P(c,cls=TextPresets.muted_sm)
def heading(): return Div(cls="space-y-5")( H2("Settings"), Subtitle("Manage your account settings and set e-mail preferences."), DividerSplit())
sidebar = NavContainer( *map(lambda x: Li(A(x)), ("Profile", "Account", "Appearance", "Notifications", "Display")), uk_switcher="connect: #component-nav; animation: uk-animation-fade", cls=(NavT.secondary,"space-y-4 p-4 w-1/5"))
def FormSectionDiv(*c, cls='space-y-2', **kwargs): return Div(*c, cls=cls, **kwargs)
def FormLayout(title, subtitle, *content, cls='space-y-3 mt-4'): return Container(Div(H3(title), Subtitle(subtitle), DividerLine(), Form(*content, cls=cls)))
def profile_form():
content = (FormSectionDiv(
LabelInput("Username", placeholder='sveltecult', id='username'),
HelpText("This is your public display name. It can be your real name or a pseudonym. You can only change this once every 30 days.")),
FormSectionDiv(
LabelSelect(
Option("Select a verified email to display", value="", selected=True, disabled=True),
*[Option(o, value=o) for o in ('m@example.com', 'm@yahoo.com', 'm@cloud.com')],
label="Email", id="email"),
HelpText("You can manage verified email addresses in your email settings.")),
FormSectionDiv(
LabelTextArea("Bio", id="bio", placeholder="Tell us a little bit about yourself"),
HelpText("You can @mention other users and organizations to link to them."),
P("String must contain at least 4 character(s)", cls="text-destructive")),
FormSectionDiv(
FormLabel("URLs"),
HelpText("Add links to your website, blog, or social media profiles."),
Input(value="https://www.franken-ui.dev"),
Input(value="https://github.com/sveltecult/franken-ui"),
Button("Add URL")),
Button('Update profile', cls=ButtonT.primary))
return FormLayout('Profile', 'This is how others will see you on the site.', *content)
def account_form(): content = ( FormSectionDiv( LabelInput("Name", placeholder="Your name", id="name"), HelpText("This is the name that will be displayed on your profile and in emails.")), FormSectionDiv( LabelInput("Date of Birth", type="date", placeholder="Pick a date", id="date_of_birth"), HelpText("Your date of birth is used to calculate your age.")), FormSectionDiv( LabelSelect(*Options("Select a language", "English", "French", "German", "Spanish", "Portuguese", selected_idx=1, disabled_idxs={0}), label='Language', id="language"), HelpText("This is the language that will be used in the dashboard.")), Button('Update profile', cls=ButtonT.primary))
return FormLayout('Account', 'Update your account settings. Set your preferred language and timezone.', *content)
def appearance_form(): def theme_item(bg_color, content_bg, text_bg): common_content = f"space-y-2 rounded-md {content_bg} p-2 shadow-sm" item_row = lambda: Div(cls=f"flex items-center space-x-2 {common_content}")( Div(cls=f"h-4 w-4 rounded-full {text_bg}"), Div(cls=f"h-2 w-[100px] rounded-lg {text_bg}"))
return Div(cls=f"space-y-2 rounded-sm {bg_color} p-2")(
Div(cls=common_content)(
Div(cls=f"h-2 w-[80px] rounded-lg {text_bg}"),
Div(cls=f"h-2 w-[100px] rounded-lg {text_bg}")),
item_row(),
item_row())
common_toggle_cls = "block cursor-pointer items-center rounded-md border-2 border-muted p-1 ring-ring"
content = (
FormSectionDiv(
LabelSelect(*Options('Select a font family', 'Inter', 'Geist', 'Open Sans', selected_idx=2, disabled_idxs={0}),
label='Font Family', id='font_family'),
HelpText("Set the font you want to use in the dashboard.")),
FormSectionDiv(
FormLabel("Theme"),
HelpText("Select the theme for the dashboard."),
Grid(
A(id="theme-toggle-light", cls=common_toggle_cls)(theme_item("bg-[#ecedef]", "bg-white", "bg-[#ecedef]")),
A(id="theme-toggle-dark", cls=f"{common_toggle_cls} bg-popover")(theme_item("bg-slate-950", "bg-slate-800", "bg-slate-400")),
cols_max=2,cls=('max-w-md','gap-8'))),
Button('Update preferences', cls=ButtonT.primary))
return FormLayout('Appearance', 'Customize the appearance of the app. Automatically switch between day and night themes.', *content)
notification_items = [ {"title": "Communication emails", "description": "Receive emails about your account activity.", "checked": False, "disabled": False}, {"title": "Marketing emails", "description": "Receive emails about new products, features, and more.", "checked": False, "disabled": False}, {"title": "Social emails", "description": "Receive emails for friend requests, follows, and more.", "checked": True, "disabled": False}, {"title": "Security emails", "description": "Receive emails about your account activity and security.", "checked": True, "disabled": True}]
def notifications_form(): def RadioLabel(label): return DivLAligned(Radio(name="notification", checked=(label=="Nothing")), FormLabel(label))
def NotificationCard(item):
return Card(
Div(cls="space-y-0.5")(
FormLabel(Strong(item['title'], cls=TextT.sm),
HelpText(item['description']))))
content = Div(
FormSectionDiv(
FormLabel("Notify me about"),
*map(RadioLabel, ["All new messages", "Direct messages and mentions", "Nothing"])),
Div(
H4("Email Notifications", cls="mb-4"),
Grid(*map(NotificationCard, notification_items), cols=1)),
LabelCheckboxX("Use different settings for my mobile devices", id="notification_mobile"),
HelpText("You can manage your mobile notifications in the mobile settings page."),
Button('Update notifications', cls=ButtonT.primary))
return FormLayout('Notifications', 'Configure how you receive notifications.', *content)
def display_form(): content = ( Div(cls="space-y-2")( Div(cls="mb-4")( H5("Sidebar"), Subtitle("Select the items you want to display in the sidebar.")), *[Div(CheckboxX(id=f"display_{i}", checked=i in [0, 1, 2]),FormLabel(label)) for i, label in enumerate(["Recents", "Home", "Applications", "Desktop", "Downloads", "Documents"])]), Button('Update display', cls=ButtonT.primary)) return FormLayout('Display', 'Turn items on or off to control what's displayed in the app.', *content)
@rt def index(): return Title("Forms Example"),Container( heading(), Div(cls="flex gap-x-12")( sidebar, Ul(id="component-nav", cls="uk-switcher max-w-2xl")( Li(cls="uk-active")(profile_form(), *map(Li, [account_form(), appearance_form(), notifications_form(), display_form()])))))
serve()