From ef86c51214d62a22c6de34b5c778ace74e616518 Mon Sep 17 00:00:00 2001 From: maheshrijal <62394512+maheshrijal@users.noreply.github.com> Date: Sat, 24 Jan 2026 10:54:52 +0530 Subject: [PATCH 1/3] feat: add orchestrated page animations and micro-interactions Motion enhancements (inspired by PR #6): - Orchestrated page entry: form slides from left, preview from right - Staggered form block animations with cascading delays - History panel scale-in animation - Focus ring pulse animation on inputs Micro-interactions: - translateX(4px) hover effect on item rows and history items - scale(0.98) button press effect on all button types - Smooth transitions for all interactive elements Layout: - Asymmetrical grid (0.9fr : 1fr) for visual interest Print improvements: - Hide noise texture and animations in print - Preserve dark total row styling - Hide skip-link and placeholder in print output All changes respect prefers-reduced-motion preference. Amp-Thread-ID: https://ampcode.com/threads/T-019bee5e-33fb-7526-9a97-ab10f03bd6ff Co-authored-by: Amp --- src/css/base/variables.css | 12 ++++ src/css/components/buttons.css | 5 +- src/css/components/forms.css | 12 +++- src/css/components/history.css | 2 + src/css/layout/grid.css | 3 +- src/css/layout/responsive.css | 25 +++++++- src/css/utilities/animations.css | 99 +++++++++++++++++++++++++++++++- 7 files changed, 149 insertions(+), 9 deletions(-) diff --git a/src/css/base/variables.css b/src/css/base/variables.css index ae42513..e7c5c46 100644 --- a/src/css/base/variables.css +++ b/src/css/base/variables.css @@ -81,4 +81,16 @@ --transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1); --transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1); --transition-spring: 400ms cubic-bezier(0.34, 1.56, 0.64, 1); + --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); + + /* Stagger delays for orchestrated animations */ + --stagger-1: 0.1s; + --stagger-2: 0.15s; + --stagger-3: 0.2s; + --stagger-4: 0.25s; + --stagger-5: 0.3s; + --stagger-6: 0.35s; + + /* Primary color as RGB for alpha usage */ + --primary-rgb: 99, 102, 241; } \ No newline at end of file diff --git a/src/css/components/buttons.css b/src/css/components/buttons.css index 3fda9d9..ccf5320 100644 --- a/src/css/components/buttons.css +++ b/src/css/components/buttons.css @@ -91,7 +91,7 @@ button:focus-visible { } .primary:active { - transform: translateY(0); + transform: translateY(0) scale(0.98); box-shadow: var(--shadow-sm); } @@ -109,7 +109,7 @@ button:focus-visible { } .secondary:active { - transform: translateY(0); + transform: translateY(0) scale(0.98); } /* Ghost Button */ @@ -129,4 +129,5 @@ button:focus-visible { .ghost:active { background: var(--gray-100); + transform: scale(0.98); } \ No newline at end of file diff --git a/src/css/components/forms.css b/src/css/components/forms.css index fc7d777..d906c53 100644 --- a/src/css/components/forms.css +++ b/src/css/components/forms.css @@ -193,9 +193,17 @@ grid-template-columns: 2fr 0.7fr 1fr 1fr 40px; gap: var(--space-3); align-items: center; - padding: var(--space-2) 0; + padding: var(--space-2) var(--space-1); margin-bottom: var(--space-2); - animation: fadeInUp var(--transition-fast) ease-out; + border-radius: var(--radius-sm); + transition: + transform var(--transition-fast), + background-color var(--transition-fast); +} + +.item-row:hover { + transform: translateX(4px); + background: var(--gray-50); } .item-row input { diff --git a/src/css/components/history.css b/src/css/components/history.css index 760c789..2d3678d 100644 --- a/src/css/components/history.css +++ b/src/css/components/history.css @@ -53,6 +53,7 @@ button.small { margin-bottom: var(--space-2); cursor: pointer; transition: + transform var(--transition-fast), border-color var(--transition-fast), background-color var(--transition-fast); } @@ -63,6 +64,7 @@ button.small { } .history-item:hover { + transform: translateX(4px); border-color: var(--primary-300); background: var(--primary-50); } diff --git a/src/css/layout/grid.css b/src/css/layout/grid.css index 72a0d45..8cc44e6 100644 --- a/src/css/layout/grid.css +++ b/src/css/layout/grid.css @@ -1,7 +1,8 @@ /* ===== Layout ===== */ .container { display: grid; - grid-template-columns: 1fr 1fr; + /* Asymmetrical grid for visual interest */ + grid-template-columns: 0.9fr 1fr; min-height: 100vh; gap: 0; } diff --git a/src/css/layout/responsive.css b/src/css/layout/responsive.css index 0db3f87..2ebad88 100644 --- a/src/css/layout/responsive.css +++ b/src/css/layout/responsive.css @@ -239,6 +239,8 @@ * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; + animation: none !important; + transition: none !important; } html, @@ -249,6 +251,11 @@ font-size: 12px !important; } + /* Hide noise texture in print */ + body::before { + display: none !important; + } + .container { display: block !important; max-width: none !important; @@ -258,11 +265,14 @@ gap: 0 !important; } - .form-section { + .form-section, + .invoice-placeholder, + .skip-link { display: none !important; } .invoice-section { + display: block !important; box-shadow: none !important; padding: 20px !important; margin: 0 !important; @@ -322,6 +332,19 @@ font-size: 11px !important; } + /* Preserve dark total row in print */ + .total-row { + background: #1f2937 !important; + color: white !important; + padding: 8px 12px !important; + border-radius: 4px !important; + } + + .total-row .total-label, + .total-row .total-amount { + color: white !important; + } + .balance-due h2 { font-size: 14px !important; } diff --git a/src/css/utilities/animations.css b/src/css/utilities/animations.css index 33f007c..2e02c5a 100644 --- a/src/css/utilities/animations.css +++ b/src/css/utilities/animations.css @@ -1,16 +1,105 @@ -/* ===== Animations ===== */ -@keyframes fadeInUp { +/* ===== Keyframe Animations ===== */ + +/* Slide in from left (form section) */ +@keyframes slideInFromLeft { from { opacity: 0; - transform: translateY(8px); + transform: translateX(-24px); + } + to { + opacity: 1; + transform: translateX(0); } +} +/* Slide in from right (invoice preview) */ +@keyframes slideInFromRight { + from { + opacity: 0; + transform: translateX(24px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* Fade up for content blocks */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(12px); + } to { opacity: 1; transform: translateY(0); } } +/* Scale in for panels/modals */ +@keyframes scaleIn { + from { + opacity: 0; + transform: scale(0.96); + } + to { + opacity: 1; + transform: scale(1); + } +} + +/* Focus ring pulse */ +@keyframes focusRingPulse { + 0% { + box-shadow: 0 0 0 0 rgba(var(--primary-rgb), 0.4); + } + 100% { + box-shadow: 0 0 0 3px rgba(var(--primary-rgb), 0.15); + } +} + +/* ===== Page Entry Orchestration ===== */ +.form-section { + animation: slideInFromLeft 0.5s var(--ease-out-expo) forwards; +} + +.invoice-section, +.invoice-placeholder { + animation: slideInFromRight 0.5s var(--ease-out-expo) 0.08s backwards; +} + +/* ===== Staggered Form Block Entry ===== */ +.form-block { + opacity: 0; + animation: fadeInUp 0.4s var(--ease-out-expo) forwards; +} + +.form-block:nth-child(1) { animation-delay: var(--stagger-1); } +.form-block:nth-child(2) { animation-delay: var(--stagger-2); } +.form-block:nth-child(3) { animation-delay: var(--stagger-3); } +.form-block:nth-child(4) { animation-delay: var(--stagger-4); } +.form-block:nth-child(5) { animation-delay: var(--stagger-5); } +.form-block:nth-child(6) { animation-delay: var(--stagger-6); } +.form-block:nth-child(7) { animation-delay: 0.4s; } +.form-block:nth-child(8) { animation-delay: 0.45s; } + +/* ===== Line Item Entry Animation ===== */ +.item-row { + animation: fadeInUp 0.25s var(--ease-out-expo) forwards; +} + +/* ===== History Panel Animation ===== */ +.history-panel { + animation: scaleIn 0.25s var(--ease-out-expo) forwards; +} + +/* ===== Focus Ring Animation ===== */ +.form-group input:focus, +.form-group textarea:focus, +.form-group select:focus { + animation: focusRingPulse 0.2s ease-out forwards; +} + /* Respect reduced motion preferences */ @media (prefers-reduced-motion: reduce) { *, @@ -21,4 +110,8 @@ transition-duration: 0.01ms !important; scroll-behavior: auto !important; } + + .form-block { + opacity: 1; + } } \ No newline at end of file From 0908d96e8150f102d4556695bd95320c2c146919 Mon Sep 17 00:00:00 2001 From: maheshrijal <62394512+maheshrijal@users.noreply.github.com> Date: Sat, 24 Jan 2026 12:17:19 +0530 Subject: [PATCH 2/3] fix: use animation-fill-mode 'both' to prevent flash of invisible content --- src/css/utilities/animations.css | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/css/utilities/animations.css b/src/css/utilities/animations.css index 2e02c5a..3df2652 100644 --- a/src/css/utilities/animations.css +++ b/src/css/utilities/animations.css @@ -70,16 +70,15 @@ /* ===== Staggered Form Block Entry ===== */ .form-block { - opacity: 0; - animation: fadeInUp 0.4s var(--ease-out-expo) forwards; + animation: fadeInUp 0.4s cubic-bezier(0.16, 1, 0.3, 1) both; } -.form-block:nth-child(1) { animation-delay: var(--stagger-1); } -.form-block:nth-child(2) { animation-delay: var(--stagger-2); } -.form-block:nth-child(3) { animation-delay: var(--stagger-3); } -.form-block:nth-child(4) { animation-delay: var(--stagger-4); } -.form-block:nth-child(5) { animation-delay: var(--stagger-5); } -.form-block:nth-child(6) { animation-delay: var(--stagger-6); } +.form-block:nth-child(1) { animation-delay: 0.1s; } +.form-block:nth-child(2) { animation-delay: 0.15s; } +.form-block:nth-child(3) { animation-delay: 0.2s; } +.form-block:nth-child(4) { animation-delay: 0.25s; } +.form-block:nth-child(5) { animation-delay: 0.3s; } +.form-block:nth-child(6) { animation-delay: 0.35s; } .form-block:nth-child(7) { animation-delay: 0.4s; } .form-block:nth-child(8) { animation-delay: 0.45s; } From 79435ddd78966b9fe01eeaba9482f78f0f08f041 Mon Sep 17 00:00:00 2001 From: maheshrijal <62394512+maheshrijal@users.noreply.github.com> Date: Sat, 24 Jan 2026 12:58:38 +0530 Subject: [PATCH 3/3] fix: remove conflicting animation rule that overrides staggered entry Addresses PR review comment: The .form-block animation in forms.css was overriding the orchestrated animation in animations.css due to CSS cascade order (components load after utilities). --- src/css/components/forms.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/components/forms.css b/src/css/components/forms.css index d906c53..19f1bcc 100644 --- a/src/css/components/forms.css +++ b/src/css/components/forms.css @@ -52,11 +52,11 @@ } /* ===== Form Blocks ===== */ +/* Animation is handled in utilities/animations.css for orchestrated staggered entry */ .form-block { padding-bottom: var(--space-5); margin-bottom: var(--space-5); border-bottom: 1px solid var(--gray-100); - animation: fadeInUp var(--transition-slow) ease-out; } .form-block:last-of-type {