Dates & disclosure¶
This page groups two small but heavily used families in tempest-core: the
date/time components — Calendar (month grid) and Clock (digital
clock) — and the one disclosure component, Accordion (a section that
expands and collapses). All are Components: they describe intent and lower
to primitives (Text / Row / Column / Container / Button) at render
time, so they work in both renderers with no renderer changes. 🚀
What you'll learn here
- How
Calendarbuilds the month grid and reports the tapped day viaon_select. - How
Clockmerely displays a time string — the app drives the tick. - Why both moved to the M3 theme tokens (Track H6) and what that changes visually.
- How
Accordionis app-controlled:openlives in state andon_toggleflips it.
Dates & time¶
Both time components share the same philosophy: the core does not tell time.
Calendar draws a month and reports which day you tapped; Clock just paints
the string the app already formatted. Both read colors from the theme instead
of hard-coded hexes.
Calendar¶
A month grid of selectable day cells. In the minimal case you pass only
on_select — the month and selection default to empty (current month, nothing
selected):
That single Calendar(on_select=…) already renders the current month against
the default M3 light theme, with a title, a weekday header and one row per week.
To control the displayed month and the highlighted day, pass month and
selected from app state:
from tempest_core import Calendar
agenda = Calendar(
month="2026-07", # (1)!
selected="2026-07-12",
on_select=lambda iso: app.set_state(selected=iso), # (2)!
color_scheme="primary",
)
monthis"YYYY-MM"; empty falls back to the current month.selectedis"YYYY-MM-DD"and only highlights when it falls in the displayed month.on_selectreceives the tapped day's ISO"YYYY-MM-DD"string. Store it in state and feed it back throughselectedto close the loop (see API reference).
Props¶
| Prop | Type | Default | What it does |
|---|---|---|---|
on_select |
Callable[[str], Any] |
(required) | Called with the tapped day's ISO "YYYY-MM-DD". |
month |
str |
"" |
The displayed month as "YYYY-MM"; empty means the current month. |
selected |
str |
"" |
The selected day as "YYYY-MM-DD"; highlighted when it falls in the displayed month; empty = no selection. |
color_scheme |
str |
"primary" |
The M3 role family the selected day fills with. |
theme |
Theme |
Theme() |
The theme whose tokens supply the colors. |
style |
Style \| None |
None |
Override merged on top of the grid's default Style (via merge_style). |
key |
str \| None |
None |
Reconciliation key; falls back to "calendar" when absent. |
Moved to the theme tokens (Track H6)
Calendar no longer hard-codes hexes. The title and day text read the
ON_SURFACE role; the weekday header and unselected days read the muted
ON_SURFACE_VARIANT / SURFACE_VARIANT roles; the selected day fills with the
color_scheme role (default primary) over its legible on_* content — all
resolved from the theme. It's backward-compatible — Calendar(on_select=…)
now renders against the M3 light theme (a visual shift from the previous
dark palette).
Calendar is controlled, like the rest of the kit
Selection doesn't live inside the component: on_select hands you the date,
you store it in app state and feed it back through selected. Same pattern as
Drawer and Accordion — the core stays stateless, the app is the source of
truth.
Clock¶
A digital clock face that renders a preformatted time string. The component
does not tick on its own — the app formats and updates the text from state (as in
the stopwatch example):
Pass a label for a muted caption under the time, and an optional color_scheme
to tint the time:
from tempest_core import Clock
stopwatch = Clock(
time="00:00:42",
label="Elapsed time", # (1)!
color_scheme="primary", # (2)!
)
labelis an optional caption; whenNone,Clockrenders the time only.color_schemeis optional —None(or"neutral") keeps the time in the neutralON_SURFACE.
Props¶
| Prop | Type | Default | What it does |
|---|---|---|---|
time |
str |
"" |
The time text (e.g. "12:34:56"); the app formats and ticks it from state. |
label |
str \| None |
None |
Optional caption shown muted under the time. |
color_scheme |
str \| None |
None |
Optional M3 role family tinting the time; None keeps the neutral ON_SURFACE. |
theme |
Theme |
Theme() |
The theme whose tokens supply the colors. |
style |
Style \| None |
None |
Override merged on top of the centered default Style. |
key |
str \| None |
None |
Reconciliation key; falls back to "clock" when absent. |
Moved to the theme tokens (Track H6)
Like Calendar, Clock stopped hard-coding hexes: the time reads ON_SURFACE
(or the color_scheme role, when given), the caption reads the muted
ON_SURFACE_VARIANT and the background reads SURFACE — all from the theme.
Clock(time=…) still works and now renders against the M3 light theme (a
visual shift from the previous dark palette).
Clock does not count time
It's a face, not a timer. Passing time="12:34:56" shows exactly that
string. Whatever increments the clock (an asyncio loop, a Timer, a state
tick) is the app — the core stays stateless and deterministic on purpose.
Disclosure¶
Disclosure is the "show/hide on demand" pattern. The kit ships one component for
it: Accordion.
Accordion¶
A titled section whose body shows only when open. There's no overlay: an
open accordion simply renders its body below the header. open is
controlled — it lives in app state and is flipped by the header's
on_toggle, mirroring Drawer:
from tempest_core import Accordion
from tempest_core import Text
details = Accordion(
title="Order details",
open=app.state.details_open, # (1)!
on_toggle=lambda: app.set_state(details_open=not app.state.details_open),
children=[Text(content="Delivery expected on Friday.")], # (2)!
)
opencomes from app state — the component never stores this boolean.childrenare revealed only whenopenisTrue; closed, the header renders on its own.
The header gets a simple rotation marker — ▸ when closed, ▾ when open —
prefixed to the title, so the user sees the state at no renderer cost.
Props¶
| Prop | Type | Default | What it does |
|---|---|---|---|
on_toggle |
Callable[[], Any] |
(required) | Called when the header is tapped (flip open in state). |
title |
str |
"" |
The header text. |
open |
bool |
False |
Whether the body is expanded. |
children |
list[Widget] |
[] |
The widgets revealed when open. |
variant |
CardVariant |
FILLED |
The header's surface treatment (filled / outlined). |
color_scheme |
str |
"neutral" |
The M3 role family to tint the header with. |
theme |
Theme |
Theme() |
The theme whose tokens resolve the header surface. |
style |
Style \| None |
None |
Override merged on top of the container's default Style. |
key |
str \| None |
None |
Reconciliation key; falls back to "accordion" when absent. |
The header is a resolved surface (Track H3)
The header doesn't hard-code colors: it goes through resolve_surface_variant,
which produces a filled or outlined Material 3 surface from variant and
color_scheme, with the spacing steps (padding/radius) coming from the
theme's scale. On top of that, the header text gets FontWeight.BOLD.
Expand/collapse is the app's call
Because open is external, you own the policy: an accordion open by default, a
"single-open" group where opening one closes the others, a state persisted
across sessions — it all lives in your set_state. Accordion just reflects
the boolean you give it.
No body when closed, no hidden cost
Closed, Accordion renders only the header button — the children never
enter the primitive tree. Opening inserts a Column with the body below;
closing removes it. Nothing stays mounted and hidden.
Recap¶
- Three components, two families:
Calendar+Clock(date/time) andAccordion(disclosure), allComponents that lower to primitives. Calendar: a controlled month grid;on_selecthands you the ISO"YYYY-MM-DD", you feed it back throughselected. The chosen day fills with thecolor_scheme.Clock: a face, not a timer — it shows thetimestring the app formats and ticks;labelandcolor_schemeoptional.- Theme tokens (Track H6):
CalendarandClockstopped hard-coding hexes and readON_SURFACE/ON_SURFACE_VARIANT/SURFACEfrom thetheme— M3 light by default. Accordion: a controlled titled section;openlives in state andon_toggleflips it. The header is resolved byresolve_surface_variant(Track H3); closed, the body isn't even mounted.