cd /news/ai-tools/show-hn-kestero-website-widgets-that… · home topics ai-tools article
[ARTICLE · art-120191] src=kestero.com ↗ pub= topic=ai-tools verified=true sentiment=· neutral

Show HN: Kestero, website widgets that record what they do (no cookies)

Kestero, a new website widget service, launched on Hacker News with tools that let users describe a widget in plain language and have AI draft the code, or write HTML, CSS, and JavaScript manually, with all widgets running in sandboxed iframes, tracking views and clicks without cookies, and integrating via a single script tag on platforms like Squarespace, Shopify, WordPress, or plain HTML.

read9 min views2 publishedSep 3, 2026
Show HN: Kestero, website widgets that record what they do (no cookies)
Image: source

Describe what you want. It gets drafted for you. Paste one line into your site and it appears.

Say what you want. The AI drafts it. #

Three descriptions, and the three widgets that came back. None of them was drawn by hand.

Somebody typed this

And got this

Open the editor and describe your own.

Or write it yourself #

When the blocks do not cover what you want, write the HTML, CSS and JavaScript. It still runs sealed off in its own frame, still gets the accessibility check, and still counts views and clicks.

index.html

<p id="bar">
  <strong id="clock">00:00:00</strong> left in the sale
</p>

style.css

#bar { margin: 0; font: 14px/1.4 system-ui, sans-serif; color: #f5f5f7; text-align: center }
#clock { font-variant-numeric: tabular-nums; letter-spacing: 0.02em }

main.js

// Counts down to midnight in the visitor's own timezone.
var end = new Date();
end.setHours(24, 0, 0, 0);

function pad(n) { return String(n).padStart(2, "0"); }

setInterval(function () {
  var s = Math.max(0, Math.round((end - Date.now()) / 1000));
  document.getElementById("clock").textContent =
    pad(Math.floor(s / 3600)) + ":" + pad(Math.floor(s / 60) % 60) + ":" + pad(s % 60);
}, 1000);

Three steps #

Design it

Change the words, the colours, where it sits.

Paste one line

Once, anywhere on your site. Never again.

Watch it work

Views, clicks, replies. Off again in a click.

<script async src="https://kestero.com/widget.js" data-key="your-key"></script>

Squarespace, Shopify, WordPress, or plain HTML.

Start from one of these #

Each one opens the editor with a working example in it.

Announcement bar

One sentence across the top of every page.

Email signup widget

A box that asks, and keeps the answers.

Notification widget

A card in the corner, with a button.

Changelog widget

A bell, and a panel of your updates.

Feedback widget

One question, a few answers, and the replies waiting.

Onboarding checklist

One step per screen, until they are done.

You decide when it shows up #

Pick one and try it on the window below. These are real — not a video.

{
  "version": 2,
  "name": "Announcement bar",
  "kind": "banner",
  "theme": {
    "fontFamily": "system-ui, -apple-system, \"Segoe UI\", sans-serif",
    "accent": {
      "hex": "#ffffff",
      "alpha": 1
    },
    "surface": {
      "hex": "#1D1D1F",
      "alpha": 1
    },
    "text": {
      "hex": "#ffffff",
      "alpha": 1
    },
    "muted": {
      "hex": "#6b7280",
      "alpha": 1
    },
    "radius": 14,
    "fontSize": 13,
    "gap": 8,
    "scheme": "auto"
  },
  "surface": {
    "mode": "bar",
    "position": "top",
    "mount": "",
    "width": 360,
    "offset": 0,
    "backdropBlur": 0,
    "backdrop": false,
    "dismissible": true,
    "animation": {
      "preset": "fade",
      "durationMs": 200
    },
    "css": ""
  },
  "trigger": {
    "on": "load",
    "delayMs": 0,
    "scrollPercent": 50,
    "idleMs": 20000,
    "siteMs": 60000,
    "selector": "",
    "frequency": "always",
    "cooldownDays": 7,
    "maxShows": 3,
    "pages": {
      "mode": "everywhere",
      "paths": []
    },
    "audience": {
      "visitor": "anyone",
      "devices": "any"
    }
  },
  "screens": [
    {
      "id": "main",
      "blocks": [
        {
          "id": "row",
          "type": "group",
          "direction": "row",
          "align": "center",
          "justify": "center",
          "gap": 12,
          "children": [
            {
              "id": "tag",
              "type": "icon",
              "name": "tag",
              "size": 16,
              "label": ""
            },
            {
              "id": "copy",
              "type": "text",
              "role": "body",
              "text": "Free delivery on everything this week.",
              "format": "plain"
            }
          ]
        }
      ]
    }
  ],
  "start": "main"
}

Safe to put on your site #

You are putting somebody else’s code on your site. Straight answers, with the numbers behind them.

It will not slow your site down

It loads in the background. If we went down, your site would not notice.

18.8 KB gzipped

It cannot break your design

Sealed off both ways. Your CSS cannot reach in; nothing in it reaches out.

Shadow DOM, :host { all: initial }

No cookies. No tracking.

Nothing to consent to. The only thing we store is what someone types into your form.

0 cookies set

It works for everyone

Keyboard, screen reader, reduced motion. The product cannot promise what somebody draws with it, so what it offers instead is a check that runs on demand and a record of what it found.

