plugins/frontend/skills/frontend-css/SKILL.md
Unified web frontend knowledge base covering CSS architecture, UX psychology, UI components, distinctive aesthetics, and interface design generation. TRIGGER WHEN: working on web styling, design systems, component decisions, responsive strategy, distinctive frontend aesthetics, or exploring multiple interface designs. DO NOT TRIGGER WHEN: the task is purely backend or unrelated to web frontend.
npx skillsauth add acaprino/anvil-toolset frontend-cssInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides a reference for writing modern, robust, and efficient CSS.
For deeper coverage of specific topics, consult the files under references/:
| File | Topic |
|------|-------|
| typography.md | Vertical rhythm, modular scales, font selection, web font loading, fluid type, OpenType features |
| color-and-contrast.md | OKLCH color spaces, tinted neutrals, palette structure, WCAG, dark mode, theming |
| motion-design.md | Duration rules, easing curves, premium motion materials, reduced motion, perceived performance |
| heuristics-scoring.md | Nielsen's 10 heuristics with 0-4 scoring rubric and P0-P3 severity |
| cognitive-load.md | Intrinsic/extraneous/germane load, working memory rule, 8 common violations |
| personas.md | 5 design-critique archetypes (Alex/Jordan/Sam/Riley/Casey) |
| css-patterns.md | Container queries, color manipulation, SASS, BEM, CSS Modules, focus, vendor prefixes, responsive images, plus a Responsive Design Foundations section (mobile-first, breakpoints, pointer detection, safe areas) |
| layout-patterns.md | Holy Grail, Split, Bento, Editorial Asymmetry, Masonry, Card Grid recipes, plus a Spatial Design Principles section (4pt spacing, hierarchy, container queries, optical adjustments) |
| ui-pattern-guide.md | Cards/list/table/gallery decisions, navigation, pagination, modals, blank slate, plus an Interaction Design section (8 states, focus rings, Popover API, anchor positioning, keyboard nav) |
| ux-patterns.md | Onboarding, trust/social proof, persuasion, paywalls, chunking, plus a UX Writing section (button labels, error messages, voice/tone, i18n, terminology) |
| visual-intensity.md | Amplify (bolder) vs restrain (quieter) on one axis: scale/weight/color/space moves, anti-AI-slop "bold", easing rules |
| delight-and-microinteractions.md | Where/when delight is earned, micro-interactions, copy personality, loading-state copy (anti-slop), celebrations, easter eggs, libraries |
| production-hardening.md | Edge-case resilience: text overflow, i18n/RTL logical properties, error/empty/loading states, validation, a11y and performance resilience |
| flow-patterns.md | User flow recipes |
| argyle-cacadia-2025-deck.md | Adam Argyle CSS Wrapped 2025 deck (offline copy) |
| token-architecture.md | Three-layer token system (primitive -> semantic -> component), naming convention, file organization, W3C DTCG alignment |
| primitive-tokens.md | Layer 1 reference: raw color scales (gray/blue/status), 4px spacing scale, type scale, radius, shadow, motion, z-index |
| semantic-tokens.md | Layer 2 reference: purpose-based aliases (background/foreground, primary/secondary/muted/accent/destructive, status, border/ring), dark-mode overrides |
| component-tokens.md | Layer 3 reference: component-scoped tokens (button, input, card, badge, alert, dialog, table) referencing the semantic layer |
| component-specs.md | Component spec tables (button, input, card, badge, alert, dialog, table) with variants, sizes, states, anatomy |
| states-and-variants.md | Interactive states (default/hover/focus/active/disabled/loading), state priority, transitions, focus rings, error states, variant patterns, ARIA |
| tailwind-integration.md | CSS variables + Tailwind config recipe, HSL opacity format, component classes, shadcn/ui alignment |
Several references and merged sections are derived from pbakaus/impeccable (Apache-2.0) and nextlevelbuilder/ui-ux-pro-max-skill (MIT); see attribution headers inside each file.
.card {
container: --my-card / inline-size;
}
@container --my-card (width < 40ch) {
/* Component-based responsive design */
}
@container (20ch < width < 50ch) {
/* Range syntax */
}
Container units: cqi, cqb, cqw, cqh - size relative to container dimensions
Anchored container queries: Style an anchored element (and its descendants) based on which position-try fallback is currently active
/* Anchored container queries (newest container-type, 2026) */
.tooltip {
position: absolute;
position-anchor: --trigger;
position-try-fallbacks: flip-inline; /* flip side if it overflows */
container-type: anchored;
}
/* When the inline-flip fallback is in use, flip the caret to match */
@container anchored(fallback: flip-inline) {
.tooltip__caret { translate: 100%; }
}
.trigger {
anchor-name: --trigger;
}
@media (width <= 1024px) { }
@media (360px < width < 1024px) { }
display: grid-lanes (CSS Grid Level 3), which packs items into the shortest lane while keeping logical tab order. This is the resolved syntax (previously proposed as grid-template-rows: masonry). Safari 26 ships it; Chrome and Firefox keep it behind a flag, so gate with @supports for progressive enhancement.:root {
color-scheme: light dark;
--surface-1: light-dark(white, #222);
--text-1: light-dark(#222, #fff);
}
/* OKLCH: uniform brightness, P3+ colors */
.vibrant {
background: oklch(72% 75% 330);
}
/* Display-P3 for HDR displays */
@media (dynamic-range: high) {
.neon {
--neon-red: color(display-p3 1 0 0);
}
}
/* Better gradients with in oklch */
.gradient {
background: linear-gradient(
to right in oklch,
color(display-p3 1 0 .5),
color(display-p3 0 1 1)
);
}
/* color-mix() */
.lighten {
background: color-mix(in oklab, var(--brand), white);
}
/* Relative color syntax */
.lighter {
background: oklch(from blue calc(l + .25) c h);
background: oklch(from blue 75% c h); /* Set to specific lightness */
}
.semi-transparent {
background: oklch(from var(--color) l c h / 50%);
}
.complementary {
background: hsl(from blue calc(h + 180) s l);
}
:root {
accent-color: hotpink; /* Tints checkboxes, radios, range inputs */
}
h1 {
text-wrap: balance; /* Balanced multi-line headings */
max-inline-size: 25ch;
}
p {
text-wrap: pretty; /* No orphans */
max-inline-size: 50ch;
}
h1, p, button {
text-box: trim-both cap alphabetic; /* Optical vertical centering */
}
.heading {
font-size: clamp(1rem, 1rem + 0.5vw, 2rem); /* Respects user preferences */
}
dvh / dvw - Dynamic (accounts for mobile browser UI)svh / svw - Small (smallest possible viewport)lvh / lvw - Large (largest possible viewport)/* Animate on scroll position */
.parallax {
animation: slide-up linear both;
animation-timeline: scroll();
}
/* Animate on viewport intersection */
.fade-in {
animation: fade linear both;
animation-timeline: view();
animation-range: cover -75cqi contain 20cqi;
}
Status: Baseline Newly Available (Same-document). Cross-document transitions are in Limited Availability (Chrome/Safari 18.2+).
@view-transition {
navigation: auto; /* Automatically animate page transitions (MPAs) */
}
nav {
view-transition-name: --persist-nav; /* Persist specific elements */
view-transition-class: --site-header; /* Group transitions with classes */
}
/* Style the active transition */
html:active-view-transition {
overflow: hidden;
}
Nested View Transition Groups: Preserve 3D transforms and clipping during transitions.
.springy {
--spring: linear(
0, 0.14 4%, 0.94 17%, 1.15 24% 30%, 1.02 43%, 0.98 51%, 1 77%, 1
);
transition: transform 1s var(--spring);
}
.dialog {
transition: opacity .5s, scale .5s;
@starting-style {
opacity: 0;
scale: 1.1;
}
}
Type-safe, animatable custom properties:
@property --gradient-angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.animate {
transition: --gradient-angle 1s ease;
&:hover {
--gradient-angle: 360deg;
}
}
Newly Available: calc-size() allows calculations and transitions on intrinsic sizes (auto, min-content).
/* Finally: Animate to auto height! */
.accordion-content {
height: 0;
overflow: hidden;
transition: height 0.3s ease;
}
.accordion-item.open .accordion-content {
height: calc-size(auto);
}
.radial-layout {
--_angle: calc(var(--sibling-index) * var(--_offset));
translate:
calc(cos(var(--_angle)) * var(--_circle-size))
calc(sin(var(--_angle)) * var(--_circle-size));
}
.staggered {
animation-delay: calc(sibling-index() * .1s);
background-color: hsl(sibling-count() 50% 50%);
}
.dynamic {
color: if(
style(--theme: dark),
white,
black
);
}
@layer reset, design-system, components, utilities;
@import "open-props/colors" layer(design-system);
@import "components/nav/base.css" layer(components.nav);
@layer components.nav.primary {
nav {
position: sticky;
inset-block-start: 0;
}
}
Benefits:
<dialog id="modal">
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="confirm">Confirm</button>
</form>
</dialog>
<button commandfor="modal" command="showModal">Open</button>
<button commandfor="modal" command="close">Close</button>
New: closedby attribute enables light-dismiss behavior
<button popovertarget="menu">Show Menu</button>
<div popover id="menu">...</div>
popover=hint: Ephemeral tooltips that don't dismiss other popovers
[popover] {
transition:
display .5s allow-discrete,
overlay .5s allow-discrete,
opacity .5s;
@starting-style {
&:popover-open {
opacity: 0;
}
}
}
.tooltip-anchor {
anchor-name: --tooltip;
}
.tooltip[popover] {
position-anchor: --tooltip;
position-area: block-start;
position-try-fallbacks: flip-block;
position-try-order: most-height;
}
Pseudo-elements: anchor(), ::scroll-button(), ::scroll-marker()
<details name="accordion">...</details>
<details name="accordion">...</details>
<!-- Only one can be open at a time -->
select {
appearance: base-select; /* Full CSS control */
}
/* Style options with rich HTML */
select option::before {
content: ""; /* Can include images, icons */
}
<search>
<form>
<input type="search" name="q">
<button type="submit">Search</button>
</form>
</search>
textarea, select, input {
field-sizing: content; /* Auto-grow to content */
}
textarea {
min-block-size: 3lh; /* Line-height units */
max-block-size: 80dvh;
}
/* Wait for user interaction before showing errors */
:user-invalid {
outline-color: red;
}
:user-valid {
outline-color: green;
}
label:has(+ input:user-invalid) {
text-decoration: underline wavy red;
}
<select>
<option>Option 1</option>
<hr>
<option>Option 2</option>
</select>
.custom-scrollbar {
scrollbar-color: hotpink transparent;
scrollbar-width: thin;
}
.complex-clip {
clip-path: shape(
from 0% 0%,
curve by 50% 25% via 25% 50%,
line to 100% 100%
);
}
.fancy-corners {
corner-shape: squircle;
corner-shape: notch;
corner-shape: scoop;
corner-shape: superellipse(0.7);
}
@supports (animation-timeline: view()) {
.fade-in {
animation: fade linear both;
animation-timeline: view();
}
}
@supports (container-type: inline-size) {
.responsive-card {
container-type: inline-size;
}
}
@media (prefers-reduced-motion: no-preference) {
.animated {
animation: slide 1s ease;
}
}
@media (prefers-color-scheme: dark) {
:root {
--surface: #222;
}
}
@media (prefers-contrast: more) {
.text {
font-weight: 600;
}
}
What is Baseline? A unified way to understand cross-browser feature availability. Features are marked as:
Remember: Always check Baseline status, use @supports for cutting-edge features, and respect user preferences with media queries. Modern CSS is about progressive enhancement and building resilient interfaces that work for everyone.
Here's a card component using many modern CSS features:
/* Cascade layer for organization */
@layer components.card {
/* Custom properties with @property */
@property --card-hue {
syntax: "<number>";
inherits: false;
initial-value: 200;
}
.card {
/* Container for responsive design */
container: card / inline-size;
/* Logical properties */
inline-size: 100%;
padding-inline: var(--space-md);
padding-block: var(--space-lg);
/* Modern color system */
background: light-dark(
oklch(98% 0.02 var(--card-hue)),
oklch(20% 0.02 var(--card-hue))
);
/* Border with relative color */
border: 1px solid oklch(from var(--surface) calc(l * 0.9) c h);
/* Smooth corners */
border-radius: var(--radius-md);
/* View transition */
view-transition-name: --card;
/* Scroll-driven animation */
animation: fade-in linear both;
animation-timeline: view();
animation-range: entry 0% cover 30%;
/* Anchor for tooltips */
anchor-name: --card-anchor;
/* Transition custom property */
transition: --card-hue 0.5s var(--ease-spring-3);
&:hover {
--card-hue: 280;
}
/* Responsive typography in container */
@container card (width > 30ch) {
.card__title {
font-size: clamp(1.5rem, 3cqi, 2.5rem);
text-wrap: balance;
}
}
@container card (width < 30ch) {
.card__image {
aspect-ratio: 16 / 9;
object-fit: cover;
}
}
}
.card__title {
/* Text box trim for optical alignment */
text-box: trim-both cap alphabetic;
text-wrap: balance;
/* Logical margin */
margin-block-end: var(--space-sm);
}
.card__body {
text-wrap: pretty;
max-inline-size: 65ch;
}
.card__cta {
/* Inherit font */
font: inherit;
/* Accent color */
accent-color: var(--brand);
/* Field sizing */
field-sizing: content;
/* Logical properties */
padding-inline: var(--space-md);
padding-block: var(--space-sm);
/* Modern color with relative syntax */
background: oklch(from var(--brand) l c h);
color: oklch(from var(--brand) 95% 0.05 h);
&:hover {
background: oklch(from var(--brand) calc(l * 1.1) c h);
}
&:user-invalid {
outline: 2px solid light-dark(red, #ff6b6b);
}
}
/* Popover tooltip anchored to card */
.card__tooltip[popover] {
position-anchor: --card-anchor;
position-area: block-start;
position-try-fallbacks: flip-block;
/* Entry animation */
@starting-style {
opacity: 0;
scale: 0.9;
}
transition:
opacity 0.2s,
scale 0.2s,
display 0.2s allow-discrete,
overlay 0.2s allow-discrete;
}
/* Scroll state container queries */
@supports (container-type: scroll-state) {
.card__sticky-header {
container-type: scroll-state;
position: sticky;
inset-block-start: 0;
@container scroll-state(stuck: top) {
box-shadow: 0 2px 8px oklch(0% 0 0 / 0.1);
}
}
}
/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
.card {
animation: none;
transition: none;
}
}
@media (prefers-contrast: more) {
.card {
border-width: 2px;
}
}
}
/* Keyframes for scroll animation */
@keyframes fade-in {
from {
opacity: 0;
scale: 0.95;
}
to {
opacity: 1;
scale: 1;
}
}
<article class="card">
<img
class="card__image"
src="image.jpg"
alt="Description"
loading="lazy"
>
<h2 class="card__title">Card Title</h2>
<p class="card__body">
Card description with pretty text wrapping that avoids orphans.
</p>
<button
class="card__cta"
popovertarget="card-tooltip"
>
Learn More
</button>
<div
class="card__tooltip"
popover="hint"
id="card-tooltip"
>
Additional information appears here
</div>
</article>
Prioritize Stability:
@supports. Or ask the user how they want to handle it.Use the web platform:
Code Style:
oklch) for new palettes.development
Coordinate parallel code reviews across multiple quality dimensions with finding deduplication, severity calibration, and consolidated reporting. Use this skill when organizing multi-reviewer code reviews, calibrating finding severity, or consolidating review results.
tools
Knowledge base for the codebase-mapper plugin. Provides writing guidelines, tone rules, and diagram conventions for generating human-readable project guides. Referenced by all codebase-mapper agents during document generation. TRIGGER WHEN: referenced by codebase-mapper pipeline agents (codebase-explorer, overview-writer, tech-writer, flow-writer, onboarding-writer, ops-writer, config-writer, guide-reviewer) during document generation. DO NOT TRIGGER WHEN: outside the /map-codebase pipeline (general documentation work should use docs:readme-craft or codebase-mapper:docs-create).
tools
Progressive Web App knowledge base for 2025-2026: Web App Manifest, Service Workers (Workbox 7, Serwist), Web Push (VAPID, RFC 8030/8291/8292, Declarative Push for Safari 18.4+), install flows (beforeinstallprompt, Window Controls Overlay), OPFS storage, Project Fugu, Core Web Vitals (INP < 200ms), security (HTTPS, CSP, COOP/COEP), and distribution (Bubblewrap, PWA Builder MSIX, Capacitor). TRIGGER WHEN: building, auditing, or debugging PWAs, including manifest, service worker, Web Push, install flow, OPFS, Background Sync, Wake Lock, vite-plugin-pwa, Next.js Serwist, @angular/pwa, @vite-pwa/nuxt, Bubblewrap, TWA, PWA Builder, or Capacitor wrapping. DO NOT TRIGGER WHEN: the task is generic frontend styling (use frontend), React performance (use react-development:review-react), cross-platform security unrelated to PWA (use platform-engineering), Tauri or Electron wrappers (use tauri-development), or GA4 / analytics (use digital-marketing).
development
Knowledge base for pure-architecture decisions on when to unify duplicated logic into a shared abstraction versus leave it duplicated. Covers the canonical theory (Rule of Three, DRY/WET/AHA, Wrong Abstraction, Locality of Behaviour, Bounded Contexts, Tidy First options framing, CUPID vs SOLID), 12 essential-duplication patterns that justify unification, 12 wrong-abstraction patterns that justify inlining or decomposition, an operational decision frame, and a verified reading list. TRIGGER WHEN: the user is making an architectural decision about whether to centralize, extract, or remove a layer; reviewing an abstraction for premature generality; auditing scattered cross-cutting concerns; spawned by the abstraction-architect agent during /abstraction-architect:audit; the user asks "should I extract this into a service" / "is this DRY enough" / "is this wrong abstraction". DO NOT TRIGGER WHEN: the task is code formatting and readability cleanup (use clean-code:clean-code), Python-specific refactoring with metrics (use python-development:python-refactor), generic dead-code removal (use senior-review:cleanup-dead-code), security review (use senior-review:security-auditor), or pure pattern-consistency review without an architecture lens (use senior-review:code-auditor).