Customization
Last updated on 2026-04-05
The Sales Dashboard Kit is designed to be customized for your sales team or SaaS product. All components use design tokens and Tailwind CSS utilities, making brand adaptation straightforward.
Changing Colors
Update CSS custom properties in app/globals.css. The default theme uses oklch hue 250. Change the hue to rebrand the entire kit:
:root {
/* Change from blue (hue 250) to emerald (hue 160) */
--primary: oklch(0.50 0.18 160);
--primary-foreground: oklch(0.98 0 0);
--accent: oklch(0.94 0.02 160);
--accent-foreground: oklch(0.35 0.15 160);
--ring: oklch(0.55 0.18 160);
}
.dark {
--primary: oklch(0.70 0.18 160);
--primary-foreground: oklch(0.15 0.02 160);
}
All 9 screens automatically inherit the new colors -- buttons, badges, links, charts, sidebar accents, pipeline stages, progress bars, and focus rings all update at once.
Changing Typography
Configure fonts in app/layout.tsx:
import { Inter, DM_Sans, JetBrains_Mono } from "next/font/google";
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
const dmSans = DM_Sans({ subsets: ["latin"], variable: "--font-dm-sans" });
const jetbrainsMono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-jetbrains" });
Swap these for any Google Font or local font files. The CSS variables --font-sans, --font-heading, and --font-mono control body text, headings, and code respectively.
Adding Sales Stages
Edit the pipeline stages in data/seed.ts and the corresponding type in types/index.ts:
// data/seed.ts
export const pipelineStages: PipelineStage[] = [
{ id: "prospecting", name: "Prospecting", color: "gray", order: 0 },
{ id: "qualification", name: "Qualification", color: "blue", order: 1 },
{ id: "discovery", name: "Discovery", color: "cyan", order: 2 }, // New stage
{ id: "proposal", name: "Proposal", color: "indigo", order: 3 },
{ id: "negotiation", name: "Negotiation", color: "violet", order: 4 },
{ id: "contract", name: "Contract Review", color: "amber", order: 5 }, // New stage
{ id: "closed-won", name: "Closed Won", color: "green", order: 6 },
{ id: "closed-lost", name: "Closed Lost", color: "red", order: 7 },
]
The pipeline board, deal list stage badges, conversion funnel, and reports all dynamically render from this stages array, so new stages appear everywhere automatically.
Modifying Deal Fields
Extend the Deal interface in types/index.ts to add custom fields:
// types/index.ts
export interface Deal {
id: string
name: string
company: string
value: number
stage: string
owner: string
closeDate: string
priority: "hot" | "warm" | "cold"
// Add custom fields
productLine: string // e.g., "Enterprise", "SMB", "Startup"
competitorInDeal: string // e.g., "Salesforce", "HubSpot", "None"
nextStepDate: string // Next scheduled action date
decisionMaker: string // Key contact name
lossReason?: string // Reason for lost deals
}
Then update the DealRow and PipelineCard components to display the new fields, and add corresponding mock data to data/seed.ts.
Connecting a CRM API
Replace the seed data imports in your page components with API calls to your CRM platform.
Salesforce REST API
// app/api/salesforce/deals/route.ts
export async function GET() {
const tokenRes = await fetch("https://login.salesforce.com/services/oauth2/token", {
method: "POST",
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.SALESFORCE_CLIENT_ID!,
client_secret: process.env.SALESFORCE_CLIENT_SECRET!,
}),
})
const { access_token, instance_url } = await tokenRes.json()
const res = await fetch(
`${instance_url}/services/data/v60.0/query?q=SELECT+Id,Name,Amount,StageName,CloseDate+FROM+Opportunity`,
{ headers: { Authorization: `Bearer ${access_token}` } }
)
const data = await res.json()
return Response.json(data.records)
}
HubSpot API
// app/api/hubspot/deals/route.ts
export async function GET() {
const res = await fetch(
"https://api.hubapi.com/crm/v3/objects/deals?limit=100&properties=dealname,amount,dealstage,closedate",
{ headers: { Authorization: `Bearer ${process.env.HUBSPOT_ACCESS_TOKEN}` } }
)
const data = await res.json()
return Response.json(data.results)
}
Supabase
import { createClient } from "@supabase/supabase-js"
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
const { data: deals } = await supabase
.from("deals")
.select("*, owner:team_members(*), company:companies(*)")
.order("created_at", { ascending: false })
Building Custom Reports
Add a new report section by creating a chart component and wiring it to your data:
// components/charts/revenue-by-region-chart.tsx
"use client"
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts"
interface RevenueByRegionProps {
data: { region: string; revenue: number }[]
}
export function RevenueByRegionChart({ data }: RevenueByRegionProps) {
return (
<ResponsiveContainer width="100%" height={300}>
<BarChart data={data}>
<XAxis dataKey="region" />
<YAxis />
<Tooltip />
<Bar dataKey="revenue" fill="var(--chart-1)" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
)
}
Then add it to the reports page:
// app/(dashboard)/reports/page.tsx
import { RevenueByRegionChart } from "@/components/charts/revenue-by-region-chart"
import { regionRevenueData } from "@/data/seed"
<section aria-label="Revenue by Region">
<h2 className="font-heading text-xl font-bold">Revenue by Region</h2>
<RevenueByRegionChart data={regionRevenueData} />
</section>
Customizing the Sidebar
Edit components/layout/app-sidebar.tsx to add or remove navigation items:
const navItems = [
{ label: "Dashboard", href: "/", icon: LayoutDashboard },
{ label: "Pipeline", href: "/pipeline", icon: Kanban },
{ label: "Deals", href: "/deals", icon: Handshake },
{ label: "Targets", href: "/targets", icon: Target },
{ label: "Leaderboard", href: "/leaderboard", icon: Trophy },
{ label: "Reports", href: "/reports", icon: BarChart3 },
// Add custom pages
{ label: "Forecasting", href: "/forecasting", icon: TrendingUp },
// Remove items you don't need
]
Each item takes a label, href, and a Lucide icon component. Active state highlighting is handled automatically based on the current pathname.
Replacing Seed Data
All mock data lives in data/seed.ts. Replace it with your data source:
API calls
import { getDeals } from "@/lib/api"
export default async function DealsPage() {
const deals = await getDeals()
return <DealsList deals={deals} />
}
Database (Prisma, Drizzle)
import { db } from "@/lib/db"
const deals = await db.deal.findMany({
orderBy: { updatedAt: "desc" },
include: { owner: true, company: true },
})
Removing Unused Features
Delete directories you don't need:
- Don't need leaderboard? Delete
app/(dashboard)/leaderboard/andcomponents/sales/rep-rank-card.tsx - Don't need targets? Delete
app/(dashboard)/targets/andcomponents/sales/target-card.tsx,components/sales/quota-progress.tsx - Don't need reports? Delete
app/(dashboard)/reports/and the chart components you no longer reference - Run
pnpm buildto verify no broken imports
Using shadcn/ui CLI
Add new components alongside the kit:
npx shadcn@latest add <component-name>
Components install to components/ui/ and integrate seamlessly with the existing design tokens. The kit uses the new-york style with CSS variables enabled.