- 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>
102 lines
2.4 KiB
Markdown
102 lines
2.4 KiB
Markdown
# CSS & JavaScript Conventions
|
|
|
|
## CSS
|
|
|
|
### Tailwind First
|
|
|
|
Use Tailwind CSS as the primary styling method. Only use custom CSS when Tailwind is insufficient.
|
|
|
|
```html
|
|
<!-- 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:
|
|
|
|
```css
|
|
/* 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:
|
|
|
|
```css
|
|
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:
|
|
|
|
```js
|
|
// Scope to this module instance
|
|
const section = document.getElementById('{{ section_id }}');
|
|
if (section) {
|
|
const buttons = section.querySelectorAll('.btn');
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### CmsApi (Client-Side)
|
|
|
|
```js
|
|
// 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:
|
|
|
|
```html
|
|
<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.
|