Skip to content
KONIGI Patterns

Dashboards / Managing screen space / Wallboard mode

‹ 6 of 6

Wallboard mode

People need to read a shared dashboard from a distance without interacting with it.

Updated September 10, 2026

Problem

A dashboard designed for a desk may be unreadable across a room, where people cannot scroll or open details.

Solution

Use a dedicated display mode with larger text, fewer panels, and minimal navigation. Put essential values and labels on screen rather than behind interaction.

Keep routine content visually quiet and make important changes distinct with labels or symbols as well as color. Test from the actual viewing distance and under the room’s lighting.

Keep urgent information visible continuously. If views rotate, use rotation for secondary awareness rather than information people must catch immediately. Show data age and connection problems clearly.

Use when

A shared display provides a small amount of useful information that people can read from a distance.

Don’t use when

Nobody in the room can act on it. A wallboard whose numbers belong to another team is decoration, and worse than decoration, because it consumes wall and trains people to ignore screens.

Trade-offs

Wallboards need their own layout and maintenance. Familiar displays can fade into the background. Rotation hides each view part of the time, and anything shown may be visible to visitors or passersby.

Checklist

  • What is the actual viewing distance, and is the smallest type readable from there?
  • How many panels, and can each be understood in a one-second glance?
  • Is anything dependent on hover, scroll or click?
  • What fraction of the screen carries colour when everything is healthy?
  • If views rotate, what is the cost of missing the one that mattered?
  • Does the page recover on its own after a reboot or a network drop?
  • Is stale data obvious from ten feet, or does the page look fine when frozen?
  • What is the aspect ratio and brightness of the actual screen in the actual room?
  • Is anything on screen that shouldn’t be seen by everyone who walks past?
  • Has anybody looked at it, from where people stand, in the last month?

Compare

Grafana provides the mechanics directly with kiosk mode and playlists, so the wallboard is the same dashboard with chrome removed, which is convenient and means the layout is rarely redesigned for the distance. Datadog offers TV mode with a similar stripping of chrome, and its tag-scoped pages make it easy to put the same layout on several screens scoped to different teams. Netdata is a poor fit by design, since per-second density is built for close inspection rather than distance, which is a useful reminder that not every dashboard has a wallboard version. Public status pages are the best-designed wallboards most people encounter, because they assume an unknown reader at an unknown distance and reduce accordingly.

Grayscale with alerts keeps warnings noticeable. Header KPI strip provides a compact summary. Semantic status color defines the state cues.

Dense small-multiple layout serves close expert reading instead. Freshness indicator helps distinguish a quiet system from a frozen display.

Wallboard mode anatomy The same dashboard at a desk and on a wall. The desk version is twelve panels with chrome, a legend and small type. The wall version is four panels, no chrome, type several times larger, direct labels instead of a legend, and grey until something is wrong. Sixty centimetres, then three metres At a desk Checkout 99.4% P95 312 Queue 7 Errors 4.1% On a wall 1 2 3 1 EVERY CONSTANT CHANGES Text that reads at 60cm needs three or four times the size at three metres. Twelve panels become four. An eight-entry legend becomes nothing anyone can read. 2 NO POINTER Anything that lived on hover is gone, so series get labelled directly or dropped. 3 GREY AT REST Six always-coloured panels spend the whole signalling budget before anything goes wrong. Rotation is fine for awareness and useless for anything anyone is expected to catch.
Wireframe — the pattern's anatomy, not any one product's version of it

Implementation

Read from ten feet away, with no mouse and nobody watching most of the time. Type goes up several steps and interaction goes to zero. The only thing that should change unprompted is state someone needs to walk over for.

Tokens
--card--card-foreground--muted--muted-foreground--border--status-critical--status-warn--status-unknown

Checkout

99.4%

P95

312

Queue

7

Errors

4.1%

Wallboard.tsxAt most four tiles, enforced by the type. Nominal is the same ink as the label, and a stalled feed is drawn as a state rather than left looking fine.

import { cn } from "@/lib/utils";
import { freshness } from "../freshness-indicator/freshness";
import type { Status } from "../semantic-status-color/status";

export type Tile = { label: string; value: string; status: Status };

