Skip to content

Feat/animated theme toggle#704

Open
ankitkr104 wants to merge 3 commits intoAOSSIE-Org:mainfrom
ankitkr104:feat/animated-theme-toggle
Open

Feat/animated theme toggle#704
ankitkr104 wants to merge 3 commits intoAOSSIE-Org:mainfrom
ankitkr104:feat/animated-theme-toggle

Conversation

@ankitkr104
Copy link
Contributor

@ankitkr104 ankitkr104 commented Mar 25, 2026

The User Experience by adding premium, high-quality animations to the theme toggle button in the Header component.
While the original implementation swapped icons instantly, this update introduces:

Icon Transitions: Uses framer-motion to animate the Sun and Moon icons with smooth rotation, scaling, and opacity changes.
Improved Reactivity: Implements a MutationObserver within the ModeToggle component to perfectly track theme changes (including system-level preference changes).
Performance Preservation: Retains the existing disableTransitionsTemporarily logic to ensure the rest of the site's theme transition remains instantaneous (no global flashes), while only the toggle button itself provides animated feedback.

Recordings:

Aossie.web.mp4

AI Usage Disclosure:

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: TODO

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without review otherwise.

Summary by CodeRabbit

  • New Features

    • Dark mode toggle includes reduced-motion support and shows a static placeholder until ready
    • Pattern grid now has explicit hit areas for improved hover interactions
  • Improvements

    • Smoother animated transitions between light/dark icons with enter/exit animations
    • Enhanced pattern visuals: cleaner color transitions, scaling/rotation on hover, and stronger drop shadows
  • Stability

    • More reliable initial dark-mode detection and sync across changes

- Resolved SVG zooming drift by setting absolute pixel-based transform origins
- Eliminated rapid hover flickering by utilizing an invisible static rect hit-area
- Upgraded zoom effects with a snappy cubic-bezier bounce curve
- Added alternating rotation physics to symbols mimicking a playful 3D effect
- Maintained sharp rx constraints per design specs
@coderabbitai
Copy link

coderabbitai bot commented Mar 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 28aac7bb-2b78-443a-bf91-d955b3584e01

📥 Commits

Reviewing files that changed from the base of the PR and between f68cccb and 4510460.

📒 Files selected for processing (1)
  • src/components/shared/Header.jsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/shared/Header.jsx

📝 Walkthrough

Walkthrough

Header's ModeToggle now tracks mounted and dark-mode state, syncing with document.documentElement via a MutationObserver and using conditional Framer Motion icon transitions. Pattern's SVG grid was reworked for overflow-visible rendering, interactive hit areas, and hover transforms via Tailwind classes.

Changes

Cohort / File(s) Summary
Dark Mode Synchronization & Toggle UI
src/components/shared/Header.jsx
Added mounted and isDarkMode state, useReducedMotion, and a MutationObserver to sync dark-mode from document.documentElement; render-time guard to avoid hydration mismatch; replaced simultaneous icon rendering with conditional Framer Motion AnimatePresence keyed transitions; adjusted button layout classes.
Interactive SVG Grid / Pointer handling
src/components/shared/Pattern.jsx
SVG set to overflow-visible; replaced flat <use/> with per-cell <g> wrappers, added transparent interactive <rect> hit areas and non-interactive <use>; applied Tailwind hover transforms (rotate, scale, drop-shadow), transition classes, and per-cell transformOrigin styling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I watched the modes flip, soft and bright,
Icons twirled from sun to night,
Patterns leap when paws come near,
Shadows bloom and colors cheer —
A tiny hop for UI delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat/animated theme toggle' directly and specifically describes the main change: adding animation to the theme toggle component in the Header. It clearly communicates the primary objective of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/shared/Header.jsx`:
- Around line 233-236: The toggle button in Header.jsx currently only has an
action label; update the button element (the JSX <button> in the Header
component) to expose its current state to assistive tech by adding
aria-pressed={isDarkMode} (or the boolean state variable that represents dark
mode in this component, e.g., isDark/isDarkTheme/darkMode) and ensure that the
boolean is derived from the same state used by the toggleTheme handler so
aria-pressed stays in sync with the visual mode.
- Around line 185-187: ModeToggle currently always runs Framer Motion
transforms; detect and respect the user's `prefers-reduced-motion` and
short-circuit animations by importing and using Framer Motion's useReducedMotion
(or window.matchMedia). Update ModeToggle to import useReducedMotion from
'framer-motion', call it (e.g., const reduceMotion = useReducedMotion()), and
when reduceMotion is true avoid passing animated props/variants to motion
elements (or pass an empty animate/transition) so the rotate/scale transitions
do not run; keep the existing state hooks (mounted, isDarkMode) and only change
the motion usage in ModeToggle and any motion.* elements inside it.

