replace Tailwind utilities in layout + page templates with CSS (Phase 5a)
Absorb ~100 Tailwind utility classes from layout.ex and all page templates into semantic CSS rules in components.css. Uses theme font-size vars (--t-text-small, --t-text-caption) instead of rem to respect the theme's em-based scaling system. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
84de1c37c5
commit
fc9c33ab0c
@ -65,7 +65,7 @@ Plans: [admin-redesign.md](docs/plans/admin-redesign.md) | [setup-wizard.md](doc
|
|||||||
| ~~33~~ | ~~Phase 1: Layout primitives + reset~~ | 32 | 1.5h | done |
|
| ~~33~~ | ~~Phase 1: Layout primitives + reset~~ | 32 | 1.5h | done |
|
||||||
| ~~34~~ | ~~Phase 2: Extract product inline styles~~ | 33 | 3h | done |
|
| ~~34~~ | ~~Phase 2: Extract product inline styles~~ | 33 | 3h | done |
|
||||||
| ~~35~~ | ~~Phase 3: Extract layout + cart inline styles~~ | 33 | 3h | done |
|
| ~~35~~ | ~~Phase 3: Extract layout + cart inline styles~~ | 33 | 3h | done |
|
||||||
| 36 | Phase 4: Extract content + template inline styles | 33 | 2.5h | |
|
| ~~36~~ | ~~Phase 4: Extract content + template inline styles~~ | 33 | 2.5h | done |
|
||||||
| 37 | Phase 5: Remove Tailwind from shop | 34-36 | 3h | |
|
| 37 | Phase 5: Remove Tailwind from shop | 34-36 | 3h | |
|
||||||
| 38 | Phase 6: Replace DaisyUI (admin) | 37 | 3h | |
|
| 38 | Phase 6: Replace DaisyUI (admin) | 37 | 3h | |
|
||||||
| 39 | Phase 7: Remove Tailwind entirely | 38 | 1.5h | |
|
| 39 | Phase 7: Remove Tailwind entirely | 38 | 1.5h | |
|
||||||
|
|||||||
@ -335,12 +335,25 @@
|
|||||||
font-size: var(--t-text-small);
|
font-size: var(--t-text-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Page container (shared responsive centering) ── */
|
||||||
|
|
||||||
|
.page-container {
|
||||||
|
max-width: 80rem;
|
||||||
|
margin-inline: auto;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Shop container (body-level defaults) ── */
|
/* ── Shop container (body-level defaults) ── */
|
||||||
|
|
||||||
.shop-container {
|
.shop-container {
|
||||||
background-color: var(--t-surface-base);
|
background-color: var(--t-surface-base);
|
||||||
font-family: var(--t-font-body);
|
font-family: var(--t-font-body);
|
||||||
color: var(--t-text-primary);
|
color: var(--t-text-primary);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shop-container[data-bottom-nav] {
|
||||||
|
padding-bottom: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Shop header ── */
|
/* ── Shop header ── */
|
||||||
@ -350,11 +363,13 @@
|
|||||||
border-bottom: 1px solid var(--t-border-default);
|
border-bottom: 1px solid var(--t-border-default);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shop-logo {
|
.shop-logo {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
@ -362,6 +377,7 @@
|
|||||||
.shop-logo-link {
|
.shop-logo-link {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,24 +393,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.shop-nav {
|
.shop-nav {
|
||||||
|
display: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shop-actions {
|
.shop-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-icon-btn {
|
.header-icon-btn {
|
||||||
|
width: 2.25rem;
|
||||||
|
height: 2.25rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
color: var(--t-text-secondary);
|
color: var(--t-text-secondary);
|
||||||
border-radius: var(--t-radius-button);
|
border-radius: var(--t-radius-button);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
&:where(button) {
|
&:where(button) {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cart-badge {
|
.cart-badge {
|
||||||
@ -441,22 +472,50 @@
|
|||||||
padding-bottom: env(safe-area-inset-bottom, 0px);
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||||
|
|
||||||
& ul {
|
& ul {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
height: 4rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& li {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-nav-link {
|
.mobile-nav-link {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
padding-block: 0.5rem;
|
||||||
|
margin-inline: 0.25rem;
|
||||||
|
min-height: 56px;
|
||||||
|
border-radius: var(--t-radius-card, 0.5rem);
|
||||||
|
font-size: var(--t-text-caption);
|
||||||
color: var(--t-text-secondary);
|
color: var(--t-text-secondary);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
&[aria-current="page"] {
|
&[aria-current="page"] {
|
||||||
color: hsl(var(--t-accent-h) var(--t-accent-s) calc(var(--t-accent-l) - 15%));
|
color: hsl(var(--t-accent-h) var(--t-accent-s) calc(var(--t-accent-l) - 15%));
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l) / 0.1);
|
background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l) / 0.1);
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,6 +532,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-panel {
|
.search-panel {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 36rem;
|
||||||
|
margin-inline: 1rem;
|
||||||
background: var(--t-surface-raised);
|
background: var(--t-surface-raised);
|
||||||
border-radius: var(--t-radius-card);
|
border-radius: var(--t-radius-card);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -480,39 +542,79 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-bar {
|
.search-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 1rem;
|
||||||
border-bottom: 1px solid var(--t-border-default);
|
border-bottom: 1px solid var(--t-border-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-icon {
|
.search-icon {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
flex-shrink: 0;
|
||||||
color: var(--t-text-tertiary);
|
color: var(--t-text-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: var(--t-text-large, 1.125rem);
|
||||||
font-family: var(--t-font-body);
|
font-family: var(--t-font-body);
|
||||||
color: var(--t-text-primary);
|
color: var(--t-text-primary);
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-kbd {
|
.search-kbd {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
font-size: var(--t-text-caption);
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
border-radius: var(--t-radius-button, 0.25rem);
|
||||||
color: var(--t-text-tertiary);
|
color: var(--t-text-tertiary);
|
||||||
border: 1px solid var(--t-border-default);
|
border: 1px solid var(--t-border-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-close {
|
.search-close {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
color: var(--t-text-tertiary);
|
color: var(--t-text-tertiary);
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: var(--t-radius-button);
|
border-radius: var(--t-radius-button);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-results {
|
.search-results {
|
||||||
max-height: 60vh;
|
max-height: 60vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
& ul {
|
||||||
|
padding-block: 0.5rem;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result {
|
.search-result {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
transition: background-color 0.15s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--t-surface-sunken);
|
background: var(--t-surface-sunken);
|
||||||
@ -520,19 +622,47 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-result-thumb {
|
.search-result-thumb {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: var(--t-radius-card, 0.25rem);
|
||||||
|
overflow: hidden;
|
||||||
background: var(--t-surface-sunken);
|
background: var(--t-surface-sunken);
|
||||||
|
|
||||||
|
& img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-details {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result-title {
|
.search-result-title {
|
||||||
|
font-size: var(--t-text-small);
|
||||||
|
font-weight: 500;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
color: var(--t-text-primary);
|
color: var(--t-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result-meta {
|
.search-result-meta {
|
||||||
|
font-size: var(--t-text-caption);
|
||||||
color: var(--t-text-tertiary);
|
color: var(--t-text-tertiary);
|
||||||
|
|
||||||
|
& span {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-hint {
|
.search-hint {
|
||||||
|
padding: 1.5rem;
|
||||||
color: var(--t-text-tertiary);
|
color: var(--t-text-tertiary);
|
||||||
|
font-size: var(--t-text-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Shop footer ── */
|
/* ── Shop footer ── */
|
||||||
@ -542,21 +672,65 @@
|
|||||||
border-top: 1px solid var(--t-border-default);
|
border-top: 1px solid var(--t-border-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shop-footer-inner {
|
||||||
|
max-width: 80rem;
|
||||||
|
margin-inline: auto;
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-links {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.footer-heading {
|
.footer-heading {
|
||||||
font-family: var(--t-font-heading);
|
font-family: var(--t-font-heading);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: var(--t-text-small);
|
||||||
|
margin-bottom: 1rem;
|
||||||
color: var(--t-text-primary);
|
color: var(--t-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: var(--t-text-small);
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.footer-link {
|
.footer-link {
|
||||||
color: var(--t-text-secondary);
|
color: var(--t-text-secondary);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-bottom {
|
.footer-bottom {
|
||||||
|
margin-top: 3rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
border-top: 1px solid var(--t-border-subtle);
|
border-top: 1px solid var(--t-border-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-copyright {
|
.footer-copyright {
|
||||||
|
font-size: var(--t-text-caption);
|
||||||
color: var(--t-text-tertiary);
|
color: var(--t-text-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1081,7 +1255,24 @@
|
|||||||
|
|
||||||
/* ── Checkout success ── */
|
/* ── Checkout success ── */
|
||||||
|
|
||||||
|
.checkout-main {
|
||||||
|
max-width: 48rem;
|
||||||
|
padding-block: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkout-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
.checkout-icon {
|
.checkout-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
background-color: var(--t-accent);
|
background-color: var(--t-accent);
|
||||||
color: var(--t-accent-contrast);
|
color: var(--t-accent-contrast);
|
||||||
}
|
}
|
||||||
@ -1089,52 +1280,120 @@
|
|||||||
.checkout-heading {
|
.checkout-heading {
|
||||||
font-family: var(--t-font-heading);
|
font-family: var(--t-font-heading);
|
||||||
color: var(--t-text-primary);
|
color: var(--t-text-primary);
|
||||||
|
|
||||||
|
&:where(h1) {
|
||||||
|
font-size: var(--t-text-3xl, 1.875rem);
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:where(h2) {
|
||||||
|
font-size: var(--t-text-large, 1.125rem);
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-meta {
|
.checkout-meta {
|
||||||
|
font-size: var(--t-text-large, 1.125rem);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
color: var(--t-text-secondary);
|
color: var(--t-text-secondary);
|
||||||
|
|
||||||
& strong {
|
& strong {
|
||||||
color: var(--t-text-primary);
|
color: var(--t-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
font-size: var(--t-text-base, 1rem);
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkout-card {
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-items {
|
.checkout-items {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin-top: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-item {
|
.checkout-item {
|
||||||
border-color: var(--t-border-default);
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
border-bottom: 1px solid var(--t-border-default);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-item-name {
|
.checkout-item-name {
|
||||||
|
font-weight: 500;
|
||||||
color: var(--t-text-primary);
|
color: var(--t-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-item-detail {
|
.checkout-item-detail {
|
||||||
|
font-size: var(--t-text-small, 0.875rem);
|
||||||
color: var(--t-text-secondary);
|
color: var(--t-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-item-price {
|
.checkout-item-price {
|
||||||
|
font-weight: 500;
|
||||||
color: var(--t-text-primary);
|
color: var(--t-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-total-border {
|
.checkout-total-border {
|
||||||
border-color: var(--t-border-default);
|
border-top: 1px solid var(--t-border-default);
|
||||||
|
padding-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-total {
|
.checkout-total {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: var(--t-text-large, 1.125rem);
|
||||||
color: var(--t-text-primary);
|
color: var(--t-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkout-total-label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkout-total-amount {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.checkout-shipping-address {
|
.checkout-shipping-address {
|
||||||
color: var(--t-text-secondary);
|
color: var(--t-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkout-actions {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkout-cta {
|
||||||
|
padding: 0.75rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.checkout-pending-icon {
|
.checkout-pending-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
background-color: var(--t-surface-sunken);
|
background-color: var(--t-surface-sunken);
|
||||||
|
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-pending-spinner {
|
.checkout-pending-spinner {
|
||||||
@ -1142,34 +1401,140 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.checkout-pending-text {
|
.checkout-pending-text {
|
||||||
|
font-size: var(--t-text-large, 1.125rem);
|
||||||
|
margin-bottom: 2rem;
|
||||||
color: var(--t-text-secondary);
|
color: var(--t-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-pending-hint {
|
.checkout-pending-hint {
|
||||||
|
font-size: var(--t-text-small, 0.875rem);
|
||||||
color: var(--t-text-tertiary);
|
color: var(--t-text-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkout-contact-link {
|
.checkout-contact-link {
|
||||||
|
text-decoration: underline;
|
||||||
color: var(--t-accent);
|
color: var(--t-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
50% { opacity: 0.5; }
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Error page ── */
|
/* ── Error page ── */
|
||||||
|
|
||||||
.error-main {
|
.error-main {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
min-height: calc(100vh - 4rem);
|
min-height: calc(100vh - 4rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── PDP variant fallback ── */
|
.error-container {
|
||||||
|
max-width: 42rem;
|
||||||
|
padding-block: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── PDP page ── */
|
||||||
|
|
||||||
|
.pdp-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 3rem;
|
||||||
|
margin-bottom: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
.pdp-variant-fallback {
|
.pdp-variant-fallback {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
font-size: var(--t-text-small, 0.875rem);
|
||||||
color: var(--t-text-secondary);
|
color: var(--t-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Cart page list ── */
|
/* ── Cart page ── */
|
||||||
|
|
||||||
|
.cart-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-page-card {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.cart-page-list {
|
.cart-page-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Contact page ── */
|
||||||
|
|
||||||
|
.contact-main {
|
||||||
|
max-width: 56rem;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 2rem;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Content page image ── */
|
||||||
|
|
||||||
|
.content-hero-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Screen reader only ── */
|
||||||
|
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Responsive breakpoints ── */
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.page-container { padding-inline: 1.5rem; }
|
||||||
|
.shop-header { padding: 0.75rem 1rem; }
|
||||||
|
.shop-footer-inner { padding-inline: 1.5rem; }
|
||||||
|
.search-kbd { display: flex; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.shop-container[data-bottom-nav] { padding-bottom: 0; }
|
||||||
|
.shop-header { padding: 1rem 2rem; }
|
||||||
|
.shop-nav { display: flex; gap: 1.5rem; }
|
||||||
|
.mobile-bottom-nav { display: none; }
|
||||||
|
.footer-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.footer-bottom { flex-direction: row; }
|
||||||
|
.pdp-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.contact-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.page-container { padding-inline: 2rem; }
|
||||||
|
.shop-footer-inner { padding-inline: 2rem; }
|
||||||
|
.cart-grid { grid-template-columns: 2fr 1fr; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
<.shop_layout {layout_assigns(assigns)} active_page="cart">
|
<.shop_layout {layout_assigns(assigns)} active_page="cart">
|
||||||
<main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<main id="main-content" class="page-container">
|
||||||
<.page_title text="Your basket" />
|
<.page_title text="Your basket" />
|
||||||
|
|
||||||
<%= if @cart_items == [] do %>
|
<%= if @cart_items == [] do %>
|
||||||
<.cart_empty_state mode={@mode} />
|
<.cart_empty_state mode={@mode} />
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
<div class="cart-grid">
|
||||||
<div class="lg:col-span-2">
|
<div>
|
||||||
<ul
|
<ul
|
||||||
role="list"
|
role="list"
|
||||||
aria-label="Cart items"
|
aria-label="Cart items"
|
||||||
class="cart-page-list flex flex-col gap-4"
|
class="cart-page-list"
|
||||||
>
|
>
|
||||||
<%= for item <- @cart_items do %>
|
<%= for item <- @cart_items do %>
|
||||||
<li>
|
<li>
|
||||||
<.shop_card class="p-4">
|
<.shop_card class="cart-page-card">
|
||||||
<.cart_item_row item={item} size={:default} show_quantity_controls mode={@mode} />
|
<.cart_item_row item={item} size={:default} show_quantity_controls mode={@mode} />
|
||||||
</.shop_card>
|
</.shop_card>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
<.shop_layout {layout_assigns(assigns)} active_page="checkout">
|
<.shop_layout {layout_assigns(assigns)} active_page="checkout">
|
||||||
<main id="main-content" class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
<main id="main-content" class="page-container checkout-main">
|
||||||
<%= if @order && @order.payment_status == "paid" do %>
|
<%= if @order && @order.payment_status == "paid" do %>
|
||||||
<div class="text-center mb-12">
|
<div class="checkout-header">
|
||||||
<div class="checkout-icon inline-flex items-center justify-center w-16 h-16 rounded-full mb-6">
|
<div class="checkout-icon">
|
||||||
<svg
|
<svg
|
||||||
class="w-8 h-8"
|
width="32"
|
||||||
|
height="32"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="2.5"
|
stroke-width="2.5"
|
||||||
@ -14,11 +15,11 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="checkout-heading text-3xl font-bold mb-3">
|
<h1 class="checkout-heading">
|
||||||
Thank you for your order
|
Thank you for your order
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="checkout-meta text-lg mb-2">
|
<p class="checkout-meta">
|
||||||
Order <strong>{@order.order_number}</strong>
|
Order <strong>{@order.order_number}</strong>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -29,38 +30,38 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<.shop_card class="p-6 mb-8">
|
<.shop_card class="checkout-card">
|
||||||
<h2 class="checkout-heading text-lg font-semibold mb-4">
|
<h2 class="checkout-heading">
|
||||||
Order details
|
Order details
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<ul class="checkout-items flex flex-col gap-4 mb-6">
|
<ul class="checkout-items">
|
||||||
<%= for item <- @order.items do %>
|
<%= for item <- @order.items do %>
|
||||||
<li class="checkout-item flex justify-between items-start pb-4 border-b last:border-b-0 last:pb-0">
|
<li class="checkout-item">
|
||||||
<div>
|
<div>
|
||||||
<p class="checkout-item-name font-medium">
|
<p class="checkout-item-name">
|
||||||
{item.product_name}
|
{item.product_name}
|
||||||
</p>
|
</p>
|
||||||
<%= if item.variant_title do %>
|
<%= if item.variant_title do %>
|
||||||
<p class="checkout-item-detail text-sm">
|
<p class="checkout-item-detail">
|
||||||
{item.variant_title}
|
{item.variant_title}
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
<p class="checkout-item-detail text-sm">
|
<p class="checkout-item-detail">
|
||||||
Qty: {item.quantity}
|
Qty: {item.quantity}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="checkout-item-price font-medium">
|
<span class="checkout-item-price">
|
||||||
{SimpleshopTheme.Cart.format_price(item.unit_price * item.quantity)}
|
{SimpleshopTheme.Cart.format_price(item.unit_price * item.quantity)}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="checkout-total-border border-t pt-4">
|
<div class="checkout-total-border">
|
||||||
<div class="checkout-total flex justify-between text-lg">
|
<div class="checkout-total">
|
||||||
<span class="font-semibold">Total</span>
|
<span class="checkout-total-label">Total</span>
|
||||||
<span class="font-bold">
|
<span class="checkout-total-amount">
|
||||||
{SimpleshopTheme.Cart.format_price(@order.total)}
|
{SimpleshopTheme.Cart.format_price(@order.total)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -68,8 +69,8 @@
|
|||||||
</.shop_card>
|
</.shop_card>
|
||||||
|
|
||||||
<%= if @order.shipping_address != %{} do %>
|
<%= if @order.shipping_address != %{} do %>
|
||||||
<.shop_card class="p-6 mb-8">
|
<.shop_card class="checkout-card">
|
||||||
<h2 class="checkout-heading text-lg font-semibold mb-3">
|
<h2 class="checkout-heading">
|
||||||
Shipping to
|
Shipping to
|
||||||
</h2>
|
</h2>
|
||||||
<div class="checkout-shipping-address">
|
<div class="checkout-shipping-address">
|
||||||
@ -86,18 +87,19 @@
|
|||||||
</.shop_card>
|
</.shop_card>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="checkout-actions">
|
||||||
<.shop_link_button href="/collections/all" class="px-8 py-3">
|
<.shop_link_button href="/collections/all" class="checkout-cta">
|
||||||
Continue shopping
|
Continue shopping
|
||||||
</.shop_link_button>
|
</.shop_link_button>
|
||||||
</div>
|
</div>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%!-- Payment pending or order not found --%>
|
<%!-- Payment pending or order not found --%>
|
||||||
<div class="text-center py-16">
|
<div class="checkout-header">
|
||||||
<div class="checkout-pending-icon inline-flex items-center justify-center w-16 h-16 rounded-full mb-6 animate-pulse">
|
<div class="checkout-pending-icon">
|
||||||
<span class="checkout-pending-spinner">
|
<span class="checkout-pending-spinner">
|
||||||
<svg
|
<svg
|
||||||
class="w-8 h-8"
|
width="32"
|
||||||
|
height="32"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
@ -112,18 +114,18 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="checkout-heading text-3xl font-bold mb-3">
|
<h1 class="checkout-heading">
|
||||||
Processing your payment
|
Processing your payment
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="checkout-pending-text text-lg mb-8">
|
<p class="checkout-pending-text">
|
||||||
Please wait while we confirm your payment. This usually takes a few seconds.
|
Please wait while we confirm your payment. This usually takes a few seconds.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="checkout-pending-hint text-sm">
|
<p class="checkout-pending-hint">
|
||||||
If this page doesn't update, please <.link
|
If this page doesn't update, please <.link
|
||||||
navigate="/contact"
|
navigate="/contact"
|
||||||
class="checkout-contact-link underline"
|
class="checkout-contact-link"
|
||||||
>contact us</.link>.
|
>contact us</.link>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<main id="main-content">
|
<main id="main-content">
|
||||||
<.collection_header title="All Products" product_count={length(assigns[:products] || [])} />
|
<.collection_header title="All Products" product_count={length(assigns[:products] || [])} />
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div class="page-container">
|
||||||
<.filter_bar categories={assigns[:categories] || []} />
|
<.filter_bar categories={assigns[:categories] || []} />
|
||||||
|
|
||||||
<.product_grid theme_settings={@theme_settings}>
|
<.product_grid theme_settings={@theme_settings}>
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
<.shop_layout {layout_assigns(assigns)} active_page="contact">
|
<.shop_layout {layout_assigns(assigns)} active_page="contact">
|
||||||
<main id="main-content" class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pb-16">
|
<main id="main-content" class="page-container contact-main">
|
||||||
<.hero_section
|
<.hero_section
|
||||||
variant={:page}
|
variant={:page}
|
||||||
title="Get in touch"
|
title="Get in touch"
|
||||||
description="Sample contact page for the demo store. Add your own message here – something friendly about how customers can reach you."
|
description="Sample contact page for the demo store. Add your own message here – something friendly about how customers can reach you."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="grid gap-8 md:grid-cols-2 mb-12">
|
<div class="contact-grid">
|
||||||
<.contact_form email="hello@example.com" />
|
<.contact_form email="hello@example.com" />
|
||||||
|
|
||||||
<div class="flex flex-col gap-6">
|
<div class="contact-sidebar">
|
||||||
<.order_tracking_card />
|
<.order_tracking_card />
|
||||||
|
|
||||||
<.info_card
|
<.info_card
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
source_width={1200}
|
source_width={1200}
|
||||||
alt={@image_alt}
|
alt={@image_alt}
|
||||||
sizes="(max-width: 800px) 100vw, 800px"
|
sizes="(max-width: 800px) 100vw, 800px"
|
||||||
class="w-full h-[300px] object-cover"
|
class="content-hero-image"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
<.shop_layout {layout_assigns(assigns)} active_page="error" error_page>
|
<.shop_layout {layout_assigns(assigns)} active_page="error" error_page>
|
||||||
<main
|
<main
|
||||||
id="main-content"
|
id="main-content"
|
||||||
class="error-main flex items-center justify-center"
|
class="error-main"
|
||||||
>
|
>
|
||||||
<div class="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
<div class="page-container error-container">
|
||||||
<.hero_section
|
<.hero_section
|
||||||
variant={:error}
|
variant={:error}
|
||||||
pre_title={@error_code}
|
pre_title={@error_code}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<.shop_layout {layout_assigns(assigns)} active_page="pdp">
|
<.shop_layout {layout_assigns(assigns)} active_page="pdp">
|
||||||
<main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<main id="main-content" class="page-container">
|
||||||
<.breadcrumb
|
<.breadcrumb
|
||||||
items={
|
items={
|
||||||
if @product.category do
|
if @product.category do
|
||||||
@ -19,7 +19,7 @@
|
|||||||
mode={@mode}
|
mode={@mode}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 mb-16">
|
<div class="pdp-grid">
|
||||||
<.product_gallery images={@gallery_images} product_name={@product.title} />
|
<.product_gallery images={@gallery_images} product_name={@product.title} />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -38,7 +38,7 @@
|
|||||||
<%!-- Fallback for products with no variant options --%>
|
<%!-- Fallback for products with no variant options --%>
|
||||||
<div
|
<div
|
||||||
:if={@option_types == []}
|
:if={@option_types == []}
|
||||||
class="pdp-variant-fallback mb-6 text-sm"
|
class="pdp-variant-fallback"
|
||||||
>
|
>
|
||||||
One size
|
One size
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -100,7 +100,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<div
|
<div
|
||||||
id={unless @error_page, do: "shop-container"}
|
id={unless @error_page, do: "shop-container"}
|
||||||
phx-hook={unless @error_page, do: "CartPersist"}
|
phx-hook={unless @error_page, do: "CartPersist"}
|
||||||
class={["shop-container min-h-screen", !@error_page && "pb-20 md:pb-0"]}
|
class="shop-container"
|
||||||
|
data-bottom-nav={!@error_page || nil}
|
||||||
>
|
>
|
||||||
<.skip_link />
|
<.skip_link />
|
||||||
|
|
||||||
@ -177,10 +178,10 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
def mobile_bottom_nav(assigns) do
|
def mobile_bottom_nav(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<nav
|
<nav
|
||||||
class="mobile-bottom-nav md:hidden"
|
class="mobile-bottom-nav"
|
||||||
aria-label="Main navigation"
|
aria-label="Main navigation"
|
||||||
>
|
>
|
||||||
<ul class="flex justify-around items-center h-16">
|
<ul>
|
||||||
<.mobile_nav_item
|
<.mobile_nav_item
|
||||||
icon={:home}
|
icon={:home}
|
||||||
label="Home"
|
label="Home"
|
||||||
@ -233,26 +234,26 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
assigns = assign(assigns, :is_current, is_current)
|
assigns = assign(assigns, :is_current, is_current)
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<li class="flex-1">
|
<li>
|
||||||
<%= if @mode == :preview do %>
|
<%= if @mode == :preview do %>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
phx-click="change_preview_page"
|
phx-click="change_preview_page"
|
||||||
phx-value-page={@page}
|
phx-value-page={@page}
|
||||||
class="mobile-nav-link flex flex-col items-center justify-center gap-1 py-2 mx-1 rounded-lg min-h-[56px]"
|
class="mobile-nav-link"
|
||||||
aria-current={if @is_current, do: "page", else: nil}
|
aria-current={if @is_current, do: "page", else: nil}
|
||||||
>
|
>
|
||||||
<.nav_icon icon={@icon} size={if @is_current, do: "w-6 h-6", else: "w-5 h-5"} />
|
<.nav_icon icon={@icon} />
|
||||||
<span class="text-xs">{@label}</span>
|
<span>{@label}</span>
|
||||||
</a>
|
</a>
|
||||||
<% else %>
|
<% else %>
|
||||||
<.link
|
<.link
|
||||||
navigate={@href}
|
navigate={@href}
|
||||||
class="mobile-nav-link flex flex-col items-center justify-center gap-1 py-2 mx-1 rounded-lg min-h-[56px]"
|
class="mobile-nav-link"
|
||||||
aria-current={if @is_current, do: "page", else: nil}
|
aria-current={if @is_current, do: "page", else: nil}
|
||||||
>
|
>
|
||||||
<.nav_icon icon={@icon} size={if @is_current, do: "w-6 h-6", else: "w-5 h-5"} />
|
<.nav_icon icon={@icon} />
|
||||||
<span class="text-xs">{@label}</span>
|
<span>{@label}</span>
|
||||||
</.link>
|
</.link>
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
@ -260,11 +261,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp nav_icon(%{icon: :home} = assigns) do
|
defp nav_icon(%{icon: :home} = assigns) do
|
||||||
assigns = assign_new(assigns, :size, fn -> "w-5 h-5" end)
|
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<svg
|
<svg
|
||||||
class={@size}
|
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -281,11 +279,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp nav_icon(%{icon: :shop} = assigns) do
|
defp nav_icon(%{icon: :shop} = assigns) do
|
||||||
assigns = assign_new(assigns, :size, fn -> "w-5 h-5" end)
|
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<svg
|
<svg
|
||||||
class={@size}
|
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -304,11 +299,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp nav_icon(%{icon: :about} = assigns) do
|
defp nav_icon(%{icon: :about} = assigns) do
|
||||||
assigns = assign_new(assigns, :size, fn -> "w-5 h-5" end)
|
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<svg
|
<svg
|
||||||
class={@size}
|
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -326,11 +318,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp nav_icon(%{icon: :contact} = assigns) do
|
defp nav_icon(%{icon: :contact} = assigns) do
|
||||||
assigns = assign_new(assigns, :size, fn -> "w-5 h-5" end)
|
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<svg
|
<svg
|
||||||
class={@size}
|
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -385,12 +374,12 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="search-panel w-full max-w-xl mx-4"
|
class="search-panel"
|
||||||
onclick="event.stopPropagation()"
|
onclick="event.stopPropagation()"
|
||||||
>
|
>
|
||||||
<div class="search-bar flex items-center gap-3 p-4">
|
<div class="search-bar">
|
||||||
<svg
|
<svg
|
||||||
class="search-icon w-5 h-5 flex-shrink-0"
|
class="search-icon"
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -405,7 +394,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
type="text"
|
type="text"
|
||||||
id="search-input"
|
id="search-input"
|
||||||
name="query"
|
name="query"
|
||||||
class="search-input flex-1 text-lg bg-transparent border-none outline-none"
|
class="search-input"
|
||||||
placeholder="Search products..."
|
placeholder="Search products..."
|
||||||
value={@search_query}
|
value={@search_query}
|
||||||
phx-keyup="search"
|
phx-keyup="search"
|
||||||
@ -417,19 +406,18 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="search-kbd hidden sm:flex items-center gap-1 text-xs px-1.5 py-0.5 rounded"
|
class="search-kbd"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<kbd>⌘</kbd><kbd>K</kbd>
|
<kbd>⌘</kbd><kbd>K</kbd>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="search-close w-8 h-8 flex items-center justify-center transition-all"
|
class="search-close"
|
||||||
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
||||||
aria-label="Close search"
|
aria-label="Close search"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="w-5 h-5"
|
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -446,7 +434,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<div class="search-results">
|
<div class="search-results">
|
||||||
<%= cond do %>
|
<%= cond do %>
|
||||||
<% @search_results != [] -> %>
|
<% @search_results != [] -> %>
|
||||||
<ul id="search-results-list" class="py-2" role="listbox" aria-label="Search results">
|
<ul id="search-results-list" role="listbox" aria-label="Search results">
|
||||||
<li
|
<li
|
||||||
:for={item <- @results_with_images}
|
:for={item <- @results_with_images}
|
||||||
id={"search-result-#{item.idx}"}
|
id={"search-result-#{item.idx}"}
|
||||||
@ -455,27 +443,26 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
>
|
>
|
||||||
<.link
|
<.link
|
||||||
navigate={"/products/#{item.product.slug || item.product.id}"}
|
navigate={"/products/#{item.product.slug || item.product.id}"}
|
||||||
class="search-result flex items-center gap-3 px-4 py-3 transition-colors"
|
class="search-result"
|
||||||
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:if={item.image_url}
|
:if={item.image_url}
|
||||||
class="search-result-thumb w-12 h-12 flex-shrink-0 rounded overflow-hidden"
|
class="search-result-thumb"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={item.image_url}
|
src={item.image_url}
|
||||||
alt={item.product.title}
|
alt={item.product.title}
|
||||||
class="w-full h-full object-cover"
|
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 min-w-0">
|
<div class="search-result-details">
|
||||||
<p class="search-result-title text-sm font-medium truncate">
|
<p class="search-result-title">
|
||||||
{item.product.title}
|
{item.product.title}
|
||||||
</p>
|
</p>
|
||||||
<p class="search-result-meta text-xs">
|
<p class="search-result-meta">
|
||||||
{item.product.category}
|
{item.product.category}
|
||||||
<span class="ml-2">
|
<span>
|
||||||
{Cart.format_price(item.product.cheapest_price)}
|
{Cart.format_price(item.product.cheapest_price)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
@ -484,12 +471,12 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<% String.length(@search_query) >= 2 -> %>
|
<% String.length(@search_query) >= 2 -> %>
|
||||||
<div class="search-hint p-6">
|
<div class="search-hint">
|
||||||
<p class="text-sm">No products found for "{@search_query}"</p>
|
<p>No products found for "{@search_query}"</p>
|
||||||
</div>
|
</div>
|
||||||
<% @hint_text != nil -> %>
|
<% @hint_text != nil -> %>
|
||||||
<div class="search-hint p-6">
|
<div class="search-hint">
|
||||||
<p class="text-sm">{@hint_text}</p>
|
<p>{@hint_text}</p>
|
||||||
</div>
|
</div>
|
||||||
<% true -> %>
|
<% true -> %>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -522,24 +509,23 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<footer class="shop-footer">
|
<footer class="shop-footer">
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
<div class="shop-footer-inner">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-12">
|
<div class="footer-grid">
|
||||||
<.newsletter_card variant={:inline} />
|
<.newsletter_card variant={:inline} />
|
||||||
|
|
||||||
<!-- Links -->
|
<div class="footer-links">
|
||||||
<div class="grid grid-cols-2 gap-8">
|
|
||||||
<div>
|
<div>
|
||||||
<h4 class="footer-heading font-semibold mb-4 text-sm">
|
<h4 class="footer-heading">
|
||||||
Shop
|
Shop
|
||||||
</h4>
|
</h4>
|
||||||
<ul class="flex flex-col gap-2 text-sm">
|
<ul class="footer-nav">
|
||||||
<%= if @mode == :preview do %>
|
<%= if @mode == :preview do %>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
phx-click="change_preview_page"
|
phx-click="change_preview_page"
|
||||||
phx-value-page="collection"
|
phx-value-page="collection"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
All products
|
All products
|
||||||
</a>
|
</a>
|
||||||
@ -550,7 +536,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
href="#"
|
href="#"
|
||||||
phx-click="change_preview_page"
|
phx-click="change_preview_page"
|
||||||
phx-value-page="collection"
|
phx-value-page="collection"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
{category.name}
|
{category.name}
|
||||||
</a>
|
</a>
|
||||||
@ -560,7 +546,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
navigate="/collections/all"
|
navigate="/collections/all"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
All products
|
All products
|
||||||
</.link>
|
</.link>
|
||||||
@ -569,7 +555,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
navigate={"/collections/#{category.slug}"}
|
navigate={"/collections/#{category.slug}"}
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
{category.name}
|
{category.name}
|
||||||
</.link>
|
</.link>
|
||||||
@ -579,17 +565,17 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4 class="footer-heading font-semibold mb-4 text-sm">
|
<h4 class="footer-heading">
|
||||||
Help
|
Help
|
||||||
</h4>
|
</h4>
|
||||||
<ul class="flex flex-col gap-2 text-sm">
|
<ul class="footer-nav">
|
||||||
<%= if @mode == :preview do %>
|
<%= if @mode == :preview do %>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
phx-click="change_preview_page"
|
phx-click="change_preview_page"
|
||||||
phx-value-page="delivery"
|
phx-value-page="delivery"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
Delivery & returns
|
Delivery & returns
|
||||||
</a>
|
</a>
|
||||||
@ -599,7 +585,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
href="#"
|
href="#"
|
||||||
phx-click="change_preview_page"
|
phx-click="change_preview_page"
|
||||||
phx-value-page="privacy"
|
phx-value-page="privacy"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
Privacy policy
|
Privacy policy
|
||||||
</a>
|
</a>
|
||||||
@ -609,7 +595,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
href="#"
|
href="#"
|
||||||
phx-click="change_preview_page"
|
phx-click="change_preview_page"
|
||||||
phx-value-page="terms"
|
phx-value-page="terms"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
Terms of service
|
Terms of service
|
||||||
</a>
|
</a>
|
||||||
@ -619,7 +605,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
href="#"
|
href="#"
|
||||||
phx-click="change_preview_page"
|
phx-click="change_preview_page"
|
||||||
phx-value-page="contact"
|
phx-value-page="contact"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
Contact
|
Contact
|
||||||
</a>
|
</a>
|
||||||
@ -628,7 +614,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
navigate="/delivery"
|
navigate="/delivery"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
Delivery & returns
|
Delivery & returns
|
||||||
</.link>
|
</.link>
|
||||||
@ -636,7 +622,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
navigate="/privacy"
|
navigate="/privacy"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
Privacy policy
|
Privacy policy
|
||||||
</.link>
|
</.link>
|
||||||
@ -644,7 +630,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
navigate="/terms"
|
navigate="/terms"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
Terms of service
|
Terms of service
|
||||||
</.link>
|
</.link>
|
||||||
@ -652,7 +638,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<li>
|
<li>
|
||||||
<.link
|
<.link
|
||||||
navigate="/contact"
|
navigate="/contact"
|
||||||
class="footer-link transition-colors hover:opacity-80"
|
class="footer-link"
|
||||||
>
|
>
|
||||||
Contact
|
Contact
|
||||||
</.link>
|
</.link>
|
||||||
@ -664,8 +650,8 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bottom Bar -->
|
<!-- Bottom Bar -->
|
||||||
<div class="footer-bottom mt-12 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
|
<div class="footer-bottom">
|
||||||
<p class="footer-copyright text-xs">
|
<p class="footer-copyright">
|
||||||
© {@current_year} {@theme_settings.site_name}
|
© {@current_year} {@theme_settings.site_name}
|
||||||
</p>
|
</p>
|
||||||
<.social_links />
|
<.social_links />
|
||||||
@ -702,7 +688,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
|
|
||||||
def shop_header(assigns) do
|
def shop_header(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<header class="shop-header px-2 py-2 sm:px-4 sm:py-3 md:px-8 md:py-4">
|
<header class="shop-header">
|
||||||
<%= if @theme_settings.header_background_enabled && @header_image do %>
|
<%= if @theme_settings.header_background_enabled && @header_image do %>
|
||||||
<div style={header_background_style(@theme_settings, @header_image)} />
|
<div style={header_background_style(@theme_settings, @header_image)} />
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -716,7 +702,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="shop-nav hidden md:flex md:gap-6">
|
<nav class="shop-nav">
|
||||||
<%= if @mode == :preview do %>
|
<%= if @mode == :preview do %>
|
||||||
<.nav_item label="Home" page="home" active_page={@active_page} mode={:preview} />
|
<.nav_item label="Home" page="home" active_page={@active_page} mode={:preview} />
|
||||||
<.nav_item
|
<.nav_item
|
||||||
@ -742,15 +728,14 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<% end %>
|
<% end %>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="shop-actions flex items-center">
|
<div class="shop-actions">
|
||||||
<.link
|
<.link
|
||||||
:if={@is_admin}
|
:if={@is_admin}
|
||||||
href="/admin"
|
href="/admin"
|
||||||
class="header-icon-btn w-9 h-9 flex items-center justify-center transition-all"
|
class="header-icon-btn"
|
||||||
aria-label="Admin"
|
aria-label="Admin"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="w-5 h-5"
|
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -772,12 +757,11 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
</.link>
|
</.link>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="header-icon-btn w-9 h-9 flex items-center justify-center transition-all"
|
class="header-icon-btn"
|
||||||
phx-click={Phoenix.LiveView.JS.dispatch("open-search", to: "#search-modal")}
|
phx-click={Phoenix.LiveView.JS.dispatch("open-search", to: "#search-modal")}
|
||||||
aria-label="Search"
|
aria-label="Search"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="w-5 h-5"
|
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -791,12 +775,11 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="header-icon-btn w-9 h-9 flex items-center justify-center transition-all relative"
|
class="header-icon-btn"
|
||||||
phx-click={open_cart_drawer_js()}
|
phx-click={open_cart_drawer_js()}
|
||||||
aria-label="Cart"
|
aria-label="Cart"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="w-5 h-5"
|
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -875,7 +858,7 @@ defmodule SimpleshopThemeWeb.ShopComponents.Layout do
|
|||||||
<img
|
<img
|
||||||
src={logo_url(@logo_image, @theme_settings)}
|
src={logo_url(@logo_image, @theme_settings)}
|
||||||
alt={@theme_settings.site_name}
|
alt={@theme_settings.site_name}
|
||||||
class="shop-logo-img mr-2"
|
class="shop-logo-img"
|
||||||
style={"height: #{@theme_settings.logo_size}px;"}
|
style={"height: #{@theme_settings.logo_size}px;"}
|
||||||
/>
|
/>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user