Skip to content

feat: v2 visual rebrand#934

Open
Xaroz wants to merge 7 commits intomainfrom
rebrand/v2-visual-changes
Open

feat: v2 visual rebrand#934
Xaroz wants to merge 7 commits intomainfrom
rebrand/v2-visual-changes

Conversation

@Xaroz
Copy link
Collaborator

@Xaroz Xaroz commented Feb 6, 2026

Summary

Cherry-picks the visual/branding changes from the xaroz/warp-ui-v2 branch — only styling, no behavioral changes.

  • New purple/pink color palette, gradient buttons, and accent glows
  • Custom fonts (PP Valve + PP Fraktion Mono) with font-primary/font-secondary
  • Redesigned header (centered logo on desktop, hamburger menu on mobile/tablet)
  • Simplified footer with gradient logo and nav links (hidden below lg to avoid Intercom overlap)
  • Updated logos, background SVG, and icon colors to match v2
  • TipCard with gradient background and watermark
  • ConnectWalletButton and SideBar styled with accent gradients
  • Fee section always visible with animated loading dots and dash for no fees
  • Discord nav link replaced with Support (help.hyperlane.xyz) + question mark icon
  • font-secondary applied to form labels, buttons, token select, and transfer detail modal
  • Softer background gradient to reduce white glare in top-right corner
  • Font fetching script (scripts/fetch-fonts.mjs) for S3-hosted fonts

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Mobile navigation menu with hamburger control and new nav items
    • Several new themed icons and gradient logos
  • Improvements

    • Simplified, responsive header and footer layouts
    • Consolidated fee UI with clearer loading states and updated modal
    • Server-side automated font fetching with downloaded fonts ignored from source
  • Style

    • Expanded color palette, new gradients and glow shadows
    • Updated global fonts, scrollbar styles, and refined button visuals

Xaroz and others added 4 commits February 6, 2026 12:31
- Tailwind: purple/pink color palette, cream colors, font-primary/secondary, gradients, shadows
- Fonts: PP Fraktion Mono + PP Valve via @font-face, S3 fetch script
- Logos: new purple chevron, gradient wordmark/title
- Icons: updated colors (collapse→gray, confirmed/delivered→green, info→black)
- Header: centered logo, mobile hamburger menu with DropdownMenu
- Footer: gradient logo, shared nav links
- Nav: shared nav system with Stake, X, Hyperlane, Discord, Docs, Github
- Buttons: accent/red use gradient+glow, rounded instead of rounded-lg
- TipCard: gradient bg, watermark logo, responsive sizing
- ConnectWallet: accent gradient with white text
- SideBar: accent gradient headers, blur collapse button
- Loading: app-gradient background with purple spinner
- Background: grid pattern overlay SVG

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n mark icon

- FeeSectionButton: show dash when no fees, animated "Loading..." dots
- TransferFeeModal: accent gradient header matching v2 style
- Nav: replace Discord with Support (help.hyperlane.xyz)
- Add QuestionMarkIcon component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t for nav

- Add font-secondary to form labels, buttons, token select, transfer modal
- Soften top-right background gradient with lavender mid-stop
- Bump header/footer nav breakpoint from sm to lg to avoid Intercom overlap

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperlane-warp-template Ready Ready Preview, Comment Feb 6, 2026 8:33pm
5 Skipped Deployments
Project Deployment Actions Updated (UTC)
analytics-test Ignored Ignored Feb 6, 2026 8:33pm
injective-bridge Ignored Ignored Feb 6, 2026 8:33pm
nexus-bridge Ignored Ignored Feb 6, 2026 8:33pm
ousdt-bridge Ignored Ignored Feb 6, 2026 8:33pm
trump-bridge Ignored Ignored Feb 6, 2026 8:33pm

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

Reintroduces an RPC newline and adds S3 font support (env vars, fetch script, gitignore), adds eight SVG icon components, reorganizes navigation (NavItem + navLinks) with responsive Header/Footer, updates theme/fonts/styles, and applies multiple UI visual tweaks across components.

Changes

