chore: complete M1.1 runtime QA and release packaging
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/* XShop base — minimal reset + tokens wiring. Logical properties throughout. */
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html { font-family: var(--xshop-font-sans); line-height: var(--xshop-leading); color: var(--xshop-text); background: var(--xshop-bg); -webkit-font-smoothing: antialiased; }
|
||||
body { margin: 0; background: var(--xshop-bg); color: var(--xshop-text); }
|
||||
a { color: var(--xshop-link); text-decoration: none; transition: color var(--xshop-transition); }
|
||||
a:hover { color: var(--xshop-link-hover); }
|
||||
a:focus-visible { outline: 2px solid var(--xshop-focus); outline-offset: 2px; }
|
||||
img { max-width: 100%; height: auto; display: block; }
|
||||
p { margin-block: 0 var(--xshop-space-4); }
|
||||
h1, h2, h3, h4, h5, h6 { line-height: var(--xshop-leading-tight); margin-block: 0 var(--xshop-space-3); font-weight: var(--xshop-weight-bold); overflow-wrap: break-word; }
|
||||
ul, ol { margin: 0; padding: 0; }
|
||||
|
||||
/* Container */
|
||||
.xshop-container { max-width: var(--xshop-container); margin-inline: auto; padding-inline: var(--xshop-space-4); }
|
||||
.xshop-container--narrow { max-width: var(--xshop-container-narrow); }
|
||||
|
||||
/* Skip link */
|
||||
.xshop-skip-link { position: absolute; inset-inline-start: -9999px; inset-block-start: auto; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
||||
.xshop-skip-link:focus { inset-inline-start: var(--xshop-space-4); inset-block-start: var(--xshop-space-4); inline-size: auto; block-size: auto; overflow: visible; clip-path: none; white-space: normal; background: var(--xshop-card); color: var(--xshop-text); padding: var(--xshop-space-2) var(--xshop-space-4); border: 2px solid var(--xshop-focus); border-radius: var(--xshop-radius-sm); z-index: var(--xshop-z-modal); font-weight: var(--xshop-weight-medium); }
|
||||
|
||||
/* Focus */
|
||||
:focus-visible { outline: 2px solid var(--xshop-focus); outline-offset: 2px; }
|
||||
:focus:not(:focus-visible) { outline: none; }
|
||||
|
||||
/* Forms */
|
||||
input, textarea, select, button { font: inherit; }
|
||||
input[type="search"], input[type="text"], input[type="email"], input[type="password"], input[type="url"], input[type="tel"], textarea, select {
|
||||
background: var(--xshop-input-bg); color: var(--xshop-input-text); border: 1px solid var(--xshop-input-border);
|
||||
border-radius: var(--xshop-radius-sm); padding: var(--xshop-space-2) var(--xshop-space-3); inline-size: 100%;
|
||||
}
|
||||
input:focus, textarea:focus, select:focus { outline: 2px solid var(--xshop-focus); outline-offset: 0; border-color: var(--xshop-focus); }
|
||||
|
||||
/* Reduced motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/* XShop Components — buttons, cards, breadcrumbs, pagination, notices, etc. */
|
||||
|
||||
/* Buttons */
|
||||
.xshop-btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--xshop-space-2); padding: var(--xshop-space-2) var(--xshop-space-4); border-radius: var(--xshop-radius-sm); border: 1px solid transparent; font-weight: var(--xshop-weight-medium); font-size: var(--xshop-text-sm); cursor: pointer; transition: background var(--xshop-transition), border-color var(--xshop-transition), color var(--xshop-transition); text-decoration: none; line-height: 1.4; }
|
||||
.xshop-btn--primary { background: var(--xshop-primary); color: #fff; border-color: var(--xshop-primary); }
|
||||
.xshop-btn--primary:hover { background: var(--xshop-primary-600); border-color: var(--xshop-primary-600); color: #fff; }
|
||||
.xshop-btn--secondary { background: var(--xshop-card); color: var(--xshop-text); border-color: var(--xshop-border); }
|
||||
.xshop-btn--secondary:hover { background: var(--xshop-surface); }
|
||||
.xshop-btn--ghost { background: transparent; color: var(--xshop-text); border-color: transparent; }
|
||||
.xshop-btn--ghost:hover { background: var(--xshop-surface); }
|
||||
.xshop-btn:disabled { opacity: .55; cursor: not-allowed; }
|
||||
|
||||
/* Card — post-card, product placeholder, generic */
|
||||
.xshop-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); }
|
||||
.xshop-card:hover { box-shadow: var(--xshop-shadow-md); }
|
||||
.xshop-card__media { aspect-ratio: 16/10; background: var(--xshop-surface); overflow: hidden; }
|
||||
.xshop-card__media img { inline-size: 100%; block-size: 100%; object-fit: cover; }
|
||||
.xshop-card__media--placeholder { display: flex; align-items: center; justify-content: center; color: var(--xshop-muted); font-size: var(--xshop-text-sm); }
|
||||
.xshop-card__body { padding: var(--xshop-space-4); display: flex; flex-direction: column; gap: var(--xshop-space-2); flex: 1; }
|
||||
.xshop-card__title { font-size: var(--xshop-text-base); margin: 0; line-height: var(--xshop-leading-tight); }
|
||||
.xshop-card__title a { color: var(--xshop-text); }
|
||||
.xshop-card__title a:hover { color: var(--xshop-link); }
|
||||
.xshop-card__meta { font-size: var(--xshop-text-sm); color: var(--xshop-muted); display: flex; gap: var(--xshop-space-2); flex-wrap: wrap; }
|
||||
.xshop-card__excerpt { font-size: var(--xshop-text-sm); color: var(--xshop-muted); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
||||
|
||||
/* Breadcrumbs */
|
||||
.xshop-breadcrumbs { font-size: var(--xshop-text-sm); color: var(--xshop-muted); }
|
||||
.xshop-breadcrumbs ol { display: flex; flex-wrap: wrap; gap: var(--xshop-space-2); list-style: none; margin: 0; padding: 0; align-items: center; }
|
||||
.xshop-breadcrumbs li { display: inline-flex; align-items: center; gap: var(--xshop-space-2); }
|
||||
.xshop-breadcrumbs li + li::before { content: "/"; opacity: .5; }
|
||||
[dir="rtl"] .xshop-breadcrumbs li + li::before { content: "/"; }
|
||||
.xshop-breadcrumbs a { color: var(--xshop-muted); }
|
||||
.xshop-breadcrumbs a:hover { color: var(--xshop-text); }
|
||||
.xshop-breadcrumbs [aria-current="page"] { color: var(--xshop-text); font-weight: var(--xshop-weight-medium); }
|
||||
|
||||
/* Pagination */
|
||||
.xshop-pagination { display: flex; justify-content: center; margin-block-start: var(--xshop-space-8); }
|
||||
.xshop-pagination .nav-links { display: flex; gap: var(--xshop-space-1); flex-wrap: wrap; align-items: center; }
|
||||
.xshop-pagination .page-numbers { display: inline-flex; align-items: center; justify-content: center; min-inline-size: 40px; block-size: 40px; padding-inline: var(--xshop-space-2); border: 1px solid var(--xshop-border); border-radius: var(--xshop-radius-sm); background: var(--xshop-card); color: var(--xshop-text); font-size: var(--xshop-text-sm); text-decoration: none; }
|
||||
.xshop-pagination .page-numbers:hover { background: var(--xshop-surface); }
|
||||
.xshop-pagination .page-numbers.current { background: var(--xshop-primary); border-color: var(--xshop-primary); color: #fff; font-weight: var(--xshop-weight-bold); }
|
||||
.xshop-pagination .page-numbers.dots { border-color: transparent; background: transparent; }
|
||||
|
||||
/* Search form */
|
||||
.xshop-search-form { display: flex; gap: var(--xshop-space-2); max-inline-size: 520px; }
|
||||
.xshop-search-form input[type="search"] { flex: 1; }
|
||||
|
||||
/* Empty state */
|
||||
.xshop-empty { text-align: center; padding: var(--xshop-space-12) var(--xshop-space-6); background: var(--xshop-surface); border: 1px solid var(--xshop-border); border-radius: var(--xshop-radius-md); }
|
||||
.xshop-empty__title { font-size: var(--xshop-text-xl); margin: 0 0 var(--xshop-space-2); }
|
||||
.xshop-empty__text { color: var(--xshop-muted); margin: 0 0 var(--xshop-space-4); }
|
||||
|
||||
/* Loading */
|
||||
.xshop-loading { display: inline-flex; align-items: center; gap: var(--xshop-space-2); color: var(--xshop-muted); font-size: var(--xshop-text-sm); }
|
||||
.xshop-loading__spinner { inline-size: 16px; block-size: 16px; border: 2px solid var(--xshop-border); border-block-start-color: var(--xshop-primary); border-radius: 50%; animation: xshop-spin 600ms linear infinite; }
|
||||
@keyframes xshop-spin { to { transform: rotate(360deg); } }
|
||||
@media (prefers-reduced-motion: reduce) { .xshop-loading__spinner { animation: none; } }
|
||||
|
||||
/* Notices */
|
||||
.xshop-notice { padding: var(--xshop-space-3) var(--xshop-space-4); border-radius: var(--xshop-radius-sm); border: 1px solid var(--xshop-border); background: var(--xshop-surface); font-size: var(--xshop-text-sm); }
|
||||
.xshop-notice--success { background: var(--xshop-success-bg); border-color: #86efac; color: #14532d; }
|
||||
[data-theme="dark"] .xshop-notice--success { color: #bbf7d0; }
|
||||
.xshop-notice--warning { background: var(--xshop-warning-bg); border-color: #fcd34d; }
|
||||
.xshop-notice--error { background: var(--xshop-danger-bg); border-color: #fca5a5; }
|
||||
|
||||
/* Comments */
|
||||
.xshop-comments { margin-block-start: var(--xshop-space-8); border-block-start: 1px solid var(--xshop-border); padding-block-start: var(--xshop-space-8); }
|
||||
.xshop-comments__title { font-size: var(--xshop-text-xl); }
|
||||
.xshop-comment-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--xshop-space-4); }
|
||||
.xshop-comment { background: var(--xshop-card); border: 1px solid var(--xshop-border); border-radius: var(--xshop-radius-md); padding: var(--xshop-space-4); }
|
||||
.xshop-comment__meta { font-size: var(--xshop-text-sm); color: var(--xshop-muted); margin-block-end: var(--xshop-space-2); }
|
||||
.xshop-comment__body { font-size: var(--xshop-text-base); }
|
||||
.xshop-comment .children { list-style: none; margin-block-start: var(--xshop-space-4); padding-inline-start: var(--xshop-space-6); display: flex; flex-direction: column; gap: var(--xshop-space-4); border-inline-start: 2px solid var(--xshop-border); }
|
||||
@media (max-width: 640px) { .xshop-comment .children { padding-inline-start: var(--xshop-space-3); } }
|
||||
|
||||
/* Page header */
|
||||
.xshop-page-header { margin-block-end: var(--xshop-space-6); }
|
||||
.xshop-page-header__title { font-size: var(--xshop-text-3xl); margin: 0; }
|
||||
.xshop-page-header__desc { color: var(--xshop-muted); margin-block-start: var(--xshop-space-2); }
|
||||
|
||||
/* 404 */
|
||||
.xshop-404 { text-align: center; padding: var(--xshop-space-16) var(--xshop-space-4); }
|
||||
.xshop-404__code { font-size: 5rem; font-weight: var(--xshop-weight-bold); line-height: 1; color: var(--xshop-border); margin: 0; }
|
||||
.xshop-404__title { font-size: var(--xshop-text-3xl); margin: var(--xshop-space-4) 0 var(--xshop-space-2); }
|
||||
|
||||
/* Promo banner foundation */
|
||||
.xshop-promo { background: var(--xshop-surface); border: 1px solid var(--xshop-border); border-radius: var(--xshop-radius-md); padding: var(--xshop-space-6); display: flex; align-items: center; justify-content: space-between; gap: var(--xshop-space-4); flex-wrap: wrap; }
|
||||
|
||||
/* Woo notice compat */
|
||||
.woocommerce-message, .woocommerce-info, .woocommerce-error { border-radius: var(--xshop-radius-sm); }
|
||||
@@ -0,0 +1,82 @@
|
||||
/* XShop Layout — header, footer, grid, sections. Logical properties only. */
|
||||
|
||||
/* Header */
|
||||
.xshop-header { position: sticky; inset-block-start: 0; z-index: var(--xshop-z-header); background: var(--xshop-card); border-block-end: 1px solid var(--xshop-border); }
|
||||
.xshop-header__inner { display: flex; align-items: center; gap: var(--xshop-space-4); padding-block: var(--xshop-space-3); flex-wrap: wrap; }
|
||||
.xshop-header__branding { display: flex; align-items: center; gap: var(--xshop-space-3); flex-shrink: 0; }
|
||||
.xshop-header__logo img { max-block-size: 40px; inline-size: auto; }
|
||||
.xshop-header__title { font-size: var(--xshop-text-xl); font-weight: var(--xshop-weight-bold); margin: 0; }
|
||||
.xshop-header__title a { color: var(--xshop-text); }
|
||||
.xshop-header__tagline { font-size: var(--xshop-text-sm); color: var(--xshop-muted); margin: 0; }
|
||||
.xshop-header__nav { flex: 1 1 auto; display: flex; justify-content: center; }
|
||||
.xshop-header__actions { display: flex; align-items: center; gap: var(--xshop-space-2); flex-shrink: 0; }
|
||||
.xshop-header__action-btn { display: inline-flex; align-items: center; justify-content: center; min-inline-size: 40px; min-block-size: 40px; border: 1px solid var(--xshop-border); border-radius: var(--xshop-radius-sm); background: var(--xshop-card); color: var(--xshop-text); cursor: pointer; }
|
||||
.xshop-header__action-btn:hover { background: var(--xshop-surface); }
|
||||
.xshop-header__search { position: relative; }
|
||||
.xshop-header__search-form { display: flex; gap: var(--xshop-space-2); }
|
||||
|
||||
/* Primary nav */
|
||||
.xshop-nav { display: flex; }
|
||||
.xshop-nav__list { display: flex; flex-wrap: wrap; gap: var(--xshop-space-1); list-style: none; margin: 0; padding: 0; align-items: center; }
|
||||
.xshop-nav__list a { display: block; padding: var(--xshop-space-2) var(--xshop-space-3); color: var(--xshop-text); border-radius: var(--xshop-radius-sm); font-weight: var(--xshop-weight-medium); font-size: var(--xshop-text-sm); }
|
||||
.xshop-nav__list a:hover, .xshop-nav__list a:focus-visible { background: var(--xshop-surface); color: var(--xshop-text); }
|
||||
.xshop-nav__list .current-menu-item > a, .xshop-nav__list .current_page_item > a { background: var(--xshop-surface-2); }
|
||||
|
||||
/* Mobile nav */
|
||||
.xshop-mobile-toggle { display: none; align-items: center; justify-content: center; inline-size: 40px; block-size: 40px; border: 1px solid var(--xshop-border); border-radius: var(--xshop-radius-sm); background: var(--xshop-card); cursor: pointer; color: var(--xshop-text); }
|
||||
.xshop-mobile-nav { display: none; border-block-start: 1px solid var(--xshop-border); padding-block: var(--xshop-space-3); }
|
||||
.xshop-mobile-nav.is-open { display: block; }
|
||||
.xshop-mobile-nav__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
|
||||
.xshop-mobile-nav__list a { display: block; padding: var(--xshop-space-2) var(--xshop-space-3); color: var(--xshop-text); border-radius: var(--xshop-radius-sm); }
|
||||
.xshop-mobile-nav__list a:hover { background: var(--xshop-surface); }
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.xshop-nav--desktop { display: none; }
|
||||
.xshop-mobile-toggle { display: inline-flex; }
|
||||
.xshop-header__search--desktop { display: none; }
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.xshop-footer { background: var(--xshop-surface); border-block-start: 1px solid var(--xshop-border); margin-block-start: var(--xshop-space-12); padding-block: var(--xshop-space-8) var(--xshop-space-6); }
|
||||
.xshop-footer__widgets { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--xshop-space-6); margin-block-end: var(--xshop-space-8); }
|
||||
.xshop-footer__bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--xshop-space-4); border-block-start: 1px solid var(--xshop-border); padding-block-start: var(--xshop-space-4); font-size: var(--xshop-text-sm); color: var(--xshop-muted); }
|
||||
.xshop-footer__nav-list { display: flex; flex-wrap: wrap; gap: var(--xshop-space-3); list-style: none; margin: 0; padding: 0; }
|
||||
.xshop-footer__nav-list a { color: var(--xshop-muted); }
|
||||
.xshop-footer__nav-list a:hover { color: var(--xshop-text); }
|
||||
@media (max-width: 768px) {
|
||||
.xshop-footer__widgets { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.xshop-footer__widgets { grid-template-columns: 1fr; }
|
||||
.xshop-footer__bottom { flex-direction: column; align-items: flex-start; }
|
||||
}
|
||||
|
||||
/* Main layout */
|
||||
.xshop-main { padding-block: var(--xshop-space-8); }
|
||||
.xshop-layout { display: grid; grid-template-columns: 1fr 320px; gap: var(--xshop-space-8); align-items: start; }
|
||||
.xshop-layout--no-sidebar { grid-template-columns: 1fr; }
|
||||
.xshop-layout__content { min-inline-size: 0; }
|
||||
.xshop-layout__sidebar { position: sticky; inset-block-start: calc(60px + var(--xshop-space-4)); }
|
||||
@media (max-width: 1024px) {
|
||||
.xshop-layout { grid-template-columns: 1fr; }
|
||||
.xshop-layout__sidebar { position: static; }
|
||||
}
|
||||
|
||||
/* Grids */
|
||||
.xshop-grid { display: grid; gap: var(--xshop-space-4); }
|
||||
.xshop-grid--posts { grid-template-columns: repeat(3, 1fr); }
|
||||
.xshop-grid--products { grid-template-columns: repeat(3, 1fr); }
|
||||
@media (max-width: 1024px) { .xshop-grid--posts, .xshop-grid--products { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (max-width: 640px) { .xshop-grid--posts, .xshop-grid--products { grid-template-columns: 1fr; } }
|
||||
|
||||
/* Section */
|
||||
.xshop-section { padding-block: var(--xshop-space-8); }
|
||||
.xshop-section__header { display: flex; align-items: baseline; justify-content: space-between; gap: var(--xshop-space-4); margin-block-end: var(--xshop-space-6); flex-wrap: wrap; }
|
||||
.xshop-section__title { font-size: var(--xshop-text-2xl); margin: 0; }
|
||||
.xshop-section__link { font-size: var(--xshop-text-sm); font-weight: var(--xshop-weight-medium); }
|
||||
|
||||
/* Hero foundation */
|
||||
.xshop-hero { background: var(--xshop-surface); border: 1px solid var(--xshop-border); border-radius: var(--xshop-radius-lg); padding: var(--xshop-space-8); display: grid; gap: var(--xshop-space-4); }
|
||||
.xshop-hero__title { font-size: var(--xshop-text-4xl); margin: 0; }
|
||||
.xshop-hero__text { color: var(--xshop-muted); max-inline-size: 60ch; }
|
||||
.xshop-hero__actions { display: flex; gap: var(--xshop-space-3); flex-wrap: wrap; }
|
||||
@@ -0,0 +1,91 @@
|
||||
/* XShop Design Tokens — CSS custom properties. Logical, RTL-safe. */
|
||||
:root {
|
||||
/* Palette */
|
||||
--xshop-primary: #0ea5e9;
|
||||
--xshop-primary-600: #0284c7;
|
||||
--xshop-primary-700: #0369a1;
|
||||
--xshop-secondary: #111827;
|
||||
--xshop-bg: #ffffff;
|
||||
--xshop-surface: #f8fafc;
|
||||
--xshop-surface-2: #f1f5f9;
|
||||
--xshop-card: #ffffff;
|
||||
--xshop-text: #0f172a;
|
||||
--xshop-muted: #64748b;
|
||||
--xshop-border: #e2e8f0;
|
||||
--xshop-input-bg: #ffffff;
|
||||
--xshop-input-border: #cbd5e1;
|
||||
--xshop-input-text: #0f172a;
|
||||
--xshop-focus: #0ea5e9;
|
||||
--xshop-link: #0ea5e9;
|
||||
--xshop-link-hover: #0284c7;
|
||||
--xshop-success: #16a34a;
|
||||
--xshop-success-bg: #f0fdf4;
|
||||
--xshop-warning: #f59e0b;
|
||||
--xshop-warning-bg: #fffbeb;
|
||||
--xshop-danger: #dc2626;
|
||||
--xshop-danger-bg: #fef2f2;
|
||||
|
||||
/* Typography */
|
||||
--xshop-font-sans: system-ui, -apple-system, "Vazirmatn", "IRANSans", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--xshop-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
--xshop-text-xs: 0.75rem; /* 12px */
|
||||
--xshop-text-sm: 0.875rem; /* 14px */
|
||||
--xshop-text-base: 1rem; /* 16px */
|
||||
--xshop-text-lg: 1.125rem; /* 18px */
|
||||
--xshop-text-xl: 1.25rem; /* 20px */
|
||||
--xshop-text-2xl: 1.5rem; /* 24px */
|
||||
--xshop-text-3xl: 1.875rem;/* 30px */
|
||||
--xshop-text-4xl: 2.25rem; /* 36px */
|
||||
--xshop-weight-regular: 400;
|
||||
--xshop-weight-medium: 500;
|
||||
--xshop-weight-bold: 700;
|
||||
--xshop-leading-tight: 1.25;
|
||||
--xshop-leading: 1.6;
|
||||
--xshop-leading-relaxed: 1.75;
|
||||
|
||||
/* Spacing / Layout */
|
||||
--xshop-space-1: 4px; --xshop-space-2: 8px; --xshop-space-3: 12px; --xshop-space-4: 16px;
|
||||
--xshop-space-5: 20px; --xshop-space-6: 24px; --xshop-space-8: 32px; --xshop-space-10: 40px;
|
||||
--xshop-space-12: 48px; --xshop-space-16: 64px;
|
||||
--xshop-container: 1280px;
|
||||
--xshop-container-narrow: 720px;
|
||||
--xshop-radius-sm: 8px; --xshop-radius-md: 14px; --xshop-radius-lg: 22px; --xshop-radius-full: 9999px;
|
||||
--xshop-shadow-sm: 0 1px 2px rgba(0,0,0,.06);
|
||||
--xshop-shadow-md: 0 8px 24px rgba(0,0,0,.08);
|
||||
--xshop-shadow-lg: 0 16px 40px rgba(0,0,0,.12);
|
||||
--xshop-z-header: 40; --xshop-z-overlay: 50; --xshop-z-modal: 60;
|
||||
--xshop-transition-fast: 120ms ease;
|
||||
--xshop-transition: 180ms ease;
|
||||
--xshop-transition-slow: 280ms ease;
|
||||
|
||||
/* Breakpoints (for reference; media queries use literal values) */
|
||||
--xshop-bp-sm: 640px;
|
||||
--xshop-bp-md: 768px;
|
||||
--xshop-bp-lg: 1024px;
|
||||
--xshop-bp-xl: 1280px;
|
||||
--xshop-bp-2xl: 1536px;
|
||||
}
|
||||
|
||||
/* Dark mode — semantic swap, not inversion */
|
||||
[data-theme="dark"] {
|
||||
--xshop-bg: #0b1220;
|
||||
--xshop-surface: #111a2e;
|
||||
--xshop-surface-2: #1a2542;
|
||||
--xshop-card: #151f38;
|
||||
--xshop-text: #e2e8f0;
|
||||
--xshop-muted: #94a3b8;
|
||||
--xshop-border: #1e2d4a;
|
||||
--xshop-input-bg: #0f1a33;
|
||||
--xshop-input-border: #24365e;
|
||||
--xshop-input-text: #e2e8f0;
|
||||
--xshop-focus: #38bdf8;
|
||||
--xshop-link: #38bdf8;
|
||||
--xshop-link-hover: #7dd3fc;
|
||||
--xshop-success-bg: #052e16;
|
||||
--xshop-warning-bg: #451a03;
|
||||
--xshop-danger-bg: #450a0a;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:root { --xshop-transition: 0ms; --xshop-transition-fast: 0ms; --xshop-transition-slow: 0ms; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* XShop Utilities — visually hidden, containers, prose. */
|
||||
.xshop-sr-only { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
||||
.xshop-prose { max-inline-size: 68ch; }
|
||||
.xshop-prose :where(p, ul, ol, blockquote) { margin-block-end: var(--xshop-space-4); }
|
||||
.xshop-prose :where(blockquote) { border-inline-start: 3px solid var(--xshop-border); padding-inline-start: var(--xshop-space-4); color: var(--xshop-muted); }
|
||||
.xshop-prose :where(a) { text-decoration: underline; text-underline-offset: 3px; }
|
||||
.xshop-prose--rtl { direction: rtl; }
|
||||
.xshop-text-muted { color: var(--xshop-muted); }
|
||||
.xshop-text-center { text-align: center; }
|
||||
.xshop-stack { display: flex; flex-direction: column; gap: var(--xshop-space-4); }
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* XShop theme bootstrap – vanilla, no jQuery.
|
||||
* Feature modules are dynamically imported when DOM markers are present.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// Dark mode toggle via [data-theme] if enabled.
|
||||
function initThemeMode() {
|
||||
var data = window.xshopData;
|
||||
if (!data) return;
|
||||
// Theme mode is server-rendered via data-theme on <html>; this handles toggle clicks.
|
||||
document.addEventListener('click', function (e) {
|
||||
var btn = e.target.closest('[data-xshop-theme-toggle]');
|
||||
if (!btn) return;
|
||||
var html = document.documentElement;
|
||||
var next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
|
||||
html.setAttribute('data-theme', next);
|
||||
try { localStorage.setItem('xshop_theme', next); } catch (_) {}
|
||||
// Cookie for server-side rendering on next request.
|
||||
document.cookie = 'xshop_theme=' + next + '; path=/; max-age=2592000; SameSite=Lax';
|
||||
});
|
||||
}
|
||||
|
||||
// Minimal focus trap helper for drawers/modals (used by later modules).
|
||||
window.xshopTrapFocus = function (container) {
|
||||
var sel = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
||||
var nodes = container.querySelectorAll(sel);
|
||||
if (!nodes.length) return function () {};
|
||||
var first = nodes[0], last = nodes[nodes.length - 1];
|
||||
function onKey(e) {
|
||||
if (e.key !== 'Tab') return;
|
||||
if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
|
||||
else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
|
||||
}
|
||||
container.addEventListener('keydown', onKey);
|
||||
return function () { container.removeEventListener('keydown', onKey); };
|
||||
};
|
||||
|
||||
function initMobileNav() {
|
||||
var toggle = document.querySelector('[data-xshop-mobile-toggle]');
|
||||
var nav = document.getElementById('xshop-mobile-nav');
|
||||
if (!toggle || !nav) return;
|
||||
var releaseTrap = null;
|
||||
function setOpen(open) {
|
||||
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
nav.classList.toggle('is-open', open);
|
||||
nav.hidden = !open;
|
||||
if (open) {
|
||||
// Move focus into nav.
|
||||
var firstLink = nav.querySelector('a, button');
|
||||
if (firstLink) firstLink.focus();
|
||||
if (window.xshopTrapFocus) releaseTrap = window.xshopTrapFocus(nav);
|
||||
document.addEventListener('keydown', onDocKey);
|
||||
} else {
|
||||
if (releaseTrap) { releaseTrap(); releaseTrap = null; }
|
||||
document.removeEventListener('keydown', onDocKey);
|
||||
toggle.focus();
|
||||
}
|
||||
}
|
||||
function onDocKey(e) {
|
||||
if (e.key === 'Escape') setOpen(false);
|
||||
}
|
||||
toggle.addEventListener('click', function () {
|
||||
var open = toggle.getAttribute('aria-expanded') === 'true';
|
||||
setOpen(!open);
|
||||
});
|
||||
// Close when clicking outside.
|
||||
document.addEventListener('click', function (e) {
|
||||
if (nav.hidden) return;
|
||||
if (nav.contains(e.target) || toggle.contains(e.target)) return;
|
||||
setOpen(false);
|
||||
});
|
||||
}
|
||||
|
||||
initThemeMode();
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initMobileNav);
|
||||
} else {
|
||||
initMobileNav();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user