DevHero

Share your wins

DevHero is show-and-tell for developers — code, live demos, and the story behind the ship. Hand-coding is the culture; AI is welcome when you disclose and lead.

11 wins shipped100% handcrafted this week
Derived
@ccrystle·Handcrafted

Derived: 222: I made the debounce faster.

Forked from @ccrystle/posts/3e8f3bc0dd2e49378c3200c2cd5ec8b1's demo and tweaked it.

function debounce(fn, ms) {
  let t;
  const wrapped = (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
  wrapped.cancel = () => clearTimeout(t);
  return wrapped;
}

const out = document.getElementById("out");
const run = debounce((v) => { out.textContent = "debounced: " + v; }, 1);
document.getElementById("q").addEventListener("input", (e) => run(e.target.value));
Live demo
Derived
@ccrystle·Handcrafted

222: I made the debounce faster.

Forked from @ccrystle/posts/1421af3546654fd49c1fd3b4a76a65fa's demo and tweaked it.

function debounce(fn, ms) {
  let t;
  const wrapped = (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
  wrapped.cancel = () => clearTimeout(t);
  return wrapped;
}

const out = document.getElementById("out");
const run = debounce((v) => { out.textContent = "debounced: " + v; }, 222);
document.getElementById("q").addEventListener("input", (e) => run(e.target.value));
Live demo
Derived
@ccrystle·Handcrafted

DIFF: I made the debounce faster.

Forked from @ccrystle/posts/1421af3546654fd49c1fd3b4a76a65fa's demo and tweaked it.

function debounce(fn, ms) {
  let t;
  const wrapped = (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
  wrapped.cancel = () => clearTimeout(t);
  return wrapped;
}
//this is a comment 
//for the sake od showing a diff

const out = document.getElementById("out");
const run = debounce((v) => { out.textContent = "debounced: " + v; }, 1);
document.getElementById("q").addEventListener("input", (e) => run(e.target.value));
Live demo
Derived
@ccrystle·Handcrafted

Derived: I made the debounce faster.

Forked from @ccrystle/posts/1421af3546654fd49c1fd3b4a76a65fa's demo and tweaked it.

javascript
function debounce(fn, ms) {
  let t;
  const wrapped = (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
  wrapped.cancel = () => clearTimeout(t);
  return wrapped;
}

const out = document.getElementById("out");
const run = debounce((v) => { out.textContent = "debounced: " + v; }, 1);
document.getElementById("q").addEventListener("input", (e) => run(e.target.value));
Live demo
Derived
@ccrystle·Handcrafted

Derived: I made the debounce faster.

Forked from @ccrystle/posts/1421af3546654fd49c1fd3b4a76a65fa's demo and tweaked it.

javascript
function debounce(fn, ms) {
  let t;
  const wrapped = (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
  wrapped.cancel = () => clearTimeout(t);
  return wrapped;
}

const out = document.getElementById("out");
const run = debounce((v) => { out.textContent = "debounced: " + v; }, 1);
document.getElementById("q").addEventListener("input", (e) => run(e.target.value));
Live demo
Derived
@ccrystle·Handcrafted

I made the debounce faster.

Forked from @ccrystle/posts/7ca7dad2f58e40f18f07499c8a039349's demo and tweaked it.

function debounce(fn, ms) {
  let t;
  const wrapped = (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
  wrapped.cancel = () => clearTimeout(t);
  return wrapped;
}

const out = document.getElementById("out");
const run = debounce((v) => { out.textContent = "debounced: " + v; }, 1);
document.getElementById("q").addEventListener("input", (e) => run(e.target.value));
Live demo
Derived
@ccrystle·Handcrafted

Derived: I made the debounce faster.

Forked from @ccrystle/posts/395af35a00d542508cb3e079b4d1019e's demo and tweaked it.

function debounce(fn, ms) {
  let t;
  const wrapped = (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
  wrapped.cancel = () => clearTimeout(t);
  return wrapped;
}

const out = document.getElementById("out");
const run = debounce((v) => { out.textContent = "debounced: " + v; }, 100);
document.getElementById("q").addEventListener("input", (e) => run(e.target.value));
Live demo
Derived
@ccrystle·Handcrafted

Derived: I made the debounce faster.

Forked from @ada/posts/seedpost2's demo and tweaked it.

function debounce(fn, ms) {
  let t;
  const wrapped = (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
  wrapped.cancel = () => clearTimeout(t);
  return wrapped;
}
const out = document.getElementById("out");
const run = debounce((v) => { out.textContent = "debounced: " + v; }, 14);
document.getElementById("q").addEventListener("input", (e) => run(e.target.value));
Live demo
@charlie·AI-deconstructed

Deconstructing an AI-generated CSS grid holy grail

The model spat out a holygrail layout with magic numbers and nested grids. Below is the simplified version that uses named areas.

css
.page {
  display: grid;
  grid-template:
    "header header" auto
    "nav main" 1fr
    "footer footer" auto
    / 12rem 1fr;
  min-height: 100vh;
}
.header { grid-area: header; }
.nav { grid-area: nav; }
.main { grid-area: main; }
.footer { grid-area: footer; }
Live demo
@ada·AI-assisted

AI wrote a debounce — I made it cancelable

Started from an AI snippet for debounce. It leaked timers on unmount. Here's the fix: return a cancel function and clear on dispose.

javascript
export function debounce(fn, ms) {
  let t;
  const wrapped = (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
  wrapped.cancel = () => clearTimeout(t);
  return wrapped;
}
Live demo
@charlie·Handcrafted

Shipped a deterministic tokenizer — no AI involved

I needed a tokenizer that always produces the same tokens for the same input. Instead of prompting a model, I wrote a small state machine. Why? Flaky tokens break snapshots and make diffs noisy. Determinism wins for too…

typescript
export function tokenize(input: string) {
  const out = [];
  let i = 0;
  while (i < input.length) {
    const ch = input[i]!;
    if (/\s/.test(ch)) { i++; continue; }
    if (/[a-zA-Z_]/.test(ch)) {
      let j = i + 1;
      while (j < input.length && /[\w]/.test(input[j]!)) j++;
      out.push({ kind: "ident", value: input.slice(i, j) });
      i = j; continue;
    }
    out.push({ kind: "op", value: ch }); i++;
  }
  return out;
}