review-bench/davidortinau_sentencestudio/blazor-activity-layout-shell
Claude Code and agent skill by review-bench/davidortinau_sentencestudio
- community
Checking that the SKILL.md file is still in the repository.
Canonical Blazor activity-page layout shell for SentenceStudio webapp. Copy VocabQuiz verbatim; only swap inner content.
Install
npx vskill@latest install review-bench/davidortinau_sentencestudio/blazor-activity-layout-shellSKILL.md
The same SKILL.md was published earlier as davidortinau/sentencestudio/blazor-activity-layout-shell.
Context
When building a new activity page in src/SentenceStudio.UI/Pages/*.razor (Blazor Hybrid webapp), there is exactly one canonical layout shell. Copy it verbatim from `VocabQuiz.razor`. Only swap inner content. Do not invent your own outer structure.
This rule was earned by failing it three times. Across publishes #7, #8, and #9, NumberDrill kept getting rebuilt with subtly-different outer markup; each rebuild produced a different visual defect (footer not pinned, card wrapper around content, empty input-bar div rendering as chrome strip). The fix every time was "make it look like VocabQuiz." There is no good reason to deviate.
Apply this skill when: authoring a new activity page, refactoring an existing activity page's outer shell, or diagnosing why an activity page's footer/chrome looks different from VocabQuiz.
Patterns
Canonical shell — copy this verbatim
Reference: `src/SentenceStudio.UI/Pages/VocabQuiz.razor` lines 8–27, 382.
CSS contract (do not change)
src/SentenceStudio.UI/wwwroot/css/app.css lines ~1381–1424 define the contract:
| Class | Behavior | |
|---|---|---|
.activity-page-wrapper | display: flex; flex-direction: column; height: calc(100% + 2rem) — fills the main content area edge-to-edge | |
.activity-content | flex: 1; overflow-y: auto; min-height: 0 — the ONLY scrolling region | |
.activity-footer | flex-shrink: 0; border-top; padding-bottom: safe-area-inset-bottom — pinned to bottom | |
.activity-input-bar | flex-shrink: 0; border-top; padding-bottom: safe-area-inset-bottom — also pinned, same chrome |
Both `.activity-footer` and `.activity-input-bar` paint visible chrome unconditionally. The flex-shrink:0 + border-top + safe-area-inset-bottom triad is what pins them to the bottom edge correctly.
Decision tree for new activity pages
- Will the page have a primary action area (Submit / Next / etc.)? → use
.activity-footer. - Will the page have a free-text input or composer? → use
.activity-input-bar. - Need both? → emit both, in that order, both inside
.activity-page-wrapper. - Need neither? → omit both. Do not emit empty placeholders.
Anti-Patterns
❌ #1 — Empty <div class="activity-input-bar"> left behind
Observed: Publish #9 (NumberDrill).
The class paints border-top + padding + safe-area-inset-bottom regardless of children. On iPhone with home-indicator, that's a ~50px visible strip below your footer.
Fix: omit the div entirely. Do not leave it "for symmetry."
❌ #2 — Outer card wrapper around activity content
Observed: Publishes #7 and #8 (NumberDrill).
A card wrapper breaks the flex-column chain on .activity-page-wrapper → .activity-content (flex: 1) → .activity-footer (flex-shrink: 0). The footer un-pins from the bottom edge because the card now constrains its sizing.
Fix: no card wrapper. Use .card.card-ss ONLY for setup screens / inline cards INSIDE .activity-content, never around the whole shell.
❌ #3 — Footer not pinned / safe-area mishandled
Observed: Publish #7 (NumberDrill).
Causes:
- Footer placed inside
.activity-contentinstead of as a sibling. - Footer using a different class (e.g., a hand-rolled bottom bar) that doesn't include
flex-shrink: 0or safe-area padding. - Footer wrapped in another element that forces it back into scroll flow.
Fix: footer is a sibling of .activity-content inside .activity-page-wrapper. Use the canonical .activity-footer class. Don't reinvent.
❌ #4 — Inventing a new wrapper class to "match VocabQuiz"
If you find yourself naming a new class like .numberdrill-shell or .my-activity-wrapper, stop. The contract is shared CSS. Diverging means future activity pages won't share the same chrome behavior across iOS / Android / desktop.
Examples
Reference (canonical)
`src/SentenceStudio.UI/Pages/VocabQuiz.razor`
Recently corrected to match canonical
`src/SentenceStudio.UI/Pages/NumberDrill.razor` — after Publish #9 fix, lines 26–29 and 465 mirror the VocabQuiz shell.
How to verify parity
Build and run the new page on iPhone. Take a screenshot. Take a screenshot of VocabQuiz on the same device. Compare side-by-side using the `maui-visual-review` skill — focus on the strip immediately below the footer (chrome height + safe-area padding should be identical).
References
- Decision log:
.squad/decisions.md— Publish #7, #8, #9 entries (2026-05-07) - CSS contract:
src/SentenceStudio.UI/wwwroot/css/app.csslines 1381–1424 (with anti-pattern #1 warning comment added 2026-05-07) - Companion skill:
~/.copilot/skills/maui-visual-review/SKILL.md— for side-by-side parity checks - Index:
.squad/skills/available-copilot-skills/SKILL.md
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryChecking.squad/skills/blazor-activity-layout-shell/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/review-bench/davidortinau_sentencestudio/blazor-activity-layout-shell)