Cohort / File(s) Summary
Font management & fetch script
\.env.example, \.gitignore, scripts/fetch-fonts.mjs
Re-adds NEXT_PUBLIC_RPC_OVERRIDES newline; adds AWS S3 env vars (server-side) and scripts/fetch-fonts.mjs to download fonts when creds present; ignores public/fonts/.
Icon components
src/components/icons/BookIcon.tsx, src/components/icons/HamburgerIcon.tsx, src/components/icons/HyperlaneGradientLogo.tsx, src/components/icons/HyperlaneTransparentLogo.tsx, src/components/icons/QuestionMarkIcon.tsx, src/components/icons/StakeIcon.tsx, src/components/icons/WebSimpleIcon.tsx, src/components/icons/XIcon.tsx
Adds eight memoized, theme-aware SVG icon components; each exported for reuse.
Navigation & layout
src/components/nav/Nav.tsx, src/components/nav/Header.tsx, src/components/nav/Footer.tsx
Adds NavItem and navLinks exports; Header refactored for responsive mobile hamburger/dropdown and centered desktop layout; Footer simplified to use shared nav data and new gradient logo.
Theme & styling system
tailwind.config.js, src/styles/globals.css, src/components/buttons/SolidButton.tsx
Extends Tailwind theme (fonts, colors, gradients, shadows, dropShadow); adds custom @font-face and scrollbar styles; SolidButton updated to gradient backgrounds, glow shadows, and revised disabled states.
UI visuals & components
src/components/tip/TipCard.tsx, src/features/transfer/FeeSectionButton.tsx, src/features/transfer/TransferFeeModal.tsx, src/features/wallet/SideBarMenu.tsx, src/features/wallet/ConnectWalletButton.tsx, src/features/WarpContextInitGate.tsx
Several UI/UX changes: TipCard redesigned with dismiss, FeeSectionButton consolidated with loading text, TransferFeeModal header/layout adjustments, wallet menu/header label and style tweaks, ConnectWalletButton restyled, spinner and loading visuals updated.
Typography, constants & pages
src/consts/app.ts, src/consts/links.ts, src/pages/_app.tsx, src/pages/_document.tsx, src/features/tokens/TokenSelectField.tsx, src/features/transfer/TransferTokenForm.tsx, src/features/transfer/TransfersDetailsModal.tsx
Removes MAIN_FONT export and Google font usage, switches to static font-primary usage, updates BACKGROUND_COLOR/IMAGE, adds new stake link, and applies font-secondary to various labels and controls.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant Header
participant DropdownMenu
participant NavItem
participant ExternalSite
User->>Header: view page / tap hamburger
Header->>DropdownMenu: open()
DropdownMenu->>NavItem: render items from navLinks
User->>NavItem: click item
NavItem->>ExternalSite: open URL (target="_blank")

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

Ah, fonts fetched from a distant S3 bog,
New icons sproutin' like frogs on a log,
Navs rearranged, gradients aglow,
Buttons gleam where the marsh winds blow,
A tidy patch, now off I go. 🧅

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: v2 visual rebrand' clearly summarizes the main change—a comprehensive visual rebrand of the UI with new colors, fonts, components, and styling throughout.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch rebrand/v2-visual-changes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/features/transfer/TransferFeeModal.tsx (1)

23-41: ⚠️ Potential issue | 🟡 Minor

Skeletons are unreachable when fees is null during initial load.

The fee rows are gated behind fees?.localQuote && fees.localQuote.amount > 0n, so when fees is null (first load, before any data arrives), all three rows — including their skeletons — get hidden entirely. The modal would just show the header and the "Read more" link with nothing in between. Not exactly a warm welcome.

This is pre-existing behavior, not introduced by this PR, but since the rebrand is coordinating loading states (loading dots in the FeeSection, etc.), it might be worth addressing here or in a follow-up so the modal isn't a barren swamp on first open.

🤖 Fix all issues with AI agents
In `@src/components/nav/Header.tsx`:
- Around line 34-40: The Link element in Header.tsx (inside the Header
component) wraps three decorative images with empty alt text, so add an
accessible name to the link by giving the Link component an aria-label (e.g.,
aria-label="Homepage" or aria-label="Go to [SiteName] home") or include
visually-hidden text inside the Link; update the Link JSX (the <Link href="/">
element) to include the aria-label or hidden text so screen readers announce the
destination.
- Around line 42-44: The header element lacks positioning so the absolutely
positioned wrapper div around ConnectWalletButton (the div with classes
"lg:absolute lg:right-12") is positioning against an ancestor (AppLayout)
instead of the header; update the Header component to add a relative positioning
class to the header element (e.g., add "relative" or "lg:relative" to the
<header> element in the Header component) so the ConnectWalletButton's absolute
positioning is anchored to the header itself.

