Layout, slots & schema
Layout quy định khung HTML và vị trí các vùng dùng chung. Mỗi slot chứa một cây element Visual được lưu riêng trong file cấu hình layout. Nội dung riêng của page được chèn vào content_for_layout.
1. Ba file, ba trách nhiệm
layouts/main.html Khung HTML + schema khai báo slot
config/layout-main.json Cây element và dữ liệu từng slot
pages/index.json Nội dung riêng của trang, có "layout": "main"Mọi page dùng layout: "main" cùng nhận header, footer và các slot của layout này. Sửa slot rồi lưu layout sẽ thay đổi vùng dùng chung trên các page đó.
2. Khai báo schema trong layouts/main.html
{% schema %}
{
"version": 1,
"name": "Layout chính",
"slots": [
{"key": "header", "label": "Đầu trang"},
{"key": "aside_right", "label": "Cột bên phải"},
{"key": "footer", "label": "Chân trang"}
]
}
{% endschema %}
<!doctype html>
<html lang="{{ doc.locale|default:'vi' }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ doc.title }}</title>
{{ head_content }}
<style>
.site-body { display: grid; grid-template-columns: minmax(0, 1fr) 280px;
gap: 24px; max-width: 1200px; margin: auto; padding: 0 16px; }
.site-body > * { min-width: 0; }
@media (max-width: 767px) { .site-body { grid-template-columns: minmax(0, 1fr); } }
</style>
</head>
<body>
<header>{{ layout_slots.header }}</header>
<div class="site-body">
<main>{{ content_for_layout }}</main>
<aside>{{ layout_slots.aside_right }}</aside>
</div>
<footer>{{ layout_slots.footer }}</footer>
{{ body_end_content }}
</body>
</html>name là tên hiển thị; version nên khai báo 1; slots liệt kê key và label của từng vùng. Thứ tự slots là thứ tự hiển thị trong công cụ Layout. Vị trí trên website do HTML quyết định.
3. Quy tắc khai báo slot
- Mỗi layout có đúng một cặp
schema/endschema. namephải có giá trị vàslotsphải là mảng; layout không có vùng chung dùngslots: [].- Key slot duy nhất, bắt đầu bằng chữ, chỉ gồm chữ, số, dấu gạch dưới, tối đa 64 ký tự. Không dùng key
content. - Mỗi slot khai báo phải có đúng một vị trí chèn trực tiếp:
layout_slots.headerhoặclayout_slots.headertrong cặp dấu output Lingo. - Không chèn slot chưa khai báo; không dùng key động hoặc vòng lặp thay cho vị trí chèn trực tiếp.
content_for_layoutxuất hiện đúng một lần. Đây là nội dung page, không khai báo nó vào slots.
Slot có thể mang tên riêng như announcement, aside_left hoặc promotion. Header/footer là các tên thường dùng, không phải hai slot duy nhất được hỗ trợ.
4. File config/layout-main.json
{
"slots": {
"header": {
"format": "skai.visual",
"version": 1,
"title": "Đầu trang",
"layout": "main",
"children": [
{
"id": "header-brand",
"type": "text",
"props": {
"text": "config:site_name"
}
}
]
},
"aside_right": {
"format": "skai.visual",
"version": 1,
"title": "Cột bên phải",
"layout": "main",
"children": [
{
"id": "aside-info",
"type": "text",
"props": {
"text": "Thông tin hữu ích"
}
}
]
},
"footer": {
"format": "skai.visual",
"version": 1,
"title": "Chân trang",
"layout": "main",
"children": [
{
"id": "footer-address",
"type": "text",
"props": {
"text": "config:address"
}
}
]
}
}
}Object slots dùng key trùng với schema layout. Mỗi giá trị là document skai.visual chứa children; node con dùng đầy đủ props/styles/hidden_devices như node của page. Engine render children thành HTML rồi cung cấp qua layout_slots.<key>; trường layout trong document slot không khiến layout lồng lại chính nó.
Muốn để một slot trống, giữ document và đặt children: []. Các slot được khai báo trong schema là danh sách đang hoạt động; dữ liệu key cũ còn trong JSON không tự tạo thêm vị trí hiển thị.
File phải nằm phẳng tại config/layout-{tên-layout}.json. Ví dụ layouts/shop.html đi với config/layout-shop.json. Không tạo thư mục configs hoặc file layouts/main.json cho theme mới; đường dẫn layouts/*.json chỉ còn là tương thích dữ liệu cũ.
5. Liên kết từ page Visual
{
"format": "skai.visual",
"version": 1,
"title": "Trang chủ",
"layout": "main",
"children": [
{"id": "page-title", "type": "heading", "props": {"level": "h1", "text": "Nội dung riêng của trang"}}
]
}Không đưa header/footer vào children của từng page nếu đã dùng slot chung. Page JSON không khai báo lại schema layout và không chép object slots vào page.
6. Layout tạo từ mẫu của hệ thống
Layout do công cụ tạo tự động có thể có thêm metadata sau trong schema:
"template": "right",
"generator": "skai.layout.v1",
"settings": {
"width": 1200,
"aside_width": 280,
"gap": 24,
"content_first": true,
"hide_sidebars": false
}Các mẫu gồm standard, left, right, both và blank. width là rộng tối đa (320–2560 px), aside_width là rộng sidebar (120–600 px), gap là khoảng cách (0–120 px). content_first ưu tiên nội dung chính trên mobile; hide_sidebars ẩn sidebar trên mobile.
Đây là tham số của bộ sinh HTML layout, không phải mảng field settings của element và không nằm trong config/layout-main.json. Với layout custom viết tay, thay đổi bố cục bằng HTML/CSS; không gắn generator để mong settings tự thay đổi HTML đang có. Hệ thống kiểm tra layout có còn đúng mẫu được sinh trước khi quản lý lại.
7. Thao tác trong Visual Builder
- Mở công cụ Layout, chọn layout cần chỉnh.
- Chọn slot theo schema, thêm hoặc chỉnh cây element trong vùng đó.
- Lưu layout để ghi nội dung slot xuống config/layout-{name}.json.
- Mở các page dùng layout và kiểm tra desktop/tablet/mobile.
Khi đổi key slot trong code, cập nhật đồng thời vị trí chèn và key trong file dữ liệu. Giữ nguyên cây children để tránh mất liên kết nội dung. Không cần đổi key nếu chỉ thay label hiển thị.
Schema trong layout khai báo khung và slot. config/layout-*.json lưu nội dung slot. config/config_schema.json và config/config_data.json quản lý cấu hình chung của theme.
Xem thêm pages/*.json · Visual Builder, schema element và config chuẩn.