Skip to content

Conversation

@AnthonyFinney
Copy link
Contributor

@AnthonyFinney AnthonyFinney commented Nov 15, 2025

What Was Changed

Redesign Features section


Why Was It Changed

To make it look less generic.


Screenshots

Screenshot 2025-11-15 182945 image

Summary by CodeRabbit

  • New Features

    • Feature section is now an interactive, slide-based carousel with auto-advance, manual prev/next/jump controls, per-slide durations and progress indicators.
    • Feature panel upgraded to a richly styled, two-column responsive layout showing media on large screens, dynamic sizing across viewports, and a prominent CTA (http(s) links open in a new tab).
    • Smooth slide transitions, play/pause controls, and consistent layout scaling.
  • Style

    • Added Playfair Display and Source Sans 3 typography.

@vercel
Copy link

vercel bot commented Nov 15, 2025

@AnthonyFinney 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 Nov 15, 2025

Warning

Rate limit exceeded

@AnthonyFinney has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 8 minutes and 52 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b803161 and 9018e04.

📒 Files selected for processing (1)
  • components/FeaturesSection.tsx (1 hunks)

Walkthrough

Replaces the feature grid with a slide-based carousel and refactors FeatureCard into a forwardRef two-column panel with new CTA/media props; adds Playfair Display and Source Sans 3 fonts and Tailwind font entries.

Changes

Cohort / File(s) Change Summary
FeatureCard refactor
components/FeatureCard.tsx
Replaced icon-based small card with a forwardRef-wrapped, two-column feature panel. Public props now accept title/description as ReactNode, ctaHref, ctaText, media, isSlideChanging, styling refs/props and children. Removed legacy icon/link handling and internal URL resolution; CTA uses ctaHref and opens external http(s) links in a new tab.
FeaturesSection → Carousel
components/FeaturesSection.tsx
Replaced static grid with a slide-driven carousel: added SlideSpec/SLIDES, per-slide durations, slide index/playback/progress state, auto-advance timers, jump/prev/next controls, transition coordination via isSlideChanging, responsive sizing (refs/ResizeObserver), and rendering of the active slide using FeatureCard.
Typography / global styles
styles/globals.css, tailwind.config.js
Added Google Fonts import for Playfair Display and Source Sans 3 in styles/globals.css and extended Tailwind theme.extend.fontFamily with playfair and source-sans.

Possibly related PRs

  • [FEAT] - Adding link to features to doc #70: Modifies components/FeatureCard.tsx link/CTA handling and default URL logic, which directly overlaps this PR’s removal of the legacy link prop and introduction of ctaHref/ctaText.

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)
tailwind.config.js (1)

11-15: Font family additions are correct; consider dropping inner quotes for consistency

