diff design_system/src/components/link.js @ 258:60a876c4587a

[ui] Add semantic primitive ownership Build a layered Zenbu token and sizing system, make authored controls use native-underneath primitives, migrate mrjunejune without imposing visual surfaces, and document/enforce HTML ownership in the catalog and wiki. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Wed, 05 Aug 2026 05:25:40 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/design_system/src/components/link.js	Wed Aug 05 05:25:40 2026 -0700
@@ -0,0 +1,53 @@
+/**
+ * Adds an optional reusable effect to one direct native link.
+ *
+ * @extends {HTMLElement}
+ */
+export class ZenLink extends HTMLElement {
+  static get observedAttributes() {
+    return ["effect"];
+  }
+
+  connectedCallback() {
+    this._observer ||= new MutationObserver(() => this.sync());
+    this._observer.observe(this, { childList: true });
+    this.sync();
+  }
+
+  disconnectedCallback() {
+    this._observer?.disconnect();
+    this.removeGeneratedDecoration();
+  }
+
+  attributeChangedCallback() {
+    if (this.isConnected) this.sync();
+  }
+
+  removeGeneratedDecoration() {
+    this.querySelector(
+      ':scope > a > zen-icon[data-zen-link-decoration][data-zen-generated="true"]',
+    )?.remove();
+  }
+
+  sync() {
+    const link = this.querySelector(":scope > a");
+    if (!link || this.getAttribute("effect") !== "paw") {
+      this.removeGeneratedDecoration();
+      return;
+    }
+    let decoration = link.querySelector(
+      ":scope > zen-icon[data-zen-link-decoration]",
+    );
+    if (!decoration) {
+      decoration = document.createElement("zen-icon");
+      decoration.dataset.zenLinkDecoration = "";
+      decoration.dataset.zenGenerated = "true";
+      decoration.setAttribute("name", "paw");
+      link.prepend(decoration);
+    }
+  }
+}
+
+if (!customElements.get("zen-link")) {
+  customElements.define("zen-link", ZenLink);
+}