diff --git a/content/blog/2023-04-14-kaktovik/index.md b/content/blog/2023-04-14-kaktovik/index.md
new file mode 100644
index 0000000..4bed56c
--- /dev/null
+++ b/content/blog/2023-04-14-kaktovik/index.md
@@ -0,0 +1,77 @@
+---
+date: 2023-04-14
+title: Kaktovik numerals
+scripts:
+ - kaktovik.mjs
+draft: true
+---
+
+I just saw a Scientific American article about the recent inclusion in Unicode of the
+[Kaktovik Numerals](https://en.wikipedia.org/wiki/Kaktovik_numerals),
+a base-20 counting system invented in 1990 by schoolchildren in northern Alaska.
+
+Let's do some counting!
+
+
+
+
Apple-inator
+
+
+
+
+
Apples
+
+
+
+
+Seems pretty easy, right?
+I had it group apples in rows, so it's easier to visually see how many apples you have.
+
+Let's use 🍊 instead of 🍏🍏🍏🍏🍏,
+to make it even easier to see how many rows we have.
+
+
+
+
Apple-inator
+
+
+
+
+
Fruit
+
+
+
+
Apples
+
+
+
+
+---
+
+
+
+
Apple-inator
+
+
+
+
+
Kaktovik
+
+
+
+
Fruit
+
+
+
+
Apples
+
+
+
+
Arabic
+
+
+
+
+Can you see how
+the number of left-and-right lines is the number of complete rows of apples,
+and the number of up-and-down lines is the number of apples left?
\ No newline at end of file
diff --git a/content/blog/2023-04-14-kaktovik/kaktovik.mjs b/content/blog/2023-04-14-kaktovik/kaktovik.mjs
new file mode 100644
index 0000000..8705100
--- /dev/null
+++ b/content/blog/2023-04-14-kaktovik/kaktovik.mjs
@@ -0,0 +1,113 @@
+const FruitNumerals = [
+ "", "🍏", "🍏🍏", "🍏🍏🍏", "🍏🍏🍏🍏",
+ "🍊", "🍊🍏", "🍊🍏🍏", "🍊🍏🍏🍏", "🍊🍏🍏🍏🍏",
+ "🍊🍊", "🍊🍊🍏", "🍊🍊🍏🍏", "🍊🍊🍏🍏🍏", "🍊🍊🍏🍏🍏🍏",
+ "🍊🍊🍊", "🍊🍊🍊🍏", "🍊🍊🍊🍏🍏", "🍊🍊🍊🍏🍏🍏", "🍊🍊🍊🍏🍏🍏🍏",
+]
+
+const KaktovikNumerals = [
+ "𝋀", "𝋁", "𝋂", "𝋃", "𝋄",
+ "𝋅", "𝋆", "𝋇", "𝋈", "𝋉",
+ "𝋊", "𝋋", "𝋌", "𝋍", "𝋎",
+ "𝋏", "𝋐", "𝋑", "𝋒", "𝋓",
+]
+
+function ToNumerals(numerals, n) {
+ let base = numerals.length
+ let its = []
+ if (n < base) {
+ return [numerals[n]]
+ }
+ while (n > 0) {
+ its.unshift(numerals[n % base])
+ n = Math.floor(n / base)
+ }
+ return its
+}
+
+function ToFruit(n) {
+ let its = ToNumerals(FruitNumerals, n)
+ let doc = new DocumentFragment()
+ for (let it of its) {
+ let row = doc.appendChild(document.createElement("div"))
+ row.classList.add("row")
+ row.textContent = it
+ }
+ return doc
+}
+
+function ToKaktovik(n) {
+ let its = ToNumerals(KaktovikNumerals, n)
+ return its.join("")
+}
+
+function ToApples(n) {
+ let doc = new DocumentFragment()
+ while (n > 0) {
+ let apples = Math.min(n, 5)
+ let row = doc.appendChild(document.createElement("div"))
+ row.classList.add("row")
+ row.textContent = "🍏".repeat(apples)
+ n -= apples
+ }
+ return doc
+}
+
+
+class Counter {
+ /**
+ * Initialize a counter element.
+ *
+ * This makes buttons active,
+ * and does an initial render.
+ *
+ * @param {HTMLElement} element
+ */
+ constructor(element, n=1) {
+ this.element = element
+ this.n = n
+ this.min = Number(this.element.dataset.min) || 0
+ this.max = Number(this.element.dataset.max) || Infinity
+
+ for (let e of this.element.querySelectorAll("button.add")) {
+ let amount = Number(e.dataset.amount) || 1
+ e.addEventListener("click", e => this.add(e, amount))
+ }
+ this.render()
+ }
+
+ add(event, amount) {
+ let n = this.n + amount
+ n = Math.min(n, this.max)
+ n = Math.max(n, this.min)
+ if (n != this.n) {
+ this.n = n
+ this.render()
+ }
+ }
+
+ render() {
+ for (let e of this.element.querySelectorAll(".kaktovik")) {
+ e.textContent = ToKaktovik(this.n)
+ }
+ for (let e of this.element.querySelectorAll(".fruit")) {
+ while (e.firstChild) e.firstChild.remove()
+ e.appendChild(ToFruit(this.n))
+ }
+ for (let e of this.element.querySelectorAll(".apples")) {
+ while (e.firstChild) e.firstChild.remove()
+ e.appendChild(ToApples(this.n))
+ }
+ for (let e of this.element.querySelectorAll(".arabic")) {
+ e.textContent = this.n
+ }
+ }
+}
+
+function init() {
+ for (let e of document.querySelectorAll(".counter")) {
+ new Counter(e)
+ }
+}
+
+document.addEventListener("DOMContentLoaded", init)
diff --git a/static/assets/css/default.css b/static/assets/css/default.css
index d476eb7..caf2b58 100644
--- a/static/assets/css/default.css
+++ b/static/assets/css/default.css
@@ -178,6 +178,9 @@ caption {
caption-side: bottom;
font-size: small;
}
+.jistify-center {
+ text-align: center;
+}
.justify-right, input[type="number"] {
text-align: right;
}
@@ -203,6 +206,16 @@ caption {
max-width: 100%;
}
}
+
+.flex {
+ display: flex;
+ justify-content: space-around;
+}
+
+.flex.wrap {
+ flex-wrap: wrap;
+}
+
@media (prefers-color-scheme: dark) {
html {
background-image: url("../images/bg-dark.jpg");