feat: implement WooCommerce storefront foundation

This commit is contained in:
XShop
2026-09-13 19:51:31 +03:30
parent f11e1d650d
commit 00439fcea9
18 changed files with 871 additions and 65 deletions
+159
View File
@@ -0,0 +1,159 @@
/* XShop WooCommerce — tokens extension + shop/product/cart/account. Logical props only. */
/* ---- Woo tokens ---- */
:root {
--xshop-wc-badge-sale: var(--xshop-danger);
--xshop-wc-badge-stock: #6b7280;
--xshop-wc-price: var(--xshop-text);
--xshop-wc-price-sale: var(--xshop-danger);
--xshop-wc-price-regular: var(--xshop-muted);
}
/* ---- Shop toolbar ---- */
.xshop-shop-toolbar { display:flex; flex-wrap:wrap; gap:var(--xshop-space-3); align-items:center; justify-content:space-between; margin-block-end:var(--xshop-space-6); }
.xshop-shop-toolbar__left, .xshop-shop-toolbar__right { display:flex; gap:var(--xshop-space-3); align-items:center; flex-wrap:wrap; }
.xshop-shop-toolbar .woocommerce-result-count { margin:0; font-size:var(--xshop-text-sm); color:var(--xshop-muted); }
.xshop-shop-toolbar .woocommerce-ordering { margin:0; }
.xshop-shop-toolbar .woocommerce-ordering select { min-inline-size:180px; }
/* ---- Product grid (CSS grid, overrides Woo <ul>) ---- */
.xshop-products { display:grid; gap:var(--xshop-space-4); grid-template-columns:repeat(4,1fr); list-style:none; margin:0; padding:0; }
.xshop-products .xshop-product-card { block-size:100%; }
@media (max-width:1280px){ .xshop-products{ grid-template-columns:repeat(3,1fr);} }
@media (max-width:1024px){ .xshop-products{ grid-template-columns:repeat(3,1fr);} }
@media (max-width:768px){ .xshop-products{ grid-template-columns:repeat(2,1fr);} }
@media (max-width:480px){ .xshop-products{ grid-template-columns:1fr;} }
/* Woo default ul.products mapped to our grid when inside .xshop theme */
.woocommerce ul.products.xshop-products { display:grid; }
.woocommerce ul.products::before, .woocommerce ul.products::after { display:none; }
/* ---- Product card ---- */
.xshop-product-card { background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); overflow:hidden; display:flex; flex-direction:column; transition:box-shadow var(--xshop-transition), transform var(--xshop-transition); position:relative; }
.xshop-product-card:hover{ box-shadow:var(--xshop-shadow-md); }
.xshop-product-card__media { position:relative; aspect-ratio:1/1; background:var(--xshop-surface); overflow:hidden; display:block; }
.xshop-product-card__media img{ inline-size:100%; block-size:100%; object-fit:cover; display:block; transition:transform var(--xshop-transition-slow); }
.xshop-product-card:hover .xshop-product-card__media img{ transform:scale(1.03); }
.xshop-product-card__badges{ position:absolute; inset-block-start:var(--xshop-space-2); inset-inline-start:var(--xshop-space-2); display:flex; flex-direction:column; gap:var(--xshop-space-1); z-index:2; }
.xshop-badge{ display:inline-flex; align-items:center; padding:2px 8px; border-radius:var(--xshop-radius-full); font-size:var(--xshop-text-xs); font-weight:var(--xshop-weight-bold); line-height:1.4; }
.xshop-badge--sale{ background:var(--xshop-wc-badge-sale); color:#fff; }
.xshop-badge--outofstock{ background:var(--xshop-wc-badge-stock); color:#fff; }
.xshop-badge--featured{ background:var(--xshop-warning); color:#111; }
.xshop-product-card__body{ padding:var(--xshop-space-3) var(--xshop-space-3) var(--xshop-space-4); display:flex; flex-direction:column; gap:var(--xshop-space-2); flex:1; }
.xshop-product-card__title{ font-size:var(--xshop-text-sm); font-weight:var(--xshop-weight-medium); line-height:1.45; margin:0; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; min-block-size:2.9em; }
.xshop-product-card__title a{ color:var(--xshop-text); text-decoration:none; }
.xshop-product-card__title a:hover{ color:var(--xshop-link); }
.xshop-product-card__price{ font-weight:var(--xshop-weight-bold); font-size:var(--xshop-text-sm); color:var(--xshop-wc-price); }
.xshop-product-card__price del{ color:var(--xshop-wc-price-regular); font-weight:400; font-size:0.9em; margin-inline-end:6px; }
.xshop-product-card__price ins{ text-decoration:none; color:var(--xshop-wc-price-sale); }
.xshop-product-card__meta{ display:flex; align-items:center; gap:var(--xshop-space-2); flex-wrap:wrap; font-size:var(--xshop-text-xs); color:var(--xshop-muted); }
.xshop-product-card__rating{ color:#f59e0b; letter-spacing:1px; }
.xshop-product-card__actions{ margin-block-start:auto; display:flex; gap:var(--xshop-space-2); align-items:center; }
.xshop-product-card__actions .button{ flex:1; text-align:center; }
.xshop-product-card__hook{ font-size:var(--xshop-text-xs); color:var(--xshop-muted); border:1px dashed var(--xshop-border); border-radius:var(--xshop-radius-sm); padding:2px 6px; }
/* Add-to-cart button inside card inherits .xshop-btn but Woo uses .button */
.xshop-product-card .button{ display:inline-flex; align-items:center; justify-content:center; padding:var(--xshop-space-2) var(--xshop-space-3); border-radius:var(--xshop-radius-sm); background:var(--xshop-primary); color:#fff; font-weight:var(--xshop-weight-medium); font-size:var(--xshop-text-sm); text-decoration:none; border:1px solid var(--xshop-primary); transition:background var(--xshop-transition); }
.xshop-product-card .button:hover{ background:var(--xshop-primary-600); color:#fff; }
.xshop-product-card .added_to_cart{ font-size:var(--xshop-text-xs); padding:4px 8px; }
/* ---- Single product ---- */
.xshop-product{ display:grid; grid-template-columns: 1.05fr 1fr; gap:var(--xshop-space-8); align-items:start; }
@media (max-width:900px){ .xshop-product{ grid-template-columns:1fr; } }
.xshop-product__gallery{ background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); overflow:hidden; padding:var(--xshop-space-2); }
.xshop-product__gallery-main{ aspect-ratio:1/1; background:var(--xshop-surface); border-radius:var(--xshop-radius-sm); overflow:hidden; position:relative; }
.xshop-product__gallery-main img{ inline-size:100%; block-size:100%; object-fit:contain; }
.xshop-product__thumbs{ display:flex; gap:var(--xshop-space-2); margin-block-start:var(--xshop-space-2); overflow-x:auto; padding-block-end:4px; }
.xshop-product__thumb{ flex:0 0 64px; inline-size:64px; block-size:64px; border:2px solid transparent; border-radius:var(--xshop-radius-sm); overflow:hidden; background:var(--xshop-surface); cursor:pointer; padding:0; }
.xshop-product__thumb.is-active{ border-color:var(--xshop-primary); }
.xshop-product__thumb img{ inline-size:100%; block-size:100%; object-fit:cover; }
.xshop-product__summary{ display:flex; flex-direction:column; gap:var(--xshop-space-4); }
.xshop-product__title{ font-size:var(--xshop-text-3xl); line-height:1.2; margin:0; }
.xshop-product__meta{ font-size:var(--xshop-text-sm); color:var(--xshop-muted); display:flex; flex-wrap:wrap; gap:var(--xshop-space-3); }
.xshop-product__price{ font-size:var(--xshop-text-2xl); font-weight:var(--xshop-weight-bold); }
.xshop-product__price del{ font-size:0.6em; color:var(--xshop-muted); }
.xshop-product__stock.in-stock{ color:var(--xshop-success); font-weight:var(--xshop-weight-medium); }
.xshop-product__stock.out-of-stock{ color:var(--xshop-danger); font-weight:var(--xshop-weight-medium); }
.xshop-product__short{ color:var(--xshop-muted); line-height:var(--xshop-leading-relaxed); }
.xshop-product__form .variations{ inline-size:100%; }
.xshop-product__form .variations th{ text-align:start; padding-inline-end:var(--xshop-space-2); font-weight:var(--xshop-weight-medium); }
.xshop-product__form .value select{ inline-size:100%; }
.xshop-product__actions{ display:flex; flex-wrap:wrap; gap:var(--xshop-space-3); align-items:center; }
.xshop-qty{ display:inline-flex; align-items:center; border:1px solid var(--xshop-input-border); border-radius:var(--xshop-radius-sm); overflow:hidden; }
.xshop-qty button{ inline-size:38px; block-size:38px; border:0; background:var(--xshop-surface); cursor:pointer; font-size:18px; }
.xshop-qty input{ inline-size:56px; text-align:center; border:0; border-inline:1px solid var(--xshop-border); block-size:38px; }
.xshop-buy-now{ background:var(--xshop-secondary); border-color:var(--xshop-secondary); }
.xshop-buy-now:hover{ background:#000; }
/* Tabs */
.woocommerce-tabs{ margin-block-start:var(--xshop-space-8); }
.woocommerce-tabs .tabs{ display:flex; gap:var(--xshop-space-2); border-block-end:2px solid var(--xshop-border); padding:0; margin:0 0 var(--xshop-space-4); list-style:none; overflow-x:auto; }
.woocommerce-tabs .tabs li a{ display:block; padding:var(--xshop-space-2) var(--xshop-space-4); font-weight:var(--xshop-weight-medium); color:var(--xshop-muted); text-decoration:none; border-block-end:2px solid transparent; margin-block-end:-2px; }
.woocommerce-tabs .tabs li.active a{ color:var(--xshop-text); border-color:var(--xshop-primary); }
.woocommerce-Tabs-panel{ background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); padding:var(--xshop-space-6); }
/* Related / upsells */
.related, .upsells, .cross-sells{ margin-block-start:var(--xshop-space-10); }
.related > h2, .upsells > h2, .cross-sells > h2{ font-size:var(--xshop-text-2xl); margin:0 0 var(--xshop-space-4); }
/* ---- Cart ---- */
.woocommerce-cart .woocommerce{ display:grid; gap:var(--xshop-space-6); }
.shop_table{ inline-size:100%; border-collapse:collapse; background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); overflow:hidden; }
.shop_table th, .shop_table td{ padding:var(--xshop-space-3) var(--xshop-space-4); text-align:start; border-block-end:1px solid var(--xshop-border); vertical-align:middle; }
.shop_table th{ background:var(--xshop-surface); font-weight:var(--xshop-weight-bold); font-size:var(--xshop-text-sm); }
.shop_table .product-thumbnail img{ inline-size:64px; block-size:64px; object-fit:cover; border-radius:var(--xshop-radius-sm); }
.shop_table .product-quantity .quantity{ display:flex; justify-content:flex-start; }
.shop_table .coupon{ display:flex; gap:var(--xshop-space-2); flex-wrap:wrap; align-items:center; }
@media (max-width:768px){
.shop_table, .shop_table thead, .shop_table tbody, .shop_table th, .shop_table td, .shop_table tr{ display:block; }
.shop_table thead{ display:none; }
.shop_table tr{ border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-sm); margin-block-end:var(--xshop-space-3); overflow:hidden; }
.shop_table td{ border:0; display:flex; justify-content:space-between; gap:var(--xshop-space-4); }
.shop_table td::before{ content:attr(data-title); font-weight:var(--xshop-weight-medium); color:var(--xshop-muted); }
.shop_table td.product-thumbnail, .shop_table td.actions{ display:flex; justify-content:flex-start; }
.shop_table td.actions::before{ content:none; }
}
.cart_totals{ background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); padding:var(--xshop-space-6); }
.cart_totals h2{ font-size:var(--xshop-text-xl); margin:0 0 var(--xshop-space-4); }
/* ---- Mini cart ---- */
.xshop-mini-cart{ background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); padding:var(--xshop-space-4); min-inline-size:300px; }
.xshop-mini-cart__count{ background:var(--xshop-primary); color:#fff; border-radius:var(--xshop-radius-full); padding:0 7px; font-size:var(--xshop-text-xs); font-weight:var(--xshop-weight-bold); }
.xshop-mini-cart__list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--xshop-space-3); max-block-size:50vh; overflow:auto; }
.xshop-mini-cart__item{ display:grid; grid-template-columns:48px 1fr auto; gap:var(--xshop-space-2); align-items:center; }
.xshop-mini-cart__item img{ inline-size:48px; block-size:48px; object-fit:cover; border-radius:var(--xshop-radius-sm); }
.xshop-mini-cart__empty{ color:var(--xshop-muted); font-size:var(--xshop-text-sm); text-align:center; padding:var(--xshop-space-6) 0; }
.widget_shopping_cart_content{ display:flex; flex-direction:column; gap:var(--xshop-space-3); }
/* ---- Account ---- */
.woocommerce-account .woocommerce{ display:grid; grid-template-columns:220px 1fr; gap:var(--xshop-space-6); align-items:start; }
@media (max-width:900px){ .woocommerce-account .woocommerce{ grid-template-columns:1fr; } }
.woocommerce-MyAccount-navigation{ background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); overflow:hidden; }
.woocommerce-MyAccount-navigation ul{ list-style:none; margin:0; padding:0; }
.woocommerce-MyAccount-navigation li{ border-block-end:1px solid var(--xshop-border); }
.woocommerce-MyAccount-navigation li:last-child{ border:0; }
.woocommerce-MyAccount-navigation a{ display:block; padding:var(--xshop-space-3) var(--xshop-space-4); color:var(--xshop-text); text-decoration:none; }
.woocommerce-MyAccount-navigation li.is-active a{ background:var(--xshop-surface); font-weight:var(--xshop-weight-bold); color:var(--xshop-primary); }
.woocommerce-MyAccount-content{ background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); padding:var(--xshop-space-6); }
.woocommerce-MyAccount-content h2, .woocommerce-MyAccount-content h3{ margin-block-start:0; }
.woocommerce-form-login, .woocommerce-form-register{ background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); padding:var(--xshop-space-6); }
/* ---- Checkout foundation ---- */
.woocommerce-checkout .col2-set{ display:grid; grid-template-columns:1fr 1fr; gap:var(--xshop-space-6); }
@media (max-width:900px){ .woocommerce-checkout .col2-set{ grid-template-columns:1fr; } }
.woocommerce-billing-fields, .woocommerce-shipping-fields, .woocommerce-additional-fields{ background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); padding:var(--xshop-space-4); margin-block-end:var(--xshop-space-4); }
.woocommerce-checkout-review-order{ background:var(--xshop-card); border:1px solid var(--xshop-border); border-radius:var(--xshop-radius-md); padding:var(--xshop-space-4); }
#payment{ background:var(--xshop-surface); border-radius:var(--xshop-radius-md); padding:var(--xshop-space-4); }
/* Forms */
.woocommerce form .form-row input, .woocommerce form .form-row textarea, .woocommerce form .form-row select{ background:var(--xshop-input-bg); border-color:var(--xshop-input-border); color:var(--xshop-input-text); }
.woocommerce form .form-row label{ font-weight:var(--xshop-weight-medium); font-size:var(--xshop-text-sm); }
/* Woo notices mapped */
.woocommerce-message, .woocommerce-info, .woocommerce-error{ border-radius:var(--xshop-radius-sm); border:1px solid var(--xshop-border); }
/* Empty states */
.xshop-empty--shop{ text-align:center; }
/* Star rating fallback */
.star-rating{ color:#f59e0b; }
+45
View File
@@ -0,0 +1,45 @@
/**
* XShop Product Gallery — vanilla, accessible, RTL-safe.
* Swaps main image on thumbnail click/keyboard.
*/
(function () {
'use strict';
function initGalleries() {
var galleries = document.querySelectorAll('[data-xshop-gallery]');
galleries.forEach(function (gal) {
var mainImg = gal.querySelector('[data-xshop-main-image]');
var thumbs = gal.querySelectorAll('.xshop-product__thumb');
if (!mainImg || !thumbs.length) return;
thumbs.forEach(function (btn) {
btn.addEventListener('click', function () {
var large = btn.getAttribute('data-large');
if (!large) return;
mainImg.setAttribute('src', large);
thumbs.forEach(function (t) {
t.classList.remove('is-active');
t.setAttribute('aria-selected', 'false');
});
btn.classList.add('is-active');
btn.setAttribute('aria-selected', 'true');
});
btn.addEventListener('keydown', function (e) {
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight' && e.key !== 'Home' && e.key !== 'End') return;
e.preventDefault();
var idx = Array.prototype.indexOf.call(thumbs, btn);
var next;
if (e.key === 'Home') next = 0;
else if (e.key === 'End') next = thumbs.length - 1;
else if (e.key === 'ArrowLeft') next = idx > 0 ? idx - 1 : thumbs.length - 1;
else next = idx < thumbs.length - 1 ? idx + 1 : 0;
thumbs[next].focus();
thumbs[next].click();
});
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initGalleries);
} else {
initGalleries();
}
})();