gladvn 0.2.19 → 0.2.20

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 (202) hide show
  1. package/bin/cli.js +15 -5
  2. package/dist/components/macro/accordion-preset.d.ts.map +1 -1
  3. package/dist/components/macro/alert-dialog-preset.d.ts.map +1 -1
  4. package/dist/components/macro/alert-preset.d.ts.map +1 -1
  5. package/dist/components/macro/avatar-preset.d.ts.map +1 -1
  6. package/dist/components/macro/breadcrumb-preset.d.ts.map +1 -1
  7. package/dist/components/macro/card-preset.d.ts.map +1 -1
  8. package/dist/components/macro/carousel-preset.d.ts.map +1 -1
  9. package/dist/components/macro/checkbox-preset.d.ts.map +1 -1
  10. package/dist/components/macro/combobox-preset.d.ts.map +1 -1
  11. package/dist/components/macro/command-preset.d.ts +1 -1
  12. package/dist/components/macro/command-preset.d.ts.map +1 -1
  13. package/dist/components/macro/date-picker.d.ts.map +1 -1
  14. package/dist/components/macro/dialog-preset.d.ts.map +1 -1
  15. package/dist/components/macro/empty-preset.d.ts +1 -1
  16. package/dist/components/macro/empty-preset.d.ts.map +1 -1
  17. package/dist/components/macro/field-preset.d.ts.map +1 -1
  18. package/dist/components/macro/input-otp-preset.d.ts.map +1 -1
  19. package/dist/components/macro/input-preset.d.ts.map +1 -1
  20. package/dist/components/macro/pagination-preset.d.ts.map +1 -1
  21. package/dist/components/macro/progress-preset.d.ts.map +1 -1
  22. package/dist/components/macro/radio-group-preset.d.ts.map +1 -1
  23. package/dist/components/macro/select-preset.d.ts.map +1 -1
  24. package/dist/components/macro/slider-preset.d.ts.map +1 -1
  25. package/dist/components/macro/switch-preset.d.ts.map +1 -1
  26. package/dist/components/macro/tabs-preset.d.ts.map +1 -1
  27. package/dist/components/micro/alert-dialog.d.ts +4 -4
  28. package/dist/components/micro/alert-dialog.d.ts.map +1 -1
  29. package/dist/components/micro/combobox.d.ts +4 -3
  30. package/dist/components/micro/combobox.d.ts.map +1 -1
  31. package/dist/components/micro/context-menu.d.ts +4 -6
  32. package/dist/components/micro/context-menu.d.ts.map +1 -1
  33. package/dist/components/micro/dialog.d.ts +4 -3
  34. package/dist/components/micro/dialog.d.ts.map +1 -1
  35. package/dist/components/micro/drawer.d.ts +4 -3
  36. package/dist/components/micro/drawer.d.ts.map +1 -1
  37. package/dist/components/micro/dropdown-menu.d.ts +7 -4
  38. package/dist/components/micro/dropdown-menu.d.ts.map +1 -1
  39. package/dist/components/micro/hover-card.d.ts +4 -3
  40. package/dist/components/micro/hover-card.d.ts.map +1 -1
  41. package/dist/components/micro/menubar.d.ts +2 -3
  42. package/dist/components/micro/menubar.d.ts.map +1 -1
  43. package/dist/components/micro/navigation-menu.d.ts +4 -3
  44. package/dist/components/micro/navigation-menu.d.ts.map +1 -1
  45. package/dist/components/micro/popover.d.ts +4 -3
  46. package/dist/components/micro/popover.d.ts.map +1 -1
  47. package/dist/components/micro/select.d.ts +4 -3
  48. package/dist/components/micro/select.d.ts.map +1 -1
  49. package/dist/components/micro/sheet.d.ts +3 -3
  50. package/dist/components/micro/sheet.d.ts.map +1 -1
  51. package/dist/components/micro/tooltip.d.ts +4 -3
  52. package/dist/components/micro/tooltip.d.ts.map +1 -1
  53. package/dist/dev/showcase/accordion.d.ts.map +1 -1
  54. package/dist/dev/showcase/alert-dialog.d.ts.map +1 -1
  55. package/dist/dev/showcase/alert.d.ts.map +1 -1
  56. package/dist/dev/showcase/aspect-ratio.d.ts.map +1 -1
  57. package/dist/dev/showcase/avatar.d.ts.map +1 -1
  58. package/dist/dev/showcase/badge.d.ts.map +1 -1
  59. package/dist/dev/showcase/breadcrumb.d.ts.map +1 -1
  60. package/dist/dev/showcase/button.d.ts.map +1 -1
  61. package/dist/dev/showcase/calendar.d.ts.map +1 -1
  62. package/dist/dev/showcase/card.d.ts.map +1 -1
  63. package/dist/dev/showcase/carousel.d.ts.map +1 -1
  64. package/dist/dev/showcase/chart.d.ts.map +1 -1
  65. package/dist/dev/showcase/checkbox.d.ts.map +1 -1
  66. package/dist/dev/showcase/collapsible.d.ts.map +1 -1
  67. package/dist/dev/showcase/combobox.d.ts.map +1 -1
  68. package/dist/dev/showcase/command.d.ts.map +1 -1
  69. package/dist/dev/showcase/context-menu.d.ts.map +1 -1
  70. package/dist/dev/showcase/dialog.d.ts.map +1 -1
  71. package/dist/dev/showcase/drawer.d.ts.map +1 -1
  72. package/dist/dev/showcase/dropdown-menu.d.ts.map +1 -1
  73. package/dist/dev/showcase/empty.d.ts.map +1 -1
  74. package/dist/dev/showcase/hover-card.d.ts.map +1 -1
  75. package/dist/dev/showcase/input-group.d.ts.map +1 -1
  76. package/dist/dev/showcase/input-otp.d.ts.map +1 -1
  77. package/dist/dev/showcase/input.d.ts.map +1 -1
  78. package/dist/dev/showcase/item.d.ts.map +1 -1
  79. package/dist/dev/showcase/kbd.d.ts.map +1 -1
  80. package/dist/dev/showcase/label.d.ts.map +1 -1
  81. package/dist/dev/showcase/menubar.d.ts.map +1 -1
  82. package/dist/dev/showcase/navigation-menu.d.ts.map +1 -1
  83. package/dist/dev/showcase/overview.d.ts.map +1 -1
  84. package/dist/dev/showcase/pagination.d.ts.map +1 -1
  85. package/dist/dev/showcase/popover.d.ts.map +1 -1
  86. package/dist/dev/showcase/progress.d.ts.map +1 -1
  87. package/dist/dev/showcase/radio-group.d.ts.map +1 -1
  88. package/dist/dev/showcase/resizable.d.ts.map +1 -1
  89. package/dist/dev/showcase/scroll-area.d.ts.map +1 -1
  90. package/dist/dev/showcase/select.d.ts.map +1 -1
  91. package/dist/dev/showcase/separator.d.ts.map +1 -1
  92. package/dist/dev/showcase/sheet.d.ts.map +1 -1
  93. package/dist/dev/showcase/sidebar.d.ts.map +1 -1
  94. package/dist/dev/showcase/skeleton.d.ts.map +1 -1
  95. package/dist/dev/showcase/slider.d.ts.map +1 -1
  96. package/dist/dev/showcase/sonner.d.ts.map +1 -1
  97. package/dist/dev/showcase/spinner.d.ts.map +1 -1
  98. package/dist/dev/showcase/switch.d.ts.map +1 -1
  99. package/dist/dev/showcase/table.d.ts.map +1 -1
  100. package/dist/dev/showcase/tabs.d.ts.map +1 -1
  101. package/dist/dev/showcase/textarea.d.ts.map +1 -1
  102. package/dist/dev/showcase/theme-provider.d.ts.map +1 -1
  103. package/dist/dev/showcase/toast.d.ts.map +1 -1
  104. package/dist/dev/showcase/toggle-group.d.ts.map +1 -1
  105. package/dist/dev/showcase/toggle.d.ts.map +1 -1
  106. package/dist/dev/showcase/tooltip.d.ts.map +1 -1
  107. package/dist/index.cjs +256 -256
  108. package/dist/index.cjs.map +1 -1
  109. package/dist/index.js +257 -244
  110. package/dist/index.js.map +1 -1
  111. package/package.json +1 -1
  112. package/src/components/macro/accordion-preset.test.tsx +1 -2
  113. package/src/components/macro/accordion-preset.tsx +1 -2
  114. package/src/components/macro/alert-dialog-preset.tsx +7 -12
  115. package/src/components/macro/alert-preset.tsx +1 -2
  116. package/src/components/macro/avatar-preset.tsx +2 -4
  117. package/src/components/macro/breadcrumb-preset.tsx +1 -2
  118. package/src/components/macro/card-preset.tsx +1 -2
  119. package/src/components/macro/carousel-preset.tsx +1 -2
  120. package/src/components/macro/checkbox-preset.tsx +1 -2
  121. package/src/components/macro/combobox-preset.tsx +8 -14
  122. package/src/components/macro/command-preset.tsx +18 -24
  123. package/src/components/macro/date-picker.tsx +14 -23
  124. package/src/components/macro/dialog-preset.tsx +45 -49
  125. package/src/components/macro/empty-preset.tsx +2 -4
  126. package/src/components/macro/field-preset.tsx +1 -2
  127. package/src/components/macro/input-otp-preset.tsx +1 -2
  128. package/src/components/macro/input-preset.tsx +1 -2
  129. package/src/components/macro/pagination-preset.tsx +3 -6
  130. package/src/components/macro/progress-preset.tsx +1 -2
  131. package/src/components/macro/radio-group-preset.tsx +2 -4
  132. package/src/components/macro/select-preset.tsx +7 -12
  133. package/src/components/macro/slider-preset.tsx +1 -2
  134. package/src/components/macro/switch-preset.tsx +1 -2
  135. package/src/components/macro/tabs-preset.tsx +1 -2
  136. package/src/components/micro/alert-dialog.tsx +16 -14
  137. package/src/components/micro/combobox.tsx +28 -22
  138. package/src/components/micro/context-menu.tsx +52 -36
  139. package/src/components/micro/dialog.tsx +23 -19
  140. package/src/components/micro/drawer.tsx +23 -19
  141. package/src/components/micro/dropdown-menu.tsx +23 -18
  142. package/src/components/micro/hover-card.tsx +31 -23
  143. package/src/components/micro/menubar.tsx +0 -8
  144. package/src/components/micro/navigation-menu.tsx +26 -19
  145. package/src/components/micro/popover.tsx +31 -25
  146. package/src/components/micro/select.tsx +36 -29
  147. package/src/components/micro/sheet.tsx +36 -31
  148. package/src/components/micro/tooltip.tsx +28 -23
  149. package/src/dev/showcase/accordion.tsx +47 -94
  150. package/src/dev/showcase/alert-dialog.tsx +27 -39
  151. package/src/dev/showcase/alert.tsx +5 -10
  152. package/src/dev/showcase/aspect-ratio.tsx +1 -2
  153. package/src/dev/showcase/avatar.tsx +5 -10
  154. package/src/dev/showcase/badge.tsx +2 -4
  155. package/src/dev/showcase/breadcrumb.tsx +8 -11
  156. package/src/dev/showcase/button.tsx +2 -4
  157. package/src/dev/showcase/calendar.tsx +5 -10
  158. package/src/dev/showcase/card.tsx +4 -8
  159. package/src/dev/showcase/carousel.tsx +40 -80
  160. package/src/dev/showcase/chart.tsx +7 -16
  161. package/src/dev/showcase/checkbox.tsx +7 -14
  162. package/src/dev/showcase/collapsible.tsx +3 -6
  163. package/src/dev/showcase/combobox.tsx +27 -43
  164. package/src/dev/showcase/command.tsx +4 -8
  165. package/src/dev/showcase/context-menu.tsx +13 -19
  166. package/src/dev/showcase/dialog.tsx +19 -33
  167. package/src/dev/showcase/drawer.tsx +19 -27
  168. package/src/dev/showcase/dropdown-menu.tsx +19 -29
  169. package/src/dev/showcase/empty.tsx +3 -6
  170. package/src/dev/showcase/hover-card.tsx +24 -36
  171. package/src/dev/showcase/input-group.tsx +4 -8
  172. package/src/dev/showcase/input-otp.tsx +5 -10
  173. package/src/dev/showcase/input.tsx +5 -10
  174. package/src/dev/showcase/item.tsx +3 -6
  175. package/src/dev/showcase/kbd.tsx +12 -16
  176. package/src/dev/showcase/label.tsx +2 -4
  177. package/src/dev/showcase/menubar.tsx +17 -19
  178. package/src/dev/showcase/navigation-menu.tsx +12 -15
  179. package/src/dev/showcase/overview.tsx +14 -27
  180. package/src/dev/showcase/pagination.tsx +4 -8
  181. package/src/dev/showcase/popover.tsx +21 -27
  182. package/src/dev/showcase/progress.tsx +4 -8
  183. package/src/dev/showcase/radio-group.tsx +8 -16
  184. package/src/dev/showcase/resizable.tsx +2 -4
  185. package/src/dev/showcase/scroll-area.tsx +1 -2
  186. package/src/dev/showcase/select.tsx +27 -39
  187. package/src/dev/showcase/separator.tsx +1 -2
  188. package/src/dev/showcase/sheet.tsx +22 -33
  189. package/src/dev/showcase/sidebar.tsx +2 -4
  190. package/src/dev/showcase/skeleton.tsx +1 -2
  191. package/src/dev/showcase/slider.tsx +5 -10
  192. package/src/dev/showcase/sonner.tsx +18 -38
  193. package/src/dev/showcase/spinner.tsx +2 -4
  194. package/src/dev/showcase/switch.tsx +6 -12
  195. package/src/dev/showcase/table.tsx +3 -6
  196. package/src/dev/showcase/tabs.tsx +20 -40
  197. package/src/dev/showcase/textarea.tsx +5 -10
  198. package/src/dev/showcase/theme-provider.tsx +384 -545
  199. package/src/dev/showcase/toast.tsx +4 -10
  200. package/src/dev/showcase/toggle-group.tsx +4 -8
  201. package/src/dev/showcase/toggle.tsx +2 -4
  202. package/src/dev/showcase/tooltip.tsx +34 -43
