Installation
-
Place a container. Put an element carrying
data-mo-toolwhere you want the calculator to appear. The value is the tool’skey.<div data-mo-tool="kdv"></div> -
Add the loader. Once per page, preferably at the end of the body:
<script src="https://api.mirac-ozdemir.com/widget.js" async></script> -
Done. When the page loads, the loader replaces the container with an iframe.
You can put a fallback link inside the container (visible if the script does not load). The loader replaces the container’s existing content with the iframe:
<div data-mo-tool="kdv"> <a href="https://mirac-ozdemir.com/tr/araclar/kdv-hesaplama">Open the VAT calculator</a></div>You can also write the same container as a custom element (<mo-calculator tool="kdv">): Custom elements and SDK. For a search box and a category list see Widget kinds.
Attributes
Section titled “Attributes”The attributes below are for a data-mo-tool (calculator) container. The extra attributes of the search and category widgets are on the Widget kinds page.
| Attribute | Values | Default | Description |
|---|---|---|---|
data-mo-tool |
Tool key: ^[a-z0-9]+(-[a-z0-9]+)*$, 2–64 characters |
required | The key of the tool to embed. |
data-locale |
tr, en |
tr |
Interface language. If the tool is not published in that language, the widget shows “not found”. |
data-theme |
auto, light, dark, inherit |
auto |
auto follows the visitor’s system theme; inherit follows your page’s theme (see below). |
data-header |
true/false (1/0, yes/no) |
true |
false hides the tool title; useful when you use your own heading. |
data-height |
Integer px, clamped to 120–10000 | remembered height, else 480 |
The initial height; it then adjusts to the content automatically. |
data-accent |
6 hex digits, # optional (0a7d5a) |
site colour | Primary colour; the text colour on it is derived from its luminance. |
data-radius |
none, sm, md, lg |
md |
Corner radius scale. |
data-density |
comfortable, compact |
comfortable |
Spacing scale. |
data-view |
full, result |
full |
result shows only the result panel: Widget kinds. |
data-autorun |
true/false |
false |
Calculates right after the inputs are applied. |
data-input-<field> |
prefilled value (≤ 64 characters) | none | Fills a single input (see below). |
data-inputs |
JSON object | none | Fills several inputs. |
data-title |
text (up to 200 characters) | localized title | The iframe’s title attribute (accessibility). |
data-lazy |
true/false |
true |
false loads the iframe eagerly. |
An invalid value is written to the console as an English console.warn and that container is skipped (e.g. [MiracOzdemirWidget] Invalid data-locale value: "de" (expected one of: tr, en)). Values are validated against strict allow-lists; if you generate the options dynamically from elsewhere, be aware of this behaviour.
Prefilled inputs and autorun
Section titled “Prefilled inputs and autorun”To fill the form on load, use data-input-<field> attributes. <field> is the name of the tool’s input field (the field name in the tool definition); in the attribute it is written in kebab-case and the loader converts it to camelCase (data-input-vat-rate-option → vatRateOption).
<div data-mo-tool="kdv" data-input-amount="1000" data-input-mode="exclusive" data-input-vat-rate-option="standard" data-autorun="true"></div>You can give the same values as JSON; if both are used, the data-input-* values override those in the JSON:
<div data-mo-tool="kdv" data-inputs='{"amount":"1000","mode":"exclusive"}' data-autorun="true"></div>Rules:
- Values use the same canonical format as calculation requests: decimals as strings (
"1234.56"), booleanstrue/false, enums as the option key, datesYYYY-MM-DD. - At most 50 fields, each value at most 64 characters. Field names must match
^[a-z][A-Za-z0-9_]{0,63}$. - Unknown fields and invalid values are silently ignored (the same rule as share links); the widget still opens.
data-autorun="true"runs the calculation by itself once the inputs are applied; themo:resultevent arrives after that calculation.- An invalid
data-inputs(not a JSON object) logs a warning and the container is skipped.
To provide values later, use setInputs.
Theme: inherit
Section titled “Theme: inherit”data-theme="inherit" takes the widget’s theme from your page. The loader checks, in order: a data-theme, data-bs-theme or data-color-mode attribute (light/dark) on the container or its ancestors, a dark/theme-dark/dark-mode (or light/theme-light/light-mode) class, then the computed color-scheme, and finally prefers-color-scheme. If your page’s theme changes at runtime (those attributes/classes change on <html> or <body>, or the system theme changes), the widget is updated without reloading the iframe.
Placeholder and layout shift (CLS)
Section titled “Placeholder and layout shift (CLS)”While the iframe loads, the loader draws a skeleton placeholder in the container and keeps the iframe invisible until it is ready, so the content is not pushed down. The initial height is determined in this order: data-height if given; otherwise the last measured height for the same {tool, locale, view} (kept in your site’s localStorage under a mo-widget-h:… key, best effort); otherwise 480.
- If you want no shift at all, give the tool’s typical height with
data-height. data-lazy="false"loads the iframe immediately (for widgets near the top of the page).- The loader also adds a
<link rel="preconnect">to the site origin; the first load gets faster. - If the container’s
positionisstatic, the loader sets it torelative(for the placeholder).
Multiple widgets
Section titled “Multiple widgets”You can put as many widgets as you like on the same page; adding the loader once is enough. If you add the script twice by accident, the second load only rescans; no duplicate widgets are created.
<div data-mo-tool="kdv" data-height="520"></div><div data-mo-tool="gelir-vergisi-hesaplama" data-theme="dark" data-accent="0a7d5a"></div><script src="https://api.mirac-ozdemir.com/widget.js" async></script>Dynamic content and SPAs
Section titled “Dynamic content and SPAs”The loader scans once when the page loads. If you add containers later (with a framework, with AJAX), mount them with window.MiracOzdemirWidget:
container.setAttribute('data-mo-tool', 'kdv');window.MiracOzdemirWidget.mount(container); // returns the iframe, or null if invalidCall unmount(container) before removing the container. To change options afterwards you usually do not need unmount: update. Framework examples: Framework integrations.
iframe attributes
Section titled “iframe attributes”The loader creates the iframe with these attributes (for accessibility and security):
title: localized, e.g.Calculator: kdv(tr:Hesaplama araci: kdv); or yourdata-title. When the iframe becomesready, and unless you gavedata-title, it is replaced with the tool’s title.width="100%",loading="lazy"(eagerwithdata-lazy="false")referrerpolicy="strict-origin-when-cross-origin"sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"- Style:
width:100%; border:0; display:block; height:<px>;opacity:0untilready
Requirements
Section titled “Requirements”- Your page must be served over HTTPS (mixed content is blocked).
- If your page has a Content-Security-Policy, allow the script and iframe sources: Security and CSP.
- No cookies or user registration are needed.
- To pin the script to a fixed version with an integrity (SRI) check: Versioning policy.