Refactor homepage for mobile: add discover/quick-start layout and responsive styles #1

Merged
LordSchmackes merged 1 commits from codex/refactor-index.php-for-mobile-support into main 2026-03-22 22:16:24 +00:00
2 changed files with 280 additions and 40 deletions
+132
View File
@@ -632,6 +632,104 @@ img, picture { display: block; max-width: 100%; height: auto; }
}
.hero-card__body { padding: 18px; display: grid; gap: 10px; }
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.discover-shell {
max-width: 1200px;
margin: 24px auto 40px;
display: grid;
grid-template-columns: minmax(0, 1.2fr) minmax(280px, 0.8fr);
gap: 24px;
align-items: stretch;
}
.discover-shell__main,
.tag-panel,
.empty-state,
.quick-start {
background: var(--surface);
border: 1px solid var(--stroke);
border-radius: var(--radius);
box-shadow: var(--shadow);
}
.discover-shell__main {
padding: 22px;
display: grid;
gap: 18px;
}
.section-header--stack {
flex-direction: column;
align-items: flex-start;
text-align: left;
}
.discover-search {
margin: 0;
}
.discover-stats {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.discover-stat {
padding: 14px;
background: var(--panel);
border: 1px solid var(--stroke);
border-radius: 14px;
display: grid;
gap: 4px;
}
.discover-stat strong {
font-size: clamp(1.1rem, 2vw, 1.5rem);
}
.discover-stat span {
color: var(--muted);
font-size: 14px;
}
.tag-panel {
padding: 16px;
display: grid;
gap: 12px;
box-shadow: none;
border-radius: 16px;
}
.tag-panel__head {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
}
.tag-panel__head h3 {
margin: 0;
}
.tag-row--panel {
margin-top: 0;
}
.quick-start {
max-width: 1200px;
margin: 0 auto 40px;
padding: 22px;
display: grid;
gap: 18px;
}
.empty-state {
padding: 22px;
display: grid;
gap: 12px;
text-align: center;
}
.empty-state h3,
.empty-state p {
margin: 0;
}
.section-header {
max-width: 1200px;
margin: 0 auto 12px;
@@ -824,6 +922,40 @@ img, picture { display: block; max-width: 100%; height: auto; }
#backToTop.show { opacity: 1; transform: translateY(0); }
#backToTop:hover { background: var(--panel-2); }
@media (max-width: 900px) {
.discover-shell {
grid-template-columns: 1fr;
gap: 16px;
margin: 16px auto 32px;
}
.discover-shell__main,
.quick-start,
.coming-features,
.empty-state {
padding: 18px;
}
.discover-stats {
grid-template-columns: 1fr;
}
.tag-panel__head {
flex-direction: column;
align-items: flex-start;
}
}
@media (max-width: 480px) {
.discover-shell__main,
.quick-start,
.coming-features,
.empty-state,
.tag-panel {
padding: 14px;
}
}
/* ══════════════════════════════════════════════════════════════════
RESPONSIVE
══════════════════════════════════════════════════════════════════ */
+148 -40
View File
@@ -108,6 +108,46 @@ $navHome = $t['home'];
$navLatest = $t['latest'];
$navBasics = $t['basics'];
$quickStartRecipes = array_slice($recipes, 0, 3);
chatgpt-codex-connector[bot] commented 2026-03-22 22:20:17 +00:00 (Migrated from github.com)
Review

P2 Badge Order Quick start recipes by total time before slicing

The new "Quick start" block promises the shortest path from craving to plate, but array_slice($recipes, 0, 3) just reuses whatever order filter_recipes() returned. In the current catalog, data/recipes.json puts the 60-minute tagliatelle before the 30-minute steak, so this section already highlights a slower recipe ahead of a faster one; any catalog order change will keep producing misleading "quick" recommendations.

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Order Quick start recipes by total time before slicing** The new "Quick start" block promises the shortest path from craving to plate, but `array_slice($recipes, 0, 3)` just reuses whatever order `filter_recipes()` returned. In the current catalog, `data/recipes.json` puts the 60-minute tagliatelle before the 30-minute steak, so this section already highlights a slower recipe ahead of a faster one; any catalog order change will keep producing misleading "quick" recommendations. Useful? React with 👍 / 👎.
$latestRecipes = array_slice($recipes, 0, 6);
chatgpt-codex-connector[bot] commented 2026-03-22 22:20:17 +00:00 (Migrated from github.com)
Review

P2 Badge Stop slicing the library down to six filtered recipes

$recipeCount still reports every matching recipe, but $latestRecipes keeps only the first six and the new library markup has no pager or "view all" link. That means as soon as there are more than six live recipes—or more than six matches for a search/tag—everything after the sixth disappears from the homepage entirely even though the header says more results exist.

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Stop slicing the library down to six filtered recipes** `$recipeCount` still reports every matching recipe, but `$latestRecipes` keeps only the first six and the new library markup has no pager or "view all" link. That means as soon as there are more than six live recipes—or more than six matches for a search/tag—everything after the sixth disappears from the homepage entirely even though the header says more results exist. Useful? React with 👍 / 👎.
$recipeCount = count($recipes);
$totalMinutes = array_sum(array_map(fn($recipe) => (int) ($recipe['total_time'] ?? 0), $recipes));
$averageMinutes = $recipeCount > 0 ? (int) ceil($totalMinutes / $recipeCount) : 0;
$activeTag = $tag !== '' ? $tag : null;
$tagCount = count($allTags);
$copy['en'] += [
'discover_title' => 'Find your next easy favorite.',
'discover_text' => 'Everything below the landing area is simplified for smaller screens: search, tap a tag, and jump into a recipe without cramped text blocks.',
'browse_tags' => 'Browse tags',
'quick_start_title' => 'Quick start',
'quick_start_text' => 'Open one of the recipes with the shortest path from craving to plate.',
'library_title' => 'Recipe library',
'library_text' => 'Clean cards, shorter descriptions, and tap-friendly actions for mobile browsing.',
'stats_recipes' => 'live recipes',
'stats_average' => 'avg. total time',
'stats_tags' => 'browseable tags',
'empty_title' => 'No recipes found yet',
'empty_text' => 'Try another search term or clear the active tag to see every recipe again.',
'view_recipe' => 'View recipe',
];
$copy['de'] += [
'discover_title' => 'Finde schnell dein nächstes Lieblingsrezept.',
'discover_text' => 'Alles unterhalb der Landingpage ist für kleine Screens vereinfacht: suchen, Tag antippen und ohne gequetschte Textblöcke ins Rezept springen.',
'browse_tags' => 'Tags entdecken',
'quick_start_title' => 'Schnell starten',
'quick_start_text' => 'Öffne eines der Rezepte mit dem kürzesten Weg von Appetit zu Teller.',
'library_title' => 'Rezeptübersicht',
'library_text' => 'Saubere Karten, kürzere Beschreibungen und gut tappbare Aktionen für Mobile.',
'stats_recipes' => 'Rezepte online',
'stats_average' => 'Ø Gesamtzeit',
'stats_tags' => 'durchsuchbare Tags',
'empty_title' => 'Noch keine Rezepte gefunden',
'empty_text' => 'Probiere einen anderen Suchbegriff oder entferne den aktiven Tag, um wieder alle Rezepte zu sehen.',
'view_recipe' => 'Rezept ansehen',
];
$t = $copy[$lang];
// Small helpers for URLs
$langQuery = ['lang' => $lang];
$recipeUrl = fn(array $recipe) => '/recipe.php?' . http_build_query(['slug' => $recipe['slug']] + $langQuery);
@@ -197,77 +237,145 @@ document.getElementById('landingCta').addEventListener('click', function(e){
</div>
<?php endif; ?>
<section class="hero">
<div class="hero-copy">
<p class="eyebrow"><?php echo e($t['eyebrow']); ?></p>
<h1><?php echo $t['headline']; ?></h1>
<p><?php echo e($t['subhead']); ?></p>
<form class="search" method="get" action="/index.php">
<section class="discover-shell" id="basics">
<div class="discover-shell__main">
<div class="section-header section-header--stack">
<p class="eyebrow"><?php echo e($t['basics']); ?></p>
<h2><?php echo e($t['discover_title']); ?></h2>
<p><?php echo e($t['discover_text']); ?></p>
</div>
<form class="search discover-search" method="get" action="/index.php">
<input type="hidden" name="lang" value="<?php echo e($lang); ?>">
<input type="text" name="q" placeholder="<?php echo e($t['search_placeholder']); ?>" value="<?php echo e($q); ?>">
<label class="sr-only" for="recipe-search"><?php echo e($t['search_placeholder']); ?></label>
<input id="recipe-search" type="text" name="q" placeholder="<?php echo e($t['search_placeholder']); ?>" value="<?php echo e($q); ?>">
<?php if ($tag): ?>
<input type="hidden" name="tag" value="<?php echo e($tag); ?>">
<?php endif; ?>
<button type="submit"><?php echo e($t['search_button']); ?></button>
</form>
<div class="tag-row">
<?php foreach ($allTags as $tTag): ?>
<a class="tag<?php echo strtolower($tTag) === strtolower($tag ?? '') ? ' active' : ''; ?>" href="<?php echo e($tagUrl($tTag)); ?>">#<?php echo e($tTag); ?></a>
<?php endforeach; ?>
<?php if ($tag): ?>
<a class="tag clear" href="/index.php?lang=<?php echo e($lang); ?>"><?php echo e($t['tag_clear']); ?></a>
<?php endif; ?>
<div class="discover-stats" aria-label="Recipe overview">
<div class="discover-stat">
<strong><?php echo e((string) $recipeCount); ?></strong>
<span><?php echo e($t['stats_recipes']); ?></span>
</div>
<div class="discover-stat">
<strong><?php echo e(format_minutes($averageMinutes)); ?></strong>
<span><?php echo e($t['stats_average']); ?></span>
</div>
<div class="discover-stat">
<strong><?php echo e((string) $tagCount); ?></strong>
<span><?php echo e($t['stats_tags']); ?></span>
</div>
</div>
</div>
<?php if ($featured): ?>
<div class="hero-card">
<img src="<?php echo e($featured['hero']); ?>" alt="<?php echo e($featured['title']); ?>">
<div class="hero-card__body">
<p class="pill"><?php echo e($t['featured']); ?></p>
<h3><?php echo e($featured['title']); ?></h3>
<p><?php echo e($featured['description']); ?></p>
<div class="meta">
<span>⏱ <?php echo format_minutes((int) ($featured['total_time'] ?? 0)); ?></span>
<span>🍽 <?php echo e($featured['servings']); ?> <?php echo e($t['servings']); ?></span>
<div class="tag-panel">
<div class="tag-panel__head">
<h3><?php echo e($t['browse_tags']); ?></h3>
<?php if ($activeTag): ?>
<a class="tag clear" href="/index.php?lang=<?php echo e($lang); ?>"><?php echo e($t['tag_clear']); ?></a>
<?php endif; ?>
</div>
<div class="tag-row tag-row--panel">
<?php foreach ($allTags as $tTag): ?>
<a class="tag<?php echo strtolower($tTag) === strtolower($activeTag ?? '') ? ' active' : ''; ?>" href="<?php echo e($tagUrl($tTag)); ?>">#<?php echo e($tTag); ?></a>
<?php endforeach; ?>
</div>
<a class="button" href="<?php echo e($recipeUrl($featured)); ?>"><?php echo e($t['cook_it']); ?></a>
</div>
</div>
<?php if ($featured): ?>
<aside class="discover-shell__feature hero-card">
<img src="<?php echo e($featured['hero']); ?>" alt="<?php echo e($featured['title']); ?>">
<div class="hero-card__body">
<p class="pill"><?php echo e($t['featured']); ?></p>
<h3><?php echo e($featured['title']); ?></h3>
<p><?php echo e($featured['description']); ?></p>
<div class="meta">
<span>⏱ <?php echo format_minutes((int) ($featured['total_time'] ?? 0)); ?></span>
<span>🍽 <?php echo e($featured['servings']); ?> <?php echo e($t['servings']); ?></span>
</div>
<a class="button" href="<?php echo e($recipeUrl($featured)); ?>"><?php echo e($t['cook_it']); ?></a>
</div>
</aside>
<?php endif; ?>
</section>
<section class="latest" id="latest">
<div class="section-header">
<h2><?php echo e($t['latest_drops']); ?></h2>
<p><?php echo count($recipes); ?><?php echo $q || $tag ? $t['count_suffix_filtered'] : $t['count_suffix_default']; ?></p>
<section class="quick-start">
<div class="section-header section-header--stack">
<h2><?php echo e($t['quick_start_title']); ?></h2>
<p><?php echo e($t['quick_start_text']); ?></p>
</div>
<div class="grid">
<?php foreach ($recipes as $recipe): ?>
<div class="grid grid--tight">
<?php foreach ($quickStartRecipes as $recipe): ?>
<article class="card">
<a href="<?php echo e($recipeUrl($recipe)); ?>" class="card__image">
<img src="<?php echo e($recipe['hero']); ?>" alt="<?php echo e($recipe['title']); ?>">
<span class="pill pill--ghost"><?php echo e($recipe['category']); ?></span>
<?php if (!empty($recipe['category'])): ?>
<span class="pill pill--ghost"><?php echo e($recipe['category']); ?></span>
<?php endif; ?>
</a>
<div class="card__body">
<h3><a href="<?php echo e($recipeUrl($recipe)); ?>"><?php echo e($recipe['title']); ?></a></h3>
<p><?php echo e($recipe['description']); ?></p>
<div class="meta">
<span>⏱ <?php echo format_minutes((int) ($recipe['total_time'] ?? 0)); ?></span>
<span>🙂 <?php echo e($recipe['difficulty']); ?></span>
</div>
<div class="tags">
<?php foreach ($recipe['tags'] as $tTag): ?>
<a href="<?php echo e($tagUrl($tTag)); ?>" class="tag">#<?php echo e($tTag); ?></a>
<?php endforeach; ?>
</div>
<a class="button" href="<?php echo e($recipeUrl($recipe)); ?>"><?php echo e($t['view_recipe']); ?></a>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
<section class="latest" id="latest">
<div class="section-header section-header--stack">
<h2><?php echo e($t['library_title']); ?></h2>
<p><?php echo e($t['library_text']); ?></p>
<p><?php echo $recipeCount; ?><?php echo $q || $tag ? $t['count_suffix_filtered'] : $t['count_suffix_default']; ?></p>
</div>
<?php if (!empty($latestRecipes)): ?>
<div class="grid">
<?php foreach ($latestRecipes as $recipe): ?>
<article class="card">
<a href="<?php echo e($recipeUrl($recipe)); ?>" class="card__image">
<img src="<?php echo e($recipe['hero']); ?>" alt="<?php echo e($recipe['title']); ?>">
<?php if (!empty($recipe['category'])): ?>
<span class="pill pill--ghost"><?php echo e($recipe['category']); ?></span>
<?php endif; ?>
</a>
<div class="card__body">
<h3><a href="<?php echo e($recipeUrl($recipe)); ?>"><?php echo e($recipe['title']); ?></a></h3>
<p><?php echo e($recipe['description']); ?></p>
<div class="meta">
<span>⏱ <?php echo format_minutes((int) ($recipe['total_time'] ?? 0)); ?></span>
<span>🙂 <?php echo e($recipe['difficulty']); ?></span>
</div>
<?php if (!empty($recipe['tags'])): ?>
<div class="tags">
<?php foreach ($recipe['tags'] as $tTag): ?>
<a href="<?php echo e($tagUrl($tTag)); ?>" class="tag">#<?php echo e($tTag); ?></a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</article>
<?php endforeach; ?>
</div>
<?php else: ?>
<article class="empty-state">
<h3><?php echo e($t['empty_title']); ?></h3>
<p><?php echo e($t['empty_text']); ?></p>
<a class="button" href="/index.php?lang=<?php echo e($lang); ?>"><?php echo e($t['tag_clear']); ?></a>
</article>
<?php endif; ?>
</section>
<section class="coming-features" id="coming-features">
<div class="section-header">
<div class="section-header section-header--stack">
<h2><?php echo e($t['coming_features']); ?></h2>
<p><?php echo e($t['coming_features_sub']); ?></p>
</div>