In `@src/components/shared/Pattern.jsx`:
- Around line 33-39: The Pattern component is applying animations
unconditionally (e.g., the circle/rect classNames use animate-pulse and there
are hover rotation/scale/drop-shadow classes) which ignores users'
reduced-motion preferences; update the Tailwind className strings to prefix
motion-safe: for the active animations (e.g., motion-safe:animate-pulse,
motion-safe:hover:rotate-..., motion-safe:hover:scale-...,
motion-safe:hover:drop-shadow-...) and add corresponding motion-reduce:
fallbacks to disable them (e.g., motion-reduce:animate-none,
motion-reduce:hover:rotate-0, motion-reduce:hover:scale-100,
motion-reduce:hover:drop-shadow-none) so the Pattern component respects
reduced-motion settings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a74b050-3c4d-496f-8c16-b0ddd0ba6314

📥 Commits

Reviewing files that changed from the base of the PR and between 6b95f35 and f68cccb.

📒 Files selected for processing (2)
  • src/components/shared/Header.jsx
  • src/components/shared/Pattern.jsx

Comment on lines 233 to +236
<button
type="button"
aria-label="Toggle dark mode"
className="group rounded-full bg-white/90 px-3 py-2 shadow-lg shadow-zinc-800/5 ring-1 ring-black/10 hover:ring-black/20 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
className="group relative flex h-10 w-10 items-center justify-center rounded-full bg-white/90 shadow-lg shadow-zinc-800/5 ring-1 ring-black/10 hover:ring-black/20 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Expose the current toggle state.

This is a two-state button, but it only exposes an action label right now. Adding aria-pressed makes the current mode understandable to assistive tech.

♿ Suggested change
 <button
   type="button"
   aria-label="Toggle dark mode"
+  aria-pressed={isDarkMode}
   className="group relative flex h-10 w-10 items-center justify-center rounded-full bg-white/90 shadow-lg shadow-zinc-800/5 ring-1 ring-black/10 hover:ring-black/20 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
   onClick={toggleMode}
 >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
type="button"
aria-label="Toggle dark mode"
className="group rounded-full bg-white/90 px-3 py-2 shadow-lg shadow-zinc-800/5 ring-1 ring-black/10 hover:ring-black/20 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
className="group relative flex h-10 w-10 items-center justify-center rounded-full bg-white/90 shadow-lg shadow-zinc-800/5 ring-1 ring-black/10 hover:ring-black/20 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
<button
type="button"
aria-label="Toggle dark mode"
aria-pressed={isDarkMode}
className="group relative flex h-10 w-10 items-center justify-center rounded-full bg-white/90 shadow-lg shadow-zinc-800/5 ring-1 ring-black/10 hover:ring-black/20 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/shared/Header.jsx` around lines 233 - 236, The toggle button
in Header.jsx currently only has an action label; update the button element (the
JSX <button> in the Header component) to expose its current state to assistive
tech by adding aria-pressed={isDarkMode} (or the boolean state variable that
represents dark mode in this component, e.g., isDark/isDarkTheme/darkMode) and
ensure that the boolean is derived from the same state used by the toggleTheme
handler so aria-pressed stays in sync with the visual mode.

Comment on lines +33 to +39
className="fill-green-300 dark:fill-yellow-300 animate-pulse transition-colors duration-500"
cx={size / 2}
cy={size / 2}
r={size / 2}
/>
<rect
className="fill-[#00843D] dark:fill-yellow-400"
className="fill-[#00843D] dark:fill-yellow-400 shadow-xl transition-colors duration-500"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

fd -t f "Pattern.jsx" --maxdepth 5

Repository: AOSSIE-Org/Website

Length of output: 138


🏁 Script executed:

git ls-files | grep -i pattern

Repository: AOSSIE-Org/Website

Length of output: 138


🏁 Script executed:

fd -t f "tailwind.config" -o "tailwind.js" | head -5

Repository: AOSSIE-Org/Website

Length of output: 203


🏁 Script executed:

wc -l src/components/shared/Pattern.jsx

Repository: AOSSIE-Org/Website

Length of output: 98


🏁 Script executed:

cat -n src/components/shared/Pattern.jsx

Repository: AOSSIE-Org/Website

Length of output: 3251


🏁 Script executed:

find . -maxdepth 2 -name "tailwind.config*" -o -name "tailwind.js"

Repository: AOSSIE-Org/Website

Length of output: 82


🏁 Script executed:

cat -n tailwind.config.js

Repository: AOSSIE-Org/Website

Length of output: 13659


🏁 Script executed:

grep -r "tailwindcss" package.json package-lock.json 2>/dev/null | head -10

Repository: AOSSIE-Org/Website

Length of output: 702


Wrap animations in motion-safe: and motion-reduce: variants to respect user motion preferences.

The animate-pulse effect on line 33, hover rotation effects on line 53, and hover scale/drop-shadow effects on line 64 run unconditionally. Users who disable motion in their operating system preferences will still see continuous animations. Use motion-safe: to gate these effects and motion-reduce: to optionally disable them explicitly, ensuring compliance with accessibility standards.

Suggested change
           <circle
-            className="fill-green-300 dark:fill-yellow-300 animate-pulse transition-colors duration-500"
+            className="fill-green-300 dark:fill-yellow-300 motion-safe:animate-pulse motion-reduce:animate-none transition-colors duration-500"
             cx={size / 2}
             cy={size / 2}
             r={size / 2}
           />
@@
-          const hoverRotation = (rowIndex + columnIndex) % 2 === 0 ? "group-hover:rotate-[15deg]" : "group-hover:-rotate-[15deg]"
+          const hoverRotation =
+            (rowIndex + columnIndex) % 2 === 0
+              ? 'motion-safe:group-hover:rotate-[15deg]'
+              : 'motion-safe:group-hover:-rotate-[15deg]'
@@
               <use
                 href={`#${id}-${shape}`}
                 x={x}
                 y={y}
                 style={{ transformOrigin: `${x + size / 2}px ${y + size / 2}px` }}
-                className={`transition-all duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] group-hover:scale-[1.4] ${hoverRotation} group-hover:drop-shadow-lg pointer-events-none`}
+                className={`transition-all duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] motion-safe:group-hover:scale-[1.4] ${hoverRotation} motion-safe:group-hover:drop-shadow-lg pointer-events-none`}
               />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/shared/Pattern.jsx` around lines 33 - 39, The Pattern
component is applying animations unconditionally (e.g., the circle/rect
classNames use animate-pulse and there are hover rotation/scale/drop-shadow
classes) which ignores users' reduced-motion preferences; update the Tailwind
className strings to prefix motion-safe: for the active animations (e.g.,
motion-safe:animate-pulse, motion-safe:hover:rotate-...,
motion-safe:hover:scale-..., motion-safe:hover:drop-shadow-...) and add
corresponding motion-reduce: fallbacks to disable them (e.g.,
motion-reduce:animate-none, motion-reduce:hover:rotate-0,
motion-reduce:hover:scale-100, motion-reduce:hover:drop-shadow-none) so the
Pattern component respects reduced-motion settings.

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.

1 participant