Skip to content

HTML and CSS: overlays and panels

Lesson 14.11

Some GUIs are easier to write as a document: text that flows, boxes with padding and borders, styles you change in one place. Celestial shows RmlUi documents for this. RML looks like HTML and RCSS like CSS. Your document can be a HUD overlay, a panel inside one of your screens, or a screen of its own.

A HUD overlay

The document, assets/my_mod/gui/status.rml:

<rml>
<head>
    <link type="text/rcss" href="celestial:theme.rcss"/>
    <link type="text/rcss" href="status.rcss"/>
</head>
<body data-model="mod">
    <div class="box cl-hud-panel">
        <span class="cl-small">{{ biome }}</span>
        <span class="cl-small cl-accent" data-if="night">Night</span>
    </div>
</body>
</rml>

The style sheet, assets/my_mod/gui/status.rcss:

body { width: 100%; height: 100%; margin: 0; }
.box { display: block; width: 100%; height: 100%; padding: 2px 4px; }

And the Lua side:

local status
cl.on("lifecycle.client_setup", function()
  status = cl.rml.open{
    path = "assets/my_mod/gui/status.rml",
    as_screen = false,                  -- an overlay
    id = "status", label = "Biome and time",
    anchor = "top_right", x = 4, y = 4,
    width = 90, height = 12,
    model = { "biome", "night" },       -- the variables the document uses
  }
  status:set("biome", "Plains")
  status:set("night", false)
end)

{{ biome }} shows a value; data-if="night" shows the element only while night is true. When you call status:set("night", true), only that part of the document updates.

The overlay is a HUD element: the player finds it in the HUD editor under your mod’s name and can move, scale or hide it there. It never takes the mouse or keyboard and it draws under every screen.

Following the player’s look

celestial:theme.rcss holds classes for the player’s Appearance: cl-hud-panel, cl-panel, cl-button, cl-h1 to cl-h3, cl-body, cl-small, cl-accent and the theme’s colours as cl-c-.... In the Vanilla style they look like the game, in Fancy like Celestial, in every theme. The body also gets cl-fancy or cl-vanilla, so you can write .cl-fancy .box { ... } for your own touches.

Lists and objects

Declare a list with [] and fill it with a Lua table:

local doc = cl.rml.open{ path = "assets/my_mod/gui/party.rml", as_screen = false, width = 100, height = 60,
                         model = { "members[]" } }
doc:set("members", { { name = "Alex", hp = 20 }, { name = "Sam", hp = 6 } })
<div data-for="m : members" data-class-low="m.hp < 8">{{ m.name }}: {{ m.hp }}</div>

Buttons in screens and panels

Overlays have no input, but a document shown as a screen or panel does. Name the actions it may call and handle them in Lua:

cl.rml.open{
  path = "assets/my_mod/gui/shop.rml",
  model = { "items[]" },
  actions = { "buy" },
  on_action = function(doc, action, item_id)
    print("buy", item_id)
  end,
}
<button data-event-click="buy(item.id)" data-for="item : items">{{ item.name }}</button>

The sandbox

A document can only use your mod’s own files. Links to websites, absolute paths, .., <script> tags and onclick-style attributes are refused when the document opens (cl.rml.open raises permission_denied and says why). Pictures come from the resource packs: my_mod:textures/gui/coin.png. All logic stays in your Lua code.

Exercise

Make an overlay with a counter in the top left corner that goes up by one every time you type !broke in chat.

Show a solution
<rml>
<head><link type="text/rcss" href="celestial:theme.rcss"/></head>
<body data-model="mod"><div class="cl-hud-panel cl-small" style="padding: 2px 4px;">Broken: {{ count }}</div></body>
</rml>
local doc, count = nil, 0
cl.on("lifecycle.client_setup", function()
  doc = cl.rml.open{ path = "assets/my_mod/gui/broken.rml", as_screen = false, width = 70, height = 12,
                     anchor = "top_left", x = 4, y = 30, model = { "count" } }
  doc:set("count", 0)
end)
cl.on("client.chat_send", function(e)
  if e.message == "!broke" then
    e:cancel()
    count = count + 1
    doc:set("count", count)
  end
end)

Recap: cl.rml.open{ as_screen = false, width, height, model } makes an overlay, doc:set(name, value) updates it, celestial:theme.rcss follows the player’s style, and actions + on_action handle buttons in screens and panels. The reference is HTML/CSS Overlays and Panels.