css-layout-patterns
GitHub提供 CSS 和 Tailwind v4 布局模式指南,解决 SCSS @apply 配置、Flex 高度链断裂、页面滚动异常及暗色主题样式问题。
Trigger Scenarios
Install
npx skills add ncoevoet/facet --skill css-layout-patterns -g -y
SKILL.md
Frontmatter
{
"name": "css-layout-patterns",
"description": "CSS and Tailwind v4 layout patterns. Use for @apply not working in SCSS, @reference tailwindcss, flex height chain, overflow not working, flex:1 in flex-row, the page scrolling instead of the container, dark theme styling, responsive\/breakpoint work, min-height issues, or flex layout debugging. Do NOT use for signals\/effects\/computed, Python or backend code, tests\/specs, or API work."
}
CSS & Layout Patterns
Theme
Facet uses a dark theme with Angular Material and Tailwind v4:
- Background:
#0b0b0b(near-black) - Primary: Material green palette
- Accent: Cyan highlights
- All components should assume dark background. Use light text colors, subtle borders (
border-white/10), and translucent hover states (hover:bg-white/[0.08]).
Tailwind v4: @apply in SCSS (CRITICAL)
All component SCSS files using @apply MUST include @reference as the first line:
@reference "tailwindcss";
.my-class {
@apply flex items-center gap-2;
}
Without @reference "tailwindcss";, @apply directives won't resolve in Tailwind v4.
Common Errors
@applysilently ignored: No error, styles just don't apply -- always check for missing@reference- Order matters:
@referencemust come BEFORE any@applyusage - Per-file requirement: Each
.scssfile needs its own@reference, not just the root
Checking a Component
# Find SCSS files using @apply without @reference
grep -rn "@apply" client/src/ --include="*.scss" -l | xargs grep -L "@reference"
Tailwind-First Rule (CRITICAL)
Never create custom SCSS classes when a standard Tailwind utility exists:
| Bad (custom SCSS) | Good (Tailwind) |
|---|---|
.detail-section { margin-bottom: 24px } |
mb-6 |
.clickable-row { cursor: pointer; &:hover { background: rgba(255,255,255,0.08) } } |
cursor-pointer hover:bg-white/[0.08] |
.text-center { text-align: center } |
text-center |
.full-width { width: 100% } |
w-full |
h3 { margin-bottom: 16px; font-weight: 500 } |
mb-4 font-medium on <h3> |
When touching a component's template/SCSS:
- Check SCSS for custom classes replaceable by Tailwind
- Flag hardcoded pixel values -- use Tailwind spacing (
mb-4,p-2,gap-2) - Keep SCSS empty if all styling is achievable via Tailwind classes
Flex Height Chain Pattern
Problem: Page scrolls instead of container scrolling
Every element from viewport to scroll container must maintain the flex chain:
viewport -> html -> body -> app-root -> main -> content -> scroll-container
Each element needs:
display: flex;
flex-direction: column;
flex: 1;
min-height: 0; /* CRITICAL - allows flex children to shrink */
overflow: hidden; /* or auto on the scroll container */
flex-row vs flex-col Height Control
| Parent Direction | Child Height Control | Why |
|---|---|---|
flex-direction: column |
flex: 1 |
Main axis is vertical -> flex controls height |
flex-direction: row |
height: 100% |
Main axis is horizontal -> flex controls width only |
Common mistake: flex: 1 in a flex-row parent does NOT constrain height!
Columns Don't Fill Width
Add width: 100% to elements in flex-row containers.
Finding Chain Breaks
Look for elements where clientHeight > parentHeight:
{ name: '.gallery-grid', height: 3200, parentHeight: 708, overflows: true }
// ^^^^ ^^^^ ^^^^^^^^^^^^
// This element needs height constraint
Tailwind Group-Scoped Hover
Use named groups to scope hover effects to specific areas (e.g., image-only overlay, not the entire card):
<!-- group/card wraps the whole card, group/img wraps just the image -->
<div class="group/card rounded-lg overflow-hidden">
<div class="group/img relative">
<img src="..." class="w-full" />
<!-- This overlay only appears when hovering the image area -->
<div class="absolute inset-0 bg-black/50 opacity-0 group-hover/img:opacity-100 transition-opacity">
...action buttons...
</div>
</div>
<!-- Tags/details below — NOT affected by image hover -->
<div class="p-2">tags, scores, etc.</div>
</div>
mat-icon Centering in Small Containers
mat-icon has default line-height that misaligns in small round buttons. Fix:
<!-- Container: use inline-flex, not flex -->
<button class="w-7 h-7 rounded-full inline-flex items-center justify-center">
<!-- Icon: override size AND line-height -->
<mat-icon class="!text-base !w-4 !h-4 !leading-4">star</mat-icon>
</button>
Common Mistake Reference
| Symptom | Cause | Fix |
|---|---|---|
| Page scrolls instead of container | Missing height constraint in chain | Add flex: 1 or height: 100% |
| Content clipped unexpectedly | Missing min-height: 0 |
Add min-height: 0 |
flex: 1 doesn't constrain height |
Parent is flex-row | Use height: 100% instead |
| Columns don't fill width | Missing width constraint | Add width: 100% |
@apply doesn't work |
Missing @reference |
Add @reference "tailwindcss"; first line |
| Overflow not scrolling | Missing min-height: 0 on parent |
Add to all flex ancestors |
| Dark theme text invisible | Using default dark text on dark bg | Use text-white or text-white/70 |
Diagnostic Script (Chrome DevTools MCP)
// Quick page-level check
() => {
const body = document.body;
const scrollContainer = document.querySelector('.scroll-container');
return {
viewport: { w: window.innerWidth, h: window.innerHeight },
pageScrolls: body.scrollHeight > body.clientHeight,
containerScrolls: scrollContainer ?
scrollContainer.scrollHeight > scrollContainer.clientHeight : 'not found',
issue: body.scrollHeight > body.clientHeight ?
'Page scrolling - check flex height chain' : 'Container scrolling - OK'
};
}
// Detailed chain trace -- find where chain breaks
() => {
const selectors = ['app-root', 'main', '.content', '.gallery-grid', '.scroll-container'];
return selectors.map(sel => {
const el = document.querySelector(sel);
if (!el) return { name: sel, error: 'not found' };
const style = getComputedStyle(el);
return {
name: sel,
height: el.clientHeight,
parentHeight: el.parentElement?.clientHeight || 0,
overflows: el.clientHeight > (el.parentElement?.clientHeight || 0),
display: style.display,
flexDirection: style.flexDirection,
flex: style.flex,
minHeight: style.minHeight,
overflow: style.overflow
};
});
}
Debugging Steps
- Run
mcp__chrome-devtools__evaluate_scriptwith the diagnostic script - Find which element breaks the chain (child height > parent height)
- Check parent's
flex-directionto determine fix:- Column parent -> add
flex: 1; min-height: 0;to child - Row parent -> add
height: 100%; min-height: 0;to child
- Column parent -> add
- Add
overflow: hiddento containers,overflow-y: autoto scroll target - Verify at multiple breakpoints (mobile, tablet, desktop)
Responsive Testing Breakpoints
// Standard Tailwind v4 breakpoints
// sm: 640px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px
// Test at common sizes via Chrome DevTools MCP:
mcp__chrome-devtools__resize_page({ width: 1920, height: 1080 }); // Desktop
mcp__chrome-devtools__resize_page({ width: 1280, height: 800 }); // Laptop
mcp__chrome-devtools__resize_page({ width: 768, height: 1024 }); // Tablet
mcp__chrome-devtools__resize_page({ width: 375, height: 667 }); // Mobile
Examples
User says: "@apply isn't working in my component SCSS"
- Check the SCSS file for
@reference "tailwindcss";as the first line - If missing, add it before any
@applyusage - Verify each SCSS file has its own
@reference(per-file requirement)
User says: "The page scrolls instead of just the gallery grid"
- Run the flex height chain diagnostic script via Chrome DevTools MCP
- Find the element where
overflows: true-- that's where the chain breaks - Check parent's
flex-direction: column -> addflex: 1; min-height: 0;; row -> addheight: 100%; min-height: 0; - Add
overflow-y: autoon the intended scroll container - Verify at desktop and mobile breakpoints
User says: "Should I use custom SCSS or Tailwind classes?"
- Always prefer Tailwind utility classes over custom SCSS
- Check if a standard Tailwind class exists for the desired style
- Only write custom SCSS when Tailwind cannot express the pattern (e.g., complex selectors, ::ng-deep)
Version History
- 284489b Current 2026-08-20 08:24


