diff --git a/index.html b/index.html
index 387e63b..24ad927 100644
--- a/index.html
+++ b/index.html
@@ -504,6 +504,7 @@
position: relative;
border: 1px solid rgba(255, 255, 255, 0.32) !important;
background: rgba(255, 255, 255, 0.2) !important;
+ background-image: none !important;
color: rgb(var(--text-body-rgb));
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.6),
@@ -517,6 +518,7 @@
.dark #app-bottom-nav .recipe-nav-toggle {
border: 1px solid rgba(255, 255, 255, 0.12) !important;
background: rgba(255, 255, 255, 0.04) !important;
+ background-image: none !important;
color: rgb(var(--text-body-rgb));
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.24),
@@ -841,6 +843,7 @@
padding: var(--dock-pad);
border-radius: var(--dock-radius);
background: rgba(255, 255, 255, 0.2);
+ background-image: none;
border: 1px solid rgba(255, 255, 255, 0.32);
overflow: hidden;
backdrop-filter: blur(28px) saturate(180%);
@@ -861,6 +864,7 @@
}
.dark #app-bottom-nav .bottom-dock {
background: rgba(255, 255, 255, 0.04);
+ background-image: none;
border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.24),
diff --git a/js/views/Filter.js b/js/views/Filter.js
index d86b80d..40ad024 100644
--- a/js/views/Filter.js
+++ b/js/views/Filter.js
@@ -3,15 +3,15 @@ import { MEAL_SLOTS } from '../planner/mealSlots.js';
import { applyFilters, getFilterState } from './RecipeList.js';
const FILTER_PANEL_TRANSITION = 'opacity 180ms ease, transform 180ms ease';
-const FILTER_SURFACE = 'rgb(var(--sunken-rgb))';
-const FILTER_SURFACE_SOFT = 'rgb(var(--app-bg-rgb))';
-const FILTER_BORDER = 'rgb(var(--border-input-rgb))';
-const FILTER_CHIP_ACTIVE_BG = 'rgb(var(--card-rgb))';
-const FILTER_TEXT_SECONDARY = 'rgb(var(--text-body-soft-rgb))';
-const FILTER_TEXT_MUTED = 'rgb(var(--text-muted-rgb))';
-const FILTER_TEXT_ACTIVE = 'rgb(var(--text-emphasis-rgb))';
-const FILTER_TRACK = 'rgb(var(--card-rgb))';
-const FILTER_TRACK_FILL = 'rgba(var(--border-input-rgb), 0.58)';
+const FILTER_SURFACE = 'var(--filter-liquid-panel-bg)';
+const FILTER_SURFACE_SOFT = 'var(--filter-liquid-chip-bg)';
+const FILTER_BORDER = 'var(--filter-liquid-border)';
+const FILTER_CHIP_ACTIVE_BG = 'var(--filter-liquid-chip-active-bg)';
+const FILTER_TEXT_SECONDARY = 'var(--filter-liquid-text-secondary)';
+const FILTER_TEXT_MUTED = 'var(--filter-liquid-text-muted)';
+const FILTER_TEXT_ACTIVE = 'var(--filter-liquid-text-active)';
+const FILTER_TRACK = 'var(--filter-liquid-track-bg)';
+const FILTER_TRACK_FILL = 'var(--filter-liquid-accent-bg)';
const PREP_TIME_MIN = 5;
const PREP_TIME_MAX = 120;
const PREP_TIME_STEP = 5;
@@ -57,10 +57,61 @@ function collectAllTags() {
export function getFilterHTML() {
return `
-
+
Filtry
@@ -179,8 +242,8 @@ function getChipStyle(active) {
const color = active ? FILTER_TEXT_ACTIVE : FILTER_TEXT_SECONDARY;
const borderRule = active ? `border:1px solid ${FILTER_BORDER};` : 'border:none;';
const shadow = active
- ? 'box-shadow:inset 0 1px 0 rgba(255,255,255,0.04), 0 0 0 1px rgba(var(--overlay-rgb),0.08);'
- : '';
+ ? 'box-shadow:0 1px 2px rgba(var(--overlay-rgb),0.08);'
+ : 'box-shadow:none;';
return `background:${background}; ${borderRule} color:${color}; ${shadow}`;
}
@@ -197,6 +260,12 @@ function getSliderPercent(value) {
return ((value - PREP_TIME_MIN) / (PREP_TIME_MAX - PREP_TIME_MIN)) * 100;
}
+function getClosedPanelTransform(panel) {
+ return panel?.dataset.placement === 'below'
+ ? 'translateY(-0.5rem) scale(0.98)'
+ : 'translateY(0.65rem) scale(0.98)';
+}
+
function setActiveTimeHandle(activeHandle) {
const minHandle = document.getElementById('prep-time-min-handle');
const maxHandle = document.getElementById('prep-time-max-handle');
@@ -244,26 +313,33 @@ function positionFilterPanel() {
const viewRect = view.getBoundingClientRect();
const anchorRect = (searchShell || button).getBoundingClientRect();
- const gap = 8;
+ const isRecipeContext = activeFilterContext === 'recipes';
+ const gap = isRecipeContext ? 12 : 8;
const margin = 12;
- const width = Math.min(anchorRect.width, viewRect.width - margin * 2);
+ const maxPanelWidth = isRecipeContext ? 352 : anchorRect.width;
+ const width = Math.min(maxPanelWidth, viewRect.width - margin * 2);
const spaceBelow = viewRect.bottom - anchorRect.bottom - margin;
const preferredMaxHeight = Math.min(420, viewRect.height - margin * 2);
const spaceAbove = anchorRect.top - viewRect.top - gap - margin;
- const opensUpward = spaceBelow < 260 && anchorRect.top - viewRect.top > spaceBelow;
+ const opensUpward = isRecipeContext || (spaceBelow < 260 && anchorRect.top - viewRect.top > spaceBelow);
const maxHeight = opensUpward
? Math.max(220, Math.min(preferredMaxHeight, spaceAbove))
: Math.max(220, viewRect.height - Math.max(margin, anchorRect.bottom - viewRect.top + gap) - margin);
- const top = opensUpward
- ? Math.max(margin, anchorRect.top - viewRect.top - gap - maxHeight)
- : Math.max(margin, anchorRect.bottom - viewRect.top + gap);
- const left = Math.max(
- margin,
- Math.min(anchorRect.left - viewRect.left, viewRect.width - width - margin),
- );
+ const leftBase = isRecipeContext
+ ? (viewRect.width - width) / 2
+ : anchorRect.left - viewRect.left;
+ const left = Math.max(margin, Math.min(leftBase, viewRect.width - width - margin));
panel.style.width = `${width}px`;
panel.style.left = `${left}px`;
- panel.style.top = `${top}px`;
+ panel.style.transformOrigin = opensUpward ? 'bottom center' : 'top center';
+ panel.dataset.placement = opensUpward ? 'above' : 'below';
+ if (opensUpward) {
+ panel.style.top = 'auto';
+ panel.style.bottom = `${Math.max(margin, viewRect.bottom - anchorRect.top + gap)}px`;
+ } else {
+ panel.style.top = `${Math.max(margin, anchorRect.bottom - viewRect.top + gap)}px`;
+ panel.style.bottom = 'auto';
+ }
panel.style.maxHeight = `${maxHeight}px`;
if (body) body.style.maxHeight = `${maxHeight - 56}px`;
}
@@ -283,6 +359,7 @@ function showFilterPanel() {
view.style.pointerEvents = 'auto';
view.setAttribute('aria-hidden', 'false');
positionFilterPanel();
+ panel.style.transform = getClosedPanelTransform(panel);
setRecipeAreaBlur(true);
syncPanelCount();
@@ -303,7 +380,7 @@ function hideFilterPanel() {
view.style.pointerEvents = 'none';
view.setAttribute('aria-hidden', 'true');
panel.style.opacity = '0';
- panel.style.transform = 'translateY(-0.5rem) scale(0.98)';
+ panel.style.transform = getClosedPanelTransform(panel);
setRecipeAreaBlur(false);
syncPanelCount();
@@ -381,7 +458,7 @@ function syncPanelCount() {
if (button) {
const highlight = isFilterPanelOpen() || count > 0;
const isRecipeGlassButton = buttonId === 'recipe-filter-btn';
- if (isRecipeGlassButton && !highlight) {
+ if (isRecipeGlassButton) {
button.style.removeProperty('background');
button.style.removeProperty('border-color');
button.style.removeProperty('color');
diff --git a/js/views/RecipeList.js b/js/views/RecipeList.js
index b8611fd..75362f1 100644
--- a/js/views/RecipeList.js
+++ b/js/views/RecipeList.js
@@ -109,7 +109,7 @@ export function getRecipeListHTML() {