$npx -y skills add dembrandt/dembrandt-skills --skill repeated-component-alignmentAny component rendered many times — cards, list rows, table cells, nav items, tiles, KPI widgets, feed entries — is a fixed slot model, not a free-form box. The same slots appear in the same place in every instance and stay aligned across siblings even when text and values vary i
| 1 | # Repeated Component Alignment |
| 2 | |
| 3 | When a component is rendered many times — a card grid, a list, a table, a nav menu, a row of KPI tiles, a feed — it stops being a single box and becomes a **pattern**. The value of a pattern is rhythm: the eye learns the layout once and scans the same slot across every instance (the title row, the price row, the action row). Variable content length breaks that rhythm unless the component is built to absorb it. |
| 4 | |
| 5 | The principle is general. A **card** is the most common case, but the same rule governs list rows, table cells, nav items, tiles, comment entries, dashboard widgets, search results — anything repeated. Treat each as a **fixed slot model**, not a free-form container. |
| 6 | |
| 7 | The goal: **content of any length, instances that look identical.** This is partly content production (write to a target length) and partly layout engineering (build slots that tolerate the variance). This skill covers the layout half and where the two meet. |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## The Slot Model |
| 12 | |
| 13 | Name the slots once and treat them as a contract every instance honours. A product card, as a worked example: |
| 14 | |
| 15 | ``` |
| 16 | ┌──────────────────────┐ |
| 17 | │ [media] │ ← fixed aspect ratio |
| 18 | │ │ |
| 19 | ├──────────────────────┤ |
| 20 | │ ● Badge │ ← optional slot, space reserved |
| 21 | │ Title │ ← clamp to N lines |
| 22 | │ Subtitle / meta │ |
| 23 | │ │ |
| 24 | │ Description text… │ ← flexible slot, grows |
| 25 | │ │ |
| 26 | ├──────────────────────┤ |
| 27 | │ €19.99 Read more│ ← anchor, pinned to bottom |
| 28 | └──────────────────────┘ |
| 29 | ``` |
| 30 | |
| 31 | The same three rules apply to a **list row** (avatar · name · meta · status pinned right), a **KPI tile** (label · big number · trend pinned bottom), or a **search result** (title · url · snippet clamped): |
| 32 | |
| 33 | - **Every slot has a fixed position**, whether or not it has content in a given instance. |
| 34 | - **One slot absorbs the variance** (usually the description/snippet). All others are fixed or clamped. |
| 35 | - **Anchor elements are pinned** — the primary action or value (CTA, price, status, trend) sits at the same position in every instance regardless of how much content is above or beside it. |
| 36 | |
| 37 | --- |
| 38 | |
| 39 | ## Aligning the Anchor |
| 40 | |
| 41 | The single most common defect: text of different lengths makes the anchor element (a "Read more" link, a price, a status chip) float to a different position in each instance. Fix it by letting the flexible slot grow and pushing the anchor to a fixed edge. |
| 42 | |
| 43 | **Vertical layout (cards, tiles) — pin the footer to the bottom:** |
| 44 | |
| 45 | ```css |
| 46 | .item { |
| 47 | display: flex; |
| 48 | flex-direction: column; |
| 49 | height: 100%; /* fill the grid/flex track */ |
| 50 | } |
| 51 | |
| 52 | .item__media { |
| 53 | aspect-ratio: 4 / 3; /* never let media height vary */ |
| 54 | object-fit: cover; |
| 55 | } |
| 56 | |
| 57 | .item__description { |
| 58 | flex: 1; /* the flexible slot absorbs the slack */ |
| 59 | } |
| 60 | |
| 61 | .item__footer { |
| 62 | margin-top: auto; /* pin the anchor (price + CTA) to the bottom */ |
| 63 | } |
| 64 | ``` |
| 65 | |
| 66 | For instances to be **equal height as siblings**, the container track must stretch them — CSS Grid and Flex do this by default (`align-items: stretch`). Then `height: 100%` makes each instance fil |