From 5393cf611cadfa5087e2de2af1b652ae4b668af5 Mon Sep 17 00:00:00 2001 From: Kunthawat Greethong Date: Sat, 13 Jun 2026 19:25:41 +0700 Subject: [PATCH] fix(animations): 8 keyframes + reveal/stagger initial state + button hovers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/styles/fx-system.css | 217 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 216 insertions(+), 1 deletion(-) diff --git a/src/styles/fx-system.css b/src/styles/fx-system.css index 0953618..a756c60 100644 --- a/src/styles/fx-system.css +++ b/src/styles/fx-system.css @@ -556,4 +556,219 @@ img{max-width:100%;display:block} } .fx-hero::after, .fx-faq-a::after { display: none !important; } -} \ No newline at end of file +} + +/* ============================================ + 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; } \ No newline at end of file