Design system/Components
Card and list row
The card is the base surface: white on the paper background, with a thin border and a quiet shadow. Lists inside cards are built from rows separated by dashed lines.
In codeapp/globals.css (.card, .dashed-top, .media-row)
Card
The .card class defines only the surface: background, border, radius, and shadow. Inner padding comes from utilities (usually p-4), because a list card is edge to edge and a form card has padding.
Upcoming meetings
Three meetings this week; the first is tomorrow at 10.
Rows
Rows inside a card are separated with .dashed-top, a 1px dashed line, and the first one has no line. A dashed line is lighter than a solid one and separates rows without scribbling over the card.
A tappable row gets its hover color only under a pointer that can actually hover, and on touch only while the finger is down. On phones, the buttons at the end of a row each become a 38px chip with their own background so they look like buttons, not decoration.
Row anatomy
- MarkAn icon, avatar, or thumbnail; fixed size, flex-none.
- TextA title and one line of description; flex-1 and min-w-0 so it truncates instead of squeezing.
- MetadataDate, size, or badge, at the end.
- Actionsicon-btn elements; on desktop they may appear on hover, on phones they're always visible.
Rules
Do
Rows in one card, dashed-top with first:border-t-0.
Don't
A separate card for each row; the page turns into scattered tiles.
Do
Row text with truncate or controlled wrapping.
Don't
Text that pushes the actions out of the row.
Do
An action needed on phones is always visible.
Don't
An action that only appears on hover; fingers can't hover.
<div className="card overflow-hidden">
{files.map((f) => (
<div key={f.id} className="media-row dashed-top first:border-t-0 flex items-center gap-3 px-4 py-2.5">
<Icon d={paths.file} size={16} />
<span className="flex-1 min-w-0 truncate">{f.name}</span>
<button className="icon-btn" aria-label={tr("دانلود")}><Icon d={paths.download} size={14} /></button>
</div>
))}
</div>Sizes
radius 16px · border 1px var(--border)0 1px 2px rgba(24,20,14,.03), 0 10px 28px -18px rgba(24,20,14,.1)border-top: 1px dashed var(--border-strong)28 × 28px · radius 8pxNo hover or touch 38px40 × 40pxIn a list row 38px · radius 11px · --mutedgap 8px · flex-wrapName up to two linesNo shadow · border 1px #ddd · break-inside: avoidStates
--mutedOnly @media (hover: hover)--muted:active, everywhere--text on --mutedOnly hover: hover--text on --mutedPhone in a row: --borderbackground, border, color · 0.25s- At rest, icon-btn is --text-tertiary (2.16 to 1 on a light card); every icon-btn must have an aria-label, and on phones it gets a --muted background so it's visible.
- The dashed --border-strong line on a card is 1.32 to 1; it's a divider, not a control boundary, so the 3 to 1 threshold doesn't apply.
Do
A tappable row is a full <button> or <Link>.
Don't
A div with onClick; you can't reach it with the keyboard.
Do
The first row has no dashed line: first:border-t-0 or a container rule.
Don't
A dashed line stuck to the card's own top edge.
Related pages