Project

General

Profile

Feature #3273 » W16_gate_responsive-audit-3273.txt

Bot AI, 09/08/2026 10:55 AM

 
Gate 7 — Responsive Audit (Ticket #3273, Forgot-Password Flow)
================================================================
Date: 2026-09-08
Feature files:
app/(auth)/forgot-password/page.tsx
app/(auth)/forgot-password/verify/page.tsx
app/(auth)/forgot-password/reset/page.tsx
components/auth/ForgotPasswordForm.tsx
components/auth/VerifyOtpForm.tsx
components/auth/ResetPasswordForm.tsx
components/auth/SignInForm.tsx
Shared deps examined:
components/auth/BrandPanel.tsx
components/ui/SubmitButton.tsx
components/ui/fields/PasswordField.tsx
components/ui/button.tsx
app/(auth)/layout.tsx
app/(auth)/login/page.tsx
app/globals.css
tailwind.config.ts
screen_inventory: <missing — UX-consistency audit used login page as comparator>

Skill resolution:
[components] read_file(".claude/skills/nextjs-tailwind-responsive-components-audit/SKILL.md")
[desktop] read_file(".claude/skills/nextjs-tailwind-responsive-desktop-audit/SKILL.md")
[mobile] read_file(".claude/skills/nextjs-tailwind-responsive-mobile-audit/SKILL.md")
[tablet] read_file(".claude/skills/nextjs-tailwind-responsive-tablet-breakpoint-audit/SKILL.md")
[typography] read_file(".claude/skills/nextjs-tailwind-responsive-typography-audit/SKILL.md")
[ux-consistency] read_file(".claude/skills/nextjs-tailwind-responsive-ux-consistency-audit/SKILL.md")

Tailwind breakpoints (default, no overrides in tailwind.config.ts):
sm=640px md=768px lg=1024px xl=1280px 2xl=1536px

Key observation: ZERO responsive breakpoint prefixes (sm:/md:/lg:/xl:) found in
any of the seven feature files or BrandPanel. The entire auth flow has no
responsive adaptations.

================================================================================
MERGED FINDINGS
================================================================================

--- FINDING 1 (P1) ---
Tags: [mobile] [components] [tablet]
File: components/auth/BrandPanel.tsx (line 7)
Component: BrandPanel
Problem: BrandPanel uses `w-[45%] shrink-0` with no responsive hiding or
collapsing. It occupies 45% of viewport at ALL widths — including mobile.
At 320px → 144px for BrandPanel, 176px remaining for the form.
At 375px → ~169px for BrandPanel, ~206px remaining for the form.
At 640px (sm) → 288px for BrandPanel, 352px remaining.
The form containers need 360-480px, so they overflow or are crushed.
Evidence: className="relative h-full w-[45%] shrink-0 overflow-hidden bg-accent"
No sm:/md:/lg: prefixes. No hidden/md:block pattern.
Why it matters: The auth flow is completely unusable on any device below ~800px.
Forms overflow their container or are clipped. This is a blocking mobile/tablet
issue affecting all four auth screens (login, forgot-password, verify, reset).
Recommended fix: Add `hidden lg:block` to BrandPanel wrapper (hide below 1024px),
or `hidden md:block` (hide below 768px). The form-side container should become
the full-width layout on mobile. Pattern: `<BrandPanel className="hidden lg:block" />`
and ensure the right-side form container doesn't depend on BrandPanel being present.
Risk of change: Low — BrandPanel is purely decorative/branding, hides no
actionable content. The three-file shell (login/page.tsx, ForgotPasswordForm,
VerifyOtpForm, ResetPasswordForm) needs the same change.
Fixed: false

--- FINDING 2 (P1) ---
Tags: [mobile] [tablet]
File: components/auth/ForgotPasswordForm.tsx (line 27)
Component: ForgotPasswordForm — form container
Problem: Form uses `w-[360px]` — a fixed pixel width with no responsive fallback.
On mobile (even if BrandPanel were hidden), 360px exceeds 320px viewport,
and on a 375px viewport the form would touch both edges with no padding.
Evidence: className="flex w-[360px] flex-col gap-1.5"
Compare with SignInForm (line 18): className="flex w-full max-w-[360px] flex-col gap-1.5"
SignInForm uses `w-full max-w-[360px]` which is the correct mobile-safe pattern.
Why it matters: Horizontal overflow on mobile — the form extends past the viewport edge,
parts of the input and button are unreachable.
Recommended fix: Change `w-[360px]` to `w-full max-w-[360px] px-6` (same pattern as
ResetPasswordForm, or `w-full max-w-[360px]` like SignInForm).
Risk of change: None — at desktop widths max-w-[360px] renders identically to w-[360px].
Fixed: false

--- FINDING 3 (P1) ---
Tags: [mobile] [tablet]
File: components/auth/VerifyOtpForm.tsx (line 103)
Component: VerifyOtpForm — form container
Problem: Same fixed-width issue as Finding 2. Form uses `w-[360px]` instead of
`w-full max-w-[360px]`.
Evidence: className="flex w-[360px] flex-col gap-1.5"
Why it matters: Same overflow as Finding 2. Additionally, the OTP digit boxes
inside need 338px minimum (see Finding 4), so even a `max-w-[360px]` container
barely fits them.
Recommended fix: Change to `w-full max-w-[360px] px-4` or `w-full max-w-[400px] px-6`.
Risk of change: None at desktop.
Fixed: false

--- FINDING 4 (P1) ---
Tags: [mobile]
File: components/auth/VerifyOtpForm.tsx (lines 123-146)
Component: VerifyOtpForm — OTP digit boxes
Problem: 6 digit inputs × w-12 (48px) + 5 × gap-[10px] (50px) = 338px minimum width.
Even if BrandPanel is hidden and the form is full-width, 338px overflows a 320px
viewport. At 360-375px with any padding, the boxes are crushed or overflow.
Evidence: className="flex gap-[10px] mb-2" (container)
Each input: className="h-[52px] w-12 rounded-md ..."
6 × 48 + 5 × 10 = 338px hard minimum.
Why it matters: OTP entry — the core action of this screen — is physically
impossible on a small mobile device. Boxes overflow and the rightmost digits
are unreachable.
Recommended fix: At mobile widths, reduce box size to w-10 (40px) and gap to
gap-1.5 (6px): 6×40+5×6 = 270px, fits comfortably in 320px with padding.
Pattern: `className="flex gap-1.5 sm:gap-[10px] mb-2"` and per-box
`className="h-11 w-10 sm:h-[52px] sm:w-12 ..."`.
Risk of change: Low — smaller boxes are still easily tappable at 40×44px.
Fixed: false

--- FINDING 5 (P2) ---
Tags: [mobile] [components]
File: components/ui/fields/PasswordField.tsx (lines 66-72)
Component: PasswordField — show/hide toggle button
Problem: Toggle button has no explicit touch-target sizing. The rendered tappable
area is approximately 16×16px (the Eye/EyeOff icon is `h-4 w-4`). No padding
on the <button>, so the hit area is just the icon itself. Well below the 44×44px
accessible touch target minimum.
Evidence: <button ... className="absolute right-2 top-1/2 -translate-y-1/2 text-text-helper">
{show ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
No p-* or min-h-*/min-w-* on the button element.
Why it matters: On mobile, users will frequently mis-tap this control or find it
difficult to toggle password visibility — a common mobile usability complaint.
Recommended fix: Add padding to the button: `className="absolute right-2 top-1/2
-translate-y-1/2 p-2 text-text-helper"` — this makes the hit area ~32×32px
(icon + padding), or use `p-3` for full 40×40px. Ensure the parent input's
`pr-9` accounts for the larger hit area.
Risk of change: Low — minor visual adjustment to clickable area, no layout shift.
Fixed: false

--- FINDING 6 (P2) ---
Tags: [mobile]
File: components/auth/ForgotPasswordForm.tsx (lines 28-34),
components/auth/VerifyOtpForm.tsx (lines 107-113)
Component: "Back to Sign In" / "Back" navigation link
Problem: Link touch target is small — the ChevronLeft icon is h-4 w-4 (16px),
and the text is `text-[13px]`. The flex container has no min-height or padding,
so the tappable area is approximately 16-18px tall. Below the accessible
minimum for a navigation control.
Evidence: <Link ... className="mb-[14px] flex cursor-pointer items-center gap-1.5">
<ChevronLeft className="h-4 w-4" />
<span className="text-[13px] ...">Back to Sign In</span>
No py-* or min-h-* on the Link element.
Why it matters: Primary navigation control for going back in the flow. Hard to tap
accurately on mobile without mis-hitting the heading below.
Recommended fix: Add vertical padding: `className="mb-[14px] flex cursor-pointer
items-center gap-1.5 py-2"` for a ~32px touch target height.
Risk of change: None — adds invisible tappable area, doesn't change visual design.
Fixed: false

--- FINDING 7 (P2) ---
Tags: [mobile]
File: components/auth/VerifyOtpForm.tsx (lines 168-175)
Component: VerifyOtpForm — "Resend Code" button
Problem: Button has no padding — rendered size is approximately text height only
(~18px) at `text-[13px] leading-[18px]`. Touch target well below 44px minimum.
Evidence: <button ... className="text-[13px] leading-[18px] font-semibold text-accent
disabled:opacity-50">
No py-*/px-*/min-h-* on the button.
Why it matters: Users who need to resend a code on mobile will struggle to tap this
accurately, especially adjacent to the "Didn't get a code?" text.
Recommended fix: Add `py-2 px-1` or wrap in a larger touch area.
Risk of change: None visual if using only py-* padding.
Fixed: false

--- FINDING 8 (P2) ---
Tags: [ux-consistency]
File: components/auth/ForgotPasswordForm.tsx (line 27),
components/auth/VerifyOtpForm.tsx (line 103),
components/auth/SignInForm.tsx (line 18),
components/auth/ResetPasswordForm.tsx (line 65)
Component: Form width strategy across auth screens
Problem: Inconsistent width approach among closely related forms in the same flow:
- SignInForm: `w-full max-w-[360px]` ← mobile-safe
- ResetPasswordForm: `w-full max-w-[480px] px-6` ← mobile-safe
- ForgotPasswordForm: `w-[360px]` ← NOT mobile-safe
- VerifyOtpForm: `w-[360px]` ← NOT mobile-safe
Two of four forms in the same auth flow use a mobile-safe pattern, two don't.
Evidence: Direct className comparison above.
Why it matters: Inconsistency means two screens in the same user flow will break
on mobile while two won't — an uneven experience that suggests the mobile-safe
pattern was established (SignInForm/ResetPasswordForm) but not applied
consistently to the newer forgot-password forms.
Recommended fix: Standardize all four forms to `w-full max-w-[360px]` (or max-w-[400px]
for VerifyOtpForm due to OTP boxes needing 338px). Add `px-6` for mobile padding.
Risk of change: None — the max-w pattern renders identically at desktop widths.
Fixed: false

--- FINDING 9 (P2) ---
Tags: [tablet]
File: components/auth/BrandPanel.tsx (lines 15-22)
Component: BrandPanel — inner content area
Problem: At tablet-range widths where BrandPanel might be shown (768-1024px),
BrandPanel at 45% = 345-460px. Inner content uses `left-12 right-12` (48px each),
leaving 249-364px for the `text-3xl` (30px) heading which wraps to 3 lines
with explicit <br> tags. At ~345px panel width (768px viewport), the content
area is 249px — cramped for the heading but technically readable.
At smaller widths below 768px (if BrandPanel isn't hidden per Finding 1's fix),
the heading becomes truly illegible.
Evidence: className="absolute left-12 right-12 top-1/2 flex -translate-y-1/2 flex-col gap-[18px]"
Heading: className="text-3xl font-bold leading-[38px] text-accent-subtle"
Content: 3 lines with explicit <br> tags.
Why it matters: If the responsive fix for Finding 1 hides BrandPanel below lg
(1024px), this is moot. If it hides at md (768px), the 768-1024px range should
be checked to ensure heading doesn't overflow. At 1024px → panel=460px,
content=364px — fine.
Recommended fix: Dependent on Finding 1's breakpoint choice. If BrandPanel shows
at md+ (768px+), consider reducing heading to `text-2xl md:text-3xl` and
reducing horizontal padding to `left-8 right-8` at md.
Risk of change: Low — BrandPanel is decorative.
Fixed: false

--- FINDING 10 (P3) ---
Tags: [typography]
File: components/auth/BrandPanel.tsx (line 16-17)
Component: BrandPanel heading
Problem: `text-3xl` (30px) heading with explicit `<br>` line breaks — no responsive
variant. If BrandPanel is visible at intermediate widths (per Finding 9), the
hard-coded line breaks may produce suboptimal wrapping.
Evidence: <span className="text-3xl font-bold leading-[38px] text-accent-subtle">
Manage your orders,<br />expenses and suppliers<br />in one place.
Why it matters: Minor — hard <br> tags prevent natural text reflow. At widths
where the text would fit on fewer lines, the breaks force it to wrap early;
at widths where it's cramped, the breaks may cause awkward single-word lines.
Recommended fix: Remove <br> tags and let text wrap naturally, OR keep them and
add responsive hiding: `<br className="hidden md:inline" />`.
Risk of change: Very low — visual-only change to decorative panel.
Fixed: false

--- FINDING 11 (P3) ---
Tags: [desktop]
File: components/auth/ResetPasswordForm.tsx (line 62)
Component: ResetPasswordForm — right-side scroll container
Problem: The right-side container uses `overflow-auto py-10` which is good practice
(allows scroll for longer form content), but the form itself uses `max-w-[480px]`
while the other forms use 360px. At desktop widths this is fine — the form is
well-centered. At 1920px+ the auth shell is appropriately proportioned. No
real desktop issue found.
Evidence: className="flex flex-grow items-center justify-center overflow-auto py-10"
form: className="flex w-full max-w-[480px] flex-col px-6"
Why it matters: Informational only — ResetPasswordForm's wider max-w is justified
by its additional content (strength meter, policy card, two buttons). Desktop
layout is correct.
Recommended fix: None needed for desktop. Already well-handled.
Risk of change: N/A
Fixed: N/A (no issue)

--- FINDING 12 (P3) ---
Tags: [typography]
File: components/auth/VerifyOtpForm.tsx (lines 159-176),
components/auth/ForgotPasswordForm.tsx (lines 83-85)
Component: Footer helper text
Problem: Footer text uses `text-xs leading-[18px]` (12px). While technically readable,
this is at the lower bound of comfortable mobile reading size. Combined with
`text-text-faint` (#a3a3a3 on white — contrast ratio ~2.6:1), it may fail WCAG
AA contrast for small text (requires 4.5:1).
Evidence: <p className="mt-5 text-center text-xs leading-[18px] text-text-faint">
color #a3a3a3 on #ffffff background = ~2.6:1 contrast ratio
Why it matters: Accessibility concern (contrast), but the text is supplementary
("The code expires in 10 minutes...") not actionable. Still, WCAG violation.
Recommended fix: Darken text-faint to at least #767676 (4.5:1 on white) or use
text-text-helper (#757575, ~4.6:1 — passes AA). This is a design-token-level
fix, not per-component.
Risk of change: Design decision — affects all text-text-faint usage app-wide.
Fixed: false

================================================================================
SUMMARY
================================================================================

Total findings: 12 (11 real issues + 1 informational/no-issue desktop confirmation)
P1: 4 (Findings 1-4) — all unresolved, all relate to the fundamental problem
that the two-panel auth shell has no responsive behavior at all.
P2: 5 (Findings 5-9)
P3: 3 (Findings 10-12)

Breakpoints reviewed: sm (640px), md (768px), lg (1024px), xl (1280px),
plus mobile (320-430px) and large desktop (1920-2560px).

Root cause: The auth shell (BrandPanel + form column) was designed and
implemented exclusively for desktop. No responsive breakpoint classes exist
in any feature file. SignInForm and ResetPasswordForm accidentally use a
more mobile-tolerant width pattern (w-full max-w-*), but the shell itself
(BrandPanel at 45% fixed width) defeats any benefit.

gate_status: FAIL — 4 unresolved P1 findings.
(9-9/10)