@onexapis/cli 1.1.44 → 1.1.46

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.
@@ -1,1764 +1,126 @@
1
- # CLAUDE.md — OneX Theme Development Guide
1
+ # CLAUDE.md — Routing rules for AI agents
2
2
 
3
- This file provides context to AI assistants (Claude, Cursor, Copilot) for developing OneX themes.
3
+ This is an **OneX theme project**. It ships with the `onexthm` MCP server,
4
+ which provides tools that **write section files for you**. You must use those
5
+ tools as your first move on every theme task.
4
6
 
5
- ## MANDATORY WORKFLOW USE MCP TOOLS FIRST
6
-
7
- **You MUST use the `onexthm` MCP tools as your PRIMARY workflow for all theme development tasks. Do NOT write section files manually when a tool can generate them.**
8
-
9
- ### Required tool usage
10
-
11
- | Task | MUST use | Why |
12
- | ---------------------------------- | ------------------------------------- | ------------------------------------------------------------------- |
13
- | Create a new section | `onexthm_create_section` | Guarantees correct file structure, schema, data attributes, imports |
14
- | Convert HTML/React code to section | `onexthm_from_html` | Parses HTML elements and maps to OneX components automatically |
15
- | Convert Figma design to section | `onexthm_from_figma` + `figma` tools | Extracts layout, colors, typography from design |
16
- | Generate schema from description | `onexthm_generate_schema` | Infers category, fields, blocks, data requirements |
17
- | Validate theme | `onexthm_validate` | Catches missing data attributes, wrong imports, structural errors |
18
- | Look up hooks | `onexthm_list_hooks` | Shows all 44 hooks with params, returns, and examples |
19
- | Look up components | Read `onexthm://components` resource | Shows all component types, fields, slots |
20
- | Look up field types | Read `onexthm://field-types` resource | Shows all 25 field types with options |
21
- | Look up rules | Read `onexthm://rules` resource | Shows DOs/DON'Ts and patterns |
22
-
23
- ### Workflow for creating sections
24
-
25
- 1. **From a description**: Use `onexthm_generate_schema` or `onexthm_create_section`
26
- 2. **From HTML/React code**: Use `onexthm_from_html` — it parses headings, paragraphs, buttons, images, lists, etc. and maps them to OneX components
27
- 3. **From Figma design**: Use `figma:get_metadata` → `onexthm_from_figma`
28
- 4. **Always validate after**: Use `onexthm_validate`
29
- 5. **Then customize**: Edit the generated files to refine layout and styling
30
-
31
- ### When you have HTML/React code to convert
32
-
33
- If the user provides HTML, JSX, or a React component, ALWAYS use `onexthm_from_html` first:
34
-
35
- ```
36
- onexthm_from_html({
37
- htmlCode: "<the HTML/JSX code>",
38
- sectionName: "section-name",
39
- category: "hero" // optional
40
- })
41
- ```
42
-
43
- This automatically:
44
-
45
- - Maps `<h1>`-`<h6>` → heading components
46
- - Maps `<p>`, `<span>` → paragraph components
47
- - Maps `<button>`, `<a class="btn">` → button components
48
- - Maps `<img>` → image components
49
- - Maps `<svg>`, icons → icon components
50
- - Maps `<ul>/<ol>` → list components
51
- - Maps `<hr>` → divider components
52
- - Detects repeating patterns → block definitions
53
- - Extracts Tailwind classes → layout settings
54
- - Generates schema + component + index files
7
+ For deep reference (component types, hooks, animation tokens, color system,
8
+ dark mode, locale, full architecture), read **`THEME_REFERENCE.md`** in this
9
+ directory or call the relevant `onexthm_*` tool.
55
10
 
56
11
  ---
57
12
 
58
- ## CRITICAL RULES — READ FIRST
59
-
60
- **IMPORTANT: When creating new sections, blocks, or modifying theme code, you MUST follow these rules. Violations will cause the editor and storefront to break.**
61
-
62
- ### Creating New Sections
63
-
64
- Every section MUST use `ComponentRenderer` and `BlockRenderer` from `@onexapis/core` to render content. Sections define LAYOUT only — all content (text, images, buttons) is rendered through Blocks and Components.
65
-
66
- Use `onexthm_create_section` MCP tool or `onexthm create:section` CLI to scaffold. If writing manually, every section MUST have:
67
-
68
- 1. **`"use client"` directive** at the top
69
- 2. **Import from `@onexapis/core/renderers`** — use `ComponentRenderer` and `BlockRenderer`
70
- 3. **Import from `@onexapis/core/utils`** — use `getSectionValues`, `toComponentInstance`, `filterEnabledComponents`
71
- 4. **`data-section-id={section.id}`** attribute on the root element
72
- 5. **`data-section-type={section.type}`** attribute on the root element
73
- 6. **`data-block-id={block.id}`** and **`data-block-type={block.type}`** on every block wrapper
74
- 7. **Handle `isEditing` prop** — show placeholders when empty, disable links
75
- 8. **A matching `.schema.ts` file** with type, name, category, settings, defaults
76
- 9. **An `index.ts`** that re-exports the component and schema
77
- 10. **Registration in `sections-registry.ts`**
78
-
79
- ### NEVER do these
80
-
81
- - **NEVER render text/buttons/icons directly** — always use `ComponentRenderer` from core
82
- - **NEVER use `<h1>`, `<p>`, `<button>` for content** — use `ComponentRenderer` which renders heading, paragraph, button components with proper editor integration
83
- - **NEVER hardcode content** — content comes from `section.components` and `section.blocks`, rendered via `ComponentRenderer`/`BlockRenderer`
84
- - **NEVER skip data attributes** — without `data-section-id`, `data-block-id`, the editor cannot select or edit sections
85
- - **NEVER use `useEffect` for data fetching** — use `useProducts()`, `useBlogs()` hooks
86
- - **NEVER import from `@onexapis/core/internal`**
87
-
88
- ### Section Template (MUST follow exactly)
89
-
90
- ```tsx
91
- "use client";
92
-
93
- import type { SectionComponentProps } from "@onexapis/core/types";
94
- import coreRenderers from "@onexapis/core/renderers";
95
- import coreUtils from "@onexapis/core/utils";
96
-
97
- const { ComponentRenderer, BlockRenderer } = coreRenderers;
98
- const { toComponentInstance, getSectionValues, filterEnabledComponents } =
99
- coreUtils;
13
+ ## Before you start
100
14
 
101
- export function MySectionDefault({
102
- section,
103
- schema,
104
- isEditing,
105
- }: SectionComponentProps) {
106
- const { settings } = getSectionValues(section, schema);
107
- const components = filterEnabledComponents(section.components || []);
108
- const blocks = (section.blocks || []).filter((b) => b.enabled !== false);
15
+ At the start of every session, confirm the `onexthm_*` tools are available
16
+ in your tool list. If they are not, STOP and tell the user:
109
17
 
110
- return (
111
- <section
112
- data-section-id={section.id}
113
- data-section-type={section.type}
114
- data-section-template="default"
115
- className="py-16"
116
- style={{ backgroundColor: String(settings.backgroundColor || "#FFFFFF") }}
117
- >
118
- <div className="container mx-auto px-4 max-w-6xl">
119
- {/* MUST use ComponentRenderer for section-level components */}
120
- {components.map((comp) => (
121
- <ComponentRenderer
122
- key={comp.id}
123
- instance={toComponentInstance(comp)}
124
- sectionId={section.id}
125
- isEditing={isEditing}
126
- />
127
- ))}
18
+ > "The onexthm MCP server is not loaded. Restart your AI client after
19
+ > verifying `.mcp.json` registers `@onexapis/theme-mcp`."
128
20
 
129
- {/* MUST use BlockRenderer for blocks */}
130
- {blocks.map((block) => (
131
- <div
132
- key={block.id}
133
- data-section-id={section.id}
134
- data-block-id={block.id}
135
- data-block-type={block.type}
136
- >
137
- <BlockRenderer
138
- block={block}
139
- sectionId={section.id}
140
- isEditing={isEditing}
141
- />
142
- </div>
143
- ))}
144
-
145
- {/* MUST show empty state in editor */}
146
- {blocks.length === 0 && isEditing && (
147
- <div className="text-center py-12 border-2 border-dashed border-gray-300 rounded-lg">
148
- <p className="text-gray-500">Add blocks to populate this section</p>
149
- </div>
150
- )}
151
- </div>
152
- </section>
153
- );
154
- }
155
-
156
- export default MySectionDefault;
157
- ```
158
-
159
- ### How content rendering works
160
-
161
- - **Sections** define LAYOUT (grid, padding, background color)
162
- - **Components** (from core) render CONTENT (text, images, buttons)
163
- - A section component NEVER renders `<h1>Hello</h1>` directly — instead:
164
-
165
- ```tsx
166
- // WRONG ❌
167
- <h1>{settings.title}</h1>;
168
-
169
- // CORRECT ✅
170
- const titleComp = components.find((c) => c.slot === "section-title");
171
- {
172
- titleComp && (
173
- <ComponentRenderer
174
- instance={toComponentInstance(titleComp)}
175
- sectionId={section.id}
176
- isEditing={isEditing}
177
- />
178
- );
179
- }
180
- ```
181
-
182
- - The editor manages component content — users edit text/images through the sidebar, not through section settings
21
+ Do not silently fall back to writing files by hand.
183
22
 
184
23
  ---
185
24
 
