Skip to content

feat: DR-7509 nav-ui footer#7583

Open
carlagn wants to merge 9 commits intomainfrom
feat/DR-7509-navui-footer
Open

feat: DR-7509 nav-ui footer#7583
carlagn wants to merge 9 commits intomainfrom
feat/DR-7509-navui-footer

Conversation

@carlagn
Copy link
Contributor

@carlagn carlagn commented Mar 3, 2026

Summary by CodeRabbit

  • New Features

    • ThemeProvider with persistent theme handling and a ThemeToggle UI.
    • New Footer component with social links, navigation sections, compliance badges, and a Platform Status indicator.
    • Reusable inline GDPR / HIPAA / ISO27001 / SOC2 badge elements.
  • Enhancements

    • Improved navigation styling and responsive behavior.
    • More robust sub-navigation rendering and link handling.
    • Blog layout now wraps content in the site theme context.
  • Documentation

    • Added comprehensive Theme usage and configuration guide.

@vercel
Copy link

vercel bot commented Mar 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blog Ready Ready Preview, Comment Mar 4, 2026 6:41pm
docs Ready Ready Preview, Comment Mar 4, 2026 6:41pm
eclipse Ready Ready Preview, Comment Mar 4, 2026 6:41pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Wraps the blog layout in a ThemeProvider and adds a Footer. Introduces theme primitives (provider, toggle, docs), footer components/data/badges, PDPStatus, URL helpers, and navigation tweaks; plus a cosmetic quoting change in docs layout. (48 words)

Changes

Cohort / File(s) Summary
Theme core
packages/ui/src/components/theme-provider.tsx, packages/ui/src/components/theme-toggle.tsx, packages/ui/THEME.md
Adds a client ThemeProvider, useTheme hook, a ThemeToggle UI with two modes, and comprehensive theme documentation (persistence, system detection, DOM attribute, Tailwind guidance).
Footer & data
packages/ui/src/components/footer.tsx, packages/ui/src/components/footer-badges.tsx, packages/ui/src/data/footer.ts
Adds a new Footer component, four inline SVG badge exports (gdpr, hipaa, iso27, soc2), and a centralized footerData module for columns and social links.
Status & URL utilities
packages/ui/src/components/pdp-status.tsx, packages/ui/src/lib/is-absolute-url.ts
Adds PDPStatus (client component fetching platform status) and URL helpers isAbsoluteUrl and getRedirectableLink to normalize/redirect relative links.
Navigation styling & keys
packages/ui/src/components/navigation-menu.tsx, packages/ui/src/components/web-navigation.tsx
Adjusts navigation className variants (background/rounding/hover states) and changes sub-item keys to ${sub.text}-${sub.url}-${index} for improved list key uniqueness.
Layout integrations
apps/blog/src/app/(blog)/layout.tsx
Wraps blog Layout in ThemeProvider (defaultTheme="system", storageKey="blog-theme") and renders Footer inside the provider alongside WebNavigation.
Docs formatting
apps/docs/src/app/layout.tsx
Cosmetic consistency: converted single quotes to double quotes and standardized whitespace/formatting only.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective: adding a footer component to the navigation UI, as evidenced by extensive footer-related changes (Footer component, footer data, theme integration, badges).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@argos-ci
Copy link

argos-ci bot commented Mar 3, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ⚠️ Changes detected (Review) 1 removed Mar 4, 2026, 6:47 PM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (5)
packages/ui/src/components/pdp-status.tsx (1)

32-33: Fallback class pattern is inconsistent with defined mappings.

If an unknown indicator value arrives (e.g., "maintenance"), the fallback "bg-gray-500" is missing the [&>div]: selector prefix and the text color class that other entries have. This would cause the status dot to not receive the color.

♻️ Proposed fix for consistent fallback
   const indicator = pdpStatus.status.indicator || "-";
