Skip to content

765-fix: Fix styles active menu item #814

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Jun 5, 2025

Conversation

JuliaAnt
Copy link
Collaborator

@JuliaAnt JuliaAnt commented Mar 12, 2025

What type of PR is this? (select all that apply)

  • πŸ• Feature
  • πŸ› Bug Fix
  • 🚧 Breaking Change
  • πŸ§‘β€πŸ’» Code Refactor
  • πŸ“ Documentation Update

Description

Rework desktop and mobile menus according to the new design.

Related Tickets & Documents

Screenshots, Recordings

Before:

Screenshot 2025-04-12 at 23 03 28

After:

Screenshot 2025-04-12 at 23 04 21

Added/updated tests?

  • πŸ‘Œ Yes
  • πŸ™…β€β™‚οΈ No, because they aren't needed
  • πŸ™‹β€β™‚οΈ No, because I need help

[optional] Are there any post deployment tasks we need to perform?

[optional] What gif best describes this PR or how it makes you feel?

Feel free to hit me with your best practices :))

so-little-work-i-done

Summary by CodeRabbit

  • New Features

    • Added interactive, collapsible navigation menus for desktop and mobile, including new "Support Us" and "Mentors Wanted" sections.
    • Introduced a "small" font size variant for paragraphs and enhanced breadcrumb and logo components with new customization options.
    • Added a custom hook for detecting outside clicks and improved keyboard accessibility in navigation.
    • Introduced a new media query mixin for large laptop viewports.
  • Improvements

    • Redesigned header and navigation for a more consistent, accessible, and visually appealing experience.
    • Enhanced menu layouts with grid and column support, improved dropdown transitions, and responsive styling.
    • Updated donation options with clearer, distinct labels and icons for buttons and menus.
    • Refined logo sizing for consistent display across devices.
    • Improved dropdown and nav item styling for better focus and hover states.
    • Enhanced mobile menu structure with dynamic accordion-style navigation and state management.
    • Added new navigation menu labels constant for consistent labeling.
    • Improved school menu layout with transition-enabled show/hide states and flexible grid options.
    • Enhanced support section with updated icon rendering and descriptive content.
  • Bug Fixes

    • Improved accessibility and keyboard navigation throughout navigation components.
    • Fixed test coverage and expectations to align with updated navigation structure and UI.
  • Chores

    • Refactored and streamlined code for better maintainability, including removal of unused features and improved state management.
    • Updated test mocks and simplified test implementations for header and footer components.

@JuliaAnt JuliaAnt self-assigned this Mar 12, 2025
@github-actions github-actions bot changed the title fix: 765 - change style of active menu item. Fix lighthouse problem 765-fix: Fix styles active menu item Mar 12, 2025
@SpaNb4
Copy link
Collaborator

SpaNb4 commented Mar 13, 2025

The arrow's focus state could be improved
image

This isn't due to your changes, but the spacing is inconsistent here
image

Copy link

Lighthouse Report:

  • Performance: 97 / 100
  • Accessibility: 96 / 100
  • Best Practices: 100 / 100
  • SEO: 100 / 100

View detailed report

Copy link
Collaborator

@SpaNb4 SpaNb4 left a comment

Choose a reason for hiding this comment

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

Keyboard navigation of submenus seems broken to me. First Enter/Space press redirects the page, second opens the menu

2025-03-15.12-16-29.mp4

Copy link
Collaborator

@ansivgit ansivgit left a comment

Choose a reason for hiding this comment

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

image

For a non-yellow background of the hero-banner, the underline blends in with the image. Also, I think the current page should be highlighted.

@YulikK YulikK changed the title 765-fix: Fix styles active menu item DRAFT - 765-fix: Fix styles active menu item Mar 18, 2025
Copy link

Lighthouse Report:

  • Performance: 96 / 100
  • Accessibility: 96 / 100
  • Best Practices: 100 / 100
  • SEO: 100 / 100

View detailed report

@SpaNb4
Copy link
Collaborator

SpaNb4 commented May 27, 2025

Noticed some issue with the new ai course
image

Copy link
Contributor

coderabbitai bot commented May 27, 2025

πŸ“ Walkthrough

Walkthrough

This update refactors navigation menus and related UI components. It introduces new menu layouts, improves accessibility and keyboard interaction, adds new color variables and mixins, updates donation data structures, and enhances test coverage. Several components receive new props or styling, and menu logic shifts to a click-to-open model with improved active state visuals.

Changes

Files/Groups Change Summary
dev-data/donate-options.data.ts, src/widgets/support/ui/support.tsx Donation options restructured: split labels and icons for button/menu, replaced string icons with React/SVG imports.
dev-data/school-menu-links.ts Added "Support Us" and "Mentors Wanted" links; renamed "About" to "About Community".
src/core/styles/_constants.scss, src/core/styles/_mixins.scss Added new $large-laptop-width variable and media-laptop-large mixin.
src/shared/constants.ts Expanded KEY_CODES with Enter and Space; added NAV_MENU_LABELS constant.
src/shared/hooks/use-outside-click/use-outside-click.ts Added useOutsideClick hook for detecting clicks outside referenced elements.
src/shared/ui/logo/logo.module.scss, src/shared/ui/logo/logo.tsx Adjusted logo size; Logo component now accepts dynamic icon and onClick props.
src/shared/ui/paragraph/paragraph.module.scss, src/shared/ui/paragraph/paragraph.tsx Added "small" font size variant and corresponding styles.
src/widgets/breadcrumbs/ui/breadcrumbs.module.scss, src/widgets/breadcrumbs/ui/breadcrumbs.tsx Breadcrumbs component accepts optional className prop; added responsive styles.
src/widgets/footer/footer.test.tsx Added mock for next/navigation and minor JSX formatting changes in tests.
src/widgets/header/header.module.scss Simplified navbar styles; updated menu gaps; added .support-text style.
src/widgets/header/header.test.tsx Added navigation mocks; updated test queries and expectations; removed obsolete tests.
src/widgets/header/header.tsx Removed scroll-based header color logic; restructured menus; added support and mentorship menu improvements.
src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss, src/widgets/header/ui/dropdown/dropdown-wrapper.tsx Converted dropdown to grid layout; added reverseLayout prop; removed mouse event handlers.
src/widgets/header/ui/nav-item/nav-item.module.scss, src/widgets/header/ui/nav-item/nav-item.tsx Refactored NavItem to use button; added icon and reverseLayout props; improved accessibility and dropdown logic.
src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss, src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx Added MobileNavItem component and styles for mobile navigation.
src/widgets/mobile-view/ui/mobile-view.module.scss, src/widgets/mobile-view/ui/mobile-view.tsx Refactored MobileView to dynamic collapsible menu with state management and icon logic.
src/widgets/school-menu/ui/school-item/school-item.module.scss, src/widgets/school-menu/ui/school-item/school-item.tsx Added conditional styling and accessibility for clickable/non-clickable school items.
src/widgets/school-menu/ui/school-menu/school-menu.module.scss, src/widgets/school-menu/ui/school-menu/school-menu.tsx, src/widgets/school-menu/ui/school-menu/school-menu.test.tsx Added layout and visibility props; implemented grid/flex layouts and transitions; updated tests.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant NavItem
    participant DropdownWrapper
    participant useOutsideClick
    participant Router

    User->>NavItem: Clicks menu button
    NavItem->>NavItem: Toggle dropdown state
    NavItem->>DropdownWrapper: Render dropdown (isOpen)
    NavItem->>useOutsideClick: Listen for outside clicks
    useOutsideClick-->>NavItem: On outside click, close dropdown
    User->>NavItem: Presses Enter/Space/Escape
    NavItem->>NavItem: Handle keyboard navigation
    NavItem->>Router: Navigate if not dropdown