186
- ## System Overview
187
-
188
- A OneX theme is a collection of **sections** compiled into a browser-ready JS bundle. Themes are:
189
-
190
- 1. Developed locally with `onexthm dev`
191
- 2. Compiled with `onexthm build` (esbuild → ES module)
192
- 3. Uploaded to S3 with `onexthm upload` (bundle.zip)
193
- 4. Loaded by the storefront (customer-facing site) and editor (visual editor)
194
-
195
- Both storefront and editor render the same bundle — sections must look identical in both.
196
-
197
- ## Architecture
198
-
199
- ```
200
- Theme
201
- ├── Sections Top-level page blocks (hero, features, pricing, footer)
202
- │ ├── Blocks Nested containers inside sections (feature-item, pricing-tier)
203
- │ └── Components Atomic UI rendered by @onexapis/core (heading, paragraph, button, icon, badge, divider, image)
204
- └── Pages Page configs that list which sections to show
205
- ```
206
-
207
- **Key principle**: Sections define layout & structure. Components (from core) handle rendering text, images, buttons with inline styles. The section component orchestrates how core components are arranged.
208
-
209
- ## File Structure
210
-
211
- ```
212
- my-theme/
213
- ├── theme.config.ts # Colors, typography, spacing, breakpoints
214
- ├── bundle-entry.ts # Build entry point (DO NOT import React here)
215
- ├── sections-registry.ts # Lazy-loaded section imports
216
- ├── theme.layout.ts # Header/footer section configuration
217
- ├── package.json # @onexapis/core as dependency
218
- ├── tsconfig.json # ES2020 target, react-jsx
219
- ├── esbuild.config.js # Bundle → dist/bundle.mjs
220
- ├── sections/
221
- │ └── hero/
222
- │ ├── hero-default.tsx # React component (the visual)
223
- │ ├── hero.schema.ts # Schema (settings, fields, defaults)
224
- │ └── index.ts # Re-exports component + schema
225
- ├── pages/
226
- │ └── home.ts # Page config with section instances
227
- └── CLAUDE.md # This file
228
- ```
229
-
230
- ## Section Component Pattern
231
-
232
- Every section component receives `SectionComponentProps` and uses core renderers:
233
-
234
- ```tsx
235
- "use client";
236
-
237
- import type { SectionComponentProps } from "@onexapis/core/types";
238
- import coreRenderers from "@onexapis/core/renderers";
239
- import coreUtils from "@onexapis/core/utils";
240
-
241
- const { ComponentRenderer, BlockRenderer } = coreRenderers;
242
- const { toComponentInstance, getSectionValues, filterEnabledComponents } =
243
- coreUtils;
244
-
245
- export function MySection({
246
- section,
247
- schema,
248
- isEditing,
249
- data,
250
- }: SectionComponentProps) {
251
- const { settings } = getSectionValues(section, schema);
252
- const { title, backgroundColor } = settings;
253
-
254
- // Section-level components (title, subtitle, etc.)
255
- const components = filterEnabledComponents(section.components || []);
256
- const titleComp = components.find((c) => c.slot === "section-title");
257
-
258
- // Nested blocks
259
- const blocks = (section.blocks || []).filter((b) => b.enabled !== false);
260
-
261
- return (
262
- <section
263
- className="py-16"
264
- style={{ backgroundColor: String(backgroundColor || "#FFFFFF") }}
265
- data-section-id={section.id}
266
- data-section-type={section.type}
267
- >
268
- <div className="container mx-auto px-4 max-w-6xl">
269
- {/* Render a core component (heading, paragraph, etc.) */}
270
- {titleComp && (
271
- <ComponentRenderer
272
- instance={toComponentInstance(titleComp)}
273
- sectionId={section.id}
274
- isEditing={isEditing}
275
- />
276
- )}
277
-
278
- {/* Render blocks (nested containers with their own components) */}
279
- {blocks.map((block) => (
280
- <div
281
- key={block.id}
282
- data-section-id={section.id}
283
- data-block-id={block.id}
284
- data-block-type={block.type}
285
- >
286
- <BlockRenderer
287
- block={block}
288
- sectionId={section.id}
289
- isEditing={isEditing}
290
- />
291
- </div>
292
- ))}
293
-
294
- {/* Editor empty state */}
295
- {blocks.length === 0 && isEditing && (
296
- <div className="text-center py-12 border-2 border-dashed border-gray-300 rounded-lg">
297
- <p className="text-gray-500">Add blocks to populate this section</p>
298
- </div>
299
- )}
300
- </div>
301
- </section>
302
- );
303
- }
304
-
305
- export default MySection;
306
- ```
307
-
308
- ## Schema Definition Pattern
309
-
310
- Every section needs a schema that defines its settings, templates, and defaults:
311
-
312
- ```tsx
313
- import type { SectionSchema } from "@onexapis/core/types";
314
-
315
- export const mySchema: SectionSchema = {
316
- type: "my-section",
317
- name: "My Section",
318
- description: "A custom section",
319
- icon: "layout",
320
- category: "content", // header | hero | content | features | testimonials | cta | gallery | pricing | faq | team | contact | footer | products | blog | newsletter
321
- templates: [
322
- { id: "default", name: "Default", isDefault: true },
323
- { id: "minimal", name: "Minimal" },
324
- ],
325
- settings: [
326
- // Text fields
327
- { id: "title", type: "text", label: "Title", default: "Hello World" },
328
- { id: "subtitle", type: "textarea", label: "Subtitle" },
329
-
330
- // Numbers
331
- { id: "columns", type: "number", label: "Columns", default: 3 },
332
- { id: "limit", type: "range", label: "Items", default: 8, min: 1, max: 20 },
333
-
334
- // Selection
335
- {
336
- id: "layout",
337
- type: "select",
338
- label: "Layout",
339
- default: "grid",
340
- options: [
341
- { value: "grid", label: "Grid" },
342
- { value: "list", label: "List" },
343
- ],
344
- },
345
-
346
- // Boolean
347
- { id: "showTitle", type: "checkbox", label: "Show Title", default: true },
348
-
349
- // Colors — use hex directly (standard approach)
350
- {
351
- id: "backgroundColor",
352
- type: "color",
353
- label: "Background",
354
- default: "#FFFFFF",
355
- },
356
- {
357
- id: "textColor",
358
- type: "color_token",
359
- label: "Text Color",
360
- default: "#1F2937",
361
- },
362
-
363
- // Images
364
- { id: "image", type: "image", label: "Background Image" },
365
- ],
366
- defaults: {
367
- title: "Hello World",
368
- columns: 3,
369
- backgroundColor: "#FFFFFF",
370
- },
371
- // Declare if this section needs commerce data for SSR
372
- dataRequirements: {
373
- products: false,
374
- blogs: false,
375
- settings: false,
376
- },
377
- };
378
- ```
379
-
380
- ## Available Field Types
381
-
382
- | Type | Description | Example Use |
383
- | ------------------------ | ------------------------------------------------- | ----------------------------- |
384
- | `text` | Single-line text input | Title, label, button text |
385
- | `textarea` | Multi-line text | Description, bio |
386
- | `number` | Numeric input | Columns, count, limit |
387
- | `range` | Slider with min/max | Opacity, spacing, items count |
388
- | `checkbox` / `boolean` | Toggle switch | Show/hide elements |
389
- | `select` | Dropdown selector | Layout variant, size |
390
- | `radio` | Radio button group | Alignment, position |
391
- | `color` | Color picker (hex + system tokens) | Any color |
392
- | `color_token` | Color picker (tokens: Primary/Secondary + Custom) | Text color, icon color |
393
- | `color_background` | Solid color or gradient | Section background |
394
- | `image` / `image_picker` | Image upload/selection | Hero image, avatar |
395
- | `video_url` | Video embed URL | Background video |
396
- | `font` / `font_picker` | Font family selector | Section font override |
397
- | `text_alignment` | Left / center / right | Text alignment |
398
- | `richtext` | Rich text editor | Content blocks, articles |
399
- | `inline_richtext` | Inline rich text | Short formatted text |
400
- | `html` | Raw HTML/code editor | Custom embeds, scripts |
401
- | `url` | URL input | Link href |
402
- | `array` / `repeater` | List of repeating items | Feature list, social links |
403
-
404
- ## Available Hooks (44 hooks)
405
-
406
- All hooks import from `@onexapis/core/hooks`. Use `onexthm_list_hooks` MCP tool for full API details.
407
-
408
- ```tsx
409
- import {
410
- useProducts,
411
- useCart,
412
- useCheckout,
413
- useDesignSystem,
414
- } from "@onexapis/core/hooks";
415
- ```
416
-
417
- ### Quick Reference
418
-
419
- | Category | Hooks |
420
- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
421
- | **Commerce** (11) | `useProducts`, `useProductBySlug`, `useProductById`, `useProductCategories`, `useBlogs`, `useBlogBySlug`, `useBlogById`, `useBlogCategories`, `useSettings`, `useProductListing`, `useBlogListing` |
422
- | **State** (3) | `useCart`, `useAuth`, `useOrders` |
423
- | **Checkout** (8) | `useCheckout`, `usePayment`, `useOrderLookup`, `useOrderStatus`, `useOrderSuccess`, `useOrderSummary`, `useFinance`, `saveBuyNowItem` |
424
- | **Products** (3) | `useSearchProducts`, `useAddToCart`, `useProductDetail` |
425
- | **Theme** (9) | `useDesignSystem`, `usePageBackground`, `useCommerceData`, `useThemeMode`, `useLocale`, `useViewport`, `usePageData`, `useWebsiteSettings`, `useMotion` |
426
- | **Utilities** (7) | `useDebounce`, `useMediaQuery`, `useIsMobile`, `useIsTablet`, `useIsDesktop`, `useContactForm`, `useCopyToClipboard`, `useFormatPrice`, `formatVndPrice` |
427
- | **Animation** (1) | `useFlyToCart` |
428
- | **Server** (4) | `fetchProducts`, `fetchBlogs`, `fetchSettings`, `prefetchSectionData` |
429
-
430
- ### Most Used Patterns
431
-
432
- ```tsx
433
- // Fetch products
434
- const { data, isLoading } = useProducts({ limit: 8 });
435
- const products = data?.data ?? [];
436
-
437
- // Cart
438
- const { items, addItem, itemCount, subtotal } = useCart();
439
-
440
- // Auth
441
- const { user, isAuthenticated, login, logout } = useAuth();
442
-
443
- // Checkout (full form)
444
- const checkout = useCheckout({ paymentRedirectUrl: "/payment" });
445
- // checkout.fullName, checkout.handleCheckout, checkout.errors
446
-
447
- // Product detail with buy now
448
- const detail = useProductDetail({ slug: routeParams.slug });
449
- // detail.product, detail.handleAddToCart, detail.handleBuyNow
450
-
451
- // Search with debounce
452
- const search = useSearchProducts({ debounceMs: 300 });
453
- // search.query, search.setQuery, search.results
454
-
455
- // Responsive
456
- const isMobile = useIsMobile();
457
- const isDesktop = useIsDesktop();
458
-
459
- // Price formatting
460
- const { formatPrice } = useFormatPrice();
461
- formatPrice(1250000); // "1.250.000đ"
462
- ```
463
-
464
- ### Orders & Payment Hooks
465
-
466
- These hooks cover the full order lifecycle: creating orders, looking up orders, processing payments, and displaying order history.
467
-
468
- #### `useOrders` — Order state management (Zustand)
469
-
470
- ```tsx
471
- import { useOrders } from "@onexapis/core/hooks";
472
-
473
- const {
474
- orders, // Order[] — list of orders
475
- currentOrder, // Order | null — single order detail
476
- isLoading,
477
- error,
478
- total,
479
- totalPages,
480
- currentPage,
481
-
482
- // Actions
483
- createOrder, // (data: CreateOrderData) => Promise<Order>
484
- createPrivateOrder, // (data: CreateOrderData) => Promise<Order> — requires auth
485
- fetchOrders, // (params?) => Promise<void> — public order list
486
- fetchOrderHistory, // (params?) => Promise<void> — authenticated user's orders
487
- fetchOrderById, // (orderId: string) => Promise<void>
488
- fetchOrderByNumber, // (orderNumber: string) => Promise<void>
489
- updateOrderStatus, // (orderId, status) => Promise<void>
490
- cancelOrder, // (orderId) => Promise<void>
491
- payOrder, // (orderId, paymentData?) => Promise<Order> — returns updated order
492
- clearError,
493
- clearCurrentOrder,
494
- } = useOrders();
495
- ```
496
-
497
- **`payOrder` returns the updated `Order`** — always check `order.status` and `order.payment_status` before navigating to success. Do NOT blindly redirect after calling `payOrder`.
498
-
499
- #### `useOrderLookup` — Search/track order by number or ID
500
-
501
- ```tsx
502
- import { useOrderLookup } from "@onexapis/core/hooks";
503
-
504
- // Basic — manual search via input
505
- const lookup = useOrderLookup();
506
-
507
- // With auto-fetch from URL route param (for dynamic pages like /order-lookup/[orderId])
508
- const routeParams = (data?.routeParams || {}) as Record<string, string>;
509
- const lookup = useOrderLookup({
510
- initialOrderId: routeParams.orderId, // auto-fetches on mount
511
- });
512
-
513
- // lookup.orderNumber — input value (string)
514
- // lookup.setOrderNumber — update input
515
- // lookup.handleTrackOrder — search (auto-detects UUID vs order number)
516
- // lookup.handleClear — reset search
517
- // lookup.order — Order | null (result)
518
- // lookup.isSearching — boolean
519
- // lookup.errorMessage — string | null
520
- // lookup.getStatusLabel — (status: string) => Vietnamese label
521
- // lookup.formatCurrency — (amount: number) => "1.250.000đ"
522
- ```
523
-
524
- **Dynamic page setup** for `/order-lookup/[orderId]`:
525
-
526
- ```typescript
527
- // pages/order-lookup.ts
528
- export const orderLookupPageConfig = {
529
- handle: "order-lookup",
530
- path: "/order-lookup/[orderId]",
531
- isDynamic: true,
532
- dynamicSegments: ["orderId"],
533
- type: "custom",
534
- // ...sections
535
- };
536
- ```
537
-
538
- #### `usePayment` — QR code payment with bank transfer
539
-
540
- ```tsx
541
- import { usePayment } from "@onexapis/core/hooks";
542
-
543
- const payment = usePayment({
544
- successRedirectUrl: "/order-success", // default
545
- checkoutRedirectUrl: "/checkout", // default — redirect if no order info
546
- isEditing: false,
547
- });
548
-
549
- // payment.orderId, payment.orderNumber, payment.total
550
- // payment.qrCodeImage — data URI for QR code
551
- // payment.qrNote — transfer note (e.g. "DH4X7K2M")
552
- // payment.bankTransferData — { accountNumber, accountName, bankName }
553
- // payment.isLoading, payment.isProcessing, payment.error
554
- // payment.handleConfirmPayment — calls payOrder API, checks response, then redirects
555
- // payment.handleCancel — clears order info, redirects to checkout
556
- ```
557
-
558
- **`handleConfirmPayment` validates the API response:**
559
-
560
- - Checks `order.status` — blocks redirect if `failed`, `cancelled`, or `refunded`
561
- - Checks `order.payment_status` — blocks redirect if `failed`
562
- - Only navigates to success page when payment is valid
563
- - Shows error message from API on failure
564
-
565
- #### `useOrderSuccess` — Post-purchase success page
566
-
567
- ```tsx
568
- import { useOrderSuccess } from "@onexapis/core/hooks";
569
-
570
- const success = useOrderSuccess();
571
- // success.orderId, success.orderNumber, success.displayOrderNumber
572
- // success.trackOrderUrl — link to order lookup page
573
- // success.hasOrder — boolean (false if no order in URL params)
574
- ```
575
-
576
- #### `useOrderSummary` — Calculate order totals
577
-
578
- ```tsx
579
- import { useOrderSummary, calculateOrderTotal } from "@onexapis/core/hooks";
580
-
581
- const summary = useOrderSummary({
582
- vatRate: 0.1,
583
- shippingFee: 30000,
584
- discount: 0,
585
- freeShippingThreshold: 500000,
586
- });
587
- // summary.subtotal, summary.discount, summary.shipping, summary.vat, summary.total
588
- // summary.formatPrice(amount)
589
-
590
- // Standalone helper (no hook needed)
591
- const total = calculateOrderTotal(items, { vatRate: 0.1, shippingFee: 30000 });
592
- ```
593
-
594
- #### `useOrderStatus` — Status label translation
595
-
596
- ```tsx
597
- import { useOrderStatus, getOrderStatusLabel } from "@onexapis/core/hooks";
598
-
599
- const { getLabel, getPaymentLabel } = useOrderStatus();
600
- getLabel("shipping"); // "Đang giao hàng"
601
- getLabel("completed"); // "Hoàn thành"
602
- getPaymentLabel("paid"); // "Đã thanh toán"
603
-
604
- // Or use the standalone function
605
- getOrderStatusLabel("pending"); // "Chờ xử lý"
606
- ```
607
-
608
- #### `useOrderListPage` — Order history page
609
-
610
- ```tsx
611
- import { useOrderListPage } from "@onexapis/core/hooks";
612
-
613
- const orderList = useOrderListPage({ autoFetch: true, collapsedItemCount: 2 });
614
- // orderList.orders — Order[]
615
- // orderList.isLoading, orderList.error
616
- // orderList.toggleOrder — expand/collapse order items
617
- // orderList.refresh — re-fetch orders
618
- // orderList.getDisplayItems — get visible items (respects collapsed state)
619
- // orderList.hasMoreItems — check if order has hidden items
620
- // orderList.formatCurrency, orderList.formatDate
621
- ```
622
-
623
- #### Order Type Reference
624
-
625
- ```typescript
626
- interface Order {
627
- id: string;
628
- order_number: string;
629
- status: string; // pending | confirmed | processing | packing | shipping | completed | cancelled | failed | refunded
630
- payment_status?: string; // paid | unpaid | failed
631
- payment_method?: string;
632
- total: number;
633
- sub_total?: number;
634
- tax?: number;
635
- shipping_fee?: number;
636
- discount?: number;
637
- order_line_items?: OrderLineItem[];
638
- shipping_address?: OrderAddress;
639
- note?: string;
640
- created_at: string;
641
- updated_at?: string;
642
- }
643
-
644
- interface OrderLineItem {
645
- name: string;
646
- unit_price: number;
647
- quantity: number;
648
- product_id: string;
649
- variant_name?: string;
650
- image_url?: string;
651
- sku?: string;
652
- location?: { id: string; name: string };
653
- }
654
- ```
655
-
656
- ## Color System
657
-
658
- Colors in OneX themes work with **two approaches** (both are valid):
659
-
660
- ### Approach 1: Direct hex colors (standard — used by simple, cool-store themes)
661
-
662
- ```tsx
663
- // In schema
664
- { id: "backgroundColor", type: "color", label: "Background", default: "#3B82F6" }
25
+ ## Routing rules — what tool to call when
665
26
 
666
- // In component
667
- <section style={{ backgroundColor: String(backgroundColor || "#FFFFFF") }}>
668
- ```
669
-
670
- Components store and render hex colors directly (`#3B82F6`). This is simple and works everywhere.
671
-
672
- ### Approach 2: Design system tokens (optional for theme-aware colors)
673
-
674
- ```tsx
675
- // In schema
676
- { id: "textColor", type: "color_token", label: "Text Color", default: "token:primary" }
677
-
678
- // Resolves to CSS variable: var(--theme-color-primary)
679
- // Changes automatically when user updates Primary Color in theme settings
680
- ```
681
-
682
- Token values: `token:primary`, `token:secondary`, `token:accent`, `token:background`, `token:foreground`, `token:muted`
683
- Custom values: `custom:#FF5733` or direct `#FF5733`
684
-
685
- ## Data Attributes (Required for Editor)
686
-
687
- Sections and blocks MUST include `data-` attributes so the editor's visual inspector can identify and select them:
688
-
689
- ```tsx
690
- // Section wrapper
691
- <section
692
- data-section-id={section.id} // REQUIRED
693
- data-section-type={section.type} // REQUIRED
694
- data-section-template="default" // Optional
695
- data-section-name="My Section" // Optional
696
- >
697
-
698
- // Block wrapper
699
- <div
700
- data-section-id={section.id} // REQUIRED — parent section
701
- data-block-id={block.id} // REQUIRED
702
- data-block-type={block.type} // REQUIRED
703
- >
704
- ```
27
+ | When the user says… | Call this tool | Notes |
28
+ |---|---|---|
29
+ | "add / create / scaffold a section" (hero, header, footer, pricing, FAQ, testimonials, gallery, CTA, features, contact, newsletter, product grid, blog list, anything) | **`onexthm_create_section`** | Pass `projectDir` (this directory). Writes 3 files + patches `sections-registry.ts` in one call. |
30
+ | "add / create a page" (home, about, product detail, blog detail, checkout, order lookup, custom) | **`onexthm_create_page`** | Pass `projectDir`. Writes page config + patches `bundle-entry.ts`. |
31
+ | "convert / port / turn this HTML into a section", user pastes markup | **`onexthm_from_html`** | Pass `projectDir` + the source markup. |
32
+ | "convert Figma to a section", user references a Figma design | **`figma:get_metadata`** + **`figma:get_variable_defs`** → **`onexthm_from_figma`** | Call the figma tools first, then pass output to `onexthm_from_figma`. |
33
+ | "validate / lint / check my theme" | **`onexthm_validate`** | Run automatically after every write. |
34
+ | "fix / auto-fix / repair errors" | **`onexthm_fix`** | Auto-fixes: missing "use client", unregistered sections, schema mismatches, invalid page refs. Call `onexthm_validate` after to confirm. |
35
+ | "build / compile / bundle my theme" | **`onexthm_build`** | Returns structured errors with file:line. Run after code changes. |
36
+ | "what hooks can I use?" | **`onexthm_list_hooks`** | Filter by category. 44 hooks across 8 categories. |
37
+ | Anything else (architecture, animation, dark mode, locale, blocks, components) | Read `THEME_REFERENCE.md` first | Do not improvise. |
705
38
 
