@ikas/code-components-mcp 0.32.0 → 0.34.0

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.
@@ -30,7 +30,7 @@
30
30
  "prop-types": {
31
31
  "title": "Available Prop Types",
32
32
  "description": "All prop types that can be used in ikas.config.json",
33
- "content": "Props define what the store editor can configure for each component. Each prop has a `type` that determines the editor UI and the TypeScript type received in your component.\n\n| Type | Editor UI | TypeScript Type | Description |\n|------|-----------|----------------|-------------|\n| `TEXT` | Text input | `string` | Single-line text |\n| `NUMBER` | Number input | `number` | Numeric value |\n| `BOOLEAN` | Toggle switch | `boolean` | True/false toggle |\n| `IMAGE` | Image picker | `IkasImage | null` | Image from editor. Use `getDefaultSrc(image)` for URL |\n| `LINK` | Link editor | `IkasNavigationLink` | Navigation link with href, label, subLinks |\n| `LIST_OF_LINK` | Link list editor | `IkasNavigationLinkList` | List of navigation links |\n| `COLOR` | Color picker | `string` | CSS color value (hex, rgb, etc.) |\n| `SELECT` | Dropdown | `string` | Single selection from options list |\n| `PRODUCT` | Product picker | `IkasProduct` | Single product reference |\n| `PRODUCT_LIST` | Product list picker | `IkasProduct[]` | Multiple product references |\n| `CATEGORY` | Category picker | `IkasCategory` | Single category reference |\n| `CATEGORY_LIST` | Category list picker | `IkasCategory[]` | Multiple category references |\n| `BRAND` | Brand picker | `IkasBrand` | Single brand reference |\n| `BRAND_LIST` | Brand list picker | `IkasBrand[]` | Multiple brand references |\n| `BLOG_POST` | Blog post picker | `IkasBlogPost` | Single blog post reference |\n| `BLOG_POST_LIST` | Blog post list picker | `IkasBlogPost[]` | Multiple blog post references |\n| `FONT_STYLE_TYPE` | Font style editor | `IkasFontStyle` | Font family, size, weight, etc. |\n\n### SELECT type example:\n```json\n{\n \"name\": \"layout\",\n \"displayName\": \"Layout\",\n \"type\": \"SELECT\",\n \"options\": [\n { \"label\": \"Grid\", \"value\": \"grid\" },\n { \"label\": \"List\", \"value\": \"list\" }\n ],\n \"defaultValue\": \"grid\"\n}\n```\n\n### IMAGE type example:\n```json\n{\n \"name\": \"heroImage\",\n \"displayName\": \"Hero Image\",\n \"type\": \"IMAGE\",\n \"required\": false\n}\n```\nAccess in component: `getDefaultSrc(props.heroImage)` (import `getDefaultSrc` from `@ikas/bp-storefront`)",
33
+ "content": "Props define what the store editor can configure for each component. Each prop has a `type` that determines the editor UI and the TypeScript type received in your component.\n\n| Type | Editor UI | TypeScript Type | Description |\n|------|-----------|----------------|-------------|\n| `TEXT` | Text input | `string` | Single-line text |\n| `NUMBER` | Number input | `number` | Numeric value |\n| `BOOLEAN` | Toggle switch | `boolean` | True/false toggle |\n| `IMAGE` | Image picker | `IkasImage | null` | Image from editor. Use `getDefaultSrc(image)` for URL |\n| `LINK` | Link editor | `IkasNavigationLink` | Navigation link with href, label, subLinks |\n| `LIST_OF_LINK` | Link list editor | `IkasNavigationLinkList` | List of navigation links |\n| `COLOR` | Color picker | `string` | CSS color value (hex, rgb, etc.) |\n| `SELECT` | Dropdown | `string` | Single selection from options list |\n| `PRODUCT` | Product picker | `IkasProduct` | Single product reference |\n| `PRODUCT_LIST` | Product list picker | `IkasProduct[]` | Multiple product references |\n| `CATEGORY` | Category picker | `IkasCategory` | Single category reference |\n| `CATEGORY_LIST` | Category list picker | `IkasCategory[]` | Multiple category references |\n| `BRAND` | Brand picker | `IkasBrand` | Single brand reference |\n| `BRAND_LIST` | Brand list picker | `IkasBrand[]` | Multiple brand references |\n| `BLOG` | Blog post picker | `IkasBlog` | Single blog post reference |\n| `BLOG_LIST` | Blog post list picker | `IkasBlog[]` | Multiple blog post references |\n| `FONT_STYLE_TYPE` | Font style editor | `IkasFontStyle` | Font family, size, weight, etc. |\n\n### SELECT type example:\n```json\n{\n \"name\": \"layout\",\n \"displayName\": \"Layout\",\n \"type\": \"SELECT\",\n \"options\": [\n { \"label\": \"Grid\", \"value\": \"grid\" },\n { \"label\": \"List\", \"value\": \"list\" }\n ],\n \"defaultValue\": \"grid\"\n}\n```\n\n### IMAGE type example:\n```json\n{\n \"name\": \"heroImage\",\n \"displayName\": \"Hero Image\",\n \"type\": \"IMAGE\",\n \"required\": false\n}\n```\nAccess in component: `getDefaultSrc(props.heroImage)` (import `getDefaultSrc` from `@ikas/bp-storefront`)",
34
34
  "tags": [
35
35
  "props",
36
36
  "types",
@@ -45,14 +45,14 @@
45
45
  "PRODUCT",
46
46
  "CATEGORY",
47
47
  "BRAND",
48
- "BLOG_POST",
48
+ "BLOG",
49
49
  "FONT_STYLE_TYPE"
50
50
  ]
51
51
  },
