Popular features/Task styling
Loading live demo…from dhtmlxcode.com
Popular features
Task styling
Compute each bar's color, progress color, and text color straight from data fields, with a manual override still available per task.
gantt.plugins({ tooltip: true });
gantt.config.date_format = "%Y-%m-%d %H:%i";
gantt.config.row_height = 38;
gantt.config.bar_height = 26;
gantt.config.scale_height = 54;
gantt.config.work_time = true;
gantt.config.min_column_width = 46;
gantt.config.scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "week", step: 1, format: "Week %W" }
];
/* Weekend shading. */
gantt.templates.timeline_cell_class = function (task, date) {
return gantt.isWorkTime({ date, task }) ? "" : "week_end";
};
/* ---------------------------------------------------------------------------
* PALETTE: one accent per phase, plus matching darker progress shade.
* ------------------------------------------------------------------------ */
let PHASES = {
discovery: { label: "Discovery", bar: "#7c3aed", progress: "#5b21b6" },
design: { label: "Design", bar: "#db2777", progress: "#9d174d" },
development: { label: "Development", bar: "#2563eb", progress: "#1d4ed8" },
qa: { label: "QA & Testing", bar: "#d97706", progress: "#b45309" },
launch: { label: "Launch", bar: "#059669", progress: "#047857" }
};
let PRIORITY = {
high: { label: "High", bar: "#e5484d", progress: "#b42318", dot: "dhx_dot--high" },
medium: { label: "Medium", bar: "#f5a623", progress: "#c77700", dot: "dhx_dot--medium" },
low: { label: "Low", bar: "#3aa757", progress: "#26803f", dot: "dhx_dot--low" }
};
let STATUS = {
done: { label: "Done", bar: "#16a34a", progress: "#15803d" },
active: { label: "In progress", bar: "#2563eb", progress: "#1d4ed8" },
planned: { label: "Not started", bar: "#94a3b8", progress: "#64748b" }
};
function statusOf(task) {
if (task.progress >= 1) return "done";
if (task.progress > 0) return "active";
return "planned";
}
/* ---------------------------------------------------------------------------
* DATA: "Mobile App Launch", one colour family per phase.
* Each task already carries its style fields (phase / priority / color).
* ------------------------------------------------------------------------ */
let data = {
tasks: [
{ id: 1, text: "Mobile App Launch", type: "project", open: true, progress: 0.46 },
{ id: 10, text: "Discovery", type: "project", parent: 1, open: true, phase: "discovery" },
{ id: 11, text: "Market research", start_date: "2026-09-01", duration: 5, parent: 10, phase: "discovery", priority: "medium", progress: 1 },
{ id: 12, text: "User interviews", start_date: "2026-09-08", duration: 4, parent: 10, phase: "discovery", priority: "high", progress: 1 },
{ id: 13, text: "Requirements approved", start_date: "2026-09-12", type: "milestone", parent: 10, phase: "discovery" },
{ id: 20, text: "Design", type: "project", parent: 1, open: true, phase: "design" },
{ id: 21, text: "Wireframes", start_date: "2026-09-15", duration: 5, parent: 20, phase: "design", priority: "high", progress: 1 },
{ id: 22, text: "Visual design", start_date: "2026-09-22", duration: 8, parent: 20, phase: "design", priority: "high", progress: 0.8 },
{ id: 23, text: "Design system", start_date: "2026-09-24", duration: 6, parent: 20, phase: "design", priority: "medium", progress: 0.6 },
{ id: 30, text: "Development", type: "project", parent: 1, open: true, phase: "development" },
{ id: 31, text: "Project setup", start_date: "2026-10-06", duration: 3, parent: 30, phase: "development", priority: "low", progress: 1 },
{ id: 32, text: "Auth & onboarding", start_date: "2026-10-09", duration: 7, parent: 30, phase: "development", priority: "high", progress: 0.5 },
{ id: 33, text: "Core features", start_date: "2026-10-20", duration: 12, parent: 30, phase: "development", priority: "high", progress: 0.25 },
{ id: 34, text: "Push notifications", start_date: "2026-10-27", duration: 6, parent: 30, phase: "development", priority: "medium", progress: 0 },
{ id: 35, text: "Offline sync", start_date: "2026-11-03", duration: 5, parent: 30, phase: "development", priority: "medium", progress: 0 },
{ id: 36, text: "Analytics integration", start_date: "2026-11-06", duration: 4, parent: 30, phase: "development", priority: "low", progress: 0 },
{ id: 40, text: "QA & Testing", type: "project", parent: 1, open: true, phase: "qa" },
{ id: 41, text: "Test plan", start_date: "2026-11-10", duration: 3, parent: 40, phase: "qa", priority: "medium", progress: 0 },
{ id: 42, text: "Regression testing", start_date: "2026-11-13", duration: 6, parent: 40, phase: "qa", priority: "high", progress: 0 },
{ id: 44, text: "Performance testing", start_date: "2026-11-17", duration: 4, parent: 40, phase: "qa", priority: "medium", progress: 0 },
{ id: 45, text: "Accessibility audit", start_date: "2026-11-19", duration: 3, parent: 40, phase: "qa", priority: "low", progress: 0 },
{ id: 43, text: "Beta feedback", start_date: "2026-11-21", duration: 5, parent: 40, phase: "qa", priority: "low", progress: 0 },
{ id: 50, text: "Launch", type: "project", parent: 1, open: true, phase: "launch" },
{ id: 51, text: "Store submission", start_date: "2026-11-27", duration: 4, parent: 50, phase: "launch", priority: "high", progress: 0 },
{ id: 52, text: "Marketing campaign", start_date: "2026-12-01", duration: 5, parent: 50, phase: "launch", priority: "medium", progress: 0 },
{ id: 53, text: "Go live", start_date: "2026-12-08", type: "milestone", parent: 50, phase: "launch" }
],
links: [
{ id: 1, source: 11, target: 12, type: "0" },
{ id: 2, source: 12, target: 13, type: "0" },
{ id: 3, source: 13, target: 21, type: "0" },
{ id: 4, source: 21, target: 22, type: "0" },
{ id: 5, source: 22, target: 31, type: "0" },
{ id: 6, source: 31, target: 32, type: "0" },
{ id: 7, source: 32, target: 33, type: "0" },
{ id: 8, source: 33, target: 41, type: "0" },
{ id: 9, source: 42, target: 51, type: "0" },
{ id: 10, source: 51, target: 53, type: "0" }
]
};
/* ---------------------------------------------------------------------------
* COLOR ENGINE: derive each task's colors from its data fields.
* A manual override (set via the palette) wins over the active mode.
* ------------------------------------------------------------------------ */
function colorsForTask(task, mode) {
if (mode === "priority" && task.priority) return PRIORITY[task.priority];
if (mode === "status") return STATUS[statusOf(task)];
return PHASES[task.phase] || PHASES.development; // default: phase
}
function applyMode(mode) {
gantt.eachTask(function (task) {
if (task.type === gantt.config.types.project) return; // keep summaries neutral
if (task.$manualColor) return; // respect overrides
let color = colorsForTask(task, mode);
task.color = color.bar;
task.progressColor = color.progress;
task.textColor = "#ffffff";
});
gantt.render();
renderLegend(mode);
}
function setMode(mode) {
DHX.ui.setActiveByAttribute("#color_seg .dhx_btn", "data-mode", mode, "active");
applyMode(mode);
}
/* ---------------------------------------------------------------------------
* LEGEND: reflects whichever mode is active.
* ------------------------------------------------------------------------ */
function renderLegend(mode) {
let map = mode === "priority" ? PRIORITY : mode === "status" ? STATUS : PHASES;
let html = Object.keys(map).map(function (key) {
return '<span class="legend__item">' +
'<span class="legend__swatch" style="background:' + map[key].bar + '"></span>' +
map[key].label + '</span>';
}).join("");
document.getElementById("legend").innerHTML = html;
}
/* ---------------------------------------------------------------------------
* GRID: task name, priority dot, duration.
* ------------------------------------------------------------------------ */
function priorityCell(task) {
if (!task.priority || task.type === gantt.config.types.project) return "";
let priority = PRIORITY[task.priority];
return '<span class="prio_cell"><span class="dhx_dot ' + priority.dot + '"></span>' + priority.label + '</span>';
}
gantt.config.columns = [
{ name: "text", label: "Task name", tree: true, width: 230, resize: true },
{ name: "priority", label: "Priority", align: "left", width: 110, resize: true, template: priorityCell },
{ name: "duration", label: "Days", align: "center", width: 60, resize: true },
{ name: "add", width: 40, resize: true }
];
/* ---------------------------------------------------------------------------
* TOOLTIP
* ------------------------------------------------------------------------ */
gantt.templates.tooltip_text = function (start, end, task) {
if (task.type === gantt.config.types.milestone) {
return "<b>" + task.text + "</b><br>" + gantt.templates.tooltip_date_format(start);
}
return "<b>" + task.text + "</b><br>" +
gantt.templates.tooltip_date_format(start) + " – " + gantt.templates.tooltip_date_format(end) +
"<br>Progress: " + Math.round((task.progress || 0) * 100) + "%";
};
/* ---------------------------------------------------------------------------
* LIGHTBOX: keep manual color / text-color pickers available.
* ------------------------------------------------------------------------ */
let lightboxColors = [
{ key: "", label: "Auto (by data)" },
{ key: "#7c3aed", label: "Violet" },
{ key: "#db2777", label: "Pink" },
{ key: "#2563eb", label: "Blue" },
{ key: "#059669", label: "Green" },
{ key: "#d97706", label: "Amber" },
{ key: "#e5484d", label: "Red" },
{ key: "#0f172a", label: "Charcoal" }
];
gantt.locale.labels.section_color = "Bar color";
gantt.locale.labels.section_textColor = "Text color";
gantt.config.lightbox.sections = [
{ name: "description", height: 38, map_to: "text", type: "textarea", focus: true },
{ name: "color", height: 22, map_to: "color", type: "select", options: lightboxColors },
{ name: "textColor", height: 22, map_to: "textColor", type: "select", options: lightboxColors },
{ name: "time", type: "duration", map_to: "auto" }
];
/* When a task is edited to a manual color in the lightbox, mark it as overridden. */
gantt.attachEvent("onLightboxSave", function (id, task) {
task.$manualColor = !!task.color;
return true;
});
/* ---------------------------------------------------------------------------
* RECOLOR PALETTE: override the selected task's color.
* ------------------------------------------------------------------------ */
let SWATCHES = ["#7c3aed", "#db2777", "#2563eb", "#059669", "#d97706",
"#e5484d", "#0ea5e9", "#14b8a6", "#f59e0b", "#0f172a"];
let selectedId = null;
function buildSwatches() {
document.getElementById("swatches").innerHTML = SWATCHES.map(function (c) {
return '<div class="swatch" style="background:' + c + '" onclick="recolorSelected(\'' + c + '\')"></div>';
}).join("");
}
function recolorSelected(color) {
if (selectedId == null) return;
let task = gantt.getTask(selectedId);
task.color = color;
task.progressColor = color;
task.textColor = "#ffffff";
task.$manualColor = true; // survives mode switches
gantt.updateTask(selectedId);
DHX.dd.close("dd_recolor");
}
gantt.attachEvent("onTaskSelected", function (id) {
let task = gantt.getTask(id);
if (task.type === gantt.config.types.project) return;
selectedId = id;
document.getElementById("btn_recolor").disabled = false;
});
gantt.attachEvent("onTaskUnselected", function () {
selectedId = null;
document.getElementById("btn_recolor").disabled = true;
DHX.dd.close("dd_recolor");
});
DHX.dd.initClickOutside();
/* ---------------------------------------------------------------------------
* INIT
* ------------------------------------------------------------------------ */
function sizeGantt() {
document.getElementById("gantt_here").style.height = (window.innerHeight - 60) + "px";
}
window.addEventListener("resize", sizeGantt);
sizeGantt();
buildSwatches();
gantt.init("gantt_here");
gantt.parse(data);
applyMode("phase"); // paint bars from data on first load
gantt.selectTask(12); // preselect a task so "Recolor task" starts active<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Task styling: 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>
#gantt_here { width: 100%; }
.gantt_task_line {
border: none;
border-radius: 7px;
box-shadow: 0 1px 2px rgba(16,24,40,.18);
}
.gantt_task_line .gantt_task_progress { border-radius: 7px 0 0 7px; }
.gantt_task_line.gantt_project { border-radius: 5px; }
.gantt_task_cell.week_end { background-color: rgba(15,23,42,.035); }
.prio_cell { display: inline-flex; align-items: center; gap: 7px; }
.prio_cell .dhx_dot { width: 9px; height: 9px; }
.ts_header { position: relative; }
.ts_header .dhx_header__center { position: absolute; left: 50%; transform: translateX(-50%); margin: 0; z-index: 5; }
.legend { display: inline-flex; align-items: center; gap: 14px; }
.legend__item {
display: inline-flex;
align-items: center;
gap: 6px;
font: 500 12px var(--dhx-font);
color: var(--dhx-ink-2);
white-space: nowrap;
}
.legend__swatch {
width: 12px;
height: 12px;
border-radius: 4px;
flex: none;
box-shadow: inset 0 0 0 1px rgba(16,24,40,.12);
}
#menu_recolor { min-width: 0; }
.swatches {
display: grid;
grid-template-columns: repeat(5, 26px);
gap: 7px;
padding: 4px;
}
.swatch {
box-sizing: border-box;
width: 26px;
height: 26px;
border-radius: 7px;
cursor: pointer;
border: 2px solid #fff;
box-shadow: 0 0 0 1px rgba(16,24,40,.12);
transition: transform .1s;
}
.swatch:hover { transform: scale(1.12); }
</style>
</head>
<body>
<div class="dhx_header ts_header">
<!-- Legend (updates with the active mode) -->
<div class="legend" id="legend"></div>
<div class="dhx_header__center">
<!-- Coloring strategy -->
<span style="font:600 13px var(--dhx-font);color:var(--dhx-ink)">Color by</span>
<div class="dhx_seg" id="color_seg">
<button class="dhx_btn active" data-mode="phase" onclick="setMode('phase')">Phase</button>
<button class="dhx_btn" data-mode="priority" onclick="setMode('priority')">Priority</button>
<button class="dhx_btn" data-mode="status" onclick="setMode('status')">Status</button>
</div>
<div class="dhx_sep"></div>
<!-- Recolor selected task -->
<div class="dhx_dd" id="dd_recolor">
<button class="dhx_btn dhx_btn--surface" id="btn_recolor" onclick="DHX.dd.toggle('dd_recolor')" disabled>
<svg class="dhx_ic" viewBox="0 0 16 16"><path d="M2 11.5 10 3.5l2.5 2.5-8 8H2z"/><path d="M9 4.5 11.5 7"/></svg>
Recolor task
<span class="dhx_caret"><svg class="dhx_ic dhx_ic--sm" viewBox="0 0 12 12"><polyline points="2 4 6 8 10 4"/></svg></span>
</button>
<div class="dhx_menu" id="menu_recolor">
<div class="swatches" id="swatches"></div>
</div>
</div>
</div>
</div>
<div id="gantt_here"></div>
</body>
</html>