feat(web): add Slack integration marketing page at /slack

- Create new /slack marketing page with SEO metadata
- Add SlackHeroSection with @Roomote branding and dual CTAs
- Add SlackValuePropsSection with 7 key value propositions
- Add ThreadToFeatureWorkflow demonstrating the flagship workflow
- Add SlackOnboardingSteps with 3-step setup guide
- Add SlackCTASection for conversion
- Update navigation to include Slack in Product dropdown (desktop + mobile)
- Add SLACK_INTEGRATION_DOCS constant for documentation link

Page follows existing patterns from /extension and /cloud pages.
This commit is contained in:
Roo Code 2026-01-20 23:48:14 +00:00
parent e356d058e9
commit 973cbc27af
8 changed files with 570 additions and 1 deletions

View file

@ -0,0 +1,69 @@
import type { Metadata } from "next"
import { StructuredData } from "@/components/structured-data"
import { SEO } from "@/lib/seo"
import { ogImageUrl } from "@/lib/og"
import { SlackHeroSection } from "./slack-hero-section"
import { SlackValuePropsSection } from "./slack-value-props-section"
import { ThreadToFeatureWorkflow } from "./thread-to-feature-workflow"
import { SlackOnboardingSteps } from "./slack-onboarding-steps"
import { SlackCTASection } from "./slack-cta-section"
const TITLE = "Roo Code for Slack"
const DESCRIPTION =
"Summon agents to explain code, plan new features, or execute coding tasks without leaving Slack. Your AI team in your team's workspace."
const OG_DESCRIPTION = "@Roomote: Your AI Team in Slack"
const PATH = "/slack"
export const metadata: Metadata = {
title: TITLE,
description: DESCRIPTION,
alternates: {
canonical: `${SEO.url}${PATH}`,
},
openGraph: {
title: TITLE,
description: DESCRIPTION,
url: `${SEO.url}${PATH}`,
siteName: SEO.name,
images: [
{
url: ogImageUrl(TITLE, OG_DESCRIPTION),
width: 1200,
height: 630,
alt: TITLE,
},
],
locale: SEO.locale,
type: "website",
},
twitter: {
card: SEO.twitterCard,
title: TITLE,
description: DESCRIPTION,
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
},
keywords: [
...SEO.keywords,
"Slack integration",
"team collaboration",
"Slack bot",
"AI Slack assistant",
"@Roomote",
"Slack agents",
],
}
export default function SlackPage() {
return (
<>
<StructuredData />
<SlackHeroSection />
<SlackValuePropsSection />
<ThreadToFeatureWorkflow />
<SlackOnboardingSteps />
<SlackCTASection />
</>
)
}

View file

@ -0,0 +1,36 @@
import { ArrowRight } from "lucide-react"
import { Button } from "@/components/ui"
import { EXTERNAL_LINKS } from "@/lib/constants"
export function SlackCTASection() {
return (
<section className="py-24">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="mx-auto max-w-4xl rounded-3xl border border-border/50 bg-gradient-to-br from-violet-500/10 via-purple-500/5 to-blue-500/5 p-8 text-center shadow-2xl backdrop-blur-xl dark:border-white/10 sm:p-16">
<h2 className="mb-6 text-3xl font-bold tracking-tight sm:text-4xl">
Bring your AI team into Slack
</h2>
<p className="mx-auto mb-10 max-w-2xl text-lg text-muted-foreground">
Start collaborating with agents where your team already works. Free to try.
</p>
<div className="flex flex-col justify-center space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0">
<Button
size="lg"
className="bg-foreground text-background hover:bg-foreground/90 transition-all duration-300"
asChild>
<a
href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center">
Get Started
<ArrowRight className="ml-2 h-4 w-4" />
</a>
</Button>
</div>
</div>
</div>
</section>
)
}

View file

@ -0,0 +1,52 @@
import { ArrowRight, ExternalLink } from "lucide-react"
import { Button } from "@/components/ui"
import { AnimatedBackground } from "@/components/homepage"
import { EXTERNAL_LINKS } from "@/lib/constants"
export function SlackHeroSection() {
return (
<section className="relative flex pt-32 pb-20 items-center overflow-hidden">
<AnimatedBackground />
<div className="container relative flex flex-col items-center h-full z-10 mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center max-w-4xl mx-auto mb-12">
<h1 className="text-4xl font-bold tracking-tight mb-6 md:text-5xl lg:text-6xl">
<span className="text-violet-500">@Roomote</span>: Your AI Team in Slack
</h1>
<p className="text-xl text-muted-foreground mb-4 max-w-2xl mx-auto">
Summon agents to explain code, plan new features, or execute coding tasks without leaving Slack.
</p>
<p className="text-lg text-muted-foreground mb-8 max-w-2xl mx-auto">
Mention @Roomote in any channel. Pick an agent and a repo. Get answers, plans, or a PR in the
thread.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button
size="xl"
className="bg-violet-600 hover:bg-violet-700 text-white transition-all duration-300 shadow-lg hover:shadow-violet-500/25"
asChild>
<a
href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center">
Get Started
<ArrowRight className="ml-2 size-5" />
</a>
</Button>
<Button variant="outline" size="xl" className="backdrop-blur-sm" asChild>
<a
href={EXTERNAL_LINKS.SLACK_INTEGRATION_DOCS}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center">
Read the Docs
<ExternalLink className="ml-2 size-4" />
</a>
</Button>
</div>
</div>
</div>
</section>
)
}

