Skip to content
Back to skills

Sage Forms

ASecurity

HTML Forms + Sage integration — log1x/sage-html-forms, hf_get_form, addPostObject html-form, Blade form views, x-form.* shared components, hf-validation JS module, hf-success hf-error hf-submitted events, Constraint Validation API, form traps pattern escaping type-tel ValidityState non-enumerable — stateless contact forms with progressive JS validation on top of the HTML Forms WordPress plugin. Invoke for: contact form with HTML Forms plugin, hf_get_form usage, form validation with Constraint...

  • 13 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 11, 2026
toolsphpbashreactapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned September 11, 2026

npx -y skills add hekivo/superpowers-sage --skill sage-forms --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Sage Forms?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Sage Forms
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/hekivo-sage-forms/badge)](https://www.skillsdirectory.com/skills/hekivo-sage-forms)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: superpowers-sage:sage-forms
description: >
  HTML Forms + Sage integration — log1x/sage-html-forms, hf_get_form,
  addPostObject html-form, Blade form views, x-form.* shared components,
  hf-validation JS module, hf-success hf-error hf-submitted events,
  Constraint Validation API, form traps pattern escaping type-tel
  ValidityState non-enumerable — stateless contact forms with progressive
  JS validation on top of the HTML Forms WordPress plugin.
  Invoke for: contact form with HTML Forms plugin, hf_get_form usage, form validation
  with Constraint API, pattern escaping trap, type=tel Chrome bug, form audit.
user-invocable: false
---

# HTML Forms + Sage Integration

Stateless form rendering via the HTML Forms WordPress plugin, bridged into Sage by `log1x/sage-html-forms`. Forms live as a CPT (`html-form`), are referenced from ACF blocks via `addPostObject`, and render through a Blade filter that routes to project-controlled form views.

## When to Use

| Approach | Best for |
|---|---|
| **HTML Forms + sage-html-forms** | Stateless contact/lead forms; editors pick the form from a dropdown; submissions handled by the HF plugin |
| **Livewire form** | Reactive state, multi-step wizards, inline validation tied to server state |
| **Blade + native `<form>`** | Single-field or trivial forms with no submission pipeline |

## When NOT to Use

- Form requires multi-step state → use Livewire (`acorn-livewire` skill)
- Form payload drives a Laravel controller action → Acorn Routes + Blade form, not HTML Forms
- Form must be embedded outside a block context (e.g. header newsletter) → Blade component + native `<form>` is simpler

## Prerequisites

- Sage 11+ / Acorn 4+ on Bedrock with Lando
- ACF Composer installed (for the block field declaration)
- Shared `x-form.*` components present in `resources/views/components/form/` (the design-system form primitives)

## Installation Summary

```bash
lando composer require wpackagist-plugin/html-forms
lando theme-composer require log1x/sage-html-forms
```

Acorn's package discovery auto-registers the service provider — no manual wiring. Full details: [references/installation.md](references/installation.md).

## Integration Pattern Summary

The form is a `html-form` CPT post. A block exposes an ACF `addPostObject` field scoped to that CPT so the editor picks the form. The block view renders it via `hf_get_form($form->ID)->get_html()`, which the sage-html-forms provider intercepts and routes to a Blade view at `resources/views/forms/{form-slug}.blade.php`. The Blade form view uses the project's `x-form.*` components.

```blade
{{-- Block view (snippet) --}}
@if ($form)
    {!! hf_get_form($form->ID)->get_html() !!}
@endif
```

```blade
{{-- resources/views/forms/{form-slug}.blade.php --}}
<x-html-forms :form="$form">
    <x-form.field label="Name" for="name" :required="true">
        <x-form.input type="text" name="name" :required="true" minlength="2" />
    </x-form.field>
    {{-- ...more fields... --}}
    <x-button type="submit" variant="primary" class="w-full">Send</x-button>
</x-html-forms>
```

Full walkthrough: [references/blade-form-views.md](references/blade-form-views.md).

## Validation Module Summary

Client-side validation is a reusable ES module imported by the block's JS — never globally enqueued. The module exposes one function:

```js
initHfValidation(formEl, { messages, validators, onSuccess, onError });
```

Four layers: native HTML5 constraints, `blur` validation, `input` lazy re-validation while `aria-invalid="true"`, and post-submit scroll via the HTML Forms plugin's DOM events (`hf-success`, `hf-error`, `hf-submitted`).

Full API and implementation: [references/hf-validation.md](references/hf-validation.md).

## Traps (Critical)

Three documented bugs that silently break forms in this stack. Full symptom/root cause/fix for each: [references/traps.md](references/traps.md).

- **T1 — `pattern` attribute backslash escaping in Blade components.** `$attributes->merge()` double-escapes backslashes; `patternMismatch` never fires. Symptom: form submits even though `pattern` regex doesn't match user input. Workaround: use a JS validator, not a `pattern` attribute.
- **T2 — `type="tel"` skips `patternMismatch` in Chrome.** Use `type="text" inputmode="tel"` instead. Symptom: phone format validator silently no-ops in Chrome (works in Firefox/Safari).
- **T3 — `ValidityState` is non-enumerable.** Spread (`{ ...field.validity }`) and `Object.keys()` return empty. Symptom: your error-message lookup object is always empty. Access named properties directly: `validity.valueMissing`, `validity.patternMismatch`, `validity.tooShort`, `validity.customError`, etc.

## File Map (canonical per form integration)

| File | Role |
|---|---|
| `app/Blocks/{ClassName}.php` | ACF block — `addPostObject` field + `with()` mapping |
| `resources/views/blocks/{slug}.blade.php` | Block view — calls `hf_get_form($form->ID)->get_html()` |
| `resources/views/forms/{form-slug}.blade.php` | Form view — `x-html-forms` + `x-form.*` structure |
| `resources/js/blocks/{slug}.js` | Block custom element — imports `initHfValidation`, configures per form |
| `resources/js/modules/hf-validation.js` | Reusable validation module (one per project, not per form) |

## Consumers

- `agents/forms.md` (user-invocable specialist — analyzes and refactors existing forms, or scaffolds new ones)
- `skills/block-scaffolding/SKILL.md` Phase 0c (coordinated scaffold when a block embeds a form)

Both consumers treat this skill as authoritative — no pattern, template, or trap is documented outside this skill.

Files in this skill

  • SKILL.md5.5 KB
  • references/blade-form-views.md5.2 KB
  • references/hf-validation.md8.6 KB
  • references/installation.md2.6 KB
  • references/traps.md5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.