Skip to content

Commit a6b6dab

Browse files
mariuspruvotclaude
andauthored
feat(web): polish login page — blink cursor, GH icon, Enter shortcut (#49)
Clarifies the product pitch: "Interactive skills for your pull requests — review, challenge, quiz, powered by Claude Code", so visitors see it as a skills platform, not just a Claude Code wrapper. Adds a blinking terminal cursor after the wordmark, the GitHub logo inside the primary CTA, and an Enter keybind with a keycap hint for keyboard-first users. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent aa1c366 commit a6b6dab

3 files changed

Lines changed: 61 additions & 6 deletions

File tree

apps/web/src/features/auth/LoginPage.test.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,14 @@ describe('LoginPage', () => {
2222
expect(link.getAttribute('href')).toContain('github.com/mariuspruvot/helprs')
2323
})
2424

25-
test('renders tagline', () => {
25+
test('renders tagline explaining interactive skills', () => {
2626
const { container } = render(<LoginPage />)
27+
expect(container.textContent).toContain('Interactive skills')
2728
expect(container.textContent).toContain('Self-hosted')
2829
})
30+
31+
test('renders enter-to-continue hint', () => {
32+
const { container } = render(<LoginPage />)
33+
expect(container.textContent?.toLowerCase()).toContain('enter')
34+
})
2935
})

apps/web/src/features/auth/LoginPage.tsx

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,69 @@
1+
import { useEffect } from 'react'
12
import { Button, GrainOverlay } from '../../shared/components'
23

34
const API_BASE = import.meta.env.VITE_API_URL ?? 'http://localhost:8000'
45
const GITHUB_URL = 'https://github.com/mariuspruvot/helprs'
6+
const SIGN_IN_URL = `${API_BASE}/api/v1/auth/github`
7+
8+
function GitHubIcon() {
9+
return (
10+
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true">
11+
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82A7.62 7.62 0 0 1 8 3.85c.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z" />
12+
</svg>
13+
)
14+
}
515

616
export default function LoginPage() {
17+
useEffect(() => {
18+
const handler = (e: KeyboardEvent) => {
19+
if (e.key !== 'Enter' || e.defaultPrevented) return
20+
const tag = (e.target as HTMLElement | null)?.tagName
21+
if (tag === 'A' || tag === 'BUTTON' || tag === 'INPUT' || tag === 'TEXTAREA') return
22+
window.location.href = SIGN_IN_URL
23+
}
24+
window.addEventListener('keydown', handler)
25+
return () => window.removeEventListener('keydown', handler)
26+
}, [])
27+
728
return (
829
<div className="relative min-h-screen bg-bg text-ink font-sans flex flex-col items-center justify-center px-6">
930
<GrainOverlay />
1031

1132
<main className="relative flex flex-col items-center text-center max-w-md w-full">
12-
<span className="font-mono text-3xl font-bold text-accent tracking-tight">helPRs</span>
33+
<h1 className="font-mono text-3xl font-bold text-accent tracking-tight inline-flex items-center">
34+
helPRs
35+
<span
36+
className="ml-1 inline-block w-[0.55ch] h-[1.1rem] bg-accent align-middle animate-cursor"
37+
aria-hidden="true"
38+
/>
39+
</h1>
1340
<p className="mt-4 text-ink2 text-sm leading-relaxed font-mono">
14-
Claude Code on your PRs. Self-hosted.
41+
Interactive skills for your pull requests.
42+
</p>
43+
<p className="mt-1 text-dim text-xs font-mono">
44+
Review, challenge, quiz {'\u2014'} powered by Claude Code. Self-hosted.
1545
</p>
1646

17-
<a href={`${API_BASE}/api/v1/auth/github`} className="mt-10 w-full sm:w-auto">
18-
<Button className="w-full sm:w-auto px-6 py-3">Sign in with GitHub</Button>
47+
<a href={SIGN_IN_URL} className="mt-10 w-full sm:w-auto">
48+
<Button className="w-full sm:w-auto px-6 py-3">
49+
<GitHubIcon />
50+
Sign in with GitHub
51+
</Button>
1952
</a>
2053

54+
<p className="mt-4 font-mono text-xs text-dim">
55+
press{' '}
56+
<kbd className="px-1.5 py-0.5 rounded border border-rule-str text-ink2 text-[10px]">
57+
{'\u21B5'} enter
58+
</kbd>{' '}
59+
to continue
60+
</p>
61+
2162
<a
2263
href={GITHUB_URL}
2364
target="_blank"
2465
rel="noopener noreferrer"
25-
className="mt-6 font-mono text-xs text-dim hover:text-ink2 transition-colors"
66+
className="mt-8 font-mono text-xs text-dim hover:text-ink2 transition-colors"
2667
>
2768
View on GitHub {'\u2192'}
2869
</a>

apps/web/src/index.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ html, body, #root {
153153
50% { opacity: 0.3; }
154154
}
155155

156+
/* Terminal cursor blink */
157+
@keyframes cursor-blink {
158+
50% { opacity: 0; }
159+
}
160+
.animate-cursor {
161+
animation: cursor-blink 1.1s steps(2, end) infinite;
162+
}
163+
156164
/* Overline letter-spacing */
157165
.overline {
158166
font-family: var(--font-family-mono);

0 commit comments

Comments
 (0)