Widget reference
Every attribute the quotometry-quote element takes, every event it emits, its methods, the JavaScript mount API, theming variables and CSP.
This page is the reference for the <quotometry-quote> element: what you can set on it, what it tells you, what
you can ask it to do, how to theme it and which content security policy it needs. For a first installation,
start with the quick start.
The widget is a custom element that renders into a shadow root. Your CSS cannot reach into it and its CSS cannot
reach out. It brings its own typeface (Inter) so it looks the same on every page; set --quotometry-font to use
yours.
The widget never throws into your page. A configuration mistake, a network failure or a bad file becomes a message inside the widget, and your page keeps working.
Attributes
| Attribute | Type | Required | Default | Description |
|---|---|---|---|---|
widget | string | Yes | - | The widget key, wgt_live_… or wgt_test_…. |
api | URL | No | https://api.quotometry.app | Base URL of the buyer API. Set it on a regional or self-hosted deployment. |
mode | inline | modal | drawer | No | inline | inline: the element is the widget. modal: a centred dialog. drawer: a panel that slides in. A modal or drawer starts closed and renders nothing until it is opened. |
locale | BCP 47 tag | No | The page’s lang, then the browser’s language | The language of the widget’s own text. A non-English locale is fetched alongside the first request; English is the fallback for any missing string. |
theme | light | dark | No | Your widget’s setting, else the system | The colour scheme the widget starts in. Buyers can switch with the light / dark button in the widget’s top bar, and their choice is remembered on their device. Your primary colour, and any colour set in your page’s CSS, still apply in both schemes (Theming). |
fonts | off | No | - | Set to off to stop the widget asking for its own typeface. It then uses the system sans-serif. Useful under a strict content security policy. |
badge | standard | minimal | hidden | No | standard | The “Powered by” badge. Whether minimal and hidden are allowed depends on your plan. |
site | URL | No | https://quotometry.app | Where the badge links. |
config | string | No | - | Pre-selects one configuration id, for a widget that offers several. |
material | string | No | - | Pre-selects a material id. |
customer-token | string | No | - | A signed token identifying a signed-in buyer. See customer tokens. |
customer-email | string | No | - | An e-mail address you already know, when you do not sign tokens. It is a claim, not proof. |
return-url | URL | No | - | Where to send the buyer after a cart hand-off by redirect. |
metadata | JSON string | No | - | Carried onto the quote and order untouched, and echoed in order.created: your cart id, campaign or line reference. |
Attributes are live: change one and the widget re-renders. Changing widget starts a new buyer session.
Methods
| Method | Description |
|---|---|
open() | Shows a modal or drawer widget. Does nothing for an inline widget. |
close() | Hides it again. Escape and a click on the backdrop do the same. |
reset() | Throws the buyer’s draft away and starts over at the drop zone. |
setCustomer({ email, token }) | Identifies the buyer: token sets customer-token, email sets customer-email. Omit one to clear it. |
const el = document.querySelector('quotometry-quote');
el.open();
el.setCustomer({ token: customerToken });
el.reset();
A single-page app should call reset() when the buyer navigates away from a page that had a widget on it.
Events
Every event is a CustomEvent on the element. Events bubble and are composed, so document.addEventListener
works too.
| Event | Fires when | detail |
|---|---|---|
quotometry:quote-updated | A re-price finishes. | { totals, state, lines } |
quotometry:cart-add | The buyer presses your cart button. | { quote_id, number, totals, signature } |
quotometry:quote-saved | The buyer saves their quote to come back to. | { quote_id, url, emailed } |
quotometry:order-submitted | An order is placed through Quotometry. | { id, number, status, totals } |
quotometry:rfq-submitted | The buyer sends a request for quote that a person must answer. | { id, number } |
el.addEventListener('quotometry:cart-add', async (e) => {
await fetch('/cart/add', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ quotometry_quote: e.detail.quote_id, quotometry_sig: e.detail.signature }),
});
window.location.href = '/cart';
});
signature proves that the quote id came from your widget and not from the address bar. Your server checks it
with the shop’s shared secret before it adds anything to the cart. It is absent when the shop has not created a
shared secret. See how to check it.
Opening from a button
For a product page that should not download the widget until someone asks for it:
<button data-quotometry-open="wgt_live_YOURKEY" data-quotometry-mode="drawer" data-quotometry-material="petg">
Get an instant price
</button>
Every data-quotometry-* attribute on the button becomes the matching widget attribute, and the widget opens as a
modal unless data-quotometry-mode says otherwise. The loader starts downloading the widget when the pointer
moves over the button or the button gets focus, so the click still feels instant, and a page whose button is
never pressed never downloads it.
Mounting from JavaScript
The loader defines a global Quotometry object:
const handle = await Quotometry.mount('#quote', { widget: 'wgt_live_YOURKEY', mode: 'inline' });
const off = handle.on('quote:updated', (detail) => render(detail));
handle.open();
// later
off();
| Function | Description |
|---|---|
Quotometry.mount(target, options) | Creates a widget inside target (a selector or an element) and resolves with a handle once the widget is loaded. Option names become attributes; customerToken and customer-token both work. |
Quotometry.get(target) | Resolves with a handle for a <quotometry-quote> that is already in the page. |
Quotometry.open(target) | Opens a mounted widget, or the widget a data-quotometry-open button refers to. |
Quotometry.preload() | Downloads the widget without showing anything, to warm the cache. |
A handle has on(event, handler), which returns a function that removes the listener, plus open(), close(),
reset(), setCustomer() and element. It is a thin wrapper: the same events reach addEventListener on
handle.element, and on accepts both quote:updated and quotometry:quote-updated.
Theming
Set the --quotometry-* custom properties on the element itself (quotometry-quote { … }). They cross the shadow
boundary because custom properties inherit; nothing else does. If you set them on an ancestor such as :root,
the widget’s own defaults on the element win over them. The exception is --quotometry-color-primary, which has
no default and is therefore read from an ancestor too.
| Variable | Default | What it colours |
|---|---|---|
--quotometry-color-primary | None | Your brand colour, set here or in the console. It becomes the highlight and the main button, in light and dark. |
--quotometry-color-on-primary | #FFFFFF | Text on the primary colour. |
--quotometry-color-highlight | #A16207 (dark: #F5B83D) | Every selected state: the chosen part, option, tool and tab, and the focus ring. |
--quotometry-color-on-highlight | #FFFFFF (dark: #17181B) | Text on a selected option filled with the highlight. |
--quotometry-color-cta / --quotometry-color-on-cta | #161B22 / #FFFFFF (dark: #ECEDEF / #17181B) | The main button: Continue, Add to cart, Order. |
--quotometry-color-canvas | #F1F2F4 (dark: #0F1012) | The background behind the three panels. |
--quotometry-color-accent | #FF6A1A | Accents. |
--quotometry-color-surface | #FFFFFF | Panels. |
--quotometry-color-surface-2 | #F6F8FA | Insets and the viewer’s backing. |
--quotometry-color-text | #161B22 | Body text. |
--quotometry-color-muted | #57606A | Secondary text. |
--quotometry-color-border | #D0D7DE | Rules and outlines. |
--quotometry-color-success / -warning / -danger / -review | - | State colours. |
--quotometry-radius | 10px | Corner radius. |
--quotometry-font | Inter, then the system sans-serif | The typeface. Set it to inherit or a font stack to use your page’s font. |
--quotometry-density | 44px | Minimum control height. Lower it for a dense B2B interface; 44px is the comfortable touch target. |
--quotometry-viewer-bg | A light gradient (dark: a charcoal one) | The background behind the 3D model. |
--quotometry-heat-1 … --quotometry-heat-4 | - | The colour ramp of the wall-thickness heat map. |
The part is drawn in the colour the buyer picked, or in neutral grey before they pick one.
quotometry-quote {
--quotometry-color-primary: #0f766e;
--quotometry-radius: 2px;
--quotometry-density: 36px;
}
Most shops set the theme in the console instead, on the widget’s Look tab. It stores the same variables against the widget and applies them before first paint, with a preview in both schemes and a contrast check for every pair of colours.
How the two sources combine:
- A primary colour works the same from either place: it becomes the highlight and the main button in light
and dark, with
--quotometry-color-on-primary(white unless you set it) as their text. - A colour set in your page’s CSS wins over the console’s.
- Colours set in the console are the light scheme’s. A buyer who switches to dark gets Quotometry’s dark surfaces and text, with your primary colour carried over.
- Colours set in your page’s CSS apply in both schemes. To colour the highlight and the main button differently,
set
--quotometry-color-highlightand--quotometry-color-ctayourself.
Content Security Policy
The widget is tested against this policy. A host page in our compatibility matrix loads the widget under it and fails on any violation:
default-src 'self';
script-src 'self' https://cdn.quotometry.example;
connect-src 'self' https://api.quotometry.example;
img-src 'self' blob: data:;
style-src 'self' 'unsafe-inline';
worker-src 'self' blob:;
Replace the example hosts with the ones in your widget’s install snippet. Five things are worth knowing:
style-src 'unsafe-inline'is required. The widget injects its stylesheet as a<style>element inside its shadow root, andstyle-srcgoverns that even though it is not in your document.blob:is for the viewer, which builds the mesh and its preview image in memory. Without it the buyer gets a price but no picture of their part.- Files go straight to Quotometry’s file store when they can. If your policy (or the buyer’s network) does not let the browser reach the store, the widget sends the file through the API instead. The policy above therefore works as it is; the blocked attempt shows in the browser console as a refused connection, and the upload continues through the API.
- The typeface is optional. The widget loads Inter from beside its own script. Add
font-src https://cdn.quotometry.exampleto allow it, or setfonts="off"; without either, the widget uses the system sans-serif and the layout is unchanged. frame-ancestorsbelongs in a header. Browsers ignore it in a<meta>tag, so if you paste a policy into a<meta>element, that directive does nothing.
If your install snippet loads the script from your widget’s address on the API (/w/wgt_live_…/loader.js), add
the API host to script-src as well.
There is no inline script and no eval anywhere in the widget, which is why script-src needs nothing more than
the hosts the script comes from.
Next steps
- Embedding: frameworks, platforms and the iframe embed.
- JavaScript SDK: build your own interface on the buyer API.
- Cart recipes: handle
quotometry:cart-addon your server. - Widgets and theming: the same settings from the console.