Selection never showed. getRowSx set a background on the <tr>, but every
<td> paints background.paper over it, and the hover rule targets cells -
so hover won and selection was invisible. Selection and hover are now
both cell-level rules in DataTable, with selection first so hover layers
on top, and rows carry an `is-selected` class from a new isRowSelected
prop. Uptime, Docker and Infrastructure tables all had the same broken
copy of this; they now share the one implementation, and any future table
gets it for free.
action.selected/selectedHover are defined explicitly and tinted with the
brand, so a selected row reads as chosen rather than dirty. Kept low
(6-11%) so the highlight does not swamp the row, especially in dark mode.
From code review:
- Dropped the Google Fonts @import at the top of index.css. Nothing has
used Inter since the switch to Geist, and it was a render-blocking
third-party request on every load - exactly what self-hosting the font
was meant to avoid on air-gapped installs.
- TextInput's autofill override painted background.default, which used
to equal paper and no longer does, so Chrome-autofilled fields showed
a grey block on white cards. Now paper.
- Sidebar background moved from sx to the bgcolor prop (convention rule
1, and it was a new lint warning), and the backdropFilter dropped -
the background is opaque, so the blur only cost a compositing layer.
- Tab no longer bolds the selected label. That is the same reflow the
ToggleButton change removed, and worse here because the indicator
animates toward a target that moves as the label widens.
- Corrected the StatBox gradient comment: contrastText is white in light
mode but near-black in dark, so the constraint is staying near `main`,
not staying dark.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
The coloured stat box ran its gradient from `<palette>.light` to
`<palette>.main`. With Material's stock warning that was #ff9800 ->
#ed6c02, a bright orange. The new warning.main is a dark amber chosen so
the "Paused" chip text clears 4.5:1, and MUI derives `light` from it, so
the gradient became brown mud.
The two roles were never compatible: `main` has to be dark enough for
small text on white, and the same value was being used as a large filled
background. The gradient now runs from lighten(main, 0.12) to main,
which keeps the box in the status colour, restores the depth that the
flat brown lost, and holds white contrastText at 4.64:1 or better at the
lightest point for all four palettes.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
Three fixes, all fallout from giving `divider` a real value:
- The graph-paper texture behind the status counts is drawn with
`divider`. That used to be rgba(0,0,0,.12) and near-invisible; now it
is opaque, so the grid jumped forward and read as the loudest thing on
the page. It is texture, not a rule, so it now takes divider at 40%.
- GradientBox called lighten() on background.paper to build its
gradient. paper is white in light mode and lightening white is a
no-op, so the gradient collapsed flat. Darkens slightly toward the
page ground instead.
- Borders were too faint at #D9E1DD (1.33:1 against paper). Now #C7D2CC
(1.55:1), and dark mode #2C3532 -> #3A4642 (1.73:1), so tables and
cards have a visible edge without turning into hard rules.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
Addresses review findings on the hover-states change.
StatBox's hover was a no-op. `GradientBox` always paints an opaque
`linear-gradient` via the `background` shorthand, which populates
background-image and covers any `backgroundColor` beneath it, so the new hover
was invisible — a regression from the `opacity: 0.95` it replaced. The overlay
now layers onto the same gradient, and moves into `GradientBox` where `bg` is
in scope, behind an `interactive` prop.
The mobile card branch gated its cursor and hover on `onRowClick` alone, so
expandable-row tables below the md breakpoint rendered cards that expand on
click but showed no pointer and no hover — the exact inconsistency this change
set out to fix, reintroduced on mobile. Both branches now share one
`isInteractive` flag.
The row action gear used the same overlay as the row beneath it. Alpha
overlays compose, so it was not invisible as reported, but the resulting delta
was 6 levels — too faint for a nested control. Adds `HOVER.CONTROL` (0.05) and
an `action.controlHover` token for controls nested inside a hoverable row: the
gear and the pagination arrows.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
Clickable surfaces across the app set `cursor: pointer` but gave no visual
response on hover, so rows and cards read as static text.
Two separate causes:
- `DataTable` never passed MUI's opt-in `hover` prop on `TableRow`, which is
`false` by default, so no table in the app shaded on hover. One change here
covers all 16 page tables (Uptime, Incidents, Tags, Team, Notifications,
Maintenance, Proxies, Status pages, Checks, Geo checks, Infrastructure,
PageSpeed and the three Logs tables).
- `IconButton` gates its hover style behind `props => !props.disableRipple`,
and the theme sets `disableRipple: true` globally, so the row action gear
and the pagination arrows lost their background. `MenuItem` is unaffected —
its hover sits on the base style — so action menu items already worked and
are left alone.
Adds a `HOVER.ROW` (0.025) constant and an `action.rowHover` palette token,
defined in both light and dark palettes so the overlay direction is correct on
each surface. Kept separate from MUI's shared `action.hover` so menu items,
which read that token, are not restyled as a side effect.
Also fixes `cursor` on expandable rows, which showed `default` despite being
clickable to expand.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
- StatBox titles now use the eyebrow variant (matches /uptime summary cards)
- BaseChart drops its leading icon block and renders the title via eyebrow so all chart cards share one header style
- Inside the average-response gauge, the qualitative label uses eyebrow with the status colour and the metric uses h1; the gauge track is a neutral disabled-action background so the active arc reads in light and dark