Files
oikos/public/styles/shopping.css
T
Ulas dd6c8a313a fix(pwa): fix remaining iOS scroll bleed and safe-area height overflow (#16)
Root cause 1 (scroll bleed): padding-top was applied to body in standalone
mode. Since .app-shell has height: 100dvh, body-padding shifted the shell
beyond the viewport bottom - enabling body-level scrolling.
Fix: moved padding-top from body to .app-shell in the standalone media query.

Root cause 2 (content overflow): fixed-height page containers
(Calendar, Shopping, Meals, Notes, Budget, Contacts) calculated height as
100dvh - nav-bottom - safe-area-inset-bottom, but never subtracted the top
safe area. This caused each page to overflow .app-content by exactly
env(safe-area-inset-top) pixels in standalone mode.
Fix: added --safe-area-inset-top token and subtracted it in all 6 height
calculations.

Service worker cache bumped to v27/v26.
2026-04-06 10:10:01 +02:00

472 lines
11 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Modul: Einkaufslisten (Shopping)
* Zweck: Styles für Listen-Tabs, Artikel-Liste, Kategorie-Gruppen, Quick-Add, Autocomplete
* Abhängigkeiten: tokens.css, layout.css
*/
/* --------------------------------------------------------
* Modul-Akzent
* -------------------------------------------------------- */
.shopping-page { --module-accent: var(--module-shopping); }
/* --------------------------------------------------------
* Seiten-Layout
* -------------------------------------------------------- */
.shopping-page {
display: flex;
flex-direction: column;
height: calc(100dvh - var(--safe-area-inset-top) - var(--nav-bottom-height) - var(--safe-area-inset-bottom));
max-width: var(--content-max-width);
margin: 0 auto;
overflow: hidden;
}
@media (min-width: 1024px) {
.shopping-page {
height: 100dvh;
}
}
/* --------------------------------------------------------
* Listen-Tabs (horizontal scroll)
* -------------------------------------------------------- */
.list-tabs-bar {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--color-border);
background-color: var(--color-surface);
overflow-x: auto;
scrollbar-width: none;
flex-shrink: 0;
}
.list-tabs-bar::-webkit-scrollbar { display: none; }
.list-tab {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-full);
font-size: var(--text-sm);
font-weight: var(--font-weight-medium);
white-space: nowrap;
cursor: pointer;
border: 1.5px solid var(--color-border);
background-color: transparent;
color: var(--color-text-secondary);
transition: all var(--transition-fast);
min-height: 36px;
}
.list-tab:hover:not(.list-tab--active) {
border-color: var(--color-text-secondary);
color: var(--color-text-primary);
}
.list-tab--active {
background-color: var(--color-accent);
border-color: var(--color-accent);
color: var(--color-text-on-accent);
}
.list-tab__count {
background-color: var(--color-glass);
border-radius: var(--radius-full);
padding: var(--space-px) var(--space-1);
font-size: var(--text-xs);
}
.list-tab--active .list-tab__count {
background-color: var(--color-glass-hover);
}
.list-tab__new {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: var(--radius-full);
border: 1.5px dashed var(--color-border);
background: none;
color: var(--color-text-secondary);
cursor: pointer;
flex-shrink: 0;
transition: all var(--transition-fast);
min-height: var(--target-lg);
}
.list-tab__new:hover {
border-color: var(--color-accent);
color: var(--color-accent);
}
/* --------------------------------------------------------
* Liste-Header (Name + Aktionen)
* -------------------------------------------------------- */
.list-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-3) var(--space-4) var(--space-2);
flex-shrink: 0;
border-top: 3px solid var(--module-accent);
}
.list-header__name {
font-size: var(--text-lg);
font-weight: var(--font-weight-bold);
cursor: pointer;
display: flex;
align-items: center;
gap: var(--space-2);
color: var(--color-text-primary);
flex: 1;
min-width: 0;
overflow: hidden;
}
.list-header__edit-icon {
width: var(--space-4);
height: var(--space-4);
color: var(--color-text-disabled);
}
.list-header__actions {
display: flex;
align-items: center;
gap: var(--space-2);
flex-shrink: 0;
}
/* --------------------------------------------------------
* Quick-Add Eingabe
* -------------------------------------------------------- */
.quick-add {
padding: var(--space-2) var(--space-4) var(--space-3);
flex-shrink: 0;
position: relative;
}
.quick-add__form {
display: flex;
gap: var(--space-2);
align-items: stretch;
}
.quick-add__input-wrap {
flex: 1;
position: relative;
}
.quick-add__input {
width: 100%;
padding: var(--space-3) var(--space-4);
padding-right: var(--space-20, 80px);
border-radius: var(--radius-sm);
border: 1.5px solid var(--color-border);
background-color: var(--color-surface);
color: var(--color-text-primary);
font-size: var(--text-md);
transition: border-color var(--transition-fast);
min-height: 44px;
}
.quick-add__input:focus {
outline: none;
border-color: var(--color-accent);
}
.quick-add__qty {
position: absolute;
right: var(--space-2);
top: 50%;
transform: translateY(-50%);
width: 70px;
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-xs);
border: 1px solid var(--color-border);
background-color: var(--color-surface);
color: var(--color-text-primary);
font-size: var(--text-md);
text-align: center;
min-height: auto;
height: auto;
}
.quick-add__qty:focus {
outline: none;
border-color: var(--color-accent);
}
.quick-add__cat {
width: auto;
min-width: 100px;
max-width: 160px;
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-sm);
border: 1.5px solid var(--color-border);
background-color: var(--color-surface);
color: var(--color-text-secondary);
font-size: var(--text-md);
cursor: pointer;
min-height: 44px;
}
.quick-add__cat:focus {
outline: none;
border-color: var(--color-accent);
}
@media (min-width: 1024px) {
.quick-add__input { font-size: var(--text-base); }
.quick-add__qty { font-size: var(--text-sm); }
.quick-add__cat { font-size: var(--text-sm); }
}
.quick-add__btn {
width: 44px;
height: 44px;
border-radius: var(--radius-sm);
background-color: var(--color-accent);
color: var(--color-text-on-accent);
display: flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
flex-shrink: 0;
transition: background-color var(--transition-fast);
min-height: var(--target-lg);
}
.quick-add__btn:hover { background-color: var(--color-accent-hover); }
/* Autocomplete-Dropdown */
.autocomplete-dropdown {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
background-color: var(--color-surface);
border: 1.5px solid var(--color-border);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-md);
z-index: var(--z-modal);
overflow: hidden;
max-height: 200px;
overflow-y: auto;
}
.autocomplete-item {
padding: var(--space-3) var(--space-4);
cursor: pointer;
font-size: var(--text-base);
transition: background-color var(--transition-fast);
}
.autocomplete-item:hover,
.autocomplete-item--active {
background-color: var(--color-accent-light);
color: var(--color-accent);
}
/* --------------------------------------------------------
* Artikel-Liste (scrollbar)
* -------------------------------------------------------- */
.items-list {
flex: 1;
overflow-y: auto;
padding: 0 var(--space-4) var(--space-4);
-webkit-overflow-scrolling: touch;
}
/* --------------------------------------------------------
* Kategorie-Gruppen
* -------------------------------------------------------- */
.item-category {
margin-bottom: var(--space-4);
}
.item-category__header {
font-size: var(--text-xs);
font-weight: var(--font-weight-semibold);
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.06em;
padding: var(--space-2) 0 var(--space-1);
border-bottom: 1px solid var(--color-border-subtle);
margin-bottom: var(--space-1);
display: flex;
align-items: center;
gap: var(--space-2);
}
.item-category__icon {
width: var(--space-3);
height: var(--space-3);
}
/* --------------------------------------------------------
* Einkaufsartikel
* -------------------------------------------------------- */
.shopping-item {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) var(--space-1);
border-bottom: 1px solid var(--color-border-subtle);
border-left: 3px solid var(--module-accent);
transition: opacity var(--transition-fast);
min-height: 44px;
}
.shopping-item:last-child { border-bottom: none; }
.shopping-item--checked {
opacity: 0.45;
}
/* Checkbox */
.item-check {
width: 24px;
height: 24px;
border-radius: var(--radius-sm);
border: 2px solid var(--color-border);
background: none;
cursor: pointer;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
transition: all var(--transition-fast);
position: relative;
}
.item-check::before {
content: '';
position: absolute;
inset: -12px;
}
.item-check--checked {
background-color: var(--color-success);
border-color: var(--color-success);
animation: check-pop 0.2s var(--ease-out);
}
.item-check__icon {
width: var(--space-3);
height: var(--space-3);
color: var(--color-text-on-accent);
display: none;
}
.item-check--checked .item-check__icon { display: block; }
/* Artikel-Text */
.item-body {
flex: 1;
min-width: 0;
}
.item-name {
font-size: var(--text-base);
color: var(--color-text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.shopping-item--checked .item-name {
text-decoration: line-through;
color: var(--color-text-secondary);
}
.item-quantity {
font-size: var(--text-sm);
color: var(--color-text-secondary);
margin-top: 1px;
}
/* Löschen-Button */
.item-delete {
width: 32px;
height: 32px;
border-radius: var(--radius-sm);
background: none;
color: var(--color-text-disabled);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0;
transition: opacity var(--transition-fast), color var(--transition-fast);
position: relative;
border: none;
flex-shrink: 0;
}
.item-delete::before {
content: '';
position: absolute;
inset: -8px;
}
.shopping-item:hover .item-delete,
.shopping-item:focus-within .item-delete {
opacity: 1;
}
.item-delete:hover { color: var(--color-danger); }
/* --------------------------------------------------------
* Swipe-Wrapper - Shopping-spezifische Styles
* -------------------------------------------------------- */
/* Kein Margin mehr am shopping-item selbst (übernimmt swipe-row) */
.swipe-row .shopping-item {
margin-bottom: 0;
border-radius: var(--radius-md);
position: relative;
z-index: 1;
will-change: transform;
}
/* Rechts hinter der Karte = Löschen (Swipe nach rechts) */
.swipe-reveal--delete {
left: 0;
background-color: var(--color-danger);
color: var(--color-text-on-accent);
border-radius: var(--radius-md) 0 0 var(--radius-md);
}
/* Touch-Feedback: leichte Hervorhebung während Swipe */
.swipe-row--swiping .shopping-item {
box-shadow: var(--shadow-lg);
}
/* × Löschen-Button auf Mobile ausblenden - Swipe übernimmt */
@media (max-width: 1023px) {
.item-delete {
display: none;
}
}
/* --------------------------------------------------------
* No-Lists-Zustand
* -------------------------------------------------------- */
.no-lists {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
gap: var(--space-4);
padding: var(--space-12);
text-align: center;
}