Skip to content

UI Ascend: completion fixes and polish#24

Merged
vayungodara merged 4 commits into
mainfrom
ui-ascend-complete
Mar 15, 2026
Merged

UI Ascend: completion fixes and polish#24
vayungodara merged 4 commits into
mainfrom
ui-ascend-complete

Conversation

@vayungodara

Copy link
Copy Markdown
Owner

Summary

  • Fix --text-tertiary WCAG AA contrast (4.45:1 → 4.9:1)
  • Toast progress bar: linear → ease-out easing
  • Framer Motion animations now respect prefers-reduced-motion (Tier 1+3 disable, Tier 2 degrades to opacity-only)
  • Shimmer sweep on completed checkmarks
  • content-visibility: auto on DailySummaryCard and GroupStats
  • Replace canvas-confetti with custom Framer Motion particle system (zero external deps)

Test plan

  • Verify preview deployment loads
  • Check dark/light mode on dashboard, pacts, groups, focus, stats
  • Complete a pact → confetti should fire (custom particles, not canvas-confetti)
  • Enable "Reduce motion" in OS → all Framer Motion animations should stop
  • Check toast appears with eased progress bar
  • Verify text contrast is readable in light mode

🤖 Generated with Claude Code

Vayun Godara and others added 4 commits March 15, 2026 15:13
- Darken --text-tertiary from #6E6A82 to #656180 (4.9:1 vs 4.45:1)
- Toast progress bar: linear → ease-out for natural deceleration
- Add shimmerSweep keyframe for completed card effects

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tier 1 (ambient) and Tier 3 (celebration) fully disable when user
prefers reduced motion. Tier 2 (responsive) degrades to opacity-only.
Uses getter-based approach so check happens at render time, not
module load time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add shimmerSweep animation to PactCard complete button on completion
- Add content-visibility: auto to DailySummaryCard and GroupStats
  for better rendering performance on below-fold components

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Custom particle system using Framer Motion — no external dependency.
Respects prefers-reduced-motion. Cleanup timer prevents memory leaks.
Legacy named exports preserved as no-ops for backwards compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 15, 2026 15:04
@vercel

vercel Bot commented Mar 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lockin Ready Ready Preview, Comment Mar 15, 2026 3:05pm

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR polishes UI motion/performance and removes the canvas-confetti dependency by introducing a Framer Motion–based particle confetti system, while also improving readability and perceived smoothness across the dashboard UI.

Changes:

  • Remove canvas-confetti and implement a custom confetti particle system via useConfetti().
  • Add reduced-motion-aware Framer Motion variants and tweak toast/checkmark visual polish.
  • Improve rendering performance with content-visibility: auto and adjust global text contrast and keyframes.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
package.json Removes canvas-confetti dependency.
package-lock.json Updates lockfile to reflect dependency removal.
lib/confetti.js Replaces canvas-based confetti with a hook-driven Framer Motion particle overlay.
lib/animations.js Adds reduced-motion support via a motionSafe wrapper and updates many exported variants to use it.
components/Toast.module.css Changes toast progress easing from linear to ease-out.
components/PactCard.module.css Adds shimmer sweep styling for the completed checkmark button.
components/PactCard.js Applies shimmer class when completion bounce is shown.
components/GroupStats.module.css Adds content-visibility and intrinsic sizing hints for performance.
components/DailySummaryCard.module.css Adds content-visibility and intrinsic sizing hints for performance.
app/globals.css Improves --text-tertiary contrast and adds @keyframes shimmerSweep.
Comments suppressed due to low confidence (1)

components/PactCard.js:229

  • The shimmer sweep is defined as a 2s animation in CSS, but showBounce (which controls applying styles.completedCheck) is cleared after 1s. That means the pseudo-element (and its animation) will be removed halfway through, so the shimmer likely won’t complete.

Consider using a separate state/timer for the shimmer duration or aligning the showBounce timeout with the shimmer animation length.

                disabled={isLoading}
                className={`${styles.completeBtn} ${showBounce ? styles.completedCheck : ''}`}
                aria-label="Mark pact as complete"
                whileHover={buttonHover}
                whileTap={buttonTap}
                {...(showBounce ? celebrationBounce : {})}
              >

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/confetti.js
Comment on lines +85 to 99
const [particles, setParticles] = useState([]);

const fire = useCallback((opts = {}) => {
if (prefersReducedMotion()) return;
const { x, y } = opts;
const origin = {};
if (x !== undefined) origin.x = typeof x === 'string' ? 0.5 : x / window.innerWidth;
if (y !== undefined) origin.y = typeof y === 'string' ? 0.5 : y / window.innerHeight;
confetti({
particleCount: 80,
spread: 70,
origin: { y: 0.6, ...origin },
colors: BRAND_COLORS,
});
const w = typeof window !== 'undefined' ? window.innerWidth : 800;
const h = typeof window !== 'undefined' ? window.innerHeight : 600;
const origin = {
x: opts.x !== undefined ? opts.x / w : 0.5,
y: opts.y !== undefined ? opts.y / h : 0.6,
};
const newParticles = createParticles(opts.count || 40, origin);
setParticles(newParticles);
const timer = setTimeout(() => setParticles([]), 2000);
return () => clearTimeout(timer);
}, []);
Comment thread lib/confetti.js
Comment on lines +17 to +29
function createParticles(count = 40, origin = { x: 0.5, y: 0.6 }) {
const w = typeof window !== 'undefined' ? window.innerWidth : 800;
const h = typeof window !== 'undefined' ? window.innerHeight : 600;
return Array.from({ length: count }, (_, i) => ({
id: `${Date.now()}-${i}`,
x: origin.x * w,
y: origin.y * h,
color: BRAND_COLORS[Math.floor(Math.random() * BRAND_COLORS.length)],
size: randomBetween(6, 12),
angle: randomBetween(0, 360),
velocity: randomBetween(200, 500),
rotation: randomBetween(-180, 180),
}));
Comment thread lib/animations.js
Comment on lines +49 to +71
/**
* Wraps a variant object so its properties check prefers-reduced-motion
* at access time (i.e., when spread into a component during render).
*
* Tier 1 (ambient) + Tier 3 (celebration): fully disabled — returns empty objects.
* Tier 2 (responsive): degrades to opacity-only transitions.
*/
function motionSafe(variant, tier) {
const result = {};
for (const [key, value] of Object.entries(variant)) {
Object.defineProperty(result, key, {
get() {
if (typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
if (tier === 1 || tier === 3) {
// Kill ambient and celebration animations entirely
return {};
}
if (tier === 2) {
if (key === 'initial') return { opacity: 0 };
if (key === 'animate') return { opacity: 1, transition: { duration: 0.2 } };
if (key === 'exit') return { opacity: 0, transition: { duration: 0.1 } };
}
}
@vayungodara vayungodara merged commit ea9e618 into main Mar 15, 2026
7 checks passed
@vayungodara vayungodara mentioned this pull request Mar 15, 2026
12 tasks
@vayungodara vayungodara deleted the ui-ascend-complete branch March 22, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants