Row accents survive the list divider on every row, not just the first

Tailwind 3's divide-<color> rule (`> :not([hidden]) ~ :not([hidden])`,
specificity 0,3,0, plus the dark-mode !important overrides in globals.css)
recolours every child but the first on all sides, so a row's own border
accent only ever showed on the first row.

- Notification dropdown: the purple GA and blue tenant bars are inset
  shadows now, so every such row keeps its bar.
- Geographic performance: the red outlier bar moves from the row to its
  first cell; it was grey on every outlier but the first table row.
- Tenant config report: the heavier separator between notification
  channels and between self-hosted AI clients moves to the cell
  (GROUP_SEPARATOR); the divider had shrunk it to a plain 1px line.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Oliver Kieselbach
2026-10-05 09:33:47 +02:00
parent a26cd5edf7
commit 81d0fe6ed3
3 changed files with 15 additions and 9 deletions
@@ -88,6 +88,10 @@ function formatByKind(kind: RowKind | undefined, value: unknown): string {
// ── Row and section primitives ───────────────────────────────────────────────
// Heavier rule between grouped rows. It sits on the cell: the section tbody's divide utility overrides
// a row's border width and color, while the collapsed table border keeps the wider cell edge.
const GROUP_SEPARATOR = 'border-t-2 border-gray-200 dark:border-gray-600';
interface ConfigRowProps {
label: string;
display: string;
@@ -185,8 +189,8 @@ function ChannelRows({ config }: { config: TenantConfiguration }) {
return (
<>
{channels.map((ch, i) => (
<tr key={ch.id ?? i} className={i > 0 ? 'border-t-2 border-gray-200 dark:border-gray-600' : ''}>
<td colSpan={2} className="p-0">
<tr key={ch.id ?? i}>
<td colSpan={2} className={`p-0 ${i > 0 ? GROUP_SEPARATOR : ''}`}>
<table className="w-full">
<tbody className="divide-y divide-gray-100 dark:divide-gray-700">
<ConfigRow label="Channel" display={`${ch.name || ch.id}${ch.enabled === false ? ' (disabled)' : ''}`} />
@@ -214,8 +218,8 @@ function SelfHostedAiClientRows({ list }: { list: McpClientRegistrationListRespo
<ConfigRow label="Self-hosted AI (platform switch)" display={list.enabled ? 'On' : 'Off'} />
{list.registrations.length === 0 && <ConfigRow label="Self-hosted AI Clients" display="—" />}
{list.registrations.map((r) => (
<tr key={r.registrationId} className="border-t-2 border-gray-200 dark:border-gray-600">
<td colSpan={2} className="p-0">
<tr key={r.registrationId}>
<td colSpan={2} className={`p-0 ${GROUP_SEPARATOR}`}>
<table className="w-full">
<tbody className="divide-y divide-gray-100 dark:divide-gray-700">
<ConfigRow label="Self-hosted AI Client" display={r.name} />
@@ -436,8 +436,8 @@ export default function GeographicPerformancePage() {
key={loc.locationKey}
id={`loc-${loc.locationKey.replace(/[^a-zA-Z0-9]/g, "-")}`}
className={`hover:bg-gray-50 transition-colors cursor-pointer ${
loc.isOutlier ? "border-l-4 border-l-red-400" : ""
} ${selectedLocation === loc.locationKey ? "bg-blue-50" : ""}`}
selectedLocation === loc.locationKey ? "bg-blue-50" : ""
}`}
onClick={() => {
const days = timeRange === "7d" ? 7 : timeRange === "30d" ? 30 : 90;
// Carry the selected tenant into the drill-in so a cross-tenant caller (GA override
@@ -447,7 +447,8 @@ export default function GeographicPerformancePage() {
}}
onMouseEnter={() => setSelectedLocation(loc.locationKey)}
>
<td className="px-4 py-3 text-sm text-gray-900">
{/* Outlier accent on the cell, not the row: the tbody's divide color overrides a row's border-color. */}
<td className={`px-4 py-3 text-sm text-gray-900 ${loc.isOutlier ? "border-l-4 border-l-red-400" : ""}`}>
<div className="flex items-center">
<span className="font-medium">{loc.locationKey}</span>
{loc.isOutlier && (
@@ -298,9 +298,10 @@ export default function Navbar() {
</div>
) : (
<div className="divide-y divide-gray-100">
{/* Row accents are inset shadows, not border-l: the divide color overrides a child's border-color on every row but the first. */}
{/* Tenant-scoped persistent notifications (e.g. hardware rejections) — top */}
{tenantNotifications.map((tn) => (
<div key={`tn-${tn.id}`} className={`px-4 py-3 hover:bg-blue-50/50 dark:hover:bg-blue-900/45 transition-colors border-l-4 border-blue-500 bg-blue-50/30 dark:bg-blue-900/20 ${tn.href ? 'cursor-pointer' : ''}`} onClick={() => { if (tn.href) openNotificationHref(tn.href); }}>
<div key={`tn-${tn.id}`} className={`px-4 py-3 hover:bg-blue-50/50 dark:hover:bg-blue-900/45 transition-colors shadow-[inset_4px_0_0_0] shadow-blue-500 bg-blue-50/30 dark:bg-blue-900/20 ${tn.href ? 'cursor-pointer' : ''}`} onClick={() => { if (tn.href) openNotificationHref(tn.href); }}>
<div className="flex items-start justify-between">
<div className="flex items-start space-x-2.5 flex-1">
<span className="text-lg">{tn.type === 'hardware_rejection' ? '🖥️' : '🔔'}</span>
@@ -333,7 +334,7 @@ export default function Navbar() {
))}
{/* Persistent Global Admin Notifications */}
{visibleGlobal.map((gn) => (
<div key={`ga-${gn.id}`} className={`px-4 py-3 hover:bg-purple-50/50 dark:hover:bg-purple-900/45 transition-colors border-l-4 border-purple-500 bg-purple-50/30 dark:bg-purple-900/20 ${gn.href ? 'cursor-pointer' : ''}`} onClick={() => { if (gn.href) openNotificationHref(gn.href); }}>
<div key={`ga-${gn.id}`} className={`px-4 py-3 hover:bg-purple-50/50 dark:hover:bg-purple-900/45 transition-colors shadow-[inset_4px_0_0_0] shadow-purple-500 bg-purple-50/30 dark:bg-purple-900/20 ${gn.href ? 'cursor-pointer' : ''}`} onClick={() => { if (gn.href) openNotificationHref(gn.href); }}>
<div className="flex items-start justify-between">
<div className="flex items-start space-x-2.5 flex-1">
<span className="text-lg">{gn.type === 'session_report' ? '\uD83D\uDCCB' : '\uD83C\uDF1F'}</span>