@@ -2,61 +2,60 @@ import { useState } from "react";
2
2
 
3
3
  import { MoonIcon, SunIcon } from "lucide-react";
4
4
 
5
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
6
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu";
7
+ import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
8
+ import { Select as SelectPrimitive } from "@base-ui/react/select";
9
+ import { Dialog as SheetPrimitive } from "@base-ui/react/dialog";
10
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
11
+
5
12
  import { Button } from "../../components/micro/button";
6
13
  import {
7
14
  Dialog,
8
15
  DialogContent,
9
16
  DialogDescription,
10
17
  DialogHeader,
11
- DialogPortal,
18
+
12
19
  DialogTitle,
13
- DialogTrigger,
14
- } from "../../components/micro/dialog";
20
+ DialogTrigger } from "../../components/micro/dialog";
15
21
  import {
16
22
  DropdownMenu,
17
23
  DropdownMenuContent,
18
24
  DropdownMenuGroup,
19
25
  DropdownMenuItem,
20
26
  DropdownMenuLabel,
21
- DropdownMenuPortal,
27
+
22
28
  DropdownMenuSeparator,
23
- DropdownMenuTrigger,
24
- } from "../../components/micro/dropdown-menu";
29
+ DropdownMenuTrigger } from "../../components/micro/dropdown-menu";
25
30
  import {
26
31
  Popover,
27
32
  PopoverContent,
28
- PopoverPortal,
29
- PopoverTrigger,
30
- } from "../../components/micro/popover";
33
+
34
+ PopoverTrigger } from "../../components/micro/popover";
31
35
  import {
32
36
  Select,
33
37
  SelectContent,
34
38
  SelectItem,
35
- SelectPortal,
39
+
36
40
  SelectTrigger,
37
- SelectValue,
38
- } from "../../components/micro/select";
41
+ SelectValue } from "../../components/micro/select";
39
42
  import {
40
43
  Sheet,
41
44
  SheetContent,
42
45
  SheetHeader,
43
- SheetPortal,
46
+
44
47
  SheetTitle,
45
- SheetTrigger,
46
- } from "../../components/micro/sheet";
48
+ SheetTrigger } from "../../components/micro/sheet";
47
49
  import {
48
50
  type ThemeMode,
49
51
  ThemeProvider,
50
- ThemeWrapper,
51
- useTheme,
52
- } from "../../components/micro/theme-provider";
52
+ useTheme } from "../../components/micro/theme-provider";
53
53
  import {
54
54
  Tooltip,
55
55
  TooltipContent,
56
- TooltipPortal,
56
+
57
57
  TooltipProvider,
58
- TooltipTrigger,
59
- } from "../../components/micro/tooltip";
58
+ TooltipTrigger } from "../../components/micro/tooltip";
60
59
  import {
61
60
  DocsCode,
62
61
  DocsH3,
@@ -64,8 +63,7 @@ import {
64
63
  ExampleSection,
65
64
  SectionHeader,
66
65
  Showcase,
67
- ShowcaseDocs,
68
- } from "../../dev/components/showcase";
66
+ ShowcaseDocs } from "../../dev/components/showcase";
69
67
 
70
68
  // ──────────────────────────────────────────────────────────
71
69
  // Demo helpers
@@ -120,7 +118,7 @@ function ThemeProviderMicroShowcase() {
120
118
  <div className="space-y-10">
121
119
  <SectionHeader
122
120
  title="ThemeProvider"
123
- description="Context provider điều phối dark/light mode cho toàn bộ component tree. Hỗ trợ hai chế độ: Uncontrolled (tự quản lý state) và Controlled (state do component cha sở hữu). Dùng ThemeWrapper để tunnel theme qua Portal boundary."
121
+ description="Context provider điều phối dark/light mode cho toàn bộ component tree. Hỗ trợ hai chế độ: Uncontrolled (tự quản lý state) và Controlled (state do component cha sở hữu). Dùng Library *Portal để tunnel theme qua Portal boundary tự động."
124
122
  />
125
123
 
126
124
  {/* ── Uncontrolled ── */}
@@ -221,7 +219,7 @@ function ThemeProviderMicroShowcase() {
221
219
  <div className="pt-16 pb-6">
222
220
  <SectionHeader
223
221
  title="ThemeWrapper & Portal Tunnels"
224
- description="ThemeWrapper tunnel theme class qua Portal boundary (Dialog, Tooltip, Popover...) bằng cách re-apply CSS variable vào container mới. Dưới đây so sánh trực tiếp giữa portal không ThemeWrapper khi theme cục bộ (ThemeProvider cha) đang ở Dark Mode."
222
+ description="So sánh trực tiếp: cột trái dùng Base UI primitive Portal trực tiếp (không ThemeWrapper mất dark mode), cột phải dùng Library Content (tích hợp sẵn ThemeWrapper từ v0.2.20 đúng màu tự động)."
225
223
  />
226
224
  </div>
227
225
 
@@ -229,101 +227,76 @@ function ThemeProviderMicroShowcase() {
229
227
  <ExampleSection
230
228
  fullWidth
231
229
  label="Tooltip"
232
- description="Tooltip được định vị gần trigger hiển thị khi hover. Không có ThemeWrapper, tooltip của bạn sẽ trắng toát giữa một dark section dười đây bằng chứng trực quan."
233
- codeString={`<ThemeProvider defaultMode="dark">
234
- <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
235
- <div className="flex-1 flex flex-col items-center gap-4">
236
- <span className="text-sm font-medium text-muted-foreground">
237
- Không dùng ThemeWrapper
238
- </span>
239
- <TooltipProvider>
240
- <Tooltip>
241
- <TooltipTrigger
242
- render={
243
- <Button variant="solid">Hover tôi</Button>
244
- }
245
- />
246
- <TooltipPortal>
247
- <TooltipContent
248
- sideOffset={8}
249
- className="bg-card text-card-foreground border border-border shadow-md"
250
- >
251
- Trắng toát! Lạc quẻ với nền đen.
252
- </TooltipContent>
253
- </TooltipPortal>
254
- </Tooltip>
255
- </TooltipProvider>
256
- </div>
257
-
258
- <div className="flex-1 flex flex-col items-center gap-4">
259
- <span className="text-sm font-medium text-muted-foreground">
260
- Có ThemeWrapper
261
- </span>
262
- <TooltipProvider>
263
- <Tooltip>
264
- <TooltipTrigger
265
- render={
266
- <Button variant="solid">Hover tôi</Button>
267
- }
268
- />
269
- <TooltipPortal>
270
- <ThemeWrapper>
271
- <TooltipContent
272
- sideOffset={8}
273
- className="bg-card text-card-foreground border border-border shadow-md"
274
- >
275
- Màu Tối! Đồng bộ với provider cha.
276
- </TooltipContent>
277
- </ThemeWrapper>
278
- </TooltipPortal>
279
- </Tooltip>
280
- </TooltipProvider>
281
- </div>
282
- </div>
283
- </ThemeProvider>`}
230
+ description="Cột trái dùng TooltipPrimitive.Portal trực tiếp từ @base-ui/react không có ThemeWrapper nên tooltip trắng toát giữa dark section. Cột phải dùng của thư viện tự động đúng màu."
231
+ codeString={`// ❌ Base UI primitive Portal — không có ThemeWrapper → mất dark mode
232
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
233
+
234
+ <ThemeProvider defaultMode="dark">
235
+ <TooltipPrimitive.Provider>
236
+ <TooltipPrimitive.Root>
237
+ <TooltipPrimitive.Trigger>Hover tôi</TooltipPrimitive.Trigger>
238
+ <TooltipPrimitive.Portal>
239
+ {/* Thoát khỏi DOM tree → mất .dark class → CSS vars sai */}
240
+ <TooltipPrimitive.Positioner>
241
+ <TooltipPrimitive.Popup>Trắng toát!</TooltipPrimitive.Popup>
242
+ </TooltipPrimitive.Positioner>
243
+ </TooltipPrimitive.Portal>
244
+ </TooltipPrimitive.Root>
245
+ </TooltipPrimitive.Provider>
246
+ </ThemeProvider>
247
+
248
+ // ✅ Library Content — ThemeWrapper tích hợp sẵn từ v0.2.20
249
+ <ThemeProvider defaultMode="dark">
250
+ <TooltipProvider>
251
+ <Tooltip>
252
+ <TooltipTrigger render={<Button>Hover tôi</Button>} />
253
+
254
+ {/* ← ThemeWrapper đã được bọc bên trong */}
255
+ <TooltipContent>Màu tối đồng bộ!</TooltipContent>
256
+
257
+ </Tooltip>
258
+ </TooltipProvider>
259
+ </ThemeProvider>`}
284
260
  >
285
261
  <ThemeProvider defaultMode="dark">
286
262
  <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
287
263
  <div className="flex-1 flex flex-col items-center gap-4">
288
264
  <span className="text-sm font-medium text-muted-foreground">
289
- Không dùng ThemeWrapper
265
+ Base UI Primitive Portal
290
266
  </span>
291
- <TooltipProvider>
292
- <Tooltip>
293
- <TooltipTrigger
267
+ <TooltipPrimitive.Provider>
268
+ <TooltipPrimitive.Root>
269
+ <TooltipPrimitive.Trigger
294
270
  render={<Button variant="solid">Hover tôi</Button>}
295
271
  />
296
- <TooltipPortal>
297
- <TooltipContent
298
- sideOffset={8}
299
- className="bg-card text-card-foreground border border-border shadow-md"
300
- >
301
- Trắng toát! Lạc quẻ với nền đen.
302
- </TooltipContent>
303
- </TooltipPortal>
304
- </Tooltip>
305
- </TooltipProvider>
272
+ <TooltipPrimitive.Portal>
273
+ <TooltipPrimitive.Positioner sideOffset={8}>
274
+ <TooltipPrimitive.Popup className="bg-card text-card-foreground border border-border shadow-md rounded px-2 py-1 text-xs">
275
+ Trắng toát! Lạc quẻ với nền đen.
276
+ </TooltipPrimitive.Popup>
277
+ </TooltipPrimitive.Positioner>
278
+ </TooltipPrimitive.Portal>
279
+ </TooltipPrimitive.Root>
280
+ </TooltipPrimitive.Provider>
306
281
  </div>
307
282
 
308
283
  <div className="flex-1 flex flex-col items-center gap-4">
309
284
  <span className="text-sm font-medium text-muted-foreground">
310
- ThemeWrapper
285
+ Library Content (auto ThemeWrapper)
311
286
  </span>
312
287
  <TooltipProvider>
313
288
  <Tooltip>
314
289
  <TooltipTrigger
315
290
  render={<Button variant="solid">Hover tôi</Button>}
316
291
  />
317
- <TooltipPortal>
318
- <ThemeWrapper>
319
- <TooltipContent
320
- sideOffset={8}
321
- className="bg-card text-card-foreground border border-border shadow-md"
322
- >
323
- Màu Tối! Đồng bộ với provider cha.
324
- </TooltipContent>
325
- </ThemeWrapper>
326
- </TooltipPortal>
292
+
293
+ <TooltipContent
294
+ sideOffset={8}
295
+ className="bg-card text-card-foreground border border-border shadow-md"
296
+ >
297
+ Màu Tối! Đồng bộ với provider cha.
298
+ </TooltipContent>
299
+
327
300
  </Tooltip>
328
301
  </TooltipProvider>
329
302
  </div>
@@ -335,115 +308,80 @@ function ThemeProviderMicroShowcase() {
335
308
  <ExampleSection
336
309
  fullWidth
337
310
  label="Popover"
338
- description="Popover chứa content phức tạp và mở khi click. portal hướng tới `document.body`, toàn bộ nền popover lấy CSS variable từ root sẽ trắng hoàn toàn trigger đang nằm trong dark section."
339
- codeString={`<ThemeProvider defaultMode="dark">
340
- <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
341
- <div className="flex-1 flex flex-col items-center gap-4">
342
- <span className="text-sm font-medium text-muted-foreground">
343
- Không dùng ThemeWrapper
344
- </span>
345
- <Popover>
346
- <PopoverTrigger
347
- render={
348
- <Button variant="outline">
349
- Click Popover
350
- </Button>
351
- }
352
- />
353
- <PopoverPortal>
354
- <PopoverContent className="w-64">
355
- <div className="space-y-2">
356
- <h4 className="font-medium leading-none">
357
- Nội dung Popover
358
- </h4>
359
- <p className="text-sm text-muted-foreground">
360
- Lỗi: Hiển thị giao diện sáng.
361
- </p>
362
- </div>
363
- </PopoverContent>
364
- </PopoverPortal>
365
- </Popover>
366
- </div>
367
-
368
- <div className="flex-1 flex flex-col items-center gap-4">
369
- <span className="text-sm font-medium text-muted-foreground">
370
- Có ThemeWrapper
371
- </span>
372
- <Popover>
373
- <PopoverTrigger
374
- render={
375
- <Button variant="outline">
376
- Click Popover
377
- </Button>
378
- }
379
- />
380
- <PopoverPortal>
381
- <ThemeWrapper>
382
- <PopoverContent className="w-64">
383
- <div className="space-y-2">
384
- <h4 className="font-medium leading-none">
385
- Nội dung Popover
386
- </h4>
387
- <p className="text-sm text-muted-foreground">
388
- Lớp Portal Tunnel đã hoạt động chính
389
- xác.
390
- </p>
391
- </div>
392
- </PopoverContent>
393
- </ThemeWrapper>
394
- </PopoverPortal>
395
- </Popover>
396
- </div>
397
- </div>
398
- </ThemeProvider>`}
311
+ description="Cột trái dùng PopoverPrimitive.Portal trực tiếp nền popover trắng trigger đang dark section. Cột phải chỉ cần của thư viện xong."
312
+ codeString={`// ❌ Base UI primitive Portal
313
+ import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
314
+
315
+ <ThemeProvider defaultMode="dark">
316
+ <PopoverPrimitive.Root>
317
+ <PopoverPrimitive.Trigger>Click Popover</PopoverPrimitive.Trigger>
318
+ <PopoverPrimitive.Portal>
319
+ {/* Không có ThemeWrapper → popover trắng */}
320
+ <PopoverPrimitive.Positioner>
321
+ <PopoverPrimitive.Popup>...</PopoverPrimitive.Popup>
322
+ </PopoverPrimitive.Positioner>
323
+ </PopoverPrimitive.Portal>
324
+ </PopoverPrimitive.Root>
325
+ </ThemeProvider>
326
+
327
+ // ✅ Library Content
328
+ <ThemeProvider defaultMode="dark">
329
+ <Popover>
330
+ <PopoverTrigger render={<Button>Click Popover</Button>} />
331
+
332
+ {/* ThemeWrapper đã được bọc bên trong */}
333
+ <PopoverContent>...</PopoverContent>
334
+
335
+ </Popover>
336
+ </ThemeProvider>`}
399
337
  >
400
338
  <ThemeProvider defaultMode="dark">
401
339
  <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
402
340
  <div className="flex-1 flex flex-col items-center gap-4">
403
341
  <span className="text-sm font-medium text-muted-foreground">
404
- Không dùng ThemeWrapper
342
+ Base UI Primitive Portal
343
+ </span>
344
+ <PopoverPrimitive.Root>
345
+ <PopoverPrimitive.Trigger
346
+ render={<Button variant="outline">Click Popover</Button>}
347
+ />
348
+ <PopoverPrimitive.Portal>
349
+ <PopoverPrimitive.Positioner>
350
+ <PopoverPrimitive.Popup className="z-50 rounded-lg border border-border bg-card text-card-foreground p-4 shadow-md w-64">
351
+ <div className="space-y-2">
352
+ <h4 className="font-medium leading-none">
353
+ Nội dung Popover
354
+ </h4>
355
+ <p className="text-sm text-muted-foreground">
356
+ Lỗi: Hiển thị giao diện sáng.
357
+ </p>
358
+ </div>
359
+ </PopoverPrimitive.Popup>
360
+ </PopoverPrimitive.Positioner>
361
+ </PopoverPrimitive.Portal>
362
+ </PopoverPrimitive.Root>
363
+ </div>
364
+
365
+ <div className="flex-1 flex flex-col items-center gap-4">
366
+ <span className="text-sm font-medium text-muted-foreground">
367
+ ✅ Library Content (auto ThemeWrapper)
405
368
  </span>
406
369
  <Popover>
407
370
  <PopoverTrigger
408
371
  render={<Button variant="outline">Click Popover</Button>}
409
372
  />
410
- <PopoverPortal>
373
+
411
374
  <PopoverContent className="w-64">
412
375
  <div className="space-y-2">
413
376
  <h4 className="font-medium leading-none">
414
377
  Nội dung Popover
415
378
  </h4>
416
379
  <p className="text-sm text-muted-foreground">
417
- Lỗi: Hiển thị giao diện sáng.
380
+ Lớp Portal Tunnel đã hoạt động chính xác.
418
381
  </p>
419
382
  </div>
420
383
  </PopoverContent>
421
- </PopoverPortal>
422
- </Popover>
423
- </div>
424
-
425
- <div className="flex-1 flex flex-col items-center gap-4">
426
- <span className="text-sm font-medium text-muted-foreground">
427
- Có ThemeWrapper
428
- </span>
429
- <Popover>
430
- <PopoverTrigger
431
- render={<Button variant="outline">Click Popover</Button>}
432
- />
433
- <PopoverPortal>
434
- <ThemeWrapper>
435
- <PopoverContent className="w-64">
436
- <div className="space-y-2">
437
- <h4 className="font-medium leading-none">
438
- Nội dung Popover
439
- </h4>
440
- <p className="text-sm text-muted-foreground">
441
- Lớp Portal Tunnel đã hoạt động chính xác.
442
- </p>
443
- </div>
444
- </PopoverContent>
445
- </ThemeWrapper>
446
- </PopoverPortal>
384
+
447
385
  </Popover>
448
386
  </div>
449
387
  </div>
@@ -454,100 +392,87 @@ function ThemeProviderMicroShowcase() {
454
392
  <ExampleSection
455
393
  fullWidth
456
394
  label="Select"
457
- description="SelectContent được render tách biệt hoàn toàn khỏi trigger. Thiếu ThemeWrapper, dropdown option sẽ trắng xuộa trong khi trigger vẫn tối quáng khó đọc."
458
- codeString={`<ThemeProvider defaultMode="dark">
459
- <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
460
- <div className="flex-1 flex flex-col items-center gap-4">
461
- <span className="text-sm font-medium text-muted-foreground">
462
- Không dùng ThemeWrapper
463
- </span>
464
- <Select
465
- items={{
466
- next: "Next.js",
467
- vite: "Vite",
468
- remix: "Remix",
469
- }}
470
- >
471
- <SelectTrigger className="w-[140px]">
472
- <SelectValue placeholder="Chọn framework..." />
473
- </SelectTrigger>
474
- <SelectPortal>
475
- <SelectContent>
476
- <SelectItem value="next">Next.js</SelectItem>
477
- <SelectItem value="vite">Vite</SelectItem>
478
- <SelectItem value="remix">Remix</SelectItem>
479
- </SelectContent>
480
- </SelectPortal>
481
- </Select>
482
- </div>
483
-
484
- <div className="flex-1 flex flex-col items-center gap-4">
485
- <span className="text-sm font-medium text-muted-foreground">
486
- Có ThemeWrapper
487
- </span>
488
- <Select
489
- items={{
490
- next: "Next.js",
491
- vite: "Vite",
492
- remix: "Remix",
493
- }}
494
- >
495
- <SelectTrigger className="w-[140px]">
496
- <SelectValue placeholder="Chọn framework..." />
497
- </SelectTrigger>
498
- <SelectPortal>
499
- <ThemeWrapper>
500
- <SelectContent>
501
- <SelectItem value="next">
502
- Next.js
503
- </SelectItem>
504
- <SelectItem value="vite">Vite</SelectItem>
505
- <SelectItem value="remix">Remix</SelectItem>
506
- </SelectContent>
507
- </ThemeWrapper>
508
- </SelectPortal>
509
- </Select>
510
- </div>
511
- </div>
512
- </ThemeProvider>`}
395
+ description="Cột trái dùng SelectPrimitive.Portal trực tiếp dropdown option trắng xuộa trong khi trigger vẫn tối. Cột phải dùng của thư viện là đúng màu ngay."
396
+ codeString={`// ❌ Base UI primitive Portal
397
+ import { Select as SelectPrimitive } from "@base-ui/react/select";
398
+
399
+ <ThemeProvider defaultMode="dark">
400
+ <SelectPrimitive.Root items={{ next: "Next.js", vite: "Vite" }}>
401
+ <SelectPrimitive.Trigger>...</SelectPrimitive.Trigger>
402
+ <SelectPrimitive.Portal>
403
+ {/* Không có ThemeWrapper → dropdown trắng */}
404
+ <SelectPrimitive.Positioner>
405
+ <SelectPrimitive.Popup>...</SelectPrimitive.Popup>
406
+ </SelectPrimitive.Positioner>
407
+ </SelectPrimitive.Portal>
408
+ </SelectPrimitive.Root>
409
+ </ThemeProvider>
410
+
411
+ // ✅ Library Content
412
+ <ThemeProvider defaultMode="dark">
413
+ <Select items={{ next: "Next.js", vite: "Vite" }}>
414
+ <SelectTrigger><SelectValue placeholder="Chọn..." /></SelectTrigger>
415
+
416
+ {/* ← ThemeWrapper đã được bọc bên trong */}
417
+ <SelectContent>...</SelectContent>
418
+
419
+ </Select>
420
+ </ThemeProvider>`}
513
421
  >
514
422
  <ThemeProvider defaultMode="dark">
515
423
  <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
516
424
  <div className="flex-1 flex flex-col items-center gap-4">
517
425
  <span className="text-sm font-medium text-muted-foreground">
518
- Không dùng ThemeWrapper
426
+ Base UI Primitive Portal
519
427
  </span>
520
- <Select items={{ next: "Next.js", vite: "Vite", remix: "Remix" }}>
521
- <SelectTrigger className="w-[140px]">
522
- <SelectValue placeholder="Chọn framework..." />
523
- </SelectTrigger>
524
- <SelectPortal>
525
- <SelectContent>
526
- <SelectItem value="next">Next.js</SelectItem>
527
- <SelectItem value="vite">Vite</SelectItem>
528
- <SelectItem value="remix">Remix</SelectItem>
529
- </SelectContent>
530
- </SelectPortal>
531
- </Select>
428
+ <SelectPrimitive.Root
429
+ items={{ next: "Next.js", vite: "Vite", remix: "Remix" }}
430
+ >
431
+ <SelectPrimitive.Trigger className="inline-flex h-8 items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent px-2.5 py-1 text-sm text-foreground outline-none w-[140px]">
432
+ <SelectPrimitive.Value placeholder="Chọn framework..." />
433
+ </SelectPrimitive.Trigger>
434
+ <SelectPrimitive.Portal>
435
+ <SelectPrimitive.Positioner>
436
+ <SelectPrimitive.Popup className="z-50 rounded-lg border border-border bg-card text-card-foreground p-1 shadow-md">
437
+ <SelectPrimitive.Item
438
+ value="next"
439
+ className="rounded px-2 py-1.5 text-sm cursor-default"
440
+ >
441
+ <SelectPrimitive.ItemText>Next.js</SelectPrimitive.ItemText>
442
+ </SelectPrimitive.Item>
443
+ <SelectPrimitive.Item
444
+ value="vite"
445
+ className="rounded px-2 py-1.5 text-sm cursor-default"
446
+ >
447
+ <SelectPrimitive.ItemText>Vite</SelectPrimitive.ItemText>
448
+ </SelectPrimitive.Item>
449
+ <SelectPrimitive.Item
450
+ value="remix"
451
+ className="rounded px-2 py-1.5 text-sm cursor-default"
452
+ >
453
+ <SelectPrimitive.ItemText>Remix</SelectPrimitive.ItemText>
454
+ </SelectPrimitive.Item>
455
+ </SelectPrimitive.Popup>
456
+ </SelectPrimitive.Positioner>
457
+ </SelectPrimitive.Portal>
458
+ </SelectPrimitive.Root>
532
459
  </div>
533
460
 
534
461
  <div className="flex-1 flex flex-col items-center gap-4">
535
462
  <span className="text-sm font-medium text-muted-foreground">
536
- ThemeWrapper
463
+ Library Content (auto ThemeWrapper)
537
464
  </span>
538
465
  <Select items={{ next: "Next.js", vite: "Vite", remix: "Remix" }}>
539
466
  <SelectTrigger className="w-[140px]">
540
467
  <SelectValue placeholder="Chọn framework..." />
541
468
  </SelectTrigger>
542
- <SelectPortal>
543
- <ThemeWrapper>
544
- <SelectContent>
545
- <SelectItem value="next">Next.js</SelectItem>
546
- <SelectItem value="vite">Vite</SelectItem>
547
- <SelectItem value="remix">Remix</SelectItem>
548
- </SelectContent>
549
- </ThemeWrapper>
550
- </SelectPortal>
469
+
470
+ <SelectContent>
471
+ <SelectItem value="next">Next.js</SelectItem>
472
+ <SelectItem value="vite">Vite</SelectItem>
473
+ <SelectItem value="remix">Remix</SelectItem>
474
+ </SelectContent>
475
+
551
476
  </Select>
552
477
  </div>
553
478
  </div>
@@ -558,88 +483,76 @@ function ThemeProviderMicroShowcase() {
558
483
  <ExampleSection
559
484
  fullWidth
560
485
  label="DropdownMenu"
561
- description="DropdownMenu thường dùng cho navigation actions (profile, settings, logout). Khi hover qua từng item, màu highlight cũng lấy từ CSS variable thiếu ThemeWrapper, cả nền menu lẫn trạng thái hover cùng lúc đều hiển thị sai màu."
562
- codeString={`<ThemeProvider defaultMode="dark">
563
- <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
564
- <div className="flex-1 flex flex-col items-center gap-4">
565
- <span className="text-sm font-medium text-muted-foreground">
566
- Không dùng ThemeWrapper
567
- </span>
568
- <DropdownMenu>
569
- <DropdownMenuTrigger
570
- render={
571
- <Button variant="outline">
572
- Open Dropdown
573
- </Button>
574
- }
575
- />
576
- <DropdownMenuPortal>
577
- <DropdownMenuContent className="w-48">
578
- <DropdownMenuGroup>
579
- <DropdownMenuLabel>
580
- Tài khoản
581
- </DropdownMenuLabel>
582
- <DropdownMenuItem>
583
- Trang cá nhân
584
- </DropdownMenuItem>
585
- <DropdownMenuItem>Cài đặt</DropdownMenuItem>
586
- </DropdownMenuGroup>
587
- <DropdownMenuSeparator />
588
- <DropdownMenuItem>Đăng xuất</DropdownMenuItem>
589
- </DropdownMenuContent>
590
- </DropdownMenuPortal>
591
- </DropdownMenu>
592
- </div>
593
-
594
- <div className="flex-1 flex flex-col items-center gap-4">
595
- <span className="text-sm font-medium text-muted-foreground">
596
- Có ThemeWrapper
597
- </span>
598
- <DropdownMenu>
599
- <DropdownMenuTrigger
600
- render={
601
- <Button variant="outline">
602
- Open Dropdown
603
- </Button>
604
- }
605
- />
606
- <DropdownMenuPortal>
607
- <ThemeWrapper>
608
- <DropdownMenuContent className="w-48">
609
- <DropdownMenuGroup>
610
- <DropdownMenuLabel>
611
- Tài khoản
612
- </DropdownMenuLabel>
613
- <DropdownMenuItem>
614
- Trang cá nhân
615
- </DropdownMenuItem>
616
- <DropdownMenuItem>
617
- Cài đặt
618
- </DropdownMenuItem>
619
- </DropdownMenuGroup>
620
- <DropdownMenuSeparator />
621
- <DropdownMenuItem>
622
- Đăng xuất
623
- </DropdownMenuItem>
624
- </DropdownMenuContent>
625
- </ThemeWrapper>
626
- </DropdownMenuPortal>
627
- </DropdownMenu>
628
- </div>
629
- </div>
630
- </ThemeProvider>`}
486
+ description="Cột trái dùng MenuPrimitive.Portal trực tiếp — cả nền menu lẫn hover state đều sai màu. Cột phải dùng thư viện là đúng ngay."
487
+ codeString={`// ❌ Base UI primitive Portal
488
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu";
489
+
490
+ <ThemeProvider defaultMode="dark">
491
+ <MenuPrimitive.Root>
492
+ <MenuPrimitive.Trigger>Open Dropdown</MenuPrimitive.Trigger>
493
+ <MenuPrimitive.Portal>
494
+ {/* Không có ThemeWrapper → menu trắng, hover sai màu */}
495
+ <MenuPrimitive.Positioner>
496
+ <MenuPrimitive.Popup>...</MenuPrimitive.Popup>
497
+ </MenuPrimitive.Positioner>
498
+ </MenuPrimitive.Portal>
499
+ </MenuPrimitive.Root>
500
+ </ThemeProvider>
501
+
502
+ // ✅ Library Content
503
+ <ThemeProvider defaultMode="dark">
504
+ <DropdownMenu>
505
+ <DropdownMenuTrigger render={<Button>Open Dropdown</Button>} />
506
+
507
+ {/* ← ThemeWrapper đã được bọc bên trong */}
508
+ <DropdownMenuContent>...</DropdownMenuContent>
509
+
510
+ </DropdownMenu>
511
+ </ThemeProvider>`}
631
512
  >
632
513
  <ThemeProvider defaultMode="dark">
633
514
  <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
634
515
  <div className="flex-1 flex flex-col items-center gap-4">
635
516
  <span className="text-sm font-medium text-muted-foreground">
636
- Không dùng ThemeWrapper
517
+ Base UI Primitive Portal
518
+ </span>
519
+ <MenuPrimitive.Root>
520
+ <MenuPrimitive.Trigger
521
+ render={<Button variant="outline">Open Dropdown</Button>}
522
+ />
523
+ <MenuPrimitive.Portal>
524
+ <MenuPrimitive.Positioner>
525
+ <MenuPrimitive.Popup className="z-50 min-w-[8rem] rounded-lg border border-border bg-card text-card-foreground p-1 shadow-md">
526
+ <MenuPrimitive.Group>
527
+ <MenuPrimitive.GroupLabel className="px-2 py-1.5 text-xs font-medium text-muted-foreground">
528
+ Tài khoản
529
+ </MenuPrimitive.GroupLabel>
530
+ <MenuPrimitive.Item className="rounded px-2 py-1.5 text-sm cursor-default">
531
+ Trang cá nhân
532
+ </MenuPrimitive.Item>
533
+ <MenuPrimitive.Item className="rounded px-2 py-1.5 text-sm cursor-default">
534
+ Cài đặt
535
+ </MenuPrimitive.Item>
536
+ </MenuPrimitive.Group>
537
+ <MenuPrimitive.Separator className="my-1 h-px bg-border" />
538
+ <MenuPrimitive.Item className="rounded px-2 py-1.5 text-sm cursor-default">
539
+ Đăng xuất
540
+ </MenuPrimitive.Item>
541
+ </MenuPrimitive.Popup>
542
+ </MenuPrimitive.Positioner>
543
+ </MenuPrimitive.Portal>
544
+ </MenuPrimitive.Root>
545
+ </div>
546
+
547
+ <div className="flex-1 flex flex-col items-center gap-4">
548
+ <span className="text-sm font-medium text-muted-foreground">
549
+ ✅ Library Content (auto ThemeWrapper)
637
550
  </span>
638
551
  <DropdownMenu>
639
552
  <DropdownMenuTrigger
640
553
  render={<Button variant="outline">Open Dropdown</Button>}
641
554
  />
642
- <DropdownMenuPortal>
555
+
643
556
  <DropdownMenuContent className="w-48">
644
557
  <DropdownMenuGroup>
645
558
  <DropdownMenuLabel>Tài khoản</DropdownMenuLabel>
@@ -649,31 +562,7 @@ function ThemeProviderMicroShowcase() {
649
562
  <DropdownMenuSeparator />
650
563
  <DropdownMenuItem>Đăng xuất</DropdownMenuItem>
651
564
  </DropdownMenuContent>
652
- </DropdownMenuPortal>
653
- </DropdownMenu>
654
- </div>
655
-
656
- <div className="flex-1 flex flex-col items-center gap-4">
657
- <span className="text-sm font-medium text-muted-foreground">
658
- Có ThemeWrapper
659
- </span>
660
- <DropdownMenu>
661
- <DropdownMenuTrigger
662
- render={<Button variant="outline">Open Dropdown</Button>}
663
- />
664
- <DropdownMenuPortal>
665
- <ThemeWrapper>
666
- <DropdownMenuContent className="w-48">
667
- <DropdownMenuGroup>
668
- <DropdownMenuLabel>Tài khoản</DropdownMenuLabel>
669
- <DropdownMenuItem>Trang cá nhân</DropdownMenuItem>
670
- <DropdownMenuItem>Cài đặt</DropdownMenuItem>
671
- </DropdownMenuGroup>
672
- <DropdownMenuSeparator />
673
- <DropdownMenuItem>Đăng xuất</DropdownMenuItem>
674
- </DropdownMenuContent>
675
- </ThemeWrapper>
676
- </DropdownMenuPortal>
565
+
677
566
  </DropdownMenu>
678
567
  </div>
679
568
  </div>
@@ -684,107 +573,81 @@ function ThemeProviderMicroShowcase() {
684
573
  <ExampleSection
685
574
  fullWidth
686
575
  label="Dialog"
687
- description="Dialog trường hợp phức tạp nhất: portal chứa cả Overlay backdrop lẫn Content panel. Cả hai layer đều cần được bọc trong ThemeWrapper để đảm bảo đồng bộ hoàn toàn."
688
- codeString={`<ThemeProvider defaultMode="dark">
689
- <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
690
- <div className="flex-1 flex flex-col items-center gap-4">
691
- <span className="text-sm font-medium text-muted-foreground">
692
- Không dùng ThemeWrapper
693
- </span>
694
- <Dialog>
695
- <DialogTrigger
696
- render={
697
- <Button variant="outline">Open Dialog</Button>
698
- }
699
- />
700
- <DialogPortal>
701
- <DialogContent className="sm:max-w-md">
702
- <DialogHeader>
703
- <DialogTitle>Mất Dark Mode!</DialogTitle>
704
- <DialogDescription>
705
- Lỗi: Hiển thị giao diện sáng mặc định.
706
- </DialogDescription>
707
- </DialogHeader>
708
- </DialogContent>
709
- </DialogPortal>
710
- </Dialog>
711
- </div>
712
-
713
- <div className="flex-1 flex flex-col items-center gap-4">
714
- <span className="text-sm font-medium text-muted-foreground">
715
- Có ThemeWrapper
716
- </span>
717
- <Dialog>
718
- <DialogTrigger
719
- render={
720
- <Button variant="outline">Open Dialog</Button>
721
- }
722
- />
723
- <DialogPortal>
724
- <ThemeWrapper>
725
- <DialogContent className="sm:max-w-md">
726
- <DialogHeader>
727
- <DialogTitle>
728
- Màu tối toàn vẹn
729
- </DialogTitle>
730
- <DialogDescription>
731
- Ngay cả khi mở Dialog bọc ngoài toàn bộ
732
- màn hình, Portal Tunnel vẫn bảo vệ được
733
- scope Dark Mode.
734
- </DialogDescription>
735
- </DialogHeader>
736
- </DialogContent>
737
- </ThemeWrapper>
738
- </DialogPortal>
739
- </Dialog>
740
- </div>
741
- </div>
742
- </ThemeProvider>`}
576
+ description="Cột trái dùng DialogPrimitive.Portal trực tiếp cả Overlay backdrop lẫn Content panel đều hiển thị sai màu. Cột phải dùng thư viện đồng bộ hoàn hảo."
577
+ codeString={`// ❌ Base UI primitive Portal
578
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
579
+
580
+ <ThemeProvider defaultMode="dark">
581
+ <DialogPrimitive.Root>
582
+ <DialogPrimitive.Trigger>Open Dialog</DialogPrimitive.Trigger>
583
+ <DialogPrimitive.Portal>
584
+ {/* Không có ThemeWrapper → dialog trắng */}
585
+ <DialogPrimitive.Backdrop />
586
+ <DialogPrimitive.Popup>...</DialogPrimitive.Popup>
587
+ </DialogPrimitive.Portal>
588
+ </DialogPrimitive.Root>
589
+ </ThemeProvider>
590
+
591
+ // ✅ Library Content
592
+ <ThemeProvider defaultMode="dark">
593
+ <Dialog>
594
+ <DialogTrigger render={<Button>Open Dialog</Button>} />
595
+
596
+ {/* ← ThemeWrapper đã được bọc bên trong */}
597
+ <DialogContent>...</DialogContent>
598
+
599
+ </Dialog>
600
+ </ThemeProvider>`}
743
601
  >
744
602
  <ThemeProvider defaultMode="dark">
745
603
  <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
746
604
  <div className="flex-1 flex flex-col items-center gap-4">
747
605
  <span className="text-sm font-medium text-muted-foreground">
748
- Không dùng ThemeWrapper
606
+ Base UI Primitive Portal
749
607
  </span>
750
- <Dialog>
751
- <DialogTrigger
608
+ <DialogPrimitive.Root>
609
+ <DialogPrimitive.Trigger
752
610
  render={<Button variant="outline">Open Dialog</Button>}
753
611
  />
754
- <DialogPortal>
755
- <DialogContent className="sm:max-w-md">
756
- <DialogHeader>
757
- <DialogTitle>Mất Dark Mode!</DialogTitle>
758
- <DialogDescription>
759
- Lỗi: Hiển thị giao diện sáng mặc định.
760
- </DialogDescription>
761
- </DialogHeader>
762
- </DialogContent>
763
- </DialogPortal>
764
- </Dialog>
612
+ <DialogPrimitive.Portal>
613
+ <DialogPrimitive.Backdrop className="fixed inset-0 bg-black/20 z-40" />
614
+ <DialogPrimitive.Popup className="fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-card text-card-foreground p-6 shadow-lg sm:max-w-md w-full">
615
+ <DialogPrimitive.Title className="text-lg font-semibold">
616
+ Mất Dark Mode!
617
+ </DialogPrimitive.Title>
618
+ <DialogPrimitive.Description className="mt-2 text-sm text-muted-foreground">
619
+ Lỗi: Hiển thị giao diện sáng mặc định.
620
+ </DialogPrimitive.Description>
621
+ <DialogPrimitive.Close
622
+ render={
623
+ <Button variant="outline" className="mt-4">
624
+ Đóng
625
+ </Button>
626
+ }
627
+ />
628
+ </DialogPrimitive.Popup>
629
+ </DialogPrimitive.Portal>
630
+ </DialogPrimitive.Root>
765
631
  </div>
766
632
 
767
633
  <div className="flex-1 flex flex-col items-center gap-4">
768
634
  <span className="text-sm font-medium text-muted-foreground">
769
- ThemeWrapper
635
+ Library Content (auto ThemeWrapper)
770
636
  </span>
771
637
  <Dialog>
772
638
  <DialogTrigger
773
639
  render={<Button variant="outline">Open Dialog</Button>}
774
640
  />
775
- <DialogPortal>
776
- <ThemeWrapper>
777
- <DialogContent className="sm:max-w-md">
778
- <DialogHeader>
779
- <DialogTitle>Màu tối toàn vẹn</DialogTitle>
780
- <DialogDescription>
781
- Ngay cả khi mở Dialog bọc ngoài toàn bộ màn hình,
782
- Portal Tunnel vẫn bảo vệ được scope Dark Mode.
783
- </DialogDescription>
784
- </DialogHeader>
785
- </DialogContent>
786
- </ThemeWrapper>
787
- </DialogPortal>
641
+
642
+ <DialogContent className="sm:max-w-md">
643
+ <DialogHeader>
644
+ <DialogTitle>Màu tối toàn vẹn</DialogTitle>
645
+ <DialogDescription>
646
+ Portal Tunnel tự động bảo vệ scope Dark Mode.
647
+ </DialogDescription>
648
+ </DialogHeader>
649
+ </DialogContent>
650
+
788
651
  </Dialog>
789
652
  </div>
790
653
  </div>
@@ -795,94 +658,75 @@ function ThemeProviderMicroShowcase() {
795
658
  <ExampleSection
796
659
  fullWidth
797
660
  label="Sheet"
798
- description="Sheet trượt từ cạnh màn hình phủ một vùng diện tích lớn lỗi theme dễ nhận biết nhất panel thông thường chứa layout phức tạp hơn Popover hay Tooltip."
799
- codeString={`<ThemeProvider defaultMode="dark">
800
- <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
801
- <div className="flex-1 flex flex-col items-center gap-4">
802
- <span className="text-sm font-medium text-muted-foreground">
803
- Không dùng ThemeWrapper
804
- </span>
805
- <Sheet>
806
- <SheetTrigger
807
- render={
808
- <Button variant="outline">Open Sheet</Button>
809
- }
810
- />
811
- <SheetPortal>
812
- <SheetContent side="right">
813
- <SheetHeader>
814
- <SheetTitle>Mất Dark Mode!</SheetTitle>
815
- </SheetHeader>
816
- </SheetContent>
817
- </SheetPortal>
818
- </Sheet>
819
- </div>
820
-
821
- <div className="flex-1 flex flex-col items-center gap-4">
822
- <span className="text-sm font-medium text-muted-foreground">
823
- Có ThemeWrapper
824
- </span>
825
- <Sheet>
826
- <SheetTrigger
827
- render={
828
- <Button variant="outline">Open Sheet</Button>
829
- }
830
- />
831
- <SheetPortal>
832
- <ThemeWrapper>
833
- <SheetContent side="right">
834
- <SheetHeader>
835
- <SheetTitle>Sheet Demo</SheetTitle>
836
- </SheetHeader>
837
- <div className="py-4">
838
- Màu tối đồng bộ hoàn hảo!
839
- </div>
840
- </SheetContent>
841
- </ThemeWrapper>
842
- </SheetPortal>
843
- </Sheet>
844
- </div>
845
- </div>
846
- </ThemeProvider>`}
661
+ description="Cột trái dùng SheetPrimitive.Portal (Dialog primitive) trực tiếppanel trắng toát. Cột phải dùng của thư viện đúng màu ngay."
662
+ codeString={`// ❌ Base UI primitive Portal (Sheet dùng Dialog primitive của Base UI)
663
+ import { Dialog as SheetPrimitive } from "@base-ui/react/dialog";
664
+
665
+ <ThemeProvider defaultMode="dark">
666
+ <SheetPrimitive.Root>
667
+ <SheetPrimitive.Trigger>Open Sheet</SheetPrimitive.Trigger>
668
+ <SheetPrimitive.Portal>
669
+ {/* Không có ThemeWrapper → sheet trắng */}
670
+ <SheetPrimitive.Popup>...</SheetPrimitive.Popup>
671
+ </SheetPrimitive.Portal>
672
+ </SheetPrimitive.Root>
673
+ </ThemeProvider>
674
+
675
+ // ✅ Library Content
676
+ <ThemeProvider defaultMode="dark">
677
+ <Sheet>
678
+ <SheetTrigger render={<Button>Open Sheet</Button>} />
679
+
680
+ {/* ← ThemeWrapper đã được bọc bên trong */}
681
+ <SheetContent side="right">...</SheetContent>
682
+
683
+ </Sheet>
684
+ </ThemeProvider>`}
847
685
  >
848
686
  <ThemeProvider defaultMode="dark">
849
687
  <div className="w-full flex gap-10 rounded-xl border border-border bg-background p-10">
850
688
  <div className="flex-1 flex flex-col items-center gap-4">
851
689
  <span className="text-sm font-medium text-muted-foreground">
852
- Không dùng ThemeWrapper
690
+ Base UI Primitive Portal
853
691
  </span>
854
- <Sheet>
855
- <SheetTrigger
692
+ <SheetPrimitive.Root>
693
+ <SheetPrimitive.Trigger
856
694
  render={<Button variant="outline">Open Sheet</Button>}
857
695
  />
858
- <SheetPortal>
859
- <SheetContent side="right">
860
- <SheetHeader>
861
- <SheetTitle>Mất Dark Mode!</SheetTitle>
862
- </SheetHeader>
863
- </SheetContent>
864
- </SheetPortal>
865
- </Sheet>
696
+ <SheetPrimitive.Portal>
697
+ <SheetPrimitive.Backdrop className="fixed inset-0 bg-black/20 z-40" />
698
+ <SheetPrimitive.Popup className="fixed right-0 top-0 z-50 h-full w-3/4 max-w-sm border-l border-border bg-card text-card-foreground p-6 shadow-lg">
699
+ <SheetPrimitive.Title className="text-lg font-semibold">
700
+ Mất Dark Mode!
701
+ </SheetPrimitive.Title>
702
+ <SheetPrimitive.Close
703
+ render={
704
+ <Button variant="ghost" size="sm" className="mt-4">
705
+ Đóng
706
+ </Button>
707
+ }
708
+ />
709
+ </SheetPrimitive.Popup>
710
+ </SheetPrimitive.Portal>
711
+ </SheetPrimitive.Root>
866
712
  </div>
867
713
 
868
714
  <div className="flex-1 flex flex-col items-center gap-4">
869
715
  <span className="text-sm font-medium text-muted-foreground">
870
- ThemeWrapper
716
+ Library Content (auto ThemeWrapper)
871
717
  </span>
872
718
  <Sheet>
873
719
  <SheetTrigger
874
720
  render={<Button variant="outline">Open Sheet</Button>}
875
721
  />
876
- <SheetPortal>
877
- <ThemeWrapper>
878
- <SheetContent side="right">
879
- <SheetHeader>
880
- <SheetTitle>Sheet Demo</SheetTitle>
881
- </SheetHeader>
882
- <div className="py-4">Màu tối đồng bộ hoàn hảo!</div>
883
- </SheetContent>
884
- </ThemeWrapper>
885
- </SheetPortal>
722
+
723
+ <SheetContent side="right">
724
+ <SheetHeader>
725
+ <SheetTitle>Sheet Demo</SheetTitle>
726
+ </SheetHeader>
727
+ <div className="py-4">Màu tối đồng bộ hoàn hảo!</div>
728
+ </SheetContent>
729
+
886
730
  </Sheet>
887
731
  </div>
888
732
  </div>
@@ -899,7 +743,7 @@ export default function ThemeProviderShowcase() {
899
743
  return (
900
744
  <Showcase
901
745
  title="Theme Provider"
902
- description="Context provider cho light/dark mode — uncontrolled (defaultMode) hoặc controlled (mode + onModeChange). Portal-safe qua ThemeWrapper."
746
+ description="Context provider cho light/dark mode — uncontrolled (defaultMode) hoặc controlled (mode + onModeChange). Tunnel-safe cho mọi nổi dung nổi (floating) qua ThemeWrapper tích hợp tự động trong các *Content components (từ v0.2.20)."
903
747
  generalConcept={
904
748
  <ShowcaseDocs>
905
749
  <DocsH3>ThemeProvider</DocsH3>
@@ -907,49 +751,44 @@ export default function ThemeProviderShowcase() {
907
751
  <DocsCode>ThemeProvider</DocsCode> bọc children trong một{" "}
908
752
  <DocsCode>{"div[display:contents]"}</DocsCode> và gắn class{" "}
909
753
  <DocsCode>light</DocsCode> hoặc <DocsCode>dark</DocsCode> lên đó,
910
- kích hoạt CSS variable cascade mà không ảnh hưởng layout. Hỗ trợ hai
911
- chế độ: <strong>Uncontrolled</strong> (truyền{" "}
754
+ kích hoạt CSS variable cascade mà không ảnh hưởng layout. Hỗ trợ
755
+ hai chế độ: <strong>Uncontrolled</strong> (truyền{" "}
912
756
  <DocsCode>defaultMode</DocsCode> — tự quản lý state) và{" "}
913
757
  <strong>Controlled</strong> (truyền <DocsCode>mode</DocsCode> +{" "}
914
758
  <DocsCode>onModeChange</DocsCode> — state do component cha sở hữu).
915
759
  Việc đọc localStorage hoặc system preference thuộc về tầng Macro.
916
760
  </DocsP>
917
761
 
918
- <DocsH3>ThemeWrapper &amp; Portal Hard Requirement</DocsH3>
762
+ <DocsH3>Zero-Portal API &amp; Tích hợp Theme tự động (v0.2.20)</DocsH3>
919
763
  <DocsP>
920
764
  CSS variable cascade theo <strong>DOM tree</strong>, không theo
921
- React component tree. Khi một component render qua Portal (ra{" "}
765
+ React component tree. Khi một component nổi (như Popover, Dialog) render nội dung thông qua chế Portaling (ra{" "}
922
766
  <DocsCode>document.body</DocsCode>), nó thoát khỏi hoàn toàn chuỗi
923
767
  DOM ancestry — không còn tổ tiên nào mang class{" "}
924
- <DocsCode>.dark</DocsCode>, và mọi CSS variable rơi thẳng về giá trị{" "}
925
- <DocsCode>:root</DocsCode> (thường là light).
768
+ <DocsCode>.dark</DocsCode>, và mọi CSS variable rơi thẳng về giá
769
+ trị <DocsCode>:root</DocsCode> (thường là light).
926
770
  </DocsP>
927
771
  <DocsP>
928
772
  <DocsCode>ThemeWrapper</DocsCode> giải quyết vấn đề này bằng cách
929
773
  đọc <DocsCode>useTheme()</DocsCode> và re-apply class theme vào một
930
- container mới ngay bên trong Portal — tái thiết lập CSS variable
931
- cascade cho toàn bộ nội dung bên trong.{" "}
932
- <strong>
933
- Đây không phải best practice tuỳ chọn — đây là yêu cầu bắt buộc
934
- </strong>{" "}
935
- khi dùng Portal trong bất kỳ scoped theme section nào. Bỏ qua{" "}
936
- <DocsCode>ThemeWrapper</DocsCode> sẽ không gây lỗi compile hay
937
- runtime warning, nhưng UI sẽ hiển thị sai màu âm thầm.
774
+ container mới ngay bên trong cây portaling — tái thiết lập CSS variable
775
+ cascade cho toàn bộ nội dung bên trong.
938
776
  </DocsP>
939
777
  <DocsP>
940
- Trách nhiệm đặt <DocsCode>ThemeWrapper</DocsCode> không thể delegate
941
- tuỳ tiện: Micro component không được tự ôm nó bên trong (vi phạm
942
- nguyên tắc pure composition). Tầng nào kiểm soát Portal — Macro
943
- component hoặc consumer trực tiếp tầng đó phải tự đặt{" "}
944
- <DocsCode>ThemeWrapper</DocsCode> ngay sau Portal boundary.
778
+ <strong>Kể từ v0.2.20</strong>, thư viện áp dụng <strong>Zero-Portal API</strong>. Tất cả{" "}
779
+ <DocsCode>*Content</DocsCode> components của thư viện (DialogContent,
780
+
781
+ TooltipContent, v.v...) đã bọc sẵn chế Portaling <DocsCode>ThemeWrapper</DocsCode> bên
782
+ trong chúng. <strong>Bạn không cần tự dùng <DocsCode>*Portal</DocsCode> hay <DocsCode>ThemeWrapper</DocsCode> nữa.</strong> Nếu bạn bypass
783
+ thư viện và dùng Base UI primitive Portal trực tiếp, vấn đề mất giao diện dark mode vẫn xảy
784
+ ra — demo bên dưới minh hoạ chính xác điều này.
945
785
  </DocsP>
946
786
  </ShowcaseDocs>
947
787
  }
948
788
  tabs={[
949
789
  {
950
790
  label: "Micro (Primitive)",
951
- content: <ThemeProviderMicroShowcase />,
952
- },
791
+ content: <ThemeProviderMicroShowcase /> },
953
792
  ]}
954
793
  />
955
794
  );