pages/*.json · Visual Builder
pages/*.json hiện dùng định dạng Visual Builder: mỗi trang là một document chứa cây element. Thứ tự phần tử trong mảng children chính là thứ tự render. Nội dung lưu trong props; thiết kế lưu trong styles.
Dùng format: "skai.visual", version: 1. Không viết page mới bằng cấu trúc section của engine cũ: sections, order, blocks, block_order hoặc section.settings.
1. Các file cần có
layouts/main.html
pages/index.json
pages/about.json
elements/promo-card.html # Chỉ cần khi tạo element customElement hệ thống như container, heading, text, button được registry cung cấp sẵn. Không cần tạo bản sao các file element hệ thống trong theme. pages/index.json ứng với /; pages/about.json ứng với /about. Tránh tạo HTML và JSON trùng route.
2. Layout HTML dùng chung
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ doc.title }}</title>
{{ head_content }}
</head>
<body>
{{ content_for_layout }}
{{ body_end_content }}
</body>
</html>layout: "main" trỏ tới layouts/main.html. content_for_layout chứa HTML của cây element. Header/footer dùng chung thuộc layout, không cần sao chép vào mỗi page. Các vùng layout do Visual Builder quản lý vẫn theo schema slot của layout.
Khai báo header/footer/sidebar trong schema của layouts/main.html và lưu cây element từng slot tại config/layout-main.json. Xem ví dụ đầy đủ: layout, slots và schema.
3. Ví dụ đầy đủ: pages/index.json
{
"format": "skai.visual",
"version": 1,
"title": "Trang chủ",
"layout": "main",
"children": [
{
"id": "hero",
"type": "container",
"name": "Giới thiệu",
"styles": {
"desktop": {
"display": "flex",
"flexDirection": "column",
"gap": "24px",
"padding": "64px 24px"
},
"mobile": {
"padding": "32px 16px",
"gap": "16px"
}
},
"children": [
{
"id": "hero-title",
"type": "heading",
"props": {
"level": "h1",
"text": "Chào mừng đến website"
}
},
{
"id": "hero-description",
"type": "text",
"props": {
"text": "<p>Nội dung có thể chỉnh bằng <strong>Visual Builder</strong>.</p>"
}
},
{
"id": "hero-cta",
"type": "button",
"props": {
"label": "Liên hệ",
"url": "/contact"
}
}
]
}
]
}4. Cấu trúc document
| Key | Ý nghĩa |
|---|---|
format | Luôn là skai.visual. |
version | Phiên bản định dạng: 1. |
title | Tiêu đề trang; layout dùng qua doc.title. |
layout | Tên layout, không gồm thư mục và đuôi .html. |
seo | Object metadata tùy chọn; theme/layout quyết định cách sử dụng. |
children | Mảng element gốc, có thể lồng cây theo khả năng của từng type. |
5. Cấu trúc mỗi element
| Key | Ý nghĩa |
|---|---|
id | ID ổn định, duy nhất trong cây. Bắt đầu bằng chữ; dùng chữ, số, dấu gạch ngang hoặc gạch dưới; tối đa 80 ký tự, không chứa --. |
type | Key element hệ thống hoặc tên file custom: promo-card → elements/promo-card.html. |
name | Tên tùy chọn hiển thị trong editor. |
props | Giá trị theo ID field trong schema của element. Đây là dữ liệu instance, không phải định nghĩa schema. |
styles | Object gồm desktop, tablet, mobile; mỗi thuộc tính CSS dùng camelCase, giá trị là chuỗi. |
children | Element con; chỉ dùng khi type cho phép. Registry có thể giới hạn type cha/con. |
hidden | Ẩn element toàn bộ. |
hidden_devices | Ẩn theo thiết bị, ví dụ {"mobile": true}. |
custom_css | CSS riêng theo cú pháp Custom CSS của builder, được xử lý theo ID element. |
bindings | Liên kết dữ liệu trong ngữ cảnh Entries; không dùng để liên kết theme config. |
ref | Dành cho tham chiếu nhóm element dùng chung, xem mục bên dưới. |
6. Thiết kế, responsive và kế thừa config
Desktop là nền; tablet/mobile ghi đè thuộc tính ở breakpoint tương ứng. Chỉ lưu thuộc tính user chủ động thay đổi. Thuộc tính không khai báo dùng default_styles của element và token config chuẩn; đổi cấu hình theme sẽ cập nhật các thuộc tính đang kế thừa.
"styles": {
"desktop": {"color": "#123456", "fontSize": "20px"},
"mobile": {"fontSize": "16px"}
}Ví dụ trên giữ màu riêng khi theme đổi màu. Bỏ color hoặc reset trong tab Thiết kế để trở lại mặc định của element. Không ghi giá trị CSS đã resolve từ config vào page. Xem 111 key config chuẩn.
7. Nội dung động và đa ngôn ngữ
"props": {"text": "config:address"}
"props": {"text": "t:home.hero_title"}
"props": {"label": "config:email", "url": "mailto:config:email"}Đây là ba ví dụ props độc lập. Field text/textarea/richtext hỗ trợ giá trị trực tiếp, config:key và key dịch t:namespace.key. Field URL hỗ trợ tham chiếu tương ứng và tiền tố mailto:/tel:. Không dùng config_bindings cũ. Định dạng props phải phù hợp schema của element.
t:home.hero_title đọc từ locales/vi/home.json, locales/en/home.json theo locale hiện tại. Visual Builder chỉ biên tập các ngôn ngữ website đã kích hoạt; không tạo ngôn ngữ mới.
8. Tạo element custom
Tạo elements/promo-card.html chứa template và một thẻ schema. Page chỉ lưu type: "promo-card", giá trị props và thiết kế riêng. Xem schema element Visual Builder.
9. Nhóm element dùng chung
Builder vẫn có cơ chế tham chiếu nhóm element tái sử dụng. Đây là cây element Visual, không phải blocks/block_order bên trong section của engine cũ.
// Node trong children của page:
{"id": "shared-contact", "type": "block", "ref": "contact-card"}
// blocks/contact-card.json:
{"version": 1, "children": [
{"id": "contact-text", "type": "text", "props": {"text": "config:address"}}
]}Không gắn props/styles/children riêng lên node tham chiếu. Detach trong builder để chỉnh một bản độc lập. Engine mở rộng nhóm khi render và tạo namespace ID; JSON trên đĩa giữ dạng tham chiếu.
10. Chuyển page cũ sang Visual
- Chọn layout và các element tương ứng.
- Tạo document
skai.visualvới mảng children. - Chuyển nội dung settings cũ sang props theo schema từng element; thứ tự order cũ thành thứ tự trong children.
- Chuyển thiết kế sang styles theo thiết bị, giữ key dịch và liên kết config phù hợp.
- Preview, kiểm tra lưu/mở lại bằng Visual Builder rồi mới publish.
Không chỉ đổi version hoặc thêm format lên JSON cũ: mô hình dữ liệu đã khác. Khi xây trang mới, dùng ví dụ Visual ở trên. Giới hạn cây hiện tại: tối đa 1.000 node được duyệt, độ sâu 32; type, ID và quan hệ cha/con phải hợp lệ.