Files
Clintchiz 2c960fc1dc
Quality / quality (ubuntu-latest) (push) Failing after 9m49s
Quality / quality (windows-latest) (push) Canceled after 0s
refactor: migrate legacy wire namespace to wrn
2026-08-12 18:51:15 +05:30

634 lines
28 KiB
Plaintext

page CaptchaShowcase {
seo {
title = "CAPTCHA Component Showcase"
description = "Test every WRNexus CAPTCHA mode, validation flow, page gate, and provider integration."
}
view {
<main
class="min-h-screen bg-[var(--wrn-color-bg)] px-4 py-10 text-[var(--wrn-color-text)] sm:px-6 lg:px-8"
>
<div
class="mx-auto flex max-w-7xl flex-col gap-10"
>
<header
class="max-w-3xl"
>
<p
class="mb-2 text-sm font-semibold text-[var(--wrn-color-primary)]"
>
@wrnexus/captcha
</p>
<h1
class="m-0 text-3xl font-bold tracking-tight sm:text-5xl"
>
CAPTCHA system showcase
</h1>
<p
class="mt-4 text-base leading-7 text-[var(--wrn-color-muted)]"
>
Self-hosted number, alphabet, alphanumeric, calculation, image, audio, not-robot, honeypot, timing, validated form-submit, and page-gate protection.
</p>
<a
href="/styles"
class="mt-4 inline-flex h-10 items-center gap-2 rounded-[var(--wrn-radius-sm)] border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] px-4 text-sm font-semibold text-[var(--wrn-color-text)] hover:border-[var(--wrn-color-primary)]"
>
<span
aria-hidden="true"
class="icon-[lucide--images] size-4 text-[var(--wrn-color-primary)]"
>
</span>
Open all 18 image styles
</a>
</header>
<section
class="grid gap-6 lg:grid-cols-2"
>
<article
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5"
>
<h2
class="mt-0 text-lg font-semibold"
>
Number CAPTCHA
</h2>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="number"
action="demo-number"
imageStyle="random"
disturbance="35"
/>
</article>
<article
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5"
>
<h2
class="mt-0 text-lg font-semibold"
>
Alphabet CAPTCHA
</h2>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="alpha"
action="demo-alpha"
imageStyle="random"
disturbance="40"
/>
</article>
<article
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5"
>
<h2
class="mt-0 text-lg font-semibold"
>
Alphanumeric CAPTCHA
</h2>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="alphanumeric"
action="demo-alphanumeric"
imageStyle="random"
difficulty="hard"
disturbance="70"
/>
</article>
<article
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5"
>
<h2
class="mt-0 text-lg font-semibold"
>
Calculation CAPTCHA
</h2>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="calculation"
action="demo-calculation"
imageStyle="random"
disturbance="50"
/>
</article>
</section>
<section
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5 sm:p-7"
>
<div
class="max-w-3xl"
>
<h2
class="mt-0 text-xl font-semibold"
>
Compact, normal, and big sizes
</h2>
<p
class="text-sm leading-6 text-[var(--wrn-color-muted)]"
>
Use the size prop to fit dense forms, standard pages, or large security flows.
</p>
</div>
<div
class="mt-5 grid items-start gap-5 xl:grid-cols-3"
>
<article
class="rounded-xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] p-4"
>
<h3
class="mt-0 text-base font-semibold"
>
Compact
</h3>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="number"
action="demo-size-compact"
size="compact"
disturbance="30"
showListen="false"
/>
</article>
<article
class="rounded-xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] p-4"
>
<h3
class="mt-0 text-base font-semibold"
>
Normal
</h3>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="number"
action="demo-size-normal"
size="normal"
disturbance="40"
/>
</article>
<article
class="rounded-xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] p-4"
>
<h3
class="mt-0 text-base font-semibold"
>
Big
</h3>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="number"
action="demo-size-big"
size="big"
disturbance="50"
/>
</article>
</div>
</section>
<section
class="grid gap-6 lg:grid-cols-2"
>
<article
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5"
>
<h2
class="mt-0 text-lg font-semibold"
>
I'm not a robot
</h2>
<p
class="text-sm leading-6 text-[var(--wrn-color-muted)]"
>
A low-friction, one-time checkbox challenge backed by timing, honeypot, expiry, action, session, and server-side token checks.
</p>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="not-robot"
action="demo-not-robot"
size="compact"
showAudio="false"
showListen="false"
/>
</article>
<article
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5"
>
<h2
class="mt-0 text-lg font-semibold"
>
Listen button hidden
</h2>
<p
class="text-sm leading-6 text-[var(--wrn-color-muted)]"
>
Set showListen to false when a separate accessible alternative is provided elsewhere.
</p>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="alphanumeric"
action="demo-listen-hidden"
size="compact"
showListen="false"
/>
</article>
</section>
<section
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5 sm:p-7"
>
<div
class="max-w-3xl"
>
<h2
class="mt-0 text-xl font-semibold"
>
Generated image disturbance
</h2>
<p
class="text-sm leading-6 text-[var(--wrn-color-muted)]"
>
Use the disturbance prop from 25 to 75. Lower values are easier to read; higher values add more dots and crossing lines.
</p>
</div>
<div
class="mt-5 grid gap-5 xl:grid-cols-3"
>
<article
class="rounded-xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] p-4"
>
<div
class="mb-3 flex items-center justify-between gap-3"
>
<h3
class="m-0 text-base font-semibold"
>
Easy image
</h3>
<code
class="rounded-md bg-[var(--wrn-color-surface)] px-2 py-1 text-xs"
>
25%
</code>
</div>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="image"
action="demo-image-easy"
disturbance="25"
size="big"
/>
</article>
<article
class="rounded-xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] p-4"
>
<div
class="mb-3 flex items-center justify-between gap-3"
>
<h3
class="m-0 text-base font-semibold"
>
Normal image
</h3>
<code
class="rounded-md bg-[var(--wrn-color-surface)] px-2 py-1 text-xs"
>
50%
</code>
</div>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="image"
action="demo-image-normal"
disturbance="50"
size="big"
/>
</article>
<article
class="rounded-xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] p-4"
>
<div
class="mb-3 flex items-center justify-between gap-3"
>
<h3
class="m-0 text-base font-semibold"
>
Hard image
</h3>
<code
class="rounded-md bg-[var(--wrn-color-surface)] px-2 py-1 text-xs"
>
75%
</code>
</div>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="image"
action="demo-image-hard"
disturbance="75"
difficulty="hard"
size="big"
/>
</article>
</div>
</section>
<section
class="grid gap-6 lg:grid-cols-2"
>
<article
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5"
>
<h2
class="mt-0 text-lg font-semibold"
>
Audio-first challenge
</h2>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="number"
presentation="audio"
action="demo-audio"
/>
</article>
<article
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5"
>
<h2
class="mt-0 text-lg font-semibold"
>
Invisible timing and honeypot
</h2>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="timing"
presentation="invisible"
action="demo-invisible"
compact="true"
/>
</article>
</section>
<section
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5 sm:p-7"
>
<div
class="max-w-3xl"
>
<p
class="mb-2 text-sm font-semibold text-[var(--wrn-color-primary)]"
>
Complete integration
</p>
<h2
class="mt-0 text-xl font-semibold"
>
Validated contact form with CAPTCHA
</h2>
<p
class="text-sm leading-6 text-[var(--wrn-color-muted)]"
>
The same contact schema validates in the browser and API. CAPTCHA verification remains mandatory on the server before the form is accepted.
</p>
</div>
<form
data-schema="contact"
method="post"
action="/api/contact"
class="mt-6 grid max-w-3xl gap-5"
novalidate
>
<div
data-success="Validated form submitted successfully."
hidden
class="rounded-xl border border-[var(--wrn-color-success)] bg-[color-mix(in_srgb,var(--wrn-color-success)_10%,transparent)] p-4 text-sm font-medium text-[var(--wrn-color-success)]"
>
</div>
<div
class="grid gap-5 sm:grid-cols-2"
>
<label
class="grid gap-2 text-sm font-medium"
>
Full name
<input
name="name"
type="text"
autocomplete="name"
placeholder="Ajay Ghanwat"
class="h-11 rounded-[var(--wrn-radius-sm)] border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] px-3 text-[var(--wrn-color-text)] outline-none focus:border-[var(--wrn-color-primary)] focus:ring-2 focus:ring-[var(--wrn-color-primary)] aria-[invalid=true]:border-[var(--wrn-color-danger)]"
/>
<span
data-error="name"
role="alert"
class="min-h-5 text-xs text-[var(--wrn-color-danger)]"
>
</span>
</label>
<label
class="grid gap-2 text-sm font-medium"
>
Email address
<input
name="email"
type="email"
autocomplete="email"
placeholder="you@example.com"
class="h-11 rounded-[var(--wrn-radius-sm)] border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] px-3 text-[var(--wrn-color-text)] outline-none focus:border-[var(--wrn-color-primary)] focus:ring-2 focus:ring-[var(--wrn-color-primary)] aria-[invalid=true]:border-[var(--wrn-color-danger)]"
/>
<span
data-error="email"
role="alert"
class="min-h-5 text-xs text-[var(--wrn-color-danger)]"
>
</span>
</label>
</div>
<label
class="grid gap-2 text-sm font-medium"
>
Topic
<select
name="topic"
class="h-11 rounded-[var(--wrn-radius-sm)] border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] px-3 text-[var(--wrn-color-text)] outline-none focus:border-[var(--wrn-color-primary)] focus:ring-2 focus:ring-[var(--wrn-color-primary)] aria-[invalid=true]:border-[var(--wrn-color-danger)]"
>
<option
value=""
>
Choose a topic
</option>
<option
value="general"
>
General question
</option>
<option
value="security"
>
Security and CAPTCHA
</option>
<option
value="billing"
>
Billing
</option>
<option
value="integration"
>
Integration support
</option>
</select>
<span
data-error="topic"
role="alert"
class="min-h-5 text-xs text-[var(--wrn-color-danger)]"
>
</span>
</label>
<label
class="grid gap-2 text-sm font-medium"
>
Message
<textarea
name="message"
rows="5"
placeholder="Tell us how we can help..."
class="min-h-32 resize-y rounded-[var(--wrn-radius-sm)] border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface-2)] px-3 py-3 text-[var(--wrn-color-text)] outline-none focus:border-[var(--wrn-color-primary)] focus:ring-2 focus:ring-[var(--wrn-color-primary)] aria-[invalid=true]:border-[var(--wrn-color-danger)]"
>
</textarea>
<span
data-error="message"
role="alert"
class="min-h-5 text-xs text-[var(--wrn-color-danger)]"
>
</span>
</label>
<label
class="flex items-start gap-3 text-sm text-[var(--wrn-color-muted)]"
>
<input
name="consent"
type="checkbox"
class="mt-0.5 size-4 rounded border-[var(--wrn-color-border)] text-[var(--wrn-color-primary)] focus:ring-[var(--wrn-color-primary)]"
/>
<span
>
I confirm that this information may be processed for this showcase request.
</span>
</label>
<span
data-error="consent"
role="alert"
class="min-h-5 text-xs text-[var(--wrn-color-danger)]"
>
</span>
<Captcha
endpoint="/api/captcha/challenge"
verifyEndpoint="/api/captcha/verify"
type="alphanumeric"
action="contact-submit"
disturbance="45"
required="true"
/>
<button
type="submit"
class="h-11 rounded-[var(--wrn-radius-sm)] bg-[var(--wrn-color-primary)] px-5 font-semibold text-[var(--wrn-color-primary-contrast)] disabled:cursor-not-allowed disabled:opacity-60"
>
Validate and submit form
</button>
</form>
</section>
<section
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5 sm:p-7"
>
<h2
class="mt-0 text-xl font-semibold"
>
Page-open protection
</h2>
<p
class="text-sm text-[var(--wrn-color-muted)]"
>
This route uses a once-per-session page grant. After successful CAPTCHA verification, the form posts the response token and safely redirects to the requested protected route.
</p>
<a
href="/protected"
class="mt-3 inline-flex h-11 items-center rounded-[var(--wrn-radius-sm)] bg-[var(--wrn-color-primary)] px-5 font-semibold text-[var(--wrn-color-primary-contrast)]"
>
Open protected page
</a>
</section>
<section
class="rounded-2xl border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-5 sm:p-7"
>
<h2
class="mt-0 text-xl font-semibold"
>
External providers
</h2>
<p
class="text-sm text-[var(--wrn-color-muted)]"
>
Add real public test keys before enabling these examples.
</p>
<div
class="mt-5 grid gap-6 lg:grid-cols-3"
>
<Captcha
provider="turnstile"
siteKey=""
action="external-turnstile"
autoLoad="false"
/>
<Captcha
provider="recaptcha"
siteKey=""
action="external-recaptcha"
autoLoad="false"
/>
<Captcha
provider="hcaptcha"
siteKey=""
action="external-hcaptcha"
autoLoad="false"
/>
</div>
</section>
</div>
</main>
}
}