Skip to main content

Template View Files Reference

This documentation contains a detailed analysis of all template files in the public/templates/basic/views/ directory. Each file is described along with its purpose, special features, and usage examples.

Main Layout and Base Templates

layouts/theme.twig.php

Main Layout Template - Master template that wraps all page content

Features:

  • HTML5 document structure and responsive meta tags
  • SEO optimization and Open Graph integration
  • Webpack bundle integration
  • JavaScript configuration injection
  • Theme support for CSS custom properties
  • Sidebar cart offcanvas integration
<main>
{{ render_header() }}
{{ content_for_layout | raw }} {# IMPORTANT: Page content coming from the backend is placed here #}
{% set footer_url = theme_config('footer_url') %}
{% if footer_url %}
{{ render_page(footer_url) }}
{% endif %}
</main>

content_for_layout Special Note:

The {{ content_for_layout | raw }} variable is one of the most critical parts of the template system. This variable:

  • Places dynamic content coming from the backend into the layout template
  • Each page (index.twig.php, product.twig.php, login.twig.php, etc.) produces its own content and that content is assigned to the content_for_layout variable.
  • The raw filter ensures that the HTML content is rendered as-is, without being escaped.
  • The layout template (theme.twig.php) acts as a frame; the actual content is inserted into this placeholder.
  • For example: when the /product/123 URL is called, the backend processes the product.twig.php template and sends the result to the layout as content_for_layout.

Workflow:

  1. The backend controller renders a page template (e.g.: product.twig.php)
  2. The rendered content is assigned to the content_for_layout variable.
  3. The layout template (theme.twig.php) is loaded.
  4. The page content is displayed in place of {{ content_for_layout | raw }}.
  5. This way, every page uses the same header, footer, and general structure.

open_graph.twig.php

Social Media Meta Tags - Dynamic Open Graph tags

Features:

  • Product-specific Open Graph tags
  • Stock status integration
  • Brand and category metadata
  • Fallback to general website metadata
{% if request.page_type == 'product' and product is defined %}
<meta property="og:type" content="product">
<meta property="product:availability" content="{{ stock_status }}">
<meta property="product:brand" content="{{ product.brand }}">
{% endif %}

index.twig.php

Homepage Template - Homepage with modular widget system

Features:

  • Webpack bundle loading
  • Custom widget placeholders
  • Modular widget includes (sliders, categories, products)
{{ webpack_bundle('index') }}
{{ ___('page__signin_box') }}

{% include 'widgets/brands_slider.twig.php' %}
{% include 'widgets/manset_slider.twig.php' %}
{% include 'widgets/category_grid.twig.php' %}
{% include 'widgets/last_products.twig.php' %}

IMPORTANT NOTE - CMS Priority:

If a page named "index" has been created in the CMS:

  • The content of the "index" page in the CMS takes priority.
  • The index.twig.php template is disabled.
  • CMS content is placed directly into the theme.twig.php layout via the content_for_layout variable
  • Widgets and custom content are managed from the CMS editor.
  • This way, the homepage content can be updated without making code changes.

Template Priority Order:

  1. If an "index" page is defined in the CMS → CMS content is used.
  2. If there is no "index" page in the CMS → the index.twig.php template is used.

Header Variants

header.twig.php

Standard Header - Basic header with two-section layout

Features:

  • Multi-language support and flag display
  • User authentication states (logged in/out)
  • Category navigation with dropdown menus
  • Search functionality
  • Shopping cart integration
  • Sticky header option
<div class="header-sec-one">
<!-- Logo, language selection, user actions -->
<div class="language">
{% for locale_data in get_locales() %}
<a href="{{ locale_data.url }}">
<img src="{{ ('flags/' ~ locale_data.code ~ '.png') | image }}">
</a>
{% endfor %}
</div>
</div>
<div class="header-sec-two">
<!-- Navigation and search -->
<nav class="navbar">
{% for category in categories %}
<a href="{{ category.url }}">{{ category.name }}</a>
{% endfor %}
</nav>
</div>

header_ella.twig.php

Alternative Header - With full-width navigation

Features:

  • Language selection dropdown
  • Horizontal category navigation
  • User account dropdown
  • Search with typewriter effect
<div class="language">
<div class="dropdown">
<button class="dropdown-toggle">
<img src="{{ ('flags/' ~ locale ~ '.png') | image }}">
</button>
</div>
</div>
<nav class="row">
<!-- Logo, navigation, user buttons -->
</nav>

header_fox.twig.php

Advanced Header - With mega menu and promotional banner

Features:

  • Top promotional carousel
  • Mega menu with image support
  • Collapsible search functionality
  • Account sidebar
<div class="mega">
<div class="listBox">
<!-- Category lists -->
{% for category in categories %}
<div class="category-group">
<h5>{{ category.name }}</h5>
{% for subcategory in category.subcategories %}
<a href="{{ subcategory.url }}">{{ subcategory.name }}</a>
{% endfor %}
</div>
{% endfor %}
</div>
<div class="visualBox">
<!-- Category images -->
<img src="{{ category.featured_image }}" alt="{{ category.name }}">
</div>
</div>

header_helen.twig.php

Three-Layer Header - With vertical hierarchy

Features:

  • Top promotional banner
  • Logo and search in the middle section
  • Bottom navigation bar
<div class="top">
<!-- Promotional carousel -->
</div>
<div class="medium">
<!-- Logo and search -->
</div>
<div class="bottom">
<!-- Navigation -->
</div>

header_mobile.twig.php

Mobile Header - Optimized with offcanvas navigation

Features:

  • Hamburger menu with offcanvas
  • Collapsible search
  • Mobile-friendly layout
  • Accordion category menu
<div class="offcanvas offcanvas-start" id="staticBackdrop">
<div class="accordion accordion-flush">
{% for category in categories %}
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" data-bs-target="#flush-collapse{{ loop.index }}">
{{ category.name }}
</button>
</h2>
<div id="flush-collapse{{ loop.index }}" class="accordion-collapse collapse">
{% for subcategory in category.subcategories %}
<a href="{{ subcategory.url }}">{{ subcategory.name }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>

Product Templates

📚 Related Documentation: For all available fields and properties in the product object, see the Product Data Fields documentation.

_product_card.twig.php

Product Card Component - Reusable product card used in listings

Features:

  • Lazy loading images
  • Favorites functionality
  • Variant selection with thumbnails
  • Add to cart form
  • Measure selection (packaging units)
  • Price display with discount support
<div class="product pnlurun-item product-card-body" id="divUrunKutu_{{ product.id }}">
<div class="pro card">
{# Favorites button for logged-in users #}
{% if config('ADD_TO_FAVORITE') and is_logged %}
<button class="fav-icon btn-add-to-favorites" data-urunid="{{ product.id }}">
<i class="bi {% if product.is_favorited %} bi-heart-fill text-danger {% else %} bi-heart {% endif %}"></i>
</button>
{% endif %}

{# Product image and link #}
<a href="{{ product.url }}" title="{{ product.title }}">
<img data-src="{{ product.med_image }}" class="mainImg lazyload"
src="{{ product.min_image }}" alt="{{ product.title }}" />
</a>

{# Variants #}
<div class="product-variant-img-cont">
{% for variant in product.variants %}
<div {{ variant['html-data'] | html_data_attributes }}
class="variant-container {{ loop.first ? 'selectedThumb' : '' }}">
<img data-src="{{ variant['image'] }}" src="{{ variant['image_min'] }}"
class="product-color-img lazyload" alt="{{ variant['name'] }}">
<p class="product-color">{{ variant['name'] ?: '&nbsp;' }}</p>
</div>
{% endfor %}
</div>

{# Add to cart form #}
{% if is_logged and not product.sale_disabled %}
<form action="{{ 'ADD_TO_CART' | route }}" method="post" class="frm-add-to-cart">
<input type="hidden" name="urun_id" value="{{ product.id }}"/>
<input type="hidden" name="renk" class="drUrunRenk{{ product.id }}"
value="{{ product.default_variant_id }}"/>

<div class="d-flex align-items-center justify-content-between">
<div class="count-btns d-flex">
<span class="decrease-btn btn-qty-decrease">
<i class="bi bi-dash"></i>
</span>
<input class="count" type="text" name="adet" value="{{ product.package_qty ?: 1 }}">
<span class="increase-btn btn-qty-increase">
<i class="bi bi-plus"></i>
</span>
</div>
<button type="submit" class="cart basket-btn addtocartbtn">
<i class="bi bi-cart-plus"></i>
</button>
</div>
</form>
{% endif %}
</div>
</div>

product.twig.php

Product Detail Page - Comprehensive product display template

Features:

  • Product gallery with zoom functionality
  • Variant selection with image switching
  • Video support with modal
  • Add to cart form with quantity controls
  • Product attributes display
  • Related products section
  • Image modal for full-screen viewing
<div class="renkkategori d-flex gap-2">
{% for variant in product.variants_with_gallery %}
<div class="productVariantSelect" data-galeri_index="{{ iiVariant }}">
<img src="{{ variant.image_thumb }}" />
<p>{{ variant.name }}</p>
</div>
{% endfor %}
</div>

{# Product gallery #}
<div class="product-gallery">
<div class="main-image">
<img id="mainProductImage" src="{{ product.main_image }}" data-zoom="{{ product.large_image }}">
</div>
<div class="thumbnail-gallery">
{% for image in product.gallery %}
<img src="{{ image.thumb }}" data-main="{{ image.large }}" class="gallery-thumb">
{% endfor %}
</div>
</div>

{# Video support #}
{% if product.video_url %}
<div class="product-video">
<button type="button" data-bs-toggle="modal" data-bs-target="#videoModal">
<i class="bi bi-play-circle"></i> {{ 'video_izle' | t }}
</button>
</div>
{% endif %}

product_list.twig.php

Category/Search Results List - Filterable product list

Features:

  • Responsive filter panel (desktop/mobile)
  • Sorting options dropdown
  • Infinite scroll pagination
  • Grid layout with product cards
  • Mobile offcanvas filters
{# Filters and sorting #}
<div class="row">
<div class="col-md-3">
{% include 'filters.twig.php' %}
</div>
<div class="col-md-9">
<div class="sorting-options">
<select id="sort-products">
<option value="name">{{ 'isme_gore_sirala' | t }}</option>
<option value="price_asc">{{ 'fiyat_artan' | t }}</option>
<option value="price_desc">{{ 'fiyat_azalan' | t }}</option>
</select>
</div>

{# Product list #}
<div class="row row-cols-2 row-cols-md-4 urunlistesi">
{% for product in products.getData %}
{% include '_product_card.twig.php' with {'product': product} %}
{% endfor %}
</div>

{# Loading indicator for infinite scroll #}
<div id="loading" class="text-center" style="display: none;">
<div class="spinner-border"></div>
</div>
</div>
</div>

Customer Management Templates

customers/login.twig.php

User Login Form - Authentication template

Features:

  • Login support with email/customer code
  • Password field with autocomplete
  • Responsive form layout
  • Links for registration and password recovery
<form id="frmLogin" action="{{ 'LOGIN' | route }}" method="post">
<div class="form-group">
<label for="email">{{ 'email_veya_musteri_kodu' | t }}</label>
<input type="email" name="email" id="email" autocomplete="username" required
placeholder="{{ 'email_giriniz' | t }}">
</div>

<div class="form-group">
<label for="password">{{ 'sifre' | t }}</label>
<input type="password" name="sifre" id="password" autocomplete="current-password" required
placeholder="{{ 'sifre_giriniz' | t }}">
</div>

<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'giris_yap' | t }}</button>
<a href="{{ 'register' | url }}">{{ 'kayit_ol' | t }}</a>
<a href="{{ 'forgot_password' | url }}">{{ 'sifremi_unuttum' | t }}</a>
</div>
</form>

customers/register.twig.php

User Registration Form - Comprehensive registration template

Features:

  • Company/personal information form
  • Country/city selection with AJAX
  • Conditional fields based on configuration
  • Custom field integration
  • reCAPTCHA support
  • Terms and conditions checkbox
<form id="frmRegister" action="{{ 'REGISTER' | route }}" method="post">
<div class="row">
<div class="col-md-6">
<label for="firma">{{ 'firma_adi' | t }}</label>
<input type="text" name="firma" id="firma" required>
</div>
<div class="col-md-6">
<label for="mail">{{ 'email' | t }}</label>
<input type="email" name="mail" id="mail" required>
</div>
</div>

<div class="row">
<div class="col-md-6">
<label for="ulke">{{ 'ulke' | t }}</label>
<select name="ulke" id="ulke" class="ulkeSecim" required>
<option value="">{{ 'ulke_seciniz' | t }}</option>
{% for ulke in ulkeler %}
<option value="{{ ulke.Id }}">{{ ulke.UlkeAdi }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-6">
<label for="sehir">{{ 'sehir' | t }}</label>
<select name="sehir" id="sehir" class="sehirSecim" required>
<option value="">{{ 'once_ulke_seciniz' | t }}</option>
</select>
</div>
</div>

{# Custom fields #}
{% include 'customers/register_customs.twig.php' %}

{# reCAPTCHA #}
{% if config('RECAPTCHA_SITE_KEY') %}
<div class="g-recaptcha" data-sitekey="{{ config('RECAPTCHA_SITE_KEY') }}"></div>
{% endif %}

<div class="form-check">
<input type="checkbox" name="terms" id="terms" required>
<label for="terms">{{ 'sartlari_kabul_ediyorum' | t }}</label>
</div>

<button type="submit" class="btn btn-primary">{{ 'kayit_ol' | t }}</button>
</form>

customers/register_customs.twig.php

Dynamic Custom Fields - Customizable fields for the registration form

Features:

  • Multiple field types (input, textarea, select, checkbox, file, datepicker)
  • Dynamic field generation from configuration
  • Conditionally required fields
{% for key_name, field_data in custom_fields %}
<div class="form-group">
<label for="custom_field_{{ key_name }}">{{ field_data.label }}</label>

{% if field_data.type == 'input' %}
<input type="text" name="custom_field_{{ key_name }}" id="custom_field_{{ key_name }}"
{{ field_data.required ? 'required' : '' }}
placeholder="{{ field_data.placeholder }}">

{% elseif field_data.type == 'textarea' %}
<textarea name="custom_field_{{ key_name }}" id="custom_field_{{ key_name }}"
{{ field_data.required ? 'required' : '' }}
placeholder="{{ field_data.placeholder }}"></textarea>

{% elseif field_data.type == 'select' %}
<select name="custom_field_{{ key_name }}" id="custom_field_{{ key_name }}"
{{ field_data.required ? 'required' : '' }}>
<option value="">{{ 'seciniz' | t }}</option>
{% for option in field_data.options %}
<option value="{{ option }}">{{ option }}</option>
{% endfor %}
</select>

{% elseif field_data.type == 'checkbox' %}
<div class="form-check">
<input type="checkbox" name="custom_field_{{ key_name }}" value="1"
id="custom_field_{{ key_name }}" {{ field_data.required ? 'required' : '' }}>
<label for="custom_field_{{ key_name }}">{{ field_data.label }}</label>
</div>

{% elseif field_data.type == 'file' %}
<input type="file" name="custom_field_{{ key_name }}" id="custom_field_{{ key_name }}"
{{ field_data.required ? 'required' : '' }}
accept="{{ field_data.accept }}">

{% elseif field_data.type == 'datepicker' %}
<input type="date" name="custom_field_{{ key_name }}" id="custom_field_{{ key_name }}"
{{ field_data.required ? 'required' : '' }}>
{% endif %}
</div>
{% endfor %}

Order Management Templates

orders/index.twig.php

Order History - User's order list

Features:

  • Order display in table format
  • Order status and amounts
  • Shipping information
  • Pagination support
  • Links to order details
<div class="orders-table-container">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>{{ 'tarih' | t }}</th>
<th>{{ 'siparis_no' | t }}</th>
<th>{{ 'tutar' | t }}</th>
<th>{{ 'durum' | t }}</th>
<th>{{ 'kargo' | t }}</th>
<th>{{ 'islemler' | t }}</th>
</tr>
</thead>
<tbody>
{% for order in orders.data %}
<tr>
<td>{{ order.short_date }}</td>
<td>{{ order.order_number }}</td>
<td>{{ order.amount_text }}</td>
<td>
<span class="badge badge-{{ order.status_class }}">
{{ order.status_text }}
</span>
</td>
<td>{{ order.shipping_info }}</td>
<td>
<a href="{{ order.detail_url }}" class="btn btn-sm btn-outline-primary">
{{ 'detay' | t }}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>

{# Pagination #}
{% if orders.pagination %}
{{ orders.pagination | raw }}
{% endif %}
</div>

orders/detail.twig.php

Order Detail - Individual order view

Features:

  • Order information table
  • Excel export functionality
  • Photo export feature
  • Reorder functionality
  • Order HTML content display
<div class="order-detail-header">
<h2>{{ 'siparis_detayi' | t }} - {{ order.order_number }}</h2>

<div class="order-actions">
<a href="{{ order.excel_export_url }}" class="btn btn-success">
<i class="bi bi-file-excel"></i> {{ 'excel_indir' | t }}
</a>

<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#photoExportModal">
<i class="bi bi-download"></i> {{ 'urun_fotograflarini_indir' | t }}
</button>

<button type="button" class="btn btn-primary" onclick="reorderProducts({{ order.id }})">
<i class="bi bi-arrow-repeat"></i> {{ 'tekrar_siparis_ver' | t }}
</button>
</div>
</div>

<div class="order-info-table">
<table class="table">
<tr>
<td>{{ 'siparis_tarihi' | t }}</td>
<td>{{ order.created_date }}</td>
</tr>
<tr>
<td>{{ 'durum' | t }}</td>
<td>{{ order.status_text }}</td>
</tr>
<tr>
<td>{{ 'toplam_tutar' | t }}</td>
<td>{{ order.total_amount_text }}</td>
</tr>
</table>
</div>

<div class="pnl-order-detail">
{{ order.order_html | raw }}
</div>

{# Photo export modal #}
{% include 'orders/_photo_export_modal.twig.php' %}

Widget Templates

widgets/brands_slider.twig.php

Brand Carousel - Brand display on the homepage

Features:

  • Swiffy slider integration
  • Configurable display settings
  • Navigation and indicators
  • Responsive design
{% set brands_banner = get_banner('brands_slider') %}
{% if brands_banner and theme_config('brands_slider_active') %}
<div class="container mt-5" data-widget="brands-slider">
<h3 class="text-center">{{ 'markalar' | t }}</h3>

<div class="swiffy-slider {{ theme_config('brands_slider_settings') }}">
<ul class="slider-container">
{% for slide in brands_banner.slide_items %}
<li class="brand-container">
{% if slide.link %}
<a href="{{ slide.link }}" target="_blank">
{% endif %}

<img src="{{ slide.image }}" alt="{{ slide.title }}" class="brand-logo">

{% if slide.link %}
</a>
{% endif %}
</li>
{% endfor %}
</ul>

<button type="button" class="slider-nav" aria-label="Go left">
<i class="bi bi-chevron-left"></i>
</button>
<button type="button" class="slider-nav slider-nav-next" aria-label="Go right">
<i class="bi bi-chevron-right"></i>
</button>

<div class="slider-indicators">
{% for slide in brands_banner.slide_items %}
<button class="{{ loop.first ? 'active' : '' }}" aria-label="Go to slide {{ loop.index }}"></button>
{% endfor %}
</div>
</div>
</div>
{% endif %}

widgets/category_grid.twig.php

Category Grid - Category showcase on the homepage

Features:

  • Configurable category grid of up to 4 items
  • Color customization
  • Featured category with subcategories
{% for ii in 1..4 %}
{% if theme_config('category_grid_slider_' ~ ii ~ '_active') %}
{% set grid_category = theme_config('category_grid_slider_' ~ ii ~ '_category') %}
{% set grid_color = theme_config('category_grid_slider_' ~ ii ~ '_color') %}

<div class="category-grid-{{ ii }}" style="background-color: {{ grid_color }};">
<div class="container">
<div class="row">
<div class="col-md-8">
<h3>{{ grid_category.name }}</h3>
<div class="subcategories">
{% for subcategory in grid_category.subcategories %}
<a href="{{ subcategory.url }}" class="subcategory-link">
{{ subcategory.name }}
</a>
{% endfor %}
</div>
</div>
<div class="col-md-4">
<img src="{{ grid_category.featured_image }}" alt="{{ grid_category.name }}" class="category-featured-image">
</div>
</div>
</div>
</div>
{% endif %}
{% endfor %}

widgets/last_products.twig.php

Latest Products - Recently added products on the homepage

Features:

  • Reusing the product card template
  • Responsive grid layout
{% set last_products = get_last_products() %}
{% if last_products %}
<div class="container mt-5" data-widget="last-products">
<h3 class="text-center">{{ 'son_eklenenler' | t }}</h3>
<div class="row row-cols-md-4 row-cols-2 g-1">
{% for product in last_products %}
{{ include('_product_card.twig.php', {'product': product}) }}
{% endfor %}
</div>
</div>
{% endif %}

widgets/instagram.twig.php

Instagram Feed - Social media integration

Features:

  • Instagram API integration
  • Follow button
  • Responsive photo grid
{% if theme_config('module_instagram') and theme_config('module_instagram_token') %}
<div class="container mt-5" data-widget="instagram">
<h3 class="text-center">{{ 'instagram' | t }}</h3>

<div id="instagram-feed1" class="instagram_feed"
data-token="{{ theme_config('module_instagram_token') }}"
data-count="{{ theme_config('module_instagram_count') ?: 6 }}">
</div>

<div class="text-center mt-3">
<a href="{{ config('SITE_INSTAGRAM_LINK') }}" target="_blank" class="btn btn-primary">
<i class="bi bi-instagram"></i> {{ 'takip_et' | t }}
</a>
</div>
</div>
{% endif %}

Helper Templates

filters.twig.php

Product Filtering Interface - Filtering on category pages

Features:

  • Accordion-style filter groups
  • Search within filters
  • Checkbox-style selection
  • URL-based filtering
<div class="accordion accordion-filter" id="accordionFilter">
{% for group in filters %}
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{ loop.index }}">
<button class="accordion-button {{ loop.first ? '' : 'collapsed' }}"
data-bs-toggle="collapse" data-bs-target="#collapse{{ loop.index }}">
{{ group.name }}
</button>
</h2>

<div id="collapse{{ loop.index }}"
class="accordion-collapse collapse {{ loop.first ? 'show' : '' }}">
<div class="accordion-body">
{% if group.filters|length > 5 %}
<input type="text" class="filter-search form-control mb-2"
placeholder="{{ 'filtrele' | t }}" />
{% endif %}

<div class="filter-options">
{% for filter in group.filters %}
<div class="filter-option">
<a href="{{ filter.url }}" class="filter-link {{ filter.selected ? 'active' : '' }}">
<span class="filter-checkbox">
{% if filter.selected %}
<i class="bi bi-check-square"></i>
{% else %}
<i class="bi bi-square"></i>
{% endif %}
</span>
{{ filter.label }}
<small class="text-muted">({{ filter.count }})</small>
</a>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>

no_records.twig.php

Empty State Template - When no results are found

Features:

  • Company logo display
  • "No products" message
  • Return to homepage button
<div class="no-records-container text-center py-5">
<div class="mb-4">
<img src="{{ config('LOGO_URL') }}" alt="{{ config('FIRMA_ISIM') }}" class="company-logo">
</div>

<h4 class="mb-3">{{ 'kategori_urun_yok' | t }}</h4>
<p class="text-muted mb-4">{{ 'aradiginiz_kriterlere_uygun_urun_bulunamadi' | t }}</p>

<a href="{{ base_url }}" class="btn btn-primary">
<i class="bi bi-house"></i> {{ 'ana_sayfaya_don' | t }}
</a>
</div>

Partial Templates

partial/product/add_to_cart.twig.php

Add to Cart Form - Reusable cart form

Features:

  • Quantity controls
  • Measure/packaging selection
  • Favorites button integration
  • Sale disabled state
  • Requirement for non-logged-in users
{% if is_logged and not product.sale_disabled %}
<form action="{{ 'ADD_TO_CART' | route }}" method="post" class="add-to-cart-form">
<input type="hidden" name="urun_id" value="{{ product.id }}">
<input type="hidden" name="renk" value="{{ product.default_variant_id }}">

{# Measure selection #}
{% if product.measures %}
<div class="measure-selection mb-3">
<label>{{ 'olcu' | t }}</label>
<select name="olcu" class="form-control">
{% for measure in product.measures %}
<option value="{{ measure.id }}">{{ measure.name }}</option>
{% endfor %}
</select>
</div>
{% endif %}

<div class="quantity-controls d-flex align-items-center mb-3">
<div class="count-btns d-flex">
<span class="decrease-btn btn-qty-decrease" data-package-qty="{{ product.package_qty }}">
<i class="bi bi-dash"></i>
</span>
<input class="count form-control" type="number" name="adet"
value="{{ product.package_qty ?: 1 }}" min="1" step="{{ product.package_qty ?: 1 }}">
<span class="increase-btn btn-qty-increase" data-package-qty="{{ product.package_qty }}">
<i class="bi bi-plus"></i>
</span>
</div>
</div>

<div class="cart-actions">
<button type="submit" class="btn btn-primary add-to-cart-btn">
<i class="bi bi-cart-plus"></i> {{ 'sepete_ekle' | t }}
</button>

{% if config('ADD_TO_FAVORITE') %}
<button type="button" class="btn btn-outline-secondary btn-add-to-favorites" data-urunid="{{ product.id }}">
<i class="bi {% if product.is_favorited %} bi-heart-fill {% else %} bi-heart {% endif %}"></i>
</button>
{% endif %}
</div>
</form>
{% else %}
{% if not is_logged %}
<div class="login-required text-center p-3">
<p>{{ 'siparis_vermek_icin_giris_yapin' | t }}</p>
<a href="{{ 'login' | url }}" class="btn btn-primary">{{ 'giris_yap' | t }}</a>
</div>
{% else %}
<div class="sale-disabled text-center p-3">
<p>{{ 'bu_urun_satis_disi' | t }}</p>
</div>
{% endif %}
{% endif %}

Core Technical Features

Multi-Language Support

All templates provide translation support via the {{ 'girisyap' | t }} filter:

{{ 'merhaba_dunya' | t }}
{{ 'hosgeldin_mesaji' | translate }}

Responsive Design

Responsive structure with Bootstrap 5 classes:

<div class="row row-cols-2 row-cols-md-4 g-2">
<div class="col">
<!-- Content -->
</div>
</div>

Lazy Loading

Image lazy loading for performance optimization:

<img data-src="{{ product.med_image }}" 
src="{{ product.min_image }}"
class="lazyload"
alt="{{ product.title }}">

Webpack Integration

Webpack bundle system for asset management:

{{ webpack_bundle('layout') }}
{{ webpack_bundle('product') }}
{{ webpack_bundle_header() }}

Theme Configuration

Dynamic theme settings:

{% if theme_config('header_style') == 'modern' %}
{% include 'header_modern.twig.php' %}
{% endif %}

User State Management

Conditional rendering:

{% if is_logged %}
{{ 'hosgeldin' | t }} {{ user.name }}!
{% else %}
<a href="{{ 'login' | url }}">{{ 'giris_yap' | t }}</a>
{% endif %}

This comprehensive reference describes in detail the purpose, features, and usage of each file so that frontend developers can work effectively with the template system.

JavaScript-Interactive CSS Classes

This section contains CSS classes used exclusively by JavaScript. These classes are used for event listeners, DOM manipulation, or library integrations. Classes used for styling purposes only are not included in this list.

Variant Selection & Image Management

productThumbSelector

JavaScript Function: Click event handler for variant selection. Toggles the selectedThumb class when a variant is selected, and updates the product image and price.

Files: product.js, product_detail.js

<div class="variant-container position-relative productThumbSelector {{ loop.first ? 'selectedThumb' : '' }}"
data-urunid="{{ product.id }}"
data-min-src="{{ variant.image_min }}"
data-med-src="{{ variant.image_thumb }}">
<img class="product-color-img card-img-top lazyload" data-src="{{ variant.image_min }}">
</div>
// product.js
on(document, "click", ".productThumbSelector", function (e) {
el.classList.toggle("selectedThumb");
renkInput.value = el.dataset.renk;
price.innerHTML = selector.dataset.price;
});

selectedThumb

JavaScript Function: Added/removed by JavaScript to mark the selected variant.

Files: product.js, product_detail.js

// product.js
selected.classList.remove("selectedThumb"); // Remove from previous
el.classList.add("selectedThumb"); // Add to new

productVariantSelect

JavaScript Function: Click handler for variant selection on the product detail page. Switches gallery images.

Files: product_detail.js

<div class="renk-img-container productVariantSelect flex-shrink-0"
data-urunid="{{ product.id }}"
data-varyantid="{{ variant.id }}"
data-galeri_index="{{ iiVariant }}">
<img class="lazyload" data-src="{{ variant.image_min }}">
</div>
// product_detail.js
on(document, "click", ".productVariantSelect", function (e) {
this.classList.toggle("selectedThumb");
getGallery(this.dataset.galeri_index, photo);
});

mainImg

JavaScript Function: Main product image - the src attribute is updated, fade animation class is added/removed.

Files: product.js, product_detail.js

<img class="mainImg lazyload"
data-src="{{ product.med_image }}"
src="{{ product.min_image }}">
// product.js
pnlMainImg.classList.add('fade-transition');
pnlMainImg.src = medPhoto;
setTimeout(() => pnlMainImg.classList.remove('fade-transition'), 50);

product-card-body

JavaScript Function: Preloads variant images on mouseenter event.

Files: product.js

<div class="product pnlurun-item product-card-body" id="divUrunKutu_{{ product.id }}">
<!-- Product card content -->
</div>
// product.js
on(document, "mouseenter", ".product-card-body", function (e) {
const variantImages = this.querySelectorAll('.productThumbSelector img');
// Preload variant images
});

urungorselleri

JavaScript Function: Changes the main image when a gallery thumbnail is clicked.

Files: product_detail.js

<div class="urungorselleri d-flex gap-2">
{% for image in product.gallery %}
<div class="imgs-container">
<img class="gallery-item" src="{{ image.thumb }}">
</div>
{% endfor %}
</div>
// product_detail.js
on(document, "click", ".urungorselleri", function (e) {
changeImages(this); // Change the main image
});

JavaScript Function: Gallery images dynamically created by JavaScript.

Files: product_detail.js

// product_detail.js
img.classList.add("w-100", "h-100", "object-fit-cover", "gallery-item");

JavaScript Function: Main gallery image - src is updated when the variant changes.

Files: product_detail.js

// product_detail.js
galleryMainItem.src = mainImg;
galleryMainItem.dataset.medSrc = mainImg;

pnl_urun_galeri

JavaScript Function: Gallery container - content is cleared and refilled when the variant changes.

Files: product_detail.js

// product_detail.js
var gallery = document.querySelector(".pnl_urun_galeri");
gallery.innerHTML = ""; // Clear
gallery.appendChild(galleryContainer); // Add new content

zoom

JavaScript Function: Image zoom with jQuery zoom plugin. Re-initialized when the image changes.

Files: product_detail.js

<figure class="zoom" style="background-image: url('{{ product.large_image }}')">
<img src="{{ product.image }}">
</figure>
// product_detail.js
$('.zoom').trigger('zoom.destroy').zoom({
url: photo, // Reinitialize zoom with new image
});

product-image-clickable

JavaScript Function: Displays the product image in large size in a modal when clicked.

Files: product_detail.js

<img class="mainImg product-image-clickable" data-src="{{ product.image }}">
// product_detail.js
on(document, "click", ".product-image-clickable", function (e) {
let fullImageUrl = this.dataset.src || this.src;
modalImage.src = fullImageUrl;
modal.show();
});

product-modal-image

JavaScript Function: Image inside the modal - src is updated via JavaScript.

Files: product_detail.js

// product_detail.js
modalImage.src = fullImageUrl;

product-video-btn

JavaScript Function: Opens the video modal and loads the video URL.

Files: product.js, index.js

<button class="product-video-btn" data-video-url="{{ product.video }}">
<i class="bi bi-play-circle"></i>
</button>
// product.js
on(document, "click", ".product-video-btn", function (e) {
const videoUrl = this.getAttribute('data-video-url');
videoSource.src = videoUrl;
bsModal.show();
});

videoSelector

JavaScript Function: Video thumbnail shows video on hover, selects on click.

Files: product.js

// product.js
$(".videoSelector").hover(function () {
mainVid.show(); // Show video on hover
}).click(function () {
selector.toggleClass("selectedThumb");
});

Cart & Quantity Controls

frm-add-to-cart

JavaScript Function: Form submit event handler, adds or updates the product in the cart.

Files: product.js, product_shared.js

<form class="frm-add-to-cart" method="post" action="{{ 'ADD_TO_CART' | route }}">
<input type="hidden" name="urun_id" value="{{ product.id }}">
<input type="hidden" name="renk" value="{{ product.default_variant_id }}">
<button type="submit" class="addtocartbtn">Sepete Ekle</button>
</form>
// product.js
on(document, "submit", ".frm-add-to-cart", function (e) {
addToCart(this, afterAddToCart);
});

btn-qty-increase

JavaScript Function: Quantity increase button, increases the product quantity on click.

Files: product_shared.js

<span class="increase-btn btn-qty-increase">
<i data-lucide="plus"></i>
</span>
// product_shared.js
on(document, "click", ".btn-qty-increase", function (e) {
updateCount(this, false); // Increase quantity
});

btn-qty-decrease

JavaScript Function: Quantity decrease button, decreases the product quantity on click.

Files: product_shared.js

<span class="decrease-btn btn-qty-decrease">
<i data-lucide="minus"></i>
</span>
// product_shared.js
on(document, "click", ".btn-qty-decrease", function (e) {
updateCount(this, true); // Decrease quantity
});

Measure/Package Selection

pc-measure-box

JavaScript Function: Measure selection in the product card - selects the radio button on click, updates the quantity input.

Files: product.js

<div class="pc-measure-box mb-1 me-1">
<p class="pc-measure-title">{{ measure.name }}</p>
<button class="pc-select-btn">{{ 'sec' | t }}</button>
</div>
// product.js
selectButton.addEventListener("click", function (e) {
radioButton.checked = true;
box.classList.add("pc-active");
updatePackageQty(radioButton);
});

pc-active

JavaScript Function: Added/removed by JavaScript to mark the active measure box.

Files: product.js

// product.js
subMeasureBoxes.forEach(function (subBox) {
subBox.classList.remove("pc-active");
});
box.classList.add("pc-active");

measure-box

JavaScript Function: Measure selection on the detail page - becomes active on click.

Files: product_detail.js

<div class="measure-box" data-measure-id="{{ measure.id }}">
<p class="measure-title">{{ measure.name }}</p>
<button class="measure-select-btn">{{ 'sec' | t }}</button>
</div>
// product_detail.js
box.addEventListener("click", function (e) {
radioButton.checked = true;
measureBoxes.forEach(box => box.classList.remove("active"));
box.classList.add("active");
});

select-btn

JavaScript Function: Measure selection button triggers click event.

Files: product.js, product_detail.js

// product.js
const selectButton = box.querySelector(".select-btn");
selectButton.addEventListener("click", function (e) {
radioButton.checked = true;
});

Favorites

btn-add-to-favorites

JavaScript Function: Adds/removes the product from favorites.

Files: product.js

<button class="btn-add-to-favorites border-0 bg-transparent"
data-urunid="{{ product.id }}">
<i data-lucide="heart" class="icon-favorite"></i>
</button>
// product.js
on(document, "click", ".btn-add-to-favorites", function (e) {
addToFavorite(this); // Add to favorites
});

pnlSidebarCart

JavaScript Function: Renders the cart content on the Bootstrap offcanvas show event.

Files: layout.js

// layout.js
pnlSidebarCart.addEventListener("show.bs.offcanvas", function () {
renderSidebarCart(); // Load cart content
});

Filtering & Listing

urunlistesi

JavaScript Function: Container for the infinite scroll library - new products are appended here.

Files: list.js

<div class="row row-cols-2 row-cols-md-3 urunlistesi">
{% for product in products %}
{% include '_product_card.twig.php' %}
{% endfor %}
</div>
// list.js
var elem = document.querySelector(".urunlistesi");
let infScroll = new InfiniteScroll(elem, {
append: ".pnlurun-item"
});

pnlurun-item

JavaScript Function: Product card appended by infinite scroll.

Files: list.js

<div class="pnlurun-item">
{% include '_product_card.twig.php' %}
</div>
// list.js
let infScroll = new InfiniteScroll(elem, {
append: ".pnlurun-item" // New products are appended here
});

JavaScript Function: Keyup event handler for searching within filters.

Files: list.js

<input class="filter-input filter-search form-control"
placeholder="{{ 'ara' | t }}">
// list.js
filter_inputs.forEach((input) => {
input.addEventListener("keyup", filterList);
});

filters

JavaScript Function: Filter item is shown/hidden based on search results.

Files: list.js

<a class="filters text-decoration-none"
href="{{ filter.url }}"
data-search="{{ filter.label }}">
{{ filter.label }}
</a>
// list.js
function filterList() {
list.forEach((item) => {
if (text.includes(filter)) {
item.style.display = ''; // Show
} else {
item.style.display = 'none'; // Hide
}
});
}

Price Display

price

JavaScript Function: Price display element - innerHTML is updated when the variant changes.

Files: product.js, product_detail.js

<h4 class="price">{{ product.first_variant_price_text }}</h4>
// product.js
var price = document.querySelector(".product-card-body .price");
price.innerHTML = selector.dataset.price;

alternate-price

JavaScript Function: Alternative currency price is updated when the variant changes.

Files: product.js, product_detail.js

// product.js
alternate_price.innerHTML = selector.dataset.alternatePrice;

Lazy Loading

lazyload

JavaScript Function: Used by the LazySizes library to load images when they enter the viewport.

Files: All template files

<img class="lazyload"
data-src="{{ product.med_image }}"
src="{{ product.min_image }}"
alt="{{ product.title }}">

Custom Container Selectors

pro / card

JavaScript Function: Used with closest() to find the product container.

Files: product.js

// product.js
var pnlContainer = selector.closest(".pro") || selector.closest(".card");

Important Data Attributes

Critical data attributes for JavaScript functionality:

{# Product Data Attributes #}
<div class="product-card-body"
data-product-id="{{ product.id }}"
data-urunid="{{ product.id }}">
</div>

{# Variant Data Attributes #}
<div class="productThumbSelector"
data-renk="{{ variant.id }}"
data-min-src="{{ variant.image_min }}"
data-med-src="{{ variant.image_thumb }}"
data-price="{{ variant.price | price_with_currency }}"
data-alternate-price="{{ variant.alternate_price }}">
</div>

{# Quantity Data Attributes #}
<input class="count"
data-min-qty="{{ product.minimum_buy_qty }}"
data-max-qty="{{ product.maximum_buy_qty }}"
data-package-qty="{{ product.package_qty }}">

{# Lazy Load Data Attributes #}
<img class="lazyload"
data-src="{{ product.med_image }}"
data-min-src="{{ product.min_image }}"
data-med-src="{{ product.med_image }}">

{# Gallery Data Attributes #}
<div class="productVariantSelect"
data-galeri_index="{{ iiVariant }}"
data-varyantid="{{ variant.id }}">
</div>

{# Filter Data Attributes #}
<a class="filters" data-search="{{ filter.label }}">
{{ filter.label }}
</a>

JavaScript Function Summary

Class NameJavaScript FunctionEvent TypeFile
productThumbSelectorVariant selection, image/price updateclickproduct.js
selectedThumbSelected state markingclassListproduct.js
productVariantSelectDetail page variant selectionclickproduct_detail.js
mainImgMain image updatesrc, classListproduct.js
product-card-bodyVariant image preloadmouseenterproduct.js
urungorselleriGallery thumbnail selectionclickproduct_detail.js
product-image-clickableImage modal openclickproduct_detail.js
product-video-btnVideo modal openclickproduct.js
frm-add-to-cartAdd to cartsubmitproduct.js
btn-qty-increaseQuantity increaseclickproduct_shared.js
btn-qty-decreaseQuantity decreaseclickproduct_shared.js
pc-measure-boxMeasure selectionclickproduct.js
pc-activeActive measure markingclassListproduct.js
measure-boxDetail measure selectionclickproduct_detail.js
btn-add-to-favoritesAdd/remove from favoritesclickproduct.js
pnlSidebarCartCart offcanvas renderbootstrap eventlayout.js
urunlistesiInfinite scroll containerplugin initlist.js
pnlurun-itemInfinite scroll appendplugin appendlist.js
filter-searchFilter searchkeyuplist.js
filtersFilter show/hideclassList togglelist.js
zoomImage zoomplugin initproduct_detail.js
pricePrice updateinnerHTMLproduct.js
alternate-priceAlternate price updateinnerHTMLproduct.js
lazyloadLazy loadinglibraryLazySizes
pro / cardContainer selectorclosest()product.js

These classes are used for all JavaScript interactions in the SerB2B system. When adding new features, use these classes or create new ones following a similar naming convention.