Files
portifolio-evandrodevbr/.kiro/specs/remove-preloader-adjust-hero/design.md
T
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

16 KiB

Design Document: Remove Preloader and Adjust Hero

Overview

This design addresses improvements to the initial user experience by removing the Preloader component and optimizing the Hero section for better readability and visual balance. The changes focus on immediate content access while maintaining the sophisticated animation system and responsive behavior.

Goals

  • Eliminate loading delay by removing the Preloader component entirely
  • Improve text readability by reducing Hero font sizes across all breakpoints
  • Enhance visual balance by adjusting Logo positioning and sizing
  • Preserve all existing animation timing and responsive behaviors
  • Maintain accessibility and reduced motion preferences

Non-Goals

  • Modifying the Hero typing animation logic or phase transitions
  • Changing the color scheme or design system variables
  • Altering other components beyond Page, Hero, and removing Preloader
  • Implementing new animation effects or transitions

Architecture

Component Structure

The implementation involves three main areas:

  1. Page Component (app/page.tsx)

    • Remove Preloader import and rendering
    • Remove body overflow manipulation side effects
    • Maintain existing component hierarchy for other sections
  2. Hero Component (app/components/Hero.tsx)

    • Adjust text size classes for all responsive breakpoints
    • Modify Logo container sizing and positioning
    • Preserve animation state machine and timing logic
  3. Preloader Component (app/components/Preloader.tsx)

    • Delete file entirely from codebase

Animation Flow

The Hero animation sequence remains unchanged:

typing → idle → glitch → logo → (loop back to typing)

Timing preserved:

  • Typing: 120ms per character
  • Idle pause: 1200ms
  • Glitch transition: 100ms
  • Glitch duration: 600ms
  • Logo display: 3500ms

Responsive Breakpoints

The design maintains Tailwind's standard breakpoints:

  • Mobile (default): < 640px
  • sm: ≥ 640px
  • md: ≥ 768px
  • lg: ≥ 1024px
  • xl: ≥ 1280px

Components and Interfaces

Page Component Changes

Current Implementation:

export default function Home() {
  return (
    <div className="flex min-h-screen flex-col overflow-x-hidden">
      <Preloader />
      <main className="ds-main">
        <Hero />
        {/* other components */}
      </main>
    </div>
  );
}

New Implementation:

export default function Home() {
  return (
    <div className="flex min-h-screen flex-col overflow-x-hidden">
      <main className="ds-main">
        <Hero />
        {/* other components */}
      </main>
    </div>
  );
}

Changes:

  • Remove import { Preloader } from "./components/Preloader";
  • Remove <Preloader /> from JSX
  • No other modifications needed

Hero Component Changes

Text Size Adjustments

Current Classes:

className="absolute leading-[0.9] tracking-[-0.01em] text-[13vw] sm:text-7xl md:text-[6rem] lg:text-[7rem] xl:text-[8rem]"

New Classes:

className="absolute leading-[0.9] tracking-[-0.01em] text-[10vw] sm:text-[4rem] md:text-[4.5rem] lg:text-[5rem] xl:text-[5.5rem]"

Size Comparison:

Breakpoint Current New Reduction
Mobile 13vw 10vw ~23%
sm 4.5rem (72px) 4rem (64px) ~11%
md 6rem (96px) 4.5rem (72px) ~25%
lg 7rem (112px) 5rem (80px) ~29%
xl 8rem (128px) 5.5rem (88px) ~31%

Logo Positioning Adjustments

Current Implementation:

<Logo
  variant="mark"
  size="xl"
  theme="dark"
  className="h-auto w-[80%] max-w-[320px] drop-shadow-[0_0_20px_rgba(204,255,0,0.34)]"
/>

New Implementation:

<Logo
  variant="mark"
  size="xl"
  theme="dark"
  className="h-auto w-[65%] max-w-[280px] drop-shadow-[0_0_20px_rgba(204,255,0,0.34)]"
/>

Changes:

  • Width: 80% → 65% (better proportion to reduced text)
  • Max-width: 320px → 280px (improved visual balance)
  • All other properties preserved (drop-shadow, theme, variant)

Container Height Adjustments

Current Implementation:

className="relative mb-sp-6 flex min-h-[40vw] items-center sm:min-h-[220px] md:min-h-[280px] lg:min-h-[350px]"

Recommended New Implementation:

className="relative mb-sp-6 flex min-h-[35vw] items-center sm:min-h-[200px] md:min-h-[240px] lg:min-h-[280px]"

Rationale:

  • Proportional reduction to match smaller text sizes
  • Maintains vertical rhythm and spacing
  • Prevents excessive whitespace around smaller text

Preloader Component Removal

Actions:

  1. Delete app/components/Preloader.tsx
  2. Remove all imports of Preloader from other files
  3. No replacement component needed

Side Effects Eliminated:

  • Body overflow manipulation (document.body.style.overflow = "hidden")
  • Window scroll reset (window.scrollTo(0, 0))
  • Progress tracking state and timers
  • AnimatePresence exit animations

Data Models

No data model changes required. This feature only modifies:

  • Component rendering logic
  • CSS classes for styling
  • File system structure (deletion)

State Management

Hero component state remains unchanged:

const [phase, setPhase] = useState<"typing" | "idle" | "glitch" | "logo">("typing");
const [typedCount, setTypedCount] = useState(0);
const [charStagger, setCharStagger] = useState(0.03);

No new state introduced. Preloader state is removed entirely.

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: Animation Phase Sequence Preservation

For any Hero component render, the animation phases should transition in the correct sequence: typing → idle → glitch → logo → (loop back to typing), with each phase completing before the next begins.

Validates: Requirements 4.1, 4.2

Property 2: Reduced Motion Adaptation

For any user with reduced motion preferences enabled, the Hero component should render with simplified animations that exclude blur and scale effects while maintaining opacity-based transitions.

Validates: Requirements 4.3, 4.4

Property 3: Responsive Character Stagger Timing

For any viewport width, the Hero component should apply character stagger timing of 0.015s for mobile viewports (< 768px) and 0.03s for desktop viewports (≥ 768px).

Validates: Requirements 5.3

Property 4: Responsive Text Sizing

For any viewport width, the Hero text should apply the appropriate font size class corresponding to the active breakpoint: 10vw (mobile), 4rem (sm), 4.5rem (md), 5rem (lg), or 5.5rem (xl).

Validates: Requirements 2.2, 2.3, 2.4, 2.5, 2.6

Error Handling

Component Rendering Errors

Scenario: Hero component fails to render due to missing dependencies or runtime errors.

Handling:

  • React error boundaries should catch rendering errors
  • Fallback UI should display gracefully
  • Console errors should provide clear debugging information

Implementation:

// No changes to error handling needed
// Existing React error boundary patterns apply

Animation State Errors

Scenario: Animation phase transitions fail or get stuck in an invalid state.

Handling:

  • useEffect cleanup functions prevent memory leaks from dangling timers
  • Phase state is constrained to valid values via TypeScript union type
  • Component remounts reset state to initial "typing" phase

Prevention:

// Existing cleanup in Hero component
useEffect(() => {
  let timeout: NodeJS.Timeout;
  // ... animation logic
  return () => clearTimeout(timeout); // Prevents memory leaks
}, [phase, typedCount]);

Media Query Errors

Scenario: window.matchMedia is unavailable or returns unexpected values.

Handling:

  • Default to desktop stagger timing (0.03s) if media query fails
  • useEffect cleanup removes event listeners properly
  • Component continues to function with default timing

Robustness:

// Existing implementation handles errors gracefully
const media = window.matchMedia("(max-width: 768px)");
const updateStagger = () => setCharStagger(media.matches ? 0.015 : 0.03);

Missing Component Errors

Scenario: Logo component import fails or is unavailable.

Handling:

  • TypeScript compilation will catch missing imports
  • Build process will fail before deployment
  • No runtime handling needed (build-time error)

Testing Strategy

Dual Testing Approach

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

  • Unit tests: Verify specific rendering outputs, CSS classes, and component structure
  • Property tests: Verify animation behavior, state transitions, and responsive adaptations across many inputs

Unit Testing

Focus Areas:

  1. Page component structure (Preloader removal)
  2. Hero text size classes at each breakpoint
  3. Logo sizing and positioning classes
  4. Animation timing constants
  5. Reduced motion configuration

Example Unit Tests:

describe('Page Component', () => {
  it('should not render Preloader component', () => {
    render(<Home />);
    expect(screen.queryByRole('img', { name: /Evandro Dev Logo/i })).not.toBeInTheDocument();
  });

  it('should render Hero component immediately', () => {
    render(<Home />);
    expect(screen.getByRole('region', { name: /home/i })).toBeInTheDocument();
  });

  it('should not set body overflow to hidden', () => {
    render(<Home />);
    expect(document.body.style.overflow).not.toBe('hidden');
  });
});

describe('Hero Text Sizing', () => {
  it('should apply reduced font sizes across all breakpoints', () => {
    render(<Hero />);
    const heading = screen.getByRole('heading', { level: 1 });
    const classes = heading.className;
    
    expect(classes).toContain('text-[10vw]');
    expect(classes).toContain('sm:text-[4rem]');
    expect(classes).toContain('md:text-[4.5rem]');
    expect(classes).toContain('lg:text-[5rem]');
    expect(classes).toContain('xl:text-[5.5rem]');
  });

  it('should maintain line-height and tracking', () => {
    render(<Hero />);
    const heading = screen.getByRole('heading', { level: 1 });
    const classes = heading.className;
    
    expect(classes).toContain('leading-[0.9]');
    expect(classes).toContain('tracking-[-0.01em]');
  });
});

describe('Logo Positioning', () => {
  it('should apply reduced width and max-width to Logo', () => {
    render(<Hero />);
    // Wait for logo phase
    act(() => {
      jest.advanceTimersByTime(5000);
    });
    
    const logo = screen.getByRole('img', { name: /Evandro mark logo/i });
    const classes = logo.className;
    
    expect(classes).toContain('w-[65%]');
    expect(classes).toContain('max-w-[280px]');
  });
});

