Death modal
Build a full-screen “you died” overlay with a respawn countdown and two action buttons. ~10 minutes.
Table of contents
- What we’re building
- Step 1 — Document
- Step 2 — Tinted full-screen backdrop
- Step 3 — Centered content column
- Step 4 — Title
- Step 5 — Killed-by line
- Step 6 — Respawn timer
- Step 7 — Buttons row
- Step 8 — Test in Play
- Step 9 — Wire up the buttons
- Step 10 — Live countdown
- Step 11 — Hover polish
- What you learned
- You’re done
What we’re building
┌────────────────────────────────────────────────────────┐
│ (dim red overlay across the entire screen) │
│ │
│ │
│ YOU DIED │
│ │
│ Killed by Bandit Archer │
│ │
│ Respawning in 5... │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ RESPAWN │ │ MAIN MENU │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ │
└────────────────────────────────────────────────────────┘
A red-tinted full-screen Panel with centered content.
Step 1 — Document
Create Assets/UI/death_modal.sui. Output:
- Class Name:
DeathModal - Namespace:
Game.UI
Step 2 — Tinted full-screen backdrop
- Drop a Panel on the root.
- Rename: Backdrop
- Anchor: Stretch, X/Y/W/H =
0/0/0/0(zero margins = full screen). - Style:
- Background:
rgba(80, 0, 0, 0.7)(dark red, 70% opaque) - Pointer Events: All (block clicks to the world below)
- Background:
Step 3 — Centered content column
Inside Backdrop:
- Drop a VerticalBox.
- Rename: ContentColumn
- Anchor: MiddleCenter
- X:
0, Y:0, Width:480, Height:380 - Gap:
24 - AlignItems:
Center - JustifyContent:
Center
Everything we add now goes inside ContentColumn.
Step 4 — Title
- Drop a Text in ContentColumn.
- Rename: TitleText
- Text:
YOU DIED - Font Size:
72, Font Weight: ExtraBold - Color:
#ef4444 - TextAlign: Center
Step 5 — Killed-by line
- Drop a Text in ContentColumn.
- Rename: KilledByText
- Text:
Killed by Bandit Archer - Font Size:
20, Font Weight: Normal - Color:
#ffffffcc
Step 6 — Respawn timer
- Drop a Text in ContentColumn.
- Rename: TimerText
- Text:
Respawning in 5... - Font Size:
28, Font Weight: SemiBold - Color:
#ffffff
This will be live-updated by your gameplay code; the static text is just a preview.
Step 7 — Buttons row
- Drop a HorizontalBox in ContentColumn.
- Rename: ButtonsRow
- Width:
400, Height:60, Gap:24 - JustifyContent:
Center
Inside ButtonsRow:
- Drop a Button.
- Name: RespawnButton
- Button Text:
RESPAWN - Font Size:
18, Font Weight: Bold - Color:
#ffffff - Background:
#dc2626 - Border Radius:
8 - Width:
180, Height:48
- Drop another Button.
- Name: MainMenuButton
- Button Text:
MAIN MENU - Font Size:
18, Font Weight: Bold - Color:
#ffffff - Background:
#374151 - Border Radius:
8 - Width:
180, Height:48
Save (Ctrl+S).
Step 8 — Test in Play
Click Test in Play. The death modal overlays the test stage scene. Walk around — the overlay stays full-screen and the buttons (visually) catch hover.
Step 9 — Wire up the buttons
After Compile (Ctrl+B), open Code/UI/DeathModal.razor:
@inherits PanelComponent
@attribute [StyleSheet]
<root>
<div class="sui-elem-1 backdrop">
<div class="sui-elem-2 content-column">
<label class="title-text">YOU DIED</label>
<label class="killed-by-text">Killed by Bandit Archer</label>
<label class="timer-text">Respawning in 5...</label>
<div class="sui-elem-6 buttons-row">
<button class="respawn-button">RESPAWN</button>
<button class="main-menu-button">MAIN MENU</button>
</div>
</div>
</div>
</root>
You don’t edit this file. Instead, create a sibling partial in Code/UI/:
// Code/UI/DeathModal.Logic.cs
using Sandbox;
namespace Game.UI;
public partial class DeathModal
{
public string KilledByName { get; set; } = "Unknown";
public float RespawnSeconds { get; set; } = 5f;
public event System.Action OnRespawnClick;
public event System.Action OnMainMenuClick;
protected override void OnAwake()
{
base.OnAwake();
// Find the buttons by their generated class names and wire them up
var respawn = Panel.Descendants.FirstOrDefault(p => p.HasClass("respawn-button"));
var menu = Panel.Descendants.FirstOrDefault(p => p.HasClass("main-menu-button"));
if (respawn != null) respawn.AddEventListener("onclick", () => OnRespawnClick?.Invoke());
if (menu != null) menu.AddEventListener("onclick", () => OnMainMenuClick?.Invoke());
}
protected override int BuildHash() => System.HashCode.Combine( KilledByName, (int)RespawnSeconds );
}
(V1.5 will expose buttons as proper [Property] references so you won’t need the Descendants scan.)
Step 10 — Live countdown
To update the timer text from gameplay code, you’d typically use a binding:
// Where you spawn the modal
var modal = ScreenPanelHost.Components.Create<DeathModal>();
modal.KilledByName = killer.DisplayName;
modal.RespawnSeconds = 5f;
// Per frame, refresh the timer
async Task CountdownAsync()
{
while ( modal.RespawnSeconds > 0 )
{
modal.RespawnSeconds -= Time.Delta;
await Task.Frame();
}
Respawn();
}
But for the text to actually update in the panel, you need to bind it. V1 emits a static label; V1.5 will introduce [Property] bindings via the Bindings tab which makes this trivial:
TimerText.Text ← $"Respawning in {(int)Math.Ceiling(RespawnSeconds)}..."
Until then, in your DeathModal partial:
protected override int BuildHash() => System.HashCode.Combine( RespawnSeconds );
public override void Tick()
{
base.Tick();
var timer = Panel.Descendants.FirstOrDefault(p => p.HasClass("timer-text")) as Label;
if (timer != null) timer.Text = $"Respawning in {(int)Math.Ceiling(RespawnSeconds)}...";
}
Forces a re-render every time RespawnSeconds changes.
Step 11 — Hover polish
Open Code/UI/DeathModal.User.scss:
DeathModal {
.respawn-button, .main-menu-button {
transition: background-color 0.15s ease, transform 0.1s ease;
cursor: pointer;
}
.respawn-button:hover { background-color: #ef4444; transform: scale(1.04); }
.respawn-button:active { transform: scale(0.98); }
.main-menu-button:hover { background-color: #4b5563; transform: scale(1.04); }
.main-menu-button:active { transform: scale(0.98); }
// A subtle fade-in when the modal appears
&:intro {
opacity: 0;
transition: opacity 0.3s ease-out;
}
}
Recompile + Play to see hovers and the fade-in.
What you learned
- Stretch anchor with all-zero margins for full-screen overlays.
- Centered content via
Anchor: MiddleCenter+ flexJustifyContent: Center. - Buttons with hover effects via
.User.scss. - Manual partial class for event wiring (until V1.5 bindings ship).
:introtransition for entrance animation.
You’re done
You’ve built three real UIs:
- Survival HUD — corners + bars.
- Inventory screen — grids + multi-region layout.
- Death modal — full-screen overlay with logic.
From here, the Element reference and the Concepts section cover everything else. The Architecture section is for when you want to extend or modify SUI Designer itself.