ref(gsAdmin): add responsive CSS for narrow screens#117056
Conversation
- PageHeader: flex-wrap on header and breadcrumbs so long trails and action buttons don't collide on narrow viewports - DetailsContainer: collapse 2-col grid to 1-col below 700px - SortSearchForm: flex-wrap so sort/search controls wrap before overflowing when the sidebar is open on a small screen Refs: table overflow-x fix tracked separately in existing PR Action taken on behalf of rahul. --- [View Session in Sentry](https://sentry.sentry.io/traces/?project=4510944073809921&query=gen_ai.conversation.id%3A%22slack%3AC0B7SMN7ZHD%3A1780797596.852199%22) Co-authored-by: rahul <rahul.chhabria@sentry.io>
Add a collapsible sidebar drawer with a hamburger toggle for mobile viewports (≤768px). On small screens the sidebar slides off-screen by default and animates in when the hamburger button is tapped, with a dark overlay backdrop that closes it on tap. Add a sticky mobile top bar showing the hamburger and inline logo, visible only on mobile. Wrap all ResultTable instances in an overflow-x scroll container so tables scroll horizontally instead of breaking page layout. Align the DetailsContainer two-to-one column breakpoint to 768px to match the layout breakpoint. Co-Authored-By: Claude <noreply@anthropic.com>
- useLocation to detect route changes (back/forward nav) and close the drawer - useEffect to toggle document.body.style.overflow when sidebar is open, preventing the page from scrolling behind the fixed overlay Action taken on behalf of rahul. --- [View Session in Sentry](https://sentry.sentry.io/traces/?project=4510944073809921&query=gen_ai.conversation.id%3A%22slack%3AC0B7SMN7ZHD%3A1780839152.391199%22) Co-authored-by: rahul <rahul.chhabria@sentry.io>
📊 Type Coverage Diff
🔍 3 new type safety issues introduced
Type assertions (
This is informational only and does not block the PR. |
On narrow viewports (≤768px), result tables transform into stacked cards instead of requiring horizontal scrolling. Each row becomes a card with the primary cell (title/name) spanning full width at the top, and remaining cells displayed as label+value pairs in a two- column grid below it. Column labels are extracted from the <th> elements already passed to ResultGrid and injected as data-label attributes onto each <td> via cloneElement, so no changes are needed in individual list views. The pattern applies automatically to all ResultGrid-based pages — Broadcasts, Users, Customers, Promos, and others. Co-Authored-By: Claude <noreply@anthropic.com>
Merge the collapsible sidebar from #117054 alongside the existing mobile drawer. Desktop users can now collapse the sidebar via a double-chevron button, reclaiming horizontal space on dense pages. The preference persists to localStorage. The mobile hamburger drawer is unaffected — it operates independently via the sidebarOpen state. Co-Authored-By: Claude <noreply@anthropic.com>
The expand button was fixed-positioned with no corresponding padding on the content area, causing it to overlap page headings. Introduce --sidebarCollapsedWidth (48px) and use it for both the content padding-left when collapsed and the button's reserved strip width. Co-Authored-By: Claude <noreply@anthropic.com>
…trip A floating fixed button created an awkward gap and overlapped content. Replace it with a thin sidebar strip (same position/border as the full sidebar) that holds the expand icon centered — the standard pattern for collapsible sidebars. Co-Authored-By: Claude <noreply@anthropic.com>
…-typed filter param - layout.tsx: add matchMedia listener to close mobile drawer when viewport widens past 768px, preventing body scroll lock from persisting on desktop - resultTable.tsx: exclude colspan-only rows (loading/error/empty/expansion placeholders) from mobile card grid layout to avoid empty padded strips - resultGrid.tsx: type extractColumnLabel filter param as unknown instead of implicit any to fix type coverage regression Action taken on behalf of rahul. --- [View Session in Sentry](https://sentry.sentry.io/traces/?project=4510944073809921&query=gen_ai.conversation.id%3A%22slack%3AC0B7SMN7ZHD%3A1780860599.277959%22) Co-authored-by: rahul <rahul.chhabria@sentry.io>
Add data-mobile-primary attribute support to ResultTable CSS so grids with a control column (e.g. expand chevron) as the first column can designate the actual primary/title cell. Uses :has() fallback to td:first-of-type for tables that render without ResultGrid. Update ResultGrid.renderResults() to inject data-mobile-primary="true" on the first cell whose column label is non-empty, skipping unnamed control columns like expand buttons. Add data-label attributes to all non-primary td cells in views that use ResultTable directly (userEmails, userEmailLog, billingPlans, invoiceDetails) so mobile card layout shows column headings for secondary cells. Co-Authored-By: Claude <noreply@anthropic.com>
… cells cloneElement only reaches the top-level node returned by columnsForRow. When a row returns a wrapper component (e.g. EditableOption in options.tsx) that renders multiple <td> elements via Fragment, data-label and data-mobile-primary land on the component prop and never reach the DOM cells, leaving secondary column labels blank on mobile. Switch to CSS custom properties (--cl-1, --cl-2, ...) set inline on <tr> in renderResults(). CSS custom properties are inherited by all descendants including ::before pseudo-elements, so they work regardless of how deeply the <td> is nested inside a wrapper component. Replace the single secondary-cell content: attr(data-label) rule with per-nth-of-type rules using content: var(--cl-N, attr(data-label)). The attr(data-label) fallback preserves correct behavior for tables rendered without ResultGrid (userEmails, billingPlans, invoiceDetails) where no CSS vars are set on <tr>. Co-Authored-By: Claude <noreply@anthropic.com>
| const TableScrollWrapper = styled('div')` | ||
| overflow-x: auto; | ||
| -webkit-overflow-scrolling: touch; | ||
|
|
||
| @media (max-width: 768px) { | ||
| overflow-x: visible; | ||
| } | ||
| `; |
There was a problem hiding this comment.
Would be nice to use our Container component for this with it's media query capabilities
There was a problem hiding this comment.
Done — replaced TableScrollWrapper with <Container overflowX={{'2xs': 'visible', sm: 'auto'}}> using the responsive prop.
| const MainArea = styled('div')` | ||
| display: flex; | ||
| flex-direction: column; | ||
| min-width: 0; | ||
| `; |
There was a problem hiding this comment.
Done — replaced MainArea with an inline <Flex direction="column" minWidth={0}>.
| const SidebarHeader = styled('div')` | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| gap: ${p => p.theme.space.md}; | ||
| `; |
There was a problem hiding this comment.
Done — replaced SidebarHeader with an inline <Flex align="center" justify="between" gap="md">.
| const CollapsedSidebar = styled('section')<{isCollapsed: boolean}>` | ||
| display: none; | ||
|
|
||
| @media (min-width: 769px) { | ||
| display: ${p => (p.isCollapsed ? 'flex' : 'none')}; | ||
| position: fixed; | ||
| top: 0; | ||
| left: 0; | ||
| bottom: 0; | ||
| width: var(--sidebarCollapsedWidth); | ||
| flex-direction: column; | ||
| align-items: center; | ||
| padding-top: ${p => p.theme.space['2xl']}; | ||
| background: ${p => p.theme.tokens.background.primary}; | ||
| border-right: 1px solid ${p => p.theme.tokens.border.primary}; | ||
| z-index: 100; | ||
| } | ||
| `; |
There was a problem hiding this comment.
Can also be using Flex here
There was a problem hiding this comment.
Done — converted CollapsedSidebar to styled(Flex) and moved flex-direction: column / align-items: center to direction="column" align="center" props.
| </SidebarHeader> | ||
| <Navigation> | ||
| <NavLink to="/_admin/" index> | ||
| <NavLink to="/_admin/" index onClick={closeSidebar}> |
There was a problem hiding this comment.
I might prefer putting the onClick handler on the Navigation element instead and in the onClick check if a anchor was the target so we can close the sidebar, instead of duplicating the onClick to every nav link
There was a problem hiding this comment.
Done — moved the handler to <Navigation onClick> with an (e.target as HTMLElement).closest('a') check, and removed the per-link onClick props.
…andler Replace hand-rolled styled flex wrappers (SidebarHeader, MainArea) with inline Flex components, convert CollapsedSidebar to extend styled(Flex) so flex props are expressed via component props rather than CSS. Replace TableScrollWrapper with Container using responsive overflowX prop. Consolidate the per-NavLink closeSidebar onClick into a single delegated handler on Navigation that checks for an anchor target. Co-Authored-By: Claude <noreply@anthropic.com>
…rapper The scraps sm breakpoint is 800px, but the admin layout shifts to desktop at 769px. Using the responsive Container prop introduced a 769–799px window where wide tables could overflow with no horizontal scroll container. Switch to styled(Container) with an explicit max-width: 768px media query to match the admin layout breakpoint exactly. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d2bc9b3. Configure here.
…ent inert Sidebar was hiding with transform only, keeping its links in the keyboard tab sequence when off-screen. Add visibility: hidden with a 0.2s delay so it disappears from the tab order after the slide animation completes. Also set inert on the main content area when the mobile drawer is open, so keyboard focus cannot reach page content behind the overlay. Co-Authored-By: Claude <noreply@anthropic.com>
## What Four responsive improvements to make `_admin` less broken at narrow viewport widths. **`pageHeader.tsx`** — Add `flex-wrap: wrap` + `gap` to the outer `PageHeader` flex container and to `Breadcrumbs`. Prevents long breadcrumb trails and action buttons from colliding or escaping the viewport at narrow widths. **`detailsContainer.tsx`** — Add a `@media (max-width: 768px)` breakpoint to collapse the fixed `1fr 1fr` grid to a single column. Keeps label/value pairs readable on smaller screens. **`resultGrid.tsx`** — Add `flex-wrap: wrap` to `SortSearchForm` and wrap `ResultTable` in a `TableScrollWrapper` with `overflow-x: auto`. Sort selector, search input, and table rows now scroll/wrap instead of overflowing when horizontal space is tight. **`layout.tsx`** — Add a collapsible mobile sidebar with a hamburger toggle, slide-in transition, and dimmed overlay. Route changes (including browser back/forward) close the drawer automatically via `useLocation`. Body scroll is locked while the overlay is open. ## Why `_admin` has no responsive CSS in its core layout or shared components. These are pure CSS and minimal JS state changes; zero new network requests or non-trivial side effects. ## Not included here - Table column hiding — more design judgment needed on which columns to deprioritize Action taken on behalf of rahul. --- [View Session in Sentry](https://sentry.sentry.io/traces/?project=4510944073809921&query=gen_ai.conversation.id%3A%22slack%3AC0B7SMN7ZHD%3A1780839152.391199%22) --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: rahul <rahul.chhabria@sentry.io> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>

What
Four responsive improvements to make
_adminless broken at narrow viewport widths.pageHeader.tsx— Addflex-wrap: wrap+gapto the outerPageHeaderflex container and toBreadcrumbs. Prevents long breadcrumb trails and action buttons from colliding or escaping the viewport at narrow widths.detailsContainer.tsx— Add a@media (max-width: 768px)breakpoint to collapse the fixed1fr 1frgrid to a single column. Keeps label/value pairs readable on smaller screens.resultGrid.tsx— Addflex-wrap: wraptoSortSearchFormand wrapResultTablein aTableScrollWrapperwithoverflow-x: auto. Sort selector, search input, and table rows now scroll/wrap instead of overflowing when horizontal space is tight.layout.tsx— Add a collapsible mobile sidebar with a hamburger toggle, slide-in transition, and dimmed overlay. Route changes (including browser back/forward) close the drawer automatically viauseLocation. Body scroll is locked while the overlay is open.Why
_adminhas no responsive CSS in its core layout or shared components. These are pure CSS and minimal JS state changes; zero new network requests or non-trivial side effects.Not included here
Action taken on behalf of rahul.
View Session in Sentry