💡 What: Added ARIA roles to form status messages and hid decorative icons.
🎯 Why: Screen readers were missing dynamic success/error messages on form submission.
♿ Accessibility:
- Added `role="alert"` and `aria-live="polite"` to success, error, and rate-limit messages.
- Added `aria-hidden="true"` to decorative icons in the contact info section.
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
- Move scroll position and bounding client rect calculations from `pointermove` handler to `requestAnimationFrame` loop.
- Use `pointerPosRef` to store raw event coordinates, reducing overhead in high-frequency event handlers.
- Ensure spotlight effect correctly accounts for scroll position updates during animation frames.
- Add regression test to verify `scrollX` is not accessed during pointer events.
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
- Add visual asterisk for required inputs in Input.tsx
- Add .required style in Input.module.css
- Update Contact form to use required props and noValidate
- Add role="alert" to Contact form success/error messages
- Add tests for required field rendering
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
Extracted the high-frequency typing animation logic into a new, memoized `TypedText` component.
This prevents the entire `Hero` component (including the heavy `GradientBlinds`) from re-rendering on every character update.
- Created `TypedText` component in `Hero.tsx`
- Wrapped `TypedText` in `React.memo`
- Moved `useTypingEffect` call into `TypedText`
- Updated `Hero` to use `TypedText`
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
Added Strict-Transport-Security (HSTS) and Permissions-Policy headers to firebase.json to improve security posture.
- HSTS ensures browsers only connect via HTTPS for 1 year.
- Permissions-Policy restricts usage of sensitive features (camera, mic, geolocation).
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
- Implemented `isValidEmail` utility with strict regex validation (rejects `<` and `>`) to prevent XSS vectors.
- Updated `Contact.tsx` to use `isValidEmail` instead of weak regex.
- Added comprehensive tests for `isValidEmail` in `src/utils/security.test.ts`.
- Fixed flaky test in `src/pages/__tests__/Contact.test.tsx` by clearing `localStorage` in `afterEach`.
- Added test case for invalid email submission.
- Documented findings in `.jules/sentinel.md`.
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
- Add `aria-busy` attribute to Button when loading.
- Refactor Button rendering to keep children in DOM (visually hidden) instead of unmounting.
- Fix layout shift regression by replicating flex properties in content wrapper.
- Move inline styles to CSS modules.
- Add tests for loading state accessibility.
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
- Added `useRateLimit` hook
- Integrated hook into `Contact.tsx`
- Added translations for rate limit error
- Added unit tests
- Fixed type error in `Button.tsx` to allow build to pass
- Replaces the `cancelAnimationFrame` pattern with a boolean ticking flag to reduce function allocation and RAF overhead on high-frequency mousemove events.
- Uses closure variables for coordinates to ensure the latest position is used in the animation frame.
- Improves performance of the custom cursor on high-refresh-rate input devices.
Refactored the `Button` component to accept and spread standard HTML button attributes.
This allows developers to add `aria-label`, `aria-expanded`, and other accessibility properties,
which were previously ignored.
Added unit tests to verify that attributes are correctly passed to the DOM element.
Added strict security headers to `firebase.json` for Firebase Hosting.
Headers included:
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- Referrer-Policy: strict-origin-when-cross-origin
- Content-Security-Policy: Includes directives for 'self', Google Fonts, EmailJS, and disallows object/frame embedding.
Also initialized `.jules/sentinel.md` with the first security learning.
Add `aria-invalid` and `aria-describedby` attributes to Input and Textarea components when an error is present.
This ensures screen readers announce the validation error when the input is focused.
Also added unit tests for these accessibility attributes.
* 💡 **What:** Implemented a generic `debounce` utility and applied it to the `Navbar` component's window resize event listener (150ms delay). Added a `.cancel()` method to the debounce utility to prevent memory leaks/errors on unmount.
* 🎯 **Why:** The `resize` event fires rapidly, causing `getBoundingClientRect` (a layout-thrashing operation) to run excessively, impacting performance.
* 📊 **Measured Improvement:** In a benchmark test simulating 100 rapid resize events:
* **Baseline:** 200 calls to `getBoundingClientRect`.
* **Optimized:** 2 calls to `getBoundingClientRect`.
* **Result:** ~99% reduction in layout calculations during rapid resizing.
* Added `src/components/layout/Navbar.perf.test.tsx` to prevent regression.
Added `sanitizeInput` utility to escape HTML characters.
Updated `Contact.tsx` to sanitize inputs before sending via `emailjs`.
Added max length validation for Name (100), Subject (200), and Message (5000).
Updated tests to cover sanitization and validation logic, including adding `cleanup()` to prevent test leakage.
- Removed synchronous `getBoundingClientRect` call from `pointermove` handler
- Implemented caching of canvas dimensions and scroll position using `ResizeObserver`
- Added logic to compensate for scroll delta in pointer position calculation
- Added regression test to verify `getBoundingClientRect` is not called during pointer interaction
- Fixed `ogl` mock in tests to include `Geometry`