@fwkui/x-css 1.0.10 → 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.
- package/DICTIONARY.md +3 -3
- package/LICENSE +21 -0
- package/README.md +326 -137
- package/dist/index-auto.js +11 -11
- package/dist/index-auto.js.map +1 -1
- package/dist/index-auto.mjs +11 -11
- package/dist/index-auto.mjs.map +1 -1
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/DICTIONARY.md
CHANGED
|
@@ -246,8 +246,8 @@ These abbreviations work with almost all properties (where applicable).
|
|
|
246
246
|
| `ins` | **inline-size** | _(raw values)_ | `ins10px`<br/>*(inline-size: 10px)* |
|
|
247
247
|
| `is` | **isolation** | `i`: isolate | `isI`<br/>*(isolation: isolate)* |
|
|
248
248
|
| `jc` | **justify-content** | `c`: center<br/> `s`: start<br/> `e`: end<br/> `fs`: flex-start<br/> `fe`: flex-end<br/> `l`: left<br/> `r`: right<br/> `n`: normal<br/> `sp`: space-between<br/> `sa`: space-around<br/> `se`: space-evenly<br/> `st`: stretch<br/> `sc`: safe center<br/> `uc`: unsafe center | `jcC`<br/>*(justify-content: center)* |
|
|
249
|
-
| `ji` | **justify-items** | `
|
|
250
|
-
| `js` | **justify-self** | `
|
|
249
|
+
| `ji` | **justify-items** | `c`: center<br/> `s`: start<br/> `e`: end<br/> `fs`: flex-start<br/> `fe`: flex-end<br/> `l`: left<br/> `r`: right<br/> `n`: normal<br/> `sp`: space-between<br/> `sa`: space-around<br/> `se`: space-evenly<br/> `st`: stretch<br/> `sc`: safe center<br/> `uc`: unsafe center | `jiC`<br/>*(justify-items: center)* |
|
|
250
|
+
| `js` | **justify-self** | `c`: center<br/> `s`: start<br/> `e`: end<br/> `fs`: flex-start<br/> `fe`: flex-end<br/> `l`: left<br/> `r`: right<br/> `n`: normal<br/> `sp`: space-between<br/> `sa`: space-around<br/> `se`: space-evenly<br/> `st`: stretch<br/> `sc`: safe center<br/> `uc`: unsafe center | `jsC`<br/>*(justify-self: center)* |
|
|
251
251
|
| `l` | **left** | _(raw values)_ | `l10px`<br/>*(left: 10px)* |
|
|
252
252
|
| `lbrk` | **line-break** | _(raw values)_ | `lbrkUn`<br/>*(line-break: unset)* |
|
|
253
253
|
| `lh` | **line-height** | _(raw values)_ | `lh10px`<br/>*(line-height: 10px)* |
|
|
@@ -335,7 +335,7 @@ These abbreviations work with almost all properties (where applicable).
|
|
|
335
335
|
| `pgba` | **page-break-after** | _(raw values)_ | `pgbaPage`<br/>*(page-break-after: page)* |
|
|
336
336
|
| `pgbb` | **page-break-before** | _(raw values)_ | `pgbbPage`<br/>*(page-break-before: page)* |
|
|
337
337
|
| `pgbi` | **page-break-inside** | _(raw values)_ | `pgbiPage`<br/>*(page-break-inside: page)* |
|
|
338
|
-
| `pi` | **padding-inline** | `s`: start<br/> `
|
|
338
|
+
| `pi` | **padding-inline** | `c`: center<br/> `s`: start<br/> `e`: end<br/> `fs`: flex-start<br/> `fe`: flex-end<br/> `l`: left<br/> `r`: right<br/> `n`: normal<br/> `sp`: space-between<br/> `sa`: space-around<br/> `se`: space-evenly<br/> `st`: stretch<br/> `sc`: safe center<br/> `uc`: unsafe center | `piC`<br/>*(padding-inline: center)* |
|
|
339
339
|
| `pie` | **padding-inline-end** | _(raw values)_ | `pie10px`<br/>*(padding-inline-end: 10px)* |
|
|
340
340
|
| `pis` | **padding-inline-start** | _(raw values)_ | `pis10px`<br/>*(padding-inline-start: 10px)* |
|
|
341
341
|
| `pl` | **padding-left** | _(raw values)_ | `pl10px`<br/>*(padding-left: 10px)* |
|
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,103 +1,230 @@
|
|
|
1
|
-
# @fwkui/x-css
|
|
1
|
+
# @fwkui/x-css
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@fwkui/x-css` là utility CSS engine siêu nhẹ, parse class theo cú pháp ngắn và 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
|

