fix(seo): comprehensive SEO audit cleanup - #213
Conversation
Reserve <h1> for page content only (Hero section). The Header site name now uses <span> to avoid duplicate h1 tags on the landing page, which SEO crawlers and audit tools flag as an issue.
Privacy and Terms footer links were <motion.button onClick> elements which are invisible to search engine crawlers. Replaced with <Link> components that render proper <a> tags, making them crawlable and improving internal link structure detection by SEO tools.
Unknown URLs previously showed a blank page with no feedback. Added a catch-all Route path="*" with a NotFoundPage component that displays a 404 heading, descriptive message, and link back to home. Includes i18n support for en/fr/ar (RTL-safe).
- Shortened meta description from 218 to ~155 chars (was exceeding 1000px pixel limit flagged by Seobility) - Changed Twitter card tags from property= to name= per Twitter spec - Added twitter:site meta tag for @bayanflow - Updated DocumentTitle.jsx queries to match new name= selectors - Unified meta, og:description, and twitter:description to same text
- Added dynamic hreflang alternate links for en/fr/ar + x-default - Added og:locale and og:locale:alternate meta tags - Added <meta name="theme-color"> for PWA browser chrome - Added <link rel="preconnect"> for Pyodide CDN (cdn.jsdelivr.net) - Locale-aware: tags update when user switches language
- Removed static <link rel="canonical"> from index.html (already managed dynamically by DocumentTitle.jsx ensureCanonicalLink()) to avoid potential duplicate canonical conflicts - Added Content-Type: text/html; charset=utf-8 to Cloudflare _headers to ensure explicit charset declaration for Seobility compliance
- Wrapped Roadmap content in <main> element for ARIA landmark - Wrapped ProComingSoonPage content in <main> element - Removed dead titleKey prop from DocumentTitle on Pro page (title is derived from ROUTE_TITLE_KEYS + pathname internally)
- Updated twitter meta tag selectors from property= to name= to match real Twitter card spec and index.html changes - Added ensureMetaTag helper to create twitter tags with name= attr - Added test for hreflang alternate links (en/fr/ar + x-default) - Added test for og:locale and og:locale:alternate tags - Added test for og:locale update when language changes to Arabic - Added test for hreflang links updating on route change - Total: 17 tests (up from 14)
- Removed unused currentLang param from ensureAlternateLinks - Updated Footer tests to check href attributes instead of navigate calls (privacy/terms are now Link components, not buttons) - Ran prettier to fix all formatting issues
|
Warning Review limit reached
Next review available in: 16 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
📝 WalkthroughWalkthroughThe PR updates SEO and social metadata, adds localized route-aware locale tags, introduces a localized wildcard 404 page, changes footer legal links to router links, and applies semantic markup updates to several pages. ChangesSEO metadata and locale handling
Localized not-found route
Navigation and semantic markup
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Preview for Bayan Flow Staging ready!
Preview alias |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@public/_headers`:
- Line 7: Remove the Content-Type: text/html; charset=utf-8 directive from the
catch-all rule in public/_headers, leaving asset MIME types to Cloudflare’s
defaults and preserving the remaining headers.
In `@src/components/DocumentTitle.jsx`:
- Around line 118-130: Update the alternate-locale reconciliation in
DocumentTitle to remove any existing og:locale:alternate meta element whose
data-lang matches the current lang before iterating through HREFLANG_LOCALES;
preserve creating or updating entries for other locales. In
src/components/DocumentTitle.test.jsx lines 300-312, add or update the
language-switching assertion to verify the current language has no
og:locale:alternate element.
- Around line 23-27: Update OG_LOCALE_MAP in DocumentTitle.jsx to use the Open
Graph territory matching the app’s Arabic content rather than ar_AR, and update
the corresponding og:locale:alternate expectations in DocumentTitle.test.jsx at
lines 281-298 to match the new locale value.
In `@src/pages/NotFoundPage.jsx`:
- Around line 23-25: Update the Button component’s `to` branch to render a
single interactive router link, using a styled Link or motion(Link) instead of
nesting a motion button inside Link. Preserve the existing styling, navigation
target, and behavior of the NotFoundPage home action.
- Around line 16-21: Update the heading structure in NotFoundPage so the
translated notFound.title becomes the sole semantic h1, retaining its existing
heading styling. Change the literal 404 into decorative or secondary content
rather than a heading, and preserve the existing notFound.message paragraph.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: a103715b-c391-4a20-a7b4-6c3650156552
📒 Files selected for processing (15)
index.htmlpublic/_headerssrc/AppRoutes.jsxsrc/components/DocumentTitle.jsxsrc/components/DocumentTitle.test.jsxsrc/components/Footer.jsxsrc/components/Footer.test.jsxsrc/components/Header.jsxsrc/constants/siteSeo.jssrc/i18n/locales/ar/translation.jsonsrc/i18n/locales/en/translation.jsonsrc/i18n/locales/fr/translation.jsonsrc/pages/NotFoundPage.jsxsrc/pages/ProComingSoonPage.jsxsrc/pages/Roadmap.jsx
- Removed Content-Type from catch-all _headers rule (would override MIME types for /assets/* causing app breakage with nosniff) - Moved charset header to /index.html specific route only - Fixed ar_AR (not a valid locale) to ar_SA in OG_LOCALE_MAP - Added cleanup of og:locale:alternate when locale becomes active - NotFoundPage: made translated title the semantic h1, 404 as decorative aria-hidden content for screen readers - NotFoundPage: replaced Button with Link to avoid nested interactive elements (motion.button inside router Link)
Add buildMetaDescription() to siteSeo.js that computes the meta description from CATEGORY_CONFIG algorithmKeys, so the count and category list stay in sync automatically when algorithms are added. - siteSeo.js: add CATEGORY_DISPLAY_NAMES map + buildMetaDescription() - DocumentTitle.jsx: use buildMetaDescription() for landing + default - index.html: sync static fallback with computed value + add comment - siteSeo.test.js: add test for buildMetaDescription() - DocumentTitle.test.jsx: update meta description assertion to match - Roadmap.jsx: remove unused useTranslation import (lint cleanup)
Contribution workflow
develop: This PR targetsdevelop, notmain.Description
Comprehensive SEO audit cleanup addressing issues flagged by Seobility, SiteGuer, and Semrush external crawlers. 9 commits across 3 files changed, 2 new files added.
Type of Change
Related Issues
Internal SEO audit — no open issue.
Changes Made
<h1>to<span>to resolve duplicate H1 conflict with page content<motion.button>with<Link>for crawlable anchor tagsNotFoundPagecomponent with i18n (en/fr/ar) + catch-all*routeproperty=toname=per Twitter/X spectwitter:sitemeta taghreflangalternate links for en/fr/ar + x-defaultog:localeandog:locale:alternatemeta tags<meta name="theme-color">for PWA browser chrome<link rel="preconnect">for Pyodide CDN<link rel="canonical">from index.html (already managed dynamically)Content-Type: text/html; charset=utf-8to Cloudflare_headers<main>landmarkstitleKeyprop from DocumentTitle on Pro pagename=for twitter selectorshrefattributes instead ofnavigatecallsproperty=→name=, added 3 new tests for hreflang/og:localeTesting
pnpm vitest run)Test Results
Code Quality
Breaking Changes
Checklist
Summary by CodeRabbit