Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import AnalysisDashboard from './pages/AnalysisDashboard';
import MarketMapPage from './pages/MarketMapPage';
import ResultsPage from './pages/ResultsPage';
import Leaderboard from './pages/Leaderboard';
import FAQPage from './pages/FAQPage';
import PostHogPageView from './components/PostHogPageView';
import NotFound from './pages/NotFound';
import InstallPrompt from './components/InstallPrompt';
Expand Down Expand Up @@ -48,7 +49,8 @@ useEffect(() => {
<Route path="/map" element={<MarketMapPage />} />
<Route path="/leaderboard" element={<Leaderboard />} />
<Route path="/results" element={<ResultsPage />} />

<Route path="/faq" element={<FAQPage />} />

{/* Public shareable report — MUST be before the * catchall */}
<Route path="/report/:id" element={<PublicReport />} />

Expand All @@ -58,4 +60,7 @@ useEffect(() => {
</Routes>
</BrowserRouter>
);
}
}



4 changes: 3 additions & 1 deletion src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export default function Navbar() {
{ to: '/', label: t('home') },
{ to: '/scanner', label: t('scanner') },
{ to: '/map', label: t('trustMap') },
{ to: '/faq', label: 'FAQ' },
];

return (
Expand Down Expand Up @@ -168,4 +169,5 @@ export default function Navbar() {
</div>
</nav>
);
}
}

84 changes: 84 additions & 0 deletions src/pages/FAQPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useState } from "react";
import { ChevronDown, ScanLine, Award, MapPin } from "lucide-react";

const faqs = [
{
title: "How should I position the fish for scanning?",
icon: ScanLine,
content:
"Place the fish on a flat surface with good lighting. Keep the camera around 15 cm away and ensure the whole fish is visible.",
},
{
title: "What do the freshness scores mean?",
icon: Award,
content:
"Scores range from 0 to 100. Higher scores indicate fresher fish. Scores above 80 are considered high quality.",
},
{
title: "How does the Market Map work?",
icon: MapPin,
content:
"The Market Map displays freshness trends from anonymized scan results and helps users identify reliable seafood vendors.",
},
];

export default function FAQPage() {
const [openIndex, setOpenIndex] = useState<number | null>(0);

return (
<div className="px-6 md:px-16 lg:px-24 py-16">
<div className="max-w-4xl mx-auto">
<span className="status-terminal block mb-4">
USER_GUIDE_AND_FAQ
</span>

<h1 className="text-4xl md:text-6xl font-bold mb-6">
How To Use
<br />
<span className="text-neon">FreshScan AI</span>
</h1>

<p className="text-on-surface-variant mb-12">
Learn how to scan fish correctly, understand freshness scores,
and use the Market Map effectively.
</p>

<div className="space-y-3">
{faqs.map((faq, index) => (
<div
key={index}
className="bg-surface-mid hover:bg-surface-high transition-colors duration-200"
>
<button
className="w-full p-5 flex items-center justify-between text-left"
onClick={() =>
setOpenIndex(openIndex === index ? null : index)
}
>
<div className="flex items-center gap-3">
<faq.icon size={20} className="text-neon" />
<span className="font-semibold">{faq.title}</span>
</div>

<ChevronDown
size={18}
className={`transition-transform ${
openIndex === index ? "rotate-180" : ""
}`}
/>
</button>

{openIndex === index && (
<div className="px-5 pb-5">
<p className="text-on-surface-variant">
{faq.content}
</p>
</div>
)}
Comment on lines +52 to +77

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

Add ARIA state/linkage for the accordion controls.

Line 52 renders interactive disclosure controls, but the button/panel pair has no aria-expanded/aria-controls + id linkage, so assistive tech can’t reliably interpret expanded state.

Suggested fix
           {faqs.map((faq, index) => (
+            const panelId = `faq-panel-${index}`;
+            const buttonId = `faq-button-${index}`;
+            return (
             <div
               key={index}
               className="bg-surface-mid hover:bg-surface-high transition-colors duration-200"
             >
               <button
+                id={buttonId}
                 className="w-full p-5 flex items-center justify-between text-left"
+                aria-expanded={openIndex === index}
+                aria-controls={panelId}
                 onClick={() =>
                   setOpenIndex(openIndex === index ? null : index)
                 }
               >
@@
-              {openIndex === index && (
-                <div className="px-5 pb-5">
+              {openIndex === index && (
+                <div
+                  id={panelId}
+                  role="region"
+                  aria-labelledby={buttonId}
+                  className="px-5 pb-5"
+                >
                   <p className="text-on-surface-variant">
                     {faq.content}
                   </p>
                 </div>
               )}
             </div>
+            );
           ))}
🤖 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/pages/FAQPage.tsx` around lines 52 - 77, The accordion button element is
missing ARIA attributes required for proper accessibility. Add an aria-expanded
attribute to the button that reflects the open state (true when openIndex equals
index, false otherwise), add a unique id to the content div that displays when
the accordion is expanded, and add an aria-controls attribute to the button that
references the content div's id. This enables assistive technologies to properly
announce the expanded/collapsed state and understand the relationship between
the control button and the content panel.

</div>
))}
</div>
</div>
</div>
);
}
Loading