playfair and source-sans will work as-is, but you can simplify to ['Playfair Display', 'serif'] and `['Source Sans 3', 'sans-serif'] to match the existing Inter entries and Tailwind docs, and avoid embedding extra quotes in the generated CSS.

styles/globals.css (1)

1-2: Global font import is aligned; next/font could be a future optimization

The Google Fonts @import correctly matches the playfair and source-sans families defined in Tailwind. If you later want to tune performance and self-hosting/privacy, consider migrating these to Next.js next/font, but it’s not required for this PR.

components/FeaturesSection.tsx (1)

137-159: Layout scaling effect is clever but a bit brittle

The requestAnimationFrame + manual padding/math to compute leftScale and leftScaledHeight works, but it’s fairly imperative and tied to the card’s exact padding and height (- 8 adjustment, fixed md/lg heights).

If this area ever gets more dynamic (content/spacing changes), consider replacing this with a ResizeObserver on the left content container, or relaxing the fixed md:h / lg:h constraints and letting the card height grow naturally. For now it’s fine, just something to watch.

Also applies to: 171-179

📜 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 b8dbed1 and bc0912c.

⛔ Files ignored due to path filters (9)
  • public/gifs/demo.gif is excluded by !**/*.gif
  • public/images/automatedActions.jpg is excluded by !**/*.jpg
  • public/images/infrastructureManagement.jpg is excluded by !**/*.jpg
  • public/images/logAggregation.jpg is excluded by !**/*.jpg
  • public/images/openSource.jpg is excluded by !**/*.jpg
  • public/images/real-timeMetrics.jpg is excluded by !**/*.jpg
  • public/images/serviceDiscovery.jpg is excluded by !**/*.jpg
  • public/images/smartAlerts.jpg is excluded by !**/*.jpg
  • public/images/unifiedMonitoring.jpg is excluded by !**/*.jpg
📒 Files selected for processing (4)
  • components/FeatureCard.tsx (1 hunks)
  • components/FeaturesSection.tsx (1 hunks)
  • styles/globals.css (2 hunks)
  • tailwind.config.js (1 hunks)
🔇 Additional comments (1)
components/FeatureCard.tsx (1)

18-37: ForwardRef FeatureCard API and layout look solid

The refactor to a forwardRef-based, two-column panel with explicit media and CTA props is clean and composes well with the FeaturesSection logic. External link handling via isExternal + rel="noopener noreferrer" is also correct, and the left* props give enough hooks for layout control without leaking implementation details.

Also applies to: 47-81, 84-89

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

🧹 Nitpick comments (2)
components/FeaturesSection.tsx (2)

214-241: Active slide indicator is visually de-emphasized when autoplay is off

When isPlaying is false (e.g., on mobile), progress stays 0, so the active slide’s bar has width: 0% while all inactive bars render at width: 100% (just with lower opacity). That can make the active slide look “empty” compared to others.

Consider making the active bar full-width when not playing so it’s still visually highlighted:

- style={{ width: slide === i ? `${Math.round(progress * 100)}%` : '100%' }}
+ style={{
+   width:
+     slide === i
+       ? (isPlaying ? `${Math.round(progress * 100)}%` : '100%')
+       : '100%',
+ }}

This keeps the animated fill behavior during autoplay while ensuring the active slide is clearly marked when playback is paused or disabled.


215-243: Extract repeated prev/next handlers for clarity

The inline onClick handlers for Previous and Next both perform the same sequence (setIsSlideChanging(true), update slide, reset progress, setTimeout to clear isSlideChanging). Extracting these into goToPrevious / goToNext helpers would deduplicate logic and make the JSX easier to scan, without behavior changes.

📜 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 bc0912c and bbf41f5.

📒 Files selected for processing (1)
  • components/FeaturesSection.tsx (1 hunks)
🔇 Additional comments (3)
components/FeaturesSection.tsx (3)

27-102: Slides config and alt text look solid

The slide data is cohesive, and each imageAlt is now specific to the feature, which resolves the earlier generic-alt accessibility concern. No changes needed here.


104-121: Autoplay behavior and timer management look correct

Using matchMedia('(min-width: 1024px)') to drive isPlaying cleanly disables autoplay on small screens while keeping it on for desktop, and the interval setup/cleanup avoids timer leaks. This also addresses the prior “no pause on mobile” UX issue by not auto-playing there.

Also applies to: 126-146


155-177: Responsive left-column scaling effect is defensive and safe

The resize effect gracefully handles missing refs and guards against non-finite/negative scale values with a sane fallback to 1, which should keep the layout stable across resizes and different slide heights.

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

🧹 Nitpick comments (3)
components/FeaturesSection.tsx (3)

27-102: Static slide config is clear; consider lifting it outside the component

The slides array is fully static and wrapped in useMemo with an empty dependency list, so its identity is already stable. You could optionally move this array to a top‑level const SLIDES: SlideSpec[] = [...] and just reference it inside the component to remove one hook and keep the render function a bit leaner. Not required, but it slightly simplifies the component structure.


126-165: Slide transition timeout could be centralized and cleaned up

isSlideChanging is flipped with a setTimeout both in the autoplay interval and in runSlideTransition. That works, but it means there can be pending timeouts when the component unmounts or when users click quickly, which can trigger late state updates.

If you want to tighten this up, consider:

  • Storing the transition timeout handle in a useRef<number | null> and clearing it in the useEffect cleanup (alongside the intervals) and before scheduling a new timeout.
  • Optionally routing the autoplay advance through a single helper (e.g., a ref-backed startTransition function) that encapsulates setIsSlideChanging, setSlide, setProgress(0), and the timeout.

This would DRY up the transition logic and ensure no stray timeouts outlive the component.


226-263: Carousel controls are accessible; minor ARIA polish possible

The prev/next and play/pause controls are generally accessible, and the slide indicators are keyboard‑focusable buttons. Two small, optional tweaks you might consider:

  • For prev/next, you can drop the inner span.sr-only since aria-label already provides the name; keeping both is redundant.
  • For the indicator buttons, aria-label={Slide ${i + 1}} works, but you could make it more descriptive by including the feature name (e.g., aria-label={\Go to feature: ${slides[i].heading}`}`) so screen reader users get more context.

Pure polish; the current implementation is already usable.

📜 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 bbf41f5 and 1a45535.

📒 Files selected for processing (1)
  • components/FeaturesSection.tsx (1 hunks)
🔇 Additional comments (2)
components/FeaturesSection.tsx (2)

104-120: Viewport‑driven autoplay initialization looks solid

Nice use of matchMedia with both addEventListener and addListener branches plus the initial setIsPlaying(mediaQuery.matches). This cleanly disables autoplay on small screens while enabling it on desktop and keeps behavior in sync on resize. No changes needed here.


167-189: Layout scaling effect and cleanup look correct

The resize/layout effect correctly guards for missing refs, computes padding‑aware available height, and cleans up both the requestAnimationFrame and resize listener on dependency changes and unmount. Given it only touches window inside useEffect, this should be safe for SSR/hydration as well. No changes needed.

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

🧹 Nitpick comments (1)
components/FeaturesSection.tsx (1)

165-187: Consider optimizing the scale calculation effect.

The current implementation re-adds the resize listener every time the slide changes. While functional, this is inefficient. The recompute function should run when the slide changes, but the listener itself only needs to be set up once.

Refactor to separate the one-time listener setup from the slide-triggered recompute:

+ useEffect(() => {
+   const recompute = () => {
+     const hero = heroRef.current;
+     const inner = leftContentRef.current;
+     if (!hero || !inner) return;
+     const styles = window.getComputedStyle(hero);
+     const padT = parseFloat(styles.paddingTop || '0');
+     const padB = parseFloat(styles.paddingBottom || '0');
+     const availableH = hero.clientHeight - padT - padB - 8;
+     const neededH = inner.scrollHeight;
+     const sRaw = Math.min(1, availableH / neededH);
+     const s = Number.isFinite(sRaw) && sRaw > 0 ? sRaw : 1;
+     setLeftScale(s);
+     setLeftScaledHeight(Math.round(neededH * s));
+   };
+   recompute();
+ }, [slide]);
+
  useEffect(() => {
    const recompute = () => {
      const hero = heroRef.current;
      const inner = leftContentRef.current;
      if (!hero || !inner) return;
      const styles = window.getComputedStyle(hero);
      const padT = parseFloat(styles.paddingTop || '0');
      const padB = parseFloat(styles.paddingBottom || '0');
      const availableH = hero.clientHeight - padT - padB - 8;
      const neededH = inner.scrollHeight;
      const sRaw = Math.min(1, availableH / neededH);
      const s = Number.isFinite(sRaw) && sRaw > 0 ? sRaw : 1;
      setLeftScale(s);
      setLeftScaledHeight(Math.round(neededH * s));
    };
-   const id = requestAnimationFrame(recompute);
-   const onResize = () => recompute();
+   const onResize = () => {
+     const id = requestAnimationFrame(recompute);
+     return () => cancelAnimationFrame(id);
+   };
    window.addEventListener('resize', onResize);
    return () => {
-     cancelAnimationFrame(id);
      window.removeEventListener('resize', onResize);
    };
- }, [slide]);
+ }, []);

This way the resize listener is registered once on mount, and the recompute logic in the separate effect runs whenever the slide changes.

📜 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 1a45535 and 0ebccdf.

📒 Files selected for processing (1)
  • components/FeaturesSection.tsx (1 hunks)
🔇 Additional comments (2)
components/FeaturesSection.tsx (2)

102-118: Responsive autoplay implementation looks good.

The media query effect properly addresses the past review concern by disabling autoplay on small screens. The fallback for older browser APIs (addListener/removeListener) ensures broad compatibility, and cleanup is handled correctly for both paths.


189-264: Well-structured carousel controls and accessibility.

The render implementation demonstrates good practices:

  • Proper semantic HTML with accessibility features (aria-labels on all interactive controls)
  • Decorative elements correctly marked with aria-hidden
  • Responsive control visibility: play/pause hidden on small screens while keeping manual navigation available
  • Progress indicators provide clear visual feedback
  • Image optimization with priority for first slide and proper sizes attribute

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

🧹 Nitpick comments (1)
components/FeaturesSection.tsx (1)

90-97: Consider more portable types for timer refs

Using NodeJS.Timeout in refs can be awkward in environments that combine DOM and Node typings (e.g., Next.js), where setInterval/setTimeout may be typed as number. To avoid type friction, you can lean on ReturnType:

-  const timerRef = useRef<NodeJS.Timeout | null>(null);
-  const progressRef = useRef<NodeJS.Timeout | null>(null);
-  const transitionRef = useRef<NodeJS.Timeout | null>(null);
+  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
+  const progressRef = useRef<ReturnType<typeof setInterval> | null>(null);
+  const transitionRef = useRef<ReturnType<typeof setTimeout> | null>(null);

This stays correct regardless of whether the global timer functions resolve to number or Timeout.

📜 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 0ebccdf and b803161.

📒 Files selected for processing (1)
  • components/FeaturesSection.tsx (1 hunks)
🔇 Additional comments (2)
components/FeaturesSection.tsx (2)

15-88: Slide metadata and alt text look solid

The SLIDES definitions read well, have specific, non-redundant imageAlt values, and the descriptions/CTAs are consistent. This nicely addresses the earlier generic-alt concern while keeping the data structure clean and extensible.


103-147: Autoplay behavior and transition cleanup look good

The media-query–driven isPlaying initialization and listener cleanly restrict autoplay to lg+ viewports, which addresses the earlier mobile-pause concern without exposing extra controls on small screens. The use of transitionRef in both the autoplay interval and runSlideTransition ensures slide transitions are debounced and that timeouts are not left dangling across transitions or unmounts.

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