Skip to content

Commit a616ec5

Browse files
kitfunsoclaude
andcommitted
Improve mobile UX and add shareable links with inputs
- Increase mobile input touch targets (larger padding and spin controls) - Add "Copy Link" to ShareResults with input params encoded in URL - Make recently used section horizontal scrollable ticker on mobile - Disable adblock popup until AdSense is approved Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ff8a21a commit a616ec5

5 files changed

Lines changed: 88 additions & 12 deletions

File tree

src/components/common/AdBlockDetector.astro

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@
5858
</div>
5959

6060
<script is:inline>
61+
// DISABLED: Re-enable when AdSense is approved
6162
// Only run in production
62-
if (window.location.hostname !== 'localhost') {
63+
if (false && window.location.hostname !== 'localhost') {
6364
// Check if user dismissed the notice recently (within 7 days)
6465
const dismissed = localStorage.getItem('adblock-notice-dismissed');
6566
const dismissedTime = dismissed ? parseInt(dismissed, 10) : 0;

src/components/ui/ShareResults.tsx

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@ interface ShareResultsProps {
1111
result: string;
1212
calculatorName: string;
1313
className?: string;
14+
/** Optional inputs to encode in the shareable URL */
15+
inputs?: Record<string, string | number | boolean>;
1416
}
1517

1618
export default function ShareResults({
1719
result,
1820
calculatorName,
1921
className = '',
22+
inputs,
2023
}: ShareResultsProps) {
2124
const [copied, setCopied] = useState(false);
25+
const [linkCopied, setLinkCopied] = useState(false);
2226
const [isOpen, setIsOpen] = useState(false);
2327
const triggerRef = useRef<HTMLButtonElement>(null);
2428
const menuRef = useRef<HTMLDivElement>(null);
@@ -54,7 +58,21 @@ export default function ShareResults({
5458
};
5559
}, [isOpen]);
5660

57-
const shareUrl = typeof window !== 'undefined' ? window.location.href : '';
61+
// Build URL with input parameters for sharing
62+
const buildShareUrl = () => {
63+
if (typeof window === 'undefined') return '';
64+
const url = new URL(window.location.href.split('?')[0]);
65+
if (inputs) {
66+
Object.entries(inputs).forEach(([key, value]) => {
67+
if (value !== undefined && value !== null && value !== '') {
68+
url.searchParams.set(key, String(value));
69+
}
70+
});
71+
}
72+
return url.toString();
73+
};
74+
75+
const shareUrl = buildShareUrl();
5876
const shareText = `${result} - Calculated with ${calculatorName} on Boring Math`;
5977

6078
const shareLinks = {
@@ -64,6 +82,16 @@ export default function ShareResults({
6482
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(shareUrl)}`,
6583
};
6684

85+
const copyLinkToClipboard = async () => {
86+
try {
87+
await navigator.clipboard.writeText(shareUrl);
88+
setLinkCopied(true);
89+
setTimeout(() => setLinkCopied(false), 2000);
90+
} catch (err) {
91+
console.error('Failed to copy link:', err);
92+
}
93+
};
94+
6795
const copyToClipboard = async () => {
6896
try {
6997
await navigator.clipboard.writeText(`${shareText}\n${shareUrl}`);
@@ -157,7 +185,39 @@ export default function ShareResults({
157185

158186
<div className="border-t border-white/10 my-1"></div>
159187

160-
{/* Copy Link */}
188+
{/* Copy Link with inputs */}
189+
<button
190+
role="menuitem"
191+
onClick={copyLinkToClipboard}
192+
className="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-white/5 text-[var(--color-subtle)] hover:text-[var(--color-cream)] transition-colors text-sm w-full text-left"
193+
>
194+
<svg
195+
className="w-4 h-4"
196+
fill="none"
197+
stroke="currentColor"
198+
viewBox="0 0 24 24"
199+
aria-hidden="true"
200+
>
201+
{linkCopied ? (
202+
<path
203+
strokeLinecap="round"
204+
strokeLinejoin="round"
205+
strokeWidth={2}
206+
d="M5 13l4 4L19 7"
207+
/>
208+
) : (
209+
<path
210+
strokeLinecap="round"
211+
strokeLinejoin="round"
212+
strokeWidth={2}
213+
d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
214+
/>
215+
)}
216+
</svg>
217+
<span>{linkCopied ? 'Link Copied!' : 'Copy Link'}</span>
218+
</button>
219+
220+
{/* Copy Result text */}
161221
<button
162222
role="menuitem"
163223
onClick={copyToClipboard}

src/components/ui/primitives/Input.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
1616
}
1717

1818
const SIZE_CLASSES = {
19-
sm: 'py-2 text-base',
20-
md: 'py-3 text-lg',
21-
lg: 'py-4 text-xl',
19+
sm: 'py-2.5 md:py-2 text-base',
20+
md: 'py-4 md:py-3 text-lg',
21+
lg: 'py-5 md:py-4 text-xl',
2222
};
2323

2424
/**

src/pages/index.astro

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,8 @@ const categoryColors: Record<string, string> = {
11881188
Clear
11891189
</button>
11901190
</div>
1191-
<div id="recently-used-grid" class="flex flex-wrap gap-2">
1191+
<!-- Mobile: horizontal scrolling ticker, Desktop: wrapped grid -->
1192+
<div id="recently-used-grid" class="flex gap-2 overflow-x-auto pb-2 -mx-6 px-6 md:mx-0 md:px-0 md:flex-wrap md:overflow-visible scrollbar-hide snap-x snap-mandatory md:snap-none">
11921193
<!-- Populated by JavaScript -->
11931194
</div>
11941195
</div>
@@ -1648,7 +1649,7 @@ const categoryColors: Record<string, string> = {
16481649
const displayTitle = data.title.replace(/\s*Calculator$/i, '');
16491650

16501651
return `
1651-
<a href="${item.href}" class="recent-card glass rounded-lg px-3 py-2 flex items-center gap-2 hover:bg-white/10 transition-all group">
1652+
<a href="${item.href}" class="recent-card glass rounded-lg px-3 py-2 flex items-center gap-2 hover:bg-white/10 transition-all group flex-shrink-0 snap-start whitespace-nowrap">
16521653
<div class="w-6 h-6 rounded-md bg-gradient-to-br ${gradient} flex items-center justify-center flex-shrink-0">
16531654
<svg class="w-3.5 h-3.5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
16541655
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="${data.icon}" />

src/styles/global.css

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -382,11 +382,17 @@ input[type='number'] {
382382
bottom: 2px;
383383
display: flex;
384384
flex-direction: column;
385-
width: 28px;
385+
width: 40px; /* Larger touch target on mobile */
386386
border-radius: 0 8px 8px 0; /* 10px input radius minus 2px inset */
387387
overflow: hidden;
388388
}
389389

390+
@media (min-width: 768px) {
391+
.spin-controls {
392+
width: 28px;
393+
}
394+
}
395+
390396
.spin-controls button {
391397
flex: 1;
392398
display: flex;
@@ -425,9 +431,17 @@ input[type='number'] {
425431
}
426432

427433
.spin-controls svg {
428-
width: 14px;
429-
height: 14px;
430-
stroke-width: 3;
434+
width: 18px;
435+
height: 18px;
436+
stroke-width: 2.5;
437+
}
438+
439+
@media (min-width: 768px) {
440+
.spin-controls svg {
441+
width: 14px;
442+
height: 14px;
443+
stroke-width: 3;
444+
}
431445
}
432446

433447
/* ═══════════════════════════════════════════════════════════════════════════

0 commit comments

Comments
 (0)