demo D285
A board that flags work running late
Every open card timed from the moment it entered its column, with the ones running long marked: an amber edge past the warning limit, a red edge and tint past the breach, and its own clock for each column
sla: { warn, breach, enteredProperty }
The configuration
'kanban-card-aging': () => ({
rows: [],
config: {},
foot: [
'every open card is measured from the moment it entered its column, and the ones running long carry an age and a coloured edge',
'a card past the warning limit gets an amber edge, one past the breach limit a red edge and a tint, so the queue that needs attention reads at a glance',
'each column sets its own limits, so fast work and slow work are held to different clocks on the same board',
'resolved work is left out of the reckoning, so a closed ticket never shows as overdue',
],
mount: (el: HTMLElement, LG: any) => {
const rows = deskSeed();
let board: any;
let disposed = false;
const legend = document.createElement('div');
legend.style.cssText = 'display:flex;flex-wrap:wrap;gap:16px;align-items:center;margin-bottom:12px;font-size:.8125rem;color:var(--ink-2)';
const chip = (colour: string, label: string) => {
const wrap = document.createElement('span');
wrap.style.cssText = 'display:inline-flex;align-items:center;gap:7px';
const swatch = document.createElement('span');
swatch.style.cssText = `display:inline-block;width:14px;height:14px;border-radius:3px;border:1px solid var(--rule);border-left:3px solid ${colour}`;
const text = document.createElement('span');
text.textContent = label;
wrap.append(swatch, text);
return wrap;
};
legend.append(chip('var(--warning,#e8a317)', 'Ageing, past the warning limit'), chip('var(--danger,#c0392b)', 'Breached, past the limit'));
const boardEl = document.createElement('div');
boardEl.style.cssText = 'height:520px;border:1px solid var(--rule);border-radius:9px;background:var(--paper);overflow:hidden';
el.append(legend, boardEl);
loadKanban()
.then((KB: any) => {
if (disposed) return;
board = KB.createKanban(boardEl, {
rows,
rowKey: 'id',
columnProperty: 'status',
columns: [
{ id: 'triage', title: 'Triage', sla: { warn: 8 * HOUR, breach: 24 * HOUR } },
{ id: 'progress', title: 'In progress', sla: { warn: 6 * HOUR, breach: 16 * HOUR } },
{ id: 'waiting', title: 'Waiting on customer', sla: { warn: 24 * HOUR, breach: 72 * HOUR } },
{ id: 'resolved', title: 'Resolved', color: '#2e7d32' },
],
card: {
title: { field: 'title' },
subtitle: 'assignee',
badges: 'priority',
},
doneColumns: ['resolved'],
ariaLabel: 'Support desk, with card ageing',
emptyText: 'No tickets',
sla: {
enteredProperty: 'enteredAt',
ignoreDone: true,
showAge: 'always',
now: () => DESK_NOW,
},
});
board.sla?.evaluate?.();
})
.catch((err) => console.error('[kanban-aging]', err));
return () => {
disposed = true;
board?.destroy?.();
};
},
})
Which work is running late, without reading a single date
A board tells you where the work is. It does not, on its own, tell you which of it has been sitting too long. This one does. Every open card is timed from the moment it entered its column, and the ones running past what you allow are marked where the eye already is: an amber edge as a card nears its limit, then a red edge and a faint tint once it has passed it. The queue that needs a person reads at a glance, before anyone opens a card to check.
Each column keeps its own clock, because fast work and slow work are not the same promise. Triage is meant to clear in hours; a ticket waiting on a customer is allowed to sit far longer before it counts against anyone. Set a warning and a breach limit per column and the board holds each stage to its own standard. Work you have marked done is left out of the reckoning, so a closed card never shows as overdue and never distracts from the ones that are.
Because the age travels with the card, it stays right as the board moves. Drag a card into a new column and its clock restarts against that column’s limits; a card that arrives on a live feed is measured the moment it lands. The same board that shows the work also shows which of it is slipping, so a standard you agreed as a team is one the board keeps for you.
How do I highlight cards that are running late?
Turn on ageing when you create the board: give it sla with an enteredProperty naming the field that holds when each card entered its column, and a warn and breach duration. Set those durations per column, so a quick stage and a slow stage are held to different clocks on the same board. A card past the warning limit takes an amber edge, one past the breach limit a red edge and a tint, and each card can show its current age. List your finished columns in doneColumns and the board leaves resolved work out, so only open cards are ever flagged.