I'd like to build a browser extension that inspects a given HTML element (table/div) and attempts to build an SVG from it- is that possible?
Yes, this is absolutely possible—and it’s a fantastic use case for a browser extension.
Because browser extensions use Content Scripts, they have direct access to the DOM (Document Object Model) of the webpage you are currently viewing. This means your extension can read any <div>, <table>, or <svg> on the page, extract its structure and styling, and convert it.
Here is the breakdown of how you can achieve this, ranging from the "easy" way to the "hardcore" way.
When converting HTML to SVG, you have two primary approaches: the <foreignObject> trick (easiest), or mapping true vector primitives (hardest).
SVG has a native tag called <foreignObject> that allows you to embed raw HTML and CSS directly inside an SVG file. This is the most accurate way to capture complex web layouts without having to write a rendering engine.
Here is the core logic you would run in your extension's content script:
JavaScript
function elementToSVG(element) {
// 1. Get exact dimensions
const rect = element.getBoundingClientRect();
// 2. Serialize the HTML structure
const xmlSerializer = new XMLSerializer();
const htmlString = xmlSerializer.serializeToString(element);
// 3. Wrap it in an SVG with a foreignObject
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="${rect.width}" height="${rect.height}">
<foreignObject width="100%" height="100%">
<div xmlns="http://www.w3.org/1999/xhtml">
${htmlString}
</div>
</foreignObject>
</svg>
`;
return svg;
}
The code above has one major catch: CSS styles. If the webpage relies on an external stylesheet (e.g., <link rel="stylesheet">), the HTML inside your SVG will lose its styling. You have to inline all the CSS properties.
Instead of writing a script to compute and inline every CSS rule manually, you can bundle a library like html-to-image or dom-to-image into your extension. They handle the heavy lifting of inlining fonts, computing CSS, and generating a clean SVG:
JavaScript
import { toSvg } from 'html-to-image';
const node = document.getElementById('my-table');
toSvg(node)
.then((dataUrl) => {
// dataUrl is your base64 encoded SVG!
console.log(dataUrl);
});
If you don't want an HTML wrapper and instead want actual SVG primitives (e.g., turning a <div> with a border into an SVG <rect>, or HTML text into SVG <text>), you have to write a parser. You would use window.getComputedStyle(element) to read the dimensions, colors, and fonts of every HTML node, and manually draw matching SVG tags. This is highly complex due to CSS features like Flexbox and Grid, but it results in a "pure" vector file.
To wire this up into a Chrome/Firefox extension, you need three main components:
- Manifest File (
manifest.json): Requests the necessary permissions. You will needactiveTabto interact with the current page, andscriptingto inject your code. - Background Script / Service Worker: Listens for the user clicking your extension icon. When clicked, it sends a message to the active tab to "start inspecting."
- Content Script: Injected into the page. This script handles the UI for selecting an element (e.g., drawing a highlight box over the table when the user hovers over it) and executes the HTML-to-SVG logic once the user clicks.
- Tainted Canvas / CORS: If the HTML element contains images hosted on a different domain without proper CORS headers, the browser's security model might block the SVG from rendering those images.
- Web Fonts: Custom web fonts (
@font-face) often break when converted to SVG unless they are converted to Base64 data URIs and embedded directly in the SVG<style>block. - Scrollable Areas: If you target a
<div>withoverflow: auto, you usually only capture the visible area unless you temporarily force the element to expand to its full scroll height before capturing.
Want to map out the exact extension files?
Yes
if i wanted option 3 would I need to use the chromium source or something for css engine?
No, you wouldn't need to touch the Chromium source code or embed your own CSS engine!
Because you are building a browser extension, your code is already running inside the world’s most powerful CSS engine: the browser itself. You don't need to figure out how Flexbox or CSS Grid calculates where things should go, because the browser has already calculated the final pixel coordinates for you.
To build a true vector representation, your extension just needs to ask the browser for the final math, element by element, and translate that math into SVG coordinates.
Here is how you would extract that data using native JavaScript APIs.
1
Traverse the DOM Tree
You write a recursive function to walk through the target element and all of its children. For every HTML node you encounter, you evaluate what kind of SVG primitive it should become.
2
Get Exact Dimensions
getBoundingClientRect()
For each element, call element.getBoundingClientRect(). This returns the exact x, y, width, and height of the element relative to the viewport. It doesn't matter if the element was positioned using Flexbox, absolute positioning, or floats — this API gives you the final, rendered bounding box.
3
Get Rendered Styles
window.getComputedStyle()
Next, call window.getComputedStyle(element). This returns an object containing the final, computed values of every CSS property. If the element had a CSS class that made the background red, getComputedStyle(element).backgroundColor will return rgb(255, 0, 0).
4
Map to SVG Primitives
You then take those values and write them into SVG tags. A <div> becomes an SVG <rect> using the width, height, x, and y from step 2, and the background color from step 3. Borders become stroke and stroke-width attributes.
While you don't need a CSS engine for the layout, this approach is still incredibly difficult because of two major hurdles:
1. Text Wrapping Browsers do not have a native API that tells you exactly where a paragraph of text broke into a new line. In an SVG, <text> elements do not auto-wrap. If you have a paragraph of text, you have to use the Range API to highlight word-by-word to calculate exactly where the browser wrapped the text, and then manually create an SVG <tspan> for every single line break.
2. Stacking Contexts (z-index) HTML draws elements based on complex z-index rules and stacking contexts. SVG does not support z-index. In an SVG, whichever element is at the bottom of the code is drawn on top. You would have to write an algorithm to sort all the HTML elements by their z-index and DOM order, and write them into the SVG in the exact sequence they should visually stack.
This is exactly why design tools like Figma actually use WebGL or Canvas rather than standard DOM elements for their UI — mapping the DOM perfectly to vectors is a notoriously brutal task.
Want to see the code for mapping a single div?
Yes
what about curves and traces and paths and whatnot?
This is exactly where the "true vector" approach graduates from a tedious DOM-walking script into a heavy math project.
While the browser calculates the final layout for you, it doesn't give you ready-made SVG paths for complex CSS shapes. It gives you computed strings, and you have to translate those strings into SVG coordinate math.
Here is how you handle the curves, paths, and complex shapes you'll encounter.
If an element has a uniform border radius (e.g., border-radius: 10px), it's easy. You map it to an SVG rectangle with rx and ry attributes: <rect rx="10" ry="10" ... />.
However, SVG <rect> elements do not support different radii per corner. If a web developer writes border-radius: 10px 50% 0 20px, the browser renders it perfectly, but you cannot use an SVG <rect>.
The Solution: You have to draw a custom SVG <path>. You must calculate the exact pixel coordinates of all four corners, determine the radius of each corner, and write a path string using the SVG Arc command (A).
- It looks like this:
<path d="M 10 0 L 90 0 A 10 10 0 0 1 100 10 L 100 90 ... Z" />
This is actually the one area where you catch a massive break.
If an HTML element is rotated or skewed using CSS (transform: rotate(45deg) skewX(20deg)), you do not need to calculate the new coordinates of its corners. When you call window.getComputedStyle(element).transform, the browser has already resolved all those rotations and skews into a single mathematical matrix string.
It will return something like: matrix(0.866, 0.5, -0.5, 0.866, 0, 0).
The Solution: SVG natively supports this exact syntax. You simply create an SVG group (<g>), apply the exact matrix string you got from CSS, and place your element inside it:
HTML
<g transform="matrix(0.866, 0.5, -0.5, 0.866, 0, 0)">
<rect ... />
</g>
Modern CSS allows developers to cut elements into custom shapes using clip-path: polygon(...) or clip-path: path(...).
When you check the computed style for this, the browser will give you the raw string, like polygon(50% 0%, 0% 100%, 100% 100%).
The Solution: You have to write a parser to convert percentages into absolute pixel coordinates (using the width and height you got from getBoundingClientRect()). Then, you generate an SVG <clipPath> element containing an SVG <polygon> or <path>, and apply it to your mapped vector shape using clip-path="url(#your-generated-id)".
Many websites already use inline SVGs for icons, logos, and graphs.
The Solution: Freebies. If your DOM-walker encounters an <svg> node, you don't need to compute its styles or map it. You just copy the outerHTML of that node and inject it directly into your generated SVG output.
If a website is drawing curves, traces, or 3D objects inside a <canvas> element (like a chart library or a WebGL animation), you cannot extract the vectors. A canvas is a raster image—a grid of pixels. The browser forgets the "paths" the millisecond they are drawn to the screen.
The Solution: You must fall back to raster graphics for that specific element. You call canvas.toDataURL("image/png") in your script, which gives you a Base64 string of the pixels. You then embed that string into your vector file using an SVG <image> tag.