fix(kiwi-a11y): ND/calm-UX policy fixes — deficit language, wildcard styling, depletion framing (#42 #46 #47 #80-M)
- #42: Replace deficit framing — "You'd need:" → "To complete this recipe:", element_gaps card-warning → card-secondary, missing/gap chips status-warning → status-info, "Your pantry is missing..." → "These would expand your options:" - #46: Add activeNutritionFilterCount computed; show count in Advanced filters summary when filters are active so it's visible while collapsed - #47: Wildcard confirmation status-warning → status-info, copy updated to calm framing; wildcard recipe card badge status-warning → status-info - M1: Add re-search hint below Hard Day Mode toggle when results are already showing - M8: Move swap candidates collapsible to after directions/steps section - L2: Add autocomplete="off" to filter search, constraint, and allergy text inputs - L5: Add title="This is an affiliate link" disclosure to grocery affiliate links Items already correct (no change needed): - M2: Level description already always visible via activeLevel computed - M3: Rate limit copy already using calm framing - M5: No-results copy already calm - M6: levelLabels already uses full names - M7: "that's part of the fun" was part of the wildcard copy fixed under #47 - L1: Neon/konami handler not present in this file
This commit is contained in:
parent
9de42c3088
commit
ceb03f8b5b
1 changed files with 42 additions and 30 deletions
|
|
@ -80,8 +80,8 @@
|
|||
</div>
|
||||
|
||||
<!-- Surprise Me confirmation -->
|
||||
<div v-if="recipesStore.level === 4" class="status-badge status-warning wildcard-warning">
|
||||
<span id="wildcard-warning-desc">The AI will freestyle recipes from whatever you have. Results can be unusual — that's part of the fun.</span>
|
||||
<div v-if="recipesStore.level === 4" class="status-badge status-info wildcard-warning">
|
||||
<span id="wildcard-warning-desc">Wildcard mode lets the AI get creative with whatever you have on hand. Results might surprise you.</span>
|
||||
<label class="flex-start gap-sm mt-xs">
|
||||
<input type="checkbox" v-model="recipesStore.wildcardConfirmed" aria-describedby="wildcard-warning-desc" />
|
||||
<span>I understand, go for it</span>
|
||||
|
|
@ -97,6 +97,9 @@
|
|||
🌿 Hard Day Mode
|
||||
<span class="hard-day-sub">{{ recipesStore.hardDayMode ? 'on — quick & simple only' : 'quick, simple recipes only' }}</span>
|
||||
</button>
|
||||
<p v-if="recipesStore.hardDayMode && recipesStore.result && recipesStore.result.suggestions.length > 0" class="text-muted text-sm mt-xs">
|
||||
Tap "Find recipes" again to apply.
|
||||
</p>
|
||||
|
||||
<!-- Dietary Preferences (collapsible) -->
|
||||
<details class="collapsible form-group" @toggle="(e: Event) => dietaryOpen = (e.target as HTMLDetailsElement).open">
|
||||
|
|
@ -125,6 +128,7 @@
|
|||
aria-describedby="constraint-hint"
|
||||
@keydown="onConstraintKey"
|
||||
@blur="commitConstraintInput"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<span id="constraint-hint" class="form-hint">Press Enter or comma to add.</span>
|
||||
</div>
|
||||
|
|
@ -159,6 +163,7 @@
|
|||
aria-describedby="allergy-hint"
|
||||
@keydown="onAllergyKey"
|
||||
@blur="commitAllergyInput"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<span id="allergy-hint" class="form-hint">No recipes containing these ingredients will appear.</span>
|
||||
</div>
|
||||
|
|
@ -194,7 +199,7 @@
|
|||
<!-- Advanced Filters (collapsible) -->
|
||||
<details class="collapsible form-group" @toggle="(e: Event) => advancedOpen = (e.target as HTMLDetailsElement).open">
|
||||
<summary class="collapsible-summary filter-summary" :aria-expanded="advancedOpen">
|
||||
Advanced filters
|
||||
Nutrition Filters{{ activeNutritionFilterCount > 0 ? ` (${activeNutritionFilterCount} active)` : '' }}
|
||||
<span v-if="advancedActive" class="filter-active-dot" aria-label="filters active"></span>
|
||||
</summary>
|
||||
|
||||
|
|
@ -313,13 +318,13 @@
|
|||
</div>
|
||||
|
||||
<!-- Element gaps -->
|
||||
<div v-if="recipesStore.result.element_gaps.length > 0" class="card card-warning mb-md">
|
||||
<p class="text-sm font-semibold">Your pantry is missing some flavor elements:</p>
|
||||
<div v-if="recipesStore.result.element_gaps.length > 0" class="card card-secondary mb-md">
|
||||
<p class="text-sm font-semibold">These would expand your options:</p>
|
||||
<div class="flex flex-wrap gap-xs mt-xs">
|
||||
<span
|
||||
v-for="gap in recipesStore.result.element_gaps"
|
||||
:key="gap"
|
||||
class="status-badge status-warning"
|
||||
class="status-badge status-info"
|
||||
>{{ gap }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -331,6 +336,7 @@
|
|||
v-model="filterText"
|
||||
placeholder="Search recipes or ingredients…"
|
||||
aria-label="Filter recipes"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<div class="filter-chips">
|
||||
<template v-if="availableLevels.length > 1">
|
||||
|
|
@ -386,7 +392,7 @@
|
|||
<div class="flex flex-wrap gap-xs" style="align-items:center">
|
||||
<span class="status-badge status-success">{{ recipe.match_count }} matched</span>
|
||||
<span class="status-badge status-info">Level {{ recipe.level }}</span>
|
||||
<span v-if="recipe.is_wildcard" class="status-badge status-warning">Wildcard</span>
|
||||
<span v-if="recipe.is_wildcard" class="status-badge status-info">Wildcard</span>
|
||||
<button
|
||||
v-if="recipe.id"
|
||||
:class="['btn-icon', 'btn-bookmark', { active: recipesStore.isBookmarked(recipe.id) }]"
|
||||
|
|
@ -450,12 +456,12 @@
|
|||
|
||||
<!-- Missing ingredients -->
|
||||
<div v-if="recipe.missing_ingredients.length > 0" class="mb-sm">
|
||||
<p class="text-sm font-semibold text-secondary">You'd need:</p>
|
||||
<p class="text-sm font-semibold text-secondary">To complete this recipe:</p>
|
||||
<div class="flex flex-wrap gap-xs mt-xs">
|
||||
<span
|
||||
v-for="ing in recipe.missing_ingredients"
|
||||
:key="ing"
|
||||
class="status-badge status-warning"
|
||||
class="status-badge status-info"
|
||||
>{{ ing }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -471,13 +477,34 @@
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="grocery-link status-badge status-info"
|
||||
title="This is an affiliate link"
|
||||
>
|
||||
{{ link.ingredient }} @ {{ link.retailer }} ↗
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Swap candidates collapsible -->
|
||||
<!-- Prep notes -->
|
||||
<div v-if="recipe.prep_notes && recipe.prep_notes.length > 0" class="prep-notes mb-sm">
|
||||
<p class="text-sm font-semibold">Before you start:</p>
|
||||
<ul class="prep-notes-list mt-xs">
|
||||
<li v-for="note in recipe.prep_notes" :key="note" class="text-sm prep-note-item">
|
||||
{{ note }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Directions — always visible; this is the content people came for -->
|
||||
<div v-if="recipe.directions.length > 0" class="directions-section">
|
||||
<p class="text-sm font-semibold directions-label">Steps</p>
|
||||
<ol class="directions-list mt-xs">
|
||||
<li v-for="(step, idx) in recipe.directions" :key="idx" class="text-sm direction-step">
|
||||
{{ step }}
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- Swap candidates collapsible — shown after directions per M8 -->
|
||||
<details
|
||||
v-if="recipe.swap_candidates.length > 0"
|
||||
class="collapsible mb-sm"
|
||||
|
|
@ -501,26 +528,6 @@
|
|||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Prep notes -->
|
||||
<div v-if="recipe.prep_notes && recipe.prep_notes.length > 0" class="prep-notes mb-sm">
|
||||
<p class="text-sm font-semibold">Before you start:</p>
|
||||
<ul class="prep-notes-list mt-xs">
|
||||
<li v-for="note in recipe.prep_notes" :key="note" class="text-sm prep-note-item">
|
||||
{{ note }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Directions — always visible; this is the content people came for -->
|
||||
<div v-if="recipe.directions.length > 0" class="directions-section">
|
||||
<p class="text-sm font-semibold directions-label">Steps</p>
|
||||
<ol class="directions-list mt-xs">
|
||||
<li v-for="(step, idx) in recipe.directions" :key="idx" class="text-sm direction-step">
|
||||
{{ step }}
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- Primary action: open detail panel -->
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-primary btn-make" @click="openRecipe(recipe)">
|
||||
|
|
@ -819,6 +826,11 @@ const advancedActive = computed(() =>
|
|||
!!recipesStore.styleId
|
||||
)
|
||||
|
||||
// #46 — count of active nutrition filters so the summary is informative when collapsed
|
||||
const activeNutritionFilterCount = computed(() =>
|
||||
Object.values(recipesStore.nutritionFilters ?? {}).filter((v) => v !== null).length
|
||||
)
|
||||
|
||||
const activeLevel = computed(() => levels.find(l => l.value === recipesStore.level))
|
||||
|
||||
const cuisineStyles = [
|
||||
|
|
|
|||
Loading…
Reference in a new issue