Compare commits

...

2 Commits

Author SHA1 Message Date
d2618a5b45 UI fixes
Some checks failed
Build and Deploy / build-and-push (push) Failing after 1m17s
2026-04-19 10:45:01 +02:00
1bca99a6bb Light mode fixes 2026-04-19 10:21:49 +02:00
4 changed files with 94 additions and 68 deletions

View File

@@ -23,46 +23,46 @@
:root {
color-scheme: light;
--app-font: 'Plus Jakarta Sans', 'Segoe UI', sans-serif;
--app-bg-rgb: 243, 239, 233; /* #f3efe9 — warm cream base */
--surface-rgb: 255, 255, 255; /* #ffffff — white panels (existing tokens) */
--surface-soft-rgb: 249, 245, 238; /* #f9f5ee */
--surface-strong-rgb: 235, 229, 220; /* #ebe5dc */
--line-rgb: 69, 58, 48; /* #453a30divider base (warm brown) */
--text-primary-rgb: 28, 24, 21; /* #1c1815 */
--text-secondary-rgb: 102, 92, 83; /* #665c53 */
--text-tertiary-rgb: 140, 130, 118; /* #8c8276 */
--app-bg-rgb: 226, 242, 241; /* #e2f2f1 — cool mint base */
--surface-rgb: 255, 255, 255; /* #ffffff — white panels */
--surface-soft-rgb: 235, 246, 245; /* #ebf6f5 */
--surface-strong-rgb: 215, 234, 232; /* #d7eae8 */
--line-rgb: 40, 58, 58; /* #283a3acool neutral divider base */
--text-primary-rgb: 26, 31, 31; /* #1a1f1f */
--text-secondary-rgb: 92, 100, 100; /* #5c6464 */
--text-tertiary-rgb: 125, 133, 133; /* #7d8585 */
--warm-rgb: 183, 142, 88; /* #b78e58 — caramel */
--success-rgb: 47, 133, 82; /* #2f8552 — deep emerald (AA on white) */
--danger-rgb: 187, 75, 75; /* #bb4b4b — deep rose (AA on white) */
--panel-shadow: 0 18px 40px rgba(24, 17, 11, 0.08);
--panel-shadow-strong: 0 24px 60px rgba(24, 17, 11, 0.16);
--dock-shadow: 0 24px 56px rgba(24, 17, 11, 0.18);
--success-rgb: 47, 133, 82; /* #2f8552 — deep emerald (harmonizes with mint) */
--danger-rgb: 187, 75, 75; /* #bb4b4b — deep rose */
--panel-shadow: 0 18px 40px rgba(20, 35, 35, 0.08);
--panel-shadow-strong: 0 24px 60px rgba(20, 35, 35, 0.16);
--dock-shadow: 0 24px 56px rgba(20, 35, 35, 0.18);
--highlight-top: transparent;
--highlight-bottom: transparent;
/* Extended light palette — mirrors dark hierarchy: sunken < app-bg < card. */
--card-rgb: 255, 255, 255; /* #ffffff — primary elevated card (raised via shadow) */
--card-soft-rgb: 239, 233, 221; /* #efe9dd — nested/medium surface on white */
--card-strong-rgb: 228, 219, 205; /* #e4dbcd — strong surface / stroke */
--card-raised-rgb: 252, 249, 242; /* #fcf9f2 — subtle raise (chip on card) */
--sunken-rgb: 235, 229, 217; /* #ebe5d9 — deep inputs / search shell */
--sunken-deep-rgb: 219, 211, 196; /* #dbd3c4 — deepest well */
--border-card-rgb: 224, 215, 200; /* #e0d7c8 — subtle card/dock border */
--border-input-rgb: 180, 168, 150; /* #b4a896 — input / active border */
--text-emphasis-rgb: 14, 12, 10; /* #0e0c0a — brightest interactive text */
--text-body-rgb: 48, 40, 33; /* #302821 — primary body text (most common) */
--text-body-soft-rgb: 72, 62, 52; /* #483e34 — body soft */
--text-muted-rgb: 102, 92, 83; /* #665c53 — muted (matches --text-secondary) */
--text-dim-rgb: 140, 130, 118; /* #8c8276 — dim / placeholder */
--text-faint-rgb: 156, 146, 137; /* #9c9289 — faint icon color */
--text-subdued-rgb: 176, 166, 154; /* #b0a69a — subdued */
--skeleton-rgb: 226, 220, 210; /* #e2dcd2 — image loading placeholder */
--card-soft-rgb: 220, 238, 236; /* #dceeec — nested/medium surface on white */
--card-strong-rgb: 206, 228, 225; /* #cee4e1 — strong surface / stroke */
--card-raised-rgb: 244, 250, 250; /* #f4fafa — subtle raise (chip on card) */
--sunken-rgb: 213, 232, 230; /* #d5e8e6 — deep inputs / search shell */
--sunken-deep-rgb: 193, 220, 218; /* #c1dcda — deepest well */
--border-card-rgb: 205, 225, 222; /* #cde1de — subtle card/dock border */
--border-input-rgb: 148, 180, 177; /* #94b4b1 — input / active border */
--text-emphasis-rgb: 12, 18, 18; /* #0c1212 — brightest interactive text */
--text-body-rgb: 42, 48, 48; /* #2a3030 — primary body text (most common) */
--text-body-soft-rgb: 63, 70, 70; /* #3f4646 — body soft */
--text-muted-rgb: 92, 100, 100; /* #5c6464 — muted (matches --text-secondary) */
--text-dim-rgb: 125, 133, 133; /* #7d8585 — dim / placeholder */
--text-faint-rgb: 143, 150, 150; /* #8f9696 — faint icon color */
--text-subdued-rgb: 163, 169, 169; /* #a3a9a9 — subdued */
--skeleton-rgb: 214, 228, 226; /* #d6e4e2 — image loading placeholder */
--on-accent-rgb: 255, 255, 255; /* white text on accent backgrounds */
--overlay-rgb: 24, 17, 11; /* #18110b — scrim / shadow base (warm brown-black) */
--shadow-card: 0 2px 8px rgba(24, 17, 11, 0.08);
--shadow-shell: 0 5px 10px rgba(24, 17, 11, 0.08), 0 14px 22px rgba(24, 17, 11, 0.12), 0 22px 34px rgba(24, 17, 11, 0.10), inset 0 1px 0 rgba(255, 255, 255, 0.5);
--icon-watermark: rgba(24, 17, 11, 0.08);
--hover-overlay: rgba(24, 17, 11, 0.05);
--overlay-rgb: 20, 35, 35; /* #142323 — scrim / shadow base (cool dark) */
--shadow-card: 0 2px 8px rgba(20, 35, 35, 0.08);
--shadow-shell: 0 5px 10px rgba(20, 35, 35, 0.08), 0 14px 22px rgba(20, 35, 35, 0.12), 0 22px 34px rgba(20, 35, 35, 0.10), inset 0 1px 0 rgba(255, 255, 255, 0.5);
--icon-watermark: rgba(20, 35, 35, 0.08);
--hover-overlay: rgba(20, 35, 35, 0.05);
}
.dark {
@@ -179,6 +179,8 @@
.text-gray-900 { color: rgb(var(--text-primary-rgb)) !important; }
.text-gray-800 { color: rgba(var(--text-primary-rgb), 0.94) !important; }
.text-gray-700 { color: rgba(var(--text-primary-rgb), 0.82) !important; }
.text-gray-100 { color: rgb(var(--text-primary-rgb)) !important; }
.text-gray-50 { color: rgb(var(--text-primary-rgb)) !important; }
.text-gray-600 { color: rgba(var(--text-secondary-rgb), 0.96) !important; }
.text-gray-500 { color: rgba(var(--text-secondary-rgb), 0.78) !important; }
.text-gray-400 { color: rgba(var(--text-tertiary-rgb), 0.94) !important; }

View File

@@ -37,11 +37,11 @@ function getCalendarDayHTML(day, meta, dayState, dayAttr, theme = {}) {
let borderClass = 'border';
if (isSelected) {
bg = theme.selectedBg || 'rgb(var(--sunken-rgb))';
bg = theme.selectedBg || 'rgb(var(--card-rgb))';
borderColor = theme.selectedBorder || 'rgb(var(--border-input-rgb))';
text = theme.selectedText || 'rgb(var(--text-emphasis-rgb))';
} else if (isDimmed) {
bg = theme.dimmedBg ?? theme.bg ?? defaultBg;
bg = theme.dimmedBg ?? 'transparent';
text = theme.dimText || 'rgb(var(--text-faint-rgb))';
borderClass = 'border-0';
} else {
@@ -52,7 +52,7 @@ function getCalendarDayHTML(day, meta, dayState, dayAttr, theme = {}) {
const dot = isSelected ? (theme.selectedDot || 'rgb(var(--text-emphasis-rgb))') : (theme.dot || 'rgb(var(--text-faint-rgb))');
const opacity = isDimmed ? String(theme.dimOpacity ?? 0.72) : '1';
const borderStyle = isDimmed ? 'border:none;' : `border-color:${borderColor};`;
const borderStyle = borderColor ? `border-color:${borderColor};` : 'border:none;';
const outerClass = `${mode === 'month' ? 'mx-auto ' : ''}flex h-[2.05rem] w-full min-w-0 max-w-full items-center justify-center rounded-full ${borderClass} text-xs font-medium transition-colors leading-tight overflow-hidden`;
const innerClass = mode === 'month'
? 'relative flex h-full w-full flex-col items-center justify-center'

View File

@@ -3,6 +3,7 @@ import { MEAL_SLOTS } from '../planner/mealSlots.js';
import {
addMonths,
addWeeks,
sameDay,
sameMonth,
startOfDay,
startOfMonth,
@@ -90,9 +91,9 @@ export function getMealPlannerHTML() {
${createCalendarWeekdayHeaderHTML()}
<div id="calendar-month-grid" class="grid grid-cols-7 gap-1.5"></div>
</div>
<div id="calendar-drag-handle" class="flex items-center justify-center pb-2 pt-0.5">
<span id="calendar-handle-icon" class="${CALENDAR_HANDLE_CLASS}" aria-hidden="true"></span>
</div>
<button id="calendar-mode-toggle" type="button" class="w-full flex items-center justify-center py-1 pb-2 pt-0.5 text-[rgb(var(--text-faint-rgb))] hover:text-[rgb(var(--text-body-soft-rgb))] transition-colors" aria-label="Przełącz widok kalendarza">
<i id="calendar-handle-icon" class="fas fa-chevron-down text-[10px]"></i>
</button>
</div>
</div>
@@ -191,8 +192,9 @@ function syncModeToggle(mode) {
mode,
weekWrapEl: document.getElementById('calendar-week-wrap'),
monthWrapEl: document.getElementById('calendar-month-wrap'),
handleEl: document.getElementById('calendar-handle-icon'),
});
const icon = document.getElementById('calendar-handle-icon');
if (icon) icon.className = mode === 'month' ? 'fas fa-chevron-up text-[10px]' : 'fas fa-chevron-down text-[10px]';
}
function bindCalendarSwipeGesture(state, rerender) {
@@ -1131,18 +1133,24 @@ export function setupMealPlanner() {
const rerender = () => {
syncModeToggle(state.mode);
syncTodayButton(state.mode, state.weekStart, state.monthAnchor, state.selected);
const today = startOfDay(new Date());
renderCollapsibleCalendar({
weekGridEl: weekGrid,
monthGridEl: monthGrid,
weekAnchorDate: state.weekStart,
monthAnchorDate: state.monthAnchor,
selectedDate: state.selected,
resolveDayState: (day, meta) => ({
dimmed: meta.mode === 'month' && !meta.inCurrentMonth,
resolveDayState: (day, meta) => {
const isSelected = sameDay(day, state.selected);
const isPast = day.getTime() < today.getTime();
return {
disabled: isPast && !isSelected,
dimmed: (isPast || (meta.mode === 'month' && !meta.inCurrentMonth)) && !isSelected,
showIndicator: meta.mode === 'month'
? meta.inCurrentMonth && dayHasAnyMeal(state.plans, day)
: dayHasAnyMeal(state.plans, day),
}),
};
},
});
renderDayContent(state, persist);
};
@@ -1422,6 +1430,17 @@ export function setupMealPlanner() {
bindCalendarSwipeGesture(state, rerender);
document.getElementById('calendar-mode-toggle')?.addEventListener('click', () => {
if (state.mode === 'week') {
state.mode = 'month';
state.monthAnchor = startOfMonth(state.selected);
} else {
state.mode = 'week';
state.weekStart = startOfWeekMonday(state.selected);
}
rerender();
});
requestAnimationFrame(() => {
const ww = document.getElementById('calendar-week-wrap');
const mw = document.getElementById('calendar-month-wrap');

View File

@@ -551,44 +551,46 @@ function classifyIngredients(searchQuery) {
/* ══════════════════════ TILE RENDERING ══════════════════════ */
function tileIconHtml(item) {
function tileIconHtml(item, size = 'sm') {
const wrap = size === 'lg' ? 'w-9 h-9' : 'w-6 h-6';
const iconSize = size === 'lg' ? 'text-[18px]' : 'text-[12px]';
if (item.image) {
return `<div class="w-6 h-6 rounded-md shrink-0 overflow-hidden" style="background:rgb(var(--card-soft-rgb));"><img src="${esc(item.image)}" alt="" class="w-full h-full object-contain" style="padding:1px;"></div>`;
return `<div class="${wrap} shrink-0 overflow-hidden"><img src="${esc(item.image)}" alt="" class="w-full h-full object-contain"></div>`;
}
return `<div class="w-6 h-6 rounded-md flex items-center justify-center shrink-0" style="background:rgb(var(--card-soft-rgb));"><i class="fas ${item.icon} text-[11px]" style="color:rgb(var(--text-faint-rgb));"></i></div>`;
return `<div class="${wrap} flex items-center justify-center shrink-0"><i class="fas ${item.icon} ${iconSize}" style="color:rgb(var(--text-faint-rgb));"></i></div>`;
}
function shortfallTileHtml(item) {
const clamp = item.name.length > 25 ? ' min-w-0' : '';
return `
<button type="button" class="pv2-tile text-left rounded-2xl flex flex-col gap-2 px-2.5 py-2${clamp} transition-all active:scale-[0.98]" style="flex:1 0 auto; min-width:6rem; max-width:100%; background:rgb(var(--card-rgb)); border:none; box-shadow:var(--shadow-card);" data-id="${esc(item.ingredientId)}">
<div class="flex items-center gap-1.5 min-w-0">
${tileIconHtml(item)}
<p class="text-[11px] font-normal leading-tight truncate min-w-0" style="color:rgb(var(--text-body-rgb));">${esc(item.name)}</p>
</div>
<div class="w-full flex items-center gap-2">
<button type="button" class="pv2-tile text-left rounded-2xl flex items-center gap-2 px-2.5 py-2${clamp} transition-all active:scale-[0.98]" style="flex:1 0 auto; min-width:8.5rem; max-width:100%; background:rgb(var(--card-rgb)); border:none; box-shadow:var(--shadow-card);" data-id="${esc(item.ingredientId)}">
${tileIconHtml(item, 'lg')}
<div class="flex-1 min-w-0 flex flex-col gap-1">
<p class="text-[11px] font-normal leading-tight truncate" style="color:rgb(var(--text-body-rgb));">${esc(item.name)}</p>
<div class="flex items-center gap-2">
<div class="flex-1 h-1 rounded-full overflow-hidden" style="background:rgb(var(--app-bg-rgb));">
<div class="h-full rounded-full" style="width:${item.fillPct}%; background:${SHORTFALL_ACCENT};"></div>
</div>
<span class="shrink-0 text-[10px] font-semibold tabular-nums" style="color:rgb(var(--text-body-rgb));">${esc(formatQty(item.pantryQty))}<span class="font-medium" style="color:rgb(var(--text-dim-rgb));">/${esc(formatQty(item.needed))} ${esc(unitLabel(item.unit))}</span></span>
</div>
</div>
</button>`;
}
function sufficientTileHtml(item) {
const clamp = item.name.length > 25 ? ' min-w-0' : '';
return `
<button type="button" class="pv2-tile text-left rounded-2xl flex flex-col gap-2 px-2.5 py-2${clamp} transition-all active:scale-[0.98]" style="flex:1 0 auto; min-width:6rem; max-width:100%; background:rgb(var(--card-rgb)); border:none; box-shadow:var(--shadow-card);" data-id="${esc(item.ingredientId)}">
<div class="flex items-center gap-1.5 min-w-0">
${tileIconHtml(item)}
<p class="text-[11px] font-normal leading-tight truncate min-w-0" style="color:rgb(var(--text-body-rgb));">${esc(item.name)}</p>
</div>
<div class="w-full flex items-center gap-2">
<button type="button" class="pv2-tile text-left rounded-2xl flex items-center gap-2 px-2.5 py-2${clamp} transition-all active:scale-[0.98]" style="flex:1 0 auto; min-width:8.5rem; max-width:100%; background:rgb(var(--card-rgb)); border:none; box-shadow:var(--shadow-card);" data-id="${esc(item.ingredientId)}">
${tileIconHtml(item, 'lg')}
<div class="flex-1 min-w-0 flex flex-col gap-1">
<p class="text-[11px] font-normal leading-tight truncate" style="color:rgb(var(--text-body-rgb));">${esc(item.name)}</p>
<div class="flex items-center gap-2">
<div class="flex-1 h-1 rounded-full overflow-hidden" style="background:rgb(var(--app-bg-rgb));">
<div class="h-full rounded-full" style="width:100%; background:rgb(var(--success-rgb));"></div>
</div>
<span class="shrink-0 text-[10px] font-semibold tabular-nums" style="color:rgb(var(--success-rgb));">${esc(formatQty(item.pantryQty))}<span class="font-medium" style="color:rgb(var(--text-dim-rgb));">/${esc(formatQty(item.needed))} ${esc(unitLabel(item.unit))}</span></span>
</div>
</div>
</button>`;
}
@@ -724,7 +726,8 @@ export function setupPantry() {
document.getElementById('pantry-search-toggle')?.addEventListener('click', () => openSearch());
document.getElementById('pantry-search-close')?.addEventListener('click', () => closeSearch());
document.getElementById('pantry-filter-toggle')?.addEventListener('click', () => toggleFilterPanel());
document.getElementById('pantry-filter-clear')?.addEventListener('click', () => {
document.getElementById('pantry-filter-clear')?.addEventListener('click', (event) => {
event.stopPropagation();
pantryFilters = { categories: [], sections: [] };
syncHorizonUI();
renderBoard();
@@ -736,6 +739,8 @@ export function setupPantry() {
const chip = target.closest('[data-pantry-filter-kind]');
if (!(chip instanceof Element)) return;
event.stopPropagation();
const kind = chip.getAttribute('data-pantry-filter-kind');
const value = chip.getAttribute('data-pantry-filter-value');
if (!kind || !value) return;