Engineering
Shipping the IndexFox embed widget: iframes, postMessage, and small bundles

The IndexFox widget you install on your site is deliberately uninteresting. That's the feature. One script tag. An iframe. A typed message protocol. Almost nothing of ours runs in your origin.
Why iframe
Embedding third-party UI into someone else's website is a CSS war. Your customer's body { font-family } fights our input { padding }. Their * { box-sizing } wins, by accident, until it doesn't. We have shipped widgets the other way (Shadow DOM, scoped CSS). We will never do it again on a marketing-site-grade widget.
An iframe gives us a clean document. Our styles, our fonts, our layout. The host site's CSS does not bleed in. The host site's analytics does not see our DOM. It's an old idea and it still works.
The postMessage protocol
Configuration travels from host to iframe via window.postMessage. The schema is intentionally tiny:
{
type: 'indexfox:config',
config: {
websiteId: '...', // required
triggerKey: 'k', // Cmd+K / Ctrl+K
triggerModifier: 'Meta', // 'Meta' on mac, 'Control' elsewhere
color: '#2563eb' // any valid CSS color
}
}
The host calls iframe.contentWindow.postMessage(...). The widget reads it, validates, and applies. The widget never trusts the host more than it has to — every field is type-checked, the color is run through a CSS parser, the website ID is validated against the backend.
One color in, a derived palette out. Hover state, focus state, focus ring opacity — all computed from the one color the host gave us. This is how customers get a branded widget without us shipping a theming UI.
The bundle
The widget is Svelte. The build output is small enough that gzip and brotli matter more than minification. The interesting numbers:
- One
<script>tag in the host page, sub-1KB. - The iframe payload is lazy — nothing loads until the user opens the widget.
- No vendored UI framework runtime in the host page. Svelte compiles to vanilla JS.
We do not preload models, embeddings, or any heavy state at host-page load. The iframe is dormant until the trigger key fires. The host site's Lighthouse score does not change after installing us. We've tested this on enough customer sites to claim it as a feature.
Keyboard-first, accessible by default
Cmd+K / Ctrl+K to open. Arrow keys to navigate results. Enter to commit. Esc to close. Tab traps inside the modal. A live region announces "N results" when the result list updates. The widget is keyboard-only operable end-to-end. We don't ship a "screen reader mode" — the same widget works for everyone, which is the only definition of accessible that matters.
What we'd build differently if we started today
The web platform has moved. View transitions, popover, anchor positioning, the upcoming @scope in CSS — all of it nibbles at the reason iframes were the right choice in 2024. We will probably ship an "inline mode" within the next year for customers who want the widget to participate in their page's animation, with the iframe mode staying default. The decision tree will be: do you control your host page's CSS rigorously enough to trust scoped styles? If yes, inline. If no, iframe.
The host integration will not change. One script tag. Same protocol. Different rendering target. We owe customers that level of stability.