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
1 change: 1 addition & 0 deletions components/AnimatedDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function AnimatedDropdown({
<button
onClick={() => setIsOpen(!isOpen)}
className="flex items-center gap-2 rounded-lg bg-slate-100 px-3 py-2 text-xs font-medium text-slate-600 transition-all hover:bg-slate-200 focus:outline-none focus:ring-2 focus:ring-primary/50 dark:bg-[#1a1622] dark:text-[#a69db9] dark:hover:bg-[#2e2839]"
aria-label={`Toggle dropdown, current selection: ${selectedOption?.label || ''}`}
>
{selectedOption?.label}
Comment on lines 40 to 45
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

Use a non-empty fallback when value no longer matches an option.

If selectedOption is undefined, this becomes Toggle dropdown, current selection: and the trigger renders as icon-only. Please give both the visible label and the aria-label a real fallback such as “Select option” or “No selection”.

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

In `@components/AnimatedDropdown.tsx` around lines 40 - 45, The trigger button in
AnimatedDropdown uses selectedOption?.label directly, causing empty visible text
and an incomplete aria-label when no option matches; update the button to use a
non-empty fallback (e.g., "Select option" or "No selection") for both the
visible label and the aria-label—replace selectedOption?.label with a
fallbackLabel variable or inline fallback in the JSX (refer to AnimatedDropdown,
setIsOpen, and selectedOption) so the button always renders readable text and a
complete aria-label like `Toggle dropdown, current selection: ${fallbackLabel}`.

<span className={`material-symbols-outlined text-sm transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}>
Expand Down
2 changes: 1 addition & 1 deletion components/AuthForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function AuthForm({ initialView = "login" }: { initialView?: "login" | "s
<span className="material-symbols-outlined text-gray-400 group-focus-within:text-primary transition-colors text-[20px]">lock</span>
</div>
<input className="w-full h-12 pl-11 pr-4 bg-gray-50 dark:bg-[#1f1c27] border border-gray-200 dark:border-[#433b54] rounded-xl text-slate-900 dark:text-white text-sm focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all placeholder:text-gray-400" id="password" placeholder="••••••••" type="password" />
<button className="absolute inset-y-0 right-0 pr-3.5 flex items-center text-gray-400 hover:text-white transition-colors" type="button">
<button aria-label="Toggle password visibility" className="absolute inset-y-0 right-0 pr-3.5 flex items-center text-gray-400 hover:text-white transition-colors" type="button">
<span className="material-symbols-outlined text-[20px]">visibility_off</span>
Comment on lines 87 to 89
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

Don’t label this as a toggle until it actually toggles.

Right now the button is focusable and announced as a password-visibility control, but it never changes the input type or icon. Please wire a showPassword state into the input, add an onClick, and expose the pressed state.

Minimal fix
+ const [showPassword, setShowPassword] = useState(false)
- <input className="w-full h-12 pl-11 pr-4 bg-gray-50 dark:bg-[`#1f1c27`] border border-gray-200 dark:border-[`#433b54`] rounded-xl text-slate-900 dark:text-white text-sm focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all placeholder:text-gray-400" id="password" placeholder="••••••••" type="password" />
- <button aria-label="Toggle password visibility" className="absolute inset-y-0 right-0 pr-3.5 flex items-center text-gray-400 hover:text-white transition-colors" type="button">
-   <span className="material-symbols-outlined text-[20px]">visibility_off</span>
+ <input className="w-full h-12 pl-11 pr-4 bg-gray-50 dark:bg-[`#1f1c27`] border border-gray-200 dark:border-[`#433b54`] rounded-xl text-slate-900 dark:text-white text-sm focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all placeholder:text-gray-400" id="password" placeholder="••••••••" type={showPassword ? "text" : "password"} />
+ <button
+   aria-label={showPassword ? "Hide password" : "Show password"}
+   aria-pressed={showPassword}
+   onClick={() => setShowPassword(v => !v)}
+   className="absolute inset-y-0 right-0 pr-3.5 flex items-center text-gray-400 hover:text-white transition-colors"
+   type="button"
+ >
+   <span className="material-symbols-outlined text-[20px]">{showPassword ? "visibility" : "visibility_off"}</span>
  </button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/AuthForm.tsx` around lines 87 - 89, In the AuthForm component,
wire up a showPassword boolean state (e.g., useState in AuthForm) and toggle it
from the password-visibility button: add an onClick handler on the button that
flips showPassword, set the password input (id="password") type to "text" when
showPassword is true else "password", update the button icon text to
"visibility" vs "visibility_off" accordingly, and expose the pressed state via
aria-pressed (and update aria-label to reflect current action like "Show
password" / "Hide password") so the control truly functions as a toggle.

</button>
</div>
Expand Down
1 change: 1 addition & 0 deletions components/EmptyState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function EmptyState({
<button
onClick={onAction}
className="inline-flex items-center gap-2 rounded-xl bg-primary px-6 py-3 text-sm font-semibold text-white transition-all hover:bg-primary/90 hover:scale-105 hover:shadow-lg hover:shadow-primary/25 active:scale-95"
aria-label={actionLabel || "Action"}
>
<span className="material-symbols-outlined text-lg">arrow_back</span>
{actionLabel}
Expand Down
1 change: 1 addition & 0 deletions components/VoiceInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export function VoiceInput({ onAudioSend, disabled }: VoiceInputProps) {
: "bg-primary hover:bg-primary/90 text-white"
}`}
title={isRecording ? t("tutor.stop") : lastError ? "Microphone Access Blocked" : t("tutor.voice_mode")}
aria-label={isRecording ? t("tutor.stop") || "Stop recording" : lastError ? "Microphone Access Blocked" : t("tutor.voice_mode") || "Voice mode"}
>
{isProcessing ? (
<span className="material-symbols-outlined animate-spin text-xl">refresh</span>
Expand Down
2 changes: 2 additions & 0 deletions components/ui/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export function Select({
onClick={() => setIsOpen(!isOpen)}
className={`w-full flex items-center justify-between gap-2 bg-slate-50 dark:bg-[#131118] border border-slate-200 dark:border-[#2e2839] rounded-xl px-4 py-3 text-left text-slate-900 dark:text-white transition-all duration-200 hover:border-primary/50 focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary ${isOpen ? 'border-primary ring-2 ring-primary/50' : ''
}`}
aria-label={`Select option, current value: ${selectedOption?.label || placeholder}`}
>
<span className={selectedOption ? '' : 'text-slate-400 dark:text-[#a69db9]'}>
{selectedOption?.label || placeholder}
Expand Down Expand Up @@ -93,6 +94,7 @@ export function Select({
animationDelay: isOpen ? `${index * 30}ms` : '0ms',
animation: isOpen ? 'slideIn 0.2s ease-out forwards' : 'none'
}}
aria-label={`Select ${option.label}`}
>
<span>{option.label}</span>
{value === option.value && (
Expand Down