706
- Without these, the editor cannot select sections/blocks for editing.
39
+ `projectDir` is **the directory containing `theme.config.ts` and `sections-registry.ts`**. In a fresh session opened in this folder, that's the current working directory.
707
40
 
708
- ## Built-in Components
709
-
710
- 34 built-in components from `@onexapis/core` that render inside sections via `ComponentRenderer`:
711
-
712
- | Component | Description | Key Settings |
713
- | --------- | ----------- | ------------ |
714
-
715
- **Text:** `heading` (H1-H6), `paragraph`, `quote`
716
- **Interactive:** `button` (default/outline/ghost/link), `link`, `input`, `textarea`, `checkbox`, `select`
717
- **Media:** `image`, `video` (youtube/vimeo/hosted), `icon` (Lucide icons), `gallery`
718
- **Layout:** `divider`, `spacer`, `container`, `grid`, `columns`, `card`
719
- **Display:** `badge`, `alert`, `progress`, `rating`, `timer`, `list`, `table`, `accordion`, `tabs`, `code`, `map`
720
- **Special:** `product-card`, `blog-card`, `social-links`, `hotline-contacts`, `company-info`
721
- **Decorative:** `torn-separator`
722
-
723
- Use `onexthm://components` MCP resource for full details (content/style fields, slots, examples).
724
-
725
- Components are rendered via `ComponentRenderer` — you don't import them directly.
726
-
727
- ## Component Slots
728
-
729
- Components use `slot` to identify their role:
730
-
731
- ```tsx
732
- const titleComp = components.find((c) => c.slot === "section-title");
733
- const ctaButton = components.find((c) => c.slot === "cta-button");
734
- ```
735
-
736
- Common slots: `section-title`, `section-subtitle`, `description`, `cta-button`, `secondary-cta`, `badge`, `icon`, `image`, `block-title`, `block-description`, `feature-icon`
737
-
738
- ## Block System
739
-
740
- Blocks are nested containers inside sections. They hold components and can nest other blocks.
741
- Use `onexthm://blocks` MCP resource for full block patterns (features, testimonials, pricing, FAQ, team).
742
-
743
- ```tsx
744
- // Section → Blocks → Components
745
- <section> // Section: "features"
746
- <div> // Block: "features-container" (layout)
747
- <div> // Block: "feature-item" (repeating card)
748
- <ComponentRenderer ... /> // Component: icon
749
- <ComponentRenderer ... /> // Component: heading
750
- <ComponentRenderer ... /> // Component: paragraph
751
- </div>
752
- <div> // Block: "feature-item" (another card)
753
- ...
754
- </div>
755
- </div>
756
- </section>
757
- ```
758
-
759
- Use `BlockRenderer` to render blocks — it handles recursive nesting automatically.
760
-
761
- ### Defining Blocks in Schema
762
-
763
- ```tsx
764
- // In your-section.schema.ts
765
- export const mySchema: SectionSchema = {
766
- type: "features",
767
- // ...settings, defaults...
768
- blocks: [
769
- {
770
- type: "feature-item",
771
- name: "Feature Item",
772
- limit: 6, // Max 6 blocks of this type
773
- settings: [
774
- {
775
- id: "backgroundColor",
776
- type: "color",
777
- label: "Background",
778
- default: "#FFFFFF",
779
- },
780
- ],
781
- components: [
782
- { type: "icon", slot: "feature-icon", label: "Icon" },
783
- { type: "heading", slot: "block-title", label: "Title" },
784
- { type: "paragraph", slot: "block-description", label: "Description" },
785
- ],
786
- },
787
- ],
788
- };
789
- ```
790
-
791
- ### Rendering Blocks
792
-
793
- ```tsx
794
- const blocks = (section.blocks || []).filter((b) => b.enabled !== false);
795
-
796
- {
797
- blocks.map((block) => (
798
- <div
799
- key={block.id}
800
- data-section-id={section.id} // REQUIRED
801
- data-block-id={block.id} // REQUIRED
802
- data-block-type={block.type} // REQUIRED
803
- className="p-6 rounded-xl border"
804
- >
805
- <BlockRenderer
806
- block={block}
807
- sectionId={section.id}
808
- isEditing={isEditing}
809
- />
810
- </div>
811
- ));
812
- }
813
-
814
- {
815
- blocks.length === 0 && isEditing && (
816
- <div className="text-center py-12 border-2 border-dashed border-gray-300 rounded-lg">
817
- <p className="text-gray-500">Add blocks to populate this section</p>
818
- </div>
819
- );
820
- }
821
- ```
822
-
823
- ## Animation System
824
-
825
- Sections, blocks, and components support animations via framer-motion:
826
-
827
- ### Animation Types (22)
828
-
829
- `fadeIn`, `fadeInUp`, `fadeInDown`, `fadeInLeft`, `fadeInRight`, `slideInUp`, `slideInDown`, `slideInLeft`, `slideInRight`, `scaleIn`, `scaleInUp`, `rotate`, `rotateIn`, `blur`, `bounce`, `pulse`, `shake`, `flip`, `typing`, `counter`, `parallax`, `none`
830
-
831
- ### Animation Triggers
832
-
833
- - `onLoad` — animate immediately on mount
834
- - `onScroll` — animate when section enters viewport (default, recommended)
835
- - `onHover` — animate on hover
836
- - `onClick` — animate on click
837
-
838
- ### Adding Animation to Schema
839
-
840
- ```tsx
841
- // In schema settings
842
- { id: "animationType", type: "select", label: "Animation",
843
- default: "none",
844
- options: [
845
- { value: "none", label: "None" },
846
- { value: "fadeInUp", label: "Fade In Up" },
847
- { value: "scaleIn", label: "Scale In" },
848
- ]
849
- },
850
- { id: "animationTrigger", type: "select", label: "Trigger",
851
- default: "onScroll",
852
- options: [
853
- { value: "onScroll", label: "On Scroll" },
854
- { value: "onLoad", label: "On Load" },
855
- ]
856
- },
857
- ```
858
-
859
- Animations are applied automatically by `SectionRenderer` and `BlockRenderer` via `AnimationWrapper`.
860
-
861
- ### Motion Tokens
862
-
863
- Themes can customize animation feel via `MotionTokens`:
864
-
865
- - `duration`: instant(0.1s), quick(0.2s), moderate(0.4s), deliberate(0.6s), slow(0.8s)
866
- - `easing`: standard, entrance, exit, expressive (cubic-bezier)
867
- - `intensity`: 0 (no motion/accessibility) to 1 (full motion)
868
- - `staggerDelay`: delay between child animations (default 0.1s)
869
-
870
- Access via `useMotion()` hook from `@onexapis/core/hooks`.
871
-
872
- ## Data Requirements (SSR)
873
-
874
- Sections can declare what data they need for server-side rendering:
875
-
876
- ```tsx
877
- // In schema
878
- dataRequirements: {
879
- products: true, // Section needs product data
880
- blogs: false, // No blog data needed
881
- settings: true, // Needs website settings
882
- }
883
- ```
884
-
885
- When `dataRequirements.products = true`:
886
-
887
- - Server pre-fetches products before rendering
888
- - Data is passed via `SectionComponentProps.data`
889
- - Use `useCommerceData(data)` to access it
890
- - Falls back to client-side `useProducts()` if not pre-fetched
891
-
892
- ## Dynamic Pages (Product Detail, Blog Detail)
893
-
894
- Dynamic pages use URL parameters like `/products/[slug]` to render detail views.
895
-
896
- ### Define a dynamic page in `pages/`
897
-
898
- ```typescript
899
- // pages/product-detail.ts
900
- import type { PageConfig } from "@onexapis/core/types";
901
-
902
- export const productDetailPageConfig: Omit<
903
- PageConfig,
904
- "id" | "createdAt" | "updatedAt"
905
- > = {
906
- title: "Product Detail",
907
- handle: "product-detail",
908
- path: "/products/[slug]", // Dynamic segment in brackets
909
- isDynamic: true, // Flag as dynamic
910
- dynamicSegments: ["slug"], // Parameter names
911
- type: "product",
912
- renderMode: "sections",
913
- themeId: "my-theme",
914
- editable: true,
915
- published: true,
916
- sections: [
917
- {
918
- id: "product-detail-1",
919
- type: "my-product-detail",
920
- template: "default",
921
- order: 0,
922
- enabled: true,
923
- settings: {},
924
- components: [],
925
- blocks: [],
926
- },
927
- ],
928
- };
929
- export default productDetailPageConfig;
930
- ```
41
+ ---
931
42
 
