fix: touch reachability, contrast and alignment

A pass over desktop, tablet and phone widths, measuring rather than
eyeballing.

The one that matters: a task could not be deleted on a phone. The delete
button is revealed on hover, touch devices report hover: none, so it sat
at zero opacity with nothing to reveal it. The README told people to hover
over a task and click it. It is now always visible where hover does not
exist.

Touch targets were under the 44px minimum: the checkbox at 24, delete at
32, the footer links at 23 tall. They now get 44px hit areas from overlays
that leave the drawn size alone, behind a pointer: coarse query so a mouse
keeps small precise targets and the full row stays draggable.

Two contrast failures in light mode. The date was the brand blue on the
near-white background at 3.54:1, so text now uses a darker --accent-text
and reads 5.14:1. Completed rows carried opacity 0.8 over an already
mid-contrast blue, which came to 3.34:1; the tint, the strikethrough and
the colour say "done" well enough without it, and dropping it gives 4.78:1.
Both themes now pass AA everywhere measured.

The checkbox sat vertically centred, so on a task wrapping to five lines
it floated in the middle of the block. Rows align to the top and task text
gets a 24px line box, matching the checkbox exactly, which also spaces
wrapped lines better.

Reduced motion was only honoured by the empty-state drawing. Svelte's
transitions run in JavaScript and never saw the media query, so tasks
still flew in on a stagger. The component reads the preference directly
and collapses its durations, and CSS covers the rest. The lift on a
dragged card stays, since it tracks the pointer rather than playing at you.

Long unbroken words already wrapped and no breakpoint scrolled sideways.
This commit is contained in:
Aculix Technologies
2026-08-16 02:36:48 +05:30
parent 9053f15ad4
commit b846717db9
3 changed files with 87 additions and 8 deletions
+63 -3
View File
@@ -4,6 +4,9 @@
--text-primary: #1A1A1A;
--text-secondary: #666666;
--accent: #607afb;
/* Darker sibling of --accent for text. The brand blue reads 3.54:1 on the
light background, under the 4.5:1 needed for body-sized text. */
--accent-text: #4C5FD5;
--border: #E0E0E0;
--hover: #F5F5F5;
--completed-bg: #EEF1FF;
@@ -18,6 +21,7 @@ html.dark {
--text-primary: #E0E0E0;
--text-secondary: #999999;
--accent: #7B93FF;
--accent-text: #8FA5FF;
--border: #333333;
--hover: #2A2A2A;
--completed-bg: rgba(96, 122, 251, 0.15);
@@ -177,7 +181,7 @@ body {
.date-display {
font-size: 16px;
font-weight: 500;
color: var(--accent);
color: var(--accent-text);
}
.task-input-container {
@@ -261,7 +265,7 @@ body {
.task-item {
display: flex;
align-items: center;
align-items: flex-start;
gap: 16px;
padding: 16px 20px;
background-color: var(--bg-surface);
@@ -321,7 +325,6 @@ html.dark .task-item.dragging {
.task-item.completed {
background-color: var(--completed-bg);
opacity: 0.8;
}
.checkbox {
@@ -365,6 +368,9 @@ html.dark .task-item.dragging {
.task-text {
flex: 1;
font-size: 16px;
/* 24px line box, the same height as the checkbox, so the two align on the
first line without nudging either. */
line-height: 1.5;
color: var(--text-primary);
transition: all 300ms ease;
word-break: break-word;
@@ -379,6 +385,7 @@ html.dark .task-item.dragging {
width: 32px;
height: 32px;
border: none;
position: relative;
background: transparent;
color: var(--text-secondary);
cursor: pointer;
@@ -461,11 +468,23 @@ html.dark .task-item.dragging {
}
/* Decorative only, so respect a reduced-motion preference. */
/* Svelte's transitions run in JavaScript and never see this query, so
App.svelte checks the same preference and zeroes its durations. This half
covers everything driven by CSS. */
@media (prefers-reduced-motion: reduce) {
.empty-art-box,
.empty-art-motes path {
animation: none;
}
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
.empty-state p {
@@ -528,6 +547,47 @@ html.dark .data-status.error {
color: #ff8a8a;
}
/* Touch devices.
*
* The delete button is revealed on hover, which does not exist here. Without
* this it stays at zero opacity and a task cannot be deleted on a phone at
* all, which is what the README told people to do.
*
* The rest widens hit areas to the 44px minimum. The overlays are positioned
* so nothing moves: the controls keep the size they were drawn at, they are
* just easier to hit. Kept behind the media query so a mouse still gets small,
* precise targets and the full row stays available to drag. */
@media (hover: none) {
.delete-btn {
opacity: 1;
}
}
@media (pointer: coarse) {
.checkbox::after,
.delete-btn::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 44px;
height: 44px;
transform: translate(-50%, -50%);
}
.today-btn,
.theme-toggle {
height: 44px;
}
.clear-completed,
.data-link {
display: inline-flex;
align-items: center;
min-height: 44px;
}
}
.visually-hidden {
position: absolute;
width: 1px;