View file

@ -0,0 +1,76 @@
import { Settings, Link2, Hash, LucideIcon } from "lucide-react"
interface OnboardingStep {
step: number
icon: LucideIcon
title: string
description: string
}
const onboardingSteps: OnboardingStep[] = [
{
step: 1,
icon: Settings,
title: "Connect your Slack workspace",
description: "Go to your personal or org settings in the top right user menu",
},
{
step: 2,
icon: Link2,
title: "Authorize the integration",
description: "Click on 'Connect' and follow the OAuth process",
},
{
step: 3,
icon: Hash,
title: "Add @Roomote to channels",
description: "Add the @Roomote bot to the channels where you want it to be available",
},
]
export function SlackOnboardingSteps() {
return (
<section className="relative overflow-hidden border-t border-border py-32">
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
<div className="absolute inset-y-0 left-1/2 h-full w-full max-w-[1200px] -translate-x-1/2 z-1">
<div className="absolute left-1/2 top-1/2 h-[400px] w-full -translate-x-1/2 -translate-y-1/2 rounded-full bg-blue-500/10 dark:bg-blue-700/20 blur-[140px]" />
</div>
<div className="mx-auto mb-12 md:mb-24 max-w-5xl text-center">
<div>
<h2 className="text-3xl font-bold tracking-tight sm:text-5xl mb-4">Get started in 3 steps</h2>
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
Connect your Slack workspace and start collaborating with agents in minutes.
</p>
</div>
</div>
<div className="relative mx-auto md:max-w-[1200px]">
<ul className="grid grid-cols-1 place-items-center gap-6 md:grid-cols-3 lg:gap-8">
{onboardingSteps.map((step) => {
const Icon = step.icon
return (
<li
key={step.step}
className="relative h-full w-full border border-border rounded-2xl bg-background p-8 transition-all duration-300 hover:shadow-lg">
<div className="flex items-center gap-3 mb-3">
<div className="bg-violet-100 dark:bg-violet-900/20 w-10 h-10 rounded-full flex items-center justify-center">
<span className="text-violet-600 dark:text-violet-400 font-bold">
{step.step}
</span>
</div>
<Icon className="size-5 text-foreground/60" />
</div>
<h3 className="mb-3 text-xl font-semibold text-foreground">{step.title}</h3>
<div className="leading-relaxed font-light text-muted-foreground">
{step.description}
</div>
</li>
)
})}
</ul>
</div>
</div>
</section>
)
}

View file

@ -0,0 +1,86 @@
import { MessageSquare, Brain, GitBranch, Users, GraduationCap, Share2, Shield, LucideIcon } from "lucide-react"
interface ValueProp {
icon: LucideIcon
title: string
description: string
}
const valueProps: ValueProp[] = [
{
icon: MessageSquare,
title: "From discussion to shipped feature",
description:
"Your team discusses a feature in Slack. @Roomote turns the discussion into a plan. Then builds it. All without leaving the conversation.",
},
{
icon: Brain,
title: "The agent knows the thread",
description:
"@Roomote reads the conversation before responding. Ask 'why is this happening?' after a team discussion and it understands.",
},
{
icon: GitBranch,
title: "Chain agents for complex work",
description:
"Start with a Planner to spec it out. Then call the Coder to build it. Multi-step workflows, one Slack thread.",
},
{
icon: Users,
title: "Non-technical team members can investigate",
description:
"PMs, CSMs, and support can ask @Roomote to explain code or investigate issues. Engineering gets pulled in only when truly needed.",
},
{
icon: GraduationCap,
title: "Team learning, built in",
description: "Public channel mentions show everyone how to leverage agents. Learn by watching.",
},
{
icon: Share2,
title: "One platform, multiple surfaces",
description:
"The same agents that work in your IDE and review your PRs are available in Slack. Different surfaces, same quality.",
},
{
icon: Shield,
title: "Safe by design",
description: "Agents never touch main/master directly. They produce branches and PRs. Humans approve.",
},
]
export function SlackValuePropsSection() {
return (
<section className="py-24 bg-muted/30">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 relative">
<div className="absolute inset-y-0 left-1/2 h-full w-full max-w-[1200px] -translate-x-1/2 z-1">
<div className="absolute left-1/2 top-1/2 h-[800px] w-full -translate-x-1/2 -translate-y-1/2 rounded-full bg-violet-500/10 dark:bg-violet-700/20 blur-[140px]" />
</div>
<div className="text-center mb-16">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl mb-4">
Why teams love working with @Roomote
</h2>
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
Collaboration where it already happens, with AI that understands context.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto relative">
{valueProps.map((prop, index) => {
const Icon = prop.icon
return (
<div
key={index}
className="bg-background p-8 rounded-2xl border border-border hover:shadow-lg transition-all duration-300">
<div className="bg-violet-100 dark:bg-violet-900/20 w-12 h-12 rounded-lg flex items-center justify-center mb-6">
<Icon className="size-6 text-violet-600 dark:text-violet-400" />
</div>
<h3 className="text-xl font-semibold mb-3">{prop.title}</h3>
<p className="text-muted-foreground leading-relaxed">{prop.description}</p>
</div>
)
})}
</div>
</div>
</section>
)
}

