Production-grade UI components built with React 18 and TypeScript — each one a rewrite of a Vanilla JS component originally built for enterprise clients in Portugal (Banco BPI, Ericsson, Fidelidade).
The goal was not to recreate the DOM manipulation logic, but to translate the intent of each component into idiomatic React: state instead of class mutation, hooks instead of prototype methods, declarative rendering instead of imperative DOM queries.
Responsive panel that switches between a side drawer on desktop and a swipeable bottom sheet on mobile.
ResizeObserverdrives the layout switch — no media query breakpoints- Touch gesture handling with snap threshold: only dismisses if the user swipes past ⅔ of the height
- Entry animation uses
requestAnimationFrameto separate mount from the open class, so the CSS transition always fires correctly - Body scroll lock synced with component state via
useEffect - Closes on ESC, overlay click, or swipe
Hooks: useDrawerPosition · useDrawerInitialHeight · useBodyScrollLock · useDrawerTouch
Horizontal card slider with drag-to-scroll and animated arrow navigation.
- Drag state (
startX,scrollLeft) stored inuseRef— persists between events without triggering re-renders - Arrow scroll uses
setIntervalwith configurablespeed,distanceandstep— same animation model as the original - Arrow disabled state derived from scroll position on each scroll event
- Responsive item count (4 / 3 / 2) driven by
ResizeObserveron the body
Hooks: useCardSliderDrag · useCardSliderScroll · useCardSliderItems
4-digit PIN input with per-field focus management, digit masking, and keyboard navigation.
- Each field is an independent controlled input — value array stored in state
- Digits are masked with
•after 100ms (same timing as original) - Backspace clears the current field if filled, or moves focus to the previous field if empty
- Arrow keys navigate between fields
onCompletefires when all fields are filled — supports async validation flows
Floating content panel anchored to a trigger button, rendered via React portal.
- Uses
createPortalto render at body level — never clipped byoverflow: hiddenancestors - Position calculated from
getBoundingClientRecton each open, scroll, and resize - Width adapts to available viewport space on smaller screens, with the arrow always pointing to the trigger center
- Multiple instances share a
PopoverContext— opening one closes all others (replaces the staticinstancesarray from the original) - Closes on ESC, outside click, or interaction with any link/button inside
Hooks: usePopoverPosition · PopoverContext
Two-level navigation dropdown with active state propagation.
- Active state derived from
defaultActiveIdprop and propagated up the tree declaratively — no DOM traversal - Only one submenu open at a time — controlled by a single
openSubmenuIdstate - Submenu opens to the right of the parent item
- Closes on ESC or outside click
Image gallery with thumbnail navigation and a full-screen lightbox, using render props for maximum flexibility.
- Render props pattern — allows custom rendering for both main images and thumbnails while keeping navigation logic internal
- Lightbox supports keyboard navigation (←, →, ESC) and touch gestures
- Thumbnail synchronization — the thumbnail strip auto-scrolls to keep the active image in view
- Clean view — toggleable UI in lightbox to focus entirely on the content
- Drag-to-scroll on thumbnail strips using
useReffor performance
Hooks: useGallery · useLightboxGallery · useTouch
Set of components for user engagement (Likes and Ratings) featuring optimistic UI patterns.
- Optimistic Updates — state updates immediately on user click, with automatic rollback if the simulated API call fails
- Persistence — uses
localStorageto remember user's choice and prevent double-voting without a backend - SEO Ready —
PostRatinggenerates JSON-LDaggregateRatingschema for Google search snippets - Accessibility — full ARIA support (
aria-pressed,aria-live) and keyboard navigation for the rating stars - Micro-animations: SVG sparkle effects on like and smooth star transitions
Lightweight loading placeholders with CSS-only shimmer animations.
- Pure CSS animations — no heavy JS-driven animation libraries
- Composable — can be used to build complex loading layouts (cards, lists, profiles)
- Configurable dimensions and border-radius via props
Visual indicator of reading progress that tracks scroll position relative to the article content.
- Targeted Tracking — tracks progress within a specific
refcontainer, not just the entire page - Performance Optimized — passive scroll listeners with window-relative calculations
- Sticky positioning at the top of the viewport
- Uses CSS
color-mixand gradients for a premium feel
State as source of truth — the original components used MutationObserver to react to class changes on DOM elements. Here, props and state drive everything. React re-renders replace the observer pattern.
useRef for mutable values that don't affect rendering — drag coordinates, touch positions, timer IDs. The direct equivalent of this.variables and this.settings in the class-based originals.
useEffect cleanup as destroy() — every effect that registers a listener returns a cleanup function. No manual removeEventListener calls scattered across the codebase.
Hooks as a decomposition tool — each hook maps to a single responsibility from the original class. The component file stays readable; the logic is testable in isolation.
React 18 · TypeScript · Vite · CSS (no animation libraries)
npm install
npm run dev