/** PNG/SVG assets that need inversion on dark backgrounds */
const TECH_INVERT_IN_DARK = new Set(["shadcn/ui", "Next.js", "Render"]);
export function getTechLogoImageClass(name: string): string {
if (TECH_INVERT_IN_DARK.has(name)) {
return "dark:invert";
}
return "";
}
/** simpleicons.org slugs — reliable CDN for tech logos */
const TECH_SLUGS: Record<string, string> = {
"Next.js": "nextdotjs",
TypeScript: "typescript",
"Tailwind CSS": "tailwindcss",
React: "react",
Figma: "figma",
Framer: "framer",
Vercel: "vercel",
PostgreSQL: "postgresql",
"OpenAI API": "openai",
"Node.js": "nodedotjs",
Stripe: "stripe",
GSAP: "gsap",
"shadcn/ui": "shadcnui",
"Sanity CMS": "sanity",
Supabase: "supabase",
Vite: "vite",
"Anthropic API": "anthropic",
"Ruby on Rails": "rubyonrails",
Mixpanel: "mixpanel",
Resend: "resend",
Amplitude: "amplitude",
"React Native": "react",
"Whisper API": "openai",
Motion: "framer",
Express: "express",
Render: "render",
};
/** Force explicit icon colors (hex without #) */
const TECH_COLORS: Record<string, string> = {
"Next.js": "000000",
Resend: "ffffff",
};
/** Local assets in /public that take priority over the simpleicons CDN */
const TECH_LOCAL_ASSETS: Record<string, string> = {
Figma: "/figma.svg",
PostgreSQL: "/250px-Postgresql_elephant.svg.webp",
Render: "/render.png",
Express: "/express.png",
};
export function getTechLogoUrl(name: string): string | null {
const local = TECH_LOCAL_ASSETS[name];
if (local) return local;
const slug = TECH_SLUGS[name];
if (!slug) return null;
const color = TECH_COLORS[name];
return color
? `https://cdn.simpleicons.org/${slug}/${color}`
: `https://cdn.simpleicons.org/${slug}`;
}
/** @deprecated Use getTechLogoUrl */
export function getTechSvgUrl(name: string): string | null {
return getTechLogoUrl(name);
}
export function getTechNamesFromStack(
techStack: { name: string }[]
): string[] {
return techStack.map((t) => t.name);
}
/** PNG/SVG assets that need inversion on dark backgrounds */
const TECH_INVERT_IN_DARK = new Set(["shadcn/ui", "Next.js", "Render"]);
export function getTechLogoImageClass(name: string): string {
if (TECH_INVERT_IN_DARK.has(name)) {
return "dark:invert";
}
return "";
}
/** simpleicons.org slugs — reliable CDN for tech logos */
const TECH_SLUGS: Record<string, string> = {
"Next.js": "nextdotjs",
TypeScript: "typescript",
"Tailwind CSS": "tailwindcss",
React: "react",
Figma: "figma",
Framer: "framer",
Vercel: "vercel",
PostgreSQL: "postgresql",
"OpenAI API": "openai",
"Node.js": "nodedotjs",
Stripe: "stripe",
GSAP: "gsap",
"shadcn/ui": "shadcnui",
"Sanity CMS": "sanity",
Supabase: "supabase",
Vite: "vite",
"Anthropic API": "anthropic",
"Ruby on Rails": "rubyonrails",
Mixpanel: "mixpanel",
Resend: "resend",
Amplitude: "amplitude",
"React Native": "react",
"Whisper API": "openai",
Motion: "framer",
Express: "express",
Render: "render",
};
/** Force explicit icon colors (hex without #) */
const TECH_COLORS: Record<string, string> = {
"Next.js": "000000",
Resend: "ffffff",
};
/** Local assets in /public that take priority over the simpleicons CDN */
const TECH_LOCAL_ASSETS: Record<string, string> = {
Figma: "/figma.svg",
PostgreSQL: "/250px-Postgresql_elephant.svg.webp",
Render: "/render.png",
Express: "/express.png",
};
export function getTechLogoUrl(name: string): string | null {
const local = TECH_LOCAL_ASSETS[name];
if (local) return local;
const slug = TECH_SLUGS[name];
if (!slug) return null;
const color = TECH_COLORS[name];
return color
? `https://cdn.simpleicons.org/${slug}/${color}`
: `https://cdn.simpleicons.org/${slug}`;
}
/** @deprecated Use getTechLogoUrl */
export function getTechSvgUrl(name: string): string | null {
return getTechLogoUrl(name);
}
export function getTechNamesFromStack(
techStack: { name: string }[]
): string[] {
return techStack.map((t) => t.name);
}