Modern CSS Craft Patterns
When to Use
Use this guide for applying modern CSS features to craft and polish. Feature syntax and browser support live in modern-css.md. This guide covers the application patterns.
Decision
| Situation | Choose | Cross-reference |
|---|---|---|
Dialog/popover entry from display: none |
@starting-style entry pattern |
modern-css → starting-style-transitions |
| Animated gradient hover | @property registered custom property |
modern-css → at-property |
| Reading progress bar tied to scroll | animation-timeline: scroll() |
modern-css → scroll-driven-animations |
| Smooth page-to-page transitions | View Transitions API | modern-css → view-transitions |
| Token-based semi-transparent colors | color-mix() with transparent |
modern-css → color-mix |
| Dynamic hover color shift | Relative color syntax in oklch | modern-css → relative-color |
Pattern
Dialog entry with @starting-style:
dialog {
opacity: 1; translate: 0 0;
transition:
opacity var(--duration-moderate) var(--ease-emphasized-decel),
translate var(--duration-moderate) var(--ease-emphasized-decel),
display var(--duration-moderate) allow-discrete,
overlay var(--duration-moderate) allow-discrete;
@starting-style { opacity: 0; translate: 0 -1rem; }
}
dialog:not([open]) { opacity: 0; translate: 0 -1rem; }
Three required pieces: @starting-style block, transition-behavior: allow-discrete on display, and overlay transition for top-layer elements.
Gradient animation with @property:
@property --gradient-start { syntax: "<color>"; initial-value: #6366f1; inherits: false; }
@property --gradient-end { syntax: "<color>"; initial-value: #8b5cf6; inherits: false; }
.gradient-btn {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
transition:
--gradient-start var(--duration-moderate) var(--ease-standard),
--gradient-end var(--duration-moderate) var(--ease-standard);
}
.gradient-btn:hover { --gradient-start: #4f46e5; --gradient-end: #7c3aed; }
Scroll-driven reading progress bar:
.progress-bar {
position: fixed; top: 0; left: 0;
width: 100%; height: 3px;
background: var(--color-primary);
transform-origin: 0 0;
animation: grow-width linear;
animation-timeline: scroll(root);
z-index: 1000;
}
@keyframes grow-width { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@supports not (animation-timeline: scroll()) { .progress-bar { display: none; } }
Uses scaleX instead of width — compositor-only. See Animation Performance.
View transitions for page navigation:
@view-transition { navigation: auto; }
.hero-image { view-transition-name: hero; }
::view-transition-old(hero) { animation: fade-out var(--duration-moderate) var(--ease-accel); }
::view-transition-new(hero) { animation: fade-in var(--duration-moderate) var(--ease-decel); }
@media (prefers-reduced-motion: reduce) {
::view-transition-old(hero), ::view-transition-new(hero) { animation-duration: var(--duration-fast); }
}
Dynamic hover with relative color syntax:
.button:hover { background: oklch(from var(--color-primary) calc(l - 0.1) c h); }
.card { background: oklch(from var(--color-primary) 95% calc(c * 0.3) h); }
Common Mistakes
- Wrong: Missing
overlaytransition on@starting-stylefor top-layer — Right: Element pops to top-layer before animating without it - Wrong: Forgetting
@propertyregistration before animating — Right: Browsers snap unregistered custom properties instead of interpolating - Wrong:
widthanimation for progress bar — Right: UsescaleXfor compositor-only performance - Wrong: Duplicate
view-transition-namevalues — Right: Names must be unique per page - Wrong: No
@supportsor@mediafallbacks — Right: Unsupported browsers show broken/invisible content
See Also
- Entrance Animations — scroll-driven reveal patterns
- Animation Performance — why
scaleXoverwidth - Accessibility and Motion — reduced-motion for view transitions
- Reference: modern-css.md — feature syntax and browser support for all referenced features