Contact link in header #3

Closed
opened 2026-05-24 16:51:11 +00:00 by chaulmark · 8 comments
chaulmark commented 2026-05-24 16:51:11 +00:00 (Migrated from gitea.sigd.net)

I need to add to the header nav the contact link that goes to the same link as what is in the footer.

I need to add to the header nav the contact link that goes to the same link as what is in the footer.
friday-bot commented 2026-05-24 16:52:03 +00:00 (Migrated from gitea.sigd.net)

Plan

The footer Contact opens a modal (via ContactModal component) — it is not a page link. To match that behavior in the header:

Changes to src/components/layout/Header.tsx

  1. Import ContactModal from ../shared/ContactModal
  2. Add useState for isContactModalOpen (already imports useState)
  3. Add a Contact button to the desktop nav — styled identically to the other nav links (with the animated underline gradient on hover)
  4. Add a matching Contact entry to the mobile nav menu — same modal trigger
  5. Render <ContactModal> at the bottom of the header component

No new pages or routes needed. The modal handles the form submission just as it does in the footer.

Please reply with approved to proceed, or let me know if you want a different approach (e.g., a /contact page instead of a modal).

## Plan The footer Contact opens a **modal** (via `ContactModal` component) — it is not a page link. To match that behavior in the header: ### Changes to `src/components/layout/Header.tsx` 1. Import `ContactModal` from `../shared/ContactModal` 2. Add `useState` for `isContactModalOpen` (already imports `useState`) 3. Add a **Contact** button to the desktop nav — styled identically to the other nav links (with the animated underline gradient on hover) 4. Add a matching **Contact** entry to the mobile nav menu — same modal trigger 5. Render `<ContactModal>` at the bottom of the header component No new pages or routes needed. The modal handles the form submission just as it does in the footer. Please reply with **approved** to proceed, or let me know if you want a different approach (e.g., a `/contact` page instead of a modal).
chaulmark commented 2026-05-24 16:53:08 +00:00 (Migrated from gitea.sigd.net)

Approved.

Approved.
friday-bot commented 2026-05-24 16:55:45 +00:00 (Migrated from gitea.sigd.net)

Done

Deployed commit 1c79957 — Contact link added to the header nav (desktop and mobile), triggering the same ContactModal used in the footer.

Changes made to src/components/layout/Header.tsx:

  • Imported ContactModal
  • Added isContactModalOpen state
  • Added Contact button to desktop nav with matching animated gradient underline on hover
  • Added Contact entry to mobile slide-out menu
  • Rendered <ContactModal> at the bottom of the header component

Site verified healthy at http://localhost:803/ after deploy.

## Done Deployed commit `1c79957` — Contact link added to the header nav (desktop and mobile), triggering the same ContactModal used in the footer. **Changes made to `src/components/layout/Header.tsx`:** - Imported `ContactModal` - Added `isContactModalOpen` state - Added **Contact** button to desktop nav with matching animated gradient underline on hover - Added **Contact** entry to mobile slide-out menu - Rendered `<ContactModal>` at the bottom of the header component Site verified healthy at http://localhost:803/ after deploy.
chaulmark commented 2026-05-24 16:58:41 +00:00 (Migrated from gitea.sigd.net)

Good job but it has a bug. After I click contact at top right corner of the website-- the nav header, it pops up that takes up top half of the screen-- the contact popup needs to be moved down to be in the middle of the webpage. See the attached screenshot.

Good job but it has a bug. After I click contact at top right corner of the website-- the nav header, it pops up that takes up top half of the screen-- the contact popup needs to be moved down to be in the middle of the webpage. See the attached screenshot.
friday-bot commented 2026-05-24 16:59:42 +00:00 (Migrated from gitea.sigd.net)

Bug Analysis

The modal appears at the top of the screen because <ContactModal> is rendered as a child of <motion.header>. Framer Motion applies CSS transform to animate the header sliding in — and when a parent element has an active CSS transform, any position: fixed children are positioned relative to that transformed ancestor, not the viewport. So the modal backdrop only covers the header area.

Plan

Modify src/components/shared/ContactModal.tsx to render the modal overlay via ReactDOM.createPortal into document.body. This makes the modal escape any stacking context no matter where in the component tree it is placed, so it always covers the full viewport and centers correctly.

Change: Wrap the <AnimatePresence> block in a portal — ReactDOM.createPortal(content, document.body) — guarded by a mounted state (required for Next.js SSR safety).

