Dashboard¶
A responsive analytics dashboard with zero hand-written CSS or JavaScript.
dashboard/
├── app.py # composition root
├── models/ # metrics.py (the data)
├── services/ # metrics.py (MetricsService, also a JSON API)
└── views/
├── components/ # sidebar, stat_card, region_chart, recent_orders
├── pages/ # overview.py
└── styles/
1. Data first¶
class MetricsService(Service):
"""Sales metrics. A real app would query a database or a warehouse here."""
@remote(method="GET")
def overview(self, days: int = 30, seed: int = 0) -> Metrics:
"""Revenue, orders, conversion, refunds and recent orders for the last ``days`` days."""
if days not in PERIODS:
raise FieldError("days", f"Choose one of: {', '.join(map(str, PERIODS))}")
rng = random.Random(days * 1000 + seed) # deterministic fake numbers; a new seed changes them
by_region = {region: rng.randint(4, 40) * days * 10 for region in REGIONS}
recent = [
Order(f"#{rng.randint(10_000, 99_999)}", rng.choice(CUSTOMERS), rng.choice(REGIONS), rng.randint(20, 900))
for _ in range(6)
]
return Metrics(
days=days,
revenue=sum(by_region.values()),
orders=rng.randint(80, 140) * days,
conversion=round(rng.uniform(1.5, 4.5), 2),
refunds=rng.randint(0, 3) * days,
by_region=by_region,
recent=recent,
)
Components never fetch data themselves here: the page loads Metrics and passes plain values
down as props. That keeps components easy to reuse and to test.
3. A chart that measures itself¶
class RegionChart(Component):
"""A bar chart made of plain elements. It adapts to its *own* width with a container query:
in a narrow card the amounts are hidden, whatever the size of the screen."""
data: dict[str, int]
def render(self):
top = max(self.data.values())
rows = [
h(
"li",
Text(region, size="sm", style=BAR_LABEL),
h("div", h("div", style=BAR | Style(width=f"{value / top * 100:.0f}%")), style=BAR_TRACK),
Text(f"${value:,}", size="sm", style=AMOUNT),
style=BAR_ROW,
)
for region, value in sorted(self.data.items(), key=lambda kv: -kv[1])
]
return Card(h("ul", *rows, role="list", style=CHART_LIST), title="Revenue by region", style=CHART)
container_type="inline-size" makes the card a container; container={"compact": ...}
hides the amounts when the card is narrow, whatever the size of the screen. That’s what
makes a component reusable in any layout.
4. The page¶
@router.page("/")
class Overview(Page):
title = "Dashboard · Rational Web"
days = State("30")
seed = State(0)
def set_range(self, value: str):
if value in dict(RANGE_OPTIONS): # never trust the browser: ignore values that aren't options
self.days = value
def refresh(self):
self.seed += 1
def render(self):
m = self.use(MetricsService).overview(int(self.days), self.seed)
return Row(
Sidebar(),
Main(
Row(
Column(Heading("Overview", level=1), Text("Sales performance at a glance", muted=True), gap="xs"),
Spacer(),
Select("Range", RANGE_OPTIONS, value=self.days, on_change=self.set_range, hide_label=True),
Button("Refresh", variant="secondary", on_click=self.refresh),
wrap=True,
align="end",
),
# As many columns as fit, at least 200px wide each: responsive without media queries.
Grid(
StatCard(label="Revenue", value=f"${m.revenue:,}", delta=12.4),
StatCard(label="Orders", value=f"{m.orders:,}", delta=4.1),
StatCard(label="Conversion", value=f"{m.conversion}%", delta=-0.6),
StatCard(label="Refunds", value=str(m.refunds), delta=-2.3),
min_column_width=200,
),
Grid(RegionChart(data=m.by_region), RecentOrders(orders=m.recent), min_column_width=340),
style=MAIN,
),
align="stretch",
gap=0,
collapse="narrow", # the sidebar moves above the content below 1024px
)
Grid(min_column_width=200)fits as many KPI tiles per row as the space allows.Row(collapse="narrow")stacks the sidebar above the content on tablets and phones.set_rangeignores values that aren’t offered options: never trust the browser.