Skip to content

fix(react): address Doctor findings#1143

Merged
tomcasaburi merged 25 commits into
masterfrom
codex/fix/doctor-score
May 29, 2026
Merged

fix(react): address Doctor findings#1143
tomcasaburi merged 25 commits into
masterfrom
codex/fix/doctor-score

Conversation

@tomcasaburi
Copy link
Copy Markdown
Member

@tomcasaburi tomcasaburi commented May 28, 2026

Summary

  • replace interactive non-controls with semantic buttons, outputs, and images while preserving existing imageboard styling
  • extract embed/catalog helpers to reduce React Doctor component-boundary findings
  • keep challenge iframes functional under the stricter embed sandbox behavior

Verification

  • corepack yarn doctor (90 / 100 Great)
  • corepack yarn type-check
  • corepack yarn lint
  • corepack yarn test (135 passed, 914 tests)
  • corepack yarn build

Note

Medium Risk
Large UI surface area (modals, media, comments) with a functional regression risk from removing edited-comment original view and stricter iframe sandbox rules.

Overview
This PR is a broad accessibility and semantics pass: many role="button" spans and div backdrops become real <button> elements, modals use native <dialog open>, media load states use <output>, and inputs gain aria-labels across boards bar, catalog filters/search, challenge flow, and comment media.

Comment and media UX changes alongside that: edited-post “show/hide original” UI and its i18n keys are removed; privileged authors skip truncated “read more” behavior; reply show/hide and mod/ban strings are added in all locale files. Challenge iframes get origin-aware sandbox via getIframeSandbox, and the completion control is renamed to Finish Challenge.

Styling updates reset button appearance and add :focus-visible outlines so the imageboard look stays intact.

Reviewed by Cursor Bugbot for commit a7c2d21. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Accessibility & UX Improvements
    • Replaced many non-semantic controls with real buttons, added ARIA labels, improved keyboard focus/interaction and screen-reader behavior.
  • Modals & Dialogs
    • Switched multiple overlays to native dialogs and improved backdrop and close handling.
  • Media & Embeds
    • Better keyboard focus for media thumbnails/previews; standardized embed host checks and safer iframe sandboxing.
  • Styling
    • Normalized button/reset styles and strengthened focus-visible outlines sitewide.
  • Localization
    • Added moderation/reply-related translation keys across many locales; removed some legacy keys.

Review Change Stack

Replace interactive non-controls with semantic controls while preserving the classic imageboard styling.

Refactor embed and catalog helpers for clearer React boundaries and preserve challenge iframe behavior.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

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

Project Deployment Actions Updated (UTC)
5chan Ready Ready Preview, Comment May 29, 2026 9:57am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6c8d9127-16b2-4b1e-9257-afd611e6d7fd

📥 Commits

Reviewing files that changed from the base of the PR and between afa2cda and a7c2d21.

📒 Files selected for processing (39)
  • public/translations/ar/default.json
  • public/translations/bn/default.json
  • public/translations/cs/default.json
  • public/translations/da/default.json
  • public/translations/de/default.json
  • public/translations/el/default.json
  • public/translations/en/default.json
  • public/translations/es/default.json
  • public/translations/fa/default.json
  • public/translations/fi/default.json
  • public/translations/fil/default.json
  • public/translations/fr/default.json
  • public/translations/he/default.json
  • public/translations/hi/default.json
  • public/translations/hu/default.json
  • public/translations/id/default.json
  • public/translations/it/default.json
  • public/translations/ja/default.json
  • public/translations/ko/default.json
  • public/translations/mr/default.json
  • public/translations/nl/default.json
  • public/translations/no/default.json
  • public/translations/pl/default.json
  • public/translations/pt/default.json
  • public/translations/ro/default.json
  • public/translations/ru/default.json
  • public/translations/sq/default.json
  • public/translations/sv/default.json
  • public/translations/te/default.json
  • public/translations/th/default.json
  • public/translations/tr/default.json
  • public/translations/uk/default.json
  • public/translations/ur/default.json
  • public/translations/vi/default.json
  • public/translations/zh/default.json
  • src/components/catalog-row/catalog-row.tsx
  • src/components/comment-content/__tests__/comment-content.test.tsx
  • src/components/comment-content/comment-content.tsx
  • src/views/post/post.module.css
💤 Files with no reviewable changes (36)
  • public/translations/he/default.json
  • public/translations/zh/default.json
  • public/translations/da/default.json
  • public/translations/fr/default.json
  • public/translations/it/default.json
  • public/translations/es/default.json
  • public/translations/ko/default.json
  • public/translations/bn/default.json
  • public/translations/ar/default.json
  • public/translations/ru/default.json
  • public/translations/el/default.json
  • public/translations/sq/default.json
  • public/translations/cs/default.json
  • public/translations/en/default.json
  • public/translations/mr/default.json
  • public/translations/no/default.json
  • public/translations/vi/default.json
  • src/components/comment-content/tests/comment-content.test.tsx
  • public/translations/uk/default.json
  • public/translations/th/default.json
  • public/translations/fa/default.json
  • public/translations/pt/default.json
  • public/translations/hu/default.json
  • public/translations/ja/default.json
  • public/translations/hi/default.json
  • public/translations/fi/default.json
  • public/translations/pl/default.json
  • public/translations/ur/default.json
  • public/translations/tr/default.json
  • public/translations/nl/default.json
  • public/translations/sv/default.json
  • public/translations/fil/default.json
  • public/translations/ro/default.json
  • public/translations/te/default.json
  • public/translations/de/default.json
  • public/translations/id/default.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/catalog-row/catalog-row.tsx

📝 Walkthrough

Walkthrough

Converts non-semantic interactive elements to semantic buttons, moves custom modal/backdrop logic to native with backdrop buttons, extracts embed host allowlists and sandbox handling, switches views to use routerLocation, and updates CSS, tests, and translations.

Changes

Semantic controls, ARIA, and UI interaction updates

Layer / File(s) Summary
Buttons, aria, and small UI edits
src/components/*, src/views/*
Many role-based span/div elements replaced with <button type="button">; inputs gained aria-labels; role="status"/status divs often replaced with <output>; challenge tracking now calls useChallengesStore.getState().addChallenge(...) inline.
Post/menu/search/filters
src/components/{post-*,board-*,catalog-*}/*
Search toggles, catalog filters, post menus, boards bar, and related controls converted to semantic buttons; some label text and input placeholders reworked.
Markdown & BBCode
src/components/markdown/*
Embed toggles converted to buttons; BBCode helper renamed/refactored and line-key generation adjusted; preview positioning memoized with useCallback.
Store call updates
src/components/*, src/views/*
Replaced module-level destructured addChallenge usage with direct store method calls in multiple moderation flows.

Modal architecture to native dialog/backdrop buttons

Layer / File(s) Summary
Modals -> + backdrop button
src/components/{boards-bar-edit-modal,create-board-modal,directory-modal,disclaimer-modal,settings-modal}/*
Modal markup replaced with native <dialog open> and explicit backdrop close <button>; header/footer close controls typed; dialog z-index and layout adjusted; related tests updated.
Settings modal
src/components/settings-modal/*
Category headers and expand/collapse controls converted to <button>; overlay/backdrop converted to buttons; many inputs gained aria labels; CSS reset rules added for consistent button styling.

Embed host logic modularization and sandboxing

Layer / File(s) Summary
Embed utils and iframe sandbox
src/components/embed/*
Added embed-utils with per-provider host allowlists and getXTweetId/canEmbed; embed.tsx imports these and applies remoteEmbedSandbox to provider iframes; index exports adjusted and tests updated.

Routing state and catalog helper refactors

Layer / File(s) Summary
Board & Catalog routing/state
src/views/{board,catalog}/*
Switched from location to routerLocation for request/virt state keys, redirects, and footer/search props; extracted getCatalogRenderFeed to its own module; stabilized footer memos and Virtuoso state keys.

CSS resets and focus-visible rules

Layer / File(s) Summary
CSS resets & focus-visible
src/components/*/*.module.css, src/views/*/*.module.css
Added all: unset/appearance resets, normalized button styling, introduced consistent :focus-visible dotted outlines, and broadened selectors to include button variants.

Tests and translations

Layer / File(s) Summary
Tests
src/**/__tests__/*
Updated DOM queries from role-based selectors to button, dialog, or output as appropriate; adjusted a few URL query param expectations (img_url -> url).
Translations
public/translations/*/default.json
Added new moderation/reply-related keys across many locales: show_replies, hide_replies, unhide, account_data, ban_duration_days, challenge_answer, ban, timestamp.

Estimated code review effort
🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

"I hopped through the code with nimble paws,
Spans became buttons, focus rings found their cause,
Dialogs went native, embeds mind their bounds,
Routes now follow routerLocation’s sounds,
A rabbit's small bless: accessible ground." 🐇

✨ 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 codex/fix/doctor-score

Comment thread src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx Outdated
Copy link
Copy Markdown

@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: 19

Caution

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

⚠️ Outside diff range comments (11)
src/components/settings-modal/subscriptions-setting/subscriptions-setting.tsx (1)

