admin: ui polish pass, thirty findings from the emil audit
Gates / frontend (push) Failing after 1m6s
Gates / package (push) Skipped
Gates / container (push) Skipped
Gates / test (push) Failing after 3m29s
Gates / test-aarch64 (push) Successful in 9m53s
CI / gates (push) Failing after 13m23s

selected states stop changing font weight, buttons gain a pressed scale and scoped 120ms transitions with a reduced-motion override, every loading and empty state reserves its height, charts measure before first paint, hit targets rise to the 44px enhanced target where layout permits, long domains clamp to two lines on an unpadded inner span, chips and name cells truncate, tabular figures on counts and time columns, a z-index layer scale replaces magic numbers and fixes the dialog-over-confirm tie, history's empty state gains a clear-filters action, page headings balance, font smoothing and color-scheme land on the html reset.
This commit is contained in:
2026-09-01 23:23:07 +02:00
parent 2ae0c974a4
commit e656670dd4
30 changed files with 399 additions and 82 deletions
+15 -1
View File
@@ -34,6 +34,11 @@ const THICKNESS = 36;
const OUTER_RADIUS = SIZE / 2;
const INNER_RADIUS = OUTER_RADIUS - THICKNESS;
/** The gap `body` puts between the ring and the legend, in pixels: 1.25rem. */
const BODY_GAP = 20;
/** One `legend` row, in pixels: its 1.25rem line height. */
const LEGEND_ROW = 20;
const numberFormat = new Intl.NumberFormat();
/** The width at which the page puts the two donuts side by side, and the page's
@@ -42,11 +47,19 @@ const numberFormat = new Intl.NumberFormat();
const TWO_COLUMN = "@media (min-width: 1280px)";
const styles = stylex.create({
/**
* The reserve is the ring and a legend, not the ring alone: 180 + 20 + 20 =
* 220px. `body` wraps once the panel is narrower than the ring plus the
* legend's 12rem floor, and below that width the filled panel is the ring, the
* body gap and at least one legend row. Side by side the same 220px holds a
* legend of nine rows, which is more than either breakdown draws — the API
* caps neither, so the ring's own height is not a ceiling.
*/
empty: {
display: "flex",
alignItems: "center",
justifyContent: "center",
minHeight: SIZE,
minHeight: SIZE + BODY_GAP + LEGEND_ROW,
borderRadius: "0.25rem",
borderWidth: 1,
borderStyle: "dashed",
@@ -99,6 +112,7 @@ const styles = stylex.create({
gap: "0.5rem",
},
swatch: {
pointerEvents: "none",
flexShrink: 0,
alignSelf: "center",
display: "inline-block",
+21 -2
View File
@@ -13,7 +13,7 @@ import { useNavigate, useSearch } from "@tanstack/react-router";
import { Radio, RadioGroup } from "react-aria-components";
import type { Period } from "@/lib/types";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";
import { colors, metrics } from "@/ui/tokens.stylex";
import { DEFAULT_PERIOD, PERIODS } from "./period";
const styles = stylex.create({
@@ -33,19 +33,31 @@ const styles = stylex.create({
fontSize: "1.5rem",
lineHeight: "2rem",
fontWeight: 600,
textWrap: "balance",
},
periodGroup: {
display: "flex",
gap: "0.25rem",
},
/**
* The weight lives here rather than on the selected variant: selection may
* change colour, but a heavier label would re-measure the row and shift every
* option beside it.
*/
period: {
cursor: "pointer",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
minHeight: metrics.hitTarget,
minWidth: metrics.hitTarget,
borderStyle: "none",
borderRadius: "0.25rem",
paddingInline: "0.625rem",
paddingBlock: "0.25rem",
fontSize: "0.875rem",
lineHeight: "1.25rem",
fontWeight: 500,
},
/** A Radio is a `label`, so RAC drives the ring rather than `:focus-visible`. */
periodFocusVisible: {
@@ -61,13 +73,20 @@ const styles = stylex.create({
"@media (prefers-color-scheme: dark)": "oklch(37% 0.013 285.805)",
},
color: colors.text,
fontWeight: 500,
},
periodIdle: {
backgroundColor: { default: "transparent", ":hover": colors.surfaceHover },
color: colors.textSecondary,
transitionProperty: metrics.transitionProperty,
transitionDuration: { default: metrics.transitionDuration, "@media (prefers-reduced-motion: reduce)": "0s" },
},
/**
* The height approximates the filled overview — stat tiles, a 240px chart and
* a 180px donut with the panel chrome around them — so that the page does not
* jump when the window lands. That is where the number comes from.
*/
loading: {
minHeight: "48rem",
fontSize: "0.875rem",
lineHeight: "1.25rem",
color: colors.textMuted,
+12 -9
View File
@@ -9,14 +9,14 @@
* StyleX tokens instead.
*/
import { useEffect, useRef, useState } from "react";
import { useLayoutEffect, useRef, useState } from "react";
import * as stylex from "@stylexjs/stylex";
import { AxisBottom, AxisLeft, type TickRendererProps } from "@visx/axis";
import { GridRows } from "@visx/grid";
import { scaleBand, scaleLinear } from "@visx/scale";
import { TooltipWithBounds } from "@visx/tooltip";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";
import { colors, layers } from "@/ui/tokens.stylex";
export const CHART_HEIGHT = 240;
export const MARGIN = { top: 8, right: 8, bottom: 22, left: 44 } as const;
@@ -53,15 +53,18 @@ export function plotArea(width: number): Plot {
}
/**
* The container's width, measured on mount and on every resize. Deliberately
* `useEffect` rather than `useLayoutEffect`: the first paint draws at the
* fallback width and the measured width lands a frame later, which is the
* timing the charts have always had.
* The container's width, measured on mount and on every resize.
*
* `useLayoutEffect` rather than `useEffect`, because the difference is visible:
* a layout effect measures and re-renders before the browser paints, so the
* first painted frame is already the real width. Under `useEffect` the measured
* width lands one painted frame later, and the reader sees a chart drawn at
* `FALLBACK_WIDTH` snap to its container.
*/
export function useMeasuredWidth(): [React.RefObject<HTMLDivElement | null>, number] {
const ref = useRef<HTMLDivElement>(null);
const [width, setWidth] = useState(0);
useEffect(() => {
useLayoutEffect(() => {
const el = ref.current;
if (el === null) return;
setWidth(el.clientWidth);
@@ -159,7 +162,7 @@ const styles = stylex.create({
tooltip: {
pointerEvents: "none",
position: "absolute",
zIndex: 10,
zIndex: layers.tooltip,
borderRadius: "0.25rem",
borderWidth: 1,
borderStyle: "solid",
@@ -240,7 +243,7 @@ function TickLabel({ x, dx, dy, textAnchor, dominantBaseline, formattedValue }:
dy={dy}
textAnchor={textAnchor}
dominantBaseline={dominantBaseline}
{...stylex.props(styles.axisLabel)}
{...stylex.props(styles.axisLabel, shared.tabularNums)}
>
{formattedValue}
</text>