diff --git a/js/ui/bottomNav.js b/js/ui/bottomNav.js
index b5fb348..4f3c6ba 100644
--- a/js/ui/bottomNav.js
+++ b/js/ui/bottomNav.js
@@ -81,6 +81,7 @@ export function setupBottomNav({ refreshPantry, refreshShoppingList } = {}) {
nav.style.setProperty('--recipe-dock-width', `${dockWidth}px`);
nav.style.setProperty('--recipe-collapsed-dock-width', `${collapsedDockWidth}px`);
nav.style.setProperty('--recipe-toggle-size', `${collapsedSlotWidth}px`);
+ document.documentElement.style.setProperty('--recipe-dock-width', `${dockWidth}px`);
document.documentElement.style.setProperty('--catalog-menu-left', `${dockLeft}px`);
document.documentElement.style.setProperty('--catalog-menu-width', `${collapsedDockWidth}px`);
document.documentElement.style.setProperty('--catalog-filter-left', `${filterLeft}px`);
diff --git a/js/ui/calendarPopover.js b/js/ui/calendarPopover.js
index 4496426..3c7ce21 100644
--- a/js/ui/calendarPopover.js
+++ b/js/ui/calendarPopover.js
@@ -1,7 +1,8 @@
+const STYLE_ID = 'calendar-popover-liquid-styles';
const DEFAULT_POPOVER_CLASS = 'absolute left-0 right-0 top-full mt-2 z-[50] transition-all duration-200 pointer-events-none';
const DEFAULT_POPOVER_STYLE = 'opacity:0; transform:translateY(-6px) scale(0.98);';
-const DEFAULT_PANEL_CLASS = 'rounded-[1.35rem] py-3';
-const DEFAULT_PANEL_STYLE = 'background:rgb(var(--sunken-rgb)); border:1px solid rgb(var(--border-input-rgb)); box-shadow:var(--shadow-shell);';
+const DEFAULT_PANEL_CLASS = 'calendar-liquid-panel rounded-[1.35rem] py-3';
+const DEFAULT_PANEL_STYLE = 'background-image:none !important;';
const DEFAULT_OPEN_TRIGGER_STYLE = {
background: 'rgb(var(--sunken-rgb))',
@@ -13,6 +14,101 @@ const DEFAULT_CLOSED_TRIGGER_STYLE = {
borderColor: 'rgb(var(--border-card-rgb))',
};
+export function ensureCalendarPopoverStyles() {
+ if (typeof document === 'undefined') return;
+ if (document.getElementById(STYLE_ID)) return;
+
+ const style = document.createElement('style');
+ style.id = STYLE_ID;
+ style.textContent = `
+ .calendar-liquid-panel {
+ background: rgba(255, 255, 255, 0.2) !important;
+ border: 1px solid rgba(255, 255, 255, 0.32) !important;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.6),
+ inset 0 -1px 0 rgba(var(--overlay-rgb), 0.06),
+ 0 8px 20px rgba(var(--overlay-rgb), 0.14),
+ 0 18px 38px rgba(var(--overlay-rgb), 0.18) !important;
+ backdrop-filter: blur(28px) saturate(180%);
+ -webkit-backdrop-filter: blur(28px) saturate(180%);
+ }
+
+ .dark .calendar-liquid-panel {
+ background: rgba(255, 255, 255, 0.04) !important;
+ border: 1px solid rgba(255, 255, 255, 0.12) !important;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.24),
+ inset 0 -1px 0 rgba(0, 0, 0, 0.22),
+ 0 10px 24px rgba(0, 0, 0, 0.3),
+ 0 24px 54px rgba(0, 0, 0, 0.38) !important;
+ }
+
+ .calendar-liquid-btn {
+ background: rgba(255, 255, 255, 0.16) !important;
+ border: 1px solid rgba(255, 255, 255, 0.24) !important;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.45),
+ inset 0 -1px 0 rgba(var(--overlay-rgb), 0.08),
+ 0 6px 14px rgba(var(--overlay-rgb), 0.14) !important;
+ backdrop-filter: blur(22px) saturate(165%);
+ -webkit-backdrop-filter: blur(22px) saturate(165%);
+ transition: background 180ms ease, border-color 180ms ease, transform 180ms ease;
+ }
+
+ .calendar-liquid-btn:hover {
+ background: rgba(255, 255, 255, 0.22) !important;
+ transform: translateY(-1px);
+ }
+
+ .dark .calendar-liquid-btn {
+ background: rgba(255, 255, 255, 0.06) !important;
+ border: 1px solid rgba(255, 255, 255, 0.12) !important;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.2),
+ inset 0 -1px 0 rgba(0, 0, 0, 0.2),
+ 0 8px 18px rgba(0, 0, 0, 0.28) !important;
+ }
+
+ .dark .calendar-liquid-btn:hover {
+ background: rgba(255, 255, 255, 0.1) !important;
+ }
+
+ .calendar-liquid-btn input,
+ .calendar-liquid-btn input:focus,
+ .calendar-liquid-btn input:active {
+ background: transparent !important;
+ background-color: transparent !important;
+ background-image: none !important;
+ border: none !important;
+ box-shadow: none !important;
+ outline: none !important;
+ appearance: none !important;
+ -webkit-appearance: none !important;
+ -moz-appearance: textfield !important;
+ backdrop-filter: none !important;
+ -webkit-backdrop-filter: none !important;
+ filter: none !important;
+ }
+
+ .calendar-liquid-btn input[type='number']::-webkit-outer-spin-button,
+ .calendar-liquid-btn input[type='number']::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+ }
+
+ .calendar-liquid-btn input:-webkit-autofill,
+ .calendar-liquid-btn input:-webkit-autofill:hover,
+ .calendar-liquid-btn input:-webkit-autofill:focus {
+ -webkit-text-fill-color: rgb(var(--text-body-rgb));
+ -webkit-box-shadow: 0 0 0 1000px transparent inset !important;
+ box-shadow: 0 0 0 1000px transparent inset !important;
+ transition: background-color 9999s ease-in-out 0s;
+ }
+
+ `;
+ document.head.appendChild(style);
+}
+
function byId(idOrElement) {
if (!idOrElement) return null;
if (typeof idOrElement === 'string') return document.getElementById(idOrElement);
@@ -40,6 +136,7 @@ export function createCalendarPopoverHTML({
panelStyle = DEFAULT_PANEL_STYLE,
wrapInPanel = true,
}) {
+ ensureCalendarPopoverStyles();
const body = wrapInPanel
? `
${calendarHTML}
`
: calendarHTML;
diff --git a/js/ui/filterPopover.js b/js/ui/filterPopover.js
index 7dff52d..b4a9606 100644
--- a/js/ui/filterPopover.js
+++ b/js/ui/filterPopover.js
@@ -48,6 +48,14 @@ export function ensureFilterPopoverStyles() {
backdrop-filter: blur(28px) saturate(180%);
-webkit-backdrop-filter: blur(28px) saturate(180%);
}
+
+ .filter-liquid-surface.filter-liquid-panel-soft {
+ --filter-liquid-panel-bg: rgba(var(--app-bg-rgb), 0.52);
+ }
+
+ .dark .filter-liquid-surface.filter-liquid-panel-soft {
+ --filter-liquid-panel-bg: rgba(var(--app-bg-rgb), 0.6);
+ }
`;
document.head.appendChild(style);
}
diff --git a/js/ui/ingredientCard.js b/js/ui/ingredientCard.js
index cea5641..0db82c5 100644
--- a/js/ui/ingredientCard.js
+++ b/js/ui/ingredientCard.js
@@ -19,6 +19,7 @@ import {
updateKitchenItemAmount,
} from '../services/pantryShopping.js?v=2';
import { showAppToast } from './toast.js';
+import { ensureCalendarPopoverStyles } from './calendarPopover.js';
const CATEGORY_ICONS = {
pieczywo: 'fa-bread-slice',
@@ -79,7 +80,7 @@ function mediaHtml(image, icon, sizeClass = 'w-9 h-9', radiusClass = 'rounded-lg
const fit = image.endsWith('.svg') ? 'object-contain' : 'object-cover';
return `
`;
}
- return `
`;
+ return `
`;
}
function compactMetaText(text, tone = 'default') {
@@ -123,6 +124,11 @@ function formatPackCount(amount, packSize) {
return `${formatPreciseQty((Number(amount) || 0) / Number(packSize))} opak.`;
}
+function parseQtyInput(value) {
+ const normalized = String(value ?? '').trim().replace(',', '.');
+ return normalizeQty(Number(normalized) || 0);
+}
+
function getQtyStepMeta(def, product = null) {
const productPackSize = Number(product?.packSize);
if (Number.isFinite(productPackSize) && productPackSize > 0) {
@@ -152,17 +158,18 @@ function getQtyStepMeta(def, product = null) {
export function getIngredientCardHTML({
idBase,
overlayClass = 'fixed inset-0 z-[70] hidden opacity-0 transition-opacity duration-200 flex items-center justify-center p-5',
- overlayStyle = 'pointer-events:none; background:rgba(var(--overlay-rgb),0.5);',
- cardClass = 'relative w-full max-w-xs rounded-2xl shadow-2xl overflow-hidden',
- cardStyle = 'background:rgb(var(--app-bg-rgb)); pointer-events:auto; max-height:85vh; overflow-y:auto; transform:translateY(0.75rem); opacity:0; transition:transform 220ms ease, opacity 220ms ease;',
+ overlayStyle = 'pointer-events:none;',
+ cardClass = 'calendar-liquid-panel relative w-full max-w-xs rounded-2xl overflow-hidden',
+ cardStyle = 'pointer-events:auto; max-height:85vh; overflow-y:auto; transform:translateY(0.75rem); opacity:0; transition:transform 220ms ease, opacity 220ms ease;',
} = {}) {
if (!idBase) throw new Error('getIngredientCardHTML requires idBase');
+ ensureCalendarPopoverStyles();
return `
-
+
@@ -170,7 +177,7 @@ export function getIngredientCardHTML({
-
-
+
@@ -371,28 +378,28 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
${esc(stockValueLabel)}
${stockSubLabel ? `
${esc(stockSubLabel)}
` : ''}
-
+
${esc(actionLabel)}
${state.stockEditorOpen ? `
` : ''}
`;
@@ -420,8 +427,8 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
wrap.querySelector('.ingredient-card-stock-input')?.addEventListener('input', (event) => {
state.stockDraftQty = usesPackStep
- ? normalizeQty((Number(event.target.value) || 0) * step)
- : normalizeQty(event.target.value);
+ ? normalizeQty(parseQtyInput(event.target.value) * step)
+ : parseQtyInput(event.target.value);
});
wrap.querySelector('.ingredient-card-stock-clear')?.addEventListener('click', () => {
@@ -432,8 +439,8 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
wrap.querySelector('.ingredient-card-stock-save')?.addEventListener('click', () => {
const input = wrap.querySelector('.ingredient-card-stock-input');
const nextQty = usesPackStep
- ? normalizeQty((Number(input?.value) || 0) * step)
- : normalizeQty(input?.value ?? state.stockDraftQty ?? qty);
+ ? normalizeQty(parseQtyInput(input?.value) * step)
+ : parseQtyInput(input?.value ?? state.stockDraftQty ?? qty);
if (product) {
setPantryProductQty(def.id, product.id, nextQty);
} else {
@@ -480,21 +487,21 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
${esc(shopValueLabel)}
${shopSubLabel ? `
${esc(shopSubLabel)}
` : ''}
-
+
${esc(actionLabel)}
${state.shopEditorOpen ? `
` : ''}
`;
@@ -531,8 +538,8 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
wrap.querySelector('.ingredient-card-shop-input')?.addEventListener('input', (event) => {
state.shopDraftQty = usesPackStep
- ? normalizeQty((Number(event.target.value) || 0) * step)
- : normalizeQty(event.target.value);
+ ? normalizeQty(parseQtyInput(event.target.value) * step)
+ : parseQtyInput(event.target.value);
});
wrap.querySelector('.ingredient-card-shop-remove')?.addEventListener('click', () => {
@@ -549,8 +556,8 @@ export function createIngredientCardController({ idBase, defaultSourceNote = 'Ze
wrap.querySelector('.ingredient-card-shop-save')?.addEventListener('click', () => {
const input = wrap.querySelector('.ingredient-card-shop-input');
const nextAmount = usesPackStep
- ? normalizeQty((Number(input?.value) || 0) * step)
- : normalizeQty(input?.value ?? state.shopDraftQty ?? defaultAmount);
+ ? normalizeQty(parseQtyInput(input?.value) * step)
+ : parseQtyInput(input?.value ?? state.shopDraftQty ?? defaultAmount);
let toastText = null;
if (shoppingItem) {
updateKitchenItemAmount(KITCHEN_LIST_ID, shoppingItem.id, nextAmount);
diff --git a/js/views/Pantry.js b/js/views/Pantry.js
index c5d0485..92bef0e 100644
--- a/js/views/Pantry.js
+++ b/js/views/Pantry.js
@@ -74,7 +74,7 @@ const MONTHS_SHORT = ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip', 'sie', 'w
const DEFAULT_HORIZON_DAYS = 7;
const SHORTFALL_ACCENT = 'rgb(var(--danger-rgb))';
const PANTRY_CALENDAR_THEME = {
- bg: 'rgb(var(--app-bg-rgb))',
+ bg: 'rgba(255,255,255,0.08)',
border: 'rgb(var(--card-raised-rgb))',
text: 'rgb(var(--text-body-soft-rgb))',
dimText: 'rgb(var(--text-faint-rgb))',
@@ -158,6 +158,7 @@ function photoStripMedia(image, icon, accentBg) {
export function getPantryHTML() {
return `
+
-
-
-
-
-
-
-
- ${createCalendarPopoverHTML({
+
+
+
+
+
+
+
+ ${createCalendarPopoverHTML({
id: 'pantry-calendar-popover',
calendarHTML: createSwipePopoverCalendarHTML({ idPrefix: 'pantry-cal' }),
})}
-
-