Dashboard Widget that Reflows by Container Size
Build a dashboard widget (KPI tile, chart card) that switches between compact, default, and detailed layouts based on its container width using stacked @container rules.
Detailed Explanation
A Widget That Earns Its Place
In a dashboard, the same widget might be dropped into a 200px sidebar slot, a 400px grid cell, or a full-width 1200px expand-mode. Container queries let one component handle all three.
Three Layout Modes
.widget-host {
container-type: inline-size;
container-name: widget;
}
/* Compact (default) */
.widget {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.5rem;
}
.widget-chart { display: none; }
.widget-detail { display: none; }
/* Default */
@container widget (min-width: 320px) {
.widget {
grid-template-columns: 1fr;
grid-template-rows: auto auto;
}
.widget-chart { display: block; height: 80px; }
}
/* Detailed */
@container widget (min-width: 600px) {
.widget {
grid-template-columns: 200px 1fr;
grid-template-rows: auto;
}
.widget-chart { height: 140px; }
.widget-detail { display: block; }
}
What Each Mode Shows
- Compact (<320px): icon + KPI value only. Suitable for sidebar metrics.
- Default (320-599px): KPI value + sparkline chart underneath. The standard grid cell view.
- Detailed (≥600px): KPI value + larger chart + a column of secondary metrics. Full-width or pinned mode.
Why Container Queries vs JS
Most dashboards historically used a ResizeObserver to watch widget width and toggle classes. That works but adds JS overhead, layout thrashing on resize, and FOUC during initial render. Container queries push the work to the browser's layout engine — no JS, no flicker, instant first paint.
Editable Layouts
For drag-and-resize dashboards (Grafana, Datadog), this pattern is the difference between a widget that "just works" at any cell size and one that requires per-widget configuration.
Use Case
Use this pattern for KPI tiles, chart cards, status widgets, and any dashboard component that users can resize, rearrange, or pin between sidebar and main grid.