layout-skill
Layer A layout-mechanics reference. Stacks on any style skill when the screen is an app shell, dashboard, settings, list-detail, mail/inbox, or any layout with fixed regions plus a scrolling body — or when a layout breaks under long, empty, or unbroken content. Owns spatial structure and scroll ownership; owns zero visual taste. Load it alongside a style skill; it does not replace one.
git clone --depth 1 https://github.com/code-yeongyu/oh-my-openagent /tmp/layout-skill && cp -r /tmp/layout-skill/packages/shared-skills/skills/frontend/references/design/layout- ~/.claude/skills/layout-skilllayout-skill.md
# Layout Mechanics
Style skills decide how a surface looks. This file decides how it is *built to hold content* — what scrolls, what stays fixed, how regions shrink, and which layouts survive real data. It stacks on top of any Layer A style skill (`taste-skill`, `minimalist-skill`, `soft-skill`, `brutalist-skill`, `gpt-tasteskill`) and any Layer B brand. It adds ZERO visual direction — color, type, shadow, radius, and motion still come from the style skill and `DESIGN.md`.
Load this when the screen is an **application shell**, not a scroll-the-whole-document marketing page: dashboards, settings, list-detail, mail/inbox, command surfaces, split panes, sidebars — or when a page that looked fine breaks the moment content gets long, empty, or unbroken.
## 1. Scroll ownership — decide this BEFORE writing layout CSS
The single most common agent-built layout bug is an app shell where the wrong thing scrolls: the whole page scrolls when only a panel should, two panels fight over the scrollbar, or a "fixed" header scrolls away. Prevent it by naming ownership up front, in `DESIGN.md` and in the component:
- **What scrolls?** Name the ONE element that owns vertical scroll for this region.
- **What stays fixed?** Header, sidebar, footer, toolbar — list them.
- **Where is height determined?** The scroll container needs a bounded height ancestor, or it will grow instead of scroll.
Rules:
- **One scroll container per region unless each extra one has a named job.** Nested scrollbars with no declared responsibility are a defect — the user loses track of what a wheel/trackpad gesture will move.
- Full-height shells are bounded by `100dvh`/`100dvb` (dynamic viewport units), never `100vh` — `vh` causes the iOS Safari address-bar jump.
- A sticky element (`position: sticky`) follows document scroll; a fixed shell region (grid row/column with `overflow: auto` body) owns its own scroll. Do not mix the two models in one region without reason.
## 2. The two CSS contracts agents get wrong
These two are worth memorizing verbatim. They fail *silently* — the layout looks right until content arrives.
**Bounded scroll shell** (fixed header/footer, scrolling body):
```css
.shell {
display: grid;
grid-template-rows: auto minmax(0, 1fr) auto; /* header / body / footer */
max-block-size: 100dvb;
}
.shell__body {
min-block-size: 0; /* WITHOUT THIS the grid child refuses to shrink and overflow never fires */
overflow: auto;
}
```
The `min-block-size: 0` (or `min-height: 0`) on the scroll child is the fix for "why won't my panel scroll — it just pushes the footer off-screen." A grid/flex child's default `min-*-size: auto` refuses to shrink below its content. The same applies to a flex column: the scrollable child needs `min-height: 0`.
**Overflow-safe intrinsic grid** (repeat as many columns as fit, no media queries):
```css
.grid {
display: grid;
gap: var(--gap);
grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
}
```
The inner `min(16rem, 100%)` is load-bearing: plain `minmax(16rem, 1fr)` forces a 16rem track even when the container is 12rem wide, causing horizontal overflow on narrow screens. `min(…, 100%)` lets the track collapse below its floor when space is genuinely tight. Use `auto-fit` to stretch the last row, `auto-fill` to keep empty tracks.
## 3. Named layout primitives (shared vocabulary)
Build shells from these named primitives instead of ad-hoc fl/grid. Naming them makes `DESIGN.md` Section 5 and subagent handoffs precise ("wrap it in a `sidebar` with a 20rem aside" beats "put it on the left, roughly"). Lineage: Every Layout + web.dev one-line layouts.
| Primitive | Spatial job | Core mechanic |
|---|---|---|
| **stack** | Vertical rhythm between siblings | flex column + `gap`, or `> * + *` margin |
| **cluster** | Wrapping row of items (tags, actions) | `flex-wrap: wrap` + `gap`; wraps before overflow |
| **content-limiter** | Readable prose measure inside a fluid parent | `max-inline-size: ~65ch; margin-inline: auto` |
| **sidebar** | Narrow aside + fluid main, wraps when tight | flex; aside fixed basis, main `min-inline-size` floor, both `flex-wrap` |
| **switcher** | N equal regions: row when roomy, stack when tight, NO breakpoint | flex + `min()` basis so it flips at a content threshold |
| **cover** | Centered region between optional header/footer, min viewport tall | grid rows `auto 1fr auto`, `min-block-size: 100dvb` |
| **frame** | Media held to an aspect ratio | `aspect-ratio` + `object-fit: cover` |
| **reel** | Row that scrolls horizontally instead of wrapping | `overflow-inline: auto` + `scroll-snap`; declare keyboard access |
| **imposter** | Overlay centered over a parent without changing document order | `position: absolute` + translate; do not use to reorder focus |
| **overlay-stack** | Several layers intentionally in one cell | single grid cell, all children `grid-area: 1/1` |
| **scroll-body-shell** | Fixed shell regions, only the body scrolls | §2 bounded scroll shell |
| **fixed-sidenav-shell** | Side nav stays put, main scrolls | grid columns `auto 1fr`, main is the scroll owner |
| **list-detail** | Explorable list beside its detail region | two-column grid, each pane's scroll ownership named |
| **sticky-aside** | Support content stays visible during a long read | `position: sticky; top:` on the aside, document scroll |
## 4. Container-local vs viewport-level responsiveness
Ask **what the layout is responding to** before reaching for a breakpoint:
- The component should adapt to *its own available width* (a card that's wide in main but narrow in a rail) → wrap it in `container-type: inline-size` and use `@container`. This is correct far more often than agents assume, because a component rarely knows the viewport it lands in.
- The *page frame itself* changes (sidebar collapses, columns drop) → `@media`.
Prefer intrinsic adaptation (`switcher`, `sidebar`, intrinsic grid, `clamp()`) over any query — the best breakpoint is oftenCompare HEAD with the latest published npm versions and list all unpublished changes by release layer. Triggers: unpublished changes, changelog, what changed, whats new.
Read-only GitHub triage for issues AND PRs. 1 item = 1 background task (category: quick). Analyzes all open items and writes evidence-backed reports to /tmp/{datetime}/. Every claim requires a GitHub permalink as proof. NEVER takes any action on GitHub - no comments, no merges, no closes, no labels. Reports only. Triggers: 'triage', 'triage issues', 'triage PRs', 'github triage'.
Adversarial multi-agent planning skill for omo-senpi. Self-orchestrates a 5-member hostile team (categories unspecified-low, unspecified-high, deep, ultrabrain, artistry) via the native lead team tools for ruthless cross-critique debate, distills only the insights that survive the attacks, then MANDATORILY hands the distilled bundle to a planner task (load_skills ulw-plan) for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', 'adversarial plan', 'hostile planning', 'cross-critique plan', '하이퍼플랜', '적대적 계획', '교차 비평'.
Easter egg command - about oh-my-opencode. Triggers: omomomo, about, easter egg.
QA opencode itself, per case: verify the CLI/terminal (opencode run, db, serve, export), prove a specific plugin hook/action/event fired via the SSE event stream, smoke-test the TUI under tmux, and investigate sessions in opencode's SQLite DB by id, title/name, or message text. Ships tested helper scripts (each with a --self-test) plus per-domain references. Use whenever someone wants to QA, smoke-test, verify, or debug opencode's CLI, HTTP server, plugin hooks/events, or TUI, or to find/inspect opencode sessions in the database. Triggers: opencode qa, qa opencode, test opencode, verify opencode hook, opencode session db, find opencode session by id/name/text, opencode tui test, opencode server health, opencode event stream.
Nuclear-grade 16-agent pre-publish release gate. Runs /get-unpublished-changes to detect all changes since last npm release, spawns up to 10 ultrabrain agents for deep per-change analysis, invokes /review-work (5 agents) for holistic review, and 1 oracle for overall release synthesis. Runs ONLY when the user explicitly asks for a pre-publish review — a plain publish/release request MUST NOT trigger this; /publish ships directly. Triggers: 'pre-publish review', 'review before publish', 'release review', 'pre-release review', 'ready to publish?', 'can I publish?', 'pre-publish', 'safe to publish', 'publishing review', 'pre-publish check'.
Publish oh-my-opencode to npm by triggering the GitHub Actions publish workflow and verifying its artifacts. Ship-only: never runs pre-publish-review or re-reviews merged code unless the user explicitly asks. Argument: <patch|minor|major|explicit-semver>. Triggers: publish, release, deploy, npm publish.
Remove unused code from this project with ultrawork mode, LSP-verified safety, atomic commits. Triggers: remove dead code, dead code, cleanup, remove unused.