Skip to content

Metaculus Storefront#4522

Open
aseckin wants to merge 2 commits intomainfrom
storefront
Open

Metaculus Storefront#4522
aseckin wants to merge 2 commits intomainfrom
storefront

Conversation

@aseckin
Copy link
Contributor

@aseckin aseckin commented Mar 22, 2026

Summary by CodeRabbit

  • New Features

    • New animated hero, globe background, forecasts carousel, and featured-publications marquee on the homepage
    • New logos and decorative SVGs added
  • Improvements

    • Enforced light theme on the homepage
    • Compact layout option and responsive refinements for forecast cards
    • Footer hides language/theme controls on homepage
  • Removed

    • Multiple legacy homepage sections and subscription/leaderboard/tournaments components removed
  • Localization

    • Added/updated marketing/service translations across multiple languages (including parameterized year count)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 22, 2026

📝 Walkthrough

Walkthrough

Redesigned the homepage: removed multiple legacy sections, added new hero, marquee, carousel, shader background, and light-mode enforcer; introduced new localization keys across several languages; added shader dependencies; threaded a new compact prop through group forecast cards and adjusted layout/wrapper behavior.

Changes

Cohort / File(s) Summary
Localization Files
front_end/messages/{cs,en,es,pt,zh-TW,zh}.json
Added ~11 new marketing/hero UI keys (including clarityInAComplexWorld, yearsOfPredictions, scrollLeft, scrollRight, forecastingPlatform, businessSolutions, collectiveIntelligenceForPublicGood, forInformedDecisionMaking, measuringForecastingAccuracyOfAI, mapTheFutureBeforeYouBuildIt, radiant, hire, run, host) and updated a few existing question-related strings in each locale.
Package deps
front_end/package.json
Added @paper-design/shaders and @paper-design/shaders-react ^0.0.72 for shader-based hero visuals.
Removed Homepage Components
front_end/src/app/(main)/(home)/components/...
all_categories_section.tsx, future_eval_section.tsx, future_eval_table.tsx, hero_ctas.tsx, homepage_forecasts.tsx, newsletter_subscription.tsx, research_and_updates.tsx, tournaments_section.tsx, why_metaculus.tsx
Deleted nine legacy/homepage components (category lists, future-eval leaderboard/table, hero CTAs, forecast tabs, newsletter subscription, research cards, tournaments, stats/featured-in).
New Homepage Components
front_end/src/app/(main)/(home)/components/...
hero_section.tsx, hero_globe_background.tsx, featured_in_marquee.tsx, force_light_mode.tsx, forecasts_carousel_section.tsx
Added five new components: hero (with CTAs/stats), shader globe background (dynamic import), featured-in infinite marquee (embla + auto-scroll), light-mode enforcer, and a horizontally scrollable forecasts carousel with controls.
Icons / Logos
front_end/src/app/(main)/(home)/components/...
hero_futureeval_symbol.tsx, metaculus_storefront_logo.tsx, radiant_logo.tsx
Added three SVG icon/logo components used by the new hero and CTAs.
Group Forecast Card: compact mode
front_end/src/components/consumer_post_card/group_forecast_card/...
Added optional compact?: boolean prop across wrapper, choice bar, index, numeric and percentage forecast cards to support denser rendering in carousels.
Homepage post card tweaks
front_end/src/app/(main)/(home)/components/homepage_post_card.tsx
Adjusted responsive padding, header layout/gaps, small typography/styling updates for CommentStatus and ForecastersCounter, scaled ConsumerQuestionTile, and enable compact mode for group forecast cards.
Layout & routing changes
front_end/src/app/(main)/page.tsx, .../layout.tsx, .../components/layout_content_wrapper.tsx, .../components/footer.tsx, front_end/src/utils/navigation.ts
Page now loads only news posts+sidebar; removed many suspended sections and replaced with new components; extracted layout wrapper to LayoutContentWrapper; footer hides language/theme selectors on homepage; getWithDefaultHeader now explicitly excludes /.
Staff Picks styling
front_end/src/app/(main)/(home)/components/staff_picks.tsx
Adjusted container and item styling: changed background/hover colors and heading typography; removed some dark-mode variants.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested Reviewers

  • cemreinanc
  • elisescu
  • ncarazon

