You've already forked wc-bootstrap
Implement Phase 2: product archive and shop loop templates (Bootstrap 5)
Add 15 Twig template overrides for the product archive and shop loop: - archive-product: 3+9 grid layout with optional filter sidebar - content-product: card component with hook-based content injection - content-product-cat: category card with thumbnail - product-searchform: input-group with search icon button - loop/loop-start, loop-end: responsive row-cols grid - loop/header: archive title with description hook - loop/result-count: showing X-Y of Z with aria-relevant - loop/orderby: form-select-sm sort dropdown - loop/pagination: delegates to components/pagination.html.twig - loop/no-products-found: alert-info empty state - loop/add-to-cart: btn-primary-sm with AJAX data attributes - loop/price: fw-semibold with sale/regular markup - loop/rating: Bootstrap Icon stars (full, half, empty) - loop/sale-flash: badge bg-danger positioned overlay CSS additions: product card hover, sale badge z-index, star rating sizing, price del/ins styling, WooCommerce grid reset. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
30
PLAN.md
30
PLAN.md
@@ -531,21 +531,21 @@ Track completion per file. Mark with `[x]` when done.
|
||||
|
||||
### Phase 2 -- Archive & Loop
|
||||
|
||||
- [ ] `archive-product.html.twig`
|
||||
- [ ] `content-product.html.twig`
|
||||
- [ ] `content-product-cat.html.twig`
|
||||
- [ ] `product-searchform.html.twig`
|
||||
- [ ] `loop/loop-start.html.twig`
|
||||
- [ ] `loop/loop-end.html.twig`
|
||||
- [ ] `loop/header.html.twig`
|
||||
- [ ] `loop/result-count.html.twig`
|
||||
- [ ] `loop/orderby.html.twig`
|
||||
- [ ] `loop/pagination.html.twig`
|
||||
- [ ] `loop/no-products-found.html.twig`
|
||||
- [ ] `loop/add-to-cart.html.twig`
|
||||
- [ ] `loop/price.html.twig`
|
||||
- [ ] `loop/rating.html.twig`
|
||||
- [ ] `loop/sale-flash.html.twig`
|
||||
- [x] `archive-product.html.twig`
|
||||
- [x] `content-product.html.twig`
|
||||
- [x] `content-product-cat.html.twig`
|
||||
- [x] `product-searchform.html.twig`
|
||||
- [x] `loop/loop-start.html.twig`
|
||||
- [x] `loop/loop-end.html.twig`
|
||||
- [x] `loop/header.html.twig`
|
||||
- [x] `loop/result-count.html.twig`
|
||||
- [x] `loop/orderby.html.twig`
|
||||
- [x] `loop/pagination.html.twig`
|
||||
- [x] `loop/no-products-found.html.twig`
|
||||
- [x] `loop/add-to-cart.html.twig`
|
||||
- [x] `loop/price.html.twig`
|
||||
- [x] `loop/rating.html.twig`
|
||||
- [x] `loop/sale-flash.html.twig`
|
||||
|
||||
### Phase 3 -- Single Product
|
||||
|
||||
|
||||
@@ -88,6 +88,71 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Product Cards
|
||||
Hover effects and layout for product loop cards.
|
||||
========================================================================== */
|
||||
|
||||
.product.card {
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
|
||||
.product.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--bs-box-shadow) !important;
|
||||
}
|
||||
|
||||
/* Product image in card */
|
||||
.product.card img {
|
||||
object-fit: cover;
|
||||
aspect-ratio: 1 / 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Sale Badge
|
||||
Positioning for the sale overlay badge on product cards.
|
||||
========================================================================== */
|
||||
|
||||
.onsale {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Star Rating
|
||||
Consistent sizing for Bootstrap Icon star ratings.
|
||||
========================================================================== */
|
||||
|
||||
.wc-star-rating .bi {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Price Styling
|
||||
Override WooCommerce default price markup with Bootstrap-aligned styles.
|
||||
========================================================================== */
|
||||
|
||||
.price del {
|
||||
text-decoration: line-through;
|
||||
color: var(--bs-secondary-color);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.price ins {
|
||||
text-decoration: none;
|
||||
color: var(--bs-danger);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
WooCommerce Grid Override
|
||||
Reset WooCommerce's default grid to let Bootstrap handle layout.
|
||||
========================================================================== */
|
||||
|
||||
.woocommerce ul.products {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Dark Mode Overrides
|
||||
Fix any plugin elements that don't adapt to Bootstrap's dark mode.
|
||||
|
||||
64
templates/archive-product.html.twig
Normal file
64
templates/archive-product.html.twig
Normal file
@@ -0,0 +1,64 @@
|
||||
{#
|
||||
# Product Archive / Shop Page (Bootstrap 5 Override)
|
||||
#
|
||||
# Main template for the shop page, product category, tag, and attribute archives.
|
||||
# Uses a 3+9 column layout with a filter sidebar and product grid.
|
||||
#
|
||||
# Expected context:
|
||||
# page_title - Archive page title
|
||||
# has_products - Whether the loop has products
|
||||
# products - Array of product objects for the loop
|
||||
# sidebar_content - Pre-rendered sidebar HTML
|
||||
#
|
||||
# WooCommerce PHP equivalent: archive-product.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
{% extends "base.html.twig" %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{{ do_action('woocommerce_before_main_content') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ do_action('woocommerce_shop_loop_header') }}
|
||||
|
||||
{% if has_products is defined and has_products %}
|
||||
{{ do_action('woocommerce_before_shop_loop') }}
|
||||
|
||||
<div class="row">
|
||||
{# Sidebar with filters #}
|
||||
{% if sidebar_content is defined and sidebar_content %}
|
||||
<aside class="col-lg-3 mb-4 mb-lg-0">
|
||||
{% include 'global/sidebar.html.twig' %}
|
||||
</aside>
|
||||
<div class="col-lg-9">
|
||||
{% else %}
|
||||
<div class="col-12">
|
||||
{% endif %}
|
||||
|
||||
{{ woocommerce_product_loop_start() }}
|
||||
|
||||
{% if products is defined %}
|
||||
{% for product in products %}
|
||||
{% include 'content-product.html.twig' with { product: product } %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{{ woocommerce_product_loop_end() }}
|
||||
|
||||
{{ do_action('woocommerce_after_shop_loop') }}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ do_action('woocommerce_no_products_found') }}
|
||||
{% endif %}
|
||||
|
||||
{{ do_action('woocommerce_after_main_content') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
{{ do_action('woocommerce_sidebar') }}
|
||||
{% endblock %}
|
||||
34
templates/content-product-cat.html.twig
Normal file
34
templates/content-product-cat.html.twig
Normal file
@@ -0,0 +1,34 @@
|
||||
{#
|
||||
# Product Category Content in Loop (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders a product category card within the shop loop grid.
|
||||
#
|
||||
# Expected context:
|
||||
# category - Product category object with:
|
||||
# .name - Category name
|
||||
# .count - Number of products
|
||||
# .link - Category URL
|
||||
# .thumbnail - Category thumbnail HTML
|
||||
#
|
||||
# WooCommerce PHP equivalent: content-product-cat.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
<div class="col">
|
||||
<article class="card h-100 shadow-sm product-category">
|
||||
{{ do_action('woocommerce_before_subcategory', category) }}
|
||||
|
||||
<div class="position-relative overflow-hidden">
|
||||
{{ do_action('woocommerce_before_subcategory_title', category) }}
|
||||
</div>
|
||||
|
||||
<div class="card-body text-center">
|
||||
{{ do_action('woocommerce_shop_loop_subcategory_title', category) }}
|
||||
{{ do_action('woocommerce_after_subcategory_title', category) }}
|
||||
</div>
|
||||
|
||||
{{ do_action('woocommerce_after_subcategory', category) }}
|
||||
</article>
|
||||
</div>
|
||||
44
templates/content-product.html.twig
Normal file
44
templates/content-product.html.twig
Normal file
@@ -0,0 +1,44 @@
|
||||
{#
|
||||
# Product Content in Loop (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders a single product card within the shop loop grid.
|
||||
# Uses Bootstrap 5 card component with stretched-link.
|
||||
#
|
||||
# Expected context:
|
||||
# product - WC_Product object with:
|
||||
# .name - Product name
|
||||
# .permalink - Product URL
|
||||
# .image - Product thumbnail HTML
|
||||
# .price_html - Formatted price HTML
|
||||
# .rating_html - Star rating HTML
|
||||
# .is_on_sale - Whether product is on sale
|
||||
# .add_to_cart - Add-to-cart button context
|
||||
#
|
||||
# WooCommerce PHP equivalent: content-product.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
<div class="col">
|
||||
<article class="card h-100 shadow-sm product">
|
||||
{{ do_action('woocommerce_before_shop_loop_item') }}
|
||||
|
||||
{# Product image with sale badge overlay #}
|
||||
<div class="position-relative overflow-hidden">
|
||||
{{ do_action('woocommerce_before_shop_loop_item_title') }}
|
||||
</div>
|
||||
|
||||
<div class="card-body d-flex flex-column">
|
||||
{# Product title #}
|
||||
{{ do_action('woocommerce_shop_loop_item_title') }}
|
||||
|
||||
{# Rating and price #}
|
||||
{{ do_action('woocommerce_after_shop_loop_item_title') }}
|
||||
</div>
|
||||
|
||||
<div class="card-footer bg-transparent border-0 pt-0 pb-3 px-3">
|
||||
{{ do_action('woocommerce_after_shop_loop_item') }}
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
47
templates/loop/add-to-cart.html.twig
Normal file
47
templates/loop/add-to-cart.html.twig
Normal file
@@ -0,0 +1,47 @@
|
||||
{#
|
||||
# Loop Add to Cart Button (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders the add-to-cart button within the product loop.
|
||||
#
|
||||
# Expected context:
|
||||
# product - WC_Product object with:
|
||||
# .add_to_cart_url() - Add to cart URL
|
||||
# .add_to_cart_text() - Button label
|
||||
# .is_purchasable() - Whether product can be purchased
|
||||
# .is_in_stock() - Whether product is in stock
|
||||
# .supports('ajax_add_to_cart') - Whether AJAX add to cart is supported
|
||||
# .get_id() - Product ID
|
||||
# args - Array with:
|
||||
# .quantity - Quantity (default: 1)
|
||||
# .class - CSS classes
|
||||
# .attributes - Additional HTML attributes
|
||||
# .aria-describedby_text - Accessibility description
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/add-to-cart.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
{% set quantity = args.quantity|default(1) %}
|
||||
{% set btn_class = 'btn btn-primary btn-sm w-100' %}
|
||||
|
||||
<a href="{{ product.add_to_cart_url()|esc_url }}"
|
||||
class="{{ btn_class }} {{ args.class|default('') }}"
|
||||
data-quantity="{{ quantity }}"
|
||||
data-product_id="{{ product.get_id() }}"
|
||||
{% if args.attributes is defined %}
|
||||
{% for attr, value in args.attributes %}
|
||||
{{ attr }}="{{ value|esc_attr }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
aria-label="{{ product.add_to_cart_text()|esc_attr }}"
|
||||
rel="nofollow">
|
||||
{{ product.add_to_cart_text() }}
|
||||
</a>
|
||||
|
||||
{% if args['aria-describedby_text'] is defined and args['aria-describedby_text'] %}
|
||||
<span id="woocommerce_loop_add_to_cart_link_describedby_{{ product.get_id() }}" class="visually-hidden">
|
||||
{{ args['aria-describedby_text'] }}
|
||||
</span>
|
||||
{% endif %}
|
||||
25
templates/loop/header.html.twig
Normal file
25
templates/loop/header.html.twig
Normal file
@@ -0,0 +1,25 @@
|
||||
{#
|
||||
# Product Archive Header (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders the archive page title and optional description.
|
||||
#
|
||||
# Expected context:
|
||||
# show_page_title - Whether to display the title (boolean, filtered)
|
||||
# page_title - Archive page title string
|
||||
# archive_description - Optional archive description HTML
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/header.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
<header class="woocommerce-products-header mb-4">
|
||||
{% if show_page_title is not defined or show_page_title %}
|
||||
<h1 class="woocommerce-products-header__title page-title mb-2">
|
||||
{{ page_title|default('')|esc_html }}
|
||||
</h1>
|
||||
{% endif %}
|
||||
|
||||
{{ do_action('woocommerce_archive_description') }}
|
||||
</header>
|
||||
12
templates/loop/loop-end.html.twig
Normal file
12
templates/loop/loop-end.html.twig
Normal file
@@ -0,0 +1,12 @@
|
||||
{#
|
||||
# Product Loop End (Bootstrap 5 Override)
|
||||
#
|
||||
# Closes the product grid container opened by loop-start.html.twig.
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/loop-end.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
</div>
|
||||
17
templates/loop/loop-start.html.twig
Normal file
17
templates/loop/loop-start.html.twig
Normal file
@@ -0,0 +1,17 @@
|
||||
{#
|
||||
# Product Loop Start (Bootstrap 5 Override)
|
||||
#
|
||||
# Opens the product grid container using Bootstrap 5 row with responsive columns.
|
||||
#
|
||||
# Expected context:
|
||||
# columns - Number of columns (from wc_get_loop_prop)
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/loop-start.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
{% set cols = columns|default(3) %}
|
||||
|
||||
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-{{ cols < 3 ? cols : 3 }} row-cols-lg-{{ cols }} g-4 products">
|
||||
17
templates/loop/no-products-found.html.twig
Normal file
17
templates/loop/no-products-found.html.twig
Normal file
@@ -0,0 +1,17 @@
|
||||
{#
|
||||
# No Products Found (Bootstrap 5 Override)
|
||||
#
|
||||
# Displayed when the product archive has no results.
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/no-products-found.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
<div class="woocommerce-no-products-found">
|
||||
<div class="alert alert-info" role="alert">
|
||||
<i class="bi bi-info-circle me-2" aria-hidden="true"></i>
|
||||
{{ __('No products were found matching your selection.') }}
|
||||
</div>
|
||||
</div>
|
||||
40
templates/loop/orderby.html.twig
Normal file
40
templates/loop/orderby.html.twig
Normal file
@@ -0,0 +1,40 @@
|
||||
{#
|
||||
# Catalog Ordering / Sort Dropdown (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders the product sort-by dropdown as a Bootstrap 5 form-select.
|
||||
#
|
||||
# Expected context:
|
||||
# catalog_orderby_options - Associative array of { value: label } sort options
|
||||
# orderby - Currently selected orderby value
|
||||
# use_label - Whether to display a label (boolean)
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/orderby.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
<form class="woocommerce-ordering d-inline-block" method="get">
|
||||
{% if use_label is defined and use_label %}
|
||||
<label for="woocommerce-orderby" class="form-label visually-hidden">
|
||||
{{ __('Sort by') }}
|
||||
</label>
|
||||
{% endif %}
|
||||
|
||||
<select name="orderby"
|
||||
id="woocommerce-orderby"
|
||||
class="form-select form-select-sm"
|
||||
aria-label="{{ __('Shop order') }}"
|
||||
onchange="this.form.submit()">
|
||||
{% if catalog_orderby_options is defined %}
|
||||
{% for value, label in catalog_orderby_options %}
|
||||
<option value="{{ value|esc_attr }}"{% if value == orderby %} selected{% endif %}>
|
||||
{{ label|esc_html }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</select>
|
||||
|
||||
<input type="hidden" name="paged" value="1" />
|
||||
{{ wc_query_string_form_fields() }}
|
||||
</form>
|
||||
24
templates/loop/pagination.html.twig
Normal file
24
templates/loop/pagination.html.twig
Normal file
@@ -0,0 +1,24 @@
|
||||
{#
|
||||
# Product Pagination (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders pagination for the product archive using Bootstrap 5 pagination component.
|
||||
#
|
||||
# Expected context:
|
||||
# total - Total number of pages
|
||||
# current - Current page number (1-based)
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/pagination.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
{% set max_pages = total|default(1) %}
|
||||
{% set current_page = current|default(1) %}
|
||||
|
||||
{% if max_pages > 1 %}
|
||||
{% include 'components/pagination.html.twig' with {
|
||||
current_page: current_page,
|
||||
max_pages: max_pages
|
||||
} %}
|
||||
{% endif %}
|
||||
21
templates/loop/price.html.twig
Normal file
21
templates/loop/price.html.twig
Normal file
@@ -0,0 +1,21 @@
|
||||
{#
|
||||
# Loop Product Price (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders the product price within the shop loop.
|
||||
# Price HTML is pre-formatted by WooCommerce (includes sale/regular markup).
|
||||
#
|
||||
# Expected context:
|
||||
# product - WC_Product object with:
|
||||
# .get_price_html() - Formatted price HTML (includes <del>/<ins> for sales)
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/price.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
{% if product is defined %}
|
||||
<span class="price fs-6 fw-semibold">
|
||||
{{ product.get_price_html()|raw }}
|
||||
</span>
|
||||
{% endif %}
|
||||
40
templates/loop/rating.html.twig
Normal file
40
templates/loop/rating.html.twig
Normal file
@@ -0,0 +1,40 @@
|
||||
{#
|
||||
# Loop Product Rating (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders star ratings for products in the shop loop.
|
||||
#
|
||||
# Expected context:
|
||||
# product - WC_Product object with:
|
||||
# .get_average_rating() - Average rating (0-5)
|
||||
# .get_review_count() - Number of reviews
|
||||
# reviews_enabled - Whether reviews/ratings are enabled (boolean)
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/rating.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
{% if reviews_enabled is not defined or reviews_enabled %}
|
||||
{% if product is defined and product.get_average_rating() > 0 %}
|
||||
{% set rating = product.get_average_rating() %}
|
||||
{% set count = product.get_review_count() %}
|
||||
|
||||
<div class="wc-star-rating d-flex align-items-center gap-1 mb-1"
|
||||
role="img"
|
||||
aria-label="{{ __('%s out of 5 stars')|format(rating) }}">
|
||||
{% for i in 1..5 %}
|
||||
{% if i <= rating|round(0, 'floor') %}
|
||||
<i class="bi bi-star-fill text-warning" aria-hidden="true"></i>
|
||||
{% elseif i - rating < 1 %}
|
||||
<i class="bi bi-star-half text-warning" aria-hidden="true"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-star text-warning" aria-hidden="true"></i>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if count > 0 %}
|
||||
<small class="text-body-secondary">({{ count }})</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
33
templates/loop/result-count.html.twig
Normal file
33
templates/loop/result-count.html.twig
Normal file
@@ -0,0 +1,33 @@
|
||||
{#
|
||||
# Result Count (Bootstrap 5 Override)
|
||||
#
|
||||
# Displays the "Showing X-Y of Z results" text.
|
||||
#
|
||||
# Expected context:
|
||||
# total - Total number of products
|
||||
# per_page - Products per page
|
||||
# current - Current page number
|
||||
# orderedby - Whether results are currently sorted (optional)
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/result-count.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
<p class="woocommerce-result-count text-body-secondary mb-0"
|
||||
role="alert"
|
||||
aria-relevant="all"
|
||||
{% if orderedby is defined and orderedby %}data-is-sorted-by="true"{% endif %}>
|
||||
{% if total is defined %}
|
||||
{% set first = ((current|default(1) - 1) * per_page|default(total)) + 1 %}
|
||||
{% set last = current|default(1) * per_page|default(total) %}
|
||||
{% if last > total %}{% set last = total %}{% endif %}
|
||||
|
||||
{% if total <= per_page|default(total) or total == 0 %}
|
||||
{{ _n('Showing the single result', 'Showing all %d results', total)|format(total) }}
|
||||
{% else %}
|
||||
{{ __('Showing %1$d–%2$d of %3$d results')|format(first, last, total) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
21
templates/loop/sale-flash.html.twig
Normal file
21
templates/loop/sale-flash.html.twig
Normal file
@@ -0,0 +1,21 @@
|
||||
{#
|
||||
# Sale Badge (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders a sale badge overlay on product cards.
|
||||
#
|
||||
# Expected context:
|
||||
# product - WC_Product object with:
|
||||
# .is_on_sale() - Whether product is currently on sale
|
||||
# post - Global post object
|
||||
#
|
||||
# WooCommerce PHP equivalent: loop/sale-flash.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
{% if product is defined and product.is_on_sale() %}
|
||||
<span class="badge bg-danger position-absolute top-0 start-0 m-2 onsale">
|
||||
{{ __('Sale!') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
32
templates/product-searchform.html.twig
Normal file
32
templates/product-searchform.html.twig
Normal file
@@ -0,0 +1,32 @@
|
||||
{#
|
||||
# Product Search Form (Bootstrap 5 Override)
|
||||
#
|
||||
# Renders the WooCommerce product search form as a Bootstrap 5 input-group.
|
||||
#
|
||||
# Expected context:
|
||||
# index - Unique form index (for multiple search forms on a page)
|
||||
#
|
||||
# WooCommerce PHP equivalent: product-searchform.php
|
||||
#
|
||||
# @package WcBootstrap
|
||||
# @since 0.1.0
|
||||
#}
|
||||
|
||||
{% set field_id = 'woocommerce-product-search-field-' ~ (index|default(0)) %}
|
||||
|
||||
<form role="search" method="get" class="woocommerce-product-search" action="{{ home_url('/') }}">
|
||||
<label class="visually-hidden" for="{{ field_id }}">{{ __('Search for:') }}</label>
|
||||
<div class="input-group">
|
||||
<input type="search"
|
||||
id="{{ field_id }}"
|
||||
class="form-control"
|
||||
placeholder="{{ __('Search products…') }}"
|
||||
value="{{ get_search_query() }}"
|
||||
name="s" />
|
||||
<button type="submit" class="btn btn-outline-primary" aria-label="{{ __('Search') }}">
|
||||
<i class="bi bi-search" aria-hidden="true"></i>
|
||||
<span class="d-none d-sm-inline ms-1">{{ __('Search') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<input type="hidden" name="post_type" value="product" />
|
||||
</form>
|
||||
Reference in New Issue
Block a user