|
|
6
11
|

|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
## Cài Đặt Nhanh
|
|
9
14
|
|
|
10
|
-
|
|
15
|
+
### 1) NPM
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
```bash
|
|
18
|
+
npm install @fwkui/x-css
|
|
19
|
+
```
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
- `m10px` ⮕ `margin: 10px`
|
|
18
|
-
- `cRed` ⮕ `color: red`
|
|
19
|
-
- `sm:p20px` ⮕ `@media (min-width: 576px) { padding: 20px }`
|
|
20
|
-
- `3bgWhite` ⮕ `@layer l3 { background: white }`
|
|
21
|
-
- `cBlue@:hover` ⮕ `.class:hover { color: blue }`
|
|
21
|
+
### 2) Dùng trực tiếp qua URL (không cần bundler)
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
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`.
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
| :--- | :--- | :--- |
|
|
28
|
-
| **Số (0-9)** | `w100px` | `w` (width) \| `100px` |
|
|
29
|
-
| **Chữ Hoa (A-Z)** | `dFlex` | `d` (display) \| `Flex` |
|
|
30
|
-
| **Dấu gạch ngang + Số** | `m-10px` | `m` (margin) \| `-10px` |
|
|
27
|
+
Option A: chủ động khởi tạo
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
>
|
|
29
|
+
```html
|
|
30
|
+
<script type="module">
|
|
31
|
+
import xcss from 'https://unpkg.com/@fwkui/x-css@latest/dist/index.mjs';
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
xcss.cssObserve(document, {
|
|
34
|
+
dictionaryImport: true
|
|
35
|
+
});
|
|
36
|
+
</script>
|
|
37
|
+
```
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
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).
|
|
39
|
+
Option B: auto observe khi import
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- **Spacing**: `m` (margin), `p` (padding), `w` (width), `h` (height).
|
|
44
|
-
- **Styling**: `c` (color), `bg` (background), `bd` (border), `op` (opacity).
|
|
45
|
-
- **Typography**: `fz` (font-size), `fw` (font-weight), `ta` (text-align).
|
|
41
|
+
```html
|
|
42
|
+
<script type="module" src="https://unpkg.com/@fwkui/x-css@latest/dist/index-auto.mjs"></script>
|
|
43
|
+
```
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
CDN thay thế:
|
|
46
|
+
`https://cdn.jsdelivr.net/npm/@fwkui/x-css@latest/dist/index.mjs`
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
## Dùng Trong 60 Giây
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
import xcss from '@fwkui/x-css';
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
xcss.cssObserve(document);
|
|
54
|
+
```
|
|
52
55
|
|
|
53
|
-
### Cách 1: Dùng qua CDN (Khuyên dùng cho thử nghiệm nhanh)
|
|
54
|
-
Thêm thẻ script sau vào `index.html`:
|
|
55
56
|
```html
|
|
56
|
-
<
|
|
57
|
+
<button class="dF aiC jcC p10px;16px bdN bdra8px bgc#0a64e8 cWhite">
|
|
58
|
+
Đăng nhập
|
|
59
|
+
</button>
|
|
57
60
|
```
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
## Contract Cú Pháp
|
|
63
|
+
|
|
64
|
+
Mỗi utility class theo form:
|
|
65
|
+
|
|
66
|
+
`[Media]:[Layer][Property][Value][@Selector]`
|
|
67
|
+
|
|
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`.
|
|
74
|
+
|
|
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
|
+
});
|
|
62
116
|
```
|
|
63
117
|
|
|
64
|
-
|
|
118
|
+
Dùng class: `tablet:dB`.
|
|
65
119
|
|
|
66
|
-
|
|
120
|
+
### Layer Mặc Định
|
|
67
121
|
|
|
68
|
-
|
|
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.
|
|
69
126
|
|
|
70
|
-
|
|
127
|
+
## Quy Tắc Điểm Ngắt Đầy Đủ (Theo Parser Hiện Tại)
|
|
71
128
|
|
|
72
|
-
|
|
73
|
-
|
|
129
|
+
Mục tiêu là tách class thành tuple:
|
|
130
|
+
`{ media, layer, property, value, selector }`
|
|
74
131
|
|
|
75
|
-
|
|
76
|
-
|
|
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 ' '
|
|
77
163
|
```
|
|
78
164
|
|
|
79
|
-
|
|
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ụ:
|
|
208
|
+
|
|
80
209
|
```html
|
|
81
|
-
<div class="
|
|
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>
|
|
82
214
|
```
|
|
83
215
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
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
|
|
87
217
|
|
|
88
218
|
```jsx
|
|
89
219
|
import { clsx } from '@fwkui/x-css';
|
|
90
220
|
|
|
91
|
-
function Button({ primary, children }) {
|
|
221
|
+
export function Button({ primary, children }) {
|
|
92
222
|
return (
|
|
93
|
-
<button
|
|
223
|
+
<button
|
|
94
224
|
className={clsx(
|
|
95
|
-
'p10px;
|
|
96
|
-
'
|
|
97
|
-
'
|
|
98
|
-
'cWhite', // color: white
|
|
99
|
-
primary ? 'bgBlue' : 'bgGray',
|
|
100
|
-
'op0.8@:hover' // Opacity 0.8 on hover
|
|
225
|
+
'dF aiC jcC p10px;16px bdN bdra8px tran0.2s',
|
|
226
|
+
primary ? 'bgc#0a64e8 cWhite' : 'bgc#e5e7eb c#111827',
|
|
227
|
+
'opc0.9@:hover'
|
|
101
228
|
)}
|
|
102
229
|
>
|
|
103
230
|
{children}
|
|
@@ -106,118 +233,180 @@ function Button({ primary, children }) {
|
|
|
106
233
|
}
|
|
107
234
|
```
|
|
108
235
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
Bạn có thể truyền object config khi khởi tạo:
|
|
236
|
+
## Cấu Hình
|
|
112
237
|
|
|
113
|
-
```
|
|
238
|
+
```js
|
|
114
239
|
import xcss from '@fwkui/x-css';
|
|
115
240
|
|
|
116
241
|
xcss.cssObserve(document, {
|
|
117
|
-
// Thêm màu sắc hoặc giá trị custom
|
|
118
242
|
theme: {
|
|
119
|
-
brand: '#
|
|
120
|
-
|
|
243
|
+
brand: '#0a64e8',
|
|
244
|
+
danger: '#ef4444'
|
|
121
245
|
},
|
|
122
|
-
// Thêm breakpoint tùy chỉnh
|
|
123
246
|
breakpoints: [
|
|
124
247
|
{ tablet: 'screen and (min-width: 768px)' }
|
|
125
248
|
],
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// [New] Thêm tiền tố (Prefix) để tránh xung đột
|
|
130
|
-
prefix: 'fk-' // Chỉ xử lý các class bắt đầu bằng 'fk-'
|
|
249
|
+
base: 'body{margin:0;font-family:system-ui,sans-serif;}',
|
|
250
|
+
prefix: 'fk-',
|
|
251
|
+
dictionaryImport: true
|
|
131
252
|
});
|
|
132
253
|
```
|
|
133
254
|
|
|
134
|
-
Sau đó
|
|
135
|
-
|
|
255
|
+
Sau đó dùng class: `fk-cBrand fk-tablet:dB`.
|
|
256
|
+
|
|
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.
|
|
136
261
|
|
|
137
|
-
|
|
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);
|
|
268
|
+
observe();
|
|
269
|
+
```
|
|
138
270
|
|
|
139
|
-
|
|
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
|
+
```
|
|
140
323
|
|
|
141
|
-
|
|
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
|
|
331
|
+
|
|
332
|
+
SSR:
|
|
333
|
+
|
|
334
|
+
```js
|
|
142
335
|
import { getCss } from '@fwkui/x-css';
|
|
143
336
|
|
|
144
|
-
// Trong file layout/server entry
|
|
145
337
|
const styles = getCss();
|
|
146
|
-
|
|
147
|
-
// Inject HTML
|
|
148
|
-
// Inject HTML
|
|
149
338
|
// <style dangerouslySetInnerHTML={{ __html: styles }} />
|
|
150
339
|
```
|
|
151
340
|
|
|
152
|
-
|
|
341
|
+
Static extraction:
|
|
153
342
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// build-css.js
|
|
158
|
-
const fs = require('fs');
|
|
159
|
-
const xcss = require('@fwkui/x-css');
|
|
343
|
+
```js
|
|
344
|
+
import xcss from '@fwkui/x-css';
|
|
345
|
+
import fs from 'node:fs';
|
|
160
346
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// Ở đây ví dụ gọi thủ công:
|
|
164
|
-
const { clsx, getCssString } = xcss({
|
|
165
|
-
theme: { brand: '#ff0000' } // Cấu hình (nếu có)
|
|
347
|
+
const { clsx, getCssString } = xcss.css({
|
|
348
|
+
theme: { brand: '#0a64e8' }
|
|
166
349
|
}).buildCss();
|
|
167
350
|
|
|
168
|
-
|
|
169
|
-
clsx('m10px p20px cBrand dFlex');
|
|
351
|
+
clsx('m10px p20px cBrand dF');
|
|
170
352
|
|
|
171
|
-
|
|
172
|
-
const cssContent = getCssString();
|
|
173
|
-
|
|
174
|
-
// 3. Ghi ra file
|
|
175
|
-
fs.writeFileSync('./public/styles.css', cssContent);
|
|
176
|
-
console.log('✅ CSS file generated!');
|
|
353
|
+
fs.writeFileSync('./public/styles.css', getCssString());
|
|
177
354
|
```
|
|
178
355
|
|
|
179
|
-
|
|
180
|
-
> **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.
|
|
181
|
-
|
|
182
|
-
### 5. Zero-FOUC (Hybrid Cache) ⚡
|
|
356
|
+
## Prompt Mẫu Cho AI (Dùng Thẳng)
|
|
183
357
|
|
|
184
|
-
|
|
358
|
+
Bạn có thể đưa block này vào prompt system/project rules:
|
|
185
359
|
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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.
|
|
191
377
|
```
|
|
192
378
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
379
|
+
Template giao việc cho AI thiết kế UI:
|
|
380
|
+
|
|
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 cú 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
|
+
```
|
|
197
393
|
|
|
198
|
-
|
|
199
|
-
> **Lưu ý về Theme Custom**:
|
|
200
|
-
> Parser dựa vào ký tự viết hoa để tách Property và Value.
|
|
201
|
-
> - `theme: { brandColor: '...' }` ⮕ Class: `cBrandColor` (Khuyên dùng).
|
|
202
|
-
> - `theme: { 'brand-color': '...' }` ⮕ Class: `cBrand-color` (Vẫn hỗ trợ, chữ `B` làm điểm ngắt).
|
|
203
|
-
>
|
|
204
|
-
> Tuyệt đối tránh viết thường toàn bộ (ví dụ `cbrandcolor`) vì thư viện sẽ không thể phân tách đúng.
|
|
394
|
+
## Checklist QA Trước Khi Build
|
|
205
395
|
|
|
206
|
-
|
|
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.
|
|
207
402
|
|
|
208
|
-
##
|
|
403
|
+
## Tài Liệu Liên Quan
|
|
209
404
|
|
|
210
|
-
|
|
405
|
+
1. Dictionary đầy đủ: [DICTIONARY.md](./DICTIONARY.md)
|
|
406
|
+
2. Source code: [https://github.com/dwork-dev/fwkui](https://github.com/dwork-dev/fwkui)
|
|
211
407
|
|
|
212
|
-
|
|
213
|
-
You are using @fwkui/x-css. Follow these rules:
|
|
214
|
-
1. Syntax: `[Media]:[Layer][Property][Value][@Selector]`
|
|
215
|
-
2. Layer prefix: `3bgWhite` (NOT `3:bgWhite`).
|
|
216
|
-
3. Selector suffix: `cRed@:hover` (NOT `hover:cRed`).
|
|
217
|
-
4. Value capitalization: `dFlex`, `posAbs`.
|
|
218
|
-
5. Use aliases from DICTIONARY.md (e.g., `m` for margin, `d` for display).
|
|
219
|
-
```
|
|
408
|
+
## License
|
|
220
409
|
|
|
221
|
-
|
|
410
|
+
Licensed under MIT. See [LICENSE](./LICENSE).
|
|
222
411
|
|
|
223
|
-
|
|
412
|
+
Updated: 2026-03-02
|