diff --git a/src/components/CCIP/Token/Token.astro b/src/components/CCIP/Token/Token.astro
index 9cde2a10ca3..daa30944c7e 100644
--- a/src/components/CCIP/Token/Token.astro
+++ b/src/components/CCIP/Token/Token.astro
@@ -1,6 +1,5 @@
---
import CcipLayout from "~/layouts/CcipLayout.astro"
-import { getEntry } from "astro:content"
import {
getAllNetworks,
getAllSupportedTokens,
@@ -14,12 +13,25 @@ import {
import ChainHero from "~/components/CCIP/ChainHero/ChainHero"
import Table from "~/components/CCIP/Tables/TokenChainsTable"
import { directoryToSupportedChain, getChainIcon, getExplorer, getTitle, getTokenIconUrl } from "~/features/utils"
-import Drawer from "~/components/CCIP/Drawer/Drawer"
const { token, logo, environment } = Astro.props as { token: string; logo: string; environment: Environment }
-const entry = await getEntry("ccip", "index")
-const { headings } = await entry.render()
+const frontmatter = {
+ title: `CCIP Supported Tokens - ${token}`,
+ section: "ccip" as const,
+ metadata: {
+ description: `View supported blockchains and configurations for ${token} token on Chainlink CCIP.`,
+ },
+}
+
+const headings = [
+ {
+ depth: 1,
+ slug: "overview",
+ text: frontmatter.title,
+ },
+]
+
const networks = getAllNetworks({ filter: environment })
const supportedTokens = getAllSupportedTokens({
@@ -56,14 +68,7 @@ const tokenLanes = getAllTokenLanes({
const searchLanes = getSearchLanes({ environment })
---
-
+
= {
+ ccip: ccipRoles,
+ // Add other products here when they're ready for the role-based layout
+}
diff --git a/src/config/roles/types.ts b/src/config/roles/types.ts
new file mode 100644
index 00000000000..8b318a6c302
--- /dev/null
+++ b/src/config/roles/types.ts
@@ -0,0 +1,20 @@
+export type LinkType = "overview" | "concept" | "get-started" | "guide" | "reference"
+
+export interface RoleLink {
+ type: LinkType
+ title: string
+ url: string
+}
+
+export interface RoleConfig {
+ id: string
+ title: string
+ description: string
+ iconType: string
+ links: RoleLink[]
+}
+
+export interface ProductRoles {
+ productId: string
+ roles: RoleConfig[]
+}
diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts
index 6b0a13ed1ac..0da19585e70 100644
--- a/src/config/sidebar.ts
+++ b/src/config/sidebar.ts
@@ -971,6 +971,10 @@ export const SIDEBAR: Partial> = {
title: "Overview",
url: "ccip",
},
+ {
+ title: "About CCIP",
+ url: "ccip/about",
+ },
{
title: "Getting Started",
url: "ccip/getting-started",
diff --git a/src/content/ccip/index.mdx b/src/content/ccip/about.mdx
similarity index 96%
rename from src/content/ccip/index.mdx
rename to src/content/ccip/about.mdx
index 65bcfb9be31..cee7d7bd56f 100644
--- a/src/content/ccip/index.mdx
+++ b/src/content/ccip/about.mdx
@@ -2,7 +2,8 @@
section: ccip
date: Last Modified
title: "Chainlink CCIP"
-isIndex: true
+metadata:
+ description: "Chainlink CCIP is a secure blockchain interoperability protocol enabling cross-chain token transfers and messaging. Built with defense-in-depth security, it powers Web3 applications across multiple networks with industry-leading oracle infrastructure."
whatsnext:
{
"Complete the Getting Started guide to learn the basics": "/ccip/getting-started",
diff --git a/src/features/landing/components/RoleCardGeneric.tsx b/src/features/landing/components/RoleCardGeneric.tsx
new file mode 100644
index 00000000000..14f55c4a8fe
--- /dev/null
+++ b/src/features/landing/components/RoleCardGeneric.tsx
@@ -0,0 +1,56 @@
+import React from "react"
+import type { RoleConfig } from "@config/roles/types"
+import { roleIconMap } from "@assets/role-icons"
+import styles from "./RoleCards.module.css"
+
+interface RoleCardGenericProps {
+ role: RoleConfig
+}
+
+export const RoleCardGeneric = ({ role }: RoleCardGenericProps) => {
+ const { title, description, iconType, links } = role
+
+ const groupedLinks = links.reduce((acc, link) => {
+ if (!acc[link.type]) {
+ acc[link.type] = []
+ }
+ acc[link.type].push(link)
+ return acc
+ }, {} as Record)
+
+ return (
+
+
+
+
+ {iconType && (
+

+ )}
+
+
+ {title}
+
+
+
+
{description}
+
+
+
+
+ )
+}
diff --git a/src/features/landing/components/RoleCards.module.css b/src/features/landing/components/RoleCards.module.css
new file mode 100644
index 00000000000..6be7e4876e4
--- /dev/null
+++ b/src/features/landing/components/RoleCards.module.css
@@ -0,0 +1,247 @@
+.rolesSection {
+ width: 100%;
+ max-width: var(--doc-content-width);
+ margin: 0 auto;
+ padding: var(--space-6x) var(--space-4x);
+}
+
+.rolesContainer {
+ margin: 0 auto;
+ width: 100%;
+}
+
+.cardGrid {
+ display: grid;
+ gap: var(--space-6x);
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
+ max-width: 1200px;
+ margin: 0 auto;
+ align-items: start;
+}
+
+.card {
+ position: relative;
+ border-radius: 12px;
+ background: var(--color-background);
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ height: 480px;
+ border: 1px solid var(--Card-Border, #e4e8ed);
+ background-color: var(--color-background, #ffffff);
+ display: flex;
+}
+
+.card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 12px rgba(26, 43, 107, 0.1);
+ background: var(--Blue-Blue-50, #eff6ff);
+}
+
+.cardInner {
+ padding: var(--space-6x);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3x);
+ flex: 1;
+ width: 100%;
+ display: grid;
+ grid-template-rows: auto auto 1fr;
+}
+
+.cardHeader {
+ display: flex;
+ align-items: center;
+ gap: var(--space-4x);
+}
+
+.iconWrapper {
+ flex-shrink: 0;
+ padding: var(--space-2x);
+ border-radius: 8px;
+ background: var(--color-background-secondary);
+ transition: transform 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 48px;
+ height: 48px;
+}
+
+.card:hover .iconWrapper {
+ transform: scale(1.05);
+}
+
+.cardIcon {
+ width: var(--space-8x);
+ height: var(--space-8x);
+ display: flex;
+}
+
+.cardTitle {
+ font-size: var(--font-size-xl);
+ font-weight: 600;
+ color: var(--color-text-heading);
+ margin: 0;
+ line-height: 1.2;
+ display: flex;
+ align-items: center;
+}
+
+.cardDescription {
+ color: var(--color-text-secondary);
+ font-size: var(--font-size-base);
+ line-height: 1.6;
+ margin: 0;
+ height: 4.8em;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ line-clamp: 3;
+ text-overflow: ellipsis;
+ opacity: 0.9;
+}
+
+.linkGroups {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-6x);
+ margin-top: var(--space-6x);
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+ /* Customize scrollbar for modern browsers */
+ scrollbar-width: thin;
+ scrollbar-color: var(--gray-200) transparent;
+
+ /* Webkit scrollbar styling */
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: transparent;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background-color: var(--gray-200);
+ border-radius: 3px;
+ }
+
+ &::-webkit-scrollbar-thumb:hover {
+ background-color: var(--gray-300);
+ }
+ @media (prefers-reduced-motion: no-preference) {
+ scroll-behavior: smooth;
+ }
+}
+
+.linkGroup {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2x);
+}
+
+.linkGroupTitle {
+ font-size: var(--font-size-sm);
+ font-weight: 600;
+ color: var(--color-text-secondary);
+ text-transform: capitalize;
+ margin: 0;
+ letter-spacing: 0.5px;
+}
+
+.linkList {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2x);
+}
+
+.link {
+ color: var(--blue-500);
+ text-decoration: none;
+ font-size: var(--font-size-base);
+ line-height: 1.4;
+ transition: all 0.2s ease;
+ display: inline-flex;
+ align-items: center;
+ padding: var(--space-1x) 0;
+ position: relative;
+ font-weight: 500;
+}
+
+.linkText {
+ position: relative;
+}
+
+.linkText::after {
+ content: "";
+ position: absolute;
+ bottom: -2px;
+ left: 0;
+ width: 100%;
+ height: 1px;
+ background: currentColor;
+ transform: scaleX(0);
+ transform-origin: right;
+ transition: transform 0.3s ease;
+}
+
+.link:hover .linkText::after,
+.link:focus .linkText::after {
+ transform: scaleX(1);
+ transform-origin: left;
+}
+
+.link:hover {
+ color: var(--blue-600);
+ text-decoration: underline;
+}
+
+/* Responsive adjustments */
+@media (max-width: 1200px) {
+ .cardGrid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+@media (max-width: 768px) {
+ .rolesSection {
+ padding: var(--space-4x) var(--space-2x);
+ }
+
+ .cardGrid {
+ grid-template-columns: 1fr;
+ gap: var(--space-4x);
+ }
+
+ .cardInner {
+ padding: var(--space-4x);
+ }
+
+ .cardTitle {
+ font-size: var(--font-size-lg);
+ }
+}
+
+/* High contrast mode */
+@media (forced-colors: active) {
+ .card {
+ border: 2px solid CanvasText;
+ }
+
+ .link:focus {
+ outline: 2px solid CanvasText;
+ }
+}
+
+/* Reduced motion */
+@media (prefers-reduced-motion: reduce) {
+ .card,
+ .iconWrapper,
+ .link,
+ .linkText::after {
+ transition: none;
+ }
+}
diff --git a/src/features/landing/components/RoleCardsGeneric.module.css b/src/features/landing/components/RoleCardsGeneric.module.css
new file mode 100644
index 00000000000..75dd5fd0dca
--- /dev/null
+++ b/src/features/landing/components/RoleCardsGeneric.module.css
@@ -0,0 +1,21 @@
+.cardGrid {
+ display: grid;
+ gap: var(--space-6x);
+ width: 100%;
+}
+
+/* Single column by default */
+@media (min-width: 640px) {
+ .cardGrid {
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: var(--space-4x);
+ }
+}
+
+/* Two columns when there's enough space */
+@media (min-width: 1024px) {
+ .cardGrid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: var(--space-6x);
+ }
+}
diff --git a/src/features/landing/components/RoleCardsGeneric.tsx b/src/features/landing/components/RoleCardsGeneric.tsx
new file mode 100644
index 00000000000..c93690c144f
--- /dev/null
+++ b/src/features/landing/components/RoleCardsGeneric.tsx
@@ -0,0 +1,21 @@
+import type { RoleConfig } from "@config/roles/types"
+import { RoleCardGeneric } from "./RoleCardGeneric"
+import styles from "./RoleCards.module.css"
+
+interface RoleCardsGenericProps {
+ roles: RoleConfig[]
+}
+
+export const RoleCardsGeneric = ({ roles }: RoleCardsGenericProps) => {
+ return (
+
+
+
+ {roles.map((role) => (
+
+ ))}
+
+
+
+ )
+}
diff --git a/src/layouts/DocsLayout.astro b/src/layouts/DocsLayout.astro
index 3d119222496..94b7e2896f5 100644
--- a/src/layouts/DocsLayout.astro
+++ b/src/layouts/DocsLayout.astro
@@ -15,8 +15,10 @@ import { detectApiReference } from "@components/VersionSelector/utils/versions"
interface Props {
frontmatter: BaseFrontmatter
headings?: MarkdownHeading[]
+ hideRightSidebar?: boolean
+ hideWhatsNext?: boolean
}
-const { frontmatter, headings } = Astro.props
+const { frontmatter, headings, hideRightSidebar, hideWhatsNext } = Astro.props
const titleHeading: MarkdownHeading = {
text: frontmatter.title,
@@ -62,11 +64,15 @@ const { isApiReference, product, isVersioned } = detectApiReference(currentPage)
- {whatsNext && }
+ {whatsNext && !hideWhatsNext && }
-