Conversation
There was a problem hiding this comment.
Pull request overview
This pull request replaces the static appjs banner with a new TopPromoRotator component that automatically rotates between multiple promotional banners in the documentation navbar. The implementation adds a paradise promo alongside the existing App.js conference promotion, creating a dynamic 5-second rotation cycle between promotional content.
Changes:
- Added
TopPromoRotatorcomponent with automatic rotation logic and animation - Created
HandIconSVG component for visual indicator - Integrated rotator into navbar above the main navigation bar
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 11 comments.
| File | Description |
|---|---|
| packages/docs-gesture-handler/src/theme/Navbar/index.js | Wraps existing Navbar with TopPromoRotator in a flex container |
| packages/docs-gesture-handler/src/components/TopPromoRotator/index.tsx | Main rotator component with interval-based switching between two promos |
| packages/docs-gesture-handler/src/components/TopPromoRotator/styles.module.css | Styles for banner layout, animations, and responsive behavior |
| packages/docs-gesture-handler/src/components/HandIcon/index.tsx | SVG icon component displaying a pointing hand gesture |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ]; | ||
|
|
||
| export default function TopPromoRotator() { | ||
| const promos = useMemo(() => PROMOS, []); |
There was a problem hiding this comment.
The useMemo hook is being used to memoize a constant array PROMOS, but since PROMOS is already defined as a constant outside the component, using useMemo here is unnecessary and adds overhead. You can directly use PROMOS in the component or simply assign it with const promos = PROMOS.
| ))} | ||
| </div> | ||
| <span className="sr-only"> | ||
| {typeof active.label === 'string' ? active.label : ''} |
There was a problem hiding this comment.
The screen reader fallback text only displays content when active.label is a string, but in this implementation, both promos use ReactNode (JSX elements) for labels. This means screen readers will never receive alternative text for the rotating banners. Consider extracting plain text versions of the labels or implementing a function to convert the ReactNode content to text for screen readers.
| useEffect(() => { | ||
| const id = window.setInterval(() => { | ||
| setIndex(i => (i + 1) % promos.length); | ||
| }, 5_000); | ||
|
|
||
| return () => window.clearInterval(id); | ||
| }, [promos.length]); |
There was a problem hiding this comment.
The automatically rotating banner doesn't provide users with controls to pause, stop, or manually navigate the rotation. According to WCAG 2.1 Success Criterion 2.2.2 (Pause, Stop, Hide), content that auto-updates or rotates for more than 5 seconds should provide a mechanism for users to pause, stop, or hide it. Consider adding pause/play controls or allowing users to navigate between banners manually.
| color: #001a72; | ||
| text-align: center; | ||
| text-decoration: none; | ||
| transition: background-color 300ms ease-out; | ||
| white-space: nowrap; | ||
| box-sizing: border-box; | ||
| } | ||
|
|
||
| .banner:hover { | ||
| color: #001a72 !important; |
There was a problem hiding this comment.
The color #001a72 is hardcoded in multiple places (lines 33, 42) in the CSS. This dark blue color doesn't appear to use CSS variables and may not respect the theme's color mode (light/dark). Consider using CSS variables for colors to maintain consistency with the rest of the site and support dark mode properly.
| }; | ||
| return ( | ||
| <Navbar heroImages={heroImages} titleImages={titleImages} {...props} /> | ||
| <div style={{ display: 'flex', flexDirection: 'column', flexShrink: 0 }}> |
There was a problem hiding this comment.
The wrapper div uses inline styles for flexbox layout. While this works, it would be more maintainable to define these styles in a CSS module file (e.g., in the Navbar's styles.module.css or a separate file) to keep styling concerns separated from the component logic and make it easier to adjust if needed.
| <div | ||
| className={styles.slider} | ||
| style={{ | ||
| transform: translateY, | ||
| transition: 'transform 700ms cubic-bezier(0.22, 1, 0.36, 1)', | ||
| }}> |
There was a problem hiding this comment.
Users with reduced motion preferences will still experience the automatic sliding animation. Consider respecting the prefers-reduced-motion media query to disable or reduce animations for users who have indicated they prefer less motion, which is important for accessibility and can help prevent motion sickness.
| </a> | ||
| ))} | ||
| </div> | ||
| <span className="sr-only"> |
There was a problem hiding this comment.
The sr-only class is used but doesn't appear to be defined in the codebase's CSS files. This class is critical for screen reader accessibility. Either this class needs to be defined in a global stylesheet, or you should use a CSS module class from styles.module.css. Without a proper definition, screen reader users won't have access to the alternative text for the rotating banners.
| <span className="sr-only"> | |
| <span | |
| style={{ | |
| border: 0, | |
| clip: 'rect(0 0 0 0)', | |
| height: '1px', | |
| margin: '-1px', | |
| overflow: 'hidden', | |
| padding: 0, | |
| position: 'absolute', | |
| width: '1px', | |
| whiteSpace: 'nowrap', | |
| }}> |
| useEffect(() => { | ||
| const id = window.setInterval(() => { | ||
| setIndex(i => (i + 1) % promos.length); | ||
| }, 5_000); | ||
|
|
||
| return () => window.clearInterval(id); |
There was a problem hiding this comment.
Direct usage of window.setInterval and window.clearInterval could cause issues during server-side rendering (SSR) in Docusaurus. Other components in the codebase (like RadonBanner, Wave, FooterBackground) wrap browser-specific code with BrowserOnly or check ExecutionEnvironment.canUseViewport. Consider wrapping this component or its interval logic to prevent SSR errors.
| <div | ||
| className={clsx(styles.wrapper)} | ||
| style={{ | ||
| backgroundColor: active.bg, | ||
| transition: 'background-color 600ms ease', | ||
| }}> |
There was a problem hiding this comment.
The rotating banner doesn't announce changes to screen readers when the content rotates. Consider adding an ARIA live region with appropriate politeness level (e.g., aria-live="polite") to the wrapper div so screen reader users are notified when the banner content changes.
| text-decoration: none !important; | ||
| background-color: rgba(0, 0, 0, 0.05); | ||
| } | ||
|
|
There was a problem hiding this comment.
The banner links don't have visible focus styles defined. Users navigating with keyboard will not see a clear indicator when the banner link is focused. Consider adding a :focus or :focus-visible style to provide clear visual feedback for keyboard navigation, which is important for accessibility.
| .banner:focus, | |
| .banner:focus-visible { | |
| color: #001a72 !important; | |
| text-decoration: none !important; | |
| background-color: rgba(0, 0, 0, 0.05); | |
| outline: 2px solid #001a72; | |
| outline-offset: 2px; | |
| } |
m-bert
left a comment
There was a problem hiding this comment.
You can take a look at copilot comments that I've added 👍 to. However, this is temporary change in docs so it should be fine anyway. Let me know if we should merge it right away, or do you plan to change something.
|
It’s a temporary change and will be updated later with new banner system. We can merge it as is for now |
Description
Replaces appjs banner with TopPromoRotator component in navbar. Adds paradise promo.