Updated:
May 2025

Instructions

Components and properties marked with ⚡ use custom code for enhanced functionality. Follow the steps below, to activate the full capabilities of FlowGuide.

1. Copy script and paste into before </body> tag.

This unlocks all FlowGuide functionality, including auto-generated menu and pagination.

<script>
// ========================================================
// 01 — Auto-Generate Menu
// ========================================================
$(document).ready(function () {
  // ============================
  // Primary Navigation (fg-nav)
  // ============================
  if ($("[fg-nav-generate='true']").length) {
    // Process each item with fg-show-menu="true"
    $("[fg-show-menu='true']").each(function (index) {
      var title = $(this).attr("fg-nav-title");
      // Set text for the corresponding text element
      $("[fg-nav='text" + (index + 1) + "']").text(title);
      // Set href for the corresponding link element
      $("[fg-nav='link" + (index + 1) + "']").attr("href", "#" + title);
    });

    // Remove all link elements that didn't get set (i.e., with index greater than the count of fg-show-menu="true")
    $("[fg-nav^='link']").each(function () {
      var navAttr = $(this).attr("fg-nav");
      var num = parseInt(navAttr.replace("link", ""), 10);
      var count = $("[fg-show-menu='true']").length;
      if (num > count) {
        $(this).remove(); // Remove the element from the DOM
      }
    });
  }

  // ============================
  // Sub Navigation (fg-subnav)
  // ============================
  if ($("[fg-subnav-generate='true']").length) {
    // Process each item with fg-show-menu="true"
    $("[fg-show-menu='true']").each(function (index) {
      var title = $(this).attr("fg-nav-title");
      // Set text for the corresponding subnav text element
      $("[fg-subnav='text" + (index + 1) + "']").text(title);
      // Set href for the corresponding subnav link element
      $("[fg-subnav='link" + (index + 1) + "']").attr("href", "#" + title);
    });

    // Remove all subnav link elements that didn't get set
    $("[fg-subnav^='link']").each(function () {
      var navAttr = $(this).attr("fg-subnav");
      var num = parseInt(navAttr.replace("link", ""), 10);
      var count = $("[fg-show-menu='true']").length;
      if (num > count) {
        $(this).remove(); // Remove the element from the DOM
      }
    });
  }
});

// ========================================================
// 02 – Keyboard Shortcuts
// ========================================================
$(document).ready(function () {
  // Listen for keydown events (works better for non-printable and number keys)
  $(document).keydown(function (e) {
    // Get the pressed key's value
    var key = String.fromCharCode(e.which).toLowerCase(); // Convert to lowercase

    // Check for number keys (1-9)
    if (e.which >= 49 && e.which <= 57) {
      key = String.fromCharCode(e.which); // Treat as the number itself
    }

    // Loop through all shortcut elements (for the number keys or letter shortcuts)
    $("[fg-nav^='shortcut']").each(function () {
      var shortcutText = $(this).text().toLowerCase(); // Get the text of the current shortcut

      // If the shortcut text matches the pressed key, trigger the corresponding link
      if (shortcutText === key) {
        var link = $("[fg-nav='link" + $(this).attr("fg-nav").replace("shortcut", "") + "']");
        
        // Trigger the click event on the matching link
        if (link.length) {
          link[0].click(); // Simulate a click on the matching link
        }
      }
    });

    // Special case for 'shortcut-cta' (CTA trigger on 'c')
    if (key === "c") { // If the key pressed is 'C', trigger the CTA link
      var linkCta = $("[fg-nav='link-cta']"); // Find the matching CTA link
      if (linkCta.length) {
        linkCta[0].click(); // Simulate a click on the CTA link
      }
    }
  });
});

// ========================================================
// 03 – Close tablet/mobile menu on anchor link click
// ========================================================
$(document).ready(function () {
    const $links = $('[fg-nav^="link"], [fg-subnav^="link"]');
    const $button = $('[fg-nav="button"]');

    $links.on('click', function () {
        if ($(window).width() <= 991) {
            $button.trigger('click');
        }
    });
});


// ========================================================
// 04 — Copy HEX code
// ========================================================
$(document).on("click", "[fg-element='color']", function() {
    let hexValue = $(this).find("[fg-element='hex']").first().text().trim();
    if (hexValue) navigator.clipboard.writeText(hexValue);
});

// ========================================================
// 05 — Typewriter (Font Section)
// ========================================================
$(document).ready(function () {
  function isLargeDevice() {
    return window.matchMedia("(min-width: 991px)").matches;
  }

  function typeWriter($element, text, callback) {
    if (!isLargeDevice()) {
      $element.text(text);
      if (callback) callback();
      return;
    }

    // Find direct parent with fg-element="font-preview-name-wrapper"
    const $wrapper = $element.closest('[fg-element="font-preview-name-wrapper"]');
    const $target = $wrapper.length ? $element : $element;

    const words = text.split(" ");
    const html = words
      .map((word) => {
        return `<span style="white-space: nowrap; display: inline-block;">
                        ${word
                          .split("")
                          .map(
                            (char) =>
                              `<span style="opacity: 0; display: inline-block;">${char}</span>`
                          )
                          .join("")}
                    </span><span style="opacity: 0; display: inline-block;">&nbsp;</span>`;
      })
      .join("");

    // Replace content at the correct level
    $target.html(html);

    const $chars = $target.find('span span');
    let i = 0;

    function revealNext() {
      if (i < $chars.length) {
        $chars.eq(i).css({
          opacity: "1",
          transition: "opacity 50ms",
        });
        i++;
        setTimeout(revealNext, 50);
      } else if (callback) {
        callback();
      }
    }

    revealNext();
  }

  // Cache the original content and structure
  const $components = $('[fg-element="font-preview-component"]');
  $components.each(function () {
    const $this = $(this);
    const $text = $this.find('[fg-element="font-preview-text"]');
    const $name = $this.find('[fg-element="font-preview-name"]');
    const $nameWrapper = $this.find('[fg-element="font-preview-name-wrapper"]');

    // Store original HTML structure
    $text.data("original-html", $text.html());
    $name.data("original-html", $name.html());
    $nameWrapper.data("original-html", $nameWrapper.html());
  });

  if (isLargeDevice()) {
    $components.on("mouseenter", function () {
      const $nameWrapper = $(this).find('[fg-element="font-preview-name-wrapper"]');
      const $text = $(this).find('[fg-element="font-preview-text"]');

      $nameWrapper.hide();
      $text.show();
      typeWriter($text, $text.data("original-html"));
    });

    $components.on("mouseleave", function () {
      const $this = $(this);
      const $sampleWrapper = $this.find('[fg-element="font-preview-sample-wrapper"]');
      const $nameWrapper = $this.find('[fg-element="font-preview-name-wrapper"]');
      const $name = $this.find('[fg-element="font-preview-name"]');
      const $text = $this.find('[fg-element="font-preview-text"]');

      // Reset text to original state
      $text.html($text.data("original-html"));

      // Reset name wrapper to original state before starting new animation
      $nameWrapper.html($nameWrapper.data("original-html"));

      $sampleWrapper.show();
      $nameWrapper.show();

      // Find the name element again after resetting HTML
      const $newName = $nameWrapper.find('[fg-element="font-preview-name"]');
      typeWriter($newName, $newName.text());
    });
  }

  let resizeTimer;
  $(window).on("resize", function () {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(function () {
      if (!isLargeDevice()) {
        $components.off("mouseenter mouseleave");

        // Reset all elements to their original structure
        $components.each(function () {
          const $this = $(this);
          const $text = $this.find('[fg-element="font-preview-text"]');
          const $nameWrapper = $this.find('[fg-element="font-preview-name-wrapper"]');

          $text.html($text.data("original-html"));
          $nameWrapper.html($nameWrapper.data("original-html"));
        });

        $('[fg-element="font-preview-name-wrapper"]').show();
      }
    }, 250);
  });
});

// ========================================================
// 06 — Pagination
// ========================================================
$(document).ready(function () {
  if ($("[fg-pagination-generate='true']").length === 0) {
    return; // Exit if the attribute is not found
  }

  var links = $("[fg-nav^='link']").filter(function () {
    return $(this).attr("href") && $(this).attr("href") !== "#";
  });
  
  var currentLink = links.filter(".w--current");
  var index = links.index(currentLink);
  var totalLinks = links.length;

  var prevLink = index > 0 ? $(links[index - 1]).attr("href") : null;
  var prevText = index > 0 ? $(links[index - 1]).find("[fg-nav^='text']").text() : null;
  var prevShortcut = index > 0 ? $(links[index - 1]).find("[fg-nav^='shortcut']").text() : null;

  var nextLink = index < totalLinks - 1 ? $(links[index + 1]).attr("href") : null;
  var nextText = index < totalLinks - 1 ? $(links[index + 1]).find("[fg-nav^='text']").text() : null;
  var nextShortcut = index < totalLinks - 1 ? $(links[index + 1]).find("[fg-nav^='shortcut']").text() : null;

  // Set previous page attributes
  $('[fg-pagination="prev-link"]').attr("href", prevLink);
  $('[fg-pagination="prev-shortcut"]').text(prevShortcut);
  $('[fg-pagination="prev-text"]').text(prevText);

  // Set next page attributes
  $('[fg-pagination="next-link"]').attr("href", nextLink);
  $('[fg-pagination="next-shortcut"]').text(nextShortcut);
  $('[fg-pagination="next-text"]').text(nextText);

  // Hide elements if no prev or next page exists
  if (!prevLink) {
    $('[fg-pagination="prev-link"], [fg-pagination="prev-shortcut"], [fg-pagination="prev-text"]').hide();
  }
  if (!nextLink) {
    $('[fg-pagination="next-link"], [fg-pagination="next-shortcut"], [fg-pagination="next-text"]').hide();
  }
});
</script>

The custom code activates the following features:

Add caption here
Auto-generate menu
Add caption here
Keyboard shortcuts for menu
Add caption here
Auto-generate pagination
Add caption here
Typewriter animation
Add caption here
Copy HEX color code
Add caption here
Mobile menu closes by tapping an anchor link