diff --git a/public/assets/icons/upper-right-arrow.svg b/public/assets/icons/upper-right-arrow.svg
new file mode 100644
index 00000000000..7f588a0dcbd
--- /dev/null
+++ b/public/assets/icons/upper-right-arrow.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/components/TabGrid/GridCard.module.css b/src/components/TabGrid/GridCard.module.css
new file mode 100644
index 00000000000..4c4e1b2626e
--- /dev/null
+++ b/src/components/TabGrid/GridCard.module.css
@@ -0,0 +1,83 @@
+.card {
+ display: flex;
+ background: var(--color-background);
+ padding: var(--space-6x);
+ align-items: start;
+ gap: var(--space-6x);
+ border-right: 1px solid var(--border);
+ border-bottom: 1px solid var(--border);
+
+ &:hover .cardFooter {
+ opacity: 1;
+ }
+}
+
+[data-columns="1"] > .card:nth-child(1) {
+ border-top: 1px solid var(--border);
+}
+
+[data-columns="2"] > .card:nth-child(-n + 2) {
+ border-top: 1px solid var(--border);
+}
+
+[data-columns="3"] > .card:nth-child(-n + 3) {
+ border-top: 1px solid var(--border);
+}
+
+[data-columns="4"] > .card:nth-child(-n + 4) {
+ border-top: 1px solid var(--border);
+}
+
+/* Tablet: adjust border-top for 2-column layouts */
+@media (max-width: 1024px) {
+ [data-columns="3"] > .card:nth-child(-n + 3),
+ [data-columns="4"] > .card:nth-child(-n + 4) {
+ border-top: none;
+ }
+
+ [data-columns="3"] > .card:nth-child(-n + 2),
+ [data-columns="4"] > .card:nth-child(-n + 2) {
+ border-top: 1px solid var(--border);
+ }
+}
+
+/* Mobile: single column - only first card has border-top */
+@media (max-width: 768px) {
+ [data-columns] > .card:nth-child(n) {
+ border-top: none;
+ }
+
+ [data-columns] > .card:nth-child(1) {
+ border-top: 1px solid var(--border);
+ }
+}
+
+.card:hover {
+ background-color: var(--muted);
+}
+
+.cardFooter {
+ opacity: 0;
+ margin-top: auto;
+ /* enforcing a width */
+ min-width: 16px;
+}
+
+.cardFooter img {
+ width: 10px;
+ height: 10px;
+}
+
+.cardTitle {
+ font-size: 16px;
+ font-weight: 525;
+ color: var(--foreground);
+ margin-bottom: var(--space-2x);
+}
+
+.cardDescription {
+ color: var(--color-text-secondary);
+ font-size: 0.9375rem;
+ line-height: 1.6;
+ margin: 0;
+}
diff --git a/src/components/TabGrid/GridCard.tsx b/src/components/TabGrid/GridCard.tsx
new file mode 100644
index 00000000000..e83883d8426
--- /dev/null
+++ b/src/components/TabGrid/GridCard.tsx
@@ -0,0 +1,25 @@
+import { Typography } from "@chainlink/blocks"
+import styles from "./GridCard.module.css"
+
+export interface GridItem {
+ title: string
+ description: string
+ link: string
+}
+
+export const GridCard = ({ title, description, link }: GridItem) => {
+ return (
+
+
+
{title}
+
+ {description}
+
+
+
+
+

+
+
+ )
+}
diff --git a/src/components/TabGrid/ItemGrid.tsx b/src/components/TabGrid/ItemGrid.tsx
new file mode 100644
index 00000000000..0a2ed5e92d3
--- /dev/null
+++ b/src/components/TabGrid/ItemGrid.tsx
@@ -0,0 +1,17 @@
+import { GridCard, GridItem } from "./GridCard.tsx"
+import styles from "./TabGrid.module.css"
+
+interface ItemGridProps {
+ links: GridItem[]
+ columns?: 1 | 2 | 3 | 4
+}
+
+export const ItemGrid = ({ links, columns = 3 }: ItemGridProps) => {
+ return (
+
+ {links.map((link, index) => (
+
+ ))}
+
+ )
+}
diff --git a/src/components/TabGrid/README.md b/src/components/TabGrid/README.md
new file mode 100644
index 00000000000..0edc1934c0c
--- /dev/null
+++ b/src/components/TabGrid/README.md
@@ -0,0 +1,89 @@
+# TabGrid Component
+
+A tabbed interface for displaying grid items organized by category.
+
+## What is this?
+
+The TabGrid component displays a collection of items in a clean, organized layout with tabs. Each tab represents a category of items (like "EVM" or "Solana"), and clicking on a tab shows the relevant items as clickable cards.
+
+This component is useful when you have multiple items and want to group them by topic or category, making it easier for users to find what they need.
+
+## Usage
+
+```tsx
+import { TabGrid } from "@components/TabGrid/TabGrid"
+;
+```
+
+## How to set it up
+
+The component requires a `tabs` prop, which is an array of tab objects. Each tab object contains:
+
+- A **name** (the label shown on the tab button)
+- A list of **links** (the items shown when that tab is active)
+
+Each grid item needs three pieces of information:
+
+- **title** - The name of the item
+- **description** - A short sentence explaining what the item covers
+- **link** - The URL where the item can be found
+
+## Props Reference
+
+### `TabGrid`
+
+| Prop | Type | Required | Description |
+| --------- | -------- | -------- | ------------------------------------------------- |
+| `header` | `string` | Yes | The heading text displayed above the tabs |
+| `tabs` | `Tab[]` | Yes | List of tabs, each containing a category of items |
+| `columns` | `number` | No | Number of columns in the grid (defaults to 2) |
+
+### `Tab`
+
+| Property | Type | Required | Description |
+| -------- | ------------ | -------- | -------------------------------------------------------- |
+| `name` | `string` | Yes | The label displayed on the tab (e.g., "Getting Started") |
+| `links` | `GridItem[]` | Yes | The list of items to show when this tab is selected |
+
+### `GridItem`
+
+| Property | Type | Required | Description |
+| ------------- | -------- | -------- | -------------------------------------------- |
+| `title` | `string` | Yes | The item's heading |
+| `description` | `string` | Yes | A brief explanation of what users will learn |
+| `link` | `string` | Yes | The URL path to the item page |
+
+## Components
+
+- **TabGrid** - Main container with tabs and header
+- **ItemGrid** - Grid layout for item cards
+- **GridCard** - Individual item card with hover effects
diff --git a/src/components/TabGrid/TabGrid.module.css b/src/components/TabGrid/TabGrid.module.css
new file mode 100644
index 00000000000..d20535265a2
--- /dev/null
+++ b/src/components/TabGrid/TabGrid.module.css
@@ -0,0 +1,60 @@
+.grid {
+ display: grid;
+ border-left: 1px solid var(--border);
+}
+
+.gridHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: var(--space-8x);
+}
+
+.tabsTrigger {
+ height: 32px;
+ padding: var(--space-1x) var(--space-2x);
+ justify-content: center;
+ align-items: center;
+ border-radius: var(--space-2x);
+ background-color: var(--pill);
+ border: 1px solid var(--pill-border);
+}
+
+.tabsTrigger:hover {
+ background-color: var(--pill-hover);
+}
+
+.tabsTrigger[data-state="active"] {
+ background-color: var(--pill-active);
+ border-color: var(--pill-active);
+
+ & h3 {
+ color: var(--pill-active-foreground);
+ }
+}
+
+.tabTitle {
+ color: var(--pill-foreground);
+ font-weight: 400;
+}
+
+.tabsList {
+ display: flex;
+ gap: var(--space-2x);
+ border-bottom: 0;
+}
+
+/* Tablet: reduce columns to 2 for 3+ column layouts */
+@media (max-width: 1024px) {
+ [data-columns="3"],
+ [data-columns="4"] {
+ grid-template-columns: repeat(2, 1fr) !important;
+ }
+}
+
+/* Mobile: single column for all layouts */
+@media (max-width: 768px) {
+ .grid {
+ grid-template-columns: 1fr !important;
+ }
+}
diff --git a/src/components/TabGrid/TabGrid.tsx b/src/components/TabGrid/TabGrid.tsx
new file mode 100644
index 00000000000..6e85ec3cfa6
--- /dev/null
+++ b/src/components/TabGrid/TabGrid.tsx
@@ -0,0 +1,40 @@
+import styles from "./TabGrid.module.css"
+import { GridItem } from "./GridCard.tsx"
+import { ItemGrid } from "./ItemGrid.tsx"
+import { Tabs, TabsContent, TabsList, TabsTrigger, Typography } from "@chainlink/blocks"
+
+export interface Tab {
+ name: string
+ links: GridItem[]
+}
+
+interface TabGridProps {
+ tabs: Tab[]
+ header: string
+ columns?: 1 | 2 | 3 | 4
+}
+
+export const TabGrid = ({ tabs, header, columns = 3 }: TabGridProps) => {
+ return (
+
+
+ {header}
+
+ {tabs.map((tab) => (
+
+ {tab.name}
+
+ ))}
+
+
+
+ {tabs.map((tab) => (
+
+
+
+
+
+ ))}
+
+ )
+}
diff --git a/src/layouts/DocsV3Layout/DocsV3Layout.astro b/src/layouts/DocsV3Layout/DocsV3Layout.astro
index 2f3e272954a..8169548be7a 100644
--- a/src/layouts/DocsV3Layout/DocsV3Layout.astro
+++ b/src/layouts/DocsV3Layout/DocsV3Layout.astro
@@ -6,6 +6,7 @@ import { BaseFrontmatter } from "~/content.config"
import * as CONFIG from "~/config"
import LeftSidebar from "~/components/LeftSidebar/LeftSidebar.astro"
import PageContent from "~/components/PageContent/PageContent.astro"
+import { TabGrid } from "~/components/TabGrid/TabGrid"
interface Props {
frontmatter: BaseFrontmatter
@@ -27,6 +28,100 @@ const formattedContentTitle = `${frontmatter.title} | ${CONFIG.SITE.title}`
const currentPage = new URL(Astro.request.url).pathname
const includeLinkToWalletScript = !!Astro.props.frontmatter.metadata?.linkToWallet
+
+// Example tutorial data
+const exampleTutorials = [
+ {
+ name: "EVM",
+ links: [
+ {
+ title: "Acquire Test Tokens",
+ description: "Get test tokens in minutes; build and test cross-chain apps with zero friction.",
+ link: "/tutorials/acquire-test-tokens",
+ },
+ {
+ title: "Transfer Tokens",
+ description: "Unlock seamless token transfers from contracts; learn, code, and deploy.",
+ link: "/tutorials/transfer-tokens",
+ },
+ {
+ title: "Transfer Tokens with Data",
+ description: "Go beyond basic transfers with logic-infused token movements in your EVM contracts.",
+ link: "/tutorials/transfer-tokens-data",
+ },
+ {
+ title: "Using the Token Manager",
+ description: "Effortlessly manage CCTs by tracking, importing and organizing tokens from your dashboard.",
+ link: "/tutorials/token-manager",
+ },
+ {
+ title: "Using the JS SDK",
+ description: "Integrate CCIP in your frontend or backend effortlessly with JavaScript SDK.",
+ link: "/tutorials/js-sdk",
+ },
+ {
+ title: "Check Message Status",
+ description: "Retrieve real-time status of your offchain transaction from EVM.",
+ link: "/tutorials/check-message-status",
+ },
+ {
+ title: "Transfer Tokens Between EOAs",
+ description: "Send tokens offchain from an Externally Owned Account with clear steps.",
+ link: "/tutorials/transfer-tokens-eoa",
+ },
+ {
+ title: "Using the CLI",
+ description: "Use offchain tools from CCIP to simplify your Ethereum workflows.",
+ link: "/tutorials/cli",
+ },
+ {
+ title: "Deploy and Register a CCT",
+ description: "Use RemixIDE to launch and configure tokens for cross-chain transfers on CCIP.",
+ link: "/tutorials/deploy-register-cct",
+ },
+ {
+ title: "Register CCT Burn & Mint EOA",
+ description: "Implement burn-mint cross-chain token logic with CCIP using Hardhat or Foundry.",
+ link: "/tutorials/register-cct-burn-mint",
+ },
+ {
+ title: "Register CCT Lock & Mint EOA",
+ description: "Implement a lock-mint token registration workflow with CCIP and Hardhat or Foundry.",
+ link: "/tutorials/register-cct-lock-mint",
+ },
+ {
+ title: "Set Token Pool Rate Limits",
+ description: "Update rate limiter settings for your cross-chain tokens using Hardhat or Foundry.",
+ link: "/tutorials/token-pool-rate-limits",
+ },
+ ],
+ },
+ {
+ name: "Solana",
+ links: [
+ {
+ title: "Getting Started with Solana",
+ description: "Learn the basics of building on Solana blockchain.",
+ link: "/tutorials/solana-getting-started",
+ },
+ {
+ title: "Solana Token Transfers",
+ description: "Transfer tokens on the Solana blockchain.",
+ link: "/tutorials/solana-transfers",
+ },
+ ],
+ },
+ {
+ name: "Aptos",
+ links: [
+ {
+ title: "Getting Started with Aptos",
+ description: "Start building on the Aptos blockchain.",
+ link: "/tutorials/aptos-getting-started",
+ },
+ ],
+ },
+]
---
@@ -40,6 +135,7 @@ const includeLinkToWalletScript = !!Astro.props.frontmatter.metadata?.linkToWall
diff --git a/src/pages/ccip/index.astro b/src/pages/ccip/index.astro
index 9eacf7c22f4..011b7d67a8d 100644
--- a/src/pages/ccip/index.astro
+++ b/src/pages/ccip/index.astro
@@ -11,4 +11,4 @@ if (!entry) {
const { Content, headings } = await render(entry)
---
-
+