Popular features/Health indicators
Loading live demo…from dhtmlxcode.com
Popular features
Health indicators
Flag risk, blockers, decisions, and budget on each row, and score every task as met, tight, or slipping against its deadline.
gantt.plugins({ marker: true, tooltip: true });
gantt.config.date_format = "%Y-%m-%d";
gantt.config.deadlines = false;
gantt.config.open_tree_initially = true;
gantt.config.row_height = 44;
gantt.config.bar_height = 24;
gantt.config.scale_height = 52;
gantt.config.grid_width = 640;
gantt.config.scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "week", step: 1, format: "%d %M" }
];
let parseDate = value => gantt.date.parseDate(value, gantt.config.date_format);
let dateToStr = gantt.date.date_to_str("%d %M");
function icon(type) {
if (type === "risk") {
return "<svg class='ind_ic' viewBox='0 0 24 24'><path d='M12 4 22 20 2 20Z'/><path d='M12 10v5'/><circle cx='12' cy='18' r='1'/></svg>";
}
if (type === "blocked") {
return "<svg class='ind_ic' viewBox='0 0 24 24'><circle cx='12' cy='12' r='8'/><path d='M7 7l10 10'/></svg>";
}
if (type === "decision") {
return "<svg class='ind_ic' viewBox='0 0 24 24'><path d='M9 9a3 3 0 1 1 5 2.2c-1.4.9-2 1.8-2 3.3'/><path d='M12 19h.01'/><circle cx='12' cy='12' r='9'/></svg>";
}
if (type === "customer") {
return "<svg class='ind_ic' viewBox='0 0 24 24'><path d='M17 21v-2a4 4 0 0 0-4-4H7a4 4 0 0 0-4 4v2'/><circle cx='10' cy='7' r='4'/><path d='M21 21v-2a4 4 0 0 0-3-3.8'/><path d='M16 3.3a4 4 0 0 1 0 7.4'/></svg>";
}
if (type === "budget") {
return "<svg class='ind_ic' viewBox='0 0 24 24'><path d='M12 2v20'/><path d='M17 5H9.5a3.5 3.5 0 0 0 0 7H14a3.5 3.5 0 0 1 0 7H6'/></svg>";
}
if (type === "note") {
return "<svg class='ind_ic' viewBox='0 0 24 24'><path d='M5 5h14v10H9l-4 4Z'/></svg>";
}
return "";
}
let FLAG_TITLE = {
risk: "At risk: needs schedule attention",
blocked: "Blocked: dependency or owner action required",
decision: "Decision needed from leadership",
customer: "Customer-facing impact",
budget: "Budget watch",
note: "Has PM note"
};
function flagsHtml(task) {
if (!task.flags || !task.flags.length) {
return "";
}
return task.flags.map(function (flag) {
return `<span class='ind ind--${flag}' aria-label='${FLAG_TITLE[flag]}' data-tip='${FLAG_TITLE[flag]}'>${icon(flag)}</span>`;
}).join("");
}
function deadlineChipHtml(task) {
let state = deadlineState(task);
if (!state) {
return "";
}
let dl = parseDate(task.deadline);
let slipDays = gantt.calculateDuration({ start_date: dl, end_date: task.end_date, task });
let tip = `Deadline ${dateToStr(dl)}${slipDays > 0 ? ` - slipping by ${slipDays}d` : (state === "tight" ? " - tight" : " - safe")}`;
return `<span class='ind ind--deadline-${state}' aria-label='${tip}' data-tip='${tip}'><span class='ind_gem'></span></span>`;
}
let FLAG_ORDER = ["risk", "blocked", "decision", "customer", "budget", "note"];
let signalsPopup = null;
function closeSignalsEditor() {
if (signalsPopup) {
signalsPopup.remove();
signalsPopup = null;
}
}
function toggleTaskFlag(task, flag) {
let flags = (task.flags || []).slice();
let idx = flags.indexOf(flag);
if (idx === -1) {
flags.push(flag);
} else {
flags.splice(idx, 1);
}
task.flags = flags;
gantt.updateTask(task.id);
}
function openSignalsEditor(taskId, event) {
event.stopPropagation();
closeSignalsEditor();
let task = gantt.getTask(taskId);
let rect = event.currentTarget.getBoundingClientRect();
let popup = document.createElement("div");
popup.className = "signals_popup";
popup.innerHTML = FLAG_ORDER.map(function (flag) {
let active = task.flags && task.flags.indexOf(flag) !== -1;
return "<label class='signals_popup__row" + (active ? " active" : "") + "' data-flag='" + flag + "'>" +
"<span class='ind ind--" + flag + "'>" + icon(flag) + "</span>" +
"<span class='signals_popup__label'>" + FLAG_TITLE[flag] + "</span>" +
"<input type='checkbox'" + (active ? " checked" : "") + " />" +
"</label>";
}).join("");
popup.style.left = Math.round(rect.left) + "px";
popup.style.top = Math.round(rect.bottom + 4) + "px";
document.body.appendChild(popup);
popup.querySelectorAll("[data-flag]").forEach(function (row) {
row.addEventListener("click", function (rowEvent) {
rowEvent.preventDefault();
rowEvent.stopPropagation();
let flag = row.getAttribute("data-flag");
toggleTaskFlag(task, flag);
row.classList.toggle("active");
row.querySelector("input").checked = row.classList.contains("active");
});
});
signalsPopup = popup;
}
document.addEventListener("mousedown", function (event) {
if (signalsPopup && !signalsPopup.contains(event.target)) {
closeSignalsEditor();
}
});
function deadlineState(task) {
if (!task.deadline || !task.end_date) {
return "";
}
let slipDays = gantt.calculateDuration({ start_date: parseDate(task.deadline), end_date: task.end_date, task });
if (slipDays > 0) {
return "missed";
}
if (slipDays > -3) {
return "tight";
}
return "met";
}
function isActionable(task) {
return task.flags && task.flags.some(function (flag) {
return flag === "risk" || flag === "blocked" || flag === "decision" || flag === "budget";
});
}
gantt.templates.grid_row_class = function (start, end, task) {
if (task.risk === "high") {
return "pm_risk_high";
}
if (task.risk === "medium") {
return "pm_risk_medium";
}
return "";
};
gantt.templates.task_class = function (start, end, task) {
if (task.risk === "high") {
return "pm_risk_high";
}
if (task.risk === "medium") {
return "pm_risk_medium";
}
return "";
};
gantt.config.columns = [
{ name: "text", label: "Workstream", tree: true, width: 245, resize: true },
{ name: "owner", label: "Owner", align: "center", width: 72, resize: true },
{ name: "health", label: "Health", align: "center", width: 78, resize: true, template: function (task) {
if (!task.health) {
return "";
}
let label = task.health === "bad" ? "Blocked" : (task.health === "warn" ? "Risk" : "OK");
return "<span class='health_cell'><i class='health_dot " + task.health + "'></i>" + label + "</span>";
}},
{ name: "impact", label: "Impact", align: "center", width: 84, resize: true, template: function (task) {
if (!task.impact) {
return "";
}
let css = task.impact === "High" ? "impact_high" : (task.impact === "Medium" ? "impact_med" : "");
return "<span class='impact_cell " + css + "'>" + task.impact + "</span>";
}},
{ name: "signals", label: "Signals", align: "center", width: 112, resize: true, template: function (task) {
if (task.type === gantt.config.types.project) {
return "";
}
let content = task.flags && task.flags.length ? flagsHtml(task) : "<span class='signals_empty'>+</span>";
return "<span class='grid_flags' onclick='openSignalsEditor(" + task.id + ", event)'>" + content + "</span>";
}},
{ name: "deadline", label: "Deadline", align: "center", width: 100, resize: true, template: function (task) {
return task.deadline ? gantt.templates.date_grid(parseDate(task.deadline), task) : "";
}},
{ name: "add", width: 38, resize: true }
];
gantt.templates.tooltip_text = function (start, end, task) {
let lines = [
"<div class='tip_title'>" + task.text + "</div>",
"<div class='tip_line'>" + dateToStr(start) + " - " + dateToStr(end) + "</div>"
];
if (task.owner) {
lines.push("<div class='tip_line'>Owner " + task.owner + "</div>");
}
if (task.deadline) {
lines.push("<div class='tip_line'>Deadline " + dateToStr(parseDate(task.deadline)) + "</div>");
}
if (task.pm_note) {
lines.push("<div class='tip_alert'>" + task.pm_note + "</div>");
}
return lines.join("");
};
let demoData = {
tasks: [
{ id: 1, text: "Self-serve onboarding release", type: "project", open: true, health: "warn", owner: "PM", impact: "High" },
{ id: 11, text: "Activation research readout", parent: 1, owner: "UX", start_date: "2026-07-06", duration: 5, progress: 1, health: "ok", impact: "Medium", deadline: "2026-07-15", flags: ["note"], pm_note: "Insight pack approved; keep it visible for launch decisions." },
{ id: 12, text: "Pricing packaging decision", parent: 1, owner: "PM", start_date: "2026-07-13", duration: 6, progress: .55, health: "warn", impact: "High", risk: "medium", deadline: "2026-07-22", flags: ["decision", "budget"], pm_note: "Leadership decision needed before checkout work can lock scope." },
{ id: 13, text: "Identity provider integration", parent: 1, owner: "BE", start_date: "2026-07-16", duration: 12, progress: .32, health: "bad", impact: "High", risk: "high", deadline: "2026-07-24", flags: ["blocked", "risk"], pm_note: "Blocked by external security review; already slipping against the integration deadline." },
{ id: 14, text: "Invite flow and empty states", parent: 1, owner: "FE", start_date: "2026-07-23", duration: 10, progress: .25, health: "warn", impact: "High", risk: "medium", deadline: "2026-08-05", flags: ["risk", "customer"], pm_note: "Customer-facing path; any slip reduces first-week activation." },
{ id: 15, text: "Admin analytics events", parent: 1, owner: "Data", start_date: "2026-07-29", duration: 7, progress: .15, health: "ok", impact: "Medium", deadline: "2026-08-11", flags: ["note"], pm_note: "Useful for launch learning, not a release blocker." },
{ id: 16, text: "Billing QA and tax scenarios", parent: 1, owner: "QA", start_date: "2026-08-05", duration: 8, progress: 0, health: "warn", impact: "High", risk: "medium", deadline: "2026-08-14", flags: ["risk", "budget"], pm_note: "Extra test matrix may increase external QA spend." },
{ id: 17, text: "Beta customer enablement", parent: 1, owner: "CS", start_date: "2026-08-10", duration: 6, progress: 0, health: "ok", impact: "High", deadline: "2026-08-20", flags: ["customer"], pm_note: "Customer success needs final screenshots by Aug 16." },
{ id: 18, text: "Executive go/no-go", parent: 1, owner: "Exec", start_date: "2026-08-18", type: "milestone", progress: 0, health: "warn", impact: "High", deadline: "2026-08-18", flags: ["decision"], pm_note: "Decision gate: ship beta, narrow scope, or move launch." },
{ id: 19, text: "Public beta launch", parent: 1, owner: "PM", start_date: "2026-08-24", type: "milestone", progress: 0, health: "ok", impact: "High", deadline: "2026-08-24", flags: ["customer"] },
{ id: 2, text: "Platform scale and reliability hardening", type: "project", open: true, health: "warn", owner: "PM", impact: "High" },
{ id: 21, text: "Capacity model and load forecast", parent: 2, owner: "SRE", start_date: "2026-07-06", duration: 6, progress: 1, health: "ok", impact: "Medium", deadline: "2026-07-14", flags: ["note"], pm_note: "Forecast confirms current headroom through beta." },
{ id: 22, text: "Autoscaling policy rollout", parent: 2, owner: "Ops", start_date: "2026-07-13", duration: 8, progress: .5, health: "warn", impact: "High", risk: "medium", deadline: "2026-07-23", flags: ["risk"], pm_note: "Policy tuning behind schedule; watch before load test." },
{ id: 23, text: "Multi-region failover drill", parent: 2, owner: "SRE", start_date: "2026-07-20", duration: 7, progress: .2, health: "bad", impact: "High", risk: "high", deadline: "2026-07-28", flags: ["blocked", "risk"], pm_note: "Drill blocked on network peering change; already slipping against the deadline." },
{ id: 24, text: "Incident response runbook refresh", parent: 2, owner: "Supp", start_date: "2026-07-27", duration: 5, progress: .4, health: "ok", impact: "Medium", deadline: "2026-08-06", flags: ["note"], pm_note: "On track; new escalation paths documented." },
{ id: 25, text: "Cost guardrails and budget alerts", parent: 2, owner: "PM", start_date: "2026-08-03", duration: 6, progress: .1, health: "warn", impact: "Medium", risk: "medium", deadline: "2026-08-12", flags: ["budget"], pm_note: "Alert thresholds still need finance sign-off." },
{ id: 26, text: "Data residency compliance review", parent: 2, owner: "Sec", start_date: "2026-08-05", duration: 9, progress: 0, health: "warn", impact: "High", risk: "medium", deadline: "2026-08-18", flags: ["decision", "customer"], pm_note: "Regulator question outstanding; affects EU customer rollout." },
{ id: 27, text: "Load test at 3x peak traffic", parent: 2, owner: "QA", start_date: "2026-08-11", duration: 6, progress: 0, health: "ok", impact: "High", deadline: "2026-08-21", flags: ["customer"], pm_note: "Scheduled after autoscaling policy stabilizes." },
{ id: 28, text: "Reliability sign-off review", parent: 2, owner: "Exec", start_date: "2026-08-21", type: "milestone", progress: 0, health: "warn", impact: "High", deadline: "2026-08-21", flags: ["decision"], pm_note: "Decision gate: confirm platform is ready for public beta scale." },
{ id: 29, text: "Scale readiness complete", parent: 2, owner: "PM", start_date: "2026-08-28", type: "milestone", progress: 0, health: "ok", impact: "High", deadline: "2026-08-28", flags: ["customer"] }
],
links: [
{ id: 1, source: 11, target: 12, type: "0" },
{ id: 2, source: 12, target: 13, type: "0" },
{ id: 3, source: 13, target: 14, type: "0" },
{ id: 4, source: 14, target: 16, type: "0" },
{ id: 5, source: 15, target: 17, type: "0" },
{ id: 6, source: 16, target: 18, type: "0" },
{ id: 7, source: 17, target: 18, type: "0" },
{ id: 8, source: 18, target: 19, type: "0" },
{ id: 9, source: 21, target: 22, type: "0" },
{ id: 10, source: 22, target: 23, type: "0" },
{ id: 11, source: 23, target: 27, type: "0" },
{ id: 12, source: 24, target: 25, type: "0" },
{ id: 13, source: 25, target: 26, type: "0" },
{ id: 14, source: 26, target: 28, type: "0" },
{ id: 15, source: 27, target: 28, type: "0" },
{ id: 16, source: 28, target: 29, type: "0" }
]
};
let indicatorsOn = true;
let deadlinesOn = true;
let actionableOnly = false;
gantt.addTaskLayer(function (task) {
let showFlags = indicatorsOn && task.flags && task.flags.length && (!actionableOnly || isActionable(task));
let showDeadline = deadlinesOn && task.deadline && task.type !== gantt.config.types.milestone;
if (!showFlags && !showDeadline) {
return null;
}
let pos = gantt.getTaskPosition(task, task.start_date, task.end_date);
if (!pos.width && task.type !== gantt.config.types.milestone) {
return null;
}
let gap = task.type === gantt.config.types.milestone ? 22 : 10;
let left = pos.left + pos.width + gap;
let el = document.createElement("div");
el.className = "ind_cluster";
el.style.left = left + "px";
el.style.top = (pos.top + Math.max(0, (pos.height - 28) / 2) + 3) + "px";
el.innerHTML = (showFlags ? flagsHtml(task) : "") + (showDeadline ? deadlineChipHtml(task) : "");
return el;
});
function renderControls() {
document.getElementById("pm_summary").innerHTML =
"<div class='pm_controls'>" +
"<label class='dhx_toggle active' onclick='toggleIndicators()'><span>Indicators</span><span class='dhx_toggle__track on' id='ind_switch'></span></label>" +
"<label class='dhx_toggle active' onclick='toggleDeadlines()'><span>Deadline health</span><span class='dhx_toggle__track on' id='dl_switch'></span></label>" +
"<label class='dhx_toggle' onclick='toggleActionable()'><span>Actionable only</span><span class='dhx_toggle__track' id='act_switch'></span></label>" +
"</div>";
}
function updateTaskCount() {
let count = 0;
gantt.eachTask(function (task) {
if (!gantt.isSummaryTask(task)) count++;
});
DHX.ui.setText("footer_count", `${count} tracked tasks`);
}
function addMarkers() {
gantt.addMarker({ start_date: new Date(2026, 6, 24), css: "today_marker", text: "Today", title: "Today" });
gantt.addMarker({ start_date: new Date(2026, 7, 18), css: "exec_marker", text: "Go/no-go", title: "Executive go/no-go" });
gantt.addMarker({ start_date: new Date(2026, 7, 24), css: "launch_marker", text: "Beta", title: "Public beta launch" });
}
function toggleIndicators() {
indicatorsOn = !indicatorsOn;
DHX.toggle.setById("ind_switch", indicatorsOn);
gantt.render();
}
function toggleDeadlines() {
deadlinesOn = !deadlinesOn;
DHX.toggle.setById("dl_switch", deadlinesOn);
gantt.render();
}
function toggleActionable() {
actionableOnly = !actionableOnly;
DHX.toggle.setById("act_switch", actionableOnly);
gantt.render();
}
gantt.attachEvent("onGanttRender", updateTaskCount);
gantt.init("gantt_here");
gantt.parse(demoData);
addMarkers();
renderControls();
gantt.ext.tooltips.tooltipFor({
selector: "[data-tip]",
html: function (e, node) {
const editText = e.target.closest(".gantt_cell") ? " (Click to change)" : "";
return node.getAttribute("data-tip") + editText;
}
})<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Product Health Indicators: dhtmlxGantt</title>
<script src="../../codebase/dhtmlxgantt.js?v=10.0.0"></script>
<link rel="stylesheet" href="../../codebase/dhtmlxgantt.css?v=10.0.0">
<link rel="stylesheet" href="../common/demo-controls/dhx_controls.css">
<script src="../common/demo-controls/dhx_controls.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<style>
body {
display: flex;
flex-direction: column;
font-family: Inter, Arial, sans-serif;
}
#gantt_here {
width: 100%;
flex: 1 1 auto;
min-height: 0;
}
.pm_summary {
display: flex;
align-items: center;
justify-content: center;
height: 60px;
padding: 0 12px;
background: var(--dhx-panel);
border-bottom: 1px solid var(--dhx-line);
}
.pm_controls {
display: flex;
align-items: center;
gap: 12px;
}
.health_cell {
display: inline-flex;
align-items: center;
gap: 6px;
}
.health_dot {
width: 9px;
height: 9px;
border-radius: 50%;
display: inline-block;
}
.health_dot.ok { background: #16a34a; }
.health_dot.warn { background: #d97706; }
.health_dot.bad { background: #dc2626; }
.impact_cell {
font-weight: 600;
color: var(--dhx-ink-2);
}
.impact_high { color: #b42318; }
.impact_med { color: #b8740a; }
.gantt_row.pm_risk_high {
background: rgba(229, 72, 77, .07);
}
.gantt_row.pm_risk_medium {
background: rgba(244, 161, 0, .07);
}
:root[data-gantt-theme="dark"] .gantt_row.pm_risk_high {
background: rgba(229, 72, 77, .13);
}
:root[data-gantt-theme="dark"] .gantt_row.pm_risk_medium {
background: rgba(244, 161, 0, .13);
}
.gantt_task_line.pm_risk_high {
box-shadow: 0 0 0 2px rgba(220, 38, 38, .55);
}
.gantt_task_line.pm_risk_medium {
box-shadow: 0 0 0 2px rgba(217, 119, 6, .55);
}
.ind_cluster {
position: absolute;
display: flex;
align-items: center;
gap: 6px;
pointer-events: auto;
z-index: 3;
}
.ind {
width: 28px;
height: 28px;
border-radius: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
box-shadow: 0 5px 14px rgba(16, 24, 40, .14), inset 0 0 0 1px rgba(255, 255, 255, .7);
cursor: default;
}
.ind_ic {
width: 18px;
height: 18px;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2.1;
}
.ind--risk {
background: var(--dhx-warn-bg);
color: var(--dhx-warn-ink);
}
.ind--blocked {
background: var(--dhx-danger-bg);
color: var(--dhx-danger-ink);
}
.ind--decision {
background: #eee7ff;
color: #6d28d9;
}
.ind--customer {
background: var(--dhx-ok-bg);
color: var(--dhx-ok-ink);
}
.ind--budget {
background: var(--dhx-info-bg);
color: var(--dhx-info-ink);
}
.ind--note {
background: var(--dhx-neutral-bg);
color: var(--dhx-neutral-ink);
}
.ind svg {
stroke: currentColor;
}
:root[data-gantt-theme="dark"] .ind {
box-shadow: 0 5px 16px rgba(0, 0, 0, .28), inset 0 0 0 1px rgba(255, 255, 255, .08);
}
:root[data-gantt-theme="dark"] .ind--decision { background: #2f2549; color: #c4b5fd; }
.grid_flags {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
width: 100%;
cursor: pointer;
}
.grid_flags .ind {
width: 22px;
height: 22px;
border-radius: 7px;
box-shadow: none;
}
.grid_flags .ind_ic {
width: 15px;
height: 15px;
}
.ind_gem {
width: 12px;
height: 12px;
border-radius: 3px;
background: currentColor;
transform: rotate(45deg);
}
.ind--deadline-met {
background: var(--dhx-ok-bg);
color: var(--dhx-ok-ink);
}
.ind--deadline-tight {
background: var(--dhx-warn-bg);
color: var(--dhx-warn-ink);
}
.ind--deadline-missed {
background: var(--dhx-danger-bg);
color: var(--dhx-danger-ink);
}
.gantt_marker.today_marker {
background: #2563eb;
}
.gantt_marker.launch_marker {
background: #16a34a;
}
.gantt_marker.exec_marker {
background: #7c3aed;
}
.gantt_tooltip {
padding: 10px 12px;
border: 1px solid var(--dhx-line);
border-radius: 8px;
background: var(--dhx-panel);
color: var(--dhx-ink);
box-shadow: 0 10px 28px rgba(16, 24, 40, .18);
font: 500 12px/1.45 var(--dhx-font);
}
.tip_title {
color: var(--dhx-ink);
font-weight: 700;
margin-bottom: 4px;
}
.tip_line {
color: var(--dhx-muted);
}
.tip_alert {
margin-top: 6px;
color: var(--dhx-ink-2);
}
.signals_empty {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 7px;
border: 1px dashed var(--dhx-line-2);
color: var(--dhx-muted);
font: 600 13px/1 var(--dhx-font);
}
.signals_popup {
position: fixed;
z-index: 200;
padding: 6px;
border: 1px solid var(--dhx-line);
border-radius: 9px;
background: var(--dhx-panel);
box-shadow: 0 12px 30px rgba(16, 24, 40, .2);
}
.signals_popup__row {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 8px;
border-radius: 6px;
cursor: pointer;
white-space: nowrap;
}
.signals_popup__row:hover {
background: rgba(37, 99, 235, .08);
}
.signals_popup__row .ind {
width: 22px;
height: 22px;
border-radius: 6px;
box-shadow: none;
flex: none;
}
.signals_popup__row .ind_ic {
width: 14px;
height: 14px;
}
.signals_popup__label {
flex: 1 1 auto;
font: 500 12px/1.3 var(--dhx-font);
color: var(--dhx-ink);
}
.signals_popup__row input {
pointer-events: none;
}
.signals_popup__row:not(.active) {
opacity: .55;
}
.pm_legend {
display: inline-flex;
align-items: center;
gap: 13px;
flex-wrap: wrap;
}
.pm_legend .lg {
display: inline-flex;
align-items: center;
gap: 6px;
}
.pm_legend .ind {
width: 20px;
height: 20px;
border-radius: 6px;
box-shadow: none;
}
.pm_legend .ind_ic {
width: 14px;
height: 14px;
}
.pm_legend .gem2 {
width: 10px;
height: 10px;
transform: rotate(45deg);
border-radius: 2px;
display: inline-block;
}
</style>
</head>
<body>
<div class="pm_summary" id="pm_summary"></div>
<div id="gantt_here"></div>
<div class="dhx_footer">
<div class="pm_legend">
<span class="lg"><span class="ind ind--blocked"></span>Blocked</span>
<span class="lg"><span class="ind ind--risk"></span>At risk</span>
<span class="lg"><span class="ind ind--decision"></span>Decision</span>
<span class="lg"><span class="ind ind--customer"></span>Customer impact</span>
<span class="lg"><span class="gem2" style="background:#16a34a"></span>Safe deadline</span>
<span class="lg"><span class="gem2" style="background:#d97706"></span>Tight</span>
<span class="lg"><span class="gem2" style="background:#dc2626"></span>Slip</span>
</div>
<div class="dhx_footer__right"><span id="footer_count"></span></div>
</div>
</body>
</html>