- 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.
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:
- Spacing System Standardization: Applying consistent spacing tokens across all sections
- Footer Component: A new footer with contact info, social links, and copyright
- CompanyLogos Component: An infinite-scroll animated banner displaying company logos
- 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
- Footer Component: Static data (email, social links) defined as constants within the component
- CompanyLogos Component: Logo data (company names, image paths) defined as constants, rendered with animation
- 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
animatewith infinite loop - Footer: Subtle hover states on social links
- Experience: Existing stagger animations maintained, category headers added
Components and Interfaces
1. Footer Component
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)withborder-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-hiddenwithborder-b: 1px solid var(--border) - Background:
var(--paper)with optional subtle grid pattern - Logo sizing:
h-12(48px) withgrayscalefilter,grayscale-0on hover - Spacing:
py-sp-8vertical padding,gap-sp-12between logos
3. Experience Component Refactor
Purpose: Organize experience entries into Software and Hardware categories.
Changes:
- Add category filtering logic
- Render two separate sections with category headers
- 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-12between 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-12between major sections - Card spacing:
space-y-sp-6for vertical stacks - Grid gaps:
gap-sp-6for 2-column,gap-sp-4for 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
SocialLink
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
}
CompanyLogo
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
Property 2: Social Links Open in New Tab
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
Property 9: ARIA Labels for Links
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
Footer Component
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
useAnimationFramehook - Fallback to static display if performance drops below threshold
- Respect
prefers-reduced-motionpreference
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