Created Jun 2025
Kanban Task Board in CSS Grid
Demonstration of display: grid
coupled with grid-auto-flow: column;
How does this work?
- If the screen has a minimum width of
800px
... - 3 columns are set up using CSS Grid.
- Each div includes a set of radio buttons with a
name
unique to each div - Based on which one is
:checked
, a:has()
selector sets thegrid-column:
property - ...result: each div is placed in the appropriate column based on the radio button selection.
grid-auto-flow: column;
causes eachdiv
to flow to the topmost available position on the grid.- When the available screen is narrower than 800px, there's one column of items, but colors are still set for the element based on checkbox state.
- No JavaScript in this demo
See also Kanban Task Board in CSS Grid (Part 2)
On Deck
In Progress
Done
One
Two
Three
Four
Five
Six
Seven
Eight
Nine
Ten
HTML
<main class="task-board">
<h2 class="on-deck">On Deck</h2>
<h2 class="in-progress">In Progress</h2>
<h2 class="done">Done</h2>
<div>
One
<form>
<label><input type="radio" name="task-1" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-1" value="in-progress">In Progress</label>
<label><input type="radio" name="task-1" value="done">Done</label>
</form>
</div>
<div>
Two
<form>
<label><input type="radio" name="task-2" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-2" value="in-progress">In Progress</label>
<label><input type="radio" name="task-2" value="done">Done</label>
</form>
</div>
<div>
Three
<form>
<label><input type="radio" name="task-3" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-3" value="in-progress">In Progress</label>
<label><input type="radio" name="task-3" value="done">Done</label>
</form>
</div>
<div>
Four
<form>
<label><input type="radio" name="task-4" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-4" value="in-progress">In Progress</label>
<label><input type="radio" name="task-4" value="done">Done</label>
</form>
</div>
<div>
Five
<form>
<label><input type="radio" name="task-5" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-5" value="in-progress">In Progress</label>
<label><input type="radio" name="task-5" value="done">Done</label>
</form>
</div>
<div>
Six
<form>
<label><input type="radio" name="task-6" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-6" value="in-progress">In Progress</label>
<label><input type="radio" name="task-6" value="done">Done</label>
</form>
</div>
<div>
Seven
<form>
<label><input type="radio" name="task-7" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-7" value="in-progress">In Progress</label>
<label><input type="radio" name="task-7" value="done">Done</label>
</form>
</div>
<div>
Eight
<form>
<label><input type="radio" name="task-8" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-8" value="in-progress">In Progress</label>
<label><input type="radio" name="task-8" value="done">Done</label>
</form>
</div>
<div>
Nine
<form>
<label><input type="radio" name="task-9" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-9" value="in-progress">In Progress</label>
<label><input type="radio" name="task-9" value="done">Done</label>
</form>
</div>
<div>
Ten
<form>
<label><input type="radio" name="task-10" value="on-deck" checked>On Deck</label>
<label><input type="radio" name="task-10" value="in-progress">In Progress</label>
<label><input type="radio" name="task-10" value="done">Done</label>
</form>
</div>
</main>
SCSS
:root {
--color-on-deck: lightblue;
--color-in-progress: lightgreen;
--color-done: #ccc;
}
body {
font-family: system-ui, sans-serif;
}
code {
font-size: 1rem;
}
main.task-board {
display: grid;
gap: 1.2rem;
padding: 0.5rem;
grid-template-columns: 1fr;
& > form {
}
h2 {
display: none;
}
div {
--color: var(--color-done);
border: 1px solid oklch(from var(--color) 0.7 c h);
border-radius: 0.5em;
background: var(--color);
padding: 1rem;
--color: var(--color-on-deck);
&:has(input[value="on-deck"]:checked) {
}
&:has(input[value="in-progress"]:checked) {
--color: var(--color-in-progress);
}
&:has(input[value="done"]:checked) {
--color: var(--color-done);
}
form {
margin: 1rem 0 0 auto;
opacity: 0.8;
display: flex;
gap: 0;
border: 1px solid oklch(from var(--color) 0.7 c h);
border-radius: 0.2rem;
width: min-content;
label {
cursor: pointer;
font-size: small;
white-space: nowrap;
padding: 0.2rem 0.5rem;
--actionColor: #0000;
background: color-mix(in srgb, var(--actionColor), var(--color));
&:has(input[value="on-deck"]) {
--actionColor: var(--color-on-deck);
}
&:has(input[value="in-progress"]) {
--actionColor: var(--color-in-progress);
}
&:has(input[value="done"]) {
--actionColor: var(--color-done);
}
&:has(input:checked) {
opacity: 0.5;
}
input {
display: none;
}
}
}
}
}
@media only screen and (min-width: 800px) {
main.task-board {
grid-template-columns: 1fr 1fr 1fr;
grid-auto-flow: column;
background: linear-gradient(
90deg,
color-mix(in srgb, var(--color-on-deck), #0000 60%) 33.333%,
color-mix(in srgb, var(--color-in-progress), #0000 60%) 0 66.667%,
color-mix(in srgb, var(--color-done), #0000 60%) 0
);
h2 {
display: block;
margin: 0;
border: solid;
padding: 0 0 0.2ch;
border-width: 0 0 0.2ch 0;
&.on-deck {
grid-column: 1;
}
&.in-progress {
grid-column: 2;
}
&.done {
grid-column: 3;
}
}
div {
grid-column: 1;
&:has(input[value="on-deck"]:checked) {
// already has default
}
&:has(input[value="in-progress"]:checked) {
grid-column: 2;
}
&:has(input[value="done"]:checked) {
grid-column: 3;
}
form {
label {
width: 100%;
}
}
}
}
}
Shoutout
...to carrvo over on the indieweb for inspiring my investigation of this layout.