--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -20,16 +20,17 @@ const ClassListPreviewer = require("devt
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {
VIEW_NODE_SELECTOR_TYPE,
VIEW_NODE_PROPERTY_TYPE,
VIEW_NODE_VALUE_TYPE,
VIEW_NODE_IMAGE_URL_TYPE,
VIEW_NODE_LOCATION_TYPE,
VIEW_NODE_SHAPE_POINT_TYPE,
+ VIEW_NODE_VARIABLE_TYPE,
} = require("devtools/client/inspector/shared/node-types");
const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
const {createChild, promiseWarn} = require("devtools/client/inspector/shared/utils");
const {debounce} = require("devtools/shared/debounce");
const EventEmitter = require("devtools/shared/old-event-emitter");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const clipboardHelper = require("devtools/shared/platform/clipboard");
@@ -333,16 +334,29 @@ CssRuleView.prototype = {
enabled: prop.enabled,
overridden: prop.overridden,
pseudoElement: prop.rule.pseudoElement,
sheetHref: prop.rule.domRule.href,
textProperty: prop,
toggleActive: getShapeToggleActive(node),
point: getShapePoint(node)
};
+ } else if ((classes.contains("ruleview-variable") ||
+ classes.contains("ruleview-unmatched-variable")) && prop) {
+ type = VIEW_NODE_VARIABLE_TYPE;
+ value = {
+ property: getPropertyNameAndValue(node).name,
+ value: node.textContent,
+ enabled: prop.enabled,
+ overridden: prop.overridden,
+ pseudoElement: prop.rule.pseudoElement,
+ sheetHref: prop.rule.domRule.href,
+ textProperty: prop,
+ variable: node.dataset.variable
+ };
} else if (classes.contains("theme-link") &&
!classes.contains("ruleview-rule-source") && prop) {
type = VIEW_NODE_IMAGE_URL_TYPE;
value = {
property: getPropertyNameAndValue(node).name,
value: node.parentNode.textContent,
url: node.href,
enabled: prop.enabled,
--- a/devtools/client/inspector/rules/test/browser_rules_variables_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_variables_01.js
@@ -12,24 +12,30 @@ add_task(function* () {
yield addTab(TEST_URI);
let {inspector, view} = yield openRuleView();
yield selectNode("#target", inspector);
info("Tests basic support for CSS Variables for both single variable " +
"and double variable. Formats tested: var(x, constant), var(x, var(y))");
let unsetColor = getRuleViewProperty(view, "div", "color").valueSpan
- .querySelector(".ruleview-variable-unmatched");
+ .querySelector(".ruleview-unmatched-variable");
let setColor = unsetColor.previousElementSibling;
is(unsetColor.textContent, " red", "red is unmatched in color");
is(setColor.textContent, "--color", "--color is not set correctly");
- is(setColor.title, "--color = chartreuse", "--color's title is not set correctly");
+ is(setColor.dataset.variable, "--color = chartreuse",
+ "--color's dataset.variable is not set correctly");
+ let previewTooltip = yield assertShowPreviewTooltip(view, setColor);
+ yield assertTooltipHiddenOnMouseOut(previewTooltip, setColor);
let unsetVar = getRuleViewProperty(view, "div", "background-color").valueSpan
- .querySelector(".ruleview-variable-unmatched");
+ .querySelector(".ruleview-unmatched-variable");
let setVar = unsetVar.nextElementSibling;
let setVarName = setVar.firstElementChild.firstElementChild;
is(unsetVar.textContent, "--not-set",
"--not-set is unmatched in background-color");
is(setVar.textContent, " var(--bg)", "var(--bg) parsed incorrectly");
is(setVarName.textContent, "--bg", "--bg is not set correctly");
- is(setVarName.title, "--bg = seagreen", "--bg's title is not set correctly");
+ is(setVarName.dataset.variable, "--bg = seagreen",
+ "--bg's dataset.variable is not set correctly");
+ previewTooltip = yield assertShowPreviewTooltip(view, setVarName);
+ yield assertTooltipHiddenOnMouseOut(previewTooltip, setVarName);
});
--- a/devtools/client/inspector/rules/test/browser_rules_variables_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_variables_02.js
@@ -21,60 +21,60 @@ add_task(function* () {
});
function* testBasic(inspector, view) {
info("Test support for basic variable functionality for var() with 2 variables." +
"Format: var(--var1, var(--var2))");
yield selectNode("#a", inspector);
let unsetVar = getRuleViewProperty(view, "#a", "font-size").valueSpan
- .querySelector(".ruleview-variable-unmatched");
+ .querySelector(".ruleview-unmatched-variable");
let setVarParent = unsetVar.nextElementSibling;
let setVar = getVarFromParent(setVarParent);
is(unsetVar.textContent, "--var-not-defined",
"--var-not-defined is not set correctly");
- is(unsetVar.title, "--var-not-defined is not set",
- "--var-not-defined's title is not set correctly");
+ is(unsetVar.dataset.variable, "--var-not-defined is not set",
+ "--var-not-defined's dataset.variable is not set correctly");
is(setVarParent.textContent, " var(--var-defined-font-size)",
"var(--var-defined-font-size) parsed incorrectly");
is(setVar.textContent, "--var-defined-font-size",
"--var-defined-font-size is not set correctly");
- is(setVar.title, "--var-defined-font-size = 60px",
- "--bg's title is not set correctly");
+ is(setVar.dataset.variable, "--var-defined-font-size = 60px",
+ "--bg's dataset.variable is not set correctly");
}
function* testNestedCssFunctions(inspector, view) {
info("Test support for variable functionality for a var() nested inside " +
"another CSS function. Format: rgb(0, 0, var(--var1, var(--var2)))");
yield selectNode("#b", inspector);
let unsetVarParent = getRuleViewProperty(view, "#b", "color").valueSpan
- .querySelector(".ruleview-variable-unmatched");
+ .querySelector(".ruleview-unmatched-variable");
let unsetVar = getVarFromParent(unsetVarParent);
let setVar = unsetVarParent.previousElementSibling;
is(unsetVarParent.textContent, " var(--var-defined-r-2)",
"var(--var-defined-r-2) not parsed correctly");
is(unsetVar.textContent, "--var-defined-r-2",
"--var-defined-r-2 is not set correctly");
- is(unsetVar.title, "--var-defined-r-2 = 0",
- "--var-defined-r-2's title is not set correctly");
+ is(unsetVar.dataset.variable, "--var-defined-r-2 = 0",
+ "--var-defined-r-2's dataset.variable is not set correctly");
is(setVar.textContent, "--var-defined-r-1",
"--var-defined-r-1 is not set correctly");
- is(setVar.title, "--var-defined-r-1 = 255",
- "--var-defined-r-1's title is not set correctly");
+ is(setVar.dataset.variable, "--var-defined-r-1 = 255",
+ "--var-defined-r-1's dataset.variable is not set correctly");
}
function* testBorderShorthandAndInheritance(inspector, view) {
info("Test support for variable functionality for shorthands/CSS styles with spaces " +
"like \"margin: w x y z\". Also tests functionality for inherticance of CSS" +
" variables. Format: var(l, var(m)) var(x) rgb(var(r) var(g) var(b))");
yield selectNode("#c", inspector);
let unsetVarL = getRuleViewProperty(view, "#c", "border").valueSpan
- .querySelector(".ruleview-variable-unmatched");
+ .querySelector(".ruleview-unmatched-variable");
let setVarMParent = unsetVarL.nextElementSibling;
// var(x) is the next sibling of the parent of M
let setVarXParent = setVarMParent.parentNode.nextElementSibling;
// var(r) is the next sibling of var(x), and var(g) is the next sibling of var(r), etc.
let setVarRParent = setVarXParent.nextElementSibling;
let setVarGParent = setVarRParent.nextElementSibling;
@@ -83,108 +83,108 @@ function* testBorderShorthandAndInherita
let setVarM = getVarFromParent(setVarMParent);
let setVarX = setVarXParent.firstElementChild;
let setVarR = setVarRParent.firstElementChild;
let setVarG = setVarGParent.firstElementChild;
let setVarB = setVarBParent.firstElementChild;
is(unsetVarL.textContent, "--var-undefined",
"--var-undefined is not set correctly");
- is(unsetVarL.title, "--var-undefined is not set",
- "--var-undefined's title is not set correctly");
+ is(unsetVarL.dataset.variable, "--var-undefined is not set",
+ "--var-undefined's dataset.variable is not set correctly");
is(setVarM.textContent, "--var-border-px",
"--var-border-px is not set correctly");
- is(setVarM.title, "--var-border-px = 10px",
- "--var-border-px's title is not set correctly");
+ is(setVarM.dataset.variable, "--var-border-px = 10px",
+ "--var-border-px's dataset.variable is not set correctly");
is(setVarX.textContent, "--var-border-style",
"--var-border-style is not set correctly");
- is(setVarX.title, "--var-border-style = solid",
- "var-border-style's title is not set correctly");
+ is(setVarX.dataset.variable, "--var-border-style = solid",
+ "var-border-style's dataset.variable is not set correctly");
is(setVarR.textContent, "--var-border-r",
"--var-defined-r is not set correctly");
- is(setVarR.title, "--var-border-r = 255",
- "--var-defined-r's title is not set correctly");
+ is(setVarR.dataset.variable, "--var-border-r = 255",
+ "--var-defined-r's dataset.variable is not set correctly");
is(setVarG.textContent, "--var-border-g",
"--var-defined-g is not set correctly");
- is(setVarG.title, "--var-border-g = 0",
- "--var-defined-g's title is not set correctly");
+ is(setVarG.dataset.variable, "--var-border-g = 0",
+ "--var-defined-g's dataset.variable is not set correctly");
is(setVarB.textContent, "--var-border-b",
"--var-defined-b is not set correctly");
- is(setVarB.title, "--var-border-b = 0",
- "--var-defined-b's title is not set correctly");
+ is(setVarB.dataset.variable, "--var-border-b = 0",
+ "--var-defined-b's dataset.variable is not set correctly");
}
function* testSingleLevelVariable(inspector, view) {
info("Test support for variable functionality of a single level of " +
"undefined variables. Format: var(x, constant)");
yield selectNode("#d", inspector);
let unsetVar = getRuleViewProperty(view, "#d", "font-size").valueSpan
- .querySelector(".ruleview-variable-unmatched");
+ .querySelector(".ruleview-unmatched-variable");
is(unsetVar.textContent, "--var-undefined",
"--var-undefined is not set correctly");
- is(unsetVar.title, "--var-undefined is not set",
- "--var-undefined's title is not set correctly");
+ is(unsetVar.dataset.variable, "--var-undefined is not set",
+ "--var-undefined's dataset.variable is not set correctly");
}
function* testDoubleLevelVariable(inspector, view) {
info("Test support for variable functionality of double level of " +
"undefined variables. Format: var(x, var(y, constant))");
yield selectNode("#e", inspector);
let allUnsetVars = getRuleViewProperty(view, "#e", "color").valueSpan
- .querySelectorAll(".ruleview-variable-unmatched");
+ .querySelectorAll(".ruleview-unmatched-variable");
is(allUnsetVars.length, 2, "The number of unset variables is mismatched.");
let unsetVar1 = allUnsetVars[0];
let unsetVar2 = allUnsetVars[1];
is(unsetVar1.textContent, "--var-undefined",
"--var-undefined is not set correctly");
- is(unsetVar1.title, "--var-undefined is not set",
- "--var-undefined's title is not set correctly");
+ is(unsetVar1.dataset.variable, "--var-undefined is not set",
+ "--var-undefined's dataset.variable is not set correctly");
is(unsetVar2.textContent, "--var-undefined-2",
"--var-undefined is not set correctly");
- is(unsetVar2.title, "--var-undefined-2 is not set",
- "--var-undefined-2's title is not set correctly");
+ is(unsetVar2.dataset.variable, "--var-undefined-2 is not set",
+ "--var-undefined-2's dataset.variable is not set correctly");
}
function* testTripleLevelVariable(inspector, view) {
info("Test support for variable functionality of triple level of " +
"undefined variables. Format: var(x, var(y, var(z, constant)))");
yield selectNode("#f", inspector);
let allUnsetVars = getRuleViewProperty(view, "#f", "border-style").valueSpan
- .querySelectorAll(".ruleview-variable-unmatched");
+ .querySelectorAll(".ruleview-unmatched-variable");
is(allUnsetVars.length, 3, "The number of unset variables is mismatched.");
let unsetVar1 = allUnsetVars[0];
let unsetVar2 = allUnsetVars[1];
let unsetVar3 = allUnsetVars[2];
is(unsetVar1.textContent, "--var-undefined",
"--var-undefined is not set correctly");
- is(unsetVar1.title, "--var-undefined is not set",
- "--var-undefined's title is not set correctly");
+ is(unsetVar1.dataset.variable, "--var-undefined is not set",
+ "--var-undefined's dataset.variable is not set correctly");
is(unsetVar2.textContent, "--var-undefined-2",
"--var-undefined-2 is not set correctly");
- is(unsetVar2.title, "--var-undefined-2 is not set",
- "--var-defined-r-2's title is not set correctly");
+ is(unsetVar2.dataset.variable, "--var-undefined-2 is not set",
+ "--var-defined-r-2's dataset.variable is not set correctly");
is(unsetVar3.textContent, "--var-undefined-3",
"--var-undefined-3 is not set correctly");
- is(unsetVar3.title, "--var-undefined-3 is not set",
- "--var-defined-r-3's title is not set correctly");
+ is(unsetVar3.dataset.variable, "--var-undefined-3 is not set",
+ "--var-defined-r-3's dataset.variable is not set correctly");
}
function getVarFromParent(varParent) {
return varParent.firstElementChild.firstElementChild;
}
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -360,17 +360,18 @@ TextPropertyEditor.prototype = {
colorSwatchClass: SHARED_SWATCH_CLASS + " " + COLOR_SWATCH_CLASS,
filterClass: "ruleview-filter",
filterSwatchClass: SHARED_SWATCH_CLASS + " " + FILTER_SWATCH_CLASS,
gridClass: "ruleview-grid",
shapeClass: "ruleview-shape",
defaultColorType: !propDirty,
urlClass: "theme-link",
baseURI: this.sheetHref,
- unmatchedVariableClass: "ruleview-variable-unmatched",
+ unmatchedVariableClass: "ruleview-unmatched-variable",
+ matchedVariableClass: "ruleview-variable",
isVariableInUse: varName => this.rule.elementStyle.getVariable(varName),
};
let frag = outputParser.parseCssProperty(name, val, parserOptions);
this.valueSpan.innerHTML = "";
this.valueSpan.appendChild(frag);
this.ruleView.emit("property-value-updated", this.valueSpan);
--- a/devtools/client/inspector/shared/node-types.js
+++ b/devtools/client/inspector/shared/node-types.js
@@ -11,8 +11,9 @@
*/
exports.VIEW_NODE_SELECTOR_TYPE = 1;
exports.VIEW_NODE_PROPERTY_TYPE = 2;
exports.VIEW_NODE_VALUE_TYPE = 3;
exports.VIEW_NODE_IMAGE_URL_TYPE = 4;
exports.VIEW_NODE_LOCATION_TYPE = 5;
exports.VIEW_NODE_SHAPE_POINT_TYPE = 6;
+exports.VIEW_NODE_VARIABLE_TYPE = 7;
--- a/devtools/client/inspector/shared/tooltips-overlay.js
+++ b/devtools/client/inspector/shared/tooltips-overlay.js
@@ -11,35 +11,39 @@
* editor tooltips that appear when clicking swatch based editors.
*/
const { Task } = require("devtools/shared/task");
const Services = require("Services");
const {
VIEW_NODE_VALUE_TYPE,
VIEW_NODE_IMAGE_URL_TYPE,
+ VIEW_NODE_VARIABLE_TYPE,
} = require("devtools/client/inspector/shared/node-types");
const { getColor } = require("devtools/client/shared/theme");
const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loader.lazyRequireGetter(this, "getCssProperties",
"devtools/shared/fronts/css-properties", true);
loader.lazyRequireGetter(this, "getImageDimensions",
"devtools/client/shared/widgets/tooltip/ImageTooltipHelper", true);
loader.lazyRequireGetter(this, "setImageTooltip",
"devtools/client/shared/widgets/tooltip/ImageTooltipHelper", true);
loader.lazyRequireGetter(this, "setBrokenImageTooltip",
"devtools/client/shared/widgets/tooltip/ImageTooltipHelper", true);
+loader.lazyRequireGetter(this, "setVariableTooltip",
+ "devtools/client/shared/widgets/tooltip/VariableTooltipHelper", true);
const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";
// Types of existing tooltips
const TOOLTIP_IMAGE_TYPE = "image";
const TOOLTIP_FONTFAMILY_TYPE = "font-family";
+const TOOLTIP_VARIABLE_TYPE = "variable";
/**
* Manages all tooltips in the style-inspector.
*
* @param {CssRuleView|CssComputedView} view
* Either the rule-view or computed-view panel
*/
function TooltipsOverlay(view) {
@@ -169,16 +173,21 @@ TooltipsOverlay.prototype = {
// Font preview tooltip
if (type === VIEW_NODE_VALUE_TYPE && prop.property === "font-family") {
let value = prop.value.toLowerCase();
if (value !== "inherit" && value !== "unset" && value !== "initial") {
tooltipType = TOOLTIP_FONTFAMILY_TYPE;
}
}
+ // Variable preview tooltip
+ if (type === VIEW_NODE_VARIABLE_TYPE) {
+ tooltipType = TOOLTIP_VARIABLE_TYPE;
+ }
+
return tooltipType;
},
/**
* Executed by the tooltip when the pointer hovers over an element of the
* view. Used to decide whether the tooltip should be shown or not and to
* actually put content in it.
* Checks if the hovered target is a css value we support tooltips for.
@@ -220,16 +229,22 @@ TooltipsOverlay.prototype = {
if (type === TOOLTIP_FONTFAMILY_TYPE) {
let font = nodeInfo.value.value;
let nodeFront = inspector.selection.nodeFront;
yield this._setFontPreviewTooltip(font, nodeFront);
return true;
}
+ if (type === TOOLTIP_VARIABLE_TYPE && nodeInfo.value.value.startsWith("--")) {
+ let variable = nodeInfo.value.variable;
+ yield this._setVariablePreviewTooltip(variable);
+ return true;
+ }
+
return false;
}),
/**
* Set the content of the preview tooltip to display an image preview. The image URL can
* be relative, a call will be made to the debuggee to retrieve the image content as an
* imageData URI.
*
@@ -285,16 +300,28 @@ TooltipsOverlay.prototype = {
let doc = this.view.inspector.panelDoc;
let {naturalWidth, naturalHeight} = yield getImageDimensions(doc, imageUrl);
yield setImageTooltip(this.getTooltip("previewTooltip"), doc, imageUrl,
{hideDimensionLabel: true, hideCheckeredBackground: true,
maxDim, naturalWidth, naturalHeight});
}),
+ /**
+ * Set the content of the preview tooltip to display a variable preview.
+ *
+ * @param {String} text
+ * The text to display for the variable tooltip
+ * @return {Promise} A promise that resolves when the preview tooltip content is ready
+ */
+ _setVariablePreviewTooltip: Task.async(function* (text) {
+ let doc = this.view.inspector.panelDoc;
+ yield setVariableTooltip(this.getTooltip("previewTooltip"), doc, text);
+ }),
+
_onNewSelection: function () {
for (let [, tooltip] of this._instances) {
tooltip.hide();
}
},
/**
* Destroy this overlay instance, removing it from the view
--- a/devtools/client/shared/output-parser.js
+++ b/devtools/client/shared/output-parser.js
@@ -222,24 +222,25 @@ OutputParser.prototype = {
}
// Get the variable name.
let varName = text.substring(tokens[0].startOffset, tokens[0].endOffset);
if (typeof varValue === "string") {
// The variable value is valid, set the variable name's title of the first argument
// in var() to display the variable name and value.
- firstOpts.title =
+ firstOpts["data-variable"] =
STYLE_INSPECTOR_L10N.getFormatStr("rule.variableValue", varName, varValue);
+ firstOpts.class = options.matchedVariableClass;
secondOpts.class = options.unmatchedVariableClass;
} else {
// The variable name is not valid, mark it unmatched.
firstOpts.class = options.unmatchedVariableClass;
- firstOpts.title = STYLE_INSPECTOR_L10N.getFormatStr("rule.variableUnset",
- varName);
+ firstOpts["data-variable"] = STYLE_INSPECTOR_L10N.getFormatStr("rule.variableUnset",
+ varName);
}
variableNode.appendChild(this._createNode("span", firstOpts, result));
// If we saw a ",", then append it and show the remainder using
// the correct highlighting.
if (sawComma) {
variableNode.appendChild(this.doc.createTextNode(","));
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/VariableTooltipHelper.js
@@ -0,0 +1,33 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+"use strict";
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+const PADDING = 2;
+
+/**
+ * Set the tooltip content of a provided HTMLTooltip instance to display a
+ * variable preview matching the provided text.
+ *
+ * @param {HTMLTooltip} tooltip
+ * The tooltip instance on which the text preview content should be set
+ * @param {Document} doc
+ * A document element to create the HTML elements needed for the tooltip
+ * @param {String} text
+ * Text to display in tooltip
+ */
+function setVariableTooltip(tooltip, doc, text) {
+ // Create tooltip content
+ let div = doc.createElementNS(XHTML_NS, "div");
+ div.classList.add("devtools-monospace", "devtools-tooltip-css-variable");
+ div.textContent = text;
+
+ tooltip.setContent(div);
+}
+
+module.exports.setVariableTooltip = setVariableTooltip;
--- a/devtools/client/shared/widgets/tooltip/moz.build
+++ b/devtools/client/shared/widgets/tooltip/moz.build
@@ -11,9 +11,10 @@ DevToolsModules(
'InlineTooltip.js',
'SwatchBasedEditorTooltip.js',
'SwatchColorPickerTooltip.js',
'SwatchCubicBezierTooltip.js',
'SwatchFilterTooltip.js',
'Tooltip.js',
'TooltipToggle.js',
'VariableContentHelper.js',
+ 'VariableTooltipHelper.js'
)
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -567,17 +567,17 @@
.ruleview-selectorcontainer {
word-wrap: break-word;
cursor: text;
}
.ruleview-selector-separator,
.ruleview-selector-unmatched,
-.ruleview-variable-unmatched {
+.ruleview-unmatched-variable {
color: #888;
}
.ruleview-selector-matched > .ruleview-selector-attribute {
/* TODO: Bug 1178535 Awaiting UX feedback on highlight colors */
}
.ruleview-selector-matched > .ruleview-selector-pseudo-class {
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -56,16 +56,23 @@
.devtools-tooltip[clamped-dimensions-no-min-height] .panel-arrowcontent,
.devtools-tooltip[clamped-dimensions-no-max-or-min-height] .panel-arrowcontent {
overflow: hidden;
}
.devtools-tooltip[wide] {
max-width: 600px;
}
+/* Tooltip: CSS variables tooltip */
+
+.devtools-tooltip-css-variable {
+ color: var(--theme-body-color);
+ padding: 2px;
+}
+
/* Tooltip: Simple Text */
.devtools-tooltip-simple-text {
max-width: 400px;
margin: 0 -4px; /* Compensate for the .panel-arrowcontent padding. */
padding: 8px 12px;
white-space: pre-wrap;
}