Loading

Assessment against linked issues

Objective (Issue #) Addressed Explanation
Make active menu item text readable and update arrow accessibility (#765) βœ…
Change desktop menu to open only on click, not hover (#660) βœ…

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes detected.

Possibly related PRs

Suggested labels

refactor

Suggested reviewers

  • andron13
  • dzmitry-varabei
  • ansivgit
  • natanchik
  • SpaNb4
  • Quiddlee

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

πŸ”§ ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-05T16_39_40_111Z-debug-0.log


πŸ“œ Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between d875abc and 9d7629d.

πŸ“’ Files selected for processing (2)
  • src/widgets/school-menu/ui/school-menu/school-menu.module.scss (2 hunks)
  • src/widgets/school-menu/ui/school-menu/school-menu.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/widgets/school-menu/ui/school-menu/school-menu.tsx
  • src/widgets/school-menu/ui/school-menu/school-menu.module.scss
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build rs.school
  • GitHub Check: CI
✨ Finishing Touches
  • πŸ“ Generate Docstrings

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
πŸͺ§ Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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

πŸ”­ Outside diff range comments (1)
src/widgets/header/header.module.scss (1)

91-100: πŸ’‘ Verification agent

🧩 Analysis chain

Verify mobile menu opacity transition consistency.

The opacity transitions should align with the mobile menu open/close behavior. Ensure the opacity values work correctly with the transform animations.


🏁 Script executed:

#!/bin/bash
# Description: Check mobile menu interaction patterns and transition consistency
# Expected: Find mobile menu state management and transition usage

rg -A 5 -B 5 'mobile.*menu.*open\|\.open.*mobile' src/widgets/
ast-grep --pattern 'className={$$$mobile$$$}'

Length of output: 112


🏁 Script executed:

#!/bin/bash
# Show SCSS context around .mobile-menu rules
rg -n -C5 '\.mobile-menu' src/widgets/header/header.module.scss

# Find where the mobile-menu class is toggled in the React component
rg -n -C3 'mobile-menu' src/widgets/header

Length of output: 3036


Fix mobile menu display and opacity transitions

The .mobile-menu.open rule currently sets display: none, which prevents any transform or opacity animation from ever showing. To allow a smooth reveal:

β€’ In src/widgets/header/header.module.scss (around lines 70–100):
– Remove or override display: none in the open state (e.g. use display: block).
– Ensure the closed state has opacity: 0 and the open state has opacity: 1.
– Add an opacity transition alongside the transform.

Suggested update:

 .mobile-menu {
-  transform: translateY(-100%);
+  transform: translateY(-100%);
+  opacity: 0;
+  display: none;
+  transition: transform 0.4s ease-out, opacity 0.4s ease-out;
 }

 .mobile-menu.open {
-  transform: translateY(0);
-  display: none;
-  opacity: 1;
+  transform: translateY(0);
+  opacity: 1;
+  display: block;
 }
♻️ Duplicate comments (9)
src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx (1)

29-35: Fix hardcoded alt text for dynamic icon usage.

The alt text is hardcoded as "Donate-icon" but should be dynamic based on the icon being displayed, as previously suggested.

Apply this fix to make the alt text contextual:

-            alt="Donate-icon"
+            alt={`${title} icon`}
src/widgets/mobile-view/ui/mobile-view.module.scss (2)

4-12: Remove default flex value and consider past feedback.

The align-items: flex-end should be sufficient here without explicitly setting the default flex-start behavior mentioned in past comments.


47-57: Use color constants for border colors.

The divider implementations use hard-coded color variables that should leverage existing constants as suggested in past feedback.

src/widgets/header/header.tsx (3)

8-8: Import path needs update

The import path for donateOptions should be from the data barrel export.


49-49: Previous suggestion addressed

The removal of dynamic class names addresses the earlier suggestion about removing the white-class when only one color is used.


155-155: Consider component decomposition

The Header component has grown significantly with the addition of the Support Us menu. Consider splitting each NavItem into separate components as previously suggested.

src/widgets/mobile-view/ui/mobile-view.tsx (2)

10-10: Import path needs update

The import path for donateOptions should be from the data barrel export.


242-242: Component has grown significantly

As suggested previously, consider splitting this large component into smaller, more manageable pieces. Each menu section could be its own component.

src/widgets/header/ui/nav-item/nav-item.module.scss (1)

115-115: Consider using color constant

The hover color $color-gray-450 should be defined as a constant as previously suggested.

🧹 Nitpick comments (10)
src/shared/ui/logo/logo.module.scss (2)

3-4: Consistent logo sizing across breakpoints
Default logo dimensions are now 48px but identical values are repeated in the media‐tablet‐large block. Consider extracting these into a variable or removing the redundant declarations to adhere to DRY principles.


12-13: Redundant declarations
The width and height here mirror the default styles. Unless future breakpoints require different sizing, you can safely remove these rules.

src/shared/hooks/use-outside-click/use-outside-click.ts (1)

3-7: Consider using useCallback for the callback parameter.

The hook works correctly, but including the callback in the dependency array could cause unnecessary re-renders if it's not memoized in consuming components.

Consider documenting that the callback should be wrapped with useCallback:

+/**
+ * Hook to detect clicks outside a referenced element
+ * @param ref - Reference to the element
+ * @param callback - Function to call on outside click (should be memoized with useCallback)
+ * @param isDropdownOpen - Whether the dropdown is currently open
+ */
 export function useOutsideClick<T extends HTMLElement>(
src/widgets/breadcrumbs/ui/breadcrumbs.module.scss (1)

37-43: Simplify padding declarations.

The mobile breadcrumb styles look good, but the padding can be streamlined.

 .mobile-breadcrumbs.breadcrumbs-content {
   @include media-tablet-large {
     width: 100%;
-    padding: 6px;
-    padding-left: 0;
+    padding: 6px 6px 6px 0;
   }
 }
src/widgets/school-menu/ui/school-item/school-item.tsx (2)

28-28: Consider using a more explicit check for non-clickable URLs.

The Boolean(url === '#') could be simplified to just url === '#' since the comparison already returns a boolean.

-  const isNonClickable = Boolean(url === '#');
+  const isNonClickable = url === '#';

31-31: Potential className conflict issue.

Using cx(props.className) may not properly merge class names if props.className contains multiple classes. Consider using cx(props.className) only if it's a single class, or handle multiple classes appropriately.

-    <li {...props} className={cx(props.className)}>
+    <li {...props} className={cx(props.className)}>

Actually, this should work correctly since cx() can handle multiple class names. The current implementation looks fine.

src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx (1)

39-45: Remove redundant role attribute.

The role="button" is redundant since the dropdown arrow is already contained within a <button> element.

       <span
         className={cx('dropdown-arrow', { rotate: isDropdownActive })}
-        role="button"
         aria-expanded={isDropdownActive}
       >
src/widgets/mobile-view/ui/mobile-view.tsx (1)

216-228: Consider alternative approach for non-interactive items

The descriptive text items use url="#" which creates non-functional links. Consider using a different component or prop to indicate these are informational items only.

-            <SchoolMenu.Item
-              className="support-title"
-              title="Your donations help us cover hosting, domains, licenses, and advertising for courses
-                          and events. Every donation, big or small, helps!"
-              url="#"
-              color={color}
-            />
+            <SchoolMenu.InfoItem
+              className="support-title"
+              title="Your donations help us cover hosting, domains, licenses, and advertising for courses
+                          and events. Every donation, big or small, helps!"
+              color={color}
+            />
src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss (1)

55-68: Consider the visual impact of the decorative element

The white ::before element might not be visible against light backgrounds. Consider if this provides the intended visual effect in all scenarios.

src/widgets/header/ui/nav-item/nav-item.tsx (1)

127-130: Consider moving aria-expanded to the parent button

The aria-expanded attribute on a non-interactive span might be confusing. Consider moving it to the parent button element for better accessibility semantics.

-        {isDropdown && (
-          <span className={cx('dropdown-arrow')} role="button" aria-expanded={isDropdownOpen}>
-            <DropdownArrow />
-          </span>
-        )}
-      </button>
+        aria-expanded={isDropdown ? isDropdownOpen : undefined}
+      >
+        {icon && (
+          <Image
+            src={icon}
+            alt={`${label} icon`}
+            width={18}
+            height={16}
+            aria-hidden="true"
+            data-testid="nav-item-icon"
+          />
+        )}
+        <span className={cx('label-bold')}>
+          {label}
+          <span className={cx('label-content')} aria-hidden="true">
+            {label}
+          </span>
+        </span>
+        {isDropdown && (
+          <span className={cx('dropdown-arrow')} aria-hidden="true">
+            <DropdownArrow />
+          </span>
+        )}
+      </button>
πŸ“œ Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between c513d17 and c7093fa.

β›” Files ignored due to path filters (3)
  • src/shared/assets/svg/heart-blue.svg is excluded by !**/*.svg
  • src/shared/assets/svg/heart-yellow.svg is excluded by !**/*.svg
  • src/shared/assets/svg/rss-logo-blue.svg is excluded by !**/*.svg
πŸ“’ Files selected for processing (30)
  • dev-data/donate-options.data.ts (1 hunks)
  • dev-data/school-menu-links.ts (1 hunks)
  • src/core/styles/_constants.scss (1 hunks)
  • src/core/styles/_mixins.scss (1 hunks)
  • src/shared/constants.ts (2 hunks)
  • src/shared/hooks/use-outside-click/use-outside-click.ts (1 hunks)
  • src/shared/ui/logo/logo.module.scss (1 hunks)
  • src/shared/ui/logo/logo.tsx (2 hunks)
  • src/shared/ui/paragraph/paragraph.module.scss (2 hunks)
  • src/shared/ui/paragraph/paragraph.tsx (1 hunks)
  • src/widgets/breadcrumbs/ui/breadcrumbs.module.scss (1 hunks)
  • src/widgets/breadcrumbs/ui/breadcrumbs.tsx (2 hunks)
  • src/widgets/footer/footer.test.tsx (2 hunks)
  • src/widgets/header/header.module.scss (4 hunks)
  • src/widgets/header/header.test.tsx (5 hunks)
  • src/widgets/header/header.tsx (6 hunks)
  • src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss (2 hunks)
  • src/widgets/header/ui/dropdown/dropdown-wrapper.tsx (1 hunks)
  • src/widgets/header/ui/nav-item/nav-item.module.scss (3 hunks)
  • src/widgets/header/ui/nav-item/nav-item.tsx (2 hunks)
  • src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss (1 hunks)
  • src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx (1 hunks)
  • src/widgets/mobile-view/ui/mobile-view.module.scss (1 hunks)
  • src/widgets/mobile-view/ui/mobile-view.tsx (2 hunks)
  • src/widgets/school-menu/ui/school-item/school-item.module.scss (3 hunks)
  • src/widgets/school-menu/ui/school-item/school-item.tsx (1 hunks)
  • src/widgets/school-menu/ui/school-menu/school-menu.module.scss (2 hunks)
  • src/widgets/school-menu/ui/school-menu/school-menu.test.tsx (1 hunks)
  • src/widgets/school-menu/ui/school-menu/school-menu.tsx (1 hunks)
  • src/widgets/support/ui/support.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
dev-data/school-menu-links.ts (2)
src/shared/constants.ts (1)
  • ANCHORS (68-77)
dev-data/index.ts (1)
  • communityMenuStaticLinks (49-49)
src/widgets/school-menu/ui/school-item/school-item.tsx (1)
src/shared/ui/logo/logo.tsx (1)
  • cx (17-17)
src/widgets/footer/footer.test.tsx (3)
src/shared/__tests__/utils/render-with-router/render-with-router.tsx (1)
  • renderWithRouter (38-49)
src/widgets/mobile-view/ui/mobile-view.tsx (1)
  • MobileView (41-244)
src/shared/__tests__/constants.ts (1)
  • mockedCourses (27-207)
src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx (1)
src/shared/icons/dropdown-arrow.tsx (1)
  • DropdownArrow (5-7)
πŸ”‡ Additional comments (60)
src/shared/ui/paragraph/paragraph.module.scss (2)

18-22: Confirm spacing requirements
Unlike the other size variants, .small-font-size doesn’t include bottom padding. Verify against the design spec if vertical spacing is needed here.


35-37: Review responsive adjustments
At the media-laptop breakpoint only line-height is overridden. Confirm whether properties like font-size or font-weight should also adapt on larger screens.

src/core/styles/_constants.scss (1)

56-59: Addition of intermediate gray variables
Both $color-gray-450 and $color-gray-250 are correctly inserted into the grayscale scale and follow the existing ordering.

src/core/styles/_mixins.scss (1)

3-7: New media-laptop-large mixin
The mixin appropriately targets viewports above $content-width. Ensure documentation or examples are updated to reflect this new breakpoint utility.

src/shared/constants.ts (1)

45-49: Extended KEY_CODES for improved keyboard interactions
Adding ENTER and SPACE aligns with accessibility best practices for menu toggles.

src/shared/hooks/use-outside-click/use-outside-click.ts (1)

13-17: Solid click detection logic.

Good use of mousedown event and proper null checking before calling contains().

dev-data/donate-options.data.ts (1)

9-12: Clean separation of button and menu variants.

The split into buttonLinkLabel/menuLinkLabel and buttonIcon/menuIcon provides good flexibility for different UI contexts.

src/widgets/school-menu/ui/school-menu/school-menu.test.tsx (1)

42-44: Test expectations correctly updated.

The increase from 2 to 4 elements aligns with the addition of new menu items mentioned in the PR objectives.

src/shared/ui/paragraph/paragraph.tsx (1)

18-18: LGTM! Clean addition of new font size variant.

The new "small" variant follows the established pattern and integrates well with the existing CVA configuration.

src/widgets/footer/footer.test.tsx (2)

10-10: Appropriate mock addition for navigation-dependent components.

The usePathname mock returning '/' is consistent with the testing pattern and necessary for components using Next.js navigation hooks.


41-43: Improved JSX formatting for better readability.

The multi-line format makes the test props more readable while maintaining the same functionality.

dev-data/school-menu-links.ts (2)

14-23: Well-structured addition of new menu items.

Both "Support Us" and "Mentors Wanted" entries follow the established pattern perfectly, using proper anchor constants and providing clear descriptions.


28-28: Improved clarity with more specific title.

Changing "About" to "About Community" provides better context and reduces ambiguity when multiple "About" sections exist.

src/widgets/breadcrumbs/ui/breadcrumbs.tsx (3)

15-17: Clean type definition for enhanced styling flexibility.

The optional className prop follows React best practices and maintains backward compatibility.


19-19: Proper component signature update.

The destructured className parameter correctly implements the new prop interface.


31-31: Correct implementation of className merging.

The cx() function properly combines existing classes with the optional external className, maintaining styling consistency.

src/widgets/support/ui/support.tsx (2)

31-31:

βœ… Verification successful

Verify destructuring matches updated data structure.

The destructuring now includes buttonLinkLabel and buttonIcon instead of the previous linkLabel and icon. Ensure the donateOptions data structure in dev-data/donate-options.data.ts exports these exact property names.


🏁 Script executed:

#!/bin/bash
# Description: Verify the donateOptions data structure has the expected properties
# Expected: Properties should include buttonLinkLabel and buttonIcon

rg -A 10 -B 5 "buttonLinkLabel|buttonIcon" --type ts

Length of output: 2744


Verification complete: destructuring matches data structure
The donateOptions array in dev-data/donate-options.data.ts exports buttonLinkLabel and buttonIcon, matching the destructuring in support.tsx.


34-34:

βœ… Verification successful

Verify icon function call pattern.

The buttonIcon() is called as a function. Ensure this returns a valid React component and that all icons in the data structure follow this pattern.


🏁 Script executed:

#!/bin/bash
# Description: Check how buttonIcon is defined in the donate options data
# Expected: Should be functions that return React components

ast-grep --pattern 'buttonIcon: $$$'

Length of output: 38


🏁 Script executed:

#!/bin/bash
# Search for where buttonIcon is defined and how it’s assigned
rg "buttonIcon" -n -C2 .

Length of output: 1519


🏁 Script executed:

#!/bin/bash
# Inspect imports in donate-options data
sed -n '1,50p' dev-data/donate-options.data.ts

# Locate component definitions for the icons
rg "export .*OpenCollectiveIcon" -n .
rg "export .*BoostyIcon" -n .

Length of output: 1124


Icon function call pattern verified
buttonIcon maps to pure functional components (OpenCollectiveIcon, BoostyIcon) that return valid React elements and don’t use hooks, so calling buttonIcon() is safe.

(Optional) For clearer JSX semantics you could destructure:

donateOptions.map(({ buttonIcon: Icon, ... }) => (
  <Icon />
  …
))

but no changes are required.

src/widgets/school-menu/ui/school-item/school-item.tsx (1)

39-39: Good accessibility improvement with tabIndex management.

Properly setting tabIndex={-1} for non-clickable items removes them from keyboard navigation, which improves accessibility and addresses potential keyboard navigation issues mentioned in the PR comments.

src/widgets/school-menu/ui/school-item/school-item.module.scss (4)

3-4: Good spacing adjustment for better visual hierarchy.

Reducing the gap from 15px to 8px and changing alignment to flex-start creates better visual spacing and addresses the spacing inconsistencies mentioned in PR comments.


10-13: Excellent accessibility enhancement for non-clickable items.

The .non-clickable class properly disables pointer events and changes cursor, which works well with the tabIndex={-1} set in the component for truly non-interactive items.


57-59: Icon sizing constraint is well-implemented.

Using the data-testid selector to constrain icon height to 32px ensures consistent icon sizing across the menu items.


32-32:

βœ… Verification successful

Verify new color variable is defined.

The $color-gray-450 variable is used but needs to be defined in the constants file.


🏁 Script executed:

#!/bin/bash
# Description: Check if the new gray color variable is defined
# Expected: Should find $color-gray-450 definition

rg "\$color-gray-450" --type scss

Length of output: 63


To catch definitions without relying on filetype detection, let’s search all SCSS files by extension:


🏁 Script executed:

#!/bin/bash
# Search for any usage or definition of $color-gray-450 in .scss files
rg '\$color-gray-450' -g '*.scss'

Length of output: 292


Confirmed: $color-gray-450 is defined
The variable is declared in src/core/styles/_constants.scss as:

$color-gray-450: hsl(210deg 9% 62%); // #969fa8

No further action needed.

src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss (4)

18-20: Good dropdown arrow animation implementation.

The rotation animation provides clear visual feedback for collapsible menu states, which should improve the user experience mentioned in PR objectives.


1-27: Well-structured mobile navigation styling.

The category link styling properly resets button defaults and provides good mobile touch targets with appropriate font sizing and spacing.


16-16:

βœ… Verification successful

Verify transition mixin is defined.

The @extend %transition-all references a placeholder selector that should be defined in the shared styles.


🏁 Script executed:

#!/bin/bash
# Description: Check if the transition-all placeholder is defined
# Expected: Should find %transition-all definition

rg "%transition-all" --type scss

Length of output: 61


🏁 Script executed:

#!/bin/bash
# Search for the %transition-all placeholder across all SCSS files
rg '%transition-all' -g '*.scss' . || grep -R --include='*.scss' '%transition-all' .

Length of output: 1570


%transition-all placeholder verified

The %transition-all placeholder is defined in src/core/styles/_placeholders.scss, so the @extend %transition-all; in mobile-nav-item.module.scss is valid. No further changes needed.


40-40:

βœ… Verification successful

Verify light color variable consistency.

Using $color-gray-100 for light text. Ensure this provides sufficient contrast for accessibility.


🏁 Script executed:

#!/bin/bash
# Description: Check the gray color palette definition
# Expected: Should find consistent gray color variable definitions

rg "\$color-gray-(100|250|450|900)" --type scss

Length of output: 77


🏁 Script executed:

#!/bin/bash
# Re-run search for gray color variables across all SCSS files
rg -g '*.scss' '\$color-gray-(100|250|450|900)' -n

Length of output: 2854


🏁 Script executed:

#!/bin/bash
# Display mobile-nav-item styles to confirm background and text color context
sed -n '1,200p' src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss

Length of output: 615


Color variable usage verified

Definitions for $color-gray-100 (#f0f2f5) and $color-gray-900 (#191b1d) are consistent in core/styles/_constants.scss, and the .light/.dark classes in mobile-nav-item.module.scss correctly apply them. Contrast between these values meets WCAG AA/AAA on their intended backgrounds.

β€’ core/styles/_constants.scss (lines 51–61): gray palette
β€’ src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss (lines 36–40): .dark/.light classes

No changes required.

src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.tsx (1)

18-47: Well-structured mobile navigation component.

The component properly handles conditional icon rendering, dropdown states, and accessibility attributes. The click handler and ARIA attributes support good UX and accessibility.

src/widgets/school-menu/ui/school-menu/school-menu.tsx (2)

15-16: Good addition of layout and visibility props.

These props address previous feedback about simplifying consumer component logic and moving complexity into the component itself.


28-36: Proper implementation of conditional visibility and layout.

The visibility classes are applied correctly based on the isVisible prop, and the dynamic layout class follows React best practices.

src/widgets/header/ui/dropdown/dropdown-wrapper.tsx (2)

10-10: Good addition of layout control prop.

The reverseLayout prop provides flexibility for dropdown positioning without complex logic.


14-27: Simplified component interface improves maintainability.

Removing mouse event handlers and positioning logic simplifies the component. This suggests a shift to click-based dropdown interaction, which can improve mobile UX.

src/shared/ui/logo/logo.tsx (2)

12-15: Good extension of props interface.

Adding the optional icon prop with proper TypeScript typing maintains backward compatibility while enabling dynamic logo usage.


21-31: Proper implementation of dynamic icon support.

The default value ensures backward compatibility while the dynamic src attribute enables flexible logo rendering across different contexts.

src/widgets/mobile-view/ui/mobile-view.module.scss (2)

31-34: Well-structured media query organization.

Good placement of overflow-y: hidden and explicit flex-direction: column in the tablet-large media query, addressing the layout structure concerns.


14-28: Excellent responsive breadcrumbs handling.

The nested .content.breadcrumbs-content selector with tablet-specific padding removal is a clean approach for responsive design.

src/widgets/header/header.test.tsx (4)

15-18: Appropriate navigation mocking for header tests.

The next/navigation mock properly stubs router functionality needed for the header component testing.


42-47: Improved test scoping with within.

Using within(desktopMenu) provides better test isolation and reduces flaky test potential compared to global queries.


90-90: Good addition of reverseLayout prop.

The reverseLayout={false} prop addition aligns with the component API changes and maintains test coverage for this feature.


68-74:

❓ Verification inconclusive

Verify mobile menu arrow count aligns with implementation.

The test expects 5 SVG arrows in mobile view. Ensure this count matches the actual mobile menu structure after the refactoring.


🏁 Script executed:

#!/bin/bash
# Description: Verify mobile menu dropdown arrow count in mobile-view components
# Expected: Find mobile navigation items with dropdown arrows

ast-grep --pattern 'aria-label="dropdown-arrow"' 
rg -A 3 -B 3 'dropdown-arrow' src/widgets/mobile-view/

Length of output: 1986


🏁 Script executed:

#!/bin/bash
# Locate the DropdownArrow icon component and inspect for aria-label
DROP_PATH=$(fd dropdown-arrow.tsx)

echo "Inspecting file: $DROP_PATH"

# Check for any aria attributes on the icon
rg -n "aria" "$DROP_PATH"

# Show the SVG markup to see if an aria-label is set
rg -n "<svg" -A5 "$DROP_PATH"

Length of output: 374


Verify mobile menu arrow count matches rendered items

I confirmed each <MobileNavItem> renders one icon with aria-label="dropdown-arrow". Please double-check the number of items passed into the mobile menu (in src/widgets/header/header.tsx where you render the items under data-testid="mobile-menu") and update the test’s expected length if it no longer equals 5.

src/widgets/school-menu/ui/school-menu/school-menu.module.scss (4)

2-2: Good use of transition placeholder.

The @extend %transition-all provides consistent transition behavior across the component.


29-39: Sophisticated grid layout implementation.

The grid-based approach with automatic column adjustment for 5+ items is well-designed for flexible menu layouts.


52-68: Excellent visibility state implementation.

The .hidden and .visible classes properly use opacity constants and create smooth transitions for show/hide behavior.


35-38:

❓ Verification inconclusive

Verify grid layout behavior with variable content.

The :has(:nth-child(5)) selector switches to single-column layout. Test this behavior with different menu item counts to ensure proper responsive behavior.


🏁 Script executed:

#!/bin/bash
# Description: Check school menu usage patterns to verify grid layout behavior
# Expected: Find school menu components and their typical item counts

rg -A 10 -B 5 'SchoolMenu.*items\|school.*menu.*data' src/
ast-grep --pattern 'SchoolMenu $$$'

Length of output: 98


Verify grid layout behavior across varying menu item counts

Please ensure the single-column switch triggered by :has(:nth-child(5)) works as intended:

β€’ Test the SchoolMenu component (in app, Storybook or tests) with fewer than 5 items, exactly 5 items, and more than 5 items.
β€’ Confirm the grid switches back to multi-column when item count drops below 5.
β€’ Validate responsive behavior at different breakpoints.

We couldn’t find automated usage patterns in the repoβ€”manual or Storybook verification is recommended.

src/widgets/header/header.module.scss (4)

8-8: Appropriate height increase for better visual hierarchy.

The navbar height increase from 64px to 70px provides better proportions and aligns with the menu refactoring goals.


14-16: Clean unified background styling.

The consistent white background with subtle shadow creates a more cohesive header appearance compared to the previous color variants.


43-43: Good use of gap for menu spacing.

The increased gap (33.8px) between menu items addresses previous feedback about using gap instead of margins for layout.


107-117: Well-structured support text styling.

The .support-text class provides proper layout for donation-related content with appropriate constraints and typography.

src/widgets/header/header.tsx (2)

35-38: LGTM!

Clean implementation of conditional logic for mentorship page detection and icon selection.


64-130: Well-structured menu implementation

Good use of NAV_MENU_LABELS constants and consistent SchoolMenu patterns across all navigation items.

src/widgets/mobile-view/ui/mobile-view.tsx (1)

49-68: Excellent state management implementation

Great use of Set for managing dropdown states and proper cleanup when menu closes. The toggle logic is clean and efficient.

src/widgets/header/ui/nav-item/nav-item.module.scss (2)

22-30: Great accessibility improvement!

The addition of focus-visible styles with proper outline enhances keyboard navigation support.


53-74: Clever solution for preventing layout shift

The nested label structure with transparent parent effectively prevents layout shift when font weight changes between regular and bold states.

src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss (3)

15-17: CSS Grid implementation looks good!

The three-column grid layout provides a clean structure for dropdown content organization.


29-29: Proper use of hsla color syntax

Good implementation following the requested pattern for colors with opacity.


31-53:

βœ… Verification successful

Verify browser support for :has() pseudo-class

The :has() pseudo-class has limited browser support. Ensure this aligns with your browser support requirements.


🌐 Web query:

What is the current browser support for CSS :has() pseudo-class and when was it added to major browsers?

πŸ’‘ Result:

The CSS :has() pseudo-class, introduced in the Selectors Level 4 specification, allows developers to select elements based on their descendants, effectively enabling parent selection in CSS. (css-tricks.com)

Browser Support Timeline:

  • Chrome: Full support began with version 105, released on September 2, 2022. (caniuse.com)

  • Edge: Support was added starting with version 105, released on September 1, 2022. (caniuse.com)

  • Safari: Support commenced with version 15.4, released on March 14, 2022. (caniuse.com)

  • Firefox: Support was introduced in version 121, released on December 19, 2023. (caniuse.com)

Current Support Status (as of May 27, 2025):

As of May 2025, the :has() pseudo-class is supported in the latest versions of major browsers:

  • Chrome: Versions 105 and later.

  • Edge: Versions 105 and later.

  • Safari: Versions 15.4 and later.

  • Firefox: Versions 121 and later.

This widespread support allows developers to utilize the :has() pseudo-class for more dynamic and context-aware styling across modern browsers.

Citations:


Browser Support for CSS :has() Verified

The CSS :has() pseudo-class is supported in all modern browsers as of these versions:

  • Chrome 105+ (Sep 2, 2022)
  • Edge 105+ (Sep 1, 2022)
  • Safari 15.4+ (Mar 14, 2022)
  • Firefox 121+ (Dec 19, 2023)

As long as your project’s browser-support baseline targets these (or later) versions, you can safely use :has() for dynamic grid layouts in src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss (lines 31–53). No further changes required.

src/widgets/header/ui/nav-item/nav-item.tsx (4)

48-53: Good implementation of dropdown coordination

The custom event pattern effectively ensures only one dropdown is open at a time.


70-76: Excellent keyboard accessibility implementation

Proper handling of Enter, Space, and Escape keys with preventDefault for form submission prevention.


80-86: Proper event listener lifecycle management

Good implementation with cleanup to prevent memory leaks.


120-125:

βœ… Verification successful

Verify the double label rendering pattern

The duplicate label with aria-hidden appears to be for styling purposes. Ensure this doesn't cause issues with screen readers or SEO.


🏁 Script executed:

#!/bin/bash
# Check if this pattern is used elsewhere in the codebase
ast-grep --pattern $'<span className={$_}>
  $_
  <span className={$_} aria-hidden="true">
    $_
  </span>
</span>'

Length of output: 117


🏁 Script executed:

#!/bin/bash
# Search for all occurrences of aria-hidden to identify similar double-render patterns
rg -n 'aria-hidden="true"'

Length of output: 1107


Double‐render label pattern is consistent and accessible

I checked similar components (mobile-nav-item, school-item, etc.) and confirmed the duplicate label with aria-hidden="true" is a deliberate styling trick. Screen readers will ignore the hidden span, so only the primary label is announced and there’s no SEO impact. No changes required.

@gbogdanova
Copy link
Collaborator

I noticed that tapping the logo in the mobile menu doesn't trigger the onClose callback, so the collapsible menu remains open. Maybe we should add onClick={onClose} to the Logo component when it's used in the mobile menu to ensure consistent behavior.

Copy link
Contributor

@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

♻️ Duplicate comments (2)
src/widgets/mobile-view/ui/mobile-view.tsx (2)

86-105: πŸ› οΈ Refactor suggestion

Extract the repeated menu pattern into a reusable component.

The MobileNavItem + SchoolMenu pattern is duplicated multiple times. This addresses the past review feedback about creating reusable components.

Create a reusable MobileMenuSection component:

type MobileMenuSectionProps = {
  title: string;
  icon?: StaticImageData;
  color: 'light' | 'dark';
  items: Array<{
    key: string;
    title: string;
    description?: string;
    url: string;
    icon?: StaticImageData;
  }>;
  isVisible: boolean;
  onToggle: () => void;
  onItemClick?: () => void;
};

const MobileMenuSection = ({ title, icon, color, items, isVisible, onToggle, onItemClick }: MobileMenuSectionProps) => (
  <div className={cx('category-container')}>
    <MobileNavItem
      title={title}
      icon={icon}
      color={color}
      isDropdownActive={isVisible}
      onMenuItemClick={onToggle}
    />
    <SchoolMenu isVisible={isVisible}>
      {items.map((item) => (
        <SchoolMenu.Item
          key={item.key}
          icon={item.icon}
          title={item.title}
          description={item.description}
          url={item.url}
          color={color}
          onClick={onItemClick}
        />
      ))}
    </SchoolMenu>
  </div>
);

40-243: πŸ› οΈ Refactor suggestion

Consider splitting this large component.

This component has grown quite large and handles multiple responsibilities. As mentioned in past reviews, it should be split into smaller components.

Consider extracting sections like:

  • MobileMenuHeader (logo + breadcrumbs)
  • MobileMenuContent (all menu sections)
  • Individual menu sections as separate components

This would improve maintainability and testability while addressing the component size concern raised in previous reviews.

🧹 Nitpick comments (2)
src/widgets/mobile-view/ui/mobile-view.tsx (2)

228-228: Avoid creating new arrays on every render.

The toReversed() call creates a new array on every render, which is unnecessary.

-            {donateOptions.toReversed().map((option) => (
+            {[...donateOptions].reverse().map((option) => (

Or better yet, reverse the array once outside the component or in useMemo:

+  const reversedDonateOptions = useMemo(() => [...donateOptions].reverse(), []);
+
   // Later in JSX:
-            {donateOptions.toReversed().map((option) => (
+            {reversedDonateOptions.map((option) => (

217-227: Extract hard-coded support messages to constants.

The support section contains hard-coded strings that should be moved to constants for maintainability.

+const SUPPORT_MESSAGES = {
+  DESCRIPTION: "Your donations help us cover hosting, domains, licenses, and advertising for courses and events. Every donation, big or small, helps!",
+  THANK_YOU: "Thank you for your support!"
+};

-              title="Your donations help us cover hosting, domains, licenses, and advertising for courses
-                          and events. Every donation, big or small, helps!"
+              title={SUPPORT_MESSAGES.DESCRIPTION}

-              title="Thank you for your support!"
+              title={SUPPORT_MESSAGES.THANK_YOU}
πŸ“œ Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between c7093fa and ae31a08.

πŸ“’ Files selected for processing (7)
  • src/shared/constants.ts (2 hunks)
  • src/shared/ui/logo/logo.tsx (2 hunks)
  • src/widgets/header/header.module.scss (4 hunks)
  • src/widgets/header/header.tsx (6 hunks)
  • src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss (2 hunks)
  • src/widgets/mobile-view/ui/mobile-view.tsx (2 hunks)
  • src/widgets/school-menu/ui/school-menu/school-menu.module.scss (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/widgets/header/header.module.scss
  • src/widgets/school-menu/ui/school-menu/school-menu.module.scss
  • src/widgets/header/header.tsx
  • src/shared/constants.ts
  • src/widgets/header/ui/dropdown/dropdown-wrapper.module.scss
  • src/shared/ui/logo/logo.tsx
πŸ”‡ Additional comments (2)
src/widgets/mobile-view/ui/mobile-view.tsx (2)

48-67: Well-implemented state management with proper cleanup.

The activeDropdowns state management using Set is efficient and the useEffect cleanup when menu closes is correctly implemented.


40-243:

❓ Verification inconclusive

Verify keyboard navigation functionality.

Based on PR comments, keyboard navigation may no longer work after these changes. Please test that all interactive elements are accessible via keyboard.

Test the following keyboard interactions:

  • Tab navigation through all menu items
  • Enter/Space to open/close dropdowns
  • Arrow keys for menu navigation
  • Escape to close menus

🏁 Script executed:

#!/bin/bash
# Check for ARIA attributes and accessibility patterns
ast-grep --pattern 'role=$_' 
ast-grep --pattern 'aria-$_=$_'
ast-grep --pattern 'tabIndex=$_'

Length of output: 99


Verify keyboard navigation functionality

Please perform manual testing to ensure all interactive elements in the mobile menu are accessible via keyboard:

  • Tab through each menu item and focusable control
  • Use Enter/Space to open and close dropdowns
  • Navigate within open menus using Arrow keys
  • Press Escape to close any open dropdown or the menu

If any of these interactions do not behave as expected, update the component to include proper ARIA roles, tabIndex, and keyboard event handlers.

Copy link

Lighthouse Report:

  • Performance: 75 / 100
  • Accessibility: 96 / 100
  • Best Practices: 100 / 100
  • SEO: 100 / 100

View detailed report

Copy link

Lighthouse Report:

  • Performance: 73 / 100
  • Accessibility: 96 / 100
  • Best Practices: 100 / 100
  • SEO: 100 / 100

View detailed report

Copy link
Contributor

@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)
src/widgets/header/ui/nav-item/nav-item.tsx (3)

41-53: Global event system needs documentation.

The custom event approach for closing all dropdowns works but could benefit from being moved to a shared constant or utility to prevent string duplication across components.

Consider extracting the event name to a shared constant:

-  const closeAllDropdowns = 'closeAllDropdowns';
+  const closeAllDropdowns = CUSTOM_EVENTS.CLOSE_ALL_DROPDOWNS;

99-131: Button structure and duplicate labels need clarification.

The JSX structure is well-organized, but the duplicate label rendering seems unnecessary and could confuse screen readers despite the aria-hidden attribute.

Consider simplifying the label structure:

-        <span className={cx('label-bold')}>
-          {label}
-          <span className={cx('label-content')} aria-hidden="true" data-testid="label-content">
-            {label}
-          </span>
-        </span>
+        <span className={cx('label-bold')}>
+          {label}
+        </span>

If the duplicate is needed for styling, explain the purpose in a comment.


127-129: Dropdown arrow ARIA attributes could be improved.

The arrow span has role="button" but the parent button already handles the interaction. This creates confusing semantics.

Remove redundant role since the parent button handles interaction:

-          <span className={cx('dropdown-arrow')} role="button" aria-expanded={isDropdownOpen}>
+          <span className={cx('dropdown-arrow')} aria-hidden="true">

The aria-expanded should be on the main button instead.

πŸ“œ Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between ae31a08 and 0c20a7b.

πŸ“’ Files selected for processing (10)
  • src/core/styles/_constants.scss (1 hunks)
  • src/core/styles/_mixins.scss (1 hunks)
  • src/widgets/breadcrumbs/ui/breadcrumbs.module.scss (1 hunks)
  • src/widgets/header/header.module.scss (4 hunks)
  • src/widgets/header/header.test.tsx (5 hunks)
  • src/widgets/header/ui/nav-item/nav-item.module.scss (3 hunks)
  • src/widgets/header/ui/nav-item/nav-item.tsx (2 hunks)
  • src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss (1 hunks)
  • src/widgets/mobile-view/ui/mobile-view.module.scss (1 hunks)
  • src/widgets/school-menu/ui/school-item/school-item.module.scss (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/core/styles/_constants.scss
  • src/widgets/breadcrumbs/ui/breadcrumbs.module.scss
  • src/core/styles/_mixins.scss
  • src/widgets/school-menu/ui/school-item/school-item.module.scss
  • src/widgets/mobile-view/ui/mobile-nav-item/mobile-nav-item.module.scss
  • src/widgets/mobile-view/ui/mobile-view.module.scss
  • src/widgets/header/header.test.tsx
  • src/widgets/header/header.module.scss
  • src/widgets/header/ui/nav-item/nav-item.module.scss
πŸ”‡ Additional comments (7)
src/widgets/header/ui/nav-item/nav-item.tsx (7)

4-4: Import additions look good.

The new imports support the enhanced functionality correctly - useCallback for memoization, Next.js components for navigation, constants for keyboard handling, and the new outside click hook.

Also applies to: 10-11, 14-15


25-26: Props addition enhances component flexibility.

The optional icon and reverseLayout props provide good customization options for different menu styles.


32-35: Ref management and variable setup is solid.

Good separation of concerns with separate refs for wrapper and button elements, and clear boolean logic for dropdown detection.


70-76: Enhanced keyboard handling is excellent.

The unified handler supporting Enter, Space, and Escape keys with proper event prevention significantly improves accessibility.


78-78: Outside click hook integration is clean.

Proper usage of the custom hook with conditional activation based on dropdown state.


80-86: Event listener cleanup is properly handled.

Good practice using the cleanup function in useEffect to prevent memory leaks.


55-61:

❓ Verification inconclusive

Navigation logic change from Link to button.

This is a significant architectural change that replaces semantic Link navigation with programmatic routing. While it works, consider the SEO and accessibility implications.

Verify that this change doesn't negatively impact:

  1. Search engine crawling (Links are more SEO-friendly)
  2. Right-click context menu functionality
  3. Middle-click to open in new tab behavior

🏁 Script executed:

#!/bin/bash
# Check if other navigation components still use Link elements
rg -A 3 "import.*Link.*from.*next" --type tsx --type ts

Length of output: 85


🏁 Script executed:

#!/bin/bash
set -e
# Locate nav-item.tsx file
file=$(find . -type f -name nav-item.tsx | head -n1)
echo "Nav-item component path: $file"
# Show context around handleClick definition and its usage on an element
grep -n -C 3 "const handleClick" "$file"
grep -n -C 3 "onClick" "$file"
echo
echo "------------------------------------------------------------------------"
# Search for any Link imports across the repo
rg "import.*Link.*from.*next/link" .

Length of output: 1763


Ensure SEO & accessibility for nav-item navigation

You’ve switched from a semantic <Link> to programmatic router.push, which can break:

  • SEO (no crawlable <a href> for search engines)
  • Native context menu (β€œOpen in new tab”)
  • Middle-click/tab-opening behavior

Please verify in src/widgets/header/ui/nav-item/nav-item.tsx around lines 55–61 & 104–110 that non-dropdown items still render an <a href={linkHref}> (or wrap with next/link) so you don’t lose these built-in behaviors.

β€’ If you intend to keep programmatic routing, ensure you provide a fallback <a> or <Link> wrapper for link semantics.
β€’ Otherwise, reconsider using <Link href={linkHref}> with an onClick handler for dropdowns only.

Copy link

github-actions bot commented Jun 1, 2025

Lighthouse Report:

  • Performance: 76 / 100
  • Accessibility: 96 / 100
  • Best Practices: 100 / 100
  • SEO: 100 / 100

View detailed report

Copy link
Contributor

@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

♻️ Duplicate comments (1)
src/widgets/mobile-view/ui/mobile-view.tsx (1)

85-240: πŸ› οΈ Refactor suggestion

Extract repeated category container pattern into reusable component.

The category container pattern is duplicated multiple times. This creates maintenance overhead and contradicts DRY principles.

Consider extracting a MenuCategory component:

type MenuCategoryProps = {
  title: string;
  icon?: StaticImageData;
  color: 'light' | 'dark';
  isActive: boolean;
  onToggle: () => void;
  children: React.ReactNode;
};

const MenuCategory = ({ title, icon, color, isActive, onToggle, children }: MenuCategoryProps) => (
  <div className={cx('category-container')}>
    <MobileNavItem
      title={title}
      icon={icon}
      color={color}
      isDropdownActive={isActive}
      onMenuItemClick={onToggle}
    />
    <SchoolMenu isVisible={isActive}>
      {children}
    </SchoolMenu>
  </div>
);

This would significantly reduce the component size and improve maintainability.

🧹 Nitpick comments (1)
src/widgets/mobile-view/ui/mobile-view.tsx (1)

48-61: Optimize callback performance with useCallback.

The onMenuItemClick function is recreated on every render, which could impact performance when the menu has many items.

-  const onMenuItemClick = (menuItem: string) => {
-    setActiveDropdowns((prev) => {
-      const newSet = new Set(prev);
-
-      if (newSet.has(menuItem)) {
-        newSet.delete(menuItem);
-      } else {
-        newSet.add(menuItem);
-      }
-      return newSet;
-    });
-  };
+  const onMenuItemClick = useCallback((menuItem: string) => {
+    setActiveDropdowns((prev) => {
+      const newSet = new Set(prev);
+
+      if (newSet.has(menuItem)) {
+        newSet.delete(menuItem);
+      } else {
+        newSet.add(menuItem);
+      }
+      return newSet;
+    });
+  }, []);

Don't forget to import useCallback from React.

πŸ“œ Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between b4195ab and e594247.

πŸ“’ Files selected for processing (4)
  • src/shared/ui/logo/logo.tsx (2 hunks)
  • src/widgets/header/header.module.scss (3 hunks)
  • src/widgets/header/header.tsx (3 hunks)
  • src/widgets/mobile-view/ui/mobile-view.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/widgets/header/header.module.scss
  • src/shared/ui/logo/logo.tsx
  • src/widgets/header/header.tsx
πŸ”‡ Additional comments (2)
src/widgets/mobile-view/ui/mobile-view.tsx (2)

72-72: Logo click handler correctly addresses mobile menu closure issue.

Good implementation of the onClick handler for the Logo component. This addresses the issue mentioned in PR comments about the logo not closing the mobile menu.


228-228:

❌ Incorrect review comment

Verify performance impact of array reversal in render.

The toReversed() method creates a new array on every render, which could impact performance if the donate options array is large.

Consider moving this operation outside the render or memoizing it:

+  const reversedDonateOptions = useMemo(() => donateOptions.toReversed(), []);

-            {donateOptions.toReversed().map((option) => (
+            {reversedDonateOptions.map((option) => (

🏁 Script executed:

#!/bin/bash
# Check the size of donateOptions array to assess performance impact
rg -A 5 -B 5 "donateOptions.*=.*\[" --type tsx --type ts

Length of output: 86


🏁 Script executed:

#!/bin/bash
# Locate where donateOptions is defined or imported
rg -g '*.tsx' -n "donateOptions" -A3 -B3
rg -g '*.ts'  -n "donateOptions" -A3 -B3

Length of output: 4471


🏁 Script executed:

#!/bin/bash
# Count number of donateOptions entries
grep -c "id:" dev-data/donate-options.data.ts

# Print donateOptions file contents for review
sed -n '1,200p' dev-data/donate-options.data.ts

Length of output: 776


No memoization needed for donateOptions – negligible cost
The donateOptions array defined in dev-data/donate-options.data.ts contains only two entries. Reversing a two-item array on each render will not introduce any measurable performance overhead. You can safely leave the toReversed().map(...) calls as-is.

Likely an incorrect or invalid review comment.

Copy link

github-actions bot commented Jun 5, 2025

Lighthouse Report:

  • Performance: 75 / 100
  • Accessibility: 96 / 100
  • Best Practices: 100 / 100
  • SEO: 100 / 100

View detailed report

@ansivgit ansivgit merged commit 609cded into main Jun 5, 2025
3 checks passed
@ansivgit ansivgit deleted the fix/765-fix-styles-active-menu-item branch June 5, 2025 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Menu items active text color Desktop menu rework
8 participants