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
Tabs.tsx hardcoded fontSize: 14, so the tab bar sat a pixel above the
13px base the rest of the chrome uses. Now typographyLevels.m, which
also satisfies the no-hardcoded-literals rule.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
The palette declared a brand green and six chart hues but never defined
the neutrals, so MUI's defaults filled the gap: body text at 60% black,
dividers at 12%, and background.default equal to background.paper. That
is the "washed out" everything sat on - table cells render through
text.secondary, and cards had no ground to sit on.
- Opaque neutral ramp, biased slightly green so it sits with the brand.
background.default is now off-white so paper separates from the page.
- Semantic success/error/warning derived from the brand instead of
Material's stock red/green/orange. Only `main` is set: `light` is
consumed as a saturated colour by status dots, histogram bars and
heatmap cells, so MUI must keep deriving it.
- Dark mode gets the same treatment; it defined background and secondary
but its text and divider were still MUI defaults.
- Geist Variable, self-hosted via @fontsource-variable so air-gapped
installs keep the typeface. Base size stays at 13px.
- h1 700 / h2 600 against 400 body, and table headers 500 -> 600. Two
weights was why everything read at the same visual weight.
All contrast pairs pass WCAG AA in both themes (lowest 5.98:1).
No component markup changed.
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
Two small pre-existing bugs in the infrastructure gauges.
Arc overdraw. Gauge computed strokeLength from the raw `progress` prop
while the label and the fill colour both used `progressWithinRange`,
the value clamped to 0-100. A reading above 100% therefore swept the
arc past a full circle - the offset (circumference - strokeLength)
went negative - while the label still read "100.0%". A negative
reading drew the arc backwards for the same reason. strokeLength now
uses the clamped value, so out-of-range input stops at exactly one
turn and matches what the label and colour already showed. Values in
range are unaffected.
The effect that animates the arc also listed `progress` in its
dependency array while only reading `strokeLength`, so it re-ran the
100ms animation on changes that could not alter the arc (105% to 110%,
say). Now that strokeLength is derived from the clamped value, the
stale dependency is dropped.
Disk key fallback. `disk?.device || 0 + idx` parses as
`disk?.device || (0 + idx)` because + binds tighter than ||, so the
fallback was just `idx` and the leading 0 did nothing. It happened to
produce unique keys, but by accident rather than intent, and a disk
whose device is the empty string collided with index 0. Replaced with
a `disk-${idx}` template so the intent is explicit.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd
- Adjust `alignItems` to `stretch` and add `maxWidth` (260) to Gauge components in Infrastructure Details page.
- Update German translations for memory titles, temperatures, and labels in locales file.
Adds explicit 'align' support to Table headers to override strict index-based alignment. Restores exact center-alignment spacer boxes to Uptime and Infrastructure tables to match develop branch styling.
Fixes#3602
- Mounts selection checkboxes in UptimeMonitorsTable and MonitorsTable
- Enhances global Table.tsx to cleanly render checkboxes on mobile cards via mobileLabel
- Injects BulkActionsBar into main list views
Fixes#3602
Dialog component:
- Title bar gets a subtle background tint + bottom border. Title is
variant=h2 weight=600. Description tucks under the title with
variant=body1 secondary color (replaces MUI's DialogContentText).
- Action bar gets a matching subtle tint + top border. Cancel is now
outlined (was contained-error, which made every modal's Cancel button
look destructive). Confirm keeps its color (primary by default,
caller can pass error for destructive flows).
- Content area sits between with its own breathing room baked in, so
callers no longer need mt={theme.spacing(...)} workarounds.
- The cancelColor prop is removed; sites that genuinely need a
destructive Cancel can use confirmColor='error' on the Confirm button
instead.
NoticeBanner design-element:
- Extracted from the inline alert pattern in PageSpeedKeyPriorityFallback
into a reusable component with severity=info|warning|error.
- Refactored PageSpeedKeyPriorityFallback to use it.
DialogResolution.tsx: drop the now-removed cancelColor prop and the
mt-Box wrapper that's no longer needed (Dialog provides the spacing).
The game monitor type relies on each game's native query protocol — many
require a config file change on the game server (e.g. Minecraft Java
needs `enable-query=true` in `server.properties`). Without that, the
monitor silently fails to reach the server.
Append a sentence to the game-type general settings description that:
- tells the user some games need server-side queries enabled
- points them to the GameDig supported-games list for protocol details
Implementation:
- Use <Trans> with a `gamedigLink` slot so the link renders inline
- Loosen ConfigBox `subtitle` from `string` to `React.ReactNode` so the
subtitle can host the <Trans> output
- Update all 16 locale files. Six (de, es, ru, th, zh-CN, zh-TW) get a
full translation of the new sentence; nine empty ones (ar, cs, fi, fr,
ja, pt-BR, tr, uk, vi) fall back to the English string per the locale
gap policy
- BaseAuthPage now renders a left form column and a right brand panel with eyebrow / tagline / description copy and a chess-board pattern over the brand green gradient
- Move the language selector and theme toggle to the page's top-right inside the form column so they read clearly in light and dark
- Every auth page (login, register, recovery, set new password) inherits the new layout
- Add brandPanel translations across all 16 locales; phrase the tagline as "your servers" instead of "stack" so it translates cleanly
- Default status-page colour now uses the brand green instead of a royal-blue hex
- PageSpeed API-key alert pulls from theme.palette.warning + alpha for both modes instead of hand-rolled amber values
- Settings JSON preview block uses action.hover instead of the hand-rolled #1e1e1e/#f5f5f5 pair
- Drop the redundant #E0E0E0 thumb override on SwitchComponent so the theme MuiSwitch override actually wins
- 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
The login/register form used percentage widths that collapsed to ~15% on wide screens, wrapping the "Forgot password" / "Reset password" links onto two lines. Switch to a fixed 400px max-width that just shrinks below it on small screens.
Add a fontFamilyMonospace token to theme.typography (ui-monospace stack with sensible fallbacks) and route the four ad-hoc fontFamily="monospace" call sites through it so the project has one source of truth.
- Tooltip backgrounds now pull from the theme's grey palette instead of a hand-rolled blue gradient (visible bug in dark mode)
- Toast progress bars use theme.palette.primary.main so they match the rest of the brand
- Introduce a reusable eyebrow Typography variant in the theme (13px, 500 weight, uppercase, 0.08em tracking) for section headings
- Apply it to ConfigBox titles in /settings and to "Job queue" / "Failed jobs" in /logs
- Drop the page header weight from 600 to 400 with tighter tracking so titles read lighter and more refined