View file

@ -0,0 +1,237 @@
"use client"
import { Map, MessageCircle, Code, GitPullRequest, Eye, CornerDownRight } from "lucide-react"
interface SlackMessage {
user: string
role: string
avatar: string
message: string
isBot?: boolean
reactions?: string[]
}
interface WorkflowStep {
step: number
title: string
description: string
result: string
messages: SlackMessage[]
}
const workflowSteps: WorkflowStep[] = [
{
step: 1,
title: "Turn the discussion into a plan",
description: "Capture complex discussions and transform them into structured specs",
result: "Planner agent reads the thread and returns a structured spec",
messages: [
{
user: "Sarah",
role: "PM",
avatar: "S",
message: "This is getting complex. Let's not lose this.",
},
{
user: "Sarah",
role: "PM",
avatar: "S",
message:
"@Roomote plan out a dark mode feature based on our discussion. Include the toggle, persistence, and system preference detection.",
},
{
user: "Roomote",
role: "Planner Agent",
avatar: "R",
message: "I'll analyze the thread and create a structured plan...",
isBot: true,
reactions: ["👀"],
},
],
},
{
step: 2,
title: "Refine the plan in the thread",
description: "Collaborate with your team to review and improve the spec",
result: "The team iterates on the plan without leaving the conversation",
messages: [
{
user: "Alex",
role: "Designer",
avatar: "A",
message: "Can we add a system preference auto-detect option?",
},
{
user: "Sarah",
role: "PM",
avatar: "S",
message: "@Roomote update the plan to include auto-detect from system preferences",
},
{
user: "Roomote",
role: "Planner Agent",
avatar: "R",
message: "Updated the plan. Added system preference detection as the default behavior...",
isBot: true,
},
],
},
{
step: 3,
title: "Build the plan",
description: "Hand off the refined spec to the Coder agent",
result: "Coder agent creates a branch and opens a PR",
messages: [
{
user: "Mike",
role: "Engineer",
avatar: "M",
message: "This looks good. Let's build it.",
},
{
user: "Mike",
role: "Engineer",
avatar: "M",
message: "@Roomote implement this plan in the frontend-web repo.",
},
{
user: "Roomote",
role: "Coder Agent",
avatar: "R",
message: "Building the dark mode feature. I'll create a PR when ready...",
isBot: true,
reactions: ["👀"],
},
],
},
{
step: 4,
title: "Review and ship",
description: "Review the PR and merge when ready",
result: "Human oversight at every step. Agents produce artifacts; humans decide what ships.",
messages: [
{
user: "Roomote",
role: "Coder Agent",
avatar: "R",
message: "PR ready for review: feat: Add dark mode with system preference detection #247",
isBot: true,
},
{
user: "Mike",
role: "Engineer",
avatar: "M",
message: "Reviewing now. LGTM! Merging.",
},
],
},
]
function SlackMessageComponent({ message }: { message: SlackMessage }) {
return (
<div className="flex gap-3 py-2">
<div
className={`w-9 h-9 rounded-lg flex items-center justify-center flex-shrink-0 text-sm font-semibold ${
message.isBot
? "bg-violet-100 dark:bg-violet-900/40 text-violet-600 dark:text-violet-400"
: "bg-muted text-muted-foreground"
}`}>
{message.avatar}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-baseline gap-2 mb-0.5">
<span className={`font-semibold text-sm ${message.isBot ? "text-violet-600 dark:text-violet-400" : ""}`}>
{message.user}
</span>
<span className="text-xs text-muted-foreground">{message.role}</span>
</div>
<p className="text-sm text-foreground/90 leading-relaxed">{message.message}</p>
{message.reactions && message.reactions.length > 0 && (
<div className="flex gap-1 mt-1.5">
{message.reactions.map((reaction, i) => (
<span
key={i}
className="inline-flex items-center gap-1 px-2 py-0.5 bg-muted rounded-full text-xs">
{reaction}
</span>
))}
</div>
)}
</div>
</div>
)
}
function WorkflowStepCard({ step }: { step: WorkflowStep }) {
const stepIcons = [Map, MessageCircle, Code, GitPullRequest]
const Icon = stepIcons[step.step - 1] || Map
return (
<div className="bg-background rounded-2xl border border-border overflow-hidden hover:shadow-lg transition-all duration-300">
{/* Step Header */}
<div className="p-6 border-b border-border bg-muted/30">
<div className="flex items-center gap-3 mb-2">
<div className="bg-violet-100 dark:bg-violet-900/20 w-8 h-8 rounded-full flex items-center justify-center">
<span className="text-violet-600 dark:text-violet-400 font-bold text-sm">{step.step}</span>
</div>
<Icon className="size-5 text-violet-600 dark:text-violet-400" />
</div>
<h3 className="text-xl font-semibold mb-1">{step.title}</h3>
<p className="text-sm text-muted-foreground">{step.description}</p>
</div>
{/* Slack Thread Mockup */}
<div className="p-4 bg-background">
<div className="space-y-1">
{step.messages.map((message, i) => (
<SlackMessageComponent key={i} message={message} />
))}
</div>
</div>
{/* Result */}
<div className="p-4 border-t border-border bg-violet-50/50 dark:bg-violet-900/10">
<div className="flex items-start gap-2 text-sm">
<CornerDownRight className="size-4 text-violet-600 dark:text-violet-400 mt-0.5 flex-shrink-0" />
<span className="text-violet-700 dark:text-violet-300 font-medium">{step.result}</span>
</div>
</div>
</div>
)
}
export function ThreadToFeatureWorkflow() {
return (
<section className="py-24 bg-background">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300 text-sm font-medium mb-4">
<Eye className="size-4" />
Featured Workflow
</div>
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl mb-4">
From Discussion to Shipped Feature
</h2>
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
The complete workflow that makes Slack the ideal surface for agent collaboration. Discussion
&rarr; spec &rarr; code &rarr; PR, all in one thread.
</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 max-w-6xl mx-auto">
{workflowSteps.map((step) => (
<WorkflowStepCard key={step.step} step={step} />
))}
</div>
<div className="mt-12 text-center">
<p className="text-muted-foreground max-w-2xl mx-auto">
<strong className="text-foreground">No context switch required.</strong> Everyone sees the
workflow in public channels. Multi-agent handoffs from Planner to Coder. Human oversight at
every step.
</p>
</div>
</div>
</section>
)
}

