Add ingredient terminology dictionaries to override DeepL translations for consistent cooking terminology:
- German cooking terms (EL→tbsp, TL→tsp, Ei→egg, etc.)
- US to British English conversions (zucchini→courgette, eggplant→aubergine, etc.)
Change DeepL target language from EN to EN-GB to force British English translations.
Apply post-processing to all translated text to ensure terminology consistency.
The global 'ul' selector was affecting all unordered lists across the app after visiting /glaube/rosenkranz. This caused layout issues with action buttons on recipe pages where the internal symbols would shift to the top instead of being centered.
Fixed by scoping the rule to only apply to ul elements within .gebet containers.
- Replace deprecated <slot> syntax with modern {#snippet} and {@render} patterns
- Add TypeScript types for snippet props in Header component
- Convert on:click event handlers to onclick attribute throughout
- Update all layout files to use new snippet-based composition pattern
Improve profile picture and navigation alignment on mobile:
- Position UserHeader fixed 2rem from viewport bottom (avoids browser UI issues)
- Center UserHeader horizontally within hamburger menu
- Add 2rem margin to links wrapper for better spacing
- Align navigation items to flex-start for left alignment
Optimize header link spacing and add visual feedback for active pages:
- Reduce link padding and gap for more compact navigation
- Shorten German labels: "In Saison" to "Saison", "Stichwörter" to "Tags"
- Remove "Tipps" section from navigation menu
Add active page highlighting across all layouts:
- Highlight current page links in red (matching hover color)
- Desktop: animated red underline that smoothly slides between links
- Mobile: static red underline for active links in hamburger menu
- Underline aligns precisely with text width (excludes padding)
Improve transitions:
- Fix color transition to only animate color, not layout properties
- Disable underline transition during window resize to prevent lag
- Underline updates immediately on resize for perfect alignment
Separate the drop shadow from the button wrapper into its own fixed
element with a lower z-index. This prevents the shadow from appearing
over the hamburger menu when it's pulled out on mobile.
- Create separate button_wrapper_shadow element
- Move box-shadow styling to shadow element
- Set shadow z-index to 9 to stay below menu but above page content
- Use fixed positioning with pointer-events: none
Extract language switching functionality from UserHeader into a new
LanguageSelector component. In mobile view, the selector appears in
the top bar next to the hamburger menu. In desktop view, it appears
in the navigation bar to the left of the UserHeader.
- Create LanguageSelector component with local element bindings
- Update Header component with language_selector_mobile and
language_selector_desktop slots
- Remove language selector code from UserHeader
- Update recipe and main layouts to use new component
- Hide desktop language selector inside mobile hamburger menu
Change English category names to match exact database values:
- 'Main Course' -> 'Main course'
- 'Pasta' -> 'Noodle'
- 'Side Dish' -> 'Side dish'
This fixes empty category sections on the main recipes page.
Use $props(), $state(), and $derived() to make image references properly
reactive. This fixes the issue where recipe card images weren't updating
correctly when switching between languages.
Use SvelteKit param matcher to constrain [recipeLang] to only match
'recipes' or 'rezepte', preventing it from catching /login, /logout,
and other non-recipe routes.
Replace window.location.reload() with custom event dispatching to avoid
flicker when switching languages on main page. Add bilingual labels for
all content including welcome message and link grid.
- Translate hardcoded German terms in IngredientsPage and InstructionsPage
- Migrate both components to Svelte 5 runes (, , )
- Fix language switcher to use correct short names via shared store
- Add recipeTranslationStore for recipe-specific language switching
Consolidate /rezepte and /recipes routes into single [recipeLang] structure to eliminate code duplication. All pages now use conditional API routing and reactive labels based on language parameter.
- Merge duplicate route structures into /[recipeLang] with 404 for invalid slugs
- Add English API endpoints for search, favorites, tags, and categories
- Implement language dropdown in header with localStorage persistence
- Convert all pages to use Svelte 5 runes (, , )
- Add German-only redirects (301) for add/edit pages
- Make all view pages (list, detail, filters, search, favorites) fully bilingual
- Remove floating language switcher in favor of header dropdown
- Add embedded translations schema to Recipe model with English support
- Create DeepL translation service with batch translation and change detection
- Build translation approval UI with side-by-side editing for all recipe fields
- Integrate translation workflow into add/edit pages with field comparison
- Create complete English recipe routes at /recipes/* mirroring German structure
- Add language switcher component with hreflang SEO tags
- Support image loading from German short_name for English recipes
- Add English API endpoints for all recipe filters (category, tag, icon, season)
- Include layout with English navigation header for all recipe subroutes
- Wrap rosary page toggles in centered container with left-aligned items
- Reduce spacing between toggles from 2rem to 0.5rem
- Simplify Toggle component styling (remove centering/margins)
- Add centered wrapper for gebete page toggle
- Add spacing between German verses in monolingual mode for readability
Create Toggle and LanguageToggle components to reduce code duplication
and enable shared state across pages.
- Add Toggle.svelte: Generic iOS-style toggle with customizable accent color
- Add LanguageToggle.svelte: Language-specific toggle with localStorage persistence
- Refactor rosary page to use new toggle components
- Add language toggle to gebete page
- Toggle state persists across both pages via localStorage
- Reduce min-height of Ave Maria decades in monolingual mode (50vh → 30vh)
Removed excessive console.log statements from recurring payments processing and monthly expenses aggregation. Error logging (console.error) is retained for troubleshooting.
- Fetch full verse data at build time in +page.server.ts
- Pass preloaded verseData to BibleModal instead of fetching client-side
- Remove onMount fetch logic from BibleModal component
- Add VerseData interface to type definitions
- Update handleCitationClick to pass verseData prop
This ensures all Bible verses are embedded in static HTML during build,
eliminating runtime API calls and improving performance.
- Add clickable Bible reference buttons that open modal with full verses
- Create BibleModal component with backdrop blur and styled close button
- Implement build-time data fetching for Bible texts while maintaining reactivity
- Redesign mystery selector with responsive grid (3-in-row/4-in-row/2×2)
- Add "Heutige" badge to indicate today's auto-selected mystery
- Reposition luminous mysteries toggle below mystery selector
- Integrate Bible reference and counter buttons side-by-side
- Restructure Bible API under /api/glaube/bibel/ for better organization
- Add RosaryFinalPrayer component with Latin and German text
- Display short mystery titles in decade headings (e.g., "5. Gesätz: Kreuzigung")
- Add descriptive titles to initial three Ave Marias (Glaube, Hoffnung, Liebe)
- Add closing cross symbol to signal final sign of the cross
- Mystery titles update dynamically when switching between rosary types
Added max-width: 100% and overflow-x: hidden to main-content and cospend-main containers to prevent child elements from forcing horizontal scroll on mobile devices.
Reduced padding on mobile screens (max-width: 600px) to prevent horizontal overflow and ensure header spans full width. Updated BarChart, DebtBreakdown, EnhancedBalance components and recent activity section.
Extract inline prayer content into dedicated components in $lib/components/prayers/
for better code organization and reusability. This reduces the gebete page from ~339
to ~95 lines while maintaining the same functionality.
- Add crosses.ttf and crosses.woff2 font files to static/fonts/
- Load crosses font globally in app.css with WOFF2 and TTF fallback
- Apply crosses font to italic elements in prayers (christ.css)
- Ensures consistent cross symbol rendering across prayers and rosary
- Add SalveRegina component with full bilingual Latin/German text
- Wrap FatimaGebet in paragraph tags for consistent styling with other prayers
- Combine final prayers (Gloria Patri, Fatima, Salve Regina) into single Abschluss section
- Change Ave Maria titles from German to Latin ('Ave Maria' instead of 'Gegrüßet seist du Maria')
- Add h3 titles to all Gloria Patri prayers for consistency
- Extend SVG viewBox to show more curve area at top and bottom
- Add CSS mask with gradients for smooth fade-out of circular connection curve
- Adjust viewBox dimensions for better bead visibility
- Add inline Benedictus medal with bar cross and C S S M letters
- Position medal at y=240 after second large bead
- Add bezier curve connecting last bead back to medal area
- Adjust vertical chain to start below cross (y=50) and end at last bead (y=1655)
- Create visual representation of circular rosary structure
- Create reusable prayer components (Paternoster, AveMaria, GloriaPatri, Kreuzzeichen, Credo, FatimaGebet)
- Add bilingual display (Latin/German) with proper styling differentiation
- Implement scrolling SVG visualization that syncs with prayers
- Add mystery highlighting for Ave Maria (Latin in red, German in orange)
- Separate Gesätze (decades) from transition prayers (Gloria, Fatima, Paternoster)
- Complete full Nicene Creed text
- Split initial three Ave Marias into individual sections (Faith, Hope, Love)
- Add Latin versions for all rosary mysteries (joyful, sorrowful, glorious, luminous)
- Make visualization beads larger and remove container styling for seamless background integration
- Fix SVG coordinate-to-pixel conversion for accurate scroll synchronization
Use the card wrapper pattern with absolute positioned main link and elevated z-index for nested links.
This maintains proper HTML semantics (no nested <a> tags) while allowing category, icon, and tag links to be clickable.
- Replace outer <a> wrapper with <div>
- Add invisible overlay link for main card click area (z-index: 1)
- Elevate nested links (category, tags, icon) with z-index: 10
- Maintain all existing hover effects and accessibility
- Keep semantic HTML structure without nesting <a> tags
- Replace 8 duplicate formatCurrency functions with shared utility
- Add comprehensive formatter utilities (currency, date, number, etc.)
- Set up Vitest for unit testing with 38 passing tests
- Set up Playwright for E2E testing
- Consolidate database connection to single source (src/utils/db.ts)
- Add auth middleware helpers to reduce code duplication
- Fix display bug: remove spurious minus sign in recent activity amounts
- Add path aliases for cleaner imports ($utils, $models)
- Add project documentation (CODEMAP.md, REFACTORING_PLAN.md)
Test coverage: 38 unit tests passing
Build: successful with no breaking changes
Allow users to click on bar segments or legend items to filter to a single category. Clicking again restores all categories. Totals displayed above bars now dynamically update to reflect only visible categories.