features.dot-widget-board · Widget board

A features pattern from Dot Matrix, drawn alone below with this design's tokens and sample copy. Used in this design on: home page, section 3. Give the section root data-pattern="features.dot-widget-board". A guide only: DESIGN.md and tokens.json are the source of truth.

Rule
A board of widgets: the intro tile in 1–4 over two rows, a tall halftone picture tile in 5–8, and four numbered feature widgets in a 2×2 across 9–12.
Slots
sectionLabel, title, body, cta, image, features[4..4]
Grid areas
intro 1-4 · picture 5-8 · widgets 9-12 (12-column grid, 1-based inclusive spans)
Tokens
bg #000000 · text #E6E6E6 · heading #FFFFFF · accent #D71921 · accent2 #FFFFFF · Doto / Space Grotesk · radius 24px · space unit 4px (full list in design/tokens.json)

Pattern notes

  • A board of three sizes. Columns 1–4 over two rows: the intro widget (padding 32 × 24 px) with the signal label, title at step 5 (≤14 ch), a muted line and an outlined pill at the bottom. Columns 5–8 over two rows: a tall halftone picture tile filling the cell. Columns 9–12: four surfaceAlt feature widgets in a 2×2, each with a mono index in the link colour at the top and the title (body face, 700) with one muted line at the bottom.

What you get

What we do

Each engagement starts with a two-week audit: interviews with customers, sales and the people who build the product. The brief writes itself after that.

Start a project

01

Positioning

Find the one sentence your best customers already believe.

02

Identity systems

Logos, type and colour that work on a factory floor and a pitch deck.

03

Websites

Fast, editable sites built on the CMS your team already knows.

04

Sales enablement

Decks, one-pagers and demos your reps actually use.

Layout tree (as in design/layout.json)
{
  "pattern": "features.dot-widget-board",
  "kind": "features",
  "grid": {
    "intro": "1-4",
    "picture": "5-8",
    "widgets": "9-12"
  },
  "slots": [
    "sectionLabel",
    "title",
    "body",
    "cta",
    "image",
    "features[4..4]"
  ],
  "tree": {
    "el": "grid",
    "gap": 3,
    "rowGap": 3,
    "align": "stretch",
    "children": [
      {
        "el": "box",
        "span": "1-4",
        "rowSpan": "1-2",
        "bg": "surface",
        "gap": 6,
        "pad": [
          8,
          6
        ],
        "justify": "between",
        "radius": "md",
        "children": [
          {
            "el": "box",
            "alignSelf": "start",
            "dir": "row",
            "pad": [
              1,
              4
            ],
            "align": "center",
            "border": "all",
            "radius": "full",
            "children": [
              {
                "el": "text",
                "color": "text",
                "slot": "sectionLabel",
                "role": "label",
                "font": "mono"
              }
            ]
          },
          {
            "el": "text",
            "slot": "title",
            "role": "h2",
            "step": 5,
            "weight": 700,
            "leading": "tight",
            "maxCh": 14
          },
          {
            "el": "text",
            "color": "textMuted",
            "slot": "body",
            "role": "body",
            "maxCh": 36
          },
          {
            "el": "button",
            "slot": "cta",
            "variant": "secondary",
            "arrow": true
          }
        ]
      },
      {
        "el": "media",
        "span": "5-8",
        "rowSpan": "1-2",
        "slot": "image",
        "aspect": "4/5",
        "treatment": "dot-matrix",
        "fill": true,
        "frame": "rounded"
      },
      {
        "el": "repeat",
        "span": "9-12",
        "rowSpan": "1-2",
        "slot": "features",
        "min": 4,
        "max": 4,
        "layout": "grid",
        "cols": 2,
        "phoneCols": 1,
        "gap": 3,
        "of": {
          "el": "box",
          "bg": "surfaceAlt",
          "gap": 8,
          "pad": [
            5,
            5
          ],
          "justify": "between",
          "radius": "md",
          "children": [
            {
              "el": "text",
              "color": "link",
              "slot": "number",
              "role": "label",
              "font": "mono"
            },
            {
              "el": "box",
              "gap": 1,
              "children": [
                {
                  "el": "text",
                  "slot": "title",
                  "role": "h4",
                  "weight": 700,
                  "font": "body"
                },
                {
                  "el": "text",
                  "color": "textMuted",
                  "slot": "body",
                  "role": "small",
                  "maxCh": 26
                }
              ]
            }
          ]
        }
      }
    ]
  }
}
Component example: reference/features.dot-widget-board.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>features.dot-widget-board — Dot Matrix</title>
<link rel="stylesheet" href="base.css">
<style>
  section { padding: var(--section) 0; }
  .intro { grid-column: 1 / 5; grid-row: 1 / 3; padding: 32px 24px; }
  .intro h2 { font-size: var(--step-5); max-width: 14ch; }
  .pic { grid-column: 5 / 9; grid-row: 1 / 3; border-radius: var(--radius); overflow: hidden; }
  .pic svg { height: 100%; }
  .widgets { grid-column: 9 / 13; grid-row: 1 / 3; display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap); }
  .widgets .widget { padding: 20px; gap: 32px; }
  @media (max-width: 640px) { .widgets { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<section class="wrap grid">
  <div class="widget intro">
    <span class="chip">Why it works</span>
    <h2>Less screen, more signal</h2>
    <p class="muted">Every light on the back means one thing. Calls, timers and charge glow in dots, not notifications.</p>
    <a class="btn secondary sm" href="#">All features →</a>
  </div>
  <div class="pic"><svg class="halftone" viewBox="0 0 280 280" preserveAspectRatio="xMidYMid slice" role="img" aria-label="Halftone of the phone back"></svg></div>
  <div class="widgets">
    <div class="widget alt"><span class="mono idx">01</span><div><p class="item-title">Glyph timer</p><p class="small muted">Counts down in light.</p></div></div>
    <div class="widget alt"><span class="mono idx">02</span><div><p class="item-title">Charge meter</p><p class="small muted">See percent at a glance.</p></div></div>
    <div class="widget alt"><span class="mono idx">03</span><div><p class="item-title">Quiet calls</p><p class="small muted">A pattern per contact.</p></div></div>
    <div class="widget alt"><span class="mono idx">04</span><div><p class="item-title">Flip to dot</p><p class="small muted">Face down, focus on.</p></div></div>
  </div>
</section>
<script>
/* Halftone: a 28-dot square grid; each dot radius follows the tone of a lit sphere; a small moon in the signal red. */
for (const svg of document.querySelectorAll("svg.halftone")) {
  const n = 28, c = 10, out = [];
  for (let i = 0; i < n; i++) for (let j = 0; j < n; j++) {
    const x = (i + .5) * c, y = (j + .5) * c, dx = (x - 130) / 95, dy = (y - 150) / 95, d2 = dx * dx + dy * dy;
    let tone = d2 <= 1 ? .18 + .82 * (1 - Math.max(0, -.55 * dx - .6 * dy + .58 * Math.sqrt(1 - d2))) : .06;
    const mx = (x - 225) / 26, my = (y - 70) / 26, sig = mx * mx + my * my <= 1; if (sig) tone = .85;
    const r = Math.min(.48, tone * .5) * c; if (r < c * .05) continue;
    out.push(`<circle class="${sig ? "sig" : "ink"}" cx="${x}" cy="${y}" r="${r.toFixed(2)}"/>`);
  }
  svg.innerHTML = out.join("");
}
</script>
</body>
</html>

Other patterns in this design

Whole design · Spec bundle (JSON) · On the style page