Aman-Mittal opened a new pull request, #443: URL: https://github.com/apache/fineract-backoffice-ui/pull/443
The responsive track from #436. ## Why Five media queries across 289 feature components, one of them `prefers-reduced-motion`. On a phone the sidebar was a fixed 240px column taking two thirds of the width, tables scrolled sideways with everything but the first column out of sight, and `100vh` put the bottom of every page under the browser chrome. One shell, two shapes. Above 768px nothing changes. At or below, the sidebar becomes a modal drawer, tables become cards, and the header sheds what does not fit. | Sign in | Dashboard | Drawer | | --- | --- | --- | | <img src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/01-login.png" width="240"> | <img src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/02-dashboard.png" width="240"> | <img src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/03-drawer.png" width="240"> | The client list, which was a sideways scroll and is now one card per row — light and dark: | | | | --- | --- | | <img src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/04-table-cards.png" width="240"> | <img src="https://raw.githubusercontent.com/Aman-Mittal/fineract-backoffice-ui/assets/pr-437/mobile/05-table-cards-dark.png" width="240"> | *(Captured from inside the passing Pixel 7 spec against the mocked backend, so they are reproducible and cannot drift from what the tests assert. Hosted on a fork branch so no binaries enter the Apache repository.)* ## One breakpoint, and why it is in TypeScript `MOBILE_BREAKPOINT_PX` in the new `ViewportService` is the only breakpoint the shell has. It exists in code rather than only in CSS for a specific reason: CSS can move the sidebar off-canvas, but it cannot tell the component that the sidebar is now a **dialog** — that it takes `role="dialog"`, takes focus, closes on Escape, and must be `inert` while hidden so it is not a long run of invisible tab stops. Once that is in code it has to be the same number as the stylesheet's, which is what `scripts/check-responsive.mjs` enforces. ## What changes below 768px **The drawer.** `SidebarService` keeps `isCollapsed` and `isDrawerOpen` as separate state deliberately — collapsing on a desktop should not leave a phone holding an overlay nobody opened, and rotating back should restore the collapsed column rather than an open panel. It closes on the backdrop, on Escape, on its own control, and on navigating. That last one matters: otherwise the page the user asked for renders behind the menu they asked with. **Tables become cards.** Driven by a `data-label` attribute and CSS, not a second template, so a column added to `columns()` appears in both layouts and cannot be added to one only. `DataTableComponent` backs **107 of the 289** feature components, which is why this single change carries most of the benefit. **`--content-padding`**, which #437 published on the branding allow-list without ever defining — a deployment could set it and nothing happened. It exists now, steps down on a narrow viewport, and is still settable. **The header** drops business date, render time, the guide, the username and the language selector, all reachable elsewhere. Navigation, search and sign-out stay, each at 44px. ## Three bugs found doing this, none visible in a diff - **The header's narrow rules were declared before its base rules.** Media queries add no specificity, so every one lost on source order and *nothing was actually hidden* — the guide button was still rendering at 87×34. Found by measuring the rendered buttons, not by reading. - **Focus never entered the drawer.** The effect ran before Angular removed `inert`, and focusing an inert subtree is silently a no-op. Deferred to `afterNextRender`. - **`login.component.ts` had `min-height: 100vh`**, pre-dating this work. The new check found it. ## One worth a reviewer's eye The card rules are in `styles/_common.scss`, not in `DataTableComponent`. Angular scopes component styles with an `_ngcontent` attribute, and the `<tbody>` cdk-table renders into is created by the HTML parser rather than the template — so a scoped `tbody` selector never matches it. Left as `table-row-group` it shrank to its content: the cards measured **227px inside a 356px table**. The screenshot looked plausible; only measuring the layout chain showed it. `_common.scss` already owns the base `table[cdk-table]` styling for the same encapsulation reason. ## What enforces it **`npm run check:responsive`** — new, wired into `ci.yml`. Three rules with one correct answer each: a media query that is not the shell breakpoint (outside a documented allow-list), `height: 100vh` anywhere, and an unbounded fixed width above 320px. A fixed width paired with a `max-width` in the same rule is fine and is not flagged — the first version of this check reported `guidance-tour` as a false positive, and a check that cries wolf gets muted rather than fixed. I verified it fails on each violation class rather than trusting a green run. **`E2E (mobile viewport)`** — new Pixel 7 job in `e2e.yml`, 11 cases covering what a regex cannot see: that the drawer is genuinely modal, that focus moves in, that `inert` applies while closed, that a tap dismisses it, that the page does not scroll sideways, that the shell is exactly as tall as the viewport, and that every header control clears 44px. One case asserts the *project's own viewport* is under the breakpoint, so widening the device cannot silently turn the suite into a desktop test that still passes. Branch protection should require `E2E (mobile viewport)`; the note at the top of `e2e.yml` has been updated to say so. ## Verification | | | | - | - | | Karma | 826 passed | | Vitest | 546 passed | | Playwright `mobile` | 11 passed (new) | | Playwright `mocked` | 338 passed, 1 failed | | lint / format / typecheck (app + e2e) | clean | | `check:responsive`, `check:nav-ids`, `check:branding-path`, `check:reference-downstream` | pass | | `i18n:check`, `check:icons`, `check:a11y-names` | pass | The one `mocked` failure is `accessibility.spec.ts › client list`, which **passes in 1.2m when run alone** and also fails on `main` without this branch — a 30s-timeout flake under full-suite load, not a regression. CI shards that suite, so it has more headroom there. ## Not included `primaryColumns` on `DataTableComponent` — collapsing a card to its most important fields with the rest behind a disclosure. Worth having, not needed for the layout to be usable, and it would add an input to a component 107 screens depend on. Better as its own change. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
