@onexapis/cli 1.1.38 → 1.1.39

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 (60) hide show
  1. package/dist/cli.js +384 -380
  2. package/dist/cli.js.map +1 -1
  3. package/dist/cli.mjs +381 -376
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/index.js +258 -293
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +255 -289
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/preview/preview-app.tsx +13 -5
  10. package/package.json +1 -3
  11. package/templates/default/AUTH_AND_PROFILE.md +167 -0
  12. package/templates/default/CLAUDE.md +334 -1
  13. package/templates/default/LAYOUT.md +195 -0
  14. package/templates/default/bundle-entry.ts +5 -0
  15. package/templates/default/esbuild.config.js +20 -0
  16. package/templates/default/hooks/index.ts +26 -0
  17. package/templates/default/hooks/use-forgot-password-form.ts +90 -0
  18. package/templates/default/hooks/use-login-form.ts +102 -0
  19. package/templates/default/hooks/use-profile-form.ts +255 -0
  20. package/templates/default/hooks/use-register-form.ts +154 -0
  21. package/templates/default/hooks/use-verify-code-form.ts +224 -0
  22. package/templates/default/index.ts +21 -1
  23. package/templates/default/pages/about.ts +2 -2
  24. package/templates/default/pages/forgot-password.ts +39 -0
  25. package/templates/default/pages/home.ts +4 -4
  26. package/templates/default/pages/login.ts +39 -0
  27. package/templates/default/pages/profile.ts +39 -0
  28. package/templates/default/pages/register.ts +39 -0
  29. package/templates/default/pages/showcase.ts +7 -7
  30. package/templates/default/pages/verify-code.ts +39 -0
  31. package/templates/default/sections/about/about.schema.ts +1 -1
  32. package/templates/default/sections/auth-forgot-password/auth-forgot-password-default.tsx +192 -0
  33. package/templates/default/sections/auth-forgot-password/auth-forgot-password.schema.ts +150 -0
  34. package/templates/default/sections/auth-forgot-password/index.ts +14 -0
  35. package/templates/default/sections/auth-login/auth-login-default.tsx +238 -0
  36. package/templates/default/sections/auth-login/auth-login.schema.ts +171 -0
  37. package/templates/default/sections/auth-login/index.ts +14 -0
  38. package/templates/default/sections/auth-register/auth-register-default.tsx +327 -0
  39. package/templates/default/sections/auth-register/auth-register.schema.ts +188 -0
  40. package/templates/default/sections/auth-register/index.ts +14 -0
  41. package/templates/default/sections/auth-verify-code/auth-verify-code-default.tsx +209 -0
  42. package/templates/default/sections/auth-verify-code/auth-verify-code.schema.ts +150 -0
  43. package/templates/default/sections/auth-verify-code/index.ts +14 -0
  44. package/templates/default/sections/cta/cta.schema.ts +1 -1
  45. package/templates/default/sections/features/features.schema.ts +1 -1
  46. package/templates/default/sections/footer/footer-default.tsx +214 -0
  47. package/templates/default/sections/footer/footer.schema.ts +170 -0
  48. package/templates/default/sections/footer/index.ts +14 -0
  49. package/templates/default/sections/gallery/gallery.schema.ts +1 -1
  50. package/templates/default/sections/header/header-default.tsx +322 -0
  51. package/templates/default/sections/header/header.schema.ts +168 -0
  52. package/templates/default/sections/header/index.ts +14 -0
  53. package/templates/default/sections/hero/hero.schema.ts +1 -1
  54. package/templates/default/sections/profile/index.ts +14 -0
  55. package/templates/default/sections/profile/profile-default.tsx +522 -0
  56. package/templates/default/sections/profile/profile.schema.ts +228 -0
  57. package/templates/default/sections/stats/stats.schema.ts +1 -1
  58. package/templates/default/sections/testimonials/testimonials.schema.ts +1 -1
  59. package/templates/default/sections-registry.ts +28 -0
  60. package/templates/default/theme.layout.ts +71 -2