View file

@ -13,7 +13,7 @@ import { EXTERNAL_LINKS } from "@/lib/constants"
import { useLogoSrc } from "@/lib/hooks/use-logo-src"
import { ScrollButton } from "@/components/ui"
import ThemeToggle from "@/components/chromes/theme-toggle"
import { Brain, ChevronDown, Cloud, Puzzle, X } from "lucide-react"
import { Brain, ChevronDown, Cloud, Puzzle, Slack, X } from "lucide-react"
interface NavBarProps {
stars: string | null
@ -54,6 +54,12 @@ export function NavBar({ stars, downloads }: NavBarProps) {
<Cloud className="size-3 inline mr-2 -mt-0.5" />
Roo Code Cloud
</Link>
<Link
href="/slack"
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground">
<Slack className="size-3 inline mr-2 -mt-0.5" />
Roo Code for Slack
</Link>
<Link
href="/provider"
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground">
@ -190,6 +196,12 @@ export function NavBar({ stars, downloads }: NavBarProps) {
onClick={() => setIsMenuOpen(false)}>
Roo Code Cloud
</Link>
<Link
href="/slack"
className="block w-full p-5 py-3 text-left text-foreground active:opacity-50"
onClick={() => setIsMenuOpen(false)}>
Roo Code for Slack
</Link>
<Link
href="/provider"
className="block w-full p-5 py-3 text-left text-foreground active:opacity-50"

View file

@ -29,6 +29,7 @@ export const EXTERNAL_LINKS = {
CLOUD_APP_SIGNUP_HOME: "https://app.roocode.com/sign-up?redirect_url=/cloud-agents/setup",
CLOUD_APP_SIGNUP_PRO: "https://app.roocode.com/sign-up?redirect_url=/cloud-agents/setup",
SUPPORT: "mailto:support@roocode.com",
SLACK_INTEGRATION_DOCS: "https://docs.roocode.com/roo-code-cloud/slack-integration",
}
export const INTERNAL_LINKS = {