Files
acai-scaffold/docs/css-js-conventions.md
Jordan 963174b4c4 Add complete Acai development documentation
- 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>
2026-03-09 18:53:10 +00:00

2.4 KiB

CSS & JavaScript Conventions

CSS

Tailwind First

Use Tailwind CSS as the primary styling method. Only use custom CSS when Tailwind is insufficient.

<!-- Good: Tailwind classes -->
<div class="flex items-center gap-4 p-6 bg-white rounded-lg shadow-md">
  <h2 class="text-2xl font-bold text-gray-900">Title</h2>
</div>

<!-- Custom CSS only when needed (animations, complex selectors, etc.) -->

BEM for Custom CSS

When custom CSS is needed, scope everything under a root class using BEM naming:

/* Root class in kebab-case */
.hero-section { }
.hero-section__title { }
.hero-section__image { }
.hero-section--dark { }

Never use global classes without a module prefix.

CSS Variables

Acai provides theme variables:

var(--main-color)        /* Primary brand color */
var(--main-color-light)  /* Lighter variant */
var(--main-color-dark)   /* Darker variant */

Utility Classes (Built-in)

Class Description
transition3s 0.3s smooth transition
click-a-child Makes parent clickable via child <a> tag
line-clamp2 / line-clamp3 / line-clamp5 Text truncation with ellipsis
filter-white CSS filter to make images/icons white
lazyload Lazy loading (use with data-src)

JavaScript

Module Scripts (script.js)

Keep JavaScript scoped to the module. Use section_id for targeting:

// Scope to this module instance
const section = document.getElementById('{{ section_id }}');
if (section) {
  const buttons = section.querySelectorAll('.btn');
  // ...
}

CmsApi (Client-Side)

// Call a hook
CmsApi.hook('/hooks/module_id/', { action: 'getData', id: 123 }, function(response) {
  console.log(response);
});

Vue 3 Integration

For complex interactivity, use Vue 3 via CDN with Composition API:

<div id="app-{{ section_id }}">
  <p>${ message }</p>
  <button @click="increment">${ count }</button>
</div>

<script>
const { createApp, ref } = Vue;
createApp({
  delimiters: ['${', '}'],  // Avoid conflict with Twig {{ }}
  setup() {
    const message = ref('Hello');
    const count = ref(0);
    const increment = () => count.value++;
    return { message, count, increment };
  }
}).mount('#app-{{ section_id }}');
</script>

Important: Use '${' and '}' as Vue delimiters to avoid conflicts with Twig's {{ }} syntax.