<div>
<h1>Sudoku Fun with CSS</h1>
<p>Figuring out how to select similar cells with just CSS.</p>
<p>Long live :has</p>
<p>Click on a cell to see it in action. (not playable yet, since this really was just to try and setup the CSS grid and blocking out non-viable cells.)</p>
</div>
<!-- static -->
<div class="sudoku">
<div class="cell original" data-sudoku-row="1" data-sudoku-box="1" data-sudoku-col="1" data-sudoku-cell-is-number="1">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">1</span>
</label>
</div>
<div class="cell original" data-sudoku-row="1" data-sudoku-box="1" data-sudoku-col="2" data-sudoku-cell-is-number="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">7</span>
</label>
</div>
<div class="cell original" data-sudoku-row="1" data-sudoku-box="1" data-sudoku-col="3" data-sudoku-cell-is-number="8">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">8</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="1" data-sudoku-box="2" data-sudoku-col="4">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value"></span>
</label>
</div>
<div class="cell solution" data-sudoku-row="1" data-sudoku-box="2" data-sudoku-col="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value"></span>
</label>
</div>
<div class="cell original" data-sudoku-row="1" data-sudoku-box="2" data-sudoku-col="6" data-sudoku-cell-is-number="3">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">3</span>
</label>
</div>
<div class="cell original" data-sudoku-row="1" data-sudoku-box="3" data-sudoku-col="7" data-sudoku-cell-is-number="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">2</span>
</label>
</div>
<div class="cell original" data-sudoku-row="1" data-sudoku-box="3" data-sudoku-col="8" data-sudoku-cell-is-number="6">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">6</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="1" data-sudoku-box="3" data-sudoku-col="9" data-sudoku-cell-is-number="9">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">9</span>
</label>
</div>
<div class="cell original" data-sudoku-row="2" data-sudoku-box="1" data-sudoku-col="1" data-sudoku-cell-is-number="4">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">4</span>
</label>
</div>
<div class="cell original" data-sudoku-row="2" data-sudoku-box="1" data-sudoku-col="2" data-sudoku-cell-is-number="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">2</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="2" data-sudoku-box="1" data-sudoku-col="3" data-sudoku-cell-is-number="6">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">6</span>
</label>
</div>
<div class="cell original" data-sudoku-row="2" data-sudoku-box="2" data-sudoku-col="4" data-sudoku-cell-is-number="1">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">1</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="2" data-sudoku-box="2" data-sudoku-col="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value"></span>
</label>
</div>
<div class="cell solution" data-sudoku-row="2" data-sudoku-box="2" data-sudoku-col="6" data-sudoku-cell-is-number="9">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">9</span>
</label>
</div>
<div class="cell original" data-sudoku-row="2" data-sudoku-box="3" data-sudoku-col="7" data-sudoku-cell-is-number="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">7</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="2" data-sudoku-box="3" data-sudoku-col="8" data-sudoku-cell-is-number="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">5</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="2" data-sudoku-box="3" data-sudoku-col="9" data-sudoku-cell-is-number="3">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">3</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="3" data-sudoku-box="1" data-sudoku-col="1" data-sudoku-cell-is-number="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">5</span>
</label>
</div>
<div class="cell original" data-sudoku-row="3" data-sudoku-box="1" data-sudoku-col="2" data-sudoku-cell-is-number="3">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">3</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="3" data-sudoku-box="1" data-sudoku-col="3" data-sudoku-cell-is-number="9">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">9</span>
</label>
</div>
<div class="cell original" data-sudoku-row="3" data-sudoku-box="2" data-sudoku-col="4" data-sudoku-cell-is-number="6">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">6</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="3" data-sudoku-box="2" data-sudoku-col="5" data-sudoku-cell-is-number="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">2</span>
</label>
</div>
<div class="cell original" data-sudoku-row="3" data-sudoku-box="2" data-sudoku-col="6" data-sudoku-cell-is-number="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">7</span>
</label>
</div>
<div class="cell original" data-sudoku-row="3" data-sudoku-box="3" data-sudoku-col="7" data-sudoku-cell-is-number="8">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">8</span>
</label>
</div>
<div class="cell original" data-sudoku-row="3" data-sudoku-box="3" data-sudoku-col="8" data-sudoku-cell-is-number="1">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">1</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="3" data-sudoku-box="3" data-sudoku-col="9" data-sudoku-cell-is-number="4">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">4</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="4" data-sudoku-box="4" data-sudoku-col="1" data-sudoku-cell-is-number="3">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">3</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="4" data-sudoku-box="4" data-sudoku-col="2" data-sudoku-cell-is-number="8">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">8</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="4" data-sudoku-box="4" data-sudoku-col="3" data-sudoku-cell-is-number="1">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">1</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="4" data-sudoku-box="5" data-sudoku-col="4" data-sudoku-cell-is-number="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">7</span>
</label>
</div>
<div class="cell original" data-sudoku-row="4" data-sudoku-box="5" data-sudoku-col="5" data-sudoku-cell-is-number="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">5</span>
</label>
</div>
<div class="cell original" data-sudoku-row="4" data-sudoku-box="5" data-sudoku-col="6" data-sudoku-cell-is-number="4">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">4</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="4" data-sudoku-box="6" data-sudoku-col="7" data-sudoku-cell-is-number="9">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">9</span>
</label>
</div>
<div class="cell original" data-sudoku-row="4" data-sudoku-box="6" data-sudoku-col="8" data-sudoku-cell-is-number="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">2</span>
</label>
</div>
<div class="cell original" data-sudoku-row="4" data-sudoku-box="6" data-sudoku-col="9" data-sudoku-cell-is-number="6">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">6</span>
</label>
</div>
<div class="cell original" data-sudoku-row="5" data-sudoku-box="4" data-sudoku-col="1" data-sudoku-cell-is-number="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">7</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="5" data-sudoku-box="4" data-sudoku-col="2" data-sudoku-cell-is-number="6">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">6</span>
</label>
</div>
<div class="cell original" data-sudoku-row="5" data-sudoku-box="4" data-sudoku-col="3" data-sudoku-cell-is-number="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">5</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="5" data-sudoku-box="5" data-sudoku-col="4" data-sudoku-cell-is-number="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">2</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="5" data-sudoku-box="5" data-sudoku-col="5" data-sudoku-cell-is-number="9">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">9</span>
</label>
</div>
<div class="cell original" data-sudoku-row="5" data-sudoku-box="5" data-sudoku-col="6" data-sudoku-cell-is-number="8">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">8</span>
</label>
</div>
<div class="cell original" data-sudoku-row="5" data-sudoku-box="6" data-sudoku-col="7" data-sudoku-cell-is-number="4">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">4</span>
</label>
</div>
<div class="cell original" data-sudoku-row="5" data-sudoku-box="6" data-sudoku-col="8" data-sudoku-cell-is-number="3">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">3</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="5" data-sudoku-box="6" data-sudoku-col="9" data-sudoku-cell-is-number="1">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">1</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="6" data-sudoku-box="4" data-sudoku-col="1" data-sudoku-cell-is-number="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">2</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="6" data-sudoku-box="4" data-sudoku-col="2" data-sudoku-cell-is-number="9">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">9</span>
</label>
</div>
<div class="cell original" data-sudoku-row="6" data-sudoku-box="4" data-sudoku-col="3" data-sudoku-cell-is-number="4">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">4</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="6" data-sudoku-box="5" data-sudoku-col="4" data-sudoku-cell-is-number="3">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">3</span>
</label>
</div>
<div class="cell original" data-sudoku-row="6" data-sudoku-box="5" data-sudoku-col="5" data-sudoku-cell-is-number="6">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">6</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="6" data-sudoku-box="5" data-sudoku-col="6" data-sudoku-cell-is-number="1">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">1</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="6" data-sudoku-box="6" data-sudoku-col="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value"></span>
</label>
</div>
<div class="cell original" data-sudoku-row="6" data-sudoku-box="6" data-sudoku-col="8" data-sudoku-cell-is-number="8">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">8</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="6" data-sudoku-box="6" data-sudoku-col="9" data-sudoku-cell-is-number="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">7</span>
</label>
</div>
<div class="cell original" data-sudoku-row="7" data-sudoku-box="7" data-sudoku-col="1" data-sudoku-cell-is-number="9">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">9</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="7" data-sudoku-box="7" data-sudoku-col="2" data-sudoku-cell-is-number="4">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">4</span>
</label>
</div>
<div class="cell original" data-sudoku-row="7" data-sudoku-box="7" data-sudoku-col="3" data-sudoku-cell-is-number="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">2</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="7" data-sudoku-box="8" data-sudoku-col="4" data-sudoku-cell-is-number="8">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">8</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="7" data-sudoku-box="8" data-sudoku-col="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value"></span>
</label>
</div>
<div class="cell solution" data-sudoku-row="7" data-sudoku-box="8" data-sudoku-col="6" data-sudoku-cell-is-number="6">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">6</span>
</label>
</div>
<div class="cell original" data-sudoku-row="7" data-sudoku-box="9" data-sudoku-col="7" data-sudoku-cell-is-number="1">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">1</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="7" data-sudoku-box="9" data-sudoku-col="8" data-sudoku-cell-is-number="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">7</span>
</label>
</div>
<div class="cell original" data-sudoku-row="7" data-sudoku-box="9" data-sudoku-col="9" data-sudoku-cell-is-number="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">5</span>
</label>
</div>
<div class="cell original" data-sudoku-row="8" data-sudoku-box="7" data-sudoku-col="1" data-sudoku-cell-is-number="8">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">8</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="8" data-sudoku-box="7" data-sudoku-col="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value"></span>
</label>
</div>
<div class="cell solution" data-sudoku-row="8" data-sudoku-box="7" data-sudoku-col="3">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value"></span>
</label>
</div>
<div class="cell original" data-sudoku-row="8" data-sudoku-box="8" data-sudoku-col="4" data-sudoku-cell-is-number="9">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">9</span>
</label>
</div>
<div class="cell original" data-sudoku-row="8" data-sudoku-box="8" data-sudoku-col="5" data-sudoku-cell-is-number="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">7</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="8" data-sudoku-box="8" data-sudoku-col="6" data-sudoku-cell-is-number="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">5</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="8" data-sudoku-box="9" data-sudoku-col="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value"></span>
</label>
</div>
<div class="cell solution" data-sudoku-row="8" data-sudoku-box="9" data-sudoku-col="8" data-sudoku-cell-is-number="4">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">4</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="8" data-sudoku-box="9" data-sudoku-col="9" data-sudoku-cell-is-number="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">2</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="9" data-sudoku-box="7" data-sudoku-col="1" data-sudoku-cell-is-number="6">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">6</span>
</label>
</div>
<div class="cell original" data-sudoku-row="9" data-sudoku-box="7" data-sudoku-col="2" data-sudoku-cell-is-number="5">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">5</span>
</label>
</div>
<div class="cell original" data-sudoku-row="9" data-sudoku-box="7" data-sudoku-col="3" data-sudoku-cell-is-number="7">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">7</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="9" data-sudoku-box="8" data-sudoku-col="4" data-sudoku-cell-is-number="4">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">4</span>
</label>
</div>
<div class="cell original" data-sudoku-row="9" data-sudoku-box="8" data-sudoku-col="5" data-sudoku-cell-is-number="1">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">1</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="9" data-sudoku-box="8" data-sudoku-col="6" data-sudoku-cell-is-number="2">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">2</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="9" data-sudoku-box="9" data-sudoku-col="7" data-sudoku-cell-is-number="3">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">3</span>
</label>
</div>
<div class="cell solution" data-sudoku-row="9" data-sudoku-box="9" data-sudoku-col="8" data-sudoku-cell-is-number="9">
<label>
<input type="number" aria-hidden="true" class="visually-hidden">
<span class="value">9</span>
</label>
</div>
<div class="cell original" data-sudoku-row="9" data-sudoku-box="9" data-sudoku-col="9" data-sudoku-cell-is-number="8">
<label>
<input type="number" aria-hidden="true" class="visually-hidden" readonly="">
<span class="value">8</span>
</label>
</div>
</div>
<template id="sudokuCellTemplate">
<div class="cell">
<label>
<input type="number" aria-hidden=true class="visually-hidden">
<span class="value"></span>
</label>
</div>
</template>
<div class="sudoku" data-sudoku></div>
@layer page, sudoku;
:root {
--c-text-input: blue;
--c-selected: red;
--c-selected--secondary: color-mix(in srgb, var(--c-selected) 15%, white);
--c-selected--same-num: color-mix(in srgb, var(--c-selected) 30%, white);
--num-font-size: 3rem;
--s-grid-height: 80vh;
--s-grid-line: 1px;
--s-grid-line--bold: 5px;
}
@layer sudoku {
.value {
display: block;
text-align: center;
width: 100%;
background: var(--_c-cell-bg, white);
font-size: var(--num-font-size);
height: 100%;
align-content: center;
}
.sudoku {
display: grid;
grid-template-columns: repeat(9, 1fr);
grid-template-rows: repeat(9, 1fr);
aspect-ratio: 1/1;
max-width: var(--s-grid-height);
gap: var(--s-grid-line);
background: black;
padding: var(--s-grid-line--bold);
}
.cell {
box-sizing: border-box;
width: 100%;
&:nth-of-type(3n):not(:nth-of-type(9n)) {
padding-right: calc(var(--s-grid-line--bold) - var(--s-grid-line));
}
&:nth-child(n + 19):nth-child(-n + 27) {
padding-bottom: calc(var(--s-grid-line--bold) - var(--s-grid-line));
}
&:nth-child(n + 55):nth-child(-n + 63) {
padding-top: calc(var(--s-grid-line--bold) - var(--s-grid-line));
}
label:focus-within {
--_c-cell-bg: var(--c-selected);
}
}
@mixin secondary-select-highlight($data-attr, $cell-bg) {
.sudoku:has(.cell[#{$data-attr}] label:focus-within) {
[#{$data-attr}] {
label:not(:focus-within) {
--_c-cell-bg: #{$cell-bg};
}
}
}
}
@for $i from 1 through 9 {
@include secondary-select-highlight(
'data-sudoku-row="#{$i}"',
var(--c-selected--secondary)
);
@include secondary-select-highlight(
'data-sudoku-col="#{$i}"',
var(--c-selected--secondary)
);
@include secondary-select-highlight(
'data-sudoku-box="#{$i}"',
var(--c-selected--secondary)
);
@include secondary-select-highlight(
'data-sudoku-cell-is-number="#{$i}"',
var(--c-selected--same-num)
);
}
.solution {
color: var(--c-text-input);
}
}
// demo stuff
@layer page {
* {
box-sizing: border-box;
}
body {
padding: 5vh;
font-family: Arial, sans-serif;
font-weight: bold;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
gap: 30px;
}
label {
height: 100%;
display: block;
}
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
}
View Compiled
// currently not playable, as it's just solving so far and highlighting stuff.
// empty cell
const x = null;
const emptySudoku = [
[x, x, x, x, x, x, x, x, x],
[x, x, x, x, x, x, x, x, x],
[x, x, x, x, x, x, x, x, x],
[x, x, x, x, x, x, x, x, x],
[x, x, x, x, x, x, x, x, x],
[x, x, x, x, x, x, x, x, x],
[x, x, x, x, x, x, x, x, x],
[x, x, x, x, x, x, x, x, x],
[x, x, x, x, x, x, x, x, x]
];
// easy from NYT
const sampleSudoku = [
[1, 7, 8, x, x, 3, 2, 6, x],
[4, 2, x, 1, x, x, 7, x, x],
[x, 3, x, 6, x, 7, 8, 1, x],
[x, x, x, x, 5, 4, x, 2, 6],
[7, x, 5, x, x, 8, 4, 3, x],
[x, x, 4, x, 6, x, x, 8, x],
[9, x, 2, x, x, x, 1, x, 5],
[8, x, x, 9, 7, x, x, x, x],
[x, 5, 7, x, 1, x, x, x, 8]
];
function isValid(sudoku, row, col, num) {
for (let x = 0; x < 9; x++) {
const boxRow = 3 * Math.floor(row / 3) + Math.floor(x / 3);
const boxCol = 3 * Math.floor(col / 3) + (x % 3);
if (
sudoku[row][x] === num ||
sudoku[x][col] === num ||
sudoku[boxRow][boxCol] === num
) {
return false;
}
}
return true;
}
function deepCopySudoku(sudoku) {
return sudoku.map((row) => [...row]);
}
function solveSudoku(sudoku) {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (sudoku[row][col] === null) {
for (let num = 1; num <= 9; num++) {
if (isValid(sudoku, row, col, num)) {
sudoku[row][col] = num;
if (solveSudoku(sudoku)) {
return sudoku;
}
sudoku[row][col] = null;
}
}
return null;
}
}
}
return sudoku;
}
const sudokuCopy = deepCopySudoku(sampleSudoku);
const solution = solveSudoku(sudokuCopy);
function getBoxIndex(row, col) {
return Math.floor(row / 3) * 3 + Math.floor(col / 3);
}
const sudokuContainer = document.querySelector("[data-sudoku]");
const template = document.getElementById("sudokuCellTemplate");
solution.forEach((row, rowIndex) => {
row.forEach((cell, cellIndex) => {
const isOriginal = sampleSudoku[rowIndex][cellIndex] !== null;
// Clone the template content
const cellElement = template.content.cloneNode(true).querySelector("div");
const inputElement = cellElement.querySelector("input");
const labelElement = cellElement.querySelector("label");
const gridBox =
Math.floor(rowIndex / 3) * 3 + Math.floor(cellIndex / 3) + 1;
const gridRow = rowIndex + 1;
const gridCol = cellIndex + 1;
if (isOriginal) {
cellElement.classList.add("original");
} else {
cellElement.classList.add("solution");
}
cellElement.setAttribute("data-sudoku-row", gridRow);
cellElement.setAttribute("data-sudoku-box", gridBox);
cellElement.setAttribute("data-sudoku-col", gridCol);
cellElement.setAttribute("data-sudoku-cell-is-number", cell);
labelElement.querySelector(".value").innerText = cell;
inputElement.value = cell;
inputElement.readOnly = isOriginal;
sudokuContainer.appendChild(cellElement);
});
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.