In `@src/consts/links.ts`:
- Line 18: The stake URL value for the stake constant contains a checksum-cased
address (0xE1F23869776c82f691d9Cb34597Ab1830Fb0De58) which should be converted
to lowercase to match the project’s EVM address style; update the stake entry in
src/consts/links.ts to use the lowercase address
(0xe1f23869776c82f691d9cb34597ab1830fb0de58) and verify the link still works in
a browser (or revert to the original mixed-case if the external site is
case-sensitive) so you don’t break routing on app.symbiotic.fi.

In `@src/pages/_app.tsx`:
- Around line 36-39: Remove or update the stale comment about requiring the font
definition in _document.tsx: the project now loads fonts via `@font-face` in
globals.css and applies them with the Tailwind class used in the JSX (the div
with className="font-primary text-black"), so delete or replace the two lines
noting the old next/font requirement to avoid confusion.

In `@tailwind.config.js`:
- Around line 124-130: The boxShadow config defines both 'accent-glow' and
'error-glow' identically; update the 'error-glow' entry in the boxShadow object
so it uses an error-specific color token instead of theme('colors.accent.100')
(e.g., theme('colors.error.*') or a red color from your palette) so error states
(shadow-error-glow) visually differ from accent states; modify the boxShadow
object where 'accent-glow' and 'error-glow' are defined to reference the correct
error color token.
🧹 Nitpick comments (16)
src/features/tokens/TokenSelectField.tsx (1)

112-114: Looks good — but while ye're in this swamp, consider tidying the class string.

The font-secondary addition fits right in with the rebrand, no issues there. One wee thing though — since you're already touching this line: the expression `${!token?.symbol && 'text-slate-400'}` stringifies to "false" when token?.symbol is truthy, which means you'll get class="ml-2 font-secondary false" in the DOM. It's harmless but a bit muddy. A ternary keeps it clean:

✨ Optional tidy-up
-        <span className={`ml-2 font-secondary ${!token?.symbol && 'text-slate-400'}`}>
+        <span className={`ml-2 font-secondary ${!token?.symbol ? 'text-slate-400' : ''}`}>
src/features/wallet/SideBarMenu.tsx (1)

78-79: Gradient headers look proper — just a wee accessibility thought.

Both section headers now use bg-accent-gradient with text-white. The purple/pink palette generally plays well with white text, but it'd be worth making sure the contrast ratio meets WCAG AA (4.5:1 for normal text). If the gradient has lighter stops, the white text might get a bit lost in the middle of the swamp... er, the midtones.

Otherwise, the rename to "My Wallets" and "Transaction History" reads cleaner. No functional concerns.

Also applies to: 88-89

src/features/transfer/FeeSectionButton.tsx (2)

6-20: dotCount doesn't reset when loading restarts — might look a wee bit off

When isLoading flips from false back to true, dotCount picks up from its last value instead of resetting to 1. So the animation might start mid-cycle — like walking into a swamp halfway through, y'know?

Also, since hooks can't be called conditionally, this interval keeps tickin' even when visible is false and the component renders nothing. Not the end of the world, but no reason to keep the donkey runnin' when nobody's watching.

🔧 Proposed fix: reset dotCount and respect visibility
-function useLoadingDots(isLoading: boolean, intervalMs = 1000) {
+function useLoadingDots(isActive: boolean, intervalMs = 1000) {
   const [dotCount, setDotCount] = useState(1);
 
   useEffect(() => {
-    if (!isLoading) return;
+    if (!isActive) {
+      setDotCount(1);
+      return;
+    }
 
     const interval = setInterval(() => {
       setDotCount((prev) => (prev % 3) + 1);
     }, intervalMs);
 
     return () => clearInterval(interval);
-  }, [isLoading, intervalMs]);
+  }, [isActive, intervalMs]);
 
   return 'Loading' + '.'.repeat(dotCount);
 }

Then at the call site, pass the combined condition:

-  const loadingText = useLoadingDots(isLoading);
+  const loadingText = useLoadingDots(isLoading && visible);

44-53: Hover styles still fire on the disabled button — minor visual thing