No changes needed to Header.tsx or Footer.tsx. The Footer modal will also benefit from this fix.

Please reply with approved to proceed, or let me know if you prefer a different approach.

## Bug Analysis The modal appears at the top of the screen because `<ContactModal>` is rendered as a child of `<motion.header>`. Framer Motion applies CSS `transform` to animate the header sliding in — and when a parent element has an active CSS transform, any `position: fixed` children are positioned *relative to that transformed ancestor*, not the viewport. So the modal backdrop only covers the header area. ## Plan Modify `src/components/shared/ContactModal.tsx` to render the modal overlay via **`ReactDOM.createPortal`** into `document.body`. This makes the modal escape any stacking context no matter where in the component tree it is placed, so it always covers the full viewport and centers correctly. **Change:** Wrap the `<AnimatePresence>` block in a portal — `ReactDOM.createPortal(content, document.body)` — guarded by a `mounted` state (required for Next.js SSR safety). No changes needed to `Header.tsx` or `Footer.tsx`. The Footer modal will also benefit from this fix. Please reply with **approved** to proceed, or let me know if you prefer a different approach.
friday-bot commented 2026-05-24 16:59:47 +00:00 (Migrated from gitea.sigd.net)

Revised Plan — Fix Modal Centering

Root cause: The ContactModal is rendered inside motion.header, which applies a CSS transform (the slide-in animation: y: -100 to y: 0). CSS transforms create a new containing block for fixed-positioned descendants, so the modal ends up anchored to the header instead of the full viewport — explaining why it appears only in the top half of the screen.

Fix: Use a React portal in ContactModal.tsx to render the modal directly at document.body, bypassing the header transform context. No changes to Header.tsx needed.

Change to src/components/shared/ContactModal.tsx

  1. Add createPortal from react-dom and useEffect to imports
  2. Add a mounted state (set to true in useEffect) to avoid SSR hydration mismatch
  3. Wrap the AnimatePresence block in createPortal(..., document.body)
  4. Return null until mounted

This ensures the modal backdrop and dialog render at the top of the DOM tree, centered in the full viewport regardless of where in the component tree the modal is placed.

Please reply approved to proceed.

## Revised Plan — Fix Modal Centering **Root cause:** The ContactModal is rendered inside motion.header, which applies a CSS transform (the slide-in animation: y: -100 to y: 0). CSS transforms create a new containing block for fixed-positioned descendants, so the modal ends up anchored to the header instead of the full viewport — explaining why it appears only in the top half of the screen. **Fix:** Use a React portal in ContactModal.tsx to render the modal directly at document.body, bypassing the header transform context. No changes to Header.tsx needed. ### Change to src/components/shared/ContactModal.tsx 1. Add createPortal from react-dom and useEffect to imports 2. Add a mounted state (set to true in useEffect) to avoid SSR hydration mismatch 3. Wrap the AnimatePresence block in createPortal(..., document.body) 4. Return null until mounted This ensures the modal backdrop and dialog render at the top of the DOM tree, centered in the full viewport regardless of where in the component tree the modal is placed. Please reply **approved** to proceed.
chaulmark commented 2026-05-24 17:01:42 +00:00 (Migrated from gitea.sigd.net)

Approved. Just make sure the contact link at the bottom is also having the modal centerized in the middle as well.

Approved. Just make sure the contact link at the bottom is also having the modal centerized in the middle as well.
friday-bot commented 2026-05-24 17:04:31 +00:00 (Migrated from gitea.sigd.net)

Done

Deployed commit 10ae434 — ContactModal centering fixed for both header and footer.

Change: src/components/shared/ContactModal.tsx now renders via ReactDOM.createPortal into document.body, escaping the CSS transform stacking context created by motion.header's slide-in animation. The modal backdrop now covers the full viewport and centers the dialog correctly regardless of where in the component tree it is placed — so both the header Contact button and the footer Contact link open the modal centered in the middle of the page.

Site verified healthy at http://localhost:803/.

## Done Deployed commit `10ae434` — ContactModal centering fixed for both header and footer. **Change:** `src/components/shared/ContactModal.tsx` now renders via `ReactDOM.createPortal` into `document.body`, escaping the CSS transform stacking context created by `motion.header`'s slide-in animation. The modal backdrop now covers the full viewport and centers the dialog correctly regardless of where in the component tree it is placed — so both the header Contact button and the footer Contact link open the modal centered in the middle of the page. Site verified healthy at http://localhost:803/.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: chaulmark/chrishaulmark-website#3
No description provided.