-  const indicatorClass = indicatorStatus[indicator] || "bg-gray-500";
+  const indicatorClass = indicatorStatus[indicator] || indicatorStatus["-"];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/pdp-status.tsx` around lines 32 - 33, The fallback
for unknown status indicators is missing the selector prefix and text color, so
update the assignment that computes indicatorClass (which uses
pdpStatus.status.indicator and indicatorStatus) to use the same pattern as other
mappings; replace the fallback "bg-gray-500" with the full consistent fallback
string (including the "[&>div]:..." selector and text color class) so that
indicatorClass = indicatorStatus[indicator] || "<selector-prefix>-bg-gray-500
text-gray-900" (use the exact selector style used in indicatorStatus entries).
packages/ui/src/components/footer.tsx (4)

17-17: Unused color prop in Link component.

The color prop is defined in LinkProps and destructured, but it's never used to affect the rendered output. Consider removing it to avoid confusion, or implement the color variant styling if intended.

Also applies to: 21-21, 24-24

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` at line 17, LinkProps defines a
ColorType and destructures a color prop in the Link component but never uses it;
either remove the unused ColorType and the color property from LinkProps and the
Link component's parameter destructuring, or implement the variant styling: keep
ColorType and color, add a small map (e.g., colorToClass for "indigo" | "teal" |
"white") inside the Link component and apply the resulting className alongside
existing classes when rendering the anchor; update any callers to stop passing
color if you remove it or ensure callers use supported values if you implement
styling.

87-103: Consider adding noreferrer alongside noopener for external links.

The social icon links use rel="noopener" which is good for security. Adding noreferrer as well (rel="noopener noreferrer") would also prevent the Referer header from being sent to external sites, providing an additional privacy benefit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 87 - 103, Update the
external social links rendered in the footer component to include noreferrer
alongside noopener in the rel attribute: locate the anchor element that maps
over socialLink (the <a href={socialLink.url} ... aria-label={socialLink.title}
...> rendering the Action and icon) and change rel="noopener" to rel="noopener
noreferrer" so external requests do not receive the Referer header.

52-67: Several props in FooterProps are unused.

className, color, hideNewsletter, lightTheme, and newsletterComponent are declared in the interface but not destructured or used in the component. If these are planned for future use, consider adding TODO comments; otherwise, remove them to keep the API surface clean.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 52 - 67, The FooterProps
interface declares props that are not used by the Footer component (className,
color, hideNewsletter, lightTheme, newsletterComponent); update the code by
either removing these unused properties from FooterProps to keep the API surface
minimal, or explicitly destructure them in the Footer function signature
(Footer) and add short TODO comments where they will be used (or implement their
handling) so the intent is clear; ensure the change references FooterProps and
the Footer component to keep types and props in sync.

123-123: Unnecessary template literal for referrerPolicy.

The template literal here can be simplified to a plain ternary expression.

♻️ Simplified expression
                     <Link
                       key={idx}
                       href={getRedirectableLink(link.url, absoluteLinks)}
                       external={isAbsoluteUrl(link.url)}
