A phone app's screen drawn from rows instead of a screenshot: a status bar, a title, then rows of fourteen kinds (a big figure, a ring of progress, a checklist, a bar chart, a card, chips, a map with a route, a player, a chat, a list, a week of days, a button, a lock screen's clock, a notification) and a tab bar.
Widget Bento
An app beyond its own screen, in a bento: its home-screen widgets in three sizes, its lock screen (a phone's top half, cut by the tile) and its watch face, each drawn from rows (AppScreen without its chrome) on a tile of a colour block, with a title and a line under it. Small widgets take one column, medium two, large two by two, the lock screen two rows. The tiles arrive in turn and each drawing fills as its tile arrives: the ring, the bars, the checks. Zero JavaScript.
Live demo
Widgets
On every screen you have
The home screen, the lock screen and the wrist each show today at a glance.
Today at a glance
The ring, on your home screen.
A nudge at your usual time
One notification a day, never more.
The week
Each day as a bar.
On your wrist
Tick a habit off from the watch.
The streak
Days in a row, kept.
Next up
The one habit left today.
Live, from the library itself, at full size: scroll, hover, navigate away and back. The demo is never gated.
Variants · each row writes the attribute the prop writes; the section's own CSS answers
Measured
JavaScript of its own
0 B
Uses Reveal and the shared runtime (1.9 kB raw, once per site). With those included: 3.0 kB raw.
CSS 26.8 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
Chrome 105 · Firefox 110 · Safari 16; needs container-units. Elsewhere: without container units the drawings keep the page's text size.
Install Pro
The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:
npx shadcn@latest add @moonarc-pro/widget-bento---
import WidgetBento from '@/components/moonarc/pro/WidgetBento.astro';
---
<WidgetBento
headline="On every screen you have"
tiles={[
{ kind: 'widget', size: 'small', title: 'Today at a glance', screen: { rows: [{ kind: 'ring', value: 0.75, text: '3/4', label: 'done' }] } },
{ kind: 'widget', size: 'medium', title: 'The week', screen: { rows: [{ kind: 'bars', values: [3, 4, 2, 4, 4, 3, 1] }] } },
{ kind: 'lock', title: 'A nudge at the right time', screen: { rows: [{ kind: 'clock', time: '7:42', date: 'Friday 9 October' }, { kind: 'notice', app: 'Sitename', title: 'Walk 20 minutes', text: 'Your usual time' }] } },
{ kind: 'watch', title: 'On your wrist', screen: { rows: [{ kind: 'ring', value: 0.5, text: '2/4', label: 'today' }] } },
]}
/>Without a key the registry answers 401 with an upgrade message. Everything this section composes is free: app-screen, reveal.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
tiles | { kind; size?; title; text?; block?; screen }[] | none | The tiles, in order (the grid fills gaps densely). kind is widget, lock or watch; size (widgets) is small, medium or large; block is the tile's colour block (unset, the tiles take blocks 1 to 5 in turn); screen is AppScreen's props, and its drawing keeps the app's accent (the primary) unless the screen names a block. |
headline | string | none | The section's heading. Slot "headline" wins and may hold an <em>. |
eyebrow | string | none | A short line above the heading. |
copy | string | none | A line under the heading. |
level | 2 | 3 | 2 | Heading level of the heading; each tile's title is one below. |
tone | 'surface' | 'muted' | 'ink' | 'brand' | unset (paints nothing) | The section's ground. |
density | 'compact' | 'cozy' | 'roomy' | 'cozy' | Block padding ×0.8 / ×1 / ×1.25 through --ma-density. |
Theming and overrides
The section reads the theme contract (shadcn's variable names with the base tokens as fallback), so a pasted theme or one --ma-brand recolours it. Its class names are the override surface and stay stable; a named slot replaces the matching prop with your own markup.
Classes
.ma-wbento.ma-wbento__art.ma-wbento__caption.ma-wbento__copy.ma-wbento__eyebrow.ma-wbento__grid.ma-wbento__head.ma-wbento__headline.ma-wbento__inner.ma-wbento__lock.ma-wbento__text.ma-wbento__tile.ma-wbento__title.ma-wbento__watch.ma-wbento__widget
Slots
slot="headline"
Reduced motion
The tiles are there at once and the drawings are drawn as they end.
With ClientRouter
CSS and Reveal.
Why it is built this way
- A widget is the app's, not the tile's: it is drawn on the page's own ground and rounded the way the systems round theirs, while the tile around it carries the colour.
- The tile carries a block, the drawing carries the app's accent: a widget on a dark tile does not take that tile's colour for its ring. A watch face is black, as watch faces are, and its accent is lifted toward white (AppScreen's --ma-as-tint) so a dark primary still reads on it.
- The drawings wait for their tile: AppScreen holds its motion while the Reveal around it has not arrived, so a ring fills as it is scrolled to.
Replaces: Widget showcase sections on app landing pages. See the migration table.
Related
App Hero
ProA hero for a mobile app: the headline, the rating line (stars, the average, how many rated and where) and the store buttons beside a phone on a disc of the app's colour.
Block Cards
ProWhat is on offer, as colour blocks: each card paints one of the palette's blocks and carries a drawing in its corner (Motif), an index, a name, a line, chips for what it includes and a foot with a price and a round arrow.
Hero Bento
ProThe first screen of one person's work, as a bento.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown