Skip to content

fix(auth): unify link affordances across the sign-in screens - #237

Merged
aspiers merged 3 commits into
mainfrom
fix/link-affordances
Aug 4, 2026
Merged

fix(auth): unify link affordances across the sign-in screens#237
aspiers merged 3 commits into
mainfrom
fix/link-affordances

Conversation

@aspiers

@aspiers aspiers commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Implements beads issue atproto-n1s.

Problem

The sign-in screens styled clickable text four different ways, so the same action rendered differently depending on where it appeared:

class underline element used for
.btn-secondary (login-page) no <button> Resend code, Use different email
.btn-secondary (account-login, recovery) yes <button>/<a> Resend code, Back to sign in
.recovery-link yes <a> Recover with backup email
.flash-action yes <button> inline error actions
.terms-link yes <a> Terms, Privacy

As reported: "resend code sometimes appears as a clickable link with no underlining and then other times with underlining, and recover with backup mail is underlined, but Use different email isn't". Both halves of that report are covered — the first is the .btn-secondary split across pages, the second is .recovery-link versus its neighbours in the same cluster.

Hover was inverted too: .flash-action removed its underline on hover while .recovery-link kept its and darkened.

The convention

The anchor-vs-button split is real in the markup but invisible to a user, since .btn-secondary elements are buttons deliberately styled to look like links. The distinction that actually carries meaning is positional:

  • Standalone (.btn-secondary, .recovery-link) — in their own row, where position and spacing already read as actionable. No underline.
  • In-sentence (.flash-action, .terms-link) — surrounded by prose and inheriting its colour, so the underline is their only marker of being clickable. Underlined.

.recovery-link was the outlier and aligns to .btn-secondary, the more numerous treatment. This preserves the reasoning from 3d31876, where .flash-action's underline is the whole of its affordance inside the error sentence.

Hover now darkens to #1A130F on all four, and no rule toggles text-decoration in either direction — an affordance that appears or vanishes under the cursor is disorienting.

Screenshots

Both sides are live Railway previews — PR #223's, which still carries the old CSS, and this branch's own. The "before" shots predate #236, so they still show the button's old "Resend code" label; that rename is unrelated to this change.

Before: in one cluster, "Recover with backup email" is underlined while "Resend code" and "Use different email" directly above it are not — same position, same purpose, different affordance.

Before: inconsistent underlines

After: all three standalone actions match.

After: consistent underlines

With a rejected code

Submitting a wrong code used to surface better-auth's own string verbatim, running straight into the recovery link: "Invalid OTP Send a new code". This PR also rewrites those messages for end users and separates them from the action.

The inline action stays underlined on both sides, and deliberately so: it sits inside the sentence and inherits its red, so the underline is the only thing marking it clickable. That is the distinction the convention preserves — position decides, not element type.

Before: the raw error runs into the action with only a space between them, and "Recover with backup email" is underlined while the buttons above it are not.

Before: raw "Invalid OTP" running into the action

After: "That code didn't work." reads as a sentence, separated from the action, and the standalone cluster below is uniform.

After: human copy, separated from the action

Accessibility

  • .recovery-link, .flash-action and .terms-link had no :focus-visible rule, so keyboard users got only the UA default ring — none at all for the buttons, since the reset zeroes it. All three now match .btn-secondary's.
  • .btn-secondary hardcoded #6b6b6b instead of reading --muted-foreground — the same defect 6ed3d05 fixed for .divider in fix(auth): darken muted sign-in text to meet WCAG AA #232, and the last standalone action ignoring a branding.css override. Following the token also lifts it from 5.02:1 to 5.41:1 against the card, so contrast improves rather than regresses.
  • Contrast verified: #666 on #F8F8F8 = 5.41:1 and on #E8E8E8 = 4.69:1, both clearing WCAG AA.

Demo theme

packages/demo/src/lib/theme.ts overrode .recovery-link to textHint and .btn-secondary to textMuted, which would have re-split the cluster under any theme, and only .recovery-link had a hover override. Both selectors now resolve identically.

Tests

13 new tests in login-page.test.ts pin each half of the convention plus the hover rule and focus ring, matching whole declaration blocks so a rule mentioning the property elsewhere cannot satisfy them. Each was verified to fail when its corresponding CSS change is reverted — no vacuous passes.

Gates: format:check, lint, typecheck all clean; pnpm test 1102 passed across 73 files.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Sign-in now shows clearer plain-English messages for invalid, expired, or exhausted verification codes.
    • Unknown sign-in errors continue displaying their original messages.
  • Style

    • Unified sign-in and recovery link styling with clearer hover states.
    • Inline links remain underlined, while standalone actions use simpler text styling.
    • Added visible keyboard focus outlines for links.
    • Improved punctuation and spacing around inline actions.

Copilot AI lite review requested due to automatic review settings August 4, 2026 18:23
@changeset-bot

changeset-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bfd481d

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
epds-demo Ready Ready Preview Aug 4, 2026 8:32pm

Request Review

@railway-app

railway-app Bot commented Aug 4, 2026

Copy link
Copy Markdown

🚅 Deployed to the ePDS-pr-237 environment in ePDS

Service Status Web Updated (UTC)
@certified-app/demo ✅ Success (View Logs) Web Aug 4, 2026 at 8:33 pm
@certified-app/demo untrusted ✅ Success (View Logs) Web Aug 4, 2026 at 8:33 pm
@certified-app/pds-core ✅ Success (View Logs) Web Aug 4, 2026 at 8:33 pm
@certified-app/auth-service ✅ Success (View Logs) Web Aug 4, 2026 at 8:33 pm

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@aspiers, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 10 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eb9ea687-cf3e-48bc-a1fa-bf54ef376bd5

📥 Commits

Reviewing files that changed from the base of the PR and between 277af89 and bfd481d.

📒 Files selected for processing (9)
  • .changeset/human-sign-in-error-copy.md
  • .changeset/unify-link-affordances.md
  • e2e/step-definitions/auth.steps.ts
  • features/passwordless-authentication.feature
  • packages/auth-service/src/__tests__/login-page.test.ts
  • packages/auth-service/src/routes/account-login.ts
  • packages/auth-service/src/routes/login-page.ts
  • packages/auth-service/src/routes/recovery.ts
  • packages/demo/src/lib/theme.ts
📝 Walkthrough

Walkthrough

The PR normalizes known OTP verification errors while preserving raw backend errors for control flow. It also standardizes standalone and inline sign-in link styling across authentication routes, tests, theme CSS, and changesets.

Changes

Sign-in user experience

Layer / File(s) Summary
OTP error normalization and branching
packages/auth-service/src/routes/login-page.ts, packages/auth-service/src/__tests__/login-page.test.ts, .changeset/human-sign-in-error-copy.md
Known OTP errors now use plain-English display messages. Raw errors remain available for expiry detection and unknown-error handling. Tests cover message rewriting, branching, punctuation, and preserved errors.
Sign-in link affordances
packages/auth-service/src/routes/login-page.ts, packages/auth-service/src/routes/account-login.ts, packages/auth-service/src/routes/recovery.ts, packages/demo/src/lib/theme.ts, packages/auth-service/src/__tests__/login-page.test.ts, .changeset/unify-link-affordances.md
Standalone actions no longer use underlines. Inline actions keep persistent underlines. Hover colors, focus outlines, and --muted-foreground usage are standardized across authentication views and theme CSS.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant login-page
  participant verifyOtp
  participant better-auth
  login-page->>verifyOtp: Submit OTP
  verifyOtp->>better-auth: Verify OTP
  better-auth-->>verifyOtp: Return raw verification error
  verifyOtp-->>login-page: Return display text and rawError
  login-page->>login-page: Detect expiry from rawError and render message
Loading

Possibly related PRs

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: consistent link styling across sign-in screens.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 fix/link-affordances

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Unifies how “link-like” actions are styled across the auth sign-in/recovery surfaces, so equivalent actions look consistent regardless of whether they’re rendered as <button>s or <a>s, and improves keyboard focus visibility for inline/standalone actions.

Changes:

  • Standardize underline/hover behavior across .btn-secondary, .recovery-link, .flash-action, and .terms-link, including adding :focus-visible outlines where missing.
  • Align demo theme overrides so .btn-secondary and .recovery-link theme identically (including hover), avoiding theme-driven divergence.
  • Add targeted CSS-pin tests in login-page.test.ts to prevent regressions in the “standalone vs in-sentence” affordance convention, hover, focus ring, and muted-foreground token usage.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/auth-service/src/routes/login-page.ts Implements the link-affordance convention in the sign-in page CSS (underline/hover/focus-visible/token usage).
packages/auth-service/src/routes/account-login.ts Updates .btn-secondary styling to match the standalone-action convention (no underline, hover darken).
packages/auth-service/src/routes/recovery.ts Updates .btn-secondary styling to match the standalone-action convention (no underline, hover darken).
packages/auth-service/src/tests/login-page.test.ts Adds tests that pin the CSS convention, hover behavior, focus ring presence, and token usage.
packages/demo/src/lib/theme.ts Ensures demo theming treats .btn-secondary and .recovery-link as a single visual class (including hover).
.changeset/unify-link-affordances.md Adds a patch changeset describing user-visible styling consistency + theming impact for client developers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coveralls-official

coveralls-official Bot commented Aug 4, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 30948284199

Coverage remained the same at 57.909%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 3106
Covered Lines: 1789
Line Coverage: 57.6%
Relevant Branches: 1945
Covered Branches: 1136
Branch Coverage: 58.41%
Branches in Coverage %: Yes
Coverage Strength: 9.78 hits per line

💛 - Coveralls

Copilot AI review requested due to automatic review settings August 4, 2026 18:55
@aspiers
aspiers force-pushed the fix/link-affordances branch from 315ec60 to a839835 Compare August 4, 2026 18:55
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-237 August 4, 2026 18:55 Destroyed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (3)

packages/auth-service/src/tests/login-page.test.ts:857

  • The test helper ruleFor() is tightly coupled to the exact indentation/newline formatting of the rendered CSS (\n ${selector} {). That makes these tests prone to false failures from harmless formatting changes (e.g., template indentation tweaks), even when the CSS behavior is unchanged. Consider locating the rule with a regex that ignores whitespace and escaping selector metacharacters.
  function ruleFor(html: string, selector: string): string {
    const idx = html.indexOf(`\n    ${selector} {`)
    expect(idx, `no rule found for "${selector}"`).toBeGreaterThan(0)
    const open = html.indexOf('{', idx)
    const close = html.indexOf('}', open)
    expect(close).toBeGreaterThan(open)
    return html.slice(open + 1, close)
  }

packages/auth-service/src/routes/recovery.ts:518

  • This comment says "Resend code" here and on the sign-in page "must not render differently", but the two screens still differ in multiple styling details (e.g., hover color, font weight/padding). To avoid misleading future readers, narrow the claim to the specific affordance being unified (underline/hover behavior).
  /* Standalone action in its own row — see the link-affordance convention
     documented in login-page.ts. No underline, darkens on hover. "Resend
     code" here and on the sign-in page must not render differently. */

packages/auth-service/src/routes/account-login.ts:254

  • This comment says "Resend code" here and on the sign-in page "must not render differently", but the two screens still differ in several styling details. Narrow the statement to the specific affordance being unified (underline/hover behavior) so it doesn't become misleading documentation.
  /* Standalone action in its own row — see the link-affordance convention
     documented in login-page.ts. No underline, darkens on hover. "Resend
     code" here and on the sign-in page must not render differently. */

Copilot AI review requested due to automatic review settings August 4, 2026 19:42
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-237 August 4, 2026 19:42 Destroyed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Suppressed comments (2)

packages/auth-service/src/routes/login-page.ts:1136

  • The separator between the error text and its inline action decides whether to insert a period by testing /[.!?]$/ against msg, but if msg has trailing whitespace/newlines the test fails and you can end up with double punctuation (e.g. "... . Send a new code"). Consider trimming trailing whitespace before both rendering and punctuation detection, and avoid adding any separator when the message is empty.
          frag.appendChild(
            document.createTextNode(/[.!?]$/.test(msg) ? ' ' : '. '),
          );

packages/auth-service/src/routes/login-page.ts:1117

  • PR description says the page renders better-auth's "Invalid OTP" message verbatim, but this change introduces otpErrorText() to rewrite several better-auth errors into end-user copy. Please update the PR description/screenshots text accordingly so reviewers and future readers aren't misled about the behavior being shipped.
      function otpErrorText(raw) {
        switch (raw) {
          case 'Invalid OTP': return "That code didn't work.";
          case 'OTP expired': return 'That code has expired.';
          case 'Too many attempts':

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@packages/auth-service/src/routes/account-login.ts`:
- Around line 252-256: Update the route-local .btn-secondary rules in
packages/auth-service/src/routes/account-login.ts:252-256 and
packages/auth-service/src/routes/recovery.ts:516-520 to use the shared
--muted-foreground token with its existing fallback and the shared hover color,
preserving consistent themed styling across both routes.
🪄 Autofix

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 Plus

Run ID: fe3666c3-b872-4f7a-9908-d26da69c7e3f

📥 Commits

Reviewing files that changed from the base of the PR and between 02c2d5e and 277af89.

📒 Files selected for processing (7)
  • .changeset/human-sign-in-error-copy.md
  • .changeset/unify-link-affordances.md
  • packages/auth-service/src/__tests__/login-page.test.ts
  • packages/auth-service/src/routes/account-login.ts
  • packages/auth-service/src/routes/login-page.ts
  • packages/auth-service/src/routes/recovery.ts
  • packages/demo/src/lib/theme.ts

Comment thread packages/auth-service/src/routes/account-login.ts
@blacksmith-sh

This comment has been minimized.

Copilot AI review requested due to automatic review settings August 4, 2026 19:52
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-237 August 4, 2026 19:52 Destroyed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (3)

packages/auth-service/src/routes/account-login.ts:256

  • The hover color here is #000, but the link-affordance convention in login-page.ts (and the PR description) says these standalone link-styled actions should darken to #1A130F. Also, the comment still references the old label "Resend code" even though the button now says "Send a new code".
  /* Standalone action in its own row — see the link-affordance convention
     documented in login-page.ts. No underline, darkens on hover. "Resend
     code" here and on the sign-in page must not render differently. */
  .btn-secondary { display: inline-block; color: #0f1828; background: none; border: none; font-size: 14px; cursor: pointer; text-decoration: none; border-radius: 4px; }
  .btn-secondary:hover { color: #000; }

packages/auth-service/src/routes/recovery.ts:520

  • The hover color here is #000, but the link-affordance convention in login-page.ts (and the PR description) says these standalone link-styled actions should darken to #1A130F. Also, the comment still references the old label "Resend code" even though the UI now says "Send a new code".
  /* Standalone action in its own row — see the link-affordance convention
     documented in login-page.ts. No underline, darkens on hover. "Resend
     code" here and on the sign-in page must not render differently. */
  .btn-secondary { display: inline-block; margin-top: 12px; color: #0f1828; background: none; border: none; font-size: 14px; cursor: pointer; text-decoration: none; border-radius: 4px; }
  .btn-secondary:hover { color: #000; }

packages/auth-service/src/routes/login-page.ts:656

  • This comment refers to the "separate Resend button", but the UI label was changed to "Send a new code" (and this PR relies on the wording distinction). Updating the comment keeps it consistent with the current UI copy.
    /* Inline action button rendered next to an OTP-expired error so
       the user doesn't have to hunt for the separate Resend button.
       Styled as a link rather than a button to make it visually

aspiers and others added 3 commits August 4, 2026 21:31
The sign-in page styled clickable text four different ways, so the same
action rendered differently depending on where it appeared. "Resend
code" is underlined on the account-login and recovery pages but not on
the sign-in page; "Recover with backup email" is underlined while the
"Use different email" button beside it in the same cluster is not.

The anchor-vs-button split behind that inconsistency is real in the
markup but invisible to a user, since .btn-secondary elements are
buttons deliberately styled to look like links. The distinction that
actually carries meaning is positional:

  STANDALONE  (.btn-secondary, .recovery-link) sit in their own row,
              where position and spacing already read as actionable, so
              they need no underline.
  IN-SENTENCE (.flash-action, .terms-link) are surrounded by prose and
              inherit its colour, so the underline is their only marker
              of being clickable and must stay.

That keeps intact the reasoning from 3d31876 for .flash-action, whose
underline is the whole of its affordance inside the error sentence.

Hover was also inverted between the two: .flash-action REMOVED its
underline on hover while .recovery-link kept its and darkened. An
affordance that appears or vanishes under the cursor is disorienting,
so every one of the four now darkens to #1A130F and no rule toggles
text-decoration in either direction.

Two accessibility gaps fixed along the way. .recovery-link,
.flash-action and .terms-link had no :focus-visible rule at all, so
keyboard users got only the UA default ring (none, for the buttons,
since the reset zeroes it); all three now match .btn-secondary's.
And .btn-secondary hardcoded #6b6b6b rather than reading
--muted-foreground — the same defect 6ed3d05 fixed for .divider, and
the last standalone action that ignored a branding.css override. It
now follows the token, which also lifts it from 5.02:1 to 5.41:1
against the card.

The demo theme overrode .recovery-link to textHint and .btn-secondary
to textMuted, which would have re-split the cluster under any theme;
both now resolve identically.

Tests pin each half of the convention, the hover rule and the focus
ring, so the four cannot silently drift apart again.

Implements beads issue atproto-n1s.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A rejected code rendered "Invalid OTP Send a new code" — better-auth's
own wording pushed straight to the user, running into the recovery
link beside it with only a space between them.

"Invalid OTP" is a developer string: an unexplained acronym, no
article, no indication of what to do next. It is also the single line
a user actually reads when sign-in fails. Map the three known reasons
to plain copy, and supply a full stop before the inline action when
the message does not already end in one.

Unrecognised errors pass through verbatim rather than collapsing into
a generic apology: an unexpected failure that still names itself can
be diagnosed from a screenshot, one that says "Something went wrong"
cannot.

verifyOtp() now returns the raw reason alongside the display text, and
the expired-code branch tests the raw one. Matching the rewritten copy
would have worked by luck today — "That code has expired." happens to
contain "expir" — but would couple control flow to wording, so a later
copy edit could silently change which recovery action is offered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The expiry scenario asserted the banner contained "OTP expired" —
better-auth's raw string, which the previous commit stopped rendering.
CI caught it: 74 scenarios passed, that one failed with

  Expected substring: "OTP expired"
  unexpected value "That code has expired. Send a new code"

The unit tests missed it because they assert against the rendered page
source, not against what a browser displays, so nothing local covered
the gap between the two.

Assert the user-facing string instead. Loosening the assertion to keep
matching the old wording would have been worse: the scenario exists to
prove the user is told something useful, and better-auth's wording is
now an implementation detail behind otpErrorText().

The step pattern becomes a regex accepting "an" or "the" so the feature
line reads as English with a message that starts with "That".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 4, 2026 20:31
@aspiers
aspiers force-pushed the fix/link-affordances branch from f8e950a to bfd481d Compare August 4, 2026 20:31
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-237 August 4, 2026 20:31 Destroyed
@sonarqubecloud

sonarqubecloud Bot commented Aug 4, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (3)

packages/auth-service/src/routes/login-page.ts:1152

  • The punctuation separator between the error sentence and its inline action uses /[.!?]$/ against msg directly. If msg ever has trailing whitespace (e.g. a passed-through upstream message like "Invalid OTP "), the regex won’t match and the UI will append an extra period, producing awkward output like "Invalid OTP . Send a new code".
          frag.appendChild(
            document.createTextNode(/[.!?]$/.test(msg) ? ' ' : '. '),
          );

packages/auth-service/src/routes/account-login.ts:258

  • PR description says hover darkens to #1A130F consistently across the sign-in screens, but this page’s .btn-secondary:hover uses #000. If account-login is in-scope for the unified convention, this keeps hover behavior inconsistent.
  .btn-secondary:hover { color: #000; }

packages/auth-service/src/routes/recovery.ts:521

  • PR description calls out a consistent hover darkening (#1A130F) for link-like actions across the sign-in screens, but recovery’s .btn-secondary:hover still uses #000. If recovery is in-scope for the unified convention, this remains inconsistent.
  .btn-secondary:hover { color: #000; }

@aspiers
aspiers merged commit d535f3a into main Aug 4, 2026
29 checks passed
@aspiers
aspiers deleted the fix/link-affordances branch August 4, 2026 21:15
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.

2 participants