/** Four at most, and the type says so. Twelve panels at three metres is
 *  twelve panels nobody can read. */
export type Tiles = [Tile] | [Tile, Tile] | [Tile, Tile, Tile] | [Tile, Tile, Tile, Tile];

type Props = {
  tiles: Tiles;
  /** When the newest sample arrived and how often one should. A frozen page
   *  looks fine from ten feet, so stale has to be a state the board draws. */
  lastArrival: Date | null;
  expectedEveryMs: number;
  now?: Date;
};

/** Grey at rest. A nominal value is the same ink as its label; colour is
 *  spent only on a state someone should walk over for. */
const VALUE: Record<Status, string> = {
  nominal: "text-card-foreground",
  warn: "text-status-warn",
  critical: "text-status-critical",
  unknown: "text-status-unknown",
};

const clock = (d: Date) => `${String(d.getUTCHours()).padStart(2, "0")}:${String(d.getUTCMinutes()).padStart(2, "0")}`;

export function Wallboard({ tiles, lastArrival, expectedEveryMs, now = new Date() }: Props) {
  const { state } = freshness(lastArrival, expectedEveryMs, now);
  const frozen = state === "stale" || state === "unknown";

  return (
    <div className="relative grid grid-cols-2 gap-4 rounded-lg border bg-muted p-5" role="region" aria-label="wallboard">
      {tiles.map((t) => (
        <div key={t.label} className={cn("rounded-md border bg-card p-4", frozen && "opacity-40")}>
          {/* No hover, no legend, no chrome: the label is on the tile. */}
          <p className="text-xs uppercase tracking-[0.12em] text-muted-foreground">{t.label}</p>
          <p className={cn("mt-1 text-5xl font-medium tabular-nums", VALUE[t.status])}>{t.value}</p>
        </div>
      ))}
      {frozen && (
        <p className="absolute inset-x-5 top-1/2 -translate-y-1/2 rounded-md bg-status-critical px-4 py-3 text-center text-2xl font-medium uppercase tracking-[0.12em] text-card" role="alert">
          {lastArrival ? `stale since ${clock(lastArrival)}` : "no data"}
        </p>
      )}
    </div>
  );
}

demo.tsxHow it is called: four tiles, one critical, data twenty seconds old against a thirty-second cadence.

import { Wallboard } from "./Wallboard";

/**
 * Four numbers for a screen across the room. Three are at rest and drawn in
 * the same ink as their labels; errors has crossed its threshold and is the
 * only colour on the wall. The clock is fixed so the board renders the same
 * on the server and in the browser.
 */
const NOW = new Date("2026-09-15T09:00:00Z");

export default function Demo() {
  return (
    <div className="w-[360px]">
      <Wallboard
        tiles={[
          { label: "Checkout", value: "99.4%", status: "nominal" },
          { label: "P95", value: "312", status: "nominal" },
          { label: "Queue", value: "7", status: "nominal" },
          { label: "Errors", value: "4.1%", status: "critical" },
        ]}
        lastArrival={new Date(NOW.getTime() - 20_000)}
        expectedEveryMs={30_000}
        now={NOW}
      />
    </div>
  );
}
What it renders. Identical markup in all three panes, with only the token values changing.

Examples

Screenshots are grouped by product and include the capture date. Dashed boxes highlight this pattern. Notes appear below each screenshot and when you hover over a box.

Grafana

Examples of panel grids, shared selectors, stat tiles, and chart configuration.

Where The Cool Dashboards Live / Chicago L Trains September 10, 2026 Grafana Play (signed out; no version string exposed) sparse · dark · wall
The departure board gives the next train more space than later arrivals and uses large text without hover-only details. Route colors connect the display to the train lines people already know. Green also appears in the On Time status, so the same hue has two meanings in one row. The status label helps distinguish them. Countdown values are useful, but the board also needs a clear indication of feed age.
  • Wallboard mode The next departure receives the largest text and panel.
  • Panel grid Later departures use smaller rows, making their lower priority visible.
  • Semantic status color Green identifies both a route and an on-time status; the words distinguish the meanings.
  • Freshness indicator A countdown shows time until arrival but does not state the feed’s age.
  • Data table The raw feed includes the color value for each route.