*/
@media (prefers-reduced-motion: reduce) {
.enter-up,
.enter-scale {
animation: enter-fade 0.3s ease both;
animation-delay: 0ms;
}
}
```
--------------------------------
### Inline Hero Image CSS Blueprint
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-hero/SKILL.md
Provides CSS for styling inline hero images, ensuring they match the x-height of text and act as visual punctuation. Use this for creative agency hero sections.
```css
/* BLUEPRINT: Inline hero images
WHY: The images must match the x-height of the surrounding text.
They are punctuation, not focal elements. Making them too large
turns the heading into a gallery instead of a sentence. */
.inline-hero-img {
display: inline-block;
width: clamp(3rem, 5vw, 5rem);
height: clamp(2rem, 3.5vw, 3.5rem);
border-radius: 9999px; /* pill shape */
object-fit: cover;
vertical-align: middle;
margin-inline: 0.25em;
}
```
--------------------------------
### Generate Placeholder Image with Picsum Photos
Source: https://github.com/yu-369/vibecurb/blob/main/skills/pixel-perfect/SKILL.md
Use this method to generate a placeholder image when a specific photograph is not available. Replace '{descriptive-keyword}' with a term relevant to the desired image content.
```text
picsum.photos/seed/{descriptive-keyword}/{w}/{h}
```
--------------------------------
### CSS for Universal Interactive Transition
Source: https://github.com/yu-369/vibecurb/blob/main/skills/pixel-perfect/SKILL.md
Applies a smooth transition effect to all interactive elements for consistent visual feedback on hover, focus, or other state changes.
```css
/* Universal interactive transition */
a, button, [role="button"], input, select, textarea {
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
```
--------------------------------
### HTML Usage for Scroll Reveal
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-motion/SKILL.md
Demonstrates how to apply the 'data-reveal' attribute in HTML to trigger scroll animations. Different attribute values can be used for variants like blur or scale effects.
```html
```
--------------------------------
### Handling 'Gray Zone' Elements in Visual Redesign
Source: https://github.com/yu-369/vibecurb/blob/main/skills/visual-redesign/SKILL.md
Carefully manage elements that blend logic and style. When in doubt, prioritize preserving the existing structure and logic over aesthetic refactoring to prevent bugs.
```markdown
| Element | Sacred or Slop? | Rule |
|---|---| --- |
| `className={isActive ? 'active' : ''}` | **Both** — logic is sacred, class names are slop | Keep the ternary. Change only the class name values: `className={isActive ? 'nav-link--active' : 'nav-link'}` |
| `style={{ display: isOpen ? 'block' : 'none' }}` | **Sacred** — this is conditional visibility logic | Do NOT replace with CSS classes. The inline style is driven by state. Leave it. Add your styles alongside it |
| `{items.map((item) => )}` | **Sacred** — the map, key, and data flow are logic | Style the Card component's internals. Do not change the map structure or key assignment |
| `ref={containerRef}` | **Sacred** — ref assignments drive JS behavior | Never remove, move, or rename refs |
| `aria-*` attributes | **Sacred** — accessibility attributes are functional | Never remove. You may add missing ones |
| `data-*` attributes | **Probably sacred** — often used by JS/tests | Never remove unless confirmed unused |
| `id` attributes | **Probably sacred** — may be used by JS selectors | Never change unless confirmed unused |
| `onClick={() => setOpen(!open)}` | **Sacred** — the handler is logic | Style the element. Do not touch the handler |
| `` that wraps conditional content | **Sacred** — the div may exist for rendering reasons | Do not remove "unnecessary" wrapper divs unless you've confirmed they're purely presentational |
```
--------------------------------
### CSS View Transitions API for Page Transitions
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-motion/SKILL.md
Defines CSS animations for root element transitions (fade-out/fade-in) and custom element transitions (e.g., hero image). Uses ::view-transition-old and ::view-transition-new pseudo-elements.
```css
::view-transition-old(root) {
animation: fade-out 0.3s var(--ease-in-out);
}
::view-transition-new(root) {
animation: fade-in 0.3s var(--ease-in-out);
}
@keyframes fade-out {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(0.98); }
}
@keyframes fade-in {
from { opacity: 0; transform: scale(1.02); }
to { opacity: 1; transform: scale(1); }
}
/* Element-level transitions (e.g., hero image persists across pages) */
.hero-image {
view-transition-name: hero-image;
}
::view-transition-old(hero-image) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-new(hero-image) {
animation: none;
mix-blend-mode: normal;
}
```
--------------------------------
### JavaScript Implementation of View Transitions API
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-motion/SKILL.md
Triggers page transitions using `document.startViewTransition`. Fetches new content, swaps it into the DOM, updates the title and URL. Falls back to direct navigation if the API is not supported.
```javascript
function navigateWithTransition(url) {
if (!document.startViewTransition) {
window.location.href = url;
return;
}
document.startViewTransition(async () => {
const response = await fetch(url);
const html = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
// Swap the main content
document.querySelector("main").innerHTML =
doc.querySelector("main").innerHTML;
// Update the title
document.title = doc.title;
// Update URL
history.pushState({}, "", url);
});
}
```
--------------------------------
### Off-Canvas Menu Slide-In Animation (CSS)
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-motion/SKILL.md
Animates an off-canvas menu sliding in from the right with staggered link entry. Uses `visibility` and `pointer-events` for animation compatibility. Requires CSS variables for easing functions.
```css
.mobile-menu {
position: fixed;
inset: 0;
background: var(--color-bg);
transform: translateX(100%);
transition: transform 0.5s var(--ease-out);
visibility: hidden;
pointer-events: none;
z-index: 100;
}
.mobile-menu.is-open {
transform: translateX(0);
visibility: visible;
pointer-events: auto;
}
.mobile-menu .menu-link {
opacity: 0;
transform: translateX(30px);
transition:
opacity 0.4s var(--ease-out),
transform 0.4s var(--ease-out);
}
.mobile-menu.is-open .menu-link:nth-child(1) { transition-delay: 100ms; opacity: 1; transform: translateX(0); }
.mobile-menu.is-open .menu-link:nth-child(2) { transition-delay: 160ms; opacity: 1; transform: translateX(0); }
.mobile-menu.is-open .menu-link:nth-child(3) { transition-delay: 220ms; opacity: 1; transform: translateX(0); }
.mobile-menu.is-open .menu-link:nth-child(4) { transition-delay: 280ms; opacity: 1; transform: translateX(0); }
.mobile-menu.is-open .menu-link:nth-child(5) { transition-delay: 340ms; opacity: 1; transform: translateX(0); }
```
--------------------------------
### 3D Card Tilt Effect Following Cursor
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-motion/SKILL.md
Implements a 3D tilt effect on cards that follows the cursor's position, creating a tangible depth feel. Rotation is clamped to prevent extreme angles, and a glare overlay simulates light reflection. `getBoundingClientRect` is used on `mousemove` for performance.
```javascript
/* BLUEPRINT: 3D tilt card that follows cursor position
WHY: The card tilts toward the cursor, creating a tangible
depth feel. The rotation is clamped to ±8deg to prevent
extreme angles. The glare overlay simulates light reflection.
getBoundingClientRect() is called on mousemove — this is
acceptable because it only fires on hovered cards (1-2 elements),
not on scroll (hundreds of elements). */
function initTiltCards() {
document.querySelectorAll("[data-tilt]").forEach((card) => {
const maxTilt = 8; // degrees
card.addEventListener("mousemove", (e) => {
const rect = card.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width; // 0 to 1
const y = (e.clientY - rect.top) / rect.height; // 0 to 1
const rotateX = (0.5 - y) * maxTilt; // tilt up/down
const rotateY = (x - 0.5) * maxTilt; // tilt left/right
card.style.transform = `
perspective(800px)
rotateX(${rotateX}deg)
rotateY(${rotateY}deg)
scale3d(1.02, 1.02, 1.02)
`;
});
card.addEventListener("mouseleave", () => {
card.style.transform =
"perspective(800px) rotateX(0) rotateY(0) scale3d(1,1,1)";
card.style.transition = "transform 0.6s var(--ease-out)";
});
card.addEventListener("mouseenter", () => {
card.style.transition = "transform 0.1s ease-out";
});
});
}
```
--------------------------------
### Tab Switch with Sliding Indicator (JavaScript)
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-motion/SKILL.md
Initializes tab functionality and moves a sliding indicator to the active tab. The indicator's width and position are dynamically calculated. Call `initTabs()` after the DOM is ready.
```javascript
function initTabs() {
const tabContainer = document.querySelector("[data-tabs]");
const indicator = tabContainer.querySelector(".tab-indicator");
const tabs = tabContainer.querySelectorAll("[data-tab]");
function moveIndicator(tab) {
const rect = tab.getBoundingClientRect();
const containerRect = tabContainer.getBoundingClientRect();
indicator.style.width = `${rect.width}px`;
indicator.style.transform = `translateX(${rect.left - containerRect.left}px)`;
}
tabs.forEach((tab) => {
tab.addEventListener("click", () => {
tabs.forEach((t) => t.classList.remove("active"));
tab.classList.add("active");
moveIndicator(tab);
});
});
// Initialize on the active tab
const activeTab = tabContainer.querySelector("[data-tab].active");
if (activeTab) moveIndicator(activeTab);
}
```
--------------------------------
### Default Image Generation Configuration Dials
Source: https://github.com/yu-369/vibecurb/blob/main/skills/imagegen-frontend/SKILL.md
These are the global default settings that calibrate the image generation engine. They can be dynamically adapted based on user briefs.
```plaintext
DESIGN_VARIANCE: 8 (1=rigid/symmetrical, 10=artsy/asymmetric)
VISUAL_DENSITY: 4 (1=airy/gallery-like, 10=packed/intense)
ART_DIRECTION: 8 (1=safe commercial, 10=bold creative statement)
IMPLEMENTATION_CLARITY: 9 (1=loose moodboard, 10=very codeable UI reference)
IMAGE_USAGE_PRIORITY: 9 (1=mostly typographic, 10=strongly image-led)
SPACING_GENEROSITY: 8 (1=compact/tight, 10=very spacious/breathable)
LAYOUT_VARIATION: 8 (1=same anchor repeats, 10=bold composition variety)
CONVERSION_DISCIPLINE: 8 (1=pure art moodboard, 10=clear funnel + design balance)
```
--------------------------------
### Preloader Controller JavaScript
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-motion/SKILL.md
Manages the preloader's progress animation, counter display, and exit sequence. It tracks font and image loading to determine completion. Initialize this script after the DOM is ready.
```javascript
/* BLUEPRINT: Preloader controller
WHY: We simulate loading progress based on font + image
readiness. The counter formats to 3 digits (001...100).
When all resources are loaded, we trigger the exit sequence
and then fire the hero entry animation. */
class Preloader {
constructor() {
this.counter = document.querySelector(".preloader-counter");
this.line = document.querySelector(".preloader-line");
this.preloader = document.querySelector(".preloader");
this.progress = 0;
this.target = 0;
}
start() {
// Track actual resource loading
const promises = [
document.fonts.ready,
...Array.from(document.images).map(
(img) =>
img.complete
? Promise.resolve()
: new Promise((res) => {
img.onload = res;
img.onerror = res;
})
),
];
// Animate counter smoothly
const tick = () => {
this.progress += (this.target - this.progress) * 0.08;
const rounded = Math.round(this.progress);
this.counter.textContent = String(rounded).padStart(3, "0");
this.line.style.setProperty("--progress", this.progress / 100);
if (rounded < 100) {
requestAnimationFrame(tick);
} else {
this.complete();
}
};
// Simulate progress stages
setTimeout(() => { this.target = 30; }, 100);
setTimeout(() => { this.target = 60; }, 400);
setTimeout(() => { this.target = 80; }, 700);
Promise.all(promises).then(() => {
this.target = 100;
});
requestAnimationFrame(tick);
}
complete() {
setTimeout(() => {
this.preloader.classList.add("is-done");
// Trigger hero entry animation after preloader exits
setTimeout(() => {
document.body.classList.add("is-loaded");
}, 800);
}, 300);
}
}
// Initialize
document.addEventListener("DOMContentLoaded", () => {
new Preloader().start();
});
```
--------------------------------
### CSS for Staggered Entry Animation
Source: https://github.com/yu-369/vibecurb/blob/main/skills/pixel-perfect/SKILL.md
Applies a fade-in and slide-up animation with a blur effect for elements entering the viewport. Includes a fallback for reduced motion preferences.
```css
/* BLUEPRINT: Staggered entry animation
WHY: Elements that fade+slide in feel intentional.
The blur-to-sharp adds perceived quality.
prefers-reduced-motion disables for accessibility. */
@keyframes enter {
from {
opacity: 0;
transform: translateY(24px);
filter: blur(6px);
}
to {
opacity: 1;
transform: translateY(0);
filter: blur(0);
}
}
.animate-in {
animation: enter 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.animate-in:nth-child(1) { animation-delay: 0.05s; }
.animate-in:nth-child(2) { animation-delay: 0.15s; }
.animate-in:nth-child(3) { animation-delay: 0.25s; }
.animate-in:nth-child(4) { animation-delay: 0.35s; }
@media (prefers-reduced-motion: reduce) {
.animate-in {
animation: none;
opacity: 1;
transform: none;
filter: none;
}
}
```
--------------------------------
### Minimal Progress Preloader CSS
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-motion/SKILL.md
Defines the styles for a preloader with an expanding line and a counter. The preloader animates its exit using clip-path. Use this for a premium loading experience.
```css
/* BLUEPRINT: Minimal preloader with line expansion
WHY: A single expanding line is more premium than a spinner.
The line grows from center (scaleX 0 -> 1) while a counter
ticks up. When loading completes, the preloader lifts away
with a clip-path wipe. */
.preloader {
position: fixed;
inset: 0;
background: var(--color-bg);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10000;
transition: clip-path 0.8s var(--ease-dramatic);
clip-path: inset(0);
}
.preloader.is-done {
clip-path: inset(0 0 100% 0); /* wipes upward */
pointer-events: none;
}
.preloader-line {
width: 120px;
height: 1px;
background: var(--color-text-3);
position: relative;
overflow: hidden;
}
.preloader-line::after {
content: '';
position: absolute;
inset: 0;
background: var(--color-text);
transform: scaleX(0);
transform-origin: left;
transition: transform 0.3s linear;
}
/* JS sets --progress from 0 to 1 */
.preloader-line::after {
transform: scaleX(var(--progress, 0));
}
.preloader-counter {
font-family: var(--font-mono, monospace);
font-size: 0.75rem;
letter-spacing: 0.1em;
color: var(--color-text-3);
margin-top: 1rem;
}
```
--------------------------------
### Custom Transition Easing
Source: https://github.com/yu-369/vibecurb/blob/main/skills/visual-redesign/SKILL.md
Replaces the default 'ease-in-out' transition with a custom cubic-bezier curve for a more unique and engaging animation feel.
```css
transition: all 0.3s cubic-bezier(0.22, 1, 0.36, 1)
```
--------------------------------
### Spring Physics Easing Palette via CSS linear()
Source: https://github.com/yu-369/vibecurb/blob/main/skills/awwwards-motion/SKILL.md
Defines a set of CSS custom properties for various spring physics-based easing curves using `linear()`. These are intended for browsers supporting `linear()` and provide realistic motion with overshoot and settle. Use the highest tier your browser support allows.
```css
:root {
/* 1. APPLE SNAPPY SPRING — Primary entrance/reveal easing
Physics: mass=1, stiffness=400, damping=30
Character: Explosive start, tiny overshoot (~2%), soft settle.
This is the iOS sheet-present / notification-arrive curve.
Use on: hero entries, scroll reveals, modal opens, everything
that "arrives" on screen. This is your DEFAULT curve. */
--spring-snappy: linear(
0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%,
0.938 16.7%, 1.017 19.4%, 1.067 22.5%, 1.089 26.0%,
1.079 30.3%, 1.049 36.0%, 1.024 42.6%, 1.011 50.3%,
1.004 59.2%, 1.001 69.3%, 1
);
--spring-snappy-duration: 0.55s;
/* 2. APPLE SMOOTH SPRING — State changes, position shifts
Physics: mass=1, stiffness=200, damping=24
Character: Gentle acceleration, visible overshoot (~5%),
two-phase settle. Feels like a precision instrument.
This is the iOS page-transition / tab-switch curve.
Use on: page transitions, tab switches, carousel slides,
anything moving from position A to position B. */
--spring-smooth: linear(
0, 0.004, 0.016 2.3%, 0.063 4.7%, 0.141 7.2%,
0.25 9.9%, 0.601 16.5%, 0.815 21.0%, 0.929 25.2%,
0.987 29.0%, 1.025 33.5%, 1.042 38.0%, 1.04 43.5%,
1.027 50.0%, 1.013 57.5%, 1.005 67.0%, 1.001 79.0%, 1
);
--spring-smooth-duration: 0.7s;
/* 3. APPLE BOUNCY SPRING — Playful micro-interactions
Physics: mass=1, stiffness=500, damping=18
Character: Very fast, pronounced overshoot (~12%), visible
bounce-settle. Feels playful, energetic, delightful.
Use SPARINGLY on: toggles, like buttons, notification pops,
small badges, emoji reactions. NEVER on large elements. */
--spring-bouncy: linear(
0, 0.014, 0.055 1.8%, 0.218 3.7%, 0.867 8.5%,
1.085 10.7%, 1.212 12.9%, 1.264 15.0%, 1.262 17.0%,
1.217 19.5%, 1.098 24.0%, 1.035 28.5%, 0.993 33.0%,
0.981 38.0%, 0.988 45.0%, 0.998 55.0%, 1.001 68.0%, 1
);
--spring-bouncy-duration: 0.5s;
/* 4. MATERIAL 3 EMPHASIZED — Google's expressive motion standard
Source: Material Design 3 motion spec (legacy cubic-bezier fallback)
Character: Very slow start, dramatic acceleration, gentle decelerate.
This is the M3 "emphasized" transition for container transforms,
shared element transitions, and FAB expansions.
Use on: container morphs, expand/collapse, shared transitions. */
--m3-emphasized: cubic-bezier(0.05, 0.7, 0.1, 1.0);
--m3-emphasized-duration: 0.5s;
/* 5. MATERIAL 3 EMPHASIZED as SPRING — for spring-capable contexts
Physics: mass=1, stiffness=300, damping=22
The spring equivalent of M3 Emphasized — with the overshoot
that Google's spec now recommends via their spring system. */
--m3-spring: linear(
0, 0.007, 0.029 2.0%, 0.118 4.2%, 0.508 10.9%,
0.797 15.4%, 0.951 19.2%, 1.029 22.2%, 1.074 25.6%,
1.088 29.2%, 1.075 33.6%, 1.045 39.5%, 1.02 46.5%,
1.007 55.0%, 1.001 66.0%, 1
);
--m3-spring-duration: 0.6s;
}
```