- 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.
502 lines
17 KiB
Markdown
502 lines
17 KiB
Markdown
# 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
|
|
|
|
### 1. Footer Component
|
|
|
|
**Purpose**: Display contact information, social links, and copyright in a consistent footer layout.
|
|
|
|
**Props**: None (self-contained component)
|
|
|
|
**Structure**:
|
|
```tsx
|
|
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**:
|
|
```tsx
|
|
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**:
|
|
```tsx
|
|
// 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**:
|
|
```tsx
|
|
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**:
|
|
```tsx
|
|
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
|
|
|
|
### SocialLink
|
|
```typescript
|
|
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
|
|
```typescript
|
|
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)
|
|
```typescript
|
|
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 `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
|