Icon Layout and Border Discipline
| Field | Value |
|---|---|
| Type | Skill Resource |
| Source | ~/.copilot/skills/design/references/icon-and-border.md |
| Description | Not 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:
- 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.
- 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 skill — decision 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.