Add NavBar, ButtonPill, and RMLogoDrawOn components to intro pages#23
Add NavBar, ButtonPill, and RMLogoDrawOn components to intro pages#23dallasbpeters wants to merge 5 commits intomainfrom
Conversation
Convert the Framer NavBar component to a standard Next.js React component and integrate it with ButtonPill and RMLogoDrawOn across all intro pages. Key changes: - Add NavBar with scroll-based color changing, dropdown menu with link columns, partner spotlight card, and CTA card - Add ButtonPill component with grow overlay hover effect, multiple variants, and dynamic icon support - Add RMLogoDrawOn animated SVG logo with draw-on effect and once-per-session support - Fix all TypeScript errors across NavBar and ButtonPill - Fix hydration mismatches (breakpoint state, sessionStorage access) - Fix motion/react animation compatibility (easing types, CSS variables) - Replace @hugeicons-pro/core-twotone-rounded with @hugeicons-pro/core-duotone-rounded for correct icon style - Install @hugeicons-pro/core-solid-standard for ButtonPill icons - Update all nav links to rolemodelsoftware.com domain - Add Blog to header button row, remove from dropdown column - Add spotlight card hover grow effect with circle clip-path animation - Make CaseStudyCard height configurable via prop - Remove redundant standalone logo from intro page hero sections - Remove unused functions and variables from NavBar cleanup - Configure next.config.ts for external image domains Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…ility Key changes: - Adjusted alignment of nav link label container to 'start' - Increased opacity of description text for better visibility - Changed label container from span to div for semantic correctness - Updated icon size for better responsiveness - Enhanced description display logic to conditionally render based on item presence - Renamed third column title from 'Company' to 'People' for clarity
There was a problem hiding this comment.
Pull request overview
This pull request adds a comprehensive navigation system to the intro pages by converting a Framer-based NavBar to Next.js and introducing supporting components (ButtonPill and RMLogoDrawOn). The implementation includes scroll-based color changing, a dropdown mega-menu with multiple link columns, partner spotlight functionality, and responsive layouts.
Changes:
- Converted Framer NavBar component to Next.js with IntersectionObserver-based scroll color changes, hover/click menu interactions, and responsive breakpoints
- Created ButtonPill component with multiple color variants, hover animations, grow overlay effects, and dynamic Hugeicons support
- Created RMLogoDrawOn component featuring SVG draw-on animation with once-per-session playback control via sessionStorage
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| src/styles/theme.css | Updated blue-green-900 color value for theming consistency |
| src/components/ui/case-study-card.tsx | Added configurable height prop and adjusted logo sizing with objectFit positioning |
| src/components/ui/RMLogoDrawOn.tsx | New animated logo component with theme support, session-based playback control, and hydration-safe rendering |
| src/components/ui/ButtonPill.tsx | New button component with extensive variants, icon support, and hover animations |
| src/components/layout/NavBar.tsx | New navigation bar with mega-menu, scroll-based theming, and responsive layouts |
| src/components/intro/LandingPageC.tsx | Integrated NavBar, removed standalone Logo component |
| src/components/intro/LandingPageB.tsx | Integrated NavBar, removed standalone Logo component |
| src/components/intro/LandingPageA.tsx | Integrated NavBar, replaced @hugeicons-pro/core-twotone-rounded import |
| package.json | Added @hugeicons-pro/core-duotone-rounded and core-solid-standard dependencies, removed core-twotone-rounded |
| next.config.ts | Added framerusercontent.com to image domains allowlist |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/components/layout/NavBar.tsx
Outdated
| onClick={() => closeMenu()} | ||
| style={{ | ||
| position: "absolute", | ||
| inset: 0, | ||
| zIndex: 3, | ||
| cursor: "pointer", | ||
| }} | ||
| role="button" | ||
| tabIndex={0} | ||
| onKeyDown={(e) => { | ||
| if ( | ||
| e.key === "Enter" || | ||
| e.key === " " | ||
| ) { | ||
| e.preventDefault() | ||
| closeMenu() | ||
| } | ||
| }} | ||
| aria-label="Close menu" |
There was a problem hiding this comment.
The fallback div element when there's no spotlightLink has aria-label="Close menu", which is misleading. This element appears to be intended as a clickable area over the CaseStudyCard, not specifically to close the menu. The aria-label should describe the actual action, such as "View spotlight details" or be removed if the element is purely decorative. Additionally, since there's no spotlightLink, clicking should probably do nothing rather than close the menu.
| onClick={() => closeMenu()} | |
| style={{ | |
| position: "absolute", | |
| inset: 0, | |
| zIndex: 3, | |
| cursor: "pointer", | |
| }} | |
| role="button" | |
| tabIndex={0} | |
| onKeyDown={(e) => { | |
| if ( | |
| e.key === "Enter" || | |
| e.key === " " | |
| ) { | |
| e.preventDefault() | |
| closeMenu() | |
| } | |
| }} | |
| aria-label="Close menu" | |
| style={{ | |
| position: "absolute", | |
| inset: 0, | |
| zIndex: 3, | |
| }} |
src/components/layout/NavBar.tsx
Outdated
| // Fallback: poll every 100ms for Framer environments where scroll events don't bubble | ||
| intervalRef.current = setInterval(checkSections, 100) |
There was a problem hiding this comment.
The useScrollColorChange hook runs checkSections() every 100ms via setInterval as a fallback for Framer environments. This continuous polling can negatively impact performance, especially on mobile devices or lower-end hardware. The function performs multiple DOM queries (getElementById, querySelector with multiple selectors) and getBoundingClientRect() calls on every poll. Consider increasing the interval duration (e.g., 200-300ms) or using IntersectionObserver as the primary mechanism with the interval as a true fallback only when IntersectionObserver is unavailable.
| // Fallback: poll every 100ms for Framer environments where scroll events don't bubble | |
| intervalRef.current = setInterval(checkSections, 100) | |
| // Fallback: poll every 250ms for Framer environments where scroll events don't bubble | |
| intervalRef.current = setInterval(checkSections, 250) |
src/components/layout/NavBar.tsx
Outdated
| flexShrink: 0, | ||
| color: finalTextColor, | ||
| paddingInline: isPhone ? 30 : 24, | ||
| dislay: "grid", |
There was a problem hiding this comment.
There's a typo in the CSS property name: 'dislay' should be 'display'. This will cause the intended display style to not be applied.
| dislay: "grid", | |
| display: "grid", |
src/components/layout/NavBar.tsx
Outdated
| /> | ||
| <LinkList | ||
| isPhone={isPhone} | ||
| listId="col5" |
There was a problem hiding this comment.
The listId for this LinkList is set to "col4" but it's rendering col3Links with col3Title. This mismatch could cause confusion when debugging or tracking list identities. Consider changing the listId to "col3" for consistency.
| listId="col5" | |
| listId="col3" |
src/components/layout/NavBar.tsx
Outdated
| textColor = "#FFFFFF", | ||
| openTextColor = "#FFFFFF", | ||
| logoColor = "#000000", | ||
| openLogoColor = "#FFFFFF", |
There was a problem hiding this comment.
Unused variable openLogoColor.
| openLogoColor = "#FFFFFF", |
src/components/layout/NavBar.tsx
Outdated
| ], | ||
| previewOpen, | ||
| previewMode = "auto", | ||
| shellMaxWidth = "1200px", |
There was a problem hiding this comment.
Unused variable shellMaxWidth.
| shellMaxWidth = "1200px", |
scriswell
left a comment
There was a problem hiding this comment.
@dallasbpeters It doesn't look like we've used Optics for this at all. That's OK, we don't need to force that here. Approved. @mark-kraemer You will likely want to review the code as well before we ship.
| --blue-green-700: #0e2e34ff; | ||
| --blue-green-800: #0b252a; | ||
| --blue-green-900: #041f20ff; | ||
| --blue-green-900: #04242B; |
There was a problem hiding this comment.
@dallasbpeters Doesn't look like we're using optics with this project. Is that correct?
There was a problem hiding this comment.
We are using optics.
| function getIconComponent( | ||
| iconName: string, | ||
| variant: 'stroke' | 'solid' | 'duotone' | 'bulk' = 'stroke' | ||
| ) { | ||
| // Convert kebab-case to PascalCase and add Icon suffix | ||
| const pascalCase = iconName | ||
| .split('-') | ||
| .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) | ||
| .join('') | ||
| const iconKey = `${pascalCase}Icon` | ||
|
|
||
| // Select the icon set based on variant | ||
| const iconSet = | ||
| variant === 'solid' | ||
| ? HugeIconsSolid | ||
| : variant === 'duotone' | ||
| ? HugeIconsDuotone | ||
| : HugeIconsStroke | ||
|
|
||
| return (iconSet as Record<string, IconSvgElement>)[iconKey] || null |
| const renderSvg = (raw: string) => { | ||
| if (!raw) return null | ||
| const sanitize = (svg: string) => | ||
| svg | ||
| .replace(/fill="(?!none)[^"]*"/gi, 'fill="currentColor"') | ||
| .replace(/stroke="(?!none)[^"]*"/gi, 'stroke="currentColor"') | ||
| .replace(/fill='(?!none)[^']*'/gi, "fill='currentColor'") | ||
| .replace(/stroke='(?!none)[^']*'/gi, "stroke='currentColor'") | ||
| const cleaned = sanitize(raw) | ||
| return ( | ||
| <span | ||
| aria-hidden="true" | ||
| className="button-icon-sanitize" | ||
| style={{ | ||
| display: 'inline-flex', | ||
| width: '100%', | ||
| height: '100%', | ||
| color: 'inherit', | ||
| }} | ||
| dangerouslySetInnerHTML={{ __html: cleaned }} | ||
| /> | ||
| ) | ||
| } |
There was a problem hiding this comment.
@mark-kraemer again, what are your thoughts here?
…and accessibility Key changes: - Removed TypeScript no-check directive from NavBar - Fixed display property typo in NavBar styles - Adjusted scroll event polling interval for better performance - Updated spotlight link in NavBar for clarity - Removed unused logo color props in NavBar - Simplified icon variant options in ButtonPill - Enhanced SVG sanitization in ButtonPill for better security
…, and LandingPageC for consistency and improved functionality Key changes: - Replaced direct imports of ButtonPill with updated import paths - Simplified icon handling by using endIconName and showEndIcon props - Removed unused icon imports and adjusted button properties for better performance - Ensured hydration compatibility for icon rendering in ButtonPill - Deleted the old button-animated component to streamline the codebase
Summary
Notable fixes
anytypes, unused variables, motion/reactEasingtype compatibility)useEffect)@hugeicons-pro/core-twotone-roundedwith@hugeicons-pro/core-duotone-roundedfor correct icon styleanimateprop no longer uses CSS variables (browser handles transition via CSStransitionproperty instead)heightprop; logo sized appropriatelyrolemodelsoftware.comdomain