Skip to main content

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-loop

2. Install dependencies

npm install next

Required 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!

2026 © Built with Next.js

By Love ❤️