From ef3263182a8b0e7a492b86aa6647dc652bd7184f Mon Sep 17 00:00:00 2001 From: Mark Shenouda Date: Mon, 1 Dec 2025 18:28:22 +0200 Subject: [PATCH 1/8] Add Grid component and refactor NetworkGrid and TokenGrid to use it --- src/components/CCIP/Landing/Grid.css | 12 ++++++ src/components/CCIP/Landing/Grid.tsx | 26 +++++++++++++ src/components/CCIP/Landing/NetworkGrid.css | 2 +- src/components/CCIP/Landing/NetworkGrid.tsx | 38 +++++++++---------- .../CCIP/Landing/ccip-landing.astro | 2 - src/components/CCIP/SeeMore/SeeMore.css | 2 +- src/components/CCIP/SeeMore/SeeMore.tsx | 7 ++-- src/components/CCIP/TokenGrid/TokenGrid.tsx | 37 +++++++++--------- 8 files changed, 78 insertions(+), 48 deletions(-) create mode 100644 src/components/CCIP/Landing/Grid.css create mode 100644 src/components/CCIP/Landing/Grid.tsx diff --git a/src/components/CCIP/Landing/Grid.css b/src/components/CCIP/Landing/Grid.css new file mode 100644 index 00000000000..77c7b07c77b --- /dev/null +++ b/src/components/CCIP/Landing/Grid.css @@ -0,0 +1,12 @@ +.grid { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-2x); +} + +@media (min-width: 992px) { + .grid { + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: var(--space-6x); + } +} diff --git a/src/components/CCIP/Landing/Grid.tsx b/src/components/CCIP/Landing/Grid.tsx new file mode 100644 index 00000000000..4d0153b2413 --- /dev/null +++ b/src/components/CCIP/Landing/Grid.tsx @@ -0,0 +1,26 @@ +import { useState, type ReactNode } from "react" +import SeeMore from "../SeeMore/SeeMore.tsx" +import "./Grid.css" + +interface GridProps { + items: any[] + renderItem: (item: any, index: number) => ReactNode + initialDisplayCount: number + seeMoreLabel: string + className?: string +} + +function Grid({ items, renderItem, initialDisplayCount, seeMoreLabel, className = "grid" }: GridProps) { + const [seeMore, setSeeMore] = useState(items.length <= initialDisplayCount) + + return ( + <> +
+ {items.slice(0, seeMore ? items.length : initialDisplayCount).map((item, index) => renderItem(item, index))} +
+ {!seeMore && setSeeMore(!seeMore)} label={seeMoreLabel} />} + + ) +} + +export default Grid diff --git a/src/components/CCIP/Landing/NetworkGrid.css b/src/components/CCIP/Landing/NetworkGrid.css index acf154ba8dc..191be522985 100644 --- a/src/components/CCIP/Landing/NetworkGrid.css +++ b/src/components/CCIP/Landing/NetworkGrid.css @@ -6,7 +6,7 @@ @media (min-width: 992px) { .networks__grid { - grid-template-columns: 1fr 1fr; + grid-template-columns: 1fr 1fr 1fr 1fr; gap: var(--space-6x); } } diff --git a/src/components/CCIP/Landing/NetworkGrid.tsx b/src/components/CCIP/Landing/NetworkGrid.tsx index fc4ff7b26d9..b2683087879 100644 --- a/src/components/CCIP/Landing/NetworkGrid.tsx +++ b/src/components/CCIP/Landing/NetworkGrid.tsx @@ -1,7 +1,5 @@ -import { useState } from "react" import NetworkCard from "../Cards/NetworkCard.tsx" -import SeeMore from "../SeeMore/SeeMore.tsx" -import "./NetworkGrid.css" +import Grid from "./Grid.tsx" interface NetworkGridProps { networks: { @@ -14,27 +12,25 @@ interface NetworkGridProps { environment: string } -const BEFORE_SEE_MORE = 2 * 7 // Number of networks to show before the "See more" button, 2 rows x 7 items +const BEFORE_SEE_MORE = 2 * 4 // Number of networks to show before the "See more" button, 2 rows x 4 items function NetworkGrid({ networks, environment }: NetworkGridProps) { - const [seeMore, setSeeMore] = useState(networks.length <= BEFORE_SEE_MORE) return ( - <> -
- {networks.slice(0, seeMore ? networks.length : BEFORE_SEE_MORE).map((chain) => ( - - - - ))} -
- {!seeMore && setSeeMore(!seeMore)} />} - + ( + + + + )} + /> ) } diff --git a/src/components/CCIP/Landing/ccip-landing.astro b/src/components/CCIP/Landing/ccip-landing.astro index e1135ffcb4b..090fe604cde 100644 --- a/src/components/CCIP/Landing/ccip-landing.astro +++ b/src/components/CCIP/Landing/ccip-landing.astro @@ -125,10 +125,8 @@ const directoryStructuredData = generateDirectoryStructuredData(environment, net @media (min-width: 992px) { .layout { --doc-padding: var(--space-10x); - display: grid; padding-top: var(--doc-padding); padding-bottom: var(--doc-padding); - grid-template-columns: 1fr 1fr; gap: var(--space-24x); } } diff --git a/src/components/CCIP/SeeMore/SeeMore.css b/src/components/CCIP/SeeMore/SeeMore.css index 8c4c9209c51..102d96cf4b1 100644 --- a/src/components/CCIP/SeeMore/SeeMore.css +++ b/src/components/CCIP/SeeMore/SeeMore.css @@ -7,6 +7,6 @@ } .seeMore__container { display: flex; - justify-content: center; + justify-content: flex-start; align-items: center; } diff --git a/src/components/CCIP/SeeMore/SeeMore.tsx b/src/components/CCIP/SeeMore/SeeMore.tsx index 3e0b7c17278..e5ebfee607c 100644 --- a/src/components/CCIP/SeeMore/SeeMore.tsx +++ b/src/components/CCIP/SeeMore/SeeMore.tsx @@ -1,13 +1,14 @@ import "./SeeMore.css" interface SeeMoreProps { onClick?: () => void + label?: string } -function SeeMore({ onClick }: SeeMoreProps) { +function SeeMore({ onClick, label = "See more" }: SeeMoreProps) { return (
-
) diff --git a/src/components/CCIP/TokenGrid/TokenGrid.tsx b/src/components/CCIP/TokenGrid/TokenGrid.tsx index 9c303e264a5..0e51fb42d08 100644 --- a/src/components/CCIP/TokenGrid/TokenGrid.tsx +++ b/src/components/CCIP/TokenGrid/TokenGrid.tsx @@ -1,7 +1,5 @@ -import { useState } from "react" -import SeeMore from "../SeeMore/SeeMore.tsx" -import "./TokenGrid.css" import TokenCard from "../Cards/TokenCard.tsx" +import Grid from "../Landing/Grid.tsx" interface TokenGridProps { tokens: { @@ -11,25 +9,24 @@ interface TokenGridProps { environment: string } -const BEFORE_SEE_MORE = 6 * 4 // Number of networks to show before the "See more" button, 6 rows x 4 items +const BEFORE_SEE_MORE = 2 * 4 // Number of tokens to show before the "See more" button, 2 rows x 4 items -function NetworkGrid({ tokens, environment }: TokenGridProps) { - const [seeMore, setSeeMore] = useState(tokens.length <= BEFORE_SEE_MORE) +function TokenGrid({ tokens, environment }: TokenGridProps) { return ( - <> -
- {tokens.slice(0, seeMore ? tokens.length : BEFORE_SEE_MORE).map((token) => ( - - ))} -
- {!seeMore && setSeeMore(!seeMore)} />} - + ( + + )} + /> ) } -export default NetworkGrid +export default TokenGrid From 08786a8f25ac8cf9fa36d8701fd1242516c5266d Mon Sep 17 00:00:00 2001 From: Mark Shenouda Date: Mon, 1 Dec 2025 18:28:32 +0200 Subject: [PATCH 2/8] Refactor card components to use a unified Card component and update styles for consistency --- src/components/CCIP/Cards/Card.css | 40 +++++++++++++++++++ src/components/CCIP/Cards/Card.tsx | 43 +++++++++++++++++++++ src/components/CCIP/Cards/NetworkCard.tsx | 16 ++------ src/components/CCIP/Cards/TokenCard.css | 26 +++++++------ src/components/CCIP/Cards/TokenCard.tsx | 43 +++++---------------- src/components/CCIP/Landing/NetworkGrid.tsx | 22 ++++++----- src/components/CCIP/TokenGrid/TokenGrid.tsx | 30 +++++++++----- 7 files changed, 144 insertions(+), 76 deletions(-) create mode 100644 src/components/CCIP/Cards/Card.css create mode 100644 src/components/CCIP/Cards/Card.tsx diff --git a/src/components/CCIP/Cards/Card.css b/src/components/CCIP/Cards/Card.css new file mode 100644 index 00000000000..bb77dae5555 --- /dev/null +++ b/src/components/CCIP/Cards/Card.css @@ -0,0 +1,40 @@ +.card__container { + display: flex; + padding: var(--space-6x); + gap: var(--space-3x); + width: 100%; + background: var(--white); + border: 1px solid var(--gray-200); + border-radius: var(--space-1x); + /* Optimize rendering performance */ + contain: layout style paint; + will-change: background-color; +} + +.card__container:hover { + background-color: var(--gray-50); +} + +.card__container img, +.card__container object, +.card__container object img { + width: var(--space-10x); + height: var(--space-10x); + margin-top: auto; + margin-bottom: auto; +} + +.card__container h3 { + font-size: var(--space-4x); + font-weight: var(--font-weight-medium); + line-height: var(--space-6x); + color: var(--gray-950); + margin-bottom: var(--space-1x); +} + +.card__container p { + margin-bottom: 0; + font-size: var(--space-3x); + line-height: var(--space-5x); + color: var(--gray-500); +} diff --git a/src/components/CCIP/Cards/Card.tsx b/src/components/CCIP/Cards/Card.tsx new file mode 100644 index 00000000000..669bd9155ff --- /dev/null +++ b/src/components/CCIP/Cards/Card.tsx @@ -0,0 +1,43 @@ +import { memo, type ReactNode } from "react" +import "./Card.css" + +interface CardProps { + logo: ReactNode + title: string + subtitle?: string + link?: string + onClick?: () => void + ariaLabel?: string +} + +const Card = memo(function Card({ logo, title, subtitle, link, onClick, ariaLabel }: CardProps) { + const content = ( + <> + {logo} +
+

{title}

+ {subtitle &&

{subtitle}

} +
+ + ) + + if (link) { + return ( + +
{content}
+
+ ) + } + + if (onClick) { + return ( + + ) + } + + return
{content}
+}) + +export default Card diff --git a/src/components/CCIP/Cards/NetworkCard.tsx b/src/components/CCIP/Cards/NetworkCard.tsx index 839f4389d4d..5d5ee0f4e99 100644 --- a/src/components/CCIP/Cards/NetworkCard.tsx +++ b/src/components/CCIP/Cards/NetworkCard.tsx @@ -1,5 +1,5 @@ import { memo } from "react" -import "./NetworkCard.css" +import Card from "./Card.tsx" interface NetworkCardProps { name: string @@ -9,17 +9,9 @@ interface NetworkCardProps { } const NetworkCard = memo(function NetworkCard({ name, totalLanes, totalTokens, logo }: NetworkCardProps) { - return ( -
- -
-

{name}

-

- {totalLanes} {totalLanes > 1 ? "lanes" : "lane"} | {totalTokens} {totalTokens > 1 ? "tokens" : "token"} -

-
-
- ) + const subtitle = `${totalLanes} ${totalLanes === 1 ? "lane" : "lanes"} | ${totalTokens} ${totalTokens === 1 ? "token" : "tokens"}` + + return } title={name} subtitle={subtitle} /> }) export default NetworkCard diff --git a/src/components/CCIP/Cards/TokenCard.css b/src/components/CCIP/Cards/TokenCard.css index c2d092aa132..d8e2b7d6e2d 100644 --- a/src/components/CCIP/Cards/TokenCard.css +++ b/src/components/CCIP/Cards/TokenCard.css @@ -1,19 +1,11 @@ .token-card__container { display: flex; - width: 100%; - height: 110px; - min-width: 110px; - margin: 0 auto; - flex-direction: column; - align-items: center; - text-align: center; - padding: var(--space-4x); + padding: var(--space-6x); gap: var(--space-3x); - background: #ffffff; + width: 100%; + background: var(--white); border: 1px solid var(--gray-200); border-radius: var(--space-1x); - justify-content: center; - cursor: pointer; /* Optimize rendering performance */ contain: layout style paint; will-change: background-color; @@ -27,14 +19,24 @@ .token-card__container object img { width: var(--space-10x); height: var(--space-10x); + margin-top: auto; + margin-bottom: auto; border-radius: 50%; } .token-card__container h3 { font-size: var(--space-4x); - font-weight: 500; + font-weight: var(--font-weight-medium); + line-height: var(--space-6x); color: var(--gray-950); + margin-bottom: var(--space-1x); +} + +.token-card__container p { margin-bottom: 0; + font-size: var(--space-3x); + line-height: var(--space-5x); + color: var(--gray-500); } .truncate { diff --git a/src/components/CCIP/Cards/TokenCard.tsx b/src/components/CCIP/Cards/TokenCard.tsx index 653908ec260..461feae3206 100644 --- a/src/components/CCIP/Cards/TokenCard.tsx +++ b/src/components/CCIP/Cards/TokenCard.tsx @@ -1,5 +1,6 @@ import { memo } from "react" import { fallbackTokenIconUrl } from "~/features/utils/index.ts" +import Card from "./Card.tsx" import "./TokenCard.css" interface TokenCardProps { @@ -7,43 +8,19 @@ interface TokenCardProps { logo?: string link?: string onClick?: () => void + totalNetworks?: number } -const TokenCard = memo(function TokenCard({ id, logo, link, onClick }: TokenCardProps) { - if (link) { - return ( - -
- {/* We cannot use the normal Image/onError syntax as a fallback as the element is server rendered - and the onerror does not seem to work correctly. Using Picture will also not work. */} - - {`${id} - -

{id}

-
-
- ) - } +const TokenCard = memo(function TokenCard({ id, logo, link, onClick, totalNetworks }: TokenCardProps) { + const logoElement = ( + + {`${id} + + ) - if (onClick) { - return ( - - ) - } + const subtitle = totalNetworks !== undefined ? `${totalNetworks} ${totalNetworks === 1 ? "network" : "networks"}` : undefined - return ( -
- - - -

{id}

-
- ) + return }) export default TokenCard diff --git a/src/components/CCIP/Landing/NetworkGrid.tsx b/src/components/CCIP/Landing/NetworkGrid.tsx index b2683087879..13728a0260c 100644 --- a/src/components/CCIP/Landing/NetworkGrid.tsx +++ b/src/components/CCIP/Landing/NetworkGrid.tsx @@ -1,4 +1,4 @@ -import NetworkCard from "../Cards/NetworkCard.tsx" +import Card from "../Cards/Card.tsx" import Grid from "./Grid.tsx" interface NetworkGridProps { @@ -20,16 +20,18 @@ function NetworkGrid({ networks, environment }: NetworkGridProps) { items={networks} initialDisplayCount={BEFORE_SEE_MORE} seeMoreLabel="View all networks" - renderItem={(chain) => ( - - { + const subtitle = `${chain.totalLanes} ${chain.totalLanes === 1 ? "lane" : "lanes"} | ${chain.totalTokens} ${chain.totalTokens === 1 ? "token" : "tokens"}` + return ( + } + title={chain.name} + subtitle={subtitle} + link={`/ccip/directory/${environment}/chain/${chain.chain}`} /> - - )} + ) + }} /> ) } diff --git a/src/components/CCIP/TokenGrid/TokenGrid.tsx b/src/components/CCIP/TokenGrid/TokenGrid.tsx index 0e51fb42d08..5840bba5f9b 100644 --- a/src/components/CCIP/TokenGrid/TokenGrid.tsx +++ b/src/components/CCIP/TokenGrid/TokenGrid.tsx @@ -1,10 +1,12 @@ -import TokenCard from "../Cards/TokenCard.tsx" +import { fallbackTokenIconUrl } from "~/features/utils/index.ts" +import Card from "../Cards/Card.tsx" import Grid from "../Landing/Grid.tsx" interface TokenGridProps { tokens: { id: string logo: string + totalNetworks?: number }[] environment: string } @@ -17,14 +19,24 @@ function TokenGrid({ tokens, environment }: TokenGridProps) { items={tokens} initialDisplayCount={BEFORE_SEE_MORE} seeMoreLabel="View all tokens" - renderItem={(token) => ( - - )} + renderItem={(token) => { + const subtitle = token.totalNetworks !== undefined ? `${token.totalNetworks} ${token.totalNetworks === 1 ? "network" : "networks"}` : undefined + const logoElement = ( + + {`${token.id} + + ) + return ( + + ) + }} /> ) } From 79b83a68648302dd289ed59ab10b1ad0f3e7322d Mon Sep 17 00:00:00 2001 From: Mark Shenouda Date: Tue, 2 Dec 2025 21:01:32 +0200 Subject: [PATCH 3/8] Add LazyVerifierGrid component and integrate verifiers section in CCIP landing page --- .../CCIP/Landing/ccip-landing.astro | 12 +++++ .../CCIP/VerifierGrid/LazyVerifierGrid.tsx | 46 +++++++++++++++++++ .../CCIP/VerifierGrid/VerifierGrid.tsx | 42 +++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 src/components/CCIP/VerifierGrid/LazyVerifierGrid.tsx create mode 100644 src/components/CCIP/VerifierGrid/VerifierGrid.tsx diff --git a/src/components/CCIP/Landing/ccip-landing.astro b/src/components/CCIP/Landing/ccip-landing.astro index 090fe604cde..bc77b4df981 100644 --- a/src/components/CCIP/Landing/ccip-landing.astro +++ b/src/components/CCIP/Landing/ccip-landing.astro @@ -6,6 +6,7 @@ import { Environment, getAllNetworks, getAllSupportedTokens, + getAllUniqueVerifiers, getChainsOfToken, getSearchLanes, Version, @@ -13,6 +14,7 @@ import { import { getTokenIconUrl } from "~/features/utils" import LazyNetworkGrid from "./LazyNetworkGrid" import LazyTokenGrid from "../TokenGrid/LazyTokenGrid" +import LazyVerifierGrid from "../VerifierGrid/LazyVerifierGrid" import StructuredData from "~/components/StructuredData.astro" import { generateDirectoryStructuredData } from "~/utils/ccipStructuredData" import { DOCS_BASE_URL } from "~/utils/structuredData" @@ -43,6 +45,10 @@ const allTokens = tokens.map((token) => { totalNetworks: getChainsOfToken({ token, filter: environment }).length, } }) +const allVerifiers = getAllUniqueVerifiers({ + environment, + version: Version.V1_2_0, +}) const searchLanes = getSearchLanes({ environment }) // Generate directory-level structured data (DataCatalog/Dataset) @@ -80,6 +86,12 @@ const directoryStructuredData = generateDirectoryStructuredData(environment, net +
+
+

Verifiers ({allVerifiers.length})

+
+ +
diff --git a/src/components/CCIP/VerifierGrid/LazyVerifierGrid.tsx b/src/components/CCIP/VerifierGrid/LazyVerifierGrid.tsx new file mode 100644 index 00000000000..7dd31d36e19 --- /dev/null +++ b/src/components/CCIP/VerifierGrid/LazyVerifierGrid.tsx @@ -0,0 +1,46 @@ +import { lazy, Suspense } from "react" +import type { Environment } from "~/config/data/ccip/types.ts" + +const VerifierGrid = lazy(() => import("./VerifierGrid.tsx")) + +interface LazyVerifierGridProps { + verifiers: Array<{ + id: string + name: string + logo: string + totalNetworks: number + }> + environment: Environment +} + +export default function LazyVerifierGrid({ verifiers, environment }: LazyVerifierGridProps) { + return ( + + {Array.from({ length: 8 }, (_, i) => ( +
+ ))} +
+ } + > + +
+ ) +} diff --git a/src/components/CCIP/VerifierGrid/VerifierGrid.tsx b/src/components/CCIP/VerifierGrid/VerifierGrid.tsx new file mode 100644 index 00000000000..c90f5eeadf6 --- /dev/null +++ b/src/components/CCIP/VerifierGrid/VerifierGrid.tsx @@ -0,0 +1,42 @@ +import Card from "../Cards/Card.tsx" +import Grid from "../Landing/Grid.tsx" + +interface VerifierGridProps { + verifiers: { + id: string + name: string + logo: string + totalNetworks: number + }[] + environment: string +} + +const BEFORE_SEE_MORE = 2 * 4 // Number of verifiers to show before the "See more" button, 2 rows x 4 items + +function VerifierGrid({ verifiers, environment }: VerifierGridProps) { + return ( + { + const subtitle = `${verifier.totalNetworks} ${verifier.totalNetworks === 1 ? "network" : "networks"}` + const logoElement = ( + {`${verifier.name} + ) + return ( + + ) + }} + /> + ) +} + +export default VerifierGrid From 5cc9dd81749a3c3c46001ed9bfef8ddbac710603 Mon Sep 17 00:00:00 2001 From: Mark Shenouda Date: Tue, 2 Dec 2025 21:02:57 +0200 Subject: [PATCH 4/8] Enhance Grid and SeeMore components with href support and adjust grid gap for better layout --- src/components/CCIP/Landing/Grid.css | 2 +- src/components/CCIP/Landing/Grid.tsx | 5 +++-- src/components/CCIP/SeeMore/SeeMore.tsx | 15 +++++++++++---- src/components/CCIP/VerifierGrid/VerifierGrid.tsx | 6 +++++- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/CCIP/Landing/Grid.css b/src/components/CCIP/Landing/Grid.css index 77c7b07c77b..14c77deb4b0 100644 --- a/src/components/CCIP/Landing/Grid.css +++ b/src/components/CCIP/Landing/Grid.css @@ -7,6 +7,6 @@ @media (min-width: 992px) { .grid { grid-template-columns: 1fr 1fr 1fr 1fr; - gap: var(--space-6x); + gap: var(--space-4x); } } diff --git a/src/components/CCIP/Landing/Grid.tsx b/src/components/CCIP/Landing/Grid.tsx index 4d0153b2413..cdc929b6b64 100644 --- a/src/components/CCIP/Landing/Grid.tsx +++ b/src/components/CCIP/Landing/Grid.tsx @@ -8,9 +8,10 @@ interface GridProps { initialDisplayCount: number seeMoreLabel: string className?: string + seeMoreLink?: string } -function Grid({ items, renderItem, initialDisplayCount, seeMoreLabel, className = "grid" }: GridProps) { +function Grid({ items, renderItem, initialDisplayCount, seeMoreLabel, className = "grid", seeMoreLink }: GridProps) { const [seeMore, setSeeMore] = useState(items.length <= initialDisplayCount) return ( @@ -18,7 +19,7 @@ function Grid({ items, renderItem, initialDisplayCount, seeMoreLabel, className
{items.slice(0, seeMore ? items.length : initialDisplayCount).map((item, index) => renderItem(item, index))}
- {!seeMore && setSeeMore(!seeMore)} label={seeMoreLabel} />} + {!seeMore && setSeeMore(!seeMore)} label={seeMoreLabel} href={seeMoreLink} />} ) } diff --git a/src/components/CCIP/SeeMore/SeeMore.tsx b/src/components/CCIP/SeeMore/SeeMore.tsx index e5ebfee607c..cab963897c7 100644 --- a/src/components/CCIP/SeeMore/SeeMore.tsx +++ b/src/components/CCIP/SeeMore/SeeMore.tsx @@ -2,14 +2,21 @@ import "./SeeMore.css" interface SeeMoreProps { onClick?: () => void label?: string + href?: string } -function SeeMore({ onClick, label = "See more" }: SeeMoreProps) { +function SeeMore({ onClick, label = "See more", href }: SeeMoreProps) { return (
- + {href ? ( + + {label} + + ) : ( + + )}
) } diff --git a/src/components/CCIP/VerifierGrid/VerifierGrid.tsx b/src/components/CCIP/VerifierGrid/VerifierGrid.tsx index c90f5eeadf6..5191cb5925d 100644 --- a/src/components/CCIP/VerifierGrid/VerifierGrid.tsx +++ b/src/components/CCIP/VerifierGrid/VerifierGrid.tsx @@ -1,3 +1,4 @@ +import { fallbackVerifierIconUrl } from "~/features/utils/index.ts" import Card from "../Cards/Card.tsx" import Grid from "../Landing/Grid.tsx" @@ -19,10 +20,13 @@ function VerifierGrid({ verifiers, environment }: VerifierGridProps) { items={verifiers} initialDisplayCount={BEFORE_SEE_MORE} seeMoreLabel="View all verifiers" + seeMoreLink="/verifiers" renderItem={(verifier) => { const subtitle = `${verifier.totalNetworks} ${verifier.totalNetworks === 1 ? "network" : "networks"}` const logoElement = ( - {`${verifier.name} + + {`${verifier.name} + ) return ( Date: Tue, 2 Dec 2025 22:00:39 +0200 Subject: [PATCH 5/8] Add AddButton component and integrate it into Chain and CCIP landing pages; include add icon SVG and update theme styles for consistency --- public/assets/icons/add.svg | 3 ++ src/components/CCIP/AddButton/AddButton.astro | 42 +++++++++++++++++++ src/components/CCIP/Chain/Chain.astro | 10 +---- .../CCIP/Landing/ccip-landing.astro | 5 ++- src/styles/theme.css | 4 ++ 5 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 public/assets/icons/add.svg create mode 100644 src/components/CCIP/AddButton/AddButton.astro diff --git a/public/assets/icons/add.svg b/public/assets/icons/add.svg new file mode 100644 index 00000000000..6a3c331c298 --- /dev/null +++ b/public/assets/icons/add.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/CCIP/AddButton/AddButton.astro b/src/components/CCIP/AddButton/AddButton.astro new file mode 100644 index 00000000000..2d8d84c134c --- /dev/null +++ b/src/components/CCIP/AddButton/AddButton.astro @@ -0,0 +1,42 @@ +--- +export interface Props { + href: string + text: string +} + +const { href, text } = Astro.props +--- + + + Add + {text} + + + diff --git a/src/components/CCIP/Chain/Chain.astro b/src/components/CCIP/Chain/Chain.astro index fd726a209a2..6a424001c00 100644 --- a/src/components/CCIP/Chain/Chain.astro +++ b/src/components/CCIP/Chain/Chain.astro @@ -17,6 +17,7 @@ import ChainTokenGrid from "./ChainTokenGrid" import { generateChainStructuredData } from "~/utils/ccipStructuredData" import StructuredData from "~/components/StructuredData.astro" import { DOCS_BASE_URL } from "~/utils/structuredData" +import AddButton from "~/components/CCIP/AddButton/AddButton.astro" interface Props { environment: Environment @@ -128,14 +129,7 @@ const chainStructuredData = generateChainStructuredData(

Tokens ({allTokens.length})

{ network.chainType !== "solana" && network.chainType !== "aptos" && ( - - Add - Add my token - + ) } diff --git a/src/components/CCIP/Landing/ccip-landing.astro b/src/components/CCIP/Landing/ccip-landing.astro index bc77b4df981..4e101df4cbe 100644 --- a/src/components/CCIP/Landing/ccip-landing.astro +++ b/src/components/CCIP/Landing/ccip-landing.astro @@ -18,6 +18,7 @@ import LazyVerifierGrid from "../VerifierGrid/LazyVerifierGrid" import StructuredData from "~/components/StructuredData.astro" import { generateDirectoryStructuredData } from "~/utils/ccipStructuredData" import { DOCS_BASE_URL } from "~/utils/structuredData" +import AddButton from "~/components/CCIP/AddButton/AddButton.astro" export type Props = { environment: Environment @@ -82,7 +83,7 @@ const directoryStructuredData = generateDirectoryStructuredData(environment, net

Tokens ({allTokens.length})

- Add my token +
@@ -139,7 +140,7 @@ const directoryStructuredData = generateDirectoryStructuredData(environment, net --doc-padding: var(--space-10x); padding-top: var(--doc-padding); padding-bottom: var(--doc-padding); - gap: var(--space-24x); + gap: var(--space-10x); } } diff --git a/src/styles/theme.css b/src/styles/theme.css index 2f394ceb5d0..a922dafcf53 100644 --- a/src/styles/theme.css +++ b/src/styles/theme.css @@ -44,6 +44,10 @@ --color-gray-90: var(--color-base-gray), 90%; --color-gray-95: var(--color-base-gray), 95%; + /* Tertiary color aliases (matches design system) */ + --tertiary-border: #d1d6de; + --tertiary-foreground: #0e1119; + --color-blue: var(--color-base-blue), 61%; --color-blue-dark: var(--color-base-blue-dark), 39%; --color-green: var(--color-base-green), 42%; From 442cc99f0077a9cf9537f6583fcc740de6ed1dc2 Mon Sep 17 00:00:00 2001 From: Mark Shenouda Date: Tue, 2 Dec 2025 22:08:25 +0200 Subject: [PATCH 6/8] Refactor AddButton styles for consistency; use CSS variables for spacing and dimensions --- src/components/CCIP/AddButton/AddButton.astro | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/CCIP/AddButton/AddButton.astro b/src/components/CCIP/AddButton/AddButton.astro index 2d8d84c134c..f2ade415216 100644 --- a/src/components/CCIP/AddButton/AddButton.astro +++ b/src/components/CCIP/AddButton/AddButton.astro @@ -16,14 +16,14 @@ const { href, text } = Astro.props .add-button { display: inline-flex; align-items: center; - gap: 8px; + gap: var(--space-2x); padding: var(--space-2x) var(--space-4x); border: 1px solid var(--tertiary-border); - border-radius: 4px; + border-radius: var(--space-1x); font-family: "Inter", sans-serif; - font-size: 12px; + font-size: var(--space-3x); font-weight: 600; - line-height: 16px; + line-height: var(--space-4x); color: var(--tertiary-foreground); text-decoration: none; background-color: transparent; @@ -36,7 +36,7 @@ const { href, text } = Astro.props } .add-button-icon { - width: 12px; - height: 12px; + width: var(--space-3x); + height: var(--space-3x); } From 40e6a8c41ca9a857a738cff93c1e9ffd6b017fca Mon Sep 17 00:00:00 2001 From: Mark Shenouda Date: Tue, 2 Dec 2025 22:29:48 +0200 Subject: [PATCH 7/8] fix lint --- src/components/CCIP/Cards/TokenCard.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/CCIP/Cards/TokenCard.tsx b/src/components/CCIP/Cards/TokenCard.tsx index 461feae3206..7810da7f4f7 100644 --- a/src/components/CCIP/Cards/TokenCard.tsx +++ b/src/components/CCIP/Cards/TokenCard.tsx @@ -18,9 +18,19 @@ const TokenCard = memo(function TokenCard({ id, logo, link, onClick, totalNetwor ) - const subtitle = totalNetworks !== undefined ? `${totalNetworks} ${totalNetworks === 1 ? "network" : "networks"}` : undefined + const subtitle = + totalNetworks !== undefined ? `${totalNetworks} ${totalNetworks === 1 ? "network" : "networks"}` : undefined - return + return ( + + ) }) export default TokenCard From 99266b5953e99ae106151cf8836261c1f1921681 Mon Sep 17 00:00:00 2001 From: Mark Shenouda Date: Tue, 2 Dec 2025 22:48:46 +0200 Subject: [PATCH 8/8] fix lint --- src/components/CCIP/TokenGrid/TokenGrid.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/CCIP/TokenGrid/TokenGrid.tsx b/src/components/CCIP/TokenGrid/TokenGrid.tsx index 5840bba5f9b..7938ef0406a 100644 --- a/src/components/CCIP/TokenGrid/TokenGrid.tsx +++ b/src/components/CCIP/TokenGrid/TokenGrid.tsx @@ -20,7 +20,10 @@ function TokenGrid({ tokens, environment }: TokenGridProps) { initialDisplayCount={BEFORE_SEE_MORE} seeMoreLabel="View all tokens" renderItem={(token) => { - const subtitle = token.totalNetworks !== undefined ? `${token.totalNetworks} ${token.totalNetworks === 1 ? "network" : "networks"}` : undefined + const subtitle = + token.totalNetworks !== undefined + ? `${token.totalNetworks} ${token.totalNetworks === 1 ? "network" : "networks"}` + : undefined const logoElement = ( {`${token.id}