mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
fix(mobile): touch-target floor, reduced-motion backstop, manifest categories, trivy ignore for npm-bundled picomatch
This commit is contained in:
commit
ba89f97176
10 files changed
+48
-14
No files matched your search
@@ -0,0 +1,14 @@
|
||||
# Trivy vulnerability ignore list.
|
||||
# One CVE id per line. Only suppress a finding that has NO upstream fix we
|
||||
# can apply; never suppress one with a fixed version available — bump instead.
|
||||
# Each entry names what it covers and the condition to re-check / remove it.
|
||||
|
||||
# CVE-2026-33671 (picomatch ReDoS, HIGH) — flagged only in the npm CLI's
|
||||
# OWN bundled copy that ships inside the Node base image
|
||||
# (usr/local/lib/node_modules/npm/node_modules/picomatch, v4.0.3), used at
|
||||
# image-build time by npm, not by our runtime. Our application dependency
|
||||
# tree pins picomatch >= 4.0.4 via a pnpm override, so the running app is
|
||||
# not affected and the path is not reachable at runtime. Re-check when the
|
||||
# Node base image ships an npm build whose bundled picomatch is >= 4.0.4,
|
||||
# then drop this entry.
|
||||
CVE-2026-33671
|
||||
@@ -6,7 +6,8 @@
|
||||
"scope": "/",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"display_override": ["standalone"],
|
||||
"display_override": ["standalone", "minimal-ui"],
|
||||
"categories": ["health", "medical", "lifestyle"],
|
||||
"theme_color": "#282a36",
|
||||
"background_color": "#282a36",
|
||||
"icons": [
|
||||
|
||||
@@ -85,7 +85,8 @@ describe("public/manifest.json PWA identity hygiene (F-7, 2026-05-16)", () => {
|
||||
const manifest = JSON.parse(raw) as Record<string, unknown>;
|
||||
expect(manifest.scope).toBe("/");
|
||||
expect(manifest.id).toBe("/?source=pwa");
|
||||
expect(manifest.display_override).toEqual(["standalone"]);
|
||||
expect(manifest.display_override).toEqual(["standalone", "minimal-ui"]);
|
||||
expect(manifest.categories).toEqual(["health", "medical", "lifestyle"]);
|
||||
});
|
||||
|
||||
it("keeps the existing display/start_url contract", () => {
|
||||
|
||||
@@ -889,3 +889,21 @@
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Global reduced-motion backstop ──────────────────────────────────
|
||||
* Defensive catch-all so any animation/transition we forget to gate
|
||||
* per-component still collapses to near-instant when the user has asked
|
||||
* for reduced motion. The per-component blocks above stay — they carry
|
||||
* tailored static fallbacks (static dots, full-height bars). This is the
|
||||
* floor, not a replacement. Near-zero (not 0) keeps JS animation-end /
|
||||
* transition-end listeners firing so state machines don't stall. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export function SortableHead({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onSort(column)}
|
||||
className="hover:text-foreground inline-flex items-center gap-1 transition-colors"
|
||||
className="hover:text-foreground focus-visible:ring-ring/50 inline-flex min-h-11 items-center gap-1 rounded-sm transition-colors focus-visible:ring-2 focus-visible:outline-none sm:min-h-0"
|
||||
>
|
||||
{label}
|
||||
{isActive ? (
|
||||
|
||||
@@ -63,7 +63,7 @@ export function GlucoseAdvancedDisclosure({
|
||||
aria-expanded={open}
|
||||
aria-controls={regionId}
|
||||
data-slot="glucose-advanced-toggle"
|
||||
className="text-muted-foreground hover:text-foreground flex min-h-11 w-full items-center justify-between gap-2 text-sm font-medium transition-colors"
|
||||
className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 flex min-h-11 w-full items-center justify-between gap-2 rounded-sm text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none"
|
||||
>
|
||||
<span>{t("insights.bloodGlucose.clinical.advanced.toggle")}</span>
|
||||
<ChevronDown
|
||||
|
||||
@@ -85,14 +85,14 @@ export function SuggestedPrompts({
|
||||
type="button"
|
||||
onClick={() => onPick(prompt)}
|
||||
data-slot="insights-suggested-prompts-chip"
|
||||
// v1.8.5 W4a — denser chips. The tap target stays comfortable
|
||||
// (`min-h-9` ≈ 36 px, the WCAG/iOS floor) but the horizontal
|
||||
// padding + type size drop a notch so the two prompts pack into
|
||||
// fewer rows and the left column stops towering over the
|
||||
// Health-Score card.
|
||||
// v1.8.5 W4a — denser chips. The horizontal padding + type size
|
||||
// drop a notch so the two prompts pack into fewer rows and the
|
||||
// left column stops towering over the Health-Score card. The tap
|
||||
// target clears the 44 px iOS/WCAG floor on phones (`min-h-11`)
|
||||
// and relaxes to the denser 36 px from `sm` up.
|
||||
className={cn(
|
||||
"border-dracula-purple/18 hover:border-dracula-purple/40 hover:text-foreground",
|
||||
"text-muted-foreground inline-flex min-h-9 items-center gap-1.5",
|
||||
"text-muted-foreground inline-flex min-h-11 items-center gap-1.5 sm:min-h-9",
|
||||
"rounded-full border bg-transparent px-3 py-1.5 text-xs",
|
||||
"transition-colors focus-visible:ring-2 focus-visible:outline-none",
|
||||
"focus-visible:ring-dracula-purple/50",
|
||||
|
||||
@@ -669,7 +669,7 @@ function LedgerRowItem({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="min-h-9 min-w-9"
|
||||
className="min-h-11 min-w-11 sm:min-h-9 sm:min-w-9"
|
||||
aria-label={t("medications.detail.verlauf.rowActionsLabel", {
|
||||
time: timeLabel,
|
||||
})}
|
||||
|
||||
@@ -238,7 +238,7 @@ export function TimesOfDayChips({
|
||||
disabled={disabled}
|
||||
aria-label={`${t(`${i18nPrefix}.remove`)} ${time}`}
|
||||
onClick={() => onRemove(time)}
|
||||
className="focus-visible:ring-ring hover:bg-background inline-flex h-7 w-7 items-center justify-center rounded-sm focus-visible:ring-2 focus-visible:outline-none disabled:opacity-50"
|
||||
className="focus-visible:ring-ring hover:bg-background relative inline-flex h-7 w-7 items-center justify-center rounded-sm before:absolute before:inset-[-8px] before:content-[''] focus-visible:ring-2 focus-visible:outline-none disabled:opacity-50"
|
||||
data-slot="times-of-day-chip-remove"
|
||||
>
|
||||
<X className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
|
||||
@@ -223,7 +223,7 @@ export function ApiTokensRow({
|
||||
onClick={() =>
|
||||
void copy(endpoint, t("medications.detail.api.urlCopied"))
|
||||
}
|
||||
className="h-7 w-7 shrink-0"
|
||||
className="relative h-7 w-7 shrink-0 before:absolute before:inset-[-8px] before:content-['']"
|
||||
aria-label={t("medications.detail.api.copyUrl")}
|
||||
data-slot="api-tokens-copy-url"
|
||||
>
|
||||
@@ -328,7 +328,7 @@ export function ApiTokensRow({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute top-1 right-1 h-7 w-7"
|
||||
className="absolute top-1 right-1 h-7 w-7 before:absolute before:inset-[-8px] before:content-['']"
|
||||
onClick={() =>
|
||||
void copy(selectedExample.value, t("common.copied"))
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user