Skip to content

Instantly share code, notes, and snippets.

@luisramirezdev
Created August 17, 2026 07:42
Show Gist options
  • Select an option

  • Save luisramirezdev/43c16f9c766f48ca1fbf242da9311f86 to your computer and use it in GitHub Desktop.

Select an option

Save luisramirezdev/43c16f9c766f48ca1fbf242da9311f86 to your computer and use it in GitHub Desktop.
gridstack canvas demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>gridstack canvas demo — click "Edit" to see the grid</title>
<!-- gridstack from CDN (v13, latest) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gridstack@13.0.2/dist/gridstack.min.css" />
<script src="https://cdn.jsdelivr.net/npm/gridstack@13.0.2/dist/gridstack-all.js"></script>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
font-family: system-ui, sans-serif;
background: #0a0a0a;
color: #e4e4e7;
}
/* Toolbar */
.bar {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
background: #18181b;
border-bottom: 1px solid #27272a;
font-size: 14px;
}
.bar button {
padding: 6px 14px;
border: 1px solid #3f3f46;
border-radius: 8px;
background: #27272a;
color: #e4e4e7;
cursor: pointer;
font-size: 14px;
}
.bar button:hover { background: #3f3f46; }
.bar button.active { background: #16a34a; border-color: #16a34a; color: #fff; }
.hint { color: #a1a1aa; font-size: 13px; }
/* The grid container — fills the window. minRow forces a full canvas. */
.wrap {
height: calc(100vh - 49px);
overflow: auto;
padding: 12px;
}
.grid-stack {
/* THE CANVAS: draw the cell grid aligned to gridstack's live CSS vars.
Two repeating-linear-gradients (vertical + horizontal lines) sized
by --gs-column-width / --gs-cell-height. When gridstack recalculates
those vars, the background follows automatically. */
background-image:
repeating-linear-gradient(
to right,
rgba(255, 255, 255, 0.06) 0,
rgba(255, 255, 255, 0.06) calc(var(--gs-column-width) - 1px),
transparent calc(var(--gs-column-width) - 1px),
transparent var(--gs-column-width)
),
repeating-linear-gradient(
to bottom,
rgba(255, 255, 255, 0.06) 0,
rgba(255, 255, 255, 0.06) calc(var(--gs-cell-height) - 1px),
transparent calc(var(--gs-cell-height) - 1px),
transparent var(--gs-cell-height)
);
}
/* Show the canvas lines more strongly while editing */
.grid-stack.editing {
background-image:
repeating-linear-gradient(
to right,
rgba(255, 255, 255, 0.14) 0,
rgba(255, 255, 255, 0.14) calc(var(--gs-column-width) - 1px),
transparent calc(var(--gs-column-width) - 1px),
transparent var(--gs-column-width)
),
repeating-linear-gradient(
to bottom,
rgba(255, 255, 255, 0.14) 0,
rgba(255, 255, 255, 0.14) calc(var(--gs-cell-height) - 1px),
transparent calc(var(--gs-cell-height) - 1px),
transparent var(--gs-cell-height)
);
}
/* Widget cards */
.grid-stack-item-content {
border-radius: 12px;
border: 1px solid #3f3f46;
background: #18181b;
padding: 14px;
font-size: 13px;
color: #a1a1aa;
}
.grid-stack-item-content strong {
display: block;
color: #fafafa;
font-size: 15px;
margin-bottom: 4px;
}
.grid-stack-item[gs-id="a"] .grid-stack-item-content { background: #1c1917; }
.grid-stack-item[gs-id="b"] .grid-stack-item-content { background: #1a2e05; }
.grid-stack-item[gs-id="c"] .grid-stack-item-content { background: #082032; }
.grid-stack-item[gs-id="d"] .grid-stack-item-content { background: #2d1a05; }
.grid-stack-item[gs-id="e"] .grid-stack-item-content { background: #160d2d; }
</style>
</head>
<body>
<div class="bar">
<button id="toggle-edit">✏️ Edit</button>
<span class="hint">Arrastra los widgets · redimensiona desde la esquina · observa el lienzo de celdas</span>
</div>
<div class="wrap">
<!-- minRow: el lienzo SIEMPRE ocupa toda la ventana aunque haya pocos widgets -->
<div class="grid-stack" id="grid"></div>
</div>
<script>
const WIDGETS = [
{ id: 'a', w: 6, h: 4, title: 'Stint Planner', body: 'Parar ahora / Estirar 3 vueltas / Estirar al máximo' },
{ id: 'b', w: 6, h: 4, title: 'Weather', body: '21°C aire · 34°C pista · viento 2.3 m/s' },
{ id: 'c', w: 6, h: 4, title: 'Forecast', body: 'START → NODE 25 → NODE 50 → NODE 75 → FINISH' },
{ id: 'd', w: 3, h: 4, title: 'Damage', body: 'Aero 12% · Body 37% · Susp 50%' },
{ id: 'e', w: 3, h: 4, title: 'Pit Stop', body: 'Damage +2.1s · Fuel +8.4s · Total +14.2s' }
];
const grid = GridStack.init({
column: 12,
cellHeight: 60,
margin: 8,
float: false, // compaction: sin huecos
minRow: 10, // lienzo siempre presente
staticGrid: true, // read-only hasta pulsar Edit
resizable: { handles: 'se', autoHide: true }
}, document.getElementById('grid'));
for (const w of WIDGETS) {
grid.addWidget({
id: w.id,
w: w.w,
h: w.h,
content: `<strong>${w.title}</strong>${w.body}`
});
}
grid.compact();
// Toggle edit: muestra el lienzo + habilita drag/resize
const btn = document.getElementById('toggle-edit');
let editing = false;
btn.addEventListener('click', () => {
editing = !editing;
btn.textContent = editing ? '✔ Done' : '✏️ Edit';
btn.classList.toggle('active', editing);
document.getElementById('grid').classList.toggle('editing', editing);
grid.setStatic(!editing);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment