Skip to content
This repository was archived by the owner on Mar 17, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/web/app/(base-org)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from 'next';
import Sidebar from 'apps/web/src/components/Layout/Navigation/Sidebar';
import { Footer } from 'apps/web/src/components/Layout/Footer/Footer';
import MobileNav from 'apps/web/src/components/Layout/Navigation/MobileNav';
import { DynamicWrappedGasPriceDropdown } from 'apps/web/src/components/Layout/Navigation/GasPriceDropdown';
import AnalyticsProvider from 'apps/web/contexts/Analytics';

export const metadata: Metadata = {
Expand Down Expand Up @@ -29,7 +30,7 @@ export default async function BaseOrgLayout({
children: React.ReactNode;
}) {
return (
<div className="bg-white text-black transition-colors">
<div className="text-black bg-white transition-colors">
<div className="min-w-screen relative mx-auto grid min-h-screen w-full max-w-[1920px] grid-cols-1 selection:bg-blue-5 selection:text-base-blue lg:grid-cols-[13.438rem_1fr]">
<AnalyticsProvider context="sidenav">
<Sidebar />
Expand All @@ -40,6 +41,11 @@ export default async function BaseOrgLayout({
</main>
<Footer />
</div>

{/* Gas Price Dropdown - Top Right */}
<div className="hidden fixed top-4 right-4 z-50 lg:block">
<DynamicWrappedGasPriceDropdown />
</div>
</div>
);
}
6 changes: 6 additions & 0 deletions apps/web/app/(base-org-dark)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from 'next';
import Sidebar from 'apps/web/src/components/Layout/Navigation/Sidebar';
import { Footer } from 'apps/web/src/components/Layout/Footer/Footer';
import MobileNav from 'apps/web/src/components/Layout/Navigation/MobileNav';
import { DynamicWrappedGasPriceDropdown } from 'apps/web/src/components/Layout/Navigation/GasPriceDropdown';
import AnalyticsProvider from 'apps/web/contexts/Analytics';

export const metadata: Metadata = {
Expand Down Expand Up @@ -40,6 +41,11 @@ export default function BaseOrgLayoutDark({
</main>
<Footer />
</div>

{/* Gas Price Dropdown - Top Right */}
<div className="fixed right-4 top-4 z-50 hidden lg:block">
<DynamicWrappedGasPriceDropdown />
</div>
</div>
);
}
111 changes: 111 additions & 0 deletions apps/web/src/components/Layout/Navigation/GasPriceDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use client';

import Card from 'apps/web/src/components/base-org/Card';
import { Icon } from 'apps/web/src/components/Icon/Icon';
import { base, mainnet } from 'viem/chains';
import { useGasPrice } from 'wagmi';
import { DynamicCryptoProviders } from 'apps/web/app/CryptoProviders.dynamic';

const convertWeiToMwei = (weiValue: bigint): number => {
// 1 mwei = 10^6 wei
const mweiValue = Number(weiValue) / 1_000_000;
return Number(mweiValue.toFixed(2)); // Round to 2 decimal places
};

export function DynamicWrappedGasPriceDropdown() {
return (
<DynamicCryptoProviders>
<GasPriceDropdown />
</DynamicCryptoProviders>
);
}

export function GasPriceDropdown() {
const { data: baseGasPriceInWei } = useGasPrice({
chainId: base.id,
query: {
refetchInterval: 10_000,
},
});

const { data: mainnetGasPriceInWei } = useGasPrice({
chainId: mainnet.id,
query: {
refetchInterval: 10_000,
},
});

return (
<div className="relative group">
<div className="flex cursor-pointer flex-row items-center gap-2 rounded-lg bg-[#FAFAFA] px-3 py-1 transition-all">
<span className="animate-pulse text-palette-positive">
<Icon name="blueCircle" color="currentColor" height="0.5rem" width="0.5rem" />
</span>
<div className="flex gap-1 items-center">
<span className="font-bold text-black font-doto">
{baseGasPriceInWei ? convertWeiToMwei(baseGasPriceInWei) : <>&mdash;</>}
</span>
<span className="text-sm text-base-gray-200">Mwei</span>
</div>
</div>
<div className="hidden absolute right-0 top-full pt-2 lg:group-hover:inline-block">
<Card
innerClassName="p-3 border border-base-black bg-white hover:bg-white font-sans text-[0.875rem]"
radius={9}
>
<ul className="flex flex-col gap-2 whitespace-nowrap">
<li className="flex gap-2">
<strong className="font-normal">{base.name}</strong>
<div className="flex gap-1 items-center">
<span className="font-bold font-doto">
{baseGasPriceInWei ? convertWeiToMwei(baseGasPriceInWei) : <>&mdash;</>}
</span>
<span className="text-base-gray-200">Mwei</span>
</div>
</li>
<li className="flex gap-2">
<strong className="font-normal">{mainnet.name}</strong>
<div className="flex gap-1 items-center">
<span className="font-bold font-doto">
{mainnetGasPriceInWei ? convertWeiToMwei(mainnetGasPriceInWei) : <>&mdash;</>}
</span>
<span className="text-base-gray-200">Mwei</span>
</div>
</li>
</ul>
</Card>
</div>
</div>
);
}

export function DynamicWrappedGasPriceDropdownItem() {
return (
<DynamicCryptoProviders>
<GasPriceDropdownItem />
</DynamicCryptoProviders>
);
}

export function GasPriceDropdownItem() {
const { data: baseGasPriceInWei } = useGasPrice({
chainId: base.id,
query: {
refetchInterval: 10_000,
},
});

return (
<div className="flex cursor-pointer flex-row items-center gap-2 rounded-lg bg-[#FAFAFA] px-3 py-2 transition-all">
<span className="animate-pulse text-palette-positive">
<Icon name="blueCircle" color="currentColor" height="0.5rem" width="0.5rem" />
</span>
<div className="flex gap-1 items-center">
<span className="font-bold text-black font-doto">
{baseGasPriceInWei ? convertWeiToMwei(baseGasPriceInWei) : <>&mdash;</>}
</span>
<span className="text-sm text-base-gray-200">Mwei</span>
</div>
</div>
);
}
188 changes: 98 additions & 90 deletions apps/web/src/components/Layout/Navigation/MobileNav/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ButtonSizes,
ButtonVariants,
} from 'apps/web/src/components/Button/Redesign/Button';
import { DynamicWrappedGasPriceDropdownItem } from 'apps/web/src/components/Layout/Navigation/GasPriceDropdown';

const navItemVariants = {
initial: { opacity: 0, x: -20 },
Expand Down Expand Up @@ -81,110 +82,117 @@ export default function MobileNav({ className }: { className?: string }) {
className,
)}
>
<Link href="/" className="flex w-fit items-center justify-between">
<Link href="/" className="flex justify-between items-center w-fit">
<MobileLogo className="size-10" />
</Link>
<Dialog.Root open={isMobileMenuOpen} onOpenChange={handleToggleMenu}>
<Dialog.Trigger
aria-label="Toggle menu"
aria-expanded={isMobileMenuOpen}
aria-controls="mobile-menu"
data-focus-visible="false"
className="focus:outline-none"
>
<MenuIcon isOpen={isMobileMenuOpen} />
</Dialog.Trigger>

<Dialog.Portal>
<Dialog.Content
className={classNames(
'fixed inset-y-0 left-0 top-[72px] z-[100] flex h-[calc(100dvh-72px)] w-full flex-col gap-4 outline-none',
isClosingFromResize
? 'transition-none'
: 'transition ease-in-out data-[state=closed]:duration-500 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left',
)}
<div className="flex gap-2 items-center">
<DynamicWrappedGasPriceDropdownItem />
<Dialog.Root open={isMobileMenuOpen} onOpenChange={handleToggleMenu}>
<Dialog.Trigger
aria-label="Toggle menu"
aria-expanded={isMobileMenuOpen}
aria-controls="mobile-menu"
data-focus-visible="false"
className="focus:outline-none"
>
<Dialog.Title className="sr-only">Mobile Menu</Dialog.Title>
<Dialog.Description className="sr-only">
Mobile site navigation menu.
</Dialog.Description>

<div className="pointer-events-none absolute inset-0 -top-[72px] -z-10 h-[calc(100dvh+72px)] w-full bg-white dark:bg-black" />

<nav className="flex h-full flex-1 flex-col">
{isBrand ? (
<div className="relative flex h-full flex-1 flex-col">
<ul className="ml-4 mt-3 flex flex-col gap-2.5 overflow-y-auto md:mb-10 md:ml-6">
<AnimatePresence>
{isMobileMenuOpen &&
routes.map((route, index) => (
<motion.li
key={route.href}
<MenuIcon isOpen={isMobileMenuOpen} />
</Dialog.Trigger>

<Dialog.Portal>
<Dialog.Content
className={classNames(
'fixed inset-y-0 left-0 top-[72px] z-[100] flex h-[calc(100dvh-72px)] w-full flex-col gap-4 outline-none',
isClosingFromResize
? 'transition-none'
: 'transition ease-in-out data-[state=closed]:duration-500 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left',
)}
>
<Dialog.Title className="sr-only">Mobile Menu</Dialog.Title>
<Dialog.Description className="sr-only">
Mobile site navigation menu.
</Dialog.Description>

<div className="pointer-events-none absolute inset-0 -top-[72px] -z-10 h-[calc(100dvh+72px)] w-full bg-white dark:bg-black" />

<nav className="flex flex-col flex-1 h-full">
{isBrand ? (
<div className="flex relative flex-col flex-1 h-full">
<ul className="ml-4 mt-3 flex flex-col gap-2.5 overflow-y-auto md:mb-10 md:ml-6">
<AnimatePresence>
{isMobileMenuOpen &&
routes.map((route, index) => (
<motion.li
key={route.href}
variants={navItemVariants}
initial="initial"
animate="animate"
exit="exit"
transition={getNavItemTransition(index)}
>
<Link
target={route.newTab ? '_blank' : '_self'}
rel={route.newTab ? 'noopener noreferrer' : undefined}
href={route.href}
className={classNames(
'flex w-fit items-center justify-between rounded-[4px] p-3 leading-[114%] transition-colors duration-150 hover:bg-base-gray-30',
isLinkActive({
pathname,
href: route.href,
}) && 'bg-base-gray-30 dark:bg-gray-90 dark:active:text-black',
)}
onClick={handleToggleMenu}
>
<Text variant={TextVariant.CTALabelSm}>{route.label}</Text>
</Link>
</motion.li>
))}
</AnimatePresence>
</ul>

<div className="flex flex-col gap-3 px-4 pb-4 mt-auto md:px-6">
<AnimatePresence>
{isMobileMenuOpen && (
<motion.div
variants={navItemVariants}
initial="initial"
animate="animate"
initial="buttonInitial"
animate="buttonAnimate"
exit="exit"
transition={getNavItemTransition(index)}
transition={getButtonTransition(1, routes.length)}
>
<Link
target={route.newTab ? '_blank' : '_self'}
rel={route.newTab ? 'noopener noreferrer' : undefined}
href={route.href}
className={classNames(
'flex w-fit items-center justify-between rounded-[4px] p-3 leading-[114%] transition-colors duration-150 hover:bg-base-gray-30',
isLinkActive({
pathname,
href: route.href,
}) && 'bg-base-gray-30 dark:bg-gray-90 dark:active:text-black',
)}
onClick={handleToggleMenu}
<Button
variant={ButtonVariants.Primary}
size={ButtonSizes.Small}
asChild
>
<Text variant={TextVariant.CTALabelSm}>{route.label}</Text>
</Link>
</motion.li>
))}
</AnimatePresence>
</ul>

<div className="mt-auto flex flex-col gap-3 px-4 pb-4 md:px-6">
<AnimatePresence>
{isMobileMenuOpen && (
<motion.div
variants={navItemVariants}
initial="buttonInitial"
animate="buttonAnimate"
exit="exit"
transition={getButtonTransition(1, routes.length)}
>
<Button variant={ButtonVariants.Primary} size={ButtonSizes.Small} asChild>
<Link
prefetch={false}
download="/base-brand.zip"
href="/base-brand.zip"
className="h-10 w-full"
>
Download Brand Assets
</Link>
</Button>
</motion.div>
)}
</AnimatePresence>
<Link
prefetch={false}
download="/base-brand.zip"
href="/base-brand.zip"
className="w-full h-10"
>
Download Brand Assets
</Link>
</Button>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</div>
) : (
<BaseNavigation isMobile />
)}
</nav>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
) : (
<BaseNavigation isMobile />
)}
</nav>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</div>
</header>
);
}

function MenuIcon({ isOpen }: { isOpen: boolean }) {
return (
<div className="relative grid size-10 place-items-center rounded-md bg-base-gray-30 will-change-transform">
<div className="grid relative place-items-center rounded-md size-10 bg-base-gray-30 will-change-transform">
<div
className={classNames(
'ease-[cubic-bezier(0.4,0.2,0,1)] absolute size-5 h-[1px] bg-black transition-all duration-300',
Expand Down