932
- Export in `bundle-entry.ts`:
43
+ ## Forbidden behaviors
933
44
 
934
- ```typescript
935
- export { default as productDetailPageConfig } from "./pages/product-detail";
936
- ```
45
+ These break the editor and storefront. Never do them.
937
46
 
938
- ### Access route params in sections
47
+ 1. **Never hand-write a section's `.tsx`, `.schema.ts`, or `index.ts`** when adding a new section. Call `onexthm_create_section`. The tool already produces correct file structure, schema shape, data attributes, imports, and registry entry. Hand-writing one-offs causes drift.
48
+ 2. **Never edit `sections-registry.ts` by hand** to add a new section. The tools patch it for you.
49
+ 3. **Never use raw `<h1>`, `<p>`, `<button>`** for *content* inside a section. Content goes through `ComponentRenderer` from `@onexapis/core/renderers`. Section components define **layout only**.
50
+ 4. **Never skip `data-section-id`, `data-section-type`, `data-block-id`, `data-block-type`** on wrapper elements. Without them, the visual editor cannot select or edit the section.
51
+ 5. **Never use `useEffect` for data fetching, never hardcode API URLs, never import from `@onexapis/core/internal`**. Use the documented hooks (`useProducts`, `useBlogs`, `useCart`, `useAuth`, `useCheckout`).
939
52
 
