diff --git a/pages/dashboard.page.tsx b/pages/dashboard.page.tsx
index 423d673672..f6647300cb 100644
--- a/pages/dashboard.page.tsx
+++ b/pages/dashboard.page.tsx
@@ -1,8 +1,8 @@
import { Trans } from '@lingui/macro';
import { Box, Typography } from '@mui/material';
import { useEffect, useState } from 'react';
-import StyledToggleButton from 'src/components/StyledToggleButton';
-import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup';
+import { StyledTxModalToggleButton } from 'src/components/StyledToggleButton';
+import { StyledTxModalToggleGroup } from 'src/components/StyledToggleButtonGroup';
import { useRootStore } from 'src/store/root';
import { useShallow } from 'zustand/shallow';
@@ -41,24 +41,24 @@ export default function Dashboard() {
mb: { xs: 3, xsm: 4 },
}}
>
- setMode(value)}
- sx={{ width: { xs: '100%', xsm: '359px' }, height: '44px' }}
+ sx={{ width: { xs: '100%', xsm: '359px' } }}
>
-
+
Supply
-
-
+
+
Borrow
-
-
+
+
)}
diff --git a/pages/dev/components/[section].page.tsx b/pages/dev/components/[section].page.tsx
new file mode 100644
index 0000000000..ec9af72e7b
--- /dev/null
+++ b/pages/dev/components/[section].page.tsx
@@ -0,0 +1,30 @@
+import { Typography } from '@mui/material';
+import { useRouter } from 'next/router';
+import { ShowcaseLayout } from 'src/modules/dev/ComponentShowcase/components/ShowcaseLayout';
+import { SHOWCASE_SECTIONS } from 'src/modules/dev/ComponentShowcase/utils/registry';
+
+/**
+ * One route per showcase section: `/dev/components/`. Renders only the active
+ * section (lazily loaded via the registry) inside the shared sidebar layout. Dev-only.
+ */
+export default function ComponentShowcaseSectionPage() {
+ const router = useRouter();
+
+ if (process.env.NODE_ENV !== 'development') {
+ return null;
+ }
+
+ const slug = typeof router.query.section === 'string' ? router.query.section : '';
+ const section = SHOWCASE_SECTIONS.find((s) => s.slug === slug);
+ const ActiveSection = section?.Component;
+
+ return (
+
+ {ActiveSection ? (
+
+ ) : router.isReady ? (
+ Section not found
+ ) : null}
+
+ );
+}
diff --git a/pages/dev/components/index.page.tsx b/pages/dev/components/index.page.tsx
new file mode 100644
index 0000000000..8a2da9ef39
--- /dev/null
+++ b/pages/dev/components/index.page.tsx
@@ -0,0 +1,16 @@
+import { useRouter } from 'next/router';
+import { useEffect } from 'react';
+import { SHOWCASE_SECTIONS } from 'src/modules/dev/ComponentShowcase/utils/registry';
+
+// `/dev/components` → redirect to the first section. Dev-only.
+export default function ComponentShowcaseIndexPage() {
+ const router = useRouter();
+
+ useEffect(() => {
+ if (process.env.NODE_ENV === 'development') {
+ router.replace(`/dev/components/${SHOWCASE_SECTIONS[0].slug}`);
+ }
+ }, [router]);
+
+ return null;
+}
diff --git a/pages/governance/index.governance.tsx b/pages/governance/index.governance.tsx
index 8fc3b2856c..bdd91b9a9a 100644
--- a/pages/governance/index.governance.tsx
+++ b/pages/governance/index.governance.tsx
@@ -2,8 +2,8 @@ import { Trans } from '@lingui/macro';
import { Grid, Typography, useMediaQuery, useTheme } from '@mui/material';
import dynamic from 'next/dynamic';
import { useEffect, useState } from 'react';
-import StyledToggleButton from 'src/components/StyledToggleButton';
-import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup';
+import { StyledTxModalToggleButton } from 'src/components/StyledToggleButton';
+import { StyledTxModalToggleGroup } from 'src/components/StyledToggleButtonGroup';
import { MainLayout } from 'src/layouts/MainLayout';
import { GovernanceTopPanel } from 'src/modules/governance/GovernanceTopPanel';
import { ProposalsV3List } from 'src/modules/governance/ProposalsV3List';
@@ -44,29 +44,28 @@ export default function Governance() {
<>
- setMode(value)}
sx={{
width: { xs: '100%', xsm: '359px' },
- height: '44px',
mb: 4,
display: { xs: 'flex', lg: 'none' },
}}
>
-
+
Proposals
-
-
+
+
Your info
-
-
+
+
{isMobile ? (
mode === Tabs.PROPOSALS ? (
diff --git a/pages/markets.page.tsx b/pages/markets.page.tsx
index ee973444a7..2046026f94 100644
--- a/pages/markets.page.tsx
+++ b/pages/markets.page.tsx
@@ -1,20 +1,16 @@
-import { Box, Container } from '@mui/material';
-import { ReactNode, useEffect } from 'react';
+import { useEffect } from 'react';
+import { ContentContainer } from 'src/components/ContentContainer';
import { MainLayout } from 'src/layouts/MainLayout';
import { MarketAssetsListContainer } from 'src/modules/markets/MarketAssetsListContainer';
import { MarketsTopPanel } from 'src/modules/markets/MarketsTopPanel';
import { useRootStore } from 'src/store/root';
-interface MarketContainerProps {
- children: ReactNode;
-}
-
+// Markets-specific Container overrides, shared with MarketsTopPanel (`containerProps=`) so the
+// top-panel stats align with the asset table at Markets' wider max-width. The theme's MuiContainer
+// override already supplies display/flexDirection/flex + the 39px bottom padding, so only the wider
+// px/maxWidth are set here.
export const marketContainerProps = {
sx: {
- display: 'flex',
- flexDirection: 'column',
- flex: 1,
- pb: '39px',
px: {
xs: 2,
xsm: 5,
@@ -33,10 +29,6 @@ export const marketContainerProps = {
},
};
-export const MarketContainer = ({ children }: MarketContainerProps) => {
- return {children};
-};
-
export default function Markets() {
const trackEvent = useRootStore((store) => store.trackEvent);
@@ -49,19 +41,9 @@ export default function Markets() {
return (
<>
-
-
-
-
-
+
+
+
>
);
}
diff --git a/pages/reserve-overview.page.tsx b/pages/reserve-overview.page.tsx
index e6fca4aaf3..30f423f957 100644
--- a/pages/reserve-overview.page.tsx
+++ b/pages/reserve-overview.page.tsx
@@ -3,8 +3,8 @@ import { Box, Typography } from '@mui/material';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
-import StyledToggleButton from 'src/components/StyledToggleButton';
-import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup';
+import { StyledTxModalToggleButton } from 'src/components/StyledToggleButton';
+import { StyledTxModalToggleGroup } from 'src/components/StyledToggleButtonGroup';
import {
ComputedReserveData,
ReserveWithId,
@@ -102,24 +102,24 @@ export default function ReserveOverview() {
mb: { xs: 3, xsm: 4 },
}}
>
- setMode(value)}
- sx={{ width: { xs: '100%', xsm: '359px' }, height: '44px' }}
+ sx={{ width: { xs: '100%', xsm: '359px' } }}
>
-
+
Overview
-
-
+
+
Your info
-
-
+
+
diff --git a/pages/safety-module.page.tsx b/pages/safety-module.page.tsx
index 66d1bb55cf..52d5bd02ee 100644
--- a/pages/safety-module.page.tsx
+++ b/pages/safety-module.page.tsx
@@ -11,17 +11,19 @@ import { ConnectWalletPaperStaking } from 'src/components/ConnectWalletPaperStak
import { ContentContainer } from 'src/components/ContentContainer';
import { Link } from 'src/components/primitives/Link';
import { Warning } from 'src/components/primitives/Warning';
-import StyledToggleButton from 'src/components/StyledToggleButton';
-import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup';
+import { StyledTxModalToggleButton } from 'src/components/StyledToggleButton';
+import { StyledTxModalToggleGroup } from 'src/components/StyledToggleButtonGroup';
import { StakeTokenFormatted, useGeneralStakeUiData } from 'src/hooks/stake/useGeneralStakeUiData';
import { useUserStakeUiData } from 'src/hooks/stake/useUserStakeUiData';
import { useModalContext } from 'src/hooks/useModal';
+import { usePinnedMarket } from 'src/hooks/usePinnedMarket';
import { MainLayout } from 'src/layouts/MainLayout';
import { GetABPToken } from 'src/modules/staking/GetABPToken';
// import { GhoStakingPanel } from 'src/modules/staking/GhoStakingPanel';
import { StakingHeader } from 'src/modules/staking/StakingHeader';
import { StakingPanel } from 'src/modules/staking/StakingPanel';
import { useRootStore } from 'src/store/root';
+import { CustomMarket } from 'src/ui-config/marketsConfig';
import { SAFETY_MODULE } from 'src/utils/events';
import { ENABLE_TESTNET, STAGING_ENV } from 'src/utils/marketsAndNetworksConfig';
@@ -107,6 +109,9 @@ export default function Staking() {
});
}, [trackEvent]);
+ // Safety Module runs on the Core (mainnet) instance (pinned for the page, restored on unmount).
+ usePinnedMarket(CustomMarket.proto_mainnet_v3);
+
const tvl = {
'Staked Aave': Number(stkAave?.totalSupplyUSDFormatted || '0'),
// 'Staked GHO': Number(stkGho?.totalSupplyUSDFormatted || '0'),
@@ -147,24 +152,24 @@ export default function Staking() {
mb: { xs: 3, xsm: 4 },
}}
>
- setMode(value)}
sx={{ width: { xs: '100%', xsm: '359px' } }}
>
-
+
Stake AAVE
-
-
+
+
Stake ABPT
-
-
+
+
diff --git a/pages/staking.page.tsx b/pages/staking.page.tsx
index 2626de20de..4d4ea7312a 100644
--- a/pages/staking.page.tsx
+++ b/pages/staking.page.tsx
@@ -1,11 +1,13 @@
import dynamic from 'next/dynamic';
import { useEffect } from 'react';
import { ContentContainer } from 'src/components/ContentContainer';
+import { usePinnedMarket } from 'src/hooks/usePinnedMarket';
import { MainLayout } from 'src/layouts/MainLayout';
import { UmbrellaAssetsListContainer } from 'src/modules/umbrella/StakeAssets/UmbrellaAssetsListContainer';
import { UmrellaAssetsDefaultListContainer } from 'src/modules/umbrella/UmbrellaAssetsDefault';
import { UmbrellaHeader } from 'src/modules/umbrella/UmbrellaHeader';
import { useRootStore } from 'src/store/root';
+import { CustomMarket } from 'src/ui-config/marketsConfig';
import { useWeb3Context } from '../src/libs/hooks/useWeb3Context';
@@ -42,6 +44,9 @@ export default function UmbrellaStaking() {
});
}, [trackEvent]);
+ // Staking always runs on the Core instance (pinned for the page, restored on unmount).
+ usePinnedMarket(CustomMarket.proto_mainnet_v3);
+
return (
<>
diff --git a/public/gho-coins.png b/public/gho-coins.png
new file mode 100644
index 0000000000..5be13fe1f6
Binary files /dev/null and b/public/gho-coins.png differ
diff --git a/src/components/Analytics/AnalyticsConsent.tsx b/src/components/Analytics/AnalyticsConsent.tsx
index e5036c286e..3f0ae33987 100644
--- a/src/components/Analytics/AnalyticsConsent.tsx
+++ b/src/components/Analytics/AnalyticsConsent.tsx
@@ -1,14 +1,19 @@
-import { Box, Typography, useMediaQuery, useTheme } from '@mui/material';
+import { Box, Button, Typography, useMediaQuery, useTheme } from '@mui/material';
import * as Sentry from '@sentry/nextjs';
import React, { useEffect, useState } from 'react';
-import { CookieConsent as AnalyticsConsentBanner } from 'react-cookie-consent';
import { Link } from 'src/components/primitives/Link';
import { CONSENT_KEY } from 'src/store/analyticsSlice';
import { useRootStore } from 'src/store/root';
+import { figVars } from 'src/utils/figmaColors';
import { useAccount } from 'wagmi';
import { useShallow } from 'zustand/shallow';
-export default function AnalyticsBanner() {
+/**
+ * `preview` (showcase/dev only, e.g. /dev/components): render inline (non-fixed) and always
+ * visible, with the consent buttons inert — so the banner can be displayed without reading or
+ * mutating real analytics consent state. Never set in production.
+ */
+export default function AnalyticsBanner({ preview = false }: { preview?: boolean } = {}) {
const [optInAnalytics, optOutAnalytics, analyticsConfigOpen, isTrackingEnabled] = useRootStore(
useShallow((store) => [
store.acceptAnalytics,
@@ -21,15 +26,14 @@ export default function AnalyticsBanner() {
const [bannerVisible, setBannerVisible] = useState(false);
useEffect(() => {
+ if (preview) return;
// Adds a delay before showing the banner.
const timerId = setTimeout(() => {
setBannerVisible(true);
}, 1000); // Start sliding in after 1 second.
return () => clearTimeout(timerId);
- }, []);
-
- const theme = useTheme();
+ }, [preview]);
const { breakpoints } = useTheme();
const isMobile = useMediaQuery(breakpoints.down('sm'));
@@ -37,6 +41,7 @@ export default function AnalyticsBanner() {
// Bind Sentry user to wallet if analytics consent is accepted
const { isConnected, address, connector } = useAccount();
useEffect(() => {
+ if (preview) return;
const hasConsent = isTrackingEnabled;
if (hasConsent && isConnected && address) {
Sentry.setUser({
@@ -46,103 +51,81 @@ export default function AnalyticsBanner() {
} else {
Sentry.setUser(null);
}
- }, [isTrackingEnabled, isConnected, address, connector]);
+ }, [isTrackingEnabled, isConnected, address, connector, preview]);
const hasUserMadeChoice =
typeof window !== 'undefined' && localStorage.getItem(CONSENT_KEY) !== null;
- // Note: If they have already chosen don't show again unless configured from footer
- if (hasUserMadeChoice) return null;
+ // Hide once the user has made a choice. Reopening from the footer clears the stored choice and
+ // reopens analyticsConfigOpen, which brings the banner back.
+ if (!preview && (hasUserMadeChoice || !analyticsConfigOpen)) return null;
return (
- <>
- Allow analytics }
- declineButtonText={Opt-out}
- disableStyles={true}
- visible={analyticsConfigOpen ? 'show' : 'hidden'}
- flipButtons
- style={{
- background: theme.palette.background.paper,
- bottom: isMobile ? '24px' : '24px',
- right: isMobile ? '50%' : '24px',
- left: isMobile ? '50%' : 'auto',
- position: 'fixed',
- width: '400px',
- // height: '184px',
- gap: '16px',
- display: 'flex',
- flexDirection: 'column',
- flexFlow: 'column',
- justifyContent: 'space-between',
- alignItems: 'center',
- color: theme.palette.text.primary,
- marginBottom: '16px',
- fontSize: '14px',
- lineHeight: '20.02px',
- padding: '16px 16px',
- zIndex: 100,
- borderRadius: '12px',
- border: '0.5px solid rgba(235, 235, 239, 0.42)',
- boxShadow: '0px 0px 2px rgba(0, 0, 0, 0.2), 0px 2px 10px rgba(0, 0, 0, 0.1)',
- transition: 'transform 0.5s ease-out', // Add this
-
- transform: bannerVisible
- ? isMobile
- ? 'translateX(-50%)'
- : 'none'
- : 'translateX(100%) translateY(100%)',
- }}
- buttonStyle={{
- background: theme.palette.mode === 'dark' ? '#F7F7F9' : '#383D51',
- color: theme.palette.mode === 'dark' ? '#383D51' : '#F7F7F9',
-
- fontSize: '14px',
- borderRadius: '4px',
- margin: '0px',
- border: '1px solid #000',
- width: '172px',
- height: '36px',
- fontWeight: '700',
- cursor: 'pointer',
- }}
- declineButtonStyle={{
- // background: '#F7F7F9',
- background: theme.palette.mode === 'dark' ? '#383D51' : '#F7F7F9',
- color: theme.palette.mode === 'dark' ? '#EAEBEF' : '#383D51',
-
- fontFamily: 'Inter',
- fontWeight: '500',
- lineHeight: '24px',
- fontSize: '14px',
- borderRadius: '4px',
- margin: '10px',
- // padding: '10px 20px',
- border: `1px solid ${theme.palette.mode === 'dark' ? '#383D51' : '#EAEBEF'}`,
- width: '172px',
- height: '36px',
- // padding: '0px',
- cursor: 'pointer',
- }}
- enableDeclineButton
- onDecline={() => {
- optOutAnalytics();
- }}
- onAccept={() => {
- optInAnalytics();
- }}
- cookieName={CONSENT_KEY}
- >
-
+
+
+
+ We value your privacy
+
+
We may employ on-the-spot tracking techniques during your browsing session to collect data
on your interactions, preferences, and behaviour. This data helps us personalise your
- experience and improve our services. See our
-
- {' '}
- Privacy Policy.
+ experience and improve our services. See our{' '}
+
+ Privacy Policy
-
-
- >
+ .
+
+
+
+
+
+
+
+
);
}
diff --git a/src/components/AssetCategoryMultiselect.tsx b/src/components/AssetCategoryMultiselect.tsx
index 731a66efa3..ad15fd3ae4 100644
--- a/src/components/AssetCategoryMultiselect.tsx
+++ b/src/components/AssetCategoryMultiselect.tsx
@@ -1,11 +1,10 @@
-import { ChevronDownIcon } from '@heroicons/react/outline';
import { Trans } from '@lingui/macro';
import {
- Box,
Button,
Checkbox,
- Popover,
- SvgIcon,
+ ListItemText,
+ Menu,
+ MenuItem,
SxProps,
Theme,
Typography,
@@ -13,6 +12,7 @@ import {
useTheme,
} from '@mui/material';
import { useEffect, useState } from 'react';
+import { ChevronUpDownIcon } from 'src/components/icons/ChevronUpDownIcon';
import { AssetCategory } from 'src/modules/markets/utils/assetCategories';
import { useRootStore } from 'src/store/root';
@@ -67,10 +67,6 @@ export const AssetCategoryMultiSelect = ({
onCategoriesChange(newCategories);
};
- const handleReset = () => {
- onCategoriesChange([]);
- };
-
const open = Boolean(anchorEl);
const selectedCount = selectedCategories.length;
@@ -80,24 +76,17 @@ export const AssetCategoryMultiSelect = ({
onClick={handleClick}
disabled={disabled}
variant="outlined"
- disableRipple
+ aria-haspopup="true"
+ aria-expanded={open}
+ endIcon={}
sx={{
- height: '38px',
- minWidth: 'auto',
width: sm ? '100%' : 'unset',
- display: 'flex',
justifyContent: sm ? 'space-between' : 'center',
- alignItems: 'center',
- gap: 3,
- p: '6px 12px',
textTransform: 'none',
...sx,
}}
>
-
+
{selectedCount === 0 ? (
All Categories
) : selectedCount === 1 ? (
@@ -106,151 +95,35 @@ export const AssetCategoryMultiSelect = ({
{selectedCount} Categories
)}
-
-
-
-
-
-
- {/* Header */}
-
-
- Select Categories
-
-
-
-
- {/* Category Options */}
- {categories.map((category) => (
- {
+ const checked = selectedCategories.includes(category);
+ return (
+
- ))}
-
-
+ {categoryLabels[category]}
+
+ );
+ })}
+
>
);
};
diff --git a/src/components/ChainAvailabilityText.tsx b/src/components/ChainAvailabilityText.tsx
index 416b0a5a1f..942a1968ee 100644
--- a/src/components/ChainAvailabilityText.tsx
+++ b/src/components/ChainAvailabilityText.tsx
@@ -25,7 +25,7 @@ export const ChainAvailabilityText: React.FC = ({
return (
-
+
Available on
= ({
>
-
+
{networkToTextMapper(chainId, network)}
diff --git a/src/components/CircleIcon.tsx b/src/components/CircleIcon.tsx
index a9c6250d6b..1f27ed640e 100644
--- a/src/components/CircleIcon.tsx
+++ b/src/components/CircleIcon.tsx
@@ -29,14 +29,15 @@ export const CircleIcon = ({ downToSM, tooltipText, children }: CircleIconProps)
>
{children}
diff --git a/src/components/ConnectWalletPaper.tsx b/src/components/ConnectWalletPaper.tsx
index 3c408addb2..620004b360 100644
--- a/src/components/ConnectWalletPaper.tsx
+++ b/src/components/ConnectWalletPaper.tsx
@@ -1,55 +1,30 @@
import { Trans } from '@lingui/macro';
-import { Box, CircularProgress, Paper, PaperProps, Typography } from '@mui/material';
+import { PaperProps } from '@mui/material';
import { useModal } from 'connectkit';
import { ReactNode } from 'react';
-import LandingGhost from '/public/resting-gho-hat-purple.svg';
-
+import { EmptyStatePaper } from './EmptyStatePaper';
import { ConnectWalletButton } from './WalletConnection/ConnectWalletButton';
interface ConnectWalletPaperProps extends PaperProps {
description?: ReactNode;
}
-export const ConnectWalletPaper = ({ description, sx, ...rest }: ConnectWalletPaperProps) => {
+export const ConnectWalletPaper = ({ description, ...rest }: ConnectWalletPaperProps) => {
const { open } = useModal();
return (
- No Wallet Connected}
+ description={
+ description || (
+ Connect your wallet to see your supplies, borrowings, and open positions.
+ )
+ }
{...rest}
- sx={{
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- justifyContent: 'center',
- textAlign: 'center',
- p: 4,
- flex: 1,
- ...sx,
- }}
>
-
-
-
- <>
- {open ? (
-
- ) : (
- <>
-
- Please, connect your wallet
-
-
- {description || (
-
- Please connect your wallet to see your supplies, borrowings, and open positions.
-
- )}
-
-
- >
- )}
- >
-
+
+
);
};
diff --git a/src/components/ConnectWalletPaperStaking.tsx b/src/components/ConnectWalletPaperStaking.tsx
index ca5f484c60..02549b7a3a 100644
--- a/src/components/ConnectWalletPaperStaking.tsx
+++ b/src/components/ConnectWalletPaperStaking.tsx
@@ -18,6 +18,7 @@ export const ConnectWalletPaperStaking = ({
}: ConnectWalletPaperStakingProps) => {
return (
Please, connect your wallet
-
+
{description || (
Please connect your wallet to see your supplies, borrowings, and open positions.
diff --git a/src/components/ContentContainer.tsx b/src/components/ContentContainer.tsx
index 846598efd0..b29459e9cc 100644
--- a/src/components/ContentContainer.tsx
+++ b/src/components/ContentContainer.tsx
@@ -1,21 +1,24 @@
-import { Box, Container } from '@mui/material';
+import { Box, Container, ContainerProps } from '@mui/material';
import { ReactNode } from 'react';
interface ContentContainerProps {
children: ReactNode;
+ // Optional passthrough to the inner MUI Container. Markets uses it for a wider maxWidth that its
+ // top panel shares for stat/table column alignment; every other page omits it (default Container).
+ containerProps?: ContainerProps;
}
-export const ContentContainer = ({ children }: ContentContainerProps) => {
+export const ContentContainer = ({ children, containerProps }: ContentContainerProps) => {
return (
- {children}
+ {children}
);
};
diff --git a/src/components/ContentWithTooltip.tsx b/src/components/ContentWithTooltip.tsx
index f6cc53e8d4..da3ec8e0a3 100644
--- a/src/components/ContentWithTooltip.tsx
+++ b/src/components/ContentWithTooltip.tsx
@@ -1,5 +1,6 @@
import { Box, ClickAwayListener, Popper, styled, Tooltip } from '@mui/material';
import { JSXElementConstructor, ReactElement, ReactNode, useState } from 'react';
+import { figVars } from 'src/utils/figmaColors';
interface ContentWithTooltipProps {
children: ReactNode;
@@ -14,19 +15,25 @@ interface ContentWithTooltipProps {
export const PopperComponent = styled(Popper)(({ theme }) =>
theme.unstable_sx({
+ // Frosted-glass tooltip: a translucent border-1 surface over an 80px backdrop blur, framed by
+ // an inset border-0 hairline + a soft shadow-medium drop. Padding lives here (not the inner Box).
'.MuiTooltip-tooltip': {
- color: 'text.primary',
- backgroundColor: 'background.paper',
- p: 0,
- borderRadius: '6px',
- boxShadow: '0px 0px 2px rgba(0, 0, 0, 0.2), 0px 2px 10px rgba(0, 0, 0, 0.1)',
- maxWidth: '280px',
+ color: 'fg-1',
+ backgroundColor: 'border-1',
+ borderRadius: '0.5rem',
+ boxShadow: `0 1px 12px 0 ${figVars['shadow-medium']}, inset 0 0 0 1px ${figVars['border-0']}`,
+ backdropFilter: 'blur(80px)',
+ padding: '0.5rem 0.75rem',
+ maxWidth: '250px',
+ textAlign: 'center',
+ whiteSpace: 'pre-wrap',
+ textWrap: 'pretty',
+ fontSize: '0.75rem',
+ fontWeight: 400,
+ lineHeight: '135%',
},
'.MuiTooltip-arrow': {
- color: 'background.paper',
- '&:before': {
- boxShadow: '0px 0px 2px rgba(0, 0, 0, 0.2), 0px 2px 10px rgba(0, 0, 0, 0.1)',
- },
+ color: 'border-1',
},
})
);
@@ -86,10 +93,7 @@ export const ContentWithTooltip = ({
>
{
+ title: ReactNode;
+ description?: ReactNode;
+ /** Optional action rendered under the description (e.g. a connect button). */
+ children?: ReactNode;
+ /** When true, replaces the content with a centered spinner. */
+ loading?: boolean;
+}
+
+/**
+ * Shared empty-state card: a centered, outlined paper with an H5 title, a muted description, and
+ * an optional action slot. Single source of truth for the app's "No Wallet Connected" /
+ * "No Positions" style empty states so their look + padding stay in sync.
+ */
+export const EmptyStatePaper = ({
+ title,
+ description,
+ children,
+ loading,
+ sx,
+ ...rest
+}: EmptyStatePaperProps) => (
+
+ {loading ? (
+
+ ) : (
+ <>
+
+ {title}
+
+ {description && (
+
+ {description}
+
+ )}
+ {children}
+ >
+ )}
+
+);
diff --git a/src/components/HealthFactorNumber.tsx b/src/components/HealthFactorNumber.tsx
index 2f7d615d91..363a337e4a 100644
--- a/src/components/HealthFactorNumber.tsx
+++ b/src/components/HealthFactorNumber.tsx
@@ -12,16 +12,16 @@ interface HealthFactorNumberProps extends TypographyProps {
}
export const HealthFactorNumber = ({ value, onInfoClick, ...rest }: HealthFactorNumberProps) => {
- const { palette } = useTheme();
+ const theme = useTheme();
const formattedHealthFactor = Number(valueToBigNumber(value).toFixed(2, BigNumber.ROUND_DOWN));
let healthFactorColor = '';
if (formattedHealthFactor >= 3) {
- healthFactorColor = palette.success.main;
+ healthFactorColor = theme.vars.palette.success.main;
} else if (formattedHealthFactor < 1.1) {
- healthFactorColor = palette.error.main;
+ healthFactorColor = theme.vars.palette.error.main;
} else {
- healthFactorColor = palette.warning.main;
+ healthFactorColor = theme.vars.palette.warning.main;
}
return (
@@ -34,7 +34,7 @@ export const HealthFactorNumber = ({ value, onInfoClick, ...rest }: HealthFactor
data-cy={'HealthFactorTopPannel'}
>
{value === '-1' ? (
-
+
∞
) : (
@@ -50,7 +50,7 @@ export const HealthFactorNumber = ({ value, onInfoClick, ...rest }: HealthFactor
{onInfoClick && (
),
proto_mainnet_v3: (
- Main Ethereum market with the largest selection of assets and yield options
+ Main market with the largest selection of assets and yield options.
),
proto_lido_v3: (
Optimized for efficiency and risk by supporting blue-chip collateral assets
@@ -272,71 +291,17 @@ export const MarketSwitcher = () => {
// --- Render helpers ---
- const renderPinnedChip = (marketId: CustomMarket) => {
- const { market, logo } = getMarketInfoById(marketId);
- const marketNaming = getMarketHelpData(market.marketTitle);
- const isSelected = marketId === currentMarket;
- return (
- handleSelectMarket(marketId)}
- onKeyDown={(e: React.KeyboardEvent) => {
- if (e.key === 'Enter' || e.key === ' ') {
- e.preventDefault();
- handleSelectMarket(marketId);
- }
- }}
- sx={{
- display: 'flex',
- alignItems: 'center',
- gap: '7px',
- height: 36,
- pl: '6px',
- pr: '10px',
- py: 1,
- borderRadius: '48px',
- border: '1px solid',
- borderColor: isSelected ? 'primary.main' : 'rgba(0,0,0,0.1)',
- bgcolor: isSelected ? 'action.selected' : 'transparent',
- cursor: 'pointer',
- '&:hover': { bgcolor: 'action.hover' },
- flexShrink: 0,
- }}
- >
-
-
-
-
-
- {marketNaming.name} {market.isFork ? 'Fork' : ''}
-
-
- handleStarClick(e, marketId)}
- sx={{
- padding: 0,
- flexShrink: 0,
- }}
- >
-
-
-
-
-
- );
- };
+ const renderRowLogo = (src: string) => (
+
+
+
+ );
const renderGridItem = (marketId: CustomMarket, isMobile?: boolean, width = '33.33%') => {
const { market, logo } = getMarketInfoById(marketId);
@@ -359,49 +324,73 @@ export const MarketSwitcher = () => {
sx={{
display: 'flex',
alignItems: 'center',
- py: '10px',
- px: '12px',
+ height: '2.5rem',
+ py: '0.5rem',
+ px: '0.75rem',
width: isMobile ? '50%' : width,
boxSizing: 'border-box',
borderRadius: '8px',
cursor: 'pointer',
- position: 'relative',
- bgcolor: isSelected ? 'action.selected' : 'transparent',
- '&:hover': { bgcolor: isSelected ? 'action.selected' : 'action.hover' },
- // Star: always visible on mobile, hover-reveal on desktop
+ // Hover/selected highlight on an inset pseudo-element so adjacent options keep a gap
+ // (shared recipe with the dropdown menu items — see insetHighlight.ts). The current
+ // market gets a persistent fill via restFill; others fill in on hover.
+ ...insetHighlightBase({
+ theme,
+ radius: '8px',
+ inset: '1px',
+ restFill: isSelected ? figVars['selected'] : undefined,
+ }),
+ ...(isSelected
+ ? {}
+ : {
+ // Row highlight on hover AND on keyboard focus — of the row itself or its star
+ // button (`:focus-within`) — so tabbing through always shows where you are.
+ '&:hover::before, &:focus-within::before': insetHighlightActive(
+ figVars['button-hover']
+ ),
+ }),
+ // Star: always visible on mobile, hover-reveal on desktop; also reveal it whenever the
+ // row or the star button is focused, so keyboard users can see the favourite toggle.
'& .grid-fav-btn': {
opacity: isMobile || isFavorite ? 1 : 0,
transition: 'opacity 0.15s',
},
- '&:hover .grid-fav-btn': {
+ '&:hover .grid-fav-btn, &:focus-within .grid-fav-btn': {
opacity: 1,
},
+ // Keyboard focus lands on the star itself: ring it with an outline so it reads as focused
+ // (global ripple is disabled — add our own affordance).
+ '& .grid-fav-btn:focus-visible': {
+ opacity: 1,
+ // Same focus ring the buttons use (MuiButton root in theme.tsx).
+ outline: `2px solid ${figVars['fg-1']}`,
+ outlineOffset: '2px',
+ },
+ // The empty (non-favourited) star fills a step stronger (fg-4 → fg-3) on hover / focus.
+ ...(isFavorite
+ ? {}
+ : {
+ '&:hover .grid-fav-btn .MuiSvgIcon-root, & .grid-fav-btn:focus-visible .MuiSvgIcon-root':
+ {
+ color: figVars['fg-3'],
+ },
+ }),
}}
>
-
-
-
+ {renderRowLogo(logo)}
{marketNaming.name} {market.isFork ? 'Fork' : ''}
{market.externalUrl && (
-
+
)}
@@ -411,94 +400,66 @@ export const MarketSwitcher = () => {
onClick={(e) => handleStarClick(e, marketId)}
sx={{ padding: '1px', ml: 0.5, flexShrink: 0 }}
>
-
-
-
+ />
);
};
- const renderAaveProLink = (isMobile?: boolean, width = '33.33%') => (
+ const renderLinkRow = (
+ {
+ logo,
+ label,
+ href,
+ badge,
+ }: { logo: string; label: React.ReactNode; href: string; badge?: React.ReactNode },
+ isMobile?: boolean,
+ width = '33.33%'
+ ) => (
window.open(AAVE_PRO_URL, '_blank')}
+ onClick={() => window.open(href, '_blank')}
onKeyDown={(e: React.KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
- window.open(AAVE_PRO_URL, '_blank');
+ window.open(href, '_blank');
}
}}
sx={{
display: 'flex',
alignItems: 'center',
- py: '10px',
- px: '12px',
+ height: '2.5rem',
+ py: '0.5rem',
+ px: '0.75rem',
width: isMobile ? '50%' : width,
boxSizing: 'border-box',
borderRadius: '8px',
cursor: 'pointer',
- '&:hover': { bgcolor: 'action.hover' },
+ // Hover highlight on an inset pseudo-element, matching the market rows (insetHighlight.ts).
+ ...insetHighlightBase({ theme, radius: '8px', inset: '1px' }),
+ '&:hover::before': insetHighlightActive(figVars['button-hover']),
}}
>
-
-
-
-
-
- Aave Pro
-
-
- V4
+ {renderRowLogo(logo)}
+ {badge ? (
+
+
+ {label}
+
+ {badge}
-
-
+ ) : (
+
+ {label}
+
+ )}
+
@@ -506,239 +467,198 @@ export const MarketSwitcher = () => {
const sectionHeader = (label: React.ReactNode) => (
{label}
);
+ const renderSection = (title: React.ReactNode, children: React.ReactNode) => (
+
+ {sectionHeader(title)}
+ {children}
+
+ );
+
const noResults =
pinned.length === 0 && ethereum.length === 0 && l2.length === 0 && other.length === 0;
const renderSelectorContent = (mobile: boolean) => (
<>
- {/* Fixed header with search */}
-
-
- {/*
-
- {ENABLE_TESTNET || STAGING_ENV ? 'Select Aave Testnet Market' : 'Select Aave Market'}
-
- */}
- {mobile && (
-
-
-
-
-
- )}
+ {/* Mobile-only close button */}
+ {mobile && (
+
+
+
+
+
+
-
-
-
+ setSearchQuery(e.target.value)}
- InputProps={{
- startAdornment: (
-
-
-
-
-
- ),
- }}
+ startAdornment={
+
+
+
+ }
sx={{
- '& .MuiOutlinedInput-root': {
- borderRadius: '6px',
- height: '36px',
- '& fieldset': {
- borderColor: '#EAEBEF',
- },
- },
- '& .MuiOutlinedInput-input': {
- fontSize: '14px',
- letterSpacing: '0.15px',
- '&::placeholder': {
- color: '#A5A8B6',
- opacity: 1,
- },
+ flex: 1,
+ color: 'fg-1',
+ fontSize: '0.875rem',
+ fontWeight: 400,
+ lineHeight: 1,
+ '& input::placeholder': {
+ color: 'fg-4',
+ opacity: 1,
},
}}
/>
- {/* Content (scrolls on mobile, extends on desktop) */}
+ {/* Contents box — 1rem padding all sides, 1.5rem between sections */}
{/* Favourites */}
- {pinned.length > 0 && (
-
-
- Favourites
-
-
- {pinned.map(renderPinnedChip)}
-
-
-
- )}
+ {pinned.length > 0 &&
+ renderSection(
+ Favourites,
+ pinned.map((id) => renderGridItem(id, mobile))
+ )}
- {/* Ethereum */}
- {ethereum.length > 0 && (
-
- {sectionHeader(Ethereum)}
-
- {ethereum.map((id) => renderGridItem(id, mobile, '33%'))}
- {renderAaveProLink(mobile, '33%')}
-
- {(other.length > 0 || l2.length > 0 || (showLegacy && legacy.length > 0)) && (
-
- )}
-
- )}
+ {/* Ethereum + Aave Pro link */}
+ {ethereum.length > 0 &&
+ renderSection(
+ Ethereum,
+ <>
+ {ethereum.map((id) => renderGridItem(id, mobile))}
+ {renderLinkRow(
+ {
+ logo: AAVE_PRO_LOGO,
+ href: AAVE_PRO_URL,
+ label: Aave Pro,
+ badge: (
+
+ V4
+
+ ),
+ },
+ mobile
+ )}
+ >
+ )}
{/* L1 Networks */}
- {other.length > 0 && (
-
- {sectionHeader(L1 Networks)}
-
- {other.map((id) => renderGridItem(id, mobile))}
-
- {(l2.length > 0 || showLegacy) && }
-
- )}
+ {other.length > 0 &&
+ renderSection(
+ L1 Networks,
+ other.map((id) => renderGridItem(id, mobile))
+ )}
{/* L2 Networks */}
- {l2.length > 0 && (
-
- {sectionHeader(L2 Networks)}
-
- {l2.map((id) => renderGridItem(id, mobile))}
-
- {showLegacy && }
-
- )}
+ {l2.length > 0 &&
+ renderSection(
+ L2 Networks,
+ l2.map((id) => renderGridItem(id, mobile))
+ )}
- {/* Legacy */}
- {showLegacy && (
-
- {sectionHeader(Legacy)}
-
+ {/* Legacy + V2 markets link */}
+ {showLegacy &&
+ renderSection(
+ Legacy,
+ <>
{legacy.map((id) => renderGridItem(id, mobile))}
- {/* V2 markets external link */}
- window.open('https://v2-market.aave.com/', '_blank')}
- onKeyDown={(e: React.KeyboardEvent) => {
- if (e.key === 'Enter' || e.key === ' ') {
- e.preventDefault();
- window.open('https://v2-market.aave.com/', '_blank');
- }
- }}
- sx={{
- display: 'flex',
- alignItems: 'center',
- py: '10px',
- px: '12px',
- width: mobile ? '50%' : '33.33%',
- boxSizing: 'border-box',
- borderRadius: '8px',
- cursor: 'pointer',
- '&:hover': { bgcolor: 'action.hover' },
- }}
- >
-
-
-
-
- V2 Markets
-
-
-
-
-
-
-
- )}
+ {renderLinkRow(
+ {
+ logo: '/favicon.ico',
+ href: 'https://v2-market.aave.com/',
+ label: V2 Markets,
+ },
+ mobile
+ )}
+ >
+ )}
{/* No results */}
{noResults && (
-
+
No markets found
)}
-
- {/* Legacy markets toggle */}
-
- setShowLegacy(e.target.checked)}
+ inputProps={{ 'aria-label': t`Show legacy markets` }}
+ />
+ }
+ label={Show legacy markets}
sx={{
- fontSize: '14px',
- fontWeight: 500,
- letterSpacing: '0.15px',
- color: 'text.secondary',
+ m: 0,
+ alignSelf: 'flex-start',
+ gap: '1rem',
+ '& .MuiFormControlLabel-label': {
+ color: 'fg-2',
+ fontSize: '0.875rem',
+ fontWeight: 400,
+ lineHeight: '100%',
+ },
}}
- >
- Show legacy markets
-
- setShowLegacy(e.target.checked)}
- inputProps={{ 'aria-label': t`Show legacy markets` }}
/>
>
@@ -774,77 +694,67 @@ export const MarketSwitcher = () => {
}}
>
+ {titlePrefix && (
+
+ {titlePrefix}
+
+ )}
-
+
+ {currentMarketNaming.name}
+ {currentMarketData.isFork ? ' Fork' : ''}
+
+
- {currentMarketNaming.name} {currentMarketData.isFork ? 'Fork' : ''}
- {upToLG && (currentMarket === 'proto_mainnet_v3' || currentMarket === 'proto_lido_v3')
- ? 'Instance'
- : ' Market'}
+ {currentMarketData.v3 ? 'v3' : 'v2'}
-
- {currentMarketData.v3 ? (
- theme.palette.gradients.aaveGradient,
- display: 'flex',
- alignItems: 'center',
- }}
- >
- V3
-
- ) : (
-
- V2
-
- )}
-
-
-
-
+
- {marketBlurbs[currentMarket] && (
+ {!hideTitleChrome && marketBlurbs[currentMarket] && (
@@ -877,7 +787,7 @@ export const MarketSwitcher = () => {
width: 36,
height: 4,
borderRadius: '2px',
- bgcolor: 'divider',
+ bgcolor: 'border-2',
}}
/>
@@ -895,16 +805,17 @@ export const MarketSwitcher = () => {
}}
slotProps={{
paper: {
+ variant: 'modal',
elevation: 0,
sx: {
- width: 535,
+ width: '32.5rem',
+ bgcolor: 'bgp-2',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
- mt: 1,
- borderRadius: '8px',
- border: '1px solid rgba(0,0,0,0.04)',
- boxShadow: '0px 0px 3px 0px rgba(0,0,0,0.1), 0px 4px 20px 0px rgba(0,0,0,0.15)',
+ mt: '-1rem',
+ // Offset the panel 1rem to the left of the trigger's left edge.
+ ml: '-1rem',
},
},
}}
diff --git a/src/components/NoSearchResults.tsx b/src/components/NoSearchResults.tsx
index 6896e0a048..9044c1b784 100644
--- a/src/components/NoSearchResults.tsx
+++ b/src/components/NoSearchResults.tsx
@@ -41,11 +41,7 @@ export const NoSearchResults: React.FC = ({ searchTerm, su
)}
{subtitle && (
-
+
{subtitle}
)}
diff --git a/src/components/PageHeader/PageHeader.tsx b/src/components/PageHeader/PageHeader.tsx
new file mode 100644
index 0000000000..1343d4b99e
--- /dev/null
+++ b/src/components/PageHeader/PageHeader.tsx
@@ -0,0 +1,92 @@
+import { Box, Container, ContainerProps, Typography } from '@mui/material';
+import { ReactNode } from 'react';
+import { figVars } from 'src/utils/figmaColors';
+
+interface PageHeaderProps {
+ title: ReactNode;
+ description?: ReactNode;
+ children: ReactNode;
+ containerProps?: ContainerProps;
+ /** Optional icon rendered before the heading-wrapped title. Ignored when `disableTitleTypography` is set. */
+ titleIcon?: ReactNode;
+ /**
+ * Render `title` as-is instead of wrapping it in the heading Typography. Use when the title is
+ * itself a styled/interactive node (e.g. the dashboard's MarketSwitcher, which brings its own
+ * title text + description).
+ */
+ disableTitleTypography?: boolean;
+}
+
+/**
+ * Reusable page header. Same outer band (bg / bottom hairline / padding) as `TopInfoPanel`, with a
+ * left title+description block and a right-aligned row of `PageHeaderStat` items. First adopted on
+ * `/staking`; intended to replace other pages' top info panels over time.
+ */
+export const PageHeader = ({
+ title,
+ titleIcon,
+ description,
+ children,
+ containerProps = {},
+ disableTitleTypography = false,
+}: PageHeaderProps) => {
+ return (
+
+
+
+
+ {disableTitleTypography ? (
+ title
+ ) : (
+
+ {titleIcon}
+
+ {title}
+
+
+ )}
+ {description && (
+
+ {description}
+
+ )}
+
+
+
+ {children}
+
+
+
+
+ );
+};
diff --git a/src/components/PageHeader/PageHeaderStat.tsx b/src/components/PageHeader/PageHeaderStat.tsx
new file mode 100644
index 0000000000..50957eb85a
--- /dev/null
+++ b/src/components/PageHeader/PageHeaderStat.tsx
@@ -0,0 +1,29 @@
+import { Box, Skeleton, Typography } from '@mui/material';
+import { ReactNode } from 'react';
+
+interface PageHeaderStatProps {
+ label: ReactNode;
+ children: ReactNode;
+ loading?: boolean;
+}
+
+/** A single label-over-value stat for `PageHeader` (0.62rem gap between label and value). */
+export const PageHeaderStat = ({ label, children, loading }: PageHeaderStatProps) => {
+ return (
+
+
+ {label}
+
+ {loading ? : children}
+
+ );
+};
diff --git a/src/components/ReserveOverviewBox.tsx b/src/components/ReserveOverviewBox.tsx
index de20c5d6f0..3c770904f0 100644
--- a/src/components/ReserveOverviewBox.tsx
+++ b/src/components/ReserveOverviewBox.tsx
@@ -1,5 +1,6 @@
import { Box, Typography } from '@mui/material';
import React, { ReactNode } from 'react';
+import { figVars } from 'src/utils/figmaColors';
type ReserveOverviewBoxProps = {
children: ReactNode;
@@ -14,13 +15,13 @@ export function ReserveOverviewBox({
}: ReserveOverviewBoxProps) {
return (
({
+ sx={{
borderRadius: '6px',
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
flex: fullWidth ? '0 100%' : '0 32%',
marginBottom: '2%',
maxWidth: fullWidth ? '100%' : '32%',
- })}
+ }}
>
{title && (
-
+
{title}
)}
diff --git a/src/components/ReserveSubheader.tsx b/src/components/ReserveSubheader.tsx
index 55119de461..d27d20b5d6 100644
--- a/src/components/ReserveSubheader.tsx
+++ b/src/components/ReserveSubheader.tsx
@@ -21,17 +21,17 @@ export function ReserveSubheader({ value, rightAlign }: ReserveSubheaderProps) {
}}
>
{value === 'Disabled' ? (
-
+
(Disabled)
) : (
)}
diff --git a/src/components/SavingsCardSkeleton.tsx b/src/components/SavingsCardSkeleton.tsx
index 6286a76e03..d6f4534739 100644
--- a/src/components/SavingsCardSkeleton.tsx
+++ b/src/components/SavingsCardSkeleton.tsx
@@ -1,4 +1,5 @@
import { Box, Grid, Skeleton, Stack } from '@mui/material';
+import { figVars } from 'src/utils/figmaColors';
interface SavingsCardSkeletonProps {
hasAccount?: boolean;
@@ -10,13 +11,13 @@ export const SavingsCardSkeleton = ({ hasAccount }: SavingsCardSkeletonProps) =>
{hasAccount && (
({
+ sx={{
borderRadius: { xs: '8px', xsm: '6px' },
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
p: 4,
mb: 4,
- background: theme.palette.background.paper,
- })}
+ background: figVars['surface-elevated'],
+ }}
>
diff --git a/src/components/SearchInput.tsx b/src/components/SearchInput.tsx
index b1b2f8192f..af8cac77b1 100644
--- a/src/components/SearchInput.tsx
+++ b/src/components/SearchInput.tsx
@@ -1,8 +1,9 @@
-import { SearchIcon } from '@heroicons/react/outline';
import { XCircleIcon } from '@heroicons/react/solid';
import { Box, BoxProps, IconButton, InputBase, useMediaQuery, useTheme } from '@mui/material';
import debounce from 'lodash/debounce';
import { useMemo, useRef, useState } from 'react';
+import { SearchIcon } from 'src/components/icons/SearchIcon';
+import { figVars } from 'src/utils/figmaColors';
interface SearchInputProps {
onSearchTermChange: (value: string) => void;
@@ -36,19 +37,17 @@ export const SearchInput = ({
}, [onSearchTermChange]);
return (
({
+ sx={{
display: 'flex',
alignItems: 'center',
gap: 2,
- border: `1px solid ${theme.palette.divider}`,
- borderRadius: '6px',
+ border: `1px solid ${figVars['border-2']}`,
+ borderRadius: '0.5rem',
height: '36px',
...wrapperSx,
- })}
+ }}
>
-
-
-
+
(({ theme }) => ({
- border: '0px',
+const CustomTxModalToggleButton = styled(ToggleButton)({
+ border: 0,
flex: 1,
- backgroundColor: '#383D51',
- borderRadius: '4px',
+ height: '100%',
+ color: figVars['fg-3'],
- '&.Mui-selected, &.Mui-selected:hover': {
- backgroundColor: '#FFFFFF',
- borderRadius: '4px !important',
+ // Inactive hover: brighten the label only (fg-3 → fg-2), never a background.
+ '&:hover': {
+ backgroundColor: 'transparent',
+ color: figVars['fg-2'],
},
- '&.Mui-selected, &.Mui-disabled': {
- zIndex: 100,
- height: '100%',
- display: 'flex',
- justifyContent: 'center',
-
- '.MuiTypography-subheader1': {
- background: theme.palette.gradients.aaveGradient,
- backgroundClip: 'text',
- textFillColor: 'transparent',
- },
- '.MuiTypography-secondary14': {
- background: theme.palette.gradients.aaveGradient,
- backgroundClip: 'text',
- textFillColor: 'transparent',
- },
+ // Active — and keyboard focus (Mui-focusVisible): a solid fill one step darker than the
+ // `bg-1` track (no shadow; the group carries the only ring) with primary text.
+ '&.Mui-selected, &.Mui-selected:hover, &.Mui-focusVisible': {
+ backgroundColor: figVars['bg-4'],
+ color: figVars['fg-1'],
},
-})) as typeof ToggleButton;
-
-const CustomTxModalToggleButton = styled(ToggleButton)(({ theme }) => ({
- border: '0px',
- flex: 1,
- color: theme.palette.text.muted,
- borderRadius: '4px',
- // Selected (active) state
- '&.Mui-selected, &.Mui-selected:hover': {
- border: `1px solid ${theme.palette.other.standardInputLine}`,
- backgroundColor: '#FFFFFF',
- borderRadius: '4px !important',
- color: theme.palette.background.header,
- zIndex: 100,
- height: '100%',
- display: 'flex',
- justifyContent: 'center',
- },
-
- // Disabled but NOT selected: keep readable text with slight fade
+ // Disabled but NOT selected: muted, still readable.
'&.Mui-disabled:not(.Mui-selected)': {
- color: theme.palette.text.secondary,
- opacity: 0.55,
+ color: figVars['fg-3'],
+ opacity: 0.5,
},
- // Disabled + selected: preserve the selected look
+ // Disabled + selected: preserve the selected look (some consumers disable the active tab).
'&.Mui-disabled.Mui-selected': {
- border: `1px solid ${theme.palette.other.standardInputLine}`,
- backgroundColor: '#FFFFFF',
- borderRadius: '4px !important',
- color: theme.palette.background.header,
+ backgroundColor: figVars['bg-4'],
+ color: figVars['fg-1'],
opacity: 1,
},
-})) as typeof ToggleButton;
+}) as typeof ToggleButton;
export function StyledTxModalToggleButton(props: ToggleButtonProps) {
return ;
}
-
-export default function StyledToggleButton(props: ToggleButtonProps) {
- return ;
-}
diff --git a/src/components/StyledToggleButtonGroup.tsx b/src/components/StyledToggleButtonGroup.tsx
index d3531fdafd..ca784ce3fe 100644
--- a/src/components/StyledToggleButtonGroup.tsx
+++ b/src/components/StyledToggleButtonGroup.tsx
@@ -1,22 +1,34 @@
import { styled, ToggleButtonGroup, ToggleButtonGroupProps } from '@mui/material';
+import { figVars } from 'src/utils/figmaColors';
-const CustomToggleGroup = styled(ToggleButtonGroup)({
- backgroundColor: '#383D51',
- border: '1px solid rgba(235, 235, 237, 0.12)',
- padding: '4px',
-}) as typeof ToggleButtonGroup;
-
-const CustomTxModalToggleGroup = styled(ToggleButtonGroup)(({ theme }) => ({
- backgroundColor: theme.palette.background.header,
+// Segmented control framed like a medium outlined button: a `bg-5` fill with an inset 1px hairline
+// (shadow-stroke-2) as the ONLY frame — no border. 36px tall / 0.5rem radius to match the medium
+// button; the active pill stays concentric (0.375rem) inside the 2px inset.
+const CustomTxModalToggleGroup = styled(ToggleButtonGroup)({
+ backgroundColor: figVars['bg-5'],
+ borderRadius: '0.5rem',
+ boxShadow: `inset 0 0 0 1px ${figVars['shadow-stroke-2']}`,
padding: '2px',
height: '36px',
width: '100%',
-})) as typeof ToggleButtonGroup;
+ // Strip MUI's per-segment border (its ToggleButton root has a 1px divider border — the stray
+ // outline on the active pill), negative margins, and merged first/last corner radii so each
+ // segment is an independent borderless pill and the group carries the only ring.
+ '& .MuiToggleButtonGroup-grouped': {
+ margin: 0,
+ border: 0,
+ borderRadius: '0.375rem',
+ '&:not(:first-of-type)': {
+ marginLeft: 0,
+ borderLeft: 0,
+ borderRadius: '0.375rem',
+ },
+ '&:not(:last-of-type)': {
+ borderRadius: '0.375rem',
+ },
+ },
+}) as typeof ToggleButtonGroup;
export function StyledTxModalToggleGroup(props: ToggleButtonGroupProps) {
return ;
}
-
-export default function StyledToggleGroup(props: ToggleButtonGroupProps) {
- return ;
-}
diff --git a/src/components/TextWithTooltip.tsx b/src/components/TextWithTooltip.tsx
index 406692221e..58bab46111 100644
--- a/src/components/TextWithTooltip.tsx
+++ b/src/components/TextWithTooltip.tsx
@@ -74,9 +74,10 @@ export const TextWithTooltip = ({
{icon || }
diff --git a/src/components/TopInfoPanel/PageTitle.tsx b/src/components/TopInfoPanel/PageTitle.tsx
index 69c9ebe9ad..e16a6129fb 100644
--- a/src/components/TopInfoPanel/PageTitle.tsx
+++ b/src/components/TopInfoPanel/PageTitle.tsx
@@ -1,7 +1,7 @@
-import { StarIcon } from '@heroicons/react/solid';
import { Trans } from '@lingui/macro';
-import { Box, Button, SvgIcon, Typography, useMediaQuery, useTheme } from '@mui/material';
+import { Box, Button, Typography, useMediaQuery, useTheme } from '@mui/material';
import { ReactNode } from 'react';
+import { FAVOURITE_STAR_COLOR, StarIcon } from 'src/components/icons/StarIcon';
import { useRootStore } from '../../store/root';
import { selectIsMigrationAvailable } from '../../store/v3MigrationSelectors';
@@ -55,7 +55,7 @@ export const PageTitle = ({
{withMarketSwitcher && }
- {/* */}
+ {/* */}
{/* NOTE:// Removing for now */}
{isMigrateToV3Available && withMigrateButton && (
-
diff --git a/src/components/TopInfoPanel/TopInfoPanel.tsx b/src/components/TopInfoPanel/TopInfoPanel.tsx
index cbc88f4d8c..64da50d391 100644
--- a/src/components/TopInfoPanel/TopInfoPanel.tsx
+++ b/src/components/TopInfoPanel/TopInfoPanel.tsx
@@ -1,5 +1,6 @@
import { Box, Container, ContainerProps } from '@mui/material';
import { ReactNode } from 'react';
+import { figVars } from 'src/utils/figmaColors';
import { PageTitle, PageTitleProps } from './PageTitle';
@@ -22,14 +23,17 @@ export const TopInfoPanel = ({
return (
-
+
{!titleComponent && (
{
@@ -35,18 +33,6 @@ export const TopInfoPanelItem = ({
width: { xs: 'calc(50% - 12px)', xsm: 'unset' },
}}
>
- {withLine && (
-
- )}
-
{!hideIcon &&
(withoutIconWrapper ? (
icon && icon
@@ -72,7 +58,7 @@ export const TopInfoPanelItem = ({
diff --git a/src/components/WalletConnection/ConnectWalletButton.tsx b/src/components/WalletConnection/ConnectWalletButton.tsx
index bb51de8b70..123e19d091 100644
--- a/src/components/WalletConnection/ConnectWalletButton.tsx
+++ b/src/components/WalletConnection/ConnectWalletButton.tsx
@@ -100,7 +100,7 @@ export const ConnectWalletButton: React.FC = ({ funnel, onCl
return (
{
// Track initial button click
trackEvent(AUTH.CONNECT_WALLET, {
@@ -120,7 +120,7 @@ export const ConnectWalletButton: React.FC = ({ funnel, onCl
titleProps={{ variant: 'buttonM' }}
/>
) : (
- Connect wallet
+ Connect Wallet
)}
);
diff --git a/src/components/WalletConnection/ReadOnlyModal.tsx b/src/components/WalletConnection/ReadOnlyModal.tsx
index 7bc6731e8e..b597da091a 100644
--- a/src/components/WalletConnection/ReadOnlyModal.tsx
+++ b/src/components/WalletConnection/ReadOnlyModal.tsx
@@ -1,13 +1,5 @@
import { Trans } from '@lingui/macro';
-import {
- Box,
- Button,
- Divider,
- InputBase,
- Typography,
- useMediaQuery,
- useTheme,
-} from '@mui/material';
+import { Box, Button, Divider, TextField, Typography } from '@mui/material';
import { useState } from 'react';
import { ReadOnlyModeTooltip } from 'src/components/infoTooltips/ReadOnlyModeTooltip';
import { ModalType, useModalContext } from 'src/hooks/useModal';
@@ -28,8 +20,6 @@ export const ReadOnlyModal = () => {
const [inputMockWalletAddress, setInputMockWalletAddress] = useState('');
const [validAddressError, setValidAddressError] = useState(false);
const { type, close } = useModalContext();
- const { breakpoints } = useTheme();
- const sm = useMediaQuery(breakpoints.down('sm'));
const trackEvent = useRootStore((store) => store.trackEvent);
const handleReadAddress = async (inputMockWalletAddress: string): Promise => {
@@ -76,47 +66,32 @@ export const ReadOnlyModal = () => {
return (
-
-
-
+
+
+
Watch a wallet balance in read-only mode
{validAddressError && (
diff --git a/src/components/caps/CapsCircularStatus.tsx b/src/components/caps/CapsCircularStatus.tsx
index 383993195f..c413e51fcd 100644
--- a/src/components/caps/CapsCircularStatus.tsx
+++ b/src/components/caps/CapsCircularStatus.tsx
@@ -52,7 +52,7 @@ export const CapsCircularStatus = ({ value, tooltipContent, onClick }: CapsCircu
theme.palette.grey[theme.palette.mode === 'light' ? 200 : 800],
+ color: (theme) => theme.vars.palette.grey[theme.palette.mode === 'light' ? 200 : 800],
position: 'absolute',
left: 1.25,
top: 1.25,
@@ -75,7 +75,7 @@ export const CapsCircularStatus = ({ value, tooltipContent, onClick }: CapsCircu
value={value <= 2 ? 2 : value > 100 ? 100 : value}
/>
-
+
{title}
= 0 ? value : 0}
compact
symbol={isUSD ? 'USD' : undefined}
- variant="tooltip"
+ variant="caption"
/>
>
)}
diff --git a/src/components/caps/DebtCeilingStatus.tsx b/src/components/caps/DebtCeilingStatus.tsx
index 48bd21f55c..f5821ec2ee 100644
--- a/src/components/caps/DebtCeilingStatus.tsx
+++ b/src/components/caps/DebtCeilingStatus.tsx
@@ -24,11 +24,11 @@ export const DebtCeilingStatus = ({
}: LinearProgressProps & DebtCeilingTooltipProps) => {
const determineColor = (theme: Theme): string => {
if (usageData.isMaxed || usageData.percentUsed >= 99.99) {
- return theme.palette.error.main;
+ return theme.vars.palette.error.main;
} else if (usageData.percentUsed >= 98) {
- return theme.palette.warning.main;
+ return theme.vars.palette.warning.main;
} else {
- return theme.palette.success.main;
+ return theme.vars.palette.success.main;
}
};
@@ -38,7 +38,7 @@ export const DebtCeilingStatus = ({
height: 5,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: (theme: Theme) =>
- theme.palette.grey[theme.palette.mode === 'light' ? 200 : 800],
+ theme.vars.palette.grey[theme.palette.mode === 'light' ? 200 : 800],
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 5,
@@ -50,7 +50,7 @@ export const DebtCeilingStatus = ({
<>
-
+
Isolated Debt Ceiling
@@ -71,24 +71,24 @@ export const DebtCeilingStatus = ({
of
diff --git a/src/components/icons/AaveLogo.tsx b/src/components/icons/AaveLogo.tsx
new file mode 100644
index 0000000000..849e8b279c
--- /dev/null
+++ b/src/components/icons/AaveLogo.tsx
@@ -0,0 +1,22 @@
+import { SVGProps } from 'react';
+
+// Aave wordmark. Fills with `currentColor` so it takes the color of its parent
+// (e.g. a palette token) — set `color` on the wrapping element to theme it.
+export const AaveLogo = (props: SVGProps) => (
+
+);
diff --git a/src/components/icons/ArrowUpRightIcon.tsx b/src/components/icons/ArrowUpRightIcon.tsx
new file mode 100644
index 0000000000..0bf43bbd5f
--- /dev/null
+++ b/src/components/icons/ArrowUpRightIcon.tsx
@@ -0,0 +1,26 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Up-right diagonal arrow — the "opens in a new tab / external" affordance shown next to a link.
+// Uses `currentColor` and inherits `color` from the consumer, so a palette token set via
+// `color`/`sx` on the parent (e.g. `purple-2`) flows straight through to the stroke. (A CSS var
+// can't be passed to an SVG `stroke` attribute directly, so color must arrive as `currentColor`.)
+export const ArrowUpRightIcon = ({ sx, ...rest }: SvgIconProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/src/components/icons/BridgeIcon.tsx b/src/components/icons/BridgeIcon.tsx
new file mode 100644
index 0000000000..43e4e92398
--- /dev/null
+++ b/src/components/icons/BridgeIcon.tsx
@@ -0,0 +1,9 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Bridge (GHO) icon. Fills with `currentColor` so it inherits the consumer's color
+// (e.g. a button's start-icon color, or a palette token via `color`/sx).
+export const BridgeIcon = ({ sx, ...rest }: SvgIconProps) => (
+
+
+
+);
diff --git a/src/components/icons/ChevronDownIcon.tsx b/src/components/icons/ChevronDownIcon.tsx
new file mode 100644
index 0000000000..9e647d653d
--- /dev/null
+++ b/src/components/icons/ChevronDownIcon.tsx
@@ -0,0 +1,23 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+import { figVars } from 'src/utils/figmaColors';
+
+// Double-chevron (up/down) dropdown indicator. Uses `currentColor` so the color
+// comes from the consumer (e.g. a palette token via the `color` prop / sx).
+export const ChevronDownIcon = ({ sx, ...rest }: SvgIconProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/src/components/icons/ChevronRightIcon.tsx b/src/components/icons/ChevronRightIcon.tsx
new file mode 100644
index 0000000000..1f1672ac0c
--- /dev/null
+++ b/src/components/icons/ChevronRightIcon.tsx
@@ -0,0 +1,21 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Right-pointing chevron used as the forward/expand indicator on dropdown option rows
+// (e.g. the settings "Language" row). Strokes with `currentColor` so the consumer's
+// `color` drives it — which keeps it Display-P3 capable (the token resolves to a CSS
+// var, and `var()` works in the `color` property even though not in SVG attributes).
+export const ChevronRightIcon = ({ sx, ...rest }: SvgIconProps) => (
+
+
+
+);
diff --git a/src/components/icons/ChevronUpDownIcon.tsx b/src/components/icons/ChevronUpDownIcon.tsx
new file mode 100644
index 0000000000..bef9325b82
--- /dev/null
+++ b/src/components/icons/ChevronUpDownIcon.tsx
@@ -0,0 +1,27 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Double-chevron (up/down) dropdown indicator. Uses `currentColor` so the color
+// comes from the consumer (e.g. a palette token via the `color` prop / sx).
+export const ChevronUpDownIcon = ({ sx, ...rest }: SvgIconProps) => {
+ return (
+
+
+
+
+ );
+};
diff --git a/src/components/icons/CloseIcon.tsx b/src/components/icons/CloseIcon.tsx
new file mode 100644
index 0000000000..2b127278eb
--- /dev/null
+++ b/src/components/icons/CloseIcon.tsx
@@ -0,0 +1,14 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Close (X) icon. Strokes with `currentColor` so it inherits the consumer's color
+// (e.g. a palette token via `color`/sx).
+export const CloseIcon = ({ sx, ...rest }: SvgIconProps) => (
+
+
+
+
+);
diff --git a/src/components/icons/DotsHorizontalIcon.tsx b/src/components/icons/DotsHorizontalIcon.tsx
new file mode 100644
index 0000000000..e1c8bef38d
--- /dev/null
+++ b/src/components/icons/DotsHorizontalIcon.tsx
@@ -0,0 +1,17 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Horizontal three-dot ("more" / meatball) menu icon. Fills with `currentColor` so it
+// inherits the consumer's color (e.g. a palette token via the `color` prop / sx) — which
+// is also what makes it Display-P3 capable, since the token resolves to a CSS var.
+export const DotsHorizontalIcon = ({ sx, ...rest }: SvgIconProps) => (
+
+
+
+
+
+);
diff --git a/src/components/icons/MinusIcon.tsx b/src/components/icons/MinusIcon.tsx
new file mode 100644
index 0000000000..365dbe1ea4
--- /dev/null
+++ b/src/components/icons/MinusIcon.tsx
@@ -0,0 +1,15 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Horizontal bar used as the collapse ("hide") control glyph. Strokes with `currentColor` so the
+// consumer sets the color. Cleaned from the Figma export (the stroke there was fg-3).
+export const MinusIcon = ({ sx, ...rest }: SvgIconProps) => (
+
+
+
+);
diff --git a/src/components/icons/SearchIcon.tsx b/src/components/icons/SearchIcon.tsx
new file mode 100644
index 0000000000..fe50ea831b
--- /dev/null
+++ b/src/components/icons/SearchIcon.tsx
@@ -0,0 +1,26 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Search / magnifying-glass icon. The lens is stroked and the handle is filled — both use
+// `currentColor` so the color comes from the consumer (a palette token via `color` / sx).
+export const SearchIcon = ({ sx, ...rest }: SvgIconProps) => {
+ return (
+
+
+
+
+ );
+};
diff --git a/src/components/icons/SettingsIcon.tsx b/src/components/icons/SettingsIcon.tsx
new file mode 100644
index 0000000000..766649cfa2
--- /dev/null
+++ b/src/components/icons/SettingsIcon.tsx
@@ -0,0 +1,19 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Settings gear. Strokes with `currentColor` so it inherits the consumer's color
+// (e.g. a palette token via `color`/sx).
+export const SettingsIcon = ({ sx, ...rest }: SvgIconProps) => (
+
+
+
+
+);
diff --git a/src/components/icons/StarIcon.tsx b/src/components/icons/StarIcon.tsx
new file mode 100644
index 0000000000..4a20c5c1a3
--- /dev/null
+++ b/src/components/icons/StarIcon.tsx
@@ -0,0 +1,14 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Theme token key for the favourited star's gold — resolves to Display-P3 + sRGB via the palette
+// (Figma: color(display-p3 1 0.7 0)). Consumers pass it as an sx `color`; the star fills it
+// through `currentColor`.
+export const FAVOURITE_STAR_COLOR = 'favourite-star';
+
+// Filled star for the "favourite" toggle. Fills with `currentColor` so the consumer
+// controls the color (e.g. gold when favourited, muted when not).
+export const StarIcon = ({ sx, ...rest }: SvgIconProps) => (
+
+
+
+);
diff --git a/src/components/icons/SwapIcon.tsx b/src/components/icons/SwapIcon.tsx
new file mode 100644
index 0000000000..f35f249a6c
--- /dev/null
+++ b/src/components/icons/SwapIcon.tsx
@@ -0,0 +1,36 @@
+import { SvgIcon, SvgIconProps } from '@mui/material';
+
+// Swap / switch arrows. Strokes with `currentColor` so it inherits the consumer's color
+// (e.g. a button's start-icon color, or a palette token via `color`/sx).
+export const SwapIcon = ({ sx, ...rest }: SvgIconProps) => (
+
+
+
+
+
+
+);
diff --git a/src/components/incentives/EthenaIncentivesTooltipContent.tsx b/src/components/incentives/EthenaIncentivesTooltipContent.tsx
index ea9aa346c6..34325379fa 100644
--- a/src/components/incentives/EthenaIncentivesTooltipContent.tsx
+++ b/src/components/incentives/EthenaIncentivesTooltipContent.tsx
@@ -13,7 +13,7 @@ export const EthenaAirdropTooltipContent = ({ points }: { points: number }) => {
href="https://app.ethena.fi/join"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
{'here'}
diff --git a/src/components/incentives/EtherfiIncentivesTooltipContent.tsx b/src/components/incentives/EtherfiIncentivesTooltipContent.tsx
index ddd0de7ea4..433776deef 100644
--- a/src/components/incentives/EtherfiIncentivesTooltipContent.tsx
+++ b/src/components/incentives/EtherfiIncentivesTooltipContent.tsx
@@ -17,7 +17,7 @@ export const EtherFiAirdropTooltipContent = ({ multiplier }: { multiplier: numbe
href="https://etherfi.gitbook.io/etherfi/getting-started/loyalty-points"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
here
diff --git a/src/components/incentives/IncentivesButton.tsx b/src/components/incentives/IncentivesButton.tsx
index 5a8fdb2ba4..98d2b54be4 100644
--- a/src/components/incentives/IncentivesButton.tsx
+++ b/src/components/incentives/IncentivesButton.tsx
@@ -11,6 +11,7 @@ import { useMerklPointsIncentives } from 'src/hooks/useMerklPointsIncentives';
import { useSonicIncentives } from 'src/hooks/useSonicIncentives';
import { useRootStore } from 'src/store/root';
import { DASHBOARD } from 'src/utils/events';
+import { figVars } from 'src/utils/figmaColors';
import { convertAprToApy } from 'src/utils/utils';
import { ContentWithTooltip } from '../ContentWithTooltip';
@@ -121,7 +122,7 @@ const BlankIncentives = () => {
justifyContent: 'center',
}}
>
-
+
@@ -380,12 +381,7 @@ const Content = ({
if (incentivesNetAPR !== INFINITY && incentivesNetAPR < 10000) {
return (
-
+
);
@@ -396,8 +392,8 @@ const Content = ({
value={incentivesNetAPR}
percent
compact
- variant="secondary12"
- color="text.secondary"
+ variant="subheader2"
+ color="fg-2"
/>
@@ -405,7 +401,7 @@ const Content = ({
} else if (incentivesNetAPR === INFINITY) {
return (
-
+
∞
@@ -426,7 +422,7 @@ const Content = ({
return (
({
+ sx={{
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
@@ -434,10 +430,10 @@ const Content = ({
justifyContent: 'center',
transition: 'opacity 0.2s ease',
'&:hover': {
- bgcolor: 'action.hover',
- borderColor: 'action.disabled',
+ bgcolor: 'button-hover',
+ borderColor: 'disabled-fg',
},
- })}
+ }}
onClick={() => {
// TODO: How to handle this for event props?
trackEvent(DASHBOARD.VIEW_LM_DETAILS_DASHBOARD, {});
@@ -500,28 +496,28 @@ const ContentButton = ({ value, iconSrc }: { value: number; iconSrc: string }) =
return (
({
+ sx={{
p: { xs: '0 4px', xsm: '2px 4px' },
- border: `1px solid ${open ? theme.palette.action.disabled : theme.palette.divider}`,
+ border: `1px solid ${open ? figVars['disabled-fg'] : figVars['border-2']}`,
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'opacity 0.2s ease',
- bgcolor: open ? 'action.hover' : 'transparent',
+ bgcolor: open ? 'button-hover' : 'transparent',
'&:hover': {
- bgcolor: 'action.hover',
- borderColor: 'action.disabled',
+ bgcolor: 'button-hover',
+ borderColor: 'disabled-fg',
},
- })}
+ }}
onClick={() => {
trackEvent(DASHBOARD.VIEW_LM_DETAILS_DASHBOARD, {});
setOpen(!open);
}}
>
-
+
{`${value}x`}
diff --git a/src/components/incentives/IncentivesCard.tsx b/src/components/incentives/IncentivesCard.tsx
index 4d914c3641..5e0e3b7455 100644
--- a/src/components/incentives/IncentivesCard.tsx
+++ b/src/components/incentives/IncentivesCard.tsx
@@ -24,8 +24,8 @@ interface IncentivesCardProps {
value: string | number;
incentives?: ReserveIncentiveResponse[];
address?: string;
- variant?: 'main14' | 'main16' | 'secondary14';
- symbolsVariant?: 'secondary14' | 'secondary16';
+ variant?: 'subheader1' | 'h4' | 'h5';
+ symbolsVariant?: 'h5' | 'secondary16';
color?: string;
tooltip?: ReactNode;
market: string;
@@ -39,7 +39,7 @@ export const IncentivesCard = ({
value,
incentives,
address,
- variant = 'secondary14',
+ variant = 'h5',
symbolsVariant,
align,
color,
@@ -166,7 +166,7 @@ export const IncentivesCard = ({
{value.toString() !== '-1' ? (
{displayAPY === 'Infinity' ? (
-
+
∞ %
) : (
@@ -195,7 +195,7 @@ export const IncentivesCard = ({
)}
) : (
-
+
)}
{!inlineIncentives && (
{
- const typographyVariant = 'secondary12';
+ const typographyVariant = 'subheader2';
const { data: meritIncentives } = useMeritIncentives({
symbol,
@@ -360,7 +360,7 @@ export const IncentivesTooltipContent = ({
flexDirection: 'column',
}}
>
-
+
Participating in this {symbol} reserve gives annualized rewards.
@@ -484,7 +484,7 @@ export const IncentivesTooltipContent = ({
{/* Show Net APR (protocol incentives only) if multiple incentives */}
{incentives.length > 1 && (
- ({ pt: 1, mt: 1 })}>
+
@@ -509,14 +509,14 @@ export const IncentivesTooltipContent = ({
{/* Show Total APY if we have Merit incentives or protocol APY */}
{(meritIncentives?.breakdown || protocolAPY > 0) && (
- ({ pt: 1, mt: 1, borderTop: '1px solid rgba(255, 255, 255, 0.1)' })}>
+
Total APY
-
+
({isBorrow ? 'Borrow Rate' : 'Supply Rate'})
diff --git a/src/components/incentives/MeritIncentivesTooltipContent.tsx b/src/components/incentives/MeritIncentivesTooltipContent.tsx
index cf0c44724e..72b1f27ca7 100644
--- a/src/components/incentives/MeritIncentivesTooltipContent.tsx
+++ b/src/components/incentives/MeritIncentivesTooltipContent.tsx
@@ -114,7 +114,7 @@ export const MeritIncentivesTooltipContent = ({
const theme = useTheme();
const { openClaimRewards } = useModalContext();
const account = useRootStore((store) => store.account);
- const typographyVariant = 'secondary12';
+ const typographyVariant = 'subheader2';
const handleClaimClick = () => {
openClaimRewards();
@@ -153,11 +153,11 @@ export const MeritIncentivesTooltipContent = ({
alt=""
/>
-
+
{campaignConfig.title}
-
+
This is a program initiated and implemented by the Aave Chan Initiative (ACI). Aave Labs
does not guarantee the program and accepts no liability.
@@ -166,7 +166,7 @@ export const MeritIncentivesTooltipContent = ({
href={'https://apps.aavechan.com/merit'}
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
Learn more
@@ -174,7 +174,7 @@ export const MeritIncentivesTooltipContent = ({
{campaignConfig.type === CampaignType.SELF_VERIFICATION && selfConfig && (
<>
-
+
Supply {selfConfig.token} and double your yield by{' '}
@@ -182,7 +182,7 @@ export const MeritIncentivesTooltipContent = ({
href="https://aave.self.xyz/"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
verifying your humanity through Self
@@ -190,7 +190,7 @@ export const MeritIncentivesTooltipContent = ({
for {selfConfig.limit} per user.
{' '}
-
+
Visit{' '}
@@ -198,7 +198,7 @@ export const MeritIncentivesTooltipContent = ({
href="https://aave.self.xyz/"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
https://aave.self.xyz/
@@ -209,19 +209,19 @@ export const MeritIncentivesTooltipContent = ({
>
)}
{isMultipleCampaigns(meritIncentives.activeActions) && remainingCustomMessage && (
-
+
{remainingCustomMessage}
)}
{meritIncentives.customMessage ? (
-
+
{meritIncentives.customMessage}
) : null}
Total APY
-
+
({meritIncentives.breakdown.isBorrow ? 'Borrow Rate' : 'Supply Rate'})
diff --git a/src/components/incentives/MerklIncentivesTooltipContent.tsx b/src/components/incentives/MerklIncentivesTooltipContent.tsx
index 81eb161146..2239764ef5 100644
--- a/src/components/incentives/MerklIncentivesTooltipContent.tsx
+++ b/src/components/incentives/MerklIncentivesTooltipContent.tsx
@@ -16,7 +16,7 @@ export const MerklIncentivesTooltipContent = ({
}) => {
const theme = useTheme();
- const typographyVariant = 'secondary12';
+ const typographyVariant = 'subheader2';
const merklIncentivesFormatted = getSymbolMap(merklIncentives);
@@ -41,7 +41,7 @@ export const MerklIncentivesTooltipContent = ({
alt=""
/>
-
+
{merklIncentives.isSelf ? (
Eligible for incentives through Merkl and Boosted Yield via Self.
) : (
@@ -49,7 +49,7 @@ export const MerklIncentivesTooltipContent = ({
)}
-
+
This is a program initiated by the Aave DAO and implemented by Merkl. Aave Labs does not
guarantee the program and accepts no liability.
@@ -80,7 +80,7 @@ export const MerklIncentivesTooltipContent = ({
href="https://aave.self.xyz/"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
verifying your humanity through Self
@@ -96,7 +96,7 @@ export const MerklIncentivesTooltipContent = ({
href="https://aave.self.xyz/"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
https://aave.self.xyz/
@@ -316,7 +316,7 @@ export const MerklIncentivesTooltipContent = ({
)}
{/* Total APY */}
-
+
-
+
APY
diff --git a/src/components/incentives/SonicIncentivesTooltipContent.tsx b/src/components/incentives/SonicIncentivesTooltipContent.tsx
index 5f784605b2..d6d6cf733f 100644
--- a/src/components/incentives/SonicIncentivesTooltipContent.tsx
+++ b/src/components/incentives/SonicIncentivesTooltipContent.tsx
@@ -13,7 +13,7 @@ export const SonicAirdropTooltipContent = ({ points }: { points: number }) => {
href="https://blog.soniclabs.com/sonic-points-simplified-how-to-qualify-for-200-million-s-airdrop/"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
{'here'}
diff --git a/src/components/infoTooltips/DarkTooltip.tsx b/src/components/infoTooltips/DarkTooltip.tsx
index 7442dbbc1b..e4e867b3b0 100644
--- a/src/components/infoTooltips/DarkTooltip.tsx
+++ b/src/components/infoTooltips/DarkTooltip.tsx
@@ -1,4 +1,5 @@
import { Box, Tooltip, TooltipProps } from '@mui/material';
+import { onAccent } from 'src/utils/figmaColors';
export const DarkTooltip = ({
title,
@@ -36,6 +37,7 @@ export const DarkTooltip = ({
title={
.{' '}
Learn more
diff --git a/src/components/infoTooltips/KernelAirdropTooltip.tsx b/src/components/infoTooltips/KernelAirdropTooltip.tsx
index 4c263436fa..10f111631b 100644
--- a/src/components/infoTooltips/KernelAirdropTooltip.tsx
+++ b/src/components/infoTooltips/KernelAirdropTooltip.tsx
@@ -23,7 +23,7 @@ export const KernelAirdropTooltip = () => {
href="https://kerneldao.gitbook.io/kernel/getting-started/editor/kernel-points-guide "
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
{'here'}
diff --git a/src/components/infoTooltips/MigrationDisabledTooltip.tsx b/src/components/infoTooltips/MigrationDisabledTooltip.tsx
index db3915d015..66ebdaa124 100644
--- a/src/components/infoTooltips/MigrationDisabledTooltip.tsx
+++ b/src/components/infoTooltips/MigrationDisabledTooltip.tsx
@@ -73,7 +73,7 @@ export const MigrationDisabledTooltip = ({
return (
}>
-
+
{warningText[warningType]}
diff --git a/src/components/infoTooltips/PointsBasedCampaignTooltip.tsx b/src/components/infoTooltips/PointsBasedCampaignTooltip.tsx
index 1ccc9f406b..9fe46d28f3 100644
--- a/src/components/infoTooltips/PointsBasedCampaignTooltip.tsx
+++ b/src/components/infoTooltips/PointsBasedCampaignTooltip.tsx
@@ -1,5 +1,6 @@
import { Box, Input, InputAdornment, Typography, useTheme } from '@mui/material';
import { ChangeEvent, useEffect, useState } from 'react';
+import { figVars } from 'src/utils/figmaColors';
import { convertAprToApy } from 'src/utils/utils';
import { FormattedNumber } from '../primitives/FormattedNumber';
@@ -23,7 +24,7 @@ export const PointsBasedCampaignTooltip = ({
...rest
}: PointBasedCampaignTooltipProps) => {
const theme = useTheme();
- const typographyVariant = 'secondary12';
+ const typographyVariant = 'subheader2';
const [inkPriceInput, setInkPriceInput] = useState('');
const [estimatedPointsValue, setEstimatedPointsValue] = useState(null);
const [estimatedAPY, setEstimatedAPY] = useState(null);
@@ -89,7 +90,7 @@ export const PointsBasedCampaignTooltip = ({
gap: 1.5,
pb: 1,
borderBottom: 1,
- borderColor: theme.palette.divider,
+ borderColor: 'border-2',
}}
>
@@ -107,11 +108,7 @@ export const PointsBasedCampaignTooltip = ({
pb: 1,
}}
>
-
+
The estimated amount of TydroInkPoints that you get per $1,000 net{' '}
{isBorrow ? 'borrowed' : 'supplied'} per day.
@@ -124,11 +121,11 @@ export const PointsBasedCampaignTooltip = ({
alignItems: 'center',
justifyContent: 'space-between',
p: 1.5,
- backgroundColor: theme.palette.action.hover,
+ backgroundColor: 'button-hover',
borderRadius: 1,
}}
>
-
+
Points per $1,000/day
@@ -148,18 +145,18 @@ export const PointsBasedCampaignTooltip = ({
gap: 1.25,
p: 1.5,
borderRadius: 1,
- border: `1px solid ${theme.palette.divider}`,
- backgroundColor: theme.palette.background.paper,
+ border: `1px solid ${figVars['border-2']}`,
+ backgroundColor: 'surface-elevated',
}}
>
{!hasInkPriceInput ? (
-
+
Calculate APY from the assumed INK price.
(INK price not public yet)
) : (
-
+
The results are based on your estimated INK price.
)}
@@ -175,7 +172,7 @@ export const PointsBasedCampaignTooltip = ({
{estimatedPointsValue !== null && (
@@ -205,10 +202,10 @@ export const PointsBasedCampaignTooltip = ({
px: 1,
py: 1,
borderRadius: 1,
- backgroundColor: theme.palette.action.hover,
+ backgroundColor: 'button-hover',
}}
>
-
+
APY
@@ -228,10 +225,10 @@ export const PointsBasedCampaignTooltip = ({
px: 1,
py: 1,
borderRadius: 1,
- backgroundColor: theme.palette.action.hover,
+ backgroundColor: 'button-hover',
}}
>
-
+
FDV
diff --git a/src/components/infoTooltips/PriceImpactTooltip.tsx b/src/components/infoTooltips/PriceImpactTooltip.tsx
index 1c3c399504..969985f60a 100644
--- a/src/components/infoTooltips/PriceImpactTooltip.tsx
+++ b/src/components/infoTooltips/PriceImpactTooltip.tsx
@@ -31,8 +31,8 @@ export const PriceImpactTooltip = ({
return (
)}
diff --git a/src/components/infoTooltips/SpkAirdropTooltip.tsx b/src/components/infoTooltips/SpkAirdropTooltip.tsx
index a5dc193330..b10eb8a4c0 100644
--- a/src/components/infoTooltips/SpkAirdropTooltip.tsx
+++ b/src/components/infoTooltips/SpkAirdropTooltip.tsx
@@ -23,7 +23,7 @@ export const SpkAirdropTooltip = () => {
href="https://forum.sky.money/t/spark-proposal-for-integrations-into-aave/25005"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
{'here'}
{' '}
@@ -32,7 +32,7 @@ export const SpkAirdropTooltip = () => {
href="https://docs.spark.fi/rewards/spk-token#what-is-the-spk-pre-farming-airdrop"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
{'here'}
diff --git a/src/components/infoTooltips/SuperFestTooltip.tsx b/src/components/infoTooltips/SuperFestTooltip.tsx
index e964668ba5..e3a20b1407 100644
--- a/src/components/infoTooltips/SuperFestTooltip.tsx
+++ b/src/components/infoTooltips/SuperFestTooltip.tsx
@@ -20,7 +20,7 @@ export const SuperFestTooltip = () => {
href="https://superfest.optimism.io"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
Learn more
{' '}
@@ -32,7 +32,7 @@ export const SuperFestTooltip = () => {
href="https://jumper.exchange/superfest"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
jumper.exchange
diff --git a/src/components/infoTooltips/WrappedTokenToolTipContent.tsx b/src/components/infoTooltips/WrappedTokenToolTipContent.tsx
index 03a90aac71..bf3b2c47e8 100644
--- a/src/components/infoTooltips/WrappedTokenToolTipContent.tsx
+++ b/src/components/infoTooltips/WrappedTokenToolTipContent.tsx
@@ -24,7 +24,7 @@ export const WrappedTokenTooltipContent = ({
return (
-
+
DAI balance will be converted via DSR contracts and then supplied as sDAI. Switching
incurs no additional costs and no slippage.
@@ -32,7 +32,7 @@ export const WrappedTokenTooltipContent = ({
-
+
Exchange rate
@@ -40,23 +40,18 @@ export const WrappedTokenTooltipContent = ({
) : (
-
- {tokenInSymbol}
+
+ {tokenInSymbol}
- {tokenOutSymbol}
+ {tokenOutSymbol}
)}
diff --git a/src/components/isolationMode/IsolatedBadge.tsx b/src/components/isolationMode/IsolatedBadge.tsx
index a75b2234a1..5a492c4c6d 100644
--- a/src/components/isolationMode/IsolatedBadge.tsx
+++ b/src/components/isolationMode/IsolatedBadge.tsx
@@ -22,7 +22,7 @@ const InfoIcon = ({ color }: InfoIconProps) => (
@@ -37,8 +37,8 @@ export const IsolatedEnabledBadge = ({
const theme = useTheme();
const sx = {
- border: `1px solid ${theme.palette.warning.main}`,
- color: theme.palette.warning.main,
+ border: `1px solid ${theme.vars.palette.warning.main}`,
+ color: theme.vars.palette.warning.main,
...contentSx,
};
return (
@@ -57,16 +57,16 @@ export const IsolatedEnabledBadge = ({
>
Isolated
-
+
);
@@ -119,7 +119,7 @@ const IsolationModeTooltipTemplate = ({ content }: { content: ReactNode }) => {
return (
{content}
-
+
Learn more in our{' '}
diff --git a/src/components/lists/ListHeaderTitle.tsx b/src/components/lists/ListHeaderTitle.tsx
index d553eaf82a..cde3ddeca6 100644
--- a/src/components/lists/ListHeaderTitle.tsx
+++ b/src/components/lists/ListHeaderTitle.tsx
@@ -1,6 +1,7 @@
import { Box, Typography } from '@mui/material';
import { ReactNode } from 'react';
import { useRootStore } from 'src/store/root';
+import { figVars } from 'src/utils/figmaColors';
import { MARKETS } from '../../utils/events';
@@ -40,7 +41,7 @@ export const ListHeaderTitle = ({
(!!onClick ? onClick() : !!sortKey && handleSorting(sortKey))}
sx={{
@@ -55,32 +56,28 @@ export const ListHeaderTitle = ({
({
+ sx={{
width: 0,
height: 0,
borderStyle: 'solid',
borderWidth: '0 4px 4px 4px',
borderColor: `transparent transparent ${
- sortName === sortKey && sortDesc
- ? theme.palette.text.secondary
- : theme.palette.divider
+ sortName === sortKey && sortDesc ? figVars['fg-2'] : figVars['border-2']
} transparent`,
mb: 0.5,
- })}
+ }}
/>
({
+ sx={{
width: 0,
height: 0,
borderStyle: 'solid',
borderWidth: '4px 4px 0 4px',
borderColor: `${
- sortName === sortKey && !sortDesc
- ? theme.palette.text.secondary
- : theme.palette.divider
+ sortName === sortKey && !sortDesc ? figVars['fg-2'] : figVars['border-2']
} transparent transparent transparent`,
- })}
+ }}
/>
)}
diff --git a/src/components/lists/ListHeaderWrapper.tsx b/src/components/lists/ListHeaderWrapper.tsx
index 3ee6be2472..4dc80587a3 100644
--- a/src/components/lists/ListHeaderWrapper.tsx
+++ b/src/components/lists/ListHeaderWrapper.tsx
@@ -19,9 +19,11 @@ export const ListHeaderWrapper = ({ px = 4, children, ...rest }: ListHeaderWrapp
position: 'sticky',
top: 0,
zIndex: 100,
- bgcolor: 'background.paper',
+ bgcolor: 'surface-elevated',
borderBottom: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-0',
+ borderTopLeftRadius: 'inherit',
+ borderTopRightRadius: 'inherit',
...rest.sx,
}}
>
diff --git a/src/components/lists/ListItem.tsx b/src/components/lists/ListItem.tsx
index 91505532d3..75ee399022 100644
--- a/src/components/lists/ListItem.tsx
+++ b/src/components/lists/ListItem.tsx
@@ -19,9 +19,9 @@ export const ListItem = ({ children, minHeight = 71, px = 4, button, ...rest }:
px,
'&:not(:last-child)': {
borderBottom: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-0',
},
- ...(button ? { '&:hover': { bgcolor: 'action.hover' } } : {}),
+ ...(button ? { '&:hover': { bgcolor: 'button-hover' } } : {}),
...rest.sx,
}}
>
diff --git a/src/components/lists/ListMobileItem.tsx b/src/components/lists/ListMobileItem.tsx
index c16f6102b8..6ae64bbc30 100644
--- a/src/components/lists/ListMobileItem.tsx
+++ b/src/components/lists/ListMobileItem.tsx
@@ -65,7 +65,7 @@ export const ListMobileItem = ({
{name}
-
+
{symbol}
{isIsolated && (
diff --git a/src/components/lists/ListWrapper.tsx b/src/components/lists/ListWrapper.tsx
index 5b3d32eb1d..2cc9e56eb3 100644
--- a/src/components/lists/ListWrapper.tsx
+++ b/src/components/lists/ListWrapper.tsx
@@ -1,6 +1,7 @@
import { Trans } from '@lingui/macro';
-import { Box, BoxProps, Paper, PaperProps, Typography } from '@mui/material';
+import { Box, BoxProps, Button, Paper, PaperProps } from '@mui/material';
import { ReactNode, useState } from 'react';
+import { MinusIcon } from 'src/components/icons/MinusIcon';
import { useRootStore } from 'src/store/root';
import { DASHBOARD } from 'src/utils/events';
@@ -20,6 +21,8 @@ interface ListWrapperProps {
paperSx?: PaperProps['sx'];
topInfoSx?: BoxProps['sx'];
onCollapseChange?: (collapsed: boolean) => void;
+ /** What the card hides — renders the toggle as "Hide {collapseLabel}" / "Show {collapseLabel}". */
+ collapseLabel?: string;
}
export const ListWrapper = ({
@@ -36,6 +39,7 @@ export const ListWrapper = ({
paperSx,
topInfoSx,
onCollapseChange,
+ collapseLabel,
}: ListWrapperProps) => {
const [isCollapse, setIsCollapse] = useState(
localStorageName ? localStorage.getItem(localStorageName) === 'true' : false
@@ -92,14 +96,25 @@ export const ListWrapper = ({
const collapsed = isCollapse && !noData;
+ const collapseText = collapsed ? (
+ collapseLabel ? (
+ Show {collapseLabel}
+ ) : (
+ Show
+ )
+ ) : collapseLabel ? (
+ Hide {collapseLabel}
+ ) : (
+ Hide
+ );
+
return (
@@ -125,31 +139,9 @@ export const ListWrapper = ({
{!!localStorageName && !noData && (
- {
handleTrackingEvents();
@@ -159,12 +151,26 @@ export const ListWrapper = ({
onCollapseChange?.(nextIsCollapse);
}
}}
+ endIcon={
+ // − when expanded; a rotated copy fades in to form a + when collapsed.
+
+
+
+
+ }
>
-
- {collapsed ? Show : Hide}
-
-
-
+ {collapseText}
+
)}
diff --git a/src/components/primitives/BasicModal.tsx b/src/components/primitives/BasicModal.tsx
index 7b20f4f0b9..ee010a03b8 100644
--- a/src/components/primitives/BasicModal.tsx
+++ b/src/components/primitives/BasicModal.tsx
@@ -1,7 +1,8 @@
-import { XIcon } from '@heroicons/react/outline';
-import { Box, IconButton, Modal, Paper, SvgIcon } from '@mui/material';
+import { Box, IconButton, Modal, Paper } from '@mui/material';
import React from 'react';
+import { CloseIcon } from '../icons/CloseIcon';
+
export interface BasicModalProps {
open: boolean;
children: React.ReactNode;
@@ -47,6 +48,9 @@ export const BasicModal = ({
'.MuiPaper-root': {
outline: 'none',
},
+ '.MuiBackdrop-root': {
+ backgroundColor: 'rgba(0, 0, 0, 0.32)',
+ },
}}
onClick={(e) => {
e.stopPropagation();
@@ -55,6 +59,7 @@ export const BasicModal = ({
data-cy={'Modal'}
>
-
-
-
+
)}
diff --git a/src/components/primitives/FormattedNumber.tsx b/src/components/primitives/FormattedNumber.tsx
index d5651b0b74..17852bdf4d 100644
--- a/src/components/primitives/FormattedNumber.tsx
+++ b/src/components/primitives/FormattedNumber.tsx
@@ -123,7 +123,7 @@ export function FormattedNumber({
component="span"
sx={{ mr: 0.5 }}
variant={symbolsVariant || rest.variant}
- color={symbolsColor || 'text.secondary'}
+ color={symbolsColor || rest.color}
>
{'<'}
@@ -133,7 +133,7 @@ export function FormattedNumber({
component="span"
sx={{ mr: 0.5 }}
variant={symbolsVariant || rest.variant}
- color={symbolsColor || 'text.secondary'}
+ color={symbolsColor || rest.color}
>
$
@@ -158,7 +158,7 @@ export function FormattedNumber({
component="span"
sx={{ ml: 0.5 }}
variant={symbolsVariant || rest.variant}
- color={symbolsColor || 'text.secondary'}
+ color={symbolsColor || rest.color}
>
%
@@ -168,7 +168,7 @@ export function FormattedNumber({
component="span"
sx={{ ml: 0.5 }}
variant={symbolsVariant || rest.variant}
- color={symbolsColor || 'text.secondary'}
+ color={symbolsColor || rest.color}
>
{symbol}
diff --git a/src/components/primitives/TypographyGradient.tsx b/src/components/primitives/TypographyGradient.tsx
index 48c033ef91..00290e22b2 100644
--- a/src/components/primitives/TypographyGradient.tsx
+++ b/src/components/primitives/TypographyGradient.tsx
@@ -2,15 +2,7 @@ import { Typography, TypographyProps } from '@mui/material';
export const TypographyGradient = ({ ...rest }: TypographyProps) => {
return (
- ({
- color: 'transparent',
- backgroundClip: 'text !important',
- webkitTextFillColor: 'transparent',
- background: theme.palette.gradients.aaveGradient,
- })}
- {...rest}
- >
+
{rest.children}
);
diff --git a/src/components/primitives/transitions/ScaleFade/index.tsx b/src/components/primitives/transitions/ScaleFade/index.tsx
new file mode 100644
index 0000000000..d1f40ee3de
--- /dev/null
+++ b/src/components/primitives/transitions/ScaleFade/index.tsx
@@ -0,0 +1,114 @@
+import { useTheme } from '@mui/material/styles';
+import { TransitionProps } from '@mui/material/transitions';
+import { unstable_useForkRef as useForkRef } from '@mui/utils';
+import * as React from 'react';
+import { Transition, TransitionStatus } from 'react-transition-group';
+import { motion } from 'src/utils/motion';
+
+// Force a reflow so the browser paints the "from" state before the transition runs.
+const reflow = (node: HTMLElement) => node.scrollTop;
+
+const resolveDuration = (timeout: TransitionProps['timeout'], mode: 'enter' | 'exit'): number =>
+ typeof timeout === 'number' ? timeout : timeout?.[mode] ?? motion.duration.overlay;
+
+// Base ("from") state is opacity 0 / scale 0.96; only the open state is declared here.
+// `transition` is set imperatively in the callbacks so React re-renders never clear it.
+const openStyles: Partial> = {
+ entering: { opacity: 1, transform: 'scale(1)' },
+ entered: { opacity: 1, transform: 'scale(1)' },
+};
+
+export interface ScaleFadeProps extends Omit {
+ children: React.ReactElement;
+}
+
+/**
+ * Shared overlay transition: opacity 0→1 + scale(0.96→1). Modeled on MUI's `Grow`
+ * (which pops from a punchier 0.75). Wired as the default `TransitionComponent` for
+ * popovers/menus/selects in the theme, and reusable by other overlays. It clones its
+ * single child (no wrapper node) so it drops into MUI's Modal/Popover plumbing and
+ * preserves the anchor-driven `transform-origin` that Popover sets on the node.
+ */
+export const ScaleFade = React.forwardRef(function ScaleFade(props, ref) {
+ const {
+ children,
+ in: inProp,
+ appear = true,
+ timeout = motion.duration.overlay,
+ style,
+ easing,
+ onEnter,
+ onEntering,
+ onEntered,
+ onExit,
+ onExiting,
+ onExited,
+ } = props;
+
+ const theme = useTheme();
+ const nodeRef = React.useRef(null);
+ const childRef = (children as React.ReactElement & { ref?: React.Ref }).ref;
+ const handleRef = useForkRef(nodeRef, childRef, ref);
+
+ const resolveEasing = (mode: 'enter' | 'exit'): string => {
+ if (typeof easing === 'string') return easing;
+ if (easing && easing[mode]) return easing[mode] as string;
+ return motion.easing.standard;
+ };
+
+ // With `nodeRef`, react-transition-group hands enter callbacks `isAppearing` and exit
+ // callbacks nothing — but MUI's own callbacks expect the DOM node, so inject it.
+ const normalize =
+ (callback?: (node: HTMLElement, isAppearing: boolean) => void) =>
+ (isAppearing?: boolean): void => {
+ if (callback && nodeRef.current) {
+ callback(nodeRef.current, isAppearing ?? false);
+ }
+ };
+
+ const handleEnter = normalize((node, isAppearing) => {
+ reflow(node);
+ node.style.transition = theme.transitions.create(['opacity', 'transform'], {
+ duration: resolveDuration(timeout, 'enter'),
+ easing: resolveEasing('enter'),
+ });
+ onEnter?.(node, isAppearing);
+ });
+
+ const handleExit = normalize((node) => {
+ node.style.transition = theme.transitions.create(['opacity', 'transform'], {
+ duration: resolveDuration(timeout, 'exit'),
+ easing: resolveEasing('exit'),
+ });
+ onExit?.(node);
+ });
+
+ return (
+
+ appear={appear}
+ in={inProp}
+ nodeRef={nodeRef}
+ timeout={timeout}
+ onEnter={handleEnter}
+ onEntering={normalize(onEntering)}
+ onEntered={normalize(onEntered)}
+ onExit={handleExit}
+ onExiting={normalize(onExiting)}
+ onExited={normalize(onExited)}
+ >
+ {(state) =>
+ React.cloneElement(children, {
+ style: {
+ opacity: 0,
+ transform: 'scale(0.96)',
+ visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
+ ...openStyles[state],
+ ...style,
+ ...children.props.style,
+ },
+ ref: handleRef,
+ })
+ }
+
+ );
+});
diff --git a/src/components/transactions/AssetInput.tsx b/src/components/transactions/AssetInput.tsx
index 18635ee701..76afc88e59 100644
--- a/src/components/transactions/AssetInput.tsx
+++ b/src/components/transactions/AssetInput.tsx
@@ -19,6 +19,7 @@ import React, { ReactNode } from 'react';
import NumberFormat, { NumberFormatProps } from 'react-number-format';
import { TrackEventProps } from 'src/store/analyticsSlice';
import { useRootStore } from 'src/store/root';
+import { figVars } from 'src/utils/figmaColors';
import { CapType } from '../caps/helper';
import { AvailableTooltip } from '../infoTooltips/AvailableTooltip';
@@ -125,18 +126,16 @@ export const AssetInput = ({
return (
-
- {inputTitle ? inputTitle : Amount}
-
+ {inputTitle ? inputTitle : Amount}
{capType && }
({
- border: `1px solid ${theme.palette.divider}`,
+ sx={{
+ border: `1px solid ${figVars['border-2']}`,
borderRadius: '6px',
overflow: 'hidden',
- })}
+ }}
>
{loading ? (
@@ -181,9 +180,9 @@ export const AssetInput = ({
p: 0,
left: 8,
zIndex: 1,
- color: 'text.muted',
+ color: 'fg-3',
'&:hover': {
- color: 'text.secondary',
+ color: 'fg-2',
},
}}
onClick={() => {
@@ -243,7 +242,7 @@ export const AssetInput = ({
},
'&.AssetInput__select .MuiOutlinedInput-notchedOutline': { display: 'none' },
'&.AssetInput__select .MuiSelect-icon': {
- color: 'text.primary',
+ color: 'fg-1',
right: '0%',
},
}}
@@ -262,7 +261,7 @@ export const AssetInput = ({
aToken={asset.aToken}
sx={{ mr: 2, ml: 4 }}
/>
-
+
{symbol}
@@ -304,23 +303,23 @@ export const AssetInput = ({
value={isNaN(Number(usdValue)) ? 0 : Number(usdValue)}
compact
symbol="USD"
- variant="secondary12"
- color="text.muted"
- symbolsColor="text.muted"
+ variant="subheader2"
+ color="fg-3"
+ symbolsColor="fg-3"
flexGrow={1}
/>
)}
{asset.balance && onChange && (
<>
-
+
{balanceText && balanceText !== '' ? balanceText : Balance}{' '}
{!disableInput && (
@@ -345,8 +344,8 @@ export const AssetInput = ({
{exchangeRateComponent && (
{
- const { palette } = useTheme();
-
const bridgeLimitTooltip = (
Due to bridging limits, the maximum amount currently available to bridge is{' '}
@@ -52,7 +50,7 @@ export const BridgeAmount = ({
amount !== '' && (maxAmountReducedDueToBridgeLimit || maxAmountReducedDueToRateLimit) ? (
+
Amount
}
diff --git a/src/components/transactions/Bridge/BridgeDestinationInput.tsx b/src/components/transactions/Bridge/BridgeDestinationInput.tsx
index b87fd36ecb..14df128283 100644
--- a/src/components/transactions/Bridge/BridgeDestinationInput.tsx
+++ b/src/components/transactions/Bridge/BridgeDestinationInput.tsx
@@ -10,6 +10,7 @@ import {
import { useEffect, useState } from 'react';
import { useIsContractAddress } from 'src/hooks/useIsContractAddress';
import { resolveEnsAddress } from 'src/utils/ensClient';
+import { figVars } from 'src/utils/figmaColors';
import { isAddress } from 'viem';
export const BridgeDestinationInput = ({
@@ -75,7 +76,7 @@ export const BridgeDestinationInput = ({
return (
-
+
To
@@ -100,7 +101,7 @@ export const BridgeDestinationInput = ({
}
labelPlacement="start"
label={
-
+
Use connected account
}
@@ -113,13 +114,13 @@ export const BridgeDestinationInput = ({
disabled={useConnectedAccount || fetchingIsContractAddress}
onChange={(e) => setDestinationAccount(e.target.value)}
placeholder={t`Enter ETH address or ENS`}
- sx={(theme) => ({
+ sx={{
height: '44px',
px: 2,
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
borderRadius: '6px',
overflow: 'hidden',
- })}
+ }}
endAdornment={
validatingENS || fetchingIsContractAddress ? (
diff --git a/src/components/transactions/Bridge/BridgeFeeTokenSelector.tsx b/src/components/transactions/Bridge/BridgeFeeTokenSelector.tsx
index 5744d104f1..94a6dc21ec 100644
--- a/src/components/transactions/Bridge/BridgeFeeTokenSelector.tsx
+++ b/src/components/transactions/Bridge/BridgeFeeTokenSelector.tsx
@@ -48,7 +48,7 @@ export const BridgeFeeTokenSelector = ({
href="https://docs.chain.link/ccip/billing"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
Learn more
@@ -119,7 +119,7 @@ export const BridgeFeeTokenSelector = ({
{!bridgeFeeFormatted && !loading ? (
-
+
) : loading ? (
) : (
@@ -129,7 +129,7 @@ export const BridgeFeeTokenSelector = ({
diff --git a/src/components/transactions/Bridge/BridgeModalContent.tsx b/src/components/transactions/Bridge/BridgeModalContent.tsx
index a3656c2cb3..23615a5255 100644
--- a/src/components/transactions/Bridge/BridgeModalContent.tsx
+++ b/src/components/transactions/Bridge/BridgeModalContent.tsx
@@ -314,7 +314,7 @@ export const BridgeModalContent = () => {
href="https://docs.chain.link/ccip/concepts#finality"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
Learn more
@@ -352,7 +352,7 @@ export const BridgeModalContent = () => {
target="_blank"
rel="noopener"
sx={{ mr: 8 }}
- variant="surface"
+ variant="outlined"
size="small"
endIcon={
@@ -377,7 +377,7 @@ export const BridgeModalContent = () => {
/>
{!user ? (
-
+
Please connect your wallet to be able to bridge your tokens.
@@ -402,11 +402,11 @@ export const BridgeModalContent = () => {
onClick={handleSwapNetworks}
sx={{
border: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-2',
position: 'absolute',
- backgroundColor: 'background.paper',
+ backgroundColor: 'surface-elevated',
mt: -1,
- '&:hover': { backgroundColor: 'background.surface' },
+ '&:hover': { backgroundColor: 'bg-2' },
}}
>
@@ -493,7 +493,7 @@ export const BridgeModalContent = () => {
sx={{ borderRadius: '4px' }}
/>
) : (
- {estimatedTimeToDestination}
+ {estimatedTimeToDestination}
)}
@@ -507,7 +507,7 @@ export const BridgeModalContent = () => {
sx={{ borderRadius: '4px' }}
/>
) : (
- {estimatedTimeToDestination}
+ {estimatedTimeToDestination}
)}
*/}
{/* Spacer */}
diff --git a/src/components/transactions/CancelCowOrder/CancelCowOrderModalContent.tsx b/src/components/transactions/CancelCowOrder/CancelCowOrderModalContent.tsx
index f4447b068e..67f558c619 100644
--- a/src/components/transactions/CancelCowOrder/CancelCowOrderModalContent.tsx
+++ b/src/components/transactions/CancelCowOrder/CancelCowOrderModalContent.tsx
@@ -57,7 +57,7 @@ export const CancelCowOrderModalContent = ({ cowOrder }: CancelCowOrderModalCont
Cancel order
-
+
{isCowSwapSubset(cowOrder) && cowOrder.usedAdapter ? (
This will cancel the order via an on-chain transaction. Note that the order will not
diff --git a/src/components/transactions/ClaimRewards/ClaimRewardsModalContent.tsx b/src/components/transactions/ClaimRewards/ClaimRewardsModalContent.tsx
index ec09aa1a01..9895501719 100644
--- a/src/components/transactions/ClaimRewards/ClaimRewardsModalContent.tsx
+++ b/src/components/transactions/ClaimRewards/ClaimRewardsModalContent.tsx
@@ -357,7 +357,7 @@ export const ClaimRewardsModalContent = ({ user, reserves }: ClaimRewardsModalCo
)}
{meritClaimRewards?.claimable && meritClaimRewards.claimable.length > 0 && (
-
+
{selectedRewardSymbol === RewardSymbol.ALL ? (
Claiming all protocol rewards and merit rewards together
) : selectedRewardSymbol === RewardSymbol.MERIT_ALL ||
@@ -415,8 +415,8 @@ export const ClaimRewardsModalContent = ({ user, reserves }: ClaimRewardsModalCo
>
-
-
+
+
{reward.symbol}
@@ -425,7 +425,7 @@ export const ClaimRewardsModalContent = ({ user, reserves }: ClaimRewardsModalCo
variant="helperText"
compact
symbol="USD"
- color="text.secondary"
+ color="fg-2"
/>
))}
@@ -458,9 +458,9 @@ export const ClaimRewardsModalContent = ({ user, reserves }: ClaimRewardsModalCo
/>
-
+
{meritReward.currency.symbol}
))}
@@ -510,11 +510,8 @@ export const ClaimRewardsModalContent = ({ user, reserves }: ClaimRewardsModalCo
>
-
-
+
+
{meritReward.symbol}
))}
@@ -565,8 +562,8 @@ export const ClaimRewardsModalContent = ({ user, reserves }: ClaimRewardsModalCo
>
-
-
+
+
{reward.symbol}
@@ -575,7 +572,7 @@ export const ClaimRewardsModalContent = ({ user, reserves }: ClaimRewardsModalCo
variant="helperText"
compact
symbol="USD"
- color="text.secondary"
+ color="fg-2"
/>
))}
diff --git a/src/components/transactions/ClaimRewards/RewardsSelect.tsx b/src/components/transactions/ClaimRewards/RewardsSelect.tsx
index aaed65f232..b895816bfe 100644
--- a/src/components/transactions/ClaimRewards/RewardsSelect.tsx
+++ b/src/components/transactions/ClaimRewards/RewardsSelect.tsx
@@ -29,7 +29,7 @@ export const RewardsSelect = ({
}: RewardsSelectProps) => {
return (
-
+
Rewards to claim
@@ -40,42 +40,42 @@ export const RewardsSelect = ({
width: '100%',
height: '44px',
borderRadius: '6px',
- borderColor: 'divider',
+ borderColor: 'border-2',
outline: 'none !important',
- color: 'text.primary',
+ color: 'fg-1',
'.MuiOutlinedInput-input': {
backgroundColor: 'transparent',
},
'&:hover .MuiOutlinedInput-notchedOutline, .MuiOutlinedInput-notchedOutline': {
- borderColor: 'divider',
+ borderColor: 'border-2',
outline: 'none !important',
borderWidth: '1px',
},
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
- borderColor: 'divider',
+ borderColor: 'border-2',
borderWidth: '1px',
},
- '.MuiSelect-icon': { color: 'text.primary' },
+ '.MuiSelect-icon': { color: 'fg-1' },
}}
native={false}
renderValue={(reward) => {
if (reward === RewardSymbol.ALL) {
return (
-
+
Claim all rewards
);
}
if (reward === RewardSymbol.MERIT_ALL) {
return (
-
+
Claim all merit rewards
);
}
if (reward === RewardSymbol.PROTOCOL_ALL) {
return (
-
+
Claim all protocol rewards
);
@@ -94,7 +94,7 @@ export const RewardsSelect = ({
return (
- {selected.symbol}
+ {selected.symbol}
);
}}
@@ -102,7 +102,7 @@ export const RewardsSelect = ({
@@ -182,7 +182,7 @@ export const RewardsSelect = ({
Protocol Rewards
@@ -191,7 +191,7 @@ export const RewardsSelect = ({
...(rewards.length > 1
? [
-
+
Claim all protocol rewards
,
@@ -208,7 +208,7 @@ export const RewardsSelect = ({
component="span"
sx={{ display: 'inline-flex', alignItems: 'center' }}
variant="caption"
- color="text.muted"
+ color="fg-3"
>
~
@@ -217,8 +217,8 @@ export const RewardsSelect = ({
variant="caption"
compact
symbol="USD"
- symbolsColor="text.muted"
- color="text.muted"
+ symbolsColor="fg-3"
+ color="fg-3"
/>
diff --git a/src/components/transactions/Emode/EmodeAssetTable.tsx b/src/components/transactions/Emode/EmodeAssetTable.tsx
index 78f5a90dd1..383dd1e415 100644
--- a/src/components/transactions/Emode/EmodeAssetTable.tsx
+++ b/src/components/transactions/Emode/EmodeAssetTable.tsx
@@ -70,7 +70,7 @@ export const EmodeAssetTable = ({ assets, maxHeight = '270px' }: EmodeAssetTable
- {asset.symbol}
+ {asset.symbol}
diff --git a/src/components/transactions/Emode/EmodeModalContent.tsx b/src/components/transactions/Emode/EmodeModalContent.tsx
index 9133e59a8f..f6cae123ea 100644
--- a/src/components/transactions/Emode/EmodeModalContent.tsx
+++ b/src/components/transactions/Emode/EmodeModalContent.tsx
@@ -376,7 +376,7 @@ export const EmodeModalContent = ({ user }: { user: ExtendedFormattedUser }) =>
percent
visibleDecimals={2}
value={user.currentLoanToValue}
- variant="secondary12"
+ variant="subheader2"
/>
>
@@ -385,7 +385,7 @@ export const EmodeModalContent = ({ user }: { user: ExtendedFormattedUser }) =>
percent
visibleDecimals={2}
value={newSummary.currentLoanToValue}
- variant="secondary12"
+ variant="subheader2"
/>
@@ -400,12 +400,12 @@ export const EmodeModalContent = ({ user }: { user: ExtendedFormattedUser }) =>
-
+
Asset category
{selectedEmode.isolated && (
<>
-
+
-
width: '100%',
height: '44px',
borderRadius: '6px',
- borderColor: 'divider',
+ borderColor: 'border-2',
outline: 'none !important',
- color: 'text.primary',
+ color: 'fg-1',
'.MuiOutlinedInput-input': {
backgroundColor: 'transparent',
},
'&:hover .MuiOutlinedInput-notchedOutline, .MuiOutlinedInput-notchedOutline': {
- borderColor: 'divider',
+ borderColor: 'border-2',
outline: 'none !important',
borderWidth: '1px',
},
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
- borderColor: 'divider',
+ borderColor: 'border-2',
borderWidth: '1px',
},
- '.MuiSelect-icon': { color: 'text.primary' },
+ '.MuiSelect-icon': { color: 'fg-1' },
}}
value={selectedEmode.id}
onChange={(e) => selectEMode(Number(e.target.value))}
@@ -476,7 +476,7 @@ export const EmodeModalContent = ({ user }: { user: ExtendedFormattedUser }) =>
)}
{!emode.available && (
-
+
Unavailable
)}
@@ -528,7 +528,7 @@ export const EmodeModalContent = ({ user }: { user: ExtendedFormattedUser }) =>
)}
{!emode.available && (
-
+
Unavailable
)}
@@ -550,7 +550,7 @@ export const EmodeModalContent = ({ user }: { user: ExtendedFormattedUser }) =>
percent
visibleDecimals={2}
value={user.currentLoanToValue}
- variant="secondary12"
+ variant="subheader2"
/>
>
@@ -559,7 +559,7 @@ export const EmodeModalContent = ({ user }: { user: ExtendedFormattedUser }) =>
percent
visibleDecimals={2}
value={Number(selectedEmode.ltv) / 10000}
- variant="secondary12"
+ variant="subheader2"
/>
diff --git a/src/components/transactions/FlowCommons/BaseCancelled.tsx b/src/components/transactions/FlowCommons/BaseCancelled.tsx
index c0d0a568ff..5cb4cec877 100644
--- a/src/components/transactions/FlowCommons/BaseCancelled.tsx
+++ b/src/components/transactions/FlowCommons/BaseCancelled.tsx
@@ -14,7 +14,7 @@ export type BaseWaitingTxViewProps = {
};
const ExtLinkIcon = () => (
-
+
);
@@ -71,7 +71,7 @@ export const BaseCancelledView = ({
(
-
+
);
@@ -75,7 +75,7 @@ export const BaseSuccessView = ({
(
-
+
);
@@ -72,7 +72,7 @@ export const BaseWaitingView = ({
disabled={!txHash}
variant="outlined"
size="large"
- sx={{ borderRadius: 1, borderColor: 'divider', borderWidth: 1, mt: 6, mb: 2 }}
+ sx={{ borderRadius: 1, borderColor: 'border-2', borderWidth: 1, mt: 6, mb: 2 }}
href={
customExplorerLink
? customExplorerLink
diff --git a/src/components/transactions/FlowCommons/PermitNonceInfo.tsx b/src/components/transactions/FlowCommons/PermitNonceInfo.tsx
index 358680be90..d73c72acd6 100644
--- a/src/components/transactions/FlowCommons/PermitNonceInfo.tsx
+++ b/src/components/transactions/FlowCommons/PermitNonceInfo.tsx
@@ -14,7 +14,7 @@ export const PermitNonceInfo = () => {
>
-
+
Approve with
- You {action} {' '}
- {symbol}
+ You {action} {symbol}
)}
@@ -82,7 +82,7 @@ export const TxSuccessView = ({
{addToken && symbol && (
({
- border: theme.palette.mode === 'dark' ? `1px solid ${theme.palette.divider}` : 'none',
+ border: theme.palette.mode === 'dark' ? `1px solid ${figVars['border-2']}` : 'none',
background: theme.palette.mode === 'dark' ? 'none' : '#F7F7F9',
borderRadius: '12px',
display: 'flex',
@@ -97,7 +97,7 @@ export const TxSuccessView = ({
aToken={addToken && addToken.aToken ? true : false}
sx={{ fontSize: '32px', mt: '12px', mb: '8px' }}
/>
-
+
Add {addToken && addToken.aToken ? 'aToken ' : 'token '} to wallet to track your
balance.
diff --git a/src/components/transactions/FlowCommons/TxModalDetails.tsx b/src/components/transactions/FlowCommons/TxModalDetails.tsx
index 41600c9360..fedd768d92 100644
--- a/src/components/transactions/FlowCommons/TxModalDetails.tsx
+++ b/src/components/transactions/FlowCommons/TxModalDetails.tsx
@@ -16,6 +16,7 @@ import {
import { Row } from 'src/components/primitives/Row';
import { SecondsToString } from 'src/components/SecondsToString';
import { CollateralType } from 'src/helpers/types';
+import { figVars } from 'src/utils/figmaColors';
import { HealthFactorNumber } from '../../HealthFactorNumber';
import { IncentivesButton } from '../../incentives/IncentivesButton';
@@ -50,19 +51,19 @@ export const TxModalDetails: React.FC = ({
}) => {
return (
-
+
Transaction overview
({
+ sx={{
p: 3,
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
borderRadius: '4px',
'.MuiBox-root:last-of-type': {
mb: 0,
},
- })}
+ }}
>
{children}
@@ -108,11 +109,11 @@ export const DetailsNumberLine = ({
<>
{iconSymbol && }
{numberPrefix && {numberPrefix}}
-
+
{futureValue && (
<>
{ArrowRightIcon}
-
+
>
)}
>
@@ -165,9 +166,9 @@ export const DetailsNumberLineWithSub = ({
{value && (
<>
-
+
{!hideSymbolSuffix && (
-
+
{symbol}
)}
@@ -175,9 +176,9 @@ export const DetailsNumberLineWithSub = ({
>
)}
{tokenIcon && }
-
+
{!hideSymbolSuffix && (
-
+
{symbol}
)}
@@ -277,7 +278,7 @@ export const DetailsIncentivesLine = ({
{ArrowRightIcon}
{futureIncentives && futureIncentives.length === 0 && (
-
+
None
)}
@@ -317,7 +318,7 @@ export const DetailsHFLine = ({
) : (
<>
-
+
{visibleHfChange && (
<>
@@ -325,7 +326,7 @@ export const DetailsHFLine = ({
>
)}
@@ -333,7 +334,7 @@ export const DetailsHFLine = ({
)}
-
+
Liquidation at
{' <1.0'}
@@ -388,7 +389,7 @@ export const DetailsCooldownLine = ({
) : (
-
+
@@ -419,11 +420,11 @@ export const DetailsTextLine = ({
) : (
{compactedProps ? (
-
+
{text}
) : (
- {text}
+ {text}
)}
)}
diff --git a/src/components/transactions/FlowCommons/TxModalTitle.tsx b/src/components/transactions/FlowCommons/TxModalTitle.tsx
index 702ed90ee4..e7a712f38c 100644
--- a/src/components/transactions/FlowCommons/TxModalTitle.tsx
+++ b/src/components/transactions/FlowCommons/TxModalTitle.tsx
@@ -1,14 +1,27 @@
-import { Typography } from '@mui/material';
+import { SxProps, Theme, Typography } from '@mui/material';
import { ReactNode } from 'react';
export type TxModalTitleProps = {
title: ReactNode;
symbol?: string;
+ sx?: SxProps;
};
-export const TxModalTitle = ({ title, symbol }: TxModalTitleProps) => {
+export const TxModalTitle = ({ title, symbol, sx }: TxModalTitleProps) => {
return (
-
+
{title} {symbol ?? ''}
);
diff --git a/src/components/transactions/FunCheckout/FunSupplyButton.tsx b/src/components/transactions/FunCheckout/FunSupplyButton.tsx
index 3e3ea7a4b4..6f77eb2074 100644
--- a/src/components/transactions/FunCheckout/FunSupplyButton.tsx
+++ b/src/components/transactions/FunCheckout/FunSupplyButton.tsx
@@ -66,7 +66,8 @@ export function FunSupplyButton({
)}
handleSupplyClick({
diff --git a/src/components/transactions/FunCheckout/FunkitCheckout.tsx b/src/components/transactions/FunCheckout/FunkitCheckout.tsx
index 4555a90013..f3e5334838 100644
--- a/src/components/transactions/FunCheckout/FunkitCheckout.tsx
+++ b/src/components/transactions/FunCheckout/FunkitCheckout.tsx
@@ -182,8 +182,10 @@ function InnerCheckout() {
const colorMode = muiTheme.palette.mode;
useEffect(() => {
- const persisted = (localStorage?.getItem('colorMode') as 'light' | 'dark') || colorMode;
- toggleTheme(persisted);
+ // `colorMode` (theme.palette.mode) is reactive under CssVarsProvider, so sync funkit to
+ // it directly. (Dropped the legacy `colorMode` localStorage read — MUI now persists the
+ // color scheme under its own key, so that value is stale.)
+ toggleTheme(colorMode);
}, [colorMode, toggleTheme]);
const beginSupply = async (reserve: FunSupplyReserve) => {
diff --git a/src/components/transactions/GasStation/GasStation.tsx b/src/components/transactions/GasStation/GasStation.tsx
index a118402650..af3eb7bbb4 100644
--- a/src/components/transactions/GasStation/GasStation.tsx
+++ b/src/components/transactions/GasStation/GasStation.tsx
@@ -96,7 +96,7 @@ export const GasStation: React.FC = ({
diff --git a/src/components/transactions/GovDelegation/DelegationTokenSelector.tsx b/src/components/transactions/GovDelegation/DelegationTokenSelector.tsx
index cce029e1e4..516f1626f5 100644
--- a/src/components/transactions/GovDelegation/DelegationTokenSelector.tsx
+++ b/src/components/transactions/GovDelegation/DelegationTokenSelector.tsx
@@ -69,7 +69,7 @@ export const TokenRow: React.FC = ({ symbol, amount }) => {
}
>
-
+
);
};
diff --git a/src/components/transactions/GovDelegation/GovDelegationModalContent.tsx b/src/components/transactions/GovDelegation/GovDelegationModalContent.tsx
index 9b3065ef8f..5aeaefcb36 100644
--- a/src/components/transactions/GovDelegation/GovDelegationModalContent.tsx
+++ b/src/components/transactions/GovDelegation/GovDelegationModalContent.tsx
@@ -166,7 +166,7 @@ export const GovDelegationModalContent: React.FC
(powers.aavePropositionDelegatee === constants.AddressZero &&
powers.stkAavePropositionDelegatee === constants.AddressZero))) || (
<>
-
+
{isRevokeModal ? 'Power to revoke' : 'Power to delegate'}
)}
{isRevokeModal ? (
-
+
Balance to revoke
) : (
/>
{!isRevokeModal && (
<>
-
+
Recipient address
(
({
- border: reps[i].remove
- ? `1px solid ${theme.palette.action.active}`
- : '1px solid transparent',
+ sx={{
+ border: reps[i].remove ? `1px solid ${figVars['fg-3']}` : '1px solid transparent',
borderRadius: '8px',
- background: reps[i].remove ? theme.palette.background.surface : 'transparent',
- })}
+ background: reps[i].remove ? figVars['bg-2'] : 'transparent',
+ }}
>
@@ -123,7 +122,7 @@ export const GovRepresentativesContent = ({
width="16px"
alt="network logo"
/>
-
+
{networkConfigs[r.chainId].name}
diff --git a/src/components/transactions/MigrateV3/MigrateV3ModalAssetsList.tsx b/src/components/transactions/MigrateV3/MigrateV3ModalAssetsList.tsx
index e2ea27f3b5..0f999b094a 100644
--- a/src/components/transactions/MigrateV3/MigrateV3ModalAssetsList.tsx
+++ b/src/components/transactions/MigrateV3/MigrateV3ModalAssetsList.tsx
@@ -35,8 +35,8 @@ export const MigrateV3ModalAssetsList = ({ caption, assets }: MigrateV3ModalAsse
>
-
-
+
+
{asset.symbol}
@@ -45,7 +45,7 @@ export const MigrateV3ModalAssetsList = ({ caption, assets }: MigrateV3ModalAsse
variant="helperText"
compact
symbol="USD"
- color="text.secondary"
+ color="fg-2"
/>
) : (
diff --git a/src/components/transactions/MigrateV3/MigrateV3ModalContent.tsx b/src/components/transactions/MigrateV3/MigrateV3ModalContent.tsx
index fc3d4c65dc..b24159ac2d 100644
--- a/src/components/transactions/MigrateV3/MigrateV3ModalContent.tsx
+++ b/src/components/transactions/MigrateV3/MigrateV3ModalContent.tsx
@@ -117,7 +117,7 @@ export const MigrateV3ModalContent = ({
-
+
Go to V3 Dashboard
diff --git a/src/components/transactions/NetworkSelect.tsx b/src/components/transactions/NetworkSelect.tsx
index a76296d51e..e13501b421 100644
--- a/src/components/transactions/NetworkSelect.tsx
+++ b/src/components/transactions/NetworkSelect.tsx
@@ -8,6 +8,7 @@ import {
Typography,
} from '@mui/material';
import * as React from 'react';
+import { figVars } from 'src/utils/figmaColors';
import { SupportedNetworkWithChainId } from './Bridge/BridgeConfig';
@@ -39,14 +40,14 @@ export const NetworkSelect = ({
return (
({
+ sx={{
p: '8px 0px',
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
borderRadius: '6px',
mb: 1,
- })}
+ }}
>
-
+
Network
diff --git a/src/components/transactions/Repay/RepayTypeSelector.tsx b/src/components/transactions/Repay/RepayTypeSelector.tsx
index f4d4f0b36c..75dc620d7e 100644
--- a/src/components/transactions/Repay/RepayTypeSelector.tsx
+++ b/src/components/transactions/Repay/RepayTypeSelector.tsx
@@ -24,7 +24,7 @@ export function RepayTypeSelector({
if (!currentMarketData.enabledFeatures?.collateralRepay) return null;
return (
-
+
Repay with
diff --git a/src/components/transactions/StakeCooldown/StakeCooldownModalContent.tsx b/src/components/transactions/StakeCooldown/StakeCooldownModalContent.tsx
index b3b9437a24..f4c358d512 100644
--- a/src/components/transactions/StakeCooldown/StakeCooldownModalContent.tsx
+++ b/src/components/transactions/StakeCooldown/StakeCooldownModalContent.tsx
@@ -193,12 +193,12 @@ export const StakeCooldownModalContent = ({ stakeAssetName, icon }: StakeCooldow
pb: '30px',
}}
>
-
+
Amount to unstake
-
+
@@ -212,18 +212,18 @@ export const StakeCooldownModalContent = ({ stakeAssetName, icon }: StakeCooldow
pb: '30px',
}}
>
-
+
Unstake window
-
+
{dateMessage(stakeCooldownSeconds)}
-
+
{dateMessage(stakeCooldownSeconds + stakeUnstakeWindow)}
diff --git a/src/components/transactions/StakingMigrate/StakingMigrateModalContent.tsx b/src/components/transactions/StakingMigrate/StakingMigrateModalContent.tsx
index 2fa61e5ac7..62fb9f6779 100644
--- a/src/components/transactions/StakingMigrate/StakingMigrateModalContent.tsx
+++ b/src/components/transactions/StakingMigrate/StakingMigrateModalContent.tsx
@@ -112,15 +112,15 @@ const TxDetailsRow = ({ caption, value, valueUSD }: TxDetailsRowProps) => {
-
+
diff --git a/src/components/transactions/Supply/CollateralOptionsSelector.tsx b/src/components/transactions/Supply/CollateralOptionsSelector.tsx
index 95f1dd6fad..a95b8f1ab4 100644
--- a/src/components/transactions/Supply/CollateralOptionsSelector.tsx
+++ b/src/components/transactions/Supply/CollateralOptionsSelector.tsx
@@ -21,6 +21,7 @@ import {
ComputedReserveData,
ExtendedFormattedUser,
} from 'src/hooks/app-data-provider/useAppDataProvider';
+import { figVars } from 'src/utils/figmaColors';
export interface CollateralOption {
emodeId: number; // 0 = default (no e-mode), >0 = specific e-mode category
@@ -256,7 +257,7 @@ export const CollateralOptionsSelector = React.memo(
return (
-
+
Collateral options
@@ -276,7 +277,7 @@ export const CollateralOptionsSelector = React.memo(
display: 'block',
textAlign: 'left',
border: `1px solid ${
- isSelected ? theme.palette.primary.main : theme.palette.divider
+ isSelected ? theme.vars.palette.primary.main : figVars['border-2']
}`,
borderRadius: '8px',
cursor: option.blocked ? 'not-allowed' : 'pointer',
@@ -284,8 +285,8 @@ export const CollateralOptionsSelector = React.memo(
opacity: option.blocked ? 0.5 : 1,
'&:hover': {
borderColor: option.blocked
- ? theme.palette.divider
- : theme.palette.primary.main,
+ ? figVars['border-2']
+ : theme.vars.palette.primary.main,
},
transition: 'all 0.15s ease',
})}
@@ -298,7 +299,7 @@ export const CollateralOptionsSelector = React.memo(
height: 16,
borderRadius: '50%',
border: `2px solid ${
- isSelected ? theme.palette.primary.main : theme.palette.divider
+ isSelected ? theme.vars.palette.primary.main : figVars['border-2']
}`,
display: 'flex',
alignItems: 'center',
@@ -321,9 +322,9 @@ export const CollateralOptionsSelector = React.memo(
percent
visibleDecimals={0}
variant="subheader1"
- color="text.primary"
+ color="fg-1"
/>
-
+
LTV
{option.isCurrentEmode && (
@@ -348,7 +349,7 @@ export const CollateralOptionsSelector = React.memo(
px: 1,
py: 0.25,
borderRadius: '4px',
- bgcolor: 'text.secondary',
+ bgcolor: 'fg-2',
color: '#fff',
fontSize: '10px',
}}
@@ -389,7 +390,7 @@ export const CollateralOptionsSelector = React.memo(
Borrow:} assets={option.borrowableAssets} />
)}
{option.emodeId === 0 && (
-
+
Borrow: All eligible assets
)}
@@ -439,7 +440,7 @@ export const CollateralOptionsSelector = React.memo(
expandIcon={}
sx={{ px: 0, minHeight: 'auto', '& .MuiAccordionSummary-content': { my: 1 } }}
>
-
+
Category assets
@@ -463,19 +464,19 @@ const AssetRow = ({
assets: Array<{ symbol: string; iconSymbol: string }>;
}) => (
-
+
{label}
{assets.slice(0, MAX_VISIBLE_ASSETS).map((asset) => (
-
+
{asset.symbol}
))}
{assets.length > MAX_VISIBLE_ASSETS && (
-
+
+{assets.length - MAX_VISIBLE_ASSETS}
)}
diff --git a/src/components/transactions/Supply/SupplyModalContent.tsx b/src/components/transactions/Supply/SupplyModalContent.tsx
index d049b37372..4b0165b2a9 100644
--- a/src/components/transactions/Supply/SupplyModalContent.tsx
+++ b/src/components/transactions/Supply/SupplyModalContent.tsx
@@ -681,10 +681,10 @@ const ExchangeRate = ({
-
+
sDAI
>
diff --git a/src/components/transactions/Swap/details/CollateralSwapDetails.tsx b/src/components/transactions/Swap/details/CollateralSwapDetails.tsx
index a69dc2cdb5..be2a616a13 100644
--- a/src/components/transactions/Swap/details/CollateralSwapDetails.tsx
+++ b/src/components/transactions/Swap/details/CollateralSwapDetails.tsx
@@ -223,7 +223,7 @@ export const ColalteralSwapDetails = ({ state }: { params: SwapParams; state: Sw
/>
+
{sourceAmountAfterSwap.toString()} {userReserve.reserve.symbol}
}
@@ -235,7 +235,7 @@ export const ColalteralSwapDetails = ({ state }: { params: SwapParams; state: Sw
@@ -248,8 +248,8 @@ export const ColalteralSwapDetails = ({ state }: { params: SwapParams; state: Sw
variant="helperText"
compact
symbol="USD"
- symbolsColor="text.secondary"
- color="text.secondary"
+ symbolsColor="fg-2"
+ color="fg-2"
/>
>
)}
@@ -275,7 +275,7 @@ export const ColalteralSwapDetails = ({ state }: { params: SwapParams; state: Sw
/>
+
{targetAmountAfterSwap.toString()} {userTargetReserve.reserve.symbol}
}
@@ -287,7 +287,7 @@ export const ColalteralSwapDetails = ({ state }: { params: SwapParams; state: Sw
@@ -300,8 +300,8 @@ export const ColalteralSwapDetails = ({ state }: { params: SwapParams; state: Sw
variant="helperText"
compact
symbol="USD"
- symbolsColor="text.secondary"
- color="text.secondary"
+ symbolsColor="fg-2"
+ color="fg-2"
/>
>
)}
diff --git a/src/components/transactions/Swap/details/CowCostsDetails.tsx b/src/components/transactions/Swap/details/CowCostsDetails.tsx
index a04d52d784..3365efdc84 100644
--- a/src/components/transactions/Swap/details/CowCostsDetails.tsx
+++ b/src/components/transactions/Swap/details/CowCostsDetails.tsx
@@ -173,15 +173,15 @@ export const CowCostsDetails = ({ state }: { state: SwapState }) => {
width="16px"
sx={{ mr: 2, ml: 4, fontSize: '16px' }}
/>
-
+
@@ -210,15 +210,15 @@ export const CowCostsDetails = ({ state }: { state: SwapState }) => {
width="16px"
sx={{ mr: 2, ml: 4, fontSize: '16px' }}
/>
-
+
@@ -240,15 +240,15 @@ export const CowCostsDetails = ({ state }: { state: SwapState }) => {
width="16px"
sx={{ mr: 2, ml: 4, fontSize: '16px' }}
/>
-
+
diff --git a/src/components/transactions/Swap/details/DebtSwapDetails.tsx b/src/components/transactions/Swap/details/DebtSwapDetails.tsx
index 0e107ddf3b..f1a17ba631 100644
--- a/src/components/transactions/Swap/details/DebtSwapDetails.tsx
+++ b/src/components/transactions/Swap/details/DebtSwapDetails.tsx
@@ -61,13 +61,13 @@ export const DebtSwapDetails = ({
<>
{ArrowRightIcon}
>
@@ -109,7 +109,7 @@ export const DebtSwapDetails = ({
/>
+
{sourceAmountAfterSwap.toString()} {state.sourceReserve.reserve.symbol}
}
@@ -121,7 +121,7 @@ export const DebtSwapDetails = ({
@@ -132,8 +132,8 @@ export const DebtSwapDetails = ({
variant="helperText"
compact
symbol="USD"
- symbolsColor="text.secondary"
- color="text.secondary"
+ symbolsColor="fg-2"
+ color="fg-2"
/>
>
)}
@@ -159,7 +159,7 @@ export const DebtSwapDetails = ({
/>
+
{targetAmountAfterSwap.toString()} {state.destinationReserve.reserve.symbol}
}
@@ -171,7 +171,7 @@ export const DebtSwapDetails = ({
@@ -182,8 +182,8 @@ export const DebtSwapDetails = ({
variant="helperText"
compact
symbol="USD"
- symbolsColor="text.secondary"
- color="text.secondary"
+ symbolsColor="fg-2"
+ color="fg-2"
/>
>
)}
diff --git a/src/components/transactions/Swap/details/ParaswapCostsDetails.tsx b/src/components/transactions/Swap/details/ParaswapCostsDetails.tsx
index ae0f632b10..d83f97a685 100644
--- a/src/components/transactions/Swap/details/ParaswapCostsDetails.tsx
+++ b/src/components/transactions/Swap/details/ParaswapCostsDetails.tsx
@@ -48,15 +48,15 @@ export const ParaswapCostsDetails = ({ state }: { state: SwapState }) => {
width="16px"
sx={{ mr: 2, ml: 4, fontSize: '16px' }}
/>
-
+
diff --git a/src/components/transactions/Swap/details/SwapDetails.tsx b/src/components/transactions/Swap/details/SwapDetails.tsx
index f5e41c6cc5..5f8db1368b 100644
--- a/src/components/transactions/Swap/details/SwapDetails.tsx
+++ b/src/components/transactions/Swap/details/SwapDetails.tsx
@@ -138,7 +138,7 @@ export const IntentTxDetails = ({
/>
+
{buyAmount} {buyToken.symbol}
}
@@ -148,7 +148,7 @@ export const IntentTxDetails = ({
leaveTouchDelay={500}
>
-
+
@@ -158,15 +158,15 @@ export const IntentTxDetails = ({
variant="helperText"
compact
symbol="USD"
- symbolsColor="text.secondary"
- color="text.secondary"
+ symbolsColor="fg-2"
+ color="fg-2"
roundDown={true}
/>
{priceImpact && priceImpact > 0 && priceImpact < 100 && (
10 ? 'error' : priceImpact > 5 ? 'warning' : 'text.secondary'}
+ color={priceImpact > 10 ? 'error' : priceImpact > 5 ? 'warning' : 'fg-2'}
>
(-{priceImpact.toFixed(priceImpact > 3 ? 0 : priceImpact > 1 ? 1 : 2)}%)
@@ -215,7 +215,7 @@ const MarketOrderTxDetails = ({
/>
+
{buyAmount} {buyToken.symbol}
}
@@ -225,12 +225,7 @@ const MarketOrderTxDetails = ({
leaveTouchDelay={500}
>
-
+
@@ -239,8 +234,8 @@ const MarketOrderTxDetails = ({
variant="helperText"
compact
symbol="USD"
- symbolsColor="text.secondary"
- color="text.secondary"
+ symbolsColor="fg-2"
+ color="fg-2"
roundDown={true}
/>
diff --git a/src/components/transactions/Swap/errors/shared/GenericError.tsx b/src/components/transactions/Swap/errors/shared/GenericError.tsx
index 89fd15b888..5047c4c34a 100644
--- a/src/components/transactions/Swap/errors/shared/GenericError.tsx
+++ b/src/components/transactions/Swap/errors/shared/GenericError.tsx
@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro';
-import { ContentCopy } from '@mui/icons-material';
+import ContentCopy from '@mui/icons-material/ContentCopy';
import { IconButton, SxProps, Tooltip, Typography } from '@mui/material';
import React, { useState } from 'react';
import { Warning } from 'src/components/primitives/Warning';
diff --git a/src/components/transactions/Swap/inputs/LimitOrderInputs.tsx b/src/components/transactions/Swap/inputs/LimitOrderInputs.tsx
index dc5ddf9beb..d4175092fc 100644
--- a/src/components/transactions/Swap/inputs/LimitOrderInputs.tsx
+++ b/src/components/transactions/Swap/inputs/LimitOrderInputs.tsx
@@ -67,7 +67,7 @@ export const LimitOrderInputs = ({
{(inputInputTitle || swapState.showNetworkSelector) && (
{inputInputTitle && (
-
+
{inputInputTitle}
)}
@@ -146,14 +146,14 @@ export const LimitOrderInputs = ({
disabled={!(customProps?.canSwitchTokens ?? false)}
sx={{
border: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-2',
transform: 'translateY(-130%)',
- backgroundColor: 'background.paper',
- '&:hover': { backgroundColor: 'background.surface' },
+ backgroundColor: 'surface-elevated',
+ '&:hover': { backgroundColor: 'bg-2' },
'&:disabled': {
- backgroundColor: 'background.surface',
+ backgroundColor: 'bg-2',
opacity: '0.7',
- color: 'text.secondary',
+ color: 'fg-2',
},
}}
>
@@ -187,13 +187,13 @@ export const LimitOrderInputs = ({
disableFocusRipple
sx={{
border: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-2',
transform: 'translateY(-130%)',
- backgroundColor: 'background.paper',
+ backgroundColor: 'surface-elevated',
'&:disabled': {
- backgroundColor: 'background.paper',
+ backgroundColor: 'surface-elevated',
},
- '&:hover': { backgroundColor: 'background.paper' },
+ '&:hover': { backgroundColor: 'surface-elevated' },
}}
>
{params.inputInputTitle && (
-
+
{params.inputInputTitle}
)}
@@ -114,13 +114,13 @@ export const MarketOrderInputs = ({
disabled={!(customProps?.canSwitchTokens ?? false)}
sx={{
border: '1px solid',
- borderColor: 'divider',
- backgroundColor: 'background.paper',
- '&:hover': { backgroundColor: 'background.surface' },
+ borderColor: 'border-2',
+ backgroundColor: 'surface-elevated',
+ '&:hover': { backgroundColor: 'bg-2' },
'&:disabled': {
- backgroundColor: 'background.surface',
+ backgroundColor: 'bg-2',
opacity: '0.7',
- color: 'text.secondary',
+ color: 'fg-2',
},
}}
>
@@ -151,12 +151,12 @@ export const MarketOrderInputs = ({
disableFocusRipple
sx={{
border: '1px solid',
- borderColor: 'divider',
- backgroundColor: 'background.paper',
+ borderColor: 'border-2',
+ backgroundColor: 'surface-elevated',
'&:disabled': {
- backgroundColor: 'background.paper',
+ backgroundColor: 'surface-elevated',
},
- '&:hover': { backgroundColor: 'background.paper' },
+ '&:hover': { backgroundColor: 'surface-elevated' },
}}
>
{title && (
-
+
{title}
)}
({
- border: `1px solid ${theme.palette.divider}`,
+ sx={{
+ border: `1px solid ${figVars['border-2']}`,
borderRadius: '6px',
overflow: 'hidden',
px: 3,
@@ -284,11 +285,11 @@ export const SwitchAssetInput = ({
? {
transition: 'background-color 0.15s ease',
'&:hover': {
- backgroundColor: 'background.surface',
+ backgroundColor: 'bg-2',
},
}
: {}),
- })}
+ }}
>
{loading ? (
@@ -334,9 +335,9 @@ export const SwitchAssetInput = ({
p: 0,
left: 8,
zIndex: 1,
- color: 'text.muted',
+ color: 'fg-3',
'&:hover': {
- color: 'text.secondary',
+ color: 'fg-2',
},
}}
onClick={() => {
@@ -372,8 +373,8 @@ export const SwitchAssetInput = ({
/>
{selectedAsset.symbol}
@@ -399,7 +400,7 @@ export const SwitchAssetInput = ({
)}
>
-
+
3 && mergedPopular.length > 0
- ? `1px solid ${theme.palette.divider}`
+ ? `1px solid ${figVars['border-2']}`
: 'none',
position: 'sticky',
top: 0,
zIndex: 2,
mb: 3,
pb: 3,
- backgroundColor: theme.palette.background.paper,
+ backgroundColor: 'surface-elevated',
boxShadow: '0px 4px 6px -6px rgba(0, 0, 0, 0.1)',
marginTop: -3,
paddingTop: 3,
@@ -494,7 +495,7 @@ export const SwitchAssetInput = ({
>
{popularSectionTitle}
@@ -509,11 +510,11 @@ export const SwitchAssetInput = ({
p: 1,
borderRadius: '16px',
border: '1px solid',
- borderColor: theme.palette.divider,
+ borderColor: 'border-2',
cursor: 'pointer',
transition: 'all 0.2s ease',
'&:hover': {
- backgroundColor: theme.palette.divider,
+ backgroundColor: 'border-2',
},
}}
onClick={() => handleSelect(asset)}
@@ -523,7 +524,7 @@ export const SwitchAssetInput = ({
symbol={asset.symbol}
sx={{ fontSize: '24px', mr: 1 }}
/>
-
+
{asset.symbol}
@@ -543,11 +544,11 @@ export const SwitchAssetInput = ({
background: 'transparent',
},
'&::-webkit-scrollbar-thumb': {
- background: theme.palette.divider,
+ background: figVars['border-2'],
borderRadius: '4px',
},
'&::-webkit-scrollbar-thumb:hover': {
- background: theme.palette.action.hover,
+ background: figVars['button-hover'],
},
}}
>
@@ -590,7 +591,7 @@ export const SwitchAssetInput = ({
sx={{ mr: 2 }}
/>
-
+
{asset.name || asset.symbol}
@@ -610,21 +611,21 @@ export const SwitchAssetInput = ({
'&:hover .launch-icon-text': {
color:
theme.palette.mode === 'dark'
- ? theme.palette.primary.light
- : theme.palette.primary.main,
+ ? theme.vars.palette.primary.light
+ : theme.vars.palette.primary.main,
},
'&:hover .launch-icon-svg': {
color:
theme.palette.mode === 'dark'
- ? theme.palette.primary.light
- : theme.palette.primary.main,
+ ? theme.vars.palette.primary.light
+ : theme.vars.palette.primary.main,
},
}}
>
{textCenterEllipsis(
@@ -635,7 +636,7 @@ export const SwitchAssetInput = ({
@@ -645,7 +646,7 @@ export const SwitchAssetInput = ({
if (!apy) return null;
return (
<>
-
+
{' • '}
@@ -654,7 +655,7 @@ export const SwitchAssetInput = ({
value={apy.value}
percent
variant="caption"
- color="text.secondary"
+ color="fg-2"
/>
@@ -679,8 +680,8 @@ export const SwitchAssetInput = ({
)}
@@ -701,8 +702,8 @@ export const SwitchAssetInput = ({
compact
symbol="USD"
variant="helperText"
- color="text.secondary"
- symbolsColor="text.secondary"
+ color="fg-2"
+ symbolsColor="fg-2"
sx={{ textAlign: 'right' }}
/>
@@ -712,8 +713,8 @@ export const SwitchAssetInput = ({
))
) : (
{allowCustomTokens ? (
@@ -738,23 +739,23 @@ export const SwitchAssetInput = ({
value={isNaN(Number(usdValue)) ? 0 : Number(usdValue)}
compact
symbol="USD"
- variant="secondary12"
- color="text.muted"
- symbolsColor="text.muted"
+ variant="subheader2"
+ color="fg-3"
+ symbolsColor="fg-3"
flexGrow={1}
/>
)}
{showBalance && selectedAsset.balance && (
<>
-
+
{balanceTitle || 'Balance'}
@@ -834,9 +835,9 @@ const PercentSelector = ({
PaperProps={{
sx: {
p: 1,
- backgroundColor: 'background.surface',
+ backgroundColor: 'bg-2',
border: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-2',
},
}}
>
@@ -844,9 +845,9 @@ const PercentSelector = ({
v && handlePick(v)}
>
@@ -860,7 +861,7 @@ const PercentSelector = ({
px: 1,
borderWidth: 2,
'&.Mui-selected': {
- backgroundColor: 'background.paper',
+ backgroundColor: 'surface-elevated',
},
}}
>
diff --git a/src/components/transactions/Swap/inputs/shared/ExpirySelector.tsx b/src/components/transactions/Swap/inputs/shared/ExpirySelector.tsx
index feaef1dedc..bea67f6945 100644
--- a/src/components/transactions/Swap/inputs/shared/ExpirySelector.tsx
+++ b/src/components/transactions/Swap/inputs/shared/ExpirySelector.tsx
@@ -1,14 +1,5 @@
-import { ChevronDownIcon } from '@heroicons/react/outline';
import { Trans } from '@lingui/macro';
-import {
- Box,
- FormControl,
- MenuItem,
- Select,
- SelectChangeEvent,
- SvgIcon,
- Typography,
-} from '@mui/material';
+import { Box, FormControl, MenuItem, Select, SelectChangeEvent, Typography } from '@mui/material';
import { Expiry } from '../../types';
@@ -25,7 +16,7 @@ export const ExpirySelector = ({ selectedExpiry, setSelectedExpiry }: ExpirySele
@@ -37,11 +28,6 @@ export const ExpirySelector = ({ selectedExpiry, setSelectedExpiry }: ExpirySele
native={false}
value={String(selectedExpiry)}
onChange={handleChange}
- IconComponent={(props) => (
-
-
-
- )}
sx={{
'&.MuiInputBase-root': {
border: 0,
@@ -66,7 +52,7 @@ export const ExpirySelector = ({ selectedExpiry, setSelectedExpiry }: ExpirySele
marginRight: -1.5,
}}
>
-
+
{value}
diff --git a/src/components/transactions/Swap/inputs/shared/NetworkSelector.tsx b/src/components/transactions/Swap/inputs/shared/NetworkSelector.tsx
index 5ce1bb177e..a3bae56746 100644
--- a/src/components/transactions/Swap/inputs/shared/NetworkSelector.tsx
+++ b/src/components/transactions/Swap/inputs/shared/NetworkSelector.tsx
@@ -1,13 +1,4 @@
-import { ChevronDownIcon } from '@heroicons/react/outline';
-import {
- Box,
- FormControl,
- MenuItem,
- Select,
- SelectChangeEvent,
- SvgIcon,
- Typography,
-} from '@mui/material';
+import { Box, FormControl, MenuItem, Select, SelectChangeEvent, Typography } from '@mui/material';
import { MarketLogo } from 'src/components/MarketSwitcher';
import { SupportedNetworkWithChainId } from '../../helpers/shared/misc.helpers';
@@ -32,11 +23,6 @@ export const NetworkSelector = ({
native={false}
value={String(selectedNetwork)}
onChange={handleChange}
- IconComponent={(props) => (
-
-
-
- )}
sx={{
'&.MuiInputBase-root': {
border: 0,
@@ -61,7 +47,7 @@ export const NetworkSelector = ({
mr: 1,
}}
/>
-
+
{network.displayName || network.name}
diff --git a/src/components/transactions/Swap/inputs/shared/PriceInput.tsx b/src/components/transactions/Swap/inputs/shared/PriceInput.tsx
index 2548279bcd..f5c31f96b4 100644
--- a/src/components/transactions/Swap/inputs/shared/PriceInput.tsx
+++ b/src/components/transactions/Swap/inputs/shared/PriceInput.tsx
@@ -5,6 +5,7 @@ import React, { useEffect, useRef, useState } from 'react';
import NumberFormat, { NumberFormatProps } from 'react-number-format';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
import { ExternalTokenIcon } from 'src/components/primitives/TokenIcon';
+import { figVars } from 'src/utils/figmaColors';
import { SwappableToken, TokenType } from '../../types';
@@ -258,8 +259,8 @@ export const PriceInput = ({
return (
({
- border: `1px solid ${theme.palette.divider}`,
+ sx={{
+ border: `1px solid ${figVars['border-2']}`,
borderRadius: '6px',
overflow: 'hidden',
px: 3,
@@ -267,11 +268,11 @@ export const PriceInput = ({
width: '100%',
transition: 'background-color 0.15s ease',
'&:hover': {
- backgroundColor: 'background.surface',
+ backgroundColor: 'bg-2',
},
- })}
+ }}
>
-
+
When 1 {fromAsset.symbol} is worth:
@@ -330,8 +331,8 @@ export const PriceInput = ({
/>
{toAsset.symbol}
@@ -350,11 +351,11 @@ export const PriceInput = ({
width: 22,
height: 22,
borderRadius: '50%',
- backgroundColor: 'background.paper',
+ backgroundColor: 'surface-elevated',
ml: 1,
transition: 'background-color 0.2s ease',
'&:hover': {
- backgroundColor: 'background.surface',
+ backgroundColor: 'bg-2',
},
'&:hover .refresh-spin': {
transform: 'rotate(360deg)',
@@ -382,20 +383,20 @@ export const PriceInput = ({
value={rate.usd ? rate.usd.toString() : 0}
compact
symbol="USD"
- variant="secondary12"
- color="text.muted"
- symbolsColor="text.muted"
+ variant="subheader2"
+ color="fg-3"
+ symbolsColor="fg-3"
flexGrow={1}
/>
)}
-
+
diff --git a/src/components/transactions/Swap/inputs/shared/QuoteProgressRing.tsx b/src/components/transactions/Swap/inputs/shared/QuoteProgressRing.tsx
index cda959fde6..e9975ac497 100644
--- a/src/components/transactions/Swap/inputs/shared/QuoteProgressRing.tsx
+++ b/src/components/transactions/Swap/inputs/shared/QuoteProgressRing.tsx
@@ -1,5 +1,4 @@
import { Box, CircularProgress, SxProps } from '@mui/material';
-import { alpha, useTheme } from '@mui/material/styles';
import { useEffect, useMemo, useState } from 'react';
type QuoteProgressRingProps = {
@@ -21,7 +20,6 @@ export const QuoteProgressRing = ({
paused = false,
sx,
}: QuoteProgressRingProps) => {
- const theme = useTheme();
const [now, setNow] = useState(Date.now());
useEffect(() => {
@@ -41,8 +39,8 @@ export const QuoteProgressRing = ({
// Opacity from 0.25 to 1.0 based on progress
const ratio = Math.max(0, Math.min(1, progress / 100));
const opacity = 0.25 + 0.75 * ratio;
- return alpha(theme.palette.primary.main, opacity);
- }, [progress, theme]);
+ return `rgba(var(--mui-palette-primary-mainChannel) / ${opacity})`;
+ }, [progress]);
if (!active || !lastUpdatedAt || intervalMs <= 0) return null;
diff --git a/src/components/transactions/Swap/inputs/shared/SwitchRates.tsx b/src/components/transactions/Swap/inputs/shared/SwitchRates.tsx
index 9de8c9fe20..bc6e0698a2 100644
--- a/src/components/transactions/Swap/inputs/shared/SwitchRates.tsx
+++ b/src/components/transactions/Swap/inputs/shared/SwitchRates.tsx
@@ -52,8 +52,8 @@ export const SwitchRates = ({
visibleDecimals={0}
variant="main12"
symbol={isSwitched ? destSymbol : srcSymbol}
- symbolsVariant="secondary12"
- symbolsColor="text.secondary"
+ symbolsVariant="subheader2"
+ symbolsColor="fg-2"
value={'1'}
/>
diff --git a/src/components/transactions/Swap/inputs/shared/SwitchSlippageSelector.tsx b/src/components/transactions/Swap/inputs/shared/SwitchSlippageSelector.tsx
index 30799d5610..8e25b9c4e8 100644
--- a/src/components/transactions/Swap/inputs/shared/SwitchSlippageSelector.tsx
+++ b/src/components/transactions/Swap/inputs/shared/SwitchSlippageSelector.tsx
@@ -123,7 +123,7 @@ export const SwitchSlippageSelector = ({
return (
-
+
{isCustomSlippage ? (
Custom slippage
) : provider === 'paraswap' ? (
@@ -159,9 +159,9 @@ export const SwitchSlippageSelector = ({
handlePresetSlippageChange(value)}
@@ -175,7 +175,7 @@ export const SwitchSlippageSelector = ({
borderWidth: 2,
backgroundColor:
(suggestedSlippage === slippage && option == 'Auto') || option === slippage
- ? 'background.paper'
+ ? 'surface-elevated'
: 'transparent',
}}
value={option}
@@ -205,7 +205,7 @@ export const SwitchSlippageSelector = ({
placeholder="Custom"
endAdornment={
-
+
%
@@ -216,10 +216,8 @@ export const SwitchSlippageSelector = ({
width: '120px',
border: 1,
borderWidth: '1px',
- backgroundColor: 'background.surface',
- borderColor: slippageValidation
- ? `${slippageValidation.severity}.main`
- : 'background.surface',
+ backgroundColor: 'bg-2',
+ borderColor: slippageValidation ? `${slippageValidation.severity}.main` : 'bg-2',
borderRadius: '4px',
}}
/>
@@ -252,7 +250,7 @@ export const SwitchSlippageSelector = ({
>
{
>
) : (
-
+
Please connect your wallet to swap collateral.
close()} />
diff --git a/src/components/transactions/Swap/modals/DebtSwapModal.tsx b/src/components/transactions/Swap/modals/DebtSwapModal.tsx
index c684129b4d..d0ce49785c 100644
--- a/src/components/transactions/Swap/modals/DebtSwapModal.tsx
+++ b/src/components/transactions/Swap/modals/DebtSwapModal.tsx
@@ -25,7 +25,7 @@ export const DebtSwapModal = () => {
>
) : (
-
+
Please connect your wallet to swap debt.
close()} />
diff --git a/src/components/transactions/Swap/modals/SwapModal.tsx b/src/components/transactions/Swap/modals/SwapModal.tsx
index b17fe507f2..e51c353a72 100644
--- a/src/components/transactions/Swap/modals/SwapModal.tsx
+++ b/src/components/transactions/Swap/modals/SwapModal.tsx
@@ -23,7 +23,7 @@ export const SwapModal = () => {
>
) : (
-
+
Please connect your wallet to swap tokens.
close()} />
diff --git a/src/components/transactions/Swap/modals/request/NoEligibleAssetsToSwap.tsx b/src/components/transactions/Swap/modals/request/NoEligibleAssetsToSwap.tsx
index aac778659d..3584cb99d1 100644
--- a/src/components/transactions/Swap/modals/request/NoEligibleAssetsToSwap.tsx
+++ b/src/components/transactions/Swap/modals/request/NoEligibleAssetsToSwap.tsx
@@ -3,7 +3,7 @@ import { Typography } from '@mui/material';
export const NoEligibleAssetsToSwap = () => {
return (
-
+
No eligible assets to swap.
);
diff --git a/src/components/transactions/Swap/modals/result/CowOrderToast.tsx b/src/components/transactions/Swap/modals/result/CowOrderToast.tsx
index 930f25f093..a3360bc757 100644
--- a/src/components/transactions/Swap/modals/result/CowOrderToast.tsx
+++ b/src/components/transactions/Swap/modals/result/CowOrderToast.tsx
@@ -1,18 +1,16 @@
-import { useTheme } from '@mui/material';
import { Toaster } from 'sonner';
+import { figVars } from 'src/utils/figmaColors';
export const CowOrderToast = () => {
- const theme = useTheme();
-
return (
diff --git a/src/components/transactions/Swap/modals/result/SwapResultView.tsx b/src/components/transactions/Swap/modals/result/SwapResultView.tsx
index 83d7a734ad..f82b3665f3 100644
--- a/src/components/transactions/Swap/modals/result/SwapResultView.tsx
+++ b/src/components/transactions/Swap/modals/result/SwapResultView.tsx
@@ -59,17 +59,17 @@ export const SwapWithSurplusTooltip = ({
<>
- Base:
+ Base:
- Surplus: {' '}
- (
+ Surplus:{' '}
+ (
)
@@ -260,7 +260,7 @@ export const SwapTxSuccessView = ({
size={20}
sx={{
mr: 1,
- color: (theme) => theme.palette.grey[400],
+ color: (theme) => theme.vars.palette.grey[400],
}}
/>
Details will be available soon
@@ -276,7 +276,7 @@ export const SwapTxSuccessView = ({
customExplorerLinkText={customExplorerLinkText}
>
-
+
{provider === 'cowprotocol' ? (
<>
{orderStatus === 'open' ? (
@@ -301,17 +301,17 @@ export const SwapTxSuccessView = ({
-
+
{provider == 'cowprotocol' &&
((orderStatus == 'open' && !isNativeToken(symbol)) || orderStatus == 'failed')
? `${resultScreenTokensFromTitle ?? 'Send'}`
@@ -327,7 +327,7 @@ export const SwapTxSuccessView = ({
/>
+
{inAmount} {symbol}
}
@@ -345,14 +345,14 @@ export const SwapTxSuccessView = ({
-
+
{symbol}
-
+
{provider == 'cowprotocol' && (orderStatus == 'open' || orderStatus == 'failed')
? `${resultScreenTokensToTitle ?? 'Receive'}`
: `${resultScreenTokensToTitle ?? 'Received'}`}
@@ -367,7 +367,7 @@ export const SwapTxSuccessView = ({
/>
+
{outFinalAmount} {outSymbol}
}
@@ -385,7 +385,7 @@ export const SwapTxSuccessView = ({
-
+
{outSymbol}
@@ -394,7 +394,7 @@ export const SwapTxSuccessView = ({
{surplusDisplay}
@@ -403,15 +403,15 @@ export const SwapTxSuccessView = ({
-
+
Swap saved in your{' '}
{
+ const { currentAccount } = useWeb3Context();
+ const { setOpen } = useModal();
+
+ return (action: () => void) => {
+ if (!currentAccount) {
+ setOpen(true);
+ return;
+ }
+ action();
+ };
+};
diff --git a/src/hooks/usePinnedMarket.ts b/src/hooks/usePinnedMarket.ts
new file mode 100644
index 0000000000..7d259fdf85
--- /dev/null
+++ b/src/hooks/usePinnedMarket.ts
@@ -0,0 +1,20 @@
+import { useEffect } from 'react';
+import { useRootStore } from 'src/store/root';
+import { CustomMarket } from 'src/ui-config/marketsConfig';
+
+/**
+ * Pins the app's selected market to `market` for the lifetime of the calling page, restoring the
+ * user's prior market on unmount — so a page that must run on a single instance (e.g. staking /
+ * safety module on Core) can force it without a lasting global change. The header, lists, and tx
+ * modals all read the market from the store, so pinning here covers the whole page. No-op when
+ * already on `market`.
+ */
+export const usePinnedMarket = (market: CustomMarket) => {
+ useEffect(() => {
+ const { currentMarket: prevMarket, setCurrentMarket } = useRootStore.getState();
+ if (prevMarket !== market) {
+ setCurrentMarket(market, true); // true = don't touch the URL query param
+ return () => setCurrentMarket(prevMarket, true);
+ }
+ }, [market]);
+};
diff --git a/src/layouts/AppFooter.tsx b/src/layouts/AppFooter.tsx
index 604995cdb9..2da49a333b 100644
--- a/src/layouts/AppFooter.tsx
+++ b/src/layouts/AppFooter.tsx
@@ -1,9 +1,13 @@
import { Trans } from '@lingui/macro';
-import { GitHub, Instagram, LinkedIn, X } from '@mui/icons-material';
+import GitHub from '@mui/icons-material/GitHub';
+import Instagram from '@mui/icons-material/Instagram';
+import LinkedIn from '@mui/icons-material/LinkedIn';
+import X from '@mui/icons-material/X';
import { Box, styled, SvgIcon, Typography } from '@mui/material';
import { DuneIcon, TikTok } from 'public/icons/footer/icons';
import { Link } from 'src/components/primitives/Link';
import { useRootStore } from 'src/store/root';
+import { figVars } from 'src/utils/figmaColors';
import { useShallow } from 'zustand/shallow';
import DiscordIcon from '/public/icons/discord.svg';
@@ -13,14 +17,14 @@ interface StyledLinkProps {
onClick?: React.MouseEventHandler;
}
-const StyledLink = styled(Link)(({ theme }) => ({
- color: theme.palette.text.muted,
+const StyledLink = styled(Link)({
+ color: figVars['fg-3'],
'&:hover': {
- color: theme.palette.text.primary,
+ color: figVars['fg-1'],
},
display: 'flex',
alignItems: 'center',
-}));
+});
const FOOTER_ICONS = [
{
diff --git a/src/layouts/AppGlobalStyles.tsx b/src/layouts/AppGlobalStyles.tsx
index ca31f629ea..e6bbca809e 100644
--- a/src/layouts/AppGlobalStyles.tsx
+++ b/src/layouts/AppGlobalStyles.tsx
@@ -1,61 +1,43 @@
-import { useMediaQuery } from '@mui/material';
import CssBaseline from '@mui/material/CssBaseline';
-import { createTheme, ThemeProvider } from '@mui/material/styles';
-import { deepmerge } from '@mui/utils';
-import React, { ReactNode, useEffect, useMemo, useState } from 'react';
+import GlobalStyles from '@mui/material/GlobalStyles';
+import { Experimental_CssVarsProvider as CssVarsProvider } from '@mui/material/styles';
+import { ReactNode, useMemo } from 'react';
-import { getDesignTokens, getThemedComponents } from '../utils/theme';
-
-export const ColorModeContext = React.createContext({
- // eslint-disable-next-line @typescript-eslint/no-empty-function
- toggleColorMode: () => {},
-});
-
-type Mode = 'light' | 'dark';
+import { buildP3Overrides, createAppTheme } from '../utils/theme';
/**
- * Main Layout component which wrapps around the whole app
- * @param param0
- * @returns
+ * Main layout wrapper around the whole app. Provides the MUI theme via the CSS-variables
+ * engine: both color schemes are baked into CSS custom properties once, and light/dark is
+ * switched by toggling the `data-mui-color-scheme` attribute on (persisted by MUI,
+ * seeded from the OS preference). Components read/set the scheme via `useColorScheme()`.
*/
export function AppGlobalStyles({ children }: { children: ReactNode }) {
- const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
- const [mode, setMode] = useState(prefersDarkMode ? 'dark' : 'light');
- const colorMode = useMemo(
- () => ({
- toggleColorMode: () => {
- setMode((prevMode) => {
- const newMode = prevMode === 'light' ? 'dark' : 'light';
- localStorage.setItem('colorMode', newMode);
- return newMode;
- });
+ const theme = useMemo(() => createAppTheme(), []);
+
+ // Display-P3 layer: on wide-gamut displays that support the syntax, override the sRGB
+ // `--mui-palette-*` vars with their P3 equivalents. Everything else keeps the sRGB base.
+ const p3Styles = useMemo(() => {
+ const { light, dark } = buildP3Overrides(theme);
+ return {
+ '@supports (color: color(display-p3 1 1 1))': {
+ '@media (color-gamut: p3)': {
+ // Doubled selectors (specificity 0,2,0) beat MUI's own var sheets (0,1,0), so the
+ // P3 layer wins regardless of stylesheet source order — and still match both
+ // and the showcase's local `data-mui-color-scheme` wrapper.
+ ':root:root, [data-mui-color-scheme="light"][data-mui-color-scheme="light"]': light,
+ '[data-mui-color-scheme="dark"][data-mui-color-scheme="dark"]': dark,
+ },
},
- }),
- []
- );
-
- useEffect(() => {
- const initialMode = localStorage?.getItem('colorMode') as Mode;
- if (initialMode) {
- setMode(initialMode);
- } else if (prefersDarkMode) {
- setMode('dark');
- }
- }, []);
-
- const theme = useMemo(() => {
- const themeCreate = createTheme(getDesignTokens(mode));
- return deepmerge(themeCreate, getThemedComponents(themeCreate));
- }, [mode]);
+ };
+ }, [theme]);
return (
-
-
- {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
-
+
+ {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
+
+
- {children}
-
-
+ {children}
+
);
}
diff --git a/src/layouts/AppHeader.tsx b/src/layouts/AppHeader.tsx
index 9902de7bfb..4351c036d8 100644
--- a/src/layouts/AppHeader.tsx
+++ b/src/layouts/AppHeader.tsx
@@ -1,13 +1,10 @@
-import {
- InformationCircleIcon,
- SparklesIcon,
- SwitchHorizontalIcon,
-} from '@heroicons/react/outline';
+import { InformationCircleIcon } from '@heroicons/react/outline';
import { Trans } from '@lingui/macro';
import {
Badge,
Button,
CircularProgress,
+ Container,
NoSsr,
Slide,
styled,
@@ -22,17 +19,22 @@ import * as React from 'react';
import { useEffect, useState } from 'react';
import { AvatarSize } from 'src/components/Avatar';
import { ContentWithTooltip } from 'src/components/ContentWithTooltip';
+import { AaveLogo } from 'src/components/icons/AaveLogo';
+import { BridgeIcon } from 'src/components/icons/BridgeIcon';
+import { SwapIcon } from 'src/components/icons/SwapIcon';
import { UserDisplay } from 'src/components/UserDisplay';
import { ConnectWalletButton } from 'src/components/WalletConnection/ConnectWalletButton';
+import { useConnectGate } from 'src/hooks/useConnectGate';
import { useModalContext } from 'src/hooks/useModal';
import { useSwapOrdersTracking } from 'src/hooks/useSwapOrdersTracking';
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
import { useRootStore } from 'src/store/root';
+import { iconButtonSx } from 'src/utils/buttonStyles';
+import { figVars } from 'src/utils/figmaColors';
import { ENABLE_TESTNET, FORK_ENABLED, isFeatureEnabled } from 'src/utils/marketsAndNetworksConfig';
import { useShallow } from 'zustand/shallow';
import { Link } from '../components/primitives/Link';
-import { uiConfig } from '../uiConfig';
import { NavItems } from './components/NavItems';
import { MobileMenu } from './MobileMenu';
import { SettingsMenu } from './SettingsMenu';
@@ -48,8 +50,8 @@ const StyledBadge = styled(Badge)(({ theme }) => ({
borderRadius: '20px',
width: '10px',
height: '10px',
- backgroundColor: `${theme.palette.secondary.main}`,
- color: `${theme.palette.secondary.main}`,
+ backgroundColor: `${theme.vars.palette.secondary.main}`,
+ color: `${theme.vars.palette.secondary.main}`,
'&::after': {
position: 'absolute',
top: 0,
@@ -77,10 +79,11 @@ const StyledBadge = styled(Badge)(({ theme }) => ({
function HideOnScroll({ children }: Props) {
const { breakpoints } = useTheme();
const md = useMediaQuery(breakpoints.down('md'));
- const trigger = useScrollTrigger({ threshold: md ? 160 : 80 });
+ const trigger = useScrollTrigger({ threshold: 80 });
+ // Mobile keeps the header pinned (never hides on scroll); desktop still hides past the threshold.
return (
-
+
{children}
);
@@ -88,11 +91,21 @@ function HideOnScroll({ children }: Props) {
const SWITCH_VISITED_KEY = 'switchVisited';
+// Dev-only environment badges (testnet / fork) — intentionally off-brand magenta to stand out.
+const envBadgeSx = {
+ backgroundColor: '#B6509E',
+ '&:hover, &.Mui-focusVisible': { backgroundColor: 'rgba(182, 80, 158, 0.7)' },
+};
+
export function AppHeader() {
const { breakpoints } = useTheme();
const md = useMediaQuery(breakpoints.down('md'));
const sm = useMediaQuery(breakpoints.down('sm'));
- const smd = useMediaQuery('(max-width:1120px)');
+ const smd = useMediaQuery(breakpoints.down('mdlg'));
+ // Shared by the Swap + Bridge triggers: icon-only square when collapsed (smd), text otherwise.
+ const collapsingTriggerSx = smd
+ ? [iconButtonSx, { alignItems: 'center', '& .MuiButton-startIcon': { mx: 0 } }]
+ : { p: '0 0.88rem', minWidth: 'unset', alignItems: 'center' };
const [, setVisitedSwitch] = useState(() => {
if (typeof window === 'undefined') return true;
@@ -111,6 +124,7 @@ export function AppHeader() {
const { openSwitch, openBridge, openReadMode } = useModalContext();
const { readOnlyMode } = useWeb3Context();
+ const openOrConnect = useConnectGate();
const [walletWidgetOpen, setWalletWidgetOpen] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const { hasActiveOrders } = useSwapOrdersTracking();
@@ -125,7 +139,7 @@ export function AppHeader() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [md]);
- const headerHeight = 48;
+ const headerHeight = 72;
const toggleMobileMenu = (state: boolean) => {
if (md) setMobileDrawerOpen(state);
@@ -151,11 +165,11 @@ export function AppHeader() {
const handleSwitchClick = () => {
localStorage.setItem(SWITCH_VISITED_KEY, 'true');
setVisitedSwitch(true);
- openSwitch();
+ openOrConnect(openSwitch);
};
const handleBridgeClick = () => {
- openBridge();
+ openOrConnect(openBridge);
};
const testnetTooltip = (
@@ -204,171 +218,158 @@ export function AppHeader() {
top: 0,
transition: theme.transitions.create('top'),
zIndex: theme.zIndex.appBar,
- bgcolor: theme.palette.background.header,
- padding: {
- xs: mobileMenuOpen || walletWidgetOpen ? '8px 20px' : '8px 8px 8px 20px',
- xsm: '8px 20px',
- },
+ bgcolor: 'bg-2',
display: 'flex',
- alignItems: 'center',
- flexDirection: 'space-between',
- boxShadow: 'inset 0px -1px 0px rgba(242, 243, 247, 0.16)',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ boxShadow: `inset 0px -1px 0px ${figVars['border-0']}`,
})}
>
- setMobileMenuOpen(false)}
>
-
-
-
- {ENABLE_TESTNET && (
-
+ setMobileMenuOpen(false)}
+ >
+
+
+
+ {ENABLE_TESTNET && (
+
+
+ TESTNET
+
+
+
+
+
+ )}
+
+
+ {FORK_ENABLED && currentMarketData?.isFork && (
+
+
+ FORK
+
+
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ {hasActiveOrders ? (
+ theme.vars.palette.grey[200],
+ }}
+ />
+ ) : (
+
+ )}
+
+ }
+ sx={collapsingTriggerSx}
+ aria-label="Switch tool"
+ disabled={!showSwitchButton}
>
- TESTNET
-
-
-
+ {!smd && (
+
+ Swap
+
+ )}
-
- )}
-
-
- {FORK_ENABLED && currentMarketData?.isFork && (
-
+
+
+
+
+
}
+ sx={collapsingTriggerSx}
>
- FORK
-
-
-
+ {!smd && (
+
+ Bridge GHO
+
+ )}
-
- )}
-
-
-
-
-
-
-
+
+
-
-
-
- {!smd && (
-
- Bridge GHO
-
- )}
-
-
-
-
-
-
-
-
-
+ {readOnlyMode ? (
{
+ openReadMode();
+ }}
>
- {!smd && (
-
- Swap
-
- )}
-
- {hasActiveOrders ? (
- theme.palette.grey[200],
- }}
- />
- ) : (
-
-
-
- )}
-
+
-
-
-
- {readOnlyMode ? (
- {
- openReadMode();
- }}
- >
-
-
- ) : (
-
- )}
-
-
-
-
+ ) : (
+
+ )}
- {!walletWidgetOpen && (
-
-
+
+
- )}
+
+ {!walletWidgetOpen && (
+
+
+
+ )}
+
);
diff --git a/src/layouts/MainLayout.tsx b/src/layouts/MainLayout.tsx
index 784056ce1f..f9bbda7aaa 100644
--- a/src/layouts/MainLayout.tsx
+++ b/src/layouts/MainLayout.tsx
@@ -13,7 +13,7 @@ import TopBarNotify from './TopBarNotify';
const getCampaignConfigs = () => ({
[ChainId.base]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
@@ -24,7 +24,7 @@ const getCampaignConfigs = () => ({
[ChainId.sonic]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
@@ -36,7 +36,7 @@ const getCampaignConfigs = () => ({
[ChainId.mainnet]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
@@ -47,7 +47,7 @@ const getCampaignConfigs = () => ({
[ChainId.polygon]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
@@ -59,7 +59,7 @@ const getCampaignConfigs = () => ({
[ChainId.avalanche]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
@@ -71,7 +71,7 @@ const getCampaignConfigs = () => ({
[ChainId.arbitrum_one]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
@@ -83,7 +83,7 @@ const getCampaignConfigs = () => ({
[ChainId.optimism]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
@@ -95,7 +95,7 @@ const getCampaignConfigs = () => ({
[ChainId.xdai]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
@@ -107,7 +107,7 @@ const getCampaignConfigs = () => ({
[ChainId.bnb]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
@@ -119,7 +119,7 @@ const getCampaignConfigs = () => ({
[ChainId.ink]: {
notifyText: 'Aave V4 is now live on Ethereum mainnet.',
- buttonText: 'Try it out here',
+ buttonText: 'Try it out',
buttonAction: {
type: 'url' as const,
value: 'https://pro.aave.com/',
diff --git a/src/layouts/MobileMenu.tsx b/src/layouts/MobileMenu.tsx
index 711b0f0984..7bb34d3234 100644
--- a/src/layouts/MobileMenu.tsx
+++ b/src/layouts/MobileMenu.tsx
@@ -1,27 +1,17 @@
-import { MenuIcon } from '@heroicons/react/outline';
import { Trans } from '@lingui/macro';
-import { useLingui } from '@lingui/react';
-import {
- Box,
- Button,
- Divider,
- List,
- ListItem,
- ListItemIcon,
- ListItemText,
- SvgIcon,
- Typography,
-} from '@mui/material';
-import React, { ReactNode, useEffect, useState } from 'react';
+import { Box, Button, Divider, List, ListItem, ListItemText } from '@mui/material';
+import { useEffect, useState } from 'react';
+import { BridgeIcon } from 'src/components/icons/BridgeIcon';
+import { SwapIcon } from 'src/components/icons/SwapIcon';
+import { useConnectGate } from 'src/hooks/useConnectGate';
import { useModalContext } from 'src/hooks/useModal';
-import { PROD_ENV } from 'src/utils/marketsAndNetworksConfig';
+import { useRootStore } from 'src/store/root';
+import { figVars } from 'src/utils/figmaColors';
+import { isFeatureEnabled, PROD_ENV } from 'src/utils/marketsAndNetworksConfig';
-import { Link } from '../components/primitives/Link';
-import { moreNavigation } from '../ui-config/menu-items';
import { DarkModeSwitcher } from './components/DarkModeSwitcher';
import { DrawerWrapper } from './components/DrawerWrapper';
import { LanguageListItem, LanguagesList } from './components/LanguageSwitcher';
-import { MobileCloseButton } from './components/MobileCloseButton';
import { NavItems } from './components/NavItems';
import { ShieldSwitcher } from './components/ShieldSwitcher';
import { TestNetModeSwitcher } from './components/TestNetModeSwitcher';
@@ -32,97 +22,184 @@ interface MobileMenuProps {
headerHeight: number;
}
-const MenuItemsWrapper = ({ children, title }: { children: ReactNode; title: ReactNode }) => (
-
-
-
- {title}
-
+// The options scroll area: full-width so its scrollbar sits on the right edge, with 0.75rem inner
+// padding for the content.
+const scrollAreaSx = {
+ flex: 1,
+ minHeight: 0,
+ overflowY: 'auto',
+ px: '0.75rem',
+ pb: '3rem',
+} as const;
- {children}
-
+// Rows inside the drawer lists: 3rem tall, H3 label text, gutters zeroed so they align with the
+// scroll area's 0.75rem inset. Applied via sx so the shared row components (SettingSwitchRow,
+// LanguagesList) don't need to know about it.
+const menuListSx = {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: '0.5rem',
+ '& .MuiListItem-root': {
+ minHeight: '3rem',
+ borderRadius: '0.5rem',
+ px: 0,
+ cursor: 'pointer',
+ },
+ '& .MuiListItemText-primary': { fontSize: '1.125rem', fontWeight: 500, lineHeight: '120%' },
+};
+
+// The hamburger (three rounded lines, per the design SVG) that morphs into an X. Rendered inside
+// one fixed-size button (below), so toggling never resizes the button and shifts the header.
+// One bar of the hamburger; the three uses below add position + the open-state transform.
+const toggleBar = {
+ position: 'absolute' as const,
+ left: '4px',
+ width: '16px',
+ height: '2px',
+ borderRadius: '1px',
+ backgroundColor: 'currentColor',
+ transition: 'transform 0.2s ease, opacity 0.2s ease',
+};
-
+const MenuToggleIcon = ({ open }: { open: boolean }) => (
+
+
+
+
);
export const MobileMenu = ({ open, setOpen, headerHeight }: MobileMenuProps) => {
- const { i18n } = useLingui();
const [isLanguagesListOpen, setIsLanguagesListOpen] = useState(false);
- const { openReadMode } = useModalContext();
+ // Drives the top scrim: it only shows once the options actually scroll, so it never dims the
+ // first row at rest.
+ const [scrolled, setScrolled] = useState(false);
+ const { openReadMode, openSwitch, openBridge } = useModalContext();
+ const openOrConnect = useConnectGate();
+ const currentMarketData = useRootStore((store) => store.currentMarketData);
+ const showSwitchButton = isFeatureEnabled.switch(currentMarketData);
useEffect(() => setIsLanguagesListOpen(false), [open]);
+ // A fresh scroll area always starts at the top, so reset on open / view switch.
+ useEffect(() => setScrolled(false), [open, isLanguagesListOpen]);
const handleOpenReadMode = () => {
setOpen(false);
openReadMode();
};
+ const handleSwap = () => {
+ setOpen(false);
+ openOrConnect(openSwitch);
+ };
+
+ const handleBridge = () => {
+ setOpen(false);
+ openOrConnect(openBridge);
+ };
+
return (
<>
- {open ? (
-
- ) : (
- setOpen(true)}
- >
-
-
-
-
- )}
+ setOpen(!open)}
+ >
+
+
+ {/* Fade scrim over the top of the scroll area (mirrors the bottom scrim). Only shown once
+ scrolled, so it never dims the first row at rest. Inset from the top by the drawer's
+ padding (clean band under the header) and from the right so it never touches the scrollbar. */}
+
{!isLanguagesListOpen ? (
<>
- Menu}>
+ {/* Only the options scroll — the action buttons below stay pinned. */}
+ setScrolled(e.currentTarget.scrollTop > 0)}>
-
- Global settings}>
-
+
+ {/* Watch Wallet sits above the global-settings rows, no divider between them. */}
+
+
+
+ Watch Wallet
+
+
{PROD_ENV && }
setIsLanguagesListOpen(true)} />
-
- Links}>
-
-
-
- Watch wallet
-
-
+
- setOpen(false)}
+
+ {/* Fade scrim over the bottom of the scroll area, in place of a divider. */}
+
+
+ }
+ onClick={handleSwap}
+ disabled={!showSwitchButton}
>
-
- Migrate to Aave V3
-
-
- {moreNavigation.map((item, index) => (
-
-
- {item.icon}
-
-
- {i18n._(item.title)}
-
- ))}
-
-
+ Swap
+
+ }
+ onClick={handleBridge}
+ >
+ Bridge GHO
+
+
+
>
) : (
-
- setIsLanguagesListOpen(false)} />
-
+ setScrolled(e.currentTarget.scrollTop > 0)}>
+
+ setIsLanguagesListOpen(false)} />
+
+
)}
>
diff --git a/src/layouts/SettingsMenu.tsx b/src/layouts/SettingsMenu.tsx
index cd5a6a98f0..4b976b0a08 100644
--- a/src/layouts/SettingsMenu.tsx
+++ b/src/layouts/SettingsMenu.tsx
@@ -1,7 +1,7 @@
-import { CogIcon } from '@heroicons/react/solid';
import { Trans } from '@lingui/macro';
-import { Button, ListItemText, Menu, MenuItem, SvgIcon, Typography } from '@mui/material';
+import { Button, Divider, ListItemText, Menu, MenuItem } from '@mui/material';
import React, { useState } from 'react';
+import { SettingsIcon } from 'src/components/icons/SettingsIcon';
import { useModalContext } from 'src/hooks/useModal';
import { DEFAULT_LOCALE } from 'src/libs/LanguageProvider';
import { useRootStore } from 'src/store/root';
@@ -64,18 +64,16 @@ export function SettingsMenu() {
return (
<>
-
-
-
+
diff --git a/src/layouts/SupportModal.tsx b/src/layouts/SupportModal.tsx
index 21a8dfbdcf..92ac117735 100644
--- a/src/layouts/SupportModal.tsx
+++ b/src/layouts/SupportModal.tsx
@@ -203,20 +203,11 @@ export const SupportModal = () => {
) : (
-
+
Support
-
+
Let us know how we can help you. You may also consider joining our community
@@ -224,7 +215,7 @@ export const SupportModal = () => {
}
localStorageName="suppliedAssetsDashboardTableCollapse"
+ collapseLabel={t`your supplies`}
noData={!sortedReserves.length}
subChildrenComponent={
!!userHasSmallBalanceAssets && (
diff --git a/src/modules/dashboard/lists/SuppliedPositionsList/SuppliedPositionsListItem.tsx b/src/modules/dashboard/lists/SuppliedPositionsList/SuppliedPositionsListItem.tsx
index bcb6968fb5..d754ac6ab8 100644
--- a/src/modules/dashboard/lists/SuppliedPositionsList/SuppliedPositionsListItem.tsx
+++ b/src/modules/dashboard/lists/SuppliedPositionsList/SuppliedPositionsListItem.tsx
@@ -111,6 +111,7 @@ export const SuppliedPositionsListItem = ({
{showSwitchButton ? (
{
@@ -130,6 +131,7 @@ export const SuppliedPositionsListItem = ({
) : (
openSupply(underlyingAsset, currentMarket, reserve.name, 'dashboard')}
@@ -138,6 +140,7 @@ export const SuppliedPositionsListItem = ({
)}
{
diff --git a/src/modules/dashboard/lists/SuppliedPositionsList/SuppliedPositionsListMobileItem.tsx b/src/modules/dashboard/lists/SuppliedPositionsList/SuppliedPositionsListMobileItem.tsx
index ccf8856a20..fa32938ed9 100644
--- a/src/modules/dashboard/lists/SuppliedPositionsList/SuppliedPositionsListMobileItem.tsx
+++ b/src/modules/dashboard/lists/SuppliedPositionsList/SuppliedPositionsListMobileItem.tsx
@@ -95,7 +95,7 @@ export const SuppliedPositionsListMobileItem = ({
incentives={aIncentivesData}
address={aTokenAddress}
symbol={symbol}
- variant="secondary14"
+ variant="h5"
market={currentMarket}
protocolAction={ProtocolAction.supply}
/>
diff --git a/src/modules/dashboard/lists/SupplyAssetsList/SupplyAssetsList.tsx b/src/modules/dashboard/lists/SupplyAssetsList/SupplyAssetsList.tsx
index 12864d5841..ec0510b696 100644
--- a/src/modules/dashboard/lists/SupplyAssetsList/SupplyAssetsList.tsx
+++ b/src/modules/dashboard/lists/SupplyAssetsList/SupplyAssetsList.tsx
@@ -1,6 +1,6 @@
import { API_ETH_MOCK_ADDRESS } from '@aave/contract-helpers';
import { USD_DECIMALS, valueToBigNumber } from '@aave/math-utils';
-import { Trans } from '@lingui/macro';
+import { t, Trans } from '@lingui/macro';
import { Box, Typography, useMediaQuery, useTheme } from '@mui/material';
import { BigNumber } from 'bignumber.js';
import { Fragment, useState } from 'react';
@@ -292,7 +292,7 @@ export const SupplyAssetsList = () => {
width: '100%',
alignItems: 'center',
justifyContent: 'space-between',
- mr: 2,
+ mr: '0.62rem',
}}
>
@@ -310,6 +310,7 @@ export const SupplyAssetsList = () => {
}
onCollapseChange={setIsListCollapsed}
localStorageName="supplyAssetsDashboardTableCollapse"
+ collapseLabel={t`assets to supply`}
withTopMargin
noData={supplyDisabled}
subChildrenComponent={
diff --git a/src/modules/dashboard/lists/SupplyAssetsList/SupplyAssetsListItem.tsx b/src/modules/dashboard/lists/SupplyAssetsList/SupplyAssetsListItem.tsx
index 4f7023053a..6c6ce8e072 100644
--- a/src/modules/dashboard/lists/SupplyAssetsList/SupplyAssetsListItem.tsx
+++ b/src/modules/dashboard/lists/SupplyAssetsList/SupplyAssetsListItem.tsx
@@ -15,6 +15,7 @@ import {
} from '@mui/material';
import { useState } from 'react';
import { ContentWithTooltip } from 'src/components/ContentWithTooltip';
+import { DotsHorizontalIcon } from 'src/components/icons/DotsHorizontalIcon';
import { IncentivesCard } from 'src/components/incentives/IncentivesCard';
import { WrappedTokenTooltipContent } from 'src/components/infoTooltips/WrappedTokenToolTipContent';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
@@ -28,8 +29,10 @@ import { useAssetCaps } from 'src/hooks/useAssetCaps';
import { useModalContext } from 'src/hooks/useModal';
import { useWrappedTokens } from 'src/hooks/useWrappedTokens';
import { useRootStore } from 'src/store/root';
+import { iconButtonSx } from 'src/utils/buttonStyles';
import { DashboardReserve } from 'src/utils/dashboardSortUtils';
import { DASHBOARD } from 'src/utils/events';
+import { onAccent } from 'src/utils/figmaColors';
import { isFeatureEnabled } from 'src/utils/marketsAndNetworksConfig';
import { showExternalIncentivesTooltip } from 'src/utils/utils';
@@ -188,12 +191,7 @@ export const SupplyAssetsListItemDesktop = ({
justifyContent: 'center',
}}
>
-
+
@@ -237,7 +235,7 @@ export const SupplyAssetsListItemDesktop = ({
{debtCeiling.isMaxed ? (
-
+
) : (
}
+ >
+
+ Click me
+
+
+
+
+
+
+ Explanation of the metric goes here.
+
+
+
+);
diff --git a/src/modules/dev/ComponentShowcase/components/Section/index.tsx b/src/modules/dev/ComponentShowcase/components/Section/index.tsx
new file mode 100644
index 0000000000..b5f7333f72
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/components/Section/index.tsx
@@ -0,0 +1,37 @@
+import { Box, Typography } from '@mui/material';
+import { ReactNode } from 'react';
+import { figVars } from 'src/utils/figmaColors';
+
+interface SectionProps {
+ title: string;
+ description?: string;
+ children: ReactNode;
+}
+
+export const Section = ({ title, description, children }: SectionProps) => (
+
+
+
+ {title}
+
+ {description && (
+
+ {description}
+
+ )}
+
+
+ {children}
+
+
+);
diff --git a/src/modules/dev/ComponentShowcase/components/ShowcaseLayout/index.tsx b/src/modules/dev/ComponentShowcase/components/ShowcaseLayout/index.tsx
new file mode 100644
index 0000000000..49fc92ffed
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/components/ShowcaseLayout/index.tsx
@@ -0,0 +1,186 @@
+import { MenuIcon } from '@heroicons/react/outline';
+import {
+ Box,
+ Container,
+ Drawer,
+ IconButton,
+ PaletteMode,
+ SvgIcon,
+ Typography,
+} from '@mui/material';
+import { useColorScheme } from '@mui/material/styles';
+import { ReactNode, useState } from 'react';
+import { Link } from 'src/components/primitives/Link';
+
+import { SHOWCASE_GROUPS, SHOWCASE_SECTIONS } from '../../utils/registry';
+import { ThemeControl } from '../ThemeControl';
+
+interface ShowcaseLayoutProps {
+ activeSlug: string;
+ children: ReactNode;
+}
+
+const SIDEBAR_WIDTH = 248;
+
+export const ShowcaseLayout = ({ activeSlug, children }: ShowcaseLayoutProps) => {
+ const { mode: appMode, systemMode } = useColorScheme();
+
+ // The showcase runs on its OWN color scheme (seeded once from the app's) so switching it
+ // here re-declares the CSS variables for this subtree only — via the `data-mui-color-scheme`
+ // attribute — without flipping the whole app. Colors below use `sx` palette shortcuts,
+ // which resolve to CSS-var refs and therefore follow that attribute.
+ const [scheme, setScheme] = useState(
+ () => (appMode === 'system' ? systemMode : appMode) ?? 'light'
+ );
+ const [mobileNavOpen, setMobileNavOpen] = useState(false);
+
+ // Some sections (page-wide banners) opt out of the max-width content container.
+ const fullBleed = SHOWCASE_SECTIONS.find((s) => s.slug === activeSlug)?.fullBleed ?? false;
+
+ // One nav block, reused by the desktop sidebar and the mobile drawer.
+ const nav = (
+ <>
+
+ Components
+
+
+ {SHOWCASE_GROUPS.map((group, index) => (
+
+
+ {group.label}
+
+
+ {group.sections.map((section) => {
+ const active = section.slug === activeSlug;
+ return (
+ setMobileNavOpen(false)}
+ sx={{
+ display: 'block',
+ py: 1,
+ px: 2,
+ borderRadius: '8px',
+ color: active ? 'fg-1' : 'fg-2',
+ backgroundColor: active ? 'selected' : 'transparent',
+ '&:hover': {
+ color: 'fg-1',
+ backgroundColor: active ? 'selected' : 'button-hover',
+ },
+ }}
+ >
+ {section.label}
+
+ );
+ })}
+
+ ))}
+ >
+ );
+
+ return (
+
+ {/* Persistent sidebar (md and up) */}
+
+ {nav}
+
+
+ {/* Mobile drawer (below md). It portals to , outside the local-scheme wrapper above,
+ so the inner Box re-declares `data-mui-color-scheme` to keep it on the showcase theme. */}
+ setMobileNavOpen(false)}
+ sx={{ display: { xs: 'block', md: 'none' } }}
+ PaperProps={{ sx: { width: SIDEBAR_WIDTH, border: 'none' } }}
+ >
+
+ {nav}
+
+
+
+ {/* Content */}
+
+
+
+ setMobileNavOpen(true)}
+ sx={{ display: { xs: 'inline-flex', md: 'none' }, ml: -1 }}
+ >
+
+
+
+
+
+ Component showcase
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+};
diff --git a/src/modules/dev/ComponentShowcase/components/Specimen/index.tsx b/src/modules/dev/ComponentShowcase/components/Specimen/index.tsx
new file mode 100644
index 0000000000..164adce784
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/components/Specimen/index.tsx
@@ -0,0 +1,55 @@
+import { Box, Typography } from '@mui/material';
+import { ReactNode } from 'react';
+import { figVars } from 'src/utils/figmaColors';
+
+interface SpecimenProps {
+ label?: string;
+ fullWidth?: boolean;
+ // Cross-axis alignment of the controls on the stage row. Defaults to 'center' (best for
+ // toggles); pass 'flex-start' when items differ in height (e.g. a field with error text) so
+ // their top edges line up.
+ align?: 'center' | 'flex-start';
+ children: ReactNode;
+}
+
+// A single example: a small uppercase caption above the component, which sits on a
+// plain bordered "stage" (no fill) — matching the reference showcase.
+export const Specimen = ({ label, fullWidth, align = 'center', children }: SpecimenProps) => (
+
+ {label && (
+
+ {label}
+
+ )}
+
+ {children}
+
+
+);
diff --git a/src/modules/dev/ComponentShowcase/components/SurfacesSection/index.tsx b/src/modules/dev/ComponentShowcase/components/SurfacesSection/index.tsx
new file mode 100644
index 0000000000..413d9db8bd
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/components/SurfacesSection/index.tsx
@@ -0,0 +1,102 @@
+import { Box, Button, Paper, Typography } from '@mui/material';
+import { ListWrapper } from 'src/components/lists/ListWrapper';
+import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
+import { Row } from 'src/components/primitives/Row';
+import { ReserveOverviewBox } from 'src/components/ReserveOverviewBox';
+import { TopInfoPanel } from 'src/components/TopInfoPanel/TopInfoPanel';
+import { TopInfoPanelItem } from 'src/components/TopInfoPanel/TopInfoPanelItem';
+import { StakeActionBox } from 'src/modules/staking/StakeActionBox';
+
+import { Section } from '../Section';
+import { Specimen } from '../Specimen';
+
+const PAPER_VARIANTS = ['elevation', 'outlined', 'modal', 'card'] as const;
+
+export const SurfacesSection = () => (
+
+ {PAPER_VARIANTS.map((variant) => (
+
+
+ Paper {variant}
+
+
+ ))}
+
+
+
+ Card title}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Showcase panel
+
+ }
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ —}
+ dataCy="showcaseStake"
+ >
+
+ Stake
+
+
+
+
+
+);
diff --git a/src/modules/dev/ComponentShowcase/components/Swatch/index.tsx b/src/modules/dev/ComponentShowcase/components/Swatch/index.tsx
new file mode 100644
index 0000000000..525cf7d711
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/components/Swatch/index.tsx
@@ -0,0 +1,38 @@
+import { Box, Typography } from '@mui/material';
+import { figVars } from 'src/utils/figmaColors';
+
+interface SwatchProps {
+ name: string;
+ value: string;
+}
+
+// A checkerboard backing so alpha tokens (borders/shadows/scrim) stay visible.
+const CHECKERBOARD = {
+ backgroundImage:
+ 'linear-gradient(45deg, #c4c4c4 25%, transparent 25%), linear-gradient(-45deg, #c4c4c4 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #c4c4c4 75%), linear-gradient(-45deg, transparent 75%, #c4c4c4 75%)',
+ backgroundSize: '12px 12px',
+ backgroundPosition: '0 0, 0 6px, 6px -6px, -6px 0px',
+};
+
+export const Swatch = ({ name, value }: SwatchProps) => (
+
+
+
+
+
+ {name}
+
+
+ {value}
+
+
+);
diff --git a/src/modules/dev/ComponentShowcase/components/ThemeControl/index.tsx b/src/modules/dev/ComponentShowcase/components/ThemeControl/index.tsx
new file mode 100644
index 0000000000..a012cef71f
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/components/ThemeControl/index.tsx
@@ -0,0 +1,30 @@
+import { Box, Button, PaletteMode, Typography } from '@mui/material';
+
+interface ThemeControlProps {
+ mode: PaletteMode;
+ onChange: (mode: PaletteMode) => void;
+}
+
+// Segmented Light/Dark control for the showcase's local theme. Uses the themed
+// Button variants so it reads natively in whichever mode is active.
+export const ThemeControl = ({ mode, onChange }: ThemeControlProps) => (
+
+
+ Theme
+
+
+ {(['light', 'dark'] as const).map((value) => (
+ onChange(value)}
+ sx={{ textTransform: 'capitalize' }}
+ >
+ {value}
+
+ ))}
+
+
+);
diff --git a/src/modules/dev/ComponentShowcase/components/TogglesBadgesSection/index.tsx b/src/modules/dev/ComponentShowcase/components/TogglesBadgesSection/index.tsx
new file mode 100644
index 0000000000..e0e4f208da
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/components/TogglesBadgesSection/index.tsx
@@ -0,0 +1,73 @@
+import { Box, Typography } from '@mui/material';
+import { useState } from 'react';
+import { BadgeSize, ExclamationBadge } from 'src/components/badges/ExclamationBadge';
+import { StyledTxModalToggleButton } from 'src/components/StyledToggleButton';
+import { StyledTxModalToggleGroup } from 'src/components/StyledToggleButtonGroup';
+
+import { Section } from '../Section';
+import { Specimen } from '../Specimen';
+
+interface ToggleOption {
+ value: string;
+ label: string;
+ disabled?: boolean;
+}
+
+const ToggleDemo = ({ options, initial }: { options: ToggleOption[]; initial: string }) => {
+ const [value, setValue] = useState(initial);
+ return (
+ v && setValue(v)}>
+ {options.map((o) => (
+
+ {o.label}
+
+ ))}
+
+ );
+};
+
+export const TogglesBadgesSection = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/src/modules/dev/ComponentShowcase/components/TypographySection/index.tsx b/src/modules/dev/ComponentShowcase/components/TypographySection/index.tsx
new file mode 100644
index 0000000000..cdba8388b2
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/components/TypographySection/index.tsx
@@ -0,0 +1,17 @@
+import { Typography } from '@mui/material';
+
+import { TYPOGRAPHY_VARIANTS } from '../../utils/catalog';
+import { Section } from '../Section';
+import { Specimen } from '../Specimen';
+
+export const TypographySection = () => (
+
+ {TYPOGRAPHY_VARIANTS.map((variant) => (
+
+
+ The quick brown fox jumps over the lazy dog — 1234567890
+
+
+ ))}
+
+);
diff --git a/src/modules/dev/ComponentShowcase/utils/catalog.ts b/src/modules/dev/ComponentShowcase/utils/catalog.ts
new file mode 100644
index 0000000000..605694b6d0
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/utils/catalog.ts
@@ -0,0 +1,58 @@
+import { TypographyProps } from '@mui/material';
+import { FigmaColorName } from 'src/utils/figmaColors';
+
+type TypographyVariant = TypographyProps['variant'];
+
+// The typography variants enabled in the theme. The default MUI variants
+// (body1/body2/button/subtitle*/h6/overline) are disabled in theme.tsx, so
+// they are intentionally omitted here.
+export const TYPOGRAPHY_VARIANTS: TypographyVariant[] = [
+ 'display1',
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'subheader1',
+ 'subheader2',
+ 'description',
+ 'caption',
+ 'secondary21',
+ 'secondary16',
+ 'main12',
+ 'buttonL',
+ 'buttonM',
+ 'buttonS',
+ 'helperText',
+];
+
+export type ColorRole = 'bg' | 'text' | 'border' | 'shadow' | 'swatch';
+
+// Figma color tokens grouped for the showcase. `role` decides how each group is presented — text
+// colors as text, backgrounds as surfaces, borders as dividers, shadows as shadows. Names are keys
+// of `figmaLight`, so each resolves in both light and dark via the flattened palette tokens.
+export const COLOR_GROUPS: { title: string; role: ColorRole; names: FigmaColorName[] }[] = [
+ {
+ title: 'Backgrounds',
+ role: 'bg',
+ names: ['bg-max', 'bg-1', 'bg-2', 'bg-3', 'bg-4', 'bg-5', 'bg-6'],
+ },
+ {
+ title: 'Foreground / Text',
+ role: 'text',
+ names: ['fg-max', 'fg-1', 'fg-2', 'fg-3', 'fg-4', 'fg-5'],
+ },
+ { title: 'Borders / dividers', role: 'border', names: ['border-0', 'border-1', 'border-2'] },
+ {
+ title: 'Shadows',
+ role: 'shadow',
+ names: [
+ 'shadow-low',
+ 'shadow-medium',
+ 'shadow-high',
+ 'shadow-strong',
+ 'shadow-stroke-1',
+ 'shadow-stroke-2',
+ ],
+ },
+];
diff --git a/src/modules/dev/ComponentShowcase/utils/registry.tsx b/src/modules/dev/ComponentShowcase/utils/registry.tsx
new file mode 100644
index 0000000000..a743e01a54
--- /dev/null
+++ b/src/modules/dev/ComponentShowcase/utils/registry.tsx
@@ -0,0 +1,112 @@
+import dynamic from 'next/dynamic';
+import { ComponentType } from 'react';
+
+export interface ShowcaseSection {
+ slug: string;
+ label: string;
+ group: string;
+ Component: ComponentType;
+ /** Opt out of the layout's max-width content container (e.g. full-width page banners). */
+ fullBleed?: boolean;
+}
+
+// One entry per route (`/dev/components/`). Each section is lazily loaded so a
+// given page only ships its own section's code — keeping every page light.
+export const SHOWCASE_SECTIONS: ShowcaseSection[] = [
+ {
+ slug: 'colors',
+ label: 'Colors',
+ group: 'Foundations',
+ Component: dynamic(() => import('../components/ColorsSection').then((m) => m.ColorsSection)),
+ },
+ {
+ slug: 'typography',
+ label: 'Typography',
+ group: 'Foundations',
+ Component: dynamic(() =>
+ import('../components/TypographySection').then((m) => m.TypographySection)
+ ),
+ },
+ {
+ slug: 'buttons',
+ label: 'Buttons',
+ group: 'Inputs & actions',
+ Component: dynamic(() => import('../components/ButtonsSection').then((m) => m.ButtonsSection)),
+ },
+ {
+ slug: 'form-controls',
+ label: 'Form controls',
+ group: 'Inputs & actions',
+ Component: dynamic(() =>
+ import('../components/FormControlsSection').then((m) => m.FormControlsSection)
+ ),
+ },
+ {
+ slug: 'toggles-badges',
+ label: 'Toggles & badges',
+ group: 'Inputs & actions',
+ Component: dynamic(() =>
+ import('../components/TogglesBadgesSection').then((m) => m.TogglesBadgesSection)
+ ),
+ },
+ {
+ slug: 'feedback',
+ label: 'Feedback',
+ group: 'Feedback & overlays',
+ Component: dynamic(() =>
+ import('../components/FeedbackSection').then((m) => m.FeedbackSection)
+ ),
+ },
+ {
+ slug: 'overlays',
+ label: 'Overlays & modal',
+ group: 'Feedback & overlays',
+ Component: dynamic(() =>
+ import('../components/OverlaysSection').then((m) => m.OverlaysSection)
+ ),
+ },
+ {
+ slug: 'empty-states',
+ label: 'Empty states',
+ group: 'Feedback & overlays',
+ Component: dynamic(() =>
+ import('../components/EmptyStatesSection').then((m) => m.EmptyStatesSection)
+ ),
+ },
+ {
+ slug: 'surfaces',
+ label: 'Surfaces & cards',
+ group: 'Data & surfaces',
+ Component: dynamic(() =>
+ import('../components/SurfacesSection').then((m) => m.SurfacesSection)
+ ),
+ },
+ {
+ slug: 'data-primitives',
+ label: 'Data primitives',
+ group: 'Data & surfaces',
+ Component: dynamic(() =>
+ import('../components/DataPrimitivesSection').then((m) => m.DataPrimitivesSection)
+ ),
+ },
+ {
+ slug: 'banners',
+ label: 'Banners',
+ group: 'Data & surfaces',
+ fullBleed: true,
+ Component: dynamic(() => import('../components/BannersSection').then((m) => m.BannersSection)),
+ },
+];
+
+// Sections grouped for the sidebar, preserving declaration order.
+export const SHOWCASE_GROUPS = SHOWCASE_SECTIONS.reduce<
+ { label: string; sections: ShowcaseSection[] }[]
+>((groups, section) => {
+ const group = groups.find((g) => g.label === section.group);
+ if (group) {
+ group.sections.push(section);
+ } else {
+ groups.push({ label: section.group, sections: [section] });
+ }
+ return groups;
+}, []);
diff --git a/src/modules/faucet/FaucetAssetsList.tsx b/src/modules/faucet/FaucetAssetsList.tsx
index f952364f34..6e1106fde6 100644
--- a/src/modules/faucet/FaucetAssetsList.tsx
+++ b/src/modules/faucet/FaucetAssetsList.tsx
@@ -114,7 +114,7 @@ export default function FaucetAssetsList() {
{reserve.name}
-
+
{reserve.symbol}
@@ -123,11 +123,7 @@ export default function FaucetAssetsList() {
{!downToXSM && (
-
+
)}
diff --git a/src/modules/governance/DelegatedInfoPanel.tsx b/src/modules/governance/DelegatedInfoPanel.tsx
index 85065eff8a..86c77e75b0 100644
--- a/src/modules/governance/DelegatedInfoPanel.tsx
+++ b/src/modules/governance/DelegatedInfoPanel.tsx
@@ -42,7 +42,7 @@ const DelegatedPower: React.FC = ({
return (
-
+
{title}
@@ -64,7 +64,7 @@ const DelegatedPower: React.FC = ({
value={Number(aavePower) + Number(stkAavePower) + Number(aAavePower)}
variant="subheader1"
/>
-
+
AAVE + stkAAVE + aAAVE
@@ -166,7 +166,7 @@ export const DelegatedInfoPanel = () => {
Delegated power
-
+
Use your AAVE, stkAAVE, or aAave balance to delegate your voting and proposition powers.
You will not be sending any tokens, only the rights to vote and propose changes to the
@@ -176,7 +176,7 @@ export const DelegatedInfoPanel = () => {
href="https://docs.aave.com/developers/v/2.0/protocol-governance/governance"
target="_blank"
variant="description"
- color="text.secondary"
+ color="fg-2"
sx={{ textDecoration: 'underline', ml: 1 }}
onClick={() => trackEvent(GENERAL.EXTERNAL_LINK, { link: 'Learn More Delegation' })}
>
@@ -184,7 +184,7 @@ export const DelegatedInfoPanel = () => {
{disableButton ? (
-
+
You have no AAVE/stkAAVE/aAave balance to delegate.
) : (
diff --git a/src/modules/governance/FormattedProposalTime.tsx b/src/modules/governance/FormattedProposalTime.tsx
index 0a14b77256..905f6d7a2d 100644
--- a/src/modules/governance/FormattedProposalTime.tsx
+++ b/src/modules/governance/FormattedProposalTime.tsx
@@ -27,11 +27,7 @@ export function FormattedProposalTime({
if ([ProposalState.Pending].includes(state)) {
return (
-
+
{state}
starts
@@ -43,11 +39,7 @@ export function FormattedProposalTime({
if ([ProposalState.Active].includes(state)) {
return (
-
+
{state}
ends
@@ -67,11 +59,7 @@ export function FormattedProposalTime({
) {
return (
-
+
{state}
on
@@ -85,11 +73,7 @@ export function FormattedProposalTime({
const canBeExecuted = timestamp > executionTime;
return (
-
+
{canBeExecuted ? Expires : Can be executed}
diff --git a/src/modules/governance/GovernanceTopPanel.tsx b/src/modules/governance/GovernanceTopPanel.tsx
index 4444a46959..5a58c98221 100644
--- a/src/modules/governance/GovernanceTopPanel.tsx
+++ b/src/modules/governance/GovernanceTopPanel.tsx
@@ -20,7 +20,7 @@ function ExternalLink({ text, href }: ExternalLinkProps) {
return (
{
- {/*
*/}
-
+
Aave Governance
-
+
Aave is a fully decentralized, community governed protocol by the AAVE token-holders.
AAVE token-holders collectively discuss, propose, and vote on upgrades to the
@@ -70,7 +66,7 @@ export const GovernanceTopPanel = () => {
trackEvent(GENERAL.EXTERNAL_LINK, { Link: 'FAQ Docs Governance' })}
href="https://aave.com/docs/ecosystem/governance"
- sx={{ textDecoration: 'underline', color: '#8E92A3' }}
+ sx={{ textDecoration: 'underline', color: 'fg-3' }}
>
documentation
diff --git a/src/modules/governance/ProposalListHeader.tsx b/src/modules/governance/ProposalListHeader.tsx
index d6ef45e8bf..cefa976fe1 100644
--- a/src/modules/governance/ProposalListHeader.tsx
+++ b/src/modules/governance/ProposalListHeader.tsx
@@ -40,7 +40,13 @@ export const ProposalListHeaderDesktop: React.FC
Filter
-
{proposal.author && (
-
+
Author: {proposal.author.replace(/^@/, '')}
)}
{proposal.shortDescription && (
{
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-between',
- borderBottom: (theme) => `1px solid ${theme.palette.divider}`,
+ borderBottom: `1px solid ${figVars['border-2']}`,
}}
>
{
-
+
{isAddressSelectedAsRepresentative ? (
Representing smart contract wallet (ie. Safe) addresses on other chains.
@@ -100,18 +101,18 @@ const Representatives = ({
{representative.representative === ZERO_ADDRESS ? (
({
+ sx={{
height: '24px',
width: '24px',
- background: theme.palette.background.disabled,
- })}
+ background: figVars['bg-6'],
+ }}
onClick={onOpenRepresentatives}
>
-
+
Connect
@@ -137,7 +138,7 @@ const Representing = ({ representing }: { representing: Rpresented[] }) => {
networkName={networkConfigs[representing.chainId].name}
/>
{representing.votersRepresented.length === 0 ? (
-
+
None
) : (
@@ -183,12 +184,12 @@ const AddressLink = ({ explorerLink, address }: { explorerLink: string; address:
{address}
({
+ sx={{
width: 14,
height: 14,
ml: 0.5,
- color: theme.palette.text.muted,
- })}
+ color: 'fg-3',
+ }}
>
diff --git a/src/modules/governance/UserGovernanceInfo.tsx b/src/modules/governance/UserGovernanceInfo.tsx
index 60083d3b30..ce2aa0dd01 100644
--- a/src/modules/governance/UserGovernanceInfo.tsx
+++ b/src/modules/governance/UserGovernanceInfo.tsx
@@ -21,7 +21,7 @@ export const UserGovernanceInfo = () => {
Your info
-
+
Please connect a wallet to view your personal information here.
diff --git a/src/modules/governance/VoteBar.tsx b/src/modules/governance/VoteBar.tsx
index 8ecfca3001..39798a11f9 100644
--- a/src/modules/governance/VoteBar.tsx
+++ b/src/modules/governance/VoteBar.tsx
@@ -7,7 +7,7 @@ const OuterBar = styled('div')(({ theme }) =>
position: 'relative',
width: '100%',
height: '8px',
- bgcolor: 'divider',
+ bgcolor: 'border-2',
display: 'block',
borderRadius: '6px',
})
@@ -45,7 +45,7 @@ export function VoteBar({ percent, yae, votes, loading, compact, ...rest }: Vote
{yae ? YAE : NAY}
{loading ? (
-
+
) : (
@@ -54,12 +54,12 @@ export function VoteBar({ percent, yae, votes, loading, compact, ...rest }: Vote
value={votes}
visibleDecimals={0}
sx={{ mr: 1 }}
- variant="secondary14"
+ variant="h5"
roundDown
compact={compact}
/>
{!compact && (
-
+
AAVE
)}
@@ -70,7 +70,7 @@ export function VoteBar({ percent, yae, votes, loading, compact, ...rest }: Vote
) : (
-
+
)}
{loading ? (
diff --git a/src/modules/governance/VotingPowerInfoPanel.tsx b/src/modules/governance/VotingPowerInfoPanel.tsx
index cdd5a415e7..a7f13f63e2 100644
--- a/src/modules/governance/VotingPowerInfoPanel.tsx
+++ b/src/modules/governance/VotingPowerInfoPanel.tsx
@@ -37,7 +37,7 @@ export function VotingPowerInfoPanel() {
subtitleProps={{
variant: 'caption',
addressCompactMode: CompactMode.XXL,
- color: 'text.secondary',
+ color: 'fg-2',
}}
funnel={'Your info: Governance'}
/>
@@ -47,7 +47,7 @@ export function VotingPowerInfoPanel() {
);
},
diff --git a/src/modules/governance/proposal/ProposalPayloads.tsx b/src/modules/governance/proposal/ProposalPayloads.tsx
index f406f1e3e2..eb458644de 100644
--- a/src/modules/governance/proposal/ProposalPayloads.tsx
+++ b/src/modules/governance/proposal/ProposalPayloads.tsx
@@ -112,7 +112,7 @@ export const ProposalPayloads = ({ payloads, loading }: ProposalPayloadsProps) =
{logo && }
-
+
Payload {p.payloadId}
diff --git a/src/modules/governance/proposal/ProposalTimeline.tsx b/src/modules/governance/proposal/ProposalTimeline.tsx
index 3485f0a873..571fec8985 100644
--- a/src/modules/governance/proposal/ProposalTimeline.tsx
+++ b/src/modules/governance/proposal/ProposalTimeline.tsx
@@ -1,6 +1,7 @@
import { ExternalLinkIcon } from '@heroicons/react/outline';
import { Trans } from '@lingui/macro';
-import { KeyboardArrowDown, KeyboardArrowUp } from '@mui/icons-material';
+import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
+import KeyboardArrowUp from '@mui/icons-material/KeyboardArrowUp';
import {
Avatar,
Box,
@@ -19,6 +20,7 @@ import { useGovernanceCoreConstants } from 'src/hooks/governance/useGovernanceCo
import { ProposalDetail, ProposalPayload } from 'src/services/GovernanceCacheService';
import { governanceV3Config } from 'src/ui-config/governanceConfig';
import { networkConfigs } from 'src/ui-config/networksConfig';
+import { figVars } from 'src/utils/figmaColors';
import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig';
// Single-spine proposal timeline. It weaves the three state machines (proposal core, voting machine,
@@ -109,16 +111,16 @@ interface Step {
const statusColor = (status: StepStatus, theme: Theme) => {
switch (status) {
case 'done':
- return theme.palette.primary.main;
+ return theme.vars.palette.primary.main;
case 'ok':
- return theme.palette.success.main;
+ return theme.vars.palette.success.main;
case 'now':
case 'settled':
- return theme.palette.warning.main;
+ return theme.vars.palette.warning.main;
case 'terminal':
- return theme.palette.error.main;
+ return theme.vars.palette.error.main;
default:
- return theme.palette.text.disabled;
+ return figVars['fg-4'];
}
};
@@ -525,7 +527,7 @@ export const ProposalTimeline = ({
}}
>
{logo && }
-
+
{sub.label}
@@ -538,10 +540,10 @@ export const ProposalTimeline = ({
sub.tone === 'ready' || sub.tone === 'countdown'
? 'warning.main'
: sub.tone === 'done'
- ? 'text.muted'
+ ? 'fg-3'
: sub.tone === 'estimate'
- ? 'text.muted'
- : 'text.secondary',
+ ? 'fg-3'
+ : 'fg-2',
whiteSpace: 'nowrap',
}}
>
@@ -608,10 +610,10 @@ export const ProposalTimeline = ({
step.status === 'settled' ||
step.status === 'terminal'
? color
- : theme.palette.background.paper,
+ : theme.vars.palette.background.paper,
boxShadow:
step.status === 'now'
- ? `0 0 0 4px ${theme.palette.background.paper}, 0 0 0 6px ${theme.palette.warning.main}33`
+ ? `0 0 0 4px ${theme.vars.palette.background.paper}, 0 0 0 6px ${theme.vars.palette.warning.main}33`
: undefined,
}}
/>
@@ -627,7 +629,7 @@ export const ProposalTimeline = ({
gap: 2,
cursor: hasSubs ? 'pointer' : 'default',
'&:focus-visible': {
- outline: `2px solid ${theme.palette.primary.main}`,
+ outline: `2px solid ${theme.vars.palette.primary.main}`,
outlineOffset: 2,
borderRadius: 1,
},
@@ -647,14 +649,14 @@ export const ProposalTimeline = ({
}
>
{step.name}
{hasSubs && (
-
+
{isOpen ? (
) : (
@@ -688,7 +690,7 @@ export const ProposalTimeline = ({
step.status === 'ok'
? 'success.main'
: step.status === 'pending'
- ? 'text.muted'
+ ? 'fg-3'
: 'warning.main',
}}
>
@@ -702,8 +704,8 @@ export const ProposalTimeline = ({
step.valueKind === 'countdown'
? 'warning.main'
: step.valueKind === 'pending'
- ? 'text.disabled'
- : 'text.muted',
+ ? 'fg-4'
+ : 'fg-3',
fontWeight: step.valueKind === 'countdown' ? 600 : 400,
fontStyle:
step.valueKind === 'pending' || step.valueKind === 'estimate'
diff --git a/src/modules/governance/proposal/ProposalTopPanel.tsx b/src/modules/governance/proposal/ProposalTopPanel.tsx
index d2dffd83e6..2573f97cf9 100644
--- a/src/modules/governance/proposal/ProposalTopPanel.tsx
+++ b/src/modules/governance/proposal/ProposalTopPanel.tsx
@@ -17,7 +17,7 @@ export const ProposalTopPanel = () => {
trackEvent(AIP.GO_BACK)}
color="primary"
diff --git a/src/modules/governance/proposal/VoteInfo.tsx b/src/modules/governance/proposal/VoteInfo.tsx
index cb428c09a1..d17b2697f3 100644
--- a/src/modules/governance/proposal/VoteInfo.tsx
+++ b/src/modules/governance/proposal/VoteInfo.tsx
@@ -53,7 +53,7 @@ export function VoteInfo({ voteData }: VoteInfoProps) {
sx={{
display: 'flex',
alignItems: 'center',
- color: 'text.secondary',
+ color: 'fg-2',
}}
>
@@ -83,7 +83,7 @@ export function VoteInfo({ voteData }: VoteInfoProps) {
{user ? (
<>
{user && !didVote && !voteOngoing && (
-
+
You did not participate in this proposal
)}
@@ -94,17 +94,13 @@ export function VoteInfo({ voteData }: VoteInfoProps) {
Voting power
-
+
(AAVE + stkAAVE)
>
}
>
-
+
)}
{showAlreadyVotedMsg && voteOnProposal && (
diff --git a/src/modules/governance/proposal/VotersList.tsx b/src/modules/governance/proposal/VotersList.tsx
index 7521e6a4c6..49678bf512 100644
--- a/src/modules/governance/proposal/VotersList.tsx
+++ b/src/modules/governance/proposal/VotersList.tsx
@@ -15,7 +15,7 @@ export const VotersList = ({ compact = false, voters, sx }: VotersListProps): JS
return (
{voters.length === 0 ? (
- —
+ —
) : (
voters
.sort((a, b) => Number(b.votingPower) - Number(a.votingPower))
diff --git a/src/modules/governance/proposal/VotersListContainer.tsx b/src/modules/governance/proposal/VotersListContainer.tsx
index 2fb34c185b..d6a7117faf 100644
--- a/src/modules/governance/proposal/VotersListContainer.tsx
+++ b/src/modules/governance/proposal/VotersListContainer.tsx
@@ -30,14 +30,14 @@ export const VotersListContainer = ({ voteInfo, voters }: VotersListProps): JSX.
return (
-
+
{voters.combinedVotes.length > 10 ? (
Top 10 addresses
) : (
Addresses
)}
-
+
Votes
diff --git a/src/modules/governance/proposal/VotersListModal.tsx b/src/modules/governance/proposal/VotersListModal.tsx
index b4c5c4d496..900183f21f 100644
--- a/src/modules/governance/proposal/VotersListModal.tsx
+++ b/src/modules/governance/proposal/VotersListModal.tsx
@@ -2,8 +2,8 @@ import { Trans } from '@lingui/macro';
import { Box, Grid, Typography, useMediaQuery, useTheme } from '@mui/material';
import { useState } from 'react';
import { Row } from 'src/components/primitives/Row';
-import StyledToggleButton from 'src/components/StyledToggleButton';
-import StyledToggleButtonGroup from 'src/components/StyledToggleButtonGroup';
+import { StyledTxModalToggleButton } from 'src/components/StyledToggleButton';
+import { StyledTxModalToggleGroup } from 'src/components/StyledToggleButtonGroup';
import { ProposalVoteDisplayInfo, VotersSplitDisplay } from 'src/modules/governance/types';
import { BasicModal } from '../../../components/primitives/BasicModal';
@@ -28,7 +28,7 @@ export const VotersListModal = ({
const [voteView, setVoteView] = useState<'yaes' | 'nays'>('yaes');
const borderBaseStyle = {
border: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-0',
borderRadius: 1,
};
@@ -52,13 +52,13 @@ export const VotersListModal = ({
px: 4,
py: 2,
borderBottom: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-0',
}}
>
-
+
Addresses ({voters.yaeVotes.length})
-
+
Votes
@@ -91,13 +91,13 @@ export const VotersListModal = ({
px: 4,
py: 2,
borderBottom: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-0',
}}
>
-
+
Addresses ({voters.nayVotes.length})
-
+
Votes
@@ -129,24 +129,24 @@ export const VotersListModal = ({
) : (
<>
- setVoteView(value)}
- sx={{ width: '100%', height: '44px', mt: 8, mb: 6 }}
+ sx={{ width: '100%', mt: 8, mb: 6 }}
>
-
+
Voted YAE
-
-
+
+
Voted NAY
-
-
+
+
{voteView === 'yaes' && yesVotesUI}
{voteView === 'nays' && noVotesUI}
>
diff --git a/src/modules/governance/proposal/VotingResults.tsx b/src/modules/governance/proposal/VotingResults.tsx
index 9e0627f76a..23c16a84a7 100644
--- a/src/modules/governance/proposal/VotingResults.tsx
+++ b/src/modules/governance/proposal/VotingResults.tsx
@@ -76,7 +76,7 @@ export const VotingResults = ({ proposal, loading, voters, votesLoading }: Votin
caption={
<>
Current votes
-
+
Required
>
@@ -96,7 +96,7 @@ export const VotingResults = ({ proposal, loading, voters, votesLoading }: Votin
value={proposal.voteInfo.quorum}
visibleDecimals={2}
roundDown
- color="text.muted"
+ color="fg-3"
/>
@@ -123,7 +123,7 @@ export const VotingResults = ({ proposal, loading, voters, votesLoading }: Votin
caption={
<>
Current differential
-
+
Required
>
@@ -143,7 +143,7 @@ export const VotingResults = ({ proposal, loading, voters, votesLoading }: Votin
value={proposal.voteInfo.requiredDifferential}
visibleDecimals={2}
roundDown
- color="text.muted"
+ color="fg-3"
/>
diff --git a/src/modules/history/HistoryFilterMenu.tsx b/src/modules/history/HistoryFilterMenu.tsx
index f29114545c..a502cd0ccd 100644
--- a/src/modules/history/HistoryFilterMenu.tsx
+++ b/src/modules/history/HistoryFilterMenu.tsx
@@ -1,6 +1,7 @@
import { XCircleIcon } from '@heroicons/react/solid';
import { Trans } from '@lingui/macro';
-import { Check as CheckIcon, Sort as SortIcon } from '@mui/icons-material';
+import CheckIcon from '@mui/icons-material/Check';
+import SortIcon from '@mui/icons-material/Sort';
import {
Box,
Button,
@@ -16,6 +17,7 @@ import React, { useEffect, useState } from 'react';
import { DarkTooltip } from 'src/components/infoTooltips/DarkTooltip';
import { useRootStore } from 'src/store/root';
import { TRANSACTION_HISTORY } from 'src/utils/events';
+import { figVars } from 'src/utils/figmaColors';
import { FilterOptions } from './types';
@@ -117,7 +119,7 @@ export const HistoryFilterMenu: React.FC = ({
return (
-
+
TXs:
{displayedFilters}
@@ -144,7 +146,7 @@ export const HistoryFilterMenu: React.FC = ({
alignItems: 'center',
height: 36,
border: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-2',
borderRadius: '4px',
mr: downToMD ? 0 : 2,
ml: downToMD ? 4 : 0,
@@ -159,7 +161,7 @@ export const HistoryFilterMenu: React.FC = ({
= ({
{!allSelected && (
+
Reset
}
@@ -190,7 +192,9 @@ export const HistoryFilterMenu: React.FC = ({
}}
onClick={handleClearFilter}
>
-
+
+
+
)}
@@ -212,12 +216,12 @@ export const HistoryFilterMenu: React.FC = ({
handleFilterClick(undefined)}
sx={{
- background: allSelected ? theme.palette.background.surface : undefined,
+ background: allSelected ? figVars['bg-2'] : undefined,
display: 'flex',
justifyContent: 'space-between',
}}
>
-
+
All transactions
{allSelected && (
@@ -247,14 +251,12 @@ export const HistoryFilterMenu: React.FC = ({
key={optionKey}
onClick={() => handleFilterClick(option)}
sx={{
- background: currentFilter.includes(option)
- ? theme.palette.background.surface
- : undefined,
+ background: currentFilter.includes(option) ? figVars['bg-2'] : undefined,
display: 'flex',
justifyContent: 'space-between',
}}
>
-
+
{currentFilter.includes(option) && (
diff --git a/src/modules/history/HistoryWrapper.tsx b/src/modules/history/HistoryWrapper.tsx
index e12aa5521b..a5780c7679 100644
--- a/src/modules/history/HistoryWrapper.tsx
+++ b/src/modules/history/HistoryWrapper.tsx
@@ -137,7 +137,7 @@ export const HistoryWrapper = () => {
Transactions
-
+
This list may not include all your swaps.
@@ -171,7 +171,7 @@ export const HistoryWrapper = () => {
-
+
.CSV
@@ -189,7 +189,7 @@ export const HistoryWrapper = () => {
-
+
.JSON
@@ -206,7 +206,7 @@ export const HistoryWrapper = () => {
.sort((a, b) => new Date(b[0]).getTime() - new Date(a[0]).getTime())
.map(([date, txns], groupIndex) => (
-
+
{date}
{txns.map((transaction: TransactionHistoryItemUnion, index: number) => {
@@ -234,10 +234,10 @@ export const HistoryWrapper = () => {
my: 24,
}}
>
-
+
Nothing found
-
+
We couldn't find any transactions related to your search. Try again with a
different asset name, or reset filters.
@@ -266,7 +266,7 @@ export const HistoryWrapper = () => {
flex: 1,
}}
>
-
+
{currentMarket === 'proto_plasma_v3' ? (
Transaction history for Plasma not supported yet, coming soon.
) : (
diff --git a/src/modules/history/HistoryWrapperMobile.tsx b/src/modules/history/HistoryWrapperMobile.tsx
index e5ffc71a28..f1095f4832 100644
--- a/src/modules/history/HistoryWrapperMobile.tsx
+++ b/src/modules/history/HistoryWrapperMobile.tsx
@@ -172,7 +172,7 @@ export const HistoryWrapperMobile = () => {
open={Boolean(menuAnchorEl)}
onClose={handleDownloadMenuClose}
>
-
+
Export data to
{
) : !isEmpty ? (
Object.entries(groupByDate(filteredTxns)).map(([date, txns], groupIndex) => (
-
+
{date}
{txns.map((transaction: TransactionHistoryItemUnion, index: number) => {
@@ -272,10 +272,10 @@ export const HistoryWrapperMobile = () => {
my: 24,
}}
>
-
+
Nothing found
-
+
We couldn't find any transactions related to your search. Try again with a
different asset name, or reset filters.
@@ -304,7 +304,7 @@ export const HistoryWrapperMobile = () => {
flex: 1,
}}
>
-
+
No transactions yet.
diff --git a/src/modules/history/PriceUnavailable.tsx b/src/modules/history/PriceUnavailable.tsx
index a27cb9b1ac..dcb223ddda 100644
--- a/src/modules/history/PriceUnavailable.tsx
+++ b/src/modules/history/PriceUnavailable.tsx
@@ -1,6 +1,7 @@
import { Trans } from '@lingui/macro';
import { Box } from '@mui/material';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
+import { onAccent } from 'src/utils/figmaColors';
export const PriceUnavailable = ({ value }: { value: number }) => {
if (value > 0) {
@@ -9,7 +10,7 @@ export const PriceUnavailable = ({ value }: { value: number }) => {
compact
compactThreshold={100000}
symbol="USD"
- symbolsColor="common.white"
+ symbolsColor={onAccent}
value={value}
/>
);
diff --git a/src/modules/history/TransactionMobileRowItem.tsx b/src/modules/history/TransactionMobileRowItem.tsx
index 866b988452..0d80bc716c 100644
--- a/src/modules/history/TransactionMobileRowItem.tsx
+++ b/src/modules/history/TransactionMobileRowItem.tsx
@@ -1,12 +1,13 @@
import { OrderStatus } from '@cowprotocol/cow-sdk';
import { Trans } from '@lingui/macro';
import ArrowOutward from '@mui/icons-material/ArrowOutward';
-import { Box, Button, SvgIcon, Typography, useTheme } from '@mui/material';
+import { Box, Button, SvgIcon, Typography } from '@mui/material';
import React, { useEffect, useState } from 'react';
import { ListItem } from 'src/components/lists/ListItem';
import { useModalContext } from 'src/hooks/useModal';
import { useRootStore } from 'src/store/root';
import { GENERAL } from 'src/utils/events';
+import { figVars } from 'src/utils/figmaColors';
import { useShallow } from 'zustand/shallow';
import { ActionDetails, ActionTextMap } from './actions/ActionDetails';
@@ -20,7 +21,7 @@ import {
function ActionTitle({ action }: { action: ActionName }) {
return (
-
+
);
@@ -36,7 +37,6 @@ function TransactionMobileRowItem({ transaction }: TransactionHistoryItemProps)
useShallow((state) => [state.currentNetworkConfig, state.trackEvent])
);
const { openCancelCowOrder } = useModalContext();
- const theme = useTheme();
const explorerLink = getExplorerLink(transaction, currentNetworkConfig);
const action = getTransactionAction(transaction);
const timestamp = Date.parse(transaction.timestamp);
@@ -60,7 +60,7 @@ function TransactionMobileRowItem({ transaction }: TransactionHistoryItemProps)
sx={{
borderWidth: `1px 0 0 0`,
borderStyle: `solid`,
- borderColor: `${theme.palette.divider}`,
+ borderColor: `${figVars['border-0']}`,
}}
>
-
+
{unixTimestampToFormattedTime({ unixTimestamp: timestamp })}
{isSwapTransaction(transaction) &&
diff --git a/src/modules/history/TransactionRowItem.tsx b/src/modules/history/TransactionRowItem.tsx
index d5fe51db78..66c610b41a 100644
--- a/src/modules/history/TransactionRowItem.tsx
+++ b/src/modules/history/TransactionRowItem.tsx
@@ -8,6 +8,7 @@ import { ListItem } from 'src/components/lists/ListItem';
import { useModalContext } from 'src/hooks/useModal';
import { useRootStore } from 'src/store/root';
import { GENERAL } from 'src/utils/events';
+import { figVars } from 'src/utils/figmaColors';
import { useShallow } from 'zustand/shallow';
import { ActionDetails, ActionTextMap } from './actions/ActionDetails';
@@ -45,7 +46,7 @@ function TransactionRowItem({ transaction }: TransactionHistoryItemProps) {
const theme = useTheme();
const downToMD = useMediaQuery(theme.breakpoints.down('md'));
- const hideStatusBadgeForCancel = useMediaQuery('(min-width: 960px) and (max-width: 1050px)');
+ const hideStatusBadgeForCancel = useMediaQuery(theme.breakpoints.between('md', 'mdlg'));
useEffect(() => {
if (copyStatus) {
@@ -66,7 +67,7 @@ function TransactionRowItem({ transaction }: TransactionHistoryItemProps) {
sx={{
borderWidth: `1px 0 0 0`,
borderStyle: `solid`,
- borderColor: `${theme.palette.divider}`,
+ borderColor: `${figVars['border-0']}`,
height: '72px',
}}
>
@@ -81,7 +82,7 @@ function TransactionRowItem({ transaction }: TransactionHistoryItemProps) {
}}
>
-
+
{unixTimestampToFormattedTime({ unixTimestamp: timestamp })}
@@ -120,7 +121,7 @@ function TransactionRowItem({ transaction }: TransactionHistoryItemProps) {
sx={{
marginLeft: '5px',
fontSize: '20px',
- color: 'text.secondary',
+ color: 'fg-2',
}}
>
diff --git a/src/modules/history/actions/ActionDetails.tsx b/src/modules/history/actions/ActionDetails.tsx
index 9d1bcf67f4..7dcab0823f 100644
--- a/src/modules/history/actions/ActionDetails.tsx
+++ b/src/modules/history/actions/ActionDetails.tsx
@@ -1,6 +1,6 @@
import { ArrowNarrowRightIcon } from '@heroicons/react/outline';
import { Trans } from '@lingui/macro';
-import { Box, SvgIcon, Typography, useTheme } from '@mui/material';
+import { Box, SvgIcon, Typography } from '@mui/material';
import { formatUnits } from 'ethers/lib/utils';
import React from 'react';
import { DarkTooltip } from 'src/components/infoTooltips/DarkTooltip';
@@ -14,6 +14,7 @@ import {
isOrderLoading,
} from 'src/components/transactions/Swap/helpers/cow';
import { swapTypesThatRequiresInvertedQuote } from 'src/components/transactions/Swap/hooks/useSwapQuote';
+import { figVars } from 'src/utils/figmaColors';
import {
ActionName,
@@ -73,7 +74,6 @@ const StatusBadgeIconOnly = ({
title: React.ReactNode;
severity: 'info' | 'success' | 'error';
}) => {
- const theme = useTheme();
return (
@@ -87,7 +87,7 @@ const StatusBadgeIconOnly = ({
pl: 1.5,
background: 'none',
border: 'none',
- color: theme.palette.text.primary,
+ color: 'fg-1',
}}
/>
@@ -102,7 +102,6 @@ const StatusBadgeText = ({
children: React.ReactNode;
severity: 'info' | 'success' | 'error';
}) => {
- const theme = useTheme();
return (
{children}
@@ -139,26 +138,17 @@ export const ActionDetails = ({
return (
-
+
{symbol}
-
- ${amount.usd}
-
+ ${amount.usd}
-
-
- {reserve.underlyingToken.symbol}
-
+
+ {reserve.underlyingToken.symbol}
}
@@ -168,8 +158,8 @@ export const ActionDetails = ({
+
{reserve.underlyingToken.name} ({reserve.underlyingToken.symbol})
}
arrow
placement="top"
>
-
+
{reserve.underlyingToken.symbol}
@@ -200,7 +190,7 @@ export const ActionDetails = ({
return (
-
+
Liquidated collateral
@@ -208,24 +198,21 @@ export const ActionDetails = ({
symbol={collateral.reserve.underlyingToken.symbol}
sx={{ fontSize: iconSize, pr: 0.5 }}
/>
-
+
−
-
- ${collateral.amount!.usd}
-
+ ${collateral.amount!.usd}
-
+
{collateral.reserve.underlyingToken.symbol}
@@ -237,15 +224,15 @@ export const ActionDetails = ({
-
+
{collateral.reserve.underlyingToken.symbol}
@@ -256,7 +243,7 @@ export const ActionDetails = ({
-
+
Covered debt
@@ -264,24 +251,21 @@ export const ActionDetails = ({
symbol={debtRepaid.reserve.underlyingToken.symbol}
sx={{ fontSize: iconSize, pr: 0.5 }}
/>
-
+
+
-
- ${debtRepaid.amount.usd}
-
+ ${debtRepaid.amount.usd}
-
+
{debtRepaid.reserve.underlyingToken.symbol}
@@ -293,15 +277,15 @@ export const ActionDetails = ({
-
+
{debtRepaid.reserve.underlyingToken.symbol}
@@ -319,7 +303,7 @@ export const ActionDetails = ({
return (
-
+
Collateralization
{enabled ? (
@@ -331,20 +315,20 @@ export const ActionDetails = ({
disabled
)}
-
+
for
+
{reserve.underlyingToken.name} ({reserve.underlyingToken.symbol})
}
arrow
placement="top"
>
-
+
{reserve.underlyingToken.symbol}
@@ -382,7 +366,7 @@ export const ActionDetails = ({
/>
+
{formatUnits(swapTx.srcAmount, swapTx.underlyingSrcToken.decimals)}{' '}
{formattedCowSwapSrcToken.symbol}
@@ -395,8 +379,8 @@ export const ActionDetails = ({
@@ -404,14 +388,14 @@ export const ActionDetails = ({
+
{formattedCowSwapSrcToken.name} ({formattedCowSwapSrcToken.symbol})
}
arrow
placement="top"
>
-
+
{formattedCowSwapSrcToken.symbol}
@@ -427,7 +411,7 @@ export const ActionDetails = ({
/>
+
{formatUnits(swapTx.destAmount, swapTx.underlyingDestToken.decimals)}{' '}
{formattedCowSwapDestToken.symbol}
@@ -440,8 +424,8 @@ export const ActionDetails = ({
@@ -449,14 +433,14 @@ export const ActionDetails = ({
+
{formattedCowSwapDestToken.name} ({formattedCowSwapDestToken.symbol})
}
arrow
placement="top"
>
-
+
{formattedCowSwapDestToken.symbol}
@@ -541,7 +525,7 @@ export const ActionDetails = ({
// FALLBACK
return (
-
+
Transaction details not available
diff --git a/src/modules/markets/Gho/GhoBanner.tsx b/src/modules/markets/Gho/GhoBanner.tsx
index c8dbfaddb7..fa3e9e51d5 100644
--- a/src/modules/markets/Gho/GhoBanner.tsx
+++ b/src/modules/markets/Gho/GhoBanner.tsx
@@ -1,13 +1,13 @@
import { Stake } from '@aave/contract-helpers';
import { Trans } from '@lingui/macro';
import { Box, Button, Skeleton, Stack, Typography, useMediaQuery, useTheme } from '@mui/material';
+import { ReactNode } from 'react';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
import { Link, ROUTES } from 'src/components/primitives/Link';
-import { TokenIcon } from 'src/components/primitives/TokenIcon';
import { useUserStakeUiData } from 'src/hooks/stake/useUserStakeUiData';
-import { useModalContext } from 'src/hooks/useModal';
import { useSGhoVaultContext } from 'src/modules/sGho/SGhoVaultContext';
import { useRootStore } from 'src/store/root';
+import { figVars } from 'src/utils/figmaColors';
/**
* Whether the connected user has any legacy stkGHO (formerly "sGHO") staked.
@@ -21,19 +21,89 @@ const useHasLegacyStkGhoPosition = () => {
return !!stkGhoRedeemable && stkGhoRedeemable !== '0';
};
-export const SavingsGhoBanner = () => {
+/**
+ * Shared card surface (radius, hairline border, green→neutral tint gradient, soft shadow) for both
+ * banner variants — the diverging layout (row/6rem vs column/188px) is applied per variant on top.
+ */
+const BANNER_SURFACE_SX = {
+ borderRadius: '0.75rem',
+ border: '1px solid',
+ borderColor: 'border-1',
+ backgroundColor: 'bg-2',
+ backgroundImage: `linear-gradient(90deg, ${figVars['sgho-banner-green']} 0%, ${figVars['sgho-banner-fade']} 42.79%)`,
+ boxShadow: `0px 2px 4px 0px ${figVars['shadow-low']}`,
+ position: 'relative',
+} as const;
+
+/** Title + subtitle, shared by the desktop and mobile banner variants. */
+const SavingsGhoBannerHeading = ({ hasLegacyPosition }: { hasLegacyPosition: boolean }) => (
+
+
+ {hasLegacyPosition ? (
+ Migrate your sGHO position
+ ) : (
+ Earn into sGHO
+ )}
+
+
+ {hasLegacyPosition ? (
+ To continue claiming rewards, migrate now.
+ ) : (
+ GHO yield with instant withdraws.
+ )}
+
+
+);
+
+/** One labelled stat (label above value), shared by both variants; shows a skeleton while loading. */
+const BannerStat = ({
+ label,
+ loading,
+ children,
+}: {
+ label: ReactNode;
+ loading: boolean;
+ children: ReactNode;
+}) => (
+
+
+ {label}
+
+ {loading ? : children}
+
+);
+
+export const SavingsGhoBanner = ({
+ hasLegacyPositionOverride,
+}: {
+ /**
+ * Showcase/dev only: force the legacy ("Migrate your sGHO position") vs default ("Earn into
+ * sGHO") copy, bypassing wallet-based detection so /dev/components can show both states.
+ */
+ hasLegacyPositionOverride?: boolean;
+} = {}) => {
const theme = useTheme();
- const isCustomBreakpoint = useMediaQuery('(min-width:1125px)');
- const isMd = useMediaQuery(theme.breakpoints.up('xs'));
- const isMd2 = useMediaQuery(theme.breakpoints.up('md'));
- const downToMd = useMediaQuery('(min-width:870px)');
- const downToSm = useMediaQuery('(max-width:780px)');
+ const downToSm = useMediaQuery(theme.breakpoints.down('sm'));
- const { openStkGhoMigrate } = useModalContext();
const { vault, loading: vaultLoading } = useSGhoVaultContext();
const totalDepositedUSD = vault?.totalAssets.usd ?? '0';
const targetRate = vault?.targetRate ? +vault.targetRate.value : 0;
- const hasLegacyPosition = useHasLegacyStkGhoPosition();
+ const detectedLegacyPosition = useHasLegacyStkGhoPosition();
+ const hasLegacyPosition = hasLegacyPositionOverride ?? detectedLegacyPosition;
if (downToSm) {
return ;
@@ -54,143 +124,43 @@ export const SavingsGhoBanner = () => {
({
- [theme.breakpoints.up(780)]: {
- height: '116px',
- flexDirection: 'row',
- alignItems: 'center',
- },
- flexDirection: 'column',
- alignItems: 'flex-start',
- height: '188px',
- borderRadius: { md: 4 },
+ sx={{
+ ...BANNER_SURFACE_SX,
display: 'flex',
- backgroundColor: theme.palette.mode === 'dark' ? '#39375A80' : '#F7F7F9',
- position: 'relative',
+ flexDirection: 'row',
+ alignItems: 'center',
justifyContent: 'space-between',
- gap: { xs: 6 },
- })}
+ gap: '1.5rem',
+ height: '6rem',
+ padding: '1.25rem 2rem 1.25rem 1.5rem',
+ overflow: 'hidden',
+ }}
>
-
-
- ({
- [theme.breakpoints.up(1125)]: { typography: 'h3' },
- typography: {
- xs: 'subheader1',
- md: 'h4',
- },
- })}
- >
- {hasLegacyPosition ? (
- Migrate your sGHO position
- ) : (
- Earn into sGHO
- )}
-
- ({
- [theme.breakpoints.up(1125)]: { typography: 'description' },
- typography: { xs: 'caption' },
- })}
- color="text.secondary"
- >
- {hasLegacyPosition ? (
- To continue claiming rewards, migrate now.
- ) : (
- GHO yield with instant withdraws.
- )}
-
-
-
-
+
-
- {vaultLoading ? (
-
- ) : (
-
-
-
- )}
-
- Total deposited
-
-
-
-
- {vaultLoading ? (
-
- ) : (
-
-
-
- )}
-
- APY
-
+
-
-
+ Total deposited} loading={vaultLoading}>
+
+
+ APY} loading={vaultLoading}>
+
+
+
View details
- {
- if (hasLegacyPosition) openStkGhoMigrate();
- }}
- >
- {hasLegacyPosition ? Migrate : Start Earning}
-
@@ -198,7 +168,6 @@ export const SavingsGhoBanner = () => {
};
const GhoSavingsBannerMobile = ({ hasLegacyPosition }: { hasLegacyPosition: boolean }) => {
- const { openStkGhoMigrate } = useModalContext();
const { vault, loading: vaultLoading } = useSGhoVaultContext();
const totalDepositedUSD = vault?.totalAssets.usd ?? '0';
const targetRate = vault?.targetRate ? +vault.targetRate.value : 0;
@@ -218,27 +187,20 @@ const GhoSavingsBannerMobile = ({ hasLegacyPosition }: { hasLegacyPosition: bool
({
- [theme.breakpoints.up(780)]: {
- height: '116px',
- flexDirection: 'row',
- alignItems: 'center',
- },
+ sx={{
+ ...BANNER_SURFACE_SX,
+ display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
- height: '188px',
- borderRadius: { md: 4 },
- display: 'flex',
- backgroundColor: theme.palette.mode === 'dark' ? '#39375A80' : '#F7F7F9',
- position: 'relative',
justifyContent: 'space-between',
+ height: '188px',
gap: { xs: 6 },
- })}
+ }}
>
-
-
- {hasLegacyPosition ? (
- Migrate your sGHO position
- ) : (
- Earn into sGHO
- )}
-
-
- {hasLegacyPosition ? (
- To continue claiming rewards, migrate now.
- ) : (
- GHO yield with instant withdraws.
- )}
-
-
+
-
-
- {vaultLoading ? (
-
- ) : (
-
-
-
- )}
-
- Total deposited
-
-
-
- {vaultLoading ? (
-
- ) : (
-
-
-
- )}
-
- APY
-
-
+ Total deposited} loading={vaultLoading}>
+
+
+ APY} loading={vaultLoading}>
+
+
-
+
View details
- {
- if (hasLegacyPosition) openStkGhoMigrate();
- }}
- >
- {hasLegacyPosition ? Migrate : Start Earning}
-
diff --git a/src/modules/markets/MarketAssetsList.tsx b/src/modules/markets/MarketAssetsList.tsx
index 7c88146a0b..8e077e9ef3 100644
--- a/src/modules/markets/MarketAssetsList.tsx
+++ b/src/modules/markets/MarketAssetsList.tsx
@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro';
-import { useMediaQuery } from '@mui/material';
+import { useMediaQuery, useTheme } from '@mui/material';
import { useState } from 'react';
import { mapAaveProtocolIncentives } from 'src/components/incentives/incentives.helper';
import { VariableAPYTooltip } from 'src/components/infoTooltips/VariableAPYTooltip';
@@ -52,7 +52,8 @@ export type ReserveWithProtocolIncentives = ReserveWithId & {
};
export default function MarketAssetsList({ reserves, loading }: MarketAssetsListProps) {
- const isTableChangedToCards = useMediaQuery('(max-width:1125px)');
+ const theme = useTheme();
+ const isTableChangedToCards = useMediaQuery(theme.breakpoints.down('mdlg'));
const [sortName, setSortName] = useState('');
const [sortDesc, setSortDesc] = useState(false);
const sortedReserves = [...reserves].sort((a, b) => {
diff --git a/src/modules/markets/MarketAssetsListContainer.tsx b/src/modules/markets/MarketAssetsListContainer.tsx
index 57ed7eab46..aea8df05f0 100644
--- a/src/modules/markets/MarketAssetsListContainer.tsx
+++ b/src/modules/markets/MarketAssetsListContainer.tsx
@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro';
-import { Box, Divider, Switch, Typography, useMediaQuery, useTheme } from '@mui/material';
+import { Box, Divider, FormControlLabel, Switch, useMediaQuery, useTheme } from '@mui/material';
import { useState } from 'react';
import { AssetCategoryMultiSelect } from 'src/components/AssetCategoryMultiselect';
import { ListWrapper } from 'src/components/lists/ListWrapper';
@@ -134,7 +134,6 @@ export const MarketAssetsListContainer = () => {
return (
{
>
{frozenOrPausedReserves.length > 0 && (
<>
-
-
- Show frozen/paused assets
-
-
-
+
+ }
+ label={Show frozen/paused assets}
+ componentsProps={{ typography: { variant: 'subheader1' } }}
+ />
{
/>
>
)}
-
-
- {'Show assets <$100k supply'}
-
- setShowLowLiquidityToggle((prev) => !prev)}
- inputProps={{ 'aria-label': 'show assets under 100k supply' }}
- />
-
+ setShowLowLiquidityToggle((prev) => !prev)}
+ inputProps={{ 'aria-label': 'show assets under 100k supply' }}
+ />
+ }
+ label={{'Show assets <$100k supply'}}
+ componentsProps={{ typography: { variant: 'subheader1' } }}
+ />
);
diff --git a/src/modules/markets/MarketAssetsListItem.tsx b/src/modules/markets/MarketAssetsListItem.tsx
index f4fce15f03..03e877db83 100644
--- a/src/modules/markets/MarketAssetsListItem.tsx
+++ b/src/modules/markets/MarketAssetsListItem.tsx
@@ -85,7 +85,7 @@ export const MarketAssetsListItem = ({ ...reserve }: ReserveWithProtocolIncentiv
p: { xs: '0', xsm: '3.625px 0px' },
}}
>
-
+
{reserve.underlyingToken.symbol}
{reserve.isolationModeConfig?.canBeCollateral && (
@@ -101,7 +101,7 @@ export const MarketAssetsListItem = ({ ...reserve }: ReserveWithProtocolIncentiv
-
+
@@ -111,7 +111,7 @@ export const MarketAssetsListItem = ({ ...reserve }: ReserveWithProtocolIncentiv
incentives={reserve.supplyProtocolIncentives}
address={reserve.aToken.address}
symbol={reserve.underlyingToken.symbol}
- variant="main16"
+ variant="h4"
symbolsVariant="secondary16"
tooltip={
<>
@@ -131,12 +131,12 @@ export const MarketAssetsListItem = ({ ...reserve }: ReserveWithProtocolIncentiv
{' '}
>
) : (
-
+
)}
@@ -150,7 +150,7 @@ export const MarketAssetsListItem = ({ ...reserve }: ReserveWithProtocolIncentiv
incentives={reserve.borrowProtocolIncentives}
address={reserve.vToken.address}
symbol={reserve.underlyingToken.symbol}
- variant="main16"
+ variant="h4"
symbolsVariant="secondary16"
tooltip={
<>
diff --git a/src/modules/markets/MarketAssetsListMobileItem.tsx b/src/modules/markets/MarketAssetsListMobileItem.tsx
index 41e5325b7f..c83c79a562 100644
--- a/src/modules/markets/MarketAssetsListMobileItem.tsx
+++ b/src/modules/markets/MarketAssetsListMobileItem.tsx
@@ -71,7 +71,7 @@ export const MarketAssetsListMobileItem = ({ ...reserve }: ReserveWithProtocolIn
textAlign: 'center',
}}
>
-
+
@@ -87,7 +87,7 @@ export const MarketAssetsListMobileItem = ({ ...reserve }: ReserveWithProtocolIn
incentives={reserve.supplyProtocolIncentives}
address={reserve.aToken.address}
symbol={reserve.underlyingToken.symbol}
- variant="secondary14"
+ variant="h5"
tooltip={
<>
{externalIncentivesTooltipsSupplySide.superFestRewards && }
@@ -117,12 +117,12 @@ export const MarketAssetsListMobileItem = ({ ...reserve }: ReserveWithProtocolIn
>
) : (
-
+
)}
@@ -148,7 +148,7 @@ export const MarketAssetsListMobileItem = ({ ...reserve }: ReserveWithProtocolIn
incentives={reserve.borrowProtocolIncentives}
address={reserve.vToken.address}
symbol={reserve.underlyingToken.symbol}
- variant="secondary14"
+ variant="h5"
tooltip={
<>
{externalIncentivesTooltipsBorrowSide.superFestRewards && }
diff --git a/src/modules/markets/MarketsTopPanel.tsx b/src/modules/markets/MarketsTopPanel.tsx
index 500406ed7c..b5351cfdc4 100644
--- a/src/modules/markets/MarketsTopPanel.tsx
+++ b/src/modules/markets/MarketsTopPanel.tsx
@@ -1,60 +1,52 @@
import { Trans } from '@lingui/macro';
import { useMediaQuery, useTheme } from '@mui/material';
import { marketContainerProps } from 'pages/markets.page';
-import * as React from 'react';
+import { MarketSwitcher } from '../../components/MarketSwitcher';
+import { PageHeader } from '../../components/PageHeader/PageHeader';
+import { PageHeaderStat } from '../../components/PageHeader/PageHeaderStat';
import { FormattedNumber } from '../../components/primitives/FormattedNumber';
-import { TopInfoPanel } from '../../components/TopInfoPanel/TopInfoPanel';
-import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem';
import { useAppDataContext } from '../../hooks/app-data-provider/useAppDataProvider';
export const MarketsTopPanel = () => {
const { market, totalBorrows, loading } = useAppDataContext();
const theme = useTheme();
const downToSM = useMediaQuery(theme.breakpoints.down('sm'));
- const valueTypographyVariant = downToSM ? 'main16' : 'main21';
- const symbolsVariant = downToSM ? 'secondary16' : 'secondary21';
+ const valueTypographyVariant = downToSM ? 'h4' : 'h2';
return (
- }
containerProps={marketContainerProps}
- pageTitle={Markets}
- withMarketSwitcher
- withFavoriteButton
>
- Total market size} loading={loading}>
+ Total market size} loading={loading}>
-
- Total available} loading={loading}>
+
+ Total available} loading={loading}>
-
- Total borrows} loading={loading}>
+
+ Total borrows} loading={loading}>
-
-
+
+
);
};
diff --git a/src/modules/markets/utils/assetCategories.ts b/src/modules/markets/utils/assetCategories.ts
index 3f3ca40db9..3586bf8749 100644
--- a/src/modules/markets/utils/assetCategories.ts
+++ b/src/modules/markets/utils/assetCategories.ts
@@ -65,3 +65,21 @@ export const isAssetInCategoryDynamic = (
ethCorrelatedCoinGeckoSymbols
).includes(category);
};
+
+// Category-filter predicate shared by the market / dashboard / staking asset lists: an asset matches
+// when nothing is selected (show all) or it falls into at least one selected category.
+export const matchesSelectedCategories = (
+ symbol: string,
+ selectedCategories: AssetCategory[],
+ stablecoinCoinGeckoSymbols: string[] = [],
+ ethCorrelatedCoinGeckoSymbols: string[] = []
+): boolean =>
+ selectedCategories.length === 0 ||
+ selectedCategories.some((category) =>
+ isAssetInCategoryDynamic(
+ symbol,
+ category,
+ stablecoinCoinGeckoSymbols,
+ ethCorrelatedCoinGeckoSymbols
+ )
+ );
diff --git a/src/modules/migration/HFChange.tsx b/src/modules/migration/HFChange.tsx
index 719ef46690..9705604574 100644
--- a/src/modules/migration/HFChange.tsx
+++ b/src/modules/migration/HFChange.tsx
@@ -22,12 +22,12 @@ export const HFChange = ({ caption, hfCurrent, hfAfter, loading }: HFChangeProps
{!loading ? : }
-
+
{!loading ? : }
-
+
Liquidation at
{' <1.0'}
diff --git a/src/modules/migration/MigrationBottomPanel.tsx b/src/modules/migration/MigrationBottomPanel.tsx
index fbc9081ead..556c1dafba 100644
--- a/src/modules/migration/MigrationBottomPanel.tsx
+++ b/src/modules/migration/MigrationBottomPanel.tsx
@@ -1,7 +1,7 @@
import { valueToBigNumber } from '@aave/math-utils';
import { ExclamationIcon } from '@heroicons/react/solid';
import { Trans } from '@lingui/macro';
-import { ArrowDownward } from '@mui/icons-material';
+import ArrowDownward from '@mui/icons-material/ArrowDownward';
import {
Box,
Button,
@@ -180,8 +180,8 @@ export const MigrationBottomPanel = ({
/>
diff --git a/src/modules/migration/MigrationList.tsx b/src/modules/migration/MigrationList.tsx
index 61e45753da..19cae07003 100644
--- a/src/modules/migration/MigrationList.tsx
+++ b/src/modules/migration/MigrationList.tsx
@@ -86,7 +86,7 @@ export const MigrationList = ({
return (
@@ -95,7 +95,7 @@ export const MigrationList = ({
{isolatedReserveV3 && !isolatedReserveV3.enteringIsolationMode && (
-
+
Some migrated assets will not be used as collateral due to enabled isolation
mode in {marketName} V3 Market. Visit{' '}
diff --git a/src/modules/migration/MigrationListItem.tsx b/src/modules/migration/MigrationListItem.tsx
index e3f5489bb5..3154fdfa75 100644
--- a/src/modules/migration/MigrationListItem.tsx
+++ b/src/modules/migration/MigrationListItem.tsx
@@ -14,6 +14,7 @@ import { TokenIcon } from 'src/components/primitives/TokenIcon';
import { ComputedUserReserveData } from 'src/hooks/app-data-provider/useAppDataProvider';
import { useRootStore } from 'src/store/root';
import { MigrationDisabled, V3Rates } from 'src/store/v3MigrationSelectors';
+import { figVars } from 'src/utils/figmaColors';
import { useShallow } from 'zustand/shallow';
import { MigrationListItemToggler } from './MigrationListItemToggler';
@@ -61,8 +62,8 @@ export const MigrationListItem = ({
);
const isMobile = useMediaQuery(theme.breakpoints.down(1125));
- const baseColor = disabled === undefined ? 'text.primary' : 'text.muted';
- const baseColorSecondary = disabled === undefined ? 'text.secondary' : 'text.muted';
+ const baseColor = disabled === undefined ? 'fg-1' : 'fg-3';
+ const baseColorSecondary = disabled === undefined ? 'fg-2' : 'fg-3';
const loadingRates = v3Rates?.ltv === undefined && v3Rates?.liquidationThreshold === undefined;
@@ -110,18 +111,16 @@ export const MigrationListItem = ({
>
({
+ sx={{
border: `2px solid ${
- disabled !== undefined
- ? theme.palette.action.disabled
- : theme.palette.text.secondary
+ disabled !== undefined ? figVars['disabled-fg'] : figVars['fg-2']
}`,
background:
disabled !== undefined
- ? theme.palette.background.disabled
+ ? figVars['bg-6']
: checked
- ? theme.palette.text.secondary
- : theme.palette.background.paper,
+ ? figVars['fg-2']
+ : figVars['surface-elevated'],
width: 16,
height: 16,
borderRadius: '2px',
@@ -131,12 +130,12 @@ export const MigrationListItem = ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- })}
+ }}
onClick={disabled !== undefined ? undefined : onCheckboxClick}
data-cy={`migration-checkbox`}
>
{disabled === undefined && (
-
+
)}
@@ -167,7 +166,7 @@ export const MigrationListItem = ({
value={v2APY}
symbol={userReserve.reserve.symbol}
incentives={v2Incentives}
- variant="main14"
+ variant="subheader1"
color={baseColor}
market={currentMarket}
/>
@@ -183,7 +182,7 @@ export const MigrationListItem = ({
value={v3APY}
symbol={userReserve.reserve.symbol}
incentives={v3Incentives}
- variant="main14"
+ variant="subheader1"
color={baseColor}
market={currentMarket}
/>
@@ -197,7 +196,7 @@ export const MigrationListItem = ({
userReserve.reserve.reserveLiquidationThreshold !== '0' ? (
) : (
-
+
)}
@@ -215,7 +214,7 @@ export const MigrationListItem = ({
enabledAsCollateral={enabledAsCollateral}
/>
) : !enabledAsCollateral ? (
-
+
) : isIsolated ? (
-
+
) : (
@@ -247,7 +246,7 @@ export const MigrationListItem = ({
@@ -258,12 +257,7 @@ export const MigrationListItem = ({
}
/>
-
+
))}
@@ -274,7 +268,7 @@ export const MigrationListItem = ({
@@ -292,7 +286,7 @@ export const MigrationListItem = ({
@@ -306,7 +300,7 @@ export const MigrationListItem = ({
{!isSupplyList &&
(loadingRates ? (
-
+
) : (
@@ -314,7 +308,7 @@ export const MigrationListItem = ({
@@ -328,7 +322,7 @@ export const MigrationListItem = ({
@@ -336,10 +330,10 @@ export const MigrationListItem = ({
))}
-
+
[store.currentMarket, store.currentMarketData])
);
const theme = useTheme();
- const baseColorSecondary = disabled === undefined ? 'text.secondary' : 'text.muted';
- const baseColorPrimary = disabled === undefined ? 'text.primary' : 'text.muted';
+ const baseColorSecondary = disabled === undefined ? 'fg-2' : 'fg-3';
+ const baseColorPrimary = disabled === undefined ? 'fg-1' : 'fg-3';
const loadingRates = v3Rates?.ltv === undefined && v3Rates?.liquidationThreshold === undefined;
@@ -88,18 +89,16 @@ export const MigrationListMobileItem = ({
>
({
+ sx={{
border: `2px solid ${
- disabled !== undefined
- ? theme.palette.action.disabled
- : theme.palette.text.secondary
+ disabled !== undefined ? figVars['disabled-fg'] : figVars['fg-2']
}`,
background:
disabled !== undefined
- ? theme.palette.background.disabled
+ ? figVars['bg-6']
: checked
- ? theme.palette.text.secondary
- : theme.palette.background.paper,
+ ? figVars['fg-2']
+ : figVars['surface-elevated'],
width: 16,
height: 16,
borderRadius: '2px',
@@ -109,11 +108,11 @@ export const MigrationListMobileItem = ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- })}
+ }}
onClick={disabled !== undefined ? undefined : onCheckboxClick}
>
{disabled === undefined && (
-
+
)}
@@ -148,11 +147,11 @@ export const MigrationListMobileItem = ({
-
+
@@ -192,7 +191,7 @@ export const MigrationListMobileItem = ({
value={v3APY}
symbol={userReserve.reserve.symbol}
incentives={v3Incentives}
- variant="main14"
+ variant="subheader1"
color={baseColorPrimary}
market={currentMarket}
/>
@@ -216,7 +215,7 @@ export const MigrationListMobileItem = ({
userReserve.reserve.reserveLiquidationThreshold !== '0' ? (
) : (
-
+
)}
@@ -234,7 +233,7 @@ export const MigrationListMobileItem = ({
enabledAsCollateral={enabledAsCollateral}
/>
) : !enabledAsCollateral ? (
-
+
) : isIsolated ? (
{loadingRates ? (
-
+
) : (
<>
@@ -336,7 +335,7 @@ export const MigrationListMobileItem = ({
>
@@ -359,13 +358,13 @@ export const MigrationListMobileItem = ({
{loadingRates ? (
-
+
) : (
<>
@@ -381,7 +380,7 @@ export const MigrationListMobileItem = ({
>
diff --git a/src/modules/migration/MigrationLists.tsx b/src/modules/migration/MigrationLists.tsx
index 419c33f6dc..4f4ab5793f 100644
--- a/src/modules/migration/MigrationLists.tsx
+++ b/src/modules/migration/MigrationLists.tsx
@@ -57,6 +57,7 @@ export const MigrationLists = ({
return (
= ({
sx={{
padding: '12px 16px 16px 16px',
border: 1,
- borderColor: 'divider',
+ borderColor: 'border-2',
borderRadius: 3,
width: '100%',
}}
>
-
+
From
@@ -107,7 +108,7 @@ export const MigrationMarketCard: FC = ({
{selectableMarkets.map((selectableMarket) => (
-
+
{selectableMarket.title}
@@ -130,7 +131,7 @@ export const MigrationMarketCard: FC = ({
>
-
+
{`${market.marketTitle}${market.isFork ? ' Fork' : ''}`}
@@ -152,7 +153,7 @@ export const MigrationMarketCard: FC = ({
) : (
)}
-
+
{!loading && userSummaryAfterMigration ? (
diff --git a/src/modules/migration/MigrationMobileList.tsx b/src/modules/migration/MigrationMobileList.tsx
index 9b5aedeb50..7af668bb09 100644
--- a/src/modules/migration/MigrationMobileList.tsx
+++ b/src/modules/migration/MigrationMobileList.tsx
@@ -34,6 +34,10 @@ export const MigrationMobileList = ({
return (
{titleComponent}
@@ -59,7 +63,7 @@ export const MigrationMobileList = ({
justifyContent: 'center',
}}
>
-
+
{numSelected}/{numAvailable} assets selected
diff --git a/src/modules/migration/MigrationSelectionBox.tsx b/src/modules/migration/MigrationSelectionBox.tsx
index c5508a75e0..8196cdb8f1 100644
--- a/src/modules/migration/MigrationSelectionBox.tsx
+++ b/src/modules/migration/MigrationSelectionBox.tsx
@@ -1,6 +1,7 @@
import { CheckIcon, MinusSmIcon } from '@heroicons/react/solid';
-import { Box, SvgIcon, useTheme } from '@mui/material';
+import { Box, SvgIcon } from '@mui/material';
import { ListHeaderTitle } from 'src/components/lists/ListHeaderTitle';
+import { figVars } from 'src/utils/figmaColors';
interface MigrationSelectionBoxProps {
allSelected: boolean;
@@ -15,10 +16,9 @@ export const MigrationSelectionBox = ({
onSelectAllClick,
disabled,
}: MigrationSelectionBoxProps) => {
- const theme = useTheme();
const selectionBoxStyle = {
- border: `2px solid ${theme.palette.text.secondary}`,
- background: theme.palette.text.secondary,
+ border: `2px solid ${figVars['fg-2']}`,
+ background: figVars['fg-2'],
width: 16,
height: 16,
borderRadius: '2px',
@@ -34,8 +34,8 @@ export const MigrationSelectionBox = ({
{allSelected ? (
-
+
) : numSelected !== 0 ? (
-
+
diff --git a/src/modules/migration/MigrationTopPanel.tsx b/src/modules/migration/MigrationTopPanel.tsx
index cc216f3117..b91b9cdb1c 100644
--- a/src/modules/migration/MigrationTopPanel.tsx
+++ b/src/modules/migration/MigrationTopPanel.tsx
@@ -25,7 +25,7 @@ export const MigrationTopPanel = () => {
}}
>
-
+
@@ -132,16 +142,13 @@ export const AddTokenDropdown = ({
keepMounted={true}
data-cy="addToWaletSelector"
>
-
-
- Underlying token
-
-
+
+ Underlying token
+
{
if (currentChainId !== connectedChainId) {
switchNetwork(currentChainId).then(() => {
@@ -166,21 +173,17 @@ export const AddTokenDropdown = ({
handleClose();
}}
>
-
-
- {poolReserve.underlyingToken.symbol}
-
{!hideAToken && (
-
-
-
- Aave aToken
-
-
+ <>
+
+
+ Aave aToken
+
-
-
- {poolReserve.aToken.symbol}
-
-
+ >
)}
{isSGHO && sGHOTokenAddress && (
-
-
-
- Savings GHO token
-
-
+ <>
+
+
+ Savings GHO token
+
-
-
- sGHO
-
+
-
+ >
)}
>
diff --git a/src/modules/reserve-overview/BorrowInfo.tsx b/src/modules/reserve-overview/BorrowInfo.tsx
index 6855b3d65e..54dc2a4b81 100644
--- a/src/modules/reserve-overview/BorrowInfo.tsx
+++ b/src/modules/reserve-overview/BorrowInfo.tsx
@@ -79,11 +79,11 @@ export const BorrowInfo = ({
<>
Maximum amount available to borrow is{' '}
- {' '}
+ {' '}
{reserve.underlyingToken.symbol} (
).
@@ -122,25 +122,22 @@ export const BorrowInfo = ({
}
>
-
+
of
-
+
@@ -159,7 +156,7 @@ export const BorrowInfo = ({
}
>
-
+
)}
@@ -189,7 +186,7 @@ export const BorrowInfo = ({
incentives={borrowProtocolIncentives}
address={reserve.vToken.address}
symbol={reserve.underlyingToken.symbol}
- variant="main16"
+ variant="h4"
market={currentMarketData.market}
protocolAction={ProtocolAction.borrow}
inlineIncentives={true}
@@ -198,7 +195,7 @@ export const BorrowInfo = ({
{reserve.borrowInfo?.borrowCap.usd && reserve.borrowInfo?.borrowCap.usd !== '0' && (
Borrow cap}>
-
+
)}
diff --git a/src/modules/reserve-overview/Gho/GhoReserveTopDetails.tsx b/src/modules/reserve-overview/Gho/GhoReserveTopDetails.tsx
index 1178591e83..f5d7790cd7 100644
--- a/src/modules/reserve-overview/Gho/GhoReserveTopDetails.tsx
+++ b/src/modules/reserve-overview/Gho/GhoReserveTopDetails.tsx
@@ -1,10 +1,10 @@
import { valueToBigNumber } from '@aave/math-utils';
import { Trans } from '@lingui/macro';
-import { Box, useMediaQuery, useTheme } from '@mui/material';
+import { useMediaQuery, useTheme } from '@mui/material';
import { BigNumber } from 'bignumber.js';
+import { PageHeaderStat } from 'src/components/PageHeader/PageHeaderStat';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
import { TextWithTooltip } from 'src/components/TextWithTooltip';
-import { TopInfoPanelItem } from 'src/components/TopInfoPanel/TopInfoPanelItem';
import { ReserveWithId, useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider';
export const GhoReserveTopDetails = ({ reserve }: { reserve: ReserveWithId }) => {
@@ -12,8 +12,7 @@ export const GhoReserveTopDetails = ({ reserve }: { reserve: ReserveWithId }) =>
const theme = useTheme();
const downToSM = useMediaQuery(theme.breakpoints.down('sm'));
- const valueTypographyVariant = downToSM ? 'main16' : 'main21';
- const symbolsTypographyVariant = downToSM ? 'secondary16' : 'secondary21';
+ const valueTypographyVariant = downToSM ? 'h4' : 'h2';
const totalBorrowed = BigNumber.min(
valueToBigNumber(reserve.borrowInfo?.total.amount.value ?? '0'),
@@ -22,33 +21,24 @@ export const GhoReserveTopDetails = ({ reserve }: { reserve: ReserveWithId }) =>
return (
<>
- Total borrowed} loading={loading} hideIcon>
-
-
+ Total borrowed} loading={loading}>
+
+
- Maximum available to borrow}
- loading={loading}
- hideIcon
- >
+ Maximum available to borrow} loading={loading}>
-
+
- Price}>
+ Price}
+ sx={{ lineHeight: '0.875rem', letterSpacing: 0 }}
+ >
The Aave Protocol is programmed to always use the price of 1 GHO = $1. This is
different from using market pricing via oracles for other crypto assets. This creates
@@ -57,18 +47,9 @@ export const GhoReserveTopDetails = ({ reserve }: { reserve: ReserveWithId }) =>
}
loading={loading}
- hideIcon
>
-
-
-
-
+
+
>
);
};
diff --git a/src/modules/reserve-overview/Gho/SavingsGho.tsx b/src/modules/reserve-overview/Gho/SavingsGho.tsx
index 22adb4ba23..b713689ac8 100644
--- a/src/modules/reserve-overview/Gho/SavingsGho.tsx
+++ b/src/modules/reserve-overview/Gho/SavingsGho.tsx
@@ -86,11 +86,7 @@ export const SavingsGho = () => {
{stakeDataLoading && }
{!stakeDataLoading && stakeData && (
-
+
{' ('}
{
}
bottomLineComponent={
-
+
Instant
}
@@ -150,15 +146,15 @@ export const SavingsGho = () => {
pt: 2,
}}
>
-
+
Amount in cooldown
diff --git a/src/modules/reserve-overview/ReserveActions.tsx b/src/modules/reserve-overview/ReserveActions.tsx
index d6ee4d9caa..d6d3b98f1f 100644
--- a/src/modules/reserve-overview/ReserveActions.tsx
+++ b/src/modules/reserve-overview/ReserveActions.tsx
@@ -1,7 +1,7 @@
import { API_ETH_MOCK_ADDRESS } from '@aave/contract-helpers';
import { BigNumberValue, USD_DECIMALS, valueToBigNumber } from '@aave/math-utils';
import { Trans } from '@lingui/macro';
-import { Box, Button, Divider, Paper, Skeleton, Stack, Typography, useTheme } from '@mui/material';
+import { Box, Button, Divider, Paper, Skeleton, Stack, Typography } from '@mui/material';
import React, { ReactNode, useState } from 'react';
import { WalletIcon } from 'src/components/icons/WalletIcon';
import { getMarketInfoById } from 'src/components/MarketSwitcher';
@@ -21,6 +21,7 @@ import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
import { BuyWithFiat } from 'src/modules/staking/BuyWithFiat';
import { useRootStore } from 'src/store/root';
import { GENERAL } from 'src/utils/events';
+import { figVars } from 'src/utils/figmaColors';
import {
assetCanBeBorrowedByUser,
getMaxAmountAvailableToBorrow,
@@ -243,7 +244,7 @@ const ActionsSkeleton = () => {
const PaperWrapper = ({ children }: { children: ReactNode }) => {
return (
-
+
Your info
@@ -255,12 +256,12 @@ const PaperWrapper = ({ children }: { children: ReactNode }) => {
const ConnectWallet = () => {
return (
-
+
<>
Your info
-
+
Please connect a wallet to view your personal information here.
@@ -316,8 +317,8 @@ const SupplyAction = ({
@@ -375,8 +376,8 @@ const BorrowAction = ({
@@ -434,8 +435,8 @@ interface ValueWithSymbolProps {
const ValueWithSymbol = ({ value, symbol, children }: ValueWithSymbolProps) => {
return (
-
-
+
+
{symbol}
{children}
@@ -449,26 +450,24 @@ interface WalletBalanceProps {
marketTitle: string;
}
export const WalletBalance = ({ balance, symbol, marketTitle }: WalletBalanceProps) => {
- const theme = useTheme();
-
return (
({
+ sx={{
width: '42px',
height: '42px',
- background: theme.palette.background.surface,
- border: `0.5px solid ${theme.palette.background.disabled}`,
+ background: figVars['bg-2'],
+ border: `0.5px solid ${figVars['bg-6']}`,
borderRadius: '12px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- })}
+ }}
>
-
+
-
+
Wallet balance
diff --git a/src/modules/reserve-overview/ReserveConfiguration.tsx b/src/modules/reserve-overview/ReserveConfiguration.tsx
index b56dbd095e..b1374d0042 100644
--- a/src/modules/reserve-overview/ReserveConfiguration.tsx
+++ b/src/modules/reserve-overview/ReserveConfiguration.tsx
@@ -182,7 +182,7 @@ export const ReserveConfiguration: React.FC = ({ rese
diff --git a/src/modules/reserve-overview/ReserveConfigurationWrapper.tsx b/src/modules/reserve-overview/ReserveConfigurationWrapper.tsx
index 0a0a009e19..37bcc75029 100644
--- a/src/modules/reserve-overview/ReserveConfigurationWrapper.tsx
+++ b/src/modules/reserve-overview/ReserveConfigurationWrapper.tsx
@@ -27,7 +27,7 @@ export const ReserveConfigurationWrapper: React.FC =
});
return (
-
+
= ({ reserve }
@@ -73,7 +73,7 @@ export const ReserveEModePanel: React.FC = ({ reserve }
@@ -88,7 +88,7 @@ export const ReserveEModePanel: React.FC = ({ reserve }
@@ -96,7 +96,7 @@ export const ReserveEModePanel: React.FC = ({ reserve }
))}
-
+
E-Mode increases your LTV for a selected category of assets, meaning that when E-mode is
enabled, you will have higher borrowing power over assets of the same E-mode category
@@ -105,7 +105,7 @@ export const ReserveEModePanel: React.FC = ({ reserve }
href={ROUTES.dashboard}
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
onClick={() => {
trackEvent(RESERVE_DETAILS.GO_DASHBOARD_EMODE);
}}
@@ -117,7 +117,7 @@ export const ReserveEModePanel: React.FC = ({ reserve }
href="https://aave.com/help/borrowing/e-mode"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
onClick={() => {
trackEvent(GENERAL.EXTERNAL_LINK, { Link: 'E-mode FAQ' });
}}
@@ -129,7 +129,7 @@ export const ReserveEModePanel: React.FC = ({ reserve }
href="https://github.com/aave/aave-v3-core/blob/master/techpaper/Aave_V3_Technical_Paper.pdf"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
onClick={() => {
trackEvent(GENERAL.EXTERNAL_LINK, { Link: 'V3 Tech Paper' });
}}
diff --git a/src/modules/reserve-overview/ReserveFactorOverview.tsx b/src/modules/reserve-overview/ReserveFactorOverview.tsx
index 9937620893..cedfa8559a 100644
--- a/src/modules/reserve-overview/ReserveFactorOverview.tsx
+++ b/src/modules/reserve-overview/ReserveFactorOverview.tsx
@@ -58,7 +58,7 @@ export const ReserveFactorOverview = ({
/>
}
>
-
+
-
+
View contract
diff --git a/src/modules/reserve-overview/ReservePanels.tsx b/src/modules/reserve-overview/ReservePanels.tsx
index 70f359669e..57590e363d 100644
--- a/src/modules/reserve-overview/ReservePanels.tsx
+++ b/src/modules/reserve-overview/ReservePanels.tsx
@@ -1,5 +1,6 @@
import { Box, BoxProps, Typography, TypographyProps, useMediaQuery, useTheme } from '@mui/material';
import type { ReactNode } from 'react';
+import { figVars } from 'src/utils/figmaColors';
export const PanelRow: React.FC = (props) => (
= ({ title, children, className
position: 'absolute',
right: 4,
top: 'calc(50% - 17px)',
- borderRight: (theme) => `1px solid ${theme.palette.divider}`,
+ borderRight: `1px solid ${figVars['border-2']}`,
},
}
: {}),
}}
className={className}
>
-
+
{title}
{
const { reserves, loading } = useAppDataContext();
- const [trackEvent, currentNetworkConfig] = useRootStore(
- useShallow((store) => [store.trackEvent, store.currentNetworkConfig])
- );
const theme = useTheme();
const downToSM = useMediaQuery(theme.breakpoints.down('sm'));
@@ -32,30 +23,19 @@ export const ReserveTopDetails = ({ underlyingAsset }: ReserveTopDetailsProps) =
(reserve) => reserve.underlyingAsset === underlyingAsset
) as ComputedReserveData;
- const valueTypographyVariant = downToSM ? 'main16' : 'main21';
- const symbolsTypographyVariant = downToSM ? 'secondary16' : 'secondary21';
-
- const iconStyling = {
- display: 'inline-flex',
- alignItems: 'center',
- color: '#A5A8B6',
- '&:hover': { color: '#F1F1F3' },
- cursor: 'pointer',
- };
+ const valueTypographyVariant = downToSM ? 'h4' : 'h2';
return (
<>
- Reserve Size} loading={loading} hideIcon>
+ Reserve size} loading={loading}>
-
+
- Available liquidity} loading={loading} hideIcon>
+ Available liquidity} loading={loading}>
-
+
- Utilization Rate} loading={loading} hideIcon>
+ Utilization rate} loading={loading}>
-
+
- Oracle price} loading={loading} hideIcon>
-
-
- {loading ? (
-
- ) : (
-
-
- trackEvent(GENERAL.EXTERNAL_LINK, {
- Link: 'Oracle Price',
- oracle: poolReserve?.priceOracle,
- assetName: poolReserve.name,
- asset: poolReserve.underlyingAsset,
- })
- }
- href={currentNetworkConfig.explorerLinkBuilder({
- address: poolReserve?.priceOracle,
- })}
- sx={iconStyling}
- >
-
-
-
-
-
- )}
-
-
+ Oracle price} loading={loading}>
+
+
>
);
};
diff --git a/src/modules/reserve-overview/ReserveTopDetailsWrapper.tsx b/src/modules/reserve-overview/ReserveTopDetailsWrapper.tsx
index d627000594..6763e5f161 100644
--- a/src/modules/reserve-overview/ReserveTopDetailsWrapper.tsx
+++ b/src/modules/reserve-overview/ReserveTopDetailsWrapper.tsx
@@ -1,30 +1,15 @@
import { Trans } from '@lingui/macro';
-import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackOutlined';
-import {
- Box,
- Button,
- Divider,
- Skeleton,
- SvgIcon,
- Typography,
- useMediaQuery,
- useTheme,
-} from '@mui/material';
+import { Box, Skeleton, SvgIcon, Typography } from '@mui/material';
import { useRouter } from 'next/router';
import { getMarketInfoById, MarketLogo } from 'src/components/MarketSwitcher';
-import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
import { useRootStore } from 'src/store/root';
import { fetchIconSymbolAndName } from 'src/ui-config/reservePatches';
import { displayGhoForMintableMarket } from 'src/utils/ghoUtilities';
-import { useShallow } from 'zustand/shallow';
import { TopInfoPanel } from '../../components/TopInfoPanel/TopInfoPanel';
-import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem';
import { useAppDataContext } from '../../hooks/app-data-provider/useAppDataProvider';
-import { AddTokenDropdown } from './AddTokenDropdown';
import { GhoReserveTopDetails } from './Gho/GhoReserveTopDetails';
import { ReserveTopDetails } from './ReserveTopDetails';
-import { TokenLinkDropdown } from './TokenLinkDropdown';
interface ReserveTopDetailsProps {
underlyingAsset: string;
@@ -33,20 +18,9 @@ interface ReserveTopDetailsProps {
export const ReserveTopDetailsWrapper = ({ underlyingAsset }: ReserveTopDetailsProps) => {
const router = useRouter();
const { supplyReserves, loading } = useAppDataContext();
- const [currentMarket, currentChainId] = useRootStore(
- useShallow((state) => [state.currentMarket, state.currentChainId])
- );
+ const currentMarket = useRootStore((state) => state.currentMarket);
const { market, logo } = getMarketInfoById(currentMarket);
- const {
- addERC20Token,
- switchNetwork,
- chainId: connectedChainId,
- currentAccount,
- } = useWeb3Context();
-
- const theme = useTheme();
- const downToSM = useMediaQuery(theme.breakpoints.down('sm'));
const poolReserve = supplyReserves.find(
(reserve) => reserve.underlyingToken.address.toLowerCase() === underlyingAsset?.toLowerCase()
@@ -65,18 +39,15 @@ export const ReserveTopDetailsWrapper = ({ underlyingAsset }: ReserveTopDetailsP
? iconSymbol
: poolReserve!.underlyingToken.symbol;
- const valueTypographyVariant = downToSM ? 'main16' : 'main21';
-
const ReserveIcon = () => {
return (
-
+
{loading ? (
-
+
) : (
)}
@@ -86,9 +57,11 @@ export const ReserveTopDetailsWrapper = ({ underlyingAsset }: ReserveTopDetailsP
const ReserveName = () => {
return loading ? (
-
+
) : (
- {poolReserve.underlyingToken.name}
+
+ {poolReserve.underlyingToken.name}
+
);
};
@@ -100,144 +73,96 @@ export const ReserveTopDetailsWrapper = ({ underlyingAsset }: ReserveTopDetailsP
return (
- {
+ // https://github.com/vercel/next.js/discussions/34980
+ if (!!history.state.idx) router.back();
+ else router.push('/markets');
+ }}
+ sx={{
+ display: 'flex',
+ alignItems: 'center',
+ gap: '0.25rem',
+ width: 'fit-content',
+ mb: '1rem',
+ cursor: 'pointer',
+ color: 'fg-3',
+ '&:hover': { color: 'fg-1' },
+ }}
+ >
+
+
+
+
-
-
-
- }
- onClick={() => {
- // https://github.com/vercel/next.js/discussions/34980
- if (!!history.state.idx) router.back();
- else router.push('/markets');
- }}
- sx={{ mr: 3, mb: downToSM ? '24px' : '0' }}
- >
- Go Back
-
-
-
-
-
- {market.marketTitle} Market
-
- {market.v3 && (
- theme.palette.gradients.aaveGradient,
- }}
- >
- Version 3
-
+ Back
+
+
+ }
+ >
+
+
+
+
+
+
+ {!loading && (
+
+ {poolReserve.underlyingToken.symbol}
+
)}
+
+
+ on
+
+
+
+ {market.marketTitle}
+
+
+
- {downToSM && (
-
-
-
- {!loading && (
-
- {poolReserve.underlyingToken.symbol}
-
- )}
-
-
- {loading ? (
-
- ) : (
-
-
- {currentAccount && (
-
- )}
-
- )}
-
-
-
+
+ {isGho ? (
+
+ ) : (
+
)}
- }
- >
- {!downToSM && (
- <>
- {poolReserve.underlyingToken.symbol}}
- withoutIconWrapper
- icon={}
- loading={loading}
- >
-
-
-
-
-
- {currentAccount && (
-
- )}
-
-
-
-
- >
- )}
- {isGho ? (
-
- ) : (
-
- )}
+
);
};
diff --git a/src/modules/reserve-overview/SupplyInfo.tsx b/src/modules/reserve-overview/SupplyInfo.tsx
index e111353d83..85f2cb498d 100644
--- a/src/modules/reserve-overview/SupplyInfo.tsx
+++ b/src/modules/reserve-overview/SupplyInfo.tsx
@@ -67,7 +67,7 @@ export const SupplyInfo = ({
valueToBigNumber(reserve.supplyInfo.supplyCap.amount.value).toNumber() -
valueToBigNumber(reserve.supplyInfo.total.value).toNumber()
}
- variant="secondary12"
+ variant="subheader2"
/>{' '}
{reserve.underlyingToken.symbol} (
).
@@ -114,26 +114,23 @@ export const SupplyInfo = ({
}
>
-
+
of
-
+
of
@@ -151,7 +148,7 @@ export const SupplyInfo = ({
}
>
-
+
)}
@@ -161,7 +158,7 @@ export const SupplyInfo = ({
incentives={supplyProtocolIncentives}
address={reserve.aToken.address}
symbol={reserve.underlyingToken.symbol}
- variant="main16"
+ variant="h4"
market={currentMarketData.market}
protocolAction={ProtocolAction.supply}
inlineIncentives={true}
@@ -265,7 +262,7 @@ export const SupplyInfo = ({
@@ -289,7 +286,7 @@ export const SupplyInfo = ({
@@ -313,7 +310,7 @@ export const SupplyInfo = ({
diff --git a/src/modules/reserve-overview/TimeRangeSelector.tsx b/src/modules/reserve-overview/TimeRangeSelector.tsx
index 395443be05..a1e5268a4f 100644
--- a/src/modules/reserve-overview/TimeRangeSelector.tsx
+++ b/src/modules/reserve-overview/TimeRangeSelector.tsx
@@ -1,5 +1,6 @@
import { TimeWindow } from '@aave/react';
import { SxProps, Theme, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material';
+import { figVars } from 'src/utils/figmaColors';
export const supportedTimeRangeOptions = ['1m', '3m', '6m', '1y'] as const;
@@ -73,18 +74,18 @@ export const TimeRangeSelector = ({
value={interval}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
- sx={(theme): SxProps | undefined => ({
+ sx={(): SxProps | undefined => ({
'&.MuiToggleButtonGroup-grouped:not(.Mui-selected), &.MuiToggleButtonGroup-grouped&.Mui-disabled':
{
border: '0.5px solid transparent',
- backgroundColor: 'background.surface',
- color: 'action.disabled',
+ backgroundColor: 'bg-2',
+ color: 'disabled-fg',
},
'&.MuiToggleButtonGroup-grouped&.Mui-selected': {
borderRadius: '4px',
- border: `0.5px solid ${theme.palette.divider}`,
+ border: `0.5px solid ${figVars['border-2']}`,
boxShadow: '0px 2px 1px rgba(0, 0, 0, 0.05), 0px 0px 1px rgba(0, 0, 0, 0.25)',
- backgroundColor: 'background.paper',
+ backgroundColor: 'surface-elevated',
},
...props.sx?.button,
})}
diff --git a/src/modules/reserve-overview/TokenLinkDropdown.tsx b/src/modules/reserve-overview/TokenLinkDropdown.tsx
index 15d71038ff..9173a4feba 100644
--- a/src/modules/reserve-overview/TokenLinkDropdown.tsx
+++ b/src/modules/reserve-overview/TokenLinkDropdown.tsx
@@ -1,15 +1,15 @@
import { ExternalLinkIcon } from '@heroicons/react/outline';
import { Trans } from '@lingui/macro';
-import { Box, Menu, MenuItem, SvgIcon, Typography } from '@mui/material';
+import { Box, Divider, Menu, MenuItem, SvgIcon } from '@mui/material';
import * as React from 'react';
import { useState } from 'react';
import { CircleIcon } from 'src/components/CircleIcon';
-import { TokenIcon } from 'src/components/primitives/TokenIcon';
import { ReserveWithId } from 'src/hooks/app-data-provider/useAppDataProvider';
import { useRootStore } from 'src/store/root';
import { useShallow } from 'zustand/shallow';
import { RESERVE_DETAILS } from '../../utils/events';
+import { MenuSectionLabel, TokenMenuItemContent } from './TokenMenuItems';
interface TokenLinkDropdownProps {
poolReserve: ReserveWithId;
@@ -66,8 +66,8 @@ export const TokenLinkDropdown = ({
sx={{
display: 'inline-flex',
alignItems: 'center',
- color: '#A5A8B6',
- '&:hover': { color: '#F1F1F3' },
+ color: 'fg-3',
+ '&:hover': { color: 'fg-1' },
cursor: 'pointer',
}}
>
@@ -87,11 +87,9 @@ export const TokenLinkDropdown = ({
keepMounted={true}
data-cy="addToWaletSelector"
>
-
-
- Underlying token
-
-
+
+ Underlying token
+
{
@@ -109,24 +107,19 @@ export const TokenLinkDropdown = ({
address: poolReserve?.underlyingToken.address.toLowerCase(),
})}
target="_blank"
- divider={showVariableDebtToken}
>
-
-
- {poolReserve.underlyingToken.symbol}
-
{!hideAToken && (
-
-
-
- Aave aToken
-
-
+ <>
+
+
+ Aave aToken
+
-
-
- {poolReserve.aToken.symbol}
-
-
+ >
)}
{showVariableDebtToken && (
-
-
+ <>
+
+
Aave debt token
-
-
- )}
- {showVariableDebtToken && (
- {
- trackEvent(RESERVE_DETAILS.RESERVE_TOKEN_ACTIONS, {
- type: 'Variable Debt',
- assetName: poolReserve.underlyingToken.name,
- asset: poolReserve.underlyingToken.address,
- aToken: poolReserve.aToken.address,
- market: currentMarket,
- variableDebtToken: poolReserve.vToken.address,
- });
- }}
- >
-
-
- {poolReserve.vToken.symbol}
-
-
+
+ {
+ trackEvent(RESERVE_DETAILS.RESERVE_TOKEN_ACTIONS, {
+ type: 'Variable Debt',
+ assetName: poolReserve.underlyingToken.name,
+ asset: poolReserve.underlyingToken.address,
+ aToken: poolReserve.aToken.address,
+ market: currentMarket,
+ variableDebtToken: poolReserve.vToken.address,
+ });
+ }}
+ >
+
+
+ >
)}
>
diff --git a/src/modules/reserve-overview/TokenMenuItems.tsx b/src/modules/reserve-overview/TokenMenuItems.tsx
new file mode 100644
index 0000000000..6637af89bb
--- /dev/null
+++ b/src/modules/reserve-overview/TokenMenuItems.tsx
@@ -0,0 +1,36 @@
+import { Box, ListItemIcon, Typography } from '@mui/material';
+import { ReactNode } from 'react';
+import { TokenIcon } from 'src/components/primitives/TokenIcon';
+
+/** Group heading inside the reserve token dropdowns; the 0.38rem inset aligns it with the rows. */
+export const MenuSectionLabel = ({ children }: { children: ReactNode }) => (
+
+
+ {children}
+
+
+);
+
+interface TokenMenuItemContentProps {
+ symbol: string;
+ label: ReactNode;
+ aToken?: boolean;
+ waToken?: boolean;
+}
+
+/** Icon + symbol row shared by the "view contracts" and "add to wallet" token dropdowns. */
+export const TokenMenuItemContent = ({
+ symbol,
+ label,
+ aToken,
+ waToken,
+}: TokenMenuItemContentProps) => (
+ <>
+
+
+
+
+ {label}
+
+ >
+);
diff --git a/src/modules/reserve-overview/graphs/ApyGraph.tsx b/src/modules/reserve-overview/graphs/ApyGraph.tsx
index 35a14591ac..86869d65a2 100644
--- a/src/modules/reserve-overview/graphs/ApyGraph.tsx
+++ b/src/modules/reserve-overview/graphs/ApyGraph.tsx
@@ -180,7 +180,7 @@ export const ApyGraph = withTooltip(
borderRadius: '99px',
}}
>
-
+
Avg {avgFormatted}%
@@ -303,11 +303,7 @@ export const ApyGraph = withTooltip(
left={tooltipLeft + 40}
style={theme.palette.mode === 'light' ? tooltipStyles : tooltipStylesDark}
>
-
+
{formatDate(getDate(tooltipData), selectedTimeRange)}
(
justifyContent="space-between"
alignItems="center"
>
-
+
{field.text}
-
+
{getData(tooltipData, field.name).toFixed(2)}%
@@ -382,7 +378,7 @@ export const PlaceholderChart = ({
-
+
No data available
diff --git a/src/modules/reserve-overview/graphs/ApyGraphContainer.tsx b/src/modules/reserve-overview/graphs/ApyGraphContainer.tsx
index 644f548bf5..a57c223030 100644
--- a/src/modules/reserve-overview/graphs/ApyGraphContainer.tsx
+++ b/src/modules/reserve-overview/graphs/ApyGraphContainer.tsx
@@ -110,7 +110,7 @@ const ApyGraphContainer = ({
}}
>
-
+
Loading data...
diff --git a/src/modules/reserve-overview/graphs/GraphLegend.tsx b/src/modules/reserve-overview/graphs/GraphLegend.tsx
index 5a4679bd0e..7060b8765c 100644
--- a/src/modules/reserve-overview/graphs/GraphLegend.tsx
+++ b/src/modules/reserve-overview/graphs/GraphLegend.tsx
@@ -23,7 +23,7 @@ export function GraphLegend({
borderRadius: '50%',
}}
/>
-
+
{label.text}
diff --git a/src/modules/reserve-overview/graphs/InterestRateModelGraph.tsx b/src/modules/reserve-overview/graphs/InterestRateModelGraph.tsx
index e5224dccdf..8e229fafcc 100644
--- a/src/modules/reserve-overview/graphs/InterestRateModelGraph.tsx
+++ b/src/modules/reserve-overview/graphs/InterestRateModelGraph.tsx
@@ -378,7 +378,7 @@ export const InterestRateModelGraph = withTooltip(
parseFloat(reserve.totalDebtUSD) >
0 ? (
<>
-
+
Borrow amount to reach {tooltipData.utilization}% utilization
@@ -394,7 +394,7 @@ export const InterestRateModelGraph = withTooltip(
>
) : (
<>
-
+
Repayment amount to reach {tooltipData.utilization}% utilization
@@ -417,10 +417,10 @@ export const InterestRateModelGraph = withTooltip(
{fields.map((field) => (
-
+
{field.text}
-
+
{tooltipValueAccessors[field.name](tooltipData).toFixed(2)}%
diff --git a/src/modules/reserve-overview/graphs/MeritApyGraph.tsx b/src/modules/reserve-overview/graphs/MeritApyGraph.tsx
index 40d848a32c..d2ab2b03f6 100644
--- a/src/modules/reserve-overview/graphs/MeritApyGraph.tsx
+++ b/src/modules/reserve-overview/graphs/MeritApyGraph.tsx
@@ -196,7 +196,7 @@ export const MeritApyGraph = withTooltip(
borderRadius: '99px',
}}
>
-
+
Avg {averageLine.avgFormatted}%
@@ -287,18 +287,14 @@ export const MeritApyGraph = withTooltip(
left={tooltipLeft + 40}
style={theme.palette.mode === 'light' ? tooltipStyles : tooltipStylesDark}
>
-
+
{formatDate(getDate(tooltipData))}
-
+
Merit APY
-
+
{getMeritApy(tooltipData).toFixed(2)}%
diff --git a/src/modules/reserve-overview/graphs/MeritApyGraphContainer.tsx b/src/modules/reserve-overview/graphs/MeritApyGraphContainer.tsx
index 5de95b4252..2581e3e2b0 100644
--- a/src/modules/reserve-overview/graphs/MeritApyGraphContainer.tsx
+++ b/src/modules/reserve-overview/graphs/MeritApyGraphContainer.tsx
@@ -98,7 +98,7 @@ export const MeritApyGraphContainer = ({
}}
>
-
+
Loading data...
diff --git a/src/modules/sGho/SGhoCard.tsx b/src/modules/sGho/SGhoCard.tsx
index 12fb435a21..c45c8cca98 100644
--- a/src/modules/sGho/SGhoCard.tsx
+++ b/src/modules/sGho/SGhoCard.tsx
@@ -42,6 +42,7 @@ export const SGhoCard = () => {
return (
({
+ sx={{
display: 'flex',
alignItems: { xs: 'stretch', xsm: 'center' },
justifyContent: 'space-between',
flexDirection: { xs: 'column', xsm: 'row' },
gap: 4,
borderRadius: { xs: '8px', xsm: '6px' },
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
p: 4,
mb: 6,
- background: theme.palette.background.paper,
- })}
+ background: figVars['surface-elevated'],
+ }}
>
@@ -43,13 +44,13 @@ export const SGhoDepositRow = ({
sGHO
-
+
Available to deposit:
@@ -66,10 +67,10 @@ export const SGhoDepositRow = ({
}}
>
-
+
Staking APR
-
+
{hasGho ? (
diff --git a/src/modules/sGho/SGhoHeader.tsx b/src/modules/sGho/SGhoHeader.tsx
index ef6d450ab9..c2af1d9551 100644
--- a/src/modules/sGho/SGhoHeader.tsx
+++ b/src/modules/sGho/SGhoHeader.tsx
@@ -1,16 +1,16 @@
import { Trans } from '@lingui/macro';
-import { Box, Stack, Typography, useMediaQuery, useTheme } from '@mui/material';
+import { Typography, useMediaQuery, useTheme } from '@mui/material';
import NumberFlow from '@number-flow/react';
import { BigNumber } from 'bignumber.js';
import { useEffect, useState } from 'react';
+import { PageHeader } from 'src/components/PageHeader/PageHeader';
+import { PageHeaderStat } from 'src/components/PageHeader/PageHeaderStat';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
import { TokenIcon } from 'src/components/primitives/TokenIcon';
import { TextWithTooltip } from 'src/components/TextWithTooltip';
-import { TopInfoPanel } from 'src/components/TopInfoPanel/TopInfoPanel';
import { useSGhoVaultContext } from 'src/modules/sGho/SGhoVaultContext';
import { useRootStore } from 'src/store/root';
-
-import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem';
+import { convertAprToApy } from 'src/utils/utils';
export const SGHOHeader: React.FC = () => {
const theme = useTheme();
@@ -23,16 +23,13 @@ export const SGHOHeader: React.FC = () => {
});
}, [trackEvent]);
- const upToLG = useMediaQuery(theme.breakpoints.up('lg'));
const downToSM = useMediaQuery(theme.breakpoints.down('sm'));
- const downToXSM = useMediaQuery(theme.breakpoints.down('xsm'));
- const valueTypographyVariant = downToSM ? 'main16' : 'main21';
- const symbolsTypographyVariant = downToSM ? 'secondary16' : 'secondary21';
- const symbolsColor = theme.palette.text.muted;
- const iconSize = valueTypographyVariant === 'main21' ? 20 : 16;
+ const valueTypographyVariant = downToSM ? 'h4' : 'h2';
+ const iconSize = valueTypographyVariant === 'h2' ? 20 : 16;
const apr = vault?.targetRate ? +vault.targetRate.value : 0;
+ const apyPercent = (convertAprToApy(apr) * 100).toFixed(2);
const totalDepositedUSD = vault?.totalAssets?.usd ?? '0';
const totalAssetsValue = vault?.totalAssets ? +vault.totalAssets.amount.value : 0;
@@ -54,77 +51,45 @@ export const SGHOHeader: React.FC = () => {
}, [weeklyRewardsEstimate]);
return (
-
-
-
-
- Savings GHO
-
-
-
-
-
- Deposit GHO into Savings GHO (sGHO) and earn{' '}
-
- {(apr * 100).toFixed(2)}%
- {' '}
- APR on your GHO holdings. There are no lockups, no rehypothecation, and you can
- withdraw anytime. Simply deposit GHO, receive sGHO tokens representing your balance,
- and watch your savings grow.
-
-
-
+ Savings GHO}
+ titleIcon={}
+ description={
+
+ Deposit GHO into Savings GHO (sGHO) and earn {apyPercent}% APY on your GHO holdings.
+
}
>
- Current APR} loading={loading}>
-
-
+ Current APR} loading={loading}>
+
+
- Total Deposited} loading={loading}>
+ Total Deposited} loading={loading}>
-
+
- Price} loading={loading}>
+ Price} loading={loading}>
-
+
-
- Weekly Rewards} variant="inherit">
-
- Estimated weekly rewards based on your current sGHO balance and APR. Actual rewards
- may vary depending on market conditions.
-
-
-
+ Weekly Rewards} variant="inherit">
+
+ Estimated weekly rewards based on your current sGHO balance and APR. Actual rewards
+ may vary depending on market conditions.
+
+
}
loading={loading}
>
@@ -167,11 +132,11 @@ export const SGHOHeader: React.FC = () => {
) : (
-
+
—
)}
-
-
+
+
);
};
diff --git a/src/modules/sGho/SGhoLoggedOutPreview.tsx b/src/modules/sGho/SGhoLoggedOutPreview.tsx
index 8347fe2d72..2c70f34fba 100644
--- a/src/modules/sGho/SGhoLoggedOutPreview.tsx
+++ b/src/modules/sGho/SGhoLoggedOutPreview.tsx
@@ -1,6 +1,7 @@
import { Trans } from '@lingui/macro';
import { Box, Button, Typography, useMediaQuery, useTheme } from '@mui/material';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
+import { figVars } from 'src/utils/figmaColors';
import { StakeActionBox } from '../staking/StakeActionBox';
@@ -24,28 +25,28 @@ export const SGhoLoggedOutPreview = ({ rate }: SGhoLoggedOutPreviewProps) => {
Deposit GHO
-
+
Deposit GHO and earn up to {(rate * 100).toFixed(2)}% APR
({
+ sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
borderRadius: { xs: '8px', xsm: '6px' },
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
p: 4,
mb: 6,
- background: theme.palette.background.paper,
- })}
+ background: figVars['surface-elevated'],
+ }}
>
-
+
Staking APR
-
+
{
valueUSD="0"
dataCy="sghoBalanceBox_loggedOut"
bottomLineTitle={
-
+
Cooldown period
}
bottomLineComponent={
-
+
Instant
}
diff --git a/src/modules/sGho/SGhoSavingsRate.tsx b/src/modules/sGho/SGhoSavingsRate.tsx
index 8b34d53daf..bdefe0d4ea 100644
--- a/src/modules/sGho/SGhoSavingsRate.tsx
+++ b/src/modules/sGho/SGhoSavingsRate.tsx
@@ -27,29 +27,29 @@ export const SGhoSavingsRate = ({ totalDepositedUSD, rate }: SGhoSavingsRateProp
sx={{ mb: 4 }}
>
-
+
Total Deposited
-
+
APR
-
+
-
+
APY, fixed rate
-
+
diff --git a/src/modules/sGho/SGhoWithdrawRow.tsx b/src/modules/sGho/SGhoWithdrawRow.tsx
index 25d520ef47..1d8a65aa91 100644
--- a/src/modules/sGho/SGhoWithdrawRow.tsx
+++ b/src/modules/sGho/SGhoWithdrawRow.tsx
@@ -20,12 +20,12 @@ export const SGhoWithdrawRow = ({ balance, balanceUSD, onWithdraw }: SGhoWithdra
valueUSD={balanceUSD}
dataCy="sghoBalanceBox"
bottomLineTitle={
-
+
Cooldown period
}
bottomLineComponent={
-
+
Instant
}
diff --git a/src/modules/sGho/YourInfoSidebar.tsx b/src/modules/sGho/YourInfoSidebar.tsx
index 2f2ead6dae..7cbb44c006 100644
--- a/src/modules/sGho/YourInfoSidebar.tsx
+++ b/src/modules/sGho/YourInfoSidebar.tsx
@@ -9,6 +9,7 @@ import { ConnectWalletButton } from 'src/components/WalletConnection/ConnectWall
export const YourInfoSidebar = () => {
return (
{
Your info
-
+
Please connect a wallet to view your personal information here.
diff --git a/src/modules/staking/GhoStakingPanel.tsx b/src/modules/staking/GhoStakingPanel.tsx
index fd79aa3b5d..0dbe8b3af1 100644
--- a/src/modules/staking/GhoStakingPanel.tsx
+++ b/src/modules/staking/GhoStakingPanel.tsx
@@ -27,6 +27,7 @@ import { useCurrentTimestamp } from 'src/hooks/useCurrentTimestamp';
import { useModalContext } from 'src/hooks/useModal';
import { CustomMarket } from 'src/ui-config/marketsConfig';
import { GENERAL } from 'src/utils/events';
+import { figVars } from 'src/utils/figmaColors';
import { StakeActionBox } from './StakeActionBox';
import { StakingPanelSkeleton } from './StakingPanelSkeleton';
@@ -125,7 +126,7 @@ export const GhoStakingPanel: React.FC = ({
// const distributionEnded = Date.now() / 1000 > Number(stakeData.distributionEnd);
return (
-
+
= ({
/>
-
+
Total deposited:{' '}
= ({
({
+ sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: { xs: 'flex-start', xsm: 'center' },
flexDirection: { xs: 'column', xsm: 'row' },
gap: { xs: 0, xsm: 2 },
borderRadius: { xs: 0, xsm: '6px' },
- border: { xs: 'unset', xsm: `1px solid ${theme.palette.divider}` },
+ border: { xs: 'unset', xsm: `1px solid ${figVars['border-2']}` },
p: { xs: 0, xsm: 4 },
background: {
xs: 'unset',
- xsm: theme.palette.background.paper,
+ xsm: figVars['surface-elevated'],
},
position: 'relative',
'&:after': {
@@ -185,9 +186,9 @@ export const GhoStakingPanel: React.FC = ({
left: '-16px',
width: 'calc(100% + 32px)',
height: '1px',
- bgcolor: { xs: 'divider', xsm: 'transparent' },
+ bgcolor: { xs: 'border-2', xsm: 'transparent' },
},
- })}
+ }}
>
= ({
/>
-
+
Total deposited{' '}
= ({
}}
>
-
+
Deposit APR
@@ -270,13 +264,10 @@ export const GhoStakingPanel: React.FC = ({
mb: { xs: 3, xsm: 0 },
}}
>
-
+
Max slashing
-
+
= ({
mb: { xs: 3, xsm: 0 },
}}
>
-
+
Wallet Balance
@@ -371,17 +359,17 @@ export const GhoStakingPanel: React.FC = ({
bottomLineComponent={
<>
{isCooldownActive && !isUnstakeWindowActive ? (
-
+
) : isUnstakeWindowActive ? (
-
+
) : (
-
+
Instant
)}
@@ -398,15 +386,15 @@ export const GhoStakingPanel: React.FC = ({
pt: 2,
}}
>
-
+
Amount in cooldown
@@ -419,7 +407,7 @@ export const GhoStakingPanel: React.FC = ({
{isUnstakeWindowActive && (
= ({
{availableToReactivateCooldown && (
+
Reactivate cooldown period to unstake{' '}
{Number(
@@ -473,11 +457,7 @@ export const GhoStakingPanel: React.FC = ({
{availableToReactivateCooldown && (
+
Reactivate cooldown period to unstake{' '}
{Number(
diff --git a/src/modules/staking/SavingsGhoProgram.tsx b/src/modules/staking/SavingsGhoProgram.tsx
index 0221cf55d3..c8b448adf3 100644
--- a/src/modules/staking/SavingsGhoProgram.tsx
+++ b/src/modules/staking/SavingsGhoProgram.tsx
@@ -71,7 +71,7 @@ export const SavingsGhoProgram = () => {
diff --git a/src/modules/staking/StakeActionBox.tsx b/src/modules/staking/StakeActionBox.tsx
index 9bac225c4b..7d97a7e8bb 100644
--- a/src/modules/staking/StakeActionBox.tsx
+++ b/src/modules/staking/StakeActionBox.tsx
@@ -1,5 +1,6 @@
import { Box, Typography } from '@mui/material';
import React, { ReactNode } from 'react';
+import { figVars } from 'src/utils/figmaColors';
import { FormattedNumber } from '../../components/primitives/FormattedNumber';
import { Row } from '../../components/primitives/Row';
@@ -29,11 +30,11 @@ export const StakeActionBox = ({
}: StakeActionBoxProps) => {
return (
({
+ sx={{
flex: 1,
display: 'flex',
borderRadius: '6px',
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
position: 'relative',
'&:after': {
content: "''",
@@ -43,26 +44,26 @@ export const StakeActionBox = ({
bottom: -1,
left: -1,
right: -1,
- background: gradientBorder ? theme.palette.gradients.aaveGradient : 'transparent',
+ background: gradientBorder ? figVars['purple-1'] : 'transparent',
},
- })}
+ }}
>
({
+ sx={{
flex: 1,
p: 4,
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
borderRadius: '6px',
- background: theme.palette.background.paper,
+ background: figVars['surface-elevated'],
position: 'relative',
zIndex: 2,
- })}
+ }}
data-cy={dataCy}
>
-
+
{title}
@@ -70,28 +71,23 @@ export const StakeActionBox = ({
value={value}
visibleDecimals={2}
variant="secondary21"
- color={+value === 0 ? 'text.muted' : 'text.primary'}
+ color={+value === 0 ? 'fg-3' : 'fg-1'}
data-cy={`amountNative`}
/>
{children}
-
+
{bottomLineComponent}
{cooldownAmount}
diff --git a/src/modules/staking/StakingHeader.tsx b/src/modules/staking/StakingHeader.tsx
index 2d37e8649d..d403acb1fb 100644
--- a/src/modules/staking/StakingHeader.tsx
+++ b/src/modules/staking/StakingHeader.tsx
@@ -1,16 +1,8 @@
-import { ChainId } from '@aave/contract-helpers';
import { Trans } from '@lingui/macro';
-import { Box, Stack, Typography, useMediaQuery, useTheme } from '@mui/material';
-import { ChainAvailabilityText } from 'src/components/ChainAvailabilityText';
+import { useMediaQuery, useTheme } from '@mui/material';
+import { PageHeader } from 'src/components/PageHeader/PageHeader';
+import { PageHeaderStat } from 'src/components/PageHeader/PageHeaderStat';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
-import { Row } from 'src/components/primitives/Row';
-import { TextWithTooltip } from 'src/components/TextWithTooltip';
-import { TopInfoPanel } from 'src/components/TopInfoPanel/TopInfoPanel';
-import { useRootStore } from 'src/store/root';
-import { GENERAL } from 'src/utils/events';
-
-import { Link } from '../../components/primitives/Link';
-import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem';
interface StakingHeaderProps {
tvl: {
@@ -22,109 +14,39 @@ interface StakingHeaderProps {
export const StakingHeader: React.FC = ({ tvl, stkEmission, loading }) => {
const theme = useTheme();
- const upToLG = useMediaQuery(theme.breakpoints.up('lg'));
const downToSM = useMediaQuery(theme.breakpoints.down('sm'));
- const downToXSM = useMediaQuery(theme.breakpoints.down('xsm'));
-
- const valueTypographyVariant = downToSM ? 'main16' : 'main21';
- const symbolsTypographyVariant = downToSM ? 'secondary16' : 'secondary21';
- const trackEvent = useRootStore((store) => store.trackEvent);
+ const valueVariant = downToSM ? 'h4' : 'h2';
const total = Object.values(tvl || {}).reduce((acc, item) => acc + item, 0);
- const TotalFundsTooltip = () => {
- return (
-
-
- {Object.entries(tvl)
- .sort((a, b) => b[1] - a[1])
- .map(([key, value]) => (
-
-
-
- ))}
-
-
- );
- };
-
return (
-
-
-
- {/*
*/}
-
- Safety Module
-
-
-
-
-
- The Safety Module has been upgraded to{' '}
-
- Umbrella
-
- , a new system that introduces automated slashing, aToken staking, and improved
- incentives design.
-
-
-
-
- AAVE and ABPT holders (Ethereum network only) can stake their assets in the Safety
- Module to add more security to the protocol and earn Safety Incentives. In the case of
- a shortfall event, your stake can be slashed to cover the deficit, providing an
- additional layer of protection for the protocol.
- {' '}
-
- trackEvent(GENERAL.EXTERNAL_LINK, {
- Link: 'Staking Risks',
- })
- }
- >
- Learn more about risks involved
-
-
-
+
+ The Safety Module has been upgraded to Umbrella, a new system that introduces automated
+ slashing, aToken staking, and improved incentives design.
+
}
>
-
- Funds in the Safety Module
-
-
- }
- loading={loading}
- >
+ Funds in the Safety Module} loading={loading}>
-
+
- Total emission per day} loading={loading}>
+ Total emission per day} loading={loading}>
-
-
+
+
);
};
diff --git a/src/modules/staking/StakingPanel.tsx b/src/modules/staking/StakingPanel.tsx
index c9657b9f10..e78e69fa1a 100644
--- a/src/modules/staking/StakingPanel.tsx
+++ b/src/modules/staking/StakingPanel.tsx
@@ -25,6 +25,7 @@ import { TextWithTooltip } from 'src/components/TextWithTooltip';
import { StakeTokenFormatted } from 'src/hooks/stake/useGeneralStakeUiData';
import { useCurrentTimestamp } from 'src/hooks/useCurrentTimestamp';
import { GENERAL } from 'src/utils/events';
+import { figVars } from 'src/utils/figmaColors';
import { StakeActionBox } from './StakeActionBox';
import { StakingPanelSkeleton } from './StakingPanelSkeleton';
@@ -122,7 +123,7 @@ export const StakingPanel: React.FC = ({
const distributionEnded = Date.now() / 1000 > Number(stakeData.distributionEnd);
return (
-
+
= ({
/>
-
+
Total staked:{' '}
= ({
({
+ sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: { xs: 'flex-start', xsm: 'center' },
flexDirection: { xs: 'column', xsm: 'row' },
gap: { xs: 0, xsm: 2 },
borderRadius: { xs: 0, xsm: '6px' },
- border: { xs: 'unset', xsm: `1px solid ${theme.palette.divider}` },
+ border: { xs: 'unset', xsm: `1px solid ${figVars['border-2']}` },
p: { xs: 0, xsm: 4 },
background: {
xs: 'unset',
- xsm: theme.palette.background.paper,
+ xsm: figVars['surface-elevated'],
},
position: 'relative',
'&:after': {
@@ -182,9 +183,9 @@ export const StakingPanel: React.FC = ({
left: '-16px',
width: 'calc(100% + 32px)',
height: '1px',
- bgcolor: { xs: 'divider', xsm: 'transparent' },
+ bgcolor: { xs: 'border-2', xsm: 'transparent' },
},
- })}
+ }}
>
= ({
/>
-
+
Total staked{' '}
= ({
}}
>
-
+
Staking APR
{distributionEnded && (
@@ -262,7 +256,7 @@ export const StakingPanel: React.FC = ({
href="https://governance.aave.com"
sx={{ textDecoration: 'underline' }}
variant="caption"
- color="text.secondary"
+ color="fg-2"
>
Learn more
@@ -276,7 +270,7 @@ export const StakingPanel: React.FC = ({
sx={{ mr: 2 }}
value={stakeData.stakeApyFormatted}
percent
- variant="secondary14"
+ variant="h5"
/>
@@ -289,13 +283,10 @@ export const StakingPanel: React.FC = ({
mb: { xs: 3, xsm: 0 },
}}
>
-
+
Max slashing
-
+
= ({
mb: { xs: 3, xsm: 0 },
}}
>
-
+
Wallet Balance
@@ -416,17 +404,17 @@ export const StakingPanel: React.FC = ({
bottomLineComponent={
<>
{isCooldownActive && !isUnstakeWindowActive ? (
-
+
) : isUnstakeWindowActive ? (
-
+
) : (
-
+
)}
@@ -443,15 +431,15 @@ export const StakingPanel: React.FC = ({
pt: 2,
}}
>
-
+
Amount in cooldown
@@ -464,7 +452,7 @@ export const StakingPanel: React.FC = ({
{isUnstakeWindowActive && (
= ({
{availableToReactivateCooldown && (
+
Reactivate cooldown period to unstake{' '}
{Number(
@@ -518,11 +502,7 @@ export const StakingPanel: React.FC = ({
{availableToReactivateCooldown && (
+
Reactivate cooldown period to unstake{' '}
{Number(
@@ -572,8 +552,8 @@ export const StakingPanel: React.FC = ({
}
>
diff --git a/src/modules/staking/StakingPanelNoWallet.tsx b/src/modules/staking/StakingPanelNoWallet.tsx
index 918ec659bc..abd95759cf 100644
--- a/src/modules/staking/StakingPanelNoWallet.tsx
+++ b/src/modules/staking/StakingPanelNoWallet.tsx
@@ -7,6 +7,7 @@ import { TokenIcon } from 'src/components/primitives/TokenIcon';
import { StakeTokenFormatted, useGeneralStakeUiData } from 'src/hooks/stake/useGeneralStakeUiData';
import { useRootStore } from 'src/store/root';
import { CustomMarket } from 'src/ui-config/marketsConfig';
+import { figVars } from 'src/utils/figmaColors';
export interface StakingPanelNoWalletProps {
description?: React.ReactNode;
@@ -39,15 +40,15 @@ export const StakingPanelNoWallet: React.FC = ({
return (
({
+ sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
flexDirection: 'row',
borderRadius: '6px',
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
p: 4,
- background: theme.palette.background.paper,
+ background: figVars['surface-elevated'],
width: '250px',
height: '68px',
margin: '0 auto',
@@ -61,7 +62,7 @@ export const StakingPanelNoWallet: React.FC = ({
height: '1px',
bgcolor: 'transparent',
},
- })}
+ }}
>
= ({
>
-
+
{stakedToken}
@@ -87,7 +88,7 @@ export const StakingPanelNoWallet: React.FC = ({
>
{stakedToken !== 'GHO' && (
-
+
Staking APR
@@ -96,14 +97,14 @@ export const StakingPanelNoWallet: React.FC = ({
)}
{stakedToken === 'GHO' && (
-
+
Incentives APR
diff --git a/src/modules/staking/StakingPanelSkeleton.tsx b/src/modules/staking/StakingPanelSkeleton.tsx
index b7f5156af7..86abc18efd 100644
--- a/src/modules/staking/StakingPanelSkeleton.tsx
+++ b/src/modules/staking/StakingPanelSkeleton.tsx
@@ -2,7 +2,7 @@ import { Paper, Skeleton, Stack } from '@mui/material';
export const StakingPanelSkeleton = () => {
return (
-
+
diff --git a/src/modules/stkGho/StkGhoCard.tsx b/src/modules/stkGho/StkGhoCard.tsx
index 829fa963e1..e3e9baf86f 100644
--- a/src/modules/stkGho/StkGhoCard.tsx
+++ b/src/modules/stkGho/StkGhoCard.tsx
@@ -34,6 +34,7 @@ export const StkGhoCard = () => {
return (
-
+
APR
-
+
{meritIncentives && }
@@ -68,18 +69,18 @@ export const StkGhoDepositRow = ({
return (
({
+ sx={{
display: 'flex',
alignItems: { xs: 'stretch', xsm: 'center' },
justifyContent: 'space-between',
flexDirection: { xs: 'column', xsm: 'row' },
gap: { xs: 4, xsm: 4 },
borderRadius: { xs: '8px', xsm: '6px' },
- border: `1px solid ${theme.palette.divider}`,
+ border: `1px solid ${figVars['border-2']}`,
p: 4,
mb: 6,
- background: theme.palette.background.paper,
- })}
+ background: figVars['surface-elevated'],
+ }}
>
@@ -88,13 +89,13 @@ export const StkGhoDepositRow = ({
stkGHO
-
+
Available to deposit:
diff --git a/src/modules/stkGho/StkGhoSavingsRate.tsx b/src/modules/stkGho/StkGhoSavingsRate.tsx
index 29b33163b2..eabc9f3501 100644
--- a/src/modules/stkGho/StkGhoSavingsRate.tsx
+++ b/src/modules/stkGho/StkGhoSavingsRate.tsx
@@ -42,22 +42,22 @@ export const StkGhoSavingsRate = ({ totalDepositedUSD }: StkGhoSavingsRateProps)
sx={{ mb: 4 }}
>
-
+
Total Deposited
-
+
APY
-
+
diff --git a/src/modules/stkGho/StkGhoWithdrawRow.tsx b/src/modules/stkGho/StkGhoWithdrawRow.tsx
index eae5dfefab..9e94b485c8 100644
--- a/src/modules/stkGho/StkGhoWithdrawRow.tsx
+++ b/src/modules/stkGho/StkGhoWithdrawRow.tsx
@@ -50,12 +50,12 @@ export const StkGhoWithdrawRow = ({
valueUSD={stakedUSD}
dataCy={`stakedBox_${stakedToken}`}
bottomLineTitle={
-
+
Cooldown period
}
bottomLineComponent={
-
+
Instant
}
diff --git a/src/modules/umbrella/AmountSharesItem.tsx b/src/modules/umbrella/AmountSharesItem.tsx
index 26d2cb716a..cb0ad24b39 100644
--- a/src/modules/umbrella/AmountSharesItem.tsx
+++ b/src/modules/umbrella/AmountSharesItem.tsx
@@ -33,7 +33,7 @@ export const AmountSharesItem = ({ stakeData }: { stakeData: MergedStakeData })
{!isCooldownActive && !isUnstakeWindowActive ? (
<>
-
+
>
) : (
diff --git a/src/modules/umbrella/AmountStakedUnderlyingItem.tsx b/src/modules/umbrella/AmountStakedUnderlyingItem.tsx
index 3dbde29b5f..6ffbe7162f 100644
--- a/src/modules/umbrella/AmountStakedUnderlyingItem.tsx
+++ b/src/modules/umbrella/AmountStakedUnderlyingItem.tsx
@@ -39,7 +39,7 @@ export const AmountStakedUnderlyingItem = ({
justifyContent="center"
gap={2}
>
-
+
);
};
diff --git a/src/modules/umbrella/AvailableToClaimItem.tsx b/src/modules/umbrella/AvailableToClaimItem.tsx
index 86d384ba36..3ca2dbfa39 100644
--- a/src/modules/umbrella/AvailableToClaimItem.tsx
+++ b/src/modules/umbrella/AvailableToClaimItem.tsx
@@ -66,7 +66,7 @@ export const AvailableToClaimItem = ({
export const AvailableToClaimTooltipContent = ({ stakeData }: { stakeData: MergedStakeData }) => {
return (
-
+
Rewards available to claim
diff --git a/src/modules/umbrella/AvailableToStakeItem.tsx b/src/modules/umbrella/AvailableToStakeItem.tsx
index d8be5de1f0..44ba2a5b5c 100644
--- a/src/modules/umbrella/AvailableToStakeItem.tsx
+++ b/src/modules/umbrella/AvailableToStakeItem.tsx
@@ -56,8 +56,8 @@ export const AvailableToStakeItem = ({
{stakeData.underlyingIsStataToken ? (
-
+
Your balance of assets that are available to stake
diff --git a/src/modules/umbrella/StakeAssets/StakeAssetName.tsx b/src/modules/umbrella/StakeAssets/StakeAssetName.tsx
index 0f5c6f2113..6932377a3d 100644
--- a/src/modules/umbrella/StakeAssets/StakeAssetName.tsx
+++ b/src/modules/umbrella/StakeAssets/StakeAssetName.tsx
@@ -33,7 +33,7 @@ export const StakeAssetName = ({
-
+
Total staked:{' '}
+
Target liquidity
}
@@ -61,7 +61,7 @@ export const StakeAssetName = ({
-
+
Reward APY at target liquidity
diff --git a/src/modules/umbrella/StakeAssets/StakeAssetsFilters.tsx b/src/modules/umbrella/StakeAssets/StakeAssetsFilters.tsx
new file mode 100644
index 0000000000..c69e346d1a
--- /dev/null
+++ b/src/modules/umbrella/StakeAssets/StakeAssetsFilters.tsx
@@ -0,0 +1,82 @@
+import { Trans } from '@lingui/macro';
+import { Box, Button, Switch, Typography } from '@mui/material';
+import { AssetCategoryMultiSelect } from 'src/components/AssetCategoryMultiselect';
+import { SearchInput } from 'src/components/SearchInput';
+import { AssetCategory } from 'src/modules/markets/utils/assetCategories';
+
+interface StakeAssetsFiltersProps {
+ searchPlaceholder: string;
+ onSearchTermChange: (value: string) => void;
+ selectedCategories: AssetCategory[];
+ onCategoriesChange: (categories: AssetCategory[]) => void;
+ categoriesDisabled?: boolean;
+ /** Optional "In Wallet" toggle — omit on the disconnected preview (no wallet to filter by). */
+ inWallet?: { value: boolean; onChange: (value: boolean) => void };
+}
+
+export const StakeAssetsFilters = ({
+ searchPlaceholder,
+ onSearchTermChange,
+ selectedCategories,
+ onCategoriesChange,
+ categoriesDisabled = false,
+ inWallet,
+}: StakeAssetsFiltersProps) => {
+ return (
+
+
+
+
+ {inWallet && (
+ // Outlined toggle: the whole pill flips the filter; the Switch just mirrors its state.
+ inWallet.onChange(!inWallet.value)}
+ aria-pressed={inWallet.value}
+ sx={{
+ gap: 2,
+ textTransform: 'none',
+ justifyContent: 'space-between',
+ flex: { xs: 1, sm: 'none' },
+ }}
+ >
+
+ In Wallet
+
+
+
+ )}
+
+
+
+
+ );
+};
diff --git a/src/modules/umbrella/StakeAssets/UmbrellaAssetsList.tsx b/src/modules/umbrella/StakeAssets/UmbrellaAssetsList.tsx
index 9387033cc8..8ea4eb8081 100644
--- a/src/modules/umbrella/StakeAssets/UmbrellaAssetsList.tsx
+++ b/src/modules/umbrella/StakeAssets/UmbrellaAssetsList.tsx
@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro';
-import { Box, useMediaQuery } from '@mui/material';
+import { Box, useMediaQuery, useTheme } from '@mui/material';
import { useMemo, useState } from 'react';
import { ListColumn } from 'src/components/lists/ListColumn';
import { ListHeaderTitle } from 'src/components/lists/ListHeaderTitle';
@@ -20,15 +20,15 @@ const listHeaders = [
sortKey: 'symbol',
},
{
- title: ,
+ title: ,
sortKey: 'totalAPY',
},
{
- title: ,
+ title: ,
sortKey: 'stakeTokenUnderlyingBalance',
},
{
- title: ,
+ title: ,
sortKey: 'stakeSharesTokens',
},
{
@@ -55,7 +55,8 @@ export default function UmbrellaAssetsList({
stakedDataWithTokenBalances,
isLoadingStakedDataWithTokenBalances,
}: UmbrelaAssetsListProps) {
- const isTableChangedToCards = useMediaQuery('(max-width:1125px)');
+ const theme = useTheme();
+ const isTableChangedToCards = useMediaQuery(theme.breakpoints.down('mdlg'));
const [sortName, setSortName] = useState('');
const [sortDesc, setSortDesc] = useState(false);
diff --git a/src/modules/umbrella/StakeAssets/UmbrellaAssetsListContainer.tsx b/src/modules/umbrella/StakeAssets/UmbrellaAssetsListContainer.tsx
index 22ab96706f..b8087c177d 100644
--- a/src/modules/umbrella/StakeAssets/UmbrellaAssetsListContainer.tsx
+++ b/src/modules/umbrella/StakeAssets/UmbrellaAssetsListContainer.tsx
@@ -1,15 +1,19 @@
import { Trans } from '@lingui/macro';
-import { useMediaQuery, useTheme } from '@mui/material';
+import { Box, Paper, useMediaQuery, useTheme } from '@mui/material';
import { useState } from 'react';
-import { ListWrapper } from 'src/components/lists/ListWrapper';
import { NoSearchResults } from 'src/components/NoSearchResults';
-import { TitleWithSearchBar } from 'src/components/TitleWithSearchBar';
import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider';
import { useUmbrellaSummary } from 'src/hooks/stake/useUmbrellaSummary';
+import { useCoingeckoCategories } from 'src/hooks/useCoinGeckoCategories';
+import {
+ AssetCategory,
+ matchesSelectedCategories,
+} from 'src/modules/markets/utils/assetCategories';
import { useRootStore } from 'src/store/root';
import { useShallow } from 'zustand/shallow';
import { NoStakeAssets } from '../NoStakeAssets';
+import { StakeAssetsFilters } from './StakeAssetsFilters';
import UmbrellaAssetsList from './UmbrellaAssetsList';
export const UmbrellaAssetsListContainer = () => {
@@ -19,54 +23,76 @@ export const UmbrellaAssetsListContainer = () => {
const { data: stakedDataWithTokenBalances, loading: isLoadingStakedDataWithTokenBalances } =
useUmbrellaSummary(currentMarketData);
+ const {
+ data: categoryData,
+ isLoading: isLoadingCategories,
+ error: categoriesError,
+ } = useCoingeckoCategories();
const [searchTerm, setSearchTerm] = useState('');
+ const [inWalletOnly, setInWalletOnly] = useState(false);
+ const [selectedCategories, setSelectedCategories] = useState([]);
const { breakpoints } = useTheme();
const sm = useMediaQuery(breakpoints.down('sm'));
- const filteredData = stakedDataWithTokenBalances?.stakeData.filter((res) => {
- if (!searchTerm) return true;
- const term = searchTerm.toLowerCase().trim();
-
- return res.name.toLowerCase().includes(term) || res.iconSymbol.toLowerCase().includes(term);
- });
+ const filteredData = stakedDataWithTokenBalances?.stakeData
+ // Search by asset name or symbol
+ .filter((res) => {
+ if (!searchTerm) return true;
+ const term = searchTerm.toLowerCase().trim();
+ return res.name.toLowerCase().includes(term) || res.iconSymbol.toLowerCase().includes(term);
+ })
+ // "In Wallet": only assets the user holds in their wallet (raw underlying token balance)
+ .filter((res) => !inWalletOnly || Number(res.formattedBalances.underlyingTokenBalance) > 0)
+ // Category filter (shares the markets page's dynamic CoinGecko categorization)
+ .filter((res) =>
+ matchesSelectedCategories(
+ res.symbol,
+ selectedCategories,
+ categoryData?.stablecoinSymbols,
+ categoryData?.ethCorrelatedSymbols
+ )
+ );
const noStakeAssetsConfigured =
!isLoadingStakedDataWithTokenBalances && !stakedDataWithTokenBalances;
return (
- Assets to stake
}
- searchPlaceholder={sm ? 'Search asset' : 'Search asset name or symbol'}
- />
- }
- >
-
+
- {noStakeAssetsConfigured ? (
-
- ) : (
- !loading &&
- !isLoadingStakedDataWithTokenBalances &&
- filteredData?.length === 0 && (
-
- We couldn't find any assets related to your search. Try again with a different
- asset name, symbol, or address.
-
- }
- />
- )
- )}
-
+
+
+
+ {noStakeAssetsConfigured ? (
+
+ ) : (
+ !loading &&
+ !isLoadingStakedDataWithTokenBalances &&
+ filteredData?.length === 0 && (
+
+ We couldn't find any assets related to your search. Try again with a
+ different asset name, symbol, or address.
+
+ }
+ />
+ )
+ )}
+
+
);
};
diff --git a/src/modules/umbrella/StakeCooldownModalContent.tsx b/src/modules/umbrella/StakeCooldownModalContent.tsx
index 9a8e6a3496..3cd33a5a29 100644
--- a/src/modules/umbrella/StakeCooldownModalContent.tsx
+++ b/src/modules/umbrella/StakeCooldownModalContent.tsx
@@ -173,26 +173,22 @@ export const StakeCooldownModalContent = ({ stakeData }: { stakeData: MergedStak
pb: '30px',
}}
>
-
+
Amount available to unstake
-
+
@@ -208,18 +204,18 @@ export const StakeCooldownModalContent = ({ stakeData }: { stakeData: MergedStak
pb: '30px',
}}
>
-
+
Unstake window
-
+
{dateMessage(stakeCooldownSeconds)}
-
+
{dateMessage(stakeCooldownSeconds + stakeUnstakeWindow)}
diff --git a/src/modules/umbrella/StakingApyItem.tsx b/src/modules/umbrella/StakingApyItem.tsx
index 769c0a11ef..6270cc789e 100644
--- a/src/modules/umbrella/StakingApyItem.tsx
+++ b/src/modules/umbrella/StakingApyItem.tsx
@@ -74,18 +74,13 @@ export const StakingApyItem = ({
justifyContent="center"
gap={2}
>
-
+
+
{stakeData.underlyingIsStataToken ? (
Staking this asset will earn the underlying asset supply yield in addition to
@@ -145,9 +140,9 @@ export const StakingApyTooltipcontent = ({
symbol={reward.symbol}
sx={{ fontSize: '20px', mr: 1 }}
/>
- {reward.name}
+ {reward.name}
{reward.fromSupply && (
-
+
(supply)
)}
@@ -157,8 +152,8 @@ export const StakingApyTooltipcontent = ({
width="100%"
>
-
-
+
+
APY
@@ -172,18 +167,18 @@ export const StakingApyTooltipcontent = ({
mt: 1,
pt: 2,
borderTop: '1px solid',
- borderColor: 'divider',
+ borderColor: 'border-2',
}}
caption={
-
+
Total
}
width="100%"
>
-
-
+
+
APY
diff --git a/src/modules/umbrella/UmbrellaAssetsDefault.tsx b/src/modules/umbrella/UmbrellaAssetsDefault.tsx
index ac2fbe44ac..afa1fe337e 100644
--- a/src/modules/umbrella/UmbrellaAssetsDefault.tsx
+++ b/src/modules/umbrella/UmbrellaAssetsDefault.tsx
@@ -1,38 +1,103 @@
import { Trans } from '@lingui/macro';
-import { Box, Skeleton, Stack, Typography, useMediaQuery } from '@mui/material';
+import { Box, Paper, Skeleton, Stack, useMediaQuery, useTheme } from '@mui/material';
+import { useState } from 'react';
import { ListColumn } from 'src/components/lists/ListColumn';
import { ListHeaderTitle } from 'src/components/lists/ListHeaderTitle';
import { ListHeaderWrapper } from 'src/components/lists/ListHeaderWrapper';
import { ListItem } from 'src/components/lists/ListItem';
-import { ListWrapper } from 'src/components/lists/ListWrapper';
+import { NoSearchResults } from 'src/components/NoSearchResults';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
import { Row } from 'src/components/primitives/Row';
import { FormattedStakeData, useStakeDataSummary } from 'src/hooks/stake/useUmbrellaSummary';
+import { useCoingeckoCategories } from 'src/hooks/useCoinGeckoCategories';
+import {
+ AssetCategory,
+ matchesSelectedCategories,
+} from 'src/modules/markets/utils/assetCategories';
import { useRootStore } from 'src/store/root';
import { useShallow } from 'zustand/shallow';
import { ListMobileItemWrapper } from '../dashboard/lists/ListMobileItemWrapper';
import { NoStakeAssets } from './NoStakeAssets';
import { StakeAssetName } from './StakeAssets/StakeAssetName';
+import { StakeAssetsFilters } from './StakeAssets/StakeAssetsFilters';
export const UmrellaAssetsDefaultListContainer = () => {
+ const [currentMarketData] = useRootStore(useShallow((store) => [store.currentMarketData]));
+ const { data: stakeData, loading } = useStakeDataSummary(currentMarketData);
+ const {
+ data: categoryData,
+ isLoading: isLoadingCategories,
+ error: categoriesError,
+ } = useCoingeckoCategories();
+
+ const [searchTerm, setSearchTerm] = useState('');
+ const [selectedCategories, setSelectedCategories] = useState([]);
+ const { breakpoints } = useTheme();
+ const sm = useMediaQuery(breakpoints.down('sm'));
+
+ const filteredAssets = stakeData?.stakeAssets
+ // Search by asset symbol
+ .filter((res) => {
+ if (!searchTerm) return true;
+ const term = searchTerm.toLowerCase().trim();
+ return res.symbol.toLowerCase().includes(term);
+ })
+ // Category filter (shares the markets page's dynamic CoinGecko categorization)
+ .filter((res) =>
+ matchesSelectedCategories(
+ res.symbol,
+ selectedCategories,
+ categoryData?.stablecoinSymbols,
+ categoryData?.ethCorrelatedSymbols
+ )
+ );
+
+ const noStakeAssetsConfigured = !loading && (!stakeData || stakeData.stakeAssets.length === 0);
+
return (
-
- Assets to stake
-
- }
- >
-
-
+
+
+
+
+
+
+ {noStakeAssetsConfigured ? (
+
+ ) : (
+ !loading &&
+ filteredAssets?.length === 0 && (
+
+ We couldn't find any assets related to your search. Try again with a
+ different asset name, symbol, or address.
+
+ }
+ />
+ )
+ )}
+
+
);
};
-export const UmbrellaAssetsDefault = () => {
- const [currentMarketData] = useRootStore(useShallow((store) => [store.currentMarketData]));
- const { data: stakeData, loading } = useStakeDataSummary(currentMarketData);
- const isTableChangedToCards = useMediaQuery('(max-width:1125px)');
+export const UmbrellaAssetsDefault = ({
+ stakeAssets,
+ loading,
+}: {
+ stakeAssets: FormattedStakeData[];
+ loading: boolean;
+}) => {
+ const theme = useTheme();
+ const isTableChangedToCards = useMediaQuery(theme.breakpoints.down('mdlg'));
if (loading) {
return isTableChangedToCards ? (
@@ -52,8 +117,9 @@ export const UmbrellaAssetsDefault = () => {
);
}
- if (!loading && (!stakeData || stakeData.stakeAssets.length === 0)) {
- return ;
+ // Empty states (no assets configured / no search results) are handled by the container.
+ if (stakeAssets.length === 0) {
+ return null;
}
return (
@@ -72,14 +138,13 @@ export const UmbrellaAssetsDefault = () => {
)}
- {stakeData &&
- stakeData.stakeAssets.map((data, index) =>
- !isTableChangedToCards ? (
-
- ) : (
-
- )
- )}
+ {stakeAssets.map((data, index) =>
+ !isTableChangedToCards ? (
+
+ ) : (
+
+ )
+ )}
>
);
};
@@ -102,7 +167,7 @@ const AssetListItem = ({ stakeData }: { stakeData: FormattedStakeData }) => {
@@ -137,7 +202,7 @@ const AssetListItemMobile = ({ stakeData }: { stakeData: FormattedStakeData }) =
diff --git a/src/modules/umbrella/UmbrellaClaimModalContent.tsx b/src/modules/umbrella/UmbrellaClaimModalContent.tsx
index 80eda03a5f..507dd8207e 100644
--- a/src/modules/umbrella/UmbrellaClaimModalContent.tsx
+++ b/src/modules/umbrella/UmbrellaClaimModalContent.tsx
@@ -130,8 +130,8 @@ export const UmbrellaClaimAllModalContent = ({ stakeData }: UmbrellaClaimAllModa
>
-
-
+
+
{reward.symbol}
@@ -140,7 +140,7 @@ export const UmbrellaClaimAllModalContent = ({ stakeData }: UmbrellaClaimAllModa
variant="helperText"
compact
symbol="USD"
- color="text.secondary"
+ color="fg-2"
/>
))}
@@ -231,8 +231,8 @@ export const UmbrellaClaimModalContent = ({ stakeData }: UmbrellaClaimModalConte
>
-
-
+
+
{reward.symbol}
@@ -241,7 +241,7 @@ export const UmbrellaClaimModalContent = ({ stakeData }: UmbrellaClaimModalConte
variant="helperText"
compact
symbol="USD"
- color="text.secondary"
+ color="fg-2"
/>
))}
diff --git a/src/modules/umbrella/UmbrellaHeader.tsx b/src/modules/umbrella/UmbrellaHeader.tsx
index 19b885315a..27bdbd1ff4 100644
--- a/src/modules/umbrella/UmbrellaHeader.tsx
+++ b/src/modules/umbrella/UmbrellaHeader.tsx
@@ -1,254 +1,88 @@
import { Trans } from '@lingui/macro';
-import { Box, Button, Stack, Typography, useMediaQuery, useTheme } from '@mui/material';
+import { useMediaQuery, useTheme } from '@mui/material';
+import { PageHeader } from 'src/components/PageHeader/PageHeader';
+import { PageHeaderStat } from 'src/components/PageHeader/PageHeaderStat';
import { FormattedNumber } from 'src/components/primitives/FormattedNumber';
-import { TopInfoPanel } from 'src/components/TopInfoPanel/TopInfoPanel';
import { useStakeDataSummary, useUmbrellaSummary } from 'src/hooks/stake/useUmbrellaSummary';
-import { useModalContext } from 'src/hooks/useModal';
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
import { useRootStore } from 'src/store/root';
import { MarketDataType } from 'src/ui-config/marketsConfig';
-import { GENERAL } from 'src/utils/events';
-import { useShallow } from 'zustand/shallow';
-import { Link } from '../../components/primitives/Link';
-import { TopInfoPanelItem } from '../../components/TopInfoPanel/TopInfoPanelItem';
-import { MarketSwitcher } from './UmbrellaMarketSwitcher';
+type StatProps = {
+ currentMarketData: MarketDataType;
+ valueVariant: 'h4' | 'h2';
+};
export const UmbrellaHeader: React.FC = () => {
const theme = useTheme();
const { currentAccount } = useWeb3Context();
- const [currentMarketData, trackEvent] = useRootStore(
- useShallow((store) => [store.currentMarketData, store.trackEvent])
- );
- // const [trackEvent, currentMarket, setCurrentMarket] = useRootStore(
- // useShallow((store) => [store.trackEvent, store.currentMarket, store.setCurrentMarket])
- // );
+ // The market is pinned to Core on the staking page (see pages/staking.page.tsx), so this reads Core.
+ const currentMarketData = useRootStore((store) => store.currentMarketData);
- const upToLG = useMediaQuery(theme.breakpoints.up('lg'));
const downToSM = useMediaQuery(theme.breakpoints.down('sm'));
- const downToXSM = useMediaQuery(theme.breakpoints.down('xsm'));
-
- const valueTypographyVariant = downToSM ? 'main16' : 'main21';
- const symbolsTypographyVariant = downToSM ? 'secondary16' : 'secondary21';
+ const valueVariant = downToSM ? 'h4' : 'h2';
return (
-
- {/* */}
-
- {/*
*/}
-
- Staking
-
-
-
-
-
-
- Umbrella is the upgraded version of the Safety Module. Manage your previously staked
- assets
- {' '}
-
- here.
-
-
-
-
- Stake your Aave aTokens or underlying assets to earn rewards. In case of a shortfall
- event, your stake may be slashed to cover the deficit.
- {' '}
-
- trackEvent(GENERAL.EXTERNAL_LINK, {
- Link: 'Staking Risks',
- })
- }
- >
- Learn more about the risks.
-
-
-
- }
+ Stake your Aave aTokens or underlying assets to earn rewards.}
>
+
{currentAccount ? (
-
- ) : (
-
- )}
-
+
+ ) : null}
+
);
};
-const UmbrellaHeaderUserDetails = ({
- currentMarketData,
- valueTypographyVariant,
- symbolsTypographyVariant,
-}: {
- currentMarketData: MarketDataType;
- valueTypographyVariant: 'main16' | 'main21';
- symbolsTypographyVariant: 'secondary16' | 'secondary21';
-}) => {
- const theme = useTheme();
+// Total staked across the instance — shown whether or not a wallet is connected.
+const TotalStakedStat = ({ currentMarketData, valueVariant }: StatProps) => {
+ const { data: stakeData, loading } = useStakeDataSummary(currentMarketData);
+
+ return (
+ Total Staked} loading={loading}>
+
+
+ );
+};
+
+// Connected-only stats. Kept separate so `useUmbrellaSummary` (user-specific) is gated to the
+// connected branch rather than run for logged-out visitors.
+const UmbrellaUserStats = ({ currentMarketData, valueVariant }: StatProps) => {
const { data: stakedDataWithTokenBalances, loading: isLoadingStakedDataWithTokenBalances } =
useUmbrellaSummary(currentMarketData);
- const { data: stakeData, loading } = useStakeDataSummary(currentMarketData);
- const { openUmbrellaClaimAll } = useModalContext();
const totalUSDAggregateStaked = stakedDataWithTokenBalances?.aggregatedTotalStakedUSD;
const weightedAverageApy = stakedDataWithTokenBalances?.weightedAverageApy;
- const userRewardsUsd = stakedDataWithTokenBalances?.stakeData.reduce((acc, stake) => {
- const totalAvailableToClaim = stake.formattedRewards.reduce(
- (sum, reward) => sum + Number(reward.accruedUsd || '0'),
- 0
- );
- return acc + totalAvailableToClaim;
- }, 0);
-
- const userHasRewards =
- userRewardsUsd !== undefined && userRewardsUsd > 0 && !isLoadingStakedDataWithTokenBalances;
-
return (
<>
-
- Total amount staked
-
- }
- loading={loading}
- >
-
-
-
- Staked Balance
-
- }
+ Staked Balance}
loading={isLoadingStakedDataWithTokenBalances}
>
-
+
- Net APY}
- loading={isLoadingStakedDataWithTokenBalances}
- >
+ Net APY} loading={isLoadingStakedDataWithTokenBalances}>
-
- {userHasRewards && (
- Available rewards}
- loading={isLoadingStakedDataWithTokenBalances}
- hideIcon
- >
-
-
-
-
-
- openUmbrellaClaimAll()}
- sx={{ minWidth: 'unset', ml: { xs: 0, xsm: 2 } }}
- >
- Claim
-
-
-
- )}
- >
- );
-};
-
-const UmbrellaHeaderDefault = ({
- currentMarketData,
- valueTypographyVariant,
- symbolsTypographyVariant,
-}: {
- currentMarketData: MarketDataType;
- valueTypographyVariant: 'main16' | 'main21';
- symbolsTypographyVariant: 'secondary16' | 'secondary21';
-}) => {
- const theme = useTheme();
- const { data: stakeData, loading } = useStakeDataSummary(currentMarketData);
-
- return (
- <>
-
- Total amount staked
-
- }
- loading={loading}
- >
-
-
+
>
);
};
diff --git a/src/modules/umbrella/UmbrellaMarketSwitcher.tsx b/src/modules/umbrella/UmbrellaMarketSwitcher.tsx
deleted file mode 100644
index 78d352f5aa..0000000000
--- a/src/modules/umbrella/UmbrellaMarketSwitcher.tsx
+++ /dev/null
@@ -1,385 +0,0 @@
-import { ChevronDownIcon } from '@heroicons/react/outline';
-import { Trans } from '@lingui/macro';
-import {
- Box,
- BoxProps,
- ListItemText,
- MenuItem,
- SvgIcon,
- TextField,
- Tooltip,
- Typography,
- useMediaQuery,
- useTheme,
-} from '@mui/material';
-import React, { useState } from 'react';
-import { useRootStore } from 'src/store/root';
-import { BaseNetworkConfig } from 'src/ui-config/networksConfig';
-import { DASHBOARD } from 'src/utils/events';
-import {
- availableMarkets,
- CustomMarket,
- ENABLE_TESTNET,
- MarketDataType,
- marketsData,
- networkConfigs,
- STAGING_ENV,
-} from 'src/utils/marketsAndNetworksConfig';
-import { useShallow } from 'zustand/shallow';
-
-export const getMarketInfoById = (marketId: CustomMarket) => {
- const market: MarketDataType = marketsData[marketId as CustomMarket];
- const network: BaseNetworkConfig = networkConfigs[market.chainId];
- const logo = market.logo || network.networkLogoPath;
-
- return { market, logo };
-};
-
-export const getMarketHelpData = (marketName: string) => {
- const testChains = [
- 'Görli',
- 'Ropsten',
- 'Mumbai',
- 'Sepolia',
- 'Fuji',
- 'Testnet',
- 'Kovan',
- 'Rinkeby',
- ];
- const arrayName = marketName.split(' ');
- const testChainName = arrayName.filter((el) => testChains.indexOf(el) > -1);
- const marketTitle = arrayName.filter((el) => !testChainName.includes(el)).join(' ');
-
- return {
- name: marketTitle,
- testChainName: testChainName[0],
- };
-};
-
-export type Market = {
- marketTitle: string;
- networkName: string;
- networkLogo: string;
- selected?: boolean;
-};
-
-type MarketLogoProps = {
- size: number;
- logo: string;
- testChainName?: string;
- sx?: BoxProps;
-};
-
-export const MarketLogo = ({ size, logo, testChainName, sx }: MarketLogoProps) => {
- return (
-
-
-
- {testChainName && (
-
-
- {testChainName.split('')[0]}
-
-
- )}
-
- );
-};
-
-enum SelectedMarketVersion {
- V2,
- V3,
-}
-
-// TODO
-// Fetch markets that are active for umbrella.
-// Strip out any code not used for v2
-// Style to design specifications
-
-export const MarketSwitcher = () => {
- const [selectedMarketVersion] = useState(SelectedMarketVersion.V3);
- const theme = useTheme();
- const upToLG = useMediaQuery(theme.breakpoints.up('lg'));
- const downToXSM = useMediaQuery(theme.breakpoints.down('xsm'));
- const [trackEvent, currentMarket, setCurrentMarket] = useRootStore(
- useShallow((store) => [store.trackEvent, store.currentMarket, store.setCurrentMarket])
- );
-
- const isV3MarketsAvailable = availableMarkets
- .map((marketId: CustomMarket) => {
- const { market } = getMarketInfoById(marketId);
-
- return market.v3;
- })
- .some((item) => !!item);
-
- const handleMarketSelect = (e: React.ChangeEvent) => {
- trackEvent(DASHBOARD.CHANGE_MARKET, { market: e.target.value });
- setCurrentMarket(e.target.value as unknown as CustomMarket);
- };
-
- // const marketBlurbs: { [key: string]: JSX.Element } = {
- // proto_mainnet_v3: (
- // Main Ethereum market with the largest selection of assets and yield options
- // ),
- // proto_lido_v3: (
- // Optimized for efficiency and risk by supporting blue-chip collateral assets
- // ),
- // };
-
- return (
- null,
- renderValue: (marketId) => {
- const { market, logo } = getMarketInfoById(marketId as CustomMarket);
-
- return (
-
- {/* Main Row with Market Name */}
-
-
-
-
- {getMarketHelpData(market.marketTitle).name} {market.isFork ? 'Fork' : ''}
- {/* {upToLG &&
- (currentMarket === 'proto_mainnet_v3' || currentMarket === 'proto_lido_v3')
- ? 'Instance'
- : ' Market'} */}
-
-
-
- {/*
- V2
- */}
-
-
-
-
-
-
-
- {/* {marketBlurbs[currentMarket] && (
-
- {marketBlurbs[currentMarket]}
-
- )} */}
-
- );
- },
-
- sx: {
- '&.MarketSwitcher__select .MuiSelect-outlined': {
- pl: 0,
- py: 0,
- backgroundColor: 'transparent !important',
- },
- '.MuiSelect-icon': { color: '#F1F1F3' },
- },
- MenuProps: {
- anchorOrigin: {
- vertical: 'bottom',
- horizontal: 'right',
- },
- transformOrigin: {
- vertical: 'top',
- horizontal: 'right',
- },
- PaperProps: {
- style: {
- minWidth: 240,
- },
- variant: 'outlined',
- elevation: 0,
- },
- },
- }}
- >
-
-
-
- {ENABLE_TESTNET || STAGING_ENV ? 'Select Aave Testnet Market' : 'Select Aave Market'}
-
-
-
- {isV3MarketsAvailable && (
-
- {/* {
- if (value !== null) {
- setSelectedMarketVersion(value);
- }
- }}
- sx={{
- width: '100%',
- height: '36px',
- background: theme.palette.primary.main,
- border: `1px solid ${
- theme.palette.mode === 'dark' ? 'rgba(235, 235, 237, 0.12)' : '#1B2030'
- }`,
- borderRadius: '6px',
- marginTop: '16px',
- marginBottom: '12px',
- padding: '2px',
- }}
- >
-
- theme.palette.gradients.aaveGradient,
- backgroundClip: 'text',
- color: 'transparent',
- }
- : {
- color: theme.palette.mode === 'dark' ? '#0F121D' : '#FFFFFF',
- }
- }
- >
- Version 3
-
-
-
- theme.palette.gradients.aaveGradient,
- backgroundClip: 'text',
- color: 'transparent',
- }
- : {
- color: theme.palette.mode === 'dark' ? '#0F121D' : '#FFFFFF',
- }
- }
- >
- Version 2
-
-
- */}
-
- )}
- {availableMarkets.map((marketId: CustomMarket) => {
- const { market, logo } = getMarketInfoById(marketId);
- const marketNaming = getMarketHelpData(market.marketTitle);
- return (
-
-
-
- {marketNaming.name} {market.isFork ? 'Fork' : ''}
-
-
-
- {marketNaming.testChainName}
-
-
-
- );
- })}
-
- );
-};
diff --git a/src/modules/umbrella/UmbrellaModalContent.tsx b/src/modules/umbrella/UmbrellaModalContent.tsx
index 7ff2fd4bc3..31cf7af011 100644
--- a/src/modules/umbrella/UmbrellaModalContent.tsx
+++ b/src/modules/umbrella/UmbrellaModalContent.tsx
@@ -224,10 +224,10 @@ export const UmbrellaModalContent = ({ stakeData, user, userReserve, poolReserve
/>
) : (
<>
-
+
-
+
>
)}
diff --git a/src/modules/umbrella/helpers/AmountAvailableItem.tsx b/src/modules/umbrella/helpers/AmountAvailableItem.tsx
index ed5a818530..99aba67b69 100644
--- a/src/modules/umbrella/helpers/AmountAvailableItem.tsx
+++ b/src/modules/umbrella/helpers/AmountAvailableItem.tsx
@@ -27,12 +27,12 @@ export const AmountAvailableItem = ({
aToken={aToken}
waToken={waToken}
/>
- {name}
+ {name}
}
width="100%"
>
-
+
);
};
diff --git a/src/modules/umbrella/helpers/ApyTooltip.tsx b/src/modules/umbrella/helpers/ApyTooltip.tsx
index 7ffc3c9a72..8b79c5205a 100644
--- a/src/modules/umbrella/helpers/ApyTooltip.tsx
+++ b/src/modules/umbrella/helpers/ApyTooltip.tsx
@@ -1,10 +1,11 @@
import { Trans } from '@lingui/macro';
+import { TypographyProps } from '@mui/material';
import { Link } from 'src/components/primitives/Link';
import { TextWithTooltip } from 'src/components/TextWithTooltip';
-export const ApyTooltip = () => {
+export const ApyTooltip = ({ variant }: { variant?: TypographyProps['variant'] }) => {
return (
- APY}>
+ APY} variant={variant}>
<>
Reward APY adjusts with total staked amount, following a curve that targets optimal
diff --git a/src/modules/umbrella/helpers/Helpers.tsx b/src/modules/umbrella/helpers/Helpers.tsx
index 831ffa4015..6e4a2c28ce 100644
--- a/src/modules/umbrella/helpers/Helpers.tsx
+++ b/src/modules/umbrella/helpers/Helpers.tsx
@@ -44,7 +44,7 @@ export const UmbrellaAssetBreakdown = ({
flexDirection: 'column',
}}
>
-
+
Participating in staking {symbol} gives annualized rewards. Your wallet balance is the
sum of your aTokens and underlying assets. The breakdown to stake is below
@@ -70,7 +70,7 @@ export const UmbrellaAssetBreakdown = ({
@@ -92,7 +92,7 @@ export const UmbrellaAssetBreakdown = ({
@@ -115,12 +115,12 @@ export const UmbrellaAssetBreakdown = ({
- ({ pt: 1, mt: 1 })}>
+
Total
} height={32}>
diff --git a/src/modules/umbrella/helpers/SharesTooltip.tsx b/src/modules/umbrella/helpers/SharesTooltip.tsx
index 1ee186dd4a..2e5849b8fc 100644
--- a/src/modules/umbrella/helpers/SharesTooltip.tsx
+++ b/src/modules/umbrella/helpers/SharesTooltip.tsx
@@ -1,9 +1,10 @@
import { Trans } from '@lingui/macro';
+import { TypographyProps } from '@mui/material';
import { TextWithTooltip } from 'src/components/TextWithTooltip';
-export const SharesTooltip = () => {
+export const SharesTooltip = ({ variant }: { variant?: TypographyProps['variant'] }) => {
return (
- Shares}>
+ Shares} variant={variant}>
<>
Shares are Umbrella Stake Tokens you receive when staking. They represent your ownership
diff --git a/src/modules/umbrella/helpers/StakedUnderlyingTooltip.tsx b/src/modules/umbrella/helpers/StakedUnderlyingTooltip.tsx
index 3449c3c03e..284e245c07 100644
--- a/src/modules/umbrella/helpers/StakedUnderlyingTooltip.tsx
+++ b/src/modules/umbrella/helpers/StakedUnderlyingTooltip.tsx
@@ -1,9 +1,10 @@
import { Trans } from '@lingui/macro';
+import { TypographyProps } from '@mui/material';
import { TextWithTooltip } from 'src/components/TextWithTooltip';
-export const StakedUnderlyingTooltip = () => {
+export const StakedUnderlyingTooltip = ({ variant }: { variant?: TypographyProps['variant'] }) => {
return (
- Staked Underlying}>
+ Staked Underlying} variant={variant}>
<>
Total amount of underlying assets staked. This number represents the combined sum of your
diff --git a/src/modules/umbrella/helpers/StakingDropdown.tsx b/src/modules/umbrella/helpers/StakingDropdown.tsx
index 803f60992f..f72ab1a931 100644
--- a/src/modules/umbrella/helpers/StakingDropdown.tsx
+++ b/src/modules/umbrella/helpers/StakingDropdown.tsx
@@ -81,7 +81,8 @@ export const StakingDropdown = ({ stakeData }: { stakeData: MergedStakeData }) =
{
trackEvent(STAKE.STAKE_TOKEN, {
action: STAKE.OPEN_STAKE_MODAL,
@@ -113,7 +114,7 @@ export const StakingDropdown = ({ stakeData }: { stakeData: MergedStakeData }) =
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
onClick={handleClick}
- size="medium"
+ size="small"
>
@@ -153,7 +154,7 @@ export const StakingDropdown = ({ stakeData }: { stakeData: MergedStakeData }) =
alignItems="center"
justifyContent="space-between"
>
-
+
Cooling down
@@ -191,7 +192,7 @@ export const StakingDropdown = ({ stakeData }: { stakeData: MergedStakeData }) =
alignItems="center"
justifyContent="space-between"
>
-
+
Withdraw
diff --git a/src/utils/buttonStyles.ts b/src/utils/buttonStyles.ts
new file mode 100644
index 0000000000..74c89cf776
--- /dev/null
+++ b/src/utils/buttonStyles.ts
@@ -0,0 +1,24 @@
+import { SxProps, Theme } from '@mui/material';
+
+/**
+ * Icon-only button styling: a square button — no min-width, equal 0.25rem padding on all
+ * sides, and a fixed 0.5rem radius regardless of button size. Compose it in `sx` on top of
+ * any Button variant/size (it only adjusts sizing):
+ *
+ *
+ *
+ *
+ */
+export const iconButtonSx = {
+ minWidth: 0,
+ p: '0.25rem',
+ // Square: match the width to the button's own height (set by its size slot) so it's a square
+ // whatever the icon's width — otherwise a medium button (36px tall) with an 18px icon renders
+ // as a tall rectangle.
+ aspectRatio: '1',
+ // Fixed radius even at size="small" (whose slot would otherwise apply 0.375rem); sx wins
+ // over the theme's per-size styleOverride.
+ borderRadius: '0.5rem',
+ // `satisfies` (not a `SxProps` annotation) keeps the narrow literal type so this can also be
+ // composed inside an `sx` array — e.g. `sx={[iconButtonSx, { ... }]}`.
+} satisfies SxProps;
diff --git a/src/utils/colorToP3.ts b/src/utils/colorToP3.ts
new file mode 100644
index 0000000000..f53dbe8aef
--- /dev/null
+++ b/src/utils/colorToP3.ts
@@ -0,0 +1,18 @@
+import { decomposeColor } from '@mui/material/styles';
+
+/**
+ * Convert an sRGB color string (hex, `rgb()`, or `rgba()`) to its Display-P3 equivalent
+ * using the same naive channel mapping the Figma export uses (channels / 255, relabeled as
+ * `color(display-p3 …)`). This matches the design source's P3 values and, on wide-gamut
+ * displays, renders saturated colors richer while leaving near-grays visually unchanged.
+ *
+ * Used to generate the `@supports (color-gamut: p3)` override layer for the theme's CSS
+ * variables. Non-color / already-`color()` inputs are returned unchanged.
+ */
+export const colorToP3 = (color: string): string => {
+ if (!color.startsWith('#') && !color.startsWith('rgb')) return color;
+ // decomposeColor parses #nnn / #nnnnnn / rgb() / rgba() → { values: [r, g, b, a?] } (r,g,b 0-255).
+ const [r, g, b, a] = decomposeColor(color).values;
+ const channels = `${r / 255} ${g / 255} ${b / 255}`;
+ return a === undefined ? `color(display-p3 ${channels})` : `color(display-p3 ${channels} / ${a})`;
+};
diff --git a/src/utils/figmaColors.ts b/src/utils/figmaColors.ts
new file mode 100644
index 0000000000..92242fd23d
--- /dev/null
+++ b/src/utils/figmaColors.ts
@@ -0,0 +1,288 @@
+/**
+ * Figma color tokens — the SINGLE SOURCE OF TRUTH for every color value in the app (light +
+ * dark). The theme flattens these onto the MUI palette root, so each becomes a `--mui-palette-*`
+ * CSS var (Display-P3 + sRGB fallback). Consume them as bare token strings in `sx`
+ * (`sx={{ bgcolor: 'bg-1' }}`) or via `figVars` outside `sx` — never hand-write hex in components.
+ */
+export const figmaLight = {
+ 'bg-max': '#ffffff',
+ 'bg-1': '#fafafa',
+ 'bg-2': '#fcfcfc',
+ 'bg-3': '#fcfbfb',
+ 'bg-4': '#f6f7f4',
+ 'bg-5': '#f6f5f5',
+ 'bg-6': '#f1f1f1',
+ 'bg-7': '#ebebeb',
+ 'bgp-1': '#f7f7f7',
+ 'bgp-2': '#fcfcfc',
+ 'border-0': 'rgba(0, 0, 0, 0.06)',
+ 'border-1': 'rgba(0, 0, 0, 0.08)',
+ 'border-2': 'rgba(0, 0, 0, 0.12)',
+ 'fg-max': '#000000',
+ 'fg-1': '#201d1d',
+ 'fg-2': '#666666',
+ 'fg-3': '#858585',
+ 'fg-4': '#bcbbbb',
+ 'fg-5': '#cfcece',
+ 'fgp-3': '#8b8b8d',
+ // Muted icon grey (search, sortable-column chevrons, …). Mode-agnostic like fg-3.
+ 'fg-icon': '#A8A8A8',
+ selected: 'rgba(46, 15, 15, 0.04)',
+ 'blue-1': '#1a88f8',
+ 'blue-2': '#48abff',
+ 'blue-3': '#a9e7ff',
+ 'yellow-1': '#ffb200',
+ 'yellow-2': '#ffd631',
+ 'yellow-3': '#fff798',
+ 'red-1': '#f24900',
+ 'red-2': '#ff8947',
+ 'red-3': '#ffc693',
+ 'purple-1': '#9391f7',
+ 'purple-2': 'hsl(241, 76%, 67%)',
+ 'purple-3': '#e2e0ff',
+ 'green-1': '#1f807b',
+ 'green-2': '#63bbb6',
+ 'green-3': '#9debe7',
+ 'cyan-1': '#6bcef5',
+ 'cyan-2': '#b5e7fa',
+ 'cyan-3': '#dff6ff',
+ 'navy-1': '#1c4886',
+ 'navy-2': '#6188c0',
+ 'navy-3': '#b0d3ff',
+ 'shadow-low': 'rgba(0, 0, 0, 0.02)',
+ 'shadow-medium': 'rgba(0, 0, 0, 0.04)',
+ 'shadow-high': 'rgba(46, 15, 15, 0.07)',
+ 'shadow-strong': 'rgba(46, 15, 15, 0.12)',
+ 'shadow-stroke-1': 'rgba(46, 15, 15, 0.08)',
+ 'shadow-stroke-2': 'rgba(0, 0, 0, 0.08)',
+ ethereum: '#25292e',
+ focus: 'rgba(26, 136, 248, 0.2)',
+ scrim: 'rgba(247, 246, 246, 0.8)',
+ 'data-red': '#ff383c',
+ 'data-orange': '#e2a48d',
+ 'data-yellow': '#e3da8d',
+ 'data-lime': '#c1e38d',
+ 'data-green': '#32C958',
+ 'data-teal': '#8de3cc',
+ 'data-blue': '#8dcfe3',
+ 'data-purple': '#bcbbff',
+ 'data-pink': '#e1a4d9',
+ 'button-hover': 'rgba(0, 0, 0, 0.025)',
+ 'data-green-gho': '#5dff93',
+ // Gold for the favourited market star (mode-agnostic; Figma color(display-p3 1 0.7 0)).
+ 'favourite-star': '#FFB300',
+ // sGHO markets-banner gradient tints — data-green / neutral washes at 6%.
+ 'sgho-banner-green': 'rgba(50, 201, 88, 0.06)',
+ 'sgho-banner-fade': 'rgba(255, 255, 255, 0.06)',
+ 'chain-testnet': '#8594ab',
+ 'chain-ethereum': '#25292e',
+ 'chain-polygon': '#8347e5',
+ 'chain-base': '#0052ff',
+ 'chain-optimism': '#e84142',
+ 'chain-lens': '#36a136',
+ 'chain-arbitrum': '#28a0f0',
+ 'chain-blast': '#ffc700',
+ 'chain-scroll': '#f8cf6e',
+ 'chain-worldchain': '#ff9d00',
+ 'chain-zksync': '#8c8dfe',
+ 'info-panel-color-one': '#fafafa',
+ 'info-panel-color-two': '#fafafa',
+ 'info-panel-color-three': '#fafafa',
+ bone: '#f6f7f4',
+ // Contained-button hover fill. A single token (not an fg-1↔bone swap via the dark selector) so
+ // it resolves to the NEAREST color scheme — the dev showcase's local toggle works even when the
+ // app's global scheme differs. Light = fg-1 ink; dark = bone off-white.
+ 'fg-max-hover': '#201d1d',
+ // --- semantic tokens promoted from theme-file literals (SoT) ---
+ 'secondary-main': '#FF607B',
+ 'secondary-light': '#FF607B',
+ 'secondary-dark': '#B34356',
+ 'error-light': '#D26666',
+ 'error-dark': '#BC0000',
+ 'error-text': '#4F1919',
+ 'error-bg': '#F9EBEB',
+ 'warning-light': '#FFCE00',
+ 'warning-dark': '#C67F15',
+ 'warning-text': '#63400A',
+ 'warning-bg': '#FEF5E8',
+ 'info-light': '#0062D2',
+ 'info-dark': '#002754',
+ 'info-text': '#002754',
+ 'info-bg': '#E5EFFB',
+ 'success-light': '#90FF95',
+ 'success-dark': '#318435',
+ 'success-text': '#1C4B1E',
+ 'success-bg': '#ECF8ED',
+ 'disabled-fg': '#BBBECA',
+ 'disabled-bg': '#EAEBEF',
+ 'input-line': '#383D511F',
+ 'input-border-hover': '#CBCDD8',
+ 'surface-elevated': '#ffffff',
+} as const;
+
+export const figmaDark = {
+ 'bg-max': '#0a0a0a',
+ 'bg-1': '#100f0f',
+ 'bg-2': '#18181B',
+ 'bg-3': '#1f1e1e',
+ 'bg-4': '#1F1E1E',
+ 'bg-5': '#2A2828',
+ 'bg-6': '#393737',
+ 'bg-7': '#3f3e3e',
+ 'bgp-1': '#0f0f10',
+ 'bgp-2': '#18181B',
+ 'border-0': 'rgba(255, 255, 255, 0.06)',
+ 'border-1': 'rgba(255, 255, 255, 0.08)',
+ 'border-2': 'rgba(255, 255, 255, 0.12)',
+ 'fg-max': '#ffffff',
+ 'fg-1': '#ffffff',
+ 'fg-2': '#bcbbbb',
+ 'fg-3': '#727274',
+ 'fg-4': '#636161',
+ 'fg-5': '#383838',
+ 'fgp-3': '#8f8e8e',
+ // Muted icon grey (search, sortable-column chevrons, …). Mode-agnostic like fg-3.
+ 'fg-icon': '#A8A8A8',
+ selected: 'rgba(255, 255, 255, 0.06)',
+ 'blue-1': '#1a88f8',
+ 'blue-2': '#48abff',
+ 'blue-3': '#a9e7ff',
+ 'yellow-1': '#ffc42c',
+ 'yellow-2': '#ffd631',
+ 'yellow-3': '#fff7ae',
+ 'red-1': '#f24900',
+ 'red-2': '#ff8947',
+ 'red-3': '#ffc693',
+ 'purple-1': '#9391ff',
+ 'purple-2': '#bcbbff',
+ 'purple-3': '#e2e0ff',
+ 'green-1': '#1f807b',
+ 'green-2': '#63bbb6',
+ 'green-3': '#9debe7',
+ 'cyan-1': '#6bcef5',
+ 'cyan-2': '#b5e7fa',
+ 'cyan-3': '#dff6ff',
+ 'navy-1': '#1c4886',
+ 'navy-2': '#6188c0',
+ 'navy-3': '#b0d3ff',
+ 'data-red': '#d34d63',
+ 'data-orange': '#e68662',
+ 'data-yellow': '#fdc75a',
+ 'data-lime': '#c1e38d',
+ 'data-green': '#66C399',
+ 'data-teal': '#8de3cc',
+ 'data-blue': '#88d5ed',
+ 'data-purple': '#a5a3ff',
+ 'data-pink': '#e1a4d9',
+ 'shadow-low': 'rgba(0, 0, 0, 0.15)',
+ 'shadow-medium': 'rgba(0, 0, 0, 0.3)',
+ 'shadow-high': 'rgba(0, 0, 0, 0.35)',
+ 'shadow-strong': 'rgba(0, 0, 0, 0.5)',
+ 'shadow-stroke-1': 'rgba(255, 255, 255, 0.08)',
+ 'shadow-stroke-2': 'rgba(255, 255, 255, 0.08)',
+ ethereum: '#434b55',
+ focus: 'rgba(85, 167, 251, 0.3)',
+ scrim: 'rgba(71, 67, 67, 0.8)',
+ 'button-hover': 'rgba(255, 255, 255, 0.025)',
+ 'table-item-hover-1': '#1e1d1d',
+ 'table-item-hover-2': '#282727',
+ 'data-green-gho': '#5dff93',
+ // Gold for the favourited market star (mode-agnostic; Figma color(display-p3 1 0.7 0)).
+ 'favourite-star': '#FFB300',
+ // sGHO markets-banner gradient tints — dark data-green / neutral washes at 6%.
+ 'sgho-banner-green': 'rgba(102, 195, 153, 0.06)',
+ 'sgho-banner-fade': 'rgba(255, 255, 255, 0.06)',
+ 'wallet-modal-more-networks-label': 'rgba(255, 255, 255, 0.4)',
+ 'chain-testnet': '#bfc6d1',
+ 'chain-ethereum': '#7e8287',
+ 'chain-polygon': '#8347e5',
+ 'chain-base': '#0052ff',
+ 'chain-optimism': '#e84142',
+ 'chain-lens': '#36a136',
+ 'chain-arbitrum': '#28a0f0',
+ 'chain-blast': '#ffc700',
+ 'chain-scroll': '#f8cf6e',
+ 'chain-worldchain': '#ff9d00',
+ 'chain-zksync': '#8c8dfe',
+ 'info-panel-color-one': 'rgba(29, 29, 33, 0.20)',
+ 'info-panel-color-two': 'rgba(41, 41, 46, 0.20)',
+ 'info-panel-color-three': 'rgba(255, 255, 255, 0.01)',
+ bone: '#f6f7f4',
+ 'fg-max-hover': '#f6f7f4',
+ // --- semantic tokens promoted from theme-file literals (SoT) ---
+ 'secondary-main': '#F48FB1',
+ 'secondary-light': '#F6A5C0',
+ 'secondary-dark': '#AA647B',
+ 'error-light': '#E57373',
+ 'error-dark': '#D32F2F',
+ 'error-text': '#FBB4AF',
+ 'error-bg': '#2E0C0A',
+ 'warning-light': '#FFB74D',
+ 'warning-dark': '#F57C00',
+ 'warning-text': '#FFDCA8',
+ 'warning-bg': '#301E04',
+ 'info-light': '#4FC3F7',
+ 'info-dark': '#0288D1',
+ 'info-text': '#A9E2FB',
+ 'info-bg': '#071F2E',
+ 'success-light': '#90FF95',
+ 'success-dark': '#388E3C',
+ 'success-text': '#C2E4C3',
+ 'success-bg': '#0A130B',
+ 'disabled-fg': '#EBEBEF4D',
+ 'disabled-bg': '#EBEBEF1F',
+ 'input-line': '#EBEBEF6B',
+ 'input-border-hover': '#CBCDD8',
+ 'surface-elevated': '#1E1E20',
+} as const;
+
+// Token names shared by both modes (light is the common subset; dark adds a few extras).
+export type FigmaColorName = keyof typeof figmaLight;
+
+/** Resolve a single Figma color token for the active mode. */
+export const figmaColor = (mode: 'light' | 'dark', name: FigmaColorName) =>
+ mode === 'dark' ? figmaDark[name] : figmaLight[name];
+
+/**
+ * Pick the whole token map for a mode — the terse way to build the palette:
+ * const t = pickFigma(mode);
+ * text: { primary: t['fg-1'], secondary: t['fg-2'] }
+ */
+export const pickFigma = (mode: 'light' | 'dark'): Record =>
+ mode === 'dark' ? figmaDark : figmaLight;
+
+/**
+ * Terse, P3-safe accessor for the design tokens as CSS variables.
+ *
+ * The tokens are flattened onto the MUI palette root (see `theme.tsx`), so MUI generates a
+ * `--mui-palette-` custom property per token and the Display-P3 layer overrides those on
+ * wide-gamut displays. `figVars['bg-1']` therefore emits `var(--mui-palette-bg-1)`, which gets
+ * P3 + the structural sRGB fallback — unlike a raw `theme.palette['bg-1']` hex read, which does
+ * not. Use it in `styled()`, plain JS, and interpolated strings; inside `sx` the bare string
+ * form (`sx={{ bgcolor: 'bg-1' }}`) already resolves to the same var with no import.
+ *
+ * Gotcha: never pass a var-based color (this, a bare `sx` token, or `theme.vars.palette.*`) to a
+ * raw SVG/icon presentation attribute (``) — `var()` doesn't
+ * resolve there. Use a concrete hex, or apply the color via `sx`/`style` (CSS) instead.
+ *
+ * The `--mui-palette-` naming is coupled to MUI's var generation and to the tokens living
+ * at the palette root — the same coupling `collectP3Vars` (theme.tsx) relies on.
+ */
+export const figVars = Object.fromEntries(
+ Object.keys(figmaLight).map((name) => [name, `var(--mui-palette-${name})`])
+) as Record;
+
+/**
+ * Always-white, mode-independent. For text/icons that sit on a fixed colored surface (brand
+ * gradients, always-dark chips). A concrete hex — NOT a CSS var — so it also resolves in raw
+ * SVG/icon presentation attributes (`color=`/`fill=`), where `var()` does not.
+ */
+export const onAccent = '#ffffff';
+
+/**
+ * The shared "surface" box-shadow: a soft drop shadow plus a 1px ring that stands in
+ * for a border. Used by the secondary buttons, menus/paper, and the dashboard cards.
+ * `stroke` selects the ring token (cards use `shadow-stroke-1` for a slightly stronger hairline).
+ */
+export const figSurfaceShadow = (stroke: FigmaColorName = 'shadow-stroke-2'): string =>
+ `0px 2px 4px 0px ${figVars['shadow-low']}, 0px 0px 0px 1px ${figVars[stroke]}`;
diff --git a/src/utils/insetHighlight.ts b/src/utils/insetHighlight.ts
new file mode 100644
index 0000000000..e99eebc062
--- /dev/null
+++ b/src/utils/insetHighlight.ts
@@ -0,0 +1,77 @@
+import { CSSObject, Theme } from '@mui/material/styles';
+
+import { motion } from './motion';
+
+interface InsetHighlightOpts {
+ /** Only `transitions` is read — accepts the app theme or a plain MUI `Theme`. */
+ theme: Pick;
+ /** Corner radius of the highlight pseudo-element. */
+ radius: string | number;
+ /** Even inset applied to every side; overridden per-side by the props below. */
+ inset?: string | number;
+ top?: string | number;
+ right?: string | number;
+ bottom?: string | number;
+ left?: string | number;
+ /** Resting scale the highlight grows in from on activation (default 0.96). */
+ restScale?: number;
+ /**
+ * When set, the highlight is "on" at rest — a persistent selected state: full scale and this
+ * fill, rather than transparent-until-hover. Leave undefined for hover-only rows so no
+ * `background-color` is emitted at rest.
+ */
+ restFill?: string;
+}
+
+/**
+ * The inset-pseudo highlight recipe shared by the dropdown menu items (`MuiMenuItem` in
+ * `theme.tsx`) and the market-switcher option rows (`MarketSwitcher.tsx`). Draws the
+ * hover/selected fill on a `::before` inset from the row's edges — so adjacent highlights keep a
+ * visual gap while the physical row is unchanged — sitting behind the row's content
+ * (`zIndex: -1` under `isolation: isolate`) and growing in from `restScale` → 1.
+ *
+ * Pair with {@link insetHighlightActive} under the consumer's own hover/focus/selected selectors
+ * to set the fill and final scale (the trigger selectors differ per consumer: MUI classes for
+ * MenuItem, `:hover` + a JS boolean for the switcher).
+ */
+export const insetHighlightBase = ({
+ theme,
+ radius,
+ inset,
+ top,
+ right,
+ bottom,
+ left,
+ restScale = 0.96,
+ restFill,
+}: InsetHighlightOpts): CSSObject => ({
+ position: 'relative',
+ isolation: 'isolate',
+ '&::before': {
+ content: '""',
+ position: 'absolute',
+ top: top ?? inset ?? 0,
+ right: right ?? inset ?? 0,
+ bottom: bottom ?? inset ?? 0,
+ left: left ?? inset ?? 0,
+ zIndex: -1,
+ borderRadius: radius,
+ transform: restFill ? 'scale(1)' : `scale(${restScale})`,
+ transition: theme.transitions.create(['transform', 'background-color'], {
+ duration: motion.duration.hover,
+ }),
+ // Only emit a resting fill when persistently "on" — hover-only consumers (and the MenuItem
+ // refactor) stay identical, with no `background-color` until their own trigger fires.
+ ...(restFill ? { backgroundColor: restFill } : {}),
+ },
+});
+
+/**
+ * The "on" state for an {@link insetHighlightBase} highlight: the fill plus the grown-in scale.
+ * Apply under the consumer's hover / keyboard-focus / selected selectors, e.g.
+ * `'&:hover::before': insetHighlightActive(figVars['button-hover'])`.
+ */
+export const insetHighlightActive = (fill: string): CSSObject => ({
+ backgroundColor: fill,
+ transform: 'scale(1)',
+});
diff --git a/src/utils/motion.ts b/src/utils/motion.ts
new file mode 100644
index 0000000000..e23d0dea0e
--- /dev/null
+++ b/src/utils/motion.ts
@@ -0,0 +1,26 @@
+/**
+ * Central motion tokens — the single source of truth for overlay/dialog animation
+ * timing across the app. Consumed by the theme's transition defaults and by the
+ * shared transition components (e.g. `ScaleFade`). Values mirror the reference
+ * project's overlay "feel": a fast, subtle pop.
+ *
+ * Kept in its own module (rather than in `theme.tsx`) so shared transitions can read
+ * these tokens without importing `theme.tsx`, which would create an import cycle
+ * (`theme` → `ScaleFade` → `theme`).
+ */
+export const motion = {
+ duration: {
+ /** dropdowns, menus, selects, popovers */
+ overlay: 100,
+ /** interactive control feedback — button hover/focus state transitions */
+ hover: 100,
+ /** modal enter/exit — reserved for Phase 2 (modals are not animated yet) */
+ modal: 200,
+ /** mobile modal slide-up — reserved for Phase 2/3 */
+ modalMobile: 300,
+ },
+ easing: {
+ standard: 'ease',
+ smooth: 'cubic-bezier(0.19, 1, 0.22, 1)',
+ },
+} as const;
diff --git a/src/utils/theme.tsx b/src/utils/theme.tsx
index adee35697d..c677a3ed82 100644
--- a/src/utils/theme.tsx
+++ b/src/utils/theme.tsx
@@ -1,16 +1,120 @@
import {
CheckCircleIcon,
- ChevronDownIcon,
ExclamationCircleIcon,
ExclamationIcon,
InformationCircleIcon,
} from '@heroicons/react/outline';
-import { SvgIcon, Theme, ThemeOptions } from '@mui/material';
-import { createTheme } from '@mui/material/styles';
+import { Box, SvgIcon, ThemeOptions } from '@mui/material';
+import { type CSSObject, createTheme, experimental_extendTheme } from '@mui/material/styles';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { ColorPartial } from '@mui/material/styles/createPalette';
+// Augments MUI's base `Theme` (the one component `sx`/`styled` callbacks receive) with `.vars`,
+// so `theme.vars.palette.*` typechecks app-wide, not only against this file's `AppTheme` param.
+import type {} from '@mui/material/themeCssVarsAugmentation';
import React from 'react';
+import { ChevronUpDownIcon } from 'src/components/icons/ChevronUpDownIcon';
+import { ScaleFade } from 'src/components/primitives/transitions/ScaleFade';
+
+import { colorToP3 } from './colorToP3';
+import { type FigmaColorName, figSurfaceShadow, figVars, onAccent, pickFigma } from './figmaColors';
+import { insetHighlightActive, insetHighlightBase } from './insetHighlight';
+import { motion } from './motion';
+
+// The app theme is built with MUI's CSS-variables engine (`experimental_extendTheme`), so it
+// carries `.vars` (CSS custom-property refs like `figVars['bg-1']`) and
+// `.applyStyles(scheme, …)` for per-color-scheme overrides.
+type AppTheme = ReturnType;
+
+// MUI's `theme.applyStyles('dark', …)` needs the provider theme's `getColorSchemeSelector`,
+// which the raw `extendTheme` result (used to build the component overrides statically)
+// doesn't carry — so calling it there hits the classic `palette.mode` branch and throws (the
+// raw theme has no top-level `palette`). This helper inlines the exact CSS-vars selector
+// `applyStyles` emits, matching any ancestor with `data-mui-color-scheme="dark"` — the
+// element (app-wide) or a local wrapper (the dev showcase) — so both switch correctly.
+const darkScheme = (styles: object) => ({
+ '*:where([data-mui-color-scheme="dark"]) &': styles,
+});
+
+// Dropdown geometry: the menu paper's corner radius and the list's inset. The option-row
+// highlight radius is derived from these (paper radius − inset) to stay concentric, so keep
+// them together here — otherwise that relationship silently drifts.
+const MENU_PAPER_RADIUS = '0.75rem';
+const MENU_LIST_INSET = '0.38rem';
+
+/**
+ * Secondary "white pill" style for the `outlined` button variant: a hairline ring instead
+ * of a border (bg-max in light, bg-4 in dark) with a subtle fill shift on hover. On hover the
+ * ring is re-asserted (the global `disableElevation` default otherwise strips box-shadow),
+ * and `border` is forced to none to suppress MUI's default outlined hover border.
+ */
+const secondaryPillStyle = {
+ color: figVars['fg-1'],
+ // Different token per mode: bg-max (white) in light, a bg-4 fill in dark.
+ backgroundColor: figVars['bg-max'],
+ border: 'none',
+ boxShadow: figSurfaceShadow(),
+ '& .MuiButton-startIcon': {
+ color: figVars['fg-3'],
+ },
+ ...darkScheme({
+ backgroundColor: figVars['bg-4'],
+ }),
+ // Open state (a menu/popover trigger with `aria-expanded="true"`) keeps the hover fill.
+ '&:hover, &.Mui-focusVisible, &[aria-expanded="true"]': {
+ backgroundColor: figVars['bg-1'],
+ // Suppress MUI's default outlined hover border (its `:hover` rule would otherwise
+ // re-introduce a 1px border on top of the borderless pill).
+ border: 'none',
+ boxShadow: figSurfaceShadow(),
+ ...darkScheme({
+ backgroundColor: figVars['bg-5'],
+ }),
+ },
+};
+
+// Shared box geometry for the custom selection-control icons (checkbox + radio).
+const checkboxIconBox = { width: 18, height: 18, borderRadius: '0.375rem' };
+
+// Keyboard-focus ring shared by the buttons and the selection controls / switch: a 2px ring in the
+// element's own colour, offset 3px out.
+const focusRing = { outline: '2px solid currentColor', outlineOffset: '3px' } as const;
+
+// Selection-control (checkbox + radio) icon recipes — shared so the two never drift. The unchecked
+// box is a bg-max fill with an inset border-0 hairline that darkens to fg-4 on hover (keyed to the shared
+// .MuiButtonBase-root both controls carry, so one selector covers both); the checked box is a
+// purple-1 fill centered on its glyph. Radio spreads these and overrides borderRadius to a circle.
+const selectionControlResting = {
+ ...checkboxIconBox,
+ backgroundColor: figVars['bg-max'],
+ boxShadow: `inset 0 0 0 1px ${figVars['border-0']}`,
+ boxSizing: 'border-box' as const,
+ '.MuiButtonBase-root:hover &': {
+ boxShadow: `inset 0 0 0 1px ${figVars['fg-4']}`,
+ },
+ // Keyboard-focus ring (see `focusRing`), hugging the icon box. The focus class lands on the
+ // shared ButtonBase root, so key it off that.
+ '.MuiButtonBase-root.Mui-focusVisible &': focusRing,
+};
+const selectionControlChecked = {
+ ...checkboxIconBox,
+ backgroundColor: figVars['purple-1'],
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ // Keyboard-focus ring (see `focusRing`).
+ '.MuiButtonBase-root.Mui-focusVisible &': focusRing,
+};
+const selectionControlRootReset = {
+ root: {
+ '&:hover, &.Mui-focusVisible': {
+ backgroundColor: 'transparent',
+ },
+ },
+};
+
+// Soft shadow under the Switch's thumb.
+const controlThumbShadow = '0px 1px 1px rgba(0, 0, 0, 0.12)';
const theme = createTheme();
const {
@@ -30,27 +134,14 @@ declare module '@mui/material/styles/createPalette' {
default: string;
paper: string;
surface: string;
- surface2: string;
- header: string;
- disabled: string;
}
- interface Palette {
- gradients: {
- aaveGradient: string;
- newGradient: string;
- };
- other: {
- standardInputLine: string;
- };
- }
+ // Design tokens are flattened onto the palette root (see `getDesignTokens`), so each token is
+ // a first-class palette member. This also turns a token name that collides with a built-in
+ // palette key (e.g. `error`, `background`) into a compile error rather than a silent overwrite.
+ interface Palette extends Record {}
- interface PaletteOptions {
- gradients: {
- aaveGradient: string;
- newGradient: string;
- };
- }
+ interface PaletteOptions extends Partial> {}
}
interface TypographyCustomVariants {
@@ -62,15 +153,9 @@ interface TypographyCustomVariants {
buttonM: React.CSSProperties;
buttonS: React.CSSProperties;
helperText: React.CSSProperties;
- tooltip: React.CSSProperties;
- main21: React.CSSProperties;
secondary21: React.CSSProperties;
- main16: React.CSSProperties;
secondary16: React.CSSProperties;
- main14: React.CSSProperties;
- secondary14: React.CSSProperties;
main12: React.CSSProperties;
- secondary12: React.CSSProperties;
}
declare module '@mui/material/styles' {
@@ -97,16 +182,10 @@ declare module '@mui/material/Typography' {
buttonM: true;
buttonS: true;
helperText: true;
- tooltip: true;
- main21: true;
secondary21: true;
- main16: true;
secondary16: true;
- main14: true;
- secondary14: true;
main12: true;
- secondary12: true;
- h5: false;
+ h5: true;
h6: false;
subtitle1: false;
subtitle2: false;
@@ -117,99 +196,89 @@ declare module '@mui/material/Typography' {
}
}
-declare module '@mui/material/Button' {
- interface ButtonPropsVariantOverrides {
- surface: true;
- gradient: true;
+declare module '@mui/material/Paper' {
+ interface PaperPropsVariantOverrides {
+ modal: true;
+ card: true;
}
}
export const getDesignTokens = (mode: 'light' | 'dark') => {
- const getColor = (lightColor: string, darkColor: string) =>
- mode === 'dark' ? darkColor : lightColor;
+ const t = pickFigma(mode); // ← the one line of setup
return {
breakpoints: {
- keys: ['xs', 'xsm', 'sm', 'md', 'lg', 'xl', 'xxl'],
+ keys: ['xs', 'xsm', 'sm', 'md', 'mdlg', 'lg', 'xl', 'xxl'],
values: { xs: 0, xsm: 640, sm: 760, md: 960, mdlg: 1125, lg: 1280, xl: 1575, xxl: 1800 },
},
palette: {
mode,
+ // Design tokens flattened onto the palette root → MUI generates a `--mui-palette-`
+ // var per token, so `sx={{ bgcolor: 'bg-1' }}` and `figVars['bg-1']` both resolve to it.
+ ...t,
primary: {
- main: getColor('#383D51', '#EAEBEF'),
- light: getColor('#62677B', '#F1F1F3'),
- dark: getColor('#292E41', '#D2D4DC'),
- contrast: getColor('#FFFFFF', '#0F121D'),
+ main: t['fg-1'],
+ light: t['fg-2'],
+ dark: t['fg-max'],
+ contrastText: t['bg-1'],
},
secondary: {
- main: getColor('#FF607B', '#F48FB1'),
- light: getColor('#FF607B', '#F6A5C0'),
- dark: getColor('#B34356', '#AA647B'),
+ main: t['secondary-main'],
+ light: t['secondary-light'],
+ dark: t['secondary-dark'],
},
error: {
- main: getColor('#BC0000B8', '#F44336'),
- light: getColor('#D26666', '#E57373'),
- dark: getColor('#BC0000', '#D32F2F'),
- '100': getColor('#4F1919', '#FBB4AF'), // for alert text
- '200': getColor('#F9EBEB', '#2E0C0A'), // for alert background
+ main: t['red-1'],
+ light: t['error-light'],
+ dark: t['error-dark'],
+ '100': t['error-text'], // alert text
+ '200': t['error-bg'], // alert background
},
warning: {
- main: getColor('#F89F1A', '#FFA726'),
- light: getColor('#FFCE00', '#FFB74D'),
- dark: getColor('#C67F15', '#F57C00'),
- '100': getColor('#63400A', '#FFDCA8'), // for alert text
- '200': getColor('#FEF5E8', '#301E04'), // for alert background
+ main: t['yellow-1'],
+ light: t['warning-light'],
+ dark: t['warning-dark'],
+ '100': t['warning-text'],
+ '200': t['warning-bg'],
},
info: {
- main: getColor('#0062D2', '#29B6F6'),
- light: getColor('#0062D2', '#4FC3F7'),
- dark: getColor('#002754', '#0288D1'),
- '100': getColor('#002754', '#A9E2FB'), // for alert text
- '200': getColor('#E5EFFB', '#071F2E'), // for alert background
+ main: t['blue-1'],
+ light: t['info-light'],
+ dark: t['info-dark'],
+ '100': t['info-text'],
+ '200': t['info-bg'],
},
success: {
- main: getColor('#4CAF50', '#66BB6A'),
- light: getColor('#90FF95', '#90FF95'),
- dark: getColor('#318435', '#388E3C'),
- '100': getColor('#1C4B1E', '#C2E4C3'), // for alert text
- '200': getColor('#ECF8ED', '#0A130B'), // for alert background
+ main: t['data-green'],
+ light: t['success-light'],
+ dark: t['success-dark'],
+ '100': t['success-text'],
+ '200': t['success-bg'],
},
text: {
- primary: getColor('#303549', '#F1F1F3'),
- secondary: getColor('#62677B', '#A5A8B6'),
- disabled: getColor('#D2D4DC', '#62677B'),
- muted: getColor('#A5A8B6', '#8E92A3'),
- highlight: getColor('#383D51', '#C9B3F9'),
+ primary: t['fg-1'],
+ secondary: t['fg-2'],
+ disabled: t['fg-4'],
+ muted: t['fg-3'],
},
background: {
- default: getColor('#F1F1F3', '#1B2030'),
- paper: getColor('#FFFFFF', '#292E41'),
- surface: getColor('#F7F7F9', '#383D51'),
- surface2: getColor('#F9F9FB', '#383D51'),
- header: getColor('#2B2D3C', '#1B2030'),
- disabled: getColor('#EAEBEF', '#EBEBEF14'),
- },
- divider: getColor('#EAEBEF', '#EBEBEF14'),
- action: {
- active: getColor('#8E92A3', '#EBEBEF8F'),
- hover: getColor('#F1F1F3', '#EBEBEF14'),
- selected: getColor('#EAEBEF', '#EBEBEF29'),
- disabled: getColor('#BBBECA', '#EBEBEF4D'),
- disabledBackground: getColor('#EAEBEF', '#EBEBEF1F'),
- focus: getColor('#F1F1F3', '#EBEBEF1F'),
+ default: t['bg-5'],
+ paper: t['surface-elevated'],
+ surface: t['bg-2'],
},
- other: {
- standardInputLine: getColor('#383D511F', '#EBEBEF6B'),
- },
- gradients: {
- aaveGradient: 'linear-gradient(248.86deg, #B6509E 10.51%, #2EBAC6 93.41%)',
- newGradient: 'linear-gradient(79.67deg, #8C3EBC 0%, #007782 95.82%)',
+ divider: t['border-2'],
+ action: {
+ active: t['fg-3'],
+ hover: t['button-hover'],
+ selected: t['selected'],
+ disabled: t['disabled-fg'],
+ disabledBackground: t['disabled-bg'],
+ focus: t['focus'],
},
},
spacing: 4,
typography: {
fontFamily: FONT,
- h5: undefined,
h6: undefined,
subtitle1: undefined,
subtitle2: undefined,
@@ -233,16 +302,14 @@ export const getDesignTokens = (mode: 'light' | 'dark') => {
},
h2: {
fontFamily: FONT,
- fontWeight: 600,
- letterSpacing: 'unset',
- lineHeight: '133.4%',
- fontSize: pxToRem(21),
+ fontWeight: 500,
+ lineHeight: '120%',
+ fontSize: pxToRem(24),
},
h3: {
fontFamily: FONT,
- fontWeight: 600,
- letterSpacing: pxToRem(0.15),
- lineHeight: '160%',
+ fontWeight: 500,
+ lineHeight: '120%',
fontSize: pxToRem(18),
},
h4: {
@@ -252,6 +319,12 @@ export const getDesignTokens = (mode: 'light' | 'dark') => {
lineHeight: pxToRem(24),
fontSize: pxToRem(16),
},
+ h5: {
+ fontFamily: FONT,
+ fontWeight: 500,
+ lineHeight: pxToRem(18),
+ fontSize: pxToRem(14),
+ },
subheader1: {
fontFamily: FONT,
fontWeight: 600,
@@ -290,7 +363,8 @@ export const getDesignTokens = (mode: 'light' | 'dark') => {
buttonM: {
fontFamily: FONT,
fontWeight: 500,
- lineHeight: pxToRem(24),
+ letterSpacing: '-0.00563rem',
+ lineHeight: '1.25rem',
fontSize: pxToRem(14),
},
buttonS: {
@@ -308,32 +382,12 @@ export const getDesignTokens = (mode: 'light' | 'dark') => {
lineHeight: pxToRem(12),
fontSize: pxToRem(10),
},
- tooltip: {
- fontFamily: FONT,
- fontWeight: 400,
- letterSpacing: pxToRem(0.15),
- lineHeight: pxToRem(16),
- fontSize: pxToRem(12),
- },
- main21: {
- fontFamily: FONT,
- fontWeight: 800,
- lineHeight: '133.4%',
- fontSize: pxToRem(21),
- },
secondary21: {
fontFamily: FONT,
fontWeight: 500,
lineHeight: '133.4%',
fontSize: pxToRem(21),
},
- main16: {
- fontFamily: FONT,
- fontWeight: 600,
- letterSpacing: pxToRem(0.15),
- lineHeight: pxToRem(24),
- fontSize: pxToRem(16),
- },
secondary16: {
fontFamily: FONT,
fontWeight: 500,
@@ -341,20 +395,6 @@ export const getDesignTokens = (mode: 'light' | 'dark') => {
lineHeight: pxToRem(24),
fontSize: pxToRem(16),
},
- main14: {
- fontFamily: FONT,
- fontWeight: 600,
- letterSpacing: pxToRem(0.15),
- lineHeight: pxToRem(20),
- fontSize: pxToRem(14),
- },
- secondary14: {
- fontFamily: FONT,
- fontWeight: 500,
- letterSpacing: pxToRem(0.15),
- lineHeight: pxToRem(20),
- fontSize: pxToRem(14),
- },
main12: {
fontFamily: FONT,
fontWeight: 600,
@@ -362,18 +402,50 @@ export const getDesignTokens = (mode: 'light' | 'dark') => {
lineHeight: pxToRem(16),
fontSize: pxToRem(12),
},
- secondary12: {
- fontFamily: FONT,
- fontWeight: 500,
- letterSpacing: pxToRem(0.1),
- lineHeight: pxToRem(16),
- fontSize: pxToRem(12),
- },
},
} as ThemeOptions;
};
-export function getThemedComponents(theme: Theme) {
+/**
+ * Subtle press feedback shared by buttons and dropdown triggers: the control scales down
+ * slightly while active (pointer/touch down), and never when disabled. Pair with a `transform`
+ * transition (at `motion.duration.hover`) so the release animates back. Reduced-motion users
+ * get the scale instantly via the global `prefers-reduced-motion` rule in MuiCssBaseline.
+ */
+const pressScaleActive = {
+ '&:active:not(.Mui-disabled)': {
+ transform: 'scale(0.99)',
+ },
+};
+
+/**
+ * Disabled button treatment: the label/icon stay crisp while the button's own background (+ box
+ * shadow) render at 50% on an `opacity: 0.5` `::before` layer. Opacity is used (not color-mix /
+ * channel alpha) so the faded fill keeps its Display-P3 color; a box-shadow also has no opacity of
+ * its own, so fading a layer is the only clean way to halve it. `isolation: isolate` makes the root
+ * a stacking context so the `z-index: -1` layer sits behind the label, not behind the parent bg.
+ */
+const disabledFade = (opts: { color: string; before: CSSObject }): CSSObject => ({
+ color: opts.color,
+ backgroundColor: 'transparent',
+ border: 'none',
+ boxShadow: 'none',
+ isolation: 'isolate',
+ '&::before': {
+ content: "''",
+ position: 'absolute',
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0,
+ borderRadius: 'inherit',
+ opacity: 0.5,
+ zIndex: -1,
+ ...opts.before,
+ },
+});
+
+export function getThemedComponents(theme: AppTheme) {
return {
components: {
MuiSkeleton: {
@@ -386,27 +458,60 @@ export function getThemedComponents(theme: Theme) {
MuiOutlinedInput: {
styleOverrides: {
root: {
- borderRadius: '6px',
- borderColor: theme.palette.divider,
- '&:hover .MuiOutlinedInput-notchedOutline': {
- borderColor: '#CBCDD8',
+ borderRadius: '0.5rem',
+ // Text inputs (everything that isn't a Select): a bg-max surface with the shared
+ // surface shadow (shadow-low drop + shadow-stroke-2 1px ring) instead of a border.
+ // Selects keep their own fill via the `:has(.MuiSelect-select)` block below.
+ '&:not(:has(.MuiSelect-select))': {
+ backgroundColor: figVars['bg-max'],
+ boxShadow: figSurfaceShadow(),
+ '& .MuiOutlinedInput-notchedOutline': { border: 'none' },
},
- '&.Mui-focused .MuiOutlinedInput-notchedOutline': {
- borderColor: '#CBCDD8',
+ // Select trigger = the outlined-button surface (secondaryPillStyle): a bg-1/bg-4 fill
+ // wrapped by the shared ring (figSurfaceShadow), same 0.5rem radius (from `root`).
+ // The notched border is dropped — the ring IS the outline — so there's no blueish or
+ // animated border; hover & open step the fill (same as the button) while the ring
+ // stays put.
+ '&:has(.MuiSelect-select)': {
+ backgroundColor: figVars['bg-1'],
+ boxShadow: figSurfaceShadow(),
+ // Animate the hover/open fill+ring step (was instant — the root had no transition).
+ transition: theme.transitions.create(['background-color', 'box-shadow'], {
+ duration: motion.duration.hover,
+ }),
+ ...darkScheme({ backgroundColor: figVars['bg-4'] }),
+ '& .MuiOutlinedInput-notchedOutline': { border: 'none' },
+ // Open fill is keyed to the Select's actual open state (`aria-expanded` on the
+ // select), NOT `.Mui-focused`: a Select keeps focus after its menu closes, so a
+ // focus-based fill would linger after closing and while other fields are focused.
+ '&:hover, &:has(.MuiSelect-select[aria-expanded="true"])': {
+ backgroundColor: figVars['bg-4'],
+ boxShadow: figSurfaceShadow(),
+ ...darkScheme({ backgroundColor: figVars['bg-5'] }),
+ '& .MuiOutlinedInput-notchedOutline': { border: 'none' },
+ },
+ // Keyboard-focus ring only (browser deems focus visible → keyboard nav, not the
+ // focus MUI restores to the trigger on close). Matches the outlined-button ring.
+ '&:has(.MuiSelect-select:focus-visible)': {
+ outline: `2px solid ${figVars['fg-1']}`,
+ outlineOffset: '3px',
+ },
+ // Disabled dropdown: inert to hover / pointer / touch (no hover fill step, no
+ // pointer cursor). The faded look still comes from MUI's `.Mui-disabled` text.
+ '&.Mui-disabled': {
+ pointerEvents: 'none',
+ },
},
},
},
},
- MuiSlider: {
- styleOverrides: {
- root: {
- '& .MuiSlider-thumb': {
- color: theme.palette.mode === 'light' ? '#62677B' : '#C9B3F9',
- },
- '& .MuiSlider-track': {
- color: theme.palette.mode === 'light' ? '#383D51' : '#9C93B3',
- },
- },
+ MuiButtonBase: {
+ defaultProps: {
+ // No ripple / pressed "splash" on any control (menu items, buttons, icon
+ // buttons, toggles, checkboxes, tabs, …). Interaction is conveyed by hover,
+ // keyboard focus, and the press-scale — not MUI's ripple. Set on ButtonBase so
+ // it covers every ButtonBase-derived component in one place.
+ disableRipple: true,
},
},
MuiButton: {
@@ -415,54 +520,154 @@ export function getThemedComponents(theme: Theme) {
},
styleOverrides: {
root: {
- borderRadius: '4px',
+ // Size to content + padding, not MUI's default 64px floor (which let buttons in tight
+ // flex rows squish below their content). Row action buttons re-add an even floor
+ // locally (ListButtonsColumn); deliberate collapses keep their own minWidth: 0.
+ minWidth: 'unset',
+ // Never wrap the label to a second line — buttons size to their text and stay one line
+ // even in tight flex rows (e.g. the sGHO markets banner's action row).
+ whiteSpace: 'nowrap',
+ // Hover/focus state transition at 100ms (overrides MUI's 250ms default).
+ // `transform` is included so the active-press scale animates in and out.
+ transition: theme.transitions.create(
+ ['background-color', 'box-shadow', 'border-color', 'color', 'transform'],
+ { duration: motion.duration.hover }
+ ),
+ // Subtle press feedback — scale down while active (not when disabled).
+ ...pressScaleActive,
+ // Keyboard-focus ring in the variant's own text color; ButtonBase zeroes the
+ // native outline, so we set our own (2px, offset 3px out).
+ '&.Mui-focusVisible': {
+ outline: '2px solid currentColor',
+ outlineOffset: '3px',
+ },
},
sizeLarge: {
...theme.typography.buttonL,
- padding: '10px 24px',
+ height: '48px',
+ padding: '0 24px',
+ borderRadius: '0.625rem',
},
sizeMedium: {
...theme.typography.buttonM,
- padding: '6px 12px',
+ height: '36px',
+ // Text-side padding; a start/end icon's -4px slot margin (MUI default) tightens
+ // the icon side to ~10px automatically.
+ padding: '0 0.88rem',
+ borderRadius: '0.5rem',
},
sizeSmall: {
- ...theme.typography.buttonS,
- padding: '0 6px',
+ // v3: small buttons use buttonM (14px / 500 / no uppercase) + 0.62rem side padding —
+ // the same label style as sizeMedium, at a compact height. (Was the legacy buttonS:
+ // uppercase 10px / 6px padding, which the button rework never migrated.)
+ ...theme.typography.buttonM,
+ height: '28px',
+ padding: '0 0.62rem',
+ borderRadius: '0.375rem',
},
},
variants: [
+ // Secondary "white pill": a hairline ring instead of a border, with a bg-4 fill
+ // in dark mode.
{
- props: { variant: 'surface' },
+ props: { color: 'primary', variant: 'outlined' },
style: {
- color: theme.palette.common.white,
- border: '1px solid',
- borderColor: '#EBEBED1F',
- backgroundColor: '#383D51',
- '&:hover, &.Mui-focusVisible': {
- backgroundColor: theme.palette.background.header,
+ ...secondaryPillStyle,
+ '&.Mui-disabled': {
+ color: figVars['fg-3'],
+ border: 'none',
+ boxShadow: figSurfaceShadow(),
},
},
},
{
- props: { variant: 'gradient' },
+ props: { variant: 'contained', color: 'primary' },
style: {
- color: theme.palette.common.white,
- background: theme.palette.gradients.aaveGradient,
- transition: 'all 0.2s ease',
+ backgroundColor: figVars['fg-max'],
+ // Same lift as the outlined pill, but ringed in the button's own fill (not
+ // shadow-stroke-2) — the opaque bg already reads as a boundary, so the ring just
+ // needs to disappear into it while the drop-shadow layer still adds the lift.
+ boxShadow: figSurfaceShadow('fg-max'),
'&:hover, &.Mui-focusVisible': {
- background: theme.palette.gradients.aaveGradient,
- opacity: '0.9',
+ // One per-scheme token (fg-max-hover: fg-1 ink in light, bone off-white in dark)
+ // instead of an fg-1↔bone swap via the dark selector — the hover follows the
+ // NEAREST color scheme (the dev showcase's local toggle), not the global .
+ backgroundColor: figVars['fg-max-hover'],
+ boxShadow: figSurfaceShadow('fg-max-hover'),
},
+ // The root focus ring uses `currentColor`, which here is contrastText (bg-1) —
+ // nearly the same shade as the page background, so it's invisible. Re-point it at
+ // fg-1 (same ink the outlined variant's ring uses) so it reads against the page.
+ '&.Mui-focusVisible': {
+ outlineColor: figVars['fg-1'],
+ },
+ // Disabled: crisp label, fg-max fill at 50% (no box-shadow on contained).
+ '&.Mui-disabled': disabledFade({
+ color: figVars['bg-1'],
+ before: { backgroundColor: figVars['fg-max'] },
+ }),
},
},
- {
- props: { color: 'primary', variant: 'outlined' },
- style: {
- background: theme.palette.background.surface,
- borderColor: theme.palette.divider,
+ ],
+ },
+ MuiIconButton: {
+ styleOverrides: {
+ root: {
+ transition: theme.transitions.create(['background-color', 'color', 'transform'], {
+ duration: motion.duration.hover,
+ }),
+ // Subtle press feedback — scale down while active (not when disabled).
+ ...pressScaleActive,
+ // Keep the hover fill while the menu this button opens is expanded (open === hover).
+ // MUI's IconButton hover is `action.hover` (= button-hover), so match it.
+ '&[aria-expanded="true"]': {
+ backgroundColor: figVars['button-hover'],
},
},
- ],
+ },
+ },
+ MuiToggleButton: {
+ styleOverrides: {
+ root: {
+ transition: theme.transitions.create(['background-color', 'color', 'transform'], {
+ duration: motion.duration.hover,
+ }),
+ // Subtle press feedback — scale down while active (not when disabled).
+ ...pressScaleActive,
+ },
+ },
+ },
+ MuiCheckbox: {
+ defaultProps: {
+ icon: ,
+ checkedIcon: (
+
+
+
+
+
+ ),
+ },
+ styleOverrides: selectionControlRootReset,
+ },
+ MuiRadio: {
+ defaultProps: {
+ // Circular twin of the custom checkbox — shares its recipe, overriding the shape.
+ icon: ,
+ checkedIcon: (
+
+
+
+ ),
+ },
+ styleOverrides: selectionControlRootReset,
},
MuiTypography: {
defaultProps: {
@@ -473,6 +678,7 @@ export function getThemedComponents(theme: Theme) {
h2: 'h2',
h3: 'h3',
h4: 'h4',
+ h5: 'p',
subheader1: 'p',
subheader2: 'p',
caption: 'p',
@@ -481,15 +687,9 @@ export function getThemedComponents(theme: Theme) {
buttonM: 'p',
buttonS: 'p',
main12: 'p',
- main14: 'p',
- main16: 'p',
- main21: 'p',
- secondary12: 'p',
- secondary14: 'p',
secondary16: 'p',
secondary21: 'p',
helperText: 'span',
- tooltip: 'span',
},
},
},
@@ -500,21 +700,56 @@ export function getThemedComponents(theme: Theme) {
},
MuiMenu: {
defaultProps: {
+ // Menu hard-defaults transitionDuration='auto' and forwards it explicitly,
+ // shadowing the MuiPopover default below — so menus/selects need the duration
+ // set here too. TransitionComponent is set explicitly as well (rather than
+ // relying on the inner Popover's own default) to keep the theme authoritative.
+ TransitionComponent: ScaleFade,
+ transitionDuration: motion.duration.overlay,
PaperProps: {
- elevation: 0,
variant: 'outlined',
style: {
minWidth: 240,
- marginTop: '4px',
},
},
},
+ styleOverrides: {
+ // Own the dropdown paper's look HERE (not only via PaperProps) so it survives
+ // components that inject their own paper slotProps and drop the theme's PaperProps —
+ // most notably Select, whose menu would otherwise lose the 8px offset + outlined
+ // surface and look nothing like our other dropdowns. `&&` outweighs the MuiPaper
+ // variant styles. With the 0.38rem list inset + 2rem rows (MuiMenuItem), every
+ // dropdown (Selects included) matches the settings menu.
+ paper: {
+ '&&': {
+ marginTop: '8px',
+ borderRadius: MENU_PAPER_RADIUS,
+ border: 'none',
+ boxShadow: figSurfaceShadow(),
+ backgroundColor: figVars['surface-elevated'],
+ },
+ // Dark surface at the SAME doubled specificity as the light fill above, so it wins
+ // in dark mode. (The darkScheme helper's single `&` lost to `&&`, which left the
+ // light paper — and light-looking options — showing in dark mode.)
+ '*:where([data-mui-color-scheme="dark"]) &&': {
+ backgroundColor: figVars['bg-2'],
+ },
+ '.MuiList-root': { padding: MENU_LIST_INSET },
+ },
+ },
+ },
+ MuiPopover: {
+ // Covers raw Popover usages (MarketSwitcher desktop, multiselects, swap inputs).
+ defaultProps: {
+ TransitionComponent: ScaleFade,
+ transitionDuration: motion.duration.overlay,
+ },
},
MuiList: {
styleOverrides: {
root: {
- '.MuiMenuItem-root+.MuiDivider-root, .MuiDivider-root': {
- marginTop: '4px',
+ '.MuiDivider-root': {
+ marginTop: '8px',
marginBottom: '4px',
},
},
@@ -527,21 +762,52 @@ export function getThemedComponents(theme: Theme) {
MuiMenuItem: {
styleOverrides: {
root: {
- padding: '12px 16px',
+ minHeight: '2rem',
+ // MUI relaxes MenuItem min-height to `auto` at ≥sm; re-assert 2rem there so
+ // every option row is a firm 2rem tall on desktop too.
+ [theme.breakpoints.up('sm')]: { minHeight: '2rem' },
+ padding: '0.31rem 0.38rem',
+ // The hover/selected highlight is a pseudo-element inset 1px top & bottom, so
+ // adjacent highlights keep a small gap while the row itself stays full-height — the
+ // hover target is continuous, so moving between rows never interrupts the highlight.
+ // Shared recipe (geometry + motion) lives in insetHighlight.ts; the radius is kept
+ // concentric with the menu paper (paper radius − list inset).
+ ...insetHighlightBase({
+ theme,
+ radius: `calc(${MENU_PAPER_RADIUS} - ${MENU_LIST_INSET})`,
+ top: '1px',
+ bottom: '1px',
+ }),
+ // Hover, keyboard focus (arrow-key nav sets .Mui-focusVisible), and the selected row
+ // all share one subtle highlight — the button-hover fill, never MUI's primary tint.
+ '&:hover::before, &.Mui-focusVisible::before, &.Mui-selected::before':
+ insetHighlightActive(figVars['button-hover']),
+ // Highlight lives on the pseudo above — keep the row's own background clear.
+ // The compound selected states are listed explicitly: MUI's base MenuItem paints
+ // `&.Mui-selected:hover` / `&.Mui-selected.Mui-focusVisible` with a primary tint at
+ // higher specificity than a lone `&.Mui-selected`, so without these the selected row
+ // would show a stronger fill than other rows on hover/keyboard-focus.
+ '&:hover, &.Mui-focusVisible, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible':
+ {
+ backgroundColor: 'transparent',
+ },
},
},
},
MuiListItemText: {
styleOverrides: {
root: {
- ...theme.typography.subheader1,
+ ...theme.typography.subheader2,
+ fontSize: pxToRem(14),
+ fontWeight: 400,
+ lineHeight: pxToRem(14),
},
},
},
MuiListItemIcon: {
styleOverrides: {
root: {
- color: theme.palette.primary.light,
+ color: theme.vars.palette.primary.light,
minWidth: 'unset !important',
marginRight: '12px',
},
@@ -558,26 +824,45 @@ export function getThemedComponents(theme: Theme) {
MuiPaper: {
styleOverrides: {
root: {
- borderRadius: '4px',
+ borderRadius: '8px',
},
},
variants: [
{
props: { variant: 'outlined' },
style: {
- border: `1px solid ${theme.palette.divider}`,
- boxShadow: '0px 0px 2px rgba(0, 0, 0, 0.2), 0px 2px 10px rgba(0, 0, 0, 0.1)',
- background:
- theme.palette.mode === 'light'
- ? theme.palette.background.paper
- : theme.palette.background.surface,
+ border: 'none',
+ boxShadow: figSurfaceShadow(),
+ background: figVars['surface-elevated'],
+ ...darkScheme({
+ background: figVars['bg-2'],
+ }),
},
},
{
props: { variant: 'elevation' },
style: {
boxShadow: '0px 2px 1px rgba(0, 0, 0, 0.05), 0px 0px 1px rgba(0, 0, 0, 0.25)',
- ...(theme.palette.mode === 'dark' ? { backgroundImage: 'none' } : {}),
+ ...darkScheme({ backgroundImage: 'none' }),
+ },
+ },
+ {
+ props: { variant: 'modal' },
+ style: {
+ borderRadius: '0.75rem',
+ backgroundColor: figVars['bg-2'],
+ boxShadow: `0 0 0 1px ${figVars['border-1']}, 0 4px 16px 0 ${figVars['shadow-medium']}`,
+ },
+ },
+ {
+ // Canonical list/content card surface — the single source of truth for ListWrapper and
+ // the module cards (reserve-overview, staking, sGho, …). surface-elevated fill, 10px
+ // radius, the shared surface ring (shadow-stroke-1 hairline + soft drop).
+ props: { variant: 'card' },
+ style: {
+ backgroundColor: figVars['surface-elevated'],
+ borderRadius: '10px',
+ boxShadow: figSurfaceShadow('shadow-stroke-1'),
},
},
],
@@ -589,10 +874,8 @@ export function getThemedComponents(theme: Theme) {
flexDirection: 'column',
flex: 1,
paddingBottom: '39px',
- [theme.breakpoints.up('xs')]: {
- paddingLeft: '8px',
- paddingRight: '8px',
- },
+ paddingLeft: '8px',
+ paddingRight: '8px',
[theme.breakpoints.up('xsm')]: {
paddingLeft: '20px',
paddingRight: '20px',
@@ -608,9 +891,9 @@ export function getThemedComponents(theme: Theme) {
[theme.breakpoints.up('lg')]: {
paddingLeft: '20px',
paddingRight: '20px',
+ maxWidth: '1280px',
},
[theme.breakpoints.up('xl')]: {
- maxWidth: 'unset',
paddingLeft: '96px',
paddingRight: '96px',
},
@@ -625,34 +908,57 @@ export function getThemedComponents(theme: Theme) {
MuiSwitch: {
styleOverrides: {
root: {
- height: 20 + 6 * 2,
- width: 34 + 6 * 2,
- padding: 6,
+ width: '1.75rem',
+ height: '1.125rem',
+ padding: 0,
+ flexShrink: 0,
+ borderRadius: '9px',
+ // Keyboard-focus ring (see `focusRing`). The focus class lands on the inner
+ // switchBase, so key the root's ring off it.
+ '&:has(.Mui-focusVisible)': focusRing,
},
switchBase: {
- padding: 8,
+ padding: 0,
+ margin: '2px',
'&.Mui-checked': {
- transform: 'translateX(14px)',
+ transform: 'translateX(10px)',
'& + .MuiSwitch-track': {
- backgroundColor: theme.palette.success.main,
+ backgroundColor: figVars['purple-1'],
opacity: 1,
},
},
'&.Mui-disabled': {
- opacity: theme.palette.mode === 'dark' ? 0.3 : 0.7,
+ opacity: 0.7,
+ ...darkScheme({ opacity: 0.3 }),
},
},
thumb: {
- color: theme.palette.common.white,
- borderRadius: '6px',
- width: '16px',
- height: '16px',
- boxShadow: '0px 1px 1px rgba(0, 0, 0, 0.12)',
+ color: onAccent,
+ borderRadius: '50%',
+ width: '14px',
+ height: '14px',
+ boxShadow: controlThumbShadow,
},
track: {
opacity: 1,
- backgroundColor: theme.palette.action.active,
- borderRadius: '8px',
+ backgroundColor: figVars['bg-7'],
+ borderRadius: '9px',
+ },
+ },
+ },
+ MuiFormControlLabel: {
+ styleOverrides: {
+ root: {
+ // A Switch has no internal padding, so MUI's default -11px label offset (meant for
+ // padded checkboxes/radios) crams the switch against whatever precedes it in a row.
+ // Zero it for switch-labeled controls, and give the switch↔label text a 0.5rem gap.
+ // Checkbox/radio labels keep MUI's defaults.
+ '&:has(.MuiSwitch-root)': {
+ marginLeft: 0,
+ '& .MuiFormControlLabel-label': {
+ marginLeft: '0.5rem',
+ },
+ },
},
},
},
@@ -669,7 +975,7 @@ export function getThemedComponents(theme: Theme) {
MuiTableCell: {
styleOverrides: {
root: {
- borderColor: theme.palette.divider,
+ borderColor: figVars['border-2'],
},
},
},
@@ -743,52 +1049,52 @@ export function getThemedComponents(theme: Theme) {
{
props: { severity: 'error' },
style: {
- color: theme.palette.error['100'],
- background: theme.palette.error['200'],
+ color: theme.vars.palette.error['100'],
+ background: theme.vars.palette.error['200'],
a: {
- color: theme.palette.error['100'],
+ color: theme.vars.palette.error['100'],
},
'.MuiButton-text': {
- color: theme.palette.error['100'],
+ color: theme.vars.palette.error['100'],
},
},
},
{
props: { severity: 'info' },
style: {
- color: theme.palette.info['100'],
- background: theme.palette.info['200'],
+ color: theme.vars.palette.info['100'],
+ background: theme.vars.palette.info['200'],
a: {
- color: theme.palette.info['100'],
+ color: theme.vars.palette.info['100'],
},
'.MuiButton-text': {
- color: theme.palette.info['100'],
+ color: theme.vars.palette.info['100'],
},
},
},
{
props: { severity: 'success' },
style: {
- color: theme.palette.success['100'],
- background: theme.palette.success['200'],
+ color: theme.vars.palette.success['100'],
+ background: theme.vars.palette.success['200'],
a: {
- color: theme.palette.success['100'],
+ color: theme.vars.palette.success['100'],
},
'.MuiButton-text': {
- color: theme.palette.success['100'],
+ color: theme.vars.palette.success['100'],
},
},
},
{
props: { severity: 'warning' },
style: {
- color: theme.palette.warning['100'],
- background: theme.palette.warning['200'],
+ color: theme.vars.palette.warning['100'],
+ background: theme.vars.palette.warning['200'],
a: {
- color: theme.palette.warning['100'],
+ color: theme.vars.palette.warning['100'],
},
'.MuiButton-text': {
- color: theme.palette.warning['100'],
+ color: theme.vars.palette.warning['100'],
},
},
},
@@ -801,48 +1107,124 @@ export function getThemedComponents(theme: Theme) {
fontWeight: 400,
fontSize: pxToRem(14),
minWidth: '375px',
+ backgroundColor: figVars['bg-2'],
'> div:first-of-type': {
minHeight: '100vh',
display: 'flex',
flexDirection: 'column',
},
},
+ // Respect the OS "reduce motion" preference app-wide (incl. the dev showcase,
+ // since CssBaseline is injected once at the app root).
+ '@media (prefers-reduced-motion: reduce)': {
+ '*, *::before, *::after': {
+ animationDuration: '0.01ms !important',
+ animationIterationCount: '1 !important',
+ transitionDuration: '0.01ms !important',
+ scrollBehavior: 'auto !important',
+ },
+ },
},
},
MuiSvgIcon: {
styleOverrides: {
colorPrimary: {
- color: theme.palette.primary.light,
+ color: theme.vars.palette.primary.light,
},
},
},
MuiSelect: {
defaultProps: {
IconComponent: (props) => (
-
-
-
+
),
},
styleOverrides: {
outlined: {
- backgroundColor: theme.palette.background.surface,
+ // The trigger's fill + ring live on the OutlinedInput root (see MuiOutlinedInput)
+ // so they're rounded and wrapped like the outlined button; here just the text.
...theme.typography.buttonM,
- padding: '6px 12px',
- color: theme.palette.primary.light,
+ color: figVars['fg-1'],
},
},
},
MuiLinearProgress: {
styleOverrides: {
bar1Indeterminate: {
- background: theme.palette.gradients.aaveGradient,
+ background: figVars['purple-1'],
},
bar2Indeterminate: {
- background: theme.palette.gradients.aaveGradient,
+ background: figVars['purple-1'],
},
},
},
},
} as ThemeOptions;
}
+
+/**
+ * Assemble the full app MUI theme (CSS-variables mode): both color schemes' design tokens
+ * plus the component overrides. Single source of truth shared by the app root
+ * (`AppGlobalStyles`) and the dev component showcase, so they can't drift apart. Color
+ * scheme is switched via the `data-mui-color-scheme` attribute, not by rebuilding the theme.
+ */
+export const createAppTheme = () => {
+ const light = getDesignTokens('light');
+ const dark = getDesignTokens('dark');
+ const shared = {
+ breakpoints: light.breakpoints,
+ spacing: light.spacing,
+ typography: light.typography,
+ colorSchemes: {
+ light: { palette: light.palette },
+ dark: { palette: dark.palette },
+ },
+ };
+ // Build a base theme first so `getThemedComponents` can read its `.vars` (CSS-var refs),
+ // then rebuild with those overrides attached. (A build-once `theme.components = …` mutation
+ // trips MUI's `Components` typing, so the two-pass is the type-clean form.)
+ const base = experimental_extendTheme(shared);
+ return experimental_extendTheme({
+ ...shared,
+ components: getThemedComponents(base).components,
+ });
+};
+
+// --- Display-P3 override layer -------------------------------------------------------------
+
+const isColorValue = (v: string) => v.startsWith('#') || v.startsWith('rgb');
+
+// Walk a color scheme's palette and, for every solid color leaf, emit a P3 override keyed to
+// the CSS variable MUI generates for it (`--mui-palette-`). Non-color
+// leaves (numbers, `mode`, channel strings like "32 29 29", gradients) are skipped.
+const collectP3Vars = (
+ node: Record,
+ path: string[],
+ out: Record
+) => {
+ Object.entries(node).forEach(([key, value]) => {
+ if (typeof value === 'string' && isColorValue(value)) {
+ out[`--mui-palette-${[...path, key].join('-')}`] = colorToP3(value);
+ } else if (value && typeof value === 'object') {
+ collectP3Vars(value as Record, [...path, key], out);
+ }
+ });
+};
+
+/**
+ * Build Display-P3 overrides for the generated `--mui-palette-*` CSS variables — one entry
+ * per solid color token, per color scheme. Injected under `@supports (color-gamut: p3)` so
+ * wide-gamut displays get the richer color while everything else keeps the sRGB base var.
+ * (Alpha-composited tints via MUI's `rgba( / a)` stay sRGB — see migration notes.)
+ */
+export const buildP3Overrides = (theme: AppTheme) => {
+ const forScheme = (scheme?: { palette?: unknown }) => {
+ const out: Record = {};
+ collectP3Vars((scheme?.palette ?? {}) as Record, [], out);
+ return out;
+ };
+ return {
+ light: forScheme(theme.colorSchemes.light),
+ dark: forScheme(theme.colorSchemes.dark),
+ };
+};