- CLAUDE.md: expanded with critical rules, hook syntax, DB conventions, web-base endpoint - docs/modular-system.md: modules, general sections, global vars, multiv2 - docs/builder-fields.md: all field types, c-if/c-for/c-class, c-form, built-in components - docs/twig-filters.md: get, hook, module, queryDB, imagec, translate, raw, etc. - docs/hooks-and-api.md: PHP hooks, CmsApi CRUD, table schemas, field formats - docs/css-js-conventions.md: Tailwind, BEM, CSS vars, Vue 3 integration, CmsApi JS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.1 KiB
5.1 KiB
Builder Fields & Acai Attributes
Field Types (data-field-type)
The builder uses data-field-type attributes on HTML elements to define editable areas.
| Type | Description | Returns |
|---|---|---|
textfield |
Single line text | String |
headfield |
Heading text (generates _tag variable for semantic tag: h1-h6) |
String |
textbox |
Multi-line text | String |
wysiwyg |
Rich text editor (HTML output) | HTML string |
link |
URL field (already includes slashes) | String |
upload |
Single image/file | Array: [0].urlPath, [0].info1 (alt), [0].info2-4 |
uploadMulti |
Multiple images | Iterable: item.urlPath |
list |
Dropdown (fixed options or from table) | String or foreign key num |
multiv2 |
Repeatable group of fields | Array of objects |
headfield Example
<{{ title_tag | default('h2') }} data-field-type="headfield" class="text-3xl font-bold">
Section Title
</{{ title_tag | default('h2') }}>
upload Example
<!-- Single image -->
<img data-field-type="upload"
src="{{ image[0].urlPath }}"
alt="{{ image[0].info1 }}"
class="w-full rounded" />
<!-- Multiple images -->
<div c-for="photo in gallery">
<img src="{{ photo.urlPath }}" alt="{{ photo.info1 }}" />
</div>
list Example
<!-- Fixed options -->
<select data-field-type="list" data-list-options="option1,option2,option3">
<option>option1</option>
</select>
<!-- From table -->
<select data-field-type="list" data-list-table="categories" data-list-field="name">
<option>Category</option>
</select>
Acai Attributes
c-if — Conditional Rendering
<!-- Boolean check -->
<div c-if="showBanner">Banner content</div>
<!-- Equality check (uses = not ==) -->
<div c-if="layout = 'grid'">Grid layout</div>
<!-- Variable exists / is not empty -->
<div c-if="subtitle">{{ subtitle }}</div>
c-else
Must immediately follow the c-if element:
<div c-if="image">
<img src="{{ image[0].urlPath }}" />
</div>
<div c-else>
<p>No image available</p>
</div>
c-for — Iteration
<!-- Over array/multiv2 -->
<div c-for="item in record.features">
<h3>{{ item.title }}</h3>
</div>
<!-- Over database table -->
<div c-for="product in products" c-where="active = 1" c-order="orden ASC" c-limit="6">
<h3>{{ product.nombre }}</h3>
</div>
Available inside loop: loop.index (1-based), loop.index is odd, loop.index is even
c-class — Dynamic CSS Classes
<div c-class="{ 'bg-blue-500': isActive, 'text-white': isActive, 'hidden': !showElement }">
Content
</div>
c-hidden — Hidden Elements
Element is not rendered but can declare builder variables:
<div c-hidden="true">
<input data-field-type="textfield" value="default config value" />
</div>
c-required — Conditional Required Fields
<input type="text" name="company" c-required="userType = 'business'" />
Forms (c-form)
Complete form handling with automatic validation, storage, and email sending.
<form c-form
tableName="'contacto'"
mailRecord="['correos', 'CONTACTO']"
sendTo="'admin@domain.com'"
sendToClient="'email'"
captcha="true"
honeypot="true"
messageOK="'Mensaje enviado correctamente'"
messageKO="'Error al enviar el mensaje'"
redirect="'/gracias/'"
attachFiles="true"
showImages="true"
>
<input type="text" name="nombre" required placeholder="Nombre" />
<input type="email" name="email" required placeholder="Email" />
<textarea name="mensaje" required placeholder="Mensaje"></textarea>
<captcha/>
<button type="submit">Enviar</button>
</form>
c-form Attributes
| Attribute | Description |
|---|---|
tableName="'table'" |
Store submissions in database table |
mailRecord="['correos', 'ID']" |
Email template from correos table |
sendTo="'email@domain.com'" |
Recipient email(s), comma-separated |
sendToClient="'fieldname'" |
Field containing client's email for auto-reply |
captcha="true" |
Enable Google reCAPTCHA |
honeypot="true" |
Anti-spam hidden field |
messageOK="'text'" |
Success message |
messageKO="'text'" |
Error message |
redirect="'/path/'" |
Redirect after successful submit |
attachFiles="true" |
Attach uploaded files to email |
showImages="true" |
Show image thumbnails in email |
Built-in Components
Carousel (c-tns-wrapper)
<div class="c-tns-wrapper"
data-responsive='{"0":1,"768":2,"1024":3}'
data-speed="400"
data-nav="true"
data-autoplay-timeout="3000">
<div c-for="slide in record.slides">
<img src="{{ slide.image[0].urlPath }}" />
</div>
</div>
Lightbox
<a href="{{ image[0].urlPath }}" class="glightbox" data-gallery="gallery1">
<img src="{{ image[0].urlPath | imagec(400) }}" />
</a>
Breadcrumb
<breadCrumb/>
Animate On Scroll (AOS)
<div data-aos="fade-up" data-aos-delay="200">
Animated content
</div>
Lazy Loading
<img class="lazyload" data-src="{{ image[0].urlPath }}" />
<!-- or -->
<img data-lazy="true" src="{{ image[0].urlPath }}" />