Components / Japan / Kintsugi Card

Kintsugi Card beta

A status card repaired in gold: the cracks of an incident, drawn as kintsugi veins that mend on arrival.

Preview

INCIDENT 214 — RESTORED

Stronger at the seams

The registry went down at 03:12 and came back at 03:54 with every part intact. We publish the cracks instead of plastering over them — the veins are where it is strongest now.

42 minDowntime
NoneData loss
3Veins
FullPostmortem
<div class="k-kin" data-kin>
  <canvas aria-hidden="true"></canvas>
  <div class="k-kin-in">
    <p class="k-kin-tag">INCIDENT 214 — RESTORED</p>
    <h3>Stronger at the seams</h3>
    <p>The registry went down at 03:12 and came back at 03:54 with every part intact. We publish the cracks instead of plastering over them — the veins are where it is strongest now.</p>
    <div class="k-kin-meta">
      <div><b>42 min</b><span>Downtime</span></div>
      <div><b>None</b><span>Data loss</span></div>
      <div><b>3</b><span>Veins</span></div>
      <div><b>Full</b><span>Postmortem</span></div>
    </div>
  </div>
</div>
/* KATA Kintsugi Card — the break, gilded. */
.k-kin{position:relative;width:100%;background:var(--k-zinc-950);color:var(--k-on-color);border-radius:14px;overflow:hidden;font-family:var(--k-font);padding:22px}
.k-kin canvas{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
.k-kin-in{position:relative;z-index:1;pointer-events:none}
.k-kin .k-kin-tag{font-size:11px;font-weight:650;letter-spacing:.16em;color:var(--k-amber)}
.k-kin h3{font-size:18.5px;font-weight:650;letter-spacing:-.015em;margin:8px 0 7px}
.k-kin p{font-size:13px;line-height:1.75;color:var(--k-zinc-400);max-width:380px}
.k-kin-meta{display:flex;gap:22px;margin-top:16px;flex-wrap:wrap}
.k-kin-meta div b{display:block;font-size:14.5px}
.k-kin-meta div span{font-size:11px;letter-spacing:.1em;color:var(--k-zinc-500);text-transform:uppercase}
// KATA Kintsugi Card — the veins draw themselves, gold over glow.
document.querySelectorAll('[data-kin]').forEach(function (card) {
  var canvas = card.querySelector('canvas');
  var ctx = canvas.getContext('2d');
  var DPR = Math.min(3, window.devicePixelRatio || 1);
  var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  var cs = getComputedStyle(card);
  var GOLD = cs.getPropertyValue('--k-amber').trim();
  var GLOW = cs.getPropertyValue('--k-amber').trim();
  var CRACKS = [
    [[.06, .1], [.2, .3], [.3, .34], [.44, .52], [.52, .6]],
    [[.44, .52], [.5, .72], [.47, .95]],
    [[.3, .34], [.34, .12], [.42, .02]],
    [[.52, .6], [.68, .66], [.84, .6], [.97, .72]],
    [[.68, .66], [.74, .86], [.72, 1.02]]
  ];
  var W, H;
  function size() { W = card.clientWidth; H = card.clientHeight; canvas.width = W * DPR; canvas.height = H * DPR; ctx.setTransform(DPR, 0, 0, DPR, 0, 0); }
  function draw(t) {
    ctx.clearRect(0, 0, W, H);
    CRACKS.forEach(function (line) {
      var total = line.length - 1;
      var upto = t * total;
      [5.5, 1.8].forEach(function (wdt, pass) {
        ctx.beginPath();
        ctx.strokeStyle = pass === 0 ? GLOW : GOLD;
        ctx.globalAlpha = pass === 0 ? .5 : 1;
        ctx.lineWidth = wdt;
        ctx.lineJoin = 'round';
        for (var i = 0; i < Math.floor(upto); i++) {
          var a = line[i], b = line[i + 1];
          if (i === 0) ctx.moveTo(a[0] * W, a[1] * H);
          ctx.lineTo(b[0] * W, b[1] * H);
        }
        var f = upto - Math.floor(upto);
        if (f > 0 && Math.floor(upto) < total) {
          var a2 = line[Math.floor(upto)], b2 = line[Math.floor(upto) + 1];
          if (Math.floor(upto) === 0) ctx.moveTo(a2[0] * W, a2[1] * H);
          ctx.lineTo((a2[0] + (b2[0] - a2[0]) * f) * W, (a2[1] + (b2[1] - a2[1]) * f) * H);
        }
        ctx.stroke();
      });
    });
    ctx.globalAlpha = 1;
  }
  var start = null;
  function mend(ts) {
    if (!start) start = ts;
    var t = Math.min(1, (ts - start) / 1100);
    draw(t);
    if (t < 1) requestAnimationFrame(mend);
  }
  function run() { size(); if (reduced) { draw(1); } else { start = null; requestAnimationFrame(mend); } }
  card.addEventListener('pointerenter', run);
  if ('IntersectionObserver' in window) {
    var io = new IntersectionObserver(function (es) { if (es[0].isIntersecting) { run(); io.disconnect(); } }, { threshold: .4 });
    io.observe(card);
  } else { run(); }
  size(); draw(reduced ? 1 : 0);
});

Installation

CLI

node kata-ui.mjs add kintsugi-card

Writes the part’s CSS and JS into your project. New to the CLI? See the CLI doc.

Manual

  1. Load tokens.css once, before any part.
  2. Copy the CSS tab into your stylesheet.
  3. Copy the HTML where it belongs. The part has a script — include it after the markup; it initializes itself.

Usage

Kintsugi is the craft of repairing broken pottery with gold — the break becomes the most valuable part. This card reports an incident the same way: what broke, how long it took, and the veins drawn across the surface as the record, gilded instead of hidden. The mend animates on arrival; hover draws it again. For status pages and postmortems that intend to be believed.

Accessibility

The facts are text — status, downtime, data loss — in a normal card; the veins are canvas decoration above it. Under prefers-reduced-motion the veins are drawn complete and still: repaired is repaired, animated or not.

Anatomy

The public surface of this part, read from its own source. Class names are the API — the CSS tab above is the full contract.

.k-kinclass
.k-kin-inclass
.k-kin-tagclass
.k-kin-metaclass
data-kindata hook
aria-hiddenARIA

Related parts