Skip to content

Mobile Friendly Process Mapper

Ed Mozley edited this page Aug 31, 2026 · 1 revision

Mobile‑Friendly: Process Mapper

The sixteenth module, and the first where the honest answer is that a phone can read the thing but not build it. Three pages β€” the mapper itself, the step‑types settings screen and the guide. Shipped in #1415‑#1418, mobile.css v124 / mobile.js v49, LAYER 30.

Read Mobile‑Friendly first for the strategy and the one hard rule, and Techniques & Tricks for the catalogue this round draws on.


⭐ Deciding the scope before writing anything

Β§22 exists to split drag into the kind that survives touch and the kind that does not, and this canvas is squarely the second:

process-mapper.js : 11 Γ— mousedown / mousemove / mouseup
                     0 Γ— touchstart / touchmove / pointerdown

A step cannot be moved with a finger. And every per‑step action β€” edit label, add a note, link to a URL, create a connected step β€” lives behind a contextmenu listener, which a touch screen has no way to fire. Between them that is the whole of authoring.

πŸ”‘ Say what the round is for, out loud, before building it. A toolbar whose buttons half‑respond is worse than a screen that is honestly read‑only. The round was scoped to making a map readable: reach the list, open a map, pan around it, read the steps, and manage step types in settings.

⭐ And then it grew, because Ed asked a question and the answer was measured rather than assumed β€” see the selection sheet below. Positioning still needs a mouse; editing what a box says does not.

⭐ And the enabler was already there: .pm-canvas is overflow: auto, so panning is native touch scrolling and needed no JavaScript at all. Measured on a real map: canvas scrollWidth 1546 inside a clientWidth of 358, panned to x=1188, nine steps rendered.


The pre‑flight, which is now five greps

Run before touching anything, and it decided most of the round:

Grep Result
a pre‑existing @media none β€” no repeat of the LMS trap
localStorage none
:hover revealing a control πŸ”΄ one, and it hides the navigation β€” below
the page's own style.display writes 39 β€” anything positional needed care
its own modal class πŸ”΄ .pm-modal / .pm-modal-overlay, invisible to LAYER 3

πŸ”΄ The preference that makes the module unusable on a phone

Process Mapper β†’ Settings β†’ Left panel offers Show on hover: the 260px process list collapses to a 16px strip that expands when the cursor approaches. It is a sensible desktop preference and it is stored per analyst.

On a phone nothing hovers. An analyst who turned it on at their desk arrives to a 16px sliver with no way to open it β€” every process map in the system behind a control a finger cannot operate.

