feat: add scrolling integrations banner to all doc pages (#616)

- Add IntegrationsBanner component with infinite left-to-right CSS scroll animation showing all clients, integrations, and LLM providers
- Place banner below the navbar on every page via Navbar theme wrapper
- Add Agno and Hermes to both the IntegrationsGrid and the banner
- Remove right border from doc sidebar via custom.css
This commit is contained in:
Nicolò Boschi 2026-03-19 12:32:23 +01:00 committed by GitHub
parent a56cd044e5
commit fe12be47a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 176 additions and 3 deletions

View file

@ -0,0 +1,82 @@
.banner {
width: 100%;
overflow: hidden;
border-bottom: 1px solid var(--ifm-color-emphasis-200);
background: var(--ifm-background-surface-color);
padding: 7px 0;
/* Color cascades down to all items; dark mode is handled by Docusaurus on the html element */
color: #555;
mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%);
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%);
}
[data-theme='dark'] .banner {
color: #e0e0e0;
border-color: var(--ifm-color-emphasis-300);
}
.track {
display: flex;
align-items: center;
white-space: nowrap;
width: max-content;
animation: scrollLeft 60s linear infinite;
}
@keyframes scrollLeft {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
.itemLink {
text-decoration: none;
color: inherit;
}
.itemLink:hover .item {
color: #0074d9;
border-color: #0074d9;
background: rgba(0, 116, 217, 0.08);
}
[data-theme='dark'] .itemLink:hover .item {
color: #60aaff;
background: rgba(96, 170, 255, 0.12);
border-color: rgba(96, 170, 255, 0.5);
}
.item {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 14px;
margin: 0 6px;
border-radius: 999px;
border: 1px solid var(--ifm-color-emphasis-300);
background: var(--ifm-background-color);
font-size: 0.78rem;
font-weight: 500;
color: inherit;
transition: color 0.15s, border-color 0.15s, background 0.15s;
user-select: none;
}
[data-theme='dark'] .item {
border-color: var(--ifm-color-emphasis-400);
background: var(--ifm-background-surface-color);
}
.itemIcon {
display: inline-flex;
align-items: center;
flex-shrink: 0;
opacity: 0.85;
}
.itemIcon img {
display: block;
}
.itemLabel {
line-height: 1;
}

View file

@ -0,0 +1,80 @@
import React from 'react';
import type {IconType} from 'react-icons';
import {SiPython, SiGo, SiOpenai, SiAnthropic, SiGooglegemini, SiOllama, SiVercel} from 'react-icons/si';
import {LuTerminal, LuZap, LuBrainCog, LuSparkles, LuGlobe} from 'react-icons/lu';
import styles from './IntegrationsBanner.module.css';
interface BannerItem {
label: string;
icon?: IconType;
imgSrc?: string;
href?: string;
}
const OpenAICompatibleIcon: IconType = ({size = 18, ...props}) => (
<span style={{position: 'relative', display: 'inline-flex'}}>
<SiOpenai size={size} {...props} />
<span style={{
position: 'absolute', bottom: -2, right: -5,
fontSize: Math.round((size as number) * 0.5), fontWeight: 900, lineHeight: 1,
color: 'currentColor',
}}>+</span>
</span>
);
const ITEMS: BannerItem[] = [
// Clients
{label: 'Python', icon: SiPython, href: '/sdks/python'},
{label: 'TypeScript', imgSrc: '/img/icons/typescript.png', href: '/sdks/nodejs'},
{label: 'Go', icon: SiGo, href: '/sdks/go'},
{label: 'CLI', icon: LuTerminal, href: '/sdks/cli'},
{label: 'HTTP', icon: LuGlobe, href: '/developer/api/quickstart'},
// Integrations
{label: 'MCP Server', imgSrc: '/img/icons/mcp.png', href: '/sdks/integrations/local-mcp'},
{label: 'LiteLLM', imgSrc: '/img/icons/litellm.png', href: '/sdks/integrations/litellm'},
{label: 'OpenClaw', imgSrc: '/img/icons/openclaw.png', href: '/sdks/integrations/openclaw'},
{label: 'Vercel AI', icon: SiVercel, href: '/sdks/integrations/ai-sdk'},
{label: 'Vercel Chat', icon: SiVercel, href: '/sdks/integrations/chat'},
{label: 'CrewAI', imgSrc: '/img/icons/crewai.png', href: '/sdks/integrations/crewai'},
{label: 'Pydantic AI', imgSrc: '/img/icons/pydanticai.png', href: '/sdks/integrations/pydantic-ai'},
{label: 'Skills', imgSrc: '/img/icons/skills.png', href: '/sdks/integrations/skills'},
{label: 'Agno', imgSrc: '/img/icons/agno.png', href: '/sdks/integrations/agno'},
{label: 'Hermes', imgSrc: '/img/icons/hermes.png', href: '/sdks/integrations/hermes'},
// LLM Providers
{label: 'OpenAI', icon: SiOpenai},
{label: 'Anthropic', icon: SiAnthropic},
{label: 'Gemini', icon: SiGooglegemini},
{label: 'Groq', icon: LuZap},
{label: 'Ollama', icon: SiOllama},
{label: 'LM Studio', icon: LuBrainCog},
{label: 'MiniMax', icon: LuSparkles},
{label: 'OpenAI Compat', icon: OpenAICompatibleIcon},
];
function BannerItemComponent({item}: {item: BannerItem}) {
const content = (
<span className={styles.item}>
<span className={styles.itemIcon}>
{item.icon && <item.icon size={18} />}
{item.imgSrc && <img src={item.imgSrc} alt={item.label} width={18} height={18} style={{objectFit: 'contain'}} />}
</span>
<span className={styles.itemLabel}>{item.label}</span>
</span>
);
return item.href
? <a href={item.href} className={styles.itemLink}>{content}</a>
: <span>{content}</span>;
}
export default function IntegrationsBanner(): JSX.Element {
const doubled = [...ITEMS, ...ITEMS];
return (
<div className={styles.banner}>
<div className={styles.track}>
{doubled.map((item, i) => (
<BannerItemComponent key={`${item.label}-${i}`} item={item} />
))}
</div>
</div>
);
}

View file

@ -38,6 +38,8 @@ export function IntegrationsGrid() {
{ label: 'CrewAI', imgSrc: '/img/icons/crewai.png', href: '/sdks/integrations/crewai' },
{ label: 'Pydantic AI', imgSrc: '/img/icons/pydanticai.png', href: '/sdks/integrations/pydantic-ai' },
{ label: 'Skills', imgSrc: '/img/icons/skills.png', href: '/sdks/integrations/skills' },
{ label: 'Agno', imgSrc: '/img/icons/agno.png', href: '/sdks/integrations/agno' },
{ label: 'Hermes', imgSrc: '/img/icons/hermes.png', href: '/sdks/integrations/hermes' },
]} />
);
}

View file

@ -1267,3 +1267,8 @@ html.docs-wrapper article h3 {
}
}
/* Remove right border from doc sidebar */
.theme-doc-sidebar-container {
border-right: none !important;
}

View file

@ -1,11 +1,15 @@
import React, {type ReactNode} from 'react';
import NavbarLayout from '@theme/Navbar/Layout';
import NavbarContent from '@theme/Navbar/Content';
import IntegrationsBanner from '@site/src/components/IntegrationsBanner';
export default function Navbar(): ReactNode {
return (
<NavbarLayout>
<NavbarContent />
</NavbarLayout>
<>
<NavbarLayout>
<NavbarContent />
</NavbarLayout>
<IntegrationsBanner />
</>
);
}