Every control from pyweft-ui, live. Change the theme at the top; the code under each section is what produced it.
Buttons
Clicked 0 times.
<ui.Button variant="primary" @click="save">Primary</ui.Button> <ui.Button variant="danger" icon="trash">Delete</ui.Button> <ui.Button :loading="busy" @click="work">Load</ui.Button> <ui.Button disabled="true">Disabled</ui.Button>
Form fields
Each one is bound with @bind
name='' email='' quantity=1 size=None date='' notes='' phone='' card=''
<ui.TextField label="Name" required="true" @bind="name" hint="Your full name" /> <ui.NumberField label="Quantity" min="1" max="99" @bind="quantity" /> <ui.Select label="Size" options="Small, Medium, Large" @bind="size" /> <ui.TextArea label="Notes" rows="3" @bind="notes" /> <ui.TextField label="Phone" mask="(999) 999-9999" @bind="phone" />
Validation
Form and rules: try submitting it empty
form = Form(
email=[rules.required(), rules.email()],
password=[rules.required(), rules.min_length(8)],
confirm=rules.same_as("password", "The passwords don't match"),
agreed=rules.accepted("Agree to the terms to continue"),
)
def submit(self):
if self.form.check(): # every message shows; the first bad field gets focus
...
<ui.TextField ref="email" label="Email" @bind="email"
:error="form.error('email')" @blur="form.touch('email')" />Stepper
A checkout in steps; Next checks each one
<ui.Stepper :steps="steps" @bind="step" linear="true"
@next="check(event)" @finish="place_order" finish_label="Place order">
<If cond="step == 'account'"> ... </If>
<ElseIf cond="step == 'delivery'"> ... </ElseIf>
</ui.Stepper>
steps = [("account", "Account"), ("delivery", "Delivery"),
{"value": "gift", "label": "Gift message", "optional": True},
("review", "Review")]
def check(self, step): # False keeps the stepper on this step
form = {"account": self.account, "delivery": self.delivery}.get(step)
return form.check() if form is not None else TrueChoices
plan = 'pro', updates = True, agreed = False
<ui.RadioGroup label="Plan" :options="[('free', 'Free'), ('pro', 'Pro')]" @bind="plan" />
<ui.Checkbox label="Send me updates" @bind="updates" />
<ui.Switch label="Dark mode" @bind="dark" />Autocomplete
Suggestions as you type
country=None assignee=None city=None
<ui.Autocomplete label="Country" :options="countries" @bind="country" />
<ui.Autocomplete label="Assign to" :options="people" @bind="assignee">
<Fill name="option">
<span class="avatar">{{ option['data']['initials'] }}</span>
{{ option['label'] }} <small>{{ option['data']['email'] }}</small>
</Fill>
</ui.Autocomplete>
<ui.Autocomplete label="City" :options="cities" match="none" :loading="searching"
@search="find_cities(event)" @bind="city" />Cascade select
Nested options one level at a time; cities load as a country opens
city=None category=None
<ui.CascadeSelect label="City" :options="places" @bind="city"
show_path="true" clearable="true" @load="load_cities(event)" />
places = [
{"value": "au", "label": "Australia", "children": [
{"value": "nsw", "label": "New South Wales", "children": [
{"value": "syd", "label": "Sydney"}, ...]},
]},
{"value": "ca", "label": "Canada", "children": None}, # loads when opened
]Chips
Choose from a row, or type to add
size='Medium' interests=['travel'] tags=['python', 'ui'] invitees=[]
<ui.Chips options="Small, Medium, Large" @bind="size" />
<ui.Chips :options="interests" multiple="true" @bind="chosen" />
<ui.Chips :options="filters" removable="true" selectable="false"
@remove="drop_filter(event)" />
<ui.ChipInput label="Tags" @bind="tags" />
<ui.ChipInput label="Invite" :options="people" free="false" @bind="invitees">
<Fill name="chip">{{ chip['data']['initials'] }} {{ chip['label'] }}</Fill>
</ui.ChipInput>Button toggle
A joined row of buttons: one choice, or several
The weft is the thread woven through the warp to make fabric.
layout='list' range='Week' styles=['bold'] align='left'
<ui.ButtonToggle options="Day, Week, Month" @bind="view" />
<ui.ButtonToggle aria_label="Text style" multiple="true" @bind="styles" :options="[
{'value': 'bold', 'label': 'Bold', 'icon': 'bold', 'icon_only': True},
{'value': 'italic', 'label': 'Italic', 'icon': 'italic', 'icon_only': True},
]" />
<ui.ButtonToggle label="Plan" options="Monthly, Yearly" full_width="true" @bind="plan" />Sliders
Drag, or use the arrow keys
volume=40 price=(100, 350) rating=4 opacity=0.8
<ui.Slider label="Volume" @bind="volume" format="{}%" />
<ui.Slider label="Price" :min="0" :max="500" :step="10" range="true"
@bind="price" format="${}" value_label="side" />
<ui.Slider label="Rating" :min="1" :max="5" ticks="true"
value_label="always" marks="1, 3, 5" @bind="rating" />
<ui.Slider label="Opacity" :max="1" :step="0.05" lazy="true" @bind="opacity" />Expansion panels
Open one, or a stack where one is open at a time
uv add pyweft pyweft-ui (or pip install), then pyweft new myapp.
Yes: pyweft run serves it, pyweft build makes a static site.
pyweft run --desktop opens a native window; pyweft build --desktop packages it.
delivery_open=False street='' speed='Standard' faq='install'
<ui.Expansion title="Delivery" description="Address and speed" @bind="delivery_open">
... fields ...
<Fill name="actions"><ui.Button @click="save">Save</ui.Button></Fill>
</ui.Expansion>
<ui.Accordion :options="faq" @bind="question">
<p>{{ answers[panel] }}</p>
</ui.Accordion>Calendars
Choosing a date, and showing events
| Mo | Tu | We | Th | Fr | Sa | Su |
|---|---|---|---|---|---|---|
A Calendar on the page, always open. Its month and year are buttons too.
picked_day=None
Click an event, or an empty day to add one.
<ui.Calendar @bind="day" />
<ui.EventCalendar :events="events" @select="open(event)" @day="add(event)" />
<ui.EventCalendar :events="events" months="3" /> <!-- three months, one under another -->
events = [
{"title": "Standup", "start": datetime.datetime(2026, 11, 3, 9, 30),
"end": datetime.datetime(2026, 11, 3, 9, 45), "color": "blue"},
{"title": "Release", "start": datetime.date(2026, 11, 7), "color": "red"}, # all day
{"title": "Trip", "start": datetime.date(2026, 11, 11), "end": datetime.date(2026, 11, 13)},
]Feedback
<ui.Alert kind="success">Saved.</ui.Alert> <ui.Alert kind="danger" title="Failed" dismissible="true" @close="hide()">...</ui.Alert> <ui.Badge kind="success">active</ui.Badge> <ui.Spinner /> <ui.Icon name="star" />
Progress
How far along: a bar or a ring, or just still working
<ui.Progress label="Uploading" :value="sent" :max="24"
format="{} of 24 files" show_value="true" />
<ui.Progress label="Waiting for the server" /> <!-- no value: still working -->
<ui.Progress :value="35" :buffer="60" />
<ui.Progress shape="circle" :value="72" kind="warning" size="lg" />Dialog
<ui.Button @click="confirming = True">Delete project</ui.Button>
<ui.Dialog title="Delete project?" @bind="confirming">
<p>This can't be undone.</p>
<Fill name="footer">
<ui.Button @click="confirming = False">Cancel</ui.Button>
<ui.Button variant="danger" @click="delete()">Delete</ui.Button>
</Fill>
</ui.Dialog>Bottom sheet
Slides up from the bottom: swipe it down, press Esc or tap outside
<ui.BottomSheet title="Share" @bind="sharing" :items="[
{'value': 'link', 'label': 'Copy link', 'icon': 'copy'},
{'value': 'email', 'label': 'Email', 'description': 'Opens your mail app'},
]" @select="share(event)" />
<ui.BottomSheet title="Filters" @bind="filtering">
... any content ...
<Fill name="actions">
<ui.Button variant="primary" @click="filtering = False">Show results</ui.Button>
</Fill>
</ui.BottomSheet>Popover and tooltip
Your own content beside a button; short labels on hover
<ui.Popover label="Share" icon="send" title="Share this page" @bind="open"> <ui.TextField label="Link" :value="link" readonly="true" /> <ui.Button variant="primary" @click="copy">Copy link</ui.Button> </ui.Popover> <ui.Popover open_on="hover" variant="link" label="Ada Lovelace"> <Fill name="trigger">@ada</Fill> Ada Lovelace · Owner · 12 projects </ui.Popover> <ui.Tooltip text="Bold (Ctrl+B)"> <ui.Button variant="ghost" icon="bold" aria-label="Bold"></ui.Button> </ui.Tooltip>
Tabs
Three people are working on this project.
Showing one day.
<ui.Tabs options="Overview, Activity, Settings" @bind="tab"> <If cond="tab == 'Overview'"> ... </If> <ElseIf cond="tab == 'Activity'"> ... </ElseIf> <Else> ... </Else> </ui.Tabs>
Breadcrumbs
Where a page sits; in a routed app, auto="true" builds the trail from the URL
Click a level, or the … to see the hidden ones.
<ui.Breadcrumbs :items="[('/', 'Home'), ('/orders', 'Orders'), 'Order #1041']" />
<ui.Breadcrumbs :items="trail" max="4" separator="slash" />
<!-- In an app with router(): the trail of the current URL, each page
labelled by `crumb = "Orders"` (or "Order {id}") in its code block -->
<ui.Breadcrumbs auto="true" home_icon="true" />Table
Click a heading to sort, a row to pick it
| 1041 | Ada Lovelace | paid | 1,250.00 |
| 1043 | Alan Turing | refunded | 410.00 |
| 1042 | Grace Hopper | pending | 89.90 |
| 1044 | Katherine Johnson | paid | 72.50 |
Picked: nothing yet
<ui.Table :rows="orders" row_key="id" sortable="true" @select="open_order"
:columns="[('id', '#'), 'customer', 'status', {'key': 'total', 'format': ',.2f'}]">
<Fill name="cell">
<If cond="column == 'status'"><ui.Badge>{{ text }}</ui.Badge></If>
<Else>{{ text }}</Else>
</Fill>
</ui.Table>Charts
Dashboards, everyday charts and data science: hover, use the arrow keys, click the legend
| Value | |
|---|---|
| 1 | 31 |
| 2 | 33 |
| 3 | 32 |
| 4 | 36 |
| 5 | 38 |
| 6 | 37 |
| 7 | 41 |
| 8 | 44 |
| 9 | 43 |
| 10 | 48 |
| Value | |
|---|---|
| 1 | 102 |
| 2 | 118 |
| 3 | 96 |
| 4 | 131 |
| 5 | 125 |
| 6 | 140 |
| 7 | 133 |
| 8 | 129 |
| 9 | 147 |
| 10 | 152 |
| Value | |
|---|---|
| 1 | 21 |
| 2 | 25 |
| 3 | 24 |
| 4 | 22 |
| 5 | 28 |
| 6 | 30 |
| 7 | 27 |
| 8 | 33 |
| 9 | 35 |
| 10 | 37 |
| Online | Store | |
|---|---|---|
| Jan | $4,200 | $3,100 |
| Feb | $5,100 | $2,900 |
| Mar | $4,800 | $3,500 |
| Apr | $6,100 | $3,300 |
| May | $7,000 | $3,600 |
| Jun | $6,600 | $4,100 |
Nothing picked yet.
| Value | |
|---|---|
| JavaScript | 62% |
| Python | 51% |
| SQL | 51% |
| TypeScript | 38% |
| Java | 30% |
| C# | 27% |
| Go | 14% |
| Desktop | Mobile | Tablet | |
|---|---|---|---|
| Mon | 820 | 620 | 120 |
| Tue | 932 | 732 | 132 |
| Wed | 901 | 801 | 101 |
| Thu | 934 | 1,034 | 134 |
| Fri | 1,290 | 990 | 190 |
| Sat | 610 | 1,120 | 230 |
| Sun | 520 | 1,210 | 210 |
| Desktop | Mobile | Tablet | |
|---|---|---|---|
| Mon | 820 | 620 | 120 |
| Tue | 932 | 732 | 132 |
| Wed | 901 | 801 | 101 |
| Thu | 934 | 1,034 | 134 |
| Fri | 1,290 | 990 | 190 |
| Sat | 610 | 1,120 | 230 |
| Sun | 520 | 1,210 | 210 |
| Rain | Temperature | |
|---|---|---|
| Jan | 78 | 4.2 |
| Feb | 52 | 5.1 |
| Mar | 61 | 8.3 |
| Apr | 45 | 11.6 |
| May | 50 | 15.2 |
| Jun | 46 | 18.4 |
| Plan | Actual | Forecast | |
|---|---|---|---|
| W1 | 40 | 38 | – |
| W2 | 45 | 47 | – |
| W3 | 50 | 49 | – |
| W4 | 55 | 58 | 58 |
| W5 | 60 | – | 63 |
| W6 | 65 | – | 66 |
| W7 | 70 | – | 72 |
| Value | Share | |
|---|---|---|
| Chrome | 64.7% | 65% |
| Safari | 18.6% | 19% |
| Edge | 5.2% | 5.2% |
| Firefox | 2.8% | 2.8% |
| Other | 8.7% | 8.7% |
| category | Value | Share |
|---|---|---|
| Rent | $1,450 | 59% |
| Food | $520 | 21% |
| Travel | $310 | 13% |
| Fun | $190 | 7.7% |
| Value | Share | |
|---|---|---|
| Green | 42 | 38% |
| Blue | 35 | 32% |
| Red | 23 | 21% |
| Other | 10 | 9.1% |
| Value | Share | |
|---|---|---|
| Mon | 18 | 21% |
| Tue | 25 | 29% |
| Wed | 12 | 14% |
| Thu | 9 | 11% |
| Fri | 21 | 25% |
| Series | Height (cm) | Weight (kg) |
|---|---|---|
| Under 30 | 152 | 49 |
| Under 30 | 158 | 55 |
| Under 30 | 163 | 54 |
| Under 30 | 167 | 63 |
| Under 30 | 171 | 64 |
| Under 30 | 176 | 70 |
| Under 30 | 181 | 73 |
| Under 30 | 186 | 79 |
| 30 and over | 155 | 58 |
| 30 and over | 160 | 63 |
| 30 and over | 165 | 66 |
| 30 and over | 170 | 74 |
| 30 and over | 174 | 77 |
| 30 and over | 178 | 79 |
| 30 and over | 183 | 88 |
| 30 and over | 189 | 92 |
| Series | Income per person ($k) | Life expectancy | Population (m) |
|---|---|---|---|
| Life | 52 | 82.1 | 38 |
| Life | 31 | 78.4 | 120 |
| Life | 8 | 67.2 | 210 |
| Life | 14 | 72.5 | 64 |
| Life | 44 | 81.3 | 9 |
| Life | 3 | 61.8 | 95 |
| Life | 23 | 76.9 | 330 |
| Life | 61 | 83 | 6 |
| Value | Of first | From previous | |
|---|---|---|---|
| Visited the site | 12,400 | 100% | |
| Started sign-up | 4,100 | 33% | 33% |
| Confirmed email | 2,650 | 21% | 65% |
| Started a trial | 980 | 7.9% | 37% |
| Paid | 310 | 2.5% | 32% |
| Value | Of first | From previous | |
|---|---|---|---|
| Visited the site | 12,400 | 100% | |
| Started sign-up | 4,100 | 33% | 33% |
| Confirmed email | 2,650 | 21% | 65% |
| Started a trial | 980 | 7.9% | 37% |
| Paid | 310 | 2.5% | 32% |
| Change | Total | |
|---|---|---|
| Revenue | $48,000 | |
| Cost of goods | −$19,500 | $28,500 |
| Salaries | −$14,200 | $14,300 |
| Marketing | −$4,100 | $10,200 |
| Other income | +$2,300 | $12,500 |
| Tax | −$3,100 | $9,400 |
| Profit | $9,400 |
| Actual | Forecast | |
|---|---|---|
| Jan | 120 | – |
| Feb | 132 | – |
| Mar | 128 | – |
| Apr | 141 | – |
| May | 150 | 150 |
| Jun | – | 156 |
| Jul | – | 163 |
| Aug | – | 169 |
| Sep | – | 176 |
| Effect | |
|---|---|
| Placebo | 1.2 |
| 5 mg | 2.9 |
| 10 mg | 4.4 |
| 20 mg | 5.1 |
| From | To | Women | Men |
|---|---|---|---|
| 140 | 145 | 1 | 0 |
| 145 | 150 | 4 | 0 |
| 150 | 155 | 13 | 0 |
| 155 | 160 | 21 | 1 |
| 160 | 165 | 39 | 10 |
| 165 | 170 | 52 | 19 |
| 170 | 175 | 24 | 25 |
| 175 | 180 | 5 | 41 |
| 180 | 185 | 1 | 41 |
| 185 | 190 | 0 | 19 |
| 190 | 195 | 0 | 3 |
| 195 | 200 | 0 | 1 |
| n | Min | Q1 | Median | Q3 | Max | Mean | |
|---|---|---|---|---|---|---|---|
| Search | 60 | 57 | 83 | 99 | 124.5 | 210 | 106.58 |
| Checkout | 60 | 74 | 148 | 177 | 225 | 449 | 188.47 |
| Login | 60 | 26 | 52.5 | 62 | 80.75 | 134 | 68.08 |
| Upload | 60 | 139 | 204 | 258.5 | 310.5 | 690 | 274.17 |
| n | Min | Q1 | Median | Q3 | Max | Mean | |
|---|---|---|---|---|---|---|---|
| Search | 60 | 57 | 83 | 99 | 124.5 | 210 | 106.58 |
| Checkout | 60 | 74 | 148 | 177 | 225 | 449 | 188.47 |
| Login | 60 | 26 | 52.5 | 62 | 80.75 | 134 | 68.08 |
| Upload | 60 | 139 | 204 | 258.5 | 310.5 | 690 | 274.17 |
| 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Mon | 5 | 3 | 20 | 28 | 31 | 37 | 33 | 45 | 40 | 45 | 42 | 29 | 32 | 27 | 10 | 6 | 3 | 0 |
| Tue | 5 | 5 | 19 | 18 | 28 | 38 | 33 | 36 | 40 | 37 | 31 | 29 | 24 | 27 | 17 | 13 | 0 | 0 |
| Wed | 0 | 8 | 17 | 21 | 33 | 34 | 38 | 44 | 35 | 45 | 39 | 32 | 32 | 19 | 21 | 9 | 0 | 0 |
| Thu | 0 | 4 | 18 | 17 | 32 | 30 | 39 | 45 | 41 | 41 | 35 | 27 | 23 | 18 | 17 | 7 | 0 | 0 |
| Fri | 0 | 5 | 17 | 16 | 22 | 32 | 32 | 38 | 37 | 40 | 38 | 30 | 30 | 22 | 11 | 13 | 0 | 0 |
| Sat | 0 | 9 | 20 | 26 | 52 | 55 | 56 | 66 | 66 | 62 | 64 | 33 | 34 | 25 | 12 | 13 | 0 | 0 |
| Sun | 0 | 5 | 10 | 26 | 47 | 59 | 67 | 60 | 61 | 66 | 62 | 35 | 32 | 18 | 13 | 5 | 0 | 0 |
| Price | Power | Weight | Mpg | Doors | |
|---|---|---|---|---|---|
| Price | 1.00 | 0.97 | 0.77 | -0.72 | 0.07 |
| Power | 0.97 | 1.00 | 0.81 | -0.77 | 0.12 |
| Weight | 0.77 | 0.81 | 1.00 | -0.92 | -0.07 |
| Mpg | -0.72 | -0.77 | -0.92 | 1.00 | 0.24 |
| Doors | 0.07 | 0.12 | -0.07 | 0.24 | 1.00 |
| Date | Open | High | Low | Close | Volume |
|---|---|---|---|---|---|
| Mar 1 | $84.00 | $85.16 | $83.50 | $84.06 | 21,000 |
| Mar 2 | $84.06 | $84.53 | $83.20 | $84.26 | 28,000 |
| Mar 3 | $84.26 | $84.49 | $83.23 | $83.47 | 28,000 |
| Mar 4 | $83.47 | $83.87 | $79.39 | $80.59 | 29,000 |
| Mar 5 | $80.59 | $81.37 | $80.13 | $81.06 | 30,000 |
| Mar 6 | $81.06 | $82.25 | $80.67 | $81.69 | 20,000 |
| Mar 7 | $81.69 | $82.59 | $81.38 | $81.38 | 22,000 |
| Mar 8 | $81.38 | $84.38 | $81.22 | $83.08 | 24,000 |
| Mar 9 | $83.08 | $84.45 | $81.29 | $81.68 | 8,000 |
| Mar 10 | $81.68 | $84.40 | $81.19 | $83.80 | 11,000 |
| Mar 11 | $83.80 | $86.01 | $83.18 | $85.81 | 14,000 |
| Mar 12 | $85.81 | $88.58 | $85.77 | $88.26 | 26,000 |
| Mar 13 | $88.26 | $88.39 | $87.28 | $87.40 | 19,000 |
| Mar 14 | $87.40 | $88.33 | $87.30 | $87.62 | 22,000 |
| Mar 15 | $87.62 | $89.43 | $86.69 | $88.39 | 11,000 |
| Mar 16 | $88.39 | $90.38 | $87.42 | $90.25 | 28,000 |
| Mar 17 | $90.25 | $91.86 | $90.16 | $91.65 | 21,000 |
| Mar 18 | $91.65 | $92.89 | $91.13 | $91.99 | 29,000 |
| Mar 19 | $91.99 | $92.46 | $91.21 | $91.40 | 24,000 |
| Mar 20 | $91.40 | $96.64 | $90.98 | $94.25 | 20,000 |
| Apr 1 | $94.25 | $94.36 | $93.20 | $93.45 | 28,000 |
| Apr 2 | $93.45 | $93.83 | $92.36 | $93.37 | 15,000 |
| Apr 3 | $93.37 | $94.32 | $92.66 | $93.93 | 21,000 |
| Apr 4 | $93.93 | $94.54 | $91.24 | $91.36 | 25,000 |
| Apr 5 | $91.36 | $92.39 | $88.62 | $89.86 | 15,000 |
| Apr 6 | $89.86 | $90.66 | $89.16 | $90.58 | 16,000 |
| Apr 7 | $90.58 | $93.32 | $89.28 | $93.20 | 16,000 |
| Apr 8 | $93.20 | $94.43 | $92.36 | $93.74 | 18,000 |
| Apr 9 | $93.74 | $93.84 | $91.45 | $92.16 | 19,000 |
| Apr 10 | $92.16 | $92.52 | $89.99 | $90.49 | 20,000 |
| Apr 11 | $90.49 | $94.03 | $89.44 | $93.53 | 12,000 |
| Apr 12 | $93.53 | $94.21 | $91.95 | $92.08 | 26,000 |
| Apr 13 | $92.08 | $92.52 | $91.05 | $92.51 | 16,000 |
| Apr 14 | $92.51 | $93.27 | $91.36 | $91.97 | 15,000 |
| Apr 15 | $91.97 | $93.62 | $91.86 | $92.87 | 20,000 |
| Apr 16 | $92.87 | $92.99 | $91.51 | $91.94 | 30,000 |
| Apr 17 | $91.94 | $92.75 | $89.13 | $89.88 | 28,000 |
| Apr 18 | $89.88 | $90.13 | $86.68 | $87.07 | 30,000 |
| Apr 19 | $87.07 | $87.67 | $86.52 | $87.39 | 11,000 |
| Apr 20 | $87.39 | $87.91 | $85.40 | $85.58 | 15,000 |
| May 1 | $85.58 | $87.26 | $84.84 | $86.56 | 12,000 |
| May 2 | $86.56 | $86.72 | $85.61 | $86.11 | 13,000 |
| May 3 | $86.11 | $86.44 | $82.01 | $83.28 | 22,000 |
| May 4 | $83.28 | $83.83 | $82.55 | $83.57 | 17,000 |
| May 5 | $83.57 | $85.08 | $82.09 | $84.52 | 10,000 |
| May 6 | $84.52 | $86.86 | $83.94 | $86.58 | 28,000 |
| May 7 | $86.58 | $89.03 | $85.92 | $88.86 | 18,000 |
| May 8 | $88.86 | $88.97 | $85.38 | $85.66 | 23,000 |
| May 9 | $85.66 | $85.80 | $81.53 | $82.55 | 28,000 |
| May 10 | $82.55 | $83.43 | $82.34 | $83.22 | 29,000 |
| May 11 | $83.22 | $83.78 | $81.14 | $82.25 | 14,000 |
| May 12 | $82.25 | $82.38 | $81.43 | $82.01 | 16,000 |
| May 13 | $82.01 | $82.42 | $79.25 | $79.57 | 20,000 |
| May 14 | $79.57 | $83.27 | $77.98 | $82.32 | 24,000 |
| May 15 | $82.32 | $82.69 | $81.59 | $81.92 | 14,000 |
| May 16 | $81.92 | $82.35 | $81.42 | $82.12 | 26,000 |
| May 17 | $82.12 | $82.96 | $81.46 | $82.91 | 25,000 |
| May 18 | $82.91 | $83.67 | $82.36 | $83.04 | 9,000 |
| May 19 | $83.04 | $83.23 | $81.46 | $82.63 | 23,000 |
| May 20 | $82.63 | $84.35 | $82.02 | $83.59 | 24,000 |
| Value | Share | |
|---|---|---|
| Europe › Germany | $182k | 13.7% |
| Europe › France | $140k | 10.6% |
| Europe › Italy | $98k | 7.4% |
| Europe › Spain | $76k | 5.7% |
| Europe › Sweden | $41k | 3.1% |
| Americas › USA | $260k | 19.6% |
| Americas › Brazil | $88k | 6.6% |
| Americas › Canada | $64k | 4.8% |
| Asia › Japan | $150k | 11.3% |
| Asia › India | $120k | 9.0% |
| Asia › Korea | $58k | 4.4% |
| Oceania › Australia | $49k | 3.7% |
| From | To | Value |
|---|---|---|
| Salary | Budget | $5,200 |
| Freelance | Budget | $1,300 |
| Budget | Housing | $1,900 |
| Budget | Food | $850 |
| Budget | Transport | $420 |
| Budget | Fun | $630 |
| Budget | Savings | $2,700 |
| Savings | Index funds | $1,800 |
| Savings | Emergency fund | $900 |
| Date | Value |
|---|---|
| 2025-11-24 | 0 |
| 2025-11-25 | 2 |
| 2025-11-26 | 0 |
| 2025-11-27 | 2 |
| 2025-11-28 | 2 |
| 2025-11-29 | 0 |
| 2025-11-30 | 0 |
| 2025-12-01 | 2 |
| 2025-12-02 | 1 |
| 2025-12-03 | 1 |
| 2025-12-04 | 1 |
| 2025-12-05 | 2 |
| 2025-12-06 | 0 |
| 2025-12-07 | 1 |
| 2025-12-08 | 3 |
| 2025-12-09 | 3 |
| 2025-12-10 | 1 |
| 2025-12-11 | 5 |
| 2025-12-12 | 5 |
| 2025-12-13 | 0 |
| 2025-12-14 | 0 |
| 2025-12-15 | 8 |
| 2025-12-16 | 1 |
| 2025-12-17 | 0 |
| 2025-12-18 | 0 |
| 2025-12-19 | 0 |
| 2025-12-20 | 1 |
| 2025-12-21 | 0 |
| 2025-12-22 | 0 |
| 2025-12-23 | 8 |
| 2025-12-24 | 0 |
| 2025-12-25 | 3 |
| 2025-12-26 | 0 |
| 2025-12-27 | 0 |
| 2025-12-28 | 0 |
| 2025-12-29 | 0 |
| 2025-12-30 | 0 |
| 2025-12-31 | 0 |
| 2026-01-01 | 5 |
| 2026-01-02 | 0 |
| 2026-01-03 | 0 |
| 2026-01-04 | 0 |
| 2026-01-05 | 2 |
| 2026-01-06 | 0 |
| 2026-01-07 | 2 |
| 2026-01-08 | 3 |
| 2026-01-09 | 5 |
| 2026-01-10 | 0 |
| 2026-01-11 | 0 |
| 2026-01-12 | 3 |
| 2026-01-13 | 1 |
| 2026-01-14 | 1 |
| 2026-01-15 | 0 |
| 2026-01-16 | 5 |
| 2026-01-17 | 0 |
| 2026-01-18 | 1 |
| 2026-01-19 | 3 |
| 2026-01-20 | 8 |
| 2026-01-21 | 0 |
| 2026-01-22 | 8 |
| 2026-01-23 | 2 |
| 2026-01-24 | 1 |
| 2026-01-25 | 0 |
| 2026-01-26 | 3 |
| 2026-01-27 | 0 |
| 2026-01-28 | 0 |
| 2026-01-29 | 0 |
| 2026-01-30 | 0 |
| 2026-01-31 | 1 |
| 2026-02-01 | 0 |
| 2026-02-02 | 0 |
| 2026-02-03 | 1 |
| 2026-02-04 | 0 |
| 2026-02-05 | 8 |
| 2026-02-06 | 1 |
| 2026-02-07 | 0 |
| 2026-02-08 | 1 |
| 2026-02-09 | 0 |
| 2026-02-10 | 2 |
| 2026-02-11 | 1 |
| 2026-02-12 | 8 |
| 2026-02-13 | 1 |
| 2026-02-14 | 0 |
| 2026-02-15 | 1 |
| 2026-02-16 | 8 |
| 2026-02-17 | 8 |
| 2026-02-18 | 0 |
| 2026-02-19 | 0 |
| 2026-02-20 | 0 |
| 2026-02-21 | 0 |
| 2026-02-22 | 0 |
| 2026-02-23 | 1 |
| 2026-02-24 | 0 |
| 2026-02-25 | 3 |
| 2026-02-26 | 1 |
| 2026-02-27 | 0 |
| 2026-02-28 | 0 |
| 2026-03-01 | 1 |
| 2026-03-02 | 5 |
| 2026-03-03 | 1 |
| 2026-03-04 | 0 |
| 2026-03-05 | 8 |
| 2026-03-06 | 1 |
| 2026-03-07 | 1 |
| 2026-03-08 | 0 |
| 2026-03-09 | 0 |
| 2026-03-10 | 0 |
| 2026-03-11 | 8 |
| 2026-03-12 | 3 |
| 2026-03-13 | 8 |
| 2026-03-14 | 1 |
| 2026-03-15 | 0 |
| 2026-03-16 | 1 |
| 2026-03-17 | 1 |
| 2026-03-18 | 5 |
| 2026-03-19 | 8 |
| 2026-03-20 | 0 |
| 2026-03-21 | 0 |
| 2026-03-22 | 0 |
| 2026-03-23 | 0 |
| 2026-03-24 | 0 |
| 2026-03-25 | 0 |
| 2026-03-26 | 3 |
| 2026-03-27 | 3 |
| 2026-03-28 | 1 |
| 2026-03-29 | 0 |
| 2026-03-30 | 1 |
| 2026-03-31 | 3 |
| 2026-04-01 | 3 |
| 2026-04-02 | 2 |
| 2026-04-03 | 0 |
| 2026-04-04 | 0 |
| 2026-04-05 | 0 |
| 2026-04-06 | 8 |
| 2026-04-07 | 3 |
| 2026-04-08 | 0 |
| 2026-04-09 | 3 |
| 2026-04-10 | 2 |
| 2026-04-11 | 1 |
| 2026-04-12 | 0 |
| 2026-04-13 | 3 |
| 2026-04-14 | 1 |
| 2026-04-15 | 0 |
| 2026-04-16 | 5 |
| 2026-04-17 | 2 |
| 2026-04-18 | 1 |
| 2026-04-19 | 0 |
| 2026-04-20 | 1 |
| 2026-04-21 | 0 |
| 2026-04-22 | 5 |
| 2026-04-23 | 8 |
| 2026-04-24 | 8 |
| 2026-04-25 | 0 |
| 2026-04-26 | 1 |
| 2026-04-27 | 2 |
| 2026-04-28 | 3 |
| 2026-04-29 | 3 |
| 2026-04-30 | 0 |
| 2026-05-01 | 3 |
| 2026-05-02 | 0 |
| 2026-05-03 | 1 |
| 2026-05-04 | 5 |
| 2026-05-05 | 3 |
| 2026-05-06 | 8 |
| 2026-05-07 | 8 |
| 2026-05-08 | 8 |
| 2026-05-09 | 0 |
| 2026-05-10 | 0 |
| 2026-05-11 | 3 |
| 2026-05-12 | 8 |
| 2026-05-13 | 8 |
| 2026-05-14 | 0 |
| 2026-05-15 | 8 |
| 2026-05-16 | 0 |
| 2026-05-17 | 0 |
| 2026-05-18 | 8 |
| 2026-05-19 | 8 |
| 2026-05-20 | 8 |
| 2026-05-21 | 3 |
| 2026-05-22 | 0 |
| 2026-05-23 | 1 |
| 2026-05-24 | 0 |
| 2026-05-25 | 1 |
| 2026-05-26 | 1 |
| 2026-05-27 | 0 |
| 2026-05-28 | 5 |
| 2026-05-29 | 1 |
| 2026-05-30 | 1 |
| 2026-05-31 | 0 |
| 2026-06-01 | 3 |
| 2026-06-02 | 2 |
| 2026-06-03 | 5 |
| 2026-06-04 | 2 |
| 2026-06-05 | 2 |
| 2026-06-06 | 0 |
| 2026-06-07 | 1 |
| 2026-06-08 | 1 |
| 2026-06-09 | 8 |
| 2026-06-10 | 3 |
| 2026-06-11 | 0 |
| 2026-06-12 | 8 |
| 2026-06-13 | 0 |
| 2026-06-14 | 0 |
| 2026-06-15 | 8 |
| 2026-06-16 | 8 |
| 2026-06-17 | 5 |
| 2026-06-18 | 5 |
| 2026-06-19 | 3 |
| 2026-06-20 | 0 |
| 2026-06-21 | 0 |
| 2026-06-22 | 1 |
| 2026-06-23 | 1 |
| 2026-06-24 | 3 |
| 2026-06-25 | 8 |
| 2026-06-26 | 8 |
| 2026-06-27 | 0 |
| 2026-06-28 | 1 |
| 2026-06-29 | 2 |
| 2026-06-30 | 2 |
| 2026-07-01 | 2 |
| 2026-07-02 | 8 |
| 2026-07-03 | 5 |
| 2026-07-04 | 0 |
| 2026-07-05 | 0 |
| 2026-07-06 | 0 |
| 2026-07-07 | 8 |
| 2026-07-08 | 0 |
| 2026-07-09 | 0 |
| 2026-07-10 | 5 |
| 2026-07-11 | 1 |
| 2026-07-12 | 1 |
| 2026-07-13 | 3 |
| 2026-07-14 | 1 |
| 2026-07-15 | 1 |
| 2026-07-16 | 0 |
| 2026-07-17 | 3 |
| 2026-07-18 | 1 |
| 2026-07-19 | 0 |
| 2026-07-20 | 3 |
| 2026-07-21 | 5 |
| 2026-07-22 | 1 |
| 2026-07-23 | 1 |
| 2026-07-24 | 2 |
| 2026-07-25 | 0 |
| 2026-07-26 | 0 |
| 2026-07-27 | 0 |
| 2026-07-28 | 0 |
| 2026-07-29 | 0 |
| 2026-07-30 | 5 |
| 2026-07-31 | 0 |
| 2026-08-01 | 0 |
| 2026-08-02 | 1 |
| 2026-08-03 | 2 |
| 2026-08-04 | 5 |
| 2026-08-05 | 3 |
| 2026-08-06 | 5 |
| 2026-08-07 | 1 |
| 2026-08-08 | 0 |
| 2026-08-09 | 0 |
| 2026-08-10 | 5 |
| 2026-08-11 | 5 |
| 2026-08-12 | 8 |
| 2026-08-13 | 1 |
| 2026-08-14 | 0 |
| 2026-08-15 | 0 |
| 2026-08-16 | 1 |
| 2026-08-17 | 0 |
| 2026-08-18 | 3 |
| 2026-08-19 | 5 |
| 2026-08-20 | 1 |
| 2026-08-21 | 0 |
| 2026-08-22 | 1 |
| 2026-08-23 | 0 |
| 2026-08-24 | 0 |
| 2026-08-25 | 8 |
| 2026-08-26 | 0 |
| 2026-08-27 | 0 |
| 2026-08-28 | 8 |
| 2026-08-29 | 0 |
| 2026-08-30 | 0 |
| 2026-08-31 | 0 |
| 2026-09-01 | 2 |
| 2026-09-02 | 0 |
| 2026-09-03 | 5 |
| 2026-09-04 | 0 |
| 2026-09-05 | 0 |
| 2026-09-06 | 1 |
| 2026-09-07 | 0 |
| 2026-09-08 | 0 |
| 2026-09-09 | 3 |
| 2026-09-10 | 2 |
| 2026-09-11 | 8 |
| 2026-09-12 | 1 |
| 2026-09-13 | 0 |
| 2026-09-14 | 3 |
| 2026-09-15 | 2 |
| 2026-09-16 | 5 |
| 2026-09-17 | 0 |
| 2026-09-18 | 0 |
| 2026-09-19 | 1 |
| 2026-09-20 | 1 |
| 2026-09-21 | 2 |
| 2026-09-22 | 8 |
| 2026-09-23 | 8 |
| 2026-09-24 | 0 |
| 2026-09-25 | 8 |
| 2026-09-26 | 0 |
| 2026-09-27 | 1 |
| 2026-09-28 | 0 |
| 2026-09-29 | 3 |
| 2026-09-30 | 5 |
| 2026-10-01 | 3 |
| 2026-10-02 | 2 |
| 2026-10-03 | 0 |
| 2026-10-04 | 0 |
| 2026-10-05 | 0 |
| 2026-10-06 | 2 |
| 2026-10-07 | 0 |
| 2026-10-08 | 1 |
| 2026-10-09 | 8 |
| 2026-10-10 | 0 |
| 2026-10-11 | 0 |
| 2026-10-12 | 8 |
| 2026-10-13 | 0 |
| 2026-10-14 | 8 |
| 2026-10-15 | 8 |
| 2026-10-16 | 1 |
| 2026-10-17 | 0 |
| 2026-10-18 | 0 |
| 2026-10-19 | 2 |
| 2026-10-20 | 0 |
| 2026-10-21 | 8 |
| 2026-10-22 | 0 |
| 2026-10-23 | 5 |
| 2026-10-24 | 0 |
| 2026-10-25 | 0 |
| 2026-10-26 | 2 |
| 2026-10-27 | 5 |
| 2026-10-28 | 5 |
| 2026-10-29 | 0 |
| 2026-10-30 | 0 |
| 2026-10-31 | 0 |
| 2026-11-01 | 0 |
| 2026-11-02 | 2 |
| 2026-11-03 | 3 |
| 2026-11-04 | 3 |
| skill | Ada | Grace |
|---|---|---|
| Python | 9 | 6 |
| SQL | 7 | 8 |
| Design | 5 | 7 |
| Testing | 8 | 6 |
| Ops | 4 | 9 |
| Writing | 7 | 5 |
<ui.BarChart :data="sales" x="month" y="online, store" title="Sales"
stacked="true" format="${:,.0f}" @select="open_month(event)" />
<ui.LineChart :data="visits" x="day" y="desktop, mobile" curve="smooth" fill="true" />
<ui.BarChart :data="weather" x="month" y="rain" :series="[
{'key': 'temp', 'type': 'line', 'axis': 'right', 'color': 'warning'}]" />
<ui.PieChart :data="{'Chrome': 64.7, 'Safari': 18.6, 'Other': 16.7}" />
<ui.PieChart :data="spend" label="category" value="amount" donut="true" center="total" />
<ui.PolarAreaChart :data="tickets" />
<ui.RadarChart :data="skills" x="skill" y="ada, grace" />
<ui.ScatterChart :data="people" x="height" y="weight" group="group" trend="true" />
<ui.ScatterChart :data="countries" x="income" y="life" size="people" label="name" />
<ui.Sparkline :data="[31, 33, 32, 36, 38, 41]" kind="area" />
<ui.Gauge :value="cpu" label="CPU" format="{:.0f}%" bands="60:success, 85:warning, 100:danger" />
<ui.FunnelChart :data="{'Visited': 12400, 'Signed up': 4100, 'Paid': 310}" />
<ui.WaterfallChart :data="bridge" label="item" value="amount" totals="Revenue, Profit" />
<ui.Histogram :data="people" y="height" group="group" curve="true" show_mean="true" />
<ui.BoxPlot :data="{'Search': times_a, 'Login': times_b}" violin="true" points="all" />
<ui.Heatmap :data="visits" x="hour" y="day" value="visits" />
<ui.CorrelationMatrix :data="cars" triangle="lower" />
<ui.CandlestickChart :data="prices" averages="5, 20" />
<ui.Treemap :data="sales" path="region, country" value="sales" />
<ui.SankeyChart :data="[('Salary', 'Budget', 5200), ('Budget', 'Savings', 2700)]" />
<ui.CalendarHeatmap :data="contributions" />
<ui.LineChart :data="forecast" x="month" :series="[
{'key': 'forecast', 'lower': 'low', 'upper': 'high'}, {'key': 'actual', 'trend': True}]" />
<ui.BarChart :data="trials" x="dose" y="effect" :series="[{'key': 'effect', 'error': 'se'}]" />
sales = [{"month": "Jan", "online": 4200, "store": 3100}, ...] # or a DataFrameTree
Folders load as they open; search; checkboxes with partly checked parents
Selected: app
3 granted: orders.read, orders.write, users.read
<ui.Tree :nodes="files" @bind="selected" :filter="query"
@load="load_folder(event)" @activate="open_file(event)" />
<ui.Tree :nodes="permissions" checkable="true" expand_all="true" @bind="granted" />
files = [
{"value": "src", "label": "src", "icon": "folder", "children": [
{"value": "app", "label": "app.weft", "icon": "file"},
]},
{"value": "docs", "label": "docs", "icon": "folder", "children": None}, # loads on open
]
async def load_folder(self, value):
children = await fetch_folder(value)
... # put them in that node's children listCarousel
Arrows, dots, a swipe or a drag; the second one plays by itself
Showing Mountains (1 of 5)
<ui.Carousel label="Places" :items="places" @bind="place" loop="true">
<Fill name="slide">
<img :src="slide['src']" :alt="slide['alt']" />
</Fill>
</ui.Carousel>
<ui.Carousel label="Products" :items="products" per_view="3" autoplay="4">
<Fill name="slide"><ProductCard :product="slide" /></Fill>
</ui.Carousel>Toasts
Messages that go away by themselves
Items: 3
<ui.Toaster /> <!-- once, inside <ui.Theme> -->
<ui.Toaster look="snackbar" /> <!-- or as a snackbar -->
from pyweft_ui import toast
toast("Changes saved.", "success")
toast("Item deleted.", action="Undo", on_action=self.undo)Layout
Row, Column, Grid, Card and Divider: no CSS needed
<ui.Grid cols="3" gap="sm"> ... </ui.Grid> <ui.Row justify="between"> ... </ui.Row> <ui.Column gap="lg"> ... </ui.Column> <ui.Divider /> <ui.Divider inset="3.1rem" spacing="none" /> <!-- under a list with avatars --> <ui.Divider label="or" /> <ui.Divider vertical="true" /> <!-- between buttons in a Row -->