kalby.disclose

Kalby Disclose

The EU AI Act’s Article 50 notice, in one line. Tells your visitors they’re interacting with an AI system, and labels AI-generated content — in all 24 official EU languages, automatically.

No dependencies. No network requests. No cookies. ~7 kB gzipped.

<script src="https://cdn.jsdelivr.net/gh/kalbydot/kalby.disclose@1.0.0/src/kalby-disclose.js"
        data-mode="both" data-lang="auto" data-theme="auto"></script>

That’s the whole install. Mark generated content with one attribute:

<img src="hero.png" data-ai-generated>
<video src="clip.mp4" data-ai-generated="deepfake" data-ai-overlay></video>

Why this exists

Since 2 August 2026, Article 50 of the EU AI Act requires you to tell people when they interact with an AI system, and to label AI-generated or manipulated content. The duty is small per organisation and universal across every EU-facing business with a chatbot — which is exactly the shape of problem that should be solved once and given away, not billed for by the hour.

So this is free, MIT-licensed, and deliberately boring to install.

Where to load it from

Source URL Best for
Self-hosted your own server Recommended. No dependency on anyone, including us
jsDelivr (pinned) cdn.jsdelivr.net/gh/kalbydot/kalby.disclose@1.0.0/src/kalby-disclose.js Immutable, survives us
npm npm i @kalbydot/kalby.disclose Bundlers, build pipelines

It’s a single file with no dependencies and no network calls — there is no technical reason it needs a CDN. We’d rather you self-hosted it. A compliance control shouldn’t depend on a third party’s domain still being renewed in five years.

Options

All optional, all set as data- attributes on the script tag.

Attribute Values Default Does
data-mode both · chat · content both Notice, labels, or both
data-lang auto or a language code auto auto reads <html lang>, then the browser
data-theme auto · light · dark auto Follows the visitor’s system preference
data-position bottom · top bottom Where the floating notice sits
data-chat-target CSS selector Render inline inside your chat UI
data-accent any CSS colour #5b5bd6 Match your brand
data-learn-more-url URL Adds a “Learn more” link
data-remember session · persistent · always session How long a dismissal sticks
data-label-style hover · plain · brand hover See “The label” below
data-no-auto present Skip auto-init; drive it from JS

JavaScript API

KalbyDisclose.init(opts)       // manual init / re-init
KalbyDisclose.update(opts)     // change options live, e.g. {lang:"fr"}
KalbyDisclose.setLang("de")
KalbyDisclose.label(el, type)  // label one element
KalbyDisclose.labelAll(root)   // scan a subtree
KalbyDisclose.dismiss()
KalbyDisclose.resetDismissal()
KalbyDisclose.getAuditLog()    // timestamped record of notices shown
KalbyDisclose.destroy()
KalbyDisclose.languages        // the 24 supported codes

Content added after load — chat replies, infinite scroll, AJAX — is labelled automatically via MutationObserver. No extra code.

How it behaves

The label

At rest the content chip shows the translated term with the sparkles the field has settled on. Move a pointer over it and it crossfades to “AI sniffed” with Kalby’s dog.

State Mark Text
Resting sparkles, in the accent colour the translated term — AI-gegenereerd, KI-generiert, …
Hovered Kalby’s dog, in ink AI sniffed

The dog is drawn in ink rather than the accent colour, because it is the brand mark: black on a light ground, inverted on a dark one, and white over an image scrim. The floating notice carries the same bare inked dog — it is the only mark the widget uses, apart from the sparkles on the label’s resting face.

Both faces occupy the same grid cell, so the chip is as wide as the wider of the two and never changes width — the text around it does not reflow on hover.

Why the plain wording is the one at rest

That chip is the legally operative label under Article 50(2) and 50(4). “AI sniffed” is wordplay: alone it does not tell a reader the content was produced by AI, and it says nothing at all to someone who does not read English. So the disclosing wording is what sits on the page, and the dog is a reward for looking closer — never a substitute for it.

No moving content

The reveal is pure CSS :hover. There is no timer and nothing updates on its own, so the WCAG rules on moving and auto-updating content simply don’t apply. Touch devices have no hover and show the translated term, which is the face that matters. The accessible name and tooltip always carry the full translated disclosure.

<script src="..." data-label-style="plain"></script>   <!-- no hover face at all -->
<script src="..." data-label-style="brand"></script>   <!-- the dog alone -->

Honest limitations

Development

npm install
npm test          # Playwright-driven behaviour tests
npm run build     # minify + print the SRI hash

Contributing

Translation review first, bug reports second, features last. See CONTRIBUTING.md.

Licence

MIT. Use it commercially, fork it, ship it inside your product. If Kalby ever disappears, this file keeps working and anyone can maintain it — which is the point.

One thing MIT does not cover: the Kalby name, the dog mark and the “AI sniffed” wording are brand, not code. Forks are welcome — see TRADEMARKS.md for the two-minute rebrand (both identifiers are defined once at the top of the source).