52
52
  "component-structure": {
53
53
  "title": "Component Structure",
54
54
  "description": "How to write a Preact component for ikas",
55
- "content": "Each component consists of three files. There are two patterns: **components** (child elements) and **sections** (page-level containers).\n\n## Component Pattern (child elements like buttons, cards, badges)\n\n### index.tsx - Component Implementation\n```tsx\nimport { Props } from \"./types\";\n\nexport default function MyComponent({ title, showButton }: Props) {\n return (\n <div className=\"my-component\">\n <h1>{title}</h1>\n {showButton && <button>Click me</button>}\n </div>\n );\n}\n```\n\n### types.ts - Props Interface\n```typescript\nexport interface Props {\n title: string; // TEXT prop -> string\n showButton?: boolean; // optional BOOLEAN prop\n count: number; // NUMBER prop -> number\n}\n```\n\n### styles.css\n```css\n.my-component {\n padding: 16px;\n}\n\n.my-component h1 {\n font-size: 24px;\n}\n```\n\n## Section Pattern (page-level containers like headers, hero banners, footers)\n\nSections use a `<section>` root element, full-width styling, and a `Props` interface. In `ikas.config.json` they have `\"type\": \"section\"`.\n\n### index.tsx - Section Implementation\n```tsx\nimport { Props } from \"./types\";\n\nexport default function HeroBanner({ heading, subtitle, backgroundColor }: Props) {\n return (\n <section className=\"hero-banner\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"hero-banner-inner\">\n <h2>{heading}</h2>\n {subtitle && <p>{subtitle}</p>}\n </div>\n </section>\n );\n}\n```\n\n### types.ts - Section Props Interface\n```typescript\nexport interface Props {\n heading: string;\n subtitle?: string;\n backgroundColor?: string;\n}\n```\n\n### styles.css - Full-width section styles\n```css\n.hero-banner {\n width: 100%;\n padding: 64px 24px;\n}\n\n.hero-banner-inner {\n max-width: 1200px;\n margin: 0 auto;\n}\n```\n\n## Key Rules\n- Export the component as `default export`\n- Use Preact (not React) - but JSX syntax is the same\n- Import types from `./types` for props\n- Use `className` not `class` for CSS classes\n- Storefront functions and types come from `@ikas/bp-storefront`\n- CSS classes are automatically scoped to your component at build time. Use plain CSS class selectors - they won't conflict with other components or the page.\n\n## Root Components Are Automatically Reactive\n\nThe ikas runtime wraps every root component render in a MobX `autorun()`, so **root exports are already reactive** — they automatically re-render when any MobX store they read (`cartStore`, `customerStore`, etc.) changes. You do **not** need `observer()` on root component exports.\n\n### Correct: Plain root export reading stores\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport default function CartSummary({ title }: Props) {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return (\n <section className=\"cart-summary\">\n <h2>{title}</h2>\n <p>{itemCount} items in cart</p>\n </section>\n );\n}\n```\n\n### Anti-pattern: Do NOT wrap root exports with observer\n```tsx\n// WRONG — observer() is redundant on root components\nconst CartSummary = observer(function CartSummary({ title }: Props) {\n ...\n});\nexport default CartSummary;\n```\n\n## Using observer for Sub-Components\n\nWhen you extract a **sub-component** that independently reads MobX stores, wrap it with `observer()` from `@ikas/component-utils` so it re-renders when store data changes.\n\n### When to use observer\n- A **sub-component** (not the root export) reads `cartStore.cart`, `customerStore.customer`, or any other MobX observable\n- You extract part of a component into a separate function that needs independent reactivity\n\n### When observer is NOT needed\n- **Root component exports** — the ikas runtime handles reactivity via `autorun()`\n- Components that only use props passed from parent — no store reads\n- Static components with no reactive data\n\n### Example: Root export with observer sub-component\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span className=\"cart-badge\">{itemCount}</span>;\n});\n\n// Root export: NO observer needed — autorun() handles reactivity\nexport default function Header({ logo }: Props) {\n return (\n <header>\n <img src={logo} alt=\"Logo\" />\n <CartBadge />\n </header>\n );\n}\n```\n\n**Important:** When using `observer` on sub-components, define the component as a named function expression (not arrow function) and assign it to a `const`. This ensures proper display names in React DevTools.\n\n## Sub-Component File Organization\n\nWhen you extract sub-components, **ALWAYS** place them in `src/sub-components/` with their own folder containing `index.tsx` and `styles.css`. Never create flat `.tsx` files inside a component folder. See `get_framework_guide(\"sub-component-patterns\")` for the full pattern, directory structure, CSS @import rules, and examples.",
55
+ "content": "Each component consists of three files. There are two patterns: **components** (child elements) and **sections** (page-level containers).\n\n## Component Pattern (child elements like buttons, cards, badges)\n\n### index.tsx - Component Implementation\n```tsx\nimport { Props } from \"./types\";\n\nexport function MyComponent({ title, showButton }: Props) {\n return (\n <div className=\"my-component\">\n <h1>{title}</h1>\n {showButton && <button>Click me</button>}\n </div>\n );\n}\n\nexport default MyComponent;\n```\n\n### types.ts - Props Interface\n```typescript\nexport interface Props {\n title: string; // TEXT prop -> string\n showButton?: boolean; // optional BOOLEAN prop\n count: number; // NUMBER prop -> number\n}\n```\n\n### styles.css\n```css\n.my-component {\n padding: 16px;\n}\n\n.my-component h1 {\n font-size: 24px;\n}\n```\n\n## Section Pattern (page-level containers like headers, hero banners, footers)\n\nSections use a `<section>` root element, full-width styling, and a `Props` interface. In `ikas.config.json` they have `\"type\": \"section\"`.\n\n### index.tsx - Section Implementation\n```tsx\nimport { Props } from \"./types\";\n\nexport function HeroBanner({ heading, subtitle, backgroundColor }: Props) {\n return (\n <section className=\"hero-banner\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"hero-banner-inner\">\n <h2>{heading}</h2>\n {subtitle && <p>{subtitle}</p>}\n </div>\n </section>\n );\n}\n\nexport default HeroBanner;\n```\n\n### types.ts - Section Props Interface\n```typescript\nexport interface Props {\n heading: string;\n subtitle?: string;\n backgroundColor?: string;\n}\n```\n\n### styles.css - Full-width section styles\n```css\n.hero-banner {\n width: 100%;\n padding: 64px 24px;\n}\n\n.hero-banner-inner {\n max-width: 1200px;\n margin: 0 auto;\n}\n```\n\n## Key Rules\n- Export the component as both a named export and a default export: `export function X() {...}` + `export default X;`\n- Use Preact (not React) - but JSX syntax is the same\n- Import types from `./types` for props\n- Use `className` not `class` for CSS classes\n- Storefront functions and types come from `@ikas/bp-storefront`\n- CSS classes are automatically scoped to your component at build time. Use plain CSS class selectors - they won't conflict with other components or the page.\n\n## Root Components Are Automatically Reactive\n\nThe ikas runtime wraps every root component render in a MobX `autorun()`, so **root exports are already reactive** — they automatically re-render when any MobX store they read (`cartStore`, `customerStore`, etc.) changes. You do **not** need `observer()` on root component exports.\n\n### Correct: Plain root export reading stores\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function CartSummary({ title }: Props) {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return (\n <section className=\"cart-summary\">\n <h2>{title}</h2>\n <p>{itemCount} items in cart</p>\n </section>\n );\n}\n\nexport default CartSummary;\n```\n\n### Anti-pattern: Do NOT wrap root exports with observer\n```tsx\n// WRONG — observer() is redundant on root components\nconst CartSummary = observer(function CartSummary({ title }: Props) {\n ...\n});\nexport default CartSummary;\n```\n\n## Using observer for Sub-Components\n\nWhen you extract a **sub-component** that independently reads MobX stores, wrap it with `observer()` from `@ikas/component-utils` so it re-renders when store data changes.\n\n### When to use observer\n- A **sub-component** (not the root export) reads `cartStore.cart`, `customerStore.customer`, or any other MobX observable\n- You extract part of a component into a separate function that needs independent reactivity\n\n### When observer is NOT needed\n- **Root component exports** — the ikas runtime handles reactivity via `autorun()`\n- Components that only use props passed from parent — no store reads\n- Static components with no reactive data\n\n### Example: Root export with observer sub-component\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span className=\"cart-badge\">{itemCount}</span>;\n});\n\n// Root export: NO observer needed — autorun() handles reactivity\nexport function Header({ logo }: Props) {\n return (\n <header>\n <img src={logo} alt=\"Logo\" />\n <CartBadge />\n </header>\n );\n}\n\nexport default Header;\n```\n\n**Important:** When using `observer` on sub-components, define the component as a named function expression (not arrow function) and assign it to a `const`. This ensures proper display names in React DevTools.\n\n## Sub-Component File Organization\n\nWhen you extract sub-components, **ALWAYS** place them in `src/sub-components/` with their own folder containing `index.tsx` and `styles.css`. Never create flat `.tsx` files inside a component folder. See `get_framework_guide(\"sub-component-patterns\")` for the full pattern, directory structure, CSS @import rules, and examples.",
56
56
  "tags": [
57
57
  "component",
58
58
  "preact",
@@ -111,7 +111,7 @@
111
111
  "imports": {
112
112
  "title": "Import Patterns",
113
113
  "description": "How to import storefront functions and types",
114
- "content": "### Storefront Functions & Types\nImport API functions and TypeScript types from `@ikas/bp-storefront`:\n```typescript\nimport {\n // Product functions\n getSelectedProductVariant,\n getDisplayedProductVariantTypes,\n selectVariantValue,\n hasProductVariantStock,\n getProductVariantFormattedFinalPrice,\n \n // Cart functions\n addItemToCart,\n cartStore,\n \n // Navigation\n Router,\n \n // Customer\n customerStore,\n customerLogin,\n hasCustomer,\n \n // Favorites\n addIkasProductToFavorites,\n isFavoriteIkasProduct,\n\n // Type models\n IkasProduct,\n IkasProductVariant,\n IkasCart,\n IkasOrderLineItem,\n IkasCustomer,\n IkasOrder,\n IkasCategory,\n IkasBrand,\n IkasBlogPost\n} from \"@ikas/bp-storefront\";\n```\n\n### Store Instances\nPre-initialized MobX stores:\n```typescript\nimport { cartStore, customerStore } from \"@ikas/bp-storefront\";\n\n// Cart data\ncartStore.cart?.orderLineItems\n\n// Customer data \ncustomerStore.customer?.email\n```\n\n### Observer (for sub-components with reactive updates)\n```typescript\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span>Cart: {itemCount}</span>;\n});\n\n// Root export: plain function — ikas runtime handles reactivity via autorun()\nexport default function MySection({ title }: Props) {\n return (\n <div>\n <h1>{title}</h1>\n <CartBadge />\n </div>\n );\n}\n```\n\nDo **NOT** wrap root component exports with `observer()`. The ikas runtime handles root reactivity automatically via `autorun()`. Only wrap **sub-components** with `observer()` when they independently read from stores (`cartStore`, `customerStore`).",
114
+ "content": "### Storefront Functions & Types\nImport API functions and TypeScript types from `@ikas/bp-storefront`:\n```typescript\nimport {\n // Product functions\n getSelectedProductVariant,\n getDisplayedProductVariantTypes,\n selectVariantValue,\n hasProductVariantStock,\n getProductVariantFormattedFinalPrice,\n \n // Cart functions\n addItemToCart,\n cartStore,\n \n // Navigation\n Router,\n \n // Customer\n customerStore,\n customerLogin,\n hasCustomer,\n \n // Favorites\n addIkasProductToFavorites,\n isFavoriteIkasProduct,\n\n // Type models\n IkasProduct,\n IkasProductVariant,\n IkasCart,\n IkasOrderLineItem,\n IkasCustomer,\n IkasOrder,\n IkasCategory,\n IkasBrand,\n IkasBlog\n} from \"@ikas/bp-storefront\";\n```\n\n### Store Instances\nPre-initialized MobX stores:\n```typescript\nimport { cartStore, customerStore } from \"@ikas/bp-storefront\";\n\n// Cart data\ncartStore.cart?.orderLineItems\n\n// Customer data \ncustomerStore.customer?.email\n```\n\n### Observer (for sub-components with reactive updates)\n```typescript\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span>Cart: {itemCount}</span>;\n});\n\n// Root export: plain function — ikas runtime handles reactivity via autorun()\nexport function MySection({ title }: Props) {\n return (\n <div>\n <h1>{title}</h1>\n <CartBadge />\n </div>\n );\n}\n\nexport default MySection;\n```\n\nDo **NOT** wrap root component exports with `observer()`. The ikas runtime handles root reactivity automatically via `autorun()`. Only wrap **sub-components** with `observer()` when they independently read from stores (`cartStore`, `customerStore`).",
115
115
  "tags": [
116
116
  "import",
117
117
  "storefront",
@@ -138,7 +138,7 @@
138
138
  "common-pitfalls": {
139
139
  "title": "Common Pitfalls",
140
140
  "description": "Frequent mistakes LLMs and developers make when building ikas code components",
141
- "content": "## Common Pitfalls\n\nThese are the most common mistakes when building ikas code components. Avoid them for correct, working code.\n\n### 1. Root Component Should NOT Use observer\n\nThe ikas runtime wraps root component renders in `autorun()`, making them automatically reactive. Wrapping a root export with `observer()` is redundant and misleading.\n\n**Wrong** — observer on root export:\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nconst CartSection = observer(function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n});\nexport default CartSection;\n```\n\n**Correct** — plain named export:\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nexport default function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n}\n```\n\n### 2. Observer Sub-Component Naming\n\nWhen using `observer()` on **sub-components**, use a named function expression — not an arrow function — for proper DevTools display names.\n\n**Wrong** — arrow function loses display name:\n```tsx\nconst CartBadge = observer(() => {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n**Correct** — named function expression:\n```tsx\nconst CartBadge = observer(function CartBadge() {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n### 3. Mutation Semantics\n\nMany storefront functions (122+) return `void` and **mutate their arguments in place**. Do NOT try to capture a return value:\n\n```tsx\n// WRONG — selectVariantValue returns void, not a new product\nconst updated = selectVariantValue(product, value);\n\n// CORRECT — mutates product in place, observer re-renders automatically\nselectVariantValue(product, dvv.variantValue);\n```\n\nOther mutation functions: `initLoginForm()`, `setLoginFormEmail()`, `clearFilter()`, `selectFilterValue()`.\n\n### 4. CSS Scoping Limits\n\nOnly **class selectors** in `styles.css` are reliably scoped. Element selectors are NOT scoped:\n\n```css\n/* SAFE — scoped to your component */\n.product-card { padding: 16px; }\n.product-card .title { font-size: 18px; }\n\n/* UNSAFE — NOT reliably scoped, may affect other components */\ndiv { margin: 0; }\nh1 { font-size: 24px; }\n```\n\nAlways use class selectors for all styles.\n\n### 5. Prop Null Handling\n\nProps from the editor can be `undefined` when the store owner hasn't set them. Always use optional chaining:\n\n```tsx\n// WRONG — will crash if product is undefined\n<h1>{props.product.name}</h1>\n\n// CORRECT\n<h1>{props.product?.name}</h1>\n{props.heroImage && <img src={getDefaultSrc(props.heroImage)} />}\n```\n\n### 6. IkasProductImage vs IkasImage\n\n`variant.images` is `IkasProductImage[]`, NOT `IkasImage[]`. You must access the `.image` property to get the `IkasImage` needed by CDN helpers:\n\n```tsx\n// WRONG — productImage is IkasProductImage, not IkasImage\ngetDefaultSrc(productImage);\n\n// CORRECT — access .image to get IkasImage\ngetDefaultSrc(productImage.image);\n\n// Full pattern:\nconst images: IkasImage[] = variant.images\n .map((pi) => pi.image)\n .filter((img): img is IkasImage => img != null);\n```\n\n### 7. Type Assertion Pattern\n\nSome storefront functions have type inference gaps. Use `as unknown as` casts when needed — this is a known pattern:\n\n```tsx\nconst inStock = hasProductStock(product) as unknown as boolean;\nconst finalPrice = getProductVariantFormattedFinalPrice(variant) as unknown as string;\nconst canAddToCart = isAddToCartEnabled(product) as unknown as boolean;\n```\n\nThis applies to functions like `hasProductStock`, `hasProductVariantStock`, `isAddToCartEnabled`, `hasProductVariantDiscount`, `getProductVariantDiscountPercentage`, `getProductVariantFormattedFinalPrice`, `getProductVariantFormattedSellPrice`, `getProductVariantFormattedDiscountAmount`, and `getProductVariantFormattedCampaignPrice`.\n\nNote: `getProductVariantMainImage` returns `IkasProductImage | undefined` (NOT `IkasImage`). Access `.image` to get the `IkasImage` for CDN helpers like `getDefaultSrc()`.\n\n### 8. Store Data Null Safety\n\nStore data (`customerStore.customer`, `cartStore.cart`, `baseStore`) is `null` before initialization completes. Always guard access:\n\n```tsx\n// WRONG — crashes if customer is null\n<h1>{customerStore.customer.firstName}</h1>\n\n// CORRECT — null check first\nif (!customerStore.customer) return <div>Loading...</div>;\n<h1>{customerStore.customer.firstName}</h1>\n\n// Also correct — optional chaining\n<h1>{customerStore.customer?.firstName ?? \"Guest\"}</h1>\n```\n\nSame for `cartStore.cart` — always use `cartStore.cart?.orderLineItems ?? []`.\n\n### 9. ProductList/BlogList Data Access\n\n`productList.products` is the correct way to access products in a product list (NOT `productList.data`). Similarly, `blogList.blogs` for blogs:\n\n```tsx\n// CORRECT\nconst products = productList?.products ?? [];\nconst blogs = blogList?.blogs ?? [];\n\n// WRONG — .data does not exist on these types\nconst products = productList?.data ?? [];\n```\n\n### 10. Form Field Access Pattern\n\nForm fields are objects with `.value`, `.label`, `.hasError`, `.message`. Never access the field directly as a primitive:\n\n```tsx\n// WRONG — loginForm.email is a field object, not a string\n<input value={loginForm.email} />\n\n// CORRECT — access .value for the actual value\n<input value={loginForm.email.value} />\n{loginForm.email.hasError && <span>{loginForm.email.message}</span>}\n```\n\n### 11. Optional Chaining for Editor Props\n\nAll props from `ikas.config.json` can be `undefined` in the editor before the store owner sets them. Always use optional chaining and defaults:\n\n```tsx\n// WRONG — will crash in the editor\n<h1>{props.title}</h1>\n<img src={getDefaultSrc(props.image)} />\n{props.links.links.map(...)}\n\n// CORRECT — safe access with defaults\n<h1>{props.title ?? \"Default Title\"}</h1>\n{props.image && <img src={getDefaultSrc(props.image)} />}\n{(props.links?.links ?? []).map(...)}\n```\n\n### 12. Event Handler Typing\n\nPreact uses different event types than React. Use `(e: Event)` not `(e: React.ChangeEvent)`. Access values with casting. Preact uses `onInput` not `onChange` for text inputs:\n\n```tsx\n// WRONG — React patterns don't work in Preact\nonChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)}\n\n// CORRECT — Preact event handling\nonInput={(e: Event) => setValue((e.target as HTMLInputElement).value)}\n```\n\nFor select elements:\n```tsx\nonChange={(e: Event) => setOption((e.target as HTMLSelectElement).value)}\n```\n\n### 13. Function Parameter Order\n\nMany storefront functions take specific parameter orders. Always verify with `get_function_doc()` before using:\n\n```tsx\n// WRONG — submitLoginForm only takes the form, not the store\nsubmitLoginForm(customerStore, loginForm);\n\n// CORRECT\nsubmitLoginForm(loginForm);\n\n// WRONG — wrong parameter order for addItemToCart\naddItemToCart(product, variant, 1);\n\n// CORRECT — variant first, then product, then quantity\naddItemToCart(variant, product, 1);\n\n// WRONG — selectVariantValue takes product and variantValue\nselectVariantValue(variant, value);\n\n// CORRECT\nselectVariantValue(product, dvv.variantValue);\n```\n\nWhen in doubt, use the `get_function_doc(functionName)` MCP tool to check the exact signature.",
141
+ "content": "## Common Pitfalls\n\nThese are the most common mistakes when building ikas code components. Avoid them for correct, working code.\n\n### 1. Root Component Should NOT Use observer\n\nThe ikas runtime wraps root component renders in `autorun()`, making them automatically reactive. Wrapping a root export with `observer()` is redundant and misleading.\n\n**Wrong** — observer on root export:\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nconst CartSection = observer(function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n});\nexport default CartSection;\n```\n\n**Correct** — plain named export:\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nexport function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n}\n\nexport default CartSection;\n```\n\n### 2. Observer Sub-Component Naming\n\nWhen using `observer()` on **sub-components**, use a named function expression — not an arrow function — for proper DevTools display names.\n\n**Wrong** — arrow function loses display name:\n```tsx\nconst CartBadge = observer(() => {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n**Correct** — named function expression:\n```tsx\nconst CartBadge = observer(function CartBadge() {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n### 3. Mutation Semantics\n\nMany storefront functions (122+) return `void` and **mutate their arguments in place**. Do NOT try to capture a return value:\n\n```tsx\n// WRONG — selectVariantValue returns void, not a new product\nconst updated = selectVariantValue(product, value);\n\n// CORRECT — mutates product in place, observer re-renders automatically\nselectVariantValue(product, dvv.variantValue);\n```\n\nOther mutation functions: `initLoginForm()`, `setLoginFormEmail()`, `clearFilter()`, `selectFilterValue()`.\n\n### 4. CSS Scoping Limits\n\nOnly **class selectors** in `styles.css` are reliably scoped. Element selectors are NOT scoped:\n\n```css\n/* SAFE — scoped to your component */\n.product-card { padding: 16px; }\n.product-card .title { font-size: 18px; }\n\n/* UNSAFE — NOT reliably scoped, may affect other components */\ndiv { margin: 0; }\nh1 { font-size: 24px; }\n```\n\nAlways use class selectors for all styles.\n\n### 5. Prop Null Handling\n\nProps from the editor can be `undefined` when the store owner hasn't set them. Always use optional chaining:\n\n```tsx\n// WRONG — will crash if product is undefined\n<h1>{props.product.name}</h1>\n\n// CORRECT\n<h1>{props.product?.name}</h1>\n{props.heroImage && <img src={getDefaultSrc(props.heroImage)} />}\n```\n\n### 6. IkasProductImage vs IkasImage\n\n`variant.images` is `IkasProductImage[]`, NOT `IkasImage[]`. You must access the `.image` property to get the `IkasImage` needed by CDN helpers:\n\n```tsx\n// WRONG — productImage is IkasProductImage, not IkasImage\ngetDefaultSrc(productImage);\n\n// CORRECT — access .image to get IkasImage\ngetDefaultSrc(productImage.image);\n\n// Full pattern:\nconst images: IkasImage[] = variant.images\n .map((pi) => pi.image)\n .filter((img): img is IkasImage => img != null);\n```\n\n### 7. Type Assertion Pattern\n\nSome storefront functions have type inference gaps. Use `as unknown as` casts when needed — this is a known pattern:\n\n```tsx\nconst inStock = hasProductStock(product) as unknown as boolean;\nconst finalPrice = getProductVariantFormattedFinalPrice(variant) as unknown as string;\nconst canAddToCart = isAddToCartEnabled(product) as unknown as boolean;\n```\n\nThis applies to functions like `hasProductStock`, `hasProductVariantStock`, `isAddToCartEnabled`, `hasProductVariantDiscount`, `getProductVariantDiscountPercentage`, `getProductVariantFormattedFinalPrice`, `getProductVariantFormattedSellPrice`, `getProductVariantFormattedDiscountAmount`, and `getProductVariantFormattedCampaignPrice`.\n\nNote: `getProductVariantMainImage` returns `IkasProductImage | undefined` (NOT `IkasImage`). Access `.image` to get the `IkasImage` for CDN helpers like `getDefaultSrc()`.\n\n### 8. Store Data Null Safety\n\nStore data (`customerStore.customer`, `cartStore.cart`, `baseStore`) is `null` before initialization completes. Always guard access:\n\n```tsx\n// WRONG — crashes if customer is null\n<h1>{customerStore.customer.firstName}</h1>\n\n// CORRECT — null check first\nif (!customerStore.customer) return <div>Loading...</div>;\n<h1>{customerStore.customer.firstName}</h1>\n\n// Also correct — optional chaining\n<h1>{customerStore.customer?.firstName ?? \"Guest\"}</h1>\n```\n\nSame for `cartStore.cart` — always use `cartStore.cart?.orderLineItems ?? []`.\n\n### 9. ProductList/BlogList Data Access\n\n`productList.data` is the correct way to access products in a product list. Similarly, `blogList.data` for blogs. The display names `.products` / `.blogs` are only used by the blueprint editor — they do NOT exist at runtime:\n\n```tsx\n// CORRECT — use .data for both product lists and blog lists\nconst products = productList?.data ?? [];\nconst blogs = blogList?.data ?? [];\n\n// WRONG — .products / .blogs are display names, not actual fields\nconst products = productList?.products ?? [];\nconst blogs = blogList?.blogs ?? [];\n```\n\n### 10. Form Field Access Pattern\n\nForm fields are objects with `.value`, `.label`, `.hasError`, `.message`. Never access the field directly as a primitive:\n\n```tsx\n// WRONG — loginForm.email is a field object, not a string\n<input value={loginForm.email} />\n\n// CORRECT — access .value for the actual value\n<input value={loginForm.email.value} />\n{loginForm.email.hasError && <span>{loginForm.email.message}</span>}\n```\n\n### 11. Optional Chaining for Editor Props\n\nAll props from `ikas.config.json` can be `undefined` in the editor before the store owner sets them. Always use optional chaining and defaults:\n\n```tsx\n// WRONG — will crash in the editor\n<h1>{props.title}</h1>\n<img src={getDefaultSrc(props.image)} />\n{props.links.links.map(...)}\n\n// CORRECT — safe access with defaults\n<h1>{props.title ?? \"Default Title\"}</h1>\n{props.image && <img src={getDefaultSrc(props.image)} />}\n{(props.links?.links ?? []).map(...)}\n```\n\n### 12. Event Handler Typing\n\nPreact uses different event types than React. Use `(e: Event)` not `(e: React.ChangeEvent)`. Access values with casting. Preact uses `onInput` not `onChange` for text inputs:\n\n```tsx\n// WRONG — React patterns don't work in Preact\nonChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)}\n\n// CORRECT — Preact event handling\nonInput={(e: Event) => setValue((e.target as HTMLInputElement).value)}\n```\n\nFor select elements:\n```tsx\nonChange={(e: Event) => setOption((e.target as HTMLSelectElement).value)}\n```\n\n### 13. Function Parameter Order\n\nMany storefront functions take specific parameter orders. Always verify with `get_function_doc()` before using:\n\n```tsx\n// WRONG — submitLoginForm only takes the form, not the store\nsubmitLoginForm(customerStore, loginForm);\n\n// CORRECT\nsubmitLoginForm(loginForm);\n\n// WRONG — wrong parameter order for addItemToCart\naddItemToCart(product, variant, 1);\n\n// CORRECT — variant first, then product, then quantity\naddItemToCart(variant, product, 1);\n\n// WRONG — selectVariantValue takes product and variantValue\nselectVariantValue(variant, value);\n\n// CORRECT\nselectVariantValue(product, dvv.variantValue);\n```\n\nWhen in doubt, use the `get_function_doc(functionName)` MCP tool to check the exact signature.",
142
142
  "tags": [
143
143
  "pitfalls",
144
144
  "gotchas",
@@ -156,7 +156,7 @@
156
156
  "sub-component-patterns": {
157
157
  "title": "Sub-Component File Organization",
158
158
  "description": "How to organize sub-components in their own folders under src/sub-components/ with proper imports and CSS",
159
- "content": "## Sub-Component File Organization\n\n**ALWAYS create sub-components in `src/sub-components/` with their own folder containing `index.tsx` and `styles.css`. NEVER create flat .tsx files inside a component folder.**\n\n### Directory Structure\n\n```\nsrc/\n├── components/\n│ ├── ProductList/\n│ │ ├── index.tsx # imports from ../../sub-components/...\n│ │ ├── types.ts # auto-generated (CLI-managed)\n│ │ └── styles.css # @import \"../../sub-components/.../styles.css\"\n│ └── index.ts\n└── sub-components/\n ├── ProductCard/\n │ ├── index.tsx # sub-component\n │ └── styles.css # sub-component styles\n └── FilterSidebar/\n ├── index.tsx # sub-component\n └── styles.css # sub-component styles\n```\n\n### Key Rules\n\n1. **`src/components/`** = registered components (listed in `ikas.config.json`)\n2. **`src/sub-components/`** = internal helpers (NOT in `ikas.config.json`)\n3. Sub-components do **NOT** have `types.ts` — define `Props` interface inline in `index.tsx`\n4. Sub-components that read MobX stores need `observer()` from `@ikas/component-utils`\n5. Sub-components that only receive props do NOT need `observer()`\n6. CSS is scoped with the parent component's `.cc_` prefix (same scope — no conflicts)\n7. Sub-components can be shared across multiple parent sections\n\n### CSS Import Pattern\n\nIn the parent component's `styles.css`, import sub-component styles with `@import`:\n\n```css\n/* src/components/ProductList/styles.css */\n@import \"../../sub-components/ProductCard/styles.css\";\n@import \"../../sub-components/FilterSidebar/styles.css\";\n\n.product-list-section {\n width: 100%;\n padding: 40px 24px;\n}\n```\n\nThe build system's CSS import resolver recursively resolves `@import` statements with relative paths. All imported CSS gets scoped with the parent component's `.cc_{componentId}` prefix.\n\n### TSX Import Pattern\n\n```tsx\n// src/components/ProductList/index.tsx\nimport ProductCard from \"../../sub-components/ProductCard\";\nimport FilterSidebar from \"../../sub-components/FilterSidebar\";\nimport { Props } from \"./types\";\n\nexport default function ProductListSection({ productList, title }: Props) {\n const products = productList?.products ?? [];\n return (\n <section className=\"product-list-section\">\n <FilterSidebar productList={productList} />\n <div className=\"product-grid\">\n {products.map((product) => (\n <ProductCard key={product.id} product={product} />\n ))}\n </div>\n </section>\n );\n}\n```\n\n### Sub-Component Example\n\n```tsx\n// src/sub-components/ProductCard/index.tsx\nimport {\n IkasProduct,\n getSelectedProductVariant,\n getProductVariantFormattedFinalPrice,\n getProductVariantMainImage,\n getSelectedProductVariantHref,\n getDefaultSrc,\n} from \"@ikas/bp-storefront\";\n\ninterface Props {\n product: IkasProduct;\n}\n\nexport default function ProductCard({ product }: Props) {\n const variant = getSelectedProductVariant(product);\n const productImage = getProductVariantMainImage(variant);\n const image = productImage?.image ?? null;\n const price = getProductVariantFormattedFinalPrice(variant) as unknown as string;\n\n return (\n <a href={getSelectedProductVariantHref(product)} className=\"product-card\">\n {image && <img src={getDefaultSrc(image)} alt={product.name} className=\"product-card-image\" />}\n <h3 className=\"product-card-name\">{product.name}</h3>\n <span className=\"product-card-price\">{price}</span>\n </a>\n );\n}\n```\n\nNotice:\n- `Props` is defined inline — no separate `types.ts` file\n- No `observer()` needed because this component only uses props passed from the parent\n- Default export like registered components\n\n### When to Extract Sub-Components\n\n- A section's `index.tsx` exceeds ~150 lines\n- A distinct UI block (card, sidebar, modal) has its own styles and logic\n- The same UI block is used by multiple parent sections\n- A part of the component independently reads MobX stores (extract + wrap with `observer()`)\n\n### When observer() Is Needed on Sub-Components\n\n```tsx\n// src/sub-components/CartBadge/index.tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\n// This sub-component independently reads a MobX store → needs observer()\nconst CartBadge = observer(function CartBadge() {\n const count = cartStore.cart?.orderLineItems.length ?? 0;\n return <span className=\"cart-badge\">{count}</span>;\n});\n\nexport default CartBadge;\n```",
159
+ "content": "## Sub-Component File Organization\n\n**ALWAYS create sub-components in `src/sub-components/` with their own folder containing `index.tsx` and `styles.css`. NEVER create flat .tsx files inside a component folder.**\n\n### Directory Structure\n\n```\nsrc/\n├── components/\n│ ├── ProductList/\n│ │ ├── index.tsx # imports from ../../sub-components/...\n│ │ ├── types.ts # auto-generated (CLI-managed)\n│ │ └── styles.css # @import \"../../sub-components/.../styles.css\"\n│ └── index.ts\n└── sub-components/\n ├── ProductCard/\n │ ├── index.tsx # sub-component\n │ └── styles.css # sub-component styles\n └── FilterSidebar/\n ├── index.tsx # sub-component\n └── styles.css # sub-component styles\n```\n\n### Key Rules\n\n1. **`src/components/`** = registered components (listed in `ikas.config.json`)\n2. **`src/sub-components/`** = internal helpers (NOT in `ikas.config.json`)\n3. Sub-components do **NOT** have `types.ts` — define `Props` interface inline in `index.tsx`\n4. Sub-components that read MobX stores need `observer()` from `@ikas/component-utils`\n5. Sub-components that only receive props do NOT need `observer()`\n6. CSS is scoped with the parent component's `.cc_` prefix (same scope — no conflicts)\n7. Sub-components can be shared across multiple parent sections\n\n### CSS Import Pattern\n\nIn the parent component's `styles.css`, import sub-component styles with `@import`:\n\n```css\n/* src/components/ProductList/styles.css */\n@import \"../../sub-components/ProductCard/styles.css\";\n@import \"../../sub-components/FilterSidebar/styles.css\";\n\n.product-list-section {\n width: 100%;\n padding: 40px 24px;\n}\n```\n\nThe build system's CSS import resolver recursively resolves `@import` statements with relative paths. All imported CSS gets scoped with the parent component's `.cc_{componentId}` prefix.\n\n### TSX Import Pattern\n\n```tsx\n// src/components/ProductList/index.tsx\nimport ProductCard from \"../../sub-components/ProductCard\";\nimport FilterSidebar from \"../../sub-components/FilterSidebar\";\nimport { Props } from \"./types\";\n\nexport function ProductListSection({ productList, title }: Props) {\n const products = productList?.data ?? [];\n return (\n <section className=\"product-list-section\">\n <FilterSidebar productList={productList} />\n <div className=\"product-grid\">\n {products.map((product) => (\n <ProductCard key={product.id} product={product} />\n ))}\n </div>\n </section>\n );\n}\n\nexport default ProductListSection;\n```\n\n### Sub-Component Example\n\n```tsx\n// src/sub-components/ProductCard/index.tsx\nimport {\n IkasProduct,\n getSelectedProductVariant,\n getProductVariantFormattedFinalPrice,\n getProductVariantMainImage,\n getSelectedProductVariantHref,\n getDefaultSrc,\n} from \"@ikas/bp-storefront\";\n\ninterface Props {\n product: IkasProduct;\n}\n\nexport default function ProductCard({ product }: Props) {\n const variant = getSelectedProductVariant(product);\n const productImage = getProductVariantMainImage(variant);\n const image = productImage?.image ?? null;\n const price = getProductVariantFormattedFinalPrice(variant) as unknown as string;\n\n return (\n <a href={getSelectedProductVariantHref(product)} className=\"product-card\">\n {image && <img src={getDefaultSrc(image)} alt={product.name} className=\"product-card-image\" />}\n <h3 className=\"product-card-name\">{product.name}</h3>\n <span className=\"product-card-price\">{price}</span>\n </a>\n );\n}\n```\n\nNotice:\n- `Props` is defined inline — no separate `types.ts` file\n- No `observer()` needed because this component only uses props passed from the parent\n- Default export like registered components\n\n### When to Extract Sub-Components\n\n- A section's `index.tsx` exceeds ~150 lines\n- A distinct UI block (card, sidebar, modal) has its own styles and logic\n- The same UI block is used by multiple parent sections\n- A part of the component independently reads MobX stores (extract + wrap with `observer()`)\n\n### When observer() Is Needed on Sub-Components\n\n```tsx\n// src/sub-components/CartBadge/index.tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\n// This sub-component independently reads a MobX store → needs observer()\nconst CartBadge = observer(function CartBadge() {\n const count = cartStore.cart?.orderLineItems.length ?? 0;\n return <span className=\"cart-badge\">{count}</span>;\n});\n\nexport default CartBadge;\n```",
160
160
  "tags": [
161
161
  "sub-component",
162
162
  "folder",
@@ -171,7 +171,7 @@
171
171
  "ai-workflow": {
172
172
  "title": "AI Agent Workflow for Building ikas Components",
173
173
  "description": "Step-by-step guide for AI coding agents building ikas storefront components using CLI commands and MCP tools",
174
- "content": "## AI Agent Workflow\n\nThis is the recommended step-by-step workflow for AI agents building ikas code components. Follow these steps in order for reliable, error-free results.\n\n**IMPORTANT: NEVER create or edit `types.ts` manually — it is auto-generated by the CLI.** The CLI commands below update BOTH `ikas.config.json` AND `types.ts` automatically.\n\n### Step 1: Create Component with Props\n\nUse `get_section_template(sectionType)` to get a starter template — it includes a ready-to-run CLI command with `--props`.\n\nThen run the single `add-component --props` command to create the component scaffold WITH all props in one step:\n```bash\nnpx ikas-component config add-component --name \"HeroSection\" --type section --props '[{\"name\":\"title\",\"displayName\":\"Title\",\"type\":\"TEXT\",\"required\":true},{\"name\":\"backgroundImage\",\"displayName\":\"Background Image\",\"type\":\"IMAGE\"},{\"name\":\"showButton\",\"displayName\":\"Show Button\",\"type\":\"BOOLEAN\"}]'\n```\n\nThis creates the component directory with `index.tsx`, `types.ts` (with correct Props interface), `styles.css`, updates `ikas.config.json`, and updates the barrel export. The output is JSON:\n```json\n{\"success\": true, \"componentId\": \"abc123-hero-section\", \"componentName\": \"HeroSection\", \"type\": \"section\", \"propsCount\": 3, \"files\": [...]}\n```\n\nThe `--props` flag accepts a JSON array of prop objects. Each prop needs `name` + `type` at minimum. `displayName` is auto-generated from camelCase name if omitted (e.g. `backgroundImage` → `\"Background Image\"`).\n\nTo add more props later, use `add-prop`:\n```bash\nnpx ikas-component config add-prop --component \"HeroSection\" --name \"subtitle\" --displayName \"Subtitle\" --type TEXT\n```\n\n### Step 2: Get Reference Material\n\nBefore writing component code, use MCP tools to get the right patterns:\n- `get_section_template(sectionType)` — Get a starter template for common section types (product-detail, cart, login, header, footer, etc.)\n- `get_framework_guide(\"common-pitfalls\")` — Review common mistakes to avoid\n- `get_framework_guide(\"component-structure\")` — Review component structure patterns\n- `get_function_doc(functionName)` — Look up exact function signatures before using them\n- `get_model_guide(typeName)` — Get comprehensive info about a model type\n\n### Step 3: Write the Component Code\n\nEdit `src/components/{ComponentName}/index.tsx` with the implementation. **Do NOT edit `types.ts`** — it was already generated correctly in Step 1. Key rules:\n- Import props from `./types` (auto-generated in Step 1)\n- Import storefront functions from `@ikas/bp-storefront`\n- Root export should be a plain named function: `export default function X({ ... }: Props) { ... }`\n- Only wrap sub-components with `observer()` when they independently read MobX stores\n- Use optional chaining for all props: `props.title ?? \"Default\"`\n- Use `as unknown as boolean` cast for functions like `hasProductStock`, `isAddToCartEnabled`\n\n### Step 4: Write Styles\n\nEdit `src/components/{ComponentName}/styles.css`. Key rules:\n- Use class selectors only (`.my-class`), never bare element selectors\n- CSS is auto-scoped at build time — no manual namespacing needed\n- For sections: `width: 100%; padding: 64px 24px;` with inner max-width container\n\n### Step 5: Verify with Type Checking\n\nRun the lightweight type checker:\n```bash\nnpx ikas-component check --json\n```\n\nSuccess output:\n```json\n{\"success\": true, \"errors\": []}\n```\n\nError output:\n```json\n{\"success\": false, \"errorCount\": 2, \"errors\": [{\"file\": \"src/components/Hero/index.tsx\", \"line\": 15, \"column\": 3, \"code\": \"TS2339\", \"message\": \"Property 'title' does not exist on type 'Props'\"}]}\n```\n\n### Step 6: Fix Errors and Re-check\n\nFor each error:\n1. Read the file and line number from the error\n2. Fix the issue (common fixes below)\n3. Re-run `npx ikas-component check --json`\n\n**Common error fixes:**\n- `Property 'x' does not exist on type 'Props'` — Prop wasn't added via CLI. Run `npx ikas-component config add-prop`.\n- `Cannot find module '@ikas/bp-storefront'` — Normal in type-check if node_modules not fully installed. Focus on component-level errors.\n- `Type 'X' is not assignable to type 'Y'` — Check function signature with `get_function_doc()`.\n\n### Step 7: Full Build Validation\n\nOnce type checking passes, run the full build:\n```bash\nnpx ikas-component build\n```\n\nThis runs type checking + esbuild compilation + CSS scoping.\n\n### Quick Reference: CLI Commands\n\n| Command | Purpose |\n|---------|--------|\n| `npx ikas-component config add-component --name X --type section --props '[...]'` | **Primary** — create component with all props |\n| `npx ikas-component config add-component --name X --type section` | Create component with no props |\n| `npx ikas-component config add-prop --component X --name y --displayName Y --type TEXT` | Add a prop incrementally |\n| `npx ikas-component config update-prop --component X --prop y --required true` | Update a prop |\n| `npx ikas-component config remove-prop --component X --prop y` | Remove a prop |\n| `npx ikas-component config remove-component --name X` | Remove a component |\n| `npx ikas-component config list` | List all components and props |\n| `npx ikas-component check --json` | Type-check with JSON output |\n| `npx ikas-component build` | Full production build |\n\n### Quick Reference: MCP Tools\n\n| Tool | When to Use |\n|------|------------|\n| `get_section_template(type)` | Starting a new section — get starter code + CLI command |\n| `get_framework_guide(topic)` | Understanding patterns, pitfalls, architecture |\n| `get_function_doc(name)` | Looking up exact function signature |\n| `get_model_guide(type)` | Working with a model type (IkasProduct, IkasCart, etc.) |\n| `get_prop_types()` | Checking available prop types for ikas.config.json |\n| `search_docs(query)` | Finding relevant functions/docs by keyword |",
174
+ "content": "## AI Agent Workflow\n\nThis is the recommended step-by-step workflow for AI agents building ikas code components. Follow these steps in order for reliable, error-free results.\n\n**IMPORTANT: NEVER create or edit `types.ts` manually — it is auto-generated by the CLI.** The CLI commands below update BOTH `ikas.config.json` AND `types.ts` automatically.\n\n### Step 1: Create Component with Props\n\nUse `get_section_template(sectionType)` to get a starter template — it includes a ready-to-run CLI command with `--props`.\n\nThen run the single `add-component --props` command to create the component scaffold WITH all props in one step:\n```bash\nnpx ikas-component config add-component --name \"HeroSection\" --type section --props '[{\"name\":\"title\",\"displayName\":\"Title\",\"type\":\"TEXT\",\"required\":true},{\"name\":\"backgroundImage\",\"displayName\":\"Background Image\",\"type\":\"IMAGE\"},{\"name\":\"showButton\",\"displayName\":\"Show Button\",\"type\":\"BOOLEAN\"}]'\n```\n\nThis creates the component directory with `index.tsx`, `types.ts` (with correct Props interface), `styles.css`, updates `ikas.config.json`, and updates the barrel export. The output is JSON:\n```json\n{\"success\": true, \"componentId\": \"abc123-hero-section\", \"componentName\": \"HeroSection\", \"type\": \"section\", \"propsCount\": 3, \"files\": [...]}\n```\n\nThe `--props` flag accepts a JSON array of prop objects. Each prop needs `name` + `type` at minimum. `displayName` is auto-generated from camelCase name if omitted (e.g. `backgroundImage` → `\"Background Image\"`).\n\nTo add more props later, use `add-prop`:\n```bash\nnpx ikas-component config add-prop --component \"HeroSection\" --name \"subtitle\" --displayName \"Subtitle\" --type TEXT\n```\n\n### Step 2: Get Reference Material\n\nBefore writing component code, use MCP tools to get the right patterns:\n- `get_section_template(sectionType)` — Get a starter template for common section types (product-detail, cart, login, header, footer, etc.)\n- `get_framework_guide(\"common-pitfalls\")` — Review common mistakes to avoid\n- `get_framework_guide(\"component-structure\")` — Review component structure patterns\n- `get_function_doc(functionName)` — Look up exact function signatures before using them\n- `get_model_guide(typeName)` — Get comprehensive info about a model type\n\n### Step 3: Write the Component Code\n\nEdit `src/components/{ComponentName}/index.tsx` with the implementation. **Do NOT edit `types.ts`** — it was already generated correctly in Step 1. Key rules:\n- Import props from `./types` (auto-generated in Step 1)\n- Import storefront functions from `@ikas/bp-storefront`\n- Root export should use named + default export: `export function X({ ... }: Props) { ... }` + `export default X;`\n- Only wrap sub-components with `observer()` when they independently read MobX stores\n- Use optional chaining for all props: `props.title ?? \"Default\"`\n- Use `as unknown as boolean` cast for functions like `hasProductStock`, `isAddToCartEnabled`\n\n### Step 4: Write Styles\n\nEdit `src/components/{ComponentName}/styles.css`. Key rules:\n- Use class selectors only (`.my-class`), never bare element selectors\n- CSS is auto-scoped at build time — no manual namespacing needed\n- For sections: `width: 100%; padding: 64px 24px;` with inner max-width container\n\n### Step 5: Verify with Type Checking\n\nRun the lightweight type checker:\n```bash\nnpx ikas-component check --json\n```\n\nSuccess output:\n```json\n{\"success\": true, \"errors\": []}\n```\n\nError output:\n```json\n{\"success\": false, \"errorCount\": 2, \"errors\": [{\"file\": \"src/components/Hero/index.tsx\", \"line\": 15, \"column\": 3, \"code\": \"TS2339\", \"message\": \"Property 'title' does not exist on type 'Props'\"}]}\n```\n\n### Step 6: Fix Errors and Re-check\n\nFor each error:\n1. Read the file and line number from the error\n2. Fix the issue (common fixes below)\n3. Re-run `npx ikas-component check --json`\n\n**Common error fixes:**\n- `Property 'x' does not exist on type 'Props'` — Prop wasn't added via CLI. Run `npx ikas-component config add-prop`.\n- `Cannot find module '@ikas/bp-storefront'` — Normal in type-check if node_modules not fully installed. Focus on component-level errors.\n- `Type 'X' is not assignable to type 'Y'` — Check function signature with `get_function_doc()`.\n\n### Step 7: Full Build Validation\n\nOnce type checking passes, run the full build:\n```bash\nnpx ikas-component build\n```\n\nThis runs type checking + esbuild compilation + CSS scoping.\n\n### Quick Reference: CLI Commands\n\n| Command | Purpose |\n|---------|--------|\n| `npx ikas-component config add-component --name X --type section --props '[...]'` | **Primary** — create component with all props |\n| `npx ikas-component config add-component --name X --type section` | Create component with no props |\n| `npx ikas-component config add-prop --component X --name y --displayName Y --type TEXT` | Add a prop incrementally |\n| `npx ikas-component config update-prop --component X --prop y --required true` | Update a prop |\n| `npx ikas-component config remove-prop --component X --prop y` | Remove a prop |\n| `npx ikas-component config remove-component --name X` | Remove a component |\n| `npx ikas-component config list` | List all components and props |\n| `npx ikas-component check --json` | Type-check with JSON output |\n| `npx ikas-component build` | Full production build |\n\n### Quick Reference: MCP Tools\n\n| Tool | When to Use |\n|------|------------|\n| `get_section_template(type)` | Starting a new section — get starter code + CLI command |\n| `get_framework_guide(topic)` | Understanding patterns, pitfalls, architecture |\n| `get_function_doc(name)` | Looking up exact function signature |\n| `get_model_guide(type)` | Working with a model type (IkasProduct, IkasCart, etc.) |\n| `get_prop_types()` | Checking available prop types for ikas.config.json |\n| `search_docs(query)` | Finding relevant functions/docs by keyword |",
175
175
  "tags": [
176
176
  "ai",
177
177
  "workflow",
@@ -185,7 +185,7 @@
185
185
  "form-handling": {
186
186
  "title": "Form Handling",
187
187
  "description": "How to use the form model pattern for login, registration, address, and other forms",
188
- "content": "## Form Handling Pattern\n\nikas storefront uses a consistent form model pattern across all form types: `init*Form()` → `set*FormField()` → `submit*Form()`.\n\n### Form Model Structure\n\nEach form field has:\n```typescript\n{\n value: string; // Current field value\n hasError: boolean; // Whether validation failed\n message?: string; // Error message to display\n label: string; // Display label\n placeholder: string; // Input placeholder\n}\n```\n\nEach form tracks:\n```typescript\n{\n isSubmitted: boolean; // Has submit been attempted\n isSubmitting: boolean; // Is submission in progress\n isSuccess?: boolean; // Did last submit succeed\n isFailure?: boolean; // Did last submit fail\n responseMessage?: string; // Server response message\n}\n```\n\n### Login Form Example\n\n```tsx\nimport { useEffect } from \"preact/hooks\";\nimport {\n customerStore,\n initLoginForm,\n setLoginFormEmail,\n setLoginFormPassword,\n submitLoginForm,\n Router,\n} from \"@ikas/bp-storefront\";\n\n// Root export — no observer() needed, ikas runtime handles reactivity via autorun()\nexport default function LoginForm() {\n const loginForm = customerStore.loginForm;\n\n useEffect(() => {\n initLoginForm(loginForm);\n }, []);\n\n const handleSubmit = async (e: Event) => {\n e.preventDefault();\n const success = await submitLoginForm(loginForm);\n if (success) {\n Router.navigate(\"/account\");\n }\n };\n\n return (\n <form onSubmit={handleSubmit}>\n {loginForm.isFailure && <div>{loginForm.responseMessage}</div>}\n\n <input\n type=\"email\"\n value={loginForm.email.value}\n onInput={(e) => setLoginFormEmail(loginForm, e.target.value)}\n />\n {loginForm.email.hasError && <span>{loginForm.email.message}</span>}\n\n <input\n type=\"password\"\n value={loginForm.password.value}\n onInput={(e) => setLoginFormPassword(loginForm, e.target.value)}\n />\n {loginForm.password.hasError && <span>{loginForm.password.message}</span>}\n\n <button type=\"submit\" disabled={loginForm.isSubmitting}>\n {loginForm.isSubmitting ? \"Signing in...\" : \"Sign In\"}\n </button>\n </form>\n );\n}\n```\n\n### Available Form Types\n\n| Form | Init | Setter Functions | Submit |\n|------|------|-----------------|--------|\n| Login | `initLoginForm(form)` | `setLoginFormEmail()`, `setLoginFormPassword()` | `submitLoginForm(form)` |\n| Register | `initRegisterForm(form)` | `setRegisterFormEmail()`, `setRegisterFormPassword()`, `setRegisterFormFirstName()`, `setRegisterFormLastName()` | `submitRegisterForm(form)` |\n| Forgot Password | `initForgotPasswordForm(form)` | `setForgotPasswordFormEmail()` | `submitForgotPasswordForm(form)` |\n| Address | `initAddressForm(form)` | `setAddressFormField()` for each field | `submitAddressForm(form)` |\n| Contact | `initContactForm(form)` | `setContactFormField()` for each field | `submitContactForm(form)` |\n| Account Info | `initAccountInfoForm(form)` | `setAccountInfoFormField()` for each field | `submitAccountInfoForm(form)` |\n| Newsletter | `initNewsletterSubscriptionForm(form)` | `setNewsletterEmail()` | `submitNewsletterSubscriptionForm(form)` |\n| Coupon Code | — | `setCouponCode()` | `applyCouponCode()` |\n\n### Key Rules\n\n1. **Always call init first** — `init*Form()` sets up default field values, labels, and placeholders\n2. **Setter functions auto-validate** — if `form.isSubmitted` is true, calling any setter re-validates the form automatically\n3. **Check field.hasError for display** — show error messages only when `field.hasError` is true\n4. **Root components are automatically reactive** — forms use MobX observables which are automatically tracked in root components via `autorun()`. Use `observer()` only if you extract form logic into a sub-component\n5. **Submit returns boolean** — `submit*Form()` returns `true` on success, `false` on validation error or server failure",
188
+ "content": "## Form Handling Pattern\n\nikas storefront uses a consistent form model pattern across all form types: `init*Form()` → `set*FormField()` → `submit*Form()`.\n\n### Form Model Structure\n\nEach form field has:\n```typescript\n{\n value: string; // Current field value\n hasError: boolean; // Whether validation failed\n message?: string; // Error message to display\n label: string; // Display label\n placeholder: string; // Input placeholder\n}\n```\n\nEach form tracks:\n```typescript\n{\n isSubmitted: boolean; // Has submit been attempted\n isSubmitting: boolean; // Is submission in progress\n isSuccess?: boolean; // Did last submit succeed\n isFailure?: boolean; // Did last submit fail\n responseMessage?: string; // Server response message\n}\n```\n\n### Login Form Example\n\n```tsx\nimport { useEffect } from \"preact/hooks\";\nimport {\n customerStore,\n initLoginForm,\n setLoginFormEmail,\n setLoginFormPassword,\n submitLoginForm,\n Router,\n} from \"@ikas/bp-storefront\";\n\n// Root export — no observer() needed, ikas runtime handles reactivity via autorun()\nexport function LoginForm() {\n const loginForm = customerStore.loginForm;\n\n useEffect(() => {\n initLoginForm(loginForm);\n }, []);\n\n const handleSubmit = async (e: Event) => {\n e.preventDefault();\n const success = await submitLoginForm(loginForm);\n if (success) {\n Router.navigate(\"/account\");\n }\n };\n\n return (\n <form onSubmit={handleSubmit}>\n {loginForm.isFailure && <div>{loginForm.responseMessage}</div>}\n\n <input\n type=\"email\"\n value={loginForm.email.value}\n onInput={(e) => setLoginFormEmail(loginForm, e.target.value)}\n />\n {loginForm.email.hasError && <span>{loginForm.email.message}</span>}\n\n <input\n type=\"password\"\n value={loginForm.password.value}\n onInput={(e) => setLoginFormPassword(loginForm, e.target.value)}\n />\n {loginForm.password.hasError && <span>{loginForm.password.message}</span>}\n\n <button type=\"submit\" disabled={loginForm.isSubmitting}>\n {loginForm.isSubmitting ? \"Signing in...\" : \"Sign In\"}\n </button>\n </form>\n );\n}\n\nexport default LoginForm;\n```\n\n### Available Form Types\n\n| Form | Init | Setter Functions | Submit |\n|------|------|-----------------|--------|\n| Login | `initLoginForm(form)` | `setLoginFormEmail()`, `setLoginFormPassword()` | `submitLoginForm(form)` |\n| Register | `initRegisterForm(form)` | `setRegisterFormEmail()`, `setRegisterFormPassword()`, `setRegisterFormFirstName()`, `setRegisterFormLastName()` | `submitRegisterForm(form)` |\n| Forgot Password | `initForgotPasswordForm(form)` | `setForgotPasswordFormEmail()` | `submitForgotPasswordForm(form)` |\n| Address | `initAddressForm(form)` | `setAddressFormField()` for each field | `submitAddressForm(form)` |\n| Contact | `initContactForm(form)` | `setContactFormField()` for each field | `submitContactForm(form)` |\n| Account Info | `initAccountInfoForm(form)` | `setAccountInfoFormField()` for each field | `submitAccountInfoForm(form)` |\n| Newsletter | `initNewsletterSubscriptionForm(form)` | `setNewsletterEmail()` | `submitNewsletterSubscriptionForm(form)` |\n| Coupon Code | — | `setCouponCode()` | `applyCouponCode()` |\n\n### Key Rules\n\n1. **Always call init first** — `init*Form()` sets up default field values, labels, and placeholders\n2. **Setter functions auto-validate** — if `form.isSubmitted` is true, calling any setter re-validates the form automatically\n3. **Check field.hasError for display** — show error messages only when `field.hasError` is true\n4. **Root components are automatically reactive** — forms use MobX observables which are automatically tracked in root components via `autorun()`. Use `observer()` only if you extract form logic into a sub-component\n5. **Submit returns boolean** — `submit*Form()` returns `true` on success, `false` on validation error or server failure",
189
189
  "tags": [
190
190
  "form",
191
191
  "login",
@@ -198,7 +198,7 @@
198
198
  "async-data-patterns": {
199
199
  "title": "Async Data & Loading Patterns",
200
200
  "description": "How to handle async operations, loading states, and store data in ikas components",
201
- "content": "## Async Data & Loading Patterns\n\n### 1. Store State is Null Until Initialized\n\n**Note:** Root component exports are automatically reactive via the ikas runtime's `autorun()` — no `observer()` needed. The examples below show `observer()` on **sub-components** that independently read store data.\n\nStore data (`cartStore.cart`, `customerStore.customer`) is `null` until the store initializes. Use null-safe access:\n\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n // cart is null until initialized — use optional chaining\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span className=\"cart-badge\">{itemCount}</span>;\n});\n```\n\n### 2. Loading Pattern for Async Operations\n\nUse `useState` for local loading flags. Always use `try/finally` to clear the loading state:\n\n```tsx\nimport { useState } from \"preact/hooks\";\nimport { addItemToCart } from \"@ikas/bp-storefront\";\n\nconst [isLoading, setIsLoading] = useState(false);\n\nconst handleAddToCart = async () => {\n if (isLoading) return; // prevent double-click\n setIsLoading(true);\n try {\n await addItemToCart(variant, product, 1);\n } finally {\n setIsLoading(false);\n }\n};\n\n// In JSX:\n<button disabled={isLoading} onClick={handleAddToCart}>\n {isLoading ? \"Adding...\" : \"Add to Cart\"}\n</button>\n```\n\n### 3. Cart Operation Results\n\n`addItemToCart` returns a result object. Check for validation errors:\n\n```tsx\nconst result = await addItemToCart(variant, product, 1);\nif (result.success) {\n // Item added successfully\n} else if (result.validationError === \"INSUFFICIENT_STOCK\") {\n // Not enough stock\n} else if (result.validationError === \"INVALID_PRODUCT_OPTION_VALUES\") {\n // Variant options not fully selected\n}\n```\n\n### 4. Form Submission Results\n\nForm submit functions return `boolean` and set status flags on the form:\n\n```tsx\nconst success = await submitLoginForm(loginForm);\nif (success) {\n // loginForm.isSuccess is true\n Router.navigate(\"/account\");\n} else {\n // loginForm.isFailure is true\n // loginForm.responseMessage has the error message\n console.log(loginForm.responseMessage);\n}\n```\n\n### 5. Observer Auto Re-rendering\n\nStore updates automatically trigger re-renders. Root components get this via `autorun()`, sub-components via `observer()`. You do NOT need manual state management for store-derived values:\n\n```tsx\n// WRONG — unnecessary useState for store data\nconst [cartItems, setCartItems] = useState([]);\nuseEffect(() => { setCartItems(cartStore.cart?.orderLineItems ?? []); }, [cartStore.cart]);\n\n// CORRECT — observer sub-component for independent reactivity\nconst CartList = observer(function CartList() {\n const items = cartStore.cart?.orderLineItems ?? [];\n return (\n <div>\n {items.map((item) => (\n <div key={item.id}>{item.variant?.name}</div>\n ))}\n </div>\n );\n});\n\n// Root export uses CartList — no observer needed on root\nexport default function CartPage({ title }: Props) {\n return (\n <section>\n <h1>{title}</h1>\n <CartList />\n </section>\n );\n}\n```\n\n### 6. Pagination / Data Fetching\n\nList stores (blog, brand, category) use async pagination functions:\n\n```tsx\nimport { hasBlogListNextPage, getBlogListNextPage } from \"@ikas/bp-storefront\";\n\n// Check if more pages exist\nif (hasBlogListNextPage(blogList)) {\n // Fetch next page — mutates blogList in place, observer re-renders\n await getBlogListNextPage(blogList);\n}\n```\n\nThese functions mutate the list model in place. If your component uses `observer()`, it will re-render automatically when new data arrives.",
201
+ "content": "## Async Data & Loading Patterns\n\n### 1. Store State is Null Until Initialized\n\n**Note:** Root component exports are automatically reactive via the ikas runtime's `autorun()` — no `observer()` needed. The examples below show `observer()` on **sub-components** that independently read store data.\n\nStore data (`cartStore.cart`, `customerStore.customer`) is `null` until the store initializes. Use null-safe access:\n\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n // cart is null until initialized — use optional chaining\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span className=\"cart-badge\">{itemCount}</span>;\n});\n```\n\n### 2. Loading Pattern for Async Operations\n\nUse `useState` for local loading flags. Always use `try/finally` to clear the loading state:\n\n```tsx\nimport { useState } from \"preact/hooks\";\nimport { addItemToCart } from \"@ikas/bp-storefront\";\n\nconst [isLoading, setIsLoading] = useState(false);\n\nconst handleAddToCart = async () => {\n if (isLoading) return; // prevent double-click\n setIsLoading(true);\n try {\n await addItemToCart(variant, product, 1);\n } finally {\n setIsLoading(false);\n }\n};\n\n// In JSX:\n<button disabled={isLoading} onClick={handleAddToCart}>\n {isLoading ? \"Adding...\" : \"Add to Cart\"}\n</button>\n```\n\n### 3. Cart Operation Results\n\n`addItemToCart` returns a result object. Check for validation errors:\n\n```tsx\nconst result = await addItemToCart(variant, product, 1);\nif (result.success) {\n // Item added successfully\n} else if (result.validationError === \"INSUFFICIENT_STOCK\") {\n // Not enough stock\n} else if (result.validationError === \"INVALID_PRODUCT_OPTION_VALUES\") {\n // Variant options not fully selected\n}\n```\n\n### 4. Form Submission Results\n\nForm submit functions return `boolean` and set status flags on the form:\n\n```tsx\nconst success = await submitLoginForm(loginForm);\nif (success) {\n // loginForm.isSuccess is true\n Router.navigate(\"/account\");\n} else {\n // loginForm.isFailure is true\n // loginForm.responseMessage has the error message\n console.log(loginForm.responseMessage);\n}\n```\n\n### 5. Observer Auto Re-rendering\n\nStore updates automatically trigger re-renders. Root components get this via `autorun()`, sub-components via `observer()`. You do NOT need manual state management for store-derived values:\n\n```tsx\n// WRONG — unnecessary useState for store data\nconst [cartItems, setCartItems] = useState([]);\nuseEffect(() => { setCartItems(cartStore.cart?.orderLineItems ?? []); }, [cartStore.cart]);\n\n// CORRECT — observer sub-component for independent reactivity\nconst CartList = observer(function CartList() {\n const items = cartStore.cart?.orderLineItems ?? [];\n return (\n <div>\n {items.map((item) => (\n <div key={item.id}>{item.variant?.name}</div>\n ))}\n </div>\n );\n});\n\n// Root export uses CartList — no observer needed on root\nexport function CartPage({ title }: Props) {\n return (\n <section>\n <h1>{title}</h1>\n <CartList />\n </section>\n );\n}\n\nexport default CartPage;\n```\n\n### 6. Pagination / Data Fetching\n\nList stores (blog, brand, category) use async pagination functions:\n\n```tsx\nimport { hasBlogListNextPage, getBlogListNextPage } from \"@ikas/bp-storefront\";\n\n// Check if more pages exist\nif (hasBlogListNextPage(blogList)) {\n // Fetch next page — mutates blogList in place, observer re-renders\n await getBlogListNextPage(blogList);\n}\n```\n\nThese functions mutate the list model in place. If your component uses `observer()`, it will re-render automatically when new data arrives.",
202
202
  "tags": [
203
203
  "async",
204
204
  "loading",
@@ -227,7 +227,7 @@
227
227
  "product-list-patterns": {
228
228
  "title": "Product List & Filtering Patterns",
229
229
  "description": "Category pages, product filtering with display-type-aware rendering (swatch, number range, checkbox), sorting, pagination, and search patterns",
230
- "content": "## Product List & Filtering Patterns\n\nProduct list sections power category pages, search results, and collection displays. They combine filtering, sorting, and pagination.\n\n### Product List Prop Setup\n\nIn `ikas.config.json`, use the `PRODUCT_LIST` prop type:\n```json\n{\n \"name\": \"productList\",\n \"displayName\": \"Product List\",\n \"type\": \"PRODUCT_LIST\",\n \"required\": true\n}\n```\n\nThis provides an `IkasProductList` object with products, filters, sorting, and pagination data.\n\n### Displaying Products\n\n```tsx\nimport {\n IkasProductList,\n getSelectedProductVariant,\n getProductVariantFormattedFinalPrice,\n getProductVariantMainImage,\n getSelectedProductVariantHref,\n getDefaultSrc,\n} from \"@ikas/bp-storefront\";\n\n// In your component:\nconst products = productList?.products ?? [];\n\n{products.map((product) => {\n const variant = getSelectedProductVariant(product);\n const productImage = getProductVariantMainImage(variant);\n const image = productImage?.image ?? null;\n const price = getProductVariantFormattedFinalPrice(variant) as unknown as string;\n const href = getSelectedProductVariantHref(product);\n\n return (\n <a key={product.id} href={href} className=\"product-card\">\n {image && <img src={getDefaultSrc(image)} alt={product.name} />}\n <h3>{product.name}</h3>\n <span>{price}</span>\n </a>\n );\n})}\n```\n\n### Filtering\n\nFilters let customers narrow down products by attributes (size, color, price, etc.). Each filter has a `displayType` that determines how it should be rendered. **Always check the display type and render accordingly** — do not render all filters as checkboxes.\n\n#### Display Types\n\n- **BOX / LIST** — Standard checkbox filters (size, brand, etc.)\n- **SWATCH** — Color/pattern swatches with `fv.colorCode` or thumbnail images\n- **NUMBER_RANGE_LIST** — Predefined price/number range buttons (e.g., $0-$50, $50-$100)\n\n#### Universal Data Pattern\n\nThe data flow is the same for all display types:\n- `getFilterDisplayedValues(filter)` — get sorted filter values (works for ALL types)\n- `handleFilterValueClick(productList, filter, filterValue)` — toggle selection + auto-refetch (works for BOX/LIST/SWATCH)\n- `handleNumberRangeOptionClick(productList, filter, option)` — toggle a range option + auto-refetch (for NUMBER_RANGE_LIST)\n\n#### Imports\n\n```tsx\nimport {\n getFilterDisplayedValues,\n handleFilterValueClick,\n handleNumberRangeOptionClick,\n getProductListFilterCategories,\n isBoxFilter,\n isListFilter,\n isSwatchFilter,\n getIkasFilterThumbnailImage,\n getDefaultSrc,\n IkasProductList,\n IkasProductFilter,\n} from \"@ikas/bp-storefront\";\n```\n\n#### Display-Type-Aware Rendering\n\n```tsx\nconst filters = productList.filters ?? [];\n\n{filters.map((filter) => {\n const values = getFilterDisplayedValues(filter);\n if (values.length === 0 && !filter.numberRangeListOptions?.length) return null;\n\n return (\n <div key={filter.id}>\n <h4>{filter.name}</h4>\n\n {/* SWATCH: render color circles / thumbnail images */}\n {isSwatchFilter(filter) ? (\n <div className=\"swatch-grid\">\n {values.map((fv) => {\n const thumbnail = getIkasFilterThumbnailImage(fv);\n return (\n <button\n key={fv.name}\n className={fv.isSelected ? \"swatch selected\" : \"swatch\"}\n onClick={() => handleFilterValueClick(productList, filter, fv)}\n title={fv.name}\n >\n {thumbnail ? (\n <img src={getDefaultSrc(thumbnail)} alt={fv.name} />\n ) : (\n <span\n className=\"swatch-color\"\n style={{ backgroundColor: fv.colorCode ?? \"#ccc\" }}\n />\n )}\n </button>\n );\n })}\n </div>\n ) : (\n /* BOX / LIST: render checkboxes */\n values.map((fv) => (\n <label key={fv.name}>\n <input\n type=\"checkbox\"\n checked={fv.isSelected}\n onChange={() => handleFilterValueClick(productList, filter, fv)}\n />\n {fv.name} {fv.count != null && `(${fv.count})`}\n </label>\n ))\n )}\n\n {/* NUMBER_RANGE_LIST: render predefined range buttons */}\n {filter.numberRangeListOptions?.map((option) => (\n <button\n key={`${option.from}-${option.to}`}\n className={option.isSelected ? \"range-btn selected\" : \"range-btn\"}\n onClick={() => handleNumberRangeOptionClick(productList, filter, option)}\n >\n {option.from} - {option.to ?? \"+\"}\n </button>\n ))}\n </div>\n );\n})}\n```\n\n### Sorting\n\n```tsx\nimport { getProductListSortOptions } from \"@ikas/bp-storefront\";\n\nconst sortOptions = getProductListSortOptions(productList);\n\n<select\n value={productList.sort}\n onChange={(e) => {\n productList.sort = (e.target as HTMLSelectElement).value;\n }}\n>\n {sortOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n</select>\n```\n\n### Pagination\n\n```tsx\nimport {\n hasProductListNextPage,\n hasProductListPrevPage,\n getProductListNextPage,\n getProductListPrevPage,\n} from \"@ikas/bp-storefront\";\n\nconst hasNext = hasProductListNextPage(productList);\nconst hasPrev = hasProductListPrevPage(productList);\n\n<div className=\"pagination\">\n <button disabled={!hasPrev} onClick={() => getProductListPrevPage(productList)}>\n Previous\n </button>\n <button disabled={!hasNext} onClick={() => getProductListNextPage(productList)}>\n Next\n </button>\n</div>\n```\n\n### Key Functions Reference\n\n| Function | Purpose |\n|----------|---------|\n| `getProductListFilterCategories(list)` | Get available filter categories |\n| `getFilterDisplayedValues(filter)` | Get sorted filter values — works for ALL display types |\n| `handleFilterValueClick(list, filter, value)` | Toggle a filter value + auto-refetch (BOX/LIST/SWATCH) |\n| `handleNumberRangeOptionClick(list, filter, option)` | Toggle a range option + auto-refetch (NUMBER_RANGE_LIST) |\n| `isBoxFilter(filter)` | Check if filter displayType is BOX |\n| `isListFilter(filter)` | Check if filter displayType is LIST |\n| `isSwatchFilter(filter)` | Check if filter displayType is SWATCH |\n| `isCustomValueFilter(filter)` | Check if filter displayType is CUSTOM_VALUE |\n| `getIkasFilterThumbnailImage(fv)` | Get swatch thumbnail IkasImage from a filter value |\n| `getProductListSortOptions(list)` | Get sort dropdown options |\n| `hasProductListNextPage(list)` / `getProductListNextPage(list)` | Next page |\n| `hasProductListPrevPage(list)` / `getProductListPrevPage(list)` | Previous page |",
230
+ "content": "## Product List & Filtering Patterns\n\nProduct list sections power category pages, search results, and collection displays. They combine filtering, sorting, and pagination.\n\n### Product List Prop Setup\n\nIn `ikas.config.json`, use the `PRODUCT_LIST` prop type:\n```json\n{\n \"name\": \"productList\",\n \"displayName\": \"Product List\",\n \"type\": \"PRODUCT_LIST\",\n \"required\": true\n}\n```\n\nThis provides an `IkasProductList` object with products, filters, sorting, and pagination data.\n\n### Displaying Products\n\n```tsx\nimport {\n IkasProductList,\n getSelectedProductVariant,\n getProductVariantFormattedFinalPrice,\n getProductVariantMainImage,\n getSelectedProductVariantHref,\n getDefaultSrc,\n} from \"@ikas/bp-storefront\";\n\n// In your component:\nconst products = productList?.data ?? [];\n\n{products.map((product) => {\n const variant = getSelectedProductVariant(product);\n const productImage = getProductVariantMainImage(variant);\n const image = productImage?.image ?? null;\n const price = getProductVariantFormattedFinalPrice(variant) as unknown as string;\n const href = getSelectedProductVariantHref(product);\n\n return (\n <a key={product.id} href={href} className=\"product-card\">\n {image && <img src={getDefaultSrc(image)} alt={product.name} />}\n <h3>{product.name}</h3>\n <span>{price}</span>\n </a>\n );\n})}\n```\n\n### Filtering\n\nFilters let customers narrow down products by attributes (size, color, price, etc.). Each filter has a `displayType` that determines how it should be rendered. **Always check the display type and render accordingly** — do not render all filters as checkboxes.\n\n#### Display Types\n\n- **BOX / LIST** — Standard checkbox filters (size, brand, etc.)\n- **SWATCH** — Color/pattern swatches with `fv.colorCode` or thumbnail images\n- **NUMBER_RANGE_LIST** — Predefined price/number range buttons (e.g., $0-$50, $50-$100)\n\n#### Universal Data Pattern\n\nThe data flow is the same for all display types:\n- `getFilterDisplayedValues(filter)` — get sorted filter values (works for ALL types)\n- `handleFilterValueClick(productList, filter, filterValue)` — toggle selection + auto-refetch (works for BOX/LIST/SWATCH)\n- `handleNumberRangeOptionClick(productList, filter, option)` — toggle a range option + auto-refetch (for NUMBER_RANGE_LIST)\n\n#### Imports\n\n```tsx\nimport {\n getFilterDisplayedValues,\n handleFilterValueClick,\n handleNumberRangeOptionClick,\n getProductListFilterCategories,\n isBoxFilter,\n isListFilter,\n isSwatchFilter,\n getIkasFilterThumbnailImage,\n getDefaultSrc,\n IkasProductList,\n IkasProductFilter,\n} from \"@ikas/bp-storefront\";\n```\n\n#### Display-Type-Aware Rendering\n\n```tsx\nconst filters = productList.filters ?? [];\n\n{filters.map((filter) => {\n const values = getFilterDisplayedValues(filter);\n if (values.length === 0 && !filter.numberRangeListOptions?.length) return null;\n\n return (\n <div key={filter.id}>\n <h4>{filter.name}</h4>\n\n {/* SWATCH: render color circles / thumbnail images */}\n {isSwatchFilter(filter) ? (\n <div className=\"swatch-grid\">\n {values.map((fv) => {\n const thumbnail = getIkasFilterThumbnailImage(fv);\n return (\n <button\n key={fv.name}\n className={fv.isSelected === true ? \"swatch selected\" : \"swatch\"}\n onClick={() => handleFilterValueClick(productList, filter, fv)}\n title={fv.name}\n >\n {thumbnail ? (\n <img src={getDefaultSrc(thumbnail)} alt={fv.name} />\n ) : (\n <span\n className=\"swatch-color\"\n style={{ backgroundColor: fv.colorCode ?? \"#ccc\" }}\n />\n )}\n </button>\n );\n })}\n </div>\n ) : (\n /* BOX / LIST: render checkboxes */\n values.map((fv) => (\n <label key={fv.name}>\n <input\n type=\"checkbox\"\n checked={fv.isSelected === true}\n onChange={() => handleFilterValueClick(productList, filter, fv)}\n />\n {fv.name} {fv.count != null && `(${fv.count})`}\n </label>\n ))\n )}\n\n {/* NUMBER_RANGE_LIST: render predefined range buttons */}\n {filter.numberRangeListOptions?.map((option) => (\n <button\n key={`${option.from}-${option.to}`}\n className={option.isSelected ? \"range-btn selected\" : \"range-btn\"}\n onClick={() => handleNumberRangeOptionClick(productList, filter, option)}\n >\n {option.from} - {option.to ?? \"+\"}\n </button>\n ))}\n </div>\n );\n})}\n```\n\n### Sorting\n\n```tsx\nimport { getProductListSortOptions, setSortType, IkasProductListSortType } from \"@ikas/bp-storefront\";\n\nconst sortOptions = getProductListSortOptions(productList);\n\n<select\n value={productList.sort}\n onChange={(e) => {\n setSortType(productList, (e.target as HTMLSelectElement).value as IkasProductListSortType);\n }}\n>\n {sortOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n</select>\n```\n\n### Pagination\n\n```tsx\nimport {\n hasProductListNextPage,\n hasProductListPrevPage,\n getProductListNextPage,\n getProductListPrevPage,\n} from \"@ikas/bp-storefront\";\n\nconst hasNext = hasProductListNextPage(productList);\nconst hasPrev = hasProductListPrevPage(productList);\n\n<div className=\"pagination\">\n <button disabled={!hasPrev} onClick={() => getProductListPrevPage(productList)}>\n Previous\n </button>\n <button disabled={!hasNext} onClick={() => getProductListNextPage(productList)}>\n Next\n </button>\n</div>\n```\n\n### Key Functions Reference\n\n| Function | Purpose |\n|----------|---------|\n| `getProductListFilterCategories(list)` | Get available filter categories |\n| `getFilterDisplayedValues(filter)` | Get sorted filter values — works for ALL display types |\n| `handleFilterValueClick(list, filter, value)` | Toggle a filter value + auto-refetch (BOX/LIST/SWATCH) |\n| `handleNumberRangeOptionClick(list, filter, option)` | Toggle a range option + auto-refetch (NUMBER_RANGE_LIST) |\n| `isBoxFilter(filter)` | Check if filter displayType is BOX |\n| `isListFilter(filter)` | Check if filter displayType is LIST |\n| `isSwatchFilter(filter)` | Check if filter displayType is SWATCH |\n| `isCustomValueFilter(filter)` | Check if filter displayType is CUSTOM_VALUE |\n| `getIkasFilterThumbnailImage(fv)` | Get swatch thumbnail IkasImage from a filter value |\n| `getProductListSortOptions(list)` | Get sort dropdown options |\n| `hasProductListNextPage(list)` / `getProductListNextPage(list)` | Next page |\n| `hasProductListPrevPage(list)` / `getProductListPrevPage(list)` | Previous page |",
231
231
  "tags": [
232
232
  "product-list",
233
233
  "filtering",
@@ -271,7 +271,7 @@
271
271
  "header-footer-patterns": {
272
272
  "title": "Header & Footer Patterns",
273
273
  "description": "Navigation links, logo, mobile menu, announcement bar, footer columns, and social links",
274
- "content": "## Header & Footer Patterns\n\nHeaders and footers appear on virtually every page (36/37 pages in a typical theme). They use `IkasNavigationLink` for navigation. Cart/customer state is automatically reactive in root components via the ikas runtime's `autorun()`.\n\n### Header Section Structure\n\nA production header typically includes:\n1. Announcement bar (optional, with slider)\n2. Logo + navigation links + utility icons (cart, account, search)\n3. Mobile menu overlay\n\n### Navigation Links\n\nUse the `LIST_OF_LINK` prop type for navigation:\n\n```json\n{\n \"name\": \"navigationLinks\",\n \"displayName\": \"Navigation Links\",\n \"type\": \"LIST_OF_LINK\"\n}\n```\n\nRender links:\n```tsx\nimport { IkasNavigationLink } from \"@ikas/bp-storefront\";\n\ninterface Props {\n navigationLinks?: IkasNavigationLink[];\n}\n\n{navigationLinks?.map((link, i) => (\n <a key={i} href={link.href} target={link.isTargetBlank ? \"_blank\" : undefined}>\n {link.label}\n </a>\n))}\n```\n\n### Cart Badge & Customer State\n\n```tsx\nimport { cartStore, customerStore, hasCustomer, Router } from \"@ikas/bp-storefront\";\n\n// In your component:\nconst itemCount = cartStore.cart?.orderLineItems.length ?? 0;\nconst isLoggedIn = hasCustomer(customerStore) as unknown as boolean;\n\n<div className=\"header-icons\">\n <button onClick={() => Router.navigateToPage(isLoggedIn ? \"ACCOUNT\" : \"LOGIN\")}>\n Account\n </button>\n <button onClick={() => Router.navigateToPage(\"CART\")}>\n Cart ({itemCount})\n </button>\n</div>\n```\n\n### Mobile Menu Overlay\n\n```tsx\nimport { IkasThemeOverlay } from \"@ikas/bp-storefront\";\n\nconst [menuOpen, setMenuOpen] = useState(false);\n\n<button className=\"hamburger\" onClick={() => setMenuOpen(true)}>Menu</button>\n\n<IkasThemeOverlay visible={menuOpen}>\n <div className=\"mobile-menu\">\n <button className=\"close\" onClick={() => setMenuOpen(false)}>Close</button>\n <nav>\n {navigationLinks?.map((link, i) => (\n <a key={i} href={link.href} onClick={() => setMenuOpen(false)}>\n {link.label}\n </a>\n ))}\n </nav>\n </div>\n</IkasThemeOverlay>\n```\n\n### Footer Section Structure\n\nA production footer includes:\n1. Logo and description\n2. Link columns (using `LIST_OF_LINK` or `COMPONENT_LIST` props)\n3. Contact information\n4. Social media links\n5. Copyright text\n\n```tsx\ninterface Props {\n logo?: { src: string; alt?: string };\n linkColumns?: IkasNavigationLink[];\n copyright?: string;\n socialLinks?: Array<{ icon: string; url: string }>;\n}\n\n<footer className=\"footer-section\">\n <div className=\"footer-inner\">\n <div className=\"footer-brand\">\n {logo?.src && <img src={logo.src} alt={logo.alt || \"Logo\"} />}\n </div>\n <div className=\"footer-links\">\n {linkColumns?.map((link, i) => (\n <a key={i} href={link.href}>{link.label}</a>\n ))}\n </div>\n <div className=\"footer-copyright\">\n <p>{copyright}</p>\n </div>\n </div>\n</footer>\n```\n\n### Key Patterns\n\n- Headers and footers are **sections** (`\"type\": \"section\"` in config)\n- Root exports are automatically reactive (reads from `cartStore` and `customerStore` are tracked by `autorun()`)\n- Use `LIST_OF_LINK` for navigation that store owners can edit\n- Mobile menu uses `IkasThemeOverlay` for slide-in drawer\n- Announcement bars use `IkasThemeSlider` for rotating messages",
274
+ "content": "## Header & Footer Patterns\n\nHeaders and footers appear on virtually every page (36/37 pages in a typical theme). They use `IkasNavigationLink` for navigation. Cart/customer state is automatically reactive in root components via the ikas runtime's `autorun()`.\n\n### Header Section Structure\n\nA production header typically includes:\n1. Announcement bar (optional, with slider)\n2. Logo + navigation links + utility icons (cart, account, search)\n3. Mobile menu overlay\n\n### Navigation Links\n\nUse the `LIST_OF_LINK` prop type for navigation:\n\n```json\n{\n \"name\": \"navigationLinks\",\n \"displayName\": \"Navigation Links\",\n \"type\": \"LIST_OF_LINK\"\n}\n```\n\nRender links:\n```tsx\nimport { IkasNavigationLink, IkasNavigationLinkList } from \"@ikas/bp-storefront\";\n\ninterface Props {\n navigationLinks?: IkasNavigationLinkList;\n}\n\n{(navigationLinks?.links ?? []).map((link: IkasNavigationLink, i: number) => (\n <a key={i} href={link.href} target={link.openInNewTab ? \"_blank\" : undefined}>\n {link.label}\n </a>\n))}\n```\n\n### Cart Badge & Customer State\n\n```tsx\nimport { cartStore, customerStore, hasCustomer, Router } from \"@ikas/bp-storefront\";\n\n// In your component:\nconst itemCount = cartStore.cart?.orderLineItems.length ?? 0;\nconst isLoggedIn = hasCustomer(customerStore) as unknown as boolean;\n\n<div className=\"header-icons\">\n <button onClick={() => Router.navigateToPage(isLoggedIn ? \"ACCOUNT\" : \"LOGIN\")}>\n Account\n </button>\n <button onClick={() => Router.navigateToPage(\"CART\")}>\n Cart ({itemCount})\n </button>\n</div>\n```\n\n### Mobile Menu Overlay\n\nUse `useState` with a plain `<div>` overlay for mobile menus. Note: `IkasThemeOverlay` is a state management type (`{ visible: boolean; show: () => void; hide: () => void; }`), NOT a JSX component.\n\n```tsx\nimport { useState } from \"preact/hooks\";\n\nconst [menuOpen, setMenuOpen] = useState(false);\n\n<button className=\"hamburger\" onClick={() => setMenuOpen(true)}>Menu</button>\n\n{menuOpen && (\n <div className=\"mobile-overlay\">\n <div className=\"mobile-backdrop\" onClick={() => setMenuOpen(false)} />\n <div className=\"mobile-menu\">\n <button className=\"close\" onClick={() => setMenuOpen(false)}>Close</button>\n <nav>\n {(navigationLinks?.links ?? []).map((link, i) => (\n <a key={i} href={link.href} onClick={() => setMenuOpen(false)}>\n {link.label}\n </a>\n ))}\n </nav>\n </div>\n </div>\n)}\n```\n\n### Footer Section Structure\n\nA production footer includes:\n1. Logo and description\n2. Link columns (using `LIST_OF_LINK` or `COMPONENT_LIST` props)\n3. Contact information\n4. Social media links\n5. Copyright text\n\n```tsx\ninterface Props {\n logo?: { src: string; alt?: string };\n linkColumns?: IkasNavigationLinkList;\n copyright?: string;\n socialLinks?: Array<{ icon: string; url: string }>;\n}\n\n<footer className=\"footer-section\">\n <div className=\"footer-inner\">\n <div className=\"footer-brand\">\n {logo?.src && <img src={logo.src} alt={logo.alt || \"Logo\"} />}\n </div>\n <div className=\"footer-links\">\n {(linkColumns?.links ?? []).map((link, i) => (\n <a key={i} href={link.href}>{link.label}</a>\n ))}\n </div>\n <div className=\"footer-copyright\">\n <p>{copyright}</p>\n </div>\n </div>\n</footer>\n```\n\n### Key Patterns\n\n- Headers and footers are **sections** (`\"type\": \"section\"` in config)\n- Root exports are automatically reactive (reads from `cartStore` and `customerStore` are tracked by `autorun()`)\n- Use `LIST_OF_LINK` for navigation that store owners can edit\n- Mobile menu uses `useState` + a plain overlay div for slide-in drawer\n- Announcement bars use `IkasThemeSlider` for rotating messages",
275
275
  "tags": [
276
276
  "header",
277
277
  "footer",
@@ -300,7 +300,7 @@
300
300
  "slider-overlay-patterns": {
301
301
  "title": "Slider & Overlay Patterns",
302
302
  "description": "IkasThemeSlider for carousels, IkasThemeOverlay for modals/drawers, IkasThemeInfiniteScroller for lazy loading",
303
- "content": "## Slider & Overlay Patterns\n\nikas provides built-in UI components for common interactive patterns.\n\n### IkasThemeSlider\n\nA carousel/slider component for rotating content (announcements, product images, banners).\n\n```tsx\nimport { IkasThemeSlider } from \"@ikas/bp-storefront\";\n\n<IkasThemeSlider\n autoplay={true}\n autoplayInterval={5000}\n loop={true}\n>\n {(sliderProps) => {\n const { current, index, goTo, dotCount, itemCount } = sliderProps;\n\n return (\n <div className=\"slider\">\n <div className=\"slider-track\">\n {items.map((item, i) => (\n <div key={i} className={`slide ${i === current ? \"active\" : \"\"}`}>\n {item.content}\n </div>\n ))}\n </div>\n\n {/* Navigation arrows */}\n <button onClick={() => goTo(current - 1)}>Previous</button>\n <button onClick={() => goTo(current + 1)}>Next</button>\n\n {/* Dot indicators */}\n <div className=\"dots\">\n {Array.from({ length: dotCount }).map((_, i) => (\n <button\n key={i}\n className={i === current ? \"active\" : \"\"}\n onClick={() => goTo(i)}\n />\n ))}\n </div>\n </div>\n );\n }}\n</IkasThemeSlider>\n```\n\n### Slider Props\n\n| Prop | Type | Description |\n|------|------|-------------|\n| `current` | `number` | Currently visible slide index |\n| `index` | `number` | Internal index (may differ with loop) |\n| `goTo(index)` | `function` | Navigate to specific slide |\n| `dotCount` | `number` | Total number of dots/pages |\n| `itemCount` | `number` | Total number of items |\n\n### IkasThemeOverlay\n\nA modal/drawer component for overlays (mobile menu, quick view, search, filters).\n\n```tsx\nimport { IkasThemeOverlay } from \"@ikas/bp-storefront\";\n\nconst [isVisible, setIsVisible] = useState(false);\n\n<button onClick={() => setIsVisible(true)}>Open Menu</button>\n\n<IkasThemeOverlay visible={isVisible}>\n <div className=\"overlay-content\">\n <button className=\"close-btn\" onClick={() => setIsVisible(false)}>×</button>\n {/* Overlay content */}\n </div>\n</IkasThemeOverlay>\n```\n\nThe overlay handles:\n- Backdrop click to close (when you handle the `visible` state)\n- Body scroll lock when visible\n- Z-index management\n\n### IkasThemeInfiniteScroller\n\nA component for infinite scroll / lazy loading patterns.\n\n```tsx\nimport { IkasThemeInfiniteScroller } from \"@ikas/bp-storefront\";\n\n<IkasThemeInfiniteScroller\n hasMore={hasNextPage}\n onLoadMore={async () => {\n await getProductListNextPage(productList);\n }}\n>\n {products.map((product) => (\n <ProductCard key={product.id} product={product} />\n ))}\n</IkasThemeInfiniteScroller>\n```\n\n### Common Usage Patterns\n\n- **Announcement bar**: `IkasThemeSlider` with autoplay for rotating promotional messages\n- **Mobile menu**: `IkasThemeOverlay` as a full-screen or side drawer\n- **Product image gallery**: `IkasThemeSlider` for main product images\n- **Quick view modal**: `IkasThemeOverlay` showing product details\n- **Product list infinite scroll**: `IkasThemeInfiniteScroller` for seamless pagination\n- **Filter drawer (mobile)**: `IkasThemeOverlay` for filter panel on mobile",
303
+ "content": "## Slider & Overlay Patterns\n\nikas provides built-in UI components for common interactive patterns.\n\n### IkasThemeSlider\n\nA carousel/slider component for rotating content (announcements, product images, banners).\n\n```tsx\nimport { IkasThemeSlider } from \"@ikas/bp-storefront\";\n\n<IkasThemeSlider\n autoplay={true}\n autoplayInterval={5000}\n loop={true}\n>\n {(sliderProps) => {\n const { current, index, goTo, dotCount, itemCount } = sliderProps;\n\n return (\n <div className=\"slider\">\n <div className=\"slider-track\">\n {items.map((item, i) => (\n <div key={i} className={`slide ${i === current ? \"active\" : \"\"}`}>\n {item.content}\n </div>\n ))}\n </div>\n\n {/* Navigation arrows */}\n <button onClick={() => goTo(current - 1)}>Previous</button>\n <button onClick={() => goTo(current + 1)}>Next</button>\n\n {/* Dot indicators */}\n <div className=\"dots\">\n {Array.from({ length: dotCount }).map((_, i) => (\n <button\n key={i}\n className={i === current ? \"active\" : \"\"}\n onClick={() => goTo(i)}\n />\n ))}\n </div>\n </div>\n );\n }}\n</IkasThemeSlider>\n```\n\n### Slider Props\n\n| Prop | Type | Description |\n|------|------|-------------|\n| `current` | `number` | Currently visible slide index |\n| `index` | `number` | Internal index (may differ with loop) |\n| `goTo(index)` | `function` | Navigate to specific slide |\n| `dotCount` | `number` | Total number of dots/pages |\n| `itemCount` | `number` | Total number of items |\n\n### IkasThemeOverlay\n\n**Note:** `IkasThemeOverlay` is a state management type, NOT a renderable JSX component. It has the shape:\n\n```typescript\ninterface IkasThemeOverlay {\n visible: boolean;\n show: () => void;\n hide: () => void;\n}\n```\n\nFor modal/drawer overlays, use `useState` with plain `<div>` elements:\n\n```tsx\nimport { useState } from \"preact/hooks\";\n\nconst [isVisible, setIsVisible] = useState(false);\n\n<button onClick={() => setIsVisible(true)}>Open Menu</button>\n\n{isVisible && (\n <div className=\"overlay\">\n <div className=\"overlay-backdrop\" onClick={() => setIsVisible(false)} />\n <div className=\"overlay-content\">\n <button className=\"close-btn\" onClick={() => setIsVisible(false)}>×</button>\n {/* Overlay content */}\n </div>\n </div>\n)}\n```\n\nImplement body scroll lock and backdrop behavior with CSS:\n- Use `position: fixed; inset: 0;` for the overlay container\n- Add a backdrop div for click-to-close\n- Use `overflow: hidden` on body when overlay is open\n\n### IkasThemeInfiniteScroller\n\nA component for infinite scroll / lazy loading patterns.\n\n```tsx\nimport { IkasThemeInfiniteScroller } from \"@ikas/bp-storefront\";\n\n<IkasThemeInfiniteScroller\n hasMore={hasNextPage}\n onLoadMore={async () => {\n await getProductListNextPage(productList);\n }}\n>\n {products.map((product) => (\n <ProductCard key={product.id} product={product} />\n ))}\n</IkasThemeInfiniteScroller>\n```\n\n### Common Usage Patterns\n\n- **Announcement bar**: `IkasThemeSlider` with autoplay for rotating promotional messages\n- **Mobile menu**: `useState` + plain div overlay as a full-screen or side drawer\n- **Product image gallery**: `IkasThemeSlider` for main product images\n- **Quick view modal**: `useState` + plain div overlay showing product details\n- **Product list infinite scroll**: `IkasThemeInfiniteScroller` for seamless pagination\n- **Filter drawer (mobile)**: `useState` + plain div overlay for filter panel on mobile",
304
304
  "tags": [
305
305
  "slider",
306
306
  "carousel",
@@ -314,7 +314,7 @@
314
314
  "blog-patterns": {
315
315
  "title": "Blog Patterns",
316
316
  "description": "Blog list display, blog post detail, pagination, date formatting, and category filtering",
317
- "content": "## Blog Patterns\n\nBlog sections display articles with pagination and optional category filtering.\n\n### Blog List Prop Setup\n\nIn `ikas.config.json`, use the `BLOG_POST_LIST` prop type or a dedicated blog list config:\n\n```json\n{\n \"name\": \"blogList\",\n \"displayName\": \"Blog List\",\n \"type\": \"BLOG_POST_LIST\"\n}\n```\n\n### Blog List Display\n\n```tsx\nimport {\n IkasBlogList,\n getIkasBlogFormattedDate,\n getIkasBlogHref,\n hasBlogListNextPage,\n getBlogListNextPage,\n hasBlogListPrevPage,\n getBlogListPrevPage,\n getDefaultSrc,\n} from \"@ikas/bp-storefront\";\n\n// In your component:\nconst blogs = blogList?.blogs ?? [];\n\n{blogs.map((blog) => (\n <a key={blog.id} href={getIkasBlogHref(blog)} className=\"blog-card\">\n {blog.image && (\n <img src={getDefaultSrc(blog.image)} alt={blog.title} />\n )}\n <div className=\"blog-card-content\">\n <span className=\"blog-date\">{getIkasBlogFormattedDate(blog)}</span>\n <h3>{blog.title}</h3>\n <p>{blog.summary}</p>\n </div>\n </a>\n))}\n```\n\n### Blog Pagination\n\n```tsx\nconst hasNext = hasBlogListNextPage(blogList);\nconst hasPrev = hasBlogListPrevPage(blogList);\n\n<div className=\"blog-pagination\">\n {hasPrev && (\n <button onClick={() => getBlogListPrevPage(blogList)}>Previous</button>\n )}\n {hasNext && (\n <button onClick={() => getBlogListNextPage(blogList)}>Next</button>\n )}\n</div>\n```\n\n### Blog Post Detail\n\nFor a blog post detail page, use the `BLOG_POST` prop type:\n\n```json\n{\n \"name\": \"blogPost\",\n \"displayName\": \"Blog Post\",\n \"type\": \"BLOG_POST\"\n}\n```\n\n```tsx\ninterface Props {\n blogPost: IkasBlogPost;\n}\n\nfunction BlogPostDetail({ blogPost }: Props) {\n if (!blogPost) return null;\n\n return (\n <article className=\"blog-post\">\n {blogPost.image && (\n <img src={getDefaultSrc(blogPost.image)} alt={blogPost.title} />\n )}\n <h1>{blogPost.title}</h1>\n <span className=\"date\">{getIkasBlogFormattedDate(blogPost)}</span>\n <div\n className=\"blog-content\"\n dangerouslySetInnerHTML={{ __html: blogPost.content }}\n />\n </article>\n );\n}\n```\n\n### Key Functions\n\n| Function | Purpose |\n|----------|---------|\n| `getIkasBlogFormattedDate(blog)` | Format blog post date |\n| `getIkasBlogHref(blog)` | Get URL for a blog post |\n| `hasBlogListNextPage(list)` | Check if next page exists |\n| `getBlogListNextPage(list)` | Fetch next page (mutates list) |\n| `hasBlogListPrevPage(list)` | Check if previous page exists |\n| `getBlogListPrevPage(list)` | Fetch previous page (mutates list) |",
317
+ "content": "## Blog Patterns\n\nBlog sections display articles with pagination and optional category filtering.\n\n### Blog List Prop Setup\n\nIn `ikas.config.json`, use the `BLOG_LIST` prop type or a dedicated blog list config:\n\n```json\n{\n \"name\": \"blogList\",\n \"displayName\": \"Blog List\",\n \"type\": \"BLOG_LIST\"\n}\n```\n\n### Blog List Display\n\n```tsx\nimport {\n IkasBlogList,\n getIkasBlogFormattedDate,\n getIkasBlogHref,\n hasBlogListNextPage,\n getBlogListNextPage,\n hasBlogListPrevPage,\n getBlogListPrevPage,\n getDefaultSrc,\n} from \"@ikas/bp-storefront\";\n\n// In your component:\nconst blogs = blogList?.data ?? [];\n\n{blogs.map((blog) => (\n <a key={blog.id} href={getIkasBlogHref(blog)} className=\"blog-card\">\n {blog.image && (\n <img src={getDefaultSrc(blog.image)} alt={blog.title} />\n )}\n <div className=\"blog-card-content\">\n <span className=\"blog-date\">{getIkasBlogFormattedDate(blog)}</span>\n <h3>{blog.title}</h3>\n <p>{blog.summary}</p>\n </div>\n </a>\n))}\n```\n\n### Blog Pagination\n\n```tsx\nconst hasNext = hasBlogListNextPage(blogList);\nconst hasPrev = hasBlogListPrevPage(blogList);\n\n<div className=\"blog-pagination\">\n {hasPrev && (\n <button onClick={() => getBlogListPrevPage(blogList)}>Previous</button>\n )}\n {hasNext && (\n <button onClick={() => getBlogListNextPage(blogList)}>Next</button>\n )}\n</div>\n```\n\n### Blog Post Detail\n\nFor a blog post detail page, use the `BLOG` prop type:\n\n```json\n{\n \"name\": \"blogPost\",\n \"displayName\": \"Blog Post\",\n \"type\": \"BLOG\"\n}\n```\n\n```tsx\ninterface Props {\n blogPost: IkasBlog;\n}\n\nfunction BlogPostDetail({ blogPost }: Props) {\n if (!blogPost) return null;\n\n return (\n <article className=\"blog-post\">\n {blogPost.image && (\n <img src={getDefaultSrc(blogPost.image)} alt={blogPost.title} />\n )}\n <h1>{blogPost.title}</h1>\n <span className=\"date\">{getIkasBlogFormattedDate(blogPost)}</span>\n <div\n className=\"blog-content\"\n dangerouslySetInnerHTML={{ __html: blogPost.blogContent.content }}\n />\n </article>\n );\n}\n```\n\n### Key Functions\n\n| Function | Purpose |\n|----------|---------|\n| `getIkasBlogFormattedDate(blog)` | Format blog post date |\n| `getIkasBlogHref(blog)` | Get URL for a blog post |\n| `hasBlogListNextPage(list)` | Check if next page exists |\n| `getBlogListNextPage(list)` | Fetch next page (mutates list) |\n| `hasBlogListPrevPage(list)` | Check if previous page exists |\n| `getBlogListPrevPage(list)` | Fetch previous page (mutates list) |",
318
318
  "tags": [
319
319
  "blog",
320
320
  "article",
@@ -327,7 +327,7 @@
327
327
  "navigation-patterns": {
328
328
  "title": "Navigation & Routing Patterns",
329
329
  "description": "Router.navigate, page navigation, href getters, breadcrumbs, and query parameters",
330
- "content": "## Navigation & Routing Patterns\n\nikas provides a `Router` utility and various href getter functions for navigation.\n\n### Basic Navigation\n\n```tsx\nimport { Router } from \"@ikas/bp-storefront\";\n\n// Navigate to a URL\nRouter.navigate(\"/products\");\n\n// Navigate to a named page\nRouter.navigateToPage(\"LOGIN\");\nRouter.navigateToPage(\"REGISTER\");\nRouter.navigateToPage(\"FORGOT_PASSWORD\");\nRouter.navigateToPage(\"ACCOUNT\");\nRouter.navigateToPage(\"CART\");\nRouter.navigateToPage(\"CHECKOUT\");\n\n// Navigate with options\nRouter.navigateToPage(\"CATEGORY\", {\n queryParams: { sort: \"price_asc\" },\n shallow: false,\n newTab: false,\n});\n\n// Go back\nRouter.goBack();\n```\n\n### Page Type Constants\n\nAvailable page types for `navigateToPage()`:\n- `INDEX` — Home page\n- `CATEGORY` — Category listing\n- `PRODUCT_DETAIL` — Product detail\n- `BLOG` — Blog listing\n- `BLOG_POST` — Blog post detail\n- `CART` — Shopping cart\n- `CHECKOUT` — Checkout flow\n- `LOGIN` — Login page\n- `REGISTER` — Registration page\n- `FORGOT_PASSWORD` — Password recovery\n- `ACCOUNT` — Customer account\n- `SEARCH` — Search results\n- `CUSTOM` — Custom pages\n\n### Href Getter Functions\n\nThese return URL strings for use in `<a href>` tags:\n\n```tsx\nimport {\n getSelectedProductVariantHref,\n getIkasCategoryHref,\n getIkasOrderHref,\n getIkasBlogHref,\n getIkasBrandHref,\n} from \"@ikas/bp-storefront\";\n\n// Product link\n<a href={getSelectedProductVariantHref(product)}>{product.name}</a>\n\n// Category link\n<a href={getIkasCategoryHref(category)}>{category.name}</a>\n\n// Order link\n<a href={getIkasOrderHref(order)}>Order #{order.orderNumber}</a>\n\n// Blog link\n<a href={getIkasBlogHref(blog)}>{blog.title}</a>\n\n// Brand link\n<a href={getIkasBrandHref(brand)}>{brand.name}</a>\n```\n\n### Breadcrumbs\n\n```tsx\nimport {\n getCategoryPath,\n getProductCategoryPath,\n getIkasCategoryHref,\n} from \"@ikas/bp-storefront\";\n\n// For category pages\nconst categoryPath = getCategoryPath(category);\n\n// For product pages\nconst productPath = getProductCategoryPath(product);\n\n<nav className=\"breadcrumbs\">\n <a href=\"/\">Home</a>\n {categoryPath?.map((cat, i) => (\n <span key={cat.id}>\n <span className=\"separator\">/</span>\n <a href={getIkasCategoryHref(cat)}>{cat.name}</a>\n </span>\n ))}\n</nav>\n```\n\n### Using Links vs Navigation\n\n- Use `<a href={getXxxHref(...)}>` for standard links (SEO-friendly, right-click works)\n- Use `Router.navigate()` for programmatic navigation after form submission or actions\n- Use `Router.navigateToPage()` for navigating to known page types\n- Use `Router.goBack()` for back buttons",
330
+ "content": "## Navigation & Routing Patterns\n\nikas provides a `Router` utility and various href getter functions for navigation.\n\n### Basic Navigation\n\n```tsx\nimport { Router } from \"@ikas/bp-storefront\";\n\n// Navigate to a URL\nRouter.navigate(\"/products\");\n\n// Navigate to a named page\nRouter.navigateToPage(\"LOGIN\");\nRouter.navigateToPage(\"REGISTER\");\nRouter.navigateToPage(\"FORGOT_PASSWORD\");\nRouter.navigateToPage(\"ACCOUNT\");\nRouter.navigateToPage(\"CART\");\nRouter.navigateToPage(\"CHECKOUT\");\n\n// Navigate with options\nRouter.navigateToPage(\"CATEGORY\", {\n queryParams: { sort: \"price_asc\" },\n shallow: false,\n newTab: false,\n});\n\n// Go back\nRouter.goBack();\n```\n\n### Page Type Constants\n\nAvailable page types for `navigateToPage()`:\n- `INDEX` — Home page\n- `CATEGORY` — Category listing\n- `PRODUCT_DETAIL` — Product detail\n- `BLOG` — Blog listing\n- `BLOG` — Blog post detail\n- `CART` — Shopping cart\n- `CHECKOUT` — Checkout flow\n- `LOGIN` — Login page\n- `REGISTER` — Registration page\n- `FORGOT_PASSWORD` — Password recovery\n- `ACCOUNT` — Customer account\n- `SEARCH` — Search results\n- `CUSTOM` — Custom pages\n\n### Href Getter Functions\n\nThese return URL strings for use in `<a href>` tags:\n\n```tsx\nimport {\n getSelectedProductVariantHref,\n getIkasCategoryHref,\n getIkasOrderHref,\n getIkasBlogHref,\n getIkasBrandHref,\n} from \"@ikas/bp-storefront\";\n\n// Product link\n<a href={getSelectedProductVariantHref(product)}>{product.name}</a>\n\n// Category link\n<a href={getIkasCategoryHref(category)}>{category.name}</a>\n\n// Order link\n<a href={getIkasOrderHref(order)}>Order #{order.orderNumber}</a>\n\n// Blog link\n<a href={getIkasBlogHref(blog)}>{blog.title}</a>\n\n// Brand link\n<a href={getIkasBrandHref(brand)}>{brand.name}</a>\n```\n\n### Breadcrumbs\n\n```tsx\nimport {\n getCategoryPath,\n getProductCategoryPath,\n getIkasCategoryHref,\n} from \"@ikas/bp-storefront\";\n\n// For category pages\nconst categoryPath = getCategoryPath(category);\n\n// For product pages\nconst productPath = getProductCategoryPath(product);\n\n<nav className=\"breadcrumbs\">\n <a href=\"/\">Home</a>\n {categoryPath?.map((cat, i) => (\n <span key={cat.id}>\n <span className=\"separator\">/</span>\n <a href={getIkasCategoryHref(cat)}>{cat.name}</a>\n </span>\n ))}\n</nav>\n```\n\n### Using Links vs Navigation\n\n- Use `<a href={getXxxHref(...)}>` for standard links (SEO-friendly, right-click works)\n- Use `Router.navigate()` for programmatic navigation after form submission or actions\n- Use `Router.navigateToPage()` for navigating to known page types\n- Use `Router.goBack()` for back buttons",
331
331
  "tags": [
332
332
  "navigation",
333
333
  "router",
@@ -341,7 +341,7 @@
341
341
  "real-world-architecture": {
342
342
  "title": "Real-World Theme Architecture",
343
343
  "description": "How production themes are structured: page types, section composition, component reuse, and prop design patterns",
344
- "content": "## Real-World Theme Architecture\n\nThis guide describes how a production ikas theme is structured, based on analysis of real deployed themes.\n\n### Page Types\n\nA complete theme typically has these page types:\n\n| Page Type | Purpose | Common Sections |\n|-----------|---------|------------------|\n| `INDEX` | Home page | Header, Hero Banner, Featured Products, Categories, Footer |\n| `CATEGORY` | Category listing | Header, Product List, Footer |\n| `PRODUCT_DETAIL` | Product page | Header, Product Detail, Reviews, Related Products, Footer |\n| `BLOG` | Blog listing | Header, Blog List, Footer |\n| `BLOG_POST` | Blog article | Header, Blog Post Detail, Footer |\n| `CART` | Shopping cart | Header, Cart Section, Footer |\n| `CHECKOUT` | Checkout flow | Minimal Header, Checkout Form |\n| `LOGIN` | Sign in | Header, Login Form, Footer |\n| `REGISTER` | Sign up | Header, Register Form, Footer |\n| `FORGOT_PASSWORD` | Password recovery | Header, Forgot Password Form, Footer |\n| `ACCOUNT` | Customer dashboard | Header, Account Section, Footer |\n| `SEARCH` | Search results | Header, Product List (search mode), Footer |\n| `CUSTOM` | Custom pages (About, Contact, FAQ) | Header, Custom Content, Footer |\n| `NOT_FOUND` | 404 page | Header, 404 Message, Footer |\n\n### Section Composition Pattern\n\nEvery page follows: **Header → Content Section(s) → Footer**\n\n- Header and Footer are shared across 36/37 pages (all except checkout)\n- Content sections are page-specific\n- Some sections are reused across multiple pages (FAQ on 10 pages, Rich Text on 9)\n\n### Section Reuse Strategy\n\nDesign sections to be reusable:\n\n| Section | Reuse Count | Strategy |\n|---------|-------------|----------|\n| Header | 36 pages | Single section, all navigation |\n| Footer | 36 pages | Single section, all links |\n| FAQ | 10 pages | Generic accordion, content via props |\n| Rich Text / HTML | 9 pages | Generic content block |\n| Product Detail | 1 page | Highly specialized |\n| Cart | 1 page | Highly specialized |\n\n### Component Hierarchy\n\nProduction sections have sub-components:\n\n```\nHeader Section (section)\n├── AnnouncementBar (component)\n├── NavigationBar (component)\n│ ├── Logo\n│ ├── NavLinks\n│ └── CartIcon / AccountIcon\n└── MobileMenu (component)\n\nProduct Detail Section (section)\n├── ImageGallery (component)\n├── VariantSelector (component)\n├── PriceDisplay (component)\n├── AddToCartButton (component)\n└── ProductDescription (component)\n```\n\n### Prop Design Patterns\n\nCommon prop patterns seen in production:\n\n1. **Content props**: `TEXT` for headings, descriptions, button labels\n2. **Style props**: `COLOR` for backgrounds, text colors, accents\n3. **Toggle props**: `BOOLEAN` for show/hide sections, features\n4. **Layout props**: `SELECT` for layout variants (grid/list, left/center/right)\n5. **Navigation props**: `LIST_OF_LINK` for editable nav menus\n6. **Data props**: `PRODUCT`, `PRODUCT_LIST`, `BLOG_POST_LIST` for dynamic data\n7. **Media props**: `IMAGE` for logos, banners, icons\n\n### Responsive Design\n\nProduction themes target these breakpoints:\n- Desktop: 1200px+ (max-width container)\n- Tablet: 768px-1199px\n- Mobile: <768px\n\nCommon responsive patterns:\n```css\n.section { width: 100%; padding: 64px 24px; }\n.section-inner { max-width: 1200px; margin: 0 auto; }\n\n@media (max-width: 768px) {\n .section { padding: 32px 16px; }\n .grid { grid-template-columns: 1fr; }\n}\n```\n\n### Essential Sections Checklist\n\nEvery complete theme needs at minimum:\n- [ ] Header (with mobile menu)\n- [ ] Footer\n- [ ] Hero Banner / Home content\n- [ ] Product List (category page)\n- [ ] Product Detail\n- [ ] Cart\n- [ ] Login / Register / Forgot Password\n- [ ] Account (orders, addresses)\n- [ ] 404 Page\n- [ ] Contact Form (optional but common)\n- [ ] FAQ (optional but common)\n- [ ] Blog List + Blog Post (optional)",
344
+ "content": "## Real-World Theme Architecture\n\nThis guide describes how a production ikas theme is structured, based on analysis of real deployed themes.\n\n### Page Types\n\nA complete theme typically has these page types:\n\n| Page Type | Purpose | Common Sections |\n|-----------|---------|------------------|\n| `INDEX` | Home page | Header, Hero Banner, Featured Products, Categories, Footer |\n| `CATEGORY` | Category listing | Header, Product List, Footer |\n| `PRODUCT_DETAIL` | Product page | Header, Product Detail, Reviews, Related Products, Footer |\n| `BLOG` | Blog listing | Header, Blog List, Footer |\n| `BLOG` | Blog article | Header, Blog Post Detail, Footer |\n| `CART` | Shopping cart | Header, Cart Section, Footer |\n| `CHECKOUT` | Checkout flow | Minimal Header, Checkout Form |\n| `LOGIN` | Sign in | Header, Login Form, Footer |\n| `REGISTER` | Sign up | Header, Register Form, Footer |\n| `FORGOT_PASSWORD` | Password recovery | Header, Forgot Password Form, Footer |\n| `ACCOUNT` | Customer dashboard | Header, Account Section, Footer |\n| `SEARCH` | Search results | Header, Product List (search mode), Footer |\n| `CUSTOM` | Custom pages (About, Contact, FAQ) | Header, Custom Content, Footer |\n| `NOT_FOUND` | 404 page | Header, 404 Message, Footer |\n\n### Section Composition Pattern\n\nEvery page follows: **Header → Content Section(s) → Footer**\n\n- Header and Footer are shared across 36/37 pages (all except checkout)\n- Content sections are page-specific\n- Some sections are reused across multiple pages (FAQ on 10 pages, Rich Text on 9)\n\n### Section Reuse Strategy\n\nDesign sections to be reusable:\n\n| Section | Reuse Count | Strategy |\n|---------|-------------|----------|\n| Header | 36 pages | Single section, all navigation |\n| Footer | 36 pages | Single section, all links |\n| FAQ | 10 pages | Generic accordion, content via props |\n| Rich Text / HTML | 9 pages | Generic content block |\n| Product Detail | 1 page | Highly specialized |\n| Cart | 1 page | Highly specialized |\n\n### Component Hierarchy\n\nProduction sections have sub-components:\n\n```\nHeader Section (section)\n├── AnnouncementBar (component)\n├── NavigationBar (component)\n│ ├── Logo\n│ ├── NavLinks\n│ └── CartIcon / AccountIcon\n└── MobileMenu (component)\n\nProduct Detail Section (section)\n├── ImageGallery (component)\n├── VariantSelector (component)\n├── PriceDisplay (component)\n├── AddToCartButton (component)\n└── ProductDescription (component)\n```\n\n### Prop Design Patterns\n\nCommon prop patterns seen in production:\n\n1. **Content props**: `TEXT` for headings, descriptions, button labels\n2. **Style props**: `COLOR` for backgrounds, text colors, accents\n3. **Toggle props**: `BOOLEAN` for show/hide sections, features\n4. **Layout props**: `SELECT` for layout variants (grid/list, left/center/right)\n5. **Navigation props**: `LIST_OF_LINK` for editable nav menus\n6. **Data props**: `PRODUCT`, `PRODUCT_LIST`, `BLOG_LIST` for dynamic data\n7. **Media props**: `IMAGE` for logos, banners, icons\n\n### Responsive Design\n\nProduction themes target these breakpoints:\n- Desktop: 1200px+ (max-width container)\n- Tablet: 768px-1199px\n- Mobile: <768px\n\nCommon responsive patterns:\n```css\n.section { width: 100%; padding: 64px 24px; }\n.section-inner { max-width: 1200px; margin: 0 auto; }\n\n@media (max-width: 768px) {\n .section { padding: 32px 16px; }\n .grid { grid-template-columns: 1fr; }\n}\n```\n\n### Essential Sections Checklist\n\nEvery complete theme needs at minimum:\n- [ ] Header (with mobile menu)\n- [ ] Footer\n- [ ] Hero Banner / Home content\n- [ ] Product List (category page)\n- [ ] Product Detail\n- [ ] Cart\n- [ ] Login / Register / Forgot Password\n- [ ] Account (orders, addresses)\n- [ ] 404 Page\n- [ ] Contact Form (optional but common)\n- [ ] FAQ (optional but common)\n- [ ] Blog List + Blog Post (optional)",
345
345
  "tags": [
346
346
  "architecture",
347
347
  "theme",