Write, configure, migrate, and debug Tailwind CSS across utility markup, themes, variants, and builds.
Browser
CSS
Diagnose CSS mechanics and produce targeted fixes or complete stylesheets for the chosen stack.
What it does
Diagnoses CSS failures by isolating the issue and naming the underlying mechanism, such as stacking contexts, flex sizing, overflow, or margin collapse, before proposing a fix. Covers layouts, responsive components, overlays, theming, performance, accessibility, browser compatibility, print, RTL, and modern CSS. Produces contextual diffs or complete stylesheets in plain CSS, Sass, Tailwind, or CSS-in-JS, based on saved preferences.
When to use it
- Debugging broken flexbox or grid layouts
- Fixing overlays trapped behind page content
- Reducing layout shift and animation jank
- Modernizing legacy styles for multiple browsers
The skill document
User preferences and memory live in ~/Clawic/data/css/ (see setup.md on first use, memory-template.md for the file format). If you have data at an old location (~/css/ or ~/clawic/css/), move it to ~/Clawic/data/css/.
Configuration
User-dependent variables. Defaults apply until the user states a preference; store them in ~/Clawic/data/css/config.yaml.
| Variable | Type | Default | Effect |
|---|---|---|---|
| authoring_mode | plain-css | sass | tailwind | css-in-js | plain-css | Syntax of every emitted example, and which advice in architecture.md applies (layers and nesting vs utility ordering vs zero-runtime extraction) |
| browser_support | evergreen | widely-available | legacy | evergreen | Gates which features ship bare vs behind @supports: legacy also adds vendor-prefixed fallbacks and blocks the Chromium-first features flagged in compatibility.md |
| naming_convention | bem | utility | css-modules | none | none | Class names in examples and the specificity ceiling enforced in reviews (architecture.md — Keeping It Clean) |
| rem_base | number px (10-16) | 16 | Every px→rem conversion, including the fluid-type derivation in responsive.md (rem term = px ÷ rem_base) |
| a11y_target | aa | aaa | aa | Which row of Accessibility Floor gates output: AA = 4.5:1 body / 24×24 targets, AAA = 7:1 body / 44×44 targets |
| explanation_depth | mechanism | fix-only | mechanism | How much of the why ships with each answer: mechanism names the cause (stacking context, flex sizing) before the declaration; fix-only emits the declaration plus one line |
| output_shape | diff | full-sheet | diff | Emitted code: diff = only the changed declarations in context; full-sheet = the complete rewritten stylesheet or component block |
Preference areas to record as the user reveals them:
- tooling — build chain (PostCSS, Lightning CSS, bundler), formatter and lint rules, whether native nesting is allowed
- conventions — spacing and type scale in use, token naming, file-per-component vs grouped sheets
- output — beyond
explanation_depthandoutput_shape: comment density in emitted CSS, longhand vs shorthand, whether fallbacks and@supportsbranches are shown or assumed, how much of the trade-off to state before choosing - platform — target surfaces (app, marketing site, HTML email, print/PDF), device mix, whether RTL or CJK is in scope
- risk posture — appetite for Chromium-first features, tolerance for
!importantin third-party overrides, how loudly to flag accessibility regressions - constraints — banned techniques (CSS-in-JS, utility classes,
@import), inherited legacy stylesheets that must keep working
When To Use
- Debugging layout: z-index that won't apply, overflow, dead
height: 100%, brokenposition: sticky, mystery horizontal scroll - Building components and starting stylesheets: reset and base layers, flexbox/grid patterns, centering, forms, tables, overlays, responsive behavior without media-query sprawl
- Production hardening: layout shift, animation jank, font loading, print output, the accessibility floor
- Replacing JS or preprocessor hacks with native CSS (
:has(),@layer,@scope, container queries, scroll snap, anchor positioning) - Cross-engine work: a rule that lands in Chrome and not Safari, RTL mirroring, HTML email constraints
- Not for visual design decisions (palettes, spacing scales, typography choice) — this skill covers mechanics, not taste
Quick Reference
| Situation | Play |
|---|---|
| z-index ignored despite a huge value | Stacking Contexts below — find the context root; never just bump the number |
| Flex item overflows / text won't truncate | min-width: 0 on the flex child (default min-width is min-content) |
| The rule is written but nothing changes | debugging.md — symptom→cause chains, starting with "is it even matching?" |
| Breaks with real content, sticky dead, footer floats, margin leaks | layout.md |
| Component must adapt to its container; fluid type; mobile viewport bugs | responsive.md |
Specificity fight, @layer, :has(), custom-property gotchas | selectors.md |
| Jank, layout shift, slow paint, font flash | performance.md |
| Transition never fires, enter/exit animation, view transitions, reduced motion | animations.md |
| Line-height inheritance, measure, variable fonts, hyphenation, tabular numbers | typography.md |
oklch vs hsl, color-mix(), gradient banding, wide gamut | color.md |
| Dark mode, design tokens, multi-brand, theme flash on load | theming.md |
| Inputs, selects, checkboxes, validation states, autofill styling | forms.md |
| Modal behind the header, dropdown clipped, tooltip placement | overlays.md |
| Scroll snap, custom scrollbars, anchor link lands under the sticky header | scrolling.md |
| Data table: sticky header, responsive behavior, column sizing | tables.md |
Shadows, filters, masks, clip-path, blend modes, 3D transforms | effects.md |
| Starting a stylesheet: what goes in the reset and base layers, in what order | reset.md |
| Sheet organization, layer strategy, nesting, Sass migration, shadow DOM | architecture.md |
| Works in Chrome, breaks in Safari or Firefox; HTML email | compatibility.md |
| Print or PDF output wrong | print.md |
| RTL mirroring, CJK line breaking, logical properties, text expansion | internationalization.md |
| Anything else CSS | Core Rules below, then debugging.md to name the mechanism |
Core Rules
- Diagnose before adding CSS: reproduce, isolate in DevTools, name the mechanism (stacking context, flex sizing algorithm, margin collapse). A property added without a named mechanism is the next bug.
- Animate only
transformandopacity— the only common properties that skip layout and paint. Frame budget = 1000ms / 60fps ≈ 16.7ms for style, paint, and your JS combined; one layout-triggering animation spends it alone. - One centering default: parent
display: grid; place-content: center. Escape hatch:position: absolute; inset: 0; margin: autowhen the child must overlay (needs a resolvable size, e.g.width: fit-content). - Never bare viewport units for text.
font-size: clamp(1rem, 0.77rem + 0.91vw, 1.5rem)— the rem term is what keeps browser zoom and user font-size working; pure-vw text fails WCAG 1.4.4 (resize to 200%). Derivation of the numbers:responsive.md. - Size intrinsically first (
min(),clamp(),fit-content,auto-fitgrids), media queries second, container queries when one component lives at different widths. !importantin component code is a debt marker. Order wars belong in@layer— unlayered author styles beat all layered ones regardless of specificity (selectors.md).- Overlays belong in the top layer, not high in the z-index scale.
.showModal()andpopoverescape every stacking context and everyoverflow: hiddenancestor; a z-index arms race means the wrong mechanism is in use (overlays.md). - A component styles its inside, never its outside: no
margin, nowidth, nopositionon the component root — the parent layout owns placement. Components that set their own outer geometry break on the second reuse and get "fixed" with!important.
Stacking Contexts
The single most common CSS debugging failure: raising z-index on an element trapped inside a context.
- Context creators (memorize): positioned element with z-index, flex/grid child with z-index,
opacity < 1,transform,filter,backdrop-filter,will-change,contain: layoutorpaint,position: fixed/sticky,isolation: isolate. - Inside a context, z-index competes only among siblings of that context. A child's
z-index: 9999never escapes its parent'sz-index: 1. - Debug procedure, in order: (1) walk up from the losing element to its first context-creating ancestor; (2) same for the winning element; (3) compare those two ancestors — that comparison decides the paint order; (4) fix z-index there, or delete the accidental trigger (usually a leftover
transformoropacityfrom an animation). isolation: isolatecreates a context with zero visual side effects — use it to cap a component's internal z-index so it can't leak out.transform,filter, andwill-changealso make the element the containing block forposition: fixeddescendants — the fixed element behaves as absolute with no warning. Same walk-up diagnosis.- Elements promoted to the top layer (modal ``,
popover) ignore all of the above: they paint above the page and above each other in open order (rule 7).
Flexbox and Grid Mental Model
flex: 1=1 1 0%: ALL space divided equally.flex: auto=1 1 auto: only leftover space divided, so larger content keeps a larger track. Choose per intent; equal columns need basis 0.- Flex children default to
min-width: min-content— the root cause of both overflow and un-truncatable text. Release withmin-width: 0(oroverflow: hidden). Column direction: same story withmin-height. 1frmeansminmax(auto, 1fr): the track refuses to shrink below its content.grid-template-columns: 1fr 1fris NOT 50/50 with unequal content — writeminmax(0, 1fr)for true halves.auto-fitcollapses empty tracks (remaining cards stretch);auto-fillkeeps them (cards hold max width). Card grid default:repeat(auto-fit, minmax(min(250px, 100%), 1fr))— the innermin()prevents overflow on viewports under 250px.gapnever collapses; margins collapse (vertical, block layout only, including parent-child bleed-through). Prefer gap and treat margin collapse as legacy behavior to route around (layout.md).margin: autoon a flex/grid child absorbs free space:margin-inline-start: autoon the last nav item is the entire "push right" pattern.- Grid when the parent decides both axes (page scaffolding, card grids, overlapping layers via named areas); flex when the children decide and simply wrap (toolbars, tag lists, button rows).
Modern CSS Worth Using
Compatibility floor: everything here is in all three engines unless marked; version-sensitive items are dated in compatibility.md.
:has()— parent and previous-sibling selection; kills a whole class of state-mirroring JS (selectors.mdfor patterns and cost).@starting-style+transition-behavior: allow-discrete— transition fromdisplay: none; replaces enter-animation JS (all engines since mid-2024).light-dark()+color-scheme— one declaration per token instead of a duplicated dark block (all engines since 2024;theming.md).text-wrap: balanceon headings — engines skip long blocks (Chromium caps at 6 lines), so it is safe to apply to all headings.scrollbar-gutter: stableon scroll containers — reserves the gutter, no shift when the scrollbar appears.overscroll-behavior: containon modals and drawers — stops scroll chaining into the page.scroll-snap-type+scroll-snap-align— carousels without JS (scrolling.md).aspect-ratio— reserve media space before load (layout-shift numbers:performance.md).accent-color— form controls on brand without rebuilding them (forms.md).@scopeand native nesting — component boundaries without naming conventions; specificity traps inarchitecture.md.- Individual transforms (
translate,rotate,scale) — compose in a fixed order and animate independently, no more one-property transform collisions. - Anchor positioning (
anchor-name,position-area) — tethered popovers without a positioning library; still needs a fallback, seeoverlays.md.
Accessibility Floor
Canonical home for these numbers — other files point here.
- Contrast (WCAG 2.2 AA): 4.5:1 body text; 3:1 for large text (≥24px, or ≥18.66px bold) and for UI components and focus indicators (1.4.3, 1.4.11). AAA raises body text to 7:1 and large text to 4.5:1 (1.4.6) — applies when
a11y_target: aaa. - Touch targets: ≥24×24 CSS px is the AA minimum (2.5.8); 44×44 matches Apple HIG and WCAG AAA (2.5.5) — use 44 for primary mobile actions and whenever
a11y_target: aaa. - Text survives 200% zoom (1.4.4): rem-based sizes plus the clamp rule (Core Rule 4).
- Motion is opt-in: wrap animation in
@media (prefers-reduced-motion: no-preference)rather than overriding after the fact. - Style
:focus-visible; neveroutline: nonewithout a replacement in the same rule. @media (forced-colors: active): system colors replace yours — check borders and focus still exist there.- Dark mode:
@media (prefers-color-scheme: dark)pluscolor-scheme: light darkso form controls and scrollbars follow. - Content reflows to a 320px-wide viewport without two-axis scrolling (1.4.10) — the practical floor for "does it work zoomed on a phone".
- Three different hides, chosen deliberately:
display: none/visibility: hiddenremove content from the accessibility tree;aria-hiddenhides from assistive tech while staying visible; screen-reader-only text needs the clip pattern —position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap.
Output Gates
Before shipping a stylesheet or component styles, verify:
- Hostile content survives: longest word (URL, German compound), empty state, missing image, 3-line title where you designed one line?
- 200% zoom and a 320px viewport reflow without horizontal scroll?
- Every interactive element has a visible
:focus-visiblestyle and meets the contrast and target-size rows above? - Animation wrapped in
prefers-reduced-motion: no-preference, and onlytransform/opacityin the frame loop? - Media and embeds have reserved space (
aspect-ratioor width/height attributes)? - No
!importantoutside a third-party override, and no new selector above single-class specificity? - Dark mode checked if the project themes, and RTL checked if
internationalization.mdis in scope?
Traps
| Trap | Why it fails | Do instead |
|---|---|---|
| Bumping z-index to 9999 | Element is inside a stacking context; only the context root competes outside | Walk-up procedure (→ Stacking Contexts) |
| Animating height/top/left/margin | Layout runs every frame and blows the 16.7ms budget (Core Rule 2) | transform; for height-to-auto, the grid-rows trick (→ layout.md) |
overflow: hidden to kill a stray scrollbar | Hides the symptom and creates a scroll container: breaks sticky descendants, clips shadows and focus rings | Find the overflowing element first; when it cannot be removed, html { overflow-x: clip } — clip creates no scroll container, so sticky survives (→ layout.md) |
var(--x, fallback) as a safety net | A declared-but-invalid value skips the fallback ("invalid at computed-value time") | @property with initial-value (→ selectors.md) |
Global will-change or translateZ(0) "GPU hints" | Every layer holds GPU memory; hundreds of layers slow compositing | will-change only on elements actually animating, only while animating (→ performance.md) |
100vh full-screen sections | Mobile browser UI overlaps the bottom of the section | 100svh; dvh only when live resize is acceptable (→ responsive.md) |
!important to win a specificity fight | Escalation is one-way; the next override needs another !important | @layer ordering (→ selectors.md) |
:empty for empty states | Whitespace text nodes count as content in most engines | Control the markup, or a class set by the renderer |
transition: all | Animates properties added later — including layout ones — and turns theme swaps into visible sweeps | Enumerate the properties you mean (→ animations.md) |
:invalid for error styling | Matches an untouched empty required field on first paint: the form is red before typing | :user-invalid (→ forms.md) |
line-height: 150% | Percentages inherit the COMPUTED value, so a big heading inherits the body's pixel leading | Unitless line-height: 1.5 (→ typography.md) |
display: none for screen-reader-only text | Removes it from the accessibility tree — nobody hears it | The clip pattern (→ Accessibility Floor) |
text-align: left and margin-left in a themeable product | The first RTL locale mirrors everything except your CSS | Logical properties (→ internationalization.md) |
Where Experts Disagree
- Selector performance: the old guard writes selectors for right-to-left matching cost; modern engines bucket by rightmost simple selector, making it negligible. Boundary: only act on a DevTools trace showing Style/Recalculate cost — usually
:has()or universal selectors on large, frequently-mutating DOMs (performance.md). - Utility-first vs handwritten CSS: utilities win on team consistency and dead-code elimination; handwritten wins for animation-heavy and design-led work. Boundary: follow whichever the codebase already uses; never mix systems inside one component.
- CSS-in-JS: colocation and typed themes vs runtime cost. Boundary: server-rendered, performance-critical pages want zero-runtime extraction (or plain CSS +
@layer); internal dashboards can afford runtime styling. - Contrast math: WCAG 2.x ratios are the legally referenced standard but misjudge dark backgrounds and thin type; APCA (the WCAG 3 candidate) models them better and is not yet normative. Boundary: ship to WCAG 2.2 numbers for compliance, use APCA as a tiebreaker when two palettes both pass.
Related Skills
More Clawic skills, get them at https://clawic.com/skills/css (install if the user confirms):
html- semantic structure and document-level concerns the CSS hooks intofrontend- component architecture, frameworks, and build tooling around the stylesanimations- motion design and choreography beyond single-property transitionsaccessibility-audit- full WCAG review beyond the CSS floor heredesign-system- tokens, theming, and scaling styles across a product
Feedback
- If useful, star it: https://clawic.com/skills/css
- Latest version: https://clawic.com/skills/css
Part of Clawic, the verified skill library. Get this skill: https://clawic.com/skills/css.
Questions people ask
- Can it debug z-index, overflow, sticky positioning, and text truncation?
- Yes. It traces stacking-context ancestors for z-index issues and uses CSS sizing and layout mechanics to diagnose overflow, broken sticky behavior, and text that will not truncate.
- Can it tailor output to my stack and browser requirements?
- Yes. Configuration supports plain CSS, Sass, Tailwind, or CSS-in-JS, plus evergreen, widely available, or legacy browser targets. It can emit a focused diff or a complete stylesheet.
- Does it make visual design choices?
- No. It handles CSS mechanics, including theming implementation and design tokens, but excludes decisions such as palettes, spacing scales, and typeface selection.
Related skills
Design and critique visual artifacts using measurable rules for hierarchy, spacing, type, color, and layout.
Build, debug, and review Vue 3 code across reactivity, components, state, routing, forms, and performance.
Build and review React code while diagnosing state, hook, rendering, hydration, and performance issues.
Build, debug, review, and migrate Svelte and SvelteKit applications.
Write, debug, and review Go code using checks for concurrency, errors, APIs, builds, tests, and performance.
More from Iván
Browse all skillsWrite, debug, and tune Java and JVM systems with JDK-aware code and diagnostic steps.
Design and critique visual artifacts using measurable rules for hierarchy, spacing, type, color, and layout.
Architect, troubleshoot, secure, and cost-control AWS infrastructure with explicit cost and blast-radius guidance.
Diagnose, validate, transform, and evolve JSON payloads across parsers, schemas, storage, and large files.
Build and operate a testable learning plan with practice, spaced review, transfer checks, and durable local records.
Architect, troubleshoot, secure, and cost-control Azure environments with explicit cost and blast-radius guidance.