25-35: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove redundant tabIndex/manual onKeyDown from native <button> elements.

<button> already activates on Enter/Space (via its default click behavior), so calling toggleSubscription() / unsubscribeAll() inside onKeyDown can run the same action twice; tabIndex={0} is also unnecessary for native buttons.

Suggested fix
       <button
         type='button'
         className={styles.button}
-        tabIndex={0}
-        onKeyDown={(e) => {
-          if (e.key === 'Enter' || e.key === ' ') {
-            e.preventDefault();
-            toggleSubscription();
-          }
-        }}
         onClick={toggleSubscription}
       >
         {recentlyUnsubscribed || !subscribed ? t('subscribe') : t('unsubscribe')}
       </button>
@@
               <button
                 type='button'
                 className={styles.button}
-                tabIndex={0}
-                onKeyDown={(e) => {
-                  if (e.key === 'Enter' || e.key === ' ') {
-                    e.preventDefault();
-                    unsubscribeAll();
-                  }
-                }}
                 onClick={unsubscribeAll}
               >
                 {t('unsubscribe_all')}
               </button>
🤖 Prompt for 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.

In
`@src/components/settings-modal/subscriptions-setting/subscriptions-setting.tsx`
around lines 25 - 35, The button handler duplicates native behavior: remove the
manual keyboard handling and explicit tabIndex on native <button> elements to
avoid double-invocation; specifically, delete tabIndex={0} and the onKeyDown
block from the button that calls toggleSubscription (and any similar buttons
like the one that calls unsubscribeAll) and rely solely on
onClick={toggleSubscription} (or onClick={unsubscribeAll}) for activation.
src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx (1)

139-160: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don’t nest image-search <a> links inside a <button> in ImageSearchButtons.
src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx (lines 139-160): interactive nesting (<a> inside <button>) is invalid HTML and can cause inconsistent focus/activation behavior (especially on mobile). Replace the outer <button> with a non-interactive wrapper and keep onClose via click bubbling or per-link onClick.

🤖 Prompt for 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.

In `@src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx` around
lines 139 - 160, The code nests multiple <a> links inside an outer <button>,
which is invalid HTML; replace the outer <button> wrapper with a non-interactive
container (e.g., a <div> or <nav> with an appropriate role) and remove the
wrapper's tabIndex/onKeyDown, then attach the existing onClose behavior to each
link by adding onClick handlers (and onKeyDown for keyboard activation if
needed) on the three anchors that use encodedUrl; keep t(...) and
styles.postMenuItem unchanged so each <a> remains interactive and closing
behavior still fires when a link is activated.
src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx (1)

130-161: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Fix invalid nesting of <a> inside <button> in image search menus.

Both the desktop ImageSearchButton and the mobile ImageSearchButtons render the trigger <button> containing <a> links (desktop: ~130–161 in src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx; mobile: ~135–160 in src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx), which is invalid interactive nesting and can break keyboard/focus behavior. Keep the button as the trigger and move the link list into a sibling/container element outside the button.

🤖 Prompt for 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.

In `@src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx` around
lines 130 - 161, The image-search trigger currently nests anchor links inside
the <button> (see the button using refs.setReference, onClose,
isImageSearchMenuOpen and refs.setFloating with encodedUrl), which is invalid;
refactor so the <button> remains only the trigger and the dropdown link list is
rendered as a sibling container (e.g., a <div> or <nav> with className
styles.dropdownMenu) outside the button; move the three <a> anchors
(Google/Yandex/SauceNAO) into that sibling, keep floatingStyles and
refs.setFloating on that sibling, preserve open/close logic tied to
isImageSearchMenuOpen, maintain the same onMouseOver/onFocus/onMouseLeave/onBlur
handlers on the trigger (or delegate them to the sibling container as needed),
and add appropriate aria-expanded/aria-controls or id linking between the button
and the dropdown to preserve keyboard accessibility.
src/components/reply-quote-preview/reply-quote-preview.tsx (1)

352-359: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Focus/blur handlers won’t run on non-focusable spans

Line 356/358 and Line 409/411 attach keyboard-focus behavior to <span>, but these elements are not focusable by default, so keyboard users still can’t trigger preview on focus.

Suggested fix
-      <span
+      <button
+        type="button"
         className={styles.backlink}
         ref={refs.setReference}
         onMouseOver={() => handleMouseOver(normalizedBacklinkReply?.cid)}
         onFocus={() => handleMouseOver(normalizedBacklinkReply?.cid)}
         onMouseLeave={() => handleMouseLeave(normalizedBacklinkReply?.cid)}
         onBlur={() => handleMouseLeave(normalizedBacklinkReply?.cid)}
       >
         {`>>${normalizedBacklinkReply?.number ?? '?'}`}
-      </span>
+      </button>
-      <span
+      <button
+        type="button"
         ref={quotelinkUnavailable || quotelinkPendingResolution ? undefined : refs.setReference}
         className={quotelinkPendingResolution ? styles.quoteLink : quotelinkClassName}
         onMouseOver={quotelinkUnavailable || quotelinkPendingResolution ? undefined : () => handleMouseOver(resolvedQuotelinkCid)}
         onFocus={quotelinkUnavailable || quotelinkPendingResolution ? undefined : () => handleMouseOver(resolvedQuotelinkCid)}
         onMouseLeave={quotelinkUnavailable || quotelinkPendingResolution ? undefined : () => handleMouseLeave(resolvedQuotelinkCid)}
         onBlur={quotelinkUnavailable || quotelinkPendingResolution ? undefined : () => handleMouseLeave(resolvedQuotelinkCid)}
       >
         {formatQuoteNumber(resolvedQuotelinkNumber)}
         {isOP && ' (OP)'}
         {isOwnQuotelink && ' (You)'}
-      </span>
+      </button>

Also applies to: 405-412

🤖 Prompt for 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.

In `@src/components/reply-quote-preview/reply-quote-preview.tsx` around lines 352
- 359, The span with class styles.backlink (the element using refs.setReference
and event handlers handleMouseOver/handleMouseLeave with
normalizedBacklinkReply?.cid) is not keyboard-focusable, so focus/blur handlers
won't fire; make the element keyboard-accessible by adding tabIndex={0} (and
consider role="button" and appropriate aria-label if it behaves like an
interactive control) and ensure keyboard activation is handled (e.g., call the
same preview logic on Enter/Space via onKeyDown) so keyboard users can trigger
the preview; apply the same changes to the other similar span that attaches
focus/blur handlers.
src/components/embed/embed.tsx (1)

30-31: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align Reddit rendering with canEmbed eligibility.

canEmbed rejects reddit URLs without /comments/, but Embed still renders RedditEmbed for any reddit host. This creates inconsistent behavior for unsupported reddit URLs.

Proposed fix
-  if (redditHosts.has(parsedUrl.host)) {
+  if (redditHosts.has(parsedUrl.host) && parsedUrl.pathname.includes('/comments/')) {
     return <RedditEmbed parsedUrl={parsedUrl} />;
   }
🤖 Prompt for 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.

In `@src/components/embed/embed.tsx` around lines 30 - 31, The Embed component
currently returns <RedditEmbed parsedUrl={parsedUrl} /> for any host in
redditHosts even though canEmbed(parsedUrl) rejects reddit URLs that lack
"/comments/"; update the conditional in the Embed component to only render
RedditEmbed when the URL is both a reddit host and passes canEmbed (or otherwise
explicitly contains "/comments/"), i.e. change the reddit branch to check
canEmbed(parsedUrl) (or parsedUrl.pathname.includes('/comments/')) before
returning RedditEmbed so rendering matches canEmbed eligibility.
src/components/board-header/board-header.tsx (1)

116-123: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove custom Enter/Space key handling + redundant tabIndex from the native <button>

This <button> already supports built-in Enter/Space activation; the existing onKeyDown + onClick can cause duplicate navigation (especially for Space), and tabIndex={0} is unnecessary on a <button>.

Suggested patch
           <button
             type='button'
             className={styles.clickableSubtitle}
-            tabIndex={0}
-            onKeyDown={(e) => {
-              if (e.key === 'Enter' || e.key === ' ') {
-                e.preventDefault();
-                navigate('/subs/settings#subscriptions-settings');
-              }
-            }}
             onClick={() => navigate('/subs/settings#subscriptions-settings')}
           >
🤖 Prompt for 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.

In `@src/components/board-header/board-header.tsx` around lines 116 - 123, The
button that calls navigate('/subs/settings#subscriptions-settings') currently
has a redundant tabIndex={0} and a custom onKeyDown handler that manually
handles Enter/Space, which can cause duplicate navigation (especially for
Space); remove the tabIndex={0} and the entire onKeyDown={(e) => { ... }}
handler from that <button> and keep the existing onClick={() =>
navigate('/subs/settings#subscriptions-settings')} so the native button keyboard
activation semantics handle Enter/Space correctly.
src/components/board-pagination/board-pagination.tsx (1)

51-58: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove redundant keyboard handler for the “[All]” button

On a native <button>, onKeyDown for Enter/Space is unnecessary and duplicates the onClick behavior; drop onKeyDown (and the redundant tabIndex={0}) so the action fires only via the button’s native activation.

Suggested patch
                 <button
                   type='button'
                   className={styles.footerPageLink}
-                  tabIndex={0}
                   onClick={() => setEnableInfiniteScroll(true)}
-                  onKeyDown={(e) => {
-                    if (e.key === 'Enter' || e.key === ' ') {
-                      e.preventDefault();
-                      setEnableInfiniteScroll(true);
-                    }
-                  }}
                 >
                   {t('all')}
                 </button>
🤖 Prompt for 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.

In `@src/components/board-pagination/board-pagination.tsx` around lines 51 - 58,
The “[All]” control in BoardPagination uses a native button but also adds a
redundant tabIndex and onKeyDown handler; remove the tabIndex={0} and the
onKeyDown prop from the element that calls setEnableInfiniteScroll (the “[All]”
button) and keep the existing onClick={() => setEnableInfiniteScroll(true)} so
activation relies on the button's native keyboard handling; update the JSX in
the BoardPagination component accordingly (identify the element that invokes
setEnableInfiniteScroll).
src/components/board-blotter/board-blotter.tsx (1)

46-53: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove custom onKeyDown (Enter/Space) from the native <button>

src/components/board-blotter/board-blotter.tsx already uses a native <button type="button">, so the browser will activate it via Enter/Space. The extra onKeyDown handler (with preventDefault) duplicates that behavior and can lead to inconsistent Space/Enter handling across browsers; tabIndex={0} is also redundant.

Suggested patch
               <button
                 type='button'
                 className={styles.hideButton}
-                tabIndex={0}
-                onKeyDown={(e) => {
-                  if (e.key === 'Enter' || e.key === ' ') {
-                    e.preventDefault();
-                    toggleVisibility();
-                  }
-                }}
                 onClick={() => toggleVisibility()}
               >
🤖 Prompt for 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.

In `@src/components/board-blotter/board-blotter.tsx` around lines 46 - 53, Remove
the redundant keyboard handling and tabindex from the native button: delete the
custom onKeyDown handler and the tabIndex={0} prop in the component where the
button invokes toggleVisibility (the element using toggleVisibility and
onClick). Keep the native <button type="button"> and its onClick={() =>
toggleVisibility()} so the browser's built-in Enter/Space activation and focus
management remain intact.
src/components/boards-bar/boards-bar.tsx (1)

166-176: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove tabIndex={0} and Enter/Space onKeyDown shims from native <button> elements in boards-bar.tsx

Those native buttons are already keyboard-activatable and the Enter/Space onKeyDown handlers duplicate the existing onClick logic (and call preventDefault, which can change native Space activation timing). Drop tabIndex={0} and the onKeyDown blocks for all the occurrences (e.g., the placeholder button and the various temporaryButton/search buttons).

Suggested pattern cleanup
 <button
   type='button'
-  tabIndex={0}
-  onKeyDown={(e) => {
-    if (e.key === 'Enter' || e.key === ' ') {
-      e.preventDefault();
-      if (!address) openDirectoryModal();
-    }
-  }}
   onClick={openDirectoryForPlaceholder}
 >
🤖 Prompt for 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.

In `@src/components/boards-bar/boards-bar.tsx` around lines 166 - 176, Remove the
manual keyboard shims from native button elements in boards-bar.tsx: delete
tabIndex={0} and the onKeyDown handlers that check for Enter/Space and call
preventDefault, for the placeholder button (className={styles.placeholder},
onClick={openDirectoryForPlaceholder}) and the other native buttons such as the
temporaryButton and search buttons that use onClick handlers; leave the existing
onClick handlers (e.g., openDirectoryForPlaceholder and any temporaryButton
click handlers) in place so native keyboard activation behavior is preserved and
no additional event.preventDefault() is used.
src/components/markdown/markdown.tsx (1)

94-104: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove redundant onKeyDown from native <button>
For a native <button>, Enter/Space already activates the button and dispatches its click behavior by default, so a custom onKeyDown (especially with preventDefault()) is redundant and can interfere with expected keyboard interaction.

Proposed fix
       <button
         type='button'
         className={styles.embedButton}
-        tabIndex={0}
-        onKeyDown={(e) => {
-          if (e.key === 'Enter' || e.key === ' ') {
-            e.preventDefault();
-            setShowMedia(!showMedia);
-          }
-        }}
         onClick={() => setShowMedia(!showMedia)}
         ref={refs.setReference}
         {...getReferenceProps()}
       >
🤖 Prompt for 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.

In `@src/components/markdown/markdown.tsx` around lines 94 - 104, The custom
onKeyDown handler on the native <button> (className styles.embedButton) is
redundant and interferes with default Enter/Space behavior; remove the onKeyDown
prop and keep the existing onClick={() => setShowMedia(!showMedia)} so keyboard
activation continues to toggle showMedia via setShowMedia without calling
preventDefault. Locate the button in markdown.tsx that references setShowMedia
and showMedia and delete the onKeyDown block only.
src/components/settings-modal/__tests__/settings-modal.test.tsx (1)

187-194: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use a stable selector for the overlay close control in the test.

Line 187 uses container.querySelector('button'), which is order-dependent and can target the wrong control if button order changes. Query the explicit close affordance instead.

Suggested fix
-    const overlay = container.querySelector('button');
+    const overlay = container.querySelector('button[aria-label="close"]');

As per coding guidelines, "Use compiled context for orientation, not as source of truth. Source of truth: Code, tests, package manifests, docs, and runtime/live evidence when relevant."

🤖 Prompt for 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.

In `@src/components/settings-modal/__tests__/settings-modal.test.tsx` around lines
187 - 194, The test currently finds the overlay with a brittle
container.querySelector('button'); replace that with a stable accessibility
selector for the close control (e.g., use getByRole or getByLabelText to find
the close button by its accessible name like "Close" or the specific aria-label
used). Update the code around the overlay variable (the place declaring overlay
and the subsequent act(...) click dispatch) to query the close affordance via
getByRole(container, 'button', { name: /close/i }) or screen.getByRole('button',
{ name: /close/i }) so the test targets the explicit close control rather than
the first button node.
🧹 Nitpick comments (1)
src/components/catalog-row/catalog-row.tsx (1)

241-242: ⚡ Quick win

Add/extend a test for keyboard focus preview behavior.

This introduces non-trivial focus/blur behavior and should be covered with a nearby test to prevent regressions.

As per coding guidelines, "Add or update tests for bug fixes and non-trivial logic changes when the code is reasonably testable."

Also applies to: 254-255, 262-268

🤖 Prompt for 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.

In `@src/components/catalog-row/catalog-row.tsx` around lines 241 - 242, Add a
unit/test that covers keyboard focus preview behavior for the CatalogRow
component: simulate keyboard focus/blur and mouse over/leave on the element that
calls setHoveredCid(cid) (the wrapper div that also uses
onMouseOver/onFocus/onMouseLeave/onBlur) and assert that the preview state
(shouldMaskPost or hoveredCid-dependent UI) appears on focus and disappears on
blur, and also verify mouse events don't interfere with keyboard focus behavior;
use the component under test (CatalogRow) and target the same cid value used in
the diff to ensure hoveredCid is set/reset correctly on onFocus/onBlur and
onMouseOver/onMouseLeave.
🤖 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 `@src/components/board-blotter/board-blotter.module.css`:
- Line 49: The CSS rule uses the keyword "currentColor" with incorrect casing in
board-blotter.module.css (the line setting "outline: 1px dotted currentColor;");
fix the stylelint violation by changing the keyword to lowercase "currentcolor"
so the declaration reads with a lowercase value to satisfy the
value-keyword-case rule.

In `@src/components/boards-bar/boards-bar.module.css`:
- Around line 20-22: The stylelint failure is due to incorrect casing of the
color keyword in the focus style for the selectors `.boardNavDesktop
button:focus-visible` and `.boardNavMobile button:focus-visible`; update the
value `currentColor` to the lowercase `currentcolor` in that ruleset so it
complies with the `value-keyword-case` rule.

In `@src/components/catalog-row/catalog-row.tsx`:
- Around line 254-255: The focus/blur preview handlers are attached to
div.mediaPaddingWrapper but keyboard focus lands on the surrounding Link, so
keyboard users won't trigger the preview; move the onFocus/onBlur handlers (and
the timeoutRef/setShowPortal logic) from div.mediaPaddingWrapper to the
interactive Link element (or alternatively make div.mediaPaddingWrapper
keyboard-focusable by adding tabIndex=0 and also wiring its onKeyDown as
needed), and ensure you also move or duplicate the corresponding
onBlur/onMouseEnter/onMouseLeave handlers so the preview opens/closes
consistently for both keyboard and mouse (references: div.mediaPaddingWrapper,
Link, timeoutRef, setShowPortal).

In `@src/components/comment-content/comment-content.tsx`:
- Around line 292-293: The aria-label currently always uses t('show_original')
in the CommentContent component for the toggle that shows/hides the original;
change the aria-label to reflect the actual state by using the component's
boolean state (e.g., showOriginal or isOriginalVisible) to choose between
t('show_original') and t('hide_original') for both places where
aria-label={t('show_original')} is used so the assistive label matches the
action (show vs hide).

In `@src/components/comment-media/comment-media.tsx`:
- Around line 124-126: The thumbnail branch in thumbnailComponent currently
renders a plain <img> when thumbnail is present, losing the click-to-expand
behavior; update the thumbnail branch so it uses the same clickable element and
handler used for the non-thumbnail case (attach the component's expand/onClick
handler and necessary accessibility attributes — e.g., role/button, tabIndex,
aria-label — and cursor styling) so that the thumbnail variable behaves
identically to the expanded-trigger used elsewhere in this component (refer to
thumbnailComponent and thumbnail to locate the branch and the existing expand
handler).

In `@src/components/create-board-modal/create-board-modal.module.css`:
- Around line 33-36: The rule that currently sets "z-index: 1; color: inherit;
margin: 0; padding: 0" is clobbering the negative margin-left used to center the
modal; replace the single "margin: 0" with explicit top/bottom resets (e.g.
"margin-top: 0; margin-bottom: 0;") or "margin: 0 auto;" as appropriate so you
don't override margin-left—update the CSS rule (the block containing z-index: 1,
color: inherit, padding: 0) to remove the global margin reset and preserve the
existing left/right centering margin.

In `@src/components/directory-modal/directory-modal.module.css`:
- Around line 34-41: The dialog styles include a duplicate color declaration:
keep the themed text color declaration "color: var(--directory-modal-text-color,
`#800`);" and remove the later "color: inherit;" so the modal uses the intended
themed color; update the CSS in directory-modal.module.css by deleting the
redundant "color: inherit" line (or move it only where inheritance is desired)
to avoid overriding the theme variable.

In `@src/components/disclaimer-modal/disclaimer-modal.module.css`:
- Around line 30-37: The .disclaimerDialog rule in disclaimer-modal.module.css
sets color twice (first to var(--disclaimer-modal-text-color, rgb(136, 0, 0))
and later to color: inherit), which nullifies the theme color; remove the
duplicate override (delete the later color: inherit) so .disclaimerDialog uses
the explicit var(...) value (or, if inherit is required, move it to a different
selector and keep the var(...) where the dialog should use the theme color).

In `@src/components/embed/embed.tsx`:
- Line 64: remoteEmbedSandbox is too permissive: remove blanket grants of
allow-forms, allow-presentation and especially allow-same-origin from the
default sandbox and enforce least-privilege; change the constant
remoteEmbedSandbox (and all usages currently referencing it) to a minimal
default (no allow-forms/allow-presentation/allow-same-origin) and implement a
small provider-specific whitelist (by provider id or URL) that explicitly
returns an augmented sandbox string only when a provider requires those
capabilities; update all occurrences that use remoteEmbedSandbox (the constant
defined alongside srcDocSandbox) to call the new selector/whitelist so only
approved embeds get extra privileges.

In `@src/components/markdown/markdown.module.css`:
- Line 50: The CSS rule using the outline property currently uses the
camel-cased keyword "currentColor" (outline: 1px dotted currentColor;) which
violates stylelint value-keyword-case; change that value to the lowercase
"currentcolor" so the rule reads outline: 1px dotted currentcolor; in the
markdown.module.css where the outline property is defined.

In
`@src/components/post-desktop/post-menu-desktop/__tests__/post-menu-desktop.test.tsx`:
- Around line 206-209: The test currently checks for the absence of the
"hide_thread" menu option by searching div elements; update that assertion to
search buttons instead so it doesn't false-pass after the menu migrated to
buttons — replace the query
Array.from(document.body.querySelectorAll('div')).find((node) =>
node.textContent === 'hide_thread') with a buttons search
(querySelectorAll('button')) and assert the result is undefined (or use the same
button-finding approach used for imageSearch) in the post-menu-desktop.test.tsx
test.

In `@src/components/post-desktop/post-menu-desktop/post-menu-desktop.module.css`:
- Around line 27-30: The CSS rule for focus-visible uses `currentColor` which
violates the stylelint keyword-case rule; update the selector block for
`.postMenu button:focus-visible, .postMenuBtn:focus-visible,
.postMenuBtnCatalog:focus-visible` to use the lowercase keyword `currentcolor`
for the outline color (replace `currentColor` with `currentcolor`) so the
stylesheet passes linting.

In `@src/components/post-mobile/post-menu-mobile/post-menu-mobile.module.css`:
- Around line 21-24: Replace the camel-cased CSS keyword "currentColor" with the
lowercase "currentcolor" wherever used to satisfy the stylelint
value-keyword-case rule; specifically update the .postMenuBtn:focus-visible rule
(the outline: 1px dotted currentColor) and the other occurrence around lines
57-60 in this module so both uses read "currentcolor".

In
`@src/components/settings-modal/crypto-address-setting/crypto-address-setting.tsx`:
- Around line 193-195: The info toggle button lacks an accessible name; update
the button in crypto-address-setting (the element using showCryptoAddressInfo
and setShowCryptoAddressInfo with class styles.infoButton) to include a
meaningful aria-label that reflects its current state, e.g. use a conditional
label like "Show crypto address info" when showCryptoAddressInfo is false and
"Hide crypto address info" when true (optionally also add aria-pressed for
toggle semantics).

In `@src/components/settings-modal/settings-modal.tsx`:
- Around line 121-122: The overlay close button and the expand/collapse-all
button are native <button>s and currently have both onClick and
onKeyDown={handleKeyDown(...)} which can cause double activation; fix by
removing the redundant onKeyDown handlers from those native buttons (leave the
onClick handlers) OR update handleKeyDown to only intercept non-activation keys
(e.g., ignore Enter/Space or call preventDefault appropriately) so it does not
re-trigger the action; locate the handlers referenced in settings-modal.tsx (the
overlay close button and the expand/collapse-all button) and adjust either the
button props or the handleKeyDown implementation to prevent duplicate
activations.

In `@src/views/archive/archive.tsx`:
- Line 244: The Actions column header is using a <td> in the table head; replace
that element with a semantic <th> (e.g., change the <td
className={styles.postblock} aria-label='Actions'> to a <th scope='col'
className={styles.postblock}>) and ensure it contains visible text or an sr-only
label (instead of only aria-label) so screen readers recognize it as a column
header in archive.tsx.

In `@src/views/home/home.module.css`:
- Around line 60-75: The existing rules for .boxBar span and .boxBar > button
(and the similar rules at the other block) use all: unset which removes native
focus indicators; add explicit focus styles to restore keyboard visibility by
defining .boxBar span:focus, .boxBar span:focus-visible, .boxBar > button:focus,
and .boxBar > button:focus-visible with a clear focus indicator (for example an
accessible outline or visible box-shadow and optional border-radius) and ensure
focus styles inherit color/contrast variables (e.g., use
--homepage-box-bar-text-color-hover or a dedicated --focus-color) so keyboard
users regain a visible, high-contrast focus ring.

In `@src/views/pass/pass.tsx`:
- Line 152: The anchor element used for the mintPass link currently hardcodes
aria-label="Mintpass", which prevents localization and can break accessibility
in non-English locales; update the JSX for the mintPass link (and the other
occurrence) to either remove the aria-label if the link text is already
descriptive, or replace it with a localized string from your i18n/translation
helper (e.g., t('mintpass') or similar) so the accessible name is localized
consistently. Ensure both occurrences (the mintPass anchor and the one at the
other reported location) are updated to use the translation function or have no
aria-label.

In `@src/views/rules/rules.tsx`:
- Around line 65-67: The list item key uses the rule text which can collide for
duplicate rules; update the rules.map call to include the index discriminator
(use the map signature rules.map((rule: string, index: number) => ...) and set
the <li> key to a unique value such as `${rule}-${index}` or `rule-${index}`) so
the list reconciliation for the <li> rendering the <Markdown content={rule} />
is stable even when rule text repeats.

---

Outside diff comments:
In `@src/components/board-blotter/board-blotter.tsx`:
- Around line 46-53: Remove the redundant keyboard handling and tabindex from
the native button: delete the custom onKeyDown handler and the tabIndex={0} prop
in the component where the button invokes toggleVisibility (the element using
toggleVisibility and onClick). Keep the native <button type="button"> and its
onClick={() => toggleVisibility()} so the browser's built-in Enter/Space
activation and focus management remain intact.

In `@src/components/board-header/board-header.tsx`:
- Around line 116-123: The button that calls
navigate('/subs/settings#subscriptions-settings') currently has a redundant
tabIndex={0} and a custom onKeyDown handler that manually handles Enter/Space,
which can cause duplicate navigation (especially for Space); remove the
tabIndex={0} and the entire onKeyDown={(e) => { ... }} handler from that
<button> and keep the existing onClick={() =>
navigate('/subs/settings#subscriptions-settings')} so the native button keyboard
activation semantics handle Enter/Space correctly.

In `@src/components/board-pagination/board-pagination.tsx`:
- Around line 51-58: The “[All]” control in BoardPagination uses a native button
but also adds a redundant tabIndex and onKeyDown handler; remove the
tabIndex={0} and the onKeyDown prop from the element that calls
setEnableInfiniteScroll (the “[All]” button) and keep the existing onClick={()
=> setEnableInfiniteScroll(true)} so activation relies on the button's native
keyboard handling; update the JSX in the BoardPagination component accordingly
(identify the element that invokes setEnableInfiniteScroll).

In `@src/components/boards-bar/boards-bar.tsx`:
- Around line 166-176: Remove the manual keyboard shims from native button
elements in boards-bar.tsx: delete tabIndex={0} and the onKeyDown handlers that
check for Enter/Space and call preventDefault, for the placeholder button
(className={styles.placeholder}, onClick={openDirectoryForPlaceholder}) and the
other native buttons such as the temporaryButton and search buttons that use
onClick handlers; leave the existing onClick handlers (e.g.,
openDirectoryForPlaceholder and any temporaryButton click handlers) in place so
native keyboard activation behavior is preserved and no additional
event.preventDefault() is used.

In `@src/components/embed/embed.tsx`:
- Around line 30-31: The Embed component currently returns <RedditEmbed
parsedUrl={parsedUrl} /> for any host in redditHosts even though
canEmbed(parsedUrl) rejects reddit URLs that lack "/comments/"; update the
conditional in the Embed component to only render RedditEmbed when the URL is
both a reddit host and passes canEmbed (or otherwise explicitly contains
"/comments/"), i.e. change the reddit branch to check canEmbed(parsedUrl) (or
parsedUrl.pathname.includes('/comments/')) before returning RedditEmbed so
rendering matches canEmbed eligibility.

In `@src/components/markdown/markdown.tsx`:
- Around line 94-104: The custom onKeyDown handler on the native <button>
(className styles.embedButton) is redundant and interferes with default
Enter/Space behavior; remove the onKeyDown prop and keep the existing
onClick={() => setShowMedia(!showMedia)} so keyboard activation continues to
toggle showMedia via setShowMedia without calling preventDefault. Locate the
button in markdown.tsx that references setShowMedia and showMedia and delete the
onKeyDown block only.

In `@src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx`:
- Around line 130-161: The image-search trigger currently nests anchor links
inside the <button> (see the button using refs.setReference, onClose,
isImageSearchMenuOpen and refs.setFloating with encodedUrl), which is invalid;
refactor so the <button> remains only the trigger and the dropdown link list is
rendered as a sibling container (e.g., a <div> or <nav> with className
styles.dropdownMenu) outside the button; move the three <a> anchors
(Google/Yandex/SauceNAO) into that sibling, keep floatingStyles and
refs.setFloating on that sibling, preserve open/close logic tied to
isImageSearchMenuOpen, maintain the same onMouseOver/onFocus/onMouseLeave/onBlur
handlers on the trigger (or delegate them to the sibling container as needed),
and add appropriate aria-expanded/aria-controls or id linking between the button
and the dropdown to preserve keyboard accessibility.

In `@src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx`:
- Around line 139-160: The code nests multiple <a> links inside an outer
<button>, which is invalid HTML; replace the outer <button> wrapper with a
non-interactive container (e.g., a <div> or <nav> with an appropriate role) and
remove the wrapper's tabIndex/onKeyDown, then attach the existing onClose
behavior to each link by adding onClick handlers (and onKeyDown for keyboard
activation if needed) on the three anchors that use encodedUrl; keep t(...) and
styles.postMenuItem unchanged so each <a> remains interactive and closing
behavior still fires when a link is activated.

In `@src/components/reply-quote-preview/reply-quote-preview.tsx`:
- Around line 352-359: The span with class styles.backlink (the element using
refs.setReference and event handlers handleMouseOver/handleMouseLeave with
normalizedBacklinkReply?.cid) is not keyboard-focusable, so focus/blur handlers
won't fire; make the element keyboard-accessible by adding tabIndex={0} (and
consider role="button" and appropriate aria-label if it behaves like an
interactive control) and ensure keyboard activation is handled (e.g., call the
same preview logic on Enter/Space via onKeyDown) so keyboard users can trigger
the preview; apply the same changes to the other similar span that attaches
focus/blur handlers.

In `@src/components/settings-modal/__tests__/settings-modal.test.tsx`:
- Around line 187-194: The test currently finds the overlay with a brittle
container.querySelector('button'); replace that with a stable accessibility
selector for the close control (e.g., use getByRole or getByLabelText to find
the close button by its accessible name like "Close" or the specific aria-label
used). Update the code around the overlay variable (the place declaring overlay
and the subsequent act(...) click dispatch) to query the close affordance via
getByRole(container, 'button', { name: /close/i }) or screen.getByRole('button',
{ name: /close/i }) so the test targets the explicit close control rather than
the first button node.

In
`@src/components/settings-modal/subscriptions-setting/subscriptions-setting.tsx`:
- Around line 25-35: The button handler duplicates native behavior: remove the
manual keyboard handling and explicit tabIndex on native <button> elements to
avoid double-invocation; specifically, delete tabIndex={0} and the onKeyDown
block from the button that calls toggleSubscription (and any similar buttons
like the one that calls unsubscribeAll) and rely solely on
onClick={toggleSubscription} (or onClick={unsubscribeAll}) for activation.

---

Nitpick comments:
In `@src/components/catalog-row/catalog-row.tsx`:
- Around line 241-242: Add a unit/test that covers keyboard focus preview
behavior for the CatalogRow component: simulate keyboard focus/blur and mouse
over/leave on the element that calls setHoveredCid(cid) (the wrapper div that
also uses onMouseOver/onFocus/onMouseLeave/onBlur) and assert that the preview
state (shouldMaskPost or hoveredCid-dependent UI) appears on focus and
disappears on blur, and also verify mouse events don't interfere with keyboard
focus behavior; use the component under test (CatalogRow) and target the same
cid value used in the diff to ensure hoveredCid is set/reset correctly on
onFocus/onBlur and onMouseOver/onMouseLeave.
🪄 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

Run ID: 63d62fde-09fc-400d-b384-3572083642ca

📥 Commits

Reviewing files that changed from the base of the PR and between ac95e58 and 7b73d59.

📒 Files selected for processing (88)
  • src/app.tsx
  • src/components/board-blotter/board-blotter.module.css
  • src/components/board-blotter/board-blotter.tsx
  • src/components/board-buttons/board-buttons.tsx
  • src/components/board-header/__tests__/board-header.test.tsx
  • src/components/board-header/board-header.tsx
  • src/components/board-pagination/__tests__/board-pagination.test.tsx
  • src/components/board-pagination/board-pagination.tsx
  • src/components/boards-bar-edit-modal/__tests__/boards-bar-edit-modal.test.tsx
  • src/components/boards-bar-edit-modal/boards-bar-edit-modal.module.css
  • src/components/boards-bar-edit-modal/boards-bar-edit-modal.tsx
  • src/components/boards-bar/__tests__/boards-bar.test.tsx
  • src/components/boards-bar/boards-bar.module.css
  • src/components/boards-bar/boards-bar.tsx
  • src/components/catalog-filters/__tests__/catalog-filters.test.tsx
  • src/components/catalog-filters/catalog-filters.module.css
  • src/components/catalog-filters/catalog-filters.tsx
  • src/components/catalog-filters/highlight-color-picker/highlight-color-picker.tsx
  • src/components/catalog-row/catalog-row.tsx
  • src/components/catalog-search/__tests__/catalog-search.test.tsx
  • src/components/catalog-search/catalog-search.tsx
  • src/components/challenge-modal/__tests__/challenge-modal.test.tsx
  • src/components/challenge-modal/challenge-modal.tsx
  • src/components/comment-content/comment-content.tsx
  • src/components/comment-media/__tests__/comment-media.test.tsx
  • src/components/comment-media/comment-media.module.css
  • src/components/comment-media/comment-media.tsx
  • src/components/create-board-modal/create-board-modal.module.css
  • src/components/create-board-modal/create-board-modal.tsx
  • src/components/directory-modal/directory-modal.module.css
  • src/components/directory-modal/directory-modal.tsx
  • src/components/disclaimer-modal/disclaimer-modal.module.css
  • src/components/disclaimer-modal/disclaimer-modal.tsx
  • src/components/edit-menu/edit-menu.tsx
  • src/components/embed/__tests__/embed.test.tsx
  • src/components/embed/embed-utils.ts
  • src/components/embed/embed.tsx
  • src/components/embed/index.ts
  • src/components/footer/footer.tsx
  • src/components/markdown/__tests__/markdown.test.tsx
  • src/components/markdown/external-number-quote-link.tsx
  • src/components/markdown/markdown.module.css
  • src/components/markdown/markdown.tsx
  • src/components/mod-empty-state/mod-empty-state.tsx
  • src/components/post-author-flags.tsx
  • src/components/post-desktop/post-desktop.tsx
  • src/components/post-desktop/post-menu-desktop/__tests__/post-menu-desktop.test.tsx
  • src/components/post-desktop/post-menu-desktop/post-menu-desktop.module.css
  • src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
  • src/components/post-form/post-form.tsx
  • src/components/post-mobile/post-menu-mobile/post-menu-mobile.module.css
  • src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx
  • src/components/post-mobile/post-mobile.tsx
  • src/components/reply-modal/reply-modal.tsx
  • src/components/reply-quote-preview/reply-quote-preview.tsx
  • src/components/settings-modal/__tests__/settings-modal.test.tsx
  • src/components/settings-modal/account-settings/account-settings.tsx
  • src/components/settings-modal/advanced-settings/advanced-settings.tsx
  • src/components/settings-modal/crypto-address-setting/crypto-address-setting.tsx
  • src/components/settings-modal/crypto-wallets-setting/crypto-wallets-setting.tsx
  • src/components/settings-modal/interface-settings/interface-settings.tsx
  • src/components/settings-modal/media-hosting-settings/media-hosting-settings.tsx
  • src/components/settings-modal/p2p-stats-settings/__tests__/p2p-stats-settings.test.tsx
  • src/components/settings-modal/p2p-stats-settings/p2p-stats-settings.tsx
  • src/components/settings-modal/settings-modal.module.css
  • src/components/settings-modal/settings-modal.tsx
  • src/components/settings-modal/subscriptions-setting/__tests__/subscriptions-setting.test.tsx
  • src/components/settings-modal/subscriptions-setting/subscriptions-setting.tsx
  • src/views/account-data-editor/account-data-editor.tsx
  • src/views/archive/archive.tsx
  • src/views/blotter/blotter.tsx
  • src/views/board/__tests__/board.test.tsx
  • src/views/board/board.tsx
  • src/views/catalog/__tests__/catalog.test.tsx
  • src/views/catalog/catalog-render-feed.ts
  • src/views/catalog/catalog.tsx
  • src/views/directory/__tests__/directory.test.tsx
  • src/views/directory/directory.tsx
  • src/views/faq/faq.tsx
  • src/views/home/boards-list/boards-filter-modal.tsx
  • src/views/home/box-modal/box-modal.tsx
  • src/views/home/home.module.css
  • src/views/home/home.tsx
  • src/views/mod-queue/mod-queue.tsx
  • src/views/pass/pass.tsx
  • src/views/post/post.module.css
  • src/views/post/post.tsx
  • src/views/rules/rules.tsx

Comment thread src/components/board-blotter/board-blotter.module.css Outdated
Comment thread src/components/boards-bar/boards-bar.module.css
Comment thread src/components/catalog-row/catalog-row.tsx Outdated
Comment thread src/components/comment-content/comment-content.tsx Outdated
Comment on lines 124 to 126
thumbnailComponent = thumbnail ? (
<img src={thumbnail} alt='' />
<img src={thumbnail} alt='Video thumbnail' />
) : (
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Video thumbnails lost click-to-expand behavior when thumbnail is present.

At Line 124-126, the thumbnail path renders a plain image, so users can’t open the expanded media view from that thumbnail.

Proposed fix
   } else if (type === 'video') {
     thumbnailComponent = thumbnail ? (
-      <img src={thumbnail} alt='Video thumbnail' />
+      <button type='button' className={styles.mediaToggleButton} aria-label='Open video' onClick={handleOpenMedia}>
+        <img src={thumbnail} alt='Video thumbnail' />
+      </button>
     ) : (
       // show first frame of the video, as a workaround for Safari not loading thumbnails
       <button type='button' className={styles.mediaToggleButton} aria-label='Open video' onClick={handleOpenMedia}>
         <video src={`${url}`#t`=0.001`} aria-label='Video thumbnail' />
       </button>
🤖 Prompt for 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.

In `@src/components/comment-media/comment-media.tsx` around lines 124 - 126, The
thumbnail branch in thumbnailComponent currently renders a plain <img> when
thumbnail is present, losing the click-to-expand behavior; update the thumbnail
branch so it uses the same clickable element and handler used for the
non-thumbnail case (attach the component's expand/onClick handler and necessary
accessibility attributes — e.g., role/button, tabIndex, aria-label — and cursor
styling) so that the thumbnail variable behaves identically to the
expanded-trigger used elsewhere in this component (refer to thumbnailComponent
and thumbnail to locate the branch and the existing expand handler).

Comment on lines +121 to +122
<button type='button' className={styles.overlay} aria-label={t('close')} tabIndex={0} onClick={closeModal} onKeyDown={handleKeyDown(closeModal)} />
<dialog open className={styles.settingsModal} aria-modal='true' aria-labelledby='settings-modal-title'>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify native buttons wired with both onClick and onKeyDown in this component.
rg -n -C2 "onClick=\{.*\}.*onKeyDown=\{handleKeyDown" src/components/settings-modal/settings-modal.tsx

Repository: bitsocialnet/5chan

Length of output: 707


Remove redundant onKeyDown activation handlers from native <button>s
In src/components/settings-modal/settings-modal.tsx, the close overlay button (line 121) and the expand/collapse-all button (line 139) each define both onClick={...} and onKeyDown={handleKeyDown(...)}. For native buttons, this can double-trigger close/toggle behavior unless handleKeyDown prevents default/only handles non-activation keys.

🤖 Prompt for 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.

In `@src/components/settings-modal/settings-modal.tsx` around lines 121 - 122, The
overlay close button and the expand/collapse-all button are native <button>s and
currently have both onClick and onKeyDown={handleKeyDown(...)} which can cause
double activation; fix by removing the redundant onKeyDown handlers from those
native buttons (leave the onClick handlers) OR update handleKeyDown to only
intercept non-activation keys (e.g., ignore Enter/Space or call preventDefault
appropriately) so it does not re-trigger the action; locate the handlers
referenced in settings-modal.tsx (the overlay close button and the
expand/collapse-all button) and adjust either the button props or the
handleKeyDown implementation to prevent duplicate activations.

<td className={styles.postblock}>No.</td>
<td className={styles.postblock}>Excerpt</td>
<td className={styles.postblock}></td>
<td className={styles.postblock} aria-label='Actions'></td>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use a semantic table header cell for the Actions column.

Line 244 is in <thead> but uses <td>, so screen readers won’t treat it as a real header. Use <th scope='col'> with visible or sr-only text.

Suggested fix
-              <td className={styles.postblock} aria-label='Actions'></td>
+              <th className={styles.postblock} scope='col'>
+                <span className={styles.srOnly}>Actions</span>
+              </th>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<td className={styles.postblock} aria-label='Actions'></td>
<th className={styles.postblock} scope='col'>
<span className={styles.srOnly}>Actions</span>
</th>
🤖 Prompt for 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.

In `@src/views/archive/archive.tsx` at line 244, The Actions column header is
using a <td> in the table head; replace that element with a semantic <th> (e.g.,
change the <td className={styles.postblock} aria-label='Actions'> to a <th
scope='col' className={styles.postblock}>) and ensure it contains visible text
or an sr-only label (instead of only aria-label) so screen readers recognize it
as a column header in archive.tsx.

Comment thread src/views/home/home.module.css
Comment thread src/views/pass/pass.tsx
i18nKey='pass_answer_nft'
components={{
mintPass: <a href='https://github.com/bitsocialnet/mintpass' target='_blank' rel='noopener noreferrer' />,
mintPass: <a href='https://github.com/bitsocialnet/mintpass' target='_blank' rel='noopener noreferrer' aria-label='Mintpass' />,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid hardcoded English aria-label values on translated links.

These labels override the accessible name and are not localized, which can regress a11y/i18n on non-English locales. Prefer localized labels or no aria-label if link text is already descriptive.

Proposed fix
-                      mintPass: <a href='https://github.com/bitsocialnet/mintpass' target='_blank' rel='noopener noreferrer' aria-label='Mintpass' />,
+                      mintPass: <a href='https://github.com/bitsocialnet/mintpass' target='_blank' rel='noopener noreferrer' />,
...
-                      bitsocial: <a href='https://bitsocial.net' target='_blank' rel='noopener noreferrer' aria-label='Bitsocial' />,
+                      bitsocial: <a href='https://bitsocial.net' target='_blank' rel='noopener noreferrer' />,

Also applies to: 161-161

🤖 Prompt for 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.

In `@src/views/pass/pass.tsx` at line 152, The anchor element used for the
mintPass link currently hardcodes aria-label="Mintpass", which prevents
localization and can break accessibility in non-English locales; update the JSX
for the mintPass link (and the other occurrence) to either remove the aria-label
if the link text is already descriptive, or replace it with a localized string
from your i18n/translation helper (e.g., t('mintpass') or similar) so the
accessible name is localized consistently. Ensure both occurrences (the mintPass
anchor and the one at the other reported location) are updated to use the
translation function or have no aria-label.

Comment thread src/views/rules/rules.tsx Outdated
The native <dialog> migration added `margin: 0` to .createBoardDialog, overriding the `margin-left: -250px` used with `left: 50%` for centering and shifting the modal ~250px right on desktop. Placement is controlled by left + margin-left, so drop the redundant margin reset.
The <dialog> migration appended `color: inherit` after the themed `color: var(--*-modal-text-color, ...)` on .directoryDialog/.disclaimerDialog, so the dialog inherited the body color. On the disclaimer modal this made body text unreadable (light grey on white) in dark themes. Remove the redundant inherit.
The help indicator became a <button> but .statusUnavailableHelp still styled the old <sup>, so it rendered as a default OS button box. Reset native chrome, restore the superscript look, and add a focus ring.
Swatches and the Close/Clear controls became <button>s without a chrome reset, rendering as OS buttons. Neutralize appearance/border/padding while preserving the swatch colors, and add keyboard focus rings.
…ve image alt

The no-thumbnail 'open embed' fallback became a classless <button> (OS chrome, lost small-layout styling) — give it a flat reset class wired into the small-layout rule. Also set alt='' on the non-interactive (disableToggle) image, which carried action-phrased 'Collapse/Expand image' alt text.
.boxBar > button and .filterModal .option use `all: unset`, which removed the default outline with no :focus-visible replacement, leaving keyboard focus invisible. Add a theme-aware focus ring.
role=status containers became <output> (inline by default); .addressWarning and .modEmptyState had no display rule and lost their block spacing. Set display: block.
getEffectiveRouteUserState falls back to the non-reactive window.history.state, so the memo must re-run per navigation. Restore the intentional locationKey/pathname deps (referenced via void, matching the catalog metrics pattern) to avoid serving stale route state.
key={rule} collides when a board has two identical rule lines, breaking reconciliation. Use a position+content composite key.
… element

The image-search menu wrapped <a> links in a <button> (invalid HTML; links hidden from assistive tech). Make the wrapper a plain container and move the menu-close handler onto each link; the hover disclosure is unchanged. Update the affected test query.
The icon-only ?/x info button had no accessible name; add an aria-label.
Yandex deprecated the `img_url` query param; the reverse image search link used it and no longer worked. Switch to `url` (matching 4chan's current image-search URL) so Yandex search opens correctly. Google Lens and SauceNAO already match. Updates the desktop and mobile link plus their test assertions.
Users found the crypto address help confusing. Rewrite the steps to explain that a .bso name is an ENS name you own (shown with a .bso ending instead of .eth), call out the exact public-key value for the "bitsocial" text record on its own line, and spell out the Check-then-Save flow.
Comment thread src/components/comment-content/comment-content.tsx Outdated
Comment thread src/components/board-header/board-header.tsx
The subscriptions subtitle became a <button> but .clickableSubtitle only set cursor/display, so it rendered with default OS button chrome (Cursor Bugbot). Add the same appearance/background/border/font/padding reset used elsewhere in this PR, plus a focus ring.
The span-to-button conversion mislabeled the controls: the "view full comment" button used t('show_replies') (Cursor Bugbot) and the hide-original toggle used t('show_original') (CodeRabbit). Use the existing, already-localized keys view/hide so screen readers announce the actual action (the previous keys were also undefined in the translation files and rendered raw).
Comment thread src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
Copy link
Copy Markdown

@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

🤖 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 `@src/components/comment-media/comment-media.module.css`:
- Around line 57-65: Replace the deprecated CSS property in the .thumbnailSmall
ruleset: the selectors ".thumbnailSmall span", ".thumbnailSmall a", and
".thumbnailSmall .noThumbnailButton" currently use "word-wrap: break-word";
update that declaration to "overflow-wrap: break-word" to maintain the same
behavior and satisfy stylelint's property-no-deprecated rule, then run yarn
build, yarn lint, and yarn type-check.
🪄 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

Run ID: 32acce5c-5335-4870-a236-e8e3f4e14646

📥 Commits

Reviewing files that changed from the base of the PR and between 7b73d59 and 5cc0b47.

📒 Files selected for processing (27)
  • src/components/board-blotter/board-blotter.module.css
  • src/components/board-header/board-header.module.css
  • src/components/boards-bar-edit-modal/boards-bar-edit-modal.module.css
  • src/components/boards-bar/boards-bar.module.css
  • src/components/catalog-filters/catalog-filters.tsx
  • src/components/catalog-filters/highlight-color-picker/highlight-color-picker.module.css
  • src/components/comment-content/comment-content.tsx
  • src/components/comment-media/comment-media.module.css
  • src/components/comment-media/comment-media.tsx
  • src/components/create-board-modal/create-board-modal.module.css
  • src/components/directory-modal/directory-modal.module.css
  • src/components/disclaimer-modal/disclaimer-modal.module.css
  • src/components/markdown/markdown.module.css
  • src/components/mod-empty-state/mod-empty-state.module.css
  • src/components/post-desktop/post-menu-desktop/__tests__/post-menu-desktop.test.tsx
  • src/components/post-desktop/post-menu-desktop/post-menu-desktop.module.css
  • src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
  • src/components/post-mobile/post-menu-mobile/__tests__/post-menu-mobile.test.tsx
  • src/components/post-mobile/post-menu-mobile/post-menu-mobile.module.css
  • src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx
  • src/components/settings-modal/crypto-address-setting/crypto-address-setting.tsx
  • src/components/settings-modal/settings-modal.module.css
  • src/views/board/board.module.css
  • src/views/directory/directory.module.css
  • src/views/home/home.module.css
  • src/views/post/post.tsx
  • src/views/rules/rules.tsx
💤 Files with no reviewable changes (5)
  • src/components/disclaimer-modal/disclaimer-modal.module.css
  • src/components/directory-modal/directory-modal.module.css
  • src/components/settings-modal/settings-modal.module.css
  • src/components/boards-bar-edit-modal/boards-bar-edit-modal.module.css
  • src/components/create-board-modal/create-board-modal.module.css
✅ Files skipped from review due to trivial changes (1)
  • src/views/board/board.module.css
🚧 Files skipped from review as they are similar to previous changes (15)
  • src/components/board-blotter/board-blotter.module.css
  • src/components/mod-empty-state/mod-empty-state.module.css
  • src/views/post/post.tsx
  • src/components/post-desktop/post-menu-desktop/tests/post-menu-desktop.test.tsx
  • src/components/boards-bar/boards-bar.module.css
  • src/components/post-desktop/post-menu-desktop/post-menu-desktop.module.css
  • src/views/home/home.module.css
  • src/views/rules/rules.tsx
  • src/components/post-mobile/post-menu-mobile/post-menu-mobile.module.css
  • src/components/catalog-filters/highlight-color-picker/highlight-color-picker.module.css
  • src/components/settings-modal/crypto-address-setting/crypto-address-setting.tsx
  • src/components/markdown/markdown.module.css
  • src/components/comment-media/comment-media.tsx
  • src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
  • src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx

Comment thread src/components/comment-media/comment-media.module.css
The accessibility pass added aria-labels referencing i18next keys that did not exist (show_replies, hide_replies, unhide, account_data, ban_duration_days, challenge_answer, ban, timestamp), so screen readers announced the raw underscored key. Add and translate all eight into the 35 supported languages.
Copy link
Copy Markdown

@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

🤖 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/translations/da/default.json`:
- Line 399: The "unhide" translation currently uses the English word; update the
JSON entry for the "unhide" key in public/translations/da/default.json to a
Danish equivalent (e.g., "vis") by replacing the value "unhide" with the Danish
string, preserving JSON syntax (quotes and trailing comma) and matching the
surrounding translation style used by other keys; verify the updated key
"unhide" now reads "unhide": "vis".

In `@public/translations/fi/default.json`:
- Line 404: The Finnish translation for the JSON key "timestamp" is incorrect:
replace the value "aikaleimaustiedosto" with the proper UI label "aikaleima" so
the translation represents a timestamp value/field (refer to the "timestamp" key
in public/translations/fi/default.json and update its value accordingly).
🪄 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

Run ID: 9b0b235c-0f1b-4a76-bbcb-e67691031158

📥 Commits

Reviewing files that changed from the base of the PR and between 5cc0b47 and 0719a34.

📒 Files selected for processing (35)
  • public/translations/ar/default.json
  • public/translations/bn/default.json
  • public/translations/cs/default.json
  • public/translations/da/default.json
  • public/translations/de/default.json
  • public/translations/el/default.json
  • public/translations/en/default.json
  • public/translations/es/default.json
  • public/translations/fa/default.json
  • public/translations/fi/default.json
  • public/translations/fil/default.json
  • public/translations/fr/default.json
  • public/translations/he/default.json
  • public/translations/hi/default.json
  • public/translations/hu/default.json
  • public/translations/id/default.json
  • public/translations/it/default.json
  • public/translations/ja/default.json
  • public/translations/ko/default.json
  • public/translations/mr/default.json
  • public/translations/nl/default.json
  • public/translations/no/default.json
  • public/translations/pl/default.json
  • public/translations/pt/default.json
  • public/translations/ro/default.json
  • public/translations/ru/default.json
  • public/translations/sq/default.json
  • public/translations/sv/default.json
  • public/translations/te/default.json
  • public/translations/th/default.json
  • public/translations/tr/default.json
  • public/translations/uk/default.json
  • public/translations/ur/default.json
  • public/translations/vi/default.json
  • public/translations/zh/default.json
✅ Files skipped from review due to trivial changes (12)
  • public/translations/ar/default.json
  • public/translations/id/default.json
  • public/translations/tr/default.json
  • public/translations/te/default.json
  • public/translations/fr/default.json
  • public/translations/sv/default.json
  • public/translations/fil/default.json
  • public/translations/uk/default.json
  • public/translations/vi/default.json
  • public/translations/pt/default.json
  • public/translations/ur/default.json
  • public/translations/hi/default.json

"flag": "Flag",
"show_replies": "Vis svar",
"hide_replies": "skjul svar",
"unhide": "unhide",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing Danish translation for "unhide".

The "unhide" key uses the English word instead of a Danish translation. Other language files provide localized translations (e.g., Russian "показать", Finnish "paljasta", Czech "odkrýt"). Danish users will see English text here.

Suggested Danish translation

Consider using a proper Danish equivalent:

-  "unhide": "unhide",
+  "unhide": "vis",

or

-  "unhide": "unhide",
+  "unhide": "fjern skjul",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"unhide": "unhide",
"unhide": "vis",
🤖 Prompt for 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.

In `@public/translations/da/default.json` at line 399, The "unhide" translation
currently uses the English word; update the JSON entry for the "unhide" key in
public/translations/da/default.json to a Danish equivalent (e.g., "vis") by
replacing the value "unhide" with the Danish string, preserving JSON syntax
(quotes and trailing comma) and matching the surrounding translation style used
by other keys; verify the updated key "unhide" now reads "unhide": "vis".

"ban_duration_days": "kiellon kesto päivissä",
"challenge_answer": "haasteen vastaus",
"ban": "kielto",
"timestamp": "aikaleimaustiedosto"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Verify Finnish translation for "timestamp".

The translation "aikaleimaustiedosto" appears to mean "timestamp file" rather than just "timestamp". Breaking it down: "aika" (time) + "leima" (stamp) + "tiedosto" (file). In UI context, this label typically refers to a timestamp value/field, not a file. Consider using "aikaleima" instead.

Suggested correction
-  "timestamp": "aikaleimaustiedosto"
+  "timestamp": "aikaleima"
🤖 Prompt for 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.

In `@public/translations/fi/default.json` at line 404, The Finnish translation for
the JSON key "timestamp" is incorrect: replace the value "aikaleimaustiedosto"
with the proper UI label "aikaleima" so the translation represents a timestamp
value/field (refer to the "timestamp" key in public/translations/fi/default.json
and update its value accordingly).

Several span-to-button conversions lacked a chrome reset, so they rendered as default OS buttons: the comment 'Click here' / show-hide-original links (.abbr button, .showOriginal), the catalog filters help/close icon buttons, the catalog search close (x), the subscriptions [subscribe] links, and the color-picker backdrop. Reset appearance/background/border/font/padding (or all: unset for the backdrop) and add focus rings, matching the pattern used elsewhere in this PR.
The image-search menu item was a non-focusable div, so keyboard users could not reach it and the submenu only opened on mouse hover. Make the label a real <button> (aria-haspopup/aria-expanded) so it is focusable and toggleable, and show the submenu whenever it is open (focus or hover) instead of only on CSS :hover. Links remain real anchors.
Copy link
Copy Markdown

@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)
src/components/catalog-filters/highlight-color-picker/highlight-color-picker.tsx (1)

60-95: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove redundant tabIndex/onKeyDown from native color-picker buttons (Floating UI trigger already handles activation)

Native <button>s are keyboard-focusable by default and already activate on Enter/Space, so tabIndex={0} and custom Enter/Space onKeyDown handlers are redundant. For the trigger that already spreads {...getReferenceProps()}, keep that + onClick as the single activation path. Apply the same cleanup to the backdrop (and other footer/swatches/preview buttons in this block).
File: src/components/catalog-filters/highlight-color-picker/highlight-color-picker.tsx (≈ lines 60-95, also ≈ 113-183)

Proposed simplification
       <button
         type='button'
         aria-label={`Select filter color ${index + 1}`}
         className={styles.colorClickbox}
         style={{ backgroundColor: item.color || '`#fff`' }}
-        tabIndex={0}
-        onKeyDown={(e) => {
-          if (e.key === 'Enter' || e.key === ' ') {
-            e.preventDefault();
-            setIsColorPickerOpen(!isColorPickerOpen);
-          }
-        }}
         ref={refs.setReference}
         {...getReferenceProps()}
       >
🤖 Prompt for 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.

In
`@src/components/catalog-filters/highlight-color-picker/highlight-color-picker.tsx`
around lines 60 - 95, The native button elements in HighlightColorPicker (the
trigger using refs.setReference/getReferenceProps, the backdrop button using
styles.colorPickerBackdrop, and the footer/swatches/preview buttons inside the
FloatingPortal) incorrectly include redundant tabIndex={0} and custom onKeyDown
handlers for Enter/Space; remove those tabIndex and onKeyDown props from all
native <button> elements and rely on the existing getReferenceProps() for the
trigger plus a single onClick handler for activation, preserving aria-labels,
ref usage (refs.setReference), isColorPickerOpen/setIsColorPickerOpen state
toggles, and any onClick backdrop-close behavior so keyboard activation uses the
built-in button semantics provided by the browser/Floating UI.
🤖 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.

Outside diff comments:
In
`@src/components/catalog-filters/highlight-color-picker/highlight-color-picker.tsx`:
- Around line 60-95: The native button elements in HighlightColorPicker (the
trigger using refs.setReference/getReferenceProps, the backdrop button using
styles.colorPickerBackdrop, and the footer/swatches/preview buttons inside the
FloatingPortal) incorrectly include redundant tabIndex={0} and custom onKeyDown
handlers for Enter/Space; remove those tabIndex and onKeyDown props from all
native <button> elements and rely on the existing getReferenceProps() for the
trigger plus a single onClick handler for activation, preserving aria-labels,
ref usage (refs.setReference), isColorPickerOpen/setIsColorPickerOpen state
toggles, and any onClick backdrop-close behavior so keyboard activation uses the
built-in button semantics provided by the browser/Floating UI.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99cbbd96-509b-40d0-a2d3-ea515513b48e

📥 Commits

Reviewing files that changed from the base of the PR and between 0719a34 and afa2cda.

📒 Files selected for processing (9)
  • src/components/catalog-filters/catalog-filters.module.css
  • src/components/catalog-filters/highlight-color-picker/highlight-color-picker.module.css
  • src/components/catalog-filters/highlight-color-picker/highlight-color-picker.tsx
  • src/components/catalog-search/catalog-search.module.css
  • src/components/comment-media/comment-media.module.css
  • src/components/post-desktop/post-menu-desktop/post-menu-desktop.module.css
  • src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
  • src/components/settings-modal/subscriptions-setting/subscriptions-setting.module.css
  • src/views/post/post.module.css
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/components/comment-media/comment-media.module.css
  • src/components/catalog-filters/highlight-color-picker/highlight-color-picker.module.css
  • src/views/post/post.module.css
  • src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx

Comment thread src/components/catalog-row/catalog-row.tsx Outdated
The onFocus/onBlur on the inner media-wrapper div were dead (focus bubbles up from the wrapping <Link>, not down to a child), so keyboard focus never opened the preview. Move all four hover/focus handlers onto the <Link> itself so the preview opens on both mouse hover and keyboard focus, keeping onMouseOver paired with onFocus.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2e473ad. Configure here.

>
{isHidden ? t('show_blotter') : t('hide')}
</span>
</button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Redundant keyboard handlers on native button elements

Low Severity

Every <span role="button"> to <button> conversion retains the manual onKeyDown handler for Enter/Space and explicit tabIndex={0}. Native <button> elements are already focusable by default and fire click events on Enter and Space natively, making both the tabIndex and onKeyDown handlers entirely redundant. This pattern is repeated in dozens of locations across the PR, roughly doubling the code needed for each interactive element, and incorrectly signals to future developers that buttons need manual keyboard wiring.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2e473ad. Configure here.

…oo-long

5chan does not support author post edits (only deletion), so remove the 'Comment edited at … / show original' display and its now-dead state, styles (.editedInfo, .showOriginal), and translation keys (comment_edited_at_timestamp, click_here_to_show_original, click_here_to_hide_original, reason_reason) across all languages. Moderation is unchanged.

Also: style the 'comment too long → view full text' control as a proper link instead of plain text, and skip the truncation entirely for admin/owner/moderator authors.
@tomcasaburi tomcasaburi merged commit e082c7b into master May 29, 2026
11 checks passed
@tomcasaburi tomcasaburi deleted the codex/fix/doctor-score branch May 29, 2026 10:09
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