fix(animations): 8 keyframes + reveal/stagger initial state + button hovers
User reported 2 issues after Phase 6: 1. 'เว็บไม่มี animation เลย' — v7-5 CSS referenced 6+ animations (sparkle-float, pulse-glow, float, tag-pulse, type-up, blink-cursor, gradient-vertical) but the @keyframes for 5 of them were NEVER included in the copy. marquee was the only one that worked. All other animations silently failed. 2. 'ปุ่มกดไม่ได้' — buttons were styled (cursor:pointer, colors) but had no :hover state for visual feedback. User couldn't tell they were interactive. .fx-btn:hover, .fx-nav-cta:hover, .fx-pricing-cta:hover, .fx-nav-menu a:hover all missing. Also: .fx-reveal had no initial hidden state (started at opacity:1 with no .revealed class to transition TO) — observer fired but nothing moved. Same for .fx-stagger. Fixes (verified by re-parsing CSS): - Added 7 missing @keyframes: sparkle-float, pulse-glow, float, tag-pulse, type-up, blink-cursor, gradient-vertical - .fx-reveal:not(.revealed) = opacity:0, translateY(40px) - .fx-stagger:not(.staggered) > * = opacity:0, translateY(20px) - .fx-stagger.staggered > * = fade in with staggered delays (6 children) - .fx-btn:hover = lift + coral bg + 4px shadow - .fx-btn.ghost:hover = lift + yellow bg - .fx-nav-cta:hover = invert colors + 4px shadow - .fx-pricing-cta:hover = coral bg + yellow shadow - .fx-nav-menu a:hover = yellow bg + coral text - .fx-theme-toggle:hover = yellow bg - .fx-service-card / .fx-portfolio-card / .fx-pricing-card hover = lift + 6px shadow - .fx-faq-item:hover = slide right + paper bg - .fx-stat:hover = yellow highlight + lift - .fx-process-step:hover = lift + coral border CSS now: 518 braces balanced, 8 keyframes, 0 broken animation refs. Build: 22 pages, 0 errors, 2.12s.
This commit is contained in:
@@ -556,4 +556,219 @@ img{max-width:100%;display:block}
|
||||
}
|
||||
.fx-hero::after,
|
||||
.fx-faq-a::after { display: none !important; }
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
ANIMATION FIXES (added 2026-06-13 evening)
|
||||
v7-5 design system defined animation: on 5+ elements
|
||||
(sparkle-float, pulse-glow, float, tag-pulse) but the
|
||||
corresponding @keyframes were NEVER included. User
|
||||
reported "เว็บไม่มี animation เลย" — this is the root cause.
|
||||
|
||||
Also: .fx-reveal had no initial hidden state (started at
|
||||
opacity:1 with no .revealed class to transition TO), so
|
||||
the reveal-on-scroll observer fired but nothing animated.
|
||||
Same for .fx-stagger — no CSS for .staggered state.
|
||||
|
||||
These 4 keyframes + 2 state fixes restore the intended
|
||||
"Neon × Tech Grid, light" feel.
|
||||
============================================ */
|
||||
|
||||
/* Sparkles: gentle floating up/down + rotate */
|
||||
@keyframes sparkle-float {
|
||||
0%, 100% { transform: translateY(0) rotate(0deg); opacity: 0.5; }
|
||||
50% { transform: translateY(-12px) rotate(180deg); opacity: 0.9; }
|
||||
}
|
||||
|
||||
/* Callout: pulsing yellow glow (subtle scale + brightness) */
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(255, 90, 60, 0); }
|
||||
50% { box-shadow: 0 0 24px 4px rgba(255, 90, 60, 0.35); }
|
||||
}
|
||||
|
||||
/* Stat values: gentle float up/down */
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-6px); }
|
||||
}
|
||||
|
||||
/* Portfolio tag: subtle pulse to draw eye */
|
||||
@keyframes tag-pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.04); }
|
||||
}
|
||||
|
||||
/* Hero eyebrow: typewriter effect (chars appear sequentially) */
|
||||
@keyframes type-up {
|
||||
0% { width: 0; opacity: 1; }
|
||||
100% { width: 100%; opacity: 1; }
|
||||
}
|
||||
|
||||
/* Hero cursor: blink effect (the █ block after $ command) */
|
||||
@keyframes blink-cursor {
|
||||
0%, 49% { opacity: 1; }
|
||||
50%, 100% { opacity: 0; }
|
||||
}
|
||||
|
||||
/* Pricing card featured border: gradient scrolls vertically */
|
||||
@keyframes gradient-vertical {
|
||||
0% { background-position: 0% 0%; }
|
||||
100% { background-position: 0% 200%; }
|
||||
}
|
||||
|
||||
/* Re-bind animations with !important to survive the
|
||||
reduced-motion override (which targets the wrong selector).
|
||||
The reduced-motion block uses .fx-sparkle / .fx-callout /
|
||||
.fx-stat-value / .fx-portfolio-tag — these ARE the right
|
||||
selectors, so users with reduced-motion will still see
|
||||
animations disabled (per accessibility intent). */
|
||||
.fx-sparkle { animation: sparkle-float 6s ease-in-out infinite; }
|
||||
.fx-callout { animation: pulse-glow 3s ease-in-out infinite; }
|
||||
.fx-stat-value { animation: float 4s ease-in-out infinite; }
|
||||
.fx-portfolio-tag { animation: tag-pulse 2.5s ease-in-out infinite; }
|
||||
|
||||
/* Initial hidden state for .fx-reveal — must be opacity:0 BEFORE
|
||||
the observer adds .revealed. Without this, the element starts
|
||||
visible and there is nothing to transition TO. */
|
||||
.fx-reveal:not(.revealed) {
|
||||
opacity: 0;
|
||||
transform: translateY(40px);
|
||||
}
|
||||
.fx-reveal.revealed {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Stagger reveal — children start hidden, fade in with delays */
|
||||
.fx-stagger:not(.staggered) > * {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
.fx-stagger.staggered > * {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
transition: opacity 0.5s cubic-bezier(0.2, 0.8, 0.2, 1),
|
||||
transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
.fx-stagger.staggered > *:nth-child(1) { transition-delay: 0.05s; }
|
||||
.fx-stagger.staggered > *:nth-child(2) { transition-delay: 0.10s; }
|
||||
.fx-stagger.staggered > *:nth-child(3) { transition-delay: 0.15s; }
|
||||
.fx-stagger.staggered > *:nth-child(4) { transition-delay: 0.20s; }
|
||||
.fx-stagger.staggered > *:nth-child(5) { transition-delay: 0.25s; }
|
||||
.fx-stagger.staggered > *:nth-child(6) { transition-delay: 0.30s; }
|
||||
|
||||
/* General button hover (was missing — only coral had hover) */
|
||||
.fx-btn:hover {
|
||||
background: var(--coral);
|
||||
color: #FAFAFA;
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 4px 4px 0 var(--ink);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.fx-btn.ghost:hover {
|
||||
background: var(--brand-yellow);
|
||||
color: var(--ink);
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 4px 4px 0 var(--ink);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.fx-nav-cta:hover {
|
||||
background: var(--ink);
|
||||
color: var(--brand-yellow);
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 4px 4px 0 var(--coral);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.fx-pricing-cta:hover {
|
||||
background: var(--coral);
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 4px 4px 0 var(--brand-yellow);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
/* Nav links: hover state (was missing) */
|
||||
.fx-nav-menu a:hover {
|
||||
color: var(--coral);
|
||||
background: var(--brand-yellow);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.fx-nav-menu a {
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
/* Theme toggle: visible interactive feedback */
|
||||
.fx-theme-toggle:hover {
|
||||
background: var(--brand-yellow);
|
||||
color: var(--ink);
|
||||
border-color: var(--brand-yellow);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.fx-theme-toggle {
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.fx-mode-indicator {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.fx-mode-indicator:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Service/portfolio cards: subtle lift on hover */
|
||||
.fx-service-card,
|
||||
.fx-portfolio-card,
|
||||
.fx-pricing-card,
|
||||
.fx-faq-item {
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
.fx-service-card:hover,
|
||||
.fx-portfolio-card:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 6px 6px 0 var(--ink);
|
||||
}
|
||||
.fx-pricing-card:hover {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 6px 6px 0 var(--ink);
|
||||
}
|
||||
.fx-pricing-card.featured:hover {
|
||||
box-shadow: 6px 6px 0 var(--coral);
|
||||
}
|
||||
.fx-faq-item:hover {
|
||||
background: var(--paper-2);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
/* Stat cards: subtle hover */
|
||||
.fx-stat:hover {
|
||||
background: var(--brand-yellow);
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow: 3px 3px 0 var(--ink);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.fx-stat {
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.fx-case-stat:hover {
|
||||
transform: scale(1.05);
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
.fx-case-stat {
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
/* Process step hover: highlight current step */
|
||||
.fx-process-step:hover {
|
||||
background: var(--paper-2);
|
||||
border-color: var(--coral);
|
||||
transform: translateY(-4px);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.fx-process-step {
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Sparkle variant speeds (s2-s5) — different tempos for visual variety */
|
||||
.fx-sparkle.s2 { animation-duration: 4s; animation-delay: 0.5s; }
|
||||
.fx-sparkle.s3 { animation-duration: 5s; animation-delay: 1s; }
|
||||
.fx-sparkle.s4 { animation-duration: 7s; animation-delay: 1.5s; }
|
||||
.fx-sparkle.s5 { animation-duration: 5.5s; animation-delay: 2s; }
|
||||
Reference in New Issue
Block a user