Home / Snippets / Layout /

Dashboard widget with CQ

A dashboard stat widget that adapts its layout from compact (icon + number stacked) to expanded (icon left, stats right) using CSS container queries.

📈
4,821
Page views
↑ 12% this week
👤
312
New users
↑ 7% this week

Drag the bottom-right corner of the box to resize and watch each widget adapt independently.

New feature
layoutno-js

Quick implementation

.widget-cq {
  container-type: inline-size;
}

.dash-widget {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 1.25rem;
  background: var(--card);
  border-radius: var(--radius);
}

.dash-widget__icon {
  font-size: 1.5rem;
  line-height: 1;
}

.dash-widget__value {
  font-size: 2rem;
  font-weight: 800;
  line-height: 1;
  color: var(--text);
}

.dash-widget__label {
  font-size: 0.8rem;
  color: var(--muted);
}

.dash-widget__trend {
  font-size: 0.8rem;
  font-weight: 600;
  color: oklch(0.7 0.18 145);
}

@container (min-width: 14rem) {
  .dash-widget {
    flex-direction: row;
    align-items: center;
    gap: 1rem;
  }

  .dash-widget__body {
    flex: 1;
  }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building adaptive dashboard widgets.

Goal: A stat widget that switches from a compact stacked layout (icon
above number above label) to a horizontal expanded layout (icon left,
stats right) based on its container width using CSS container queries.

Technical constraints:
- Apply container-type: inline-size to a .widget-cq wrapper around each
  widget instance.
- Default layout: display: flex; flex-direction: column.
- At container min-width: 14rem, switch to flex-direction: row with the
  icon on the left and a .dash-widget__body div on the right.
- .dash-widget__body gets flex: 1 to fill remaining space.
- Use oklch(0.7 0.18 145) for the positive trend color — pair it with an
  accessible text label or arrow character, not color alone.
- Use var(--card) for the widget background.
- Use oklch() for all other color values — no hex or rgba.

Framework variant (pick one):
A) Vanilla CSS + HTML — .widget-cq wrapper, .dash-widget, .dash-widget__icon,
   .dash-widget__body, .dash-widget__value, .dash-widget__label,
   .dash-widget__trend classes.
B) React component — accepts icon, value, label, and trend props; applies
   container-type via an inline style or CSS Module on the wrapper div.

Edge cases to handle:
- If the trend value is negative, use a different oklch() color (e.g.
  oklch(0.65 0.2 25) for red) and a downward arrow — never rely on
  color alone.
- If the icon is absent, the text column should still align correctly
  without a gap placeholder.
- Add container-name to .widget-cq if widgets are nested inside another
  container query context.

Return CSS only (or a React component if variant B).

Why

Dashboard widgets are the canonical container query use case — the same widget appears in a narrow sidebar, a wide main panel, and a modal. Each context demands a different layout. Container queries remove the need for per-instance CSS overrides or JavaScript ResizeObserver logic.

The logic

Wrap each widget in .widget-cq with container-type: inline-size. The default layout is flex-direction: column. At container widths of 14rem or more, @container switches to flex-direction: row with the icon on the left and a .dash-widget__body div on the right holding the number, label, and trend. The icon size and number size stay constant — only the arrangement changes.

Accessibility & performance

The trend indicator oklch(0.7 0.18 145) (green) must not be the only signal of direction — pair it with an accessible text label or an arrow character. Container queries have no runtime cost and do not require ResizeObserver polyfills in modern browsers.