Skip to content

Layouts

Build a custom radar detector skin from HTML and CSS using the data-hud-* binding grammar.

A layout is a skin: a folder under layouts/ holding a module.json manifest, a ui.html entry point, and its images. Folders are auto-discovered on server start and appear in the HUD Layout selector in /tdetector.

Every element that changes carries a data-hud-* attribute and tLib's HudBinder writes state into it — the grammar tRadar's, tELS's and tRadio's skins also use. A skin decides only how it looks; the timing is shared.

Bundled layouts

FolderSizeWhat it is
RDX-8000480 × 197Compact windshield unit, the default. Red-outlined speed box, one large block arrow, band / frequency / a skewed green-to-red segment meter
RDX-4000480 × 211Wide strip. Cyan chamfered speed tag, a cyan rule above a status row, and a stepped meter whose bars rise in height as well as colour
ARC-360480 × 189360-degree unit. Blue speed block, orange band tile, frequency over a red stepped meter. Direction is eight physical arrow lenses, not on-screen arrows

All three on the same alert — 38 MPH, Ka on 34.700 GHz, six of eight bars, source ahead:

All three are dot-matrix OLEDs: a .px overlay lays a black mesh on a 2–3px pitch, and each lit element carries a text-shadow or box-shadow halo. Keep both — and nothing below ~12px is legible.

Folder structure

A layout folder holds module.json, ui.html and its images; layouts/sounds/ holds the shared .wav files, which every layout uses. Image src attributes are rewritten to absolute before injection, so use relative paths. Sounds are not a layout's concern — they are mapped in /tdetector → Sounds.

The binding grammar

AttributeEffect
data-hud-text="key"textContent becomes state[key]
data-hud-show="key"Visible while state[key] is truthy
data-hud-hide="key"Hidden while state[key] is truthy
data-hud-eq="key:value"Gains .active while state[key] === value
data-hud-led="key"Gains .active while state[key] is truthy
data-hud-html="key"innerHTML becomes state[key]
data-hud-attr="key:attr"Sets attr to state[key]; removes it when null, "" or false
data-hud-led-color="key"backgroundColor from an { r, g, b, a } value
data-hud-btn="action"Click sends action to Lua — mute, dim or mark

Reach for the first six: no tDetector state key is an { r, g, b, a } colour, and data-hud-text covers what data-hud-html would. In play the HUD takes no pointer events, so data-hud-btn regions are reachable only in the HUD editor and the marketplace preview.

State keys

The keys the driver emits.

KeyTypeDescription
poweredbooleanDetector on and the player is in a vehicle
bootstring"logo", "selftest", "sweep", "gps", or "" once complete
idlebooleanPowered, booted, nothing detected
alertbooleanA radar source is being tracked
bandKeystring"ka", "k", "x", "laser", or ""
bandLabelstringThe same band as printable letters ("Ka")
freqTextstringFrequency in GHz, e.g. "34.712"
bar1 … bar8booleanSignal strength, lit cumulatively
speedTextstringRounded speed
unitLabelstring"MPH" or "KM/H"
arrowFront arrowRear arrowLeft arrowRightbooleanDirection; flashes in time with the blip
mutedbooleanAlerts silenced
dimbooleanDisplay turned down, across the whole HUD
multiThreatbooleanMore than one source is audible
bogeyTextstringHow many, e.g. "3". Empty while idle.
modeKeystring"constant", "instant", or "" — for data-hud-eq
modeLabelstring"I/O" for instant-on, empty for constant-on

Create a layout

Create the folder

layouts/MyLayout/ — the folder name is what players see in the layout selector.

Add module.json

json
{
  "schema": 1,
  "kind": "layout",
  "id": "MyLayout",
  "name": "My Layout",
  "version": "1.0.0",
  "author": "You",
  "license": "UNLICENSED"
}

id must equal the folder name exactly, version must look like 1.2.3, and kind must be "layout". Each is rejected outright rather than warned about, and sounds and themes are reserved.

Add ui.html

One root element with fixed pixel bounds, its own <style> block, and the markup. Declare data-radio-width / data-radio-height on the root: the marketplace preview sizes its canvas from them, and the HUD editor needs them to place the skin.

Add images, then restart

ini
restart tDetector

Geometry a skin must provide

The HUD editor measures the injected container's unscaled, in-flow bounds: RDX-8000 is 480 × 197, RDX-4000 480 × 211, ARC-360 480 × 189.

A skin must keep a stable, intrinsic, in-flow footprint, not draw outside it, and not size itself in viewport units.

Minimal example

html
<div class="ui-root det" data-radio-width="200" data-radio-height="80"
     style="position:relative;width:200px;height:80px;">
  <style>
    .det { background: #111; color: #0f0; font-family: monospace; }
    .det .row { position: absolute; left: 10px; }
    /* eq elements toggle a class, so they may start hidden */
    .det .boot { position: absolute; inset: 0; display: none; }
    .det .boot.active { display: block; }
  </style>

  <div class="boot" data-hud-eq="boot:logo">MY DETECTOR</div>
  <div class="row scan" data-hud-show="idle">Scanning…</div>
  <div class="row band" data-hud-show="alert" data-hud-text="bandLabel"></div>
  <div class="row freq" data-hud-show="alert" data-hud-text="freqText"></div>
  <div class="row speed" data-hud-show="powered" data-hud-text="speedText"></div>

  <div data-hud-btn="mute" role="button" tabindex="0"
       style="position:absolute;right:6px;bottom:6px;width:30px;height:20px;"></div>
</div>

UI Studio

Build and publish custom layouts with the UI Studio — drafts, design vs HTML mode, lint rules, bundle limits, forking and publishing are all in the Studio itself.

Next steps

  • Reference — the sound slots a skin's alerts play through.
  • Usage — what each readout means to the player you are designing for.

Still stuck? Support can answer it directly.

Was this page helpful?