npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@qadar-ai/disclose

v0.8.0

Published

Disclose by Qadar AI — a drop-in JavaScript widget that adds EU AI Act Art. 50-aligned "AI-generated" transparency labels to any website. Transparency tool, not a compliance guarantee.

Readme

Disclose by Qadar AI

A small (~20 kB gzip, all 24 EU languages inline), dependency-free JavaScript widget that adds EU AI Act Art. 50-aligned "AI-generated" transparency labels to any website's images, video, articles and pages. You mark content with an HTML attribute; Disclose renders the official EU AI-label icon as an accessible badge that expands to a plain-language disclosure.

Disclose is a transparency tool, not a compliance guarantee. It renders the deployer visible-disclosure layer (Art. 50(4)). It does not perform the machine-readable provider marking of Art. 50(2), and using it does not by itself make you compliant. The badge uses the European Commission's official AI-labelling icons unmodified; displaying them does not by itself signal adherence to the Commission's Code of Practice. It is provided by Qadar AI.

Install

Via CDN (no build step):

<script
  src="https://cdn.qadar.ai/disclose/disclose.umd.js"
  data-locale="en"
  data-theme="auto"
  crossorigin="anonymous"
></script>

Use the browser build disclose.umd.js for <script> embeds — CDNs serve the .cjs variant as application/node, which browsers refuse to execute. Pin the exact build with Subresource Integrity using the hash from dist/sri.json (integrity="sha384-…"). The public CDN origin is finalized at deploy time — coordinate the canonical URL before going live.

Via npm:

npm install @qadar-ai/disclose
import { init } from '@qadar-ai/disclose';
init({ locale: 'de' });

Ships both an ESM build (import) and a UMD/CJS build (require), with TypeScript types (dist/index.d.ts). The published tarball contains the dist build only — no source.

Via jsDelivr / unpkg (auto-mirrored from npm, no account):

<script
  src="https://cdn.jsdelivr.net/npm/@qadar-ai/disclose"
  crossorigin="anonymous"
></script>
<!-- or: https://unpkg.com/@qadar-ai/disclose -->

The bare package URL resolves to the browser build (disclose.umd.js). Pin a version and add integrity from the release's sri.json for production use, e.g. https://cdn.jsdelivr.net/npm/@qadar-ai/[email protected]/dist/disclose.umd.js.

Mark your content

Add data-ai-disclose with one of four levels to any element:

| Value | Meaning | | --- | --- | | none | No AI used — nothing is rendered | | ai-assisted | AI-assisted | | ai-generated | AI-generated | | ai-reviewed | AI-generated & human-reviewed (text only — see note) | | ai-modified | AI modified — real content manipulated with AI (uses the official AI MODIFIED icon) |

<img src="art.png" data-ai-disclose="ai-generated" />
<article data-ai-disclose="ai-assisted">…</article>
<video data-ai-disclose="ai-generated"></video>
<body data-ai-disclose="ai-generated" data-ai-disclose-type="page">…</body>

Content type is inferred from the tag (img/picture → image, video, audio, article or [role=article] → article, body/html → page). Override it with data-ai-disclose-type.

Deepfake rule (binding): for AI-generated image, audio or video, the ai-reviewed level never suppresses or softens the visible disclosure — human review does not remove a deepfake disclosure. The "human-reviewed" wording is only shown for text (article/page).

Badge size and position (per element)

Two optional attributes tune each badge. Both are additive — omit them and nothing changes.

| Attribute | Values | Default | Applies to | | --- | --- | --- | --- | | data-ai-disclose-size | sm, md, lg, xl, 2xl, 3xl | md | all badges | | data-ai-disclose-position | top-left, top-right, bottom-left, bottom-right, center | top-left | media overlays |

Sizes are fixed pixel steps (~1.4× per step), not a percentage of the container, so the label stays legible. The official EU icons carry generous built-in whitespace, so these are total badge heights:

| Size | Badge height | | --- | --- | | sm | 27 px | | md (default) | 38 px | | lg | 53 px | | xl | 74 px | | 2xl | 104 px | | 3xl | 146 px |

The detail popover re-anchors per position so it never clips the viewport, at every size.

<!-- EN: a large badge in the top-right corner of a hero image -->
<!-- DE: ein großes Badge in der oberen rechten Ecke eines Hero-Bilds -->
<img src="hero.jpg" data-ai-disclose="ai-generated" data-ai-disclose-size="lg"
     data-ai-disclose-position="top-right" />

For fine-tuning beyond the steps, set the --ai-disclose-scale CSS custom property on the widget host (or any ancestor); it multiplies the chosen step:

.disclose-host { --ai-disclose-scale: 1.15; }

Badge tone (per element)

The official EU icon ships in two tones — a dark (black) pill for light backgrounds and a light (white) pill for dark ones. By default the tone follows the resolved theme; override it per element with data-ai-disclose-tone (parity with Disclose Stamp's --badge-tone):

| Value | Effect | | --- | --- | | dark | Always the dark/black pill. | | light | Always the light/white pill. | | auto | Pick the pill that contrasts with the underlying media — best-effort luminance sampling. |

<img src="dark-hero.jpg" data-ai-disclose="ai-generated" data-ai-disclose-tone="auto" />

auto is same-origin only. It samples the media's pixels on a <canvas>; a cross-origin image (or one without permissive CORS headers) taints the canvas, so sampling is refused and the badge falls back to the theme default. It applies to image/video overlays; for text/pages there is nothing to sample. To make auto work on remote images, serve them with Access-Control-Allow-Origin and load them crossorigin="anonymous".

(DE) Badge-Ton (pro Element). Das offizielle EU-Icon gibt es in zwei Tönen — eine dunkle (schwarze) Pille für helle Hintergründe und eine helle (weiße) für dunkle. Standardmäßig folgt der Ton dem Theme; mit data-ai-disclose-tone (dark / light / auto, Parität mit Stamps --badge-tone) pro Element überschreibbar. auto funktioniert nur same-origin: Es tastet die Pixel des Mediums per <canvas> ab; ein cross-origin-Bild „verunreinigt" den Canvas, das Abtasten wird verweigert und der Ton fällt auf den Theme-Standard zurück. Für auto bei entfernten Bildern: Access-Control-Allow-Origin senden und crossorigin="anonymous" laden.

How the panel opens (per element)

Three optional attributes tune the interaction. All are additive — omit them for the default click-to-open behavior.

| Attribute | Values | Effect | | --- | --- | --- | | data-ai-disclose-trigger | click (default), hover | hover also opens the panel on mouse-over. It's an addition, not a replacement — the badge stays a real button, so click, Enter/Space, and Esc always work (essential for keyboard and touch users). | | data-ai-disclose-auto-open | present = on | Opens the panel once, when the badge first scrolls into view. | | data-ai-disclose-panel | off | Badge-only mode: show the badge with no detail panel. The badge becomes a static labelled image (role="img" with the disclosure as its accessible name) — no button, nothing to open. |

<img src="art.png" data-ai-disclose="ai-generated" data-ai-disclose-trigger="hover" />
<article data-ai-disclose="ai-reviewed" data-ai-disclose-auto-open>…</article>
<img src="thumb.jpg" data-ai-disclose="ai-modified" data-ai-disclose-panel="off" />

(DE) Wie sich das Panel öffnet (pro Element). data-ai-disclose-trigger="hover" öffnet das Panel zusätzlich beim Überfahren — ergänzend, nicht ersetzend: Klick, Enter/Leertaste und Esc funktionieren weiterhin (wichtig für Tastatur- und Touch-Bedienung). data-ai-disclose-auto-open öffnet das Panel einmalig, sobald das Badge erstmals in den sichtbaren Bereich scrollt. data-ai-disclose-panel="off" = Nur-Badge-Modus: Badge ohne Panel, als statisches beschriftetes Bild (role="img") — kein Button, nichts zum Öffnen.

Provenance metadata (per element)

Add optional detail about how the content was made. It shows in the panel (below the level note) and enriches the JSON-LD marker:

| Attribute | Example | Panel | JSON-LD | | --- | --- | --- | --- | | data-ai-disclose-tool | Midjourney | Tool | creator (SoftwareApplication) | | data-ai-disclose-date | 2026-07-01 | Created (localized) | dateCreated (only if a valid ISO date) | | data-ai-disclose-note | Generated with v6 | Note | disambiguatingDescription |

<img src="art.png" data-ai-disclose="ai-generated"
     data-ai-disclose-tool="Midjourney" data-ai-disclose-date="2026-07-01"
     data-ai-disclose-note="Upscaled, minor retouch" />

Safety: all three are free text rendered escaped — panel values are set via textContent (never parsed as HTML), and the JSON-LD serializer escapes <, so a value can't break out of the <script> element or inject markup. Labels are authoritative in EN + DE; other locales fall back to English for these three words.

(DE) Provenienz-Metadaten (pro Element). Optionale Angaben zur Entstehung: data-ai-disclose-tool (z. B. Midjourney), data-ai-disclose-date (ISO), data-ai-disclose-note (kurzer Freitext). Sie erscheinen im Panel (unter dem Hinweis) und reichern den JSON-LD-Marker an (creator, dateCreated nur bei gültigem ISO-Datum, disambiguatingDescription). Sicherheit: alle drei sind Freitext und werden escaped gerendert (Panel via textContent, JSON-LD escaped <) — kein HTML-/Script-Ausbruch möglich.

Mark without touching the HTML (selector config)

When you can't (or don't want to) add attributes to each element — a CMS you don't control, a third-party theme, marking via a tag manager — map CSS selectors → disclosure instead. Set window.DiscloseConfig.mark before the widget loads, and/or pass a JSON data-mark on the loader script:

<script>
  window.DiscloseConfig = {
    mark: {
      '.blog-hero img': 'ai-generated',
      'article.news': { level: 'ai-reviewed', size: 'lg' },
    },
  };
</script>
<script src="https://cdn.jsdelivr.net/npm/@qadar-ai/disclose" defer></script>

Each value is a level string, or an object { level, size?, position?, type? }. It works by stamping the normal marking attributes onto matching elements, so everything else behaves identically — and a real per-element attribute always wins (precedence: element attribute > selector-config > script-tag default > built-in). It's SPA-safe: elements added later (client-side routing, infinite scroll) are matched and marked by the same observer.

Google Tag Manager recipe

Create a Custom HTML tag (fire on the pages you want) that sets the config and injects the loader — the global is read when the script initializes:

<script>
  window.DiscloseConfig = { mark: { 'main img.ai': 'ai-generated' } };
  var s = document.createElement('script');
  s.src = 'https://cdn.jsdelivr.net/npm/@qadar-ai/disclose';
  s.defer = true;
  document.head.appendChild(s);
</script>

(DE) Ohne HTML-Änderung markieren (Selector-Config). Wenn Sie die Elemente nicht anfassen können (fremdes CMS, Theme, Tag-Manager), ordnen Sie CSS-Selektoren → Kennzeichnung zu: window.DiscloseConfig.mark vor dem Laden setzen und/oder ein JSON-data-mark am Loader-Script. Wert ist ein Level-String oder { level, size?, position?, type? }. Ein echtes Element-Attribut gewinnt immer (Vorrang: Element-Attribut > Selector-Config > Script-Default > eingebaut). SPA-sicher — später eingefügte Elemente werden vom Observer mitmarkiert. Für Google Tag Manager: ein Custom-HTML-Tag, das window.DiscloseConfig setzt und den Loader einfügt (siehe Snippet oben).

Languages

Disclose ships full catalogs for all 24 official EU languages, inline — so it keeps its zero runtime network calls with no locale to fetch. The locale is auto-detected; override it with data-ai-disclose-lang (or data-locale) on the loader script, or init({ locale }).

Locale precedence: init({ locale })data-ai-disclose-langdata-locale<html lang>navigator.languageen. Region variants normalize to the base language (pt-BRpt, de-ATde); an unsupported language falls back to English.

Supported: bg cs da de el en es et fi fr ga hr hu it lt lv mt nl pl pt ro sk sl sv.

Brand tokens stay English in every language — AI (never a localized abbreviation), EU AI Act, Disclose, Qadar AI — while the surrounding copy and the Article-50 legal terms use each language's official wording.

Translation review status. English and German are authoritative. The other 22 are drafted with each language's official EU AI Act (Reg. (EU) 2024/1689) Article-50 terminology and are pending native review before production legal use. Confidence per language: High (anchor terms verified against the official OJ/EUR-Lex text): da de en fr it lt lv nl sv. Medium (official anchor terms from knowledge, prose idiomatic — native check recommended): bg cs el es et fi ga hr hu pl pt ro sk sl. Low (native review required): mt.

Machine-readable page marker (JSON-LD)

Alongside the visible badge, Disclose injects one consolidated <script type="application/ld+json"> into the page <head> (light DOM — so search engines and AI crawlers can read it, unlike the Shadow-DOM badge). It describes each marked element with its schema.org type (ImageObject / VideoObject / AudioObject / Article / CreativeWork), a resolvable contentUrl, and the IPTC digitalSourceType — the same machine-readable vocabulary Disclose Stamp embeds in-file (trainedAlgorithmicMedia for AI-generated/-reviewed; compositedWithTrainedAlgorithmicMedia for AI-assisted/-modified). It is built inline from the DOM — zero network calls, no visible change, and it updates when new marked content appears.

What this is — and isn't. This is a standards-based, page-level machine-readable marker, complementary to provider-side EU AI Act Article 50(2) marking. It does not by itself discharge the provider's Article 50(2) duty (that is the generator's job, at source). Disclose adds an on-page, crawlable signal on the deployer's side.

Publishers who manage their own structured data can turn it off with data-ai-disclose-jsonld="off" on the loader script (default on).

(DE) Maschinenlesbarer Seiten-Marker (JSON-LD). Neben dem sichtbaren Badge fügt Disclose einen gebündelten <script type="application/ld+json"> in den <head> ein (Light DOM, also für Suchmaschinen und AI-Crawler lesbar). Er beschreibt jedes markierte Element mit seinem schema.org-Typ, einer auflösbaren contentUrl und dem IPTC-digitalSourceType — demselben maschinenlesbaren Vokabular, das Disclose Stamp in die Datei einbettet. Alles inline, ohne Netzwerkaufrufe, ohne sichtbare Änderung. Das ist ein standardbasierter, seitenweiter maschinenlesbarer Marker, ergänzend zur anbieterseitigen Kennzeichnung nach Art. 50 Abs. 2 EU AI Act — er erfüllt die Anbieterpflicht aus Art. 50 Abs. 2 nicht selbst. Abschalten mit data-ai-disclose-jsonld="off" am Loader-Script (standardmäßig an).

Global defaults (per page)

Set a page-wide fallback for badge size, overlay position, or content type on the loader script, so you don't repeat the same attribute on every element:

<script src="https://cdn.jsdelivr.net/npm/@qadar-ai/disclose" defer
        data-default-size="lg" data-default-position="bottom-right" data-default-type="image">
</script>

Precedence (the rule): element attribute > selector-config > script-tag default > built-in default. A per-element data-ai-disclose-size (etc.) always wins; the script-tag data-default-* fills in only where an element is silent; the built-in default (md, top-left, inferred type) applies when neither is set. For content type, a confident tag/role (<img>, <video>, <article> …) is element-level signal and still outranks data-default-type — the default only resolves ambiguous elements like a bare <div>.

(DE) Globale Defaults. data-default-size, data-default-position, data-default-type am Loader-Script setzen seitenweite Fallbacks. Vorrang-Regel: Element-Attribut > Selector-Config > Script-Tag-Default > eingebauter Default. Ein eindeutiges Tag (<img> …) schlägt data-default-type; der Typ-Default greift nur bei mehrdeutigen Elementen (z. B. <div>).

What the panel explains (and the "Learn more" link)

The detail panel answers the visitor's core question in-panel and in their language — an "About these labels" block (localized in all 24 languages) lists the three official label families in one line each and states that the labels are applied by the website's operator. No click and no off-site hop are needed to understand a label.

Below that, an optional "Learn about AI transparency" link points to Qadar's visitor explainer page qadar.ai/ai-label. It is locale-gated: shown only where that page exists (launch: en, de) — for any other locale there is no default link (the in-panel block already covers it; never a silent language fallback). The link is rel="nofollow noopener".

Publishers can point "Learn more" at their own AI-transparency policy with data-learn-more-url — on the loader script (page-wide) or per element. A publisher override always shows, in any locale; only the link target changes, never the (localized) label text. Unsafe URLs (javascript:, data: …) are rejected.

<img src="art.png" data-ai-disclose="ai-generated" data-learn-more-url="/legal/ai-transparency" />
<script src="…/disclose" defer data-learn-more-url="https://acme.example/ai-policy"></script>

(DE) Was das Panel erklärt (und der „Mehr erfahren"-Link). Das Panel beantwortet die Kernfrage im Panel und in der Sprache der besuchenden Person: ein „Über diese Kennzeichnungen"-Block (in allen 24 Sprachen) nennt die drei offiziellen Label-Familien je einlinig und stellt klar, dass die Kennzeichnungen vom Betreiber der Website vergeben werden — ohne Klick, ohne externen Sprung. Darunter steht optional der „Mehr über AI-Transparenz"-Link auf Qadars Besucher-Erklärseite qadar.ai/ai-label, locale-gated (Start: en, de) — für andere Sprachen kein Standard-Link (keine stille Sprach-Rückfall-Lösung). Mit data-learn-more-url zeigt er auf eine eigene Seite (am Script oder pro Element; Override wird immer angezeigt, in jeder Sprache; nur das Ziel ist konfigurierbar, der Text bleibt fest). rel="nofollow noopener".

Theming the panel

Match the detail panel to your brand with a small, controlled set of CSS custom properties. They inherit through the Shadow DOM, so set them anywhere on the page (e.g. :root):

:root {
  --ai-disclose-accent: #e11d48; /* level pill + links + focus ring */
  --ai-disclose-bg: #fff7ed; /* panel background */
  --ai-disclose-text: #7c2d12; /* panel text */
  --ai-disclose-radius: 2px; /* panel corner radius */
  --ai-disclose-font: Georgia, serif; /* panel font */
}

Each is optional and falls back to the built-in look (which still respects light/dark and prefers-color-scheme). What you can theme is deliberately limited to the panel chrome. The official EU AI-label icon and the disclosure wording are never themeable — they're the trust anchor and must look the same everywhere. Contrast is your responsibility: if you override colors, keep the panel text readable (aim for WCAG AA) in both light and dark.

(DE) Panel gestalten. Passen Sie das Detail-Panel mit einem kleinen, kontrollierten Satz CSS-Custom-Properties an Ihre Marke an (--ai-disclose-accent, --ai-disclose-bg, --ai-disclose-text, --ai-disclose-radius, --ai-disclose-font) — sie wirken durch das Shadow DOM, z. B. auf :root gesetzt. Jede ist optional und fällt auf das eingebaute Aussehen zurück (inkl. Light/Dark). Nur das Panel-Chrome ist gestaltbar — das offizielle EU-AI-Label-Icon und der Kennzeichnungstext bleiben unveränderbar (Vertrauensanker). Kontrast liegt in Ihrer Verantwortung (Ziel WCAG AA, in Light und Dark).

Configure

Options resolve as JS override > script data-* > <html lang> > navigator.language > default.

| Option | data-* | Values | Default | | --- | --- | --- | --- | | Locale | data-ai-disclose-lang / data-locale | any of the 24 EU languages | auto-detected → en | | Theme | data-theme | auto, light, dark | auto | | Page-bar position | data-position | top, bottom | bottom | | Default badge size | data-default-size | sm3xl | md | | Default overlay position | data-default-position | top-leftcenter | top-left | | Default content type | data-default-type | image/video/audio/article/page | inferred | | Policy link | data-learn-more-url (also per element) | any safe URL | Qadar Disclose page | | Selector marks | data-mark (JSON) / window.DiscloseConfig.mark | selector → level or { level, size?, … } | none | | JSON-LD marker | data-ai-disclose-jsonld | off = don't emit | on | | Manual init | data-manual | present = don't auto-init | auto-init |

API

import { init, refresh, version } from '@qadar-ai/disclose';

init(); // decorate existing content + observe for dynamically-added content
init({ locale: 'de', theme: 'dark' });
refresh(); // re-scan on demand (e.g. after a manual DOM update); returns the count decorated

Dynamically-added and lazily-loaded content is picked up automatically via a MutationObserver.

Bundle & privacy

Disclose is ~20 kB gzip and never phones home — no network calls, no tracking, no third-party requests at runtime. Everything (all 24 language catalogs and the official EU icons) is embedded, so a marked page renders with zero runtime network calls, and the whole widget self-hosts as a single file if you don't want to load it from a CDN at all.

That "nothing phones home" property is deliberate and is why the language catalogs are inline rather than lazy-loaded: fetching a per-language chunk at runtime would make every non-English visitor's browser call the widget's origin, leaking their IP + host site + language to it — the opposite of a transparency tool's promise. Keeping locales inline costs bundle size but keeps the privacy guarantee absolute.

Approximate gzip budget: language catalogs ~10 kB · official EU icons ~4–5 kB · core logic ~5–6 kB. At ~20 kB total the widget is still tiny (smaller than most single images), so we optimize for the privacy promise and one-line embed over raw kB. If a deployment ever needs a sub-10 kB single-locale build, per-locale artifacts (disclose.<lang>.js, still zero-network, still one SRI) are the planned option — lazy runtime loading is intentionally not offered.

Accessibility

Every badge is a real focusable button with an accessible label; the signal is carried by the official EU icon and the button's text alternative (not color alone). The icon ships in both light and dark treatments and follows the resolved theme (and prefers-color-scheme under auto); a semi-transparent variant is used when the badge overlays media. The detail panel is a labelled dialog, keyboard-operable, closable with Esc, and restores focus to the badge. Targets WCAG 2.2 AA. Styling is isolated in a Shadow DOM, so host CSS neither bleeds in nor is affected. The icons are embedded as data URIs, so the widget makes zero network calls at runtime.

Develop

npm install
npm test          # vitest (jsdom)
npm run build     # ESM + UMD + .d.ts + SRI hashes
npm run lint      # eslint + prettier

See examples/index.html for a live demo (run npm run build first, then serve the repo root).

Releasing (maintainers)

Publishing to npm is gated and manual — the Publish to npm workflow only runs via workflow_dispatch and defaults to a dry run. It uses npm OIDC Trusted Publishing (no long-lived NPM_TOKEN), so the following one-time setup is required on the npm side:

  1. npm account + org. Sign in to npmjs.com; ensure the qadar-ai org exists (the package is @qadar-ai/disclose).
  2. First publish creates the package. Trusted-publisher settings live under a package's settings, so the package must exist first. Do one manual publish from a trusted machine with 2FA: npm publish (version 0.1.0 or an rc).
  3. Add the Trusted Publisher. In the package settings → Trusted Publisher, add:
    • Organization/user: qadar-ai · Repository: disclose
    • Workflow filename: publish.yml · Environment name: npm-publish
    • Allowed action: npm publish
  4. Every later publish is CI-only. Push a tag, then dispatch the Publish to npm workflow with dry_run=false. No token needed; npm authenticates via OIDC.
  5. Provenance attaches automatically once the repository is public; it is skipped while the repo is private.

Requirements (handled by the workflow): Node ≥ 22.14, npm ≥ 11.5.1, id-token: write.

License

MIT © Qadar AI