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:
Kunthawat Greethong
2026-06-13 19:25:41 +07:00
parent b8ac07996e
commit 5393cf611c

View File

@@ -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; }