Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const nextConfig = {

// Experimental features for better performance
experimental: {
// Inline critical CSS into <head> to eliminate render-blocking stylesheet requests
inlineCss: true,
// Optimize memory usage and tree-shaking for large dependencies
optimizePackageImports: [
'@radix-ui/react-dialog',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
"zod": "^4.4.3",
"zustand": "^5.0.14"
},
"browserslist": ["chrome 111", "safari 16.4", "firefox 128", "edge 111"],
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
Expand Down
49 changes: 49 additions & 0 deletions public/video/voidpay-9x16-v2.en.vtt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
WEBVTT

00:00.167 --> 00:02.833
Sending wallet addresses in Telegram?

00:03.000 --> 00:05.333
Create an invoice.

00:05.667 --> 00:08.000
No signup.

00:08.333 --> 00:10.667
No account. No KYC.

00:11.000 --> 00:15.000
Get a link.

00:15.000 --> 00:17.333
Invoice ready.

00:17.667 --> 00:19.833
The link is the invoice.

00:20.167 --> 00:22.500
Hash never leaves the browser.

00:22.833 --> 00:24.833
Share it anywhere.

00:24.834 --> 00:26.667
Payer opens the link.

00:27.000 --> 00:29.500
No intermediary.

00:30.667 --> 00:33.333
Unique amount. Unique payment.

00:34.333 --> 00:37.000
Exact on-chain match.

00:37.000 --> 00:39.000
Payment confirmed.

00:39.000 --> 00:41.000
Not our servers.

00:41.000 --> 00:43.500
Works even if we shut down.
9 changes: 8 additions & 1 deletion src/features/wallet-connect/ui/LazyWalletButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,14 @@ function hasPersistedWalletConnection(): boolean {
*/
function PlaceholderButton({ onClick, isLoading }: { onClick: () => void; isLoading: boolean }) {
return (
<Button variant="outline" size="sm" className="gap-1.5" onClick={onClick} disabled={isLoading}>
<Button
variant="outline"
size="sm"
className="gap-1.5"
onClick={onClick}
disabled={isLoading}
aria-label={isLoading ? 'Loading wallet' : 'Connect wallet'}
>
{isLoading ? (
<>
<Loader2Icon className="h-4 w-4 animate-spin" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ exports[`NetworkSelect - Snapshots > should match snapshot with default state 1`
<span
aria-label="Ethereum"
class="inline-flex items-center justify-center"
role="img"
>
<svg
class="web3icons flex-shrink-0"
Expand Down Expand Up @@ -114,6 +115,7 @@ exports[`NetworkSelect - Snapshots > should match snapshot with different networ
<span
aria-label="Polygon"
class="inline-flex items-center justify-center"
role="img"
>
<svg
class="web3icons flex-shrink-0"
Expand Down Expand Up @@ -200,6 +202,7 @@ exports[`NetworkSelect - Snapshots > should match snapshot with disabled state 1
<span
aria-label="Arbitrum"
class="inline-flex items-center justify-center"
role="img"
>
<svg
class="web3icons flex-shrink-0"
Expand Down
2 changes: 2 additions & 0 deletions src/shared/ui/network-icon/NetworkIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const NetworkIcon = forwardRef<HTMLSpanElement, NetworkIconProps>(
return (
<span
ref={ref}
role="img"
className={cn(
'inline-flex items-center justify-center',
variant === 'mono' && 'grayscale',
Expand All @@ -117,6 +118,7 @@ export const NetworkIcon = forwardRef<HTMLSpanElement, NetworkIconProps>(
return (
<span
ref={ref}
role="img"
className={cn(
'inline-flex items-center justify-center rounded-full text-white font-bold',
variant === 'mono' ? 'bg-zinc-500' : bgColor,
Expand Down
2 changes: 1 addition & 1 deletion src/widgets/landing/comparison/ComparisonTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export function ComparisonTable() {
</div>

{/* Disclaimer */}
<Text variant="small" className="mt-6 text-center text-zinc-500">
<Text variant="small" className="mt-6 text-center text-zinc-400">
Comparison based on public documentation as of April 2026. Features may vary.
</Text>
<div className="mt-4 text-center">
Expand Down
35 changes: 31 additions & 4 deletions src/widgets/landing/video-section/VideoSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,39 @@ export function VideoSection() {
controls={prefersReducedMotion || isMobile}
aria-label="VoidPay product walkthrough: creating and paying a crypto invoice"
onPlay={handlePlay}
/>
>
<track
kind="captions"
srcLang="en"
label="English"
src="/video/voidpay-9x16-v2.en.vtt"
default
/>
</video>
</div>

<figcaption className="px-4 py-3 text-sm text-zinc-500">
Silent by design. Captions tell the story.
</figcaption>
{/* Transcript — server-rendered crawlable text matching VTT cues. */}
<details className="px-4 py-3 text-left text-sm text-zinc-400">
<summary className="cursor-pointer select-none">Transcript</summary>
<ol className="mt-2 list-inside list-decimal space-y-1">
<li>Sending wallet addresses in Telegram?</li>
<li>Create an invoice.</li>
<li>No signup.</li>
<li>No account. No KYC.</li>
<li>Get a link.</li>
<li>Invoice ready.</li>
<li>The link is the invoice.</li>
<li>Hash never leaves the browser.</li>
<li>Share it anywhere.</li>
<li>Payer opens the link.</li>
<li>No intermediary.</li>
<li>Unique amount. Unique payment.</li>
<li>Exact on-chain match.</li>
<li>Payment confirmed.</li>
<li>Not our servers.</li>
<li>Works even if we shut down.</li>
</ol>
</details>
</figure>

{/* CTA */}
Expand Down
26 changes: 21 additions & 5 deletions src/widgets/landing/video-section/__tests__/VideoSection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ describe('VideoSection', () => {
expect(screen.queryByText(/No account\. No server\./)).not.toBeInTheDocument()
})

it('should render the figcaption', () => {
it('should render the transcript summary', () => {
render(<VideoSection />)
expect(screen.getByText('Silent by design. Captions tell the story.')).toBeInTheDocument()
expect(screen.getByText('Transcript')).toBeInTheDocument()
})

it('should render the CTA button linking to /create', () => {
Expand Down Expand Up @@ -218,12 +218,28 @@ describe('VideoSection', () => {
expect(heading).toBeInTheDocument()
})

it('should wrap video in figure with figcaption', () => {
it('should wrap video in figure with a transcript', () => {
render(<VideoSection />)
const figure = document.querySelector('figure')
expect(figure).toBeInTheDocument()
const figcaption = figure?.querySelector('figcaption')
expect(figcaption).toBeInTheDocument()
const details = figure?.querySelector('details')
expect(details).toBeInTheDocument()
expect(details?.querySelector('summary')).toBeInTheDocument()
})

it('should have a captions track on the video', () => {
render(<VideoSection />)
const track = document.querySelector('track')
expect(track).toBeInTheDocument()
expect(track).toHaveAttribute('kind', 'captions')
expect(track).toHaveAttribute('srclang', 'en') // DOM attr is lowercase
expect(track).toHaveAttribute('src', '/video/voidpay-9x16-v2.en.vtt')
})

it('transcript should contain key caption text', () => {
render(<VideoSection />)
expect(screen.getByText('Works even if we shut down.')).toBeInTheDocument()
expect(screen.getByText('Payment confirmed.')).toBeInTheDocument()
})
})

Expand Down
9 changes: 5 additions & 4 deletions src/widgets/navigation/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export function Navigation() {
<div className="flex items-center gap-1.5 sm:gap-2">
<Link
href="/history"
aria-label="History"
className={`inline-flex min-h-[44px] items-center rounded-lg px-3 py-2 text-sm font-medium transition-colors ${
isHistory
? 'bg-zinc-800 text-zinc-50'
Expand All @@ -62,12 +63,12 @@ export function Navigation() {
<span className="hidden sm:inline">History</span>
</Link>

<Link href="/create">
<Button variant="outline" size="sm" className="gap-1.5">
<Button asChild variant="outline" size="sm" className="gap-1.5" aria-label="Create">
<Link href="/create">
<PlusIcon className="h-4 w-4" />
<span className="hidden sm:inline">Create</span>
</Button>
</Link>
</Link>
</Button>

{/* Separator */}
<div className="mx-2 h-6 w-px bg-zinc-800" />
Expand Down