Skip to content

Commit ca2b6c9

Browse files
ux(mobile): reduce footer height and improve copy
Made-with: Cursor
1 parent d42c883 commit ca2b6c9

3 files changed

Lines changed: 76 additions & 9 deletions

File tree

src/app/globals.css

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,10 @@ a.subpage-inline-mail {
543543
@media (max-width: 640px) {
544544
/* Page padding */
545545
main { padding-left: 16px !important; padding-right: 16px !important; }
546-
.page-shell-main { padding-top: calc(env(safe-area-inset-top) + 28px); }
546+
.page-shell-main {
547+
min-height: 100svh;
548+
padding-top: calc(env(safe-area-inset-top) + 36px) !important;
549+
}
547550

548551
/* Certificate wrapper spacing only; scale is handled in CertificateCard.tsx */
549552
.certificate-scroll-zone {
@@ -570,11 +573,22 @@ a.subpage-inline-mail {
570573
}
571574

572575
/* Hero: keep CTA above the fold on small screens */
573-
.page-hero { padding-top: 6px; padding-bottom: 8px; }
576+
.page-hero { padding-top: 14px; padding-bottom: 10px; }
574577
.page-hero-emoji { font-size: clamp(48px, 13vw, 62px); margin-bottom: 6px; }
575578
.page-hero-title { font-size: clamp(2.4rem, 12vw, 3.2rem); margin: 4px 0 8px; }
576579
.page-hero-sub { font-size: 13px; }
577-
.page-shell-main { min-height: 100svh; padding-top: calc(env(safe-area-inset-top) + 20px) !important; }
580+
581+
/* Hall of Fame marquee: less clipped on phones */
582+
.recent-marquee {
583+
padding-left: 16px !important;
584+
padding-right: 16px !important;
585+
height: 284px !important;
586+
-webkit-mask-image: linear-gradient(to right, transparent, black 18px, black calc(100% - 18px), transparent) !important;
587+
mask-image: linear-gradient(to right, transparent, black 18px, black calc(100% - 18px), transparent) !important;
588+
}
589+
.recent-card {
590+
height: 230px !important;
591+
}
578592

579593
.input-button-wrapper {
580594
flex-direction: column;
@@ -613,7 +627,8 @@ a.subpage-inline-mail {
613627
}
614628

615629
/* Footer: keep links low-noise but tappable */
616-
footer a { font-size: 13px !important; padding: 8px 0 !important; min-height: 44px !important; }
630+
.site-footer { padding-top: 10px; padding-bottom: max(10px, env(safe-area-inset-bottom)); gap: 2px; }
631+
footer a { font-size: 12px !important; padding: 6px 0 !important; min-height: 36px !important; }
617632

618633
.mode-tab {
619634
text-align: center;

src/components/ReadmeBadge.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

33
import { useState } from 'react'
4+
import { copyText, promptCopy } from '@/lib/clipboard'
45

56
const MONO = `var(--font-courier), system-ui, sans-serif`
67

@@ -18,11 +19,11 @@ export default function ReadmeBadge({ username }: Props) {
1819
const altText = `Commitment Issues — @${username}'s graveyard`
1920
const markdown = `[![${altText}](${badgeUrl})](${profileUrl})`
2021

21-
function handleCopy() {
22-
navigator.clipboard.writeText(markdown).then(() => {
23-
setCopied(true)
24-
setTimeout(() => setCopied(false), 2000)
25-
})
22+
async function handleCopy() {
23+
const ok = await copyText(markdown)
24+
if (!ok) promptCopy(markdown, 'Copy this README badge markdown')
25+
setCopied(true)
26+
setTimeout(() => setCopied(false), 2000)
2627
}
2728

2829
return (

src/lib/clipboard.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
export async function copyText(text: string): Promise<boolean> {
2+
if (!text) return false
3+
4+
// Modern API (works on HTTPS + user gesture; can fail on iOS/Safari)
5+
try {
6+
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
7+
await navigator.clipboard.writeText(text)
8+
return true
9+
}
10+
} catch {
11+
// fall back below
12+
}
13+
14+
// Fallback: textarea + execCommand('copy')
15+
try {
16+
if (typeof document === 'undefined') return false
17+
18+
const ta = document.createElement('textarea')
19+
ta.value = text
20+
ta.setAttribute('readonly', 'true')
21+
ta.style.position = 'fixed'
22+
ta.style.top = '0'
23+
ta.style.left = '0'
24+
ta.style.width = '1px'
25+
ta.style.height = '1px'
26+
ta.style.opacity = '0'
27+
ta.style.pointerEvents = 'none'
28+
ta.style.zIndex = '-1'
29+
document.body.appendChild(ta)
30+
31+
ta.focus()
32+
ta.select()
33+
ta.setSelectionRange(0, ta.value.length)
34+
35+
const ok = document.execCommand('copy')
36+
document.body.removeChild(ta)
37+
return ok
38+
} catch {
39+
return false
40+
}
41+
}
42+
43+
export function promptCopy(text: string, label = 'Copy this'): void {
44+
try {
45+
// Works as a last resort on mobile/desktop.
46+
window.prompt(label, text)
47+
} catch {
48+
// ignore
49+
}
50+
}
51+

0 commit comments

Comments
 (0)