Property-Based Testing

Library: fast-check (for TypeScript/JavaScript)

Configuration:

  • Minimum 100 iterations per property test
  • Each test tagged with feature name and property reference

Property Test 1: Animation Phase Sequence

import fc from 'fast-check';

describe('Property 1: Animation Phase Sequence Preservation', () => {
  it('should transition through phases in correct order', async () => {
    // Feature: remove-preloader-adjust-hero, Property 1: Animation phases should transition typing → idle → glitch → logo → typing
    
    await fc.assert(
      fc.asyncProperty(
        fc.nat(10), // Random starting iteration
        async (startIteration) => {
          const { result } = renderHook(() => {
            const [phase, setPhase] = useState<"typing" | "idle" | "glitch" | "logo">("typing");
            // ... hook implementation
            return phase;
          });
          
          const phases: string[] = [];
          
          // Capture phase transitions over time
          for (let i = 0; i < 4; i++) {
            phases.push(result.current);
            await act(async () => {
              jest.advanceTimersByTime(6000); // Full cycle time
            });
          }
          
          // Verify sequence pattern
          const expectedPattern = ["typing", "idle", "glitch", "logo"];
          expect(phases.slice(0, 4)).toEqual(expectedPattern);
        }
      ),
      { numRuns: 100 }
    );
  });
});

Property Test 2: Reduced Motion Adaptation

describe('Property 2: Reduced Motion Adaptation', () => {
  it('should simplify animations when reduced motion is preferred', async () => {
    // Feature: remove-preloader-adjust-hero, Property 2: Reduced motion should exclude blur and scale effects
    
    await fc.assert(
      fc.asyncProperty(
        fc.boolean(), // Random reduced motion preference
        async (shouldReduce) => {
          jest.mock('framer-motion', () => ({
            ...jest.requireActual('framer-motion'),
            useReducedMotion: () => shouldReduce,
          }));
          
          const { container } = render(<Hero />);
          const motionElements = container.querySelectorAll('[style*="filter"], [style*="scale"]');
          
          if (shouldReduce) {
            // Should not have blur or scale in inline styles
            motionElements.forEach(el => {
              const style = el.getAttribute('style') || '';
              expect(style).not.toContain('blur');
              expect(style).not.toContain('scale');
            });
          }
        }
      ),
      { numRuns: 100 }
    );
  });
});

Property Test 3: Responsive Character Stagger

describe('Property 3: Responsive Character Stagger Timing', () => {
  it('should apply correct stagger timing based on viewport width', async () => {
    // Feature: remove-preloader-adjust-hero, Property 3: Character stagger should be 0.015s mobile, 0.03s desktop
    
    await fc.assert(
      fc.asyncProperty(
        fc.integer({ min: 320, max: 1920 }), // Random viewport width
        async (viewportWidth) => {
          global.innerWidth = viewportWidth;
          
          const { result } = renderHook(() => {
            const [charStagger, setCharStagger] = useState(0.03);
            
            useEffect(() => {
              const media = window.matchMedia("(max-width: 768px)");
              const updateStagger = () => setCharStagger(media.matches ? 0.015 : 0.03);
              updateStagger();
              media.addEventListener("change", updateStagger);
              return () => media.removeEventListener("change", updateStagger);
            }, []);
            
            return charStagger;
          });
          
          const expectedStagger = viewportWidth <= 768 ? 0.015 : 0.03;
          expect(result.current).toBe(expectedStagger);
        }
      ),
      { numRuns: 100 }
    );
  });
});

Property Test 4: Responsive Text Sizing

describe('Property 4: Responsive Text Sizing', () => {
  it('should apply appropriate font size class for any viewport', async () => {
    // Feature: remove-preloader-adjust-hero, Property 4: Text size should match breakpoint
    
    await fc.assert(
      fc.asyncProperty(
        fc.integer({ min: 320, max: 1920 }), // Random viewport width
        async (viewportWidth) => {
          global.innerWidth = viewportWidth;
          
          render(<Hero />);
          const heading = screen.getByRole('heading', { level: 1 });
          const classes = heading.className;
          
          // All responsive classes should be present
          expect(classes).toContain('text-[10vw]');
          expect(classes).toContain('sm:text-[4rem]');
          expect(classes).toContain('md:text-[4.5rem]');
          expect(classes).toContain('lg:text-[5rem]');
          expect(classes).toContain('xl:text-[5.5rem]');
          
          // Tailwind will apply the correct one based on viewport
          // This verifies all breakpoint classes are present
        }
      ),
      { numRuns: 100 }
    );
  });
});

Test Coverage Goals

  • Unit Test Coverage: 100% of modified components (Page, Hero)
  • Property Test Coverage: All 4 correctness properties implemented
  • Integration Testing: Manual verification of visual appearance and animation smoothness
  • Accessibility Testing: Verify reduced motion preferences work correctly

Testing Tools

  • Unit Testing: Jest + React Testing Library
  • Property Testing: fast-check
  • Visual Testing: Manual review in browser at all breakpoints
  • Accessibility: Manual testing with reduced motion system preference