While working on a side project this week, I needed to be able to highlight the margin, border, and padding of HTML elements like the chrome inspector does. Turns out you can't do it with CSS, because that would be far too convenient. I lean back, breathe deeply, and ponder my career choice once again and open up VSCode.

Grabbing Coordinates

So styling with CSS is impossible, but you can tell where the element should be on the page. If you get an element in Javascript (using something like querySelectorAll ), you can get its bounding positions (top, bottom, left, right) using Element.getBoundingClientRect() . These coordinates represent the box that the element is contained in.

But what about border and margin?

Since the bounding rect doesn't include the margin and border, we have to get this from the element's style. To do this, we can use Window.getComputedStyle(element). This gives us string versions of the style properties we need, like marginLeft, paddingLeft, and borderLeft. From here, just convert the properties to numbers we can use.

// Ex, turns '12px' to the number 12 function pxToNumber(px) { return parseInt(px.replace('px', '')); }

Drawing the outline

Now that we have the coordinates of the element, and how large the border and margins of an element are, we know where we could draw the rectangles around the element. To draw the rectangles in the right location, we can use a canvas element, which can be used to programmatically draw shapes on our page.

function getDocumentHeightAndWidth() { const body = document.body; const html = document.documentElement; const height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); const width = document.body.offsetWidth; return { height, width }; } function createCanvas() { var canvas = document.createElement('canvas'); //Create a canvas element //Set canvas width/height setCanvasWidthAndHeight(canvas); //Position canvas canvas.style.position = 'absolute'; canvas.style.left = '0'; canvas.style.top = '0'; canvas.style.zIndex = '100000'; canvas.style.pointerEvents = 'none'; //Make sure you can click 'through' the canvas document.body.appendChild(canvas); //Append canvas to body element const context = canvas.getContext('2d'); context.globalAlpha = 0.5; return { canvas, context: context }; } function setCanvasWidthAndHeight(canvas) { const { height, width } = getDocumentHeightAndWidth(); canvas.style.width = `${width}`; canvas.style.height = `${height}`; canvas.width = width; canvas.height = height; }

This canvas overlays the entire page, so we can draw wherever there are visible elements. It sits over all the elements, but to avoid clicking on it, its pointerEvents style property is set to none, so all clicks will pass through to the underlying page.

Time to Draw

To show the margins, we need to draw 4 rectangles around an element to create what looks like a thick border around the element. This technique can then be used to draw the padding and border as well.

function drawMarginBorderPadding(ctx, element) { const style = getComputedStyle(element); let { top, left, right, bottom, width, height } = element.getBoundingClientRect(); const { marginTop, marginBottom, marginLeft, marginRight, paddingTop, paddingBottom, paddingLeft, paddingRight, borderBottomWidth, borderTopWidth, borderLeftWidth, borderRightWidth, display } = style; top = top + window.scrollY; left = left + window.scrollX; bottom = bottom + window.scrollY; right = right + window.scrollX; const numMarginTop = pxToNumber(marginTop); const numMarginBottom = pxToNumber(marginBottom); const numMarginLeft = pxToNumber(marginLeft); const numMarginRight = pxToNumber(marginRight); const numPaddingTop = pxToNumber(paddingTop); const numPaddingBottom = pxToNumber(paddingBottom); const numPaddingLeft = pxToNumber(paddingLeft); const numPaddingRight = pxToNumber(paddingRight); const numBorderTop = pxToNumber(borderTopWidth); const numBorderBottom = pxToNumber(borderBottomWidth); const numBorderLeft = pxToNumber(borderLeftWidth); const numBorderRight = pxToNumber(borderRightWidth); const marginHeight = height + numMarginBottom + numMarginTop; // DRAW MARGIN ctx.fillStyle = '#ffa50094'; // Top margin rect ctx.fillRect(left, top - numMarginTop, width, numMarginTop); // Bottom margin rect ctx.fillRect(left, bottom, width, numMarginBottom); // Left margin rect ctx.fillRect(left - numMarginLeft, top - numMarginTop, numMarginLeft, marginHeight); // Right margin rect ctx.fillRect(right, top - numMarginTop, numMarginRight, marginHeight); const paddingWidth = width - numBorderLeft - numBorderRight; const paddingHeight = height - numPaddingBottom - numPaddingBottom - numBorderTop - numBorderBottom; // DRAW PADDING ctx.fillStyle = '#00800040'; // Top padding rect ctx.fillRect(left + numBorderLeft, top + numBorderTop, paddingWidth, numPaddingTop); // Bottom padding rect ctx.fillRect(left + numBorderLeft, bottom - numPaddingBottom - numBorderBottom, paddingWidth, numPaddingBottom); // Left padding rect ctx.fillRect(left + numBorderLeft, top + numPaddingTop + numBorderTop, numPaddingLeft, paddingHeight); // Right padding rect ctx.fillRect(right - numPaddingRight - numBorderRight, top + numPaddingTop + numBorderTop, numPaddingRight, paddingHeight); const borderHeight = height - numBorderTop - numBorderBottom; // DRAW BORDER ctx.fillStyle = '#0000ff1a'; // Top border rect ctx.fillRect(left, top, width, numBorderTop); // Bottom border rect ctx.fillRect(left, bottom - numBorderBottom, width, numBorderBottom); // Left border rect ctx.fillRect(left, top + numBorderTop, numBorderLeft, borderHeight); // Right border rect ctx.fillRect(right - numBorderRight, top + numBorderTop, numBorderRight, borderHeight); }

Putting it all together

When the page initializes, we want to query the elements we want to highlight, draw their bounding styles, and redraw them when the window resizes.

const rowComponentSelector = ".higlightmeplease"; function findComponentsByDefinition(querySelector) { return document.querySelectorAll(querySelector); } const { canvas, context: ctx } = createCanvas(); const rows = findComponentsByDefinition(rowComponentSelector); function drawSelectedElements(elements) { const { height, width } = getDocumentHeightAndWidth(); ctx.clearRect(0, 0, width, height); setCanvasWidthAndHeight(canvas); elements.forEach((e) => { drawMarginBorderPadding(ctx, e); }); } drawSelectedElements(rows); window.addEventListener('resize', () => { drawSelectedElements(rows); });

Demo Time

I'm a row!

I'm a row with different sized borders!

Full source code here: https://gist.github.com/awestbro/e668c12662ad354f02a413205b65fce7

The project I'm working on is dedicated to making front end web development much simpler. Subscribe for updates!