Skip to content

Conversation

@MohammadRehaanDev
Copy link
Contributor

@MohammadRehaanDev MohammadRehaanDev commented Oct 31, 2025

Issue Reference

Closes #[Issue Number]

What Was Changed

This PR introduces responsive behavior to the Features section by implementing a Show More/Show Less toggle on mobile devices.

The changes include:

Creation of a custom useIsMobile React hook to reliably detect screen width.

Conditional rendering in FeaturesSection.tsx to display only the first 4 feature cards by default on screens under 768px (mobile).

Addition of the toggle button functionality using React useState.

Adjustment of the Tailwind gap utility for a more compact mobile layout (gap-4 md:gap-6).

Why Was It Changed

The original Features section displayed all 8 feature cards stacked vertically on mobile, causing the page to feel overloaded and requiring excessive scrolling, which hurts user experience and engagement.

This change improves mobile UX by showing key highlights first, making the section compact and visually balanced without affecting the original desktop layout.

Screenshot 2025-10-31 at 10 45 37 PM Screenshot 2025-10-31 at 10 46 08 PM

Summary by CodeRabbit

  • New Features
    • Added mobile-responsive behavior to the features section with a "Show More/Show Less" button.
    • On mobile devices, the features section now displays a limited set of features initially.
    • Users can expand to view all available features with a toggle button.

@vercel
Copy link

vercel bot commented Oct 31, 2025

@MohammadRehaanDev is attempting to deploy a commit to the idan lodzki's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Oct 31, 2025

Walkthrough

A new custom hook useIsMobile detects mobile viewports against a configurable breakpoint. The FeaturesSection component was updated to conditionally display features on mobile with an expandable Show More/Show Less toggle button. Grid rendering logic was adjusted for mobile responsiveness.

Changes

Cohort / File(s) Summary
Mobile Responsiveness Implementation
components/FeaturesSection.tsx, hooks/useIsMobile.ts
FeaturesSection.tsx: React imports updated with useState; useIsMobile hook integrated for mobile detection; MOBILE_VISIBLE_COUNT constant and isExpanded state added; featuresToDisplay computed conditionally based on mobile state; handleToggle callback added; Show More/Show Less button rendered on mobile when feature count exceeds threshold; grid gap adjusted. useIsMobile.ts: New custom hook that detects whether viewport width is below breakpoint (default 768px); uses SSR-safe state initialization with window resize listener; performs initial check and cleanup on unmount; exports hook as default.

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: 2

🧹 Nitpick comments (3)
hooks/useIsMobile.ts (1)

3-20: Consider using CSS media queries or matchMedia API for better performance.

While the custom hook works, the window.matchMedia API is more performant and better suited for responsive breakpoint detection. It only fires when the media query condition changes, not on every pixel of resize.

Here's an alternative implementation:

-const useIsMobile = (breakpoint = 768) => {
-  const [isMobile, setIsMobile] = useState(
-      typeof window !== 'undefined' ? window.innerWidth < breakpoint : false
-  );
+const useIsMobile = (breakpoint = 768) => {
+  const [isMobile, setIsMobile] = useState(() => {
+    if (typeof window === 'undefined') return false;
+    return window.matchMedia(`(max-width: ${breakpoint - 1}px)`).matches;
+  });

  useEffect(() => {
-   const checkMobile = () => {
-     setIsMobile(window.innerWidth < breakpoint);
-   };
+   const mediaQuery = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
+   
+   const handleChange = (e: MediaQueryListEvent) => {
+     setIsMobile(e.matches);
+   };

-   window.addEventListener('resize', checkMobile);
-   checkMobile();
+   mediaQuery.addEventListener('change', handleChange);
+   // Sync initial state
+   setIsMobile(mediaQuery.matches);

-   return () => window.removeEventListener('resize', checkMobile);
+   return () => mediaQuery.removeEventListener('change', handleChange);
  }, [breakpoint]);

  return isMobile;
};
components/FeaturesSection.tsx (2)

69-71: Consider adding scroll behavior when collapsing.

When users click "Show Less", they might be scrolled below the visible features, creating a jarring experience. Consider scrolling to the features section when collapsing.

Update the handleToggle function:

  const handleToggle = () => {
    setIsExpanded(!isExpanded);
+   
+   // Scroll to features section when collapsing
+   if (isExpanded) {
+     const featuresSection = document.getElementById('features');
+     if (featuresSection) {
+       featuresSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
+     }
+   }
  };

89-98: Consider adding smooth height transitions.

The instant appearance/disappearance of features could be jarring. Adding CSS transitions would improve the user experience.

You could add a CSS transition by:

  1. Using a CSS class with transition properties
  2. Or using a library like framer-motion for smooth animations

Example with CSS:

-        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
+        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6 transition-all duration-300">

And wrap the feature cards in a container that handles the transition:

{featuresToDisplay.map((feature, index) => (
  <div key={index} className="animate-fadeIn">
    <FeatureCard
      icon={feature.icon}
      title={feature.title}
      description={feature.description}
    />
  </div>
))}

Add to your CSS:

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.animate-fadeIn {
  animation: fadeIn 0.3s ease-out;
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2cf3ac5 and 59eb9c3.

📒 Files selected for processing (2)
  • components/FeaturesSection.tsx (5 hunks)
  • hooks/useIsMobile.ts (1 hunks)
🔇 Additional comments (3)
components/FeaturesSection.tsx (3)

1-2: Imports look good.

The imports are correctly structured with useState for managing expansion state and the custom useIsMobile hook.


14-19: State management and constants are well-structured.

The MOBILE_VISIBLE_COUNT constant makes the code maintainable, and the state initialization is appropriate.


64-74: Logic for conditional display is correct.

The implementation correctly slices the features array on mobile when not expanded and properly determines when the toggle button should be shown.

Comment on lines +100 to +109
{requiresToggle && (
<div className="flex justify-center mt-8 md:hidden">
<button
onClick={handleToggle}
className="py-2 px-6 text-sm font-semibold rounded-lg bg-primary-600 text-white hover:bg-primary-700 transition-colors duration-300"
>
{isExpanded ? 'Show Less Features' : 'Show More Features'}
</button>
</div>
)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add accessibility attributes to the toggle button.

The toggle button is missing ARIA attributes that are essential for screen reader users to understand the button's purpose and state.

Apply this diff to add proper accessibility:

        {requiresToggle && (
            <div className="flex justify-center mt-8 md:hidden"> 
                <button
                    onClick={handleToggle}
+                   aria-expanded={isExpanded}
+                   aria-controls="features-grid"
+                   aria-label={isExpanded ? 'Show fewer features' : 'Show more features'}
                    className="py-2 px-6 text-sm font-semibold rounded-lg bg-primary-600 text-white hover:bg-primary-700 transition-colors duration-300"
                >
                    {isExpanded ? 'Show Less Features' : 'Show More Features'}
                </button>
            </div>
        )}

Also add the corresponding id to the features grid:

-        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
+        <div id="features-grid" className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
📝 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
{requiresToggle && (
<div className="flex justify-center mt-8 md:hidden">
<button
onClick={handleToggle}
className="py-2 px-6 text-sm font-semibold rounded-lg bg-primary-600 text-white hover:bg-primary-700 transition-colors duration-300"
>
{isExpanded ? 'Show Less Features' : 'Show More Features'}
</button>
</div>
)}
{requiresToggle && (
<div className="flex justify-center mt-8 md:hidden">
<button
onClick={handleToggle}
aria-expanded={isExpanded}
aria-controls="features-grid"
aria-label={isExpanded ? 'Show fewer features' : 'Show more features'}
className="py-2 px-6 text-sm font-semibold rounded-lg bg-primary-600 text-white hover:bg-primary-700 transition-colors duration-300"
>
{isExpanded ? 'Show Less Features' : 'Show More Features'}
</button>
</div>
)}
🤖 Prompt for AI Agents
In components/FeaturesSection.tsx around lines 100 to 109, the toggle button
lacks ARIA attributes and the features grid has no id; add accessibility by
giving the features grid a stable id (e.g., id="features-grid") and update the
button to include aria-expanded={isExpanded} and aria-controls="features-grid"
plus a descriptive aria-label (e.g., aria-label={isExpanded ? 'Collapse
features' : 'Expand features'}) so screen readers know the button's purpose and
state.

Comment on lines +8 to +17
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < breakpoint);
};

window.addEventListener('resize', checkMobile);
checkMobile();

return () => window.removeEventListener('resize', checkMobile);
}, [breakpoint]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add debounce/throttle to the resize event listener.

The resize event can fire 100+ times per second during active window resizing, causing excessive state updates and re-renders. This degrades performance, especially on lower-end mobile devices.

Apply this diff to add a debounced resize handler:

  useEffect(() => {
+   let timeoutId: NodeJS.Timeout;
+   
    const checkMobile = () => {
-     setIsMobile(window.innerWidth < breakpoint);
+     clearTimeout(timeoutId);
+     timeoutId = setTimeout(() => {
+       setIsMobile(window.innerWidth < breakpoint);
+     }, 150);
    };

    window.addEventListener('resize', checkMobile);
-   checkMobile();
+   // Initial check without debounce
+   setIsMobile(window.innerWidth < breakpoint);

-   return () => window.removeEventListener('resize', checkMobile);
+   return () => {
+     window.removeEventListener('resize', checkMobile);
+     clearTimeout(timeoutId);
+   };
  }, [breakpoint]);
📝 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
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < breakpoint);
};
window.addEventListener('resize', checkMobile);
checkMobile();
return () => window.removeEventListener('resize', checkMobile);
}, [breakpoint]);
useEffect(() => {
let timeoutId: NodeJS.Timeout;
const checkMobile = () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
setIsMobile(window.innerWidth < breakpoint);
}, 150);
};
window.addEventListener('resize', checkMobile);
// Initial check without debounce
setIsMobile(window.innerWidth < breakpoint);
return () => {
window.removeEventListener('resize', checkMobile);
clearTimeout(timeoutId);
};
}, [breakpoint]);
🤖 Prompt for AI Agents
In hooks/useIsMobile.ts around lines 8 to 17, the resize handler calls
setIsMobile on every resize event which can fire many times; debounce (or
throttle) the checkMobile function to limit updates. Create a debounced version
of checkMobile (using a small utility debounce or lodash.debounce) with a
sensible delay (e.g., 100–200ms), attach the debounced handler to the resize
event, call the debounced handler once immediately (or call the original
checkMobile once for initial state), and ensure the cleanup removes the
debounced listener and cancels any pending debounce timer on unmount to prevent
memory leaks.

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