#
tokens: 31743/50000 1/825 files (page 59/61)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 59 of 61. Use http://codebase.md/taurgis/sfcc-dev-mcp?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .DS_Store
├── .github
│   ├── dependabot.yml
│   ├── instructions
│   │   ├── mcp-node-tests.instructions.md
│   │   └── mcp-yml-tests.instructions.md
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── documentation.yml
│   │   ├── feature_request.yml
│   │   └── question.yml
│   ├── PULL_REQUEST_TEMPLATE
│   │   ├── bug_fix.md
│   │   ├── documentation.md
│   │   └── new_tool.md
│   ├── pull_request_template.md
│   └── workflows
│       ├── ci.yml
│       ├── deploy-pages.yml
│       ├── publish.yml
│       └── update-docs.yml
├── .gitignore
├── .husky
│   └── pre-commit
├── aegis.config.docs-only.json
├── aegis.config.json
├── aegis.config.with-dw.json
├── AGENTS.md
├── ai-instructions
│   ├── claude-desktop
│   │   └── claude_custom_instructions.md
│   ├── cursor
│   │   └── .cursor
│   │       └── rules
│   │           ├── debugging-workflows.mdc
│   │           ├── hooks-development.mdc
│   │           ├── isml-templates.mdc
│   │           ├── job-framework.mdc
│   │           ├── performance-optimization.mdc
│   │           ├── scapi-endpoints.mdc
│   │           ├── security-patterns.mdc
│   │           ├── sfcc-development.mdc
│   │           ├── sfra-controllers.mdc
│   │           ├── sfra-models.mdc
│   │           ├── system-objects.mdc
│   │           └── testing-patterns.mdc
│   └── github-copilot
│       └── copilot-instructions.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── docs
│   ├── best-practices
│   │   ├── cartridge_creation.md
│   │   ├── isml_templates.md
│   │   ├── job_framework.md
│   │   ├── localserviceregistry.md
│   │   ├── ocapi_hooks.md
│   │   ├── performance.md
│   │   ├── scapi_custom_endpoint.md
│   │   ├── scapi_hooks.md
│   │   ├── security.md
│   │   ├── sfra_client_side_js.md
│   │   ├── sfra_controllers.md
│   │   ├── sfra_models.md
│   │   └── sfra_scss.md
│   ├── dw_campaign
│   │   ├── ABTest.md
│   │   ├── ABTestMgr.md
│   │   ├── ABTestSegment.md
│   │   ├── AmountDiscount.md
│   │   ├── ApproachingDiscount.md
│   │   ├── BonusChoiceDiscount.md
│   │   ├── BonusDiscount.md
│   │   ├── Campaign.md
│   │   ├── CampaignMgr.md
│   │   ├── CampaignStatusCodes.md
│   │   ├── Coupon.md
│   │   ├── CouponMgr.md
│   │   ├── CouponRedemption.md
│   │   ├── CouponStatusCodes.md
│   │   ├── Discount.md
│   │   ├── DiscountPlan.md
│   │   ├── FixedPriceDiscount.md
│   │   ├── FixedPriceShippingDiscount.md
│   │   ├── FreeDiscount.md
│   │   ├── FreeShippingDiscount.md
│   │   ├── PercentageDiscount.md
│   │   ├── PercentageOptionDiscount.md
│   │   ├── PriceBookPriceDiscount.md
│   │   ├── Promotion.md
│   │   ├── PromotionMgr.md
│   │   ├── PromotionPlan.md
│   │   ├── SlotContent.md
│   │   ├── SourceCodeGroup.md
│   │   ├── SourceCodeInfo.md
│   │   ├── SourceCodeStatusCodes.md
│   │   └── TotalFixedPriceDiscount.md
│   ├── dw_catalog
│   │   ├── Catalog.md
│   │   ├── CatalogMgr.md
│   │   ├── Category.md
│   │   ├── CategoryAssignment.md
│   │   ├── CategoryLink.md
│   │   ├── PriceBook.md
│   │   ├── PriceBookMgr.md
│   │   ├── Product.md
│   │   ├── ProductActiveData.md
│   │   ├── ProductAttributeModel.md
│   │   ├── ProductAvailabilityLevels.md
│   │   ├── ProductAvailabilityModel.md
│   │   ├── ProductInventoryList.md
│   │   ├── ProductInventoryMgr.md
│   │   ├── ProductInventoryRecord.md
│   │   ├── ProductLink.md
│   │   ├── ProductMgr.md
│   │   ├── ProductOption.md
│   │   ├── ProductOptionModel.md
│   │   ├── ProductOptionValue.md
│   │   ├── ProductPriceInfo.md
│   │   ├── ProductPriceModel.md
│   │   ├── ProductPriceTable.md
│   │   ├── ProductSearchHit.md
│   │   ├── ProductSearchModel.md
│   │   ├── ProductSearchRefinementDefinition.md
│   │   ├── ProductSearchRefinements.md
│   │   ├── ProductSearchRefinementValue.md
│   │   ├── ProductVariationAttribute.md
│   │   ├── ProductVariationAttributeValue.md
│   │   ├── ProductVariationModel.md
│   │   ├── Recommendation.md
│   │   ├── SearchModel.md
│   │   ├── SearchRefinementDefinition.md
│   │   ├── SearchRefinements.md
│   │   ├── SearchRefinementValue.md
│   │   ├── SortingOption.md
│   │   ├── SortingRule.md
│   │   ├── Store.md
│   │   ├── StoreGroup.md
│   │   ├── StoreInventoryFilter.md
│   │   ├── StoreInventoryFilterValue.md
│   │   ├── StoreMgr.md
│   │   ├── Variant.md
│   │   └── VariationGroup.md
│   ├── dw_content
│   │   ├── Content.md
│   │   ├── ContentMgr.md
│   │   ├── ContentSearchModel.md
│   │   ├── ContentSearchRefinementDefinition.md
│   │   ├── ContentSearchRefinements.md
│   │   ├── ContentSearchRefinementValue.md
│   │   ├── Folder.md
│   │   ├── Library.md
│   │   ├── MarkupText.md
│   │   └── MediaFile.md
│   ├── dw_crypto
│   │   ├── CertificateRef.md
│   │   ├── CertificateUtils.md
│   │   ├── Cipher.md
│   │   ├── Encoding.md
│   │   ├── JWE.md
│   │   ├── JWEHeader.md
│   │   ├── JWS.md
│   │   ├── JWSHeader.md
│   │   ├── KeyRef.md
│   │   ├── Mac.md
│   │   ├── MessageDigest.md
│   │   ├── SecureRandom.md
│   │   ├── Signature.md
│   │   ├── WeakCipher.md
│   │   ├── WeakMac.md
│   │   ├── WeakMessageDigest.md
│   │   ├── WeakSignature.md
│   │   └── X509Certificate.md
│   ├── dw_customer
│   │   ├── AddressBook.md
│   │   ├── AgentUserMgr.md
│   │   ├── AgentUserStatusCodes.md
│   │   ├── AuthenticationStatus.md
│   │   ├── Credentials.md
│   │   ├── Customer.md
│   │   ├── CustomerActiveData.md
│   │   ├── CustomerAddress.md
│   │   ├── CustomerCDPData.md
│   │   ├── CustomerContextMgr.md
│   │   ├── CustomerGroup.md
│   │   ├── CustomerList.md
│   │   ├── CustomerMgr.md
│   │   ├── CustomerPasswordConstraints.md
│   │   ├── CustomerPaymentInstrument.md
│   │   ├── CustomerStatusCodes.md
│   │   ├── EncryptedObject.md
│   │   ├── ExternalProfile.md
│   │   ├── OrderHistory.md
│   │   ├── ProductList.md
│   │   ├── ProductListItem.md
│   │   ├── ProductListItemPurchase.md
│   │   ├── ProductListMgr.md
│   │   ├── ProductListRegistrant.md
│   │   ├── Profile.md
│   │   └── Wallet.md
│   ├── dw_extensions.applepay
│   │   ├── ApplePayHookResult.md
│   │   └── ApplePayHooks.md
│   ├── dw_extensions.facebook
│   │   ├── FacebookFeedHooks.md
│   │   └── FacebookProduct.md
│   ├── dw_extensions.paymentrequest
│   │   ├── PaymentRequestHookResult.md
│   │   └── PaymentRequestHooks.md
│   ├── dw_extensions.payments
│   │   ├── SalesforceBancontactPaymentDetails.md
│   │   ├── SalesforceCardPaymentDetails.md
│   │   ├── SalesforceEpsPaymentDetails.md
│   │   ├── SalesforceIdealPaymentDetails.md
│   │   ├── SalesforceKlarnaPaymentDetails.md
│   │   ├── SalesforcePaymentDetails.md
│   │   ├── SalesforcePaymentIntent.md
│   │   ├── SalesforcePaymentMethod.md
│   │   ├── SalesforcePaymentRequest.md
│   │   ├── SalesforcePaymentsHooks.md
│   │   ├── SalesforcePaymentsMgr.md
│   │   ├── SalesforcePaymentsSiteConfiguration.md
│   │   ├── SalesforcePayPalOrder.md
│   │   ├── SalesforcePayPalOrderAddress.md
│   │   ├── SalesforcePayPalOrderPayer.md
│   │   ├── SalesforcePayPalPaymentDetails.md
│   │   ├── SalesforceSepaDebitPaymentDetails.md
│   │   └── SalesforceVenmoPaymentDetails.md
│   ├── dw_extensions.pinterest
│   │   ├── PinterestAvailability.md
│   │   ├── PinterestFeedHooks.md
│   │   ├── PinterestOrder.md
│   │   ├── PinterestOrderHooks.md
│   │   └── PinterestProduct.md
│   ├── dw_io
│   │   ├── CSVStreamReader.md
│   │   ├── CSVStreamWriter.md
│   │   ├── File.md
│   │   ├── FileReader.md
│   │   ├── FileWriter.md
│   │   ├── InputStream.md
│   │   ├── OutputStream.md
│   │   ├── PrintWriter.md
│   │   ├── RandomAccessFileReader.md
│   │   ├── Reader.md
│   │   ├── StringWriter.md
│   │   ├── Writer.md
│   │   ├── XMLIndentingStreamWriter.md
│   │   ├── XMLStreamConstants.md
│   │   ├── XMLStreamReader.md
│   │   └── XMLStreamWriter.md
│   ├── dw_job
│   │   ├── JobExecution.md
│   │   └── JobStepExecution.md
│   ├── dw_net
│   │   ├── FTPClient.md
│   │   ├── FTPFileInfo.md
│   │   ├── HTTPClient.md
│   │   ├── HTTPRequestPart.md
│   │   ├── Mail.md
│   │   ├── SFTPClient.md
│   │   ├── SFTPFileInfo.md
│   │   ├── WebDAVClient.md
│   │   └── WebDAVFileInfo.md
│   ├── dw_object
│   │   ├── ActiveData.md
│   │   ├── CustomAttributes.md
│   │   ├── CustomObject.md
│   │   ├── CustomObjectMgr.md
│   │   ├── Extensible.md
│   │   ├── ExtensibleObject.md
│   │   ├── Note.md
│   │   ├── ObjectAttributeDefinition.md
│   │   ├── ObjectAttributeGroup.md
│   │   ├── ObjectAttributeValueDefinition.md
│   │   ├── ObjectTypeDefinition.md
│   │   ├── PersistentObject.md
│   │   ├── SimpleExtensible.md
│   │   └── SystemObjectMgr.md
│   ├── dw_order
│   │   ├── AbstractItem.md
│   │   ├── AbstractItemCtnr.md
│   │   ├── Appeasement.md
│   │   ├── AppeasementItem.md
│   │   ├── Basket.md
│   │   ├── BasketMgr.md
│   │   ├── BonusDiscountLineItem.md
│   │   ├── CouponLineItem.md
│   │   ├── CreateAgentBasketLimitExceededException.md
│   │   ├── CreateBasketFromOrderException.md
│   │   ├── CreateCouponLineItemException.md
│   │   ├── CreateOrderException.md
│   │   ├── CreateTemporaryBasketLimitExceededException.md
│   │   ├── GiftCertificate.md
│   │   ├── GiftCertificateLineItem.md
│   │   ├── GiftCertificateMgr.md
│   │   ├── GiftCertificateStatusCodes.md
│   │   ├── Invoice.md
│   │   ├── InvoiceItem.md
│   │   ├── LineItem.md
│   │   ├── LineItemCtnr.md
│   │   ├── Order.md
│   │   ├── OrderAddress.md
│   │   ├── OrderItem.md
│   │   ├── OrderMgr.md
│   │   ├── OrderPaymentInstrument.md
│   │   ├── OrderProcessStatusCodes.md
│   │   ├── PaymentCard.md
│   │   ├── PaymentInstrument.md
│   │   ├── PaymentMethod.md
│   │   ├── PaymentMgr.md
│   │   ├── PaymentProcessor.md
│   │   ├── PaymentStatusCodes.md
│   │   ├── PaymentTransaction.md
│   │   ├── PriceAdjustment.md
│   │   ├── PriceAdjustmentLimitTypes.md
│   │   ├── ProductLineItem.md
│   │   ├── ProductShippingCost.md
│   │   ├── ProductShippingLineItem.md
│   │   ├── ProductShippingModel.md
│   │   ├── Return.md
│   │   ├── ReturnCase.md
│   │   ├── ReturnCaseItem.md
│   │   ├── ReturnItem.md
│   │   ├── Shipment.md
│   │   ├── ShipmentShippingCost.md
│   │   ├── ShipmentShippingModel.md
│   │   ├── ShippingLineItem.md
│   │   ├── ShippingLocation.md
│   │   ├── ShippingMethod.md
│   │   ├── ShippingMgr.md
│   │   ├── ShippingOrder.md
│   │   ├── ShippingOrderItem.md
│   │   ├── SumItem.md
│   │   ├── TaxGroup.md
│   │   ├── TaxItem.md
│   │   ├── TaxMgr.md
│   │   ├── TrackingInfo.md
│   │   └── TrackingRef.md
│   ├── dw_order.hooks
│   │   ├── CalculateHooks.md
│   │   ├── OrderHooks.md
│   │   ├── PaymentHooks.md
│   │   ├── ReturnHooks.md
│   │   └── ShippingOrderHooks.md
│   ├── dw_rpc
│   │   ├── SOAPUtil.md
│   │   ├── Stub.md
│   │   └── WebReference.md
│   ├── dw_suggest
│   │   ├── BrandSuggestions.md
│   │   ├── CategorySuggestions.md
│   │   ├── ContentSuggestions.md
│   │   ├── CustomSuggestions.md
│   │   ├── ProductSuggestions.md
│   │   ├── SearchPhraseSuggestions.md
│   │   ├── SuggestedCategory.md
│   │   ├── SuggestedContent.md
│   │   ├── SuggestedPhrase.md
│   │   ├── SuggestedProduct.md
│   │   ├── SuggestedTerm.md
│   │   ├── SuggestedTerms.md
│   │   ├── Suggestions.md
│   │   └── SuggestModel.md
│   ├── dw_svc
│   │   ├── FTPService.md
│   │   ├── FTPServiceDefinition.md
│   │   ├── HTTPFormService.md
│   │   ├── HTTPFormServiceDefinition.md
│   │   ├── HTTPService.md
│   │   ├── HTTPServiceDefinition.md
│   │   ├── LocalServiceRegistry.md
│   │   ├── Result.md
│   │   ├── Service.md
│   │   ├── ServiceCallback.md
│   │   ├── ServiceConfig.md
│   │   ├── ServiceCredential.md
│   │   ├── ServiceDefinition.md
│   │   ├── ServiceProfile.md
│   │   ├── ServiceRegistry.md
│   │   ├── SOAPService.md
│   │   └── SOAPServiceDefinition.md
│   ├── dw_system
│   │   ├── AgentUserStatusCodes.md
│   │   ├── Cache.md
│   │   ├── CacheMgr.md
│   │   ├── HookMgr.md
│   │   ├── InternalObject.md
│   │   ├── JobProcessMonitor.md
│   │   ├── Log.md
│   │   ├── Logger.md
│   │   ├── LogNDC.md
│   │   ├── OrganizationPreferences.md
│   │   ├── Pipeline.md
│   │   ├── PipelineDictionary.md
│   │   ├── RemoteInclude.md
│   │   ├── Request.md
│   │   ├── RequestHooks.md
│   │   ├── Response.md
│   │   ├── RESTErrorResponse.md
│   │   ├── RESTResponseMgr.md
│   │   ├── RESTSuccessResponse.md
│   │   ├── SearchStatus.md
│   │   ├── Session.md
│   │   ├── Site.md
│   │   ├── SitePreferences.md
│   │   ├── Status.md
│   │   ├── StatusItem.md
│   │   ├── System.md
│   │   └── Transaction.md
│   ├── dw_util
│   │   ├── ArrayList.md
│   │   ├── Assert.md
│   │   ├── BigInteger.md
│   │   ├── Bytes.md
│   │   ├── Calendar.md
│   │   ├── Collection.md
│   │   ├── Currency.md
│   │   ├── DateUtils.md
│   │   ├── Decimal.md
│   │   ├── FilteringCollection.md
│   │   ├── Geolocation.md
│   │   ├── HashMap.md
│   │   ├── HashSet.md
│   │   ├── Iterator.md
│   │   ├── LinkedHashMap.md
│   │   ├── LinkedHashSet.md
│   │   ├── List.md
│   │   ├── Locale.md
│   │   ├── Map.md
│   │   ├── MapEntry.md
│   │   ├── MappingKey.md
│   │   ├── MappingMgr.md
│   │   ├── PropertyComparator.md
│   │   ├── SecureEncoder.md
│   │   ├── SecureFilter.md
│   │   ├── SeekableIterator.md
│   │   ├── Set.md
│   │   ├── SortedMap.md
│   │   ├── SortedSet.md
│   │   ├── StringUtils.md
│   │   ├── Template.md
│   │   └── UUIDUtils.md
│   ├── dw_value
│   │   ├── EnumValue.md
│   │   ├── MimeEncodedText.md
│   │   ├── Money.md
│   │   └── Quantity.md
│   ├── dw_web
│   │   ├── ClickStream.md
│   │   ├── ClickStreamEntry.md
│   │   ├── Cookie.md
│   │   ├── Cookies.md
│   │   ├── CSRFProtection.md
│   │   ├── Form.md
│   │   ├── FormAction.md
│   │   ├── FormElement.md
│   │   ├── FormElementValidationResult.md
│   │   ├── FormField.md
│   │   ├── FormFieldOption.md
│   │   ├── FormFieldOptions.md
│   │   ├── FormGroup.md
│   │   ├── FormList.md
│   │   ├── FormListItem.md
│   │   ├── Forms.md
│   │   ├── HttpParameter.md
│   │   ├── HttpParameterMap.md
│   │   ├── LoopIterator.md
│   │   ├── PageMetaData.md
│   │   ├── PageMetaTag.md
│   │   ├── PagingModel.md
│   │   ├── Resource.md
│   │   ├── URL.md
│   │   ├── URLAction.md
│   │   ├── URLParameter.md
│   │   ├── URLRedirect.md
│   │   ├── URLRedirectMgr.md
│   │   └── URLUtils.md
│   ├── sfra
│   │   ├── account.md
│   │   ├── address.md
│   │   ├── billing.md
│   │   ├── cart.md
│   │   ├── categories.md
│   │   ├── content.md
│   │   ├── locale.md
│   │   ├── order.md
│   │   ├── payment.md
│   │   ├── price-default.md
│   │   ├── price-range.md
│   │   ├── price-tiered.md
│   │   ├── product-bundle.md
│   │   ├── product-full.md
│   │   ├── product-line-items.md
│   │   ├── product-search.md
│   │   ├── product-tile.md
│   │   ├── querystring.md
│   │   ├── render.md
│   │   ├── request.md
│   │   ├── response.md
│   │   ├── server.md
│   │   ├── shipping.md
│   │   ├── store.md
│   │   ├── stores.md
│   │   └── totals.md
│   └── TopLevel
│       ├── APIException.md
│       ├── arguments.md
│       ├── Array.md
│       ├── ArrayBuffer.md
│       ├── BigInt.md
│       ├── Boolean.md
│       ├── ConversionError.md
│       ├── DataView.md
│       ├── Date.md
│       ├── Error.md
│       ├── ES6Iterator.md
│       ├── EvalError.md
│       ├── Fault.md
│       ├── Float32Array.md
│       ├── Float64Array.md
│       ├── Function.md
│       ├── Generator.md
│       ├── global.md
│       ├── Int16Array.md
│       ├── Int32Array.md
│       ├── Int8Array.md
│       ├── InternalError.md
│       ├── IOError.md
│       ├── Iterable.md
│       ├── Iterator.md
│       ├── JSON.md
│       ├── Map.md
│       ├── Math.md
│       ├── Module.md
│       ├── Namespace.md
│       ├── Number.md
│       ├── Object.md
│       ├── QName.md
│       ├── RangeError.md
│       ├── ReferenceError.md
│       ├── RegExp.md
│       ├── Set.md
│       ├── StopIteration.md
│       ├── String.md
│       ├── Symbol.md
│       ├── SyntaxError.md
│       ├── SystemError.md
│       ├── TypeError.md
│       ├── Uint16Array.md
│       ├── Uint32Array.md
│       ├── Uint8Array.md
│       ├── Uint8ClampedArray.md
│       ├── URIError.md
│       ├── WeakMap.md
│       ├── WeakSet.md
│       ├── XML.md
│       ├── XMLList.md
│       └── XMLStreamError.md
├── docs-site
│   ├── .gitignore
│   ├── App.tsx
│   ├── components
│   │   ├── Badge.tsx
│   │   ├── BreadcrumbSchema.tsx
│   │   ├── CodeBlock.tsx
│   │   ├── Collapsible.tsx
│   │   ├── ConfigBuilder.tsx
│   │   ├── ConfigHero.tsx
│   │   ├── ConfigModeTabs.tsx
│   │   ├── icons.tsx
│   │   ├── Layout.tsx
│   │   ├── LightCodeContainer.tsx
│   │   ├── NewcomerCTA.tsx
│   │   ├── NextStepsStrip.tsx
│   │   ├── OnThisPage.tsx
│   │   ├── Search.tsx
│   │   ├── SEO.tsx
│   │   ├── Sidebar.tsx
│   │   ├── StructuredData.tsx
│   │   ├── ToolCard.tsx
│   │   ├── ToolFilters.tsx
│   │   ├── Typography.tsx
│   │   └── VersionBadge.tsx
│   ├── constants.tsx
│   ├── index.html
│   ├── main.tsx
│   ├── metadata.json
│   ├── package-lock.json
│   ├── package.json
│   ├── pages
│   │   ├── AIInterfacesPage.tsx
│   │   ├── ConfigurationPage.tsx
│   │   ├── DevelopmentPage.tsx
│   │   ├── ExamplesPage.tsx
│   │   ├── FeaturesPage.tsx
│   │   ├── HomePage.tsx
│   │   ├── SecurityPage.tsx
│   │   ├── ToolsPage.tsx
│   │   └── TroubleshootingPage.tsx
│   ├── postcss.config.js
│   ├── public
│   │   ├── .well-known
│   │   │   └── security.txt
│   │   ├── 404.html
│   │   ├── android-chrome-192x192.png
│   │   ├── android-chrome-512x512.png
│   │   ├── apple-touch-icon.png
│   │   ├── explain-product-pricing-methods-no-mcp.png
│   │   ├── explain-product-pricing-methods.png
│   │   ├── favicon-16x16.png
│   │   ├── favicon-32x32.png
│   │   ├── favicon.ico
│   │   ├── llms.txt
│   │   ├── robots.txt
│   │   ├── site.webmanifest
│   │   └── sitemap.xml
│   ├── README.md
│   ├── scripts
│   │   ├── generate-search-index.js
│   │   ├── generate-sitemap.js
│   │   └── search-dev.js
│   ├── src
│   │   └── styles
│   │       ├── input.css
│   │       └── prism-theme.css
│   ├── tailwind.config.js
│   ├── tsconfig.json
│   ├── types.ts
│   ├── utils
│   │   ├── search.ts
│   │   └── toolsData.ts
│   └── vite.config.ts
├── eslint.config.js
├── jest.config.js
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── scripts
│   └── convert-docs.js
├── SECURITY.md
├── server.json
├── src
│   ├── clients
│   │   ├── base
│   │   │   ├── http-client.ts
│   │   │   ├── oauth-token.ts
│   │   │   └── ocapi-auth-client.ts
│   │   ├── best-practices-client.ts
│   │   ├── cartridge-generation-client.ts
│   │   ├── docs
│   │   │   ├── class-content-parser.ts
│   │   │   ├── class-name-resolver.ts
│   │   │   ├── documentation-scanner.ts
│   │   │   ├── index.ts
│   │   │   └── referenced-types-extractor.ts
│   │   ├── docs-client.ts
│   │   ├── log-client.ts
│   │   ├── logs
│   │   │   ├── index.ts
│   │   │   ├── log-analyzer.ts
│   │   │   ├── log-client.ts
│   │   │   ├── log-constants.ts
│   │   │   ├── log-file-discovery.ts
│   │   │   ├── log-file-reader.ts
│   │   │   ├── log-formatter.ts
│   │   │   ├── log-processor.ts
│   │   │   ├── log-types.ts
│   │   │   └── webdav-client-manager.ts
│   │   ├── ocapi
│   │   │   ├── code-versions-client.ts
│   │   │   ├── site-preferences-client.ts
│   │   │   └── system-objects-client.ts
│   │   ├── ocapi-client.ts
│   │   └── sfra-client.ts
│   ├── config
│   │   ├── configuration-factory.ts
│   │   └── dw-json-loader.ts
│   ├── core
│   │   ├── handlers
│   │   │   ├── abstract-log-tool-handler.ts
│   │   │   ├── base-handler.ts
│   │   │   ├── best-practices-handler.ts
│   │   │   ├── cartridge-handler.ts
│   │   │   ├── client-factory.ts
│   │   │   ├── code-version-handler.ts
│   │   │   ├── docs-handler.ts
│   │   │   ├── job-log-handler.ts
│   │   │   ├── job-log-tool-config.ts
│   │   │   ├── log-handler.ts
│   │   │   ├── log-tool-config.ts
│   │   │   ├── sfra-handler.ts
│   │   │   ├── system-object-handler.ts
│   │   │   └── validation-helpers.ts
│   │   ├── server.ts
│   │   └── tool-definitions.ts
│   ├── index.ts
│   ├── main.ts
│   ├── services
│   │   ├── file-system-service.ts
│   │   ├── index.ts
│   │   └── path-service.ts
│   ├── tool-configs
│   │   ├── best-practices-tool-config.ts
│   │   ├── cartridge-tool-config.ts
│   │   ├── code-version-tool-config.ts
│   │   ├── docs-tool-config.ts
│   │   ├── job-log-tool-config.ts
│   │   ├── log-tool-config.ts
│   │   ├── sfra-tool-config.ts
│   │   └── system-object-tool-config.ts
│   ├── types
│   │   └── types.ts
│   └── utils
│       ├── cache.ts
│       ├── job-log-tool-config.ts
│       ├── job-log-utils.ts
│       ├── log-cache.ts
│       ├── log-tool-config.ts
│       ├── log-tool-constants.ts
│       ├── log-tool-utils.ts
│       ├── logger.ts
│       ├── ocapi-url-builder.ts
│       ├── path-resolver.ts
│       ├── query-builder.ts
│       ├── utils.ts
│       └── validator.ts
├── tests
│   ├── __mocks__
│   │   ├── docs-client.ts
│   │   ├── src
│   │   │   └── clients
│   │   │       └── base
│   │   │           └── http-client.js
│   │   └── webdav.js
│   ├── base-handler.test.ts
│   ├── base-http-client.test.ts
│   ├── best-practices-handler.test.ts
│   ├── cache.test.ts
│   ├── cartridge-handler.test.ts
│   ├── class-content-parser.test.ts
│   ├── class-name-resolver.test.ts
│   ├── client-factory.test.ts
│   ├── code-version-handler.test.ts
│   ├── code-versions-client.test.ts
│   ├── config.test.ts
│   ├── configuration-factory.test.ts
│   ├── docs-handler.test.ts
│   ├── documentation-scanner.test.ts
│   ├── file-system-service.test.ts
│   ├── job-log-handler.test.ts
│   ├── job-log-utils.test.ts
│   ├── log-client.test.ts
│   ├── log-handler.test.ts
│   ├── log-processor.test.ts
│   ├── logger.test.ts
│   ├── mcp
│   │   ├── AGENTS.md
│   │   ├── node
│   │   │   ├── activate-code-version-advanced.full-mode.programmatic.test.js
│   │   │   ├── code-versions.full-mode.programmatic.test.js
│   │   │   ├── generate-cartridge-structure.docs-only.programmatic.test.js
│   │   │   ├── get-available-best-practice-guides.docs-only.programmatic.test.js
│   │   │   ├── get-available-sfra-documents.programmatic.test.js
│   │   │   ├── get-best-practice-guide.docs-only.programmatic.test.js
│   │   │   ├── get-hook-reference.docs-only.programmatic.test.js
│   │   │   ├── get-job-execution-summary.full-mode.programmatic.test.js
│   │   │   ├── get-job-log-entries.full-mode.programmatic.test.js
│   │   │   ├── get-latest-debug.full-mode.programmatic.test.js
│   │   │   ├── get-latest-error.full-mode.programmatic.test.js
│   │   │   ├── get-latest-info.full-mode.programmatic.test.js
│   │   │   ├── get-latest-job-log-files.full-mode.programmatic.test.js
│   │   │   ├── get-latest-warn.full-mode.programmatic.test.js
│   │   │   ├── get-log-file-contents.full-mode.programmatic.test.js
│   │   │   ├── get-sfcc-class-documentation.docs-only.programmatic.test.js
│   │   │   ├── get-sfcc-class-info.docs-only.programmatic.test.js
│   │   │   ├── get-sfra-categories.docs-only.programmatic.test.js
│   │   │   ├── get-sfra-document.programmatic.test.js
│   │   │   ├── get-sfra-documents-by-category.docs-only.programmatic.test.js
│   │   │   ├── get-system-object-definition.full-mode.programmatic.test.js
│   │   │   ├── get-system-object-definitions.docs-only.programmatic.test.js
│   │   │   ├── get-system-object-definitions.full-mode.programmatic.test.js
│   │   │   ├── list-log-files.full-mode.programmatic.test.js
│   │   │   ├── list-sfcc-classes.docs-only.programmatic.test.js
│   │   │   ├── search-best-practices.docs-only.programmatic.test.js
│   │   │   ├── search-custom-object-attribute-definitions.full-mode.programmatic.test.js
│   │   │   ├── search-job-logs-by-name.full-mode.programmatic.test.js
│   │   │   ├── search-job-logs.full-mode.programmatic.test.js
│   │   │   ├── search-logs.full-mode.programmatic.test.js
│   │   │   ├── search-sfcc-classes.docs-only.programmatic.test.js
│   │   │   ├── search-sfcc-methods.docs-only.programmatic.test.js
│   │   │   ├── search-sfra-documentation.docs-only.programmatic.test.js
│   │   │   ├── search-site-preferences.full-mode.programmatic.test.js
│   │   │   ├── search-system-object-attribute-definitions.full-mode.programmatic.test.js
│   │   │   ├── search-system-object-attribute-groups.full-mode.programmatic.test.js
│   │   │   ├── summarize-logs.full-mode.programmatic.test.js
│   │   │   ├── tools.docs-only.programmatic.test.js
│   │   │   └── tools.full-mode.programmatic.test.js
│   │   ├── README.md
│   │   ├── test-fixtures
│   │   │   └── dw.json
│   │   └── yaml
│   │       ├── activate-code-version.docs-only.test.mcp.yml
│   │       ├── activate-code-version.full-mode.test.mcp.yml
│   │       ├── get_latest_error.test.mcp.yml
│   │       ├── get-available-best-practice-guides.docs-only.test.mcp.yml
│   │       ├── get-available-best-practice-guides.full-mode.test.mcp.yml
│   │       ├── get-available-sfra-documents.docs-only.test.mcp.yml
│   │       ├── get-available-sfra-documents.full-mode.test.mcp.yml
│   │       ├── get-best-practice-guide.docs-only.test.mcp.yml
│   │       ├── get-best-practice-guide.full-mode.test.mcp.yml
│   │       ├── get-code-versions.docs-only.test.mcp.yml
│   │       ├── get-code-versions.full-mode.test.mcp.yml
│   │       ├── get-hook-reference.docs-only.test.mcp.yml
│   │       ├── get-hook-reference.full-mode.test.mcp.yml
│   │       ├── get-job-execution-summary.full-mode.test.mcp.yml
│   │       ├── get-job-log-entries.full-mode.test.mcp.yml
│   │       ├── get-latest-debug.full-mode.test.mcp.yml
│   │       ├── get-latest-error.full-mode.test.mcp.yml
│   │       ├── get-latest-info.full-mode.test.mcp.yml
│   │       ├── get-latest-job-log-files.full-mode.test.mcp.yml
│   │       ├── get-latest-warn.full-mode.test.mcp.yml
│   │       ├── get-log-file-contents.full-mode.test.mcp.yml
│   │       ├── get-sfcc-class-documentation.docs-only.test.mcp.yml
│   │       ├── get-sfcc-class-documentation.full-mode.test.mcp.yml
│   │       ├── get-sfcc-class-info.docs-only.test.mcp.yml
│   │       ├── get-sfcc-class-info.full-mode.test.mcp.yml
│   │       ├── get-sfra-categories.docs-only.test.mcp.yml
│   │       ├── get-sfra-categories.full-mode.test.mcp.yml
│   │       ├── get-sfra-document.docs-only.test.mcp.yml
│   │       ├── get-sfra-document.full-mode.test.mcp.yml
│   │       ├── get-sfra-documents-by-category.docs-only.test.mcp.yml
│   │       ├── get-sfra-documents-by-category.full-mode.test.mcp.yml
│   │       ├── get-system-object-definition.docs-only.test.mcp.yml
│   │       ├── get-system-object-definition.full-mode.test.mcp.yml
│   │       ├── get-system-object-definitions.docs-only.test.mcp.yml
│   │       ├── get-system-object-definitions.full-mode.test.mcp.yml
│   │       ├── list-log-files.full-mode.test.mcp.yml
│   │       ├── list-sfcc-classes.docs-only.test.mcp.yml
│   │       ├── list-sfcc-classes.full-mode.test.mcp.yml
│   │       ├── search-best-practices.docs-only.test.mcp.yml
│   │       ├── search-best-practices.full-mode.test.mcp.yml
│   │       ├── search-custom-object-attribute-definitions.docs-only.test.mcp.yml
│   │       ├── search-custom-object-attribute-definitions.test.mcp.yml
│   │       ├── search-job-logs-by-name.full-mode.test.mcp.yml
│   │       ├── search-job-logs.full-mode.test.mcp.yml
│   │       ├── search-logs.full-mode.test.mcp.yml
│   │       ├── search-sfcc-classes.docs-only.test.mcp.yml
│   │       ├── search-sfcc-classes.full-mode.test.mcp.yml
│   │       ├── search-sfcc-methods.docs-only.test.mcp.yml
│   │       ├── search-sfcc-methods.full-mode.test.mcp.yml
│   │       ├── search-sfra-documentation.docs-only.test.mcp.yml
│   │       ├── search-sfra-documentation.full-mode.test.mcp.yml
│   │       ├── search-site-preferences.docs-only.test.mcp.yml
│   │       ├── search-site-preferences.full-mode.test.mcp.yml
│   │       ├── search-system-object-attribute-definitions.docs-only.test.mcp.yml
│   │       ├── search-system-object-attribute-definitions.full-mode.test.mcp.yml
│   │       ├── search-system-object-attribute-groups.docs-only.test.mcp.yml
│   │       ├── search-system-object-attribute-groups.full-mode.test.mcp.yml
│   │       ├── summarize-logs.full-mode.test.mcp.yml
│   │       ├── tools.docs-only.test.mcp.yml
│   │       └── tools.full-mode.test.mcp.yml
│   ├── oauth-token.test.ts
│   ├── ocapi-auth-client.test.ts
│   ├── ocapi-client.test.ts
│   ├── path-service.test.ts
│   ├── query-builder.test.ts
│   ├── referenced-types-extractor.test.ts
│   ├── servers
│   │   ├── sfcc-mock-server
│   │   │   ├── mock-data
│   │   │   │   └── ocapi
│   │   │   │       ├── code-versions.json
│   │   │   │       ├── custom-object-attributes-customapi.json
│   │   │   │       ├── custom-object-attributes-globalsettings.json
│   │   │   │       ├── custom-object-attributes-versionhistory.json
│   │   │   │       ├── site-preferences-ccv.json
│   │   │   │       ├── site-preferences-fastforward.json
│   │   │   │       ├── site-preferences-sfra.json
│   │   │   │       ├── site-preferences-storefront.json
│   │   │   │       ├── site-preferences-system.json
│   │   │   │       ├── system-object-attribute-groups-campaign.json
│   │   │   │       ├── system-object-attribute-groups-category.json
│   │   │   │       ├── system-object-attribute-groups-order.json
│   │   │   │       ├── system-object-attribute-groups-product.json
│   │   │   │       ├── system-object-attribute-groups-sitepreferences.json
│   │   │   │       ├── system-object-attributes-customeraddress.json
│   │   │   │       ├── system-object-attributes-product-expanded.json
│   │   │   │       ├── system-object-attributes-product.json
│   │   │   │       ├── system-object-definition-category.json
│   │   │   │       ├── system-object-definition-customer.json
│   │   │   │       ├── system-object-definition-customeraddress.json
│   │   │   │       ├── system-object-definition-order.json
│   │   │   │       ├── system-object-definition-product.json
│   │   │   │       ├── system-object-definitions-old.json
│   │   │   │       └── system-object-definitions.json
│   │   │   ├── package-lock.json
│   │   │   ├── package.json
│   │   │   ├── README.md
│   │   │   ├── scripts
│   │   │   │   └── setup-logs.js
│   │   │   ├── server.js
│   │   │   └── src
│   │   │       ├── app.js
│   │   │       ├── config
│   │   │       │   └── server-config.js
│   │   │       ├── middleware
│   │   │       │   ├── auth.js
│   │   │       │   ├── cors.js
│   │   │       │   └── logging.js
│   │   │       ├── routes
│   │   │       │   ├── ocapi
│   │   │       │   │   ├── code-versions-handler.js
│   │   │       │   │   ├── oauth-handler.js
│   │   │       │   │   ├── ocapi-error-utils.js
│   │   │       │   │   ├── ocapi-utils.js
│   │   │       │   │   ├── site-preferences-handler.js
│   │   │       │   │   └── system-objects-handler.js
│   │   │       │   ├── ocapi.js
│   │   │       │   └── webdav.js
│   │   │       └── utils
│   │   │           ├── mock-data-loader.js
│   │   │           └── webdav-xml.js
│   │   └── sfcc-mock-server-manager.ts
│   ├── sfcc-mock-server.test.ts
│   ├── site-preferences-client.test.ts
│   ├── system-objects-client.test.ts
│   ├── utils.test.ts
│   ├── validation-helpers.test.ts
│   └── validator.test.ts
├── tsconfig.json
└── tsconfig.test.json
```

# Files

--------------------------------------------------------------------------------
/docs/best-practices/isml_templates.md:
--------------------------------------------------------------------------------

```markdown
   1 | # Salesforce B2C Commerce ISML Templates: Best Practices & Development Guide
   2 | 
   3 | This guide provides comprehensive best practices for developing ISML (Internet Store Markup Language) templates within the Salesforce B2C Commerce Storefront Reference Architecture (SFRA). Master these principles to build secure, maintainable, and high-performing storefront experiences.
   4 | 
   5 | **IMPORTANT**: Before developing ISML templates, consult the **Performance and Stability Best Practices** and **SFRA Controllers** guides from this MCP server. Understanding the MVC separation and controller patterns is essential for proper ISML development.
   6 | 
   7 | ## Core Principles
   8 | 
   9 | ### The Golden Rule: Logic-Free Templates
  10 | 
  11 | **NEVER use `<isscript>` for business logic in ISML templates.** This is the most critical rule in SFRA development.
  12 | 
  13 | ISML templates are presentation-only layers that should receive all data from controllers via the `pdict` object. Any business logic, data manipulation, or API calls belong in controllers or models.
  14 | 
  15 | #### ❌ Anti-Pattern: Logic in Templates
  16 | ```html
  17 | <isscript>
  18 |     var ProductMgr = require('dw/catalog/ProductMgr');
  19 |     var product = ProductMgr.getProduct(pdict.pid);
  20 |     var price = product.getPriceModel().getPrice();
  21 | </isscript>
  22 | <div class="price">${price}</div>
  23 | ```
  24 | 
  25 | #### ✅ Correct Pattern: Data from Controller
  26 | ```javascript
  27 | // Controller
  28 | server.get('Show', function (req, res, next) {
  29 |     var product = ProductFactory.get({ pid: req.querystring.pid });
  30 |     res.render('product/productDetails', { product: product });
  31 |     next();
  32 | });
  33 | ```
  34 | 
  35 | ```html
  36 | <!-- ISML Template -->
  37 | <div class="price">${pdict.product.price.sales.formatted}</div>
  38 | ```
  39 | 
  40 | ### Exception: Asset Management Only
  41 | 
  42 | The **only** acceptable use of `<isscript>` is for managing static assets:
  43 | 
  44 | ```html
  45 | <isscript>
  46 |     var assets = require('*/cartridge/scripts/assets.js');
  47 |     assets.addCss('/css/product.css');
  48 |     assets.addJs('/js/product.js');
  49 | </isscript>
  50 | ```
  51 | 
  52 | ## Security Best Practices
  53 | 
  54 | ### XSS Prevention with Proper Encoding
  55 | 
  56 | **Always rely on default encoding.** The `<isprint>` tag automatically HTML-encodes output to prevent XSS attacks.
  57 | 
  58 | #### ✅ Secure (Default Behavior)
  59 | ```html
  60 | <div class="search-term">
  61 |     You searched for: <isprint value="${pdict.searchPhrase}" />
  62 | </div>
  63 | ```
  64 | 
  65 | #### ❌ Vulnerable
  66 | ```html
  67 | <div class="search-term">
  68 |     You searched for: <isprint value="${pdict.searchPhrase}" encoding="off" />
  69 | </div>
  70 | ```
  71 | 
  72 | ### Context-Specific Encoding
  73 | 
  74 | For non-HTML contexts, use `dw.util.SecureEncoder`:
  75 | 
  76 | ```html
  77 | <isscript>
  78 |     var SecureEncoder = require('dw/util/SecureEncoder');
  79 | </isscript>
  80 | 
  81 | <!-- For JavaScript context -->
  82 | <script>
  83 |     var searchTerm = "${SecureEncoder.forJavaScript(pdict.searchPhrase)}";
  84 | </script>
  85 | 
  86 | <!-- For HTML attributes -->
  87 | <input type="hidden" value="${SecureEncoder.forHTMLAttribute(pdict.token)}" />
  88 | ```
  89 | 
  90 | ## Template Architecture Patterns
  91 | 
  92 | ### 1. Layout Decorators
  93 | 
  94 | Use decorators for consistent page structure:
  95 | 
  96 | #### SFRA Default Decorator Templates
  97 | 
  98 | **IMPORTANT**: SFRA provides only **two default decorator templates** out of the box:
  99 | 
 100 | 1. **`common/layout/page`** - Standard storefront pages (homepage, PDP, search, account, etc.)
 101 | 2. **`common/layout/checkout`** - Secure checkout process pages
 102 | 
 103 | If you need additional layout patterns (modal dialogs, email templates, mobile-specific layouts, etc.), you must **create custom decorator templates** in your cartridge's `templates/default/common/layout/` directory.
 104 | 
 105 | #### Common Custom Decorators You May Need to Create:
 106 | 
 107 | ```html
 108 | <!-- Custom Modal Layout -->
 109 | common/layout/modal.isml
 110 | 
 111 | <!-- Email Template Layout -->
 112 | common/layout/email.isml
 113 | 
 114 | <!-- Popup/Lightbox Layout -->
 115 | common/layout/popup.isml
 116 | 
 117 | <!-- Print-Friendly Layout -->
 118 | common/layout/print.isml
 119 | 
 120 | <!-- Mobile App Layout -->
 121 | common/layout/mobile.isml
 122 | ```
 123 | 
 124 | #### Default Template Usage Examples:
 125 | 
 126 | **Using the Standard Page Layout**:
 127 | ```html
 128 | <!-- ✅ Standard storefront pages -->
 129 | <isdecorate template="common/layout/page">
 130 |     <div class="product-details">
 131 |         <h1><isprint value="${pdict.product.productName}" /></h1>
 132 |         <!-- Content goes here -->
 133 |     </div>
 134 | </isdecorate>
 135 | ```
 136 | 
 137 | **Using the Checkout Layout**:
 138 | ```html
 139 | <!-- ✅ Secure checkout pages -->
 140 | <isdecorate template="common/layout/checkout">
 141 |     <div class="checkout-content">
 142 |         <h1>Checkout</h1>
 143 |         <!-- Checkout form content -->
 144 |     </div>
 145 | </isdecorate>
 146 | ```
 147 | 
 148 | #### Creating Custom Decorator Templates:
 149 | 
 150 | If you need a custom layout, create it in your cartridge at `templates/default/common/layout/[name].isml`:
 151 | 
 152 | **Example Custom Modal Layout** (`common/layout/modal.isml`):
 153 | ```html
 154 | <!doctype html>
 155 | <html lang="${require('dw/util/Locale').getLocale(request.locale).getLanguage()}">
 156 | <head>
 157 |     <isinclude template="common/htmlHead" />
 158 | </head>
 159 | <body class="modal-body">
 160 |     <div class="modal-container">
 161 |         <header class="modal-header">
 162 |             <button type="button" class="close" data-dismiss="modal">
 163 |                 <span>&times;</span>
 164 |             </button>
 165 |         </header>
 166 |         <main class="modal-content">
 167 |             <isreplace/>
 168 |         </main>
 169 |     </div>
 170 | </body>
 171 | </html>
 172 | ```
 173 | 
 174 | **Using Your Custom Layout**:
 175 | ```html
 176 | <!-- ✅ Using custom decorator template -->
 177 | <isdecorate template="common/layout/modal">
 178 |     <div class="modal-body-content">
 179 |         <h2>Modal Title</h2>
 180 |         <p>Modal content goes here</p>
 181 |     </div>
 182 | </isdecorate>
 183 | ```
 184 | 
 185 | ### 2. Component Modularity
 186 | 
 187 | Break complex templates into reusable components:
 188 | 
 189 | **Product Tile Component** (`product/components/productTile.isml`):
 190 | ```html
 191 | <div class="product-tile" data-pid="${pdict.product.id}">
 192 |     <div class="tile-image">
 193 |         <isinclude template="product/components/productTileImage" />
 194 |     </div>
 195 |     
 196 |     <div class="tile-body">
 197 |         <div class="pdp-link">
 198 |             <a class="link" href="${pdict.product.url}">
 199 |                 <isprint value="${pdict.product.productName}" />
 200 |             </a>
 201 |         </div>
 202 |         
 203 |         <isinclude template="product/components/pricing/main" />
 204 |         
 205 |         <isinclude template="product/components/productTileActions" />
 206 |     </div>
 207 | </div>
 208 | ```
 209 | 
 210 | **Usage in Grid**:
 211 | ```html
 212 | <div class="row product-grid">
 213 |     <isloop items="${pdict.productSearch.productHits}" var="productHit">
 214 |         <div class="col-6 col-sm-4">
 215 |             <isinclude template="product/components/productTile" />
 216 |             <isset name="product" value="${productHit.product}" scope="page" />
 217 |         </div>
 218 |     </isloop>
 219 | </div>
 220 | ```
 221 | 
 222 | ### 3. Remote Includes vs Local Includes (Advanced Fragment Architecture)
 223 | 
 224 | Remote includes are NOT just an alternative syntax – they change request boundaries, data scoping, caching strategy, security posture, and performance characteristics. Choose them only when a fragment truly needs an independent cache policy or isolation.
 225 | 
 226 | | Attribute | Local Include `<isinclude template="...">` | Remote Include `<isinclude url="...">` |
 227 | |-----------|---------------------------------------------|-------------------------------------------|
 228 | | Processing | Single request on Application Server | Parent request + secondary request orchestrated by Web Adapter |
 229 | | Data Scope | Full access to parent `pdict` & variables | Isolated – only URL query params available |
 230 | | Cache Policy | Inherits parent (lowest TTL wins) | Independent TTL per fragment |
 231 | | Typical Use | Reusable markup, product tile partials | Mini-cart, personalized promo slot, dynamic inventory widget |
 232 | | Overhead | Minimal | Extra HTTP round trip per include |
 233 | | Security | Inherits parent auth state | New unauthenticated request – must add explicit protection |
 234 | | Failure Mode | Template error breaks page render | Fragment timeout can delay full page assembly |
 235 | 
 236 | #### 3.1 When to Use Which
 237 | Use a local include unless ALL are true:
 238 | 1. Fragment volatility differs meaningfully from page shell
 239 | 2. Output can be parameterized via simple query params
 240 | 3. Performance gain from separate caching outweighs added request
 241 | 4. Security implications are understood and mitigated
 242 | 
 243 | #### 3.2 Implementing a Remote Include
 244 | Template example (mini-cart header icon kept uncached while page shell cached hours):
 245 | ```html
 246 | <div class="header-cart" data-component="mini-cart">
 247 |     <isinclude url="${URLUtils.url('Cart-MiniCart')}" />
 248 |     <!-- Controller route name MUST match exactly -->
 249 | </div>
 250 | ```
 251 | 
 252 | Controller (excerpt):
 253 | ```javascript
 254 | var server = require('server');
 255 | var cache = require('*/cartridge/scripts/middleware/cache');
 256 | 
 257 | server.get('MiniCart',
 258 |     server.middleware.include, // Gatekeeper: only valid inside remote include flow
 259 |     cache.applyShortPromotionSensitiveCache, // Or cache.disableCaching() equivalent for uncached
 260 |     function (req, res, next) {
 261 |         // Build isolated model – no parent pdict access
 262 |         res.render('components/header/miniCart');
 263 |         next();
 264 |     }
 265 | );
 266 | 
 267 | module.exports = server.exports();
 268 | ```
 269 | 
 270 | #### 3.3 Passing Data
 271 | All context must be URL params:
 272 | ```html
 273 | <isinclude url="${URLUtils.url('Page-Include', 'cid', 'footer-content-asset')}" />
 274 | ```
 275 | Avoid over-parameterization – each unique full URL becomes a distinct cache entry (cache fragmentation risk).
 276 | 
 277 | #### 3.4 Caching Strategy Patterns
 278 | | Scenario | Page Shell TTL | Remote Include TTL | Rationale |
 279 | |----------|----------------|--------------------|-----------|
 280 | | Marketing landing + live inventory badge | 12h | 5m | Keep inventory fresh without invalidating hero layout |
 281 | | Category grid + personalized banner | 4h | 15m | Personalized offers rotate more often than navigation |
 282 | | PDP + mini-cart | 2h | 0 (uncached) | Basket state must reflect current session |
 283 | 
 284 | Keep total remote includes per page conservative (<20 recommended) to avoid waterfall latency.
 285 | 
 286 | #### 3.5 Performance Anti-Patterns
 287 | | Anti-Pattern | Why It’s Harmful | Better Alternative |
 288 | |--------------|------------------|--------------------|
 289 | | One remote include per product tile in a grid | N extra HTTP requests, destroys cache efficiency | Single parent render with local includes |
 290 | | Adding position/index params that change per render | Creates unique cache keys, low hit ratio | Omit non-functional varying params |
 291 | | Deep nesting (include inside include chain) | Hard to debug, compounding latency | Flatten architecture – coalesce related fragments |
 292 | 
 293 | #### 3.6 Security Criticals
 294 | Remote include endpoints are effectively public unless you add authentication middleware (e.g., `userLoggedIn.validateLoggedIn`). NEVER expose PII or account state in an unprotected include.
 295 | 
 296 | Always start chain with:
 297 | ```javascript
 298 | server.get('SensitiveFragment',
 299 |   server.middleware.include,
 300 |   userLoggedIn.validateLoggedIn, // if user-specific
 301 |   function (req, res, next) { ... }
 302 | );
 303 | ```
 304 | 
 305 | #### 3.7 Observability & Debugging
 306 | Use extended request IDs in logs: `reqId-depth-index` (e.g., `AbC123-1-02`). Search logs to isolate slow fragment origins.
 307 | 
 308 | #### 3.8 Checklist
 309 | ```text
 310 | [ ] Justified different cache TTL
 311 | [ ] URL params minimal & non-sensitive
 312 | [ ] server.middleware.include first
 313 | [ ] Additional auth middleware if user data
 314 | [ ] Explicit cache middleware (or disabled)
 315 | [ ] Fragment count on page < 20
 316 | [ ] No nested chains beyond depth 2
 317 | ```
 318 | 
 319 | If you cannot satisfy most checklist items, prefer a local include.
 320 | 
 321 | #### 3.9 When NOT to Use Remote Includes
 322 | - Pure presentational partials (icons, button groups)
 323 | - Iterative children of a paginated list
 324 | - Form bodies that rely on parent controller’s validation context
 325 | - Anything requiring access to parent `pdict` objects without trivial serialization
 326 | 
 327 | ---
 328 | 
 329 | ## Utility Helpers Available in Templates
 330 | 
 331 | SFRA templates run inside the B2C Commerce template processor, which autowires a set of utility objects and classes so you can call them without additional imports. Knowing what is available keeps templates lean and avoids unnecessary `<isscript>` blocks.
 332 | 
 333 | ### Top-Level Variables
 334 | 
 335 | The template scope automatically exposes:
 336 | 
 337 | - `pdict`
 338 | - `out`
 339 | - `request`
 340 | - `session`
 341 | 
 342 | Each object gives you direct access to storefront context data and helper APIs.
 343 | 
 344 | ### `dw.system.Request`
 345 | 
 346 | Because `request` is a top-level variable, you can call methods directly without `require` statements.
 347 | 
 348 | | Method | Description |
 349 | | --- | --- |
 350 | | `getHttpCookies()` → `Cookies` | Returns the `Cookies` object so you can inspect or manipulate client-side cookies. |
 351 | | `getHttpHeaders()` → `Map` | Returns a map of HTTP header values on the current request. |
 352 | | `isHttpSecure()` → `Boolean` | Indicates whether the request is secure (`HTTPS`). |
 353 | | `isSCAPI()` → `Boolean` | Distinguishes between OCAPI and SCAPI requests in extension points (hooks). |
 354 | 
 355 | **Example**
 356 | 
 357 | ```html
 358 | <td class="price merchandizetotal">
 359 |     <isprint value="${request.custom.Container.adjustedMerchandiseTotalNetPrice}" />
 360 | </td>
 361 | ```
 362 | 
 363 | ### `dw.system.Session`
 364 | 
 365 | The `session` top-level variable exposes the current storefront or Business Manager session (Business Manager sessions return `null` for customer lookups).
 366 | 
 367 | | Method | Description |
 368 | | --- | --- |
 369 | | `getCustomer()` → `Customer` | Returns the current customer associated with the storefront session; `null` in Business Manager. |
 370 | | `isCustomerAuthenticated()` → `Boolean` | Indicates whether the customer for this session is authenticated (equivalent to `customer.isAuthenticated()`). |
 371 | 
 372 | **Example**
 373 | 
 374 | ```html
 375 | <isprint value="${session.getCustomer().firstname}" />
 376 | ```
 377 | 
 378 | ### `dw.util.StringUtils`
 379 | 
 380 | `StringUtils` is pre-imported, letting you call static helpers by their simple name to format and sanitize output.
 381 | 
 382 | | Method | Description |
 383 | | --- | --- |
 384 | | `formatDate(date)` → `String` | Formats a `Date` with the current site’s default date format. |
 385 | | `formatInteger(number)` → `String` | Formats a number using the site’s default integer format; floats are coerced to integers. |
 386 | | `formatNumber(number)` → `String` | Formats a number using the site’s default number format. |
 387 | | `garble(str, replaceChar, suffixLength)` → `String` | Masks a string, leaving the last `suffixLength` characters intact. |
 388 | | `pad(str, width)` → `String` | Pads a string to a target width (useful for alignment in tables). |
 389 | | `stringToHtml(str)` → `String` | Converts a string to an HTML-safe representation. |
 390 | | `stringToWml(str)` → `String` | Converts a string to a WML-safe representation. |
 391 | | `stringToXml(str)` → `String` | Converts a string to an XML-safe representation. |
 392 | | `trim(str)` → `String` | Removes leading and trailing whitespace. |
 393 | | `truncate(str, maxLength, mode, suffix)` → `String` | Truncates text using the provided mode and optional suffix. |
 394 | 
 395 | **Example**
 396 | 
 397 | ```html
 398 | <isprint value="${StringUtils.pad('abc', 5)}" />
 399 | ```
 400 | 
 401 | ### `dw.web.URLUtils`
 402 | 
 403 | `URLUtils` is also pre-imported. Use it for URL generation instead of manual string concatenation.
 404 | 
 405 | | Method | Description |
 406 | | --- | --- |
 407 | | `abs(action, ...namesAndParams)` → `URL` | Generates an absolute URL using protocol and host from the calling request. |
 408 | | `http(action, ...namesAndParams)` → `URL` | Generates an absolute HTTP URL using site preferences when available. |
 409 | | `https(action, ...namesAndParams)` → `URL` | Generates an absolute HTTPS URL; respects secure host preferences. |
 410 | | `httpsWebRoot()` → `URL` | Returns an absolute HTTPS web root URL for static asset references. |
 411 | | `httpWebRoot()` → `URL` | Returns an absolute HTTP web root URL for static asset references. |
 412 | | `url(action, ...namesAndParams)` → `URL` | Generates a relative URL. |
 413 | | `webRoot()` → `URL` | Returns a relative web root URL for referencing static media. |
 414 | 
 415 | **Example**
 416 | 
 417 | ```html
 418 | <form
 419 |     action="${URLUtils.httpsContinue()}"
 420 |     method="post"
 421 |     id="${pdict.CurrentForms.login.login.htmlName}"
 422 | >
 423 | </form>
 424 | ```
 425 | 
 426 | ---
 427 | 
 428 | ## ISML Expressions
 429 | 
 430 | ISML expressions let templates embed storefront logic and data access inline, using syntax that mirrors JavaScript expressions. They are the primary way to render data from the Pipeline Dictionary (`pdict`) without resorting to `<isscript>` blocks.
 431 | 
 432 | ### Expression Basics
 433 | 
 434 | - **Syntax**: `${ ... }` where `${` begins the expression and `}` ends it.
 435 | - **Scope**: Expressions evaluate in the context of template variables (`pdict`, `request`, `session`, etc.).
 436 | - **Usage**: Place expressions directly in markup or inside tag attributes. Always ensure the data you reference has been added to `pdict` (by the controller, decorator, or include).
 437 | 
 438 | #### Common Pattern
 439 | 
 440 | ```html
 441 | ${pdict.<KeyAttributeName>}
 442 | ```
 443 | 
 444 | ### Expression Examples
 445 | 
 446 | ```html
 447 | <!-- Attribute value -->
 448 | <isprint value="${pdict.Product.name}" />
 449 | 
 450 | <!-- Method call on a pdict object -->
 451 | "${pdict.Product.getLongDescription() != null}"
 452 | 
 453 | <!-- Using URL helper within an attribute -->
 454 | <form
 455 |     action="${URLUtils.continueURL()}"
 456 |     method="post"
 457 |     id="${pdict.CurrentForms.cart.htmlName}"
 458 | >
 459 | </form>
 460 | ```
 461 | 
 462 | ### Additional Quick References
 463 | 
 464 | | Expression | Description |
 465 | | --- | --- |
 466 | | `${pdict.Product}` | References the current product object. |
 467 | | `${pdict.Content.template}` | Accesses the content asset template attribute. |
 468 | | `${pdict.ProductPrices}` | Outputs the product prices data structure placed in `pdict`. |
 469 | | `${pdict.Order.orderNo}` | Outputs the current order number. |
 470 | 
 471 | ### Protected `</body>` Tag
 472 | 
 473 | The literal string `</body>` is reserved by the ISML parser. Do **not** include it in comments or inline scripts. If you need to emit the closing body tag from JavaScript, obfuscate it:
 474 | 
 475 | ```javascript
 476 | var endBodyIndex = markup.indexOf('</bo' + 'dy>');
 477 | ```
 478 | 
 479 | ---
 480 | 
 481 | ### 4. Essential ISML Tags & Usage
 482 | Renumbered after expanded section.
 483 | 
 484 | ### Conditional Logic
 485 | 
 486 | ```html
 487 | <isif condition="${pdict.product.available}">
 488 |     <span class="in-stock">In Stock</span>
 489 | <iselseif condition="${pdict.product.preorderAvailable}">
 490 |     <span class="preorder">Available for Pre-order</span>
 491 | <iselse>
 492 |     <span class="out-of-stock">Out of Stock</span>
 493 | </isif>
 494 | ```
 495 | 
 496 | ### Loops with Status
 497 | 
 498 | ```html
 499 | <isloop items="${pdict.productSearch.productHits}" var="productHit" status="loopstate">
 500 |     <div class="product-item ${loopstate.first ? 'first' : ''} ${loopstate.last ? 'last' : ''}">
 501 |         <!-- Product content -->
 502 |     </div>
 503 |     
 504 |     <isif condition="${loopstate.count % 3 === 0 && !loopstate.last}">
 505 |         </div><div class="row"> <!-- Start new row every 3 items -->
 506 |     </isif>
 507 | </isloop>
 508 | ```
 509 | 
 510 | ### Variable Setting
 511 | 
 512 | Use `<isset>` sparingly and only for simple template variables:
 513 | 
 514 | ```html
 515 | <isset name="showRatings" value="${pdict.product.ratingsEnabled && pdict.product.reviews.count > 0}" scope="page" />
 516 | 
 517 | <isif condition="${showRatings}">
 518 |     <isinclude template="product/components/reviews/reviewsSection" />
 519 | </isif>
 520 | ```
 521 | 
 522 | ## Performance Optimization
 523 | 
 524 | ### Caching Strategy
 525 | 
 526 | **Controller-based caching** (preferred):
 527 | ```javascript
 528 | // In controller
 529 | server.get('Show', cache.applyDefaultCache, function (req, res, next) {
 530 |     // Controller logic
 531 | });
 532 | ```
 533 | 
 534 | **Template-based caching** (for personalized content, also preferred in the controller):
 535 | ```html
 536 | <iscache type="relative" hour="1" varyby="price_promotion" />
 537 | ```
 538 | 
 539 | ### Efficient Asset Loading
 540 | 
 541 | ```html
 542 | <isscript>
 543 |     var assets = require('*/cartridge/scripts/assets.js');
 544 |     assets.addCss('/css/product.css');
 545 |     assets.addJs('/js/product.js');
 546 | </isscript>
 547 | ```
 548 | 
 549 | ## Localization Best Practices
 550 | 
 551 | ### Simple Text Resources
 552 | 
 553 | ```html
 554 | <!-- Properties file: checkout.properties -->
 555 | <!-- label.billing.address=Billing Address -->
 556 | 
 557 | <label class="form-control-label">
 558 |     ${Resource.msg('label.billing.address', 'checkout', 'Billing Address')}
 559 | </label>
 560 | ```
 561 | 
 562 | ### Parameterized Messages
 563 | 
 564 | ```html
 565 | <!-- Properties file: cart.properties -->
 566 | <!-- items.in.cart=You have {0} items totaling {1} -->
 567 | 
 568 | <div class="cart-summary">
 569 |     ${Resource.msgf('items.in.cart', 'cart', null, pdict.basket.numItems, pdict.basket.subTotal)}
 570 | </div>
 571 | ```
 572 | 
 573 | ## Common Patterns & Examples
 574 | 
 575 | ### 1. Form Rendering with Validation
 576 | 
 577 | ```html
 578 | <form action="${URLUtils.url('Account-SubmitRegistration')}" method="post">
 579 |     <input type="hidden" name="${pdict.csrf.tokenName}" value="${pdict.csrf.token}"/>
 580 |     
 581 |     <div class="form-group ${pdict.forms.profile.customer.firstname.invalid ? 'is-invalid' : ''}">
 582 |         <label for="registration-form-fname">
 583 |             ${Resource.msg('field.customer.firstname', 'registration', null)}
 584 |         </label>
 585 |         <input type="text" 
 586 |                class="form-control" 
 587 |                id="registration-form-fname"
 588 |                name="${pdict.forms.profile.customer.firstname.htmlName}"
 589 |                value="${pdict.forms.profile.customer.firstname.htmlValue}"
 590 |                <isprint value="${pdict.forms.profile.customer.firstname.attributes}" encoding="off" />>
 591 |         
 592 |         <isif condition="${pdict.forms.profile.customer.firstname.invalid}">
 593 |             <div class="invalid-feedback">
 594 |                 <isprint value="${pdict.forms.profile.customer.firstname.error}" />
 595 |             </div>
 596 |         </isif>
 597 |     </div>
 598 | </form>
 599 | ```
 600 | 
 601 | ### 2. Product Grid with Pagination
 602 | 
 603 | ```html
 604 | <div class="search-results">
 605 |     <div class="row product-grid">
 606 |         <isloop items="${pdict.productSearch.productHits}" var="productHit">
 607 |             <div class="col-6 col-sm-4 col-lg-3">
 608 |                 <isset name="product" value="${productHit.product}" scope="page" />
 609 |                 <isinclude template="product/components/productTile" />
 610 |             </div>
 611 |         </isloop>
 612 |     </div>
 613 |     
 614 |     <isif condition="${pdict.productSearch.count > pdict.productSearch.pageSize}">
 615 |         <isinclude template="search/components/pagination" />
 616 |     </isif>
 617 | </div>
 618 | ```
 619 | 
 620 | ### 3. Responsive Image Handling
 621 | 
 622 | ```html
 623 | <picture class="product-image">
 624 |     <source media="(max-width: 544px)" 
 625 |             srcset="${pdict.product.images.small[0].url}">
 626 |     <source media="(max-width: 768px)" 
 627 |             srcset="${pdict.product.images.medium[0].url}">
 628 |     <img src="${pdict.product.images.large[0].url}" 
 629 |          alt="${pdict.product.images.large[0].alt}"
 630 |          title="${pdict.product.images.large[0].title}"
 631 |          class="img-fluid">
 632 | </picture>
 633 | ```
 634 | 
 635 | ## Error Handling in Templates
 636 | 
 637 | ### Defensive Programming
 638 | 
 639 | ```html
 640 | <isif condition="${pdict.product && pdict.product.available}">
 641 |     <div class="product-availability">
 642 |         <isif condition="${pdict.product.availabilityModel.availability > 0}">
 643 |             <span class="in-stock">
 644 |                 ${Resource.msgf('label.quantity.in.stock', 'product', null, pdict.product.availabilityModel.availability)}
 645 |             </span>
 646 |         <iselse>
 647 |             <span class="limited-stock">
 648 |                 ${Resource.msg('label.limited.availability', 'product', null)}
 649 |             </span>
 650 |         </isif>
 651 |     </div>
 652 | </isif>
 653 | ```
 654 | 
 655 | ### Graceful Degradation
 656 | 
 657 | ```html
 658 | <div class="product-reviews">
 659 |     <isif condition="${pdict.product.reviews && pdict.product.reviews.count > 0}">
 660 |         <div class="reviews-summary">
 661 |             <span class="review-count">${pdict.product.reviews.count}</span>
 662 |             <isinclude template="product/components/reviewStars" />
 663 |         </div>
 664 |     <iselse>
 665 |         <div class="no-reviews">
 666 |             ${Resource.msg('label.no.reviews', 'product', 'No reviews yet')}
 667 |         </div>
 668 |     </isif>
 669 | </div>
 670 | ```
 671 | 
 672 | ## Testing & Debugging
 673 | 
 674 | ### Template Comments for Development
 675 | 
 676 | ```html
 677 | <iscomment>
 678 |     TODO: Implement wishlist functionality
 679 |     Controller needs to pass wishlist status in product model
 680 | </iscomment>
 681 | 
 682 | <iscomment>
 683 |     Debug: Product ID = ${pdict.product.id}
 684 |     Available = ${pdict.product.available}
 685 |     Price = ${pdict.product.price.sales.value}
 686 | </iscomment>
 687 | ```
 688 | 
 689 | ### Conditional Debug Output
 690 | 
 691 | ```html
 692 | <isif condition="${dw.system.System.getInstanceType() === dw.system.System.DEVELOPMENT_SYSTEM}">
 693 |     <div class="debug-info" style="background: yellow; padding: 10px;">
 694 |         <strong>Debug Info:</strong><br>
 695 |         Product ID: ${pdict.product.id}<br>
 696 |         Template: product/productDetails.isml<br>
 697 |         Timestamp: ${new Date()}
 698 |     </div>
 699 | </isif>
 700 | ```
 701 | 
 702 | ## Template Decoration and Layout
 703 | 
 704 | ### The `<isreplace>` Element
 705 | 
 706 | The `<isreplace>` element is a crucial component of SFCC's template decoration system. It identifies where the decorated content should be included within a decorator template.
 707 | 
 708 | #### Syntax
 709 | ```html
 710 | <isreplace/>
 711 | ```
 712 | 
 713 | #### Purpose and Behavior
 714 | 
 715 | The decorator template uses `<isreplace/>` to specify the insertion point for decorated content. Understanding its behavior is essential for proper template architecture:
 716 | 
 717 | - **Single `<isreplace/>` (typical)**: The decorated content is inserted at the location of the tag
 718 | - **Multiple `<isreplace/>` tags**: The decorated content is duplicated at each tag location
 719 | - **Zero `<isreplace/>` tags**: The decorated content is effectively omitted from the output
 720 | 
 721 | #### Example 1: Basic Template Decoration
 722 | 
 723 | **Rendering Template (uses decorator):**
 724 | ```html
 725 | <isset name="DecoratorTemplate" value="common/layout/page" scope="page"/>
 726 | <isdecorate template="${DecoratorTemplate}">
 727 |     <div class="product-details">
 728 |         <h1>${pdict.product.displayName}</h1>
 729 |         <div class="price">${pdict.product.price.sales.formatted}</div>
 730 |     </div>
 731 | </isdecorate>
 732 | ```
 733 | 
 734 | **Decorator Template (`common/layout/page.isml`):**
 735 | ```html
 736 | <!DOCTYPE html>
 737 | <html>
 738 | <head>
 739 |     <title>${pdict.pageTitle}</title>
 740 |     <meta charset="UTF-8">
 741 |     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 742 | </head>
 743 | <body>
 744 |     <div id="page-container">
 745 |         <isinclude template="components/header" />
 746 |         
 747 |         <main id="main-content">
 748 |             <div class="content-wrapper">
 749 |                 <isreplace/>
 750 |             </div>
 751 |         </main>
 752 |         
 753 |         <isinclude template="components/footer" />
 754 |     </div>
 755 | </body>
 756 | </html>
 757 | ```
 758 | 
 759 | #### Example 2: Multiple `<isreplace/>` Tags
 760 | 
 761 | **Decorator Template with Sidebar:**
 762 | ```html
 763 | <div class="layout-two-column">
 764 |     <aside class="sidebar">
 765 |         <div class="sidebar-content">
 766 |             <isreplace/>
 767 |         </div>
 768 |     </aside>
 769 |     
 770 |     <main class="main-content">
 771 |         <div class="content-area">
 772 |             <isreplace/>
 773 |         </div>
 774 |     </main>
 775 | </div>
 776 | ```
 777 | 
 778 | *In this example, the decorated content appears in both the sidebar and main content areas.*
 779 | 
 780 | #### Example 3: Conditional Content Placement
 781 | 
 782 | **Advanced Decorator Pattern:**
 783 | ```html
 784 | <div class="page-layout">
 785 |     <isinclude template="components/breadcrumb" />
 786 |     
 787 |     <isif condition="${pdict.showInSidebar}">
 788 |         <div class="with-sidebar">
 789 |             <main class="content">
 790 |                 <isreplace/>
 791 |             </main>
 792 |             <aside class="sidebar">
 793 |                 <isinclude template="components/relatedProducts" />
 794 |             </aside>
 795 |         </div>
 796 |     <iselse>
 797 |         <main class="full-width">
 798 |             <isreplace/>
 799 |         </main>
 800 |     </isif>
 801 | </div>
 802 | ```
 803 | 
 804 | #### Best Practices for `<isreplace>`
 805 | 
 806 | 1. **Single Replacement Point**: Use one `<isreplace/>` per decorator for clarity and maintainability
 807 | 2. **Semantic Placement**: Position `<isreplace/>` within semantic HTML elements (`<main>`, `<section>`, etc.)
 808 | 3. **CSS Class Structure**: Wrap `<isreplace/>` in containers with appropriate CSS classes for styling
 809 | 4. **Documentation**: Comment complex decorator patterns to explain the layout structure
 810 | 
 811 | #### Common Patterns
 812 | 
 813 | **Standard Page Layout:**
 814 | ```html
 815 | <isdecorate template="common/layout/page">
 816 |     <!-- Page-specific content goes here -->
 817 | </isdecorate>
 818 | ```
 819 | 
 820 | **Modal/Dialog Layout:**
 821 | ```html
 822 | <isdecorate template="common/layout/modal">
 823 |     <!-- Modal content goes here -->
 824 | </isdecorate>
 825 | ```
 826 | 
 827 | **Email Template Layout:**
 828 | ```html
 829 | <isdecorate template="common/layout/email">
 830 |     <!-- Email content goes here -->
 831 | </isdecorate>
 832 | ```
 833 | 
 834 | ## SFRA Base Templates Architecture
 835 | 
 836 | Understanding SFRA's base template structure is crucial for effective storefront development. SFRA provides a well-organized hierarchy of layout templates, page-specific templates, and reusable components that work together to create consistent user experiences.
 837 | 
 838 | **IMPORTANT**: SFRA uses **Bootstrap 4** as its foundational CSS framework, providing responsive grid systems, utility classes, and component styling throughout all templates. Understanding Bootstrap classes and responsive breakpoints is essential for effective SFRA development.
 839 | 
 840 | ### Core Layout Templates
 841 | 
 842 | SFRA uses a **three-tier layout system** with base layouts that define the overall page structure:
 843 | 
 844 | #### 1. Main Page Layout (`common/layout/page.isml`)
 845 | 
 846 | The primary layout for most storefront pages including homepage, product details, search results, and category pages:
 847 | 
 848 | ```html
 849 | <iscontent type="text/html" charset="UTF-8" compact="true"/>
 850 | <isinclude template="/components/modules" sf-toolkit="off" />
 851 | 
 852 | <!DOCTYPE html>
 853 | <html lang="${require('dw/util/Locale').getLocale(request.getLocale()).getLanguage()}">
 854 |     <head>
 855 |         <!--[if gt IE 9]><!-->
 856 |             <isinclude sf-toolkit="off" template="/common/scripts" />
 857 |         <!--<![endif]-->
 858 |         <isinclude template="/common/htmlHead" />
 859 |         <isif condition="${pdict.canonicalUrl}" >
 860 |             <link rel="canonical" href="${pdict.canonicalUrl}"/>
 861 |         </isif>
 862 |         <isactivedatahead/>
 863 |         <isinclude template="/components/schema" />
 864 |     </head>
 865 |     <body>
 866 |         ${dw.system.HookMgr.callHook('app.template.beforeHeader', 'beforeHeader', pdict) || ''}
 867 | 
 868 |         <div class="page" data-action="${pdict.action}" data-querystring="${pdict.queryString}" >
 869 |             <isinclude template="/components/header/pageHeader" />
 870 |             <div role="main" id="maincontent">
 871 |                 <isreplace/>
 872 |             </div>
 873 |             <isinclude template="/components/footer/pageFooter" />
 874 |         </div>
 875 |         <div class="error-messaging"></div>
 876 |         <div class="modal-background"></div>
 877 |         <iscontentasset aid="cookie_hint" />
 878 |          <!--[if lt IE 10]>
 879 |             <isinclude sf-toolkit="off" template="/common/scripts" />
 880 |         <![endif]-->
 881 |         <iscomment>
 882 |             hook for Marketing Cloud connector & other integration which need to inject
 883 |             logic at the page end
 884 |             IMPORTANT: Note that this hook will be called to cached as well as uncached pages
 885 |                         which means you need to put privacy information into another remote include
 886 |         </iscomment>
 887 |         ${dw.system.HookMgr.callHook('app.template.afterFooter', 'afterFooter', pdict) || ''}
 888 |         <isinclude url="${URLUtils.url('ConsentTracking-Check')}"/>
 889 |     </body>
 890 | </html>
 891 | ```
 892 | 
 893 | **Key Features:**
 894 | - **Full navigation header**: Includes main menu, search, and account links via `pageHeader`
 895 | - **Hook integration**: `beforeHeader` and `afterFooter` hooks for customization
 896 | - **SEO elements**: Canonical URLs, structured data, meta tags
 897 | - **Accessibility**: Proper ARIA roles and semantic HTML
 898 | - **Analytics support**: ActiveData integration
 899 | - **Error handling**: Error messaging and modal background containers
 900 | 
 901 | #### 2. Checkout Layout (`common/layout/checkout.isml`)
 902 | 
 903 | Complete layout for checkout process with minimal navigation header:
 904 | 
 905 | ```html
 906 | <iscontent type="text/html" charset="UTF-8" compact="true"/>
 907 | <isinclude template="/components/modules" sf-toolkit="off" />
 908 | 
 909 | <!DOCTYPE html>
 910 | <html lang="${require('dw/util/Locale').getLocale(request.getLocale()).getLanguage()}">
 911 |     <head>
 912 |         <!--[if gt IE 9]><!-->
 913 |             <isinclude sf-toolkit="off" template="/common/scripts" />
 914 |         <!--<![endif]-->
 915 |         <isinclude template="/common/htmlHead" />
 916 |         <isactivedatahead/>
 917 |     </head>
 918 |     <body>
 919 |         ${dw.system.HookMgr.callHook('app.template.beforeHeader', 'beforeHeader', pdict) || ''}
 920 | 
 921 |         <div class="page">
 922 |             <isinclude template="/components/header/pageHeaderNomenu" />
 923 |             <div role="main" id="maincontent">
 924 |                 <isreplace/>
 925 |             </div>
 926 |             <isinclude template="/components/footer/pageFooter" />
 927 |         </div>
 928 |         <!--[if lt IE 10]>
 929 |             <isinclude sf-toolkit="off" template="/common/scripts" />
 930 |         <![endif]-->
 931 | 
 932 |         <iscomment>
 933 |             hook for Marketing Cloud connector & other integration which need to inject
 934 |             logic at the page end
 935 |             IMPORTANT: Note that this hook will be called to cached as well as uncached pages
 936 |                         which means you need to put privacy information into another remote include
 937 |         </iscomment>
 938 |         ${dw.system.HookMgr.callHook('app.template.afterFooter', 'afterFooter', pdict) || ''}
 939 | 
 940 |         <isinclude url="${URLUtils.url('ConsentTracking-Check')}"/>
 941 |     </body>
 942 | </html>
 943 | ```
 944 | 
 945 | **Key Features:**
 946 | - **Simplified header**: `pageHeaderNomenu` without main navigation menu
 947 | - **Secure context**: HTTPS enforcement and minimal external dependencies
 948 | - **Same structure as main layout**: Maintains consistency while removing distractions
 949 | - **Hook support**: Full hook integration for customization
 950 | - **Accessibility**: Maintains ARIA roles and semantic structure
 951 | 
 952 | #### 3. Page Designer Store Layout (`common/layout/pdStorePage.isml`)
 953 | 
 954 | Specialized layout for Page Designer store pages with campaign banner support:
 955 | 
 956 | ```html
 957 | <iscontent type="text/html" charset="UTF-8" compact="true"/>
 958 | <isinclude template="/components/modules" sf-toolkit="off" />
 959 | 
 960 | <!-- Include Page Designer Campaign Banner JavaScript and Styles only once here rather than at component level. -->
 961 | <!-- There should only be one Campagin Banner added on a PD page. Multiple Banners is unsupported at the moment. -->
 962 | 
 963 | <isif condition="${pdict.resetEditPDMode}">
 964 |     <script> var resetCampaignBannerSessionToken = true; </script>
 965 | </isif>
 966 | 
 967 | <isscript>
 968 |     var assets = require('*/cartridge/scripts/assets.js');
 969 |     assets.addCss('/css/experience/components/commerceAssets/campaignBanner.css');
 970 |     assets.addJs('/js/campaignBanner.js');
 971 | </isscript>
 972 | 
 973 | <!DOCTYPE html>
 974 | <html lang="${require('dw/util/Locale').getLocale(request.getLocale()).getLanguage()}">
 975 |     <head>
 976 |         <!--[if gt IE 9]><!-->
 977 |             <isinclude sf-toolkit="off" template="/common/scripts" />
 978 |         <!--<![endif]-->
 979 |         <isinclude template="/common/htmlHead" />
 980 |         <isif condition="${pdict.canonicalUrl}" >
 981 |             <link rel="canonical" href="${pdict.canonicalUrl}"/>
 982 |         </isif>
 983 |         <isactivedatahead/>
 984 |         <isinclude template="/components/schema" />
 985 |     </head>
 986 |     <body>
 987 |         ${dw.system.HookMgr.callHook('app.template.beforeHeader', 'beforeHeader', pdict) || ''}
 988 | 
 989 |         <div class="page" data-action="${pdict.action}" data-querystring="${pdict.queryString}" >
 990 |             <isinclude template="/components/header/pdStorePageHeader" />
 991 |             <div role="main" id="maincontent">
 992 |                 <isreplace/>
 993 |             </div>
 994 |             <isinclude template="/components/footer/pageFooter" />
 995 |         </div>
 996 |         <div class="error-messaging"></div>
 997 |         <div class="modal-background"></div>
 998 |         <iscontentasset aid="cookie_hint" />
 999 |          <!--[if lt IE 10]>
1000 |             <isinclude sf-toolkit="off" template="/common/scripts" />
1001 |         <![endif]-->
1002 |         <iscomment>
1003 |             hook for Marketing Cloud connector & other integration which need to inject
1004 |             logic at the page end
1005 |             IMPORTANT: Note that this hook will be called to cached as well as uncached pages
1006 |                         which means you need to put privacy information into another remote include
1007 |         </iscomment>
1008 |         ${dw.system.HookMgr.callHook('app.template.afterFooter', 'afterFooter', pdict) || ''}
1009 |         <isinclude url="${URLUtils.url('ConsentTracking-Check')}"/>
1010 |     </body>
1011 | </html>
1012 | ```
1013 | 
1014 | **Key Features:**
1015 | - **Page Designer header**: Uses `pdStorePageHeader` for Page Designer-specific navigation
1016 | - **Campaign banner support**: Includes campaign banner CSS and JavaScript assets
1017 | - **Edit mode support**: Handles Page Designer edit mode with session token reset
1018 | - **Same core structure**: Maintains consistency with main layout
1019 | - **SEO and analytics**: Full SEO and analytics support
1020 | 
1021 | ### SFRA Page Template Patterns
1022 | 
1023 | #### Homepage Template Structure
1024 | 
1025 | ```html
1026 | <isdecorate template="common/layout/page">
1027 |     <isscript>
1028 |         var assets = require('*/cartridge/scripts/assets.js');
1029 |         assets.addJs('/js/productTile.js');
1030 |         assets.addCss('/css/homePage.css');
1031 |     </isscript>
1032 | 
1033 |     <div class="home-main homepage">
1034 |         <isslot id="home-main-m" description="Main home page slot." context="global" />
1035 |     </div>
1036 | 
1037 |     <div class="container home-categories homepage">
1038 |         <div class="row home-main-categories no-gutters">
1039 |             <isslot id="home-categories-m" description="Categories slots on the home page." context="global" />
1040 |         </div>
1041 |     </div>
1042 | 
1043 |     <div class="container home-product-tiles homepage">
1044 |         <div class="hp-product-grid" itemtype="http://schema.org/SomeProducts" itemid="#product">
1045 |             <isslot id="home-products-m" description="Product tiles on the home page." context="global" />
1046 |         </div>
1047 |     </div>
1048 | </isdecorate>
1049 | ```
1050 | 
1051 | **Pattern Highlights:**
1052 | - **Content slots**: Uses Business Manager-configurable content slots
1053 | - **Asset management**: Page-specific CSS and JavaScript loading
1054 | - **SEO structure**: Schema.org markup for products
1055 | - **Responsive design**: Bootstrap grid system integration
1056 | 
1057 | #### Product Detail Page Structure
1058 | 
1059 | ```html
1060 | <isdecorate template="common/layout/page">
1061 |     <isscript>
1062 |         var assets = require('*/cartridge/scripts/assets');
1063 |         assets.addJs('/js/productDetail.js');
1064 |         assets.addCss('/css/product/detail.css');
1065 |     </isscript>
1066 | 
1067 |     <isset name="product" value="${pdict.product}" scope="page" />
1068 |     <isset name="isQuickView" value="${false}" scope="page" />
1069 |     <isset name="isProductSet" value="${pdict.product.productType === 'set'}" scope="page" />
1070 |     
1071 |     <isobject object="${product.raw}" view="detail">
1072 |         <div class="container product-detail product-wrapper" data-pid="${product.id}">
1073 |             <div class="row">
1074 |                 <div class="col-12">
1075 |                     <div class="product-breadcrumb d-md-none">
1076 |                         <isinclude template="components/breadcrumbs/pageBreadcrumbs"/>
1077 |                     </div>
1078 | 
1079 |                     <div class="row">
1080 |                         <div class="d-md-none col-sm-12">
1081 |                             <h1 class="product-name">${product.productName}</h1>
1082 |                         </div>
1083 |                     </div>
1084 |                 </div>
1085 |             </div>
1086 |             <!-- Product images, details, add to cart, etc. -->
1087 |         </div>
1088 |     </isobject>
1089 | </isdecorate>
1090 | ```
1091 | 
1092 | **Pattern Highlights:**
1093 | - **Product context**: `<isobject>` tag for product data binding
1094 | - **Variable scoping**: Page-scoped variables for template logic
1095 | - **Mobile-first design**: Responsive breakpoint handling
1096 | - **SEO optimization**: Structured product data and breadcrumbs
1097 | 
1098 | #### Cart Page Structure
1099 | 
1100 | ```html
1101 | <isdecorate template="common/layout/page">
1102 |     <isscript>
1103 |         var assets = require('*/cartridge/scripts/assets.js');
1104 |         assets.addCss('/css/cart.css');
1105 |     </isscript>
1106 | 
1107 |     <isif condition="${pdict.reportingURLs && pdict.reportingURLs.length}">
1108 |         <isinclude template="reporting/reportingUrls" />
1109 |     </isif>
1110 | 
1111 |     <div class="cart-error-messaging cart-error">
1112 |         <isif condition="${pdict.valid.error && pdict.items.length !== 0}">
1113 |             <div class="alert alert-danger alert-dismissible valid-cart-error fade show" role="alert">
1114 |                 <button type="button" class="close" data-dismiss="alert" aria-label="Close">
1115 |                     <span aria-hidden="true">&times;</span>
1116 |                 </button>
1117 |                 ${pdict.valid.message}
1118 |             </div>
1119 |         </isif>
1120 |     </div>
1121 | 
1122 |     <div class="container">
1123 |         <h1 class="page-title">${Resource.msg('title.cart','cart',null)}</h1>
1124 |         <div class="row cart-header">
1125 |             <div class="col-sm-4 hidden-xs-down">
1126 |                 <a class="continue-shopping-link" href="${URLUtils.url('Home-Show')}" title="${Resource.msg('link.continue.shopping','cart',null)}">
1127 |                     ${Resource.msg('link.continue.shopping','cart',null)}
1128 |                 </a>
1129 |             </div>
1130 |             <div class="col-sm-3 text-center">
1131 |                 <h2 class="number-of-items">${Resource.msgf('label.number.items.in.cart','cart', null, pdict.numItems)}</h2>
1132 |             </div>
1133 |             <div class="col-sm-5 text-right hidden-xs-down">
1134 |                 <div>
1135 |                     <span>${Resource.msg('info.need.help','cart',null)}</span>
1136 |                     <span><a class="help-phone-number" href="tel:${Resource.msg('info.phone.number','common',null)}">${Resource.msg('info.phone.number','common',null)}</a></span>
1137 |                 </div>
1138 |             </div>
1139 |         </div>
1140 |         <hr class="no-margin-top">
1141 |     </div>
1142 | 
1143 |     <isif condition="${pdict.items.length === 0}">
1144 |         <div class="container cart-empty">
1145 |             <div class="row">
1146 |                 <div class="col-12 text-center">
1147 |                     <h1>${Resource.msg('info.cart.empty.msg','cart',null)}</h1>
1148 |                 </div>
1149 |             </div>
1150 |         </div>
1151 |     <iselse/>
1152 |         <div class="container cart cart-page">
1153 |             <div class="row">
1154 |                 <!---product cards--->
1155 |                 <div class="col-sm-7 col-md-8">
1156 |                     <isloop items="${pdict.items}" var="lineItem">
1157 |                         <isif condition="${lineItem.productType === 'bundle'}">
1158 |                             <isinclude template="cart/productCard/cartBundleCard" />
1159 |                         <iselse/>
1160 |                             <isif condition="${lineItem.noProduct === true}">
1161 |                                 <isinclude template="cart/productCard/uncategorizedCartProductCard" />
1162 |                             <iselse/>
1163 |                                 <isinclude template="cart/productCard/cartProductCard" />
1164 |                             </isif>
1165 |                         </isif>
1166 |                     </isloop>
1167 |                     <isinclude template="cart/cartApproachingDiscount" />
1168 |                 </div>
1169 |                 <!---totals, and checkout actions--->
1170 |                 <div class="col-sm-5 col-md-4 totals">
1171 |                     <isinclude template="cart/cartPromoCode" />
1172 |                     <div class="coupons-and-promos">
1173 |                         <isinclude template="cart/cartCouponDisplay" />
1174 |                     </div>
1175 |                     <div class="row">
1176 |                         <isinclude template="cart/cartShippingMethodSelection" />
1177 |                     </div>
1178 |                     <isinclude template="cart/cartTotals" />
1179 |                     <div class="row">
1180 |                         <div class="col-12 checkout-continue">
1181 |                             <isinclude template="cart/checkoutButtons" />
1182 |                         </div>
1183 |                     </div>
1184 |                 </div>
1185 |             </div>
1186 |             <isinclude template="cart/cartRemoveProductModal"/>
1187 |         </div>
1188 | 
1189 |         <isinclude template="cart/cartRemoveCouponModal"/>
1190 |     </isif>
1191 |     <div class="container">
1192 |         <isslot id="cart-recommendations-m" description="Recommended products" context="global" />
1193 |     </div>
1194 | </isdecorate>
1195 | ```
1196 | 
1197 | **Pattern Highlights:**
1198 | - **Error handling**: Comprehensive validation error display with dismissible alerts
1199 | - **Analytics integration**: Reporting URL includes for tracking
1200 | - **User feedback**: Alert system for cart validation messages
1201 | - **Empty cart state**: Dedicated empty cart display with clear messaging
1202 | - **Product cards**: Dynamic product card rendering based on product type (bundle, regular, uncategorized)
1203 | - **Responsive layout**: Bootstrap grid system for desktop and mobile layouts
1204 | - **Promotional features**: Coupon display, promo codes, and approaching discount messaging
1205 | - **Checkout integration**: Seamless transition to checkout with proper button placement
1206 | - **Recommendations**: Content slot for product recommendations
1207 | - **Modal support**: Product removal and coupon removal modals
1208 | 
1209 | #### Account Page Structure
1210 | 
1211 | ```html
1212 | <isdecorate template="common/layout/page">
1213 |     <isscript>
1214 |         var assets = require('*/cartridge/scripts/assets.js');
1215 |         assets.addJs('/js/account.js');
1216 |         assets.addCss('/css/account.css');
1217 |     </isscript>
1218 | 
1219 |     <div class="hero slant-down account-hero">
1220 |         <div class="container">
1221 |             <h1 class="page-title">${Resource.msg('page.heading.myaccount','account',null)}</h1>
1222 |         </div>
1223 |     </div>
1224 | 
1225 |     <div class="container">
1226 |         <div class="row justify-content-center">
1227 |             <div class="col">
1228 |                 <div class="myaccount-row">
1229 |                     <!-- Account Navigation -->
1230 |                     <div class="col-sm-3">
1231 |                         <div class="account-nav">
1232 |                             <isinclude template="account/accountNav" />
1233 |                         </div>
1234 |                     </div>
1235 |                     
1236 |                     <!-- Account Content -->
1237 |                     <div class="col-sm-9">
1238 |                         <div class="card">
1239 |                             <div class="card-header">
1240 |                                 <h2 class="pull-left">${pdict.pageTitle}</h2>
1241 |                             </div>
1242 |                             <div class="card-body card-body-positioning">
1243 |                                 <!-- Page-specific content will be inserted here -->
1244 |                                 <isif condition="${pdict.addressBook}">
1245 |                                     <isinclude template="account/addressBook" />
1246 |                                 <iselseif condition="${pdict.orderHistory}">
1247 |                                     <isinclude template="account/orderHistory" />
1248 |                                 <iselseif condition="${pdict.profile}">
1249 |                                     <isinclude template="account/profile" />
1250 |                                 <iselseif condition="${pdict.paymentInstruments}">
1251 |                                     <isinclude template="account/payment/paymentMethods" />
1252 |                                 <iselse>
1253 |                                     <isinclude template="account/dashboard" />
1254 |                                 </isif>
1255 |                             </div>
1256 |                         </div>
1257 |                     </div>
1258 |                 </div>
1259 |             </div>
1260 |         </div>
1261 |     </div>
1262 | </isdecorate>
1263 | ```
1264 | 
1265 | **Pattern Highlights:**
1266 | - **Account navigation**: Consistent sidebar navigation across all account pages
1267 | - **Card-based layout**: Professional UI with card components
1268 | - **Conditional content**: Dynamic content loading based on page type
1269 | - **Responsive design**: Mobile-friendly account management interface
1270 | 
1271 | #### Search Results Page Structure
1272 | 
1273 | ```html
1274 | <isdecorate template="common/layout/page">
1275 |     <isscript>
1276 |         var assets = require('*/cartridge/scripts/assets.js');
1277 |         assets.addJs('/js/search.js');
1278 |         assets.addCss('/css/search.css');
1279 |     </isscript>
1280 | 
1281 |     <div class="search-results-header">
1282 |         <div class="container">
1283 |             <div class="row">
1284 |                 <div class="col-sm-12">
1285 |                     <h1 class="header page-title">
1286 |                         <isif condition="${pdict.productSearch.isCategorySearch}">
1287 |                             ${pdict.productSearch.category.name}
1288 |                         <iselse>
1289 |                             ${Resource.msgf('heading.search.results', 'search', null, pdict.productSearch.searchKeywords)}
1290 |                         </isif>
1291 |                     </h1>
1292 |                     
1293 |                     <!-- Search Result Count -->
1294 |                     <div class="result-count pull-left">
1295 |                         <isinclude template="search/searchResultsCount"/>
1296 |                     </div>
1297 |                 </div>
1298 |             </div>
1299 |         </div>
1300 |     </div>
1301 | 
1302 |     <div class="container search-results">
1303 |         <div class="row search-nav">
1304 |             <div class="tab-content col-12">
1305 |                 <div class="tab-pane container active" id="product-search-results">
1306 |                     <div class="row grid-header">
1307 |                         <div class="result-count col-6 col-md-9 order-2 order-md-1">
1308 |                             <isinclude template="search/searchResultsCount"/>
1309 |                         </div>
1310 |                         <div class="col-6 col-md-3 order-1 order-md-2">
1311 |                             <isinclude template="search/sortOrderMenu"/>
1312 |                         </div>
1313 |                     </div>
1314 |                     
1315 |                     <!-- Refinements and Product Grid -->
1316 |                     <div class="row">
1317 |                         <!-- Refinements Sidebar -->
1318 |                         <div class="refinement-bar col-md-3">
1319 |                             <isinclude template="search/refinements"/>
1320 |                         </div>
1321 |                         
1322 |                         <!-- Product Grid -->
1323 |                         <div class="col-sm-12 col-md-9">
1324 |                             <div class="container">
1325 |                                 <isif condition="${pdict.productSearch.productHits.length > 0}">
1326 |                                     <!-- Product Grid -->
1327 |                                     <div class="row product-grid" itemtype="http://schema.org/SomeProducts" itemid="#product">
1328 |                                         <isloop items="${pdict.productSearch.productHits}" var="productHit">
1329 |                                             <div class="col-6 col-sm-4 product-tile-wrapper">
1330 |                                                 <isset name="product" value="${productHit.product}" scope="page" />
1331 |                                                 <div class="product-tile" data-pid="${product.id}" data-gtm-product='${JSON.stringify(product.gtm)}'>
1332 |                                                     <isinclude template="product/productTile"/>
1333 |                                                 </div>
1334 |                                             </div>
1335 |                                         </isloop>
1336 |                                     </div>
1337 |                                     
1338 |                                     <!-- Pagination -->
1339 |                                     <div class="row">
1340 |                                         <isinclude template="search/searchResultsPagination"/>
1341 |                                     </div>
1342 |                                     
1343 |                                     <!-- Show More Products -->
1344 |                                     <div class="row">
1345 |                                         <div class="col-12 text-center">
1346 |                                             <div class="show-more btn btn-outline-primary" data-url="${pdict.productSearch.showMoreUrl}">
1347 |                                                 ${Resource.msg('button.search.showmore', 'search', null)}
1348 |                                             </div>
1349 |                                         </div>
1350 |                                     </div>
1351 |                                 <iselse>
1352 |                                     <!-- No Results -->
1353 |                                     <div class="row justify-content-center">
1354 |                                         <div class="col">
1355 |                                             <div class="no-results-message">
1356 |                                                 <h3>${Resource.msg('heading.no.results', 'search', null)}</h3>
1357 |                                                 <p>${Resource.msg('msg.no.results', 'search', null)}</p>
1358 |                                                 
1359 |                                                 <!-- Search Suggestions -->
1360 |                                                 <isif condition="${pdict.productSearch.searchSuggestions && pdict.productSearch.searchSuggestions.length > 0}">
1361 |                                                     <div class="search-suggestions">
1362 |                                                         <p>${Resource.msg('label.search.suggestions', 'search', null)}</p>
1363 |                                                         <ul class="suggestions-list">
1364 |                                                             <isloop items="${pdict.productSearch.searchSuggestions}" var="suggestion">
1365 |                                                                 <li><a href="${suggestion.url}">${suggestion.value}</a></li>
1366 |                                                             </isloop>
1367 |                                                         </ul>
1368 |                                                     </div>
1369 |                                                 </isif>
1370 |                                             </div>
1371 |                                         </div>
1372 |                                     </div>
1373 |                                 </isif>
1374 |                             </div>
1375 |                         </div>
1376 |                     </div>
1377 |                 </div>
1378 |             </div>
1379 |         </div>
1380 |     </div>
1381 | 
1382 |     <!-- Recently Viewed -->
1383 |     <div class="container">
1384 |         <isinclude template="search/components/recentlyViewed"/>
1385 |     </div>
1386 | </isdecorate>
1387 | ```
1388 | 
1389 | **Pattern Highlights:**
1390 | - **Dual search support**: Category browsing and keyword search functionality
1391 | - **Advanced filtering**: Refinement sidebar with faceted search
1392 | - **Responsive grid**: Product tiles with mobile-optimized layout
1393 | - **Pagination system**: Multiple pagination patterns including "show more"
1394 | - **No results handling**: Graceful degradation with search suggestions
1395 | 
1396 | #### Category Landing Page Structure
1397 | 
1398 | ```html
1399 | <isdecorate template="common/layout/page">
1400 |     <isscript>
1401 |         var assets = require('*/cartridge/scripts/assets.js');
1402 |         assets.addJs('/js/categoryLanding.js');
1403 |         assets.addCss('/css/categoryLanding.css');
1404 |     </isscript>
1405 | 
1406 |     <!-- Category Hero Section -->
1407 |     <div class="hero category-hero" 
1408 |          <isif condition="${pdict.category.image}">style="background-image: url('${pdict.category.image}')"</isif>>
1409 |         <div class="container">
1410 |             <div class="row">
1411 |                 <div class="col-12 text-center">
1412 |                     <h1 class="category-title">${pdict.category.displayName}</h1>
1413 |                     <isif condition="${pdict.category.description}">
1414 |                         <p class="category-description">${pdict.category.description}</p>
1415 |                     </isif>
1416 |                 </div>
1417 |             </div>
1418 |         </div>
1419 |     </div>
1420 | 
1421 |     <!-- Breadcrumbs -->
1422 |     <div class="container">
1423 |         <div class="row">
1424 |             <div class="col-12">
1425 |                 <isinclude template="components/breadcrumbs/pageBreadcrumbs"/>
1426 |             </div>
1427 |         </div>
1428 |     </div>
1429 | 
1430 |     <!-- Category Content Slots -->
1431 |     <isif condition="${pdict.category.template && pdict.category.template !== ''}">
1432 |         <!-- Custom Category Template -->
1433 |         <isinclude template="${pdict.category.template}"/>
1434 |     <iselse>
1435 |         <!-- Default Category Content -->
1436 |         <div class="container category-content">
1437 |             <!-- Category Slots -->
1438 |             <div class="category-slots">
1439 |                 <isslot id="category-banner" description="Category banner content" context="category" context-object="${pdict.category.raw}" />
1440 |             </div>
1441 |             
1442 |             <!-- Sub-categories -->
1443 |             <isif condition="${pdict.category.subCategories && pdict.category.subCategories.length > 0}">
1444 |                 <div class="sub-categories">
1445 |                     <div class="row">
1446 |                         <div class="col-12">
1447 |                             <h2 class="sub-categories-title">${Resource.msg('heading.browse.categories', 'search', null)}</h2>
1448 |                         </div>
1449 |                     </div>
1450 |                     <div class="row">
1451 |                         <isloop items="${pdict.category.subCategories}" var="subCategory">
1452 |                             <div class="col-6 col-md-4 col-lg-3">
1453 |                                 <div class="category-tile">
1454 |                                     <a href="${subCategory.url}" class="category-link">
1455 |                                         <isif condition="${subCategory.image}">
1456 |                                             <div class="category-image">
1457 |                                                 <img src="${subCategory.image}" 
1458 |                                                      alt="${subCategory.displayName}" 
1459 |                                                      class="img-fluid">
1460 |                                             </div>
1461 |                                         </isif>
1462 |                                         <div class="category-info">
1463 |                                             <h3 class="category-name">${subCategory.displayName}</h3>
1464 |                                             <isif condition="${subCategory.productCount}">
1465 |                                                 <span class="product-count">
1466 |                                                     ${Resource.msgf('label.category.product.count', 'search', null, subCategory.productCount)}
1467 |                                                 </span>
1468 |                                             </isif>
1469 |                                         </div>
1470 |                                     </a>
1471 |                                 </div>
1472 |                             </div>
1473 |                         </isloop>
1474 |                     </div>
1475 |                 </div>
1476 |             </isif>
1477 |             
1478 |             <!-- Featured Products -->
1479 |             <isif condition="${pdict.productSearch && pdict.productSearch.productHits.length > 0}">
1480 |                 <div class="featured-products">
1481 |                     <div class="row">
1482 |                         <div class="col-12">
1483 |                             <h2 class="featured-title">${Resource.msg('heading.featured.products', 'search', null)}</h2>
1484 |                         </div>
1485 |                     </div>
1486 |                     <div class="row product-grid">
1487 |                         <isloop items="${pdict.productSearch.productHits}" var="productHit" begin="0" end="7">
1488 |                             <div class="col-6 col-md-4 col-lg-3">
1489 |                                 <isset name="product" value="${productHit.product}" scope="page" />
1490 |                                 <isinclude template="product/productTile"/>
1491 |                             </div>
1492 |                         </isloop>
1493 |                     </div>
1494 |                     <div class="row">
1495 |                         <div class="col-12 text-center">
1496 |                             <a href="${pdict.category.url}?showAll=true" class="btn btn-primary">
1497 |                                 ${Resource.msg('button.view.all.products', 'search', null)}
1498 |                             </a>
1499 |                         </div>
1500 |                     </div>
1501 |                 </div>
1502 |             </isif>
1503 |             
1504 |             <!-- Category Content Slots -->
1505 |             <div class="category-content-slots">
1506 |                 <isslot id="category-content" description="Category content area" context="category" context-object="${pdict.category.raw}" />
1507 |             </div>
1508 |         </div>
1509 |     </isif>
1510 | </isdecorate>
1511 | ```
1512 | 
1513 | **Pattern Highlights:**
1514 | - **Hero section**: Visual category introduction with background imagery
1515 | - **Content slots**: Business Manager-configurable category content
1516 | - **Sub-category navigation**: Hierarchical category browsing
1517 | - **Featured products**: Limited product showcase with view-all option
1518 | - **Flexible templating**: Support for custom category templates
1519 | 
1520 | #### Login/Registration Page Structure
1521 | 
1522 | ```html
1523 | <isdecorate template="common/layout/page">
1524 |     <isscript>
1525 |         var assets = require('*/cartridge/scripts/assets.js');
1526 |         assets.addJs('/js/login.js');
1527 |         assets.addCss('/css/login.css');
1528 |     </isscript>
1529 | 
1530 |     <div class="hero slant-down login-banner">
1531 |         <h1 class="page-title">${Resource.msg('title.login.page', 'login', null)}</h1>
1532 |     </div>
1533 | 
1534 |     <div class="container login-page">
1535 |         <div class="row justify-content-center">
1536 |             <!-- Login Form -->
1537 |             <div class="col-md-6">
1538 |                 <div class="card login-form-nav">
1539 |                     <div class="card-header">
1540 |                         <h2>${Resource.msg('heading.returning.customers', 'login', null)}</h2>
1541 |                     </div>
1542 |                     
1543 |                     <div class="card-body">
1544 |                         <form action="${URLUtils.url('Account-Login')}" class="login" method="post" name="login-form">
1545 |                             <input type="hidden" name="${pdict.csrf.tokenName}" value="${pdict.csrf.token}"/>
1546 |                             
1547 |                             <!-- Email Field -->
1548 |                             <div class="form-group 
1549 |                                 <isif condition="${pdict.loginForm.username.invalid}">is-invalid</isif>">
1550 |                                 <label class="form-control-label" for="login-form-email">
1551 |                                     <isprint value="${pdict.loginForm.username.label}" encoding="htmlcontent" />
1552 |                                 </label>
1553 |                                 <input type="email" 
1554 |                                        required 
1555 |                                        class="form-control" 
1556 |                                        id="login-form-email"
1557 |                                        name="${pdict.loginForm.username.htmlName}" 
1558 |                                        value="${pdict.loginForm.username.value}"
1559 |                                        autocomplete="email">
1560 |                                 <isif condition="${pdict.loginForm.username.invalid}">
1561 |                                     <div class="invalid-feedback">
1562 |                                         <isprint value="${pdict.loginForm.username.error}" />
1563 |                                     </div>
1564 |                                 </isif>
1565 |                             </div>
1566 |                             
1567 |                             <!-- Password Field -->
1568 |                             <div class="form-group 
1569 |                                 <isif condition="${pdict.loginForm.password.invalid}">is-invalid</isif>">
1570 |                                 <label class="form-control-label" for="login-form-password">
1571 |                                     <isprint value="${pdict.loginForm.password.label}" encoding="htmlcontent" />
1572 |                                 </label>
1573 |                                 <input type="password" 
1574 |                                        required 
1575 |                                        class="form-control" 
1576 |                                        id="login-form-password"
1577 |                                        name="${pdict.loginForm.password.htmlName}" 
1578 |                                        autocomplete="current-password">
1579 |                                 <isif condition="${pdict.loginForm.password.invalid}">
1580 |                                     <div class="invalid-feedback">
1581 |                                         <isprint value="${pdict.loginForm.password.error}" />
1582 |                                     </div>
1583 |                                 </isif>
1584 |                             </div>
1585 |                             
1586 |                             <!-- Remember Me -->
1587 |                             <div class="form-group custom-control custom-checkbox">
1588 |                                 <input type="checkbox" 
1589 |                                        class="custom-control-input" 
1590 |                                        id="rememberMe" 
1591 |                                        name="${pdict.loginForm.rememberMe.htmlName}"
1592 |                                        value="true"
1593 |                                        <isif condition="${pdict.loginForm.rememberMe.checked}">checked</isif>>
1594 |                                 <label class="custom-control-label" for="rememberMe">
1595 |                                     ${Resource.msg('field.login.remember.me', 'login', null)}
1596 |                                 </label>
1597 |                             </div>
1598 |                             
1599 |                             <!-- Login Button -->
1600 |                             <button type="submit" class="btn btn-primary btn-block login-btn">
1601 |                                 ${Resource.msg('button.text.loginform', 'login', null)}
1602 |                             </button>
1603 |                             
1604 |                             <!-- Forgot Password -->
1605 |                             <div class="forgot-password text-center">
1606 |                                 <a href="${URLUtils.url('Account-PasswordReset')}" title="${Resource.msg('link.login.forgotpassword', 'login', null)}">
1607 |                                     ${Resource.msg('link.login.forgotpassword', 'login', null)}
1608 |                                 </a>
1609 |                             </div>
1610 |                         </form>
1611 |                     </div>
1612 |                 </div>
1613 |             </div>
1614 |             
1615 |             <!-- Registration Form -->
1616 |             <div class="col-md-6">
1617 |                 <div class="card registration-form-nav">
1618 |                     <div class="card-header">
1619 |                         <h2>${Resource.msg('heading.new.customers', 'login', null)}</h2>
1620 |                     </div>
1621 |                     
1622 |                     <div class="card-body">
1623 |                         <p>${Resource.msg('msg.create.account', 'login', null)}</p>
1624 |                         
1625 |                         <form action="${URLUtils.url('Account-SubmitRegistration')}" 
1626 |                               class="registration" 
1627 |                               method="post" 
1628 |                               name="registration-form">
1629 |                             <input type="hidden" name="${pdict.csrf.tokenName}" value="${pdict.csrf.token}"/>
1630 |                             
1631 |                             <!-- First Name -->
1632 |                             <div class="form-group 
1633 |                                 <isif condition="${pdict.registrationForm.customer.firstname.invalid}">is-invalid</isif>">
1634 |                                 <label class="form-control-label" for="registration-form-fname">
1635 |                                     <isprint value="${pdict.registrationForm.customer.firstname.label}" encoding="htmlcontent" />
1636 |                                 </label>
1637 |                                 <input type="text" 
1638 |                                        required 
1639 |                                        class="form-control" 
1640 |                                        id="registration-form-fname"
1641 |                                        name="${pdict.registrationForm.customer.firstname.htmlName}"
1642 |                                        value="${pdict.registrationForm.customer.firstname.value}"
1643 |                                        autocomplete="given-name">
1644 |                                 <isif condition="${pdict.registrationForm.customer.firstname.invalid}">
1645 |                                     <div class="invalid-feedback">
1646 |                                         <isprint value="${pdict.registrationForm.customer.firstname.error}" />
1647 |                                     </div>
1648 |                                 </isif>
1649 |                             </div>
1650 | 
1651 |                             <!-- Last Name -->
1652 |                             <div class="form-group
1653 |                                 <isif condition="${pdict.registrationForm.customer.lastname.invalid}">is-invalid</isif>">
1654 |                                 <label class="form-control-label" for="registration-form-lname">
1655 |                                     <isprint value="${pdict.registrationForm.customer.lastname.label}" encoding="htmlcontent" />
1656 |                                 </label>
1657 |                                 <input type="text"
1658 |                                        required
1659 |                                        class="form-control"
1660 |                                        id="registration-form-lname"
1661 |                                        name="${pdict.registrationForm.customer.lastname.htmlName}"
1662 |                                        value="${pdict.registrationForm.customer.lastname.value}"
1663 |                                        autocomplete="family-name">
1664 |                                 <isif condition="${pdict.registrationForm.customer.lastname.invalid}">
1665 |                                     <div class="invalid-feedback">
1666 |                                         <isprint value="${pdict.registrationForm.customer.lastname.error}" />
1667 |                                     </div>
1668 |                                 </isif>
1669 |                             </div>
1670 | 
1671 |                             <!-- Email -->
1672 |                             <div class="form-group
1673 |                                 <isif condition="${pdict.registrationForm.customer.email.invalid}">is-invalid</isif>">
1674 |                                 <label class="form-control-label" for="registration-form-email">
1675 |                                     <isprint value="${pdict.registrationForm.customer.email.label}" encoding="htmlcontent" />
1676 |                                 </label>
1677 |                                 <input type="email"
1678 |                                        required
1679 |                                        class="form-control"
1680 |                                        id="registration-form-email"
1681 |                                        name="${pdict.registrationForm.customer.email.htmlName}"
1682 |                                        value="${pdict.registrationForm.customer.email.value}"
1683 |                                        autocomplete="email">
1684 |                                 <isif condition="${pdict.registrationForm.customer.email.invalid}">
1685 |                                     <div class="invalid-feedback">
1686 |                                         <isprint value="${pdict.registrationForm.customer.email.error}" />
1687 |                                     </div>
1688 |                                 </isif>
1689 |                             </div>
1690 | 
1691 |                             <!-- Password -->
1692 |                             <div class="form-group
1693 |                                 <isif condition="${pdict.registrationForm.newPasswords.newpassword.invalid}">is-invalid</isif>">
1694 |                                 <label class="form-control-label" for="registration-form-password">
1695 |                                     <isprint value="${pdict.registrationForm.newPasswords.newpassword.label}" encoding="htmlcontent" />
1696 |                                 </label>
1697 |                                 <input type="password"
1698 |                                        required
1699 |                                        class="form-control"
1700 |                                        id="registration-form-password"
1701 |                                        name="${pdict.registrationForm.newPasswords.newpassword.htmlName}"
1702 |                                        autocomplete="new-password">
1703 |                                 <isif condition="${pdict.registrationForm.newPasswords.newpassword.invalid}">
1704 |                                     <div class="invalid-feedback">
1705 |                                         <isprint value="${pdict.registrationForm.newPasswords.newpassword.error}" />
1706 |                                     </div>
1707 |                                 </isif>
1708 |                             </div>
1709 | 
1710 |                             <!-- Confirm Password -->
1711 |                             <div class="form-group
1712 |                                 <isif condition="${pdict.registrationForm.newPasswords.newpasswordconfirm.invalid}">is-invalid</isif>">
1713 |                                 <label class="form-control-label" for="registration-form-password-confirm">
1714 |                                     <isprint value="${pdict.registrationForm.newPasswords.newpasswordconfirm.label}" encoding="htmlcontent" />
1715 |                                 </label>
1716 |                                 <input type="password"
1717 |                                        required
1718 |                                        class="form-control"
1719 |                                        id="registration-form-password-confirm"
1720 |                                        name="${pdict.registrationForm.newPasswords.newpasswordconfirm.htmlName}"
1721 |                                        autocomplete="new-password">
1722 |                                 <isif condition="${pdict.registrationForm.newPasswords.newpasswordconfirm.invalid}">
1723 |                                     <div class="invalid-feedback">
1724 |                                         <isprint value="${pdict.registrationForm.newPasswords.newpasswordconfirm.error}" />
1725 |                                     </div>
1726 |                                 </isif>
1727 |                             </div>
1728 | 
1729 |                             <!-- Privacy Policy -->
1730 |                             <div class="form-group custom-control custom-checkbox">
1731 |                                 <input type="checkbox"
1732 |                                        required
1733 |                                        class="custom-control-input"
1734 |                                        id="add-to-email-list"
1735 |                                        name="${pdict.registrationForm.customer.addtoemaillist.htmlName}"
1736 |                                        value="true">
1737 |                                 <label class="custom-control-label" for="add-to-email-list">
1738 |                                     ${Resource.msg('label.registration.email.subscribe', 'login', null)}
1739 |                                 </label>
1740 |                             </div>
1741 | 
1742 |                             <!-- Create Account Button -->
1743 |                             <button type="submit" class="btn btn-primary btn-block create-account-btn">
1744 |                                 ${Resource.msg('button.createaccount.registration', 'login', null)}
1745 |                             </button>
1746 |                         </form>
1747 |                     </div>
1748 |                 </div>
1749 |             </div>
1750 |         </div>
1751 |     </div>
1752 | </isdecorate>
1753 | ```
1754 | 
1755 | **Pattern Highlights:**
1756 | - **Dual-form layout**: Login and registration side-by-side
1757 | - **Form validation**: Client and server-side validation with error display
1758 | - **Accessibility features**: Proper form labels, autocomplete attributes
1759 | - **Security elements**: CSRF protection and password requirements
1760 | - **Mobile responsiveness**: Stacked layout on smaller screens
1761 | 
1762 | ## Common Components Architecture
1763 | 
1764 | SFRA organizes reusable components in the `/components/` directory:
1765 | 
1766 | #### HTML Head Component (`common/htmlHead.isml`)
1767 | 
1768 | ```html
1769 | <meta charset=UTF-8>
1770 | <meta http-equiv="x-ua-compatible" content="ie=edge">
1771 | <meta name="viewport" content="width=device-width, initial-scale=1">
1772 | 
1773 | <isif condition="${dw.system.System.getInstanceType() != dw.system.System.PRODUCTION_SYSTEM}">
1774 |     <title>${pdict.CurrentPageMetaData.title} | ${Resource.msg('global.site.name', 'version', null)} | ${Resource.msg('global.version.number', 'version', null)}</title>
1775 | <iselse/>
1776 |     <title><isprint value="${pdict.CurrentPageMetaData.title}" encoding="htmlcontent" /></title>
1777 | </isif>
1778 | 
1779 | <meta name="description" content="${pdict.CurrentPageMetaData.description ? pdict.CurrentPageMetaData.description : Resource.msg('global.storename','common',null)}"/>
1780 | <meta name="keywords" content="${pdict.CurrentPageMetaData.keywords ? pdict.CurrentPageMetaData.keywords : Resource.msg('global.storename','common',null)}"/>
1781 | 
1782 | <!-- Rule-based page meta tags -->
1783 | <isloop items="${pdict.CurrentPageMetaData.pageMetaTags}" var="pageMetaTag">
1784 |     <isif condition="${pageMetaTag.name}">
1785 |         <meta name="<isprint value="${pageMetaTag.ID}">" content="<isprint value="${pageMetaTag.content}">">
1786 |     <iselseif condition="${pageMetaTag.property}">
1787 |         <meta property="<isprint value="${pageMetaTag.ID}">" content="<isprint value="${pageMetaTag.content}">">
1788 |     </isif>
1789 | </isloop>
1790 | 
1791 | <!-- Favicons -->
1792 | <link rel="icon" type="image/png" href="${URLUtils.staticURL('/images/favicons/favicon-196x196.png')}" sizes="196x196" />
1793 | <!-- Additional favicon sizes... -->
1794 | ```
1795 | 
1796 | **Component Features:**
1797 | - **Environment awareness**: Different titles for non-production environments
1798 | - **SEO management**: Dynamic meta tags from Page Designer or manual configuration
1799 | - **Multi-device support**: Comprehensive favicon set
1800 | - **Content security**: Proper encoding for meta content
1801 | 
1802 | ### SFRA Template Directory Structure
1803 | 
1804 | ```
1805 | templates/default/
1806 | ├── common/                    # Shared layout and utility templates
1807 | │   ├── layout/
1808 | │   │   ├── page.isml         # Main page layout
1809 | │   │   ├── checkout.isml     # Checkout layout
1810 | │   │   └── pdStorePage.isml  # Page Designer layout
1811 | │   ├── htmlHead.isml         # HTML head component
1812 | │   ├── scripts.isml          # JavaScript includes
1813 | │   └── consent.isml          # Cookie consent
1814 | ├── components/               # Reusable UI components
1815 | │   ├── header/              # Header variations
1816 | │   ├── footer/              # Footer components
1817 | │   ├── breadcrumbs/         # Navigation breadcrumbs
1818 | │   ├── modules.isml         # Module registration
1819 | │   └── schema.isml          # Structured data
1820 | ├── home/                    # Homepage templates
1821 | │   └── homePage.isml        # Main homepage
1822 | ├── product/                 # Product-related templates
1823 | │   ├── productD etails.isml  # PDP template
1824 | │   ├── productTile.isml     # Product tile component
1825 | │   └── quickView.isml       # Quick view modal
1826 | ├── cart/                    # Shopping cart templates
1827 | │   └── cart.isml           # Cart page
1828 | ├── checkout/               # Checkout process templates
1829 | │   └── checkout.isml       # Main checkout
1830 | ├── account/                # Customer account templates
1831 | │   ├── login.isml          # Login page
1832 | │   ├── profile.isml        # Account profile
1833 | │   └── orderHistory.isml   # Order history
1834 | ├── search/                 # Search and category templates
1835 | │   └── searchResults.isml  # Search results page
1836 | └── error/                  # Error page templates
1837 |     ├── error.isml          # Generic error page
1838 |     └── notFound.isml       # 404 page
1839 | ```
1840 | 
1841 | ### Best Practices for SFRA Template Development
1842 | 
1843 | #### 1. Follow the Layout Hierarchy
1844 | 
1845 | ```html
1846 | <!-- ✅ Use appropriate base layout -->
1847 | <isdecorate template="common/layout/page">
1848 |     <!-- Standard page content -->
1849 | </isdecorate>
1850 | 
1851 | <!-- ✅ Use checkout layout for secure pages -->
1852 | <isdecorate template="common/layout/checkout">
1853 |     <!-- Checkout/payment content -->
1854 | </isdecorate>
1855 | ```
1856 | 
1857 | #### 2. Leverage Component Reusability
1858 | 
1859 | ```html
1860 | <!-- ✅ Include reusable components -->
1861 | <isinclude template="components/breadcrumbs/pageBreadcrumbs" />
1862 | <isinclude template="components/product/productTile" />
1863 | ```
1864 | 
1865 | #### 3. Implement Proper Asset Management
1866 | 
1867 | ```html
1868 | <!-- ✅ Page-specific assets -->
1869 | <isscript>
1870 |     var assets = require('*/cartridge/scripts/assets.js');
1871 |     assets.addCss('/css/product/detail.css');
1872 |     assets.addJs('/js/productDetail.js');
1873 | </isscript>
1874 | ```
1875 | 
1876 | #### 4. Use Semantic HTML Structure
1877 | 
1878 | ```html
1879 | <!-- ✅ Semantic and accessible markup -->
1880 | <main id="maincontent" role="main">
1881 |     <article class="product-detail">
1882 |         <header class="product-header">
1883 |             <h1 class="product-name">${product.productName}</h1>
1884 |         </header>
1885 |         <section class="product-images">
1886 |             <!-- Product imagery -->
1887 |         </section>
1888 |     </article>
1889 | </main>
1890 | ```
1891 | 
1892 | #### 5. Implement Responsive Design Patterns
1893 | 
1894 | ```html
1895 | <!-- ✅ Mobile-first responsive structure -->
1896 | <div class="row">
1897 |     <div class="col-12 col-md-6 col-lg-4">
1898 |         <!-- Mobile: full width, tablet: half, desktop: one-third -->
1899 |     </div>
1900 | </div>
1901 | 
1902 | <!-- ✅ Progressive disclosure -->
1903 | <div class="product-breadcrumb d-md-none">
1904 |     <!-- Only show on mobile -->
1905 | </div>
1906 | <div class="d-none d-md-block">
1907 |     <!-- Only show on tablet and up -->
1908 | </div>
1909 | ```
1910 | 
1911 | This SFRA base template architecture provides a solid foundation for building consistent, maintainable, and scalable storefronts while following Commerce Cloud best practices for performance, security, and user experience
1912 | 
1913 | 
1914 | ## Quick Reference Tables
1915 | 
1916 | ### Essential ISML Tags
1917 | | Tag | Purpose | Example |
1918 | |-----|---------|---------|
1919 | | `<isprint>` | Output data safely | `<isprint value="${pdict.product.name}" />` |
1920 | | `<isif>` | Conditional logic | `<isif condition="${product.available}">` |
1921 | | `<isloop>` | Iterate collections | `<isloop items="${products}" var="product">` |
1922 | | `<isinclude>` | Include templates | `<isinclude template="components/header" />` |
1923 | | `<isdecorate>` | Apply layout | `<isdecorate template="common/layout/page">` |
1924 | | `<isset>` | Set variables | `<isset name="showPrice" value="${true}" scope="page" />` |
1925 | 
1926 | ### URL Generation Functions
1927 | | Function | Purpose | Example |
1928 | |----------|---------|---------|
1929 | | `URLUtils.url()` | Relative URLs | `URLUtils.url('Product-Show', 'pid', product.id)` |
1930 | | `URLUtils.https()` | Secure URLs | `URLUtils.https('Account-Login')` |
1931 | | `URLUtils.staticURL()` | Static assets | `URLUtils.staticURL('/images/logo.png')` |
1932 | 
1933 | Remember: **Keep business logic in controllers, use ISML only for presentation, and always encode output for security.**
1934 | 
```
Page 59/61FirstPrevNextLast