How to use this article: select everything below the line, copy it, and paste it into your AI chat (Claude, ChatGPT, etc.) before asking it to build or fix an xsyte custom page. It teaches the AI the platform rules so the page works the first time.
xsyte Custom Page Rules — for AI Assistants
To the AI assistant: the user is building a "custom page" on the xsyte sports-league platform (hockeysyte / sportssyte / soccersyte). A custom page is HTML + Twig pasted into a WYSIWYG editor in the site admin. Follow every rule below, and run the validation checklist before delivering any HTML.
1. The environment
- Pages render server-side through Twig —
{{ }},{% %}, and{# #}are live template syntax. - Pages are edited in a WYSIWYG editor built on a
<textarea>— this drives rule 2. - Font Awesome 6, flag-icons, and a Foundation datepicker are already loaded — never add
<link>/<script>tags for them. - The public URL is
/page/<slug>.
2. CRITICAL: never emit raw form controls
Never write raw <input>, <textarea>, <select>, or <button> tags. The admin editor is itself a textarea: a raw closing </textarea> terminates the editor's own field and destroys the page on save. Use the platform's Twig form helpers instead — they're plain text in the editor and only become controls at render time.
Helpers take positional arguments — (name, value, extra-attributes-string) — never attribute hashes.
{{ form_input('player_name', '', 'id="player_name" required') }}
{{ form_input('date_start', '', 'class="fdatepicker"') }}
{{ form_textarea('notes', '', 'id="notes" rows="4" placeholder="Anything else?"') }}
{{ form_hidden('form_id', '5') }}
{{ form_dropdown('division', {'': 'Choose...', 'masters': 'Masters'}, '', 'required') }}
{{ form_radio('gear_hire', 'Yes', false, 'required') }}
{{ form_checkbox('agree', 'Yes', false, 'id="agree" required') }}
{{ form_submit('submit', 'Send', 'class="btn-submit"') }}
- Inputs render as
type="text"; there is no type override. Dates useclass="fdatepicker", nottype="date". Email/phone validation is server-side. form_dropdown(name, options, selected, extra)— options is a value→label hash (data, not attributes).form_radio/form_checkboxare(name, value, checked, extra)— checked is boolean.form_submitrenders<input type="submit">, not<button>— style its class.- Labels, wrapper divs, and other non-control markup are fine as raw HTML.
3. Form wiring (data-capture pages)
{{ form_open('/forms/handle_form_response') }}
{{ form_hidden('form_id', 'FORM_ID') }}
{{ form_hidden('page_id', 'PAGE_ID') }}
{{ form_input('website', '', 'class="honeypot" tabindex="-1" autocomplete="off"') }}
[visible fields via helpers]
{{ form_submit('submit', 'Submit') }}
{{ form_close() }}
- Always
form_open/form_close— never a plain<form>tag (CSRF failure, every submission rejected). form_id= the numeric ID of the form in the site admin. Ask the user; never invent it.page_id= this page's numeric ID. Without it the thank-you state never shows.website= the honeypot and the platform's spam defense. Hide it offscreen (position:absolute; left:-9999px), NOTdisplay:none. Do not add reCAPTCHA — it is not required and not validated.- Field names must exactly match the form's schema in the admin — mismatches are silently dropped.
4. The success state
After submission the page re-renders with success true. Wrap swappable sections:
{% if success %}
[thank-you: confirm receipt, what happens next, fallback contact]
{% else %}
[normal content / the form]
{% endif %}
Preview during development with {% if true %} — always restore before delivering.
5. Twig safety
- Every
{% if %}needs{% endif %}; every{% for %}needs{% endfor %}; every{#needs#}. An unclosed block = fatal blank page. - Keep Twig out of
<style>blocks. - Watch for stray
{{or{%in ordinary text.
6. Full-bleed pages (optional)
To hide the site header/footer/nav for a full-viewport page, add these overrides at the top of the <style> block; omit them for pages inside the normal site chrome. Prefix custom CSS classes with a unique namespace.
.title-bar.show-for-small-only, .top-bar.show-for-medium,
#header-hero, #main-nav, #off-canvas-mobile-menu,
#footer, #sponsors-large, .page-title-bar, .breadcrumbs { display: none !important; }
#full-width-content.row { max-width: 100% !important; width: 100% !important; margin: 0 !important; padding: 0 !important; }
#full-width-content > .large-12.columns { padding: 0 !important; margin: 0 !important; }
body { background: #0a0a0a !important; margin: 0 !important; }
7. Not supported
Multi-step wizards, file uploads, conditional show/hide fields (without custom JS), third-party webhooks, payment capture.
8. Validation checklist — run before delivering any HTML
- No raw
<input>/<textarea>/<select>/<button>/<form>anywhere. - Helpers use positional args, never hashes.
- All Twig blocks and comments balanced.
- Form wiring complete and in order: form_open → form_id → page_id → honeypot → fields → form_submit → form_close.
- Real form_id/page_id from the user, or clearly marked placeholders.
- Field names verified against the admin form schema.
- No re-imported libraries; no Twig inside <style>; no leftover
{% if true %}; honeypot hidden offscreen.
9. Troubleshooting
- Blank page / "Unexpected end of template" — unclosed Twig block or comment; or the page was truncated on save (compare the saved end against the original).
- Editor breaks, content spills below it — raw form control in the body; convert to helpers and re-paste everything.
- Form submits but data vanishes — field names don't match the admin schema.
- Every submission rejected — plain
<form>instead of form_open. - Thank-you never shows — missing/wrong page_id.
- Page stuck on thank-you — leftover
{% if true %}. - Date picker missing — use
class="fdatepicker", nottype="date".
Comments
0 comments
Please sign in to leave a comment.