Rust discount function (one automatic discount evaluating all offers in priority order, parity with the Odoo engine), React Router admin with Polaris, theme app extension and app proxy pages, webhooks incl. GDPR, managed-pricing limits, and a Docker Compose stack (Postgres + Caddy HTTPS) for a single VPS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
163 lines
9.3 KiB
CSS
163 lines
9.3 KiB
CSS
/* Bundle Offers storefront styles.
|
|
Inherits the theme: fonts, text colour and background come from the page. Lines are a mix of the text
|
|
colour, so they work on light and dark themes alike. The merchant can set an accent and a radius (Settings).
|
|
The one decorative device is the coupon stub on the badge: festival offers as tear-off coupons. */
|
|
|
|
:root {
|
|
--bo-radius: 8px;
|
|
}
|
|
|
|
.bo-section,
|
|
.bo-box,
|
|
.bo-hints,
|
|
.bo-page {
|
|
--bo-ink: currentColor;
|
|
--bo-accent-fallback: rgb(var(--color-button, 0 0 0));
|
|
--bo-accent-color: var(--bo-accent, var(--bo-accent-fallback));
|
|
--bo-line: color-mix(in srgb, currentColor 14%, transparent);
|
|
--bo-soft: color-mix(in srgb, currentColor 5%, transparent);
|
|
--bo-muted: color-mix(in srgb, currentColor 68%, transparent);
|
|
--bo-on-accent: rgb(var(--color-button-text, 255 255 255));
|
|
}
|
|
|
|
.bo-visually-hidden {
|
|
position: absolute !important;
|
|
width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
|
|
clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
|
}
|
|
|
|
.bo-section { margin: 2.5rem auto; max-width: var(--page-width, 120rem); padding: 0 1.5rem; }
|
|
.bo-section__title { margin: 0; line-height: 1.2; }
|
|
.bo-section__sub { margin: .25rem 0 0; color: var(--bo-muted); }
|
|
.bo-link { color: inherit; text-underline-offset: .2em; white-space: nowrap; }
|
|
|
|
/* ---------- grid & strip ---------- */
|
|
.bo-grid { display: grid; gap: 1.5rem; grid-template-columns: 1fr; margin-top: 1.25rem; }
|
|
@media (min-width: 600px) { .bo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
|
@media (min-width: 990px) { .bo-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
|
|
|
|
.bo-strip__head { display: flex; align-items: end; justify-content: space-between; gap: 1rem; }
|
|
.bo-strip__track {
|
|
list-style: none; margin: 1.25rem 0 0; padding: 0 0 .5rem;
|
|
display: grid; grid-auto-flow: column; grid-auto-columns: 80%; gap: 1rem;
|
|
overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
|
|
}
|
|
@media (min-width: 600px) { .bo-strip__track { grid-auto-columns: calc((100% - 1rem) / 2.15); } }
|
|
@media (min-width: 990px) { .bo-strip__track { grid-auto-columns: calc((100% - 3rem) / 4); } }
|
|
.bo-strip__item { scroll-snap-align: start; display: flex; }
|
|
|
|
/* ---------- card ---------- */
|
|
.bo-card {
|
|
position: relative; display: flex; flex-direction: column; width: 100%;
|
|
border: 1px solid var(--bo-line); border-radius: var(--bo-radius); overflow: hidden;
|
|
}
|
|
.bo-card__media { display: block; aspect-ratio: 4 / 3; background: var(--bo-soft); }
|
|
.bo-card__media > img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
.bo-card__thumbs { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; height: 100%; }
|
|
.bo-card__thumbs img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
.bo-card__thumbs--1 { grid-template-columns: 1fr; }
|
|
.bo-card__thumbs--3 img:first-child { grid-row: span 2; }
|
|
.bo-card__body { display: flex; flex-direction: column; gap: .4rem; padding: 1rem 1.1rem 1.15rem; flex: 1; }
|
|
.bo-card__title { margin: 0; font-size: 1.15em; line-height: 1.3; }
|
|
.bo-card__title a { color: inherit; text-decoration: none; }
|
|
.bo-card__title a::after { content: ""; position: absolute; inset: 0; } /* whole card is clickable */
|
|
.bo-card__title a:focus-visible { outline: none; }
|
|
.bo-card:has(.bo-card__title a:focus-visible) { outline: 2px solid currentColor; outline-offset: 2px; }
|
|
.bo-card__deal { margin: 0; font-weight: 600; }
|
|
.bo-card__prices { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: .25rem .6rem; }
|
|
.bo-card__now { font-size: 1.25em; font-weight: 700; }
|
|
.bo-card__was { color: var(--bo-muted); }
|
|
.bo-card__count, .bo-card__ends { color: var(--bo-muted); font-size: .9em; }
|
|
.bo-card__ends { margin: 0; }
|
|
.bo-card .bo-button { margin-top: auto; position: relative; z-index: 1; }
|
|
|
|
/* ---------- the coupon stub (signature) ----------
|
|
A tear-off ticket with notches cut on both short sides and a dashed perforation line. */
|
|
.bo-stub {
|
|
position: absolute; top: .75rem; left: 0; margin: 0; z-index: 1;
|
|
--notch: 6px;
|
|
padding: .3rem .85rem .3rem .7rem;
|
|
background: var(--bo-accent-color); color: var(--bo-on-accent);
|
|
font-size: .8rem; font-weight: 700; line-height: 1.3;
|
|
-webkit-mask: radial-gradient(circle var(--notch) at 100% 50%, transparent 98%, #000) right / 100% 100% no-repeat;
|
|
mask: radial-gradient(circle var(--notch) at 100% 50%, transparent 98%, #000) right / 100% 100% no-repeat;
|
|
border-radius: 0 2px 2px 0;
|
|
}
|
|
.bo-stub span { display: block; padding-right: .55rem; border-right: 1.5px dashed color-mix(in srgb, currentColor 60%, transparent); }
|
|
|
|
/* ---------- buttons ---------- */
|
|
.bo-button {
|
|
display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
|
|
min-height: 44px; padding: .65rem 1.25rem; border: 0; border-radius: var(--bo-radius);
|
|
background: var(--bo-accent-color); color: var(--bo-on-accent);
|
|
font: inherit; font-weight: 600; text-decoration: none; cursor: pointer;
|
|
}
|
|
.bo-button:hover { filter: brightness(1.08); }
|
|
.bo-button:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
|
|
.bo-button[disabled] { opacity: .45; cursor: not-allowed; filter: none; }
|
|
|
|
.bo-empty { padding: 2rem 0; display: grid; gap: 1rem; justify-items: start; }
|
|
.bo-empty p { margin: 0; }
|
|
.bo-powered { margin-top: 1.5rem; font-size: .8rem; color: var(--bo-muted); }
|
|
.bo-powered a { color: inherit; }
|
|
|
|
/* ---------- product page box ---------- */
|
|
.bo-box { margin: 1.25rem 0; border: 1px dashed var(--bo-line); border-radius: var(--bo-radius); padding: .9rem 1rem; }
|
|
.bo-box__title { margin: 0 0 .4rem; font-size: 1em; }
|
|
.bo-box ul { list-style: none; margin: 0; padding: 0; }
|
|
.bo-box li + li { border-top: 1px solid var(--bo-line); }
|
|
.bo-box__row { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .25rem 1rem; min-height: 44px; align-items: center; padding: .4rem 0; color: inherit; text-decoration: none; }
|
|
.bo-box__row:hover .bo-box__name { text-decoration: underline; }
|
|
.bo-box__name { font-weight: 600; }
|
|
.bo-box__deal { color: var(--bo-muted); }
|
|
|
|
/* ---------- cart hints ---------- */
|
|
.bo-hints ul { list-style: none; margin: 1rem 0; padding: 0; display: grid; gap: .5rem; }
|
|
.bo-hint {
|
|
display: block; padding: .75rem 1rem; border-radius: var(--bo-radius); min-height: 44px;
|
|
background: var(--bo-soft); border-left: 3px solid var(--bo-accent-color); color: inherit; text-decoration: none;
|
|
}
|
|
.bo-hint:hover { text-decoration: underline; }
|
|
|
|
/* ---------- bundle page (app proxy) ---------- */
|
|
.bo-page { max-width: var(--page-width, 120rem); margin: 2rem auto 4rem; padding: 0 1.5rem; display: grid; gap: 2rem; }
|
|
@media (min-width: 990px) { .bo-page { grid-template-columns: 5fr 6fr; gap: 3.5rem; align-items: start; } }
|
|
.bo-page__hero { position: relative; border-radius: var(--bo-radius); overflow: hidden; background: var(--bo-soft); aspect-ratio: 4 / 3; }
|
|
.bo-page__hero img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
.bo-page__hero .bo-card__thumbs { height: 100%; }
|
|
.bo-page__title { margin: 1rem 0 .25rem; line-height: 1.15; }
|
|
.bo-page__deal { margin: 0; font-size: 1.35em; font-weight: 700; }
|
|
.bo-page__desc { margin: 1rem 0; max-width: 60ch; }
|
|
.bo-page__ends { margin: .5rem 0 0; color: var(--bo-muted); }
|
|
.bo-page__note { margin: .5rem 0 0; font-size: .9em; color: var(--bo-muted); }
|
|
.bo-page__h { margin: 0 0 .75rem; font-size: 1.15em; }
|
|
.bo-page__group + .bo-page__group { margin-top: 1.75rem; }
|
|
|
|
.bo-choice { display: grid; grid-template-columns: 64px 1fr auto; gap: .9rem; align-items: center; padding: .7rem 0; border-bottom: 1px solid var(--bo-line); }
|
|
.bo-choice img, .bo-choice__ph { width: 64px; height: 64px; object-fit: cover; border-radius: calc(var(--bo-radius) / 1.5); background: var(--bo-soft); display: block; }
|
|
.bo-choice__name { color: inherit; font-weight: 600; text-decoration: none; overflow-wrap: anywhere; }
|
|
.bo-choice__name:hover { text-decoration: underline; }
|
|
.bo-choice__price { display: block; color: var(--bo-muted); font-size: .9em; }
|
|
.bo-choice__qty { font-weight: 600; white-space: nowrap; }
|
|
.bo-choice--off { opacity: .5; }
|
|
|
|
.bo-qty { display: inline-flex; align-items: center; border: 1px solid var(--bo-line); border-radius: 999px; overflow: hidden; }
|
|
.bo-qty button { width: 44px; height: 44px; border: 0; background: transparent; color: inherit; font-size: 1.2rem; cursor: pointer; }
|
|
.bo-qty button:focus-visible { outline: 2px solid currentColor; outline-offset: -4px; }
|
|
.bo-qty input {
|
|
width: 3rem; height: 44px; border: 0; background: transparent; color: inherit; text-align: center;
|
|
font: inherit; font-size: 16px; font-weight: 600; -moz-appearance: textfield; appearance: textfield;
|
|
}
|
|
.bo-qty input::-webkit-inner-spin-button, .bo-qty input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
|
|
|
|
.bo-totals { margin: 1.25rem 0; padding: 1rem 1.1rem; border-radius: var(--bo-radius); background: var(--bo-soft); display: grid; grid-template-columns: 1fr auto; gap: .35rem 1rem; }
|
|
.bo-totals dt, .bo-totals dd { margin: 0; }
|
|
.bo-totals dd { text-align: right; font-variant-numeric: tabular-nums; }
|
|
.bo-totals__now { font-size: 1.3em; font-weight: 700; }
|
|
.bo-totals__save { font-weight: 700; }
|
|
.bo-counter { margin: 1rem 0 .5rem; font-weight: 600; }
|
|
.bo-error { margin: .75rem 0 0; color: rgb(var(--color-error, 177 39 4)); font-weight: 600; }
|
|
.bo-submit { width: 100%; max-width: 26rem; margin-top: .5rem; }
|
|
|
|
@media (prefers-reduced-motion: reduce) { .bo-strip__track { scroll-behavior: auto; } }
|