Reveal content when the correct value is entered

This recipe builds a form field that, when the correct value is typed, hides itself and reveals a block of content — a lightweight “enter the code to continue” gate. It uses conditional visibility (show_when) and htmx, with no client-side JavaScript of your own.

This is not authentication. The correct value lives in your S3 config and is compared server-side (it is never sent to the browser), but the endpoint is still guessable by anyone who can POST to it. Treat this as a soft gate for low-stakes content, and pair it with captcha, rate limiting / stage throttling, and the min-fill token if it guards anything you care about. Never use it for real access control.

Prerequisites


1. Configure the form

Two elements, each with a show_when that references the same code field:

# webform.yaml
forms:
  gate:
    confirmation: "Thanks!"
    fields:
      - name: code
        type: text
        label: Access code
        # Visible until the correct value is entered.
        show_when: { field: code, not_equals: "open-sesame" }

      - type: html
        content: |
          <div class="unlocked">
            <h2>You're in</h2>
            <p>Here is the protected content…</p>
          </div>
        # Revealed only once the correct value is entered.
        show_when: { field: code, equals: "open-sesame" }
    outputs:
      - type: email
        to: you@example.com

How it works:

  • On first load, code is empty, so code’s condition (not_equals "open-sesame") is true → the input shows; the html element’s condition (equals "open-sesame") is false → it’s hidden.
  • code is referenced by a show_when, so it is a controller: the server renders it with htmx attributes that re-fetch the form whenever it changes.
  • When the visitor types open-sesame and the field loses focus (a change event), htmx re-requests the form. Now code’s condition is false, so the input collapses to a hidden input that carries the value — keeping the reveal stable — and the html element’s condition is true, so the content appears.

Because the collapsed code still submits its value, the state survives a form submission, and validation skips the (now hidden) field.

Upload the config:

task relay:upload:config CONFIG=webform.yaml

2. Embed the form with htmx

Load the server-rendered form (endpoint A) and let it re-render itself in place:

<script src="https://unpkg.com/htmx.org@2"></script>

<div hx-get="https://<api-id>.execute-api.<region>.amazonaws.com/api/v1/form/gate"
     hx-trigger="load"
     hx-swap="innerHTML"></div>
  • hx-trigger="load" fetches the initial form when the page loads.
  • The relay emits the controller’s re-render attributes (hx-get back to /api/v1/form/gate, hx-trigger="change", hx-target="closest form", hx-swap="outerHTML", hx-include="closest form"), so each change to code re-renders the whole form with the current values preserved.

If your form is on a different origin than the relay, make sure the relay’s AllowedOrigins includes your site (see CORS) — the htmx request headers are already on the allowlist.

3. Try it

Load the page: you see the Access code field. Type open-sesame and tab out — the field disappears and the protected content appears. Enter anything else and the field stays, repopulated with what you typed.

Notes and limits

  • Server-side secret. open-sesame never leaves the server; the browser only ever receives the rendered form. But a determined visitor can brute-force the endpoint — see the not-authentication warning above.
  • One-way reveal. Once code collapses to a hidden input it no longer fires change, so the reveal is final for that page load. Reloading starts over.
  • Multiple gates / richer conditions. show_when supports all/any grouping and the one_of / filled / empty operators — see the field model reference.