Skip to the content

Kazu数

Generate checked Sudoku and logic puzzles, read native rules and hints, and draw or embed playable boards.

Kazu is the Japanese word 数 for number. About the name

Contents

Reference · Playable number grid

KAZU_PLAY_STYLEconstant

THE STYLE a playable Kazu board wears (mountKazu, <kazu-board>): the drawing's own (KAZU_STYLE) and the board's box, its number pad, its buttons and its lines of words. Colours are custom properties on .kazu-play (--kzp-ink, --kzp-muted, --kzp-rule, --kzp-surface, --kzp-accent, --kzp-good) so a page sets only what it wants different.

Nothing moves when something is chosen: the board is one square box, the lines of words keep the room their longest wording takes, and the buttons are one size. Nothing the player touches can be selected, and no tap on it zooms the page.

Import it

import { KAZU_PLAY_STYLE } from "@johnmorrisdotca/kazu/play";

Definition

const KAZU_PLAY_STYLE: "\n.kazu {\n  --kz-paper: #fbf8f1; --kz-ink: #1f2320; --kz-given: #1f2320; --kz-entry: #1d5fa8; --kz-note: #5b6b7d;\n  --kz-grid: #cfc6b2; --kz-box: #3a3d38; --kz-frame: #a98954; --kz-cage: #6a5a8e; --kz-clue: #7a4b14;\n  --kz-diagonal: #e9dfc6; --kz-peer: #efe8d8; --kz-same: #dcd0f2; --kz-select: #ffe08a; --kz-hint: #b9e3c4;\n  --kz-conflict: #f4b8ad; --kz-wrong: #f4b8ad; --kz-bad: #b5452c; --kz-good: #2f7a4f;\n  --kz-font: system-ui, -apple-system, \"Segoe UI\", sans-serif;\n  display: block; width: 100%; height: auto;\n  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: manipulation; -webkit-tap-highlight-color: transparent;\n  overflow: visible;\n}\n@media (prefers-color-scheme: dark) {\n  :root:not([data-theme=\"light\"]) .kazu {\n    --kz-paper: #262a27; --kz-ink: #ece8dc; --kz-given: #ece8dc; --kz-entry: #8fc1ff; --kz-note: #9fb0c2;\n    --kz-grid: #454a44; --kz-box: #c9c5b8; --kz-frame: #6b5632; --kz-cage: #b8a5e6; --kz-clue: #e8c48f;\n    --kz-diagonal: #34382f; --kz-peer: #2f332f; --kz-same: #433a5c; --kz-select: #6b5a1f; --kz-hint: #25503a;\n    --kz-conflict: #6e2f26; --kz-wrong: #6e2f26; --kz-bad: #ff8a6b; --kz-good: #6fcf97;\n  }\n}\n:root[data-theme=\"dark\"] .kazu {\n  --kz-paper: #262a27; --kz-ink: #ece8dc; --kz-given: #ece8dc; --kz-entry: #8fc1ff; --kz-note: #9fb0c2;\n  --kz-grid: #454a44; --kz-box: #c9c5b8; --kz-frame: #6b5632; --kz-cage: #b8a5e6; --kz-clue: #e8c48f;\n  --kz-diagonal: #34382f; --kz-peer: #2f332f; --kz-same: #433a5c; --kz-select: #6b5a1f; --kz-hint: #25503a;\n  --kz-conflict: #6e2f26; --kz-wrong: #6e2f26; --kz-bad: #ff8a6b; --kz-good: #6fcf97;\n}\n.kazu * { user-select: none; -webkit-user-select: none; }\n.kazu .kz-frame { fill: var(--kz-frame); }\n.kazu .kz-paper { fill: var(--kz-paper); }\n.kazu .kz-diagonal { fill: var(--kz-diagonal); }\n.kazu .kz-peer { fill: var(--kz-peer); }\n.kazu .kz-same { fill: var(--kz-same); }\n.kazu .kz-select { fill: var(--kz-select); }\n.kazu .kz-hint { fill: var(--kz-hint); }\n.kazu .kz-conflict, .kazu .kz-wrong { fill: var(--kz-conflict); }\n.kazu .kz-solved { fill: var(--kz-good); opacity: .14; pointer-events: none; }\n.kazu .kz-grid { fill: none; stroke: var(--kz-grid); stroke-width: 1px; vector-effect: non-scaling-stroke; }\n.kazu .kz-box { fill: none; stroke: var(--kz-box); stroke-width: 2.5px; stroke-linecap: square; vector-effect: non-scaling-stroke; }\n.kazu .kz-cage { stroke: var(--kz-cage); stroke-width: 1.5px; stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; }\n.kazu .kz-digit { font-family: var(--kz-font); font-weight: 600; text-anchor: middle; dominant-baseline: central; font-variant-numeric: tabular-nums; pointer-events: none; }\n.kazu .kz-given { fill: var(--kz-given); font-weight: 700; }\n.kazu .kz-entry { fill: var(--kz-entry); }\n.kazu .kz-digit.kz-bad { fill: var(--kz-bad); }\n.kazu .kz-note { font-family: var(--kz-font); font-weight: 600; fill: var(--kz-note); text-anchor: middle; dominant-baseline: central; pointer-events: none; }\n.kazu .kz-cage-sum { font-family: var(--kz-font); font-weight: 600; fill: var(--kz-cage); text-anchor: start; dominant-baseline: hanging; pointer-events: none; }\n.kazu .kz-clue { font-family: var(--kz-font); font-weight: 700; fill: var(--kz-clue); text-anchor: middle; dominant-baseline: central; pointer-events: none; }\n.kazu .kz-mark-bg { fill: var(--kz-paper); stroke: none; pointer-events: none; }\n.kazu .kz-mark { fill: none; stroke: var(--kz-ink); stroke-width: 2px; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; pointer-events: none; }\n.kazu .kz-hit { fill: transparent; cursor: pointer; }\n\n.kazu-play {\n  --kzp-ink: #1f2320; --kzp-muted: #6b6f68; --kzp-rule: #ddd6c6; --kzp-surface: #fbf8f1; --kzp-accent: #b5452c; --kzp-good: #2f7a4f;\n  display: block; max-width: 100%; box-sizing: border-box; color: var(--kzp-ink); font-family: system-ui, -apple-system, \"Segoe UI\", sans-serif;\n  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent;\n}\n@media (prefers-color-scheme: dark) {\n  :root:not([data-theme=\"light\"]) .kazu-play { --kzp-ink: #ece8dc; --kzp-muted: #a09d93; --kzp-rule: #3a3d38; --kzp-surface: #1d201e; --kzp-accent: #ff8a6b; --kzp-good: #6fcf97; }\n}\n:root[data-theme=\"dark\"] .kazu-play { --kzp-ink: #ece8dc; --kzp-muted: #a09d93; --kzp-rule: #3a3d38; --kzp-surface: #1d201e; --kzp-accent: #ff8a6b; --kzp-good: #6fcf97; }\n.kazu-play *, .kazu-play *::before, .kazu-play *::after { box-sizing: border-box; }\n.kazu-play .kzp-bar { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 8px; min-height: 1.5em; font-variant-numeric: tabular-nums; }\n.kazu-play .kzp-clock { font-weight: 700; font-size: 1.05rem; min-width: 4.5ch; }\n.kazu-play .kzp-progress { color: var(--kzp-muted); font-size: .85rem; }\n.kazu-play .kzp-box { position: relative; width: 100%; aspect-ratio: 1; overflow: hidden; touch-action: manipulation; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; cursor: pointer; border-radius: 8px; }\n.kazu-play .kzp-box:focus-visible { outline: 3px solid var(--kzp-accent); outline-offset: 2px; }\n.kazu-play .kzp-box[data-over=\"true\"] { cursor: default; }\n.kazu-play .kzp-inner { position: absolute; inset: 0; }\n.kazu-play .kzp-inner .kazu { position: absolute; inset: 0; width: 100%; height: 100%; }\n.kazu-play .kzp-pad { display: grid; grid-template-columns: repeat(var(--kzp-columns, 5), minmax(0, 1fr)); gap: 6px; margin-top: 10px; }\n.kazu-play .kzp-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }\n.kazu-play [hidden] { display: none !important; }\n.kazu-play button { font: inherit; color: inherit; user-select: none; -webkit-user-select: none; touch-action: manipulation; }\n.kazu-play .kzp-key, .kazu-play .kzp-button { border: 1px solid var(--kzp-rule); background: var(--kzp-surface); color: var(--kzp-ink); border-radius: 12px; min-height: 44px; min-width: 44px; padding: 0 12px; font-size: 1.05rem; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; gap: 6px; cursor: pointer; }\n.kazu-play .kzp-button { border-radius: 999px; font-size: .85rem; font-weight: 600; }\n.kazu-play .kzp-key:hover:not(:disabled), .kazu-play .kzp-button:hover:not(:disabled) { border-color: var(--kzp-ink); }\n.kazu-play .kzp-key:disabled, .kazu-play .kzp-button:disabled { opacity: .32; cursor: default; }\n.kazu-play .kzp-key[data-done=\"true\"] { opacity: .4; }\n.kazu-play .kzp-key[data-erase=\"true\"] { color: var(--kzp-muted); }\n.kazu-play .kzp-button[aria-pressed=\"true\"] { background: var(--kzp-ink); color: var(--kzp-surface); border-color: var(--kzp-ink); }\n.kazu-play .kzp-key:focus-visible, .kazu-play .kzp-button:focus-visible { outline: 3px solid var(--kzp-accent); outline-offset: 2px; }\n.kazu-play .kzp-says { margin: 10px 0 0; min-height: 3.9em; font-size: .85rem; line-height: 1.4; color: var(--kzp-muted); }\n.kazu-play .kzp-says[data-warn=\"true\"] { color: var(--kzp-accent); font-weight: 600; }\n.kazu-play[data-solved=\"true\"] .kzp-says, .kazu-play[data-solved=\"true\"] .kzp-clock { color: var(--kzp-good); font-weight: 600; }\n.kazu-play .kzp-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n"

Examples

import { KAZU_PLAY_STYLE } from "@johnmorrisdotca/kazu/play";
console.log(KAZU_PLAY_STYLE.length);
7288

What it prints (run when this page was built)

View sourceEdit on GitHubMade from the source of @johnmorrisdotca/kazu 2.1.0.