When !isClickable, the button gets disabled and cursor-default, which is grand. But the hover pseudo-classes (hover:text-gray-900, [&_path]:hover:fill-gray-900) still apply visually on disabled buttons in most browsers, making it look like somethin' is gonna happen when nothin' will. A little misleading for the folks in the swamp.

You could conditionally apply the hover classes, or just slap pointer-events-none on there when not clickable. Not urgent — just a bit of polish.

✨ Proposed tweak
-          className={`flex w-fit items-center font-secondary text-xxs text-gray-700 hover:text-gray-900 [&_path]:fill-gray-700 [&_path]:hover:fill-gray-900 ${!isClickable ? 'cursor-default' : ''}`}
+          className={`flex w-fit items-center font-secondary text-xxs text-gray-700 [&_path]:fill-gray-700 ${isClickable ? 'hover:text-gray-900 [&_path]:hover:fill-gray-900' : 'cursor-default pointer-events-none'}`}
src/features/transfer/TransfersDetailsModal.tsx (1)

143-143: Stray items class on this line.

The font-secondary addition is spot on for the rebrand. But while we're in this neck of the woods — there's a lone items class that doesn't do anything useful in Tailwind. Looks like it wandered in from somewhere and never left. Might want to shoo it out.

🧹 Suggested cleanup
-        <div className="items ml-2 flex items-baseline font-secondary">
+        <div className="ml-2 flex items-baseline font-secondary">
src/features/wallet/ConnectWalletButton.tsx (1)

21-22: Broad descendant override — just somethin' to keep an eye on.

The [&_*]:text-white selector applies white text to every descendant inside the inner widget. Right now that's fine for a gradient button, but if ConnectWalletButtonInner ever renders status badges or colored indicators, this'll steamroll their styles. The [&_path]:fill-white has the same reach for SVG fills.

Not a blocker at all — the visual result is correct for the rebrand. Just worth knowin' it's there if things ever look a bit... onion-layered later.

src/components/icons/QuestionMarkIcon.tsx (1)

5-21: Default fill differs from sibling icons.

This icon defaults to Color.primary[500] while others (like HamburgerIcon) default to 'currentColor'. That means this one won't inherit the parent's text color out of the box, which could be a wee surprise if someone tries to theme it via a parent text-* class.

If the primary fill is intentional for this specific icon's usage, no worries — just somethin' to be aware of for consistency across the icon family.

src/components/icons/StakeIcon.tsx (1)

4-14: Hardcoded colors diverge from the pattern used by sibling icons.

The other icon components in this PR (XIcon, BookIcon, WebSimpleIcon) destructure and apply the color prop with a fallback. This one spreads all props but never uses color, so all fills/strokes are baked in. That's fair enough for a multi-color icon — but it means if somebody passes color expecting it to do something, it'll just vanish into the swamp.

Worth at least destructuring and discarding color explicitly, or applying it to the primary fill so the intent is clear.

src/components/icons/HyperlaneTransparentLogo.tsx (1)

3-18: This component doesn't accept any props, unlike its siblings.

_HyperlaneGradientLogo in HyperlaneGradientLogo.tsx accepts DefaultIconProps and spreads them onto the <svg>, letting consumers control className, dimensions, and accessibility attributes. This one is sealed shut — hardcoded width, height, and stroke="white" with no way to override anything from the outside.

If this logo needs to be used at different sizes or needs an aria-label, nobody can do that without editing this file.