The check

  • axe-core 4.10, against WCAG 2.1 AA.
  • Run against the exact spec, inside the real shadow root — not a copy of the widget built for testing.
  • In both colour schemes, because a widget set to follow the visitor ships both and a contrast failure in one is a failure.

The record it writes

  • checkedAt
  • The date it ran.
  • specHash
  • A fingerprint of the widget it ran against. Edit the widget and the fingerprints stop matching, so the record stops applying — rather than ageing quietly into a badge that means nothing.

If you have to get this signed off — the record every widget carries, and how each claim is known. #

Every widget is saved as plain data with no code in it, which means a program can read a widget and work out what it is able to do. One does. The two cards below are real records it produced, from two real widgets, while this page was rendering — the first is the widget you scrolled past, the second is the same widget with one image added from somebody else’s server.

The widget above

What this widget does not do

  • No custom code
  • No custom CSS
  • No third-party requests
  • Not checked for accessibility since it was last edited

Worked out from the saved widget itself every time this page loads, not recorded once and trusted afterwards.

The same widget, with one image from another host

What this widget does not do

  • No custom code
  • No custom CSS
  • Loads from another host — images.example.com
  • Not checked for accessibility since it was last edited

Worked out from the saved widget itself every time this page loads, not recorded once and trusted afterwards.

Neither is written down anywhere on this page — which is why the second one is allowed to be worse than the first, and why it is. Both were computed by the same function the API runs on every save and every read, lib/schemas/widget-guarantee.ts

.

Look at the last line of each. Neither widget has been checked for accessibility, and both say so plainly rather than staying quiet about it. A badge that could only ever come out green would not be worth reading, and a reviewer would be right not to.

What the embedded file does

All of this is true by reading one file, which is the point of it being one file.

  • PropertyNo third-party code in it — no import, no require, nothing bundled
  • PropertyNothing that arrives over the network is ever assigned to innerHTML; the two assignments in the file both take icon markup defined in the file
  • PropertyRenders in a shadow root under :host { all: initial }, so the host stylesheet cannot reach it and it cannot reach the host
  • UnrepresentableA link can only be http, https or mailto — a javascript: URL is refused by the schema, so no stored widget contains one
  • Enforced at saveCustom CSS is parsed, scoped into the widget's own subtree, and refused if it fixes itself over the host page or fetches from a host that is not ours
  • Enforced at savePer-project domain allow-list, so a scraped embed key renders nowhere else

What is checked for accessibility

A check, not a promise. The product cannot guarantee what someone draws with it, so what it offers is the check and the date it ran.

  • Checked per widgetaxe-core runs against the exact spec, in both colour schemes, inside the real shadow root — and the result is stamped with that spec's fingerprint, so it stops applying the moment the widget is edited
  • UnrepresentableAn image without alt text and an input without a label are not storable specs
  • PropertyA text block's role decides the heading level the renderer emits, so restyling a block cannot change what it announces itself to be
  • TestedFocus moves into a modal and returns on close, Escape closes unless the spec says the widget cannot be dismissed, and a closed widget leaves the DOM rather than hiding in the tab order — 232 tests drive the real renderer in a browser environment
  • Propertyprefers-reduced-motion is read by the renderer, and the animation the spec asked for does not run

What is stored about a visitor

One thing, and only because you asked for it.

  • PropertyNo cookies, so nothing to consent to
  • PropertyNo visitor table and no fingerprint. How often a widget comes back, and which changelog posts have been read, are timestamps in the visitor's own localStorage
  • PropertyWhat a visitor types into a form you built is stored, and that is the whole of it. It is stored because your widget asked a question — nothing is collected that a visitor did not type
  • SettingRecording the page and recording the referrer are each switchable off, independently
  • DeploymentData at rest in the EU — the database is in AWS eu-west-1

Measured, not estimated

  • widget.js — 18.8 KB gzipped
  • Zero dependencies
  • No cookies, no visitor row
  • axe-checked per widget
  • Data stored in the EU

Each of those has a command beside it in the repository that produces it, and a test that fails when it stops being true. The first column above is checkable without asking us anything — read the file your visitors would run.

One price, or none #

Keeping one is free. Pay only for the second.

Free

Free

One widget, on one site, for as long as you like.

  • One saved widget
  • A small Kestero link on your widget
  • Every block, colour and trigger — build anything Pro can build
  • Describe what you want and have it drafted — 10 a day
  • The accessibility check, and the record it writes

Make one, free

Pro

€19/month

Coming soon

For running more than one thing at a time.

  • Views, clicks and form replies, for every widget
  • Which pages and which sources they came from
  • As many widgets as you want
  • No Kestero link on your widgets
  • 100 drafts a day, not 10
  • Across as many sites as you want
  • Email support, answered by the person who built it

Start free while it opens

Free does not expire and asks for no card. Pro is not open yet, so there is nothing to buy and nothing to cancel. When it opens it will be billed monthly through Stripe, and cancelling will delete nothing and switch nothing off.

Need an invoice, a DPA or a signed security questionnaire? Talk to us.

Go and make one #

The editor opens now — no account, no email, nothing to fill in. Decide afterwards whether you want to keep what you made.

Open the editor

── more in #ai-tools 4 stories · sorted by recency
── more on @kestero 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-kestero-webs…] indexed:0 read:9min 2026-09-03 ·