Redesign the setup flow and consolidate the CSS into one primitives layer

Onboarding asks explicitly for one or two parents and shows one card per
parent for balances; the balance form is per parent with inline
validation; the partner can be filled from the FK deduction or added
from it. Settings and Dashboard reuse the same form.

CSS: bare buttons carry no colour any more; buttons, cards, choice
cards, fields, alerts, badges, eyebrow and modal chrome are defined once
in primitives.css. Media queries live only in responsive.css. A style
guard test blocks hex, !important and stray media queries. Bilingual
fixes: calendar selects size by content, header no longer wraps. Vitest
is capped at four workers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 12:27:29 +02:00
parent e4719cb9ae
commit ae6db7e16f
105 changed files with 5683 additions and 3690 deletions
+19
View File
@@ -36,12 +36,31 @@ export function calendarDayCell(page: Page, isoDate: string) {
* cells entirely, which reads as a click on the anchor date. Moving in
* several steps per cell, with a short pause between cells, is what
* reliably fires a `pointerenter` on each one.
*
* The whole dragged range is scrolled into view ONCE, before the drag
* starts — never mid-drag. A default-viewport (1280×720) run of a 6-row
* month puts the trailing week's cells below the fold (`getBoundingClientRect`
* still reports their real, off-screen `y`); moving the mouse to those
* coordinates without scrolling first sends it past the bottom of the
* actually-rendered viewport, where there is no element to receive
* `pointerenter` at all — the browser just stops delivering the event, and
* the drag's `dragCurrent` freezes on the last on-screen cell it entered
* while still climbing toward the target. Scrolling once up front (via the
* last, typically lowest, target date) keeps every waypoint's precomputed
* center valid for the whole gesture; scrolling again after `mouse.down()`
* would invalidate the coordinates of any cell already computed and risk
* disrupting the in-progress native pointer sequence.
*/
export async function dragSelectDates(page: Page, isoDates: string[]): Promise<void> {
if (isoDates.length < 2) {
throw new Error('dragSelectDates needs at least two dates to actually drag across')
}
const lastDate = isoDates[isoDates.length - 1]
if (lastDate) {
await calendarDayCell(page, lastDate).scrollIntoViewIfNeeded()
}
const centerOf = async (isoDate: string): Promise<{ x: number; y: number }> => {
const box = await calendarDayCell(page, isoDate).boundingBox()
if (!box) throw new Error(`calendar day cell not visible for ${isoDate} — is it in the current month view?`)