-                      referrerPolicy={`${link.url ? "no-referrer" : ""}`}
+                      referrerPolicy={link.url ? "no-referrer" : undefined}
                     >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` at line 123, The referrerPolicy prop
is using an unnecessary template literal; in the JSX where referrerPolicy is set
(the referrerPolicy={`${link.url ? "no-referrer" : ""}`} expression in the
footer component), replace the template literal with a plain ternary expression
(referrerPolicy={link.url ? "no-referrer" : ""}) to simplify the code and remove
the superfluous string interpolation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/footer-badges.tsx`:
- Around line 201-203: The SVG path data in the FooterBadges component contains
a typo: the 'd' attribute string includes "6.970f67" which is invalid and will
break rendering; locate the path element (the JSX <path d="M4.67535
9.56769L3.6897 10.5533L6.970f67 13.8343L13.0398 7.76519L12.0541 6.77954L6.97067
11.863L4.67535 9.56769Z" ... />) and replace the malformed token "6.970f67" with
the correct numeric value "6.97067" so the 'd' attribute contains only valid
numbers.

In `@packages/ui/src/components/footer.tsx`:
- Line 144: In the Footer component update all anchor tags that mistakenly use
target="__blank" to the correct reserved target="_blank" so each external link
opens in a true new tab; search for occurrences of target="__blank" inside the
Footer JSX (e.g., the social/trust badge links referenced in the component) and
replace them with target="_blank" (and consider adding rel="noopener noreferrer"
if not present for security on external links).

In `@packages/ui/src/components/pdp-status.tsx`:
- Around line 21-30: The fetch block inside the useEffect should validate HTTP
status before parsing: after fetch, check response.ok and if false throw a new
Error including response.status and response.statusText so we don't call
setPdpStatus with an error payload; only call response.json() and setPdpStatus
when response.ok is true. Also replace console.log with console.error in the
catch to surface failures. Update the code that references useEffect, fetch,
response.json, setPdpStatus, and the catch handler accordingly.

In `@packages/ui/src/components/theme-provider.tsx`:
- Around line 93-99: Persisted theme is being force-cast from localStorage via
storageKey into Theme without validation; update the retrieval logic to safely
parse and validate the stored value before using it. Read the raw string from
localStorage.getItem(storageKey), check it against allowed Theme values (or use
a validator like resolveTheme to detect invalid values), and fall back to
defaultTheme if validation fails; then call setThemeState(validTheme),
setResolvedTheme(resolvedValidTheme), and applyTheme(resolvedValidTheme). Ensure
you reference storageKey, Theme, resolveTheme, setThemeState, setResolvedTheme,
and applyTheme when implementing the guard so corrupted or unknown strings never
propagate to state or DOM.

In `@packages/ui/src/components/theme-toggle.tsx`:
- Around line 55-57: In the full.map(...) callback inside the ThemeToggle
component (the map that destructures [key, Icon]), replace the implicit
undefined return used for skipping the "system" key with an explicit return
null; so the callback reads that it returns null when key === "system" — this
makes the React intent clear and follows the convention for skipping children in
JSX.
- Around line 26-33: The ThemeToggle component currently swallows root props in
the "light-dark" branch and returns undefined from a map callback; update
ThemeToggle so the root element is consistent and always receives {...props}
(e.g., render a <div {...props}> wrapper around the toggle button in both modes
or forward {...props} onto the rendered element in the "light-dark" branch),
ensure the mode handling still uses setTheme/theme/resolvedTheme, and replace
any map callback that returns undefined with an explicit null to satisfy React
(change undefined -> null in the map used to render theme options).

In `@packages/ui/src/lib/is-absolute-url.ts`:
- Around line 3-8: getRedirectableLink currently concatenates root-relative
links to "https://www.prisma.io/" which can produce double slashes (e.g.,
https://www.prisma.io//path); update getRedirectableLink to normalize the link
when it's not absolute by trimming any leading slashes from the link (or
otherwise ensuring exactly one slash between domain and path) before building
the redirect URL so `isAbsoluteUrl(link)` stays the same but the constructed
string uses the normalized link.

---

Nitpick comments:
In `@packages/ui/src/components/footer.tsx`:
- Line 17: LinkProps defines a ColorType and destructures a color prop in the
Link component but never uses it; either remove the unused ColorType and the
color property from LinkProps and the Link component's parameter destructuring,
or implement the variant styling: keep ColorType and color, add a small map
(e.g., colorToClass for "indigo" | "teal" | "white") inside the Link component
and apply the resulting className alongside existing classes when rendering the
anchor; update any callers to stop passing color if you remove it or ensure
callers use supported values if you implement styling.
- Around line 87-103: Update the external social links rendered in the footer
component to include noreferrer alongside noopener in the rel attribute: locate
the anchor element that maps over socialLink (the <a href={socialLink.url} ...
aria-label={socialLink.title} ...> rendering the Action and icon) and change
rel="noopener" to rel="noopener noreferrer" so external requests do not receive
the Referer header.
- Around line 52-67: The FooterProps interface declares props that are not used
by the Footer component (className, color, hideNewsletter, lightTheme,
newsletterComponent); update the code by either removing these unused properties
from FooterProps to keep the API surface minimal, or explicitly destructure them
in the Footer function signature (Footer) and add short TODO comments where they
will be used (or implement their handling) so the intent is clear; ensure the
change references FooterProps and the Footer component to keep types and props
in sync.
- Line 123: The referrerPolicy prop is using an unnecessary template literal; in
the JSX where referrerPolicy is set (the referrerPolicy={`${link.url ?
"no-referrer" : ""}`} expression in the footer component), replace the template
literal with a plain ternary expression (referrerPolicy={link.url ?
"no-referrer" : ""}) to simplify the code and remove the superfluous string
interpolation.

In `@packages/ui/src/components/pdp-status.tsx`:
- Around line 32-33: The fallback for unknown status indicators is missing the
selector prefix and text color, so update the assignment that computes
indicatorClass (which uses pdpStatus.status.indicator and indicatorStatus) to
use the same pattern as other mappings; replace the fallback "bg-gray-500" with
the full consistent fallback string (including the "[&>div]:..." selector and
text color class) so that indicatorClass = indicatorStatus[indicator] ||
"<selector-prefix>-bg-gray-500 text-gray-900" (use the exact selector style used
in indicatorStatus entries).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac8da00 and fdeec01.

📒 Files selected for processing (12)
  • apps/blog/src/app/(blog)/layout.tsx
  • apps/docs/src/app/layout.tsx
  • packages/ui/THEME.md
  • packages/ui/src/components/footer-badges.tsx
  • packages/ui/src/components/footer.tsx
  • packages/ui/src/components/navigation-menu.tsx
  • packages/ui/src/components/pdp-status.tsx
  • packages/ui/src/components/theme-provider.tsx
  • packages/ui/src/components/theme-toggle.tsx
  • packages/ui/src/components/web-navigation.tsx
  • packages/ui/src/data/footer.ts
  • packages/ui/src/lib/is-absolute-url.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
packages/ui/src/components/footer.tsx (2)

170-195: ⚠️ Potential issue | 🟠 Major

Use _blank (not __blank) for trust badge links.

Lines 170, 178, 186, and 194 use target="__blank", which creates a named window instead of true _blank behavior.

🐛 Proposed fix
-                target="__blank"
+                target="_blank"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 170 - 195, The anchor
tags in the footer component currently use target="__blank" which opens a named
window instead of a new tab; update the four anchor elements that render the
trust badges (the anchors that wrap {gdpr}, {hipaa}, {iso27}, etc., inside the
Footer component) to use target="_blank" and keep rel="noopener noreferrer" and
aria-label unchanged to ensure true new-tab behavior and security.

146-147: ⚠️ Potential issue | 🟠 Major

Add rel for external dropdown links opened in new tabs.

Line 146 uses target="_blank" without rel="noopener noreferrer", which weakens tab isolation.

🔒 Proposed fix
-                                target="_blank"
+                                target="_blank"
+                                rel="noopener noreferrer"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 146 - 147, The anchor
element that uses target="_blank" (the JSX anchor with className "text-left
capitalize text-foreground-neutral-weak text-md font-semibold") must include
rel="noopener noreferrer" to restore tab isolation; update that anchor (or any
external dropdown link opened with target="_blank") to add rel="noopener
noreferrer" alongside target="_blank" so external links are safe.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/footer-badges.tsx`:
- Line 205: In the FooterBadges component
(packages/ui/src/components/footer-badges.tsx) there is an orphaned JSX closing
token "/>" sitting between two <path> elements that breaks parsing; remove that
stray "/>" so the adjacent <path> elements are directly contiguous within the
SVG/return, ensuring the JSX contains only well-formed opening/closing tags for
elements referenced in the component (e.g., the <path> entries inside the SVG in
FooterBadges).

In `@packages/ui/src/components/footer.tsx`:
- Around line 142-150: Fix the malformed JSX for the dropdown link by
consolidating the duplicated/incomplete anchor tags into a single <a> element
that uses href={dropLink.url}, a single className="text-left capitalize
text-foreground-neutral-weak text-md font-semibold", target="_blank" and add
rel="noopener noreferrer" to the same anchor (the element rendering dropLink in
the Footer component/JSX). Remove the stray/incomplete <a> and duplicate
className attributes so the tag parses correctly, and also correct the other
typos where target="__blank" (the other anchors that use target) to
target="_blank" to ensure proper new-tab behavior.

---

Duplicate comments:
In `@packages/ui/src/components/footer.tsx`:
- Around line 170-195: The anchor tags in the footer component currently use
target="__blank" which opens a named window instead of a new tab; update the
four anchor elements that render the trust badges (the anchors that wrap {gdpr},
{hipaa}, {iso27}, etc., inside the Footer component) to use target="_blank" and
keep rel="noopener noreferrer" and aria-label unchanged to ensure true new-tab
behavior and security.
- Around line 146-147: The anchor element that uses target="_blank" (the JSX
anchor with className "text-left capitalize text-foreground-neutral-weak text-md
font-semibold") must include rel="noopener noreferrer" to restore tab isolation;
update that anchor (or any external dropdown link opened with target="_blank")
to add rel="noopener noreferrer" alongside target="_blank" so external links are
safe.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fdeec01 and 5b5d228.

📒 Files selected for processing (3)
  • packages/ui/src/components/footer-badges.tsx
  • packages/ui/src/components/footer.tsx
  • packages/ui/src/lib/is-absolute-url.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui/src/lib/is-absolute-url.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/ui/src/components/footer.tsx (2)

166-190: ⚠️ Potential issue | 🟠 Major

Fix target="__blank" typo on trust badge links.

Lines 166, 174, 182, and 190 still use __blank (double underscore), which opens a named window instead of the reserved new-tab target.

🔧 Proposed fix
-                target="__blank"
+                target="_blank"
...
-                target="__blank"
+                target="_blank"
...
-                target="__blank"
+                target="_blank"
...
-                target="__blank"
+                target="_blank"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 166 - 190, The footer
component contains anchor tags for the trust badges (the links rendering {gdpr},
{hipaa}, {iso27}, etc.) that mistakenly use target="__blank" (double
underscore); update each anchor in the Footer component to use the correct
reserved target target="_blank" (single underscore) so links open in a new tab,
keeping rel="noopener noreferrer" and aria-labels intact; search for any other
occurrences of "__blank" in this file and replace them similarly.

142-145: ⚠️ Potential issue | 🟠 Major

Add rel on dropdown external anchor.

Line 144 uses target="_blank" without rel. This repeats the previously flagged issue in this file.

🔧 Proposed fix
                               <a
                                 href={dropLink.url}
                                 target="_blank"
+                                rel="noopener noreferrer"
                                 className="text-left capitalize text-foreground-neutral-weak text-md font-semibold"
                               >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 142 - 145, The anchor
element rendering external links (the JSX <a> with href={dropLink.url} and
target="_blank" in the Footer component / footer.tsx) is missing a rel
attribute; add rel="noopener noreferrer" to that <a> (the element with
href={dropLink.url} target="_blank" className="text-left capitalize ...") to
prevent tabnabbing and security issues when opening external links in a new tab.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/footer.tsx`:
- Around line 29-36: The Link component returns an anchor when external or href
is external but omits rel, creating reverse tabnabbing risk; update the anchor
returned in the external branch of the Link component to add a rel attribute
that merges any existing rel from props (rest.rel) with "noopener noreferrer"
(dedupe if necessary) while preserving other props like href, className and
target={external ? "_blank" : "_self"} so external links include rel="noopener
noreferrer".

---

Duplicate comments:
In `@packages/ui/src/components/footer.tsx`:
- Around line 166-190: The footer component contains anchor tags for the trust
badges (the links rendering {gdpr}, {hipaa}, {iso27}, etc.) that mistakenly use
target="__blank" (double underscore); update each anchor in the Footer component
to use the correct reserved target target="_blank" (single underscore) so links
open in a new tab, keeping rel="noopener noreferrer" and aria-labels intact;
search for any other occurrences of "__blank" in this file and replace them
similarly.
- Around line 142-145: The anchor element rendering external links (the JSX <a>
with href={dropLink.url} and target="_blank" in the Footer component /
footer.tsx) is missing a rel attribute; add rel="noopener noreferrer" to that
<a> (the element with href={dropLink.url} target="_blank" className="text-left
capitalize ...") to prevent tabnabbing and security issues when opening external
links in a new tab.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b5d228 and fe5ce52.

📒 Files selected for processing (1)
  • packages/ui/src/components/footer.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/ui/src/components/footer-badges.tsx (1)

10-61: Hoist repeated fill to a parent container.

Most paths repeat the same fill token; setting it once on a parent <g>/<svg> will shrink markup and simplify future edits.

Also applies to: 80-119, 151-170, 197-212

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer-badges.tsx` around lines 10 - 61, The SVG
in footer-badges.tsx repeats fill="var(--color-foreground-neutral-weaker)" on
many <path> elements; remove those duplicate fill attributes and hoist the fill
to a parent (either the root <svg> or a wrapping <g>) so the paths inherit it
(update the component JSX where the long sequence of <path> elements is
rendered, e.g. wrap the block of repeated <path> tags in <g
fill="var(--color-foreground-neutral-weaker)"> or set that fill on the outer
<svg>, and then delete the per-path fill attributes).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/footer-badges.tsx`:
- Line 9: The SVG clipPath IDs in footer-badges.tsx (e.g., the clipPath elements
and their usages like clipPath="url(`#clip0_2221_28464`)") are fixed and will
collide when the component is rendered multiple times; update FooterBadges (or
the component exporting these SVGs) to generate unique IDs per instance (for
example using a stable unique suffix or React's useId) and replace all matching
references (clipPath attributes and url(#...) usages) to that generated ID, or
remove the clipping <clipPath> defs and corresponding clipPath attributes
entirely if clipping is not needed; ensure every occurrence (the defs and the g
elements referencing them) is updated to use the same generated identifier.

---

Nitpick comments:
In `@packages/ui/src/components/footer-badges.tsx`:
- Around line 10-61: The SVG in footer-badges.tsx repeats
fill="var(--color-foreground-neutral-weaker)" on many <path> elements; remove
those duplicate fill attributes and hoist the fill to a parent (either the root
<svg> or a wrapping <g>) so the paths inherit it (update the component JSX where
the long sequence of <path> elements is rendered, e.g. wrap the block of
repeated <path> tags in <g fill="var(--color-foreground-neutral-weaker)"> or set
that fill on the outer <svg>, and then delete the per-path fill attributes).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe5ce52 and 19d9c94.

📒 Files selected for processing (1)
  • packages/ui/src/components/footer-badges.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
packages/ui/src/components/footer.tsx (3)

164-195: ⚠️ Potential issue | 🟠 Major

Typo: __blank should be _blank.

Lines 166, 174, 182, and 190 use target="__blank" (double underscore) instead of target="_blank". The double underscore creates a named window called "__blank" rather than using the reserved _blank target. This means all four links will open/reuse the same named window instead of each opening a new tab, which is almost certainly unintended.

🐛 Proposed fix
             <a
               href="https://trust.prisma.io/"
-              target="__blank"
+              target="_blank"
               rel="noopener noreferrer"
               aria-label="Prisma Trust"
             >

Apply this same change to all four trust badge links (lines 166, 174, 182, 190).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 164 - 195, Fix the typo
where anchor targets use "__blank" (double underscore) so links reuse a named
window; change target="__blank" to the standard target="_blank" for the four
trust badge anchors that render {gdpr}, {hipaa}, {iso27}, and {soc2} (in the
Footer component) and keep the existing rel and aria-label attributes unchanged.

29-42: ⚠️ Potential issue | 🟠 Major

Security: Add rel="noopener noreferrer" to prevent reverse tabnabbing.

When target="_blank" is used without rel="noopener noreferrer", the opened page can access window.opener and potentially redirect your original tab to a phishing page. This is a well-known vulnerability called reverse tabnabbing.

Additionally, there's a logic inconsistency: links matching href.startsWith("http") enter this branch but only get target="_blank" when external is explicitly true.

🛡️ Proposed fix
 const Link = ({ external, color, children, href, ...rest }: LinkProps) => {
   const className = clsx(
     "text-foreground-neutral-weak text-md font-semibold leading-md flex items-center cursor-pointer font-medium box-border no-underline px-2.5 -ml-2.5 py-1.5 transition-colors hover:bg-background-ppg-strong rounded-square transition-all",
   );
+  const isExternal = external || (href && (href.startsWith("http") || href.startsWith("//")));

-  if (external || !href || href.startsWith("http") || href.startsWith("#")) {
+  if (isExternal || !href || href.startsWith("#")) {
     return (
       <a
         {...rest}
         href={href}
         className={className}
-        target={external ? "_blank" : "_self"}
+        target={isExternal ? "_blank" : "_self"}
+        rel={isExternal ? "noopener noreferrer" : undefined}
       >
         {children}
-        {external && (
+        {isExternal && (
           <i className="fa-regular fa-arrow-up-right text-foreground-neutral-weaker text-xs ml-1" />
         )}
       </a>
     );
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 29 - 42, The anchor
rendering in the footer component uses target="_blank" without rel="noopener
noreferrer" and also only sets target="_blank" when external is true even though
href.startsWith("http") also enters the same branch; update the logic in the
component (referencing variables external and href and the anchor rendering
block that spreads ...rest and renders children) so that links opened in a new
tab always include rel="noopener noreferrer" (add rel when target is "_blank"),
and normalize the decision for opening in a new tab by treating external =
external || href?.startsWith("http") (or otherwise set target based on
href.startsWith("http")) so HTTP links get target="_blank" and the rel attribute
consistently.

142-148: ⚠️ Potential issue | 🟠 Major

Security: Add rel="noopener noreferrer" to dropdown links.

This anchor tag opens external links with target="_blank" but lacks the rel attribute, making it vulnerable to reverse tabnabbing attacks.

🛡️ Proposed fix
                             <a
                               href={dropLink.url}
                               target="_blank"
+                              rel="noopener noreferrer"
                               className="text-left capitalize text-foreground-neutral-weak text-md font-semibold"
                             >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 142 - 148, The anchor
rendering the dropdown link for dropLink (the <a> inside the footer component
that uses href={dropLink.url} and target="_blank") is missing rel attributes;
update that anchor in the footer.tsx component to include rel="noopener
noreferrer" to prevent reverse tabnabbing and ensure external links opened with
target="_blank" are safe.
🧹 Nitpick comments (4)
packages/ui/src/components/footer.tsx (4)

87-102: Consider adding noreferrer to social links.

Line 90 has rel="noopener" which prevents reverse tabnabbing, but adding noreferrer provides an extra privacy layer by not sending the Referer header to the destination site. This is a minor enhancement.

🛡️ Proposed fix
               <a
                 href={socialLink.url}
                 target="_blank"
-                rel="noopener"
+                rel="noopener noreferrer"
                 key={idx}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 87 - 102, Update the
anchor elements that render social links to include noreferrer in the rel
attribute; specifically modify the <a ... rel="noopener"> usage inside the
footer component where socialLink is iterated (the anchor rendering the Action
component and icon) to use rel="noopener noreferrer" so the links both prevent
reverse tabnabbing and avoid sending Referer headers to external sites.

86-87: Replace any types with proper typing.

Using any for socialLink, footerItem, and link defeats TypeScript's type checking. Consider defining interfaces that match the data shape from footerData, which will catch bugs at compile time and improve IDE autocomplete.

♻️ Example type definitions
interface SocialIcon {
  url: string;
  title: string;
  icon: string;
}

interface FooterLink {
  _type: "footerLinkType" | "footerDropdownType";
  title: string;
  url?: string;
  links?: Array<{ title: string; url: string }>;
}

interface FooterItem {
  title: string;
  links: FooterLink[];
}

Also applies to: 109-109, 117-117

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 86 - 87, The code uses
`any` for socialLink, footerItem, and link in the footer component which
disables type checking; define proper interfaces (e.g., SocialIcon, FooterLink,
FooterItem or similar shapes that match `footerData`) and replace the `any`
annotations in the map callbacks and props (references: the
`footerData.socialIcons.map` callback, the `footerData.map` items currently
typed as `footerItem`, and the inner link mappings typed as `link`) with these
interfaces so TypeScript enforces shapes and provides autocomplete—update
component props/state types and any function signatures that consume
`footerData` accordingly.

24-27: Unused color prop.

The color prop is destructured from LinkProps but never used in the component. Either implement color-based styling or remove this prop to avoid confusion.

🧹 Proposed fix to remove unused prop
-const Link = ({ external, color, children, href, ...rest }: LinkProps) => {
+const Link = ({ external, children, href, ...rest }: LinkProps) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 24 - 27, The Link
component is destructuring a color prop that is never used; update the Link
component and its type to remove the unused color prop (remove color from the
destructuring in the Link function signature and from LinkProps) or, if design
requires variant coloring, use the color value to conditionally add classes to
the className variable (refer to the Link component, LinkProps type, and the
className construction) so the prop is either consumed or removed to eliminate
the unused prop warning.

52-67: Multiple unused props and any types in FooterProps.

Several props are defined but never used in the component: className, color, hideNewsletter, lightTheme, and newsletterComponent. Additionally, style and newsletterComponent use the any type, which bypasses TypeScript's type checking.

If these are for future implementation, consider removing them until needed (YAGNI principle). If they're intentional extension points, using React.CSSProperties for style and React.ReactNode for newsletterComponent would provide better type safety.

♻️ Proposed cleanup
 interface FooterProps {
   className?: string;
-  style?: any;
+  style?: React.CSSProperties;
   color?: ColorType;
   darker?: boolean;
-  hideNewsletter?: boolean;
-  lightTheme?: boolean;
   absoluteLinks?: boolean;
-  newsletterComponent?: any;
+  newsletterComponent?: React.ReactNode;
 }

 const Footer = ({
+  className,
   style,
   darker = false,
   absoluteLinks = false,
 }: FooterProps) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/footer.tsx` around lines 52 - 67, The FooterProps
interface defines several unused props (className, color, hideNewsletter,
lightTheme, newsletterComponent) and uses unsafe any types for style and
newsletterComponent; update FooterProps and the Footer component to either
remove unused props if not yet used (drop className, color, hideNewsletter,
lightTheme, newsletterComponent from FooterProps and from Footer's parameter
list) or, if they are intended as extension points, replace the any types with
proper React types (use style: React.CSSProperties and newsletterComponent:
React.ReactNode) and ensure Footer's parameter destructuring includes those
props; modify the Footer function signature and prop typing consistently
(reference FooterProps and the Footer component) so there are no unused or
any-typed props left.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/footer.tsx`:
- Around line 121-123: The referrerPolicy prop in the Footer component is being
set to an empty string via `${link.url ? "no-referrer" : ""}`, which is invalid;
change it to use undefined or omit the prop when link.url is falsy (e.g.
referrerPolicy={link.url ? "no-referrer" : undefined} or conditionally spread
the prop) so that getRedirectableLink/isAbsoluteUrl usage remains unchanged and
no invalid empty string is rendered.

---

Duplicate comments:
In `@packages/ui/src/components/footer.tsx`:
- Around line 164-195: Fix the typo where anchor targets use "__blank" (double
underscore) so links reuse a named window; change target="__blank" to the
standard target="_blank" for the four trust badge anchors that render {gdpr},
{hipaa}, {iso27}, and {soc2} (in the Footer component) and keep the existing rel
and aria-label attributes unchanged.
- Around line 29-42: The anchor rendering in the footer component uses
target="_blank" without rel="noopener noreferrer" and also only sets
target="_blank" when external is true even though href.startsWith("http") also
enters the same branch; update the logic in the component (referencing variables
external and href and the anchor rendering block that spreads ...rest and
renders children) so that links opened in a new tab always include rel="noopener
noreferrer" (add rel when target is "_blank"), and normalize the decision for
opening in a new tab by treating external = external || href?.startsWith("http")
(or otherwise set target based on href.startsWith("http")) so HTTP links get
target="_blank" and the rel attribute consistently.
- Around line 142-148: The anchor rendering the dropdown link for dropLink (the
<a> inside the footer component that uses href={dropLink.url} and
target="_blank") is missing rel attributes; update that anchor in the footer.tsx
component to include rel="noopener noreferrer" to prevent reverse tabnabbing and
ensure external links opened with target="_blank" are safe.

---

Nitpick comments:
In `@packages/ui/src/components/footer.tsx`:
- Around line 87-102: Update the anchor elements that render social links to
include noreferrer in the rel attribute; specifically modify the <a ...
rel="noopener"> usage inside the footer component where socialLink is iterated
(the anchor rendering the Action component and icon) to use rel="noopener
noreferrer" so the links both prevent reverse tabnabbing and avoid sending
Referer headers to external sites.
- Around line 86-87: The code uses `any` for socialLink, footerItem, and link in
the footer component which disables type checking; define proper interfaces
(e.g., SocialIcon, FooterLink, FooterItem or similar shapes that match
`footerData`) and replace the `any` annotations in the map callbacks and props
(references: the `footerData.socialIcons.map` callback, the `footerData.map`
items currently typed as `footerItem`, and the inner link mappings typed as
`link`) with these interfaces so TypeScript enforces shapes and provides
autocomplete—update component props/state types and any function signatures that
consume `footerData` accordingly.
- Around line 24-27: The Link component is destructuring a color prop that is
never used; update the Link component and its type to remove the unused color
prop (remove color from the destructuring in the Link function signature and
from LinkProps) or, if design requires variant coloring, use the color value to
conditionally add classes to the className variable (refer to the Link
component, LinkProps type, and the className construction) so the prop is either
consumed or removed to eliminate the unused prop warning.
- Around line 52-67: The FooterProps interface defines several unused props
(className, color, hideNewsletter, lightTheme, newsletterComponent) and uses
unsafe any types for style and newsletterComponent; update FooterProps and the
Footer component to either remove unused props if not yet used (drop className,
color, hideNewsletter, lightTheme, newsletterComponent from FooterProps and from
Footer's parameter list) or, if they are intended as extension points, replace
the any types with proper React types (use style: React.CSSProperties and
newsletterComponent: React.ReactNode) and ensure Footer's parameter
destructuring includes those props; modify the Footer function signature and
prop typing consistently (reference FooterProps and the Footer component) so
there are no unused or any-typed props left.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7dfdc02d-2f4a-405a-990d-84b3a73d0629

📥 Commits

Reviewing files that changed from the base of the PR and between 19d9c94 and 034ac45.

📒 Files selected for processing (1)
  • packages/ui/src/components/footer.tsx

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 4, 2026
aidankmcalister
aidankmcalister previously approved these changes Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants