Just a quick overview of my Etch interface resting. As I get older, contrast becomes increasingly important, so my adjustments focus on it. I initially did some color changes, but the revised interface is much better in that respect. In my case, code is added to the wpCodeBox, and a location rule of URL contains “/?etch=magic”.

Updated Feb 3, 2026: I have since added a nice color coding option from Luke Wakefield and moved the template button to the top of the options in the left toolbar.

Updated April 13, 2026: I have added modified variables to improve the visibility of the new component interface.

Update April 16, 2026: Added styling for the mini-GUI. As well as Luke Wakefield’s “wrap in” update that highlights the loop, condition, and insert icons as well as the contextual menu.

Update March 25, 2026: Add larger, more colorful repeater and preview toggles.

Image 124
Component UI changes

The CSS

I just added this to wpCodeBox.

:root {
    --e-base: hsl(240, 5%, 6%);
    --e-foreground-color-muted: #ffffff;
    --e-light: hsl(240, 7%, 99%);
    --e-base-light: hsl(240, 4%, 17%);
    --e-primary: oklch(85.19% 0.125 186.39);
    --etch-neutral-dark: #202020;
    --etch-opacity-high: 1;
    --etch-opacity-medium: 0.9;
    --etch-opacity-low: 0.8;
    --etch-cps-color-family-category-label-opacity: var(--etch-opacity-medium);
    --etch-font-size-m: 14px;
    --e-font-size-m: 13px;
    --etch-css-selector-pill-bg-active: color-mix(
        in oklch,
        var(--e-primary),
        white 10%
    );
    --etch-css-qa-contextual-menu-bg: var(--e-base);
    --wp-admin-theme-color: var(--primary-dark);
    --etch-css-toolbar-icon-bg-hover: var(--e-base);
}
.etch-css-toolbar-toggle--active {
    --etch-css-toolbar-icon: var(--e-base);
    background: var(--e-primary) !important;
}
.right-click-menu__content {
    --etch-panel-bg: color-mix(
        in oklch,
        var(--etch-css-qa-contextual-menu-bg),
        white 10%
    );
}

.etch-builder-accordion__item:has(
    > .etch-builder-accordion__header--condition
) {
    --e-structure-item-header-foreground-color: oklch(
        from var(--e-condition) calc(l * 1.5) c h / 1
    ) !important;
}
.icon-wrapper:not(.etch-builder-accordion__quick-actions .icon-wrapper) svg {
    color: #ffffff;
    fill: #ffffff;
    width: 100%;
    height: 1.3rem;
}
.icon-wrapper:not(.etch-builder-accordion__quick-actions .icon-wrapper) {
    border-radius: var(--radius);
    padding: 6px;
}
.icon-wrapper:not(.etch-builder-accordion__quick-actions .icon-wrapper):hover {
    background-color: var(--e-primary);

    & svg {
        fill: var(--e-base);
        color: var(--e-base);
    }
}
.etch-label {
    font-size: 0.9rem !important;
}
.css-input-tabs {
    & .css-input-tabs__trigger {
        font-size: 12px !important;
    }
    & .css-input-tabs__wrapper {
        grid-template-columns: repeat(auto-fit, minmax(15ch, 1fr)) !important;
    }
}

.component-button.etch-builder-button {
    justify-content: start;
}
.etch-css-toolbar-toggle {
    --etch-css-toolbar-inactive-icon: 0.85 !important;
    & svg {
        width: 16px;
    }
}

/* =========================================================
   Etch Structure Panel: Colour-coded tag markers
   ========================================================= */

.etch-builder-structure-wrapper .etch-builder-accordion__header {
    position: relative;
    isolation: isolate;
}

.etch-builder-structure-wrapper .etch-builder-accordion__header::after {
    content: '';
    position: absolute;
    left: 0;
    top: 0px;
    bottom: 0px;
    width: 4px;
    z-index: -1;
    border-radius: 0;
    background: var(--etch-tag-color, #64748b);
    opacity: 1;
    pointer-events: none;
}

.etch-builder-structure-wrapper
    .etch-builder-accordion__header.etch-tag--selected {
    box-shadow: inset 0 0 0 1px
        color-mix(in oklch, var(--etch-tag-color, #64748b) 35%, transparent);
}

.etch-builder-structure-wrapper .etch-builder-accordion__header:hover {
    background-color: color-mix(
        in oklch,
        var(--etch-tag-color, #64748b) 25%,
        transparent
    );

    box-shadow: inset 0 0 0 1px
        color-mix(in srgb, var(--etch-tag-color, #64748b) 35%, transparent);
}
.etch-builder-structure-wrapper
    .etch-builder-accordion__header.etch-tag--selected {
    background-color: color-mix(
        in oklch,
        var(--etch-tag-color, #64748b) 35%,
        transparent
    );

    box-shadow: inset 0 0 0 1px
        color-mix(in srgb, var(--etch-tag-color, #64748b) 45%, transparent);
}

/* move the template button to the top the ID can change base on what is instlled */
#bits-c7 {
    order: -1;
}
.media-router .media-menu-item,
.media-frame-content,
.media-button {
    color: black;
}

.etch-builder-icon-switch__thumb {
    --etch-toggle-icon-size: 14px;
    width: calc(var(--etch-toggle-icon-size) * 1.2) !important;
    display: flex;
    align-items: center;
    justify-content: center;
    padding-inline: 2px;
}
.etch-builder-icon-switch__root {
    background: color-mix(in oklch, var(--e-primary) 70%, transparent);
    --etch-toggle-width: 40px;
    --etch-toggle-height: 20px;
    &[data-state='checked'] {
        background: var(--etch-toggle-bg-low-active) !important;
    }
}
/* =========================================================
   Luke's contextual menu update
   ========================================================= */
/* Element Menu (Bottom Bar) */
.element-menu > button[aria-label='Add loop block'],
.element-menu > button[aria-label='Add condition block'],
.element-menu > button:last-of-type {
    --color-mix-perc: 30%;
}

.element-menu > button[aria-label='Add loop block']:hover,
.element-menu > button[aria-label='Add condition block']:hover,
.element-menu > button:last-of-type:hover {
    --color-mix-perc: 40%;
}

.element-menu > button[aria-label='Add loop block'] {
    background-color: color-mix(
        in oklch,
        var(--e-loop) var(--color-mix-perc),
        transparent
    ) !important;
    color: var(--e-loop);
}

.element-menu > button[aria-label='Add condition block'] {
    background-color: color-mix(
        in oklch,
        var(--e-condition) var(--color-mix-perc),
        transparent
    ) !important;
    color: var(--e-condition);
}

.element-menu > button:last-of-type {
    background-color: color-mix(
        in oklch,
        var(--e-primary) var(--color-mix-perc),
        transparent
    ) !important;
    color: var(--e-primary);
}

/* Right Click Menu */
div[textvalue='Wrap in Div'],
div[textvalue='Wrap in Condition'],
div[textvalue='Wrap in Loop'] {
    --e-content-gap: 0.4em;
    color: var(--right-click-menu-item-color);
}

div[textvalue='Wrap in Div'] .right-click-menu__item-label::before,
div[textvalue='Wrap in Condition'] .right-click-menu__item-label::before,
div[textvalue='Wrap in Loop'] .right-click-menu__item-label::before {
    content: '';
    width: 1em;
    height: 1em;
    display: inline-block;
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-position: center;
    -webkit-mask-size: contain;
    mask-repeat: no-repeat;
    mask-position: center;
    mask-size: contain;
    mask-image: var(--right-click-menu-icon);
    -webkit-mask-image: var(--right-click-menu-icon);
    background: var(--right-click-menu-item-color);
}

div[textvalue='Wrap in Div'] {
    --right-click-menu-item-color: var(--etch-text-color);
    --right-click-menu-icon: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGFyaWEtaGlkZGVuPSJ0cnVlIiByb2xlPSJpbWciIGNsYXNzPSJldGNoLWljb24gaWNvbmlmeSBpY29uaWZ5LS1odWdlaWNvbnMtc3Ryb2tlIiB3aWR0aD0iMTZweCIgaGVpZ2h0PSIxNnB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik02IDNINEMyLjg5NTQzIDMgMiAzLjg5NTQzIDIgNVY3TTE3IDNIMTlDMjAuMTA0NiAzIDIxIDMuODk1NDMgMjEgNVY3TTIgMThWMjBDMiAyMS4xMDQ2IDIuODk1NDMgMjIgNCAyMkg2TTE3IDIySDE5QzIwLjEwNDYgMjIgMjEgMjEuMTA0NiAyMSAyMFYxOCIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiPjwvcGF0aD4KICAgIAkJCQk8cGF0aCBkPSJNOS41IDNIMTMuNU05LjUgMjJIMTMuNU0yMSAxMC41VjE0LjVNMiAxMC41TDIgMTQuNSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiPjwvcGF0aD48L3N2Zz4=');
}

div[textvalue='Wrap in Condition'] {
    --right-click-menu-item-color: var(--e-condition);
    --right-click-menu-icon: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGFyaWEtaGlkZGVuPSJ0cnVlIiByb2xlPSJpbWciIGNsYXNzPSJldGNoLWljb24gaWNvbmlmeSBpY29uaWZ5LS1odWdlaWNvbnMtc3Ryb2tlIiB3aWR0aD0iMTZweCIgaGVpZ2h0PSIxNnB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xNSAxOS4wMDAyTDE1IDQuOTk5ODhMMTkgNy45OTk4OCIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiPjwvcGF0aD4KICAgIAkJCQk8cGF0aCBkPSJNOSA0Ljk5OTUxTDkgMTguOTk5OEw1IDE1Ljk5OTgiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBmaWxsPSJub25lIj48L3BhdGg+PC9zdmc+');
}

div[textvalue='Wrap in Loop'] {
    --right-click-menu-item-color: var(--e-loop);
    --right-click-menu-icon: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGFyaWEtaGlkZGVuPSJ0cnVlIiByb2xlPSJpbWciIGNsYXNzPSJldGNoLWljb24gaWNvbmlmeSBpY29uaWZ5LS1odWdlaWNvbnMtc3Ryb2tlIiB3aWR0aD0iMTZweCIgaGVpZ2h0PSIxNnB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMiAxMkMxMiAxMiA5LjI2MTQyIDE3IDYuNSAxN0MzLjczODU4IDE3IDIgMTQuNzYxNCAyIDEyQzIgOS4yMzg1OCAzLjczODU4IDcgNi41IDdDOS4yNjE0MiA3IDEyIDEyIDEyIDEyWk0xMiAxMkMxMiAxMiAxNC43Mzg2IDE3IDE3LjUgMTdDMjAuMjYxNCAxNyAyMiAxNC43NjE0IDIyIDEyQzIyIDkuMjM4NTggMjAuMjYxNCA3IDE3LjUgN0MxNC43Mzg2IDcgMTIgMTIgMTIgMTJaIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiPjwvcGF0aD48L3N2Zz4=');
}
CSS

The JS for color labels

(() => {
  "use strict";

  /* -----------------------------
     Config
  ----------------------------- */

  const PSEUDO_TAG_SELECTOR = ".etch-builder-accordion__header-button > span:not([class])";
  const LABEL_SELECTOR = ".etch-builder-accordion__header-label";

  const COMPONENT_COLOUR = "#6f4598";
  const LOOP_COLOUR = "#5d6bc3";
  const CONDITION_COLOUR = "#8b7332";

  const TAG_GROUPS = {
    section: "section",
    div: "div",

    header: "landmark",
    main: "landmark",
    footer: "landmark",

    a: "link",

    p: "text",
    h1: "heading",
    h2: "heading",
    h3: "heading",
    h4: "heading",
    h5: "heading",
    h6: "heading",

    ul: "list",
    ol: "list",
    li: "list-item",

    img: "media",
    "etch:img": "media",
    figure: "media",
    video: "media",
    svg: "media",
    "etch:svg" : "media",

    iframe: "embed",
  };

  const GROUP_COLOURS = {
    // Structural
    section: "#6434eb",
    div: "#4a7dff",
    landmark: "#3d4c73",

    // Text
    text: "#6c9ca1",
    heading: "#6c9ca1",

    // Interaction
    link: "#28c95b",

    // Lists
    list: "#6c9ca1",
    "list-item": "#6c9ca1",

    // Media
    media: "#f0980c",

    // Embedded
    embed: "#ff1744",

    // Fallback
    other: "#9e9e9e",
  };


  const WRAPPER_SELECTOR = ".etch-builder-structure-wrapper";
  const HEADER_SELECTOR = ".etch-builder-accordion__header";
  const TAG_SELECTOR = ".etch-builder-accordion__header-tag";

  // Prevent double-observing the same root
  const OBSERVED_ROOTS = new WeakSet();

  /* -----------------------------
     Helpers
  ----------------------------- */

  function normaliseTag(text) {
    return String(text || "").trim().toLowerCase();
  }

  function resolveGroup(tag) {
    return TAG_GROUPS[tag] || "other";
  }

  function resolveColour(group) {
    return GROUP_COLOURS[group] || GROUP_COLOURS.other;
  }

  function applyToHeader(headerEl) {
    // -----------------------------
    // 1) SPECIAL BUILDER NODES (OVERRIDES)
    // -----------------------------
    if (headerEl.classList.contains("etch-builder-accordion__header--component")) {
      applySpecialType(headerEl, "component", COMPONENT_COLOUR);
      return;
    }

    if (headerEl.classList.contains("etch-builder-accordion__header--loop")) {
      applySpecialType(headerEl, "loop", LOOP_COLOUR);
      return;
    }

    if (headerEl.classList.contains("etch-builder-accordion__header--condition")) {
      applySpecialType(headerEl, "condition", CONDITION_COLOUR);
      return;
    }

    // -----------------------------
    // 2) NORMAL TAG RESOLUTION (your existing logic)
    // -----------------------------
    let tag = "";

    const tagEl = headerEl.querySelector(TAG_SELECTOR);
    if (tagEl) tag = normaliseTag(tagEl.textContent);

    if (!tag) tag = normaliseBuilderTag(extractPseudoTag(headerEl));

    if (!tag) {
      const label = extractLabelText(headerEl);
      if (label === "svg") tag = "svg";
      if (label === "image") tag = "img";
    }

    if (!tag) return;

    const group = resolveGroup(tag);
    const colour = resolveColour(group);

    headerEl.dataset.etchTag = tag;
    headerEl.dataset.etchTagGroup = group;
    headerEl.style.setProperty("--etch-tag-color", colour);

    headerEl.classList.toggle(
      "etch-tag--selected",
      headerEl.classList.contains("etch-builder-accordion__header--selected")
    );
  }

  function applyToAllIn(wrapperEl) {
    wrapperEl.querySelectorAll(HEADER_SELECTOR).forEach(applyToHeader);
  }

  function extractPseudoTag(headerEl) {
    // Looks for text like "<etch:svg>"
    const spans = headerEl.querySelectorAll(PSEUDO_TAG_SELECTOR);

    for (const span of spans) {
      const txt = (span.textContent || "").trim();
      if (!txt) continue;

      // Normalise patterns like "<etch:svg>" or "<svg>"
      const m = txt.match(/^<\s*([a-z0-9:_-]+)\s*>$/i);
      if (!m) continue;

      return normaliseTag(m[1]); // e.g. "etch:svg"
    }

    return "";
  }

  function extractLabelText(headerEl) {
    const labelEl = headerEl.querySelector(LABEL_SELECTOR);
    return normaliseTag(labelEl ? labelEl.textContent : "");
  }

  function normaliseBuilderTag(tag) {
    // Map builder-prefixed tags to real tags
    // etch:svg -> svg
    if (!tag) return "";
    if (tag === "etch:svg") return "svg";
    if (tag === "etch:img") return "img";
    return tag;
  }
  
  function applySpecialType(headerEl, type, colour) {
    headerEl.dataset.etchTag = type;
    headerEl.dataset.etchTagGroup = type;
    headerEl.style.setProperty("--etch-tag-color", colour);

    headerEl.classList.toggle(
      "etch-tag--selected",
      headerEl.classList.contains("etch-builder-accordion__header--selected")
    );
  }

  /* -----------------------------
     Shadow DOM traversal (open only)
  ----------------------------- */

  function* iterOpenShadowRoots(rootDoc) {
    // Walk all elements; yield any open shadowRoot we can access
    const treeWalker = rootDoc.createTreeWalker(rootDoc.documentElement, NodeFilter.SHOW_ELEMENT);
    let node = treeWalker.currentNode;

    while (node) {
      if (node.shadowRoot) yield node.shadowRoot;
      node = treeWalker.nextNode();
    }
  }

  function findWrappersInRoot(root) {
    // root can be Document, ShadowRoot, or Element
    if (!root || !root.querySelectorAll) return [];
    return Array.from(root.querySelectorAll(WRAPPER_SELECTOR));
  }

  function findAllWrappers(doc) {
    const wrappers = new Set();

    // Main light DOM
    findWrappersInRoot(doc).forEach((w) => wrappers.add(w));

    // Open shadow roots
    for (const sr of iterOpenShadowRoots(doc)) {
      findWrappersInRoot(sr).forEach((w) => wrappers.add(w));
    }

    return Array.from(wrappers);
  }

  /* -----------------------------
     Observers
  ----------------------------- */

  function observeWrapper(wrapperEl) {
    if (OBSERVED_ROOTS.has(wrapperEl)) return;
    OBSERVED_ROOTS.add(wrapperEl);

    // Initial pass
    applyToAllIn(wrapperEl);

    const obs = new MutationObserver((mutations) => {
      for (const m of mutations) {
        if (m.type === "childList" && m.addedNodes.length) {
          m.addedNodes.forEach((node) => {
            if (!(node instanceof Element)) return;

            if (node.matches?.(HEADER_SELECTOR)) {
              applyToHeader(node);
              return;
            }

            node.querySelectorAll?.(HEADER_SELECTOR).forEach(applyToHeader);
          });
        }

        if (m.type === "attributes") {
          const t = m.target;
          if (t instanceof Element && t.matches(HEADER_SELECTOR)) {
            applyToHeader(t);
          }
        }

        if (m.type === "characterData") {
          const parent = m.target?.parentElement;
          const header = parent?.closest?.(HEADER_SELECTOR);
          if (header) applyToHeader(header);
        }
      }
    });

    obs.observe(wrapperEl, {
      subtree: true,
      childList: true,
      attributes: true,
      attributeFilter: ["class", "data-state", "aria-expanded"],
      characterData: true,
    });
  }

  function scanDocument(doc) {
    // Find wrappers in doc + its open shadow roots
    findAllWrappers(doc).forEach(observeWrapper);

    // Also watch for wrappers appearing later (light DOM only)
    if (!OBSERVED_ROOTS.has(doc)) {
      OBSERVED_ROOTS.add(doc);

      const docObs = new MutationObserver(() => {
        findAllWrappers(doc).forEach(observeWrapper);
      });

      docObs.observe(doc.documentElement || doc.body, {
        subtree: true,
        childList: true,
      });
    }
  }

  function scanSameOriginIframes() {
    const iframes = Array.from(document.querySelectorAll("iframe"));
    for (const iframe of iframes) {
      try {
        const doc = iframe.contentDocument;
        if (doc) scanDocument(doc);
      } catch (e) {
        // Cross-origin iframe: cannot access.
        // If the builder is cross-origin, you will need to run this script in that iframe context instead.
      }
    }
  }

  function init() {
    // Scan main document
    scanDocument(document);

    // Scan iframes now
    scanSameOriginIframes();

    // Keep rescanning occasionally (builders often mount/unmount panels)
    window.setInterval(scanSameOriginIframes, 1000);
  }

  init();
  document.addEventListener("DOMContentLoaded", init, { once: true });
})();
JavaScript