Bug 1498115 - Part 2: Add badges to show the flex-direction and flex-wrap properties of the flex container. r=pbro
☠☠ backed out by 4228a1200fdc ☠ ☠
authorGabriel Luong <gabriel.luong@gmail.com>
Thu, 15 Nov 2018 10:02:44 -0500
changeset 503024 ad62c3439651910289415eb6b1c23b2173e66a10
parent 503023 06b4d9761d46e8e9d56589f17c3f78aee6f78644
child 503025 e7f8e9c7301bc4cd4088bcf244ca55b877fbb034
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1498115
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1498115 - Part 2: Add badges to show the flex-direction and flex-wrap properties of the flex container. r=pbro We move the .markup-badge styles from markup.css to inspector.css instead of common.css for a smaller import size in the markup.xhtml.
devtools/client/inspector/flexbox/components/FlexContainer.js
devtools/client/inspector/flexbox/test/browser.ini
devtools/client/inspector/flexbox/test/browser_flexbox_container_properties.js
devtools/client/inspector/index.xhtml
devtools/client/inspector/markup/markup.xhtml
devtools/client/inspector/markup/test/browser_markup_display_node_01.js
devtools/client/inspector/markup/test/browser_markup_display_node_02.js
devtools/client/inspector/markup/test/browser_markup_events-overflow.js
devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js
devtools/client/inspector/markup/test/browser_markup_flex_display_badge.js
devtools/client/inspector/markup/test/browser_markup_grid_display_badge_01.js
devtools/client/inspector/markup/test/browser_markup_grid_display_badge_02.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_open_debugger.js
devtools/client/inspector/markup/test/helper_events_test_runner.js
devtools/client/inspector/markup/views/element-editor.js
devtools/client/jar.mn
devtools/client/themes/badge.css
devtools/client/themes/layout.css
devtools/client/themes/markup.css
--- a/devtools/client/inspector/flexbox/components/FlexContainer.js
+++ b/devtools/client/inspector/flexbox/components/FlexContainer.js
@@ -70,44 +70,65 @@ class FlexContainer extends PureComponen
 
   render() {
     const {
       color,
       flexContainer,
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
     } = this.props;
-    const { nodeFront } = flexContainer;
+    const {
+      nodeFront,
+      properties,
+    } = flexContainer;
 
     return createElement(Fragment, null,
-      Rep({
-        defaultRep: ElementNode,
-        mode: MODE.TINY,
-        object: translateNodeFrontToGrip(nodeFront),
-        onDOMNodeMouseOut: () => onHideBoxModelHighlighter(),
-        onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
-      }),
-      dom.div({
-        className: "layout-color-swatch",
-        ref: this.swatchEl,
-        style: {
-          backgroundColor: color,
-        },
-        title: color,
-      }),
-      // The SwatchColorPicker relies on the nextSibling of the swatch element to
-      // apply the selected color. This is why we use a span in display: none for
-      // now. Ideally we should modify the SwatchColorPickerTooltip to bypass this
-      // requirement. See https://bugzilla.mozilla.org/show_bug.cgi?id=1341578
-      dom.span(
-        {
-          className: "layout-color-value",
-          ref: this.colorValueEl,
-        },
-        color
+      dom.div({ className: "flex-header-container-label" },
+        Rep({
+          defaultRep: ElementNode,
+          mode: MODE.TINY,
+          object: translateNodeFrontToGrip(nodeFront),
+          onDOMNodeMouseOut: () => onHideBoxModelHighlighter(),
+          onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
+        }),
+        dom.div({
+          className: "layout-color-swatch",
+          ref: this.swatchEl,
+          style: {
+            backgroundColor: color,
+          },
+          title: color,
+        }),
+        // The SwatchColorPicker relies on the nextSibling of the swatch element to
+        // apply the selected color. This is why we use a span in display: none for
+        // now. Ideally we should modify the SwatchColorPickerTooltip to bypass this
+        // requirement. See https://bugzilla.mozilla.org/show_bug.cgi?id=1341578
+        dom.span(
+          {
+            className: "layout-color-value",
+            ref: this.colorValueEl,
+          },
+          color
+        )
+      ),
+      dom.div({ className: "flex-header-container-properties" },
+        dom.div(
+          {
+            className: "inspector-badge",
+            title: `flex-direction: ${properties["flex-direction"]}`,
+          },
+          properties["flex-direction"]
+        ),
+        dom.div(
+          {
+            className: "inspector-badge",
+            title: `flex-wrap: ${properties["flex-wrap"]}`,
+          },
+          properties["flex-wrap"]
+        )
       )
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
     color: state.flexbox.color,
--- a/devtools/client/inspector/flexbox/test/browser.ini
+++ b/devtools/client/inspector/flexbox/test/browser.ini
@@ -15,16 +15,17 @@ support-files =
   !/devtools/client/shared/test/shared-redux-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_flexbox_accordion_state.js]
 [browser_flexbox_container_and_item.js]
 [browser_flexbox_container_element_rep.js]
+[browser_flexbox_container_properties.js]
 [browser_flexbox_empty_state.js]
 [browser_flexbox_highlighter_color_picker_on_ESC.js]
 [browser_flexbox_highlighter_color_picker_on_RETURN.js]
 [browser_flexbox_item_list_01.js]
 [browser_flexbox_item_list_02.js]
 [browser_flexbox_item_outline_exists.js]
 [browser_flexbox_item_outline_has_correct_layout.js]
 [browser_flexbox_item_outline_hidden_when_useless.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_container_properties.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the flex container properties are shown when a flex container is selected.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #container1 {
+      display: flex;
+    }
+
+    #container2 {
+      display: flex;
+      flex-direction: column;
+      flex-wrap: wrap;
+    }
+  </style>
+  <div id="container1">
+    <div id="item"></div>
+  </div>
+  <div id="container2"></div>
+`;
+
+add_task(async function() {
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector, flexboxInspector } = await openLayoutView();
+  const { document: doc } = flexboxInspector;
+
+  info("Selecting the flex container #container1 and checking the values of the flex " +
+    "container properties for #container1.");
+  let onFlexContainerPropertiesRendered = waitForDOM(doc,
+    ".flex-header-container-properties");
+  await selectNode("#container1", inspector);
+  let [flexContainerProperties] = await onFlexContainerPropertiesRendered;
+
+  ok(flexContainerProperties, "The flex container properties is rendered.");
+  is(flexContainerProperties.children[0].textContent, "row",
+    "Got expected flex-direction.");
+  is(flexContainerProperties.children[1].textContent, "nowrap",
+    "Got expected flex-wrap.");
+
+  info("Selecting a flex item and expecting the flex container properties to not be " +
+    "shown.");
+  const onFlexHeaderRendered = waitForDOM(doc, ".flex-header");
+  await selectNode("#item", inspector);
+  const [flexHeader] = await onFlexHeaderRendered;
+
+  ok(!flexHeader.querySelector(".flex-header-container-properties"),
+    "The flex container properties is not shown in the header.");
+
+  info("Selecting the flex container #container2 and checking the values of the flex " +
+    "container properties for #container2.");
+  onFlexContainerPropertiesRendered = waitForDOM(doc,
+    ".flex-header-container-properties");
+  await selectNode("#container2", inspector);
+  [flexContainerProperties] = await onFlexContainerPropertiesRendered;
+
+  ok(flexContainerProperties, "The flex container properties is rendered.");
+  is(flexContainerProperties.children[0].textContent, "column",
+    "Got expected flex-direction.");
+  is(flexContainerProperties.children[1].textContent, "wrap",
+    "Got expected flex-wrap.");
+});
--- a/devtools/client/inspector/index.xhtml
+++ b/devtools/client/inspector/index.xhtml
@@ -5,16 +5,17 @@
 <!DOCTYPE html>
 
 <html xmlns="http://www.w3.org/1999/xhtml" dir="">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 
   <link rel="stylesheet" href="chrome://devtools/skin/breadcrumbs.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/inspector.css"/>
+  <link rel="stylesheet" href="chrome://devtools/skin/badge.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/rules.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/computed.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/changes.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/fonts.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/boxmodel.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/layout.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/animation.css"/>
   <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/Tabs.css"/>
--- a/devtools/client/inspector/markup/markup.xhtml
+++ b/devtools/client/inspector/markup/markup.xhtml
@@ -2,16 +2,17 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <!DOCTYPE html>
 
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+  <link rel="stylesheet" href="chrome://devtools/skin/badge.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/markup.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css" type="text/css"/>
 
   <script type="application/javascript"
           src="chrome://devtools/content/shared/theme-switching.js"></script>
 </head>
--- a/devtools/client/inspector/markup/test/browser_markup_display_node_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_display_node_01.js
@@ -36,43 +36,43 @@ add_task(async function() {
 
   const {inspector} = await openInspectorForURL("data:text/html;charset=utf-8," +
     encodeURIComponent(TEST_URI));
 
   info("Check the display node is shown and the value of #grid.");
   await selectNode("#grid", inspector);
   const gridContainer = await getContainerForSelector("#grid", inspector);
   const gridDisplayNode = gridContainer.elt.querySelector(
-    ".markup-badge[data-display]");
+    ".inspector-badge.interactive[data-display]");
   ok(gridDisplayNode, "#grid display node is shown.");
   is(gridDisplayNode.textContent, "grid", "Got the correct display type for #grid.");
 
   info("Check the display node is shown and the value of #subgrid.");
   await selectNode("#subgrid", inspector);
   const subgridContainer = await getContainerForSelector("#subgrid", inspector);
   const subgridDisplayNode = subgridContainer.elt.querySelector(
-    ".markup-badge[data-display]");
+    ".inspector-badge[data-display]");
   ok(subgridDisplayNode, "#subgrid display node is shown");
   is(subgridDisplayNode.textContent, "subgrid",
     "Got the correct display type for #subgrid");
 
   info("Check the display node is shown and the value of #flex.");
   await selectNode("#flex", inspector);
   const flexContainer = await getContainerForSelector("#flex", inspector);
   const flexDisplayNode = flexContainer.elt.querySelector(
-    ".markup-badge[data-display]");
+    ".inspector-badge.interactive[data-display]");
   ok(flexDisplayNode, "#flex display node is shown.");
   is(flexDisplayNode.textContent, "flex", "Got the correct display type for #flex");
 
   info("Check the display node is hidden for #block.");
   await selectNode("#block", inspector);
   const blockContainer = await getContainerForSelector("#block", inspector);
   const blockDisplayNode = blockContainer.elt.querySelector(
-    ".markup-badge[data-display]");
+    ".inspector-badge.interactive[data-display]");
   ok(!blockDisplayNode, "#block display node is hidden.");
 
   info("Check the display node is hidden for span.");
   await selectNode("span", inspector);
   const spanContainer = await getContainerForSelector("span", inspector);
   const spanDisplayNode = spanContainer.elt.querySelector(
-    ".markup-badge[data-display]");
+    ".inspector-badge.interactive[data-display]");
   ok(!spanDisplayNode, "span display node is hidden.");
 });
--- a/devtools/client/inspector/markup/test/browser_markup_display_node_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_display_node_02.js
@@ -107,17 +107,18 @@ add_task(async function() {
   }
 });
 
 async function runTestData(inspector, testActor,
                       {selector, before, changeStyle, after}) {
   await selectNode(selector, inspector);
   const container = await getContainerForSelector(selector, inspector);
 
-  const beforeBadge = container.elt.querySelector(".markup-badge[data-display]");
+  const beforeBadge = container.elt.querySelector(
+    ".inspector-badge.interactive[data-display]");
   is(!!beforeBadge, before.visible,
     `Display badge is visible as expected for ${selector}: ${before.visible}`);
   if (before.visible) {
     is(beforeBadge.textContent, before.textContent,
       `Got the correct before display type for ${selector}: ${beforeBadge.textContent}`);
   }
 
   info("Listening for the display-change event");
@@ -132,17 +133,18 @@ async function runTestData(inspector, te
   for (const node of nodes) {
     if (getContainerForNodeFront(node, inspector) === container) {
       foundContainer = true;
       break;
     }
   }
   ok(foundContainer, "Container is part of the list of changed nodes");
 
-  const afterBadge = container.elt.querySelector(".markup-badge[data-display]");
+  const afterBadge = container.elt.querySelector(
+    ".inspector-badge.interactive[data-display]");
   is(!!afterBadge, after.visible,
     `Display badge is visible as expected for ${selector}: ${after.visible}`);
   if (after.visible) {
     is(afterBadge.textContent, after.textContent,
       `Got the correct after display type for ${selector}: ${afterBadge.textContent}`);
   }
 }
 
--- a/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
@@ -29,17 +29,18 @@ const TEST_DATA = [
     alignTop: false,
   },
 ];
 
 add_task(async function() {
   const { inspector } = await openInspectorForURL(TEST_URL);
 
   const markupContainer = await getContainerForSelector("#events", inspector);
-  const evHolder = markupContainer.elt.querySelector(".markup-badge[data-event]");
+  const evHolder = markupContainer.elt.querySelector(
+    ".inspector-badge.interactive[data-event]");
   const tooltip = inspector.markup.eventDetailsTooltip;
 
   info("Clicking to open event tooltip.");
   EventUtils.synthesizeMouseAtCenter(evHolder, {},
     inspector.markup.doc.defaultView);
   await tooltip.once("shown");
   info("EventTooltip visible.");
 
--- a/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
@@ -27,17 +27,18 @@ add_task(async function() {
   await toolbox.switchHost("bottom");
   await runTests(inspector);
 
   await toolbox.destroy();
 });
 
 async function runTests(inspector) {
   const markupContainer = await getContainerForSelector("#events", inspector);
-  const evHolder = markupContainer.elt.querySelector(".markup-badge[data-event]");
+  const evHolder = markupContainer.elt.querySelector(
+    ".inspector-badge.interactive[data-event]");
   const tooltip = inspector.markup.eventDetailsTooltip;
 
   info("Clicking to open event tooltip.");
 
   let onInspectorUpdated = inspector.once("inspector-updated");
   const onTooltipShown = tooltip.once("shown");
   EventUtils.synthesizeMouseAtCenter(evHolder, {}, inspector.markup.doc.defaultView);
 
--- a/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js
@@ -22,20 +22,22 @@ const TEST_URL = `
 
 add_task(async function() {
   const {inspector, toolbox} = await openInspectorForURL(
     "data:text/html;charset=utf-8," + encodeURI(TEST_URL));
 
   await inspector.markup.expandAll();
 
   const container1 = await getContainerForSelector("#d1", inspector);
-  const evHolder1 = container1.elt.querySelector(".markup-badge[data-event]");
+  const evHolder1 = container1.elt.querySelector(
+    ".inspector-badge.interactive[data-event]");
 
   const container2 = await getContainerForSelector("#d2", inspector);
-  const evHolder2 = container2.elt.querySelector(".markup-badge[data-event]");
+  const evHolder2 = container2.elt.querySelector(
+    ".inspector-badge.interactive[data-event]");
 
   const tooltip = inspector.markup.eventDetailsTooltip;
 
   info("Click the event icon for the first element");
   let onShown = tooltip.once("shown");
   EventUtils.synthesizeMouseAtCenter(evHolder1, {}, inspector.markup.win);
   await onShown;
   info("event tooltip for the first div is shown");
--- a/devtools/client/inspector/markup/test/browser_markup_flex_display_badge.js
+++ b/devtools/client/inspector/markup/test/browser_markup_flex_display_badge.js
@@ -21,17 +21,18 @@ add_task(async function() {
   await pushPref("devtools.flexboxinspector.enabled", true);
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector } = await openLayoutView();
   const { highlighters, store } = inspector;
 
   info("Check the flex display badge is shown and not active.");
   await selectNode("#flex", inspector);
   const flexContainer = await getContainerForSelector("#flex", inspector);
-  const flexDisplayBadge = flexContainer.elt.querySelector(".markup-badge[data-display]");
+  const flexDisplayBadge = flexContainer.elt.querySelector(
+    ".inspector-badge.interactive[data-display]");
   ok(!flexDisplayBadge.classList.contains("active"), "flex display badge is not active.");
   ok(flexDisplayBadge.classList.contains("interactive"),
     "flex display badge is interactive.");
 
   info("Check the initial state of the flex highlighter.");
   ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
     "No flexbox highlighter exists in the highlighters overlay.");
   ok(!highlighters.flexboxHighlighterShown, "No flexbox highlighter is shown.");
--- a/devtools/client/inspector/markup/test/browser_markup_grid_display_badge_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge_01.js
@@ -17,17 +17,18 @@ const TEST_URI = `
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector } = await openLayoutView();
   const { highlighters, store } = inspector;
 
   info("Check the grid display badge is shown and not active.");
   await selectNode("#grid", inspector);
   const gridContainer = await getContainerForSelector("#grid", inspector);
-  const gridDisplayBadge = gridContainer.elt.querySelector(".markup-badge[data-display]");
+  const gridDisplayBadge = gridContainer.elt.querySelector(
+    ".inspector-badge.interactive[data-display]");
   ok(!gridDisplayBadge.classList.contains("active"), "grid display badge is not active.");
   ok(gridDisplayBadge.classList.contains("interactive"),
     "grid display badge is interactive.");
 
   info("Check the initial state of the grid highlighter.");
   ok(!highlighters.gridHighlighters.size,
     "No CSS grid highlighter exists in the highlighters overlay.");
 
--- a/devtools/client/inspector/markup/test/browser_markup_grid_display_badge_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge_02.js
@@ -30,19 +30,22 @@ add_task(async function() {
   await pushPref("devtools.gridinspector.maxHighlighters", 2);
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector } = await openLayoutView();
   const { highlighters } = inspector;
 
   const grid1 = await getContainerForSelector("#grid1", inspector);
   const grid2 = await getContainerForSelector("#grid2", inspector);
   const grid3 = await getContainerForSelector("#grid3", inspector);
-  const gridDisplayBadge1 = grid1.elt.querySelector(".markup-badge[data-display]");
-  const gridDisplayBadge2 = grid2.elt.querySelector(".markup-badge[data-display]");
-  const gridDisplayBadge3 = grid3.elt.querySelector(".markup-badge[data-display]");
+  const gridDisplayBadge1 = grid1.elt.querySelector(
+    ".inspector-badge.interactive[data-display]");
+  const gridDisplayBadge2 = grid2.elt.querySelector(
+    ".inspector-badge.interactive[data-display]");
+  const gridDisplayBadge3 = grid3.elt.querySelector(
+    ".inspector-badge.interactive[data-display]");
 
   info("Check the initial state of the grid display badges and grid highlighters");
   ok(!gridDisplayBadge1.classList.contains("active"),
     "#grid1 display badge is not active.");
   ok(!gridDisplayBadge2.classList.contains("active"),
     "#grid2 display badge is not active.");
   ok(!gridDisplayBadge3.classList.contains("active"),
     "#grid3 display badge is not active.");
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_open_debugger.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_open_debugger.js
@@ -58,34 +58,36 @@ add_task(async function() {
 });
 
 async function runTest(inspector, toolbox, selector, contentMethod) {
   // Test element is a regular element (no shadow root or custom element definition).
   info(`Select <${selector}>.`);
   await selectNode(selector, inspector);
   const testFront = await getNodeFront(selector, inspector);
   const testContainer = inspector.markup.getContainer(testFront);
-  let customBadge = testContainer.elt.querySelector(".markup-badge[data-custom]");
+  let customBadge = testContainer.elt.querySelector(
+    ".inspector-badge.interactive[data-custom]");
 
   // Verify that the "custom" badge and menu item are hidden.
   ok(!customBadge, "[custom] badge is hidden");
   let menuItem = getMenuItem("node-menu-jumptodefinition", inspector);
   ok(!menuItem, selector + ": The menu item was not found in the contextual menu");
 
   info("Call the content method that should attach a custom element definition");
   const mutated = waitForMutation(inspector, "customElementDefined");
   ContentTask.spawn(gBrowser.selectedBrowser, { contentMethod }, function(args) {
     content.wrappedJSObject[args.contentMethod]();
   });
   await mutated;
 
   // Test element should now have a custom element definition.
 
   // Check that the badge opens the debugger.
-  customBadge = testContainer.elt.querySelector(".markup-badge[data-custom]");
+  customBadge = testContainer.elt.querySelector(
+    ".inspector-badge.interactive[data-custom]");
   ok(customBadge, "[custom] badge is visible");
 
   info("Click on the `custom` badge and verify that the debugger opens.");
   let onDebuggerReady = toolbox.getPanelWhenReady("jsdebugger");
   customBadge.click();
   await onDebuggerReady;
 
   const debuggerContext = createDebuggerContext(toolbox);
--- a/devtools/client/inspector/markup/test/helper_events_test_runner.js
+++ b/devtools/client/inspector/markup/test/helper_events_test_runner.js
@@ -50,17 +50,18 @@ async function runEventPopupTests(url, t
 async function checkEventsForNode(test, inspector, testActor) {
   const {selector, expected, beforeTest, isSourceMapped} = test;
   const container = await getContainerForSelector(selector, inspector);
 
   if (typeof beforeTest === "function") {
     await beforeTest(inspector, testActor);
   }
 
-  const evHolder = container.elt.querySelector(".markup-badge[data-event]");
+  const evHolder = container.elt.querySelector(
+    ".inspector-badge.interactive[data-event]");
 
   if (expected.length === 0) {
     // If no event is expected, check that event bubble is hidden.
     ok(!evHolder, "event bubble should be hidden");
     return;
   }
 
   const tooltip = inspector.markup.eventDetailsTooltip;
--- a/devtools/client/inspector/markup/views/element-editor.js
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -282,17 +282,17 @@ ElementEditor.prototype = {
       this._eventBadge = null;
     } else if (showEventBadge && !this._eventBadge) {
       this._createEventBadge();
     }
   },
 
   _createEventBadge: function() {
     this._eventBadge = this.doc.createElement("div");
-    this._eventBadge.classList.add("markup-badge");
+    this._eventBadge.className = "inspector-badge interactive";
     this._eventBadge.dataset.event = "true";
     this._eventBadge.textContent = "event";
     this._eventBadge.title = INSPECTOR_L10N.getStr("markupView.event.tooltiptext");
     // Badges order is [event][display][custom], insert event badge before others.
     this.elt.insertBefore(this._eventBadge, this._displayBadge || this._customBadge);
   },
 
   /**
@@ -314,17 +314,17 @@ ElementEditor.prototype = {
       }
 
       this._updateDisplayBadgeContent();
     }
   },
 
   _createDisplayBadge: function() {
     this._displayBadge = this.doc.createElement("div");
-    this._displayBadge.classList.add("markup-badge");
+    this._displayBadge.className = "inspector-badge";
     this._displayBadge.addEventListener("click", this.onDisplayBadgeClick);
     // Badges order is [event][display][custom], insert display badge before custom.
     this.elt.insertBefore(this._displayBadge, this._customBadge);
 
     this.startTrackingFlexboxHighlighterEvents();
     this.startTrackingGridHighlighterEvents();
   },
 
@@ -338,16 +338,18 @@ ElementEditor.prototype = {
       this.highlighters.gridHighlighters.has(this.node));
 
     if (displayType === "flex" || displayType === "inline-flex") {
       this._displayBadge.classList.toggle("interactive",
         Services.prefs.getBoolPref("devtools.inspector.flexboxHighlighter.enabled"));
     } else if (displayType === "grid" || displayType === "inline-grid") {
       this._displayBadge.classList.toggle("interactive",
         this.highlighters.canGridHighlighterToggle(this.node));
+    } else {
+      this._displayBadge.classList.remove("interactive");
     }
   },
 
   /**
    * Update the markup custom element badge.
    */
   updateCustomBadge: function() {
     const showCustomBadge = !!this.node.customElementLocation;
@@ -356,17 +358,17 @@ ElementEditor.prototype = {
       this._customBadge = null;
     } else if (!this._customBadge && showCustomBadge) {
       this._createCustomBadge();
     }
   },
 
   _createCustomBadge: function() {
     this._customBadge = this.doc.createElement("div");
-    this._customBadge.classList.add("markup-badge");
+    this._customBadge.className = "inspector-badge interactive";
     this._customBadge.dataset.custom = "true";
     this._customBadge.textContent = "custom…";
     this._customBadge.title = INSPECTOR_L10N.getStr("markupView.custom.tooltiptext");
     this._customBadge.addEventListener("click", this.onCustomBadgeClick);
     // Badges order is [event][display][custom], insert custom badge at the end.
     this.elt.appendChild(this._customBadge);
   },
 
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -174,16 +174,17 @@ devtools.jar:
     skin/images/debugger-step-in.svg (themes/images/debugger-step-in.svg)
     skin/images/debugger-step-out.svg (themes/images/debugger-step-out.svg)
     skin/images/debugger-step-over.svg (themes/images/debugger-step-over.svg)
     skin/images/dock-bottom.svg (themes/images/dock-bottom.svg)
     skin/images/dock-side-left.svg (themes/images/dock-side-left.svg)
     skin/images/dock-side-right.svg (themes/images/dock-side-right.svg)
     skin/images/dock-undock.svg (themes/images/dock-undock.svg)
     skin/floating-scrollbars-responsive-design.css (themes/floating-scrollbars-responsive-design.css)
+    skin/badge.css (themes/badge.css)
     skin/inspector.css (themes/inspector.css)
     skin/images/profiler-stopwatch.svg (themes/images/profiler-stopwatch.svg)
     skin/images/debugging-addons.svg (themes/images/debugging-addons.svg)
     skin/images/debugging-tabs.svg (themes/images/debugging-tabs.svg)
     skin/images/debugging-workers.svg (themes/images/debugging-workers.svg)
     skin/images/datastore.svg (themes/images/datastore.svg)
     skin/images/globe.svg (themes/images/globe.svg)
     skin/images/folder.svg (themes/images/folder.svg)
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/badge.css
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+:root {
+  --badge-active-background-color: var(--blue-50);
+  --badge-active-border-color: #FFFFFFB3;
+  --badge-background-color: white;
+  --badge-border-color: #CACAD1;
+  --badge-color: var(--grey-60);
+  --badge-hover-background-color: #DFDFE8;
+  --badge-interactive-background-color: var(--grey-20);
+  --badge-interactive-color: var(--grey-90);
+}
+
+.theme-dark:root {
+  --badge-active-background-color: var(--blue-60);
+  --badge-active-border-color: #FFF6;
+  --badge-background-color: var(--grey-80);
+  --badge-border-color: var(--grey-50);
+  --badge-color: var(--grey-40);
+  --badge-hover-background-color: var(--grey-80);
+  --badge-interactive-background-color: var(--grey-70);
+  --badge-interactive-color: var(--grey-30);
+}
+
+/* Inspector badge */
+.inspector-badge {
+  display: inline-block;
+  /* 9px text is too blurry on low-resolution screens */
+  font-size: 10px;
+  font-weight: normal;
+  line-height: 10px;
+  height: 12px;
+  margin-top: 1px;
+  vertical-align: top;
+  border: 1px solid var(--badge-border-color);
+  border-radius: 3px;
+  padding: 0px 2px;
+  margin-inline-start: 5px;
+  -moz-user-select: none;
+  background-color: var(--badge-background-color);
+  color: var(--badge-color);
+  box-sizing: border-box;
+}
+
+@media (min-resolution: 1.1dppx) {
+  .inspector-badge {
+    font-size: 9px;
+  }
+}
+
+/* Inspector badges that are interactive/clickable */
+.inspector-badge.interactive {
+  background-color: var(--badge-interactive-background-color);
+  color: var(--badge-interactive-color);
+  cursor: pointer;
+}
+
+.inspector-badge:not(.active).interactive:focus,
+.inspector-badge:not(.active).interactive:hover {
+  background-color: var(--badge-hover-background-color);
+}
+
+.inspector-badge.active,
+.inspector-badge.interactive.active {
+  background-color: var(--badge-active-background-color);
+  border-color: var(--badge-active-border-color);
+  color: var(--theme-selection-color);
+}
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -91,59 +91,71 @@
  * Flex Container
  */
 
 #layout-flexbox-container {
   display: flex;
   flex-direction: column;
 }
 
-#layout-flexbox-container .flex-header-content:not(.flex-item-shown) {
-  overflow: hidden;
-  display: flex;
-}
-
-#layout-flexbox-container .flex-header-content:not(.flex-item-shown) .objectBox {
-  max-width: calc(100% - 20px);
-  margin-inline-end: 5px;
-  text-overflow: ellipsis;
-  overflow: hidden;
-  white-space: nowrap;
-}
-
 /**
  * Header
  */
 
 .flex-header {
   display: flex;
   align-items: center;
-  height: 32px;
-  padding: 0 3px;
+  padding: 3px;
   border-block-end: 1px solid var(--theme-splitter-color);
 }
 
 .flex-header-button-prev::before {
   background-image: url("chrome://devtools/skin/images/arrowhead-left.svg");
   background-size: 16px;
 }
 
 .flex-header-content {
+  display: flex;
   flex: 1;
-  padding-top: 2px;
+  padding: 2px 0;
   padding-inline-start: 20px;
   -moz-user-select: none;
 }
 
+.flex-header-content:not(.flex-item-shown) {
+  flex-direction: column;
+  overflow: hidden;
+}
+
+.flex-header-content:not(.flex-item-shown) .objectBox {
+  max-width: calc(100% - 20px);
+  margin-inline-end: 5px;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
+}
+
 .flex-header-content.flex-item-shown {
-  display: flex;
   justify-content: center;
   padding: 0;
 }
 
+.flex-header-container-properties {
+  display: flex;
+}
+
+.flex-header-container-properties .inspector-badge:first-child {
+  margin-inline-start: 0;
+}
+
+.flex-header-container-label,
+.flex-header-container-properties {
+  padding: 3px 0;
+}
+
 /**
  * Flex Item List
  */
 
 .flex-item-list {
   font-size: 12px;
   margin: 0;
   padding-inline-start: 34px;
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -1,37 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 :root {
-  --markup-badge-active-background-color: var(--blue-50);
-  --markup-badge-active-border-color: #FFFFFFB3;
-  --markup-badge-background-color: white;
-  --markup-badge-border-color: #CACAD1;
-  --markup-badge-color: var(--grey-60);
-  --markup-badge-hover-background-color: #DFDFE8;
-  --markup-badge-interactive-background-color: var(--grey-20);
-  --markup-badge-interactive-color: var(--grey-90);
   --markup-hidden-attr-name-color: #BA89B8;
   --markup-hidden-attr-value-color: #5C6D87;
   --markup-hidden-punctuation-color: #909090;
   --markup-hidden-tag-color: #97A4B3;
   --markup-outline: var(--theme-splitter-color);
 }
 
 .theme-dark:root {
-  --markup-badge-active-background-color: var(--blue-60);
-  --markup-badge-active-border-color: #FFF6;
-  --markup-badge-background-color: var(--grey-80);
-  --markup-badge-border-color: var(--grey-50);
-  --markup-badge-color: var(--grey-40);
-  --markup-badge-hover-background-color: var(--grey-80);
-  --markup-badge-interactive-background-color: var(--grey-70);
-  --markup-badge-interactive-color: var(--grey-30);
   --markup-hidden-attr-name-color: #B07EB3;
   --markup-hidden-attr-value-color: #9893A3;
   --markup-hidden-punctuation-color: #909090;
   --markup-hidden-tag-color: #AFB5BF;
   --markup-outline: var(--theme-selection-background);
 }
 
 * {
@@ -244,31 +228,36 @@ ul.children + .tag-line::before {
   height: 16px;
   margin: -1px 0;
   padding: 3px 2px;
 }
 
 .expandable.collapsed .markup-expand-badge::before {
   /* Display an ellipsis character in collapsed nodes that can be expanded. */
   content: "";
-  background-color: var(--markup-badge-interactive-background-color);
+  background-color: var(--badge-interactive-background-color);
   background-image: url(chrome://devtools/skin/images/more.svg);
   background-repeat: no-repeat;
   background-position: center;
-  border: 1px solid var(--markup-badge-border-color);
-  color: var(--markup-badge-color);
-  fill: var(--markup-badge-interactive-color);
+  border: 1px solid var(--badge-border-color);
+  color: var(--badge-color);
+  fill: var(--badge-interactive-color);
   display: block;
   width: 12px;
   height: 8px;
   line-height: 8px;
   border-radius: 3px;
   -moz-context-properties: fill;
 }
 
+.expandable.collapsed .markup-expand-badge:focus::before,
+.expandable.collapsed .markup-expand-badge:hover::before {
+  background-color: var(--badge-hover-background-color);
+}
+
 /* Hide HTML void elements (img, hr, br, …) closing tag when the element is not
  * expanded (it can be if it has pseudo-elements attached) */
 .child.collapsed > .tag-line .void-element .close {
   display: none;
 }
 
 .closing-bracket {
   pointer-events: none;
@@ -407,67 +396,8 @@ ul.children + .tag-line::before {
 .theme-selected ~ .editor .theme-fg-color5 {
   color: var(--theme-selection-color);
 }
 
 /* Applicable to the DOCTYPE */
 .doctype {
   font-style: italic;
 }
-
-/* Markup Badges */
-.markup-badge {
-  display: inline-block;
-  /* 9px text is too blurry on low-resolution screens */
-  font-size: 10px;
-  font-weight: normal;
-  line-height: 10px;
-  height: 10px;
-  margin-top: 1px;
-  vertical-align: top;
-  border: 1px solid var(--markup-badge-border-color);
-  border-radius: 3px;
-  padding: 0px 2px;
-  margin-inline-start: 5px;
-  -moz-user-select: none;
-  background-color: var(--markup-badge-background-color);
-  color: var(--markup-badge-color);
-}
-
-@media (min-resolution: 1.1dppx) {
-  .markup-badge {
-    font-size: 9px;
-  }
-}
-
-/* Markup badges that are interactive/clickable */
-.markup-badge[data-custom],
-.markup-badge[data-display="flex"].interactive,
-.markup-badge[data-display="grid"].interactive,
-.markup-badge[data-display="inline-flex"].interactive,
-.markup-badge[data-display="inline-grid"].interactive,
-.markup-badge[data-event] {
-  background-color: var(--markup-badge-interactive-background-color);
-  color: var(--markup-badge-interactive-color);
-  cursor: pointer;
-}
-
-.markup-badge[data-display="flex"]:not(.active).interactive:focus,
-.markup-badge[data-display="flex"]:not(.active).interactive:hover,
-.markup-badge[data-display="grid"]:not(.active).interactive:focus,
-.markup-badge[data-display="grid"]:not(.active).interactive:hover,
-.markup-badge[data-display="inline-flex"]:not(.active).interactive:focus,
-.markup-badge[data-display="inline-flex"]:not(.active).interactive:hover,
-.markup-badge[data-display="inline-grid"]:not(.active).interactive:focus,
-.markup-badge[data-display="inline-grid"]:not(.active).interactive:hover,
-.markup-badge[data-event]:focus,
-.markup-badge[data-event]:hover,
-.expandable.collapsed .markup-expand-badge:focus::before,
-.expandable.collapsed .markup-expand-badge:hover::before {
-  background-color: var(--markup-badge-hover-background-color);
-}
-
-.markup-badge.active,
-.markup-badge.interactive.active {
-  background-color: var(--markup-badge-active-background-color);
-  border-color: var(--markup-badge-active-border-color);
-  color: var(--theme-selection-color);
-}