From ddd204baa0f8a1f2fddbd5db996a99ae318a3e0a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 10:04:03 +0000 Subject: [PATCH] Migrate cart-overlay and quote-cart styles into the design system Cart-item-controls, the cart-overlay dialog, and the quote-cart blocks were all styled in the legacy src/css/cart.scss with raw values (0.75rem, 1.5rem, etc.) instead of design-system tokens. Move the cart-overlay (and its cart-item, cart-item-info, cart-item-controls, cart-checkout, etc.), the quote-cart family, and the quote-price summary into new files under src/css/design-system/, scoped to .design-system and using $space-*, $border-light, $radius-md, and button-* mixins. Wrap the cart-icon, cart-overlay, and theme-switcher includes in
from base.html so the design-system styles also win on legacy layouts. The cart.html template now wraps quantity-controls in .item-quantity for consistency with list-item-cart-controls. Legacy cart.scss is reduced to the inline list-item widgets (still needed by products listings and item-reviews that are not yet wrapped in .design-system) plus the global dialog-scroll-lock rule that has to stay outside the scope. https://claude.ai/code/session_01MyGvKCdJGipPrBLSAm13V6 --- src/_includes/templates/cart.html | 4 +- src/_layouts/base.html | 8 +- src/css/cart.scss | 402 +------------------------ src/css/design-system/_cart.scss | 140 +++++++++ src/css/design-system/_index.scss | 2 + src/css/design-system/_quote-cart.scss | 179 +++++++++++ 6 files changed, 338 insertions(+), 397 deletions(-) create mode 100644 src/css/design-system/_cart.scss create mode 100644 src/css/design-system/_quote-cart.scss diff --git a/src/_includes/templates/cart.html b/src/_includes/templates/cart.html index bdfd1d53a..35ad6e3a4 100644 --- a/src/_includes/templates/cart.html +++ b/src/_includes/templates/cart.html @@ -6,7 +6,9 @@
- {% include "templates/quantity-controls.html" %} +
+ {% include "templates/quantity-controls.html" %} +
diff --git a/src/_layouts/base.html b/src/_layouts/base.html index 77e98f3ce..904e7145d 100644 --- a/src/_layouts/base.html +++ b/src/_layouts/base.html @@ -25,9 +25,11 @@ {%- endif -%} {%- include "footer.html" -%} - {%- include "theme-switcher.html" -%} - {%- include "cart-icon.html" -%} - {%- include "cart-overlay.html" -%} +
+ {%- include "theme-switcher.html" -%} + {%- include "cart-icon.html" -%} + {%- include "cart-overlay.html" -%} +
{%- include "image-popup.html" -%} {%- include "client-templates.html" -%} {% slot "templates" %} diff --git a/src/css/cart.scss b/src/css/cart.scss index df4fa8f76..88afeff4e 100644 --- a/src/css/cart.scss +++ b/src/css/cart.scss @@ -1,202 +1,18 @@ -// Shopping Cart Styles +// Legacy cart widget styles +// +// Cart-overlay (dialog), cart-items, cart-icon, and the quote-cart blocks all +// live in the design system now (see src/css/design-system/_cart.scss, +// _cart-icon.scss, and _quote-cart.scss). This file only retains the inline +// list-item add-to-cart row + quantity selector for legacy layouts (the +// products listing and item-reviews pages) where items.html is not yet +// wrapped in `.design-system`, plus the global scroll-lock rule for the +// cart overlay (which has to live outside the `.design-system` scope). @use "breakpoints"; @use "dialog"; -// Floating cart icon -.cart-icon { - cursor: pointer; - - position: fixed; - z-index: 999; - bottom: 2rem; - left: 2rem; - - display: flex; - gap: 0.5rem; - - margin: 0; - padding: 0.5rem 1rem; - border-radius: var(--border-radius); - - svg { - width: 1.5rem; - height: 1.5rem; - } - - .cart-count { - height: 1.25rem; - font-size: 0.75rem; - } -} - -// Cart bounce animation -@keyframes cartBounce { - 0%, - 100% { - transform: scale(1); - } - - 50% { - transform: scale(1.2); - } -} - -.cart-icon.cart-bounce { - animation: cartBounce 0.6s ease; -} - -// Cart overlay using native element -// Provides built-in modal behavior, Escape key handling, and focus management -dialog.cart-overlay { - @include dialog.dialog-box(40rem); - - // Custom header padding for cart - > header { - margin: 0; - padding: 2rem 1rem; - border: 0; - box-shadow: none; - } -} - @include dialog.dialog-scroll-lock("dialog.cart-overlay"); -.cart-body { - overflow-y: auto; - flex: 1; - min-height: 15rem; - padding: 1.5rem; -} - -.cart-empty { - padding: 3rem 1rem; - text-align: center; -} - -.cart-items { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.cart-item { - display: flex; - flex-direction: column; - gap: 0.75rem; - - padding: 1rem; - border: var(--border); - border-radius: var(--border-radius); - - @include breakpoints.up("md") { - flex-direction: row; - align-items: center; - justify-content: space-between; - } -} - -.cart-item-info { - flex: 1; - - .cart-item-name { - margin-bottom: 0.25rem; - font-weight: bold; - } -} - -.cart-item-controls { - display: flex; - flex-wrap: wrap; - gap: 0.75rem; - align-items: center; -} - -.cart-item-remove { - margin: 0; - padding: 0.5rem 1rem; -} - -.cart-footer { - display: flex; - flex-direction: column; - gap: 1rem; - - padding: 1.5rem; - border-top: var(--border); -} - -.cart-total { - display: flex; - align-items: center; - justify-content: space-between; - - font-size: 1.25rem; - font-weight: bold; -} - -// Checkout buttons container -.cart-checkout-buttons { - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.cart-checkout { - display: flex; - gap: 0.5rem; - align-items: center; - justify-content: center; - - width: 100%; - margin: 0; - - .payment-icon { - flex-shrink: 0; - width: 1.25rem; - height: 1.25rem; - } -} - -// Card checkout button styling -.cart-checkout-stripe { - border-color: #635bff; - color: #fff; - background-color: #635bff; - - &:hover:not(:disabled) { - border-color: #4f46e5; - background-color: #4f46e5; - } - - &:disabled { - cursor: not-allowed; - opacity: 0.6; - } -} - -// Mobile adjustments -@include breakpoints.down("md") { - .cart-icon { - bottom: 1rem; - left: 1rem; - } - - dialog.cart-overlay { - @include dialog.dialog-mobile-fullscreen; - - > header { - padding: 1rem; - } - } - - .cart-body, - .cart-footer { - padding: 1rem; - } -} - -// List item quantity controls (inline on list pages) .list-item-cart-controls { display: flex; flex-direction: column; @@ -249,203 +65,3 @@ dialog.cart-overlay { border-bottom-left-radius: 0; } } - -.quote-cart-empty { - padding: 2rem 1rem; - border: var(--border); - border-radius: var(--border-radius); - text-align: center; -} - -.quote-cart-items { - overflow: hidden; - display: flex; - flex-direction: column; - gap: 1rem; - - margin: 0; - padding: 0; - - list-style: none; -} - -.quote-cart-items:not(:empty) { - animation: slideInFade 0.4s ease-out forwards; -} - -@keyframes slideInFade { - from { - max-height: 0; - opacity: 0; - } - - to { - max-height: 2000px; - opacity: 1; - } -} - -.quote-cart-item { - padding: 1rem; - border: var(--border); - border-radius: var(--border-radius); - - > ul { - display: flex; - flex-direction: column; - gap: 0.5rem; - - margin: 0; - padding: 0; - - list-style: none; - - @include breakpoints.up("sm") { - flex-flow: row wrap; - align-items: center; - } - } - - .quote-cart-item-info { - display: flex; - flex-wrap: wrap; - gap: 0.5rem 1rem; - font-weight: bold; - - @include breakpoints.up("sm") { - flex: 1; - min-width: 0; - } - } - - .quote-cart-item-actions { - display: flex; - gap: 0; - align-items: center; - - button { - height: 2.5rem; - margin: 0; - } - - .quantity-decrease { - width: 2rem; - padding: 0.25rem 0.75rem; - border-right: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - - .quantity-increase { - width: 2rem; - padding: 0.25rem 0.75rem; - border-left: 0; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - - input[type="number"] { - width: 3rem; - height: 2.5rem; - margin: 0; - border-radius: 0; - - text-align: center; - - appearance: textfield; - - &::-webkit-inner-spin-button, - &::-webkit-outer-spin-button { - margin: 0; - appearance: none; - } - } - - button[data-action="remove"] { - margin-left: 0.75rem; - padding: 0 1rem; - } - } - - .quote-cart-item-specs { - font-size: 0.9em; - color: var(--color-text); - opacity: 0.8; - - @include breakpoints.up("sm") { - flex-basis: 100%; - } - } - - .quote-cart-item-subtitle { - width: 100%; - } -} - -.quote-cart-actions { - justify-content: end; - margin-top: 1.5rem; - text-align: center; - animation: slideInFade 0.4s ease-out forwards; -} - -// .quote-checkout-summary base styles are in style.scss (shared with .hire-dates-section) - -.quote-checkout-items { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.quote-checkout-item { - display: flex; - gap: 1rem; - padding: 0.5rem 0; - border-bottom: var(--border); - - &:last-child { - border-bottom: none; - } -} - -.quote-checkout-item-name { - flex: 1; -} - -.quote-checkout-item-qty { - color: var(--color-text); - opacity: 0.6; -} - -.quote-checkout-item-price { - font-weight: bold; -} - -// Quote price summary -.quote-price { - margin: 1rem 0; - padding: 1rem; - border: var(--border); - border-radius: var(--border-radius); - - list-style: none; - - background: var(--color-accent); - - > li { - padding: 0.25rem 0; - } - - ul { - margin: 0.5rem 0; - padding-left: 0; - list-style: none; - - li { - display: flex; - justify-content: space-between; - padding: 0.25rem 0; - } - } -} - diff --git a/src/css/design-system/_cart.scss b/src/css/design-system/_cart.scss new file mode 100644 index 000000000..0900a98bb --- /dev/null +++ b/src/css/design-system/_cart.scss @@ -0,0 +1,140 @@ +@use "../variables" as *; +@use "../mixins" as *; +@use "../breakpoints" as breakpoint; +@use "../dialog"; + +// ============================================================================= +// CART (Design System) +// ============================================================================= +// Shopping cart overlay (native ) and the cart-item rows inside it. +// The cart icon lives in _cart-icon.scss; inline list-item cart widgets +// (`.list-item-cart-controls`, `.item-quantity`) live in the legacy +// src/css/cart.scss because they also need to apply on legacy product +// listings that are not yet wrapped in `.design-system`. + +.design-system { + dialog.cart-overlay { + @include dialog.dialog-box(40rem); + + > header { + padding: $space-md $space-sm; + border: 0; + box-shadow: none; + } + + @include breakpoint.down("md") { + @include dialog.dialog-mobile-fullscreen; + + > header { + padding: $space-sm; + } + } + } + + .cart-body { + overflow-y: auto; + flex: 1; + min-height: 15rem; + padding: $space-md; + + @include breakpoint.down("md") { + padding: $space-sm; + } + } + + .cart-empty { + padding: $space-xl $space-sm; + text-align: center; + } + + .cart-items { + @include flex-col($space-sm); + } + + .cart-item { + @include flex-col($space-sm); + + padding: $space-sm; + border: $border-light; + border-radius: $radius-md; + + @include breakpoint.up("md") { + flex-direction: row; + align-items: center; + justify-content: space-between; + } + } + + .cart-item-info { + flex: 1; + + [data-field="name"] { + font-weight: 700; + } + } + + .cart-item-controls { + @include flex-wrap($space-sm); + + align-items: center; + + button[data-action="remove"] { + @include button-ghost; + + margin: 0; + padding: $space-xs $space-sm; + } + } + + .cart-footer { + @include flex-col($space-sm); + + padding: $space-md; + border-top: $border-light; + + @include breakpoint.down("md") { + padding: $space-sm; + } + } + + .cart-total { + @include flex-between; + + font-size: $font-size-lg; + font-weight: 700; + } + + .cart-minimum-message { + color: var(--color-error); + text-align: center; + } + + .cart-checkout-buttons { + @include flex-col($space-sm); + } + + .cart-checkout { + @include button-primary; + + width: 100%; + margin: 0; + + .payment-icon { + flex-shrink: 0; + width: 1.25rem; + height: 1.25rem; + } + } + + .cart-checkout-stripe { + border-color: #635bff; + color: $color-white; + background-color: #635bff; + + &:hover:not(:disabled) { + border-color: #4f46e5; + color: $color-white; + background-color: #4f46e5; + } + } +} diff --git a/src/css/design-system/_index.scss b/src/css/design-system/_index.scss index d5a3e9bfb..c284459c5 100644 --- a/src/css/design-system/_index.scss +++ b/src/css/design-system/_index.scss @@ -50,6 +50,8 @@ @forward "guide"; @forward "theme-switcher"; @forward "cart-icon"; +@forward "cart"; +@forward "quote-cart"; @forward "freetobook"; @forward "iframe-embed"; @forward "buy-options"; diff --git a/src/css/design-system/_quote-cart.scss b/src/css/design-system/_quote-cart.scss new file mode 100644 index 000000000..0225541a6 --- /dev/null +++ b/src/css/design-system/_quote-cart.scss @@ -0,0 +1,179 @@ +@use "../variables" as *; +@use "../mixins" as *; +@use "../breakpoints" as breakpoint; + +// ============================================================================= +// QUOTE CART (Design System) +// ============================================================================= +// Used by the quote-cart and quote-checkout blocks. The quote flow is the +// enquiry-mode counterpart to the Stripe cart: items are collected, then +// submitted via the multi-step quote form. + +.design-system { + @mixin quote-accent-card { + margin: $space-sm 0; + padding: $space-sm; + border: $border-light; + border-radius: $radius-md; + + background: var(--color-accent); + } + + @keyframes quoteCartSlideIn { + from { + max-height: 0; + opacity: 0; + } + + to { + max-height: 2000px; + opacity: 1; + } + } + + .quote-cart-empty { + padding: $space-md $space-sm; + border: $border-light; + border-radius: $radius-md; + text-align: center; + } + + .quote-cart-items { + @include flex-col($space-sm); + + overflow: hidden; + list-style: none; + + &:not(:empty) { + animation: quoteCartSlideIn 0.4s ease-out forwards; + } + } + + .quote-cart-item { + padding: $space-sm; + border: $border-light; + border-radius: $radius-md; + + > ul { + @include flex-col($space-xs); + + list-style: none; + + @include breakpoint.up("sm") { + flex-flow: row wrap; + align-items: center; + } + } + + .quote-cart-item-info { + @include flex-wrap($space-sm); + + gap: $space-xs $space-sm; + font-weight: 700; + + @include breakpoint.up("sm") { + flex: 1; + min-width: 0; + } + } + + .quote-cart-item-actions { + display: flex; + align-items: center; + + button[data-action="remove"] { + @include button-ghost; + + margin-left: $space-sm; + padding: 0 $space-sm; + } + } + + .quote-cart-item-specs { + @include body-sm; + + color: var(--color-text-muted); + + @include breakpoint.up("sm") { + flex-basis: 100%; + } + } + + .quote-cart-item-subtitle { + width: 100%; + } + } + + .quote-cart-actions { + justify-content: end; + margin-top: $space-md; + text-align: center; + animation: quoteCartSlideIn 0.4s ease-out forwards; + } + + // --------------------------------------------------------------------------- + // Quote checkout summary (inside quote-form-steps recap) + // --------------------------------------------------------------------------- + + .quote-checkout-summary { + @include quote-accent-card; + + h3 { + margin: 0 0 $space-sm; + font-size: $font-size-base; + } + } + + .quote-checkout-items { + @include flex-col($space-xs); + } + + .quote-checkout-item { + display: flex; + gap: $space-sm; + padding: $space-xs 0; + border-bottom: $border-light; + + &:last-child { + border-bottom: 0; + } + } + + .quote-checkout-item-name { + flex: 1; + } + + .quote-checkout-item-qty { + color: var(--color-text-muted); + } + + .quote-checkout-item-price { + font-weight: 700; + } + + // --------------------------------------------------------------------------- + // Quote price summary (sticky aside on the quote pages) + // --------------------------------------------------------------------------- + + .quote-price { + @include quote-accent-card; + + list-style: none; + + > li { + padding: $space-xs 0; + } + + ul { + margin: $space-xs 0; + padding-left: 0; + list-style: none; + + li { + @include flex-between; + + padding: $space-xs 0; + } + } + } +}