Feat/liquidglass - #4
Merged
Merged
Conversation
Each domain (project, experience, about, contact) gets its own module file. src/di/index.ts re-exports all controllers so existing imports are unchanged.
Introduces ViewModel types and presenter functions for all four domains (about, contact, experience, project). Controllers now map entities to ViewModels via presenters before returning data. UI components import ViewModel types from the presenter layer instead of directly from entities, closing the last boundary gap in the clean architecture.
…ve max-width constraint
…emantic structure and update line wrapping behavior
…rendering and updated spacing
…clamping to project card content
…ns in a new ProjectList component
Replace 3-column grid with horizontal scroll gallery: caption above image tile, h-64 hero with hover zoom, paddle nav below (left/right chevron buttons), snap-scroll always on with no-scrollbar utility. Right button disables after one click since the third card is already visible as a peek at 44% card width.
Move shine element back into JSX (was a module-level string constant that Tailwind JIT could miss). Add pointer-events-none and aria-hidden. Inner span now flex items-center gap-2 so icons stay inline with label text.
Replace plain <a> download button with TactileButton for tap/shimmer animation. Extract ReactMarkdown + remark-gfm into CvMarkdown component loaded via next/dynamic, keeping them out of the initial bundle.
Add cache()-wrapped getProjects, getExperiences, getContact alongside their controllers, matching the existing getAbout pattern. Prevents duplicate data fetches if multiple RSCs ever call the same controller.
Replace static icon with an Apple-style animated hamburger that morphs to X via SMIL <animate> elements driven by beginElement() calls. Add slide-down AnimatePresence mobile menu, auto-close on desktop resize, and accessibility attributes (aria-expanded, aria-label).
Replace vertical alternating timeline with a horizontal scroll gallery matching the projects pattern. Circle nodes sit on a continuous h-1 timeline rail; a vertical stem connects each node to a glass card below. Cards scroll with snap, paddle nav below, IntersectionObserver-driven left/right enable state, equal closed heights via JS measurement after animation settles (350ms), and closed cards hold their height when one expands.
Switch container to items-start; equalize closed card heights via JS measurement after animation settles (350ms) so cards start short and grow only when opened. Equalize caption heights via useLayoutEffect so images align across cards. Read more/less button right-aligned with self-end and scrolls card into view on expand.
Replace flat gray values with dual-mode zinc classes on card descriptions, company names, and toggle buttons. Update experience color fields in data to use darker variants on light backgrounds.
Adds a keydown listener that fires only while the dialog is open, satisfying the ARIA dialog pattern for keyboard users.
…ar highlight Replace full-width edge-to-edge bar with a floating rounded pill (max-w-3xl, rounded-2xl, pt-4 offset). Upgrade glass properties: bg-white/70 light / bg-zinc-900/50 dark, backdrop-blur-2xl, specular inner ring, soft shadow. Soften mobile menu divider to border-white/30.
Increase opacity to 50%/40%, blur to backdrop-blur-2xl, add specular inner ring, and replace harsh shadow-2xl with a soft diffuse shadow. Fixes ExperienceCard and BackToTop which inherit this class.
Replace opaque zinc background with translucent glass surface (bg-white/40 light / bg-zinc-900/40 dark, backdrop-blur-md, glass border) so the toggle blends with the floating navbar pill it lives inside.
The extra border-zinc-200/border-zinc-700 classes were overriding glass-card's translucent border with opaque zinc. Removing them lets the updated glass-card utility render correctly.
Backdrop: bg-black/40 + backdrop-blur-xl (was blur-sm). Dialog surface: bg-white/80 dark:bg-zinc-900/70 + backdrop-blur-2xl, specular ring, soft shadow. Header/footer dividers softened to border-white/30 dark:border-white/8.
…ar ring Replace opaque zinc backgrounds with translucent glass (bg-white/60 light / bg-zinc-900/50 dark, backdrop-blur-md, specular inner ring). Soften hover border to accent/50 to avoid clashing with glass edges.
… CTAs Replace invisible border-transparent with a glass surface: bg-white/20 light / bg-zinc-900/20 dark, backdrop-blur-sm, translucent border. Applied to Contact Me (hero) and View CV (cv-dialog trigger).
Challenge block: bg-white/50 light / bg-white/5 dark, backdrop-blur-sm, translucent border. Tech stack badges: same glass treatment replacing opaque zinc-200/zinc-800 surfaces.
…go circles Replace opaque bg-white/bg-zinc-900 with translucent glass surface (bg-white/70 light / bg-zinc-900/60 dark, backdrop-blur-sm, glass border).
…ark surface Add backdrop-blur-sm, translucent border, and specular inner ring to the Swift hobbies code block without compromising code readability.
Saturation boost is a core Liquid Glass property — the glass enriches the color of content behind it rather than washing it out. Applied to: glass-card utility, navbar pill, ThemeToggle, CvDialog (backdrop + surface + trigger), contact circles, hero ghost buttons, ProjectCard challenge block and badges, ExperienceCard timeline nodes, MoreAbout code snippet.
Defines a hidden feTurbulence+feDisplacementMap SVG filter in layout.tsx (scale=6, fractalNoise baseFrequency=0.012) that bends background content at glass edges, recreating the refraction in Apple's Liquid Glass. Applied via filter:url(#liquid-glass-distort) to glass-card utility and the navbar pill. Chromium-only; degrades gracefully in Firefox/Safari.
Adds useGlassTilt hook that tracks cursor position within a glass element and drives a spring-animated rotateX/rotateY transform (max ~4deg), giving the elastic deformation response from Apple's Liquid Glass. Applied to the navbar pill (intensity 0.8) and ExperienceCard glass surface (intensity 0.6).
feDisplacementMap applies to every pixel of the element including text, not just the backdrop layer. At any useful scale the warping is visible on nav links and card body text. The filter needs a separate background layer architecture to isolate content from distortion — removed for now.
… glass year badge Timeline line and vertical stem: h-px + bg-white/15 (was thick zinc bar). Year badge: glass surface with backdrop-blur-sm and translucent border replacing opaque bg-zinc-800.
Adds a CSS mask-image gradient (82% opaque → transparent) on the scroll container so the peeking next card fades out gracefully. Tied to canScrollRight so the fade removes itself when the last card is reached.
React does not auto-prefix mask-image, so the fade was invisible on iOS/Safari. Adding WebkitMaskImage alongside maskImage fixes it.
…lass styling The mask (70%→90%) was fading the entire hint area, so the peeking card appeared as a dark smear with no visible border or glass surface. Removing the mask lets the next card's glass-card styling (border, ring, rounded corners) show naturally through the scroll overflow clip.
…-on-white bg-white/60 + border-white/50 + ring-white/30 were invisible on the white light background. Replaced with bg-zinc-100/80 + border-zinc-200 + ring-zinc-100 for light mode; dark mode values unchanged.
…hite bg-white/50 + border-white/50 + ring-white/30 were invisible on white backgrounds. Replaced with bg-zinc-100/80 + border-zinc-200 + ring-zinc-100 for light mode; dark mode values unchanged. Fixes BackToTop and ExperienceCard.
Section vertical padding py-16→py-8 (64px→32px) on all non-hero sections so the border divider has 32px above and below instead of 64px. Section heading margins mb-16→mb-8 and mb-12→mb-8 for consistency. Remove leftover w-6 hint spacer from ProjectList.
…ards Title (h3) is the only variable-height element (can wrap to 2 lines). Mark it with data-exp-title and add a useLayoutEffect in ExperienceList that resets and re-measures all titles to the tallest one before paint, matching the same pattern ProjectList uses for data-caption. Once titles are aligned, year/company/description fall into place naturally.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Liquid Glass redesign
Applies Apple's Liquid Glass design language across the entire site — every surface that overlays content is now a translucent, frosted material rather than an opaque panel.
What changed
Navbar
Replaced the full-width edge-to-edge bar with a floating rounded-2xl pill (max-w-3xl, pt-4 offset from screen edge). Glass properties: backdrop-blur-2xl, backdrop-saturate-150, bg-white/70 light / bg-zinc-900/50 dark, specular inner ring, soft shadow. The pill tilts in 3D toward the cursor via a spring-animated useGlassTilt hook.
glass-card utility
Upgraded globally — stronger blur, visible tint, specular ring, soft shadow. Fixes ExperienceCard and BackToTop at once. Light-mode values use zinc-100/zinc-200 so the surface is visible on white backgrounds.
All glass surfaces get backdrop-saturate-150
The saturation boost is a core Liquid Glass property — it enriches the color of content behind the glass rather than washing it out. Applied to every component with a backdrop-filter.
Mouse-reactive 3D tilt
useGlassTilt hook tracks cursor position within a glass element and drives rotateX/rotateY spring animations (max ~4°). Applied to the navbar pill and ExperienceCard glass surface.
Component-by-component glass treatment
ThemeToggle, CvDialog (backdrop + surface + trigger), contact icon circles, hero ghost buttons, ProjectCard challenge block and tech badges, ExperienceCard timeline nodes and year badge, MoreAbout code snippet — all converted from opaque zinc surfaces to translucent glass.
ExperienceCard timeline
Timeline line and vertical stem thinned to h-px and softened to bg-white/15. Year badge converted to glass.
Spacing
Section vertical padding halved (py-16 → py-8) and heading margins normalised (mb-16/mb-12 → mb-8) for tighter, more consistent rhythm between sections.
Experience card alignment
data-exp-title + useLayoutEffect in ExperienceList equalises title heights before paint, so year / title / company / description stay vertically aligned across all cards regardless of title line count — matching the same pattern ProjectList uses for captions.
What was tried and reverted
SVG feTurbulence + feDisplacementMap filter for edge refraction was implemented but reverted — filter applies to the element's full render including text, so at any useful scale text content visibly warped.