Game — Health / Mana / XP Bar Variants
A neon sci-fi HUD showcase of six game stat-bar variants: a segmented ten-cell health bar, a gradient health bar with a delayed damage-chip trail, a shield-over-health stack that absorbs hits first, a self-regenerating mana bar, an XP bar with level-up overflow flash, and a centered boss banner with phase ticks. Combat-sim buttons deal damage, heal, cast spells and grant XP, driving fill animations, color thresholds, bar shake, toasts and a pulsing low-health red vignette.
MCP
Code
:root {
--bg: #0a0b10;
--bg-2: #12131c;
--panel: #171926;
--panel-2: #1f2233;
--text: #e7e9f3;
--muted: #9aa0bf;
--line: rgba(231, 233, 243, 0.10);
--line-2: rgba(231, 233, 243, 0.18);
--accent: #00e5ff;
--accent-2: #7c4dff;
--accent-3: #ff3d71;
--success: #36e27a;
--warn: #ffc857;
--danger: #ff4d4d;
--glow: 0 0 18px rgba(0, 229, 255, 0.45);
--r-sm: 6px;
--r-md: 10px;
--r-lg: 16px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
background:
radial-gradient(1200px 600px at 80% -10%, rgba(124, 77, 255, 0.12), transparent 60%),
radial-gradient(900px 500px at 10% 110%, rgba(0, 229, 255, 0.08), transparent 60%),
var(--bg);
color: var(--text);
font-family: "Inter", system-ui, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
min-height: 100vh;
}
/* ---------- low-health vignette ---------- */
.vignette {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 50;
opacity: 0;
background: radial-gradient(ellipse at center, transparent 55%, rgba(255, 30, 40, 0.45) 100%);
transition: opacity 0.4s ease;
}
.vignette.is-active {
opacity: 1;
animation: vignette-pulse 1.1s ease-in-out infinite;
}
@keyframes vignette-pulse {
0%, 100% { opacity: 0.55; }
50% { opacity: 1; }
}
/* ---------- layout ---------- */
.page {
max-width: 1060px;
margin: 0 auto;
padding: 48px 24px 64px;
}
.page-head {
margin-bottom: 28px;
}
.kicker {
font-family: "Orbitron", sans-serif;
font-weight: 700;
font-size: 0.72rem;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--accent);
margin: 0 0 10px;
text-shadow: var(--glow);
}
.page-head h1 {
font-family: "Orbitron", sans-serif;
font-weight: 900;
font-size: clamp(1.8rem, 4.5vw, 2.8rem);
margin: 0 0 12px;
letter-spacing: 0.02em;
background: linear-gradient(100deg, var(--text) 30%, var(--accent) 75%, var(--accent-2));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.lede {
color: var(--muted);
max-width: 60ch;
margin: 0;
}
.lede strong {
color: var(--text);
}
/* ---------- panels ---------- */
.panel {
background: linear-gradient(165deg, var(--panel) 0%, var(--bg-2) 100%);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: inset 0 1px 0 rgba(231, 233, 243, 0.05), 0 14px 34px rgba(0, 0, 0, 0.4);
}
.card {
padding: 22px;
position: relative;
clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 18px, 100% 100%, 18px 100%, 0 calc(100% - 18px));
transition: border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
border-color: var(--line-2);
transform: translateY(-2px);
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 18px;
margin-top: 18px;
}
.card-wide {
grid-column: 1 / -1;
}
.card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 16px;
}
.card-head h2 {
font-family: "Orbitron", sans-serif;
font-weight: 700;
font-size: 1rem;
letter-spacing: 0.06em;
text-transform: uppercase;
margin: 0;
}
.badge {
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--accent);
border: 1px solid rgba(0, 229, 255, 0.4);
background: rgba(0, 229, 255, 0.08);
padding: 4px 10px;
border-radius: 999px;
white-space: nowrap;
}
.badge-danger {
color: var(--accent-3);
border-color: rgba(255, 61, 113, 0.45);
background: rgba(255, 61, 113, 0.08);
}
.badge-shield {
color: var(--warn);
border-color: rgba(255, 200, 87, 0.45);
background: rgba(255, 200, 87, 0.08);
}
.badge-mana {
color: var(--accent-2);
border-color: rgba(124, 77, 255, 0.5);
background: rgba(124, 77, 255, 0.1);
}
.badge-xp {
color: var(--success);
border-color: rgba(54, 226, 122, 0.45);
background: rgba(54, 226, 122, 0.08);
font-family: "Orbitron", sans-serif;
}
/* ---------- character row ---------- */
.char-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
}
.portrait {
width: 44px;
height: 44px;
flex: 0 0 44px;
display: grid;
place-items: center;
font-family: "Orbitron", sans-serif;
font-weight: 700;
font-size: 0.85rem;
color: var(--accent);
background: linear-gradient(150deg, var(--panel-2), var(--bg-2));
border: 1px solid rgba(0, 229, 255, 0.4);
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px));
}
.portrait-2 { color: var(--accent-3); border-color: rgba(255, 61, 113, 0.45); }
.portrait-3 { color: var(--warn); border-color: rgba(255, 200, 87, 0.45); }
.portrait-4 { color: var(--accent-2); border-color: rgba(124, 77, 255, 0.5); }
.char-name {
margin: 0;
font-weight: 700;
font-size: 0.95rem;
}
.char-role {
margin: 0;
color: var(--muted);
font-size: 0.78rem;
}
/* ---------- tracks & fills ---------- */
.track {
position: relative;
height: 22px;
background: rgba(0, 0, 0, 0.55);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
overflow: hidden;
clip-path: polygon(0 0, calc(100% - 9px) 0, 100% 9px, 100% 100%, 9px 100%, 0 calc(100% - 9px));
}
.fill {
position: absolute;
inset: 0;
width: 100%;
transform-origin: left center;
transform: scaleX(1);
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.hp-fill {
background: linear-gradient(180deg, #5dff9c 0%, var(--success) 45%, #15a04e 100%);
box-shadow: 0 0 14px rgba(54, 226, 122, 0.5);
}
.hp-fill.is-low {
background: linear-gradient(180deg, #ff8a8a 0%, var(--danger) 45%, #b91c1c 100%);
box-shadow: 0 0 16px rgba(255, 77, 77, 0.65);
animation: low-pulse 0.9s ease-in-out infinite;
}
.hp-fill.is-mid {
background: linear-gradient(180deg, #ffe29a 0%, var(--warn) 45%, #c9921f 100%);
box-shadow: 0 0 14px rgba(255, 200, 87, 0.5);
}
@keyframes low-pulse {
0%, 100% { filter: brightness(1); }
50% { filter: brightness(1.45); }
}
.chip-fill {
background: linear-gradient(180deg, rgba(255, 120, 120, 0.95), rgba(200, 40, 40, 0.9));
transition: transform 0.9s cubic-bezier(0.4, 0, 0.2, 1) 0.45s;
}
.shield-fill {
background: linear-gradient(180deg, #bfe9ff 0%, var(--accent) 50%, #0593a6 100%);
box-shadow: 0 0 16px rgba(0, 229, 255, 0.6);
mix-blend-mode: normal;
}
.mana-fill {
background: linear-gradient(180deg, #b79bff 0%, var(--accent-2) 50%, #4a21b8 100%);
box-shadow: 0 0 16px rgba(124, 77, 255, 0.6);
}
.xp-fill {
background: linear-gradient(90deg, #1fc46a, var(--success) 60%, #9dffc6);
box-shadow: 0 0 16px rgba(54, 226, 122, 0.55);
transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.boss-fill {
background: linear-gradient(180deg, #ff7b9c 0%, var(--accent-3) 45%, #a8123f 100%);
box-shadow: 0 0 18px rgba(255, 61, 113, 0.55);
}
.track-sheen {
position: absolute;
inset: 0 0 50% 0;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0));
pointer-events: none;
}
.bar-readout {
margin: 10px 0 0;
font-family: "Orbitron", sans-serif;
font-weight: 500;
font-size: 0.82rem;
letter-spacing: 0.08em;
color: var(--muted);
}
.bar-readout span {
color: var(--text);
font-weight: 700;
}
.shield-text,
.shield-text span {
color: var(--accent) !important;
}
/* ---------- segmented bar ---------- */
.seg-bar {
display: grid;
grid-template-columns: repeat(10, 1fr);
gap: 5px;
}
.seg-cell {
height: 22px;
background: linear-gradient(180deg, #5dff9c, #15a04e);
border: 1px solid rgba(0, 0, 0, 0.4);
box-shadow: 0 0 10px rgba(54, 226, 122, 0.45);
clip-path: polygon(15% 0, 100% 0, 85% 100%, 0 100%);
transition: opacity 0.25s ease, filter 0.25s ease;
}
.seg-cell.is-empty {
background: rgba(231, 233, 243, 0.08);
box-shadow: none;
filter: saturate(0);
}
.seg-cell.is-partial {
background: linear-gradient(180deg, #ffe29a, #c9921f);
box-shadow: 0 0 10px rgba(255, 200, 87, 0.5);
}
/* ---------- stacked shield ---------- */
.stack-track {
height: 24px;
}
.stack-track .shield-fill {
z-index: 2;
}
/* ---------- xp ---------- */
.xp-wrap {
display: flex;
align-items: center;
gap: 14px;
}
.xp-level {
flex: 0 0 52px;
width: 52px;
height: 52px;
display: grid;
place-items: center;
font-family: "Orbitron", sans-serif;
font-weight: 900;
font-size: 1.25rem;
color: var(--success);
background: radial-gradient(circle at 50% 35%, var(--panel-2), var(--bg-2));
border: 1px solid rgba(54, 226, 122, 0.5);
border-radius: 50%;
box-shadow: 0 0 18px rgba(54, 226, 122, 0.35);
transition: transform 0.25s ease;
}
.xp-level.is-popping {
animation: level-pop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes level-pop {
0% { transform: scale(1); }
40% { transform: scale(1.35); box-shadow: 0 0 36px rgba(54, 226, 122, 0.9); }
100% { transform: scale(1); }
}
.xp-track {
flex: 1;
}
.levelup-flash {
position: absolute;
inset: 0;
display: grid;
place-items: center;
font-family: "Orbitron", sans-serif;
font-weight: 900;
font-size: 0.78rem;
letter-spacing: 0.3em;
color: #06210f;
background: linear-gradient(90deg, #9dffc6, #36e27a, #9dffc6);
opacity: 0;
pointer-events: none;
z-index: 3;
}
.levelup-flash.is-flashing {
animation: levelup-strobe 0.9s ease;
}
@keyframes levelup-strobe {
0% { opacity: 0; }
15% { opacity: 1; }
35% { opacity: 0.4; }
55% { opacity: 1; }
100% { opacity: 0; }
}
/* ---------- boss ---------- */
.boss-card {
background:
linear-gradient(165deg, rgba(255, 61, 113, 0.06), transparent 50%),
linear-gradient(165deg, var(--panel) 0%, var(--bg-2) 100%);
border-color: rgba(255, 61, 113, 0.3);
}
.boss-head {
text-align: center;
margin-bottom: 14px;
}
.boss-sub {
margin: 0 0 4px;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.26em;
text-transform: uppercase;
color: var(--muted);
}
.boss-name {
margin: 0;
font-family: "Orbitron", sans-serif;
font-weight: 900;
font-size: clamp(1.1rem, 3vw, 1.6rem);
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text);
text-shadow: 0 0 22px rgba(255, 61, 113, 0.6);
}
.boss-skulls {
margin-top: 4px;
color: var(--accent-3);
letter-spacing: 0.5em;
font-size: 0.8rem;
}
.boss-track {
height: 18px;
clip-path: polygon(12px 0, calc(100% - 12px) 0, 100% 50%, calc(100% - 12px) 100%, 12px 100%, 0 50%);
border-color: rgba(255, 61, 113, 0.45);
}
.boss-ticks {
position: absolute;
inset: 0;
z-index: 4;
pointer-events: none;
background-image: repeating-linear-gradient(
90deg,
transparent 0,
transparent calc(25% - 1px),
rgba(10, 11, 16, 0.85) calc(25% - 1px),
rgba(10, 11, 16, 0.85) 25%
);
}
.boss-foot {
display: flex;
align-items: center;
justify-content: center;
gap: 14px;
margin-top: 12px;
flex-wrap: wrap;
}
.boss-foot .bar-readout {
margin: 0;
}
.track.is-shaking {
animation: bar-shake 0.3s linear;
}
@keyframes bar-shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-4px); }
50% { transform: translateX(3px); }
75% { transform: translateX(-2px); }
}
/* ---------- controls ---------- */
.controls {
padding: 16px 20px;
}
.controls-row {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.controls-label {
font-family: "Orbitron", sans-serif;
font-weight: 700;
font-size: 0.72rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--accent);
margin-right: 6px;
text-shadow: var(--glow);
}
.btn {
font-family: "Orbitron", sans-serif;
font-weight: 700;
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text);
background: var(--panel-2);
border: 1px solid var(--line-2);
padding: 10px 16px;
cursor: pointer;
clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
.btn:hover {
transform: translateY(-1px);
}
.btn:active {
transform: translateY(1px) scale(0.98);
}
.btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
}
.btn-danger {
border-color: rgba(255, 77, 77, 0.55);
background: linear-gradient(180deg, rgba(255, 77, 77, 0.22), rgba(255, 77, 77, 0.08));
}
.btn-danger:hover {
box-shadow: 0 0 16px rgba(255, 77, 77, 0.45);
}
.btn-success {
border-color: rgba(54, 226, 122, 0.55);
background: linear-gradient(180deg, rgba(54, 226, 122, 0.22), rgba(54, 226, 122, 0.08));
}
.btn-success:hover {
box-shadow: 0 0 16px rgba(54, 226, 122, 0.45);
}
.btn-mana {
border-color: rgba(124, 77, 255, 0.6);
background: linear-gradient(180deg, rgba(124, 77, 255, 0.25), rgba(124, 77, 255, 0.08));
}
.btn-mana:hover {
box-shadow: 0 0 16px rgba(124, 77, 255, 0.5);
}
.btn-accent {
border-color: rgba(0, 229, 255, 0.55);
background: linear-gradient(180deg, rgba(0, 229, 255, 0.22), rgba(0, 229, 255, 0.08));
}
.btn-accent:hover {
box-shadow: var(--glow);
}
.btn-ghost {
background: transparent;
color: var(--muted);
}
.btn-ghost:hover {
color: var(--text);
border-color: var(--line-2);
}
.btn-sm {
padding: 7px 12px;
font-size: 0.66rem;
}
/* ---------- toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 16px);
background: var(--panel-2);
border: 1px solid rgba(0, 229, 255, 0.5);
color: var(--text);
font-family: "Orbitron", sans-serif;
font-size: 0.74rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 12px 22px;
opacity: 0;
pointer-events: none;
z-index: 60;
box-shadow: var(--glow), 0 12px 30px rgba(0, 0, 0, 0.5);
clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
transition: opacity 0.25s ease, transform 0.25s ease;
}
.toast.is-visible {
opacity: 1;
transform: translate(-50%, 0);
}
/* ---------- footer ---------- */
.page-foot {
margin-top: 32px;
text-align: center;
color: var(--muted);
font-size: 0.78rem;
}
.page-foot p {
margin: 0;
}
/* ---------- responsive ---------- */
@media (max-width: 760px) {
.grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 520px) {
.page {
padding: 32px 14px 48px;
}
.card {
padding: 16px;
}
.controls {
padding: 14px;
}
.controls-row {
gap: 8px;
}
.controls-label {
width: 100%;
margin: 0 0 2px;
}
.btn {
flex: 1 1 calc(50% - 8px);
padding: 10px 8px;
font-size: 0.66rem;
}
.xp-wrap {
gap: 10px;
}
.xp-level {
flex-basis: 44px;
width: 44px;
height: 44px;
font-size: 1.05rem;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}/* Game stat-bar variants — combat simulation (Nullforge HUD kit demo) */
(function () {
"use strict";
var $ = function (id) {
return document.getElementById(id);
};
/* ---------- toast helper ---------- */
var toastEl = $("toast");
var toastTimer = null;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("is-visible");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-visible");
}, 1800);
}
var clamp = function (v, min, max) {
return Math.max(min, Math.min(max, v));
};
/* ---------- state ---------- */
var state = {
hp: 100, // shared by segmented + chip bars
shield: 50,
stackHp: 100,
mana: 100,
xp: 35,
level: 12,
boss: 1000,
};
var HP_MAX = 100;
var SHIELD_MAX = 50;
var MANA_MAX = 100;
var XP_MAX = 100;
var BOSS_MAX = 1000;
var LOW_HP_THRESHOLD = 0.3;
/* ---------- segmented bar ---------- */
var segBar = $("seg-bar");
var SEG_COUNT = 10;
for (var i = 0; i < SEG_COUNT; i++) {
var cell = document.createElement("div");
cell.className = "seg-cell";
segBar.appendChild(cell);
}
var segCells = segBar.children;
function renderSegments() {
var perCell = HP_MAX / SEG_COUNT;
for (var i = 0; i < SEG_COUNT; i++) {
var cellHp = state.hp - i * perCell;
segCells[i].classList.toggle("is-empty", cellHp <= 0);
segCells[i].classList.toggle(
"is-partial",
cellHp > 0 && cellHp < perCell
);
}
segBar.setAttribute("aria-valuenow", String(state.hp));
$("seg-readout").textContent = String(state.hp);
}
/* ---------- gradient hp + damage chip ---------- */
var chipHp = $("chip-hp");
var chipTrail = $("chip-trail");
function hpColorClass(fillEl, ratio) {
fillEl.classList.toggle("is-low", ratio <= LOW_HP_THRESHOLD);
fillEl.classList.toggle("is-mid", ratio > LOW_HP_THRESHOLD && ratio <= 0.6);
}
function renderChipBar() {
var ratio = state.hp / HP_MAX;
chipHp.style.transform = "scaleX(" + ratio + ")";
// trail follows after a delay (CSS transition-delay handles the chip effect)
chipTrail.style.transform = "scaleX(" + ratio + ")";
hpColorClass(chipHp, ratio);
$("chip-meter").setAttribute("aria-valuenow", String(state.hp));
$("chip-readout").textContent = String(state.hp);
}
/* ---------- shield over health ---------- */
function renderStack() {
var hpRatio = state.stackHp / HP_MAX;
var shRatio = state.shield / SHIELD_MAX;
$("stack-hp").style.transform = "scaleX(" + hpRatio + ")";
$("stack-shield").style.transform = "scaleX(" + shRatio * 0.5 + ")"; // shield occupies up to half the track
hpColorClass($("stack-hp"), hpRatio);
$("stack-hp").setAttribute("aria-valuenow", String(state.stackHp));
$("stack-shield").setAttribute("aria-valuenow", String(state.shield));
$("stack-hp-readout").textContent = String(state.stackHp);
$("stack-shield-readout").textContent = String(state.shield);
}
/* ---------- mana ---------- */
function renderMana() {
$("mana-fill").style.transform = "scaleX(" + state.mana / MANA_MAX + ")";
$("mana-meter").setAttribute("aria-valuenow", String(state.mana));
$("mana-readout").textContent = String(state.mana);
}
// passive mana regen, 4 MP per second
setInterval(function () {
if (state.mana < MANA_MAX) {
state.mana = clamp(state.mana + 4, 0, MANA_MAX);
renderMana();
}
}, 1000);
/* ---------- xp + level up ---------- */
function renderXp() {
$("xp-fill").style.transform = "scaleX(" + state.xp / XP_MAX + ")";
$("xp-meter").setAttribute("aria-valuenow", String(state.xp));
$("xp-readout").textContent = String(state.xp);
$("xp-level-num").textContent = String(state.level);
$("xp-level-badge").textContent = "Level " + state.level;
}
function gainXp(amount) {
var total = state.xp + amount;
if (total >= XP_MAX) {
// fill to full, flash, then carry the overflow
state.xp = XP_MAX;
renderXp();
setTimeout(function () {
state.level += 1;
state.xp = total - XP_MAX;
var flash = $("levelup-flash");
var orb = $("xp-level-num");
flash.classList.remove("is-flashing");
orb.classList.remove("is-popping");
void flash.offsetWidth; // restart animations
flash.classList.add("is-flashing");
orb.classList.add("is-popping");
toast("Level up! Now level " + state.level);
// snap the fill back, then animate the overflow in
$("xp-fill").style.transition = "none";
$("xp-fill").style.transform = "scaleX(0)";
void $("xp-fill").offsetWidth;
$("xp-fill").style.transition = "";
renderXp();
}, 520);
} else {
state.xp = total;
renderXp();
toast("+" + amount + " XP");
}
}
/* ---------- boss ---------- */
var bossFill = $("boss-fill");
var bossTrail = $("boss-trail");
function renderBoss() {
var ratio = state.boss / BOSS_MAX;
bossFill.style.transform = "scaleX(" + ratio + ")";
bossTrail.style.transform = "scaleX(" + ratio + ")";
$("boss-meter").setAttribute("aria-valuenow", String(state.boss));
$("boss-readout").textContent = String(state.boss);
}
function shake(trackEl) {
trackEl.classList.remove("is-shaking");
void trackEl.offsetWidth;
trackEl.classList.add("is-shaking");
}
/* ---------- low-health vignette ---------- */
function renderVignette() {
var low =
state.hp / HP_MAX <= LOW_HP_THRESHOLD ||
(state.shield === 0 && state.stackHp / HP_MAX <= LOW_HP_THRESHOLD);
$("vignette").classList.toggle("is-active", low);
}
/* ---------- combat actions ---------- */
function takeDamage(amount) {
if (state.hp <= 0 && state.stackHp <= 0) {
toast("You are down — heal or reset");
return;
}
state.hp = clamp(state.hp - amount, 0, HP_MAX);
// shield absorbs first on the stacked bar
var remaining = amount;
if (state.shield > 0) {
var absorbed = Math.min(state.shield, remaining);
state.shield -= absorbed;
remaining -= absorbed;
if (absorbed > 0 && remaining > 0) {
toast("Shield broken! " + remaining + " DMG bled through");
} else {
toast("Shield absorbed " + absorbed + " DMG");
}
} else {
toast("-" + amount + " HP");
}
state.stackHp = clamp(state.stackHp - remaining, 0, HP_MAX);
renderAll();
shake($("chip-meter"));
if (state.hp === 0) toast("Vex Kestrel is down!");
}
function heal(amount) {
if (state.hp === HP_MAX && state.stackHp === HP_MAX && state.shield === SHIELD_MAX) {
toast("Already at full health");
return;
}
state.hp = clamp(state.hp + amount, 0, HP_MAX);
state.stackHp = clamp(state.stackHp + amount, 0, HP_MAX);
if (state.stackHp === HP_MAX) {
state.shield = clamp(state.shield + Math.ceil(amount / 2), 0, SHIELD_MAX);
}
toast("+" + amount + " HP restored");
renderAll();
}
function castSpell(cost) {
if (state.mana < cost) {
toast("Not enough mana!");
shake($("mana-meter"));
return;
}
state.mana -= cost;
renderMana();
toast("Void Lance cast — " + cost + " MP");
gainXp(8); // casting grants a little XP
}
function strikeBoss(amount) {
if (state.boss <= 0) {
toast("The Pale Choir has fallen");
return;
}
state.boss = clamp(state.boss - amount, 0, BOSS_MAX);
renderBoss();
shake($("boss-meter"));
if (state.boss === 0) {
toast("BOSS DEFEATED — Maw of the Pale Choir");
gainXp(60);
} else {
toast("-" + amount + " boss HP");
}
}
function resetAll() {
state.hp = HP_MAX;
state.shield = SHIELD_MAX;
state.stackHp = HP_MAX;
state.mana = MANA_MAX;
state.xp = 35;
state.level = 12;
renderAll();
toast("Encounter reset");
}
function renderAll() {
renderSegments();
renderChipBar();
renderStack();
renderMana();
renderXp();
renderVignette();
}
/* ---------- wire up ---------- */
$("btn-dmg-sm").addEventListener("click", function () { takeDamage(12); });
$("btn-dmg-lg").addEventListener("click", function () { takeDamage(34); });
$("btn-heal").addEventListener("click", function () { heal(20); });
$("btn-cast").addEventListener("click", function () { castSpell(25); });
$("btn-xp").addEventListener("click", function () { gainXp(40); });
$("btn-reset").addEventListener("click", resetAll);
$("btn-boss-hit").addEventListener("click", function () { strikeBoss(87); });
$("btn-boss-reset").addEventListener("click", function () {
state.boss = BOSS_MAX;
renderBoss();
toast("Boss restored to full health");
});
renderAll();
renderBoss();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Game — Health / Mana / XP Bar Variants</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="vignette" id="vignette" aria-hidden="true"></div>
<main class="page">
<header class="page-head">
<p class="kicker">Nullforge UI Kit · HUD Components</p>
<h1>Stat Bar Variants</h1>
<p class="lede">
Health, shield, mana and XP bars from <strong>Hollow Reign</strong> —
segmented frames, delayed damage chips, level-up overflow and a boss encounter banner.
Use the combat controls below to drive every bar at once.
</p>
</header>
<!-- ============ CONTROLS ============ -->
<section class="controls panel" aria-label="Combat simulation controls">
<div class="controls-row">
<span class="controls-label">Combat Sim</span>
<button class="btn btn-danger" id="btn-dmg-sm" type="button">Take 12 DMG</button>
<button class="btn btn-danger" id="btn-dmg-lg" type="button">Take 34 DMG</button>
<button class="btn btn-success" id="btn-heal" type="button">Heal +20</button>
<button class="btn btn-mana" id="btn-cast" type="button">Cast Spell −25 MP</button>
<button class="btn btn-accent" id="btn-xp" type="button">Gain +40 XP</button>
<button class="btn btn-ghost" id="btn-reset" type="button">Reset</button>
</div>
</section>
<div class="grid">
<!-- ============ SEGMENTED HEALTH ============ -->
<section class="panel card" aria-label="Segmented health bar">
<header class="card-head">
<h2>Segmented Health</h2>
<span class="badge">10 cells · 10 HP each</span>
</header>
<div class="char-row">
<div class="portrait" aria-hidden="true">VK</div>
<div class="char-meta">
<p class="char-name">Vex Kestrel</p>
<p class="char-role">Ashen Vanguard · Lv 27</p>
</div>
</div>
<div class="seg-bar" id="seg-bar" role="meter" aria-label="Health"
aria-valuemin="0" aria-valuemax="100" aria-valuenow="100"></div>
<p class="bar-readout"><span id="seg-readout">100</span> / 100 HP</p>
</section>
<!-- ============ GRADIENT + DAMAGE CHIP ============ -->
<section class="panel card" aria-label="Gradient health bar with damage chip">
<header class="card-head">
<h2>Damage Chip Trail</h2>
<span class="badge badge-danger">delayed drain</span>
</header>
<div class="char-row">
<div class="portrait portrait-2" aria-hidden="true">RM</div>
<div class="char-meta">
<p class="char-name">Riya Moss</p>
<p class="char-role">Neon Drift · Lv 31</p>
</div>
</div>
<div class="track hp-track" role="meter" aria-label="Health"
aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" id="chip-meter">
<div class="fill chip-fill" id="chip-trail"></div>
<div class="fill hp-fill" id="chip-hp"></div>
<div class="track-sheen" aria-hidden="true"></div>
</div>
<p class="bar-readout"><span id="chip-readout">100</span> / 100 HP</p>
</section>
<!-- ============ SHIELD OVER HEALTH ============ -->
<section class="panel card" aria-label="Shield over health stacked bar">
<header class="card-head">
<h2>Shield + Health Stack</h2>
<span class="badge badge-shield">absorbs first</span>
</header>
<div class="char-row">
<div class="portrait portrait-3" aria-hidden="true">OB</div>
<div class="char-meta">
<p class="char-name">Oryn Blackvane</p>
<p class="char-role">Hollow Reign · Lv 44</p>
</div>
</div>
<div class="track stack-track" aria-label="Shield and health">
<div class="fill hp-fill" id="stack-hp" role="meter" aria-label="Health"
aria-valuemin="0" aria-valuemax="100" aria-valuenow="100"></div>
<div class="fill shield-fill" id="stack-shield" role="meter" aria-label="Shield"
aria-valuemin="0" aria-valuemax="50" aria-valuenow="50"></div>
<div class="track-sheen" aria-hidden="true"></div>
</div>
<p class="bar-readout">
<span class="shield-text"><span id="stack-shield-readout">50</span> SH</span>
· <span id="stack-hp-readout">100</span> / 100 HP
</p>
</section>
<!-- ============ MANA ============ -->
<section class="panel card" aria-label="Mana bar">
<header class="card-head">
<h2>Mana / Energy</h2>
<span class="badge badge-mana">regen 4 / s</span>
</header>
<div class="char-row">
<div class="portrait portrait-4" aria-hidden="true">SY</div>
<div class="char-meta">
<p class="char-name">Sable Ys</p>
<p class="char-role">Voidweaver · Lv 19</p>
</div>
</div>
<div class="track mana-track" role="meter" aria-label="Mana"
aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" id="mana-meter">
<div class="fill mana-fill" id="mana-fill"></div>
<div class="track-sheen" aria-hidden="true"></div>
</div>
<p class="bar-readout"><span id="mana-readout">100</span> / 100 MP</p>
</section>
<!-- ============ XP ============ -->
<section class="panel card card-wide" aria-label="XP bar with level up">
<header class="card-head">
<h2>XP & Level-Up Overflow</h2>
<span class="badge badge-xp" id="xp-level-badge">Level 12</span>
</header>
<div class="xp-wrap">
<div class="xp-level" id="xp-level-num" aria-hidden="true">12</div>
<div class="track xp-track" role="meter" aria-label="Experience"
aria-valuemin="0" aria-valuemax="100" aria-valuenow="35" id="xp-meter">
<div class="fill xp-fill" id="xp-fill"></div>
<div class="track-sheen" aria-hidden="true"></div>
<div class="levelup-flash" id="levelup-flash" aria-hidden="true">LEVEL UP</div>
</div>
</div>
<p class="bar-readout"><span id="xp-readout">35</span> / 100 XP to next level</p>
</section>
<!-- ============ BOSS ============ -->
<section class="panel card card-wide boss-card" aria-label="Boss health bar">
<header class="boss-head">
<p class="boss-sub">Act III · World Boss</p>
<h2 class="boss-name">Maw of the Pale Choir</h2>
<div class="boss-skulls" aria-hidden="true">☠ ☠ ☠</div>
</header>
<div class="track boss-track" role="meter" aria-label="Boss health"
aria-valuemin="0" aria-valuemax="1000" aria-valuenow="1000" id="boss-meter">
<div class="fill chip-fill" id="boss-trail"></div>
<div class="fill boss-fill" id="boss-fill"></div>
<div class="boss-ticks" aria-hidden="true"></div>
</div>
<div class="boss-foot">
<span class="bar-readout"><span id="boss-readout">1000</span> / 1000</span>
<button class="btn btn-danger btn-sm" id="btn-boss-hit" type="button">Strike −87</button>
<button class="btn btn-ghost btn-sm" id="btn-boss-reset" type="button">Reset Boss</button>
</div>
</section>
</div>
<footer class="page-foot">
<p>Nullforge Interactive — HUD library demo. All values are simulated client-side.</p>
</footer>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Health / Mana / XP Bar Variants
A dark, angular HUD-styled gallery of the stat bars every game UI needs. Six variants sit in clipped-corner panels: a segmented health bar built from ten beveled cells (with a partial-cell warning state), a smooth gradient health bar where a red “damage chip” trails behind the real fill, a stacked bar where a cyan shield absorbs damage before green health, a violet mana bar that passively regenerates 4 MP per second, an XP bar with a level orb, and a boss-style centered name plate with a long, phase-ticked health bar.
Everything is driven by one shared combat simulator. Damage buttons drain health (shield first on the stacked bar), heal restores it, casting a spell spends mana and grants a trickle of XP, and the XP button can overflow the bar — triggering a strobing LEVEL UP flash, a popping level orb and a carry-over of the remaining XP into the next level. Bars shake on impact, switch from green to amber to pulsing red as health drops, and below 30% health a red vignette pulses around the whole viewport.
The fills animate with transform: scaleX() for cheap GPU compositing, the chip trail uses a longer delayed CSS transition rather than JS timers, and every meter exposes role="meter" with live aria-valuenow updates. A small toast() helper narrates each event via aria-live, and reduced-motion preferences collapse all animation.
Illustrative UI only — fictional games, studios, characters, and data. Not engine integrations.