UI
Tech loop
Expandable tech stack pills
Client · Next.js
Live preview
Interact with the component below — same behavior as on the About page.
Summary
A row of compact tech stack cards: each pill shows a logo by default and expands on hover to reveal the technology name. Logos resolve from custom URLs, local assets, or svgl.app slugs. Duplicate names are filtered and dark-mode inversion is handled for icons that need it.
- Hover expands dashed-border pills with smooth grid animation
- Automatic logo lookup via svgl.app with local overrides
- Accepts tech names or full TechItem objects with custom logo URLs
- Accessible list with project-scoped aria-label
Add to your project
Run the CLI to copy files into your project. Pick your language and styling below — the command updates automatically.
1. Add via CLI (recommended)
npx @shashank-portfolio/cli add tech-loop2. Install dependencies
npm install nextRequired packages
- next ^16.0.0 · Image component
3. Manual installation
Copy both files below. TechItem is { name: string; logoUrl?: string } — inline the type or import from your project types module.
/** PNG/SVG assets that need inversion on dark backgrounds */
const TECH_INVERT_IN_DARK = new Set(["Next.js"]);
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",
"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",
};
export function getTechLogoUrl(name: string): string | null {
const slug = TECH_SLUGS[name];
return slug ? `https://cdn.simpleicons.org/${slug}` : null;
}
/** @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);
}
4. Use in your app
"use client";
import { ProjectTechLoop } from "@/components/projects/project-tech-loop";
export function ProjectTech() {
return (
<ProjectTechLoop
projectTitle="My App"
techNames={["React", "Next.js", "TypeScript", "Tailwind CSS"]}
/>
);
}Let’s connect
I’m always open to discussing new projects, creative ideas, or opportunities to be part of your visions. Just reach out!