940
- Sections receive `data.routeParams` with the extracted URL parameters:
53
+ ---
941
54
 
942
- ```tsx
943
- export function ProductDetail({
944
- section,
945
- schema,
946
- isEditing,
947
- data,
948
- }: SectionComponentProps) {
949
- const routeParams = (data?.routeParams || {}) as Record<string, string>;
950
- const slug = routeParams.slug || ""; // "blue-shirt" from /products/blue-shirt
55
+ ## Calling the tools — minimal examples
951
56
 
952
- const { data: product, isLoading } = useProductBySlug(slug, !!slug);
953
- // ...
954
- }
955
- ```
57
+ ```jsonc
58
+ // Create a new section
59
+ onexthm_create_section({
60
+ projectDir: "<absolute path to this directory>",
61
+ name: "pricing-table",
62
+ description: "Three-tier pricing table with featured tier"
63
+ })
956
64
 
957
- ### Locale-aware paths
65
+ // Convert pasted HTML/JSX to a section
66
+ onexthm_from_html({
67
+ projectDir: "<absolute path to this directory>",
68
+ sectionName: "hero-banner",
69
+ htmlCode: "<div>...the user's markup...</div>"
70
+ })
958
71
 
959
- Use `localizedPaths` to define different URL slugs per locale:
72
+ // Convert a Figma frame (after calling figma:get_metadata first)
73
+ onexthm_from_figma({
74
+ projectDir: "<absolute path to this directory>",
75
+ sectionName: "feature-grid",
76
+ figmaMetadata: "<output of figma:get_metadata>",
77
+ figmaVariables: "<output of figma:get_variable_defs>"
78
+ })
960
79
 
961
- ```typescript
962
- export const productDetailPageConfig = {
80
+ // Create a page
81
+ onexthm_create_page({
82
+ projectDir: "<absolute path to this directory>",
963
83
  handle: "product-detail",
964
- path: "/products/[slug]", // Default/fallback
84
+ path: "/products/[slug]",
965
85
  isDynamic: true,
966
- dynamicSegments: ["slug"],
967
- localizedPaths: {
968
- // Locale-specific paths (typed by Locale enum)
969
- vi: "/san-pham/[slug]", // /vi/san-pham/blue-shirt
970
- en: "/products/[slug]", // /en/products/blue-shirt
971
- },
972
- type: "product",
973
- // ...
974
- };
975
- ```
976
-
977
- The `Locale` type (`"vi" | "en"`) is from `@onexapis/core/types` — gives autocomplete.
978
-
979
- Sections also receive `data.locale` to know the current locale:
980
-
981
- ```tsx
982
- const locale = data?.locale as string; // "vi" or "en"
983
- ```
984
-
985
- ### Key conventions
986
-
987
- - `path` uses bracket syntax: `/products/[slug]`, `/blog/[slug]`
988
- - `localizedPaths` maps `Locale` → path for locale-specific URLs
989
- - `dynamicSegments` lists parameter names extracted from the path
990
- - `type: "product"` or `"blog"` indicates what kind of detail page
991
- - Sections access params via `data.routeParams.slug` (or whatever parameter name)
992
- - Sections access locale via `data.locale` or `useLocale()` hook
993
- - Use `useProductBySlug()` / `useBlogBySlug()` hooks to fetch detail data
994
-
995
- ## Product & Blog Types
996
-
997
- ### Product
998
-
999
- ```tsx
1000
- {
1001
- id, title, slug, category, categoryLabel,
1002
- image: { url, alt },
1003
- images: [{ url, alt }],
1004
- salePrice, originalPrice, discount,
1005
- inStock: boolean,
1006
- badge: "hot" | "new" | "sale" | null,
1007
- rating, reviewCount,
1008
- variants: [{ sku, options, prices }],
1009
- tags: string[],
1010
- }
1011
- ```
1012
-
1013
- ### Blog
1014
-
1015
- ```tsx
1016
- {
1017
- id, title, slug, description, excerpt,
1018
- content: string (HTML, detail only),
1019
- image, coverImage,
1020
- category, categoryLabel,
1021
- author: { name, avatar },
1022
- createdAt, publishedAt,
1023
- tags: string[],
1024
- locale,
1025
- }
1026
- ```
1027
-
1028
- ### WebsiteSettings
1029
-
1030
- ```tsx
1031
- {
1032
- social_connections: [{ name, icon, link, platform, enabled }],
1033
- hotline_connections: [{ name, type, value, enabled }],
1034
- company_info: { name, tagline, description, logo_url },
1035
- contact_email: { email, enabled },
1036
- }
1037
- ```
1038
-
1039
- ## Context Provider Hierarchy
1040
-
1041
- The rendering tree wraps sections in multiple context providers:
1042
-
1043
- ```
1044
- PageDataProvider (products, blogs, settings, company)
1045
- → MotionProvider (animation tokens)
1046
- → ViewportProvider (isEditing, viewportMode)
1047
- → LocaleProvider (locale, supportedLocales)
1048
- → ThemeModeProvider (light/dark mode)
1049
- → CartProvider (shopping cart)
1050
- → SectionListRenderer
1051
- → SectionRenderer (per section)
1052
- → AnimationWrapper
1053
- → Your Section Component
1054
- ```
1055
-
1056
- All contexts are accessible via hooks from `@onexapis/core/hooks`.
1057
-
1058
- ## Editor Integration
1059
-
1060
- When `isEditing = true` (section is rendered in the editor preview):
1061
-
1062
- ```tsx
1063
- export function MySection({ section, isEditing }: SectionComponentProps) {
1064
- return (
1065
- <section>
1066
- {/* Show empty state placeholder in editor */}
1067
- {items.length === 0 && isEditing && (
1068
- <div className="border-2 border-dashed border-gray-300 p-8 text-center text-gray-500">
1069
- Add items to this section
1070
- </div>
1071
- )}
1072
-
1073
- {/* Disable navigation in editor */}
1074
- {isEditing ? (
1075
- <span className="cursor-default">{linkText}</span>
1076
- ) : (
1077
- <a href={url}>{linkText}</a>
1078
- )}
1079
- </section>
1080
- );
1081
- }
1082
- ```
1083
-
1084
- ## Page Background System
1085
-
1086
- 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.
1087
-
1088
- ### How it works
1089
-
1090
- Background is resolved with priority:
1091
-
1092
- 1. **Per-page override** (`PageConfig.background`) — set via editor or page config
1093
- 2. **Theme default** (`ThemeDesignSystem.pageBackground`) — set in `theme.layout.ts`
1094
- 3. **Fallback** — solid white `#FFFFFF`
1095
-
1096
- ### Setting the theme default
1097
-
1098
- In `theme.layout.ts`, add `pageBackground` inside `designSystem`:
1099
-
1100
- ```typescript
1101
- export const layoutConfig: ThemeLayoutConfig = {
1102
- // ...headerSections, footerSections...
1103
- designSystem: {
1104
- colors: {
1105
- primaryColor: "#3B82F6",
1106
- secondaryColor: "#8B5CF6",
1107
- colorMode: "light",
1108
- },
1109
- typography: { headingFont: "system-ui", bodyFont: "system-ui" },
1110
- layout: { spacing: "comfortable" },
1111
- pageBackground: {
1112
- type: "solid", // "solid" | "gradient" | "image" | "pattern" | "none"
1113
- color: "#FFFFFF", // Base background color
1114
- },
1115
- },
1116
- };
1117
- ```
1118
-
1119
- ### Background types
1120
-
1121
- | Type | Fields | Example |
1122
- | ---------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
1123
- | `solid` | `color` | `{ type: "solid", color: "#FFF8F0" }` |
1124
- | `gradient` | `gradient`, `color` (fallback) | `{ type: "gradient", gradient: "linear-gradient(135deg, #667eea, #764ba2)" }` |
1125
- | `image` | `image`, `imageSize`, `imagePosition`, `imageFixed`, `color` (fallback) | `{ type: "image", image: "/bg.jpg", imageSize: "cover", imageFixed: true }` |
1126
- | `pattern` | `pattern`, `color`, `opacity`, `overlayColor` | `{ type: "pattern", pattern: "dots", color: "#FAFAFA", opacity: 0.05 }` |
1127
- | `none` | — | `{ type: "none" }` (transparent, no wrapper) |
1128
-
1129
- ### Built-in CSS patterns
1130
-
1131
- These work out of the box with `type: "pattern"`:
1132
-
1133
- - `dots` — dot grid
1134
- - `grid` — line grid
1135
- - `diagonal-lines` — 45-degree stripes
1136
- - `cross-dots` — offset dot grid
1137
- - `noise` — fractal noise texture
1138
-
1139
- ### Custom animated patterns
1140
-
1141
- Themes can provide custom pattern renderers (e.g., SVG-based animated backgrounds). Export the component from `bundle-entry.ts`:
1142
-
1143
- ```typescript
1144
- // In bundle-entry.ts
1145
- export { SenBackground } from "./assets/sen-background";
1146
- ```
1147
-
1148
- The storefront will pick up exported `*Background` components and match them to pattern names. For example, `SenBackground` maps to `pattern: "sen"`.
1149
-
1150
- Custom pattern components receive these props:
1151
-
1152
- ```typescript
1153
- interface PatternProps {
1154
- opacity?: number; // Pattern opacity (default: 0.05)
1155
- color?: string; // Overlay color
1156
- className?: string; // Additional CSS classes
1157
- }
1158
- ```
1159
-
1160
- ### Per-page override
1161
-
1162
- In a page config (e.g., `pages/about.ts`), set `background`:
1163
-
1164
- ```typescript
1165
- export const aboutPageConfig = {
1166
- title: "About",
1167
- handle: "about",
1168
- // ...other config...
1169
- background: {
1170
- type: "gradient",
1171
- gradient: "linear-gradient(180deg, #EEF2FF 0%, #FFFFFF 50%)",
1172
- },
1173
- };
1174
- ```
1175
-
1176
- ### Accessing in sections via hook
1177
-
1178
- Use `usePageBackground()` to read the resolved background in section components:
1179
-
1180
- ```tsx
1181
- import { usePageBackground } from "@onexapis/core/hooks";
1182
-
1183
- export function MySection({ section }: SectionComponentProps) {
1184
- const { config, styles, hasPattern, pattern, patternOpacity } =
1185
- usePageBackground();
1186
- // config.type — "solid" | "gradient" | "image" | "pattern" | "none"
1187
- // config.color — base color
1188
- // styles — React.CSSProperties derived from config
1189
- // hasPattern — boolean, true if type === "pattern" && pattern is set
1190
- // pattern — pattern name (e.g., "sen", "dots")
1191
- // patternOpacity — number (0-1)
1192
- }
1193
- ```
1194
-
1195
- ### PageBackgroundConfig type reference
1196
-
1197
- ```typescript
1198
- interface PageBackgroundConfig {
1199
- type: "solid" | "gradient" | "image" | "pattern" | "none";
1200
- color?: string; // Base background color (hex/CSS)
1201
- gradient?: string; // CSS gradient value
1202
- image?: string; // Background image URL
1203
- imageSize?: "cover" | "contain" | "auto";
1204
- imagePosition?: string; // CSS background-position (default: "center")
1205
- imageFixed?: boolean; // Fixed background attachment
1206
- pattern?: string; // Pattern identifier
1207
- opacity?: number; // Pattern/overlay opacity (0-1, default: 0.05)
1208
- overlayColor?: string; // Pattern overlay color
1209
- className?: string; // Additional CSS classes
1210
- }
1211
- ```
1212
-
1213
- ### CSS variables
1214
-
1215
- The storefront injects these CSS variables when a page background is configured:
1216
-
1217
- - `--page-bg-color` — base color
1218
- - `--page-bg-gradient` — gradient value
1219
- - `--page-bg-pattern` — pattern name
1220
- - `--page-bg-pattern-opacity` — opacity value
1221
- - `--page-bg-overlay-color` — overlay color
1222
-
1223
- Use these in custom CSS if needed: `background-color: var(--page-bg-color);`
1224
-
1225
- ## Dark/Light Mode
1226
-
1227
- Themes support light, dark, and system color modes via `useThemeMode()`:
1228
-
1229
- ```tsx
1230
- import { useThemeMode } from "@onexapis/core/hooks";
1231
-
1232
- export function MySection({ section }: SectionComponentProps) {
1233
- const { mode, isDark } = useThemeMode();
1234
- // mode: "light" | "dark" | "system"
1235
- // isDark: boolean (resolved — accounts for system preference)
1236
-
1237
- return (
1238
- <section
1239
- style={{
1240
- backgroundColor: isDark ? "#1F2937" : "#FFFFFF",
1241
- color: isDark ? "#F9FAFB" : "#111827",
1242
- }}
1243
- >
1244
- {/* Content adapts to color mode */}
1245
- </section>
1246
- );
1247
- }
1248
- ```
1249
-
1250
- ### How dark mode works
1251
-
1252
- - The `ThemeModeProvider` at the app root sets a `dark` class on the HTML element
1253
- - Tailwind's `dark:` variants work automatically: `className="bg-white dark:bg-gray-900"`
1254
- - `useThemeMode()` gives you the current mode for JS-driven styling
1255
- - The design system's `colorMode` field determines the default mode
1256
- - Users can toggle via the editor's theme settings
1257
-
1258
- ### Best practices
1259
-
1260
- - Use Tailwind's `dark:` variant for simple color swaps: `text-gray-900 dark:text-white`
1261
- - Use `useThemeMode()` for complex conditional logic
1262
- - Test sections in both modes — don't assume light mode only
1263
- - CSS variables (`--background`, `--foreground`) auto-switch with dark mode
1264
-
1265
- ## Locale / i18n
1266
-
1267
- Themes support multiple languages via `useLocale()`:
1268
-
1269
- ```tsx
1270
- import { useLocale } from "@onexapis/core/hooks";
1271
-
1272
- export function MySection({ section }: SectionComponentProps) {
1273
- const { locale, defaultLocale, supportedLocales } = useLocale();
1274
- // locale: "vi" | "en" | ...
1275
- // defaultLocale: "vi"
1276
- // supportedLocales: ["vi", "en"]
1277
-
1278
- return (
1279
- <section>{locale === "vi" ? <h1>Xin chào</h1> : <h1>Hello</h1>}</section>
1280
- );
1281
- }
1282
- ```
1283
-
1284
- ### How i18n works
1285
-
1286
- - The default locale is `"vi"` (Vietnamese)
1287
- - Supported locales: `["vi", "en"]`
1288
- - Page content is stored per-locale in the database
1289
- - The API supports `?locale=en` query parameter to get translated content
1290
- - Translation overrides are stored as diffs from the base (default locale) page
1291
- - The `LocaleProvider` at the app root provides the current locale via context
1292
-
1293
- ### Best practices
1294
-
1295
- - Section components usually DON'T need i18n logic — the content comes from the editor (already translated)
1296
- - The `ComponentRenderer` renders text from `component.content.text` which is locale-specific
1297
- - Only use `useLocale()` if you need locale-aware formatting (dates, numbers, currency)
1298
- - Use `toLocaleString()` for numbers: `price.toLocaleString("vi-VN")`
1299
- - Use `Intl.DateTimeFormat` for dates: `new Intl.DateTimeFormat(locale).format(date)`
1300
-
1301
- ## Section Registry
1302
-
1303
- Every section must be registered in `sections-registry.ts` with lazy imports:
1304
-
1305
- ```tsx
1306
- export const sectionsRegistry = {
1307
- hero: () => import("./sections/hero"),
1308
- features: () => import("./sections/features"),
1309
- pricing: () => import("./sections/pricing"),
1310
- // Add new sections here
1311
- };
1312
- ```
1313
-
1314
- The build system uses this registry to code-split sections into the bundle.
1315
-
1316
- ## Rules
1317
-
1318
- ### DO
1319
-
1320
- - Use `ComponentRenderer` / `BlockRenderer` from `@onexapis/core/renderers` for nested content
1321
- - Use `getSectionValues(section, schema)` to extract typed settings
1322
- - Use `filterEnabledComponents()` to skip disabled components
1323
- - Use `toComponentInstance()` to convert raw component data for ComponentRenderer
1324
- - Include `data-section-id`, `data-block-id` attributes on wrapper elements
1325
- - Handle the `isEditing` prop (show placeholders, disable navigation links)
1326
- - Use `"use client"` directive at the top of section components
1327
- - Use Tailwind CSS classes for layout (included in the build)
1328
- - Declare `dataRequirements` in schema if section needs products/blogs/settings
1329
-
1330
- ### DON'T
1331
-
1332
- - Don't import from `@onexapis/core/internal` — it's a private API
1333
- - Don't use `document` or `window` without checking `typeof window !== "undefined"`
1334
- - Don't hardcode API URLs — use hooks (`useProducts`, `useBlogs`)
1335
- - Don't use `useEffect` for data fetching — use React Query hooks instead
1336
- - Don't mutate `section.settings` directly — use the `onSettingsChange` callback
1337
- - Don't use `require()` — themes are ES modules
1338
- - Don't import React explicitly — it's auto-injected by the JSX transform
1339
-
1340
- ## CLI Commands
1341
-
1342
- ```bash
1343
- onexthm init my-theme # Scaffold new theme project
1344
- onexthm create:section hero # Create section (component + schema + index)
1345
- onexthm create:block card # Create block
1346
- onexthm create:component badge # Create component
1347
- onexthm list # List all sections/blocks/components
1348
- onexthm validate # Validate theme structure
1349
- onexthm dev # Start dev server with live preview
1350
- onexthm build # Compile theme for production
1351
- onexthm upload --theme my-theme # Upload bundle.zip to S3
1352
- onexthm clone simple # Clone an existing theme from S3
1353
- onexthm download -t simple # Download compiled theme
1354
- onexthm config # Configure AWS/API credentials
1355
- ```
1356
-
1357
- ## Example: Product Grid Section
1358
-
1359
- ```tsx
1360
- "use client";
1361
-
1362
- import type { SectionComponentProps } from "@onexapis/core/types";
1363
- import { useProducts, useCart } from "@onexapis/core/hooks";
1364
- import coreUtils from "@onexapis/core/utils";
1365
-
1366
- const { getSectionValues } = coreUtils;
1367
-
1368
- export function ProductGrid({
1369
- section,
1370
- schema,
1371
- isEditing,
1372
- }: SectionComponentProps) {
1373
- const { settings } = getSectionValues(section, schema);
1374
- const limit = Number(settings.limit) || 8;
1375
- const columns = Number(settings.columns) || 3;
1376
-
1377
- const { data, isLoading } = useProducts({ limit });
1378
- const { addItem } = useCart();
1379
- const products = data?.data ?? [];
1380
-
1381
- if (isLoading) {
1382
- return (
1383
- <section
1384
- data-section-id={section.id}
1385
- data-section-type="product-grid"
1386
- className="py-16"
1387
- >
1388
- <div className="container mx-auto px-4">
1389
- <div className="animate-pulse grid grid-cols-3 gap-4">
1390
- {Array.from({ length: limit }).map((_, i) => (
1391
- <div key={i} className="h-64 bg-gray-200 rounded-lg" />
1392
- ))}
1393
- </div>
1394
- </div>
1395
- </section>
1396
- );
1397
- }
1398
-
1399
- const colClass =
1400
- columns === 2
1401
- ? "md:grid-cols-2"
1402
- : columns === 4
1403
- ? "md:grid-cols-4"
1404
- : "md:grid-cols-3";
1405
-
1406
- return (
1407
- <section
1408
- data-section-id={section.id}
1409
- data-section-type="product-grid"
1410
- className="py-16"
1411
- style={{ backgroundColor: String(settings.backgroundColor || "#FFFFFF") }}
1412
- >
1413
- <div className="container mx-auto px-4 max-w-6xl">
1414
- <div className={`grid grid-cols-1 ${colClass} gap-6`}>
1415
- {products.map((product) => (
1416
- <div
1417
- key={product.id}
1418
- className="border rounded-lg p-4 hover:shadow-md transition-shadow"
1419
- >
1420
- {product.image?.url && (
1421
- <img
1422
- src={product.image.url}
1423
- alt={product.name}
1424
- className="w-full h-48 object-cover rounded mb-3"
1425
- />
1426
- )}
1427
- <h3 className="font-semibold text-lg">{product.name}</h3>
1428
- <p className="text-gray-600 mt-1">
1429
- {product.price?.toLocaleString()}đ
1430
- </p>
1431
- {!isEditing && (
1432
- <button
1433
- onClick={() => addItem(product, 1)}
1434
- className="mt-3 w-full py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
1435
- >
1436
- Add to Cart
1437
- </button>
1438
- )}
1439
- </div>
1440
- ))}
1441
- </div>
1442
- </div>
1443
- </section>
1444
- );
1445
- }
1446
-
1447
- export default ProductGrid;
1448
- ```
1449
-
1450
- ## Cart & Fly-to-Cart Animation
1451
-
1452
- The cart system uses `useCart()` for state and a flexible fly-to-cart animation triggered by `data-fly-to-cart-target`.
1453
-
1454
- ### Cart State
1455
-
1456
- ```tsx
1457
- import { useCart } from "@onexapis/core/hooks";
1458
-
1459
- const {
1460
- items,
1461
- addItem,
1462
- removeItem,
1463
- updateQuantity,
1464
- clearCart,
1465
- itemCount,
1466
- subtotal,
1467
- } = useCart();
1468
-
1469
- // Add to cart
1470
- addItem({
1471
- productId: product.id,
1472
- name: product.title,
1473
- image: product.image,
1474
- price: product.salePrice,
1475
- slug: product.slug,
1476
- });
1477
- ```
1478
-
1479
- ### Fly-to-Cart Animation
1480
-
1481
- When a user clicks "Add to Cart", the product thumbnail flies from the button to the cart icon in the header.
1482
-
1483
- **Step 1: Mark any element as the cart target** (in header section):
1484
-
1485
- ```tsx
1486
- // Option A: Use any element — just add the data attribute
1487
- <div data-fly-to-cart-target className="my-cart-icon">
1488
- <ShoppingCartIcon />
1489
- <span>{itemCount}</span>
1490
- </div>;
1491
-
1492
- // Option B: Use CartIcon convenience component (auto-adds data-fly-to-cart-target)
1493
- import { CartIcon } from "@onexapis/core/components";
1494
- <CartIcon count={itemCount} onClick={() => openCartDrawer()} />;
1495
- ```
1496
-
1497
- **Step 2: ProductCard triggers animation automatically** — just pass `onAddToCart`:
1498
-
1499
- ```tsx
1500
- <ProductCard
1501
- product={product}
1502
- onAddToCart={(p) =>
1503
- addItem({
1504
- productId: p.id,
1505
- name: p.title,
1506
- image: p.image,
1507
- price: p.salePrice,
1508
- })
1509
- }
1510
- />
1511
- ```
1512
-
1513
- **Step 3: Custom trigger** (for buttons that aren't ProductCard):
1514
-
1515
- ```tsx
1516
- import { useFlyToCart } from "@onexapis/core/hooks";
1517
-
1518
- const { flyToCart } = useFlyToCart();
1519
-
1520
- <button onClick={(e) => {
1521
- flyToCart(e.currentTarget, product.image); // animation
1522
- addItem({ ... }); // cart logic
1523
- }}>
1524
- Buy Now
1525
- </button>
1526
- ```
1527
-
1528
- ### How it works
1529
-
1530
- - `data-fly-to-cart-target` on any element → animation lands there (detected via `querySelector`)
1531
- - `FlyToCartProvider` renders a portal with the flying thumbnail (CSS transition)
1532
- - No refs or context wiring needed — just add the data attribute
1533
- - Without `data-fly-to-cart-target` → cart still works, no animation
1534
-
1535
- ## Server-Side APIs
1536
-
1537
- Server-side data fetching is available from `@onexapis/core/server`. Use in Next.js server components, server actions, or API routes.
1538
-
1539
- ```tsx
1540
- import {
1541
- fetchProducts,
1542
- fetchBlogs,
1543
- fetchSettings,
1544
- fetchCompany,
1545
- fetchTheme,
1546
- fetchPage,
1547
- prefetchSectionData,
1548
- } from "@onexapis/core/server";
1549
-
1550
- // Fetch products (server-side with ISR caching)
1551
- const { data: products, pagination } = await fetchProducts(companyId, {
1552
- limit: 12,
1553
- });
1554
-
1555
- // Fetch website settings
1556
- const settings = await fetchSettings(companyId);
1557
-
1558
- // Smart prefetch — scans sections for dataRequirements, fetches in parallel
1559
- const data = await prefetchSectionData({
1560
- companyId,
1561
- sections: allSections,
1562
- dataRequirements: manifest.dataRequirements,
1563
- });
1564
- // data.products, data.blogs, data.settings
1565
- ```
1566
-
1567
- ### Advanced: Custom server client
1568
-
1569
- ```tsx
1570
- import { CommerceServerClient, noopCacheAdapter } from "@onexapis/core/server";
1571
-
1572
- const client = new CommerceServerClient({
1573
- apiUrl: "https://api.example.com",
1574
- companyId: "xxx",
1575
- cacheAdapter: noopCacheAdapter, // For non-Next.js environments
1576
- });
1577
-
1578
- const products = await client.getProducts({ limit: 8 });
1579
- const blog = await client.getBlogBySlug("my-post");
1580
- ```
1581
-
1582
- Environment variables auto-detected: `NEXT_PUBLIC_COMMERCE_API_URL`, `NEXT_PUBLIC_API_URL`.
1583
-
1584
- ## MCP Servers
1585
-
1586
- This project has THREE MCP servers. Do NOT confuse them:
1587
-
1588
- ### `onexthm` MCP (Theme Development) — USE THIS
1589
-
1590
- Registered in `.mcp.json` in this project. Provides theme-specific tools:
1591
-
1592
- - `onexthm_create_section` — Generate section files (component + schema + index) from structured input
1593
- - `onexthm_from_html` — **Convert HTML/React/JSX code to OneX section** (parses elements, maps to components, detects blocks)
1594
- - `onexthm_from_figma` — **Convert Figma design to OneX section** (see Figma Integration below)
1595
- - `onexthm_generate_schema` — Generate schema from natural language description
1596
- - `onexthm_validate` — Validate theme structure (7 checks: entry file, config, sections, blocks, code quality, registry)
1597
- - `onexthm_list_hooks` — List available hooks with usage examples
1598
-
1599
- Resources (auto-loaded context):
1600
-
1601
- - `onexthm://rules` — Theme development rules (DOs/DON'Ts)
1602
- - `onexthm://field-types` — All available field types and categories
1603
- - `onexthm://hooks` — Hooks reference with examples
1604
- - `onexthm://components` — All component types with fields, slots, examples
1605
- - `onexthm://blocks` — Block system patterns and data attributes
1606
-
1607
- Prompts (guided workflows):
1608
-
1609
- - `create_section` — Guided section creation workflow
1610
- - `html_to_section` — Convert HTML/React code to OneX section
1611
- - `figma_to_section` — Full Figma-to-OneX conversion pipeline
1612
- - `review_theme` — Review theme for issues
1613
-
1614
- ### `figma` MCP (Figma Design) — USE WITH `onexthm`
1615
-
1616
- Registered in `.mcp.json`. Reads Figma designs for design-to-code conversion:
1617
-
1618
- - `get_metadata` — Layer hierarchy (IDs, names, types, positions, sizes)
1619
- - `get_design_context` — React + Tailwind code suggestion for selected layers
1620
- - `get_variable_defs` — Design tokens (colors, typography, spacing variables)
1621
- - `get_screenshot` — Visual screenshot of selected elements
1622
- - `search_design_system` — Search components, variables, styles from libraries
1623
-
1624
- **Setup**: Requires Figma API key in `.mcp.json`:
1625
-
1626
- ```json
1627
- {
1628
- "figma": {
1629
- "command": "npx",
1630
- "args": ["-y", "figma-developer-mcp", "--figma-api-key=YOUR_KEY", "--stdio"]
1631
- }
1632
- }
1633
- ```
1634
-
1635
- Get your key: Figma → Settings → Account → Personal access tokens.
1636
-
1637
- ### `onex-platform` MCP (Backend Services) — DO NOT USE FOR THEMES
1638
-
1639
- This is for managing microservices on the OneXEOS platform. Its tools are:
1640
-
1641
- - `onex_invoke`, `onex_status`, `onex_deploy`, `onex_logs`, `onex_test`
1642
- - These manage backend services (auth, product, order, etc.)
1643
- - **Do NOT use these for theme development** — they have nothing to do with sections, schemas, or theme components
1644
-
1645
- ### When to use which
1646
-
1647
- | Task | MCP to use |
1648
- | -------------------------------- | ------------------------------------ |
1649
- | Create a new section | `onexthm_create_section` |
1650
- | Convert HTML/React to section | `onexthm_from_html` |
1651
- | Convert Figma design to section | `onexthm_from_figma` + `figma` tools |
1652
- | Generate schema from description | `onexthm_generate_schema` |
1653
- | Validate theme structure | `onexthm_validate` |
1654
- | Look up available hooks | `onexthm_list_hooks` |
1655
- | Read Figma design layers | `figma:get_metadata` |
1656
- | Get Figma design tokens | `figma:get_variable_defs` |
1657
- | Deploy a backend service | `onex_deploy` (platform MCP) |
1658
- | Check service health | `onex_status` (platform MCP) |
1659
-
1660
- ## Figma → OneX Conversion
1661
-
1662
- Convert Figma designs directly to OneX theme sections using the combined Figma + OneX MCP pipeline.
1663
-
1664
- ### Quick Start
1665
-
1666
- Use the `figma_to_section` prompt for a guided workflow:
1667
-
1668
- ```
1669
- "Convert the selected Figma frame to a section called 'hero'"
1670
- ```
1671
-
1672
- Or manually orchestrate the steps:
86
+ dynamicSegments: ["slug"]
87
+ })
1673
88
 
1674
- ```
1675
- 1. figma:get_metadata → Get layer hierarchy
1676
- 2. figma:get_variable_defs → Get design tokens
1677
- 3. figma:get_design_context → Get React+Tailwind code
1678
- 4. onexthm:onexthm_from_figma → Convert to OneX section files
1679
- 5. Write files to sections/ directory
1680
- 6. onexthm:onexthm_validate → Validate
1681
- 7. Register in sections-registry.ts
1682
- ```
89
+ // Validate after any write
90
+ onexthm_validate({ projectDir: "<absolute path to this directory>" })
1683
91
 
1684
- ### How Figma Layers Map to OneX
92
+ // Auto-fix validation errors
93
+ onexthm_fix({ projectDir: "<absolute path to this directory>" })
1685
94
 
1686
- ```
1687
- Figma Layer → OneX Concept
1688
- ────────────────────────────────────────────────
1689
- Top-level Frame → Section
1690
- Nested Frame/Group → Block
1691
- TEXT (large >=24px, bold >=600) → heading (h1-h6)
1692
- TEXT (body <24px) → paragraph
1693
- Button instance → button component
1694
- Image node → image component
1695
- Icon instance → icon component
1696
- Badge/Label → badge component
1697
- Repeating children (same shape) → Block definition
1698
- Background color fill → backgroundColor setting
1699
- Background image fill → backgroundImage setting
1700
- Auto Layout horizontal → flex flex-row (Tailwind)
1701
- Auto Layout vertical → flex flex-col (Tailwind)
1702
- Auto Layout gap → gap-N (Tailwind)
95
+ // Build the theme
96
+ onexthm_build({ projectDir: "<absolute path to this directory>" })
1703
97
  ```
1704
98
 
1705
- ### `onexthm_from_figma` Tool Parameters
99
+ All tools default to writing files on disk. Pass `dryRun: true` if the user
100
+ explicitly asks to preview without writing. Pass `force: true` to overwrite
101
+ existing files.
1706
102
 
1707
- ```typescript
1708
- {
1709
- figmaMetadata: string, // Required: XML/JSON from figma:get_metadata
1710
- figmaVariables?: string, // Optional: JSON from figma:get_variable_defs
1711
- figmaCode?: string, // Optional: code from figma:get_design_context (fallback)
1712
- sectionName: string, // Required: kebab-case name (e.g., "hero")
1713
- category?: string, // Optional: section category
1714
- themePrefix?: string, // Optional: type prefix (e.g., "my-simple")
1715
- }
1716
- ```
1717
-
1718
- Returns generated files: `{name}.schema.ts`, `{name}-default.tsx`, `index.ts`, plus registry entry.
103
+ ---
1719
104
 
1720
- ### `onexthm_from_html` Tool Parameters
105
+ ## After a tool call
1721
106
 
1722
- ```typescript
1723
- {
1724
- htmlCode: string, // Required: Raw HTML or React/JSX code
1725
- sectionName: string, // Required: kebab-case name (e.g., "hero-banner")
1726
- category?: string, // Optional: section category
1727
- themePrefix?: string, // Optional: type prefix (e.g., "my-simple")
1728
- }
1729
- ```
107
+ 1. Read the tool's response — it lists what was written and warns about anything skipped.
108
+ 2. Run `onexthm_validate` if the tool didn't already.
109
+ 3. Show the user the new file paths so they know where to edit further.
110
+ 4. Only edit the generated files **after** they exist on disk — never re-generate by hand.
1730
111
 
1731
- Returns generated files: `{name}.schema.ts`, `{name}-default.tsx`, `index.ts`, plus registry entry.
112
+ ---
1732
113
 
1733
- ### How HTML Elements Map to OneX
114
+ ## When the answer isn't here
1734
115
 
1735
- ```
1736
- HTML Element → OneX Component
1737
- ────────────────────────────────────────────────────
1738
- <h1>-<h6> → heading (with tag h1-h6)
1739
- <p>, <span> → paragraph
1740
- <button>, <a class="btn/cta"> → button
1741
- <a> → link
1742
- <img> → image
1743
- <svg>, icon components → icon
1744
- <ul>/<ol> → list
1745
- <hr> → divider
1746
- <blockquote> → quote
1747
- <input>, <textarea>, <select> → input/textarea/select
1748
- <video> → video
1749
- <table> → table
1750
- <div>, <section>, <article> → layout containers (recurse)
1751
- Repeating sibling structures → block definitions
1752
- Tailwind flex/grid classes → layout settings
1753
- Tailwind bg-* classes → backgroundColor setting
1754
- Tailwind text-* classes → fontSize/textAlign
1755
- React <Button>, <Card>, etc. → resolved to HTML equivalents
1756
- ```
116
+ If the user's question is about hooks, components, blocks, animation, dark
117
+ mode, locale, dynamic pages, payments, the cart, or any deep API question,
118
+ either:
1757
119
 
1758
- ### Pixel Tailwind Reference
120
+ - Read `THEME_REFERENCE.md` (lives next to this file), or
121
+ - Call `onexthm_list_hooks` for hook signatures, or
122
+ - Read the `onexthm://components`, `onexthm://blocks`, `onexthm://field-types`,
123
+ `onexthm://hooks`, or `onexthm://rules` MCP resources.
1759
124
 
1760
- ```
1761
- Font Size: 12→xs 14→sm 16→base 18→lg 20→xl 24→2xl 30→3xl 36→4xl 48→5xl 60→6xl
1762
- Spacing: 4→1 8→2 12→3 16→4 20→5 24→6 32→8 40→10 48→12 64→16 80→20 96→24
1763
- Weight: 100-400→normal 500→medium 600→semibold 700+→bold
1764
- ```
125
+ `THEME_REFERENCE.md` is the canonical reference document for everything not
126
+ covered by these routing rules.