Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nicolasgrajaleshoyos/portafolio/llms.txt

Use this file to discover all available pages before exploring further.

The About component occupies the second section of the portfolio (id #about) and gives visitors a personal introduction to Nicolas Grajales Hoyos. It is divided into two visual areas: a left-side profile photo with a floating animation and a rotated colour tint panel, paired with a bio text column on the right, and below them an 18-tile skills grid that reveals itself with a staggered entrance animation as the section scrolls into view.

Props

About accepts no external props. The bio copy, profile image path, and full skills list are all defined as module-level constants inside the component file.

Behavior

Visibility via IntersectionObserver

A useRef is attached to the outer <section> element. An IntersectionObserver configured with threshold: 0.1 sets isVisible to true the first time 10 % of the section enters the viewport, then immediately unobserves the target so the animation only fires once. The isVisible flag drives:
  • Section opacityopacity-0opacity-100 via transition-opacity duration-1000.
  • Individual skill card opacity and translateY — each card is hidden (opacity: 0, translateY: 20px) until isVisible is true, at which point the inline styles switch to opacity: 1, translateY: 0.

Skills grid layout

The 18 skill tiles are arranged in a responsive grid that increases column count as the viewport widens:
grid-cols-3 sm:grid-cols-4 md:grid-cols-5
This gives 3 columns on mobile, 4 on small screens, and 5 on medium/large screens.

Staggered skill card animation

The skills are rendered via .map(), and each card receives a transitionDelay proportional to its index:
style={{ transitionDelay: `${index * 100}ms` }}
This means the first card appears immediately, the second after 100 ms, the third after 200 ms, and so on — creating a ripple-style entrance across the grid.

Profile image

The photo is served from the public/icons/ directory at the path /icons/2.jpg. It sits inside a relative container that also renders a rotated bg-primary/20 background panel. The parent group container pauses the CSS float animation on hover:
animate-float motion-reduce:animate-none group-hover:[animation-play-state:paused]

Skills Data

The full list of 18 skills is defined as a typed array at module level. Icons come from a mixture of custom SVG wrappers (@/components/Icons/SkillIcons) and the react-icons library:
const skills: Skill[] = [
  { name: 'React', icon: ReactIcon, className: 'text-sky-500' },
  { name: 'TypeScript', icon: TypeScriptIcon, className: 'text-blue-600' },
  { name: 'JavaScript', icon: JavaScriptIcon, className: 'text-yellow-400' },
  { name: 'Next.js', icon: NextJsIcon, className: 'text-sky' },
  { name: 'Node.js', icon: NodeJsIcon, className: 'text-green-500' },
  { name: 'Tailwind CSS', icon: TailwindIcon, className: 'text-teal-500' },
  { name: 'HTML5', icon: HTMLIcon, className: 'text-orange-600' },
  { name: 'CSS3', icon: CSSIcon, className: 'text-blue-500' },
  { name: 'Figma', icon: FigmaIcon, className: 'text-pink-500' },
  { name: 'Git', icon: GitIcon, className: 'text-red-600' },
  { name: 'Python', icon: FaPython, className: 'text-sky-600' },
  { name: 'Angular', icon: FaAngular, className: 'text-red-600' },
  { name: 'Docker', icon: FaDocker, className: 'text-sky-500' },
  { name: 'Mongodb', icon: SiMongodb, className: 'text-green-500' },
  { name: 'vscode', icon: BiLogoVisualStudio, className: 'text-blue-500' },
  { name: 'Postgresql', icon: SiPostgresql, className: 'text-blue-700' },
  { name: 'Notion', icon: PiNotionLogoBold, className: 'text-black dark:text-white' },
  { name: 'Vue.js', icon: FaVuejs, className: 'text-green-500' },
];
The Skill interface is defined in src/types.ts:
export interface Skill {
  name: string;
  icon: React.FC<{ className?: string }>;
  className?: string;
}

Usage Example

About is rendered inside <main> in App.tsx, directly after <Hero />:
import About from '@/components/About';

const App: React.FC = () => (
  <main className="flex-grow">
    <Hero />
    <About />
    <Projects />
    <Contact />
  </main>
);

Customization

  1. Import the icon from react-icons or create a custom wrapper in @/components/Icons/SkillIcons.
  2. Add a new entry to the skills array:
import { SiRust } from 'react-icons/si';

const skills: Skill[] = [
  // ... existing entries
  { name: 'Rust', icon: SiRust, className: 'text-orange-600' },
];
The new tile will automatically be assigned the next staggered delay.
Delete the corresponding object from the skills array. The remaining tiles reflow in the responsive grid and their stagger delays recalculate automatically.
Replace /icons/2.jpg in the public/icons/ directory with your new image, keeping the same filename, or update the src attribute in the <img> tag:
<img src="/icons/profile-new.jpg" alt="Foto de perfil de Nicolas Grajales" />
The two bio paragraphs are hardcoded <p> elements inside the lg:col-span-3 div. Edit their text content directly in About.tsx to update the personal introduction.
Change the multiplier in the transitionDelay inline style to speed up or slow down the cascade:
style={{ transitionDelay: `${index * 50}ms` }}  // faster
style={{ transitionDelay: `${index * 200}ms` }} // slower

Build docs developers (and LLMs) love