@@ -455,6 +455,14 @@ function PreviewApp() {
455
455
  >("disconnected");
456
456
  const [error, setError] = useState<string | null>(null);
457
457
  const wsRef = useRef<WebSocket | null>(null);
458
+ const [isEditing, setIsEditing] = useState(() => {
459
+ if (typeof window !== "undefined") {
460
+ return (
461
+ new URLSearchParams(window.location.search).get("editing") === "true"
462
+ );
463
+ }
464
+ return false;
465
+ });
458
466
 
459
467
  // Load theme bundle
460
468
  const loadTheme = useCallback(async (timestamp?: number) => {
@@ -701,7 +709,7 @@ function PreviewApp() {
701
709
  section={section}
702
710
  schema={schema}
703
711
  template={template}
704
- isEditing={false}
712
+ isEditing={isEditing}
705
713
  data={sectionData}
706
714
  />
707
715
  );
@@ -712,16 +720,16 @@ function PreviewApp() {
712
720
  {/* Inject theme CSS variables (Gap 2) */}
713
721
  {themeCSS && <style dangerouslySetInnerHTML={{ __html: themeCSS }} />}
714
722
 
715
- {/* Header (Gap 1) */}
716
- {headerSections.length > 0 && (
723
+ {/* Header hidden if page config sets hideHeader: true */}
724
+ {headerSections.length > 0 && !currentPage.config?.hideHeader && (
717
725
  <header>{headerSections.map(renderSection)}</header>
718
726
  )}
719
727
 
720
728
  {/* Page sections */}
721
729
  <main>{pageSections.map(renderSection)}</main>
722
730
 
723
- {/* Footer (Gap 1) */}
724
- {footerSections.length > 0 && (
731
+ {/* Footer hidden if page config sets hideFooter: true */}
732
+ {footerSections.length > 0 && !currentPage.config?.hideFooter && (
725
733
  <footer>{footerSections.map(renderSection)}</footer>
726
734
  )}
727
735
  </>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onexapis/cli",
3
- "version": "1.1.38",
3
+ "version": "1.1.39",
4
4
  "description": "CLI tool for OneX theme development - scaffolds themes using @onexapis/core",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -57,7 +57,6 @@
57
57
  "chalk": "^5.3.0",
58
58
  "chokidar": "^4.0.0",
59
59
  "commander": "^12.1.0",
60
- "cross-spawn": "^7.0.6",
61
60
  "dotenv": "^17.3.1",
62
61
  "ejs": "^3.1.10",
63
62
  "esbuild": "^0.25.0",
@@ -78,7 +77,6 @@
78
77
  "devDependencies": {
79
78
  "@types/adm-zip": "^0.5.7",
80
79
  "@types/archiver": "^7.0.0",
81
- "@types/cross-spawn": "^6.0.6",
82
80
  "@types/ejs": "^3.1.5",
83
81
  "@types/fs-extra": "^11.0.4",
84
82
  "@types/inquirer": "^9.0.7",
@@ -0,0 +1,167 @@
1
+ # Authentication & Profile — Default Theme
2
+
3
+ This document covers the built-in authentication and profile sections included in the default theme template.
4
+
5
+ ## Overview
6
+
7
+ When you initialize a theme with `onexthm init`, it comes with 5 auth/profile sections and their corresponding hooks ready to use:
8
+
9
+ | Section | Path | Description |
10
+ | -------------------- | ------------------ | -------------------------------------------- |
11
+ | Auth Login | `/login` | Username/email + password sign-in |
12
+ | Auth Register | `/register` | Email, username, password, confirm password |
13
+ | Auth Forgot Password | `/forgot-password` | Request verification code via email/username |
14
+ | Auth Verify Code | `/verify-code` | 6-digit OTP input with resend + countdown |
15
+ | Profile | `/profile` | View/edit profile + change password |
16
+
17
+ ## Architecture
18
+
19
+ ```
20
+ Theme Section (UI only)
21
+ └── Theme Hook (form state, validation)
22
+ └── useAuth() from @onexapis/core/hooks (Zustand store)
23
+ └── AuthService (API calls)
24
+ └── ApiClient (HTTP client with token management)
25
+ ```
26
+
27
+ ### Hooks
28
+
29
+ All hooks live in `hooks/` and use `useAuth` from `@onexapis/core/hooks`:
30
+
31
+ | Hook | Uses | Manages |
32
+ | ----------------------- | ------------------------------------------------------------- | ---------------------------------------------------- |
33
+ | `useLoginForm` | `useAuth().login()` | Username, password, show/hide toggle, validation |
34
+ | `useRegisterForm` | `useAuth().signup()` | Email, username, password, confirm, validation |
35
+ | `useForgotPasswordForm` | `useAuth().forgotPassword()` | Username/email input, validation |
36
+ | `useVerifyCodeForm` | `useAuth().verifyCode()`, `verifyCodeReset()`, `resendCode()` | 6-digit OTP, auto-focus, paste, countdown |
37
+ | `useProfileForm` | `useAuth().updateProfile()`, `changePassword()`, `logout()` | Profile fields, dirty tracking, change password form |
38
+
39
+ ### API Endpoints
40
+
41
+ The `AuthService` in `@onexapis/core` handles all API calls:
42
+
43
+ | Action | Method | Endpoint |
44
+ | ------------------- | ------ | ----------------------------------- |
45
+ | Login | POST | `/auth/login` |
46
+ | Register | POST | `/auth/register` |
47
+ | Forgot Password | POST | `/auth/forgot` |
48
+ | Verify Code | POST | `/auth/confirm` |
49
+ | Verify Code (reset) | POST | `/auth/confirm_reset_password_code` |
50
+ | Resend Code | POST | `/auth/resend_code` |
51
+ | Reset Password | POST | `/auth/new_password` |
52
+ | Change Password | POST | `/auth/change_password` |
53
+ | Get User Info | GET | `/auth/user_info` |
54
+ | Update Profile | POST | `/auth/user_info` |
55
+
56
+ ### Token Handling
57
+
58
+ - **IdToken** → `Authorization: Bearer {IdToken}` header
59
+ - **AccessToken** → `?access_token={AccessToken}` query parameter
60
+ - **Tokens stored** in `localStorage` under key `auth_tokens`
61
+ - **Signup** auto-injects `company_id` (from `initializeOnex()` config) and `is_customer: true`
62
+
63
+ ## Editable Settings
64
+
65
+ All sections expose these layout settings via the editor sidebar:
66
+
67
+ | Setting | Type | Default | Description |
68
+ | ------------------ | ------ | --------- | ------------------------------------ |
69
+ | `backgroundColor` | color | `#F9FAFB` | Page background |
70
+ | `cardBackground` | color | `#FFFFFF` | Card/form container background |
71
+ | `primaryColor` | color | `#2563EB` | Buttons and links |
72
+ | `textColor` | color | `#111827` | Heading text color |
73
+ | `cardBorderRadius` | select | `2xl` | Card corner rounding (lg/xl/2xl/3xl) |
74
+
75
+ Plus all text labels, placeholders, button text, and URLs are customizable per section.
76
+
77
+ ## Auth Flow
78
+
79
+ ```
80
+ Register → Verify Code → Login → Profile
81
+ ↑ │
82
+ Forgot Password → Verify Code (reset) → Reset Password
83
+ ```
84
+
85
+ 1. **Register**: User fills email, username, password → `signup()` → redirects to `/verify-code?username=...`
86
+ 2. **Verify Code**: User enters 6-digit OTP → `verifyCode()` → redirects to `/login`
87
+ 3. **Login**: User signs in → `login()` → tokens stored → redirects to `/`
88
+ 4. **Forgot Password**: User enters email → `forgotPassword()` → redirects to `/verify-code?mode=reset&username=...`
89
+ 5. **Verify Code (reset)**: OTP → `verifyCodeReset()` → returns session → redirects to `/reset-password?session=...`
90
+ 6. **Profile**: View/edit name, phone, address → `updateProfile()`. Change password → `changePassword()`
91
+
92
+ ## Configuration
93
+
94
+ ### Required Environment Variables
95
+
96
+ ```env
97
+ NEXT_PUBLIC_API_URL=https://api-dev.onexeos.com
98
+ NEXT_PUBLIC_COMPANY_ID=your-company-id
99
+ ```
100
+
101
+ These are read by `initializeOnex()` which creates the `AuthService` and registers it with the `useAuth` Zustand store.
102
+
103
+ ## Testing Locally
104
+
105
+ ### Preview mode (UI only)
106
+
107
+ ```bash
108
+ onexthm dev
109
+ # Visit http://localhost:3456/login
110
+ ```
111
+
112
+ ### Preview with editing mode
113
+
114
+ ```bash
115
+ onexthm dev
116
+ # Visit http://localhost:3456/login?editing=true
117
+ ```
118
+
119
+ ### Preview with real API (requires .env)
120
+
121
+ Create `.env` in your theme directory:
122
+
123
+ ```env
124
+ NEXT_PUBLIC_API_URL=https://api-dev.onexeos.com
125
+ NEXT_PUBLIC_COMPANY_ID=your-company-id
126
+ ```
127
+
128
+ Then run `onexthm dev` — login/register/verify will make real API calls.
129
+
130
+ ## File Structure
131
+
132
+ ```
133
+ hooks/
134
+ index.ts
135
+ use-login-form.ts
136
+ use-register-form.ts
137
+ use-forgot-password-form.ts
138
+ use-verify-code-form.ts
139
+ use-profile-form.ts
140
+ sections/
141
+ auth-login/
142
+ auth-login-default.tsx # Login form UI
143
+ auth-login.schema.ts # Editable settings
144
+ index.ts
145
+ auth-register/
146
+ auth-register-default.tsx
147
+ auth-register.schema.ts
148
+ index.ts
149
+ auth-forgot-password/
150
+ auth-forgot-password-default.tsx
151
+ auth-forgot-password.schema.ts
152
+ index.ts
153
+ auth-verify-code/
154
+ auth-verify-code-default.tsx
155
+ auth-verify-code.schema.ts
156
+ index.ts
157
+ profile/
158
+ profile-default.tsx
159
+ profile.schema.ts
160
+ index.ts
161
+ pages/
162
+ login.ts
163
+ register.ts
164
+ forgot-password.ts
165
+ verify-code.ts
166
+ profile.ts
167
+ ```
@@ -369,7 +369,7 @@ import {
369
369
  | **State** (3) | `useCart`, `useAuth`, `useOrders` |
370
370
  | **Checkout** (8) | `useCheckout`, `usePayment`, `useOrderLookup`, `useOrderStatus`, `useOrderSuccess`, `useOrderSummary`, `useFinance`, `saveBuyNowItem` |
371
371
  | **Products** (3) | `useSearchProducts`, `useAddToCart`, `useProductDetail` |
372
- | **Theme** (8) | `useDesignSystem`, `useCommerceData`, `useThemeMode`, `useLocale`, `useViewport`, `usePageData`, `useWebsiteSettings`, `useMotion` |
372
+ | **Theme** (9) | `useDesignSystem`, `usePageBackground`, `useCommerceData`, `useThemeMode`, `useLocale`, `useViewport`, `usePageData`, `useWebsiteSettings`, `useMotion` |
373
373
  | **Utilities** (7) | `useDebounce`, `useMediaQuery`, `useIsMobile`, `useIsTablet`, `useIsDesktop`, `useContactForm`, `useCopyToClipboard`, `useFormatPrice`, `formatVndPrice` |
374
374
  | **Animation** (1) | `useFlyToCart` |
375
375
  | **Server** (4) | `fetchProducts`, `fetchBlogs`, `fetchSettings`, `prefetchSectionData` |
@@ -408,6 +408,198 @@ const { formatPrice } = useFormatPrice();
408
408
  formatPrice(1250000); // "1.250.000đ"
409
409
  ```
410
410
 
411
+ ### Orders & Payment Hooks
412
+
413
+ These hooks cover the full order lifecycle: creating orders, looking up orders, processing payments, and displaying order history.
414
+
415
+ #### `useOrders` — Order state management (Zustand)
416
+
417
+ ```tsx
418
+ import { useOrders } from "@onexapis/core/hooks";
419
+
420
+ const {
421
+ orders, // Order[] — list of orders
422
+ currentOrder, // Order | null — single order detail
423
+ isLoading,
424
+ error,
425
+ total,
426
+ totalPages,
427
+ currentPage,
428
+
429
+ // Actions
430
+ createOrder, // (data: CreateOrderData) => Promise<Order>
431
+ createPrivateOrder, // (data: CreateOrderData) => Promise<Order> — requires auth
432
+ fetchOrders, // (params?) => Promise<void> — public order list
433
+ fetchOrderHistory, // (params?) => Promise<void> — authenticated user's orders
434
+ fetchOrderById, // (orderId: string) => Promise<void>
435
+ fetchOrderByNumber, // (orderNumber: string) => Promise<void>
436
+ updateOrderStatus, // (orderId, status) => Promise<void>
437
+ cancelOrder, // (orderId) => Promise<void>
438
+ payOrder, // (orderId, paymentData?) => Promise<Order> — returns updated order
439
+ clearError,
440
+ clearCurrentOrder,
441
+ } = useOrders();
442
+ ```
443
+
444
+ **`payOrder` returns the updated `Order`** — always check `order.status` and `order.payment_status` before navigating to success. Do NOT blindly redirect after calling `payOrder`.
445
+
446
+ #### `useOrderLookup` — Search/track order by number or ID
447
+
448
+ ```tsx
449
+ import { useOrderLookup } from "@onexapis/core/hooks";
450
+
451
+ // Basic — manual search via input
452
+ const lookup = useOrderLookup();
453
+
454
+ // With auto-fetch from URL route param (for dynamic pages like /order-lookup/[orderId])
455
+ const routeParams = (data?.routeParams || {}) as Record<string, string>;
456
+ const lookup = useOrderLookup({
457
+ initialOrderId: routeParams.orderId, // auto-fetches on mount
458
+ });
459
+
460
+ // lookup.orderNumber — input value (string)
461
+ // lookup.setOrderNumber — update input
462
+ // lookup.handleTrackOrder — search (auto-detects UUID vs order number)
463
+ // lookup.handleClear — reset search
464
+ // lookup.order — Order | null (result)
465
+ // lookup.isSearching — boolean
466
+ // lookup.errorMessage — string | null
467
+ // lookup.getStatusLabel — (status: string) => Vietnamese label
468
+ // lookup.formatCurrency — (amount: number) => "1.250.000đ"
469
+ ```
470
+
471
+ **Dynamic page setup** for `/order-lookup/[orderId]`:
472
+
473
+ ```typescript
474
+ // pages/order-lookup.ts
475
+ export const orderLookupPageConfig = {
476
+ handle: "order-lookup",
477
+ path: "/order-lookup/[orderId]",
478
+ isDynamic: true,
479
+ dynamicSegments: ["orderId"],
480
+ type: "custom",
481
+ // ...sections
482
+ };
483
+ ```
484
+
485
+ #### `usePayment` — QR code payment with bank transfer
486
+
487
+ ```tsx
488
+ import { usePayment } from "@onexapis/core/hooks";
489
+
490
+ const payment = usePayment({
491
+ successRedirectUrl: "/order-success", // default
492
+ checkoutRedirectUrl: "/checkout", // default — redirect if no order info
493
+ isEditing: false,
494
+ });
495
+
496
+ // payment.orderId, payment.orderNumber, payment.total
497
+ // payment.qrCodeImage — data URI for QR code
498
+ // payment.qrNote — transfer note (e.g. "DH4X7K2M")
499
+ // payment.bankTransferData — { accountNumber, accountName, bankName }
500
+ // payment.isLoading, payment.isProcessing, payment.error
501
+ // payment.handleConfirmPayment — calls payOrder API, checks response, then redirects
502
+ // payment.handleCancel — clears order info, redirects to checkout
503
+ ```
504
+
505
+ **`handleConfirmPayment` validates the API response:**
506
+
507
+ - Checks `order.status` — blocks redirect if `failed`, `cancelled`, or `refunded`
508
+ - Checks `order.payment_status` — blocks redirect if `failed`
509
+ - Only navigates to success page when payment is valid
510
+ - Shows error message from API on failure
511
+
512
+ #### `useOrderSuccess` — Post-purchase success page
513
+
514
+ ```tsx
515
+ import { useOrderSuccess } from "@onexapis/core/hooks";
516
+
517
+ const success = useOrderSuccess();
518
+ // success.orderId, success.orderNumber, success.displayOrderNumber
519
+ // success.trackOrderUrl — link to order lookup page
520
+ // success.hasOrder — boolean (false if no order in URL params)
521
+ ```
522
+
523
+ #### `useOrderSummary` — Calculate order totals
524
+
525
+ ```tsx
526
+ import { useOrderSummary, calculateOrderTotal } from "@onexapis/core/hooks";
527
+
528
+ const summary = useOrderSummary({
529
+ vatRate: 0.1,
530
+ shippingFee: 30000,
531
+ discount: 0,
532
+ freeShippingThreshold: 500000,
533
+ });
534
+ // summary.subtotal, summary.discount, summary.shipping, summary.vat, summary.total
535
+ // summary.formatPrice(amount)
536
+
537
+ // Standalone helper (no hook needed)
538
+ const total = calculateOrderTotal(items, { vatRate: 0.1, shippingFee: 30000 });
539
+ ```
540
+
541
+ #### `useOrderStatus` — Status label translation
542
+
543
+ ```tsx
544
+ import { useOrderStatus, getOrderStatusLabel } from "@onexapis/core/hooks";
545
+
546
+ const { getLabel, getPaymentLabel } = useOrderStatus();
547
+ getLabel("shipping"); // "Đang giao hàng"
548
+ getLabel("completed"); // "Hoàn thành"
549
+ getPaymentLabel("paid"); // "Đã thanh toán"
550
+
551
+ // Or use the standalone function
552
+ getOrderStatusLabel("pending"); // "Chờ xử lý"
553
+ ```
554
+
555
+ #### `useOrderListPage` — Order history page
556
+
557
+ ```tsx
558
+ import { useOrderListPage } from "@onexapis/core/hooks";
559
+
560
+ const orderList = useOrderListPage({ autoFetch: true, collapsedItemCount: 2 });
561
+ // orderList.orders — Order[]
562
+ // orderList.isLoading, orderList.error
563
+ // orderList.toggleOrder — expand/collapse order items
564
+ // orderList.refresh — re-fetch orders
565
+ // orderList.getDisplayItems — get visible items (respects collapsed state)
566
+ // orderList.hasMoreItems — check if order has hidden items
567
+ // orderList.formatCurrency, orderList.formatDate
568
+ ```
569
+
570
+ #### Order Type Reference
571
+
572
+ ```typescript
573
+ interface Order {
574
+ id: string;
575
+ order_number: string;
576
+ status: string; // pending | confirmed | processing | packing | shipping | completed | cancelled | failed | refunded
577
+ payment_status?: string; // paid | unpaid | failed
578
+ payment_method?: string;
579
+ total: number;
580
+ sub_total?: number;
581
+ tax?: number;
582
+ shipping_fee?: number;
583
+ discount?: number;
584
+ order_line_items?: OrderLineItem[];
585
+ shipping_address?: OrderAddress;
586
+ note?: string;
587
+ created_at: string;
588
+ updated_at?: string;
589
+ }
590
+
591
+ interface OrderLineItem {
592
+ name: string;
593
+ unit_price: number;
594
+ quantity: number;
595
+ product_id: string;
596
+ variant_name?: string;
597
+ image_url?: string;
598
+ sku?: string;
599
+ location?: { id: string; name: string };
600
+ }
601
+ ```
602
+
411
603
  ## Color System
412
604
 
413
605
  Colors in OneX themes work with **two approaches** (both are valid):
@@ -836,6 +1028,147 @@ export function MySection({ section, isEditing }: SectionComponentProps) {
836
1028
  }
837
1029
  ```
838
1030
 
1031
+ ## Page Background System
1032
+
1033
+ Every page has a background that comes from the theme by default, overridable per-page. This is a **page-level** feature (not section-level) — it wraps all sections on the page.
1034
+
1035
+ ### How it works
1036
+
1037
+ Background is resolved with priority:
1038
+
1039
+ 1. **Per-page override** (`PageConfig.background`) — set via editor or page config
1040
+ 2. **Theme default** (`ThemeDesignSystem.pageBackground`) — set in `theme.layout.ts`
1041
+ 3. **Fallback** — solid white `#FFFFFF`
1042
+
1043
+ ### Setting the theme default
1044
+
1045
+ In `theme.layout.ts`, add `pageBackground` inside `designSystem`:
1046
+
1047
+ ```typescript
1048
+ export const layoutConfig: ThemeLayoutConfig = {
1049
+ // ...headerSections, footerSections...
1050
+ designSystem: {
1051
+ colors: {
1052
+ primaryColor: "#3B82F6",
1053
+ secondaryColor: "#8B5CF6",
1054
+ colorMode: "light",
1055
+ },
1056
+ typography: { headingFont: "system-ui", bodyFont: "system-ui" },
1057
+ layout: { spacing: "comfortable" },
1058
+ pageBackground: {
1059
+ type: "solid", // "solid" | "gradient" | "image" | "pattern" | "none"
1060
+ color: "#FFFFFF", // Base background color
1061
+ },
1062
+ },
1063
+ };
1064
+ ```
1065
+
1066
+ ### Background types
1067
+
1068
+ | Type | Fields | Example |
1069
+ | ---------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
1070
+ | `solid` | `color` | `{ type: "solid", color: "#FFF8F0" }` |
1071
+ | `gradient` | `gradient`, `color` (fallback) | `{ type: "gradient", gradient: "linear-gradient(135deg, #667eea, #764ba2)" }` |
1072
+ | `image` | `image`, `imageSize`, `imagePosition`, `imageFixed`, `color` (fallback) | `{ type: "image", image: "/bg.jpg", imageSize: "cover", imageFixed: true }` |
1073
+ | `pattern` | `pattern`, `color`, `opacity`, `overlayColor` | `{ type: "pattern", pattern: "dots", color: "#FAFAFA", opacity: 0.05 }` |
1074
+ | `none` | — | `{ type: "none" }` (transparent, no wrapper) |
1075
+
1076
+ ### Built-in CSS patterns
1077
+
1078
+ These work out of the box with `type: "pattern"`:
1079
+
1080
+ - `dots` — dot grid
1081
+ - `grid` — line grid
1082
+ - `diagonal-lines` — 45-degree stripes
1083
+ - `cross-dots` — offset dot grid
1084
+ - `noise` — fractal noise texture
1085
+
1086
+ ### Custom animated patterns
1087
+
1088
+ Themes can provide custom pattern renderers (e.g., SVG-based animated backgrounds). Export the component from `bundle-entry.ts`:
1089
+
1090
+ ```typescript
1091
+ // In bundle-entry.ts
1092
+ export { SenBackground } from "./assets/sen-background";
1093
+ ```
1094
+
1095
+ The storefront will pick up exported `*Background` components and match them to pattern names. For example, `SenBackground` maps to `pattern: "sen"`.
1096
+
1097
+ Custom pattern components receive these props:
1098
+
1099
+ ```typescript
1100
+ interface PatternProps {
1101
+ opacity?: number; // Pattern opacity (default: 0.05)
1102
+ color?: string; // Overlay color
1103
+ className?: string; // Additional CSS classes
1104
+ }
1105
+ ```
1106
+
1107
+ ### Per-page override
1108
+
1109
+ In a page config (e.g., `pages/about.ts`), set `background`:
1110
+
1111
+ ```typescript
1112
+ export const aboutPageConfig = {
1113
+ title: "About",
1114
+ handle: "about",
1115
+ // ...other config...
1116
+ background: {
1117
+ type: "gradient",
1118
+ gradient: "linear-gradient(180deg, #EEF2FF 0%, #FFFFFF 50%)",
1119
+ },
1120
+ };
1121
+ ```
1122
+
1123
+ ### Accessing in sections via hook
1124
+
1125
+ Use `usePageBackground()` to read the resolved background in section components:
1126
+
1127
+ ```tsx
1128
+ import { usePageBackground } from "@onexapis/core/hooks";
1129
+
1130
+ export function MySection({ section }: SectionComponentProps) {
1131
+ const { config, styles, hasPattern, pattern, patternOpacity } =
1132
+ usePageBackground();
1133
+ // config.type — "solid" | "gradient" | "image" | "pattern" | "none"
1134
+ // config.color — base color
1135
+ // styles — React.CSSProperties derived from config
1136
+ // hasPattern — boolean, true if type === "pattern" && pattern is set
1137
+ // pattern — pattern name (e.g., "sen", "dots")
1138
+ // patternOpacity — number (0-1)
1139
+ }
1140
+ ```
1141
+
1142
+ ### PageBackgroundConfig type reference
1143
+
1144
+ ```typescript
1145
+ interface PageBackgroundConfig {
1146
+ type: "solid" | "gradient" | "image" | "pattern" | "none";
1147
+ color?: string; // Base background color (hex/CSS)
1148
+ gradient?: string; // CSS gradient value
1149
+ image?: string; // Background image URL
1150
+ imageSize?: "cover" | "contain" | "auto";
1151
+ imagePosition?: string; // CSS background-position (default: "center")
1152
+ imageFixed?: boolean; // Fixed background attachment
1153
+ pattern?: string; // Pattern identifier
1154
+ opacity?: number; // Pattern/overlay opacity (0-1, default: 0.05)
1155
+ overlayColor?: string; // Pattern overlay color
1156
+ className?: string; // Additional CSS classes
1157
+ }
1158
+ ```
1159
+
1160
+ ### CSS variables
1161
+
1162
+ The storefront injects these CSS variables when a page background is configured:
1163
+
1164
+ - `--page-bg-color` — base color
1165
+ - `--page-bg-gradient` — gradient value
1166
+ - `--page-bg-pattern` — pattern name
1167
+ - `--page-bg-pattern-opacity` — opacity value
1168
+ - `--page-bg-overlay-color` — overlay color
1169
+
1170
+ Use these in custom CSS if needed: `background-color: var(--page-bg-color);`
1171
+
839
1172
  ## Dark/Light Mode
840
1173
 
841
1174
  Themes support light, dark, and system color modes via `useThemeMode()`: