Compare commits

...
4 Commits
Author SHA1 Message Date
nobswebdev 93c132b0df Fix SimpleX branding in shop footer link label. 2026-09-09 16:33:04 +02:00
nobswebdev 771d95d1cd Stack and center the shop header on mobile.
Column-layout logo, nav, and crypto rates with full-width wrapping for many order links.
2026-09-09 16:27:58 +02:00
nobswebdev 254736356b Show storefront feedback as fixed top-center toasts.
Use CSS-only dismiss animations, render the partial at the end of body, and add top margin to cart issue messages.
2026-09-09 16:07:58 +02:00
nobswebdev 738e406725 Rename sf-discount-error to sf-cart-issue for cart validation messages.
The class is used for stock, discount, and cart-total issues—not only discounts.
2026-09-09 15:38:15 +02:00
6 changed files with 100 additions and 10 deletions
@@ -380,6 +380,26 @@ a:hover {
white-space: nowrap; white-space: nowrap;
} }
@media (max-width: 768px) {
.sf-header__inner {
flex-direction: column;
align-items: center;
justify-content: center;
}
.sf-header__brand-group {
flex-direction: column;
align-items: center;
}
.sf-nav,
.sf-rates {
width: 100%;
max-width: 100%;
justify-content: center;
}
}
.sf-crypto-icon { .sf-crypto-icon {
width: 1.25rem; width: 1.25rem;
height: 1.25rem; height: 1.25rem;
@@ -519,6 +539,76 @@ a:hover {
border-color: var(--sf-warning-border); border-color: var(--sf-warning-border);
} }
.sf-alert--toast {
position: fixed;
top: var(--sf-space-5);
left: 50%;
z-index: 20;
display: inline-flex;
align-items: center;
gap: var(--sf-space-2);
width: max-content;
max-width: min(24rem, calc(100% - 2 * var(--sf-space-4)));
margin: 0;
padding: 0.6875rem 0.9375rem;
font-size: 0.875rem;
line-height: 1.25;
box-shadow: 0 2px 12px rgb(0 0 0 / 10%);
pointer-events: none;
transform: translateX(-50%);
}
.sf-alert--toast.sf-alert--success {
animation: sf-toast-dismiss 3s ease forwards;
}
.sf-alert--toast.sf-alert--error {
animation: sf-toast-dismiss 6s ease forwards;
}
.sf-alert--toast::before {
display: inline-flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 1rem;
height: 1rem;
border-radius: var(--sf-radius-full);
color: var(--sf-on-accent);
font-size: 0.6875rem;
font-weight: 700;
line-height: 1;
}
.sf-alert--toast.sf-alert--success::before {
content: '✓';
background: var(--sf-success);
}
.sf-alert--toast.sf-alert--error::before {
content: '×';
background: var(--sf-error);
}
@keyframes sf-toast-dismiss {
0%,
70% {
opacity: 1;
visibility: visible;
}
100% {
opacity: 0;
visibility: hidden;
}
}
@media (prefers-reduced-motion: reduce) {
.sf-alert--toast {
animation: none;
}
}
.sf-btn { .sf-btn {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -798,7 +888,7 @@ a:hover {
margin: 0; margin: 0;
} }
.sf-line-item__body > .sf-discount-error { .sf-line-item__body > .sf-cart-issue {
margin: 0.125rem 0 0; margin: 0.125rem 0 0;
} }
@@ -875,8 +965,8 @@ a:hover {
color: var(--sf-text-muted); color: var(--sf-text-muted);
} }
.sf-discount-error { .sf-cart-issue {
margin: 0 0 var(--sf-space-2); margin: var(--sf-space-2) 0;
font-size: 0.85rem; font-size: 0.85rem;
color: var(--sf-error); color: var(--sf-error);
line-height: 1.35; line-height: 1.35;
@@ -12,10 +12,10 @@
{{> shop-nav}} {{> shop-nav}}
{{> category-nav}} {{> category-nav}}
<main class='sf-main'> <main class='sf-main'>
{{> feedback}}
{{{body}}} {{{body}}}
</main> </main>
{{> shop-footer}} {{> shop-footer}}
</div> </div>
{{> feedback}}
</body> </body>
</html> </html>
@@ -28,7 +28,7 @@
{{@root.shopFiatCurrency}}</div> {{@root.shopFiatCurrency}}</div>
<div class='sf-line-item__note'>{{> product-delivery-note deliveryMode=deliveryMode}}</div> <div class='sf-line-item__note'>{{> product-delivery-note deliveryMode=deliveryMode}}</div>
{{#if stockIssueMessage}} {{#if stockIssueMessage}}
<p class='sf-discount-error'>{{stockIssueMessage}}</p> <p class='sf-cart-issue'>{{stockIssueMessage}}</p>
{{else}} {{else}}
{{#unless stockForSession}} {{#unless stockForSession}}
<p class='sf-text-subtle'>No more in stock beside your order</p> <p class='sf-text-subtle'>No more in stock beside your order</p>
@@ -36,7 +36,7 @@
{{/if}} {{/if}}
{{#each @root.discounts}} {{#each @root.discounts}}
{{#if (includes ineligibleVariantIds ../id)}} {{#if (includes ineligibleVariantIds ../id)}}
<p class='sf-discount-error'>Not eligible for code {{code}}</p> <p class='sf-cart-issue'>Not eligible for code {{code}}</p>
{{/if}} {{/if}}
{{/each}} {{/each}}
<form class='sf-form-row' method='post' action='/shop/cart/product/update'> <form class='sf-form-row' method='post' action='/shop/cart/product/update'>
@@ -23,7 +23,7 @@
}} }}
<span class='sf-discount-row__code sf-text-error'>{{code}}</span> <span class='sf-discount-row__code sf-text-error'>{{code}}</span>
</div> </div>
<p class='sf-discount-error'>{{issueMessage}}</p> <p class='sf-cart-issue'>{{issueMessage}}</p>
{{else if amount}} {{else if amount}}
<div class='sf-discount-row sf-discount-item'> <div class='sf-discount-row sf-discount-item'>
{{> dismiss-button {{> dismiss-button
@@ -62,7 +62,7 @@
</form> </form>
{{#if cartTotalIssueMessage}} {{#if cartTotalIssueMessage}}
<p class='sf-discount-error'>{{cartTotalIssueMessage}}</p> <p class='sf-cart-issue'>{{cartTotalIssueMessage}}</p>
{{/if}} {{/if}}
<div class='sf-stack sf-mt-4'> <div class='sf-stack sf-mt-4'>
@@ -1,5 +1,5 @@
{{#if feedback}} {{#if feedback}}
<p class='sf-alert sf-alert--{{feedback.type}}' role='status'> <p class='sf-alert sf-alert--{{feedback.type}} sf-alert--toast' role='status'>
{{feedback.text}} {{feedback.text}}
</p> </p>
{{/if}} {{/if}}
@@ -2,7 +2,7 @@
<div class='sf-footer__row'> <div class='sf-footer__row'>
<div class='sf-footer__start'> <div class='sf-footer__start'>
{{#if simplexLink}} {{#if simplexLink}}
<a href='{{simplexLink}}' target='_blank' rel='noopener noreferrer'>Contact via Simplex</a> <a href='{{simplexLink}}' target='_blank' rel='noopener noreferrer'>Contact via SimpleX</a>
{{/if}} {{/if}}
<a href='https://nullcart.net/' target='_blank' rel='noopener noreferrer'>Powered by NullCart</a> <a href='https://nullcart.net/' target='_blank' rel='noopener noreferrer'>Powered by NullCart</a>
</div> </div>