@fwkui/x-css 1.0.11 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +314 -204
  3. package/package.json +1 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FKUI Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,101 +1,34 @@
1
- # @fwkui/x-css 🚀
1
+ # @fwkui/x-css
2
2
 
3
- **@fwkui/x-css**một thư viện CSS-in-JS siêu nhẹ, hiệu năng cao, kế thừa tinh hoa từ `fwxcss` với cú pháp Emmet-like. giúp bạn viết mã CSS nhanh hơn bằng cách sử dụng các lớp atomic ngắn gọn, hỗ trợ đầy đủ TypeScript, SSR và khả năng mở rộng mạnh mẽ.
3
+ `@fwkui/x-css`utility CSS engine siêu nhẹ, parse class theo cú pháp ngắn sinh CSS runtime theo layer + media.
4
+
5
+ Mục tiêu của README này:
6
+ 1. Người dùng có thể tích hợp ngay.
7
+ 2. AI có thể suy luận đúng cú pháp để sinh class dùng được ngay.
8
+ 3. QA có thể kiểm thử parser theo vector cố định.
4
9
 
5
10
  ![License](https://img.shields.io/npm/l/@fwkui/x-css)
6
11
  ![Version](https://img.shields.io/npm/v/@fwkui/x-css)
7
12
 
8
- ---
9
-
10
- ## 📖 Hướng dẫn Cơ bản
11
-
12
- ### 1. Cú pháp Cốt lõi
13
- Mỗi class trong @fwkui/x-css được cấu tạo theo công thức:
14
- `[Media]:[Layer][Property][Value][@Selector]`
15
-
16
- **Thứ tự parse chính xác:**
17
- 1. `Media` (tùy chọn) + dấu `:`
18
- 2. `Layer` (tùy chọn, dạng số)
19
- 3. `Property` (bắt buộc)
20
- 4. `Value` (bắt buộc)
21
- 5. `@Selector` (tùy chọn, nằm cuối class)
22
-
23
- **Media mặc định và thứ tự áp dụng nội bộ:**
24
-
25
- | Key | Query |
26
- | :--- | :--- |
27
- | `default` | Không bọc `@media` (áp dụng trực tiếp) |
28
- | `xs` | `screen and (max-width: 575px)` |
29
- | `sm` | `screen and (min-width: 576px)` |
30
- | `md` | `screen and (min-width: 768px)` |
31
- | `lg` | `screen and (min-width: 992px)` |
32
- | `xl` | `screen and (min-width: 1200px)` |
33
- | `2xl` | `screen and (min-width: 1400px)` |
34
- | `sma` | `screen and (max-width: 768px)` |
35
- | `mda` | `screen and (max-width: 992px)` |
36
- | `lga` | `screen and (max-width: 1200px)` |
37
- | `xla` | `screen and (max-width: 1400px)` |
38
-
39
- `breakpoints` custom sẽ được nối vào sau danh sách trên. Nếu trùng key, giá trị khai báo sau cùng sẽ ghi đè key trước đó.
40
-
41
- **Layer mặc định và dải số:**
42
- - Nếu không truyền `Layer`, hệ thống dùng mặc định `0`.
43
- - Engine tạo sẵn 24 layer: `l0` đến `l23`.
44
- - Để thứ tự ổn định, nên dùng dải số `0-23`.
45
- - Số layer lớn hơn sẽ có độ ưu tiên cascade cao hơn trong cùng media.
46
-
47
- **Ví dụ:**
48
- - `m10px` ⮕ `margin: 10px`
49
- - `cRed` ⮕ `color: red`
50
- - `sm:p20px` ⮕ `@media (min-width: 576px) { padding: 20px }`
51
- - `3bgWhite` ⮕ `@layer l3 { background: white }`
52
- - `sm:3bgWhite` ⮕ `@media (min-width: 576px) { @layer l3 { background: white } }`
53
- - `cBlue@:hover` ⮕ `.class:hover { color: blue }`
54
-
55
- ### 2. Nguyên lý Parser (Scan & Slice) 🧠
56
- Thư viện quét class từ trái sang phải và tự động cắt Property/Value dựa trên các điểm ngắt (Số, Chữ Hoa, Ký tự đặc biệt...), giúp tốc độ xử lý nhanh hơn ~1.6x so với Regex truyền thống.
57
-
58
- | Loại điểm ngắt | Ví dụ Class | Phân tích (Prop \| Value) |
59
- | :--- | :--- | :--- |
60
- | **Số (0-9)** | `w100px` | `w` (width) \| `100px` |
61
- | **Chữ Hoa (A-Z)** | `dF` | `d` (display) \| `F` |
62
- | **Dấu gạch ngang + Số** | `m-10px` | `m` (margin) \| `-10px` |
63
-
64
- > [!IMPORTANT]
65
- > **Lưu ý về CamelCase**: Sử dụng `mt10px` hoặc `margin-top-10px`, tránh `marginTop10px` để đảm bảo parser hoạt động chính xác.
66
- >
67
- > **Lưu ý quan trọng về Value viết tắt bằng chữ cái**:
68
- > - Value dạng chữ cái phải viết hoa ký tự đầu (ví dụ: `bdN`, `dF`, `posA`).
69
- > - Tránh viết thường toàn bộ như `bdn`, `df`, `posa`.
70
-
71
- ---
72
-
73
- ## 📚 Bộ Từ điển (Dictionary)
74
- Danh sách đầy đủ các từ viết tắt được cập nhật liên tục tại [DICTIONARY.md](./DICTIONARY.md).
13
+ ## Cài Đặt Nhanh
75
14
 
76
- ### Một số Alias phổ biến:
77
- - **Layout**: `d` (display), `pos` (position), `z` (z-index), `fl` (float).
78
- - **Flexbox**: `fx` (flex), `ai` (align-items), `jc` (justify-content).
79
- - **Spacing**: `m` (margin), `p` (padding), `w` (width), `h` (height).
80
- - **Styling**: `c` (color), `bg` (background), `bd` (border), `op` (opacity).
81
- - **Typography**: `fns` (font-size), `fw` (font-weight), `ta` (text-align).
15
+ ### 1) NPM
82
16
 
83
- ---
84
-
85
- Bạn có thể tải thư viện hoặc xem mã nguồn tại: [https://github.com/dwork-dev/fwkui](https://github.com/dwork-dev/fwkui)
17
+ ```bash
18
+ npm install @fwkui/x-css
19
+ ```
86
20
 
87
- ## 📦 Cài đặt
21
+ ### 2) Dùng trực tiếp qua URL (không cần bundler)
88
22
 
89
- ### Cách 1: Dùng trực tiếp module qua URL (không cần bundler)
23
+ Lưu ý:
24
+ 1. `dist/index.js` là CommonJS (Node).
25
+ 2. Trình duyệt dùng `dist/index.mjs` hoặc `dist/index-auto.mjs`.
90
26
 
91
- > [!IMPORTANT]
92
- > Bản `dist/index.js` là CommonJS cho môi trường Node.
93
- > Khi chạy trực tiếp trên trình duyệt, hãy dùng `dist/index.mjs` hoặc `dist/index-auto.mjs`.
27
+ Option A: chủ động khởi tạo
94
28
 
95
- **Option A - Tự khởi tạo (khuyên dùng):**
96
29
  ```html
97
30
  <script type="module">
98
- import xcss from 'https://unpkg.com/@fwkui/x-css@1.0.11/dist/index.mjs';
31
+ import xcss from 'https://unpkg.com/@fwkui/x-css@latest/dist/index.mjs';
99
32
 
100
33
  xcss.cssObserve(document, {
101
34
  dictionaryImport: true
@@ -103,65 +36,195 @@ Bạn có thể tải thư viện hoặc xem mã nguồn tại: [https://github.
103
36
  </script>
104
37
  ```
105
38
 
106
- **Option B - Auto observe ngay khi import:**
39
+ Option B: auto observe khi import
40
+
107
41
  ```html
108
- <script type="module" src="https://unpkg.com/@fwkui/x-css@1.0.11/dist/index-auto.mjs"></script>
42
+ <script type="module" src="https://unpkg.com/@fwkui/x-css@latest/dist/index-auto.mjs"></script>
109
43
  ```
110
44
 
111
- Bạn cũng có thể thay `unpkg` bằng `jsDelivr`:
112
- `https://cdn.jsdelivr.net/npm/@fwkui/x-css@1.0.11/dist/index.mjs`
45
+ CDN thay thế:
46
+ `https://cdn.jsdelivr.net/npm/@fwkui/x-css@latest/dist/index.mjs`
113
47
 
114
- ### Cách 2: Cài đặt qua NPM
115
- ```bash
116
- npm install @fwkui/x-css
48
+ ## Dùng Trong 60 Giây
49
+
50
+ ```js
51
+ import xcss from '@fwkui/x-css';
52
+
53
+ xcss.cssObserve(document);
117
54
  ```
118
55
 
119
- ---
56
+ ```html
57
+ <button class="dF aiC jcC p10px;16px bdN bdra8px bgc#0a64e8 cWhite">
58
+ Đăng nhập
59
+ </button>
60
+ ```
120
61
 
121
- ## 🚀 Hướng dẫn Sử dụng
62
+ ## Contract Pháp
122
63
 
123
- ### 1. Vanilla JavaScript (Tự động)
64
+ Mỗi utility class theo form:
124
65
 
125
- Để tự động scan và apply style cho toàn bộ document:
66
+ `[Media]:[Layer][Property][Value][@Selector]`
126
67
 
127
- ```javascript
128
- import xcss from '@fwkui/x-css';
68
+ Thứ tự parse bắt buộc:
69
+ 1. `selector` (hậu tố `@...`, nằm ngoài `[]`).
70
+ 2. `media` (tiền tố trước `:`).
71
+ 3. `layer` (chuỗi số liên tiếp ở đầu).
72
+ 4. `property`.
73
+ 5. `value`.
129
74
 
130
- // Khởi tạo và lắng nghe thay đổi DOM
131
- xcss.cssObserve(document);
75
+ Ý nghĩa từng phần:
76
+ 1. `Media` (tùy chọn): key media như `sm`, `md`, `lg`, hoặc key custom.
77
+ 2. `Layer` (tùy chọn): số ưu tiên cascade.
78
+ 3. `Property` (bắt buộc): alias thuộc dictionary hoặc CSS property hợp lệ.
79
+ 4. `Value` (bắt buộc với utility chuẩn): giá trị CSS, alias value hoặc arbitrary value.
80
+ 5. `@Selector` (tùy chọn): ví dụ `@:hover`, `@::before`.
81
+
82
+ Ngoại lệ parser (special syntax):
83
+ 1. `[AliasName]`: class group alias (không dùng value trực tiếp).
84
+ 2. `&...`: nhánh selector đặc biệt theo parser hiện tại.
85
+
86
+ ### Media Mặc Định Và Thứ Tự Nội Bộ
87
+
88
+ Engine nạp media theo thứ tự:
89
+
90
+ | Thứ tự | Key | Query |
91
+ | :--- | :--- | :--- |
92
+ | 1 | `default` | Không bọc `@media` |
93
+ | 2 | `xs` | `screen and (max-width: 575px)` |
94
+ | 3 | `sm` | `screen and (min-width: 576px)` |
95
+ | 4 | `md` | `screen and (min-width: 768px)` |
96
+ | 5 | `lg` | `screen and (min-width: 992px)` |
97
+ | 6 | `xl` | `screen and (min-width: 1200px)` |
98
+ | 7 | `2xl` | `screen and (min-width: 1400px)` |
99
+ | 8 | `sma` | `screen and (max-width: 768px)` |
100
+ | 9 | `mda` | `screen and (max-width: 992px)` |
101
+ | 10 | `lga` | `screen and (max-width: 1200px)` |
102
+ | 11 | `xla` | `screen and (max-width: 1400px)` |
103
+
104
+ Quy tắc custom breakpoint:
105
+ 1. `breakpoints` được nối vào sau danh sách mặc định.
106
+ 2. Nếu trùng key, key khai báo sau cùng ghi đè key trước (`last write wins`).
107
+
108
+ Ví dụ:
109
+
110
+ ```js
111
+ xcss.cssObserve(document, {
112
+ breakpoints: [
113
+ { tablet: 'screen and (min-width: 768px)' }
114
+ ]
115
+ });
132
116
  ```
133
117
 
134
- Sử dụng trong HTML:
135
- ```html
136
- <div class="dF cRed m20px">Hello World</div>
118
+ Dùng class: `tablet:dB`.
119
+
120
+ ### Layer Mặc Định
121
+
122
+ 1. Nếu không khai báo layer, engine dùng `0`.
123
+ 2. Engine tạo sẵn 24 layer: `l0 -> l23`.
124
+ 3. Nên dùng dải `0-23` để giữ thứ tự ổn định.
125
+ 4. Số layer lớn hơn có ưu tiên cascade cao hơn trong cùng media.
126
+
127
+ ## Quy Tắc Điểm Ngắt Đầy Đủ (Theo Parser Hiện Tại)
128
+
129
+ Mục tiêu là tách class thành tuple:
130
+ `{ media, layer, property, value, selector }`
131
+
132
+ Thứ tự suy luận bắt buộc:
133
+ 1. Tách `selector`: lấy phần sau ký tự `@` cuối cùng, chỉ khi `@` nằm ngoài `[]`.
134
+ 2. Tách `media`: nếu còn `:` thì phần trước `:` là `media`.
135
+ 3. Tách `layer`: đọc chuỗi số liên tiếp ở đầu phần còn lại.
136
+ 4. Tách `property/value`: quét trái -> phải và dừng `property` theo bảng quyết định.
137
+ 5. Nếu phần còn lại bắt đầu bằng `&` hoặc `[` thì đi vào nhánh special syntax.
138
+
139
+ ### Bảng Quyết Định Khi Quét `property`
140
+
141
+ | Ký tự đang xét | Điều kiện | Hành động |
142
+ | :--- | :--- | :--- |
143
+ | `a-z` | luôn đúng | vẫn là `property` |
144
+ | `-` hoặc `.` | ký tự kế tiếp là số | dừng `property`, phần còn lại là `value` |
145
+ | `-` | gặp `--` và đã có ít nhất 1 ký tự property | dừng `property`, bắt đầu `value` (CSS variable) |
146
+ | `-` hoặc `.` | không rơi vào 2 điều kiện trên | vẫn là `property` |
147
+ | ký tự khác (`A-Z`, `0-9`, `#`, `!`, `[`, `(`, `%`, ...) | luôn đúng | dừng `property`, phần còn lại là `value` |
148
+
149
+ ### Chuẩn Hóa `value` Sau Khi Tách
150
+
151
+ 1. Value bắt đầu bằng `!` -> thêm hậu tố `!important`.
152
+ 2. Value bắt đầu bằng `--` -> đổi thành `var(--...)`.
153
+ 3. Value dạng `[...]` -> bỏ `[` `]`, rồi thay `;` thành khoảng trắng.
154
+ 4. Ký tự `#` trong value giữ nguyên (hex color).
155
+
156
+ Pseudo-flow cho AI:
157
+
158
+ ```text
159
+ class -> selector -> media -> layer -> property -> value
160
+ if value startsWith('!') => important
161
+ if value startsWith('--') => var(value)
162
+ if value is bracketed [..] => strip brackets + replace ';' with ' '
137
163
  ```
138
164
 
139
- Nếu bạn không dùng bundler, thể import thẳng qua URL:
165
+ ### Test Vector Mini (10 input -> expected tuple)
166
+
167
+ | # | Input | Expected tuple |
168
+ | :--- | :--- | :--- |
169
+ | 1 | `m10px` | `{ media: '', layer: '', property: 'm', value: '10px', selector: '' }` |
170
+ | 2 | `md:w100%` | `{ media: 'md', layer: '', property: 'w', value: '100%', selector: '' }` |
171
+ | 3 | `sm:3bgWhite` | `{ media: 'sm', layer: '3', property: 'bg', value: 'White', selector: '' }` |
172
+ | 4 | `cBlue@:hover` | `{ media: '', layer: '', property: 'c', value: 'Blue', selector: ':hover' }` |
173
+ | 5 | `m-10px` | `{ media: '', layer: '', property: 'm', value: '-10px', selector: '' }` |
174
+ | 6 | `opc0.8` | `{ media: '', layer: '', property: 'opc', value: '0.8', selector: '' }` |
175
+ | 7 | `bgc--brand` | `{ media: '', layer: '', property: 'bgc', value: '--brand', selector: '' }` |
176
+ | 8 | `c!#0a64e8` | `{ media: '', layer: '', property: 'c', value: '!#0a64e8', selector: '' }` |
177
+ | 9 | `w[calc(100%;-;10px)]` | `{ media: '', layer: '', property: 'w', value: '[calc(100%;-;10px)]', selector: '' }` |
178
+ | 10 | `[btnPrimary]` | `{ media: '', layer: '', property: '[btnPrimary]', value: '', selector: '' }` |
179
+
180
+ ## Bảng Sai -> Đúng (Những Lỗi Gây Vỡ Parse)
181
+
182
+ | Sai | Đúng | Giải thích |
183
+ | :--- | :--- | :--- |
184
+ | `bdn` | `bdN` | Value viết tắt dạng chữ cái phải viết hoa ký tự đầu (`N` = none). |
185
+ | `df` | `dF` | `F` là value viết tắt của `flex`. |
186
+ | `posa` | `posA` | `A` là value viết tắt của `absolute`. |
187
+ | `tr0.2s` | `tran0.2s` | Property `transition` là `tran`, không phải `tr`. |
188
+ | `op0.8` | `opc0.8` | `op` là `object-position`; `opc` mới là `opacity`. |
189
+ | `3:bgWhite` | `3bgWhite` | Layer là số đứng liền trước property, không có `:` sau layer. |
190
+ | `hover:cRed` | `cRed@:hover` | Selector modifier dùng hậu tố `@Selector`. |
191
+ | `tablet:dB` (chưa khai báo) | `tablet:dB` + `breakpoints` config | Media custom phải được khai báo trước trong config. |
192
+ | `m--10px` | `m-10px` | Số âm dùng `-`; `--` dành cho CSS variable (`bgc--brand`). |
193
+ | `bgcbrand` | `bgcBrand` hoặc `bgc--brand` | Cần điểm ngắt rõ ràng để parser tách đúng value. |
194
+ | `wcalc(100%-10px)` | `w[calc(100%;-;10px)]` | Value phức tạp nên bọc `[]`, dùng `;` để biểu diễn khoảng trắng. |
195
+ | `!cRed` | `c!Red` | `!` phải đứng trong phần value (sau property), không đứng đầu class. |
196
+
197
+ ## Ví Dụ Chính Xác Theo Dictionary
198
+
199
+ Danh sách đầy đủ alias xem tại [DICTIONARY.md](./DICTIONARY.md).
200
+
201
+ Một số alias dễ nhầm:
202
+ 1. `op` = `object-position`
203
+ 2. `opc` = `opacity`
204
+ 3. `tran` = `transition`
205
+ 4. `tr` = `transparent` (value alias, không phải property transition)
206
+
207
+ Ví dụ:
140
208
 
141
209
  ```html
142
- <script type="module">
143
- import xcss from 'https://unpkg.com/@fwkui/x-css@1.0.11/dist/index.mjs';
144
- xcss.cssObserve(document);
145
- </script>
210
+ <div class="dF aiC jcSB p12px;16px bdN bgcWhite"></div>
211
+ <div class="tran0.2s opc0.8@:hover"></div>
212
+ <div class="c!#0a64e8"></div>
213
+ <div class="w[calc(100%;-;10px)]"></div>
146
214
  ```
147
215
 
148
- ### 2. React / Components (`clsx`)
149
-
150
- Sử dụng `clsx` để kết hợp class động và tối ưu việc gom nhóm string (tương tự `classnames` nhưng tích hợp sẵn parser engine):
216
+ ## Dùng Trong React / Component
151
217
 
152
218
  ```jsx
153
219
  import { clsx } from '@fwkui/x-css';
154
220
 
155
- function Button({ primary, children }) {
221
+ export function Button({ primary, children }) {
156
222
  return (
157
- <button
223
+ <button
158
224
  className={clsx(
159
- 'p10px;20px', // padding: 10px 20px
160
- 'bdN bdra4px', // border: none, border-radius: 4px
161
- 'tran0.2s', // transition: 0.2s
162
- 'cWhite', // color: white
163
- primary ? 'bgBlue' : 'bgGray',
164
- 'opc0.8@:hover' // opacity: 0.8 when hover
225
+ 'dF aiC jcC p10px;16px bdN bdra8px tran0.2s',
226
+ primary ? 'bgc#0a64e8 cWhite' : 'bgc#e5e7eb c#111827',
227
+ 'opc0.9@:hover'
165
228
  )}
166
229
  >
167
230
  {children}
@@ -170,133 +233,180 @@ function Button({ primary, children }) {
170
233
  }
171
234
  ```
172
235
 
173
- ### 3. Cấu hình (Configuration)
174
-
175
- Bạn có thể truyền object config khi khởi tạo:
236
+ ## Cấu Hình
176
237
 
177
- ```javascript
238
+ ```js
178
239
  import xcss from '@fwkui/x-css';
179
240
 
180
241
  xcss.cssObserve(document, {
181
- // Thêm màu sắc hoặc giá trị custom
182
242
  theme: {
183
- brand: '#ff5722',
184
- dark: '#333333'
243
+ brand: '#0a64e8',
244
+ danger: '#ef4444'
185
245
  },
186
- // Thêm breakpoint tùy chỉnh
187
246
  breakpoints: [
188
247
  { tablet: 'screen and (min-width: 768px)' }
189
248
  ],
190
- // Base CSS
191
- base: 'body { margin: 0; font-family: sans-serif; }',
192
-
193
- // [New] Thêm tiền tố (Prefix) để tránh xung đột
194
- prefix: 'fk-', // Chỉ xử lý các class bắt đầu bằng 'fk-'
195
-
196
- // [New] Điều khiển import dictionary viết tắt
197
- // true (mặc định): dùng dictionary.ts của thư viện
198
- // false: tắt dictionary mặc định
199
- // string URL/path: import dictionary external
249
+ base: 'body{margin:0;font-family:system-ui,sans-serif;}',
250
+ prefix: 'fk-',
200
251
  dictionaryImport: true
201
252
  });
202
253
  ```
203
254
 
204
- Sau đó sử dụng: `fk-cBrand`, `fk-tablet:dB`.
205
- Các class không có tiền tố (ví dụ `m10px`) sẽ bị **bỏ qua**.
255
+ Sau đó dùng class: `fk-cBrand fk-tablet:dB`.
206
256
 
207
- Nếu dùng `dictionaryImport` là URL/path ngoài, bạn có thể đợi nạp xong trước khi render:
257
+ `dictionaryImport`:
258
+ 1. `true` (mặc định): dùng dictionary tích hợp.
259
+ 2. `false`: tắt dictionary mặc định.
260
+ 3. `string` URL/path: import dictionary ngoài.
208
261
 
209
- ```javascript
210
- const cssEngine = xcss.css({ dictionaryImport: 'https://cdn.example.com/xcss-dict.mjs' });
211
- await cssEngine.ready;
212
- const { clsx, observe } = cssEngine.buildCss(document);
262
+ Nếu import dictionary ngoài:
263
+
264
+ ```js
265
+ const engine = xcss.css({ dictionaryImport: 'https://cdn.example.com/xcss-dict.mjs' });
266
+ await engine.ready;
267
+ const { clsx, observe } = engine.buildCss(document);
213
268
  observe();
214
269
  ```
215
270
 
216
- ### 4. Server-Side Rendering (SSR)
271
+ Mẫu file để thay thế trực tiếp URL `https://cdn.example.com/xcss-dict.mjs`:
272
+
273
+ ```js
274
+ // xcss-dict.mjs
275
+ // Có thể public lên CDN của bạn rồi truyền URL vào dictionaryImport
276
+
277
+ export const SHORT_PROPERTIES = {
278
+ d: 'display',
279
+ c: 'color',
280
+ bgc: 'background-color',
281
+ bd: 'border',
282
+ w: 'width',
283
+ h: 'height',
284
+ p: 'padding',
285
+ m: 'margin',
286
+ tran: 'transition',
287
+ opc: 'opacity'
288
+ };
289
+
290
+ export const COMMON_VALUES = {
291
+ n: 'none',
292
+ b: 'block',
293
+ f: 'flex',
294
+ t: 'transparent',
295
+ i: 'inherit'
296
+ };
297
+
298
+ export const SPECIFIC_VALUES = {
299
+ d: {
300
+ f: 'flex',
301
+ b: 'block',
302
+ ib: 'inline-block'
303
+ },
304
+ bd: {
305
+ n: 'none'
306
+ },
307
+ c: {
308
+ pri: '#0a64e8',
309
+ danger: '#ef4444'
310
+ },
311
+ bgc: {
312
+ pri: '#0a64e8',
313
+ soft: '#e8f1ff'
314
+ }
315
+ };
316
+
317
+ export default {
318
+ SHORT_PROPERTIES,
319
+ COMMON_VALUES,
320
+ SPECIFIC_VALUES
321
+ };
322
+ ```
323
+
324
+ Quy trình thay link:
325
+ 1. Tạo file `xcss-dict.mjs` theo mẫu trên.
326
+ 2. Upload lên CDN/public URL của bạn.
327
+ 3. Thay `dictionaryImport` bằng URL thật.
328
+ 4. Chờ `await engine.ready` trước khi render class.
329
+
330
+ ## SSR Và Static Extraction
217
331
 
218
- Để hỗ trợ SSR (Next.js, Remix...), bạn cần inject CSS sinh ra từ server vào thẻ `<head>`:
332
+ SSR:
219
333
 
220
- ```javascript
334
+ ```js
221
335
  import { getCss } from '@fwkui/x-css';
222
336
 
223
- // Trong file layout/server entry
224
337
  const styles = getCss();
225
-
226
- // Inject HTML
227
- // Inject HTML
228
338
  // <style dangerouslySetInnerHTML={{ __html: styles }} />
229
339
  ```
230
340
 
231
- ### 5. Xuất File CSS (Static Extraction)
232
-
233
- Nếu bạn muốn tạo file `.css` tĩnh (dành cho Static Site Generation hoặc Cache build), bạn có thể dùng script sau:
341
+ Static extraction:
234
342
 
235
- ```javascript
236
- // build-css.js
237
- const fs = require('fs');
238
- const xcss = require('@fwkui/x-css');
343
+ ```js
344
+ import xcss from '@fwkui/x-css';
345
+ import fs from 'node:fs';
239
346
 
240
- // 1. Giả lập quá trình Render để thu thập class
241
- // (Bạn thể import App và renderToString nếu dùng React/Vue)
242
- // Ở đây ví dụ gọi thủ công:
243
- const { clsx, getCssString } = xcss({
244
- theme: { brand: '#ff0000' } // Cấu hình (nếu có)
347
+ const { clsx, getCssString } = xcss.css({
348
+ theme: { brand: '#0a64e8' }
245
349
  }).buildCss();
246
350
 
247
- // Gọi clsx với các class bạn sử dụng trong project
248
351
  clsx('m10px p20px cBrand dF');
249
352
 
250
- // 2. Lấy nội dung CSS đã sinh
251
- const cssContent = getCssString();
252
-
253
- // 3. Ghi ra file
254
- fs.writeFileSync('./public/styles.css', cssContent);
255
- console.log('✅ CSS file generated!');
353
+ fs.writeFileSync('./public/styles.css', getCssString());
256
354
  ```
257
355
 
258
- > [!NOTE]
259
- > **Custom Config SSR**: Nếu dự án dùng config tùy chỉnh (Theme, Prefix...), hãy đảm bảo khởi tạo `xcss(config)` và truyền instance đó xuống các component (qua Context/Props) thay vì dùng `import { clsx }` mặc định. Điều này đảm bảo Server và Client đồng bộ hash.
356
+ ## Prompt Mẫu Cho AI (Dùng Thẳng)
260
357
 
261
- ### 5. Zero-FOUC (Hybrid Cache)
358
+ Bạn thể đưa block này vào prompt system/project rules:
262
359
 
263
- Để tăng tốc độ hiển thị và ngăn chặn FOUC (Flash of Unstyled Content) hoàn toàn, bạn hãy thêm đoạn script Bootloader này vào thẻ `<head>` của `index.html` (đặt **trước** tất cả các thẻ style/script khác):
264
-
265
- ```html
266
- <script>
267
- // @fwkui/x-css Bootloader
268
- (function(){try{if(typeof window==='undefined')return;var d=localStorage.getItem('xcss_cache_v1');if(!d)return;var j=JSON.parse(d);if(!j||!j.cssText)return;var s=document.getElementById('fwkui');if(!s){s=document.createElement('style');s.id='fwkui';document.head.appendChild(s)}var c='';if(j.cssText.root)c+=j.cssText.root+'\n';for(var k in j.cssText){if(k!=='root')c+=j.cssText[k]+'\n'}s.textContent=c}catch(e){}})()
269
- </script>
360
+ ```markdown
361
+ You are using @fwkui/x-css.
362
+ Generate class names strictly with syntax: [Media]:[Layer][Property][Value][@Selector].
363
+
364
+ Rules:
365
+ 1. Value is required for normal utility classes.
366
+ 2. Layer must be numeric and placed directly before Property (e.g. 3bgWhite).
367
+ 3. Selector must be suffix @Selector (e.g. cBlue@:hover).
368
+ 4. Use dictionary aliases from DICTIONARY.md.
369
+ 5. Keep abbreviation values capitalized when needed (bdN, dF, posA).
370
+ 6. For complex CSS values, use bracket notation, and use ';' as space placeholder:
371
+ w[calc(100%;-;10px)].
372
+ 7. Use opc for opacity, tran for transition, op for object-position.
373
+
374
+ Before final answer:
375
+ - Validate each class can be parsed into {media, layer, property, value, selector}.
376
+ - Avoid invalid forms like bdn, tr0.2s, op0.8, 3:bgWhite, hover:cRed.
270
377
  ```
271
378
 
272
- **Cơ chế hoạt động:**
273
- 1. **Lần đầu truy cập**: Thư viện load bình thường, sinh CSS và tự động lưu vào `localStorage`.
274
- 2. **Lần sau (F5/Revisit)**: Script trên sẽ chạy ngay lập tức (10-50ms), đọc CSS từ cache và inject vào trang trước khi bất kỳ nội dung nào được render.
275
- 3. **Tự động Invalidate**: Nếu bạn thay đổi config (Theme/Breakpoints), cache cũ sẽ tự động bị xóa để tránh lỗi giao diện.
379
+ Template giao việc cho AI thiết kế UI:
276
380
 
277
- > [!TIP]
278
- > **Lưu ý về Theme Custom**:
279
- > Parser dựa vào ký tự viết hoa để tách Property và Value.
280
- > - `theme: { brandColor: '...' }` ⮕ Class: `cBrandColor` (Khuyên dùng).
281
- > - `theme: { 'brand-color': '...' }` ⮕ Class: `cBrand-color` (Vẫn hỗ trợ, chữ `B` làm điểm ngắt).
282
- >
283
- > Tuyệt đối tránh viết thường toàn bộ ( dụ `cbrandcolor`) vì thư viện sẽ không thể phân tách đúng.
381
+ ```markdown
382
+ Thiết kế giao diện [màn hình] bằng @fwkui/x-css.
383
+ Yêu cầu:
384
+ 1. Trả về HTML/JSX hoàn chỉnh.
385
+ 2. Chỉ dùng class theo pháp [Media]:[Layer][Property][Value][@Selector].
386
+ 3. Với value phức tạp, dùng [] và ';' thay cho khoảng trắng.
387
+ 4. Không dùng class sai quy tắc (bdn, tr0.2s, op0.8, hover:cRed...).
388
+ 5. Cuối câu trả lời thêm bảng kiểm:
389
+ - class
390
+ - parsed tuple {media, layer, property, value, selector}
391
+ - css dự kiến
392
+ ```
284
393
 
285
- ---
394
+ ## Checklist QA Trước Khi Build
286
395
 
287
- ## ⚙️ Hỗ trợ AI Coding
396
+ 1. Không còn class sai viết hoa value (`bdn`, `df`, `posa`).
397
+ 2. Không dùng nhầm alias (`op`/`opc`, `tr`/`tran`).
398
+ 3. Các value phức tạp đều bọc `[]`.
399
+ 4. Media custom đã khai báo trong `breakpoints`.
400
+ 5. Không có dạng sai layer/selector (`3:bg`, `hover:cRed`).
401
+ 6. Test parser với ít nhất bộ 10 test vector ở trên.
288
402
 
289
- Để AI (Cursor, Copilot) code chính xác với cú pháp của @fwkui/x-css, hãy thêm rule sau vào `.cursorrules` hoặc prompt:
403
+ ## Tài Liệu Liên Quan
290
404
 
291
- ```markdown
292
- You are using @fwkui/x-css. Follow these rules:
293
- 1. Syntax: `[Media]:[Layer][Property][Value][@Selector]`
294
- 2. Layer prefix: `3bgWhite` (NOT `3:bgWhite`).
295
- 3. Selector suffix: `cRed@:hover` (NOT `hover:cRed`).
296
- 4. Value capitalization: `bdN`, `dF`, `posA` (NOT `bdn`, `df`, `posa`).
297
- 5. Use aliases from DICTIONARY.md (e.g., `m` for margin, `d` for display).
298
- ```
405
+ 1. Dictionary đầy đủ: [DICTIONARY.md](./DICTIONARY.md)
406
+ 2. Source code: [https://github.com/dwork-dev/fwkui](https://github.com/dwork-dev/fwkui)
407
+
408
+ ## License
299
409
 
300
- ---
410
+ Licensed under MIT. See [LICENSE](./LICENSE).
301
411
 
302
- *Verified & Updated at 2026-03-02*
412
+ Updated: 2026-03-02
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fwkui/x-css",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "A fast, modular CSS-in-JS library with utility-first approach and static extraction support.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",