-
Notifications
You must be signed in to change notification settings - Fork 121
feat:UI/UX enhancement added using material UI library #226
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
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdds MUI/Emotion dependencies, a new reusable Button, extensive CSS/tailwind token updates, and large MUI + Framer Motion rewrites for Hero, Waitlist, Navbar, and Footer; also removes four frontend page re-exports. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Waitlist as Waitlist Component
participant Google as Google Forms
participant UI as UI State/Toast
User->>Waitlist: Fill form + click Submit
Waitlist->>Waitlist: Validate, set submitting state
Waitlist->>Google: POST form data (FORM_URL)
Google-->>Waitlist: 200 OK / error
alt success
Waitlist->>UI: show success toast, reset fields, set submitted
else error
Waitlist->>UI: show error Alert, unset submitting
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In @landing/src/components/layout/Navbar.tsx:
- Around line 140-148: The Button in Navbar.tsx is rendered with an href which
makes it an anchor and prevents the onClick (handleDrawerToggle) from firing;
either update the Button component to forward onClick when href is present or
change this usage to an onClick-only flow: remove href and call
handleDrawerToggle then programmatically navigate to the waitlist fragment (use
your router/navigation helper) using isHomePage to decide between '#waitlist' vs
'/#waitlist'; ensure the handler name handleDrawerToggle and the isHomePage
check remain used so the drawer closes and navigation occurs correctly.
In @landing/src/components/ui/Button.tsx:
- Around line 29-38: The Button component's CSS string (baseStyles) and other
class strings use non-standard Tailwind classes `duration-400` and
`duration-600` which won't work unless you extend Tailwind; either replace those
with supported durations like `duration-300`/`duration-500` in the `baseStyles`
(and any other class constants/usages in this file) or add `400` and `600` to
`theme.extend.transitionDuration` in your Tailwind config so `duration-400` and
`duration-600` resolve correctly; update all occurrences consistently (e.g., the
`baseStyles` variable and any hover/active class strings) to match the chosen
approach.
- Around line 94-105: The Button component renders an anchor (<motion.a>) when
href is present but currently drops the onClick prop; update the render so the
anchor forwards the onClick handler (and respect disabled by ignoring calls or
preventing default when disabled) — locate the conditional branch that returns
<motion.a ...> (references: Button component, combinedStyles, content, motion.a)
and add the onClick prop forwarding and appropriate disabled handling so
consumers passing both href and onClick have their handler invoked.
In @landing/tailwind.config.js:
- Around line 63-69: The Tailwind config is missing a transitionDuration
extension required by Button.tsx's nonstandard classes; add a
theme.extend.transitionDuration entry (e.g., keys 400 and 600 mapped to '400ms'
and '600ms') alongside the existing animation and backdropBlur entries so the
duration-400 and duration-600 utility classes work.
🧹 Nitpick comments (7)
landing/src/components/sections/Waitlist.tsx (2)
262-324: Consider using the reusable Button component.The Waitlist form implements a custom premium button with animations inline, while a reusable
Buttoncomponent exists in../ui/Button.tsx. This creates inconsistency and duplicates styling logic.However, given the specialized nature of this form submit button (loading state, form-specific animations), keeping it inline may be intentional. If so, consider extracting this into a variant of the
Buttoncomponent or a dedicatedSubmitButtoncomponent to reduce duplication and ensure consistency.
59-69: Theno-corsmode prevents response validation.With
mode: 'no-cors', the fetch response is opaque and cannot be inspected. The code assumes success if no exception is thrown. This is a known limitation when posting to Google Forms cross-origin, but be aware that network errors may not be distinguishable from successful submissions.Consider adding a comment explaining this limitation for future maintainers.
landing/src/index.css (1)
134-194: Button CSS duplicates the React Button component styles.The
.btn-primaryand.btn-secondaryCSS classes (lines 134-256) implement similar styling logic to theButton.tsxcomponent. This creates two sources of truth for button styling.Consider consolidating: either use the CSS classes within the React component or remove the CSS classes if they're unused.
landing/src/components/layout/Footer.tsx (1)
98-114: Quick Links may not scroll correctly on non-home pages.When
isHomePageis false, the href becomes/${link.href}(e.g.,/#features). Using an<a>tag with this href will navigate to the home page but may not scroll to the anchor consistently across browsers.Consider using react-router's
Linkcomponent with hash navigation or a programmatic scroll handler for better reliability.landing/src/components/sections/Hero.tsx (3)
30-31: Consider performance impact of large blur effects.Multiple blur effects with large values (
blur-[120px],blur-[100px]) on overlapping divs can significantly impact rendering performance, especially on mobile devices or lower-end hardware.💡 Optimization suggestions
- Reduce blur radius values (e.g.,
blur-[80px]instead ofblur-[120px])- Use CSS
will-change: filtersparingly if blur animations are added- Consider using a single composite background image for better performance if the design allows
- Test on mobile devices to ensure smooth scrolling
65-75: Improved readability with better typography.The updated text sizing and
leading-relaxedenhance readability. The content restructuring with emphasis on the value proposition is effective.Minor: Consider using CSS margin/padding instead of
<br />(line 73) for semantic HTML, though the current implementation is acceptable for content control.
97-115: Premium visual enhancements for dashboard preview.The layered effects (glow, fade, border) create a polished presentation. The updated alt text "Devr.AI Dashboard Preview" is appropriately descriptive for accessibility.
The dashboard preview now has three overlay divs (lines 104, 107, plus the container). Consider extracting this into a reusable
ImageShowcaseorPreviewCardcomponent if similar patterns are used elsewhere, to improve maintainability.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
landing/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (8)
landing/package.jsonlanding/src/components/layout/Footer.tsxlanding/src/components/layout/Navbar.tsxlanding/src/components/sections/Hero.tsxlanding/src/components/sections/Waitlist.tsxlanding/src/components/ui/Button.tsxlanding/src/index.csslanding/tailwind.config.js
🔇 Additional comments (13)
landing/src/index.css (1)
319-326: Good accessibility focus styles.The focus-visible styles with ring offset provide clear keyboard navigation indicators. This is a good accessibility practice.
landing/src/components/layout/Footer.tsx (1)
76-91: Good accessibility implementation for social links.The social icon links correctly include
aria-labelattributes, making them accessible to screen readers. Therel="noreferrer"is also properly set for external links.landing/tailwind.config.js (1)
42-53: Well-structured shadow tokens with semantic naming.The shadow extensions provide a good range of elevations and glow effects. The naming convention (glow, glow-strong, glow-lg, glow-cyan) is clear and follows a consistent pattern.
landing/src/components/layout/Navbar.tsx (2)
20-32: Well-implemented styled component with proper prop filtering.The
shouldForwardPropcorrectly prevents thescrolledboolean from being passed to the DOM element, avoiding React warnings. The glassmorphism effect with backdrop-filter and transitions is cleanly implemented.
253-255: Good practice: Toolbar spacer for fixed positioning.The empty
Toolbarcomponent at the end provides the correct spacing to prevent content from being hidden behind the fixed navbar.landing/package.json (1)
13-15: React 18.2.0 is fully supported by MUI v7 (react: ^18.0.0is an explicit peer dependency). No compatibility concerns.Note: MUI v7 ships with react-is@19; according to the upgrade guide, if staying on React 18 you may need to force react-is to match your React version to avoid potential issues.
landing/src/components/sections/Hero.tsx (7)
23-23: Improved spacing enhances visual hierarchy.The increased padding (
pt-32 pb-20) provides better breathing room for the hero section and aligns well with the Navbar height reduction mentioned in the PR (80px → 64px).
57-63: Enhanced typography creates stronger visual impact.The larger font sizes (
text-6xltolg:text-8xl) andfont-extraboldweight significantly improve the hero heading's presence and align with modern landing page design best practices.
77-95: Clean CTA implementation with reusable component.The Button component usage creates consistency across the application, and the icon integration (Users icon on primary CTA) adds visual interest. The spacing (
gap-6,mb-16) provides good visual separation.
48-48: Increased content width complements larger typography.The wider container (
max-w-6xl) appropriately accommodates the larger heading and text sizes, maintaining good line lengths for readability.
34-34: All custom CSS classes (bg-grid-pattern,gradient-text,elevation-xl,interactive-lift) are already properly defined insrc/index.css. No action required.Likely an incorrect or invalid review comment.
82-94: No action needed — Button component correctly handles href as a semantic link.The Button component already renders as a proper
<a>tag (viamotion.aon line 96) whenhrefis provided, and as a<button>when it's not. This ensures proper accessibility and SEO without requiring changes.
30-31: Verify color consistency: Hero section uses primary (green) while other sections use secondary (cyan).The Hero section accent gradients use
bg-green-600/10andbg-green-500/5(primary colors), while the Footer usesbg-cyan-500/5(secondary colors). The tailwind config defines cyan as the secondary color. Clarify whether this color choice in Hero is intentional or should align with secondary colors used elsewhere.
…nce Tailwind CSS configuration for improved styling
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @landing/src/components/layout/Navbar.tsx:
- Around line 1-4: The named import from lucide-react in Navbar.tsx currently
has a trailing comma inside the braces ({ Menu, X,}) which causes lint/prettier
churn; open the import line that imports Menu and X and remove the trailing
comma so it reads with a normal named import (e.g., { Menu, X }) and ensure
spacing matches project formatting rules.
In @landing/src/components/ui/Button.tsx:
- Around line 61-90: The overlay span uses hover:opacity-100 which won't
activate when hovering the child content because the content sits above it;
change that class to group-hover:opacity-100 on the background overlay span and
ensure the root element that renders this content (the wrapping
Button/component) includes the "group" utility class so both the bg overlay and
the shimmer (which already uses group-hover) respond when hovering any part of
the button; update the overlay span with group-hover and verify the parent/root
element (the component wrapping content) contains "group" in its className.
🧹 Nitpick comments (2)
landing/src/components/ui/Button.tsx (1)
4-15: BroadenonClicktyping + forward the event to consumers.Right now
onClickcan’t receive the click event (Line 8), and the anchor path drops the event entirely (Line 99). Consider passing througheso callers canpreventDefault(), stop propagation, etc.Proposed fix
interface ButtonProps { children: React.ReactNode; variant?: 'primary' | 'secondary'; href?: string; - onClick?: () => void; + onClick?: (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => void; type?: 'button' | 'submit' | 'reset'; disabled?: boolean; className?: string; startIcon?: React.ReactNode; endIcon?: React.ReactNode; fullWidth?: boolean; } @@ const handleAnchorClick = (e: React.MouseEvent<HTMLAnchorElement>) => { if (disabled) { e.preventDefault(); return; } - onClick?.(); + onClick?.(e); }; @@ return ( <motion.button type={type} - onClick={onClick} + onClick={onClick as unknown as React.MouseEventHandler<HTMLButtonElement>} disabled={disabled} className={`${combinedStyles} group`} whileHover={!disabled ? { y: -3, scale: 1.02 } : {}} whileTap={!disabled ? { y: -1, scale: 1 } : {}} >If you’d rather avoid casts, the cleaner version is a discriminated union for props (
hrefpresent ⇒ anchor handler type; otherwise ⇒ button handler type).Also applies to: 94-100, 118-129
landing/tailwind.config.js (1)
35-43: Consider addingsystem-ui(and other default fallbacks) tofontFamily.sans.Right now
sansomitssystem-ui(common Tailwind fallback) which can help on non-Apple / non-Windows setups.Possible tweak
fontFamily: { sans: [ 'Inter', + 'system-ui', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'sans-serif', ], },
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
poetry.lockis excluded by!**/*.lock
📒 Files selected for processing (3)
landing/src/components/layout/Navbar.tsxlanding/src/components/ui/Button.tsxlanding/tailwind.config.js
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @landing/src/components/ui/Button.tsx:
- Around line 102-116: When Button.tsx would render an anchor with both href and
disabled, change the behavior to render a non-interactive element instead:
detect the case (href && disabled) and render a motion.span (or plain span) with
the same styling/className and content, but remove href,
onClick/handleAnchorClick, keyboard handlers, and tabIndex so the element is not
focusable; keep visual motion props inert (no whileHover/whileTap) and remove
aria-disabled on the anchor path so there is no semantic mismatch—this ensures
the disabled state for the component (Button) is non-interactive and accessible
instead of using a disabled anchor.
🧹 Nitpick comments (2)
landing/src/components/ui/Button.tsx (2)
29-38: Consider using a classnames utility library.String concatenation for composing Tailwind classes can be fragile and harder to maintain. Consider using
clsxorclassnamesfor more robust class composition, especially with conditional logic.♻️ Example refactor with clsx
+import clsx from 'clsx'; - const baseStyles = ` - relative overflow-hidden - px-8 py-4 rounded-xl - font-semibold text-lg - inline-flex items-center justify-center gap-3 - transition-all duration-500 ease-out - ${fullWidth ? 'w-full' : ''} - ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'} - ${className} - `; + const baseStyles = clsx( + 'relative overflow-hidden', + 'px-8 py-4 rounded-xl', + 'font-semibold text-lg', + 'inline-flex items-center justify-center gap-3', + 'transition-all duration-500 ease-out', + fullWidth && 'w-full', + disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer', + className + );
40-59: Variant styles look good.The gradient and shadow definitions are well-structured. For a minor optimization, consider extracting
variantStylesas a constant outside the component to avoid recreation on every render.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
landing/src/components/layout/Navbar.tsxlanding/src/components/ui/Button.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- landing/src/components/layout/Navbar.tsx
🔇 Additional comments (4)
landing/src/components/ui/Button.tsx (4)
1-15: LGTM! Well-typed component API.The interface design is clean with appropriate types for all props. The
variantunion type and icon support provide good flexibility while maintaining type safety.
61-90: LGTM! Layered visual effects implemented correctly.The z-index stacking (z-10 on content) properly ensures text visibility during hover animations. The shimmer effect correctly uses
pointer-events-noneto avoid interfering with interactions.
94-100: Handler logic is correct but see accessibility concerns below.The disabled check and optional onClick invocation are implemented correctly. However, there's a broader accessibility issue with disabled anchors that needs attention (see next comment).
118-132: LGTM! Button element implementation is correct.The button rendering uses native HTML semantics properly. The
disabledattribute works as expected for button elements, and the conditional animations respect the disabled state.
…ist sections with improved styling and functionality
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
landing/package.json (1)
12-24: Pinreact-isto v18 for MUI v7 compatibility + upgradereact-router-domto >=7.5.2 for security fixesMUI v7 ships with
react-is@19, which conflicts with React 18. Add a package-manager-specific override:
- npm:
"overrides": { "react-is": "^18.3.1" }- yarn/pnpm:
"resolutions": { "react-is": "^18.3.1" }
[email protected]has two high-severity SSR/cache-poisoning vulnerabilities (CVE-2025-43864, CVE-2025-43865) that allow header-based data injection attacks and pre-render cache bypass. Upgrade to >=7.5.2 immediately.Recommended package.json changes
{ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/icons-material": "^7.3.7", "@mui/material": "^7.3.7", "@studio-freight/lenis": "^1.0.42", "framer-motion": "^10.16.4", "lucide-react": "^0.344.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.5.2", - "react-router-dom": "^7.5.0" + "react-router-dom": "^7.5.2" }, + "overrides": { + "react-is": "^18.3.1" + } }
🤖 Fix all issues with AI agents
In @landing/src/components/layout/Footer.tsx:
- Around line 45-64: The socialLinks array and other anchors in the Footer
component open external sites with target="_blank" but are missing rel="noopener
noreferrer"; update the anchor elements that render items from socialLinks (and
any other Footer anchors between the previously noted ranges) to include
rel="noopener noreferrer" alongside target="_blank" to prevent tabnabbing and
ensure safe external link behavior.
- Around line 248-283: Replace the plain MuiLink href usage in the quickLinks
map with the Navbar-style custom hash scroll handler: preventDefault on clicks,
detect if link.href contains a hash, if not on the home page navigate to '/'
first, then call the shared handleSmoothScroll utility (or import the Navbar's
handleSmoothScroll) to perform the smooth scroll to the target element; update
the MuiLink in the quickLinks map to use onClick instead of href and ensure it
still renders link.label and preserves styles/className while keeping isHomePage
logic to decide whether to navigate before scrolling.
In @landing/src/components/layout/Navbar.tsx:
- Around line 24-92: The current handleSmoothScroll prevents navigation and
duplicates scrolling logic, causing the URL hash to never update and risking
timing races; change handleSmoothScroll to stop calling e.preventDefault() and
instead call setIsOpen(false) then navigate(href) with the hash included (e.g.,
'/#section'), remove the duplicated scrolling code in handleSmoothScroll so all
scroll behavior is handled by the existing useEffect that watches location.hash,
and update that useEffect to consolidate scroll logic and add a cleanup to clear
any timeout (store the timer id and clearTimeout in the return cleanup) to avoid
race conditions when navigating between pages.
In @landing/src/components/sections/Waitlist.tsx:
- Around line 505-544: The Select onChange handler is untyped causing TS
implicit any errors; import SelectChangeEvent from '@mui/material' and update
the handler to accept a typed event (e.g. onChange={(e:
SelectChangeEvent<string>) => setRole(e.target.value)}) ensuring setRole expects
a string; add the SelectChangeEvent import near other MUI imports and adjust
types if role/state is not already typed as string.
🧹 Nitpick comments (6)
landing/src/components/layout/Navbar.tsx (2)
13-22: Scroll listener: initialize state once + mark listener as passiveThis currently won’t set
scrolledcorrectly on initial render if the page loads mid-scroll (e.g., refresh on an anchor). Also,scrollshould be passive.Proposed tweak
useEffect(() => { const handleScroll = () => { setScrolled(window.scrollY > 50); }; + handleScroll(); - window.addEventListener('scroll', handleScroll); + window.addEventListener('scroll', handleScroll, { passive: true }); return () => { window.removeEventListener('scroll', handleScroll); }; }, []);
111-123: Avoid duplicating gradient styling in bothgradient-textclass and inline styleRight now the
<span>hasclassName="... gradient-text"plus hard-coded inline gradients. Prefer one source of truth (CSS class or theme token) to avoid drift.landing/src/components/layout/Footer.tsx (1)
377-421: “Back to top”: considertype="button"+ use a button component
MuiLink component="button"works, but you’ll get more consistent keyboard/focus styling by usingButtonBase/Buttonand explicitly settingtype="button".landing/src/components/sections/Waitlist.tsx (2)
54-103:mode: "no-cors"prevents confirming Google Form submissionWith
no-cors, you can’t inspect status/response; users may see success even if the form rejects (you’ll only catch network-level failures). If that’s acceptable, consider adding a small disclaimer or logging; if not, you likely need a proxy endpoint you control.
321-598: Deduplicate repeated inputsxblocks to reduce driftThe TextField/FormControl styling is copy/pasted across multiple inputs; extracting shared
conststyles (and then composing per-field deltas) will make future tweaks much safer.landing/src/components/sections/Hero.tsx (1)
33-150: Consider consolidating repeated gradient/color strings into theme tokensThe same multi-stop gradients appear across Navbar/Footer/Hero. Centralizing them (MUI theme or a small constants module) will reduce visual drift.
Also applies to: 165-251
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
landing/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
landing/package.jsonlanding/src/components/layout/Footer.tsxlanding/src/components/layout/Navbar.tsxlanding/src/components/sections/Hero.tsxlanding/src/components/sections/Waitlist.tsx
…onents with QuickLink for improved hash navigation, and update Waitlist component for type safety
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @landing/src/components/layout/Footer.tsx:
- Around line 39-72: The MuiLink component rendering (the JSX return that uses
MuiLink with onClick and styling) is missing an href which breaks standard link
behavior and accessibility; update the MuiLink element to include an href
attribute (e.g., href={link.href} or href={link.url}) and keep the existing
onClick handler (handleClick) so the link is both accessible and preserves
client-side navigation; ensure link data (the property used for the URL) is
present where link.label is used and default to '#' or omit onClick behavior if
no URL exists.
- Around line 433-441: The ArrowUpIcon's '&:hover' won't fire because the parent
button/motion.div handles hover; remove the redundant '&:hover' block from the
ArrowUpIcon SX prop and keep only the static styles (e.g., fontSize and
transition) or rely fully on the motion.div's whileHover={{ y: -2 }} to provide
the lift effect; update the ArrowUpIcon SX in the Footer component to eliminate
the hover transform so the motion.div's animation is the single source of hover
behavior.
In @landing/src/components/layout/Navbar.tsx:
- Around line 52-59: handleSmoothScroll currently declares the event parameter e
but doesn't call e.preventDefault(), causing the anchor's native navigation and
navigate(href) to both run; update handleSmoothScroll to call e.preventDefault()
at the start, keep setIsOpen(false) to close the mobile menu, then call
navigate(href) so react-router handles navigation exclusively (refer to
handleSmoothScroll, navigate, and setIsOpen).
🧹 Nitpick comments (10)
landing/src/components/layout/Navbar.tsx (3)
24-50: Deprecated API and potential offset mismatch.
window.pageYOffset(line 34) is deprecated. Usewindow.scrollYinstead.- The offset is hardcoded to
80, but the PR description indicates the navbar height was reduced to 64px. This may cause the scroll position to be 16px off.Suggested fix
- const offset = 80; // Account for fixed navbar height - const elementPosition = element.getBoundingClientRect().top + window.pageYOffset; + const offset = 64; // Account for fixed navbar height + const elementPosition = element.getBoundingClientRect().top + window.scrollY;
80-90: Redundant gradient styling.The
gradient-textclass is applied alongside inline styles that define the same gradient effect. This duplication creates maintenance overhead and potential style conflicts.Consider removing either the class or the inline styles to have a single source of truth.
If inline styles are preferred (for this unique gradient)
<span - className="text-2xl font-bold gradient-text" + className="text-2xl font-bold" style={{
109-115: Minor: Duplicated href logic.The waitlist href is computed identically in both the
hrefattribute and theonClickhandler argument. Consider extracting to a variable for DRY:+ const waitlistHref = isHomePage ? "#waitlist" : "/#waitlist"; <a - href={isHomePage ? "#waitlist" : "/#waitlist"} - onClick={(e) => handleSmoothScroll(e, isHomePage ? "#waitlist" : "/#waitlist")} + href={waitlistHref} + onClick={(e) => handleSmoothScroll(e, waitlistHref)}Same pattern applies to the mobile menu waitlist link (lines 147-148).
landing/src/components/layout/Footer.tsx (2)
145-158: Consider removing redundant styling declarations.The styling is duplicated between Tailwind
classNameand MUIsxprops throughout the file (e.g., positioning, dimensions, colors). This works but creates maintenance overhead since changes require updates in both places. Consider using one approach consistently.
28-73: Consider extractingQuickLinkoutside the component.Defining
QuickLinkinsideFooterrecreates the component definition on each render. While unlikely to cause noticeable performance issues for a footer, extracting it as a standalone component (or usinguseCallbackfor the handler) would be cleaner and avoid unnecessary re-definitions.landing/src/components/sections/Waitlist.tsx (5)
36-53: Consider moving constants outside the component.
roleOptions,FORM_ID,FORM_URL, andFIELD_MAPPINGSare static values that don't depend on props or state. Moving them outside the component avoids recreating these objects on every render.♻️ Suggested refactor
+const ROLE_OPTIONS = [ + "Student (Currently in school, university, or bootcamp)", + "Working Professional (Employed in tech or a related field)", + "Business Professional (Entrepreneur, startup founder, or executive)", + "Open Source & Community Member (Contributor, maintainer, or community volunteer)", + "Other:" +]; + +const FORM_ID = '1FAIpQLSet1YnQTfOmfSzDblYxTXHSEHDhT4l7_LoqtN-5k-WUJAZUBw'; +const FORM_URL = `https://docs.google.com/forms/d/e/${FORM_ID}/formResponse`; + +const FIELD_MAPPINGS = { + name: 'entry.1958162697', + email: 'entry.1305603809', + organization: 'entry.1486768385', + role: 'entry.1449836771', + suggestions: 'entry.1484217632' +} as const; + const Waitlist: React.FC = () => { // ... state declarations - - const roleOptions = [ - "Student (Currently in school, university, or bootcamp)", - // ... - ]; - - const FORM_ID = '1FAIpQLSet1YnQTfOmfSzDblYxTXHSEHDhT4l7_LoqtN-5k-WUJAZUBw'; - const FORM_URL = `https://docs.google.com/forms/d/e/${FORM_ID}/formResponse`; - - const FIELD_MAPPINGS = { - // ... - };
107-140: Remove duplicate styling betweenclassNameandsxprops.The Tailwind classes in
classNameand the MUIsxstyles are duplicating the same visual effects (e.g.,overflow-hidden+overflow: 'hidden',relative+position: 'relative'). This creates maintenance overhead and risks inconsistency if one is updated without the other.Since you're migrating to MUI, consider keeping only the
sxprop styling for consistency:♻️ Example for first decorative Box
<Box - className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-secondary/5 blur-3xl" sx={{ position: 'absolute', inset: 0, background: 'linear-gradient(to bottom right, rgba(34, 197, 94, 0.05), rgba(59, 130, 246, 0.03), transparent, rgba(6, 182, 212, 0.05))', filter: 'blur(64px)', }} />
323-416: Extract repeated TextField styles to reduce duplication.The
sxprop styling for TextFields is duplicated ~4 times across name, email, organization, and suggestions fields (~80 lines each). This violates DRY and makes future theme changes error-prone.♻️ Suggested refactor
Extract to a shared style object:
// Define once, outside component or in a styles file const textFieldSx = { '& .MuiOutlinedInput-root': { backgroundColor: 'rgba(39, 39, 42, 0.8)', backdropFilter: 'blur(8px)', color: 'white', borderRadius: '12px', border: '1px solid rgba(63, 63, 70, 1)', transition: 'all 0.3s ease', '&:hover': { borderColor: 'rgba(82, 82, 91, 1)' }, '&.Mui-focused': { borderColor: 'rgba(34, 197, 94, 0.5)', boxShadow: '0 0 0 2px rgba(34, 197, 94, 0.1)', }, '& fieldset': { border: 'none' }, }, '& .MuiInputLabel-root': { color: 'rgba(209, 213, 219, 1)', fontWeight: 600, fontSize: '0.875rem', '&.Mui-focused': { color: 'rgba(34, 197, 94, 1)' }, }, '& .MuiInputBase-input': { color: 'white', '&::placeholder': { color: 'rgba(113, 113, 122, 1)', opacity: 1 }, }, }; // Then use: <TextField sx={textFieldSx} /* other props */ />
614-644: Hover selectors on decorative Boxes won't trigger.The
&:hoverselectors at lines 624-626 and 641-643 are on absolutely positioned decorativeBoxelements that don't receive pointer events. The hover effects are already handled by the Tailwind classes (group-hover:opacity-100), so these sx hover rules are dead code.♻️ Remove unused hover selectors
sx={{ position: 'absolute', inset: '-2px', background: 'linear-gradient(to right, #22c55e, #3b82f6, #06b6d4, #2563eb, #22c55e)', borderRadius: '12px', filter: 'blur(4px)', opacity: 0.75, transition: 'opacity 0.3s ease', - '&:hover': { - opacity: 1, - }, }}
719-725: Static "1000+" count may become stale.The hardcoded "1000+ developers" claim will need manual updates as the waitlist grows. Consider either making this dynamic (if you have an API) or using vaguer language like "thousands of developers" to avoid maintenance burden.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
frontend/src/components/pages/index.tslanding/src/components/layout/Footer.tsxlanding/src/components/layout/Navbar.tsxlanding/src/components/sections/Waitlist.tsx
💤 Files with no reviewable changes (1)
- frontend/src/components/pages/index.ts
🔇 Additional comments (6)
landing/src/components/layout/Footer.tsx (1)
245-279: LGTM!Social links are well-implemented with proper accessibility (
aria-label), security attributes (rel="noopener noreferrer"), and smooth motion animations.landing/src/components/sections/Waitlist.tsx (5)
1-24: LGTM!Imports are well-organized, properly typed with
SelectChangeEvent, and all appear to be used in the component.
79-89:no-corsmode prevents error detection from Google Forms.With
mode: 'no-cors', the fetch response is opaque—you cannot read status codes or body. If Google Forms rejects the submission (e.g., form disabled, field validation fails server-side), the code will still execute the success path. This is a known limitation when posting directly to Google Forms without a backend proxy.Consider documenting this limitation or, if reliable submission confirmation is critical, routing through a backend endpoint that can properly handle the Google Forms API response.
167-175: LGTM!The Paper component with glassmorphism styling (backdrop blur, semi-transparent background) creates a nice visual effect. The gradient border wrapper technique works well for the premium look.
230-285: LGTM!The success panel provides clear feedback with a smooth animation, appropriate success colors, and informative messaging.
536-544: LGTM!The Select implementation with placeholder MenuItem and proper
SelectChangeEventtyping is correct. Using the option string as the key works since role options are unique.
…default behavior in handleSmoothScroll function in Navbar
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @landing/src/components/layout/Footer.tsx:
- Around line 40-41: The MuiLink element currently only has an onClick handler
(handleClick) which harms accessibility and prevents normal link behaviors; add
an href prop to the same MuiLink (using the same target URL used by handleClick
or a dedicated url/route prop) so the link is a real anchor for keyboard/screen
readers and "open in new tab" functionality, and keep the onClick for
analytics/side effects (if handleClick calls e.preventDefault(), only do so when
necessary so progressive enhancement remains functional).
🧹 Nitpick comments (4)
landing/src/components/layout/Footer.tsx (4)
27-73: MoveQuickLinkoutsideFooterto avoid recreating it on every render.Defining
QuickLinkas a component insideFootermeans it's recreated on every render, causing React to unmount/remount these elements and losing any internal state. Extract it to module scope and passnavigateandisHomePageas props.Additionally, the component has duplicate styling in both
className(Tailwind) andsx(MUI). Consider consolidating to one approach for maintainability.♻️ Suggested refactor
+// Move outside Footer component at module scope +interface QuickLinkProps { + link: { label: string; href: string }; + isHomePage: boolean; + navigate: ReturnType<typeof useNavigate>; +} + +const QuickLink: React.FC<QuickLinkProps> = ({ link, isHomePage, navigate }) => { + const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => { + e.preventDefault(); + const target = isHomePage ? link.href : `/${link.href}`; + navigate(target); + }; + + return ( + <MuiLink + href={link.href} + onClick={handleClick} + sx={{ + color: 'rgba(161, 161, 170, 1)', + fontSize: '0.875rem', + textDecoration: 'none', + display: 'inline-flex', + alignItems: 'center', + gap: '8px', + position: 'relative', + transition: 'color 0.3s ease', + '&:hover': { + color: 'rgba(34, 197, 94, 1)', + }, + '&::before': { + content: '""', + width: 0, + height: '2px', + background: 'linear-gradient(to right, #4ade80, #22d3ee)', + transition: 'width 0.3s ease', + position: 'absolute', + left: 0, + bottom: '-2px', + }, + '&:hover::before': { + width: '16px', + }, + }} + > + {link.label} + </MuiLink> + ); +};Then update the usage in Footer:
<QuickLink key={link.label} link={link} isHomePage={isHomePage} navigate={navigate} />
79-93: Consider moving animation variants to module scope.These variant objects are static and don't depend on props or state. Defining them inside the component creates new object references on each render, which can cause unnecessary Framer Motion recalculations.
♻️ Move to module scope
+// At module scope, before Footer component +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + delayChildren: 0.2, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { opacity: 1, y: 0 }, +}; + const Footer: React.FC = () => { // ... remove the variant definitions from inside
95-126: Move static data arrays to module scope.
quickLinksandlegalLinksare static and can be hoisted. ForsocialLinks, consider storing icon component references instead of JSX instances to avoid recreating icon elements on each render.♻️ Suggested refactor
// At module scope const quickLinks = [ { label: 'Features', href: '#features' }, { label: 'How It Works', href: '#how-it-works' }, { label: 'Integrations', href: '#integrations' }, { label: 'Join Waitlist', href: '#waitlist' }, ]; const legalLinks = [ { label: 'Privacy Policy', to: '/privacy-policy' }, { label: 'Terms of Service', to: '/terms-of-service' }, ]; const socialLinks = [ { Icon: GitHubIcon, href: 'https://github.com/AOSSIE-Org/Devr.AI/', label: 'GitHub', hoverColor: 'rgba(34, 197, 94, 1)' }, { Icon: TwitterIcon, href: 'https://x.com/aossie_org?lang=en', label: 'Twitter', hoverColor: 'rgba(6, 182, 212, 1)' }, { Icon: LinkedInIcon, href: 'https://www.linkedin.com/company/aossie/?originalSubdomain=au', label: 'LinkedIn', hoverColor: 'rgba(34, 197, 94, 1)' }, ]; // Then in JSX: <social.Icon /> instead of {social.icon}
137-198: Background styling pattern is functional but has redundant declarations.The decorative background elements work correctly with
pointer-events: none. However, the pattern of duplicating styles in bothclassName(Tailwind) andsx(MUI) continues here. Consider adopting one styling approach consistently to reduce bundle size and avoid specificity conflicts.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
landing/src/components/layout/Footer.tsxlanding/src/components/layout/Navbar.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- landing/src/components/layout/Navbar.tsx
🔇 Additional comments (3)
landing/src/components/layout/Footer.tsx (3)
1-18: LGTM!Imports are well-organized by source (React, Framer Motion, MUI components, MUI icons, React Router) with appropriate aliasing to avoid naming conflicts.
252-277: LGTM! External links are properly secured.Good security practices with
target="_blank"paired withrel="noopener noreferrer", and properaria-labelfor accessibility. The conditional hover color logic (lines 268-270) works but could be cleaner if the color was stored directly in thesocialLinksdata.
399-439: LGTM! Scroll to top implementation is solid.Using
MuiLinkwithcomponent="button"maintains semantic correctness for a click action. ThescrollToTopfunction correctly useswindow.scrollTowith smooth behavior. Motion animations provide good user feedback.
There was a problem hiding this 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 (4)
landing/src/components/layout/Footer.tsx (4)
27-74: Consider extractingQuickLinkoutside the component or memoizing it.Defining
QuickLinkas a component insideFootercauses it to be recreated on every render, which can lead to unnecessary re-renders of the link elements and loss of any internal state. SinceQuickLinkdepends onnavigatefrom the parent scope, you have two options:
- Move outside and pass
navigateas a prop (preferred for reusability)- Memoize with
useMemoif keeping it insideOption 1: Extract outside the component
// Outside Footer component interface QuickLinkProps { link: { label: string; href: string }; isHomePage: boolean; navigate: ReturnType<typeof useNavigate>; } const QuickLink: React.FC<QuickLinkProps> = ({ link, isHomePage, navigate }) => { const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => { e.preventDefault(); const target = isHomePage ? link.href : `/${link.href}`; navigate(target); }; return ( <MuiLink href={link.href} onClick={handleClick} // ... rest of the props > {link.label} </MuiLink> ); };
96-127: Consider moving static data arrays outside the component.
socialLinks,quickLinks, andlegalLinksare static and don't depend on component state or props. Moving them outsideFooteravoids recreating these arrays on every render.Move static arrays outside
+import { + GitHub as GitHubIcon, + Twitter as TwitterIcon, + LinkedIn as LinkedInIcon, + KeyboardArrowUp as ArrowUpIcon +} from '@mui/icons-material'; + +const socialLinks = [ + { icon: <GitHubIcon />, href: 'https://github.com/AOSSIE-Org/Devr.AI/', label: 'GitHub', color: 'hover:text-green-400' }, + { icon: <TwitterIcon />, href: 'https://x.com/aossie_org?lang=en', label: 'Twitter', color: 'hover:text-cyan-400' }, + { icon: <LinkedInIcon />, href: 'https://www.linkedin.com/company/aossie/?originalSubdomain=au', label: 'LinkedIn', color: 'hover:text-green-400' }, +]; + +const quickLinks = [ + { label: 'Features', href: '#features' }, + { label: 'How It Works', href: '#how-it-works' }, + { label: 'Integrations', href: '#integrations' }, + { label: 'Join Waitlist', href: '#waitlist' }, +]; + +const legalLinks = [ + { label: 'Privacy Policy', to: '/privacy-policy' }, + { label: 'Terms of Service', to: '/terms-of-service' }, +]; + const Footer: React.FC = () => {
138-199: Redundant styling betweenclassNameandsxprops.The same styles are defined in both Tailwind classes and MUI's
sxprop (e.g.,position: 'absolute'withclassName="absolute", dimensions, transforms). This duplication creates maintenance overhead and potential for inconsistencies if only one is updated.Consider choosing one approach consistently—either Tailwind classes or MUI's
sxprop—for these decorative elements.Example: Use sx only for these decorative boxes
<Box - className="absolute top-0 left-0 w-96 h-96 rounded-full bg-green-500/5 blur-3xl -translate-x-1/2 -translate-y-1/2" sx={{ position: 'absolute', top: 0, left: 0, width: '24rem', height: '24rem', borderRadius: '50%', backgroundColor: 'rgba(34, 197, 94, 0.05)', filter: 'blur(64px)', transform: 'translate(-50%, -50%)', }} />
367-372: Potentially redundant animation setup.This
motion.divsets its owninitial="hidden"andwhileInView="visible"while already being nested inside a parentmotion.div(line 206) with the same configuration. Since it uses the sameitemVariants, the inner animation controls may be unnecessary and could cause the animation to trigger twice.Consider removing the redundant props if the parent container's stagger animation is intended to control this section.
Simplify by removing redundant animation props
<motion.div variants={itemVariants} - initial="hidden" - whileInView="visible" - viewport={{ once: true }} >
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
landing/src/components/layout/Footer.tsx
🔇 Additional comments (5)
landing/src/components/layout/Footer.tsx (5)
1-18: LGTM!Imports are well-organized and appropriately grouped by package. The MUI, Framer Motion, and React Router imports align with the component's functionality.
76-94: LGTM!The
scrollToTopfunction and animation variants are straightforward and functional. The variants follow standard Framer Motion patterns with staggered children animations.
246-280: LGTM!The social links implementation follows best practices:
- External links have
target="_blank"withrel="noopener noreferrer"for securityaria-labelattributes provide accessibility for screen readers- Hover animations with Framer Motion enhance UX
324-361: LGTM!The legal links section correctly integrates React Router's
Linkcomponent with MUI'sMuiLinkusing thecomponentprop pattern. The styling is consistent with the Quick Links section, and properkeyprops are provided for list rendering.
447-449: LGTM!The component is properly exported and the overall structure is complete.
Closes #
Overview
This PR refreshes the landing page UI with a modern, premium look and feel by leveraging Material UI, reusable custom components, and a refined turquoise/cyan theme.
The goal is to improve visual appeal, consistency, responsiveness, and overall user experience, while keeping animations smooth and performance-friendly.
🔧 What’s Changed
🧩 New Reusable Components
Introduced a custom Button component to standardize interactions across the app
startIcon,endIcon)🧭 Material UI Integration
Navbar redesign using Material UI AppBar
🎨 Theme & Visual Enhancements
Added turquoise/cyan as the secondary brand color
secondarypalette:cyan-400 → cyan-700Extended
tailwind.config.jsto support the new design tokens🐞 Bug Fixes & Stability Improvements
lucide-reacticon imports🎯 CSS & Interaction Improvements
📷 Visual Highlights
🤝 Collaboration
None — implemented independently.
✅ Checklist
✅ Checklist
Summary by CodeRabbit
New Features
Style
Chores
✏️ Tip: You can customize this high-level summary in your review settings.