Proposed fix
-function _HyperlaneTransparentLogo() {
+import { DefaultIconProps } from '@hyperlane-xyz/widgets';
+
+function _HyperlaneTransparentLogo({ ...props }: DefaultIconProps) {
   return (
     <svg
-      width="146"
-      height="127"
       viewBox="0 0 146 127"
       fill="none"
       xmlns="http://www.w3.org/2000/svg"
+      {...props}
     >
scripts/fetch-fonts.mjs (2)

73-76: Top-level catch swallows unexpected errors with only a warn.

This catch-all handles any error — including programming bugs or invariant violations — with console.warn and a zero exit code. That's fine for the "missing fonts shouldn't block the build" case, but per the project guidelines, unexpected issues should fail loudly. If the script has a genuine bug (not an S3/network failure), you'd want to know about it.

Consider using console.error here, or at least distinguishing between expected S3/network errors (already handled per-font in the loop) and unexpected ones at this level.

Proposed tweak
 fetchFonts().catch((error) => {
-  console.warn('Font fetch script encountered an error:', error.message);
-  // Exit gracefully - don't fail the build
+  console.error('Font fetch script encountered an unexpected error:', error.message);
+  // Don't fail the build, but log as error for visibility
 });

52-55: Pipe the stream straight through without the conversion dance.

response.Body from AWS SDK v3's GetObjectCommand is already a Node Readable stream in Node.js. Converting it to a web stream and back again is just taking the long way around the barn.

Simpler pipeline
-      await pipeline(Readable.fromWeb(response.Body.transformToWebStream()), writeStream);
+      await pipeline(response.Body, writeStream);
src/components/icons/HyperlaneGradientLogo.tsx (1)

4-30: Exported name doesn't match the file name — could confuse someone lookin' for it.

The file is HyperlaneGradientLogo.tsx, the internal function is _HyperlaneGradientLogo, but the export is HyperlaneFooterLogo. When someone goes searchin' for where this logo lives, the name mismatch could send them on a bit of a wild goose chase through the swamp.

Either rename the file to HyperlaneFooterLogo.tsx or the export to HyperlaneGradientLogo — whichever reflects the intended usage better.

tailwind.config.js (1)

28-38: Heads up: overriding default Tailwind gray keys.

Spreadin' defaultColors.gray and then overridin' keys like 300, 400, 900, 950 means anyone reachin' for the standard Tailwind gray at those stops will get your custom values instead. That's perfectly fine if it's intentional — just make sure the rest of the team knows the swamp rules have changed.

src/styles/globals.css (1)

78-96: Hardcoded hex values duplicate theme tokens.

#e8caff matches primary.50 and #d4a3ff is close to primary.100 from your Tailwind config. If those tokens ever change, these scrollbar colors will be left behind in the swamp. Consider using CSS custom properties set from your theme, or at minimum drop a comment noting which token each hex corresponds to.

src/components/nav/Nav.tsx (1)

45-63: forwardRef usage and Link for external URLs — works, with a note.

The forwardRef pattern is appropriate if a parent needs to grab the anchor ref (e.g., for tooltips or focus management). The use of Next.js Link for external URLs is supported in Next.js 13+ — it essentially renders a standard <a> tag for absolute URLs, so no concern there.

One thing to be mindful of: Next.js Link adds rel="noopener" automatically when target="_blank" is set, but it does not add noreferrer. If you want to prevent leaking the referrer header to third-party sites (X.com, GitHub, etc.), you'd want to add that explicitly. It's not a security vulnerability per se — just a privacy consideration.

Optional: add noreferrer
     <Link
       ref={ref}
       className={clsx(
         'flex items-center gap-2 text-primary-500 decoration-primary-500 underline-offset-2 hover:underline',
         className,
       )}
       target="_blank"
+      rel="noopener noreferrer"
       href={item.url}
     >
src/consts/app.ts (1)

6-8: Some hardcoded colors exist as theme tokens and should be refactored.

The hex values in this file are a bit of a mixed bunch, really. #F8F8FF maps to cream.300 and #E8CAFF maps to primary.50 in the Tailwind theme, so those could pull from Color.cream['300'] and Color.primary['50'] respectively to stay consistent with how BRAND_COLOR works on line 5.

However, #F2E4FF and #F8F2FF aren't defined in the theme, so they'll need to stay as hardcoded values unless those colors are added to the Tailwind config first.

- Header: add `relative` positioning, `aria-label` on logo link
- Nav: add `rel="noopener noreferrer"` to external links
- FeeSectionButton: reset dot count on load end, fix hover on disabled state
- TokenSelectField: fix false class string in DOM (use ternary)
- TransfersDetailsModal: remove stray `items` class
- _app.tsx: remove stale comment about old next/font approach
- fetch-fonts.mjs: use console.error for unexpected errors, simplify stream pipeline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/components/nav/Header.tsx`:
- Around line 16-18: The mobile logo Link in Header.tsx (the Link wrapping
Image/Logo) is missing an accessible name; update that Link component to include
an accessible label (e.g., aria-label="Homepage") so screen readers announce its
purpose (the same pattern used for the desktop logo Link) — locate the Link
around the Image (Logo) and add the aria-label attribute to it.
🧹 Nitpick comments (2)
src/features/transfer/FeeSectionButton.tsx (2)

6-23: Consider refactoring to avoid synchronous setState inside the effect

Look, I get it — this hook works just fine in me swamp and yours. But the ESLint react-hooks/set-state-in-effect rule is grumbling about setDotCount(1) being called synchronously in the effect body (line 11), which can cause cascading renders. Since the caller only reads loadingText when isLoading is true anyway, you could sidestep the whole thing with a useRef for the counter and a tick-driven state bump:

♻️ Suggested refactor using useRef
-function useLoadingDots(isLoading: boolean, intervalMs = 1000) {
-  const [dotCount, setDotCount] = useState(1);
-
-  useEffect(() => {
-    if (!isLoading) {
-      setDotCount(1);
-      return;
-    }
-
-    const interval = setInterval(() => {
-      setDotCount((prev) => (prev % 3) + 1);
-    }, intervalMs);
-
-    return () => clearInterval(interval);
-  }, [isLoading, intervalMs]);
-
-  return 'Loading' + '.'.repeat(dotCount);
-}
+function useLoadingDots(isLoading: boolean, intervalMs = 1000) {
+  const dotCountRef = useRef(1);
+  const [, tick] = useState(0);
+
+  useEffect(() => {
+    if (!isLoading) {
+      dotCountRef.current = 1;
+      return;
+    }
+
+    const interval = setInterval(() => {
+      dotCountRef.current = (dotCountRef.current % 3) + 1;
+      tick((t) => t + 1);
+    }, intervalMs);
+
+    return () => clearInterval(interval);
+  }, [isLoading, intervalMs]);
+
+  return 'Loading' + '.'.repeat(dotCountRef.current);
+}

(You'd need to add useRef to the React import on line 3.)

Alternatively, if you'd rather keep things simple and the team is fine suppressing the rule for this isolated case, an inline // eslint-disable-next-line would do — just make sure that's a conscious choice, not something lurking in the swamp unnoticed.


47-56: pointer-events-none makes cursor-default a no-op and is redundant with disabled

Now, I'm not one to tell folks how to decorate their swamp, but there are a few layers here doing the same job. When !isClickable:

  1. disabled already prevents click events and signals non-interactivity to assistive tech.
  2. pointer-events-none kills all pointer events — meaning cursor-default on the same branch never actually takes effect (no pointer → no cursor style).
  3. The hover styles (hover:text-gray-900, etc.) are already absent from the non-clickable branch, so pointer-events-none isn't needed to suppress them.

pointer-events-none can also bite you later if someone adds a tooltip to explain why fees aren't shown — the tooltip won't fire. Consider dropping pointer-events-none and cursor-default in favor of just relying on disabled:

♻️ Simplified class string
-          className={`flex w-fit items-center font-secondary text-xxs text-gray-700 [&_path]:fill-gray-700 ${isClickable ? 'hover:text-gray-900 [&_path]:hover:fill-gray-900' : 'cursor-default pointer-events-none'}`}
+          className={`flex w-fit items-center font-secondary text-xxs text-gray-700 [&_path]:fill-gray-700 ${isClickable ? 'hover:text-gray-900 [&_path]:hover:fill-gray-900 cursor-pointer' : ''}`}

Comment on lines +16 to 18
<Link href="/">
<Image src={Logo} width={36} alt="" className="h-auto" />
</Link>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Mobile logo link is still wandering around without a name tag.

The desktop logo link got its aria-label="Homepage" (line 34), but this mobile counterpart was left out of that party. Screen readers will just announce "link" with nothing useful. Same swamp, different day.

🛡️ Proposed fix
-        <Link href="/">
+        <Link href="/" aria-label="Homepage">
           <Image src={Logo} width={36} alt="" className="h-auto" />
         </Link>
🤖 Prompt for AI Agents
In `@src/components/nav/Header.tsx` around lines 16 - 18, The mobile logo Link in
Header.tsx (the Link wrapping Image/Logo) is missing an accessible name; update
that Link component to include an accessible label (e.g., aria-label="Homepage")
so screen readers announce its purpose (the same pattern used for the desktop
logo Link) — locate the Link around the Image (Logo) and add the aria-label
attribute to it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant