634 lines
28 KiB
Plaintext
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>
|
|
}
|
|
}
|