Poem

🐰
I twitched my nose at old UI dust,
Hopped in carousels, shaders and trust.
New logos gleam, the marquee hums,
A brighter home — behold what comes! ✨

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Metaculus Storefront' is directly related to the main changes, which introduce a new storefront/marketing section with hero components, carousel sections, and marketing copy across multiple languages.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch storefront

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.

❤️ Share

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

Tip

CodeRabbit can use Trivy to scan for security misconfigurations and secrets in Infrastructure as Code files.

Add a .trivyignore file to your project to customize which findings Trivy reports.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 22, 2026

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-4522-storefront-preview.mtcl.cc
📦 Docker Image ghcr.io/metaculus/metaculus:storefront-f086687
🗄️ PostgreSQL NeonDB branch preview/pr-4522-storefront
Redis Fly Redis mtc-redis-pr-4522-storefront

Details

  • Commit: 9e85a2e8e991649f0085d7a3d8df2c0dc4a2ceb8
  • Branch: storefront
  • Fly App: metaculus-pr-4522-storefront

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

…ed light mode

- Dark hero section with dithered globe shader, offset right
- Two CTA cards (Forecasting Platform + Business Solutions) with hover effects
- FutureEval and Radiant banner cards with backdrop blur
- Staff picks horizontal scroll pills
- Native CSS scroll forecast carousel with snap and arrow navigation
- Infinite-scrolling featured-in logos marquee
- Force light mode on homepage regardless of user theme preference
- Compact mode for forecast cards on mobile
- Hide dark mode and language selectors from footer on homepage
- Right-arrow icons on all hero cards with hover animation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.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: 9

🧹 Nitpick comments (1)
front_end/src/app/(main)/(home)/components/hero_globe_background.tsx (1)

28-35: Consider honoring reduced-motion for the animated shader background.

This decorative animation currently always runs. Hiding it for prefers-reduced-motion users improves accessibility and avoids unnecessary GPU work on constrained devices.

♿ Suggested adjustment
 const HeroGlobeBackground: FC = () => {
   return (
-    <div className="pointer-events-none absolute inset-0 z-0 overflow-hidden opacity-60">
-      <div className="absolute -top-[20%] h-[130%] w-[130%]">
-        <DitheringShader />
-      </div>
-    </div>
+    <>
+      <div className="absolute inset-0 z-0 bg-[`#283c52`] opacity-60" aria-hidden="true" />
+      <div className="pointer-events-none absolute inset-0 z-0 overflow-hidden opacity-60 motion-reduce:hidden" aria-hidden="true">
+        <div className="absolute -top-[20%] h-[130%] w-[130%]">
+          <DitheringShader />
+        </div>
+      </div>
+    </>
   );
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/src/app/`(main)/(home)/components/hero_globe_background.tsx around
lines 28 - 35, The HeroGlobeBackground component currently always renders the
animated DitheringShader; update HeroGlobeBackground to respect the user's
prefers-reduced-motion setting and skip or hide the DitheringShader when reduced
motion is requested. Detect prefers-reduced-motion (via CSS media query or a
small React hook using window.matchMedia) inside HeroGlobeBackground, and
conditionally render or replace <DitheringShader /> with a static/non-animated
fallback (or null), ensuring the element remains aria-hidden if purely
decorative; reference the HeroGlobeBackground component and the DitheringShader
symbol when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@front_end/messages/cs.json`:
- Around line 1909-1915: The JSON contains two values still in English: the keys
"clarityInAComplexWorld" and "measuringForecastingAccuracyOfAI" should be
translated to Czech to avoid mixed-language UI; update the value for
"clarityInAComplexWorld" from "Clarity in a complex world" to a Czech
translation (e.g., "Jasnost v složitém světě") and update
"measuringForecastingAccuracyOfAI" from "Our AI Forecasting Benchmark" to an
appropriate Czech string (e.g., "Náš benchmark přesnosti AI prognóz"),
preserving the keys and JSON structure.

In `@front_end/messages/es.json`:
- Around line 1904-1910: Translate the untranslated English values for the keys
clarityInAComplexWorld and measuringForecastingAccuracyOfAI in the Spanish
messages file so the Spanish locale is consistent; replace "Clarity in a complex
world" with an appropriate Spanish translation (e.g., "Claridad en un mundo
complejo") and replace "Our AI Forecasting Benchmark" with a Spanish equivalent
(e.g., "Nuestro punto de referencia de predicción con IA"), preserving the
existing JSON key names and surrounding punctuation.

In `@front_end/messages/pt.json`:
- Around line 1902-1912: Several newly added Portuguese strings are still in
English and must be translated; update the values for the keys
"clarityInAComplexWorld", "measuringForecastingAccuracyOfAI",
"howSoonDoYouNeedForecasts", "learnAboutMetaculusPros" and the
confidentiality/copy strings referenced in the same diff so the pt.json file
contains Portuguese text (also check the same set in the other added range
around lines 1998-2016). Locate these keys in front_end/messages/pt.json (e.g.
"clarityInAComplexWorld", "measuringForecastingAccuracyOfAI",
"howSoonDoYouNeedForecasts", "learnAboutMetaculusPros" and the confidentiality
copy keys) and replace the English values with correct Portuguese translations,
preserving JSON quoting and punctuation and not changing the key names.

In `@front_end/messages/zh.json`:
- Around line 1906-1913: The zh.json locale contains untranslated English
strings for keys clarityInAComplexWorld, measuringForecastingAccuracyOfAI, and
radiant; update those entries with the correct Chinese translations (replace the
English text values for "clarityInAComplexWorld",
"measuringForecastingAccuracyOfAI", and "radiant" in front_end/messages/zh.json)
so the homepage UI is fully localized for Chinese users, and run a quick
lint/validation of the JSON to ensure no syntax errors after editing.

In `@front_end/src/app/`(main)/(home)/components/featured_in_marquee.tsx:
- Around line 86-116: The cloned logos created by const logos = [...FEATURED_IN,
...FEATURED_IN, ...FEATURED_IN] are currently all accessible; update the
rendering in the logos.map(...) so only the first set of FEATURED_IN is
keyboard/screen-reader accessible and the cloned copies are hidden: compute a
boolean (e.g. isClone = index >= FEATURED_IN.length) inside the map and add
aria-hidden={isClone} and tabIndex={isClone ? -1 : 0} (or omit tabIndex for
originals) to the anchor element (the <a> with href={item.href}) so that only
the primary items (from FEATURED_IN) are in the accessibility tree while the
duplicated items used for visual infinite scroll remain inert.

In `@front_end/src/app/`(main)/(home)/components/force_light_mode.tsx:
- Line 3: Replace the import of useEffect with useLayoutEffect and update any
hook usage in this file (the hook call currently on lines 6-11) from
useEffect(...) to useLayoutEffect(...) so the class toggle runs synchronously
before paint and prevents the initial dark-mode flash; ensure the import
statement now reads import { useLayoutEffect } from "react" and the component's
existing effect callback is unchanged aside from switching to useLayoutEffect.

In `@front_end/src/app/`(main)/(home)/components/forecasts_carousel_section.tsx:
- Around line 84-105: The carousel arrow buttons keep being focusable and use
hardcoded English labels; update the component to call useTranslations() (e.g.,
const t = useTranslations(...)) and replace the aria-labels with t(...) keys
(e.g., t('scrollLeft') / t('scrollRight')), and make the buttons actually
non-interactive when canScrollLeft/canScrollRight are false by adding the
disabled attribute (disabled={!canScrollLeft} / disabled={!canScrollRight}) and
keeping the visual classes (or adjusting them) so pointer-events and opacity
still reflect state; ensure the existing scroll(...) handler and className logic
(canScrollLeft, canScrollRight, scroll) are preserved while adding translated
labels and disabled props.

In `@front_end/src/app/`(main)/(home)/components/hero_section.tsx:
- Around line 104-109: Replace the assembled label that concatenates
stats.years_of_predictions + t("years") + t("ofPredictions") with a single i18n
key that accepts a count/placeholder and handles pluralization and
locale-specific ordering; e.g., change the call inside the component that
currently uses stats.years_of_predictions and t(...) to one call like
t("yearsOfPredictions", { count: stats.years_of_predictions }) (update the
translation JSONs/namespace to add "yearsOfPredictions" with a "{count}"
placeholder and plural forms for each locale), and remove the manual
concatenation around the <strong> element so all visible text comes from that
single t(...) invocation.

In `@front_end/src/app/`(main)/(home)/components/homepage_post_card.tsx:
- Around line 56-60: The mobile card uses CSS transform scale which only
visually shrinks content but leaves the layout box unchanged; remove the
scale-75/md:scale-100 wrapper and instead render ConsumerQuestionTile with an
explicit compact/responsive sizing approach (e.g., pass a compact prop like
compact={true} to ConsumerQuestionTile or give the wrapper explicit responsive
width/height/typography classes) and, if ConsumerQuestionTile lacks a compact
API, add a compact prop to ConsumerQuestionTile to apply smaller
paddings/font-sizes and reduced container dimensions; keep the conditional
guards isQuestionPost(post) and !isMultipleChoicePost(post) unchanged.

---

Nitpick comments:
In `@front_end/src/app/`(main)/(home)/components/hero_globe_background.tsx:
- Around line 28-35: The HeroGlobeBackground component currently always renders
the animated DitheringShader; update HeroGlobeBackground to respect the user's
prefers-reduced-motion setting and skip or hide the DitheringShader when reduced
motion is requested. Detect prefers-reduced-motion (via CSS media query or a
small React hook using window.matchMedia) inside HeroGlobeBackground, and
conditionally render or replace <DitheringShader /> with a static/non-animated
fallback (or null), ensuring the element remains aria-hidden if purely
decorative; reference the HeroGlobeBackground component and the DitheringShader
symbol when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cd51280b-57db-48ee-b16b-87fbf71a9f93

📥 Commits

Reviewing files that changed from the base of the PR and between 7f5364d and 2ed73fb.

⛔ Files ignored due to path filters (1)
  • front_end/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (36)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/package.json
  • front_end/src/app/(main)/(home)/components/all_categories_section.tsx
  • front_end/src/app/(main)/(home)/components/featured_in_marquee.tsx
  • front_end/src/app/(main)/(home)/components/force_light_mode.tsx
  • front_end/src/app/(main)/(home)/components/forecasts_carousel_section.tsx
  • front_end/src/app/(main)/(home)/components/future_eval_section.tsx
  • front_end/src/app/(main)/(home)/components/future_eval_table.tsx
  • front_end/src/app/(main)/(home)/components/hero_ctas.tsx
  • front_end/src/app/(main)/(home)/components/hero_futureeval_symbol.tsx
  • front_end/src/app/(main)/(home)/components/hero_globe_background.tsx
  • front_end/src/app/(main)/(home)/components/hero_section.tsx
  • front_end/src/app/(main)/(home)/components/homepage_forecasts.tsx
  • front_end/src/app/(main)/(home)/components/homepage_post_card.tsx
  • front_end/src/app/(main)/(home)/components/metaculus_storefront_logo.tsx
  • front_end/src/app/(main)/(home)/components/newsletter_subscription.tsx
  • front_end/src/app/(main)/(home)/components/radiant_logo.tsx
  • front_end/src/app/(main)/(home)/components/research_and_updates.tsx
  • front_end/src/app/(main)/(home)/components/staff_picks.tsx
  • front_end/src/app/(main)/(home)/components/tournaments_section.tsx
  • front_end/src/app/(main)/(home)/components/why_metaculus.tsx
  • front_end/src/app/(main)/(home)/page.tsx
  • front_end/src/app/(main)/components/footer.tsx
  • front_end/src/app/(main)/components/layout_content_wrapper.tsx
  • front_end/src/app/(main)/layout.tsx
  • front_end/src/components/consumer_post_card/group_forecast_card/forecast_card_wrapper.tsx
  • front_end/src/components/consumer_post_card/group_forecast_card/forecast_choice_bar.tsx
  • front_end/src/components/consumer_post_card/group_forecast_card/index.tsx
  • front_end/src/components/consumer_post_card/group_forecast_card/numeric_forecast_card.tsx
  • front_end/src/components/consumer_post_card/group_forecast_card/percentage_forecast_card.tsx
  • front_end/src/utils/navigation.ts
💤 Files with no reviewable changes (9)
  • front_end/src/app/(main)/(home)/components/future_eval_section.tsx
  • front_end/src/app/(main)/(home)/components/why_metaculus.tsx
  • front_end/src/app/(main)/(home)/components/hero_ctas.tsx
  • front_end/src/app/(main)/(home)/components/homepage_forecasts.tsx
  • front_end/src/app/(main)/(home)/components/future_eval_table.tsx
  • front_end/src/app/(main)/(home)/components/newsletter_subscription.tsx
  • front_end/src/app/(main)/(home)/components/all_categories_section.tsx
  • front_end/src/app/(main)/(home)/components/research_and_updates.tsx
  • front_end/src/app/(main)/(home)/components/tournaments_section.tsx

Comment on lines +1906 to +1913
"clarityInAComplexWorld": "Clarity in a complex world",
"forecastingPlatform": "预测平台",
"businessSolutions": "商业解决方案",
"collectiveIntelligenceForPublicGood": "为公共利益服务的集体智慧",
"forInformedDecisionMaking": "为明智决策服务",
"measuringForecastingAccuracyOfAI": "Our AI Forecasting Benchmark",
"mapTheFutureBeforeYouBuildIt": "在构建之前绘制未来蓝图",
"radiant": "Radiant",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

New zh locale entries include untranslated English copy.

Line 1906 (clarityInAComplexWorld), Line 1911 (measuringForecastingAccuracyOfAI), and Line 1913 (radiant) are still English, so Chinese users will see mixed-language UI in the updated homepage sections.

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

In `@front_end/messages/zh.json` around lines 1906 - 1913, The zh.json locale
contains untranslated English strings for keys clarityInAComplexWorld,
measuringForecastingAccuracyOfAI, and radiant; update those entries with the
correct Chinese translations (replace the English text values for
"clarityInAComplexWorld", "measuringForecastingAccuracyOfAI", and "radiant" in
front_end/messages/zh.json) so the homepage UI is fully localized for Chinese
users, and run a quick lint/validation of the JSON to ensure no syntax errors
after editing.

Comment on lines 56 to 60
{isQuestionPost(post) && !isMultipleChoicePost(post) && (
<ConsumerQuestionTile question={post.question} />
<div className="scale-75 md:scale-100">
<ConsumerQuestionTile question={post.question} />
</div>
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

scale-75 won’t actually make the mobile card more compact.

transform: scale() shrinks the rendering, but the wrapper still keeps the full ConsumerQuestionTile layout box. These cards can end up with extra dead space or clipped content inside the carousel. Prefer a compact prop or explicit responsive sizing here.

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

In `@front_end/src/app/`(main)/(home)/components/homepage_post_card.tsx around
lines 56 - 60, The mobile card uses CSS transform scale which only visually
shrinks content but leaves the layout box unchanged; remove the
scale-75/md:scale-100 wrapper and instead render ConsumerQuestionTile with an
explicit compact/responsive sizing approach (e.g., pass a compact prop like
compact={true} to ConsumerQuestionTile or give the wrapper explicit responsive
width/height/typography classes) and, if ConsumerQuestionTile lacks a compact
API, add a compact prop to ConsumerQuestionTile to apply smaller
paddings/font-sizes and reduced container dimensions; keep the conditional
guards isQuestionPost(post) and !isMultipleChoicePost(post) unchanged.

…ements

- Translate storefront keys (clarityInAComplexWorld, measuringForecastingAccuracyOfAI,
  howSoonDoYouNeedForecasts, learnAboutMetaculusPros) in cs, es, pt, zh, zh-TW
- Add aria-hidden/tabIndex to cloned marquee logos for screen reader accessibility
- Switch ForceLightMode from useEffect to useLayoutEffect to prevent dark flash
- Add disabled prop and translated aria-labels to carousel scroll buttons
- Replace hero stats concatenation with single i18n key (yearsOfPredictions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
front_end/messages/cs.json (1)

2009-2021: ⚠️ Potential issue | 🟡 Minor

Mixed-language block remains in the Pro Forecasters section.

After the update on Line 2009, Lines 2010 and 2018-2021 are still English, which will show mixed-language UI in Czech locale.

🌐 Suggested Czech translations
-  "metaculusProsDescription": "Metaculus Pro Forecasters are a vetted network of skilled forecasters with proven track records on the Metaculus platform. They specialize in making well-calibrated predictions across diverse domains.",
+  "metaculusProsDescription": "Metaculus Pro Forecasters jsou prověřená síť zkušených prognostiků s prokazatelnými výsledky na platformě Metaculus. Specializují se na dobře kalibrované předpovědi napříč různými oblastmi.",
-  "semiConfidentialityDescription": "Organization hidden but forecasts can be public",
+  "semiConfidentialityDescription": "Organizace je skrytá, ale předpovědi mohou být veřejné",
-  "fullConfidentialityDescription": "Both organization and forecasts hidden",
+  "fullConfidentialityDescription": "Organizace i předpovědi jsou skryté",
-  "noConfidentiality": "No confidentiality",
+  "noConfidentiality": "Bez důvěrnosti",
-  "noConfidentialityDescription": "Organization and forecasts are public",
+  "noConfidentialityDescription": "Organizace i předpovědi jsou veřejné",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/messages/cs.json` around lines 2009 - 2021, Several message values
in the Czech locale are still in English: update the keys
"metaculusProsDescription", "semiConfidentialityDescription",
"fullConfidentialityDescription", and "noConfidentialityDescription" to proper
Czech translations (keeping same intent and tone as surrounding keys like
"learnAboutMetaculusPros" and "semiConfidentiality"), ensuring grammar and
punctuation match the locale and that no mixed-language strings remain in the
Pro Forecasters/confidentiality section.
🧹 Nitpick comments (3)
front_end/src/app/(main)/(home)/components/force_light_mode.tsx (1)

27-35: Consider defensive handling for localStorage access.

In rare cases (e.g., certain private browsing modes or when storage is disabled), localStorage.getItem can throw a SecurityError. Wrapping this in a try-catch would prevent the cleanup from failing silently.

🛡️ Optional defensive fix
     return () => {
       observer.disconnect();
       // Restore dark mode if the user's saved preference requires it
-      const saved = localStorage.getItem("theme");
+      let saved: string | null = null;
+      try {
+        saved = localStorage.getItem("theme");
+      } catch {
+        // Storage access denied
+      }
       if (
         saved === "dark" ||
         (!saved && window.matchMedia("(prefers-color-scheme: dark)").matches) ||
         (saved === "system" &&
           window.matchMedia("(prefers-color-scheme: dark)").matches)
       ) {
         html.classList.add("dark");
       }
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/src/app/`(main)/(home)/components/force_light_mode.tsx around lines
27 - 35, Wrap the localStorage access in a try-catch inside the module where the
theme is computed: protect the call to localStorage.getItem("theme") (and any
subsequent uses) by catching SecurityError/any exception and falling back to
null/undefined so the rest of the logic in this file (the block that reads saved
and uses window.matchMedia and html.classList.add("dark")) can continue; also
defensively check window and window.matchMedia exist before calling matches.
Target the localStorage.getItem call and the saved variable in this file
(force_light_mode.tsx) and ensure errors are caught and handled by using a safe
default rather than letting the exception propagate.
front_end/messages/cs.json (1)

2003-2003: Key intent drift in Czech copy for forecast timing.

Line 2003 currently asks about “our services,” while the key is specifically about forecasts. This can make the step less precise.

✏️ Suggested wording tweak
-  "howSoonDoYouNeedForecasts": "Jak brzy potřebujete naše služby?",
+  "howSoonDoYouNeedForecasts": "Jak brzy potřebujete prognózy?",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/messages/cs.json` at line 2003, The Czech string for the key
"howSoonDoYouNeedForecasts" currently mentions “our services” instead of
explicitly referring to forecasts; update the value for the
"howSoonDoYouNeedForecasts" key so it specifically asks about forecasts (e.g.,
use wording with "předpovědi" such as "Jak brzy potřebujete předpovědi?" or "Jak
brzy potřebujete naše předpovědi?") to match the key intent.
front_end/src/app/(main)/(home)/components/forecasts_carousel_section.tsx (1)

39-50: Recompute scroll affordance state when carousel data changes.

updateScrollState is only wired to scroll/resize events. If initialPosts changes after mount, the arrow/gradient state can remain stale until a user scroll or resize occurs. Consider re-running this effect when the filtered list size changes.

Suggested patch
   useEffect(() => {
     const el = scrollRef.current;
     if (!el) return;
     updateScrollState();
     el.addEventListener("scroll", updateScrollState, { passive: true });
     const observer = new ResizeObserver(updateScrollState);
     observer.observe(el);
     return () => {
       el.removeEventListener("scroll", updateScrollState);
       observer.disconnect();
     };
-  }, [updateScrollState]);
+  }, [updateScrollState, posts.length]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front_end/src/app/`(main)/(home)/components/forecasts_carousel_section.tsx
around lines 39 - 50, The scroll affordance state can become stale when the
carousel data changes because useEffect only listens to scroll/resize; update
the effect to also re-run when the carousel data size changes (e.g.,
initialPosts.length or the filtered posts array length) so updateScrollState is
invoked after data updates; specifically, add the data-length dependency to the
dependency array for the effect that references scrollRef and updateScrollState
(the effect that sets up el.addEventListener/ResizeObserver) so it re-runs and
calls updateScrollState whenever the list of posts changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@front_end/messages/cs.json`:
- Around line 2009-2021: Several message values in the Czech locale are still in
English: update the keys "metaculusProsDescription",
"semiConfidentialityDescription", "fullConfidentialityDescription", and
"noConfidentialityDescription" to proper Czech translations (keeping same intent
and tone as surrounding keys like "learnAboutMetaculusPros" and
"semiConfidentiality"), ensuring grammar and punctuation match the locale and
that no mixed-language strings remain in the Pro Forecasters/confidentiality
section.

---

Nitpick comments:
In `@front_end/messages/cs.json`:
- Line 2003: The Czech string for the key "howSoonDoYouNeedForecasts" currently
mentions “our services” instead of explicitly referring to forecasts; update the
value for the "howSoonDoYouNeedForecasts" key so it specifically asks about
forecasts (e.g., use wording with "předpovědi" such as "Jak brzy potřebujete
předpovědi?" or "Jak brzy potřebujete naše předpovědi?") to match the key
intent.

In `@front_end/src/app/`(main)/(home)/components/force_light_mode.tsx:
- Around line 27-35: Wrap the localStorage access in a try-catch inside the
module where the theme is computed: protect the call to
localStorage.getItem("theme") (and any subsequent uses) by catching
SecurityError/any exception and falling back to null/undefined so the rest of
the logic in this file (the block that reads saved and uses window.matchMedia
and html.classList.add("dark")) can continue; also defensively check window and
window.matchMedia exist before calling matches. Target the localStorage.getItem
call and the saved variable in this file (force_light_mode.tsx) and ensure
errors are caught and handled by using a safe default rather than letting the
exception propagate.

In `@front_end/src/app/`(main)/(home)/components/forecasts_carousel_section.tsx:
- Around line 39-50: The scroll affordance state can become stale when the
carousel data changes because useEffect only listens to scroll/resize; update
the effect to also re-run when the carousel data size changes (e.g.,
initialPosts.length or the filtered posts array length) so updateScrollState is
invoked after data updates; specifically, add the data-length dependency to the
dependency array for the effect that references scrollRef and updateScrollState
(the effect that sets up el.addEventListener/ResizeObserver) so it re-runs and
calls updateScrollState whenever the list of posts changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a183168f-a13e-4082-b7a5-f2f3dfed7c7a

📥 Commits

Reviewing files that changed from the base of the PR and between 2ed73fb and f086687.

📒 Files selected for processing (10)
  • front_end/messages/cs.json
  • front_end/messages/en.json
  • front_end/messages/es.json
  • front_end/messages/pt.json
  • front_end/messages/zh-TW.json
  • front_end/messages/zh.json
  • front_end/src/app/(main)/(home)/components/featured_in_marquee.tsx
  • front_end/src/app/(main)/(home)/components/force_light_mode.tsx
  • front_end/src/app/(main)/(home)/components/forecasts_carousel_section.tsx
  • front_end/src/app/(main)/(home)/components/hero_section.tsx
✅ Files skipped from review due to trivial changes (1)
  • front_end/src/app/(main)/(home)/components/featured_in_marquee.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
  • front_end/messages/es.json
  • front_end/messages/en.json
  • front_end/src/app/(main)/(home)/components/hero_section.tsx
  • front_end/messages/zh.json
  • front_end/messages/zh-TW.json
  • front_end/messages/pt.json

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.

1 participant