diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-09-12 22:26:09 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-09-12 22:26:09 +0200 |
| commit | 2a7c4f969874b850e30170f3aef5b578677220b0 (patch) | |
| tree | 7fafc0aaf017d4f045cedd92a6fa2d55024fb63d /src | |
| parent | 5f0a73920034741236034ed7a721c68eceb8ab82 (diff) | |
Scope badges, titles, and one-off styles; add Button outline
ScoreBadge/StatusBadge carry their own color scales, no
bg-*/text-* globals. SectionTitle unifies category-name and
section-title dupes (h2/h3 via level prop). Grocery add-bar
buttons use Button, new outline variant for the secondary
action. save-status moves into its only page. Delete dead
globals and page overrides.
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.css | 76 | ||||
| -rw-r--r-- | src/lib/components/Button.svelte | 23 | ||||
| -rw-r--r-- | src/lib/components/ScoreBadge.svelte | 35 | ||||
| -rw-r--r-- | src/lib/components/SectionTitle.svelte | 23 | ||||
| -rw-r--r-- | src/lib/components/StatusBadge.svelte | 48 | ||||
| -rw-r--r-- | src/routes/groceries/+page.svelte | 58 | ||||
| -rw-r--r-- | src/routes/groceries/catalog/+page.svelte | 3 | ||||
| -rw-r--r-- | src/routes/grocery/[id]/+page.svelte | 15 | ||||
| -rw-r--r-- | src/routes/projects/[id]/+page.svelte | 14 | ||||
| -rw-r--r-- | src/routes/projects/[projectId]/routine/[routineId]/+page.svelte | 16 |
10 files changed, 144 insertions, 167 deletions
diff --git a/src/app.css b/src/app.css index daeffd7..0f25b28 100644 --- a/src/app.css +++ b/src/app.css @@ -15,18 +15,6 @@ gap: 0.5rem; } -/* Category heading */ -.category-name { - font-size: 0.75rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.08em; - color: var(--accent); - margin: 1.25rem 0 0.5rem; - padding-left: 0.5rem; - border-left: 3px solid var(--accent); -} - /* Empty state */ .empty { color: var(--text-secondary); @@ -99,41 +87,6 @@ box-shadow: 0 0 0 3px var(--shadow-accent-focus); } -.score { - flex-shrink: 0; - min-width: 2.75rem; - padding: 0.25rem 0.5rem; - border-radius: 9999px; - text-align: center; - font-size: 0.875rem; - font-weight: 700; - color: var(--white); -} - -.bg-success { - background: var(--success); -} - -.bg-accent { - background: var(--accent); -} - -.bg-danger { - background: var(--danger); -} - -.bg-accent-soft { - background: var(--shadow-accent); -} - -.text-accent { - color: var(--accent); -} - -.text-white { - color: var(--white); -} - .add-bar { display: flex; gap: 0.5rem; @@ -146,19 +99,6 @@ min-width: 0; } -.status { - flex-shrink: 0; - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.5rem; - height: 1.5rem; - border-radius: 0.375rem; - font-size: 0.875rem; - font-weight: 700; - line-height: 1; -} - /* Shared route scaffolding */ .page-header { display: flex; @@ -186,19 +126,3 @@ .input-invalid { border-color: var(--danger); } - -.save-status { - font-size: 0.875rem; - color: var(--success); - font-weight: 500; - margin-top: -0.25rem; - visibility: hidden; - opacity: 0; - transition: opacity 0.2s ease; - min-height: 1.25rem; -} - -.save-status.visible { - visibility: visible; - opacity: 1; -} diff --git a/src/lib/components/Button.svelte b/src/lib/components/Button.svelte index 30e9fc2..27b9b7f 100644 --- a/src/lib/components/Button.svelte +++ b/src/lib/components/Button.svelte @@ -15,7 +15,7 @@ onclick, children, }: { - variant?: 'primary' | 'accent' | 'ghost' | 'done'; + variant?: 'primary' | 'accent' | 'ghost' | 'done' | 'outline'; type?: 'button' | 'submit' | 'reset'; disabled?: boolean; name?: string; @@ -143,6 +143,27 @@ font-size: 0.9375rem; } + .outline { + width: 3rem; + height: 3rem; + flex-shrink: 0; + padding: 0; + border-radius: 0.75rem; + background: var(--surface); + color: var(--text-secondary); + border: 1.5px solid var(--border); + font-size: 1.25rem; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + } + + .outline:hover:not(:disabled) { + border-color: var(--accent); + color: var(--text); + } + .block { align-self: stretch; width: 100%; diff --git a/src/lib/components/ScoreBadge.svelte b/src/lib/components/ScoreBadge.svelte index 6384667..ae11918 100644 --- a/src/lib/components/ScoreBadge.svelte +++ b/src/lib/components/ScoreBadge.svelte @@ -1,13 +1,32 @@ <script lang="ts"> let { score }: { score: number } = $props(); - const className = $derived( - score >= 80 - ? 'score bg-success' - : score >= 50 - ? 'score bg-accent' - : 'score bg-danger', - ); + const level = $derived(score >= 80 ? 'high' : score >= 50 ? 'mid' : 'low'); </script> -<span class={className}>{score}</span> +<span class="score {level}">{score}</span> + +<style> + .score { + flex-shrink: 0; + min-width: 2.75rem; + padding: 0.25rem 0.5rem; + border-radius: 9999px; + text-align: center; + font-size: 0.875rem; + font-weight: 700; + color: var(--white); + } + + .high { + background: var(--success); + } + + .mid { + background: var(--accent); + } + + .low { + background: var(--danger); + } +</style> diff --git a/src/lib/components/SectionTitle.svelte b/src/lib/components/SectionTitle.svelte new file mode 100644 index 0000000..be77a46 --- /dev/null +++ b/src/lib/components/SectionTitle.svelte @@ -0,0 +1,23 @@ +<script lang="ts"> + import type { Snippet } from 'svelte'; + + let { level = 2, children }: { level?: 2 | 3; children: Snippet } = $props(); +</script> + +<svelte:element this={level === 2 ? 'h2' : 'h3'}> + {@render children()} +</svelte:element> + +<style> + h2, + h3 { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--accent); + margin: 1.25rem 0 0.5rem; + padding-left: 0.5rem; + border-left: 3px solid var(--accent); + } +</style> diff --git a/src/lib/components/StatusBadge.svelte b/src/lib/components/StatusBadge.svelte index 4c149d2..2bbdf5a 100644 --- a/src/lib/components/StatusBadge.svelte +++ b/src/lib/components/StatusBadge.svelte @@ -9,16 +9,6 @@ overdueDays = 0, }: { status: RoutineStatus; overdueDays?: number } = $props(); - const className = $derived( - status === 'ok' - ? 'status bg-success text-white' - : status === 'due' - ? 'status bg-accent-soft text-accent' - : status === 'late' - ? 'status bg-accent text-white' - : 'status bg-danger text-white', - ); - const icon = $derived( status === 'ok' ? '✓' @@ -36,5 +26,41 @@ ); </script> -<span class={className} role="img" aria-label={label} title={label}>{icon}</span +<span class="status {status}" role="img" aria-label={label} title={label} + >{icon}</span > + +<style> + .status { + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.5rem; + height: 1.5rem; + border-radius: 0.375rem; + font-size: 0.875rem; + font-weight: 700; + line-height: 1; + } + + .ok { + background: var(--success); + color: var(--white); + } + + .due { + background: var(--shadow-accent); + color: var(--accent); + } + + .late { + background: var(--accent); + color: var(--white); + } + + .veryLate { + background: var(--danger); + color: var(--white); + } +</style> diff --git a/src/routes/groceries/+page.svelte b/src/routes/groceries/+page.svelte index b134da7..5d88e49 100644 --- a/src/routes/groceries/+page.svelte +++ b/src/routes/groceries/+page.svelte @@ -4,7 +4,9 @@ import { enhance } from '$app/forms'; import FuzzyAutocomplete from '$lib/components/FuzzyAutocomplete.svelte'; import GroceryRow from '$lib/components/GroceryRow.svelte'; + import Button from '$lib/components/Button.svelte'; import { groupByCategory } from '$lib/shared/group-by-category'; + import SectionTitle from '$lib/components/SectionTitle.svelte'; import type { EnrichedGrocery } from '$lib/server/services/groceries'; let { data, form } = $props(); @@ -75,11 +77,17 @@ handleAdd(false); }} /> - <button type="button" aria-label="Ajouter" onclick={() => handleAdd(false)} - >+</button + <Button + type="button" + variant="accent" + ariaLabel="Ajouter" + onclick={() => handleAdd(false)}>+</Button > - <button type="button" aria-label="Modifier" onclick={() => handleAdd(true)} - >→</button + <Button + type="button" + variant="outline" + ariaLabel="Modifier" + onclick={() => handleAdd(true)}>→</Button > </div> @@ -93,7 +101,7 @@ {:else} <form method="POST" action="?/toggle" use:enhance> {#each groupByCategory(localItems, data.categories) as [cat, items] (cat)} - <h3 class="category-name">{cat}</h3> + <SectionTitle level={3}>{cat}</SectionTitle> <ul class="item-list"> {#each items as item (item.id)} <GroceryRow {item} from="/groceries" /> @@ -116,46 +124,6 @@ z-index: 10; } - .grocery-add-bar button { - width: 3rem; - height: 3rem; - flex-shrink: 0; - padding: 0; - border: none; - border-radius: 0.75rem; - font-size: 1.25rem; - font-weight: 500; - font-family: inherit; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; - } - - .grocery-add-bar > button:nth-of-type(1) { - background: var(--accent); - color: var(--white); - box-shadow: 0 1px 2px var(--shadow); - } - - .grocery-add-bar > button:nth-of-type(1):hover { - background: var(--accent-hover); - transform: translateY(-1px); - box-shadow: 0 4px 8px var(--shadow-accent-lg); - } - - .grocery-add-bar > button:nth-of-type(2) { - background: var(--surface); - color: var(--text-secondary); - border: 1.5px solid var(--border); - } - - .grocery-add-bar > button:nth-of-type(2):hover { - border-color: var(--accent); - color: var(--text); - } - .error { margin-bottom: 1rem; } diff --git a/src/routes/groceries/catalog/+page.svelte b/src/routes/groceries/catalog/+page.svelte index a7f3c0e..076d3ce 100644 --- a/src/routes/groceries/catalog/+page.svelte +++ b/src/routes/groceries/catalog/+page.svelte @@ -4,6 +4,7 @@ import GroceryRow from '$lib/components/GroceryRow.svelte'; import Button from '$lib/components/Button.svelte'; import { groupByCategory } from '$lib/shared/group-by-category'; + import SectionTitle from '$lib/components/SectionTitle.svelte'; let { data, form } = $props(); @@ -73,7 +74,7 @@ {:else} <form method="POST" action="?/toggle" use:enhance> {#each groupByCategory(filteredItems, data.categories) as [cat, items] (cat)} - <h3 class="category-name">{cat}</h3> + <SectionTitle level={3}>{cat}</SectionTitle> <ul class="item-list"> {#each items as item (item.id)} <GroceryRow diff --git a/src/routes/grocery/[id]/+page.svelte b/src/routes/grocery/[id]/+page.svelte index 54134f0..c672730 100644 --- a/src/routes/grocery/[id]/+page.svelte +++ b/src/routes/grocery/[id]/+page.svelte @@ -177,6 +177,21 @@ </main> <style> + .save-status { + font-size: 0.875rem; + color: var(--success); + font-weight: 500; + margin-top: -0.25rem; + visibility: hidden; + opacity: 0; + transition: opacity 0.2s ease; + min-height: 1.25rem; + } + + .save-status.visible { + visibility: visible; + opacity: 1; + } .ref-row { display: flex; gap: 0.75rem; diff --git a/src/routes/projects/[id]/+page.svelte b/src/routes/projects/[id]/+page.svelte index b48a331..8d62a39 100644 --- a/src/routes/projects/[id]/+page.svelte +++ b/src/routes/projects/[id]/+page.svelte @@ -11,6 +11,7 @@ parseIntervalToDays, type RoutineState, } from '$lib/shared/routine-metrics'; + import SectionTitle from '$lib/components/SectionTitle.svelte'; import { formatDayMonth } from '$lib/shared/temporal'; let { data, form } = $props(); @@ -44,7 +45,7 @@ {#if data.healthSeries.length > 0} <section> - <h2 class="section-title">Santé</h2> + <SectionTitle>Santé</SectionTitle> <div class="form-card chart-card"> <HealthChart series={data.healthSeries} @@ -187,17 +188,6 @@ </main> <style> - .section-title { - font-size: 0.75rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.08em; - color: var(--accent); - margin: 1.5rem 0 0.5rem; - padding-left: 0.5rem; - border-left: 3px solid var(--accent); - } - .chart-card { align-items: stretch; } diff --git a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte index 55185cc..ea292ae 100644 --- a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte +++ b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte @@ -11,6 +11,7 @@ computeHealthSeries, } from '$lib/shared/routine-metrics'; import { formatDayMonth, formatDayMonthYear } from '$lib/shared/temporal'; + import SectionTitle from '$lib/components/SectionTitle.svelte'; let { data, form } = $props(); let name = $state(''); let frequency = $state(''); @@ -125,7 +126,7 @@ </form> {/if} - <h2 class="section-title">Santé</h2> + <SectionTitle>Santé</SectionTitle> <div class="form-card chart-card"> <HealthChart {series} @@ -150,7 +151,7 @@ {/if} </div> - <h2 class="section-title">Historique</h2> + <SectionTitle>Historique</SectionTitle> {#if !data.routine.archived} <form class="form-card form-card--compact add-completion" @@ -216,17 +217,6 @@ font-family: inherit; } - .section-title { - font-size: 0.75rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.08em; - color: var(--accent); - margin: 1.5rem 0 0.5rem; - padding-left: 0.5rem; - border-left: 3px solid var(--accent); - } - .archived-badge { margin: 0 0 1rem; color: var(--text-secondary); |
