Skip to content

Issue 101 Source Code Editor Opened Behind The Modal

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

The Source code editor opened behind the window that asked for it (issue #101)

Editing an email template, pressing the <> button on the toolbar opened the Source Code box underneath the template window. You could see it through the dimmed background, read the HTML in it, and do absolutely nothing with it.

Reported in issue #101 by mbsouth, who diagnosed it correctly in the report: "The ticket pop-up has a z-index of 2000, but the source code pop-up only has z-index: 1".

Fixed in 950fbd73, released as update #1197.


1. What you saw

Tickets β†’ Settings β†’ Email templates, edit a template, press <>.

The Source Code panel appeared at the left of the screen, greyed out behind the template window's overlay. Its title was legible, its contents were legible, and it would not accept a click or a keystroke. Cancelling the template window revealed it sitting there perfectly happily underneath.

That is a particular kind of infuriating: the feature clearly worked, the box clearly opened, and it was clearly unusable.


2. The mechanism

FreeITSM's rich text editing is TinyMCE. When TinyMCE opens a dialog, it does not put it inside the editor, or inside whatever opened the editor. It appends it to the very end of <body>, into a container of its own called the sink:

<body>
    …the whole application…
    <div class="modal" id="templateModal">…the template window, with the editor inside…</div>
    <div class="tox-tinymce-aux">…every dialog and menu TinyMCE opens…</div>
</body>

That sink carries a fixed depth chosen by TinyMCE, not by us:

/* TinyMCE's own skin */
.tox-tinymce-aux { z-index: 1300 }

FreeITSM's shared pop-up window is deeper:

/* assets/css/inbox.css */
.modal { z-index: 2000 }

1300 < 2000, so the dialog rendered behind. Being later in the document does not help: an explicit z-index beats document order, and TinyMCE had picked a smaller one.

Nothing was misconfigured, and no page had done anything wrong. Two components had each chosen a sensible number in isolation and nobody had ever put them in the same room.

About the "z-index: 1" in the report

The reporter's diagnosis was right in every way that mattered β€” a dialog stacked below a 2000 modal. The specific 1 they read in the inspector was one of TinyMCE's inner elements, .tox-dialog-wrap__backdrop, which really is z-index: 1 relative to its own parent. The number that actually decided the outcome was the sink's 1300 further up the tree.


3. It was never only the Source code box

The sink holds everything TinyMCE pops up. So the trap applied to:

Dialog Where it appears
Source code the reported one
Insert/edit link the toolbar of every rich-text editor in FreeITSM
Insert table reply templates
Special character reply templates

and to both editors on the settings page, not just the reported one:

Editor Its window
#templateBody β€” email templates <div class="modal" id="templateModal">
#replyTemplateBody β€” reply templates <div class="modal" id="replyTemplateModal">

The condition is simply a rich-text editor inside a pop-up. Anything meeting it was affected, on every dialog it offered.


4. The fix

One rule, placed directly above the .modal rule it has to clear β€” because the two numbers only make sense read together:

/* assets/css/inbox.css */
body .tox-tinymce-aux {
    z-index: 2500;
}

Why 2500. It has to clear every container that can hold an editor, and stay under the things that should still cover a dialog:

Layer Depth
.modal, and the two mobile full-screen editors 2000 must be cleared
TinyMCE sink 2500 ← the fix
.search-modal 3000 should still cover a dialog
.toast 10000 should still be readable over one

Why body is there. It is a specificity bump, not decoration. TinyMCE injects its skin stylesheet at runtime, so it lands after inbox.css and would win any tie on document order. Out-specifying it avoids reaching for !important.

Why one selector is enough. Everything TinyMCE opens lives inside that sink, including the dialog wrapper with its own z-index: 1100. Raising the sink moves the whole stacking context, and the numbers inside it become relative to the new position. Nothing else needs touching β€” and any editor placed inside a pop-up in future is covered without anyone having to remember this page exists.

The fix also had to reach the browser

assets/css/inbox.css was referenced 148 times with no cache-buster at all, plus three different versions elsewhere (v22, v37, v60). A CSS fix that browsers do not refetch is not a fix. All 165 references are now ?v=61.


5. πŸ“ The files involved

File What changed
assets/css/inbox.css The one rule, above .modal.
165 pages inbox.css β†’ ?v=61, so the rule actually arrives.

πŸ—„οΈ Deliberately not changed

Why
The TinyMCE skin files Vendor code. Editing it would be undone by the next upgrade.
.tox-dialog-wrap The skin styles it as .tox .tox-dialog-wrap, which outranks anything reasonable we could write β€” and it is inside the sink, so it does not need to be targeted.

6. How it was verified

The page was driven for real in a headless browser at a desktop viewport, opening the actual template window and calling the actual toolbar command.

The bug was reproduced first. Before any change, the topmost element at the centre of the Source Code dialog was tox-edit-area__iframe β€” the editor behind it. That is the proof the dialog was unreachable.

The assertion is elementFromPoint, not the computed z-index. What matters is what a person can actually click. A computed number can look correct while a stacking context somewhere above makes it irrelevant.

After the fix, the topmost element at that same point is tox-textarea β€” inside the dialog. Checked on Source code and Insert link on the email template window, and Source code on the reply template window.

A negative control on the layer stack confirms the fix did not overshoot: the dialog clears .modal, the search window still covers the dialog, and a toast is still readable over it.

Two things the live run corrected

Both had been read straight out of the CSS, and both were wrong:

  1. The sink's effective depth is 1300, not the 1201 visible in one skin file β€” there are several skins carrying different values, and only the loaded one counts.
  2. .tox-dialog-wrap appeared to sit outside the sink, which would have meant a second selector was needed. Asking the live DOM (dialog.closest('.tox-dialog-wrap')) showed it is inside. The second selector was therefore both unnecessary and inert, and was removed rather than left in beside a comment claiming it was load-bearing.

7. What this means for you

Source code, Insert link, Insert table and Special character now open in front of the window that asked for them, on email templates and reply templates alike.

If you had previously worked around this by writing template HTML somewhere else and pasting it in, you no longer need to.


Related pages

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally