Skip to content

Icon Layout and Border Discipline

FieldValue
TypeSkill Resource
Source~/.copilot/skills/design/references/icon-and-border.md
DescriptionNot specified

Source Content

Icon Layout and Border Discipline

The binding decision and rationale for every rule below live in the adr skill (decision index) — cite the ADR, do not restate it. This file is the fast code-time reference and worked patterns.

Two recurring bugs this file exists to stop:

  1. Content stacking below an icon. Every icon row is a CSS grid — icon in column one, text in column two, nothing below the icon. See ADR-009.
  2. Nested / doubled borders. One neutral perimeter border per local stack. See ADR-008.

Icon-text layout (ADR-009)

Every component with a leading or trailing icon uses a two- or three-column CSS grid; content never stacks below the icon; action buttons go in the rightmost column. grid-cols-[auto_1fr] for a two-column row (icon + text), grid-cols-[auto_1fr_auto] when a trailing action button joins the row. Use items-start and mt-0.5 on the icon. Never flex-col on an icon row.

{/* WRONG: flex-col lets content wrap below the icon */}
<div className="flex flex-col gap-1">
<FileText className="size-5" aria-hidden="true" />
<span>Card title</span>
</div>
{/* RIGHT: grid keeps icon and text side by side, nothing falls below */}
<div className="grid grid-cols-[auto_1fr] items-start gap-3">
<FileText className="mt-0.5 size-5 shrink-0 text-muted-foreground" aria-hidden="true" />
<span className="font-body text-sm text-foreground">Card title</span>
</div>

Icon row gaps: gap-3 (12px, tighter list items) or gap-4 (16px, standard cards) — see spacing.md.

Border, surface & overlay discipline (ADR-008)

One neutral perimeter border per local stack (border border-border). If the parent already has one, the child does not add another — prefer bg-muted/40 or spacing over a second border. Multiple borders are allowed only for semantic state (selection ring, destructive, drop target) — those are exempt because they carry meaning, not decoration. Overlays escape overflow:hidden and show a caret; Storybook previews never invent borders.

{/* WRONG: parent and child both draw a neutral border — visibly doubled edge */}
<div className="rounded-xl border border-border p-4">
<div className="rounded-lg border border-border p-3"></div>
</div>
{/* RIGHT: child uses a muted background instead of a second border */}
<div className="rounded-xl border border-border p-4">
<div className="rounded-lg bg-muted/40 p-3"></div>
</div>

scripts/lint_design_principles.sh <path> heuristically checks both rules (icon-text rows use CSS grid, no doubled neutral border) — grep/awk-based, no external tool dependency, so it always runs. It is heuristic, not AST-exact: verify by eye, especially on unusual formatting or cleverly split className strings.

Component-level patterns

Real compositions that apply both rules together with tokens and typography (tokens-and-theming.md, typography.md).

Card with icon header

<div className="rounded-xl border border-border bg-card p-4">
<div className="grid grid-cols-[auto_1fr_auto] items-start gap-4">
<FileText className="mt-0.5 size-5 shrink-0 text-muted-foreground" aria-hidden="true" />
<div className="flex flex-col gap-0.5">
<span className="font-body text-[10px] font-semibold uppercase tracking-[0.28em] text-muted-foreground">
Category
</span>
<span className="font-body text-base font-semibold text-foreground">Card title</span>
<span className="font-body text-xs text-muted-foreground">Supporting detail</span>
</div>
<Button variant="outline" size="sm">Action</Button>
</div>
</div>

List item with leading icon

<li className="grid grid-cols-[auto_1fr] items-start gap-3 py-2">
<CheckCircle className="mt-0.5 size-4 shrink-0 text-success" aria-hidden="true" />
<div className="flex flex-col gap-0.5">
<span className="font-body text-sm text-foreground">Item label</span>
<span className="font-body text-xs text-muted-foreground">Optional description</span>
</div>
</li>

These compose ADR-009 (grid), ADR-008 (single border), ADR-003 (font families — typography.md), and ADR-001/ADR-006 (semantic tokens — tokens-and-theming.md).

When I’m unsure, I ask

  • “Is this icon decorative (aria-hidden) or does it convey meaning the text doesn’t?”
  • “Does the parent already have a border? If yes, the child does not add another neutral one.”
  • “Will this surface be printed? If yes, apply print discipline in addition to screen — see css/print-and-legal.md.”
  • “Is this action button part of the icon row? If yes, it goes in a third grid column — not below the content.”

References

  • Decisions (ADRs): the adr skilldecision index. Cite by number; do not restate.
  • scripts/lint_design_principles.sh — ADR-009 icon-grid and ADR-008 single-border heuristic checks.
  • foundation-a11y.md — full accessibility reference for icons, focus, ARIA, and touch targets.