⭐ This is Β§26 crossed with the saved‑desktop‑mode trap that the tickets pop‑out (#762) and the knowledge editor (#1000) both sprang. The remedy is the same in all three: leave the stored preference alone and neutralise its effect at phone width. The class stays on the element and comes back untouched above 768px.

The list becomes a slide‑in sheet either way, because 260px of a 360px screen leaves about 100px of canvas.


πŸ”΄πŸ”΄ The bug this round is worth remembering for

Ed tested the first build and reported it in one sentence:

"when I tap processes on the hamburger menu it flashes for a split second then vanishes"

The attribute flipped, the scrim appeared, and the panel did not move. The reason is a specificity trap that is easy to walk into and gives no warning at all.

The closed rule was written as a selector list so it could catch the hover mode too:

[data-mobile-module="process-mapper"] .pm-sidebar,                              /* (0,2,0) */
[data-mobile-module="process-mapper"] .pm-layout.sidebar-hover .pm-sidebar {    /* (0,4,0) */
    transform: translateX(-100%);
    visibility: hidden;
}
body[data-mobile-module="process-mapper"][data-pm-sidebar="open"] .pm-sidebar { /* (0,3,1) */
    transform: translateX(0);
}

A selector list carries its specificity per selector. (0,3,1) beats the first and loses to the second β€” so the sheet opened for anyone on the default preference and stayed shut for anyone on hover. Ed was on hover, so for him it never worked once.

πŸ”‘ Adding a variant to a CLOSED rule silently raises the bar its OPEN rule has to clear. Mirror the variants on both halves of an open/closed pair, or the pair only half works β€” and it will be the less common configuration that breaks, which is the hardest kind to notice.

Mirrored, the open rule's second selector is (0,5,1) and wins.

⚠️ Note what did not catch this: the measurements said data-pm-sidebar="open" and reported the panel's box. The box was correct β€” it was still at left: -310. A state assertion has to check the thing the state is for, not that the state was set.


πŸ”΄ Two modules, one pm- prefix

With the sheet finally opening, a screenshot showed the process names cut to a letter or two β€” "S…", "I…", "A…" β€” with + New and the search box side by side.

.pm-sidebar is Problem Management's class as well. In LAYER 22 pm- means Problem Management; in LAYER 30 it means Process Mapper. And LAYER 22's block is unscoped:

.pm-sidebar { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 10px 12px; … }

So one module's entire layout mode was arranging another module's panel.

⭐ Β§15 in its sharpest form so far. It has always warned about a shared component class; this is two modules that independently picked the same three‑letter prefix, which no amount of reading one module's CSS would reveal.

Restated in this layer rather than by scoping LAYER 22, because Problem Management's pages carry no module marker to scope it to β€” giving them one is a change to a shipped module and belongs in its own round, not as a side effect of opening a new one. Recorded as owed.

⚠️ Again: only the screenshot showed it. The panel measured 310px wide, at left: 0, visible, with six items in it, and every one of those numbers was true.


⭐ The question that widened the round

Ed, part‑way through:

"push back if this is a bad idea but do you think it would be good if we had a little bar at the bottom which shows the currently selected item (box, lane, group etc.) with a little edit button"

The instinct was right and the answer was better than the question, because the module already had most of it β€” which is only knowable by driving a tap and watching what happens:

tap a step β†’ mousedown fires (browsers synthesise the compatibility mouse
             events after a tap), step gains .selected, and
             .pm-detail-panel opens with "Step Details" in it

Steps, groups, lanes, connectors and annotations each already have their own detail body and translated title. So the answer was not a new bar with an Edit button β€” that would have put two taps where one already worked, and needed a new component and new strings to do it. It was to take the module's own 320px right‑hand column and move it to the bottom.

πŸ”‘ Before building what somebody asks for, check whether the module already does it badly. A feature request is often a layout complaint wearing a costume.

⭐ The half of the request that shaped the design was the half not about the bar: shows the currently selected item. A sheet that covers the map tells you what you are editing and hides which thing it is. So it takes 58dvh, the canvas is capped to the strip above it, and the selected item is scrolled into that strip. Measured: the step moved from y=411 (behind the sheet) to y=186, fully above it.

πŸ”΄ Two measurements that looked like failures

The scroll did nothing, and the reason was one line of output:

canvasScroll = 0,0 (max 1186,0)

The canvas scrolls sideways across a wide map, but its content is no taller than its box β€” so scrollTop has a maximum of zero. There was nowhere to scroll to. padding-bottom on the scroller did not buy the range either (its steps are absolutely positioned). Capping .pm-canvas-wrap to 42dvh does, and it gives the honest layout as well: map above, details below, rather than a sheet floating over a canvas that still believes it is full height.

And then it still read as 0,0 β€” because MutationObserver callbacks are microtasks, and the probe was reading synchronously in the same task. After a tick: 0,225, exactly the arithmetic. The third time this rollout that check what you actually measured has been the answer rather than a code change.


The rest of the layer

  • 30a β€” the shell. .pm-layout is height: calc(100vh - 48px), the fourth module in a row (21, 27, 29, this). Handed back to LAYER 2's 100dvh flex column, with min-height: 0 so Β§28 does not bite.
  • 30c β€” the toolbar, measured at 749px inside 360. It scrolls sideways in its own box rather than being hidden: some of it (export, the map picker) works by tap perfectly well, and a control you can see and not use is more honest than one that has vanished.
  • 30d β€” .pm-modal promoted to a full‑screen sheet. Fourth module to roll its own modal class, and as always nothing in the measurements complains: a 90%‑wide centred box is perfectly contained.
  • 30e β€” settings, six columns (Shape Β· Name Β· Colour Β· Order Β· Active Β· Actions) as a card feed, with Β§21 labels on colour, order and active. The shape swatch rides beside the name rather than taking a line.

⭐ Zero new locale keys. The sheet button reads process-mapper.nav.processes β€” the module's own name for that list, already translated wherever the namespace exists.


How it was verified

  • All three pages contained at 360px, from docScrollW=837.
  • Each has a scroller that reaches its end (Β§28, now part of the routine rather than an afterthought): help 10,952px, settings 344px, and the mapper's canvas panned to x=1188 of 1546.
  • The sheet driven open in BOTH sidebar modes β€” the default and the hover preference β€” landing at left: 0, 310px, visible, with elementFromPoint at its centre returning the panel rather than the scrim, six processes listed, and tapping one closing it.
  • Desktop control at 1100px: the button is display: none, the body attribute is absent, the toolbar's overflow-x is back to visible, settings is a real <table> with table-header-group, and zero data-mobile-label is stamped anywhere.
  • πŸ”‘ Problem Management measured as a positive control at 360px and 1100px, because this round writes rules against a class it shares. Unchanged at both: static grid w=360 and static block w=250.
  • The Β§25 audit: 83 files, +166/βˆ’166, every line a ?v= bump; the module's own diff is three pages Γ— three lines of opt‑in.

⬜ Known and not fixed

  • Authoring on a phone. Deliberate, and explained at the top.
  • Problem Management's LAYER 22 block is unscoped. It is wrong in principle even though nothing is broken today β€” the next module whose classes start pm- will hit it too. It needs a module marker on Problem Management's pages, which is its own small round.

Related

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally