-
Notifications
You must be signed in to change notification settings - Fork 17
Mobile Friendly Change Management
Change Management is the eighth module made mobileβfriendly. Same hard rule: one @media (max-width: 768px) block, desktop byteβidentical.
π§° Codeβlevel catalogue of the techniques: Mobile: Techniques & Tricks.
Shipped as #1184, commit 9d8a16d6. LAYER 23.
| Page | State |
|---|---|
change-management/ β list, detail, editor |
Done β sidebar β header strip, cards stacked, pane hiding (LAYER 23) |
change-management/approvals.php |
Done β sidebar becomes a single scrolling filter row |
change-management/table.php |
Done by opting in β inherits .dt-wrap from the Assets round |
change-management/calendar.php |
Done by opting in β inherits the Calendar layer |
change-management/settings/ |
Done by opting in β the shared settings rules, plus the body marker |
change-management/help.php |
Done by opting in β inherits 16h |
π Half the module cost nothing. That is the return on building earlier layers around shared class names (
.dt-page,.calendar-grid,.container) rather than page ids β a new module that reuses a component inherits its mobile treatment for free.
viewport = 360 docScrollW = 1286 β on ALL SIX pages
index.php sidebar 280 β list pane 80px
approvals.php sidebar 260 β list pane 100px
1286px on a 360px screen, and an 80px column of content β the narrowest yet recorded here. The 1286 was the shared header, so opting in fixed it outright; the 80px was not touched by that at all.
It mirrors LAYER 22 closely, because the module is built the same way: a fixed sidebar beside a flexible pane, plus a list/detail swap the module already does in its own JS.
The sidebar holds three sections: Search, Status filters, New change β in that order. Making the filters span both columns should have left the two buttons sharing the top line. It didn't, because the filters sit between them in the DOM, so the grid placed Search on row 1, filters on row 2, and New change on row 3.
.changes-sidebar .sidebar-section:has(.status-filter-list) {
grid-column: 1 / -1;
order: 1; /* β move the filters LAST */
}order moves the filters after both buttons, which puts the two actions side by side and reads better anyway: do the thing, then narrow what you are looking at.
Its filters are direct children of the sidebar, with no list element around them, so there is nothing to turn into a strip. The sidebar itself becomes the scrolling row, with the "Filter" heading riding along at the start rather than being hidden β it costs one word of scroll and it is the only thing naming what the chips are.
var showView = window.showView;
window.showView = function (view) {
var out = showView.apply(this, arguments);
setPane(view === 'detail' || view === 'editor' ? view : 'list');
return out;
};A cleaner hook than Problem Management's two wrappers: showView('list' | 'detail' | 'editor') is a single synchronous function, so there is no promise to wait on and no failure path to guard.
Every containment check passed β docScrollW === innerWidth, zero uncontained elements β while the change list looked like this: priority and status pills marching off the rightβhand edge, and the assignee colliding with the date.
.change-card { display: flex; align-items: center; gap: 16px; } /* ref | title+meta | badges */
.change-card-badges { flex-shrink: 0; } /* β four badges that will not give way */Four badges that refuse to shrink make the card wider than the screen whatever the panes do, and because an ancestor scrolls, the document never widens. The page was contained; the content was off the edge of the box it sat in.
.change-card { flex-direction: column; align-items: stretch; gap: 6px; }
.change-card-title { white-space: normal; overflow: visible; } /* two lines beat an ellipsis here */
.change-card-meta { flex-wrap: wrap; gap: 2px 12px; }
.change-card-badges { flex-wrap: wrap; flex-shrink: 1; }π
flex-shrink: 0is the single most common cause of a row that will not fit a phone. It is correct on desktop β it stops badges being squashed into illegibility β and it is exactly wrong at 360px, where something has to give. Grep a module's CSS for it before wondering why a row overflows.
The first sweep reported table.php as having a table overflowing its parent: 938 > 326. It wasn't a bug. The parent is the scroller β .dt-wrap with overflow-x: auto β so scrollWidth > clientWidth is precisely what a working scroller looks like.
// A table is only WRONG if it is wider than its box AND no ancestor scrolls.
let scrolled = false;
for (let p = t; p; p = p.parentElement) {
const ox = getComputedStyle(p).overflowX;
if (ox === 'auto' || ox === 'scroll') { scrolled = true; break; }
}Reβchecked against asset-management/table.php as a knownβgood control: identical readings. When a probe reports a fault on shipped code that has been through a device pass, suspect the probe first.
| Check | Result |
|---|---|
| All six pages at 360px | β contained, zero uncontained, no unscrolled table spills |
| Pane cycle: list β detail β editor β back | β
sidebar grid β none β none β grid
|
| Desktop @ 1200px and 1400px |
body block/row, sidebar 280px, cards still flex-direction: row
|
data-cm-pane on desktop |
β never appears, at any point in the cycle |
mobile.js parse check |
β with a negative control |
| Looked at, not only measured | β β and it is the only thing that caught the card bug |
- Preβexisting, not introduced here: at a 1200px desktop window this module's shared header needs 1282px, so the page scrolls sideways until roughly 1290. It is fine at 1400. That is a desktop header issue β this module carries more nav items than the others β and is deliberately left alone.
Ed, viewing a change on a phone. The vertical scroll had been working all along; the bar he could see was a horizontal one belonging to .changes-main, and dragging it moved almost nothing.
Written up in full as Techniques Β§12 β the short version is that .changes-main sets only overflow-y: auto, and per spec that makes overflow-x compute to auto too. It was silently a horizontal scroller, and every containment check passed because an ancestor scroller absorbed the overflow.
The only signal without looking at the page: the pane's box height was 650px and its clientHeight 635px. That 15px is a horizontal scrollbar.
Three things were too wide, all found by measuring against the pane's content edge:
.risk-matrix-wrapper |
a flex row β 140px info panel + 30px gap + 256px grid = 426px in a 336px pane. Stacked. |
.change-detail-header |
space-between with a nowrap action group. The buttons ran past the edge, and once they wrapped, Back sat centred against a two-row block and read as though Delete were lying on top of it. Now packs from the left and wraps as one group. |
.linked-incidents-table |
squeezed a prose subject. Card feed by Β§11 β and it is headerless already, so reading order was carrying the meaning anyway. |
I first matched the sticky header's -30px full-bleed to .changes-main's new 12px padding. Wrong box β its parent is .change-detail-content, whose 30px padding is what the -30px cancels. Setting -12px against a still-30px padding un-bled it and made the overflow worse, which is how the error announced itself.
Result: horizontal scrollbar 15px β 0px, scrollWidth === clientWidth, zero elements past the content edge, vertical scroll unaffected. Desktop at 1400px unchanged on every rule touched.
- MobileβFriendly β the overview and the rollout state
- Mobile: Techniques & Tricks β the codeβlevel catalogue
- Mobile: Problem Management β the layer this one mirrors
- Change Management β the module itself
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- π Date & Time Formats
- Theming & Dark Mode
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
-
MobileβFriendly
- β³ π« Mobile: Tickets
- β³ π» Mobile: Assets
- β³ π Mobile: Calendar
- β³ π Mobile: Knowledge
- β³ π¦ Mobile: Service Status
- β³ πΌ Mobile: Watchtower
- β³ π§© Mobile: Problem Management
- β³ π Mobile: Change Management
- β³ πΏ Mobile: Software
- β³ β Mobile: Tasks
- β³ π§° Mobile: Techniques & Tricks
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ π Ticket notes: internal or shared
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ ποΈ The folder pane
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- β³ π Scheduled work in your own calendar
- Problem Management
- Tasks
- Assets
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
- Forms
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)