WIDGETS
Sudoku Widget
Click To Go Back To The Widgets PagePreview
About
This is a web sudoku widget that fastly generates a random sudoku puzzle for guests.
- To play it, click on a cell and press a number on your keyboard (or use the number pad on the widget).
- To set a number on a cell, press a number once.
- To leave a mark on a cell, press the same number twice or add a different number.
- To remove a mark or turn a single mark into a number, press the target number on your keyboard.
- To clear a cell, press Backspace or Delete button on your keyboard.
- You can also use arrow keys or tab button to move the cell cursor.
Version: 1.0, File Size: 21.33KB
How To Add?
By hotlinking:
Put this script on your HTML document.
<script src="https://404city.neocities.org/widget/sudoku.js" data-difficulty="random" data-symmetrical="true"></script> By uploading on your site (Recommended):
Click here to download the script. Then, upload it on your site and put this line on your HTML document.
<script src="YOUR_SCRIPT_PATH_HERE" data-difficulty="random" data-symmetrical="true"></script> - Make sure the script is located after <body> but before </body>.
- The sudoku will instantly pop-up where you put the script.
Script Attributes:
Here are the script attributes of the sudoku widget.
| Attribute | Conditions |
|---|---|
| data-difficulty |
easy => generates an easy level sudoku. A random number of cells between 36 and 45 are revealed. medium => generates a medium level sudoku. A random number of cells between 30 and 35 are revealed. hard => generates a hard level sudoku. A random number of cells between 26 and 30 are revealed. expert => generates an expert level sudoku. A random number of cells between 22 and 26 are revealed. random => generates a random level sudoku. |
| data-symmetrical |
true => generates a symmetrical number layout. Makes generation process faster and puzzle looks better. false => generates a random number layout. |
ID, Class and Variable Information:
Here are the ID, Class and Variable selectors of the sudoku widget's elements. You can use them on your own CSS file to customize your sudoku widget.
| ID/Class/Variable | Element Type | Description |
|---|---|---|
| #sudoku | div | Main body of the sudoku widget. |
| #sudokuText | span | Shows the current status of the puzzle and displays checking messages. |
| #sudokuGrid | div | Contains every sudoku cell. |
| .sudokuCell | div | General class for each sudoku cell. |
| .sudokuCell::before | pseudo-element | Displays cell numbers. |
| .sudokuCell[data-readonlycell="false"] | div | Selector for editable cells. |
| .sudokuCell[data-readonlycell="true"] | div | Selector for uneditable cells. |
| .sudokuCell:focus | div | Marks the chosen cell (acts as a cursor). |
| --numericalHintBackground | defined in .sudokuCell | Background mark variable for the same numbers with the chosen cell's number. |
| --numericalHintColor | defined in .sudokuCell | Color mark variable for the same numbers with the chosen cell's number. |
| .sudokuMistakeHighlight | div | Marks incorrect cells after checking. |
| .sudokuMark | div | Each number marks in the cells. |
| #sudokuNumButtonPad | div | Container of number entry buttons. |
| .sudokuNumButton | button | Each number entry button of the sudoku. |
| #sudokuFooter | div | Wrapper of time display and control buttons. |
| #sudokuTime | span | Displays game time. |
| .sudokuButton | button | General class for the sudoku's control buttons. |
| #sudokuCheckButton | button | Checks the puzzle when clicked. |
| #sudokuRevealButton | button | Reveals the chosen cell when clicked. |
| #sudokuResetButton | button | Restarts the game when clicked. |
| #sudokuNewButton | button | Generates a new random puzzle when clicked. |
Default CSS Rules
These are the default CSS selectors that are automatically generated and applied by the script:
:where(#sudoku, #sudoku *) {margin: 0; padding: 0; box-sizing: border-box;}
:where(#sudoku) {
height: 330px;
width: 260px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
padding: 0.25em;
gap: 0.25rem;
border: 1px solid #000;
}
:where(#sudokuGrid) {
display: grid;
grid-template-columns: repeat(9, 1fr);
width: 100%;
border: 1px solid #000;
border-right: none;
border-bottom: none;
}
:where(.sudokuCell) {
--numericalHintBackground: #0ff;
--numericalHintColor: #000;
width: 100%;
height: 100%;
min-width: 0;
aspect-ratio: 1 / 1;
display: flex;
justify-content: center;
align-items: center;
cursor: cell;
border: none;
border-right: 1px dotted #000;
border-bottom: 1px dotted #000;
:where(&:nth-child(3n)) {border-right-style: solid;}
:where(&:nth-child(n + 19):nth-child(-n + 27),
&:nth-child(n + 46):nth-child(-n + 54),
&:nth-child(n + 73):nth-child(-n + 81)) {border-bottom-style: solid;}
:where(&[data-readonlycell="true"]) {
font-weight: bold;
user-select: none;
}
:where(.sudokuMark) {
display: none !important;
visibility: hidden !important;
line-height: 0.5;
font-size: 10px;
pointer-events: none;
user-select: none;
place-self: center;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
text-align: center;
letter-spacing: 0.125rem;
}
:where(&[data-cellmark="0"]:not([data-cellvalue="0"]))::before {content: attr(data-cellvalue);}
:where(&:not([data-cellmark="0"])) {
display: grid !important;
grid-template-columns: repeat(3, 1fr);
:where(.sudokuMark) {display: flex !important;}
}
:where(&[data-cellmark*="1"] .sudokuMark:nth-child(1),
&[data-cellmark*="2"] .sudokuMark:nth-child(2),
&[data-cellmark*="3"] .sudokuMark:nth-child(3),
&[data-cellmark*="4"] .sudokuMark:nth-child(4),
&[data-cellmark*="5"] .sudokuMark:nth-child(5),
&[data-cellmark*="6"] .sudokuMark:nth-child(6),
&[data-cellmark*="7"] .sudokuMark:nth-child(7),
&[data-cellmark*="8"] .sudokuMark:nth-child(8),
&[data-cellmark*="9"] .sudokuMark:nth-child(9)) {visibility: visible !important;}
:where(#sudoku:has(&[data-cellvalue="1"]:focus) &[data-cellvalue="1"], #sudoku:has(&[data-cellvalue="1"]:focus) &[data-cellmark*="1"] .sudokuMark:nth-child(1),
#sudoku:has(&[data-cellvalue="2"]:focus) &[data-cellvalue="2"], #sudoku:has(&[data-cellvalue="2"]:focus) &[data-cellmark*="2"] .sudokuMark:nth-child(2),
#sudoku:has(&[data-cellvalue="3"]:focus) &[data-cellvalue="3"], #sudoku:has(&[data-cellvalue="3"]:focus) &[data-cellmark*="3"] .sudokuMark:nth-child(3),
#sudoku:has(&[data-cellvalue="4"]:focus) &[data-cellvalue="4"], #sudoku:has(&[data-cellvalue="4"]:focus) &[data-cellmark*="4"] .sudokuMark:nth-child(4),
#sudoku:has(&[data-cellvalue="5"]:focus) &[data-cellvalue="5"], #sudoku:has(&[data-cellvalue="5"]:focus) &[data-cellmark*="5"] .sudokuMark:nth-child(5),
#sudoku:has(&[data-cellvalue="6"]:focus) &[data-cellvalue="6"], #sudoku:has(&[data-cellvalue="6"]:focus) &[data-cellmark*="6"] .sudokuMark:nth-child(6),
#sudoku:has(&[data-cellvalue="7"]:focus) &[data-cellvalue="7"], #sudoku:has(&[data-cellvalue="7"]:focus) &[data-cellmark*="7"] .sudokuMark:nth-child(7),
#sudoku:has(&[data-cellvalue="8"]:focus) &[data-cellvalue="8"], #sudoku:has(&[data-cellvalue="8"]:focus) &[data-cellmark*="8"] .sudokuMark:nth-child(8),
#sudoku:has(&[data-cellvalue="9"]:focus) &[data-cellvalue="9"], #sudoku:has(&[data-cellvalue="9"]:focus) &[data-cellmark*="9"] .sudokuMark:nth-child(9)) {
background: var(--numericalHintBackground);
color: var(--numericalHintColor);
}
:where(&:focus) {
outline: none;
background: #00f;
color: #fff;
}
:where(&.sudokuMistakeHighlight) {
color: #fff;
background: #f00;
}
}
:where(#sudokuText) {
font-size: 14px;
user-select: none;
margin-bottom: -0.125rem;
&.sudokuTextBlink {animation: sudokuTextBlink 1s steps(1) infinite;}
}
@keyframes sudokuTextBlink {50% {visibility: hidden;}}
:where(#sudokuNumButtonPad) {
display: flex;
width: 100%;
border: 1px solid #000;
border-right: none;
}
:where(.sudokuNumButton) {
appearance: none;
border: none;
border-right: 1px solid #000;
background: #0000;
font-size: 1em;
cursor: pointer;
flex: 1;
aspect-ratio: 1 / 1;
box-sizing: content-box;
}
:where(#sudokuFooter) {
width: 100%;
display: flex;
align-items: center;
font-size: 14px;
gap: 0.25rem;
}
:where(#sudokuTime) {
flex: 1;
user-select: none;
}
:where(.sudokuButton) {
appearance: none;
font-size: 1em;
border: 1px solid #000;
background: none;
padding: 0 0.125rem;
cursor: pointer;
}
HTML Structure
The HTML code that is automatically generated by the script:
<div id='sudoku'>
<span id="sudokuText">Loading...</span>
<div id='sudokuGrid'>
<div class="sudokuCell" tabindex="0" data-cellrow="#" data-cellcol="#" data-cellvalue="#" data-cellmark="#" data-readonlycell="#">
::before
<div class="sudokuMark">1</div>
<div class="sudokuMark">2</div>
<div class="sudokuMark">3</div>
<div class="sudokuMark">4</div>
<div class="sudokuMark">5</div>
<div class="sudokuMark">6</div>
<div class="sudokuMark">7</div>
<div class="sudokuMark">8</div>
<div class="sudokuMark">9</div>
</div>
. . .
(81 sudoku cells in total.)
. . .
<div class="sudokuCell" tabindex="0" data-cellrow="#" data-cellcol="#" data-cellvalue="#" data-cellmark="#" data-readonlycell="#"> . . . </div>
</div>
<div id="sudokuNumButtonPad">
<button class="sudokuNumButton" data-numbuttonvalue="1">1</button>
<button class="sudokuNumButton" data-numbuttonvalue="2">2</button>
<button class="sudokuNumButton" data-numbuttonvalue="3">3</button>
<button class="sudokuNumButton" data-numbuttonvalue="4">4</button>
<button class="sudokuNumButton" data-numbuttonvalue="5">5</button>
<button class="sudokuNumButton" data-numbuttonvalue="6">6</button>
<button class="sudokuNumButton" data-numbuttonvalue="7">7</button>
<button class="sudokuNumButton" data-numbuttonvalue="8">8</button>
<button class="sudokuNumButton" data-numbuttonvalue="9">9</button>
<button class="sudokuNumButton" data-numbuttonvalue="Delete">X</button>
</div>
<div id="sudokuFooter">
<span id="sudokuTime"><u>Time</u>:0</span>
<button id="sudokuCheckButton" class="sudokuButton">Check</button>
<button id="sudokuRevealButton" class="sudokuButton">Reveal</button>
<button id="sudokuResetButton" class="sudokuButton">Reset</button>
<button id="sudokuNewButton" class="sudokuButton">New</button>
</div>
</div>
Update Log
[v1.0] - 1 September 2026, Tuesday
- Widget is ready to use!
Click To Go Back To The Widgets Page