You can write a widget as a single HTML file, containing Javascript and CSS, and use that by pasting it into the HTML tab of the External Widget window. For example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Widget</title>
<!-- Framework CSS + JS — required for all widgets -->
<link rel="stylesheet" type="text/css"
href="https://mytrader.fxbluelabs.com/css/widget-css"/>
<script src="https://mytrader.fxbluelabs.com/scripts/widget-js"></script>
<style>
/* Use framework CSS variables so the widget follows the user's theme
automatically. Never hard-code colours — always fall back to --clr-*. */
html, body {
margin: 0;
padding: 0;
height: 100%;
font-family: var(--font-family, system-ui);
font-size: var(--font-size-base, 13px);
color: var(--clr-text, #e4e4e4);
/* The framework does not publish a widget-background variable;
leaving background transparent lets the platform frame show
through correctly in every theme. */
background: transparent;
}
.widget-header {
padding-block: 10px;
padding-inline-start: 14px;
padding-inline-end: var(--widget-chrome-inset-horizontal, 40px);
border-bottom: 1px solid var(--clr-border1, rgba(255,255,255,0.08));
font-weight: 600;
}
</style>
</head>
<body>
<div class="widget-header">My Widget</div>
<div id="content"></div>
<script>
// ------------------------------------------------------------------
// 1. Create a framework instance.
// Widgets must do this explicitly; scripts and UDIXes get one
// automatically.
// ------------------------------------------------------------------
var Framework = new FXB.Framework();
// ------------------------------------------------------------------
// 2. Choose a namespace for persisted settings.
// This ID both scopes your data and authorises later writes — treat
// it as mildly sensitive. Prefer category settings (work everywhere)
// over private settings (page-container-only; silently no-op in
// floating windows).
// ------------------------------------------------------------------
var SETTINGS_ID = "my-widget-unique-id";
// ------------------------------------------------------------------
// 3. OnLoad is the ONLY safe place to start using framework APIs.
// Anything that touches Framework.Instruments, Framework.Ask, etc.
// before OnLoad fires may fail silently.
// ------------------------------------------------------------------
Framework.OnLoad = function() {
// Category settings must be loaded before they can be saved. The
// callback fires asynchronously with the stored object, or {} if
// nothing has been saved yet.
Framework.LoadCategorySettings(SETTINGS_ID, function(settings) {
settings = settings || {};
// ... restore UI state from `settings` ...
render();
});
};
// ------------------------------------------------------------------
// 4. React to runtime events. IMPORTANT: a single message can carry
// multiple update types at once (e.g. a price tick AND an order
// change). Always use Msg.is(...) with SEPARATE `if` blocks.
// Never switch on .type and never use else-if — both silently
// drop updates when more than one type is present. See §4.1.
// ------------------------------------------------------------------
Framework.OnMessage = function(Msg) {
if (!Msg) return;
if (Msg.is(FXB.MessageTypes.THEME)) {
// CSS variables and body classes update automatically; uncomment
// below only if you draw theme-dependent colours into canvas/SVG.
// redrawCanvas();
}
if (Msg.is(FXB.MessageTypes.CATEGORY_SETTINGS_CHANGE)) {
// Another instance of this widget changed settings — or we did.
// Settings are already updated in Framework.categorySettings;
// no need to call LoadCategorySettings() again.
render();
}
};
// ------------------------------------------------------------------
// 5. Persist user choices via SaveCategorySettings. All instances of
// this widget will then receive CATEGORY_SETTINGS_CHANGE.
// ------------------------------------------------------------------
function saveSettings(data) {
// Note: SaveCategorySettings() takes ONLY the data — no ID parameter.
// The ID was bound by the earlier LoadCategorySettings() call on start-up.
// Passing an ID here will silently save the wrong value with no error.
Framework.SaveCategorySettings(data);
}
function render() {
// ... redraw UI from Framework.categorySettings ...
}
</script>
</body>
</html>