@ikas/code-components-mcp 1.4.0-beta.4 → 1.4.0-beta.6

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.
@@ -173,7 +173,7 @@
173
173
  "common-pitfalls": {
174
174
  "title": "Common Pitfalls",
175
175
  "description": "Frequent mistakes LLMs and developers make when building ikas code components",
176
- "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.\n\n### 14. Missing backgroundColor Prop on Sections\n\nEvery section MUST include a `backgroundColor` COLOR prop so store owners can customize the section background. Without it, the section has a hardcoded background that cannot be changed in the editor.\n\n**Wrong** — section with no backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" }\n]}\n```\n```tsx\nexport function MySection({ title }: Props) {\n return <section className=\"my-section\"><h1>{title}</h1></section>;\n}\n```\n\n**Correct** — section with backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" },\n { \"name\": \"backgroundColor\", \"displayName\": \"Background Color\", \"type\": \"COLOR\", \"defaultValue\": \"#ffffff\" }\n]}\n```\n```tsx\nexport function MySection({ title, backgroundColor = \"#ffffff\" }: Props) {\n return (\n <section className=\"my-section\" style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n </section>\n );\n}\n```\n\nOptionally, also consider adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the store owner changes the background color.\n\n### 15. Forgetting to Group Props for Complex Sections\n\nSections with 5+ props should use prop groups to organize the editor sidebar. Without groups, store owners see a long flat list of unrelated props.\n\n**Better** — organize related props into groups:\n```json\n{\n \"propGroups\": [\n { \"id\": \"content\", \"name\": \"Content\" },\n { \"id\": \"appearance\", \"name\": \"Appearance\" }\n ],\n \"props\": [\n { \"name\": \"heading\", \"groupId\": \"content\", ... },\n { \"name\": \"bgColor\", \"groupId\": \"appearance\", ... }\n ]\n}\n```\n\nSee `get_framework_guide(\"prop-groups\")` for full details.\n\n### 16. Hardcoded Static Text in Components\n\nAll user-visible text MUST be TEXT props — never hardcode strings in JSX. Hardcoded text cannot be translated for multilingual storefronts.\n\n**Wrong** — hardcoded strings:\n```tsx\nexport function LoginSection({ backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>Sign In</h1>\n <button>{form.isSubmitting ? \"Signing in...\" : \"Sign In\"}</button>\n <p>Don't have an account? <a href=\"/register\">Create one</a></p>\n </section>\n );\n}\n```\n\n**Correct** — all text as TEXT props with defaultValues:\n```json\n// ikas.config.json\n{ \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submitButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submittingButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Signing in...\", \"groupId\": \"texts\" },\n { \"name\": \"noAccountText\", \"type\": \"TEXT\", \"defaultValue\": \"Don't have an account?\", \"groupId\": \"texts\" },\n { \"name\": \"createAccountLinkText\", \"type\": \"TEXT\", \"defaultValue\": \"Create one\", \"groupId\": \"texts\" }\n],\n\"propGroups\": [{ \"id\": \"texts\", \"name\": \"Texts\" }]}\n```\n```tsx\nexport function LoginSection({ title = \"Sign In\", submitButtonText = \"Sign In\",\n submittingButtonText = \"Signing in...\", noAccountText = \"Don't have an account?\",\n createAccountLinkText = \"Create one\", backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n <button>{form.isSubmitting ? submittingButtonText : submitButtonText}</button>\n <p>{noAccountText} <a href=\"/register\">{createAccountLinkText}</a></p>\n </section>\n );\n}\n```\n\nFor button loading states, use two separate TEXT props (e.g., `submitButtonText` + `submittingButtonText`). Group text props under a \"Texts\" propGroup when the component has 5+ props.",
176
+ "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.\n\n### 14. Missing backgroundColor Prop on Sections\n\nEvery section MUST include a `backgroundColor` COLOR prop so store owners can customize the section background. Without it, the section has a hardcoded background that cannot be changed in the editor.\n\n**Wrong** — section with no backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" }\n]}\n```\n```tsx\nexport function MySection({ title }: Props) {\n return <section className=\"my-section\"><h1>{title}</h1></section>;\n}\n```\n\n**Correct** — section with backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" },\n { \"name\": \"backgroundColor\", \"displayName\": \"Background Color\", \"type\": \"COLOR\", \"defaultValue\": \"#ffffff\" }\n]}\n```\n```tsx\nexport function MySection({ title, backgroundColor = \"#ffffff\" }: Props) {\n return (\n <section className=\"my-section\" style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n </section>\n );\n}\n```\n\nOptionally, also consider adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the store owner changes the background color.\n\n### 15. Forgetting to Group Props for Complex Sections\n\nSections with 5+ props should use prop groups to organize the editor sidebar. Without groups, store owners see a long flat list of unrelated props.\n\n**Better** — organize related props into groups:\n```json\n{\n \"propGroups\": [\n { \"id\": \"content\", \"name\": \"Content\" },\n { \"id\": \"appearance\", \"name\": \"Appearance\" }\n ],\n \"props\": [\n { \"name\": \"heading\", \"groupId\": \"content\", ... },\n { \"name\": \"bgColor\", \"groupId\": \"appearance\", ... }\n ]\n}\n```\n\nSee `get_framework_guide(\"prop-groups\")` for full details.\n\n### 16. Hardcoded Static Text in Components\n\nAll user-visible text MUST be TEXT props — never hardcode strings in JSX. Hardcoded text cannot be translated for multilingual storefronts.\n\n**Wrong** — hardcoded strings:\n```tsx\nexport function LoginSection({ backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>Sign In</h1>\n <button>{form.isSubmitting ? \"Signing in...\" : \"Sign In\"}</button>\n <p>Don't have an account? <a href=\"/register\">Create one</a></p>\n </section>\n );\n}\n```\n\n**Correct** — all text as TEXT props with defaultValues:\n```json\n// ikas.config.json\n{ \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submitButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submittingButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Signing in...\", \"groupId\": \"texts\" },\n { \"name\": \"noAccountText\", \"type\": \"TEXT\", \"defaultValue\": \"Don't have an account?\", \"groupId\": \"texts\" },\n { \"name\": \"createAccountLinkText\", \"type\": \"TEXT\", \"defaultValue\": \"Create one\", \"groupId\": \"texts\" }\n],\n\"propGroups\": [{ \"id\": \"texts\", \"name\": \"Texts\" }]}\n```\n```tsx\nexport function LoginSection({ title = \"Sign In\", submitButtonText = \"Sign In\",\n submittingButtonText = \"Signing in...\", noAccountText = \"Don't have an account?\",\n createAccountLinkText = \"Create one\", backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n <button>{form.isSubmitting ? submittingButtonText : submitButtonText}</button>\n <p>{noAccountText} <a href=\"/register\">{createAccountLinkText}</a></p>\n </section>\n );\n}\n```\n\nFor button loading states, use two separate TEXT props (e.g., `submitButtonText` + `submittingButtonText`). Group text props under a \"Texts\" propGroup when the component has 5+ props.\n\n### 17. IkasOrderLineItemOption.values is an Array, Not a Scalar\n\n`IkasOrderLineItemOption` (the per-item options on cart/order line items) has a `.values` field that is an **array** of `IkasOrderLineItemOptionValue`, NOT a scalar `.value`. Each value in the array has its own `.name` / `.value` / `.formattedPrice`.\n\n```tsx\n// WRONG — there is no .value on IkasOrderLineItemOption\n<span>{option.value}</span>\n\n// CORRECT — .values is an array; map over it\n{option.values.map((v) => (\n <span key={v.name}>{v.name}: {v.value}</span>\n))}\n```\n\nThis is the most common confusion when rendering order summary lines or cart line item options. See `get_migration_guide(\"prop-runtime-shapes\")` for the broader catalog of order-shape gotchas.\n\n### 18. IkasOrderAdjustment Has No .formattedAmount — Use the Helper\n\n`IkasOrderAdjustment` does not expose a `.formattedAmount` property directly. Call the helper `getOrderAdjustmentFormattedAmount(adjustment)` from `@ikas/bp-storefront` — it returns the formatted currency string with the correct sign (negated for decrements).\n\n```tsx\nimport { getOrderAdjustmentFormattedAmount } from \"@ikas/bp-storefront\";\n\n// WRONG — .formattedAmount does not exist\n<td>{adjustment.formattedAmount}</td>\n\n// CORRECT\n<td>{getOrderAdjustmentFormattedAmount(adjustment)}</td>\n```\n\nSee `get_function_doc(\"getOrderAdjustmentFormattedAmount\")` for the exact signature.\n\n### 19. Old-System Property Patterns That Don't Exist in the New System\n\nSeveral `@ikas/storefront` properties look like they should still work but don't exist on the corresponding `@ikas/bp-storefront` types. Use the helper function instead. **If TypeScript reports TS2339 (\"Property X does not exist on type Y\") on a `.selectedVariant`, `.mainImage`, `.href`, or `.checkoutUrl` access, you're hitting one of these.**\n\n| Old pattern | New replacement |\n|---|---|\n| `product.selectedVariant` | `getSelectedProductVariant(product)` → `IkasProductVariant \\| undefined` |\n| `product.href` | `getProductHref(product)` (one of several `get*Href` helpers — see `navigation-patterns`) |\n| `variant.mainImage` | `getProductVariantMainImage(variant)` → `IkasProductImage`, then access `.image` for the `IkasImage` |\n| `cartStore.checkoutUrl` | `getCheckoutUrlFromCartStore(cartStore)` (handles auth-state branching internally) |\n\nThe variant-to-image chain in particular is 4 hops, which trips up most migrations:\n\n```tsx\nconst variant = getSelectedProductVariant(product);\nconst productImage = variant && getProductVariantMainImage(variant);\nconst image = productImage?.image;\nif (image) {\n const src = getDefaultSrc(image);\n}\n```\n\nEach hop has a real reason: variant selection (which size/color) → that variant's image set → the image record (carrying metadata like `order`, `isMain`) → the actual `IkasImage` the CDN helpers accept. Memorize the chain — it appears everywhere product imagery is rendered.",
177
177
  "tags": [
178
178
  "pitfalls",
179
179
  "gotchas",
@@ -313,7 +313,7 @@
313
313
  "header-footer-patterns": {
314
314
  "title": "Header & Footer Patterns (Serel Reference)",
315
315
  "description": "IkasComponentRenderer-based Header with Navbar/Announcements/CookieBar children, Footer with SocialMediaIcon children, Toast system, isHeader/isFooter flags",
316
- "content": "## How to build a container section (Header/Footer pattern)\n\nContainer sections are NOT self-contained. The pattern is **always** 3 steps:\n\n1. **Create each child component first.** Capture the `componentId` from each CLI response:\n ```bash\n npx ikas-component config add-component --name Navbar --type component --props '[...]'\n # → { \"success\": true, \"componentId\": \"7ojrigep-Eml9n5sN3i\", ... }\n npx ikas-component config add-component --name Announcements --type component --props '[...]'\n npx ikas-component config add-component --name CookieBar --type component --props '[...]'\n ```\n\n2. **Create the parent section** (without `filteredComponentIds` — those reference ids that don't exist yet):\n ```bash\n npx ikas-component config add-component --name Header --type section --isHeader \\\n --props '[{\"name\":\"components\",\"type\":\"COMPONENT_LIST\"},{\"name\":\"backgroundColor\",\"type\":\"COLOR\"}]'\n ```\n\n3. **Wire the parent's COMPONENT_LIST slot** to the captured child ids:\n ```bash\n npx ikas-component config update-prop --component Header --prop components \\\n --filteredComponentIds '[\"7ojrigep-Eml9n5sN3i\", \"<ANNOUNCEMENTS_ID>\", \"<COOKIEBAR_ID>\"]'\n ```\n\nIf you skip Step 3 the slot is empty and the editor will let the store owner drag in ANY component — usually not what you want. Component ids are opaque random strings; you cannot guess or derive them from names. Use `config list` to look up existing ids at any time.\n\n---\n\n## Header & Footer Patterns (Serel Reference)\n\nIn the serel theme, the Header and Footer are sections that use `IkasComponentRenderer` to render child components. This is the recommended production pattern.\n\n### Header Architecture\n\nThe serel Header section declares a `COMPONENT_LIST` prop with `filteredComponentIds` restricting it to three child components: **Navbar**, **Announcements**, and **CookieBar**.\n\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function Header({ components, backgroundColor, ...props }: Props) {\n return (\n <header className=\"header\" style={backgroundColor ? { backgroundColor } : undefined}>\n <IkasComponentRenderer id=\"header-components\" components={components} parentProps={props} />\n <ToastContainer />\n </header>\n );\n}\nexport default Header;\n```\n\nConfig setup with `isHeader: true` and `filteredComponentIds`:\n```json\n{\n \"id\": \"7ojrigep-IBBg5nI1PC\",\n \"name\": \"Header\",\n \"type\": \"section\",\n \"isHeader\": true,\n \"props\": [\n {\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"<NAVBAR_ID>\", \"<ANNOUNCEMENTS_ID>\", \"<COOKIEBAR_ID>\"]\n }\n ]\n}\n```\n\nThe `isHeader: true` flag ensures this section appears on every page automatically.\n\n### Toast Notification System\n\nThe serel Header hosts a `ToastContainer` component. The `useToast` hook provides a global notification system:\n\n```tsx\nimport { useToast } from \"../../hooks/useToast\";\n\n// In any component:\nconst { showToast } = useToast();\nshowToast({ message: \"Added to cart!\", type: \"success\" });\n```\n\nThe ToastContainer renders as a portal to ensure toasts appear above all content. Toasts stack vertically and auto-dismiss after a timeout.\n\n### Navbar Child Component\n\nThe Navbar child component handles: logo display, navigation links (`LIST_OF_LINK`), cart badge (reads `cartStore`), account icon (reads `customerStore`), search functionality, and a mobile hamburger menu. It also has its own `COMPONENT_LIST` slot for product search results using CardProductName/CardProductVariants/CardProductPrice with `privateVarMap` for the product variable.\n\n### Announcements Child Component\n\nThe Announcements child component uses `IkasThemeSlider` for a rotating announcement bar. It has its own child component slot for individual Announcement items.\n\n### CookieBar Child Component\n\nThe CookieBar manages cookie consent with accept/decline actions and persists the choice to localStorage.\n\n### Footer Architecture\n\nThe serel Footer section uses `IkasComponentRenderer` with a `COMPONENT_LIST` filtered to **SocialMediaIcon** children:\n\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function Footer({ components, backgroundColor, ...props }: Props) {\n return (\n <footer className=\"footer\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"footer-inner\">\n {/* Logo, link columns, contact info */}\n <div className=\"footer-social\">\n <IkasComponentRenderer id=\"footer-components\" components={components} parentProps={props} />\n </div>\n {/* Copyright */}\n </div>\n </footer>\n );\n}\nexport default Footer;\n```\n\nConfig with `isFooter: true`:\n```json\n{\n \"id\": \"7ojrigep-fTZb2qP9aV\",\n \"name\": \"Footer\",\n \"type\": \"section\",\n \"isFooter\": true,\n \"props\": [\n {\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"<SOCIALMEDIAICON_ID>\"]\n }\n ]\n}\n```\n\n### Key Patterns\n\n- Headers and footers use `IkasComponentRenderer` with `filteredComponentIds` for controlled child composition\n- `isHeader: true` / `isFooter: true` flags in config make them appear on all pages\n- Root exports are automatically reactive (cartStore/customerStore tracked by autorun)\n- Toast system lives in the Header for global accessibility\n- `parentProps` passes section data to child components\n- The Navbar child component itself nests additional child components via its own COMPONENT_LIST",
316
+ "content": "## How to build a container section (Header/Footer pattern)\n\nContainer sections are NOT self-contained. The pattern is **always** 3 steps:\n\n1. **Create each child component first.** Capture the `componentId` from each CLI response:\n ```bash\n npx ikas-component config add-component --name Navbar --type component --props '[...]'\n # → { \"success\": true, \"componentId\": \"7ojrigep-Eml9n5sN3i\", ... }\n npx ikas-component config add-component --name Announcements --type component --props '[...]'\n npx ikas-component config add-component --name CookieBar --type component --props '[...]'\n ```\n\n2. **Create the parent section** (without `filteredComponentIds` — those reference ids that don't exist yet):\n ```bash\n npx ikas-component config add-component --name Header --type section --isHeader \\\n --props '[{\"name\":\"components\",\"type\":\"COMPONENT_LIST\"},{\"name\":\"backgroundColor\",\"type\":\"COLOR\"}]'\n ```\n\n3. **Wire the parent's COMPONENT_LIST slot** to the captured child ids:\n ```bash\n npx ikas-component config update-prop --component Header --prop components \\\n --filteredComponentIds '[\"7ojrigep-Eml9n5sN3i\", \"<ANNOUNCEMENTS_ID>\", \"<COOKIEBAR_ID>\"]'\n ```\n\nIf you skip Step 3 the slot is empty and the editor will let the store owner drag in ANY component — usually not what you want. Component ids are opaque random strings; you cannot guess or derive them from names. Use `config list` to look up existing ids at any time.\n\n---\n\n## Header & Footer Patterns (Serel Reference)\n\nIn the serel theme, the Header and Footer are sections that use `IkasComponentRenderer` to render child components. This is the recommended production pattern.\n\n### Header Architecture\n\nThe serel Header section declares a `COMPONENT_LIST` prop with `filteredComponentIds` restricting it to three child components: **Navbar**, **Announcements**, and **CookieBar**.\n\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function Header({ components, backgroundColor, ...props }: Props) {\n return (\n <header className=\"header\" style={backgroundColor ? { backgroundColor } : undefined}>\n <IkasComponentRenderer id=\"header-components\" components={components} parentProps={props} />\n <ToastContainer />\n </header>\n );\n}\nexport default Header;\n```\n\nConfig setup with `isHeader: true` and `filteredComponentIds`:\n```json\n{\n \"id\": \"7ojrigep-IBBg5nI1PC\",\n \"name\": \"Header\",\n \"type\": \"section\",\n \"isHeader\": true,\n \"props\": [\n {\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"<NAVBAR_ID>\", \"<ANNOUNCEMENTS_ID>\", \"<COOKIEBAR_ID>\"]\n }\n ]\n}\n```\n\nThe `isHeader: true` flag ensures this section appears on every page automatically.\n\n### Toast Notification System\n\nThe serel Header hosts a `ToastContainer` component. The `useToast` hook provides a global notification system:\n\n```tsx\nimport { useToast } from \"../../hooks/useToast\";\n\n// In any component:\nconst { showToast } = useToast();\nshowToast({ message: \"Added to cart!\", type: \"success\" });\n```\n\nThe ToastContainer renders as a portal to ensure toasts appear above all content. Toasts stack vertically and auto-dismiss after a timeout.\n\n### Navbar Child Component\n\nThe Navbar child component handles: logo display, navigation links (`LIST_OF_LINK`), cart badge (reads `cartStore`), account icon (reads `customerStore`), search functionality, and a mobile hamburger menu. It also has its own `COMPONENT_LIST` slot for product search results using CardProductName/CardProductVariants/CardProductPrice with `privateVarMap` for the product variable.\n\n### Announcements Child Component\n\nThe Announcements child component uses `IkasThemeSlider` for a rotating announcement bar. It has its own child component slot for individual Announcement items.\n\n### CookieBar Child Component\n\nThe CookieBar manages cookie consent with accept/decline actions and persists the choice to localStorage.\n\n### Footer Architecture\n\nThe serel Footer section uses `IkasComponentRenderer` with a `COMPONENT_LIST` filtered to **SocialMediaIcon** children:\n\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function Footer({ components, backgroundColor, ...props }: Props) {\n return (\n <footer className=\"footer\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"footer-inner\">\n {/* Logo, link columns, contact info */}\n <div className=\"footer-social\">\n <IkasComponentRenderer id=\"footer-components\" components={components} parentProps={props} />\n </div>\n {/* Copyright */}\n </div>\n </footer>\n );\n}\nexport default Footer;\n```\n\nConfig with `isFooter: true`:\n```json\n{\n \"id\": \"7ojrigep-fTZb2qP9aV\",\n \"name\": \"Footer\",\n \"type\": \"section\",\n \"isFooter\": true,\n \"props\": [\n {\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"<SOCIALMEDIAICON_ID>\"]\n }\n ]\n}\n```\n\n### Key Patterns\n\n- Headers and footers use `IkasComponentRenderer` with `filteredComponentIds` for controlled child composition\n- `isHeader: true` / `isFooter: true` flags in config make them appear on all pages\n- Root exports are automatically reactive (cartStore/customerStore tracked by autorun)\n- Toast system lives in the Header for global accessibility\n- `parentProps` passes section data to child components\n- The Navbar child component itself nests additional child components via its own COMPONENT_LIST\n\n### Mega-Menu / MenuItem Example (Rich Per-Link Items)\n\nWhen the header needs navigation items with **per-link metadata** (image thumbnail, colored chip, custom icon, column count, badge text), `LIST_OF_LINK` cannot carry that data — `IkasNavigationLink` only has `href`, `label`, `subLinks`, `openInNewTab`. The correct pattern is identical to the Navbar/Announcements/CookieBar composition above: build a child component (`MenuItem`) and wire it into a `COMPONENT_LIST` slot on the Header.\n\n```bash\n# 1. Create the MenuItem child component\nnpx ikas-component config add-component --name \"MenuItem\" --type component \\\n --props '[{\"name\":\"link\",\"type\":\"LINK\",\"required\":true},{\"name\":\"image\",\"type\":\"IMAGE\"},{\"name\":\"title\",\"type\":\"TEXT\"}]'\n# → { \"success\": true, \"componentId\": \"cc_menu_item_xyz\", ... }\n\n# 2. Add a COMPONENT_LIST slot to the Header for menu items\nnpx ikas-component config update-prop --component Header --prop menuItems \\\n --filteredComponentIds '[\"cc_menu_item_xyz\"]'\n```\n\nIn `MenuItem/index.tsx`:\n\n```tsx\nimport { getDefaultSrc } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport default function MenuItem({ link, image, title }: Props) {\n if (!link) return null;\n return (\n <a className=\"menu-item\" href={link.href}>\n {image && <img className=\"menu-item-image\" src={getDefaultSrc(image)} alt={title} />}\n <span className=\"menu-item-title\">{title}</span>\n </a>\n );\n}\n```\n\nIn the Header section, render the list via `IkasComponentRenderer`:\n\n```tsx\n<IkasComponentRenderer id=\"menu-items\" components={menuItems as any[]} parentProps={props} />\n```\n\nThe store owner now drags `MenuItem` instances into the Header's slot and configures each item's link, image, and title individually. **If you flatten this into `LIST_OF_LINK` you will silently drop the image and every other non-link field.**\n\nThis pattern generalizes to any \"list of richly-decorated items\" — cards, badges, featured callouts, social icons. The decision criterion is whether each item needs more than `href + label`. If yes, build a child component.\n",
317
317
  "tags": [
318
318
  "header",
319
319
  "footer",
@@ -378,7 +378,7 @@
378
378
  "navigation-patterns": {
379
379
  "title": "Navigation & Routing Patterns",
380
380
  "description": "Router.navigate, page navigation, href getters, breadcrumbs, and query parameters",
381
- "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 with options\nRouter.navigate(\"/products\", true); // shallow navigation (no full page reload)\nRouter.navigate(\"/products\", false, true); // open in new tab\n// Signature: Router.navigate(path: string, shallow?: boolean, openInNewTab?: boolean)\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",
381
+ "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 with options\nRouter.navigate(\"/products\", true); // shallow navigation (no full page reload)\nRouter.navigate(\"/products\", false, true); // open in new tab\n// Signature: Router.navigate(path: string, shallow?: boolean, openInNewTab?: boolean)\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\n\n### Rich Navigation Items (When LIST_OF_LINK Is Insufficient)\n\n`LIST_OF_LINK` gives you `IkasNavigationLinkList` → an array of `IkasNavigationLink` items with `{ href, label, subLinks, openInNewTab, itemId, type }`. There is **no slot for an image, color, icon, column count, or any other custom field per link**.\n\nIf the navigation needs richer per-item data — a mega-menu with thumbnails, colored category chips, multi-column submenus, badges, custom icons — `LIST_OF_LINK` is the wrong tool. Build a dedicated component instead:\n\n```bash\nnpx ikas-component config add-component --name \"MenuItem\" --type component \\\n --props '[{\"name\":\"link\",\"type\":\"LINK\"},{\"name\":\"image\",\"type\":\"IMAGE\"},{\"name\":\"title\",\"type\":\"TEXT\"}]'\n```\n\nThen wire the parent header/navbar with a `COMPONENT_LIST` prop and `filteredComponentIds: [\"<MenuItem id>\"]`. Each menu item becomes a self-contained component instance with its own link, image, and title.\n\nSee `get_framework_guide(\"header-footer-patterns\")` for the COMPONENT_LIST recipe and `get_migration_guide(\"custom-data-conversion\")` for the broader enum-vs-component decision framework (the mega-menu case is one of the worked examples).\n",
382
382
  "tags": [
383
383
  "navigation",
384
384
  "router",
@@ -530,4 +530,4 @@
530
530
  ]
531
531
  }
532
532
  }
533
- }
533
+ }