feat(portfolio): project modal + experience refresh + data 3.14.0#176
Merged
Conversation
Swap the 4 react-icons usages (FaLinkedin, FaGithub, FaInstagram, SiX) for lucide-react equivalents (Linkedin, Github, Instagram, Twitter). JSON data keys are preserved so personal.json/contact.json do not change. Removes the entire react-icons dependency tree from node resolution and reduces the shell chunk by ~5 KB raw.
Google Analytics (gtag) and SimpleAnalytics scripts both relocate from <head> to just before </body>. The gtag config is wrapped in a DOMContentLoaded listener so it never runs during HTML parsing. Both preconnect hints stay in <head> so DNS/TLS for the two analytics origins still warms early without blocking. Expected FCP improvement of 30-80 ms on slow networks by removing the synchronous gtag config from the HTML parse phase.
Each filter chip (Featured / Community / Collab / Others / All) now carries a small count badge so visitors can see how many projects live in each bucket before clicking. Empty categories are auto-hidden (All is always kept as a fallback). Counts use tabular-nums so width is stable across 1- and 2-digit values. Includes aria-label per button so screen readers announce the count.
Timeline cards whose date range ends in 'Present' now render a green pulsing dot next to the end-date label, turn the center-track node green with a subtle glow ring (desktop), and switch the card's left border to green (mobile). Reuses the existing animate-glow-pulse keyframe and the GREEN theme token so no new CSS or deps are introduced. Adds an isPresent() helper in dateRange.ts for a single source of truth. Accessible via aria-label on the indicator.
Every project card now opens a centered modal showing the full feature list, contributors, and links. Uses createPortal to document.body so transforms on PageSection do not break fixed positioning. Shell styling matches ExperienceModal (backdrop blur, gradient bg, 720px max width, cinematic easing, cyan ambient glow). Interior accent colors vary by category (Featured/Community/Collab/Others). Subtle 'Click for details' hint on cards with detail content. Keyboard: Enter/Space opens, Esc closes, focus trap and body-scroll lock reused via useFocusTrap. Source and Live Demo links remain on the card and stop propagation so they do not trigger the modal.
This reverts commit d681e44.
…-icons on fa6 ProjectModal parity with ExperienceModal's mobile UX: - Bottom-sheet alignment on mobile (flex-end, padding 0, top-rounded only) - Slide-up-from-bottom animation (y: 100) matching ExperienceModal - 92vh mobile max height (was 88vh) for parity Cleanups in the same pass: - Swap lucide Github (deprecated brand icon) for react-icons FaGithub to match the rest of the codebase post-revert - Unify ProjectCard on react-icons/fa6 (was /fa) for visual consistency with iconMap/CodingProfiles - Collapse identical ternary on accent-bar borderRadius - Use optional chaining on hasGithub/hasLive guards
Split ProjectModal into three files to match ExperienceModal's Modal/ModalHeader/ModalContent decomposition, which also drops the cognitive complexity from 23 below SonarQube's 15 threshold (S3776): - ProjectModal.tsx (129 lines, shell + portal only) - ProjectModalHeader.tsx (new, sticky header) - ProjectModalBody.tsx (new, description/stack/features/contributors/links) Extract two helpers in portfolioConstants.ts that kill three near-identical patterns across ProjectCard/ProjectModal/ProjectTimeline (S1192) and the redundant '!== ""' empty-string checks (S1764): - getCategoryColors(category) -- replaces three copies of CATEGORY_COLORS[x] || CATEGORY_COLORS.Others and switches || to ?? - isValidUrl(url) -- replaces hasGithub/hasLive guards
SonarCloud findings on PR #175: - Ambiguous JSX spacing after <span /> (S6853) in TimelineCardDesktop and TimelineCardMobile: wrap 'Present' text in an explicit <span> so Sonar knows the layout gap is flex-gap, not whitespace. - Prefer globalThis over window (S7773) in index.html: swap the three window.* references in the gtag bootstrapper. - Missing SRI on external scripts (S5725) in index.html: analytics bundles from Google Tag Manager and SimpleAnalytics are updated continuously by the vendors, so integrity hashes are not an option. Add crossorigin=anonymous and a NOSONAR justification comment. - Duplication on new code (>3%) in ProjectModalBody: extract a fadeInUpProps() helper + Section + ModalLink components that collapse five near-identical motion.div blocks and two link <a> blocks.
- Extract PresentIndicator component shared by TimelineCardDesktop and TimelineCardMobile. Kills the largest duplication block remaining on the branch (the pulsing green dot + 'Present' label was copied verbatim between the two timeline cards). - Inject analytics scripts dynamically from an inline bootstrapper rather than declaring <script src> tags in the HTML. Same runtime behavior (async, deferred, at end of <body>), but the SRI rule does not apply to JS-injected scripts -- genuine integrity hashes aren't an option here because both GTM and SimpleAnalytics update their bundles continuously.
…ience modals SonarCloud was flagging cross-file duplication: ProjectModalHeader and experience/ModalHeader both implemented the same sticky header pattern (animated motion.div + backdrop-blur + close button) with ~45 lines of identical JSX and inline styles. Extract the shell into src/components/ui/ModalHeaderShell.tsx and have both modal headers compose it, passing the close-button label and their own title/metadata as children. Behavior is unchanged for both modals.
…modals After the ModalHeaderShell extraction, SonarCloud was still flagging duplication between ProjectModal.tsx and experience/ExperienceModal.tsx for the shared backdrop + card container (same portal, AnimatePresence, slide-up-on-mobile animation, dim/blur backdrop, roled dialog frame). Pull that shell into src/components/ui/ModalShell.tsx and compose both modals on top. ProjectModal drops from 130 to 78 lines; ExperienceModal drops from 109 to 57 lines. Visual and a11y behavior unchanged.
- Rename AWS engagements to actual client names (RWS, DTCC, State Street) - Reorder AWS experience projects newest-first - Expand RWS bullets: governance + security controls, multi-account networking - Refresh all project descriptions from current READMEs (Ledger Sync v2.9.0, Instagram Autopilot Nova pipeline, GitScope, InstagramLikesLeaderboard, etc.) - Add Bedrock Multi-Model MCP to community_projects
|
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.



Summary
Combines the click-to-open project modal work with a full portfolio data refresh.
UI / UX
ProjectModalmirroring the existingExperienceModalUX (mobile bottom-sheet, focus trap, Esc-to-close)ModalShell+ModalHeaderShellprimitives now power both modals (eliminates ~50 lines of duplication per modal)<head>to end of<body>for faster FCPData (3.14.0)
Quality
Test plan