Files
Evandro Fonseca Junior 08dd046887 feat: add animated components for enhanced UI/UX
- Introduced DrawLine component for drawing animations.
- Added LazyRevealImage component for lazy loading images with reveal animations.
- Implemented TextDiffuseReveal for animated text reveals with staggered effects.
- Created TopProgressBar for a dynamic loading indicator.
- Added PointCloud mock for testing animations.
- Developed useReducedMotion hook to handle user preferences for reduced motion.
- Centralized animation configurations in config.ts for easier management.
- Defined animation variants in variants.ts for consistent animation effects.
- Established AnimationProvider to supply global animation settings.
- Created tests for AnimationProvider and useReducedMotion hook.
- Added SVG assets for branding and UI elements.
- Documented frontend review plan and hero circuit board animation plan.
2026-03-15 21:06:43 -03:00

17 KiB

Design Document: Site Improvements - Footer & Companies

Overview

This design implements site-wide improvements including spacing standardization, a complete footer component with contact information and social links, an animated company logos banner with infinite scroll, and reorganization of the Experience section into Software/Hardware categories. The implementation maintains the existing minimalist design system using Monument Extended typography, #ccff00 accent color, and Framer Motion animations.

The design focuses on three main areas:

  1. Spacing System Standardization: Applying consistent spacing tokens across all sections
  2. Footer Component: A new footer with contact info, social links, and copyright
  3. CompanyLogos Component: An infinite-scroll animated banner displaying company logos
  4. Experience Section Refactor: Categorizing experiences into Software and Hardware groups

All components follow the established design system patterns from globals.css and integrate with the existing Framer Motion animation infrastructure.

Architecture

Component Structure

app/
├── components/
│   ├── Footer.tsx (replace existing)
│   ├── CompanyLogos.tsx (new)
│   ├── Experience.tsx (refactor)
│   └── Section.tsx (update spacing)
├── page.tsx (update layout)
└── globals.css (spacing documentation)

Data Flow

  1. Footer Component: Static data (email, social links) defined as constants within the component
  2. CompanyLogos Component: Logo data (company names, image paths) defined as constants, rendered with animation
  3. Experience Component: Existing data from usePortfolioData() hook, filtered and categorized by type field

Animation Strategy

All animations use Framer Motion with reduced motion support:

  • CompanyLogos: Continuous horizontal scroll using animate with infinite loop
  • Footer: Subtle hover states on social links
  • Experience: Existing stagger animations maintained, category headers added

Components and Interfaces

Purpose: Display contact information, social links, and copyright in a consistent footer layout.

Props: None (self-contained component)

Structure:

interface SocialLink {
  name: string;
  href: string;
  icon: string; // SVG path or icon identifier
  ariaLabel: string;
}

const socialLinks: SocialLink[] = [
  { name: 'GitHub', href: 'https://github.com/...', icon: '...', ariaLabel: 'Visit GitHub profile' },
  { name: 'LinkedIn', href: 'https://linkedin.com/...', icon: '...', ariaLabel: 'Visit LinkedIn profile' },
  // ... more links
];

Layout:

  • Desktop: Three-column grid (contact info | social links | copyright)
  • Tablet: Two-column grid (contact/social | copyright)
  • Mobile: Single column stack

Styling:

  • Background: var(--ink) with border-t: 1px solid rgba(255,255,255,0.06)
  • Typography: Monument Extended for headings, Geist Mono for body text
  • Accent: var(--slash) for link hover states
  • Padding: py-sp-12 px-sp-16 (desktop), py-sp-8 px-sp-6 (mobile)

2. CompanyLogos Component

Purpose: Display an infinite-scrolling banner of company logos after the header.

Props: None (self-contained component)

Structure:

interface CompanyLogo {
  name: string;
  imagePath: string;
  alt: string;
}

const companies: CompanyLogo[] = [
  { name: 'Apolo Energia', imagePath: '/logos/apolo.svg', alt: 'Apolo Energia logo' },
  { name: 'Julia Beauty Lashes', imagePath: '/logos/julia.svg', alt: 'Julia Beauty Lashes logo' },
  { name: 'Innovo', imagePath: '/logos/innovo.svg', alt: 'Innovo logo' },
  { name: 'TeraByteShop', imagePath: '/logos/terabyte.svg', alt: 'TeraByteShop logo' },
  { name: 'Pichau', imagePath: '/logos/pichau.svg', alt: 'Pichau logo' },
];

Animation Implementation:

// Duplicate logo array for seamless loop
const duplicatedLogos = [...companies, ...companies];

// Framer Motion animation
<motion.div
  animate={{
    x: [0, -50%], // Move by half the width (one set of logos)
  }}
  transition={{
    duration: 30, // 30 seconds for full loop
    repeat: Infinity,
    ease: "linear",
  }}
>
  {duplicatedLogos.map((logo, index) => (
    <img key={`${logo.name}-${index}`} src={logo.imagePath} alt={logo.alt} />
  ))}
</motion.div>

Reduced Motion Handling:

const shouldReduceMotion = useReducedMotion();

// If reduced motion, display static grid instead of animation
{shouldReduceMotion ? (
  <div className="grid grid-cols-5 gap-sp-6">
    {companies.map(logo => <img key={logo.name} ... />)}
  </div>
) : (
  <motion.div animate={...} />
)}

Styling:

  • Container: overflow-hidden with border-b: 1px solid var(--border)
  • Background: var(--paper) with optional subtle grid pattern
  • Logo sizing: h-12 (48px) with grayscale filter, grayscale-0 on hover
  • Spacing: py-sp-8 vertical padding, gap-sp-12 between logos

3. Experience Component Refactor

Purpose: Organize experience entries into Software and Hardware categories.

Changes:

  1. Add category filtering logic
  2. Render two separate sections with category headers
  3. Maintain existing card structure and animations

Implementation:

const softwareExperiences = sortedExperiences.filter(exp => exp.type === 'Software');
const hardwareExperiences = sortedExperiences.filter(exp => exp.type === 'Hardware');

return (
  <Section id="experience" ...>
    {/* Software Category */}
    <div className="mb-sp-12">
      <h3 className="font-display text-2xl font-bold mb-sp-6 flex items-center gap-sp-3">
        <span className="text-[var(--slash)]">/</span> Software
      </h3>
      <motion.div variants={listVariants}>
        {softwareExperiences.map(exp => <ExperienceCard key={exp.id} {...exp} />)}
      </motion.div>
    </div>

    {/* Hardware Category */}
    <div>
      <h3 className="font-display text-2xl font-bold mb-sp-6 flex items-center gap-sp-3">
        <span className="text-[var(--slash)]">/</span> Hardware
      </h3>
      <motion.div variants={listVariants}>
        {hardwareExperiences.map(exp => <ExperienceCard key={exp.id} {...exp} />)}
      </motion.div>
    </div>
  </Section>
);

Styling:

  • Category headers: Monument Extended, text-2xl, with slash accent
  • Separator: mb-sp-12 between categories
  • Cards: Maintain existing styling and animations

4. Spacing System Updates

Standardization: All sections should use consistent spacing tokens from globals.css:

  • Section padding: py-sp-20 px-sp-16 (desktop), py-sp-16 px-sp-6 (mobile)
  • Section margins: mb-sp-12 between major sections
  • Card spacing: space-y-sp-6 for vertical stacks
  • Grid gaps: gap-sp-6 for 2-column, gap-sp-4 for 4-column

Responsive Scaling: Tailwind responsive utilities handle breakpoints:

  • Mobile: px-sp-6 py-sp-16
  • Tablet: px-sp-10 py-sp-18
  • Desktop: px-sp-16 py-sp-20

Data Models

interface SocialLink {
  name: string;          // Display name (e.g., "GitHub")
  href: string;          // Full URL
  icon: string;          // SVG path or icon component reference
  ariaLabel: string;     // Accessibility label
}
interface CompanyLogo {
  name: string;          // Company name
  imagePath: string;     // Path to logo image (SVG preferred)
  alt: string;           // Alt text for accessibility
}

Experience (existing, with type field)

interface Experience {
  id: string;
  title: string;
  company: string;
  type: 'Software' | 'Hardware';  // Used for categorization
  period: string;
  location: string;
  description: string;
  achievements: string[];
  technologies: string[];
  featured: boolean;
}

Correctness Properties

A property is a characteristic or behavior that should hold true across all valid executions of a system—essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.

Property 1: Consistent Spacing System

For any major section or container element in the site, it should use spacing tokens from the design system (sp-1 through sp-24) with appropriate responsive modifiers for different viewport sizes.

Validates: Requirements 1.1, 1.2, 1.3, 1.4

For any social media link in the Footer component, it should have target="_blank" and rel="noopener noreferrer" attributes to open in a new tab securely.

Validates: Requirements 2.7

Property 3: Responsive Component Layout

For any new component (Footer, CompanyLogos, Experience categories), it should include Tailwind responsive utility classes (sm:, md:, lg:) to adapt layout across viewport sizes.

Validates: Requirements 2.6, 3.7, 4.5

Property 4: Reduced Motion Respect

For any animated component, when useReducedMotion() returns true, the component should render a static alternative without motion animations.

Validates: Requirements 3.6, 6.3

Property 5: Experience Categorization

For any experience entry in the Experience section, it should be filtered by its type field and rendered within the corresponding Software or Hardware category group.

Validates: Requirements 4.1

Property 6: Chronological Ordering Within Categories

For any category group in the Experience section, the experience entries within that group should be sorted in descending chronological order by year.

Validates: Requirements 4.3

Property 7: Monument Extended Typography

For any heading element (h1, h2, h3) in new components (Footer, CompanyLogos, Experience categories), it should use the font-display class which applies Monument Extended font family.

Validates: Requirements 2.4, 4.2, 5.4

Property 8: Slash Color Accents

For any accent element (category separators, hover states, decorative elements) in new components, it should use var(--slash) color (#ccff00) via Tailwind classes like text-[var(--slash)] or bg-[var(--slash)].

Validates: Requirements 5.1, 5.2

For any social media link in the Footer component, it should have a descriptive aria-label attribute that identifies the platform and action.

Validates: Requirements 6.1

Property 10: Alt Text for Images

For any company logo image in the CompanyLogos component, it should have a non-empty alt attribute describing the company.

Validates: Requirements 6.2

Property 11: Semantic Heading Hierarchy

For any sequence of heading elements in the Experience section, the heading levels should follow proper semantic hierarchy (h2 for section title, h3 for categories, no skipped levels).

Validates: Requirements 6.4

Property 12: Keyboard Navigation Support

For any interactive element (links, buttons) in new components, it should be keyboard accessible with proper focus states and no negative tabindex values.

Validates: Requirements 6.5

Error Handling

Missing Social Links Data:

  • Gracefully render empty state or hide social links section
  • Log warning to console in development mode
  • Ensure layout doesn't break with missing data

Invalid Email Format:

  • Display email as-is (validation is not the footer's responsibility)
  • Ensure mailto: link is properly formatted

CompanyLogos Component

Missing Logo Images:

  • Display company name as fallback text
  • Log warning for missing images in development
  • Maintain layout spacing even with missing images

Animation Performance Issues:

  • Monitor frame rate using useAnimationFrame hook
  • Fallback to static display if performance drops below threshold
  • Respect prefers-reduced-motion preference

Experience Component

Missing Type Field:

  • Default to "Software" category if type is undefined
  • Log warning for entries without type field
  • Ensure all entries are displayed (don't silently drop data)

Empty Categories:

  • Hide category header if no entries exist for that type
  • Maintain spacing consistency when categories are hidden

General Error Handling

Component Render Errors:

  • Wrap components in Error Boundaries
  • Display user-friendly error message
  • Log detailed error information for debugging

Responsive Layout Issues:

  • Use CSS fallbacks for unsupported features
  • Test across major browsers and devices
  • Ensure content remains accessible even if styling fails

Testing Strategy

Dual Testing Approach

This feature requires both unit tests and property-based tests to ensure comprehensive coverage:

Unit Tests: Focus on specific examples, edge cases, and integration points

  • Specific component rendering scenarios
  • Edge cases (empty data, missing fields)
  • User interaction flows (clicking links, reduced motion)
  • Integration between components

Property-Based Tests: Verify universal properties across all inputs

  • Spacing consistency across generated component trees
  • Accessibility attributes across generated link/image sets
  • Categorization logic across generated experience data
  • Responsive class application across generated components

Testing Framework

Framework: Vitest + React Testing Library + fast-check

  • Vitest for test runner and assertions
  • React Testing Library for component testing
  • fast-check for property-based testing (TypeScript-native)

Property Test Configuration:

  • Minimum 100 iterations per property test
  • Each test tagged with: Feature: site-improvements-footer-companies, Property {number}: {property_text}
  • Use custom generators for component props and data structures

Unit Test Coverage

Footer Component:

  • Renders contact information correctly
  • Renders all social links with correct attributes
  • Displays current year in copyright
  • Opens social links in new tab
  • Applies correct styling classes
  • Responsive layout changes at breakpoints

CompanyLogos Component:

  • Renders all five company logos
  • Duplicates logo array for infinite scroll
  • Applies animation with correct configuration
  • Displays static grid when reduced motion is enabled
  • Handles missing logo images gracefully
  • Maintains responsive layout

Experience Component:

  • Filters experiences by Software/Hardware type
  • Renders category headers with correct styling
  • Maintains chronological order within categories
  • Hides empty categories
  • Applies consistent spacing between categories
  • Responsive layout on mobile devices

Property-Based Test Coverage

Property 1: Consistent Spacing System

  • Generate random component trees with various nesting levels
  • Verify all spacing classes use design system tokens
  • Check responsive modifiers are present

Property 2: Social Links Open in New Tab

  • Generate random arrays of social links
  • Verify each link has target="_blank" and rel="noopener noreferrer"

Property 3: Responsive Component Layout

  • Generate random component configurations
  • Verify presence of responsive utility classes

Property 4: Reduced Motion Respect

  • Generate random animation configurations
  • Verify static rendering when reduced motion is true

Property 5: Experience Categorization

  • Generate random experience arrays with mixed types
  • Verify correct filtering and grouping by type

Property 6: Chronological Ordering Within Categories

  • Generate random experience arrays with various dates
  • Verify descending chronological order within each category

Property 7: Monument Extended Typography

  • Generate random heading elements
  • Verify font-display class is applied

Property 8: Slash Color Accents

  • Generate random accent elements
  • Verify var(--slash) color usage

Property 9: ARIA Labels for Links

  • Generate random social link arrays
  • Verify each has descriptive aria-label

Property 10: Alt Text for Images

  • Generate random logo arrays
  • Verify each image has non-empty alt text

Property 11: Semantic Heading Hierarchy

  • Generate random heading sequences
  • Verify proper h2 → h3 hierarchy without skips

Property 12: Keyboard Navigation Support

  • Generate random interactive elements
  • Verify focusability and proper tabindex values

Integration Testing

Page Layout Integration:

  • Verify CompanyLogos appears after Header
  • Verify Footer appears at bottom of page
  • Verify spacing between all sections is consistent
  • Test full page scroll behavior

Animation Performance:

  • Monitor CompanyLogos animation frame rate
  • Verify smooth scrolling across devices
  • Test reduced motion fallback behavior

Accessibility Testing:

  • Run axe-core automated accessibility tests
  • Verify keyboard navigation through all components
  • Test screen reader compatibility
  • Verify color contrast ratios meet WCAG AA standards

Visual Regression Testing

Snapshot Tests:

  • Footer component in all responsive states
  • CompanyLogos with animation and static states
  • Experience section with both categories populated
  • Experience section with one empty category

Cross-Browser Testing:

  • Chrome, Firefox, Safari, Edge
  • Mobile browsers (iOS Safari, Chrome Mobile)
  • Verify consistent rendering and animation performance