# Sygnal Attributes v5

Webflow Tools that Make Your Site Better

{% hint style="success" %}
**SA5 is the lastest version of Sygnal's Webflow Utilities Library.**\
The tools here are 100% free, 100% open-source, and 100% designed for Webflow. &#x20;
{% endhint %}

## Libraries

SA5 is divided into a series of individual libraries.&#x20;

### Our Most Popular Libs

| Library                                       | Purpose                                                                                                                | Features                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [SA5 HTML](/sa5-html/about)                   | A huge range of page-modification capabilities                                                                         | <ul><li><a href="/pages/8h8IaddHWgDuUNQy9mDY">Dynamic attributes</a></li><li><a href="/pages/3D36E83XoXedqdUOJc2k">Truncate text w/ ellipses</a></li><li><a href="/pages/s4dePQlzf8HNAipPjMo3">Advanced sorting</a></li><li><a href="/pages/1j1joskPNxrmUQuThFpS">Advanced filtering</a></li><li><a href="/pages/s6FxpB4UmXWaVBl0A52x">Encoded Emails</a></li><li><a href="/pages/Ccjd0AMs9HAhxCUxeLaN">Hide Sections w/ Empty Collection Lists</a></li><li>and much more...</li></ul> |
| [SA5 User Accounts](/sa5-user-accounts/about) | <p>Get logged in user info to personalize your site and improve user experience.<br>Custom-route on login. </p>        | <ul><li>User name</li><li>Email</li><li>Custom fields</li><li>Access groups</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                  |
| [SA5 Rich Text](/sa5-richtext/about)          | Add capabilities to Webflow's Rich Text Block                                                                          | <ul><li>Responsive inline images</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| [SA5 Layout](/sa5-layout/about)               | Advanced, dynamic layouts for your Webflow pages                                                                       | <ul><li>Restructure your collection lists entirely into groups</li><li>Create dynamic tabs from the CMS</li><li>Overcome Webflow's 5-nested-item limit </li></ul>                                                                                                                                                                                                                                                                                                                      |
| [SA5 Forms](/sa5-forms/about)                 | Easily replace Webflow's native form handler with your choice of 3rd party handlers like Basin, Zapier, Make, and n8n. | <ul><li>Eliminate SPAM</li><li>Trigger automations  </li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                         |
| [SA5 Modals](/sa5-modals/about)               | Create powerful, consistent modals with full design control and zero interactions                                      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| [SA5 Hotkeys](/sa5-hotkeys/about)             | Define custom hotkeys to trigger your scripts, page actions and navigation                                             |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| [SA5 URL](/sa5-url/about)                     | Special URL enhancement tools                                                                                          | <ul><li>Pass-through your query params from one page to another for referer tracking</li><li>Target all external links to a new tab</li></ul>                                                                                                                                                                                                                                                                                                                                          |
| [SA5 Video](/sa5-video/about)                 | Add capabilities to your video elements                                                                                | <ul><li>Youtube Hide related videos</li><li>Background Video poster image</li><li>Video player</li></ul>                                                                                                                                                                                                                                                                                                                                                                               |
| [SA5 Elements](/sa5-elements/about)           | Control Webflow's elements with code, and receive events when they change                                              | <ul><li>Tabs</li><li>Sliders</li><li>Buttons</li><li>Dropdown menus</li><li>Lightboxes</li><li>Radio buttons</li><li>Accordions</li><li>Lotties</li><li>Locale Switcher</li></ul>                                                                                                                                                                                                                                                                                                      |
| [SA5 Format](/sa5-format/about)               | Format numbers and currencies & dates specially                                                                        | <ul><li>Locale-specific formatting</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| [SA5 Track](/sa5-track/about)                 | Special tracking using cookies and webStorage                                                                          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| [SA5 Select Custom](/sa5-selectcustom/about)  | Custom Select element based on Finsweet's attribute of the same name.  Adds some dynamic update capabilities           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |

### Specialized Use / Advanced Libs;

| Library                                         | Purpose                                                                                                       | Features                                                                                                                                                                                                                           |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [SA5 Core](/sa5-core/overview)                  | Central to SA5, but has a few tricks up its sleeve                                                            | <ul><li>Hide objects in the designer and make them visible in the published site</li></ul>                                                                                                                                         |
| [SA5 Cache](/sa5-cache/about)                   | Handle high-latency tasks such as content-fetching with a built-in caching layer.                             | <ul><li>Allows complex calculations and slow data retrievals to easily cache.</li><li>Code your update directly to the cache layer and it will "lazy load" the content when needed.</li></ul>                                      |
| [SA5 Embeds](/sa5-embeds/about)                 | Embed external content into your blog posts and pages.                                                        | <ul><li>Tables from Google Docs</li></ul>                                                                                                                                                                                          |
| [SA5 Social Share](/sa5-socialshare/about)      | Adds Email as a social share option.  Designed to work in conjunction with Finsweet's Social Share component. | <ul><li>Adds share-to-email</li></ul>                                                                                                                                                                                              |
| [SA5 Analytics](/sa5-analytics/about)           | Adds some analytics capabilities to your site using attributes.                                               | <ul><li>GTM dataLayer events</li><li>UTM Tracking</li><li>Rel Attributes</li><li>A/B testing</li></ul>                                                                                                                             |
| [SA5 Data](/sa5-data/about)                     | Extract CMS data into JS objects from your collection lists, to power your custom code and calculations.      | <ul><li>Extract data from collection lists into datasources</li><li>Extract querystring data, and URL data</li><li>Cookies and webStorage</li><li>Data-bind these pieces of information to text elements and form inputs</li></ul> |
| [SA5 404](/sa5-404/article)                     | Improve your 404 pages                                                                                        | <ul><li>Smart search trigger Webflow site search based on the Path the user was looking for </li></ul>                                                                                                                             |
| [SA5 Countup](/sa5-countup/countup)             | Animated count up to a set value. Triggers on scroll-into-view                                                |                                                                                                                                                                                                                                    |
| [SA5 UI](/sa5-ui/about)                         | Special UI components                                                                                         | <ul><li>5-star ratings component ( display only ) </li></ul>                                                                                                                                                                       |
| [SA5 Demo](/sa5-demo/webflow-preview-link)      | Add special capabilities to your Webflow demo sites, such as links to the correct readonly link page.         |                                                                                                                                                                                                                                    |
| [SA5 Commerce](/sa5-commerce/payment-providers) | Simple commerce solutions for one-off purchases.                                                              | <ul><li>Paypal</li><li>Windcave</li></ul>                                                                                                                                                                                          |
| [SA5 Trigger](/sa5-trigger/about)               | Custom trigger items and interactions from text links and buttons.                                            | <ul><li>Trigger interactions, such as pop-ups and modals from a link</li><li>Support CMS-driven scenarios </li></ul>                                                                                                               |

### Experimental Libraries & Features 🧪

*These are libraries we're experimenting with.*&#x20;

Now you can [sponsor the development](/overview/sponsor) of those features, and they will be added to the library for everyone to use.&#x20;

| Library                                        | Purpose                                                                                                                         | Features                                                    |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| [SA5 Calc](/sa5-calc/article) 🧪               | Calculate & sum items                                                                                                           |                                                             |
| [SA5 SEO](/sa5-seo/about) 🧪                   | Some SEO tools                                                                                                                  | <ul><li>Noindex</li><li>Nofollow</li><li>JSON-LD </li></ul> |
| [SA5 Fixup](/sa5-fixup/viewport-meta) 🧪       | Fix a few things in Webflow editor and published sites.                                                                         |                                                             |
| [SA5 Logic](/sa5-logic/about) 🧪               | Add logic capabilities using attributes, for conditional visibility and page structuring                                        | <ul><li>If</li><li>Switch</li></ul>                         |
| [SA5 Effects](/sa5-effects/about) 🧪           | Experimental effects                                                                                                            | <ul><li>Depthmap ( fake 3d ) </li></ul>                     |
| [SA5 State](/sa5-state/about) 🧪               | State management                                                                                                                |                                                             |
| [SA5 Booking](/sa5-booking/about) 🧪           | Tag trigger elements easily to invoke 3rd party booking systems with the correct service, location, category, and staff member. | <ul><li>GetTimely</li><li>SimplyBook</li></ul>              |
| [SA5 Localization](/sa5-localization/about) 🧪 | Various localization features                                                                                                   |                                                             |
| [SA5 Detect](/sa5-detect/about) 🧪             | Detect and manipulate your page depending on how                                                                                |                                                             |
| [SA5 Table](/sa5-table/html-tables)            | Add HTML tables                                                                                                                 |                                                             |

{% hint style="info" %}
We've also begun including our dev team and roadmap notes in these docs so that they are available for community comment and discussion. You can expand most features for a Future notes document.&#x20;
{% endhint %}

## Feature Requests

* Share your ideas in SA5's forum.&#x20;
* If you'd like a specific feature built that you already see on our design board here, you can sponsor a feature.  Features marked with&#x20;

## 2023 Review of SA5's Capabilities

{% embed url="<https://www.loom.com/share/f31effc1fa604bc7a701c25ebcda513f>" %}

## Navigating the Docs

* Documentation is organized by library&#x20;
* At the top of most libraries, you'll two important pages-
  * 🔍 **About this Library**, which gives you an overview of the capabilities
  * 🚀 **Quick Start**, which gives you the *library code* you need&#x20;
* Within each library the features are grouped separately
* A *lot of features* have **subpages**- make sure to click the **>** to expand those sections\
  ![](/files/Cw8RD0TSj8cyaFwnPu44)
* 🧪 indicates EXPERIMENTAL items, which are **not yet available**&#x20;
* 📝 indicates NOTES, which are primarily for the dev team&#x20;
* ▶️ indicates VIDEO TUTORIALS, which we're just beginning to add

## **What’s new in v5?**

**The tech changes we've made in v5 open the doors to a lot of new capabilities.**

{% hint style="success" %}
If you are using v4, none of these changes will affect your current websites. \
Since all of our CDN URLs are version-locked, you’ll continue to use the same libraries you are using now until you upgrade to the newer versions, *someday*, if you want to.
{% endhint %}

### The v5 Tech Stack

* We’ve changing from a JavaScript ES6 codebase to TypeScript
* We’ve changed fully from CSS to SASS.
* We’ve eliminating all use of jQuery in the libraries.
* We’ve separated classes through the library into discrete source files
* We’ve bundled the distributed files differently for even greater efficiency
* We've integrated debugging features&#x20;

We’re excited about the tremendous capabilities the new stack gives us.

### **Switching to v5**

If you *choose to* switch to the upgraded v5 libraries at some point, you’ll see a few minor integration changes;

**Javascript** \<script> elements;

* Will be moved from the **before-/body** section to the **before-/head** section of your pages and site-wide code settings.
* The library URLs will change to point at the `/dist/` path, rather than the `/src/` path.
* You'll also notice that the `type=module` is dropped

**CSS** `<link>` elements will essentially remain unchanged, and will continue to point to `/dist/css/`

{% hint style="info" %}
All of this is covered in the docs for each feature, and we’ll update the docs as each library is migrated, so that you can upgrade them if you choose to.
{% endhint %}

#### What about attribute or code changes?

You can simply reference the new libraries *with no changes* to your *custom attributes* or existing features- and you’ll still get the enhanced features and performance benefits.

#### Will I need to upgrade to v5 eventually?

**Nope!** If you’re happy with things as they are and don’t need any of the new features, you don’t need to change a thing. v4 will continue to run indefinitely.&#x20;


# How to Add Custom Code

Webflow's 5 Custom Code Areas

If you're unfamiliar with Webflow, it has 5 distinct areas where you can place custom code-

* The [site-wide HEAD area](https://university.webflow.com/lesson/custom-code-in-the-head-and-body-tags#head-code). Under site settings on the Custom Code tab, this section is currently called **Head Code**. &#x20;
* The [site-wide BODY area](https://university.webflow.com/lesson/custom-code-in-the-head-and-body-tags#footer-code). Under site settings on the Custom Code tab, this section is currently called **Footer Code**.&#x20;
* A [specific page's HEAD area](https://university.webflow.com/lesson/custom-code-in-the-head-and-body-tags#in-the-<head>-tag). Under page settings towards the bottom, this section is currently called **Inside \<head> tag.**
* A [specific page's BODY area](https://university.webflow.com/lesson/custom-code-in-the-head-and-body-tags#before-the-<-body>-tag). Under page settings towards the bottom, this section is currently called **Before \</body> tag**.
* An [HTML Embed element](https://university.webflow.com/lesson/custom-code-embed?topics=elements) on your page, Anywhere on your page, added from the elements panel.&#x20;

Here's a walkthrough of how to access and use the 5 areas.&#x20;

{% embed url="<https://www.loom.com/share/21e67856145e4132b9913bb44b1c9e5a>" %}


# What is Sygnal Attributes?

Webflow Utils ( WFU ), reloaded for 2023

{% hint style="success" %}
In Jan-2023, **Sygnal's Webflow Utils ( WFU )** library was renamed as \
**Sygnal Attributes** to align with our focus in easy integration into your Webflow projects.

It is still the same great library, still 100% open source, and 100% free to use. &#x20;
{% endhint %}

{% hint style="info" %}
In documentation we'll often abbreviate **Sygnal Attributes** as **SA**, however the attributes themselves will continue to use the legacy `wfu-` prefix for convenience.
{% endhint %}

**Sygnal Attributes** is a collection of JavaScript libraries that add functionality to [Webflow](https://webflow.com/) sites. It focuses on overcoming certain limitations of Webflow, and adding certain capabilities.

Some of the most popular features include;

* Data-binding form INPUT and SELECT elements to your collection lists
* Adding custom attributes dynamically to elements within a collection list.
* Adding tables support, with data sourced from Google Sheets
* Multilingual support through language-detection and a dynamic content-switcher

All of these features work with Webflow’s native hosting, and are focused on creating as much capability within Webflow as possible - with no 3rd party plug-ins or monthly service fees.

This project is also a practical exploration into the use of Javascript ES6 modules, NPM, and other state-of-the art web dev technologies, and how they can be used to enhance Webflow sites.

*This library will be expanded as needed, and broken into modules for convenient selection of the parts you need.*

## Getting Started <a href="#getting-started" id="getting-started"></a>

* The Github Repo is [sygnal/webflow-util](https://github.com/sygnaltech/webflow-util)


# Sponsor a Feature ✨

Need a capability that SA5 doesn't have yet?  You can sponsor it.

**SA5 has been under development for 6 years, and its development is largely directed by the needs of Sygnal's clients and the projects we're working on.**&#x20;

Occasionally, library users request special features or enhancements that aren't currently on our roadmap, or that we're planning, or that are under development but which we haven't needed to implement yet.&#x20;

{% hint style="success" %}
**UNDER DEVELOPMENT** \
In these docs, feature pages marked with a green test-tube 🧪 in the title are experimental and under-development.  These are likely already built in our own code bases, but not redesigned as attributes for the general public,, tested, or fully documented.&#x20;

*These are the closest features to completion.*&#x20;
{% endhint %}

Now you can sponsor the development of those features, and they will be added to the library for everyone to use. &#x20;

## How much?

Cost depends on the feature, complexity, and our availability, but for many feature additions it would be quite low, in the $100 to $300 range.

You get;

* The feature you need
* The awesome feeling of supporting other Webflow devs around the world&#x20;
* Our thanks, and a credit to you listed on that feature page&#x20;

**If you're interested, drop a message in SA5's Forum ( link at top ), and we can discuss.**&#x20;

{% hint style="info" %}
Please note that sponsored features must make sense for the community as a whole and must be compatible with SA5's engineering roadmap.  If yours is not, it can still be developed for you privately as a site-specific feature just for your project.&#x20;

See our [Webflow development & micro-consulting](https://www.sygnal.com/webflow) options for details.&#x20;
{% endhint %}


# What's New?

### v3.40 <a href="#v340" id="v340"></a>

Under development.

### v3.39 <a href="#v339" id="v339"></a>

* webflow-html
  * Enhanced sorting so that nested lists can be sorted as well
  * Added sequence numbering

### v3.38 <a href="#v338" id="v338"></a>

* webflow-url
  * Bugfix on url morphing in certain page constructions

### v3.371 <a href="#v3371" id="v3371"></a>

* webflow-membership-routing
  * Added internal tracing
  * Revisions to . path handling

### v3.37 <a href="#v337" id="v337"></a>

* webflow-membership-routing
  * Refactored to utilize the login form’s internal redirect
  * Expanded to support log-in forms on other pages

### v3.36 <a href="#v336" id="v336"></a>

* webflow-membership-routing
  * Added advanced log-in & sign-up routing
* webflow-membership
  * Refactored expanded login button area feature

### v3.35 <a href="#v335" id="v335"></a>

* webflow-html
  * Added dynamic limit items by breakpoint
  * Added dynamic random sort, on refresh

### v3.34 <a href="#v334" id="v334"></a>

* Membership
  * Added expand login button

### v3.33 <a href="#v333" id="v333"></a>

* Updates for lightbox captions
  * Suppressed captions on video elements, since they’re ‘Undefined’

### v3.32 <a href="#v332" id="v332"></a>

* Added Webflow Elements
  * Added CMS Lightbox + Captions

### v3.31 <a href="#v331" id="v331"></a>

* Added Webflow Membership module, with conditional element display.

### v3.30 <a href="#v330" id="v330"></a>

* Tracking, added cookies and localStorage options.

### v3.29 <a href="#v329" id="v329"></a>

* Additional numeric formats added to `webflow-format`.

### v3.28 <a href="#v328" id="v328"></a>

* Added user tracking w/ localStorage as `webflow-track`.

### v3.27 <a href="#v327" id="v327"></a>

* Added Decode HTML to `webflow-html`.

### v3.26 <a href="#v326" id="v326"></a>

* Added Hide Section w/ Empty Collection Lists

### v3.25 <a href="#v325" id="v325"></a>

* Added semver sorting.

### v3.24 <a href="#v324" id="v324"></a>

* Upgraded Dynamic attributes.

### v3.23 <a href="#v323" id="v323"></a>

**Lib: `webflow`**

* Added Editor-mode detector.

**Lib: `webflow-html`**

* Added Webflow Editor-mode behavior for nested lists.

### v3.22 <a href="#v322" id="v322"></a>

**Lib: `webflow-html`**

* Sorting expanded to include numeric keys

### v3.21 <a href="#v321" id="v321"></a>

**Lib: `webflow-format`**

* Number formatting for numbers and currency

### v3.20 <a href="#v320" id="v320"></a>

**Lib: `webflow-html`**

* List sorting

### v3.19 <a href="#v319" id="v319"></a>

**Lib: `webflow-commerce`**

* Simple commerce

### v3.18 <a href="#v318" id="v318"></a>

**Lib: `webflow-forms`**

* Added IP Info capture-and-append.

### v3.17 <a href="#v317" id="v317"></a>

**Lib: `webflow-forms`**

* Added ‘n8n’ handler.
* Change handlers so that the form action is retrieve at the point of submit. This way it can be changed live (e.g. for debugging purposes).

### v3.16 <a href="#v316" id="v316"></a>

**Lib: `webflow-forms`**

* Changed `success` handler to `other`.
* Bugfix on form data serilaization.

### v3.15 <a href="#v315" id="v315"></a>

**Lib: `webflow-forms`**

* Redesigned to a webhook-handler approach, which is specified by a `wfu-form-handler` attribute.
* The zapier handler is named `zapier`
* A new `success` handler is added for webhooks that have other JSON response formats.

### v3.14 <a href="#v314" id="v314"></a>

**Lib: `webflow-url`**

* [Automatic targeting of external links](https://wfu.sygnal.com/docs/webflow-url/link-targeting/)
* [CMS relative link fixups](https://wfu.sygnal.com/docs/webflow-url/cms-fixups/)

### v3.13 <a href="#v313" id="v313"></a>

**Dynamic Attributes**

**Breaking Changes**

Attribute name changed from `apply-attr` to `wfu-apply-attr`, e.g.

```
<data type="wfu-apply-attr" apply="prev">
    <data attr="style" value="background-color: yellow;"></data>
</data>
```

**Nested Lists**

**Breaking Changes**

Skeleton loader temporarily disabled, as we work through some issues with Webflow’s editor.

Custom attribute tag is now recommended as `wfu-lists=nested` rather than `wfu-lists=all`.

Both are currently identical, but we will expand on this in the future.


# Library Modules

WFU’s capabilities are separated into functional modules;

* `webflow-data` - Creates data-sources from Collection Lists and remote JSON and CSV sources.
* `webflow-forms` - Enabled binding data-sources to Form elements including **select** and **text** elements.
* `webflow-html` - Lower-level HTML functionality, including-
  * The ability to bind Collection List data to a Webflow element’s custom attributes.
  * IFRAME utilities, such as auto-sizing
* `webflow-id` - Creates unique ID’s, including UUIDs and MUIDs, for programmatic use.
* `webflow-content` - Generates content, such as lorem-ipsum text, primarily for layout testing.
* `webflow-multilingual` - Enabled language detection and easy content-switching for multilingual sites.
* `webflow-table` - Generate HTML tables from a JSON datasource.

### Under Development <a href="#under-development" id="under-development"></a>

* `webflow-modal` - Supporting modal-pop-ups without the need for interactions. Full Webflow design capabilities. CMS-driven scheduling options.
* `webflow-svg` - Experimental support for SVG manipulations.
* `webflow-ui` - UI Components, such as 5-star ratings.

<br>


# Technical Notes & Goals

## Design Philosophy

#### Separation of Functionality <a href="#separation-of-functionality" id="separation-of-functionality"></a>

As the library grows, I found the need to partition the code better for accessibility & reusability.

* Webflow-specific code is being distinguished from more general functionality.
* Libraries specific to Webflow implementation are prefixed with `webflow-`.
* Other libraries will be named, grouped and prefixed according to the functional intent and the platforms they pertain to.
* This means that much of the functionality here can be used in non-Webflow sites, or in sites that you are hosting separately. Around that capability is a Webflow-specific wrapper that applies those features in Webflow’s unique HTML-generation and hosting-environment context.

#### 3 Distinct Integration Approaches <a href="#id-3-distinct-integration-approaches" id="id-3-distinct-integration-approaches"></a>

I want to embrace the reality that many Webflow designers are unfamiliar with programming, while others need maximum functionality.

To support a wide range of users, I’m conceptually dividing integration approaches into 3 zones;

* **Code.** *Programmers* will generally use the libraries under `src/modules` directly. These functions and libraries can be arranged to accomplish complex tasks with unique configurations, and using these modules directly gives you full access to their capabilities.
* **Lo-Code.** *Designers* with basic scripting knowledge are provided with *locode* libraries that encapsulate the most commonly-used functionality into single function calls. These libraries exist under `/src/locode` and have a `-helper` suffix. These will provide the core functionality of the full libraries, with minimal setup.
* **No-Code.** Where possible, I intend to offer codeless possibilities where the functionality is automatically added by simply adding the library. Paste in the library refernce, tag your elements for the functionality you want, and everything else happens automatically. This entirely avoids script-writing, while giving you much of the functionality designers want.

## **Technical Notes**

**Webflow Utilities (WFU)** is an experimental project as much as a practical toolset. Where possible, I’m employing the latest javascript technologies as a practical implementation exercise, and to test their capabilities.

### Webflow-centric Design <a href="#webflow-centric-design" id="webflow-centric-design"></a>

All of the tools in this library are designed specifically to work with websites that are built and hosted on Webflow.

This means;

* I prioritize HTML5 generation, with little attention to backwards compatability.
* I use jQuery, which is present in all Webflow hosted sites.
* Script files are be hosted externally to the site. In-site scripting is limited to “glue” and configuration scripts.
* I’m prioritizing solutions which overcome limitations & problems in Webflow, even if they wouldn’t be a common issue on other platforms.

### Redesigned Using ES6 Modules <a href="#redesigned-using-es6-modules" id="redesigned-using-es6-modules"></a>

As of 3.0, WFU uses [JavaScript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) as a way to better define and separate code. These are widely supported across modern browsers, however are not supported by Internet Explorer.

Without the ability to use NPM, javascript modules appear to be the most effective way to separate and re-use use code between the libraries.

*Note, I’m using the `.js` extension on our modules rather than `.ejs`, as this currently appears to be the recommended practice. However, these are ES6 modules, so you will need to specify that in your script references.*

If you’re unfamiliar with ES6 modules, accessing them looks like this;

Let’s suppose you want to call `myFunc()` from your Webflow page. In the “Before /BODY” code area, you might include this;

```
<script type="module">
    import { myFunc } from 'some-wfu-library.js';
    $(function () {
        myFunc();
    }
</script>
```

Notes;

* The `type="module"` on the `<script>` element is important, to allow ES6 use in your script.
* Modules are included through `import`, where you specify the function(s) you want to access, and the path to the `.js` library.
* You can use other javascript and jQuery 100% as normal.

I’ll give specific examples of script you can use with each library.

### Object-Oriented patterns <a href="#object-oriented-patterns" id="object-oriented-patterns"></a>

For complex utility modules, our library is designed using OO patterns as much as possible.

## Future Plans

### Node Package Manager (NPM) <a href="#node-package-manager-npm" id="node-package-manager-npm"></a>

Ideally, I’d like to evolve this toolset as a proper NPM package, as it would allow for much richer scripting options, a plug-in style architecture, version and dependency management.

At the moment this is on the backburner, as I’m unfamiliar with using NPM to generate browser-compatible ES6 bundles.

*Contributors welcome.*

<br>


# CSS Media Queries

All the things we can do with them.

Media selectors or media queries in CSS are used to apply different styles for different devices or device characteristics.&#x20;

Webflow makes heavy use of them in its responsive breakpoint architecture, but it only uses the **viewport width**. Other device characteristics are not supported as "contexts" that you can design for.

One of our objectives with SA is to expand on these capabilities and support certain capabilities in orientation, aspect ratio, and so on.&#x20;

But there are many!&#x20;

1. **Width and Height of the viewport**: This is probably the most common use of media queries. You can use `min-width`, `max-width`, `min-height`, and `max-height` to apply different styles based on the size of the browser window.
2. **Width and Height of the device**: Similar to the viewport, but for the physical dimensions of the device screen, you can use `min-device-width`, `max-device-width`, `min-device-height`, and `max-device-height`.
3. **Orientation**: You can check if the device is in landscape or portrait mode.
4. **Resolution**: You can apply different styles for devices with different screen resolutions using `min-resolution` or `max-resolution`.
5. **Aspect Ratio**: You can use `aspect-ratio`, `min-aspect-ratio`, and `max-aspect-ratio` to apply styles based on the width-to-height ratio of the viewport, and `device-aspect-ratio`, `min-device-aspect-ratio`, and `max-device-aspect-ratio` for the device screen.
6. **Color**: You can query the number of bits per color component of the output device or the color index using `color`, `min-color`, and `max-color`.
7. **Monochrome**: You can query the number of bits per pixel in a monochrome frame buffer using `monochrome`, `min-monochrome`, and `max-monochrome`.
8. **Scan Process**: You can differentiate between interlaced and progressive scan televisions using the `scan` media feature.
9. **Grid**: You can query whether the output device is grid or bitmap.
10. **Light Level**: You can apply different styles depending on the ambient light level of the device environment (like `dim`, `normal`, `washed`).
11. **Pointer**: You can apply different styles based on the presence and accuracy of a pointing device like a mouse (`none`, `coarse`, `fine`).

<br>


# Callback Handlers

Callback Handlers used to configure and extend SA5 module functionality.

SA5 uses callbacks for two purposes;

1. Specifying configuration options, for modules which have them&#x20;
2. Notifying custom code handlers of state changes that the site owner may want to specially handle&#x20;

An example callback;&#x20;

```
<script>
window.sa5 = window.sa5 || [];
window.sa5.push(['getMembershipRoutingConfig', 
  (config) => {
  
    // ... your custom code here ... 

    return config;
  }]); 
</script>
```

## Defined Callbacks

<table><thead><tr><th width="284.3333333333333">Callback Name</th><th width="153">Module</th><th>Use</th></tr></thead><tbody><tr><td>getMembershipRoutingConfig</td><td>Memberships</td><td><a href="/pages/qJhFuSJm4MeWvoG2XtOH">Configuration</a></td></tr><tr><td>userInfoChanged</td><td>Memberships</td><td><a href="/pages/i1aP9XW45cKdNk07CpwV">Event Notification</a> when new user information is loaded</td></tr><tr><td>breakpointChanged</td><td>HTML</td><td><a href="/pages/AM2IVshU3PVQTTA5Phkr">Event Notification</a> on a breakpoint change</td></tr></tbody></table>


# Custom Attributes

SA5's Custom Attributes

SA5 uses custom attributes;

1. To target specific elements which you want the library to perform specific actions on
2. To identify your intended relationship between things&#x20;
3. To configure the settings and behaviors you want &#x20;

## Defined Attributes

{% hint style="info" %}
Not yet catalogued
{% endhint %}

<table><thead><tr><th width="180.33333333333331">Attribute Name</th><th width="106">Module</th><th>Element Type</th><th>Use</th></tr></thead><tbody><tr><td><code>wfu-map-dict</code></td><td>Data</td><td></td><td></td></tr><tr><td><code>wfu-bind</code></td><td>Forms</td><td>INPUT element</td><td>( your data source name )</td></tr><tr><td><code>wfu-bind</code></td><td>Forms</td><td>SELECT element</td><td>( your data source name )</td></tr><tr><td><code>wfu-form-handler</code></td><td>Forms</td><td>FORM block element</td><td></td></tr><tr><td><code>wfu-form-ipinfo</code></td><td>Forms</td><td>FORM block element</td><td></td></tr><tr><td></td><td>HTML</td><td></td><td></td></tr><tr><td></td><td>Table</td><td></td><td></td></tr><tr><td></td><td>UI</td><td></td><td></td></tr><tr><td></td><td>Hotkeys</td><td></td><td></td></tr><tr><td></td><td>Blog</td><td></td><td></td></tr><tr><td></td><td>Demo</td><td></td><td></td></tr><tr><td></td><td>Url</td><td></td><td></td></tr><tr><td></td><td>Video</td><td></td><td></td></tr><tr><td></td><td>Commerce</td><td></td><td></td></tr><tr><td></td><td>Membership</td><td></td><td></td></tr><tr><td></td><td>Format</td><td></td><td></td></tr><tr><td></td><td>Elements</td><td></td><td></td></tr><tr><td></td><td>Track</td><td></td><td></td></tr><tr><td></td><td>Countup</td><td></td><td></td></tr><tr><td></td><td>Cache</td><td></td><td></td></tr><tr><td></td><td>SEO</td><td></td><td></td></tr><tr><td></td><td>Fixup</td><td></td><td></td></tr></tbody></table>


# Documentation

## Future Documentation Plans

GitBook has proven excellent in some ways but has frustrating limitations that make it unsuitable as a long-term solution for SA5's docs.  &#x20;

<table><thead><tr><th>Feature</th><th width="128.33333333333331">GitBook</th><th>Other</th></tr></thead><tbody><tr><td>Rapid updates via a web-based docs editor</td><td></td><td></td></tr><tr><td>Redirects tracking as docs are re-organized</td><td></td><td></td></tr><tr><td>Custom domain</td><td></td><td></td></tr><tr><td>Attributes copy structure</td><td></td><td></td></tr><tr><td>Cost</td><td>FREE</td><td></td></tr><tr><td>Template options &#x26; styling control</td><td>Poor to none</td><td></td></tr></tbody></table>

## Alternatives we're Exploring

1. **Docusaurus**:
   * **Pros**: Open-source, supports versioned documentation, integrates well with React, and offers a rich set of plugins.
   * **Cons**: Requires some familiarity with React for advanced customizations.
2. **MkDocs**:
   * **Pros**: Open-source, written in Python, and has a wide range of plugins. It's lightweight and easy to set up.
   * **Cons**: Might require familiarity with Python for some customizations.
3. **Sphinx**:
   * **Pros**: Highly extensible, open-source, and written in Python. It's especially popular for technical and software documentation.
   * **Cons**: Has a steeper learning curve compared to some other options.
4. **VuePress**:
   * **Pros**: Built on Vue.js, it offers a great balance between writing content and using Vue components. It's also optimized for performance.
   * **Cons**: Requires knowledge of Vue.js for advanced customizations.
5. **Jekyll**:
   * **Pros**: Widely used static site generator, integrates seamlessly with GitHub Pages, and has a large community.
   * **Cons**: Written in Ruby, which might not be familiar to everyone.
6. **Hugo**:
   * **Pros**: Extremely fast static site generator written in Go. It has a rich ecosystem and can handle large sites with ease.
   * **Cons**: Templating can be complex for beginners.
7. **Read the Docs**:
   * **Pros**: Hosted solution that integrates well with GitHub, Bitbucket, and GitLab. It's especially popular for open-source projects.
   * **Cons**: More suited for technical documentation.


# Configuration Blocks

Configuration Blocks have many purposes;

* Attribute library configuration
* Callback handlers&#x20;
* Special functionality, like Trigger or Action definitions &#x20;

## Usage Notes&#x20;

All SA configuration blocks are HTML `<script>`elements with a specific structure.

Here's an example;&#x20;

```html
<script type="application/sa+json" handler="handler-name" name="my-name">
{
  "@context": "https://attr.sygnal.com",
  "@version": "0.1",
  ...
}
</script>
```

{% hint style="success" %}
In the SA5 documentation, configuration blocks will be presented for you to easily copy, paste, and edit in your Webflow project.&#x20;
{% endhint %}

{% hint style="info" %}
Explore the full details of Sygnal's [configuration block specification](/overview/configuration-blocks/configuration-block-specification).&#x20;
{% endhint %}

### Configuration Script Attributes&#x20;

The script tag supports has 3 defined attributes;&#x20;

<table><thead><tr><th>Attribute</th><th width="171">Required?</th><th>Value</th><th>Notes</th></tr></thead><tbody><tr><td><code>type</code> </td><td>Required</td><td><code>application/sa+json</code> </td><td></td></tr><tr><td><code>handler</code> </td><td>Required</td><td>The handler's name </td><td>See the module documention for details. </td></tr><tr><td><code>name</code> </td><td>Varies, depending on the handler </td><td>The specific name of the configuration block, which is library-dependent. </td><td></td></tr></tbody></table>

### Configuration Block Metadata&#x20;

{% hint style="info" %}
Currently, SA configuration blocks are always JSON, as identified in the MIME type.  In the future, we'll be expanding to support other formats.&#x20;
{% endhint %}

<table><thead><tr><th>Key</th><th width="283">Value</th><th></th></tr></thead><tbody><tr><td><code>@context</code> </td><td><code>https://attr.sygnal.com</code> </td><td></td></tr><tr><td><code>@version</code> </td><td><code>0.1</code> </td><td>Indicates the version of the configuration block being used. Specific modules may increment this. </td></tr></tbody></table>

### Configuration Block Contents&#x20;

The JSON content used depends on the handler type. &#x20;

See the module documentation for details.&#x20;


# Configuration Block Specification

All SA configuration blocks are HTML `<script>`elements with a specific structure.

Here's an example;&#x20;

```html
<script type="application/sa+json" handler="handler-name" name="my-name">
{
  "@context": "https://attr.sygnal.com",
  "@version": "0.1",
  ... 
}
</script>
```

The script tag supports has 3 defined attributes;

### `type` = `application/sa+json`&#x20;

Identifies the script MIME type as an SA configuration block.&#x20;

{% hint style="success" %}
SA6 will support [Sygnal's HSON data format](https://hson.sygnal.com/) natively as well, with the `application/sa+hson` type.&#x20;
{% endhint %}

* Type
* Name
* Handler

Here's an example;&#x20;

```html
<script type="application/sa+json" handler="handler-name" name="my-name">
{
  "@context": "https://attr.sygnal.com",
  "@version": "0.1",
  "url": "https://conversion-tracker-url.com", 
  "transactionIdType": "query", 
  "transactionId": "transactionId",
  "type": "contact",
  "item": ""  
}
</script>
```

## Engineer's Technical Notes&#x20;

We've designed SA's Configuration Blocks protocol with a number of primary considerations.&#x20;

Standards compliance;&#x20;

* Conventional HTML `<script>` mechanics are used&#x20;
  * Scripts are never rendered as page content &#x20;
  * The MIME type ensures that it is not confused with regular scripts &#x20;
* Conventional JSON standards are supported&#x20;

Script efficiency;&#x20;

* SA library scripts can easily locate relevant configuration blocks&#x20;
  * Explicit types can be found by matching the handler name; \
    script\[handler="handler-name" i]&#x20;
  * And/or the handler categories; \
    `script[handler="handler-name" i], script[handler^="handler-name." i]`&#x20;
  * Where possible these handler names are case-insensitive&#x20;
* Configuration blocks can also be identified by specific name&#x20;
*

Convenience

* Where possible, case-insensitive value matches \
  `script[handler="handler-name" i]`&#x20;

Extensibility & future proofing; &#x20;

```
'script[handler="foo" i], script[handler^="foo." i]'
```

e.g. a typical Basin Contact Us form&#x20;

```html
<script type="application/sa+json">
{
  "@context": "https://attr.sygnal.com",
  "@type": "ConversionEvent",
  "@version": "0.1",
  "url": "https://conversion-tracker-url.com", 
  "transactionIdType": "query", 
  "transactionId": "transactionId",
  "type": "contact",
  "item": ""  
}
</script>
```

* Data pulled in from session var&#x20;
* Overridden with data here
* TransactionID created / set&#x20;

Make these easier to find&#x20;

```html
<script type="application/sa5+json" handler="ConversionEvent">
{
  "@context": "https://attr.sygnal.com",
  "@type": "ConversionEvent",
  "@version": "0.1",
  "url": "https://conversion-tracker-url.com", 
  "transactionIdType": "query", 
  "transactionId": "transactionId",
  "type": "contact",
  "item": ""  
}
</script>
```

## Decisions&#x20;

`@context`

`@type`

`@version`

```html
<script type="application/sa+json" handler="Trigger.Timer">
{
  "@context": "https://attr.sygnal.com",
  "@type": "TimerTrigger",
  "@version": "0.1",
  "timer": "60", 
  "timerRepeat": "120",
  "event": "my-event" 
}
</script>
```

```html
<script type="application/sa+json" handler="Event" name=""> 
{
  "@context": "https://attr.sygnal.com",
  "@type": "TimerTrigger",
  "@version": "0.1",
  "timer": "60", 
  "timerRepeat": "120",
  "event": "my-event" 
}
</script>
```

? change @type to @handler   &#x20;

? is there value to context and type?  &#x20;

SEO?&#x20;

Docs?&#x20;

Make these easier to find&#x20;

```html
<script type="application/sa5+json" handler="ConversionEvent">
{
  "@context": "https://attr.sygnal.com",
  "@type": "ConversionEvent",
  "@version": "0.1",
  "url": "https://conversion-tracker-url.com", 
  "transactionIdType": "query", 
  "transactionId": "transactionId",
  "type": "contact",
  "item": ""  
}
</script>
```

### Named Config Blocks

Used when referenced from a `:config` modifier on a base attribute.&#x20;

```html
<script type="application/sa5+json" name="foo">
{
  "@context": "https://attr.sygnal.com",
  "@type": "ConversionEvent",
  "@version": "0.1",
  "url": "https://conversion-tracker-url.com", 
  "transactionIdType": "query", 
  "transactionId": "transactionId",
  "type": "contact",
  "item": ""  
}
</script>
```

* `name` added&#x20;

## Supported Data Formats

* JSON
* HSON ( coming soon )&#x20;

## Approach Notes

We're moving towards a precedent set by JSON-LD, as it's cleaner and more utilitarian.&#x20;

### About the MIME Type

In general, we'll stick with standard conventions; &#x20;

The type of application/sa+json follows conventions like JSON-LD, which is \`application&#x20;

```
application/sa+json
```

```
application/sa+hson
```

## Legacy&#x20;

```
application/sa5+json
```

```
application/sa5+hson
```

* **`application/`**:
  * The primary type (`application`) represents a **general type of data**. It’s often used for data that doesn't fall under other primary types such as `text`, `image`, `audio`, etc.
  * `application/` typically indicates that the content **is not intended to be directly displayed** to the user but rather used or processed by an application. For example, `application/json`, `application/xml`, and `application/pdf` indicate that the content is primarily intended for programmatic use.
* **`sa5+json`**:
  * The `+json` suffix tells the system that the content is in **JSON format** and should be handled as such. It helps parsers and applications recognize how to process the content.
  * The `sa5` is a custom subtype, giving a specific indication about what the JSON content represents in the context of your system.

####


# Preloaders

Much of SA5 is dedicated to creating a dynamic UX experience- sorting, filtering, data-binding, calculations, and much more.

This requires placeholder elements which - before the SA5 code executes, will be uninitialized.

To ensure a perfect view, we are standardizing on a specific preloader approach which can be controlled per-element using the wfu-preload attribute.

{% hint style="info" %}
Final details are being refined, and this capability is being rolled out across all libs gradually.&#x20;
{% endhint %}

## Options

`wfu-preload` = ( method )&#x20;

* `hidden` - the element is initially hidden and collapsed from view using CSS display: none.
* `invisible` - the element is initially invisible, but takes space using CSS visibility: hidden.
* `animated` - the element has an element-specific loading animation.&#x20;

Animation types;&#x20;

* redacted, animated &#x20;
* SVG animation
  * dot-dot-dot
* static placeholder text
* blurred placeholder text, then unblurred on load&#x20;

## Technical Notes&#x20;

* All preloaders are CSS-based, and are driven by the wfu-preload attribute.&#x20;
* On load, the value is set first
* Then the wfu-preload attribute is removed. This gives the element its default visibility.&#x20;

{% hint style="info" %}
Preload effects generally do not appear at design time, only in the published site.
{% endhint %}


# Breakpoints

Webflow offers 7 [breakpoints](https://help.webflow.com/hc/en-us/articles/33961300305811-Breakpoints-overview#01JDAHFPAZZAB1RDDCC69D8YVG).&#x20;

| Breakpoint Name  | Abbr | Minimum Width | Maximum Width |
| ---------------- | ---- | ------------- | ------------- |
| XX-Large         | 1920 | 1920px        | ∞             |
| X-Large          | 1440 | 1440px        | 1919px        |
| Large            | 1280 | 1280px        | 1439px        |
| Desktop (base)   | D    | 992px         | 1279px        |
| Tablet           | T    | 768px         | 991px         |
| Mobile Landscape | L    | 480px         | 767px         |
| Mobile Portrait  | P    | -             | 479px         |

Goals;&#x20;

* Allow some SA5 attributes to be enabled only on certain breakpoints&#x20;
* Allow some SA5 attributes to be configured differently on different breakpoints&#x20;
  * Utilize Webflow's natural "override from desktop" behavior here if possible&#x20;

## SA5 & Breakpoints&#x20;

Some of SA5's attribute settings are breakpoint-aware, meaning that their configuration can be changed for specific breakpoints.&#x20;

* In simple cases, we simply want to be able to switch on or off the attribute's behavior at specific breakpoints.&#x20;
* In more complex cases, we may have breakpoint-specific configurations.&#x20;

This leads to 3 classes of attributes;&#x20;

| Attribute type                                    | Responsive enable/disable? | Responsive config? |
| ------------------------------------------------- | -------------------------- | ------------------ |
| <p>Standard attributes <br>( Non-responsive )</p> | -                          | -                  |
| Simple Responsive attributes                      | Yes                        | -                  |
| Complex Responsive attributes                     | Yes                        | Yes                |

## Simple-Responsive Attributes

A Simple Reponsive attribute can be switched on or off using a special modifier;

`*:bp` = ( breakpoints list )   &#x20;

{% hint style="info" %}
e.g. we might have `sa-layout:bp` = `d+`  &#x20;
{% endhint %}

The breakpoints list is a comma-separated list, defining the breakpoints which should be supported.

* Breakpoints can be listed in any order &#x20;
* They must be comma-separated
* They are case-insensitive - D and d are identical&#x20;
* They can be range-expanded using `+` and `-` modifiers&#x20;
  * `+` meaning *this breakpoint and larger* &#x20;
  * `-` meaning *this breakpoint and smaller* &#x20;

Examples;

<table><thead><tr><th width="152"></th><th>1920</th><th>1440</th><th>1280</th><th>Desktop</th><th>Tablet</th><th>Landscape</th><th>Portrait</th></tr></thead><tbody><tr><td><code>d+</code></td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td><td>-</td><td>-</td><td>-</td></tr><tr><td><code>d-</code></td><td>-</td><td>-</td><td>-</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td><code>l-</code></td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td><td>Yes</td><td>Yes</td></tr><tr><td><code>1440+,t,p</code></td><td>Yes</td><td>Yes</td><td>-</td><td>-</td><td>Yes</td><td>-</td><td>Yes</td></tr></tbody></table>

Notes;

* Overlapping specifications are fine, but not recommended
  * e.g. `1280+,1920` is redundant, but will be processed fine&#x20;

## Complex-Responsive Attributes

{% hint style="danger" %}
Still under specification&#x20;
{% endhint %}

On these attributes, that's achieved by adding a special suffix to the custom attribute name.  For example, an attribute named `sa-attr` could have these suffixes.&#x20;

{% hint style="info" %}
These attributes are applied in a cascading fashion in the same way that breakpoints work within Webflow directly.  However, if you have overlapping configs, they will be applied in the order specified with the last one taking precedence.&#x20;
{% endhint %}

Ideas;

:bp - specify breakpoints this attribute applies on&#x20;

sa-layout

sa-layout-target

sa-layout-pos = 5

sa-layout-pos:T = 4

sa-layout-pos:L = 3

Layout-sensitive elements

Re-execute on breakpoint changes&#x20;

## Configuration Blocks

\``wfu-grid-equalheightrows:bp` = ( breakpoints )&#x20;

### Combined Config Block&#x20;

With breakpoint-specific sub-objects&#x20;

```html
<script type="application/sa5+json" name="foo">
{
  "@context": "https://attr.sygnal.com",
  "@type": "ConversionEvent",
  "@version": "0.1",
  "url": "https://conversion-tracker-url.com", 
  "transactionIdType": "query", 
  "transactionId": "transactionId",
  "type": "contact",
  "item": "",
  "1280": {
    "type": "contact",
    "item": ""  
  } 
}
</script>
```

### Responsive Config Blocks

```html
<script type="application/sa5+json" name="foo" bp="1280+">
{
  "@context": "https://attr.sygnal.com",
  "@type": "ConversionEvent",
  "@version": "0.1",
  "url": "https://conversion-tracker-url.com", 
  "transactionIdType": "query", 
  "transactionId": "transactionId",
  "type": "contact",
  "item": "" 
}
</script>
```

## Technical Notes

SA6 might add core attribute foundations, e.g.&#x20;

* Default attribute
* Configurable attribute ( can have a config block )&#x20;
* Responsive attribute
* Responsive configurable attribute&#x20;


# Component Support 🧪

Webflow Components are great, but have quite a few limitations that can make them very difficult to work with in more functional setups;

## Key Limitations

* HTML Embeds within a component cannot +Add Field any content from the component properties.&#x20;
* Custom Attributes on elements within a component can only data-bind to "Attribute" type properties.
  * These must be created directly from the attribute-binding&#x20;
  * No binding to e.g. an Image url, or plain text &#x20;
* Limited property types&#x20;
  * No general URL type, for e.g. specifying a video or file download&#x20;
    * Plain text can work but does not validate the field
  * No option-list setups &#x20;
* Attribute content cannot be automatically exposed as e.g. CSS vars or JS vars for use in custom styling code

CMS

Custom Attributes can only bind to a special custom attribute type.&#x20;

Text element - text property

Rich text element - rich text property&#x20;

Div - text property

<https://www.sygnal.com/lessons/component-data-binding>

## Conditional Visibility

Leverage the current set of property capabilities

Conceptual

\`wfu-if-set\` = ( value )

`wfu-if-true` = ( truthy value )&#x20;

wfu-if-hide-method = remove | hide&#x20;

wfu-switch&#x20;

wfu-switch-case = x

### How Webflow Elements Bind to Component Properties&#x20;

<https://docs.google.com/document/d/1D5lfPycDO9G-Y0-1yFtYiz6EbKhbRzsjvSZSAGU0IDE/edit?tab=t.0#heading=h.o112x0xag8a1>

### How Component Properties Bind to CMS Fields

Within a collection list or collection page.&#x20;

<https://docs.google.com/document/d/1D5lfPycDO9G-Y0-1yFtYiz6EbKhbRzsjvSZSAGU0IDE/edit?tab=t.0#heading=h.e7y6xt8gwe9q>

## Rethinking Embeds&#x20;

As of 2025-02-08 Embeds do not support the dynamic integration of data from component properties.  However, it's possible to bind a custom attribute to aproperty,&#x20;

|                     |                                                                                                                         |   |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------- | - |
| Attribute property  | Add a propertyBind it to the                                                                                            |   |
| Text property       | Add a text property. Bind it to a text element within the component.  T                                                 |   |
| Rich text property  |                                                                                                                         |   |
| Image property      | Add an image property.  Bind it to an image element within the component ( can be hidden ).  Use script to retrieve it. |   |
| Visibility property |                                                                                                                         |   |

## Referencing Data

To do this we first want to establish a standard for referencing data simply and flexibly from other elements within the Webflow Component's generated HTML.&#x20;

There are t

Component n

This technique can also be adjusted to the target the parent element's attribute or immediate siblings. &#x20;

| Target Element                 | Attribute                                                      | InnerText    | InnerHTML    |
| ------------------------------ | -------------------------------------------------------------- | ------------ | ------------ |
|                                | Here `attr` is an example attribute name we want to reference. |              |              |
| Current element attribute      | `{{#attr}}`                                                    |              |              |
| Parent element attribute       | `{{^#attr}}`                                                   |              |              |
| Prev sibling element attribute | `{{<#attr}}`                                                   |              |              |
| Next sibling element attribute | `{{>#attr}}`                                                   |              |              |
| Current element innerText      |                                                                |              |              |
| Specific named element         | `{{name.#attr}}`                                               | `{{name.$}}` | `{{name.%}}` |
|                                |                                                                |              |              |

{% hint style="info" %}
Theoretically, we'd want to populate attributes first, then embeds, to allow some advanced changing.&#x20;
{% endhint %}

## Referencing Data from an Attribute &#x20;

To populate a custom attribute with Property values, we want

To do this we'll leverage SA5's existing spec for Dynamic Attributes.  &#x20;

Under consideration

```
x:attr:= 
xx:attr=  
x::attr= 

```

```
x:attr = {{#attr}} 
```

This technique can also be adjusted to the target the parent element's attribute or immediate siblings. &#x20;

| Target Element                 | Attribute                                                      | InnerText    | InnerHTML    |
| ------------------------------ | -------------------------------------------------------------- | ------------ | ------------ |
|                                | Here `attr` is an example attribute name we want to reference. |              |              |
| Current element attribute      | `{x:myval=#attr}}`                                             |              |              |
| Parent element attribute       | `{{^#attr}}`                                                   |              |              |
| Prev sibling element attribute | `{{<#attr}}`                                                   |              |              |
| Next sibling element attribute | `{{>#attr}}`                                                   |              |              |
| Current element innerText      |                                                                |              |              |
| Specific named element         | `{{name.#attr}}`                                               | `{{name.$}}` | `{{name.%}}` |
|                                |                                                                |              |              |

## Referencing Data from an Embed&#x20;

This technique can also be adjusted to the target the parent element's attribute or immediate siblings. &#x20;

| Target Element                 | Attribute                                                      | InnerText    | InnerHTML    |
| ------------------------------ | -------------------------------------------------------------- | ------------ | ------------ |
|                                | Here `attr` is an example attribute name we want to reference. |              |              |
| Current element attribute      | `{{#attr}}`                                                    |              |              |
| Parent element attribute       | `{{^#attr}}`                                                   |              |              |
| Prev sibling element attribute | `{{<#attr}}`                                                   |              |              |
| Next sibling element attribute | `{{>#attr}}`                                                   |              |              |
| Current element innerText      |                                                                |              |              |
| Specific named element         | `{{name.#attr}}`                                               | `{{name.$}}` | `{{name.%}}` |
|                                |                                                                |              |              |

### Attribute Property      &#x20;

{% hint style="info" %}
As of 2025-02-08 attribute properties need to be created from the custom attribute binding directly.  Select an element, add a custom attribute, and choose the option to bind it to a component property.  You will be given an option to create a new one.&#x20;
{% endhint %}

1. Bind the attribute property directly to the Embed element itelf.  Any unique attribute name is fine, for example `my-attr`&#x20;
2. Within the embed, reference it using that attribute name, e.g.;     &#x20;

```
{{#my-attr}}
```

You can position it anywhere, and use text adjacent to it, for example;

```html
<style>
.my-class {
  color: #{{#hex-color}};
}
</style>
```

This cosntruction will pull the exact contents of the attribute named `hex-color` from the Embed element, and integrate it into the text precisely.&#x20;

{% hint style="info" %}
We're using the distinctive `{{...}}` construction because Webflow styles these specially in Embeds.  For example;&#x20;
{% endhint %}

<figure><img src="/files/91dpi7YxWtbfNgVM7qIR" alt=""><figcaption></figcaption></figure>

### Parent & Sibling Attributes  &#x20;

This technique can also be adjusted to the target the parent element's attribute or immediate siblings. &#x20;

| Target Element                 | Attribute       | InnerText | InnerHTML |
| ------------------------------ | --------------- | --------- | --------- |
| Current element attribute      | `{{#my-attr}}`  |           |           |
| Parent element attribute       | `{{^#my-attr}}` |           |           |
| Prev sibling element attribute | `{{<#my-attr}}` |           |           |
| Next sibling element attribute | `{{>#my-attr}}` |           |           |
| Current element innerText      |                 |           |           |

## Image Property&#x20;

1. Place an image element immediately adjecent to and prior to the Embed &#x20;
2. Bind it to your image property&#x20;
3. In the embed, target the sibling's source attribute, e.g.&#x20;

```html
<a href="{{<#src}}" target="_blank">Open this image in a new tab</a> 
```

## Named

The component itself MUST have an attribute;

This identifies the outer bound of the component cleanly, so that variables can be scoped within it.&#x20;

```
wfu-component-name="foo" 
```

Within that

```
<img src="..." wfu-component-value=""    
```

Visibility Property

? Check for element existance &#x20;


# Sygnal Attributes ( SA5 ) Support

A review of the support options for the SA5 library and toolset

A note from Mike-&#x20;

It has been great to watch the SA5 community grow so dramatically over the past several years. What started as a convenient toolset for my agency Sygnal and our client projects has becomes something much larger and more powerful than I anticipated - and it has helped many thousands of companies build better sites on Webflow.&#x20;

However this success also comes at a price. The number of emails, feature quests, configuration questions and custom implementation requests I receive has multiplied 100-fold.

**To put it simply, the number of requests I receive now is not manageable, and this means that I can no longer afford to offer free support for a free product.**&#x20;

I am still committed to SA5, and Sygnal still relies on it daily for every project we build. I'll still remain committed to improving the toolset, expanding the documentation and use cases.

However starting Jan 2024, all 1:1 support requests need to be handled differently, so you'll find the current options below.&#x20;

*Thanks for understanding!*

## Support options

### Read the docs, and use the cloneables

There is an absolute *ton* of detailed documentation here. Please read it, and use the Webflow cloneable sites as your configuration reference. These will almost certainly have the answers to your questions.&#x20;

{% hint style="info" %}
In this documentation site, make certain to expand the left side navigation items as a lot of the details are collapsed in the navigation hierarchy.&#x20;
{% endhint %}

### Sygnal's micro-consulting

Sygnal has a special micro-consulting offering for Webflow designers and agencies. Essentially you can purchase a few hours of time, and then use it fractionally for bug fixes, troubleshooting, feature builds, integrations work, tech mentoring, and so on.&#x20;

* Details- <https://www.sygnal.com/webflow>
* Packages- <https://www.sygnal.com/webflow#rates>

### Open source community

**SA5 is an open source library, based on TypeScript.**&#x20;

We built it this way specifically so that the Webflow community could augment and extend the library in a manageable way.&#x20;

### Discourse forum

While we can't field specific support requests, we always want to know if the documentation need additional detail. &#x20;

If something's unclear, you can message Sygnal's team in our Discourse forum.&#x20;

<https://sygnal-attr.discourse.group/>


# Dev Team Notes

Dev env

* VSCode

Github Codespaces&#x20;

## Extensions&#x20;

### Live Sass Compiler

<https://marketplace.visualstudio.com/items?itemName=glenn2223.live-sass>

### Region Folding&#x20;

<https://marketplace.visualstudio.com/items?itemName=maptz.regionfolder>


# GDPR

Sygnal's SA5 libraries are delivered on the jsdelivr CDN.

{% hint style="success" %}
These notes are for anyone concerned about using CDNs in the EU, due to GDPR regulations.&#x20;
{% endhint %}

In January 2022, a Munich court ruled that a website using Google Fonts violated the GDPR by transmitting users' IP addresses to Google without consent. This decision caused concern among developers about the implications for other CDN services like jsDelivr.

To address these concerns, jsDelivr consulted R\&S Partners, a law firm in Krakow, Poland. The firm clarified that the ruling is specific to the Google Fonts case and does not establish a general precedent across the EU. Each case requires individual assessment.

Key distinctions between Google Fonts and jsDelivr include:

* **Necessity of Data Processing**: jsDelivr processes IP addresses to deliver content efficiently through its global CDN infrastructure. This processing is essential for the service's functionality, unlike Google Fonts, which can be used without connecting to Google's servers.
* **Purpose of Data Use**: jsDelivr uses IP data solely for statistical purposes, such as optimizing server locations. The data is aggregated and deleted promptly, not used for marketing or sold to third parties.
* **Security Measures**: jsDelivr employs encrypted connections and regional servers to minimize risks like man-in-the-middle attacks, enhancing user data protection.

The law firm concluded that jsDelivr's data processing practices align with GDPR requirements, particularly under Article 6(1)(f), which allows processing based on legitimate interests. They recommend that websites using jsDelivr update their privacy policies to inform users about IP address processing for service functionality and security.

In summary, the German court's ruling on Google Fonts does not directly impact jsDelivr. jsDelivr's data handling practices are considered compliant with GDPR, making it safe to use.

## Our Perspective

It's our perspective that it's legally GDPR-compliant to use SA5 in its CDN hosted form.

**However if you should have any requirement to host it differently, SA5 is fully open source and can be downloaded and deployed elsewhere from our Github repo.**&#x20;

{% hint style="info" %}
Finsweet's libraries also use JSDelivr as their chosen CDN.&#x20;
{% endhint %}

## References&#x20;

<https://www.jsdelivr.com/blog/how-the-german-courts-ruling-on-google-fonts-affects-jsdelivr-and-why-it-is-safe-to-use/>

<https://news.ycombinator.com/item?id=35793009>


# SA6

Thoughts.

## Central Concerns&#x20;

* Single library include, no more separate libs
  * Possible live bootstrapping ( check Safari issues here )&#x20;
* `sa-` attribute replaces everything&#x20;
* Attributes auto-executed in progressive layer fashion
* Standardized configs
  * Layered HSON&#x20;
* Pre-loaded configs, ready for use
  * e.g. Kiosk config&#x20;

Consider JSON5 support

* For use in JSON configs    &#x20;
* Other conveniences for SA6 data&#x20;

<https://www.npmjs.com/package/json5/v/2.2.1>

<https://spec.json5.org/>

<https://json5.org/>

## WFX

Add docs, debug info&#x20;

## Execution order&#x20;

* Custom Attributes
* Organization and data manipulation
  * SA5 Calc
  * SA5 Layout&#x20;
  * pulling external data&#x20;
* Formatting&#x20;


# Durations

Attributes that specify a duration are being migrated to a standard convention.&#x20;

Duration is specified as an integer followed by an optional unit of measurement, as follows;

<table><thead><tr><th width="160">Suffix</th><th width="175">Unit </th><th>Examples</th></tr></thead><tbody><tr><td><code>ms</code> ( default )</td><td>Milliseconds</td><td><code>10000ms</code> = 10 seconds<br><code>10000</code> = 10 seconds</td></tr><tr><td><code>s</code></td><td>Seconds</td><td><code>30s</code> = 30 seconds</td></tr><tr><td><code>m</code></td><td>Minutes</td><td><code>20m</code> = 20 minutes</td></tr><tr><td><code>h</code></td><td>Hours</td><td><code>4h</code> = 4 hours</td></tr><tr><td><code>d</code></td><td>Days</td><td><code>3d</code> = 3 days</td></tr><tr><td><code>w</code></td><td>Weeks</td><td><code>1w</code> = 1 week</td></tr><tr><td><code>M</code></td><td>Months</td><td><code>2M</code> = 2 months ( at 30 days per month )</td></tr><tr><td><code>y</code></td><td>Years</td><td><code>2y</code> = 2 years ( at 365 days per year )</td></tr></tbody></table>


# Triggers, Events & Actions

Create functionality in your Webflow site using SA5's triggers, events, & actions.

{% hint style="success" %}
Much of SA5's most notable developments have been behavioral.  Show or hide items, connect things to the position of a video, perform actions as your slider advances. As a result we've been rethinking how triggers, actions, and events work in SA5.&#x20;

This library is being re-conceptualized and is likely to be absorbed into **SA6 Core**.&#x20;
{% endhint %}

SA5's Trigger lib was originally about triggering an interaction such as a pop-up modal from a click event.  The user clicks something and we trigger an interaction event.&#x20;

This was useful for;

* Displaying a modal from a nav button click
* Displaying a pop-up definition when clicking on a linked term in a blog post&#x20;
* Displaying a modal when the user clicks a map pin&#x20;

## How It's Evolving&#x20;

This concept has expanded significantly, and we're re-conceptualizing everything;

* The types of trigger events we want to support&#x20;
* The resulting actions that we want to support&#x20;
* The model connecting those two&#x20;

At the center of this model is a more versatile structure we refer to as **Trigger-Event-Action ( TEA )**.

This is the simplest TEA arrangement.&#x20;

<img src="/files/f9Nuce4aX4EK3OIhHsfL" alt="" class="gitbook-drawing">

Here's the basic mechanic.  Elements in your webpage are marked up with attributes that identify the Triggers, Events, and Actions you want configured.&#x20;

1. A Trigger is fired, such as a button click&#x20;
2. This invokes the specified Event&#x20;
3. The Event then performs the specified Actions&#x20;

Let's look at a simple example;&#x20;

Here's an example of a click Trigger, which invokes `my-event`.  &#x20;

```html
<button wfu-trigger-click="my-event">Click me</button> 
```

When a user clicks this button, SA5 invokes the Event named `my-event`.&#x20;

This in turn performs whatever Actions are connected to `my-event`.

By itself, the above construction does nothing, because the Event is not connected to any Actions.

Let's add an Action to a Webflow tab, so that clicking the button clicks ( and changes to ) that specific tab.&#x20;

Here's an example;&#x20;

```html
<div class="w-tab" wfu-action-click="my-event">Tab 1</div> 
```

When the `my-event` Event is invoked, a click Action will be performed on this tab.&#x20;

In this construction;

* `wfu-action-click` indicates the type of Action that will be performed&#x20;
* `my-event` indicates the Event&#x20;
* The element itself is where the Action will be performed.&#x20;

## More Advanced Eventing&#x20;

The Event is the center of the TEA architecture, and there are two crucial concepts to understand here that grant you a huge amount of flexibility in your designs;&#x20;

* Any number of Triggers can invoke the same Event.&#x20;
* When the Event is invoked, any number of Actions may be performed.&#x20;

<img src="/files/xKVhBUrgx4q2W55z5XyP" alt="" class="gitbook-drawing">

## So Many Types of TEA&#x20;

{% hint style="info" %}
If you're familiar with systems engineering, TEA follows a familiar Pub/Sub style pattern.&#x20;
{% endhint %}

### Triggers&#x20;

Here are a few types of **SA5 Triggers** we're working on, which can invoke *SA5 Events*;&#x20;

* User triggers.  Click, scroll, hover, etc.&#x20;
* Systems triggers.  Timers, etc.&#x20;
* Webflow triggers. &#x20;
  * Interactions, at any point in the interaction.&#x20;
  * Possibly, certain other system.level mechanics... open or close of a hamburger menu
  * Tab switches
  * Slider events&#x20;
* Custom triggers.  Custom code-triggered events. &#x20;
* Breakpoint change triggers.&#x20;
* Keyboard triggers.&#x20;
* Mutation Observer triggers. &#x20;
* Custom triggers. &#x20;
* Other triggers. &#x20;

### Event&#x20;

An **SA5 Event** is effectively a messaging pipeline, which has a name that you assign.  When an event fires, it triggers a series of *SA5 Actions*. &#x20;

* Multiple Triggers can invoke the same Event.&#x20;
* An Event can perform several Actions&#x20;

{% hint style="info" %}
**FUTURE NOTE** \
We're considering the ability for a *single* Trigger to invoke *multiple* Events. \
For example, `wfu-trigger-click` = `event1,event2,event3` \
If built, this may be limited to certain trigger types, owever at this point we have not identified key use cases where this adds substantial value.&#x20;
{% endhint %}

### Action&#x20;

An **SA5 Action** is a resulting action that can be invoked when an SA5 Event occurs.

Examples;

* A click on another element&#x20;
* Navigation
* A webhook call&#x20;
* A logged event&#x20;
* A GTM event&#x20;
* etc.&#x20;

{% hint style="info" %}
**FUTURE NOTE** \
We're also considering the concept of a Trigger as an Action type, so that we can chain Trigger-Event-Action sequences.&#x20;
{% endhint %}

## Examples&#x20;

Here are some rough examples;

<img src="/files/8exRpGUNj5vEAyPXbPQB" alt="" class="gitbook-drawing">

## Use Cases&#x20;

This model handles an unthinkable number of possible use cases.

* Mirror click.  Clicking one button clicks another.&#x20;

* Form submit.  Clicking a non-form-submit button triggers a form submission.&#x20;

*

* There may evolve a distinction between Named Events and Unnamed Events

* Named Events can be invoked directly from script&#x20;

*

Concept

Triggers are centralized around the concept of a named event.

A&#x20;

## Event Detail&#x20;

{% hint style="info" %}
UNDER CONSIDERATION&#x20;
{% endhint %}

In some scenarios there may be detail or context bundled into the event, which can affect the Action.&#x20;

Considering;&#x20;

* Sub-triggers, such as `##link` items for&#x20;
  * Invoke dictionary pop-ups&#x20;
  * Map pin pop-ups&#x20;
  * Trigger chatbots, with content
  * Trigger AI chatbots ( like Gista ) with a query&#x20;
  * Trigger GTM datalayer and events &#x20;

## Considerations

* Need to be mindful of preventing unrestrained circular event firing.&#x20;
* E.g. differentiate between user-triggered and code-triggered clicks  &#x20;


# Triggers

An **SA5 Trigger** is a user action or system state-change that occurs, which invokes an *SA5 Event*.  These are generally classified as;

* User triggers.  Click, scroll, hover, etc.&#x20;
* Systems triggers.  Timers, etc.&#x20;
* Webflow triggers. &#x20;
  * Interactions, at any point in the interaction.&#x20;
  * Open or close of a dropdown&#x20;
  * Tab switches
  * Slider events&#x20;
* Custom triggers.  Custom code-triggered events. &#x20;
* Breakpoint change triggers.&#x20;
* Keyboard triggers.&#x20;
* Mutation Observer triggers. &#x20;
* Custom triggers. &#x20;
* Other triggers. &#x20;

## SA5 Triggers&#x20;

### Types of Triggers

Broadly, there are two types of SA5 Triggers...&#x20;

* **Element Triggers.**  An element-specific Trigger that is tied to user-interaction with a specific element.  Clicking an element, changing a tab, scrolling an element into view are examples.&#x20;
* **Page Triggers.** These are Triggers that occur in the context of the page, but are not tied to any specific element. Scrolling a page to 50% of its length, a timer trigger, or an exit intent action are examples.  We generally configure these using a special SA5 script Trigger block.&#x20;

The sub-pages in this section contain specific trigger events.&#x20;

Make certain to ready the About page for an understanding of&#x20;

A Trigger invokes an Event which results in Actions.&#x20;

| Trigger                                                                                 | Applies to                    | Notes                                                                                                    |
| --------------------------------------------------------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------- |
| <ul><li>An interaction occurring, reaching a certain point in the interaction</li></ul> |                               | Triggered by affecting a specific element, and a mutation observer is watching that element for chagnes. |
| <p>Click<br><code>sa-trigger-click</code> </p>                                          | Any element                   | Also touch                                                                                               |
| <p>Scroll into view <br><code>sa-trigger-scrollintoview</code> </p>                     | Any element                   | Currently fires only once                                                                                |
| Mousover on an element                                                                  | Any element                   | Only works on devices with a pointer                                                                     |
| Focus                                                                                   | Any focusable element         |                                                                                                          |
| Blur                                                                                    |                               |                                                                                                          |
| Change                                                                                  | Input elements                |                                                                                                          |
| Scroll to                                                                               |                               |                                                                                                          |
| Video played                                                                            | Video elements                |                                                                                                          |
| Video paused                                                                            |                               |                                                                                                          |
| Video at %                                                                              |                               |                                                                                                          |
| Link Click                                                                              |                               |                                                                                                          |
| Hash change, like nav to `#hash`                                                        | hashchange event              | wfu-trigger-hashchange                                                                                   |
| Load w/ Hash                                                                            | load event, with the hash set | wfu-trigger-hashload                                                                                     |
| Hash                                                                                    | load or change                | wfu-trigger-hash                                                                                         |
| Gated Trigger                                                                           |                               |                                                                                                          |

Triggers

## Element-Level Triggers&#x20;

### All Elements

<table><thead><tr><th width="130">Type</th><th width="197">Trigger</th><th>Detail</th><th>Notes</th><th></th></tr></thead><tbody><tr><td>Any</td><td><a href="/pages/qOQ79BM12yWIYnMUGGMP">Click</a></td><td></td><td>wfu-trigger-click </td><td>wfu-action-click </td></tr><tr><td>Any</td><td><a href="/pages/IQxmd5SmhpozLYuQNhHX">Link click</a></td><td></td><td>Used in <code>##event</code> constructions </td><td></td></tr><tr><td>Any</td><td>Scrolled into view</td><td></td><td>wfu-trigger-scrollintoview</td><td></td></tr><tr><td>Any</td><td>Hover </td><td></td><td>wfu-trigger-hover</td><td></td></tr><tr><td>Any</td><td>Interaction trigger</td><td></td><td>wfu-trigger-ix</td><td></td></tr><tr><td>Dropdown</td><td>Opened</td><td></td><td>wfu-trigger-opened</td><td>wfu-action-open </td></tr><tr><td>Dropdown</td><td>Closed</td><td></td><td>wfu-trigger-closed</td><td>wfu-action-close </td></tr><tr><td>Tab</td><td>Changed</td><td></td><td></td><td></td></tr><tr><td>Form</td><td>Submit Attempt</td><td></td><td>wfu-trigger-submit </td><td></td></tr><tr><td>Form</td><td>Submit Success</td><td></td><td>wfu-trigger-submit-success </td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td>ECom</td><td>Cart Opened</td><td></td><td>wfu-trigger-open </td><td>wfu-action-open </td></tr><tr><td>ECom</td><td>Cart Closed</td><td></td><td>wfu-trigger-close </td><td>wfu-action-close </td></tr><tr><td>ECom</td><td>Item Added</td><td></td><td>wfu-trigger-add-item </td><td></td></tr><tr><td>ECom</td><td>Item Removed</td><td></td><td>wfu-trigger-remove-item </td><td></td></tr></tbody></table>

### Video Elements

<table><thead><tr><th width="130">Type</th><th width="197">Trigger</th><th>Detail</th><th>Notes</th><th></th></tr></thead><tbody><tr><td>Any</td><td><a href="/pages/qOQ79BM12yWIYnMUGGMP">Click</a></td><td></td><td>wfu-trigger-click </td><td>wfu-action-click </td></tr><tr><td>Any</td><td><a href="/pages/IQxmd5SmhpozLYuQNhHX">Link click</a></td><td></td><td>Used in <code>##event</code> constructions </td><td></td></tr><tr><td>Any</td><td>Scrolled into view</td><td></td><td>wfu-trigger-scrollintoview</td><td></td></tr><tr><td>Any</td><td>Hover </td><td></td><td>wfu-trigger-hover</td><td></td></tr><tr><td>Any</td><td>Interaction trigger</td><td></td><td>wfu-trigger-ix</td><td></td></tr><tr><td>Video</td><td>Video Played</td><td></td><td>wfu-trigger-play</td><td>wfu-action-play </td></tr><tr><td>Video</td><td>Video Paused</td><td></td><td>wfu-trigger-pause</td><td>wfu-action-pause </td></tr><tr><td>Video</td><td>Video Ended</td><td></td><td>wfu-trigger-finished</td><td></td></tr><tr><td>Video</td><td>Video Progression %</td><td></td><td>wfu-trigger-seek</td><td>wfu-action-seek </td></tr><tr><td>Dropdown</td><td>Opened</td><td></td><td>wfu-trigger-opened</td><td>wfu-action-open </td></tr><tr><td>Dropdown</td><td>Closed</td><td></td><td>wfu-trigger-closed</td><td>wfu-action-close </td></tr><tr><td>Tab</td><td>Changed</td><td></td><td></td><td></td></tr><tr><td>Form</td><td>Submit Attempt</td><td></td><td>wfu-trigger-submit </td><td></td></tr><tr><td>Form</td><td>Submit Success</td><td></td><td>wfu-trigger-submit-success </td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td>ECom</td><td>Cart Opened</td><td></td><td>wfu-trigger-open </td><td>wfu-action-open </td></tr><tr><td>ECom</td><td>Cart Closed</td><td></td><td>wfu-trigger-close </td><td>wfu-action-close </td></tr><tr><td>ECom</td><td>Item Added</td><td></td><td>wfu-trigger-add-item </td><td></td></tr><tr><td>ECom</td><td>Item Removed</td><td></td><td>wfu-trigger-remove-item </td><td></td></tr></tbody></table>

### Dropdown Elements&#x20;

<table><thead><tr><th width="130">Type</th><th width="197">Trigger</th><th>Detail</th><th>Notes</th><th></th></tr></thead><tbody><tr><td>Any</td><td><a href="/pages/qOQ79BM12yWIYnMUGGMP">Click</a></td><td></td><td>wfu-trigger-click </td><td>wfu-action-click </td></tr><tr><td>Any</td><td><a href="/pages/IQxmd5SmhpozLYuQNhHX">Link click</a></td><td></td><td>Used in <code>##event</code> constructions </td><td></td></tr><tr><td>Any</td><td>Scrolled into view</td><td></td><td>wfu-trigger-scrollintoview</td><td></td></tr><tr><td>Any</td><td>Hover </td><td></td><td>wfu-trigger-hover</td><td></td></tr><tr><td>Any</td><td>Interaction trigger</td><td></td><td>wfu-trigger-ix</td><td></td></tr><tr><td>Video</td><td>Video Played</td><td></td><td>wfu-trigger-play</td><td>wfu-action-play </td></tr><tr><td>Video</td><td>Video Paused</td><td></td><td>wfu-trigger-pause</td><td>wfu-action-pause </td></tr><tr><td>Video</td><td>Video Ended</td><td></td><td>wfu-trigger-finished</td><td></td></tr><tr><td>Video</td><td>Video Progression %</td><td></td><td>wfu-trigger-seek</td><td>wfu-action-seek </td></tr><tr><td>Dropdown</td><td>Opened</td><td></td><td>wfu-trigger-opened</td><td>wfu-action-open </td></tr><tr><td>Dropdown</td><td>Closed</td><td></td><td>wfu-trigger-closed</td><td>wfu-action-close </td></tr><tr><td>Tab</td><td>Changed</td><td></td><td></td><td></td></tr><tr><td>Form</td><td>Submit Attempt</td><td></td><td>wfu-trigger-submit </td><td></td></tr><tr><td>Form</td><td>Submit Success</td><td></td><td>wfu-trigger-submit-success </td><td></td></tr></tbody></table>

### Webflow Element Triggers&#x20;

| Trigger      | Detail |                         |
| ------------ | ------ | ----------------------- |
| Cart Opened  |        | wfu-trigger-open        |
| Cart Closed  |        | wfu-trigger-close       |
| Item Added   |        | wfu-trigger-add-item    |
| Item Removed |        | wfu-trigger-remove-item |

### Form Triggers&#x20;

| Trigger        | Detail |                    |
| -------------- | ------ | ------------------ |
| Submit Attempt |        | wfu-trigger-submit |
| Submit Success |        | wfu-submit-success |

### ECom Triggers&#x20;

| Trigger      | Detail |                         |
| ------------ | ------ | ----------------------- |
| Cart Opened  |        | wfu-trigger-open        |
| Cart Closed  |        | wfu-trigger-close       |
| Item Added   |        | wfu-trigger-add-item    |
| Item Removed |        | wfu-trigger-remove-item |

## Page-Level Triggers&#x20;

### User Activity&#x20;

| Trigger            | Detail              | Notes                     |
| ------------------ | ------------------- | ------------------------- |
| Page scrolled to % | % amount to trigger | wfu-trigger-scrollpercent |
| Exit intent        |                     | wfu-trigger-exitintent    |
| Rageclick          |                     | wfu-trigger-rageclick     |
| Inactivity         | Inactivity timer    | wfu-trigger-inactivity    |
| Activity           |                     | wfu-trigger-activity      |

### Breakpoint Triggers&#x20;

| Trigger            | Detail        | Notes                          |
| ------------------ | ------------- | ------------------------------ |
| Breakpoint Left    | Breakpoint(s) | wfu-trigger-breakpoint-left    |
| Breakpoint Entered | Breakpoint(s) | wfu-trigger-breakpoint-entered |

### System Triggers&#x20;

| Trigger                 | Detail                                             | Notes             |
| ----------------------- | -------------------------------------------------- | ----------------- |
| Timer                   | Timer duration                                     | wfu-trigger-timer |
| On Load                 |                                                    |                   |
| Reactive Trigger Truthy | Formula evals to true ( and was previously false ) |                   |
| Reactive Trigger Falsy  | Formula evals to false ( and was previously true ) |                   |

### SA5 Modal Triggers&#x20;

Opened&#x20;

Closed&#x20;

{% hint style="info" %}
Triggers from modal invocation should be placed here.&#x20;
{% endhint %}

## Interaction Triggers&#x20;

Refers to a category of triggers that are invoked by an interaction.&#x20;

Since IX2 does not include support for mouse / keyboard events&#x20;

Make these easier to find&#x20;

```html
<script type="application/sa5+json" handler="ConversionEvent">
{
  "@context": "https://attr.sygnal.com",
  "@type": "ConversionEvent",
  "@version": "0.1",
  "url": "https://conversion-tracker-url.com", 
  "transactionIdType": "query", 
  "transactionId": "transactionId",
  "type": "contact",
  "item": ""  
}
</script>
```

? change @type to @handler   &#x20;


# Click Triggers

Invoke an Event by clicking any element

## Use Cases&#x20;

* Click a button to select a specific tab &#x20;

## Usage Notes&#x20;

### `sa-trigger-click` = ( *event name* )&#x20;

Place on any element that should be invoke the specified event when it is clicked. &#x20;

Assign whatever event name you like, depending on your trigger-event-action setup.&#x20;

## Example &#x20;

This is a simple *mirror-click* setup.  Clicking the link clicks the corresponding button.  they are connected by virtue of the Event name.&#x20;

Trigger;

```html
<a sa-trigger-click="my-event">Click me</a>
```

Action;

```html
<button sa-action-click="my-event">Hidden button</button>
```

## Future&#x20;

### Link Triggers&#x20;

Place on a container, esp. a rich text element.&#x20;

#### `sa-trigger-click:context` = `child-links`&#x20;

{% hint style="success" %}
This changes the trigger setup so that the parent element is ignored, and only links within it are triggered.&#x20;
{% endhint %}

Generally you would also also specify a namespace;&#x20;

#### `sa-trigger-click:ns` = ( *namespace* )&#x20;

### Link Triggers | Example&#x20;

{% code overflow="wrap" %}

```html
<div class="w-richtext" sa-trigger-click="*" sa-trigger-click:context="child-links" sa-trigger-click:ns="myns">
  <p>Some text and <a href="##mylink">a link</a></p> 
</div>
```

{% endcode %}

In this example;

* A Webflow Richtext element is being used&#x20;
* The `:context` attribute is used and specifies `child-links`&#x20;
* The `:ns` attribute is used and specifies `myns` as the namespace name&#x20;
* Within it is a link, with an href of `##mylink`&#x20;

In this setup;&#x20;

* All links beginning with `##` are treated as SA5 Triggers.&#x20;
* The event name they trigger follows the `##`, in this example the event name is `mylink`&#x20;
* They are handled as click triggers due to the attribute on the rich text element, and the :context setting&#x20;

Inside,&#x20;

`##link2`&#x20;

This is a click trigger for that event&#x20;

In this example;&#x20;

The event fired on link click will be `ns1.link2` &#x20;

Consider sa-trigger-click:\*   as the indicator for children&#x20;


# Scroll Into View Trigger

Invoke the specified event when the element scrolls into view

{% hint style="info" %}
This Trigger fires only once per page load.&#x20;
{% endhint %}

{% hint style="warning" %}
Scroll trigger interactions currently don’t work on iOS devices (iPhone, iPad, iPod) due to Apple’s security restrictions on running scripts during browser scrolling. This limitation is documented in the Apple developer library.&#x20;
{% endhint %}

## Use Cases&#x20;

* Trigger a modal   &#x20;

## Usage Notes&#x20;

### `sa-trigger-scrollintoview` = ( *event name* )&#x20;

Place on any element that should be invoke the specified event when it is scrolled into view. &#x20;

Assign whatever event name you like, depending on your trigger-event-action setup.&#x20;

```html
<div sa-trigger-scrollintoview="my-event"></div>
```

## Future

Allow repeat triggering&#x20;

* Once
* Unlimited, but element must leave page to reset &#x20;


# Hover Trigger

Invoke the specified event when the element scrolls into view

## Usage Notes&#x20;

### `sa-trigger-mouseenter` = ( *event name* )&#x20;

Place on any element that should invoke the specified event when the mouse enters. &#x20;

Assign whatever event name you like, depending on your trigger-event-action setup.&#x20;

```html
<div sa-trigger-mouseenter="my-event"></div>
```

### `sa-trigger-mouseleave` = ( *event name* )&#x20;

Place on any element that should invoke the specified event when the mouse leaves. &#x20;

Assign whatever event name you like, depending on your trigger-event-action setup.&#x20;

```html
<div sa-trigger-mouseleave="my-event"></div>
```

## Future&#x20;

Allow restricted triggering&#x20;

* Once
* Unlimited, but element must leave page to reset &#x20;

Do this at the event level?&#x20;


# Timer Trigger

Defines a timer as a trigger.

Create a timer as a trigger.  You can specify both the time to the first trigger event, and an optional recurring trigger event as well.&#x20;

```html
<script type="application/sa+json" handler="trigger.timer" event="timer1">
{
  "@context": "https://attr.sygnal.com",
  "@version": "0.1",
  "timer": "60", 
  "timerRepeat": "120",
}
</script>
```

## Usage Notes

### Script Configuration

These are the settings of the script element.&#x20;

### type  = application/sa+json

**Required.**  Identifies the script block as an SA5 configuration block, formatted as JSON.&#x20;

### handler = trigger.timer

**Required.**  Identifies that this is a timer trigger.&#x20;

### event  = ( *event name* )

**Required.**  The event to trigger.&#x20;

### JSON Configuration&#x20;

### timer = ( *seconds* )

**Required.**  Indicates the number of seconds before the timer will fire.&#x20;

### timerRepeat = ( *seconds* )&#x20;

**Optional.**  If specified, the timer will repeat after the first firing every timerRepeat

| timer | timerRepeat   |                                                                                             |
| ----- | ------------- | ------------------------------------------------------------------------------------------- |
| 60    | Not specified | Timer fires once, 60 seconds after page load.                                               |
| 0     | 120           | Timer fires immediately after page load, and then fires again every 120 seconds afterwards. |
|       |               |                                                                                             |


# Form Triggers 🧪

## Checkbox Trigger&#x20;

sa-trigger-form-checkbox-on = ( event name )&#x20;

sa-trigger-form-checkbox-off = ( event name ) &#x20;

## Button Trigger&#x20;

sa-trigger-form-button-click = ( event name )&#x20;

## Show / Hide Fieldset&#x20;

Usage notes;&#x20;

Must be a custom element `<fieldset>` wrapping the elements&#x20;

sa-action-form-fieldset-show = ( event name )&#x20;

sa-action-form-fieldset-hide = ( event name )&#x20;

sa-action-form-fieldset-toggle = ( event name )&#x20;

Use cases;

* Show / hide a group of inputs&#x20;
* Enable / disable validation automatically&#x20;

Technical notes;&#x20;

* Uses disabled on the fieldset directly, to novalidate all of the fields within so that the form can be submitted with the fields hidden &#x20;


# Form Submit Trigger 🧪

Trigger on a successful form submission


# Exit Intent Trigger 🧪

```html
<script type="application/sa+json" handler="trigger.exit-intent" event="exitintent1">
{
  "@context": "https://attr.sygnal.com",
  "@version": "0.1" 
}
</script>
```

## Design Notes&#x20;

### **Common Exit Intent Detection Methods**

1. **Mouse Movement to Top of Viewport (Most Common)**
   * Detects when the user moves their cursor **near the top of the browser window** (towards the close tab button).
   * Ideal for **desktop users**.
   * Not effective on mobile.
2. **Mouse Speed and Direction**
   * Detects **fast cursor movement** toward the browser edges.
   * Useful for **more aggressive detection**.
3. **Lost Focus on Window**
   * Fires when the user **switches tabs or minimizes the window**.
   * Good for detecting intent but can **fire too often** in normal browsing.
4. **Page Visibility API**
   * Detects when the page goes into the background (`visibilitychange` event).
   * Best for **tracking when users switch tabs**.
5. **Back Button Navigation**
   * Detects when users press the **back button** before leaving.
   * Works well for preventing abandonment.
6. **Session Timeout**
   * Detects when the user is inactive for a set time.
   * Useful for **re-engaging users**.


# ECommerce Triggers 🧪

## Types

### Cart Item Count Changes &#x20;

Considering;

* Cart open
* Cart closed
* Item added
* Item removed&#x20;

<https://discourse.webflow.com/t/close-cart-go-back-to-top-of-the-page/215008/6>


# Inactivity Trigger 🧪

Activity / Inactivity&#x20;

Inactivity-timer based triggers&#x20;


# Page Load Trigger 🧪

Fires when page has loaded

* DOM loaded
* Webflow\.js loaded
* jQuery loaded&#x20;


# Url Hash Trigger 🧪

Trigger on URL hash

## Goals

Fires when page has loaded and it has the specified #hash&#x20;

Fires when change&#x20;

## Use Cases

* Hash specified on URL;
  * Select specific tab
  * Scroll to section and select specific tab&#x20;
  * Display modal &#x20;
* Hash changed after load, e.g. menu hash nav&#x20;

## Configuration &#x20;

This Trigger uses an SA5 configuration block.&#x20;

```html
<script type="application/sa+json" handler="trigger.hash" event="timer1">
{
  "@context": "https://attr.sygnal.com",
  "@version": "0.1",
  "hash": "myhash", 
  "triggerOnLoad": "yes", 
  "triggerOnChange": "yes",
  "triggerTimes": "1" 
}
</script> 
```

### hash = ( hash to trigger on )

### triggerOnLoad = ( truthy )

### triggerOnChange ( truthy )

### triggerTimes = ( trigger # of times )&#x20;

**Optional.**  Default = 1.&#x20;

On a page load, indicates the number of times this trigger can fire.  &#x20;

## Future

trigger on hash patterns?&#x20;


# Gated Trigger 🧪

* Special trigger type, which returns a success/fail
* IF success, a second trigger is then fired&#x20;

## Use Cases&#x20;

Trigger executes


# Link Triggers 🧪

Trigger Your Click Interactions with a Link click

**Webflow's interactions are powerful, but they are not easily triggered from JavaScript.**&#x20;

This library is designed to make interactions easier to trigger and easier to associate with specific CMS items, to open up a range of use cases.&#x20;

## Use Cases

* Interactions-based CMS-driven pop-ups triggered;&#x20;
  * from within within a slider or other overflow: hidden container, where the modal would be cut off if it were placed inside of the slider with the trigger button.&#x20;
  * from a script-generated map pin&#x20;
  * from simple user-generated links within a blog or article text
  * from script-generated text links such as automatically tagged glossary terms with a definition pop-up&#x20;
* Other complex CMS-based modal + trigger setups&#x20;

Although we primarily use this as an interaction trigger, you can think of this more as a CMS-friendly proxy-click capability. This means that you can similarly trigger a tab change, a form submission, a slider advance, or anything else that can be clicked.&#x20;

## How Does it Work?&#x20;

The basic idea is a very simple proxy-click.&#x20;

* You build your normal interactions setup, such as a set of CMS-driven modals ( rightmost collection list here )&#x20;
* You setup a hidden trigger button for each modal, that targets the interaction relative relative to the button, e.g. parent-only or sibling-only.  This ensures you only display the desired popup.&#x20;
* That trigger button is then tagged with some custom attributes so that the SA5 scripts can identify it easily.&#x20;
* You generate a second collection list ( shown leftmost here ), which generates the buttons you want users to see.  These proxy buttons are also tagged with attributes to match them up to the hidden buttons you want to trigger.&#x20;
  * This can be used with Finsweet CMS Slider to generate slides
  * They can be rendered as map pins&#x20;
  * Or any other setup you want
* SA5's Trigger lib identifies both the trigger buttons and the proxy buttons, and establishes click handlers. Any click on the proxy button will issue a JavaScript click on the interactions trigger button, which will trigger whatever interaction you've setup- popup, animation, etc.&#x20;

<img src="/files/G8LGINO0xwG2FvIBXDe2" alt="" class="gitbook-drawing">

## Proxy Trigger Setups

Once you've setup your interactions and click-triggers as described below, you can trigger your named interactions in one of two ways-

### Attribute-Based Trigger&#x20;

You can assign a custom attribute to the button or element you want to use as a trigger, with a value identifying the specific interaction you want to fire.&#x20;

### Link-Based Trigger&#x20;

Or in a link element, we support a special link format.

`##my-interaction-id`

Link anything and set the URL to a double-hash `##` link with the name of the interaction you want to trigger.&#x20;

For example,&#x20;

* `##cta1` can launch your CTA #1 pop-up
* `##enroll` can launch a pop-up form for enrollment&#x20;
* `##llm`, `##ai`, and `##chatgpt` can all be connected to CMS-driven pop-ups to drive a glossary of terms&#x20;
* `##p0543` can display a modal with product or product-category information from your ECommerce store

Because these are set as the link URLs, the test displayed to the user can be anything you like.

## Usage Notes

### STEP 1 - Add the Library <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

First, **add the library** as detailed in [Quick Start](/sa5-trigger/quick-start).

### STEP 2 - Setup your data-binding attributes ( Basic Setup ) <a href="#step-2---setup-your-zap-and-link-your-webflow-form" id="step-2---setup-your-zap-and-link-your-webflow-form"></a>

* Create your interaction to do whatever you want
* Create a button element, and set it to trigger your interaction on click
* Assign the `[wfu-ix-id]` attribute to that button element, and give it a unique ID
* Now on any link or button anywhere in your page, you can assign a `[wfu-ix-trigger]` attribute to that element. Give it that same ID to proxy-trigger your interaction.&#x20;
* Or, in any link on your page, assign a URL with `##` with the ID you've assigned to the interaction you want

Now, clicking any of those links will trigger your interactions.&#x20;

### OR/ STEP 2 - Setup your data-binding attributes ( Advanced Setup ) <a href="#step-2---setup-your-zap-and-link-your-webflow-form" id="step-2---setup-your-zap-and-link-your-webflow-form"></a>

When you are want CMS-sourced modals such as product pop-ups or glossary pop-ups, here are some tips;

* Design your modal first
* Place it inside of your CMS Collection List
* Place your triggering button as an immediate sibling of your modal's parent element
* In your interaction, trigger the modal *by class*, targeted to *sibling elements only*. That will give much more reliable results&#x20;
* Assign your `[wfu-ix-id]` custom attribute to that button, and set the value using Webflow's CMS data-binding feature, to the `slug` of the collection item

Now, anywhere in your page, you can link to `##slug` to trigger that item.&#x20;

## Specific Setup Examples&#x20;

### You want to click an element, and have a popup appear

* Create your interaction-based popup so that it is triggered by a button. The button can be hidden, but it is needed to invoke the interaction.&#x20;
* Add the `wfu-ix-id` custom attribute to that button&#x20;
* To the triggering element, add the `wfu-ix-trigger` attribute  &#x20;

### You want to click a text link, like a dictionary definition, product, or a staff member's name, and have a link-specific pop-up appear&#x20;

* Create your interaction-based popup so that it is triggered by a button. The button can be hidden, but it is needed to invoke the interaction.&#x20;
* Add the `wfu-ix-id` custom attribute to that button&#x20;
* In your text, create any link you want, and set the link href so that it begins with `##` and then the ID you've assigned to the your modal button&#x20;

You can generate the modals and their triggering buttons from the CMS, and use the slug as the ID.  This makes it easy for you to invoke CMS-generated popups.  &#x20;

## Future <a href="#getting-started-locode" id="getting-started-locode"></a>

### You want an interaction to trigger automatically after N seconds

FUTURE.&#x20;


# Reactiv Triggers 🧪

## Types

### Value Changes&#x20;

* cart items&#x20;

### Value Eval True&#x20;

An evaluation based on a reactive value evaluates to true although it previously evaluated to false.&#x20;

* e.g. cart items > 20&#x20;

### Value Eval False&#x20;

An evaluation based on a reactive value evaluates to false although it previously evaluated to true.&#x20;

e.g.

* cart items > 20&#x20;


# Interaction Trigger 🧪

Trigger an SA5 Event from an Interaction

In Webflow's current Interactions ( IX2 ), the button would then be set as a click trigger to trigger the interaction.&#x20;

Use cases;&#x20;

* Start a video after an interaction has completed
* Display a pop-up in the middle of an interaction sequence&#x20;

Technical notes

### wfu-ix-trigger-script = ( ... ) &#x20;

## Actions

Triggers;

* User action
  * Click
  * Scroll
  * Hover
* Variable change ( reactive );&#x20;
  * e.g. x > 1000
* Timer
* Interaction step&#x20;
* Form submit &#x20;
* Alpine.js triggers  &#x20;

Actions;&#x20;

* Click ( other elements )
  * Interaction&#x20;
* Scroll ( other elements )
* Code execution
* Class add/remove&#x20;
* Form submit

Mechanic;&#x20;

* Define triggers
  * e.g. attribute wfu-click = ( name ) &#x20;
*

<https://codepen.io/memetican/pen/pvzXrKQ/30243a91b6fb117a92b0e5b3b5b69503?editors=1111>


# Events

## Simple v. Complex Events

Most events in SA5 Events are defined by attributes directly on the affected element.  However, there is a potential for "attribute glut" and complexity here in two situations;&#x20;

* If a single element has many possible Events triggering many possible Actions.&#x20;
  * e.g. A tabstrip where 10 buttons might activate 10 different tabstrip actions.&#x20;
* If an Event fires many different Actions on many different elements to a point where it's easy to lose track of the attributes in use.&#x20;
  * e.g. A button Trigger, whose Action triggers 100 different Events.&#x20;

In both cases, we can potentially centralize the admin with an Event configuration block.

This would look something like;

```
<script type="sa5-event+json" name="event1">
{
  "actions": [
    {
      "type": "scrollToPercent",
      "value": "50%"
    },
    {
      "type": "selectTabNum",
      "target": "tab1",
      "value": "2" 
    }
  ]
}
</script> 

```

There may be a case for supportin&#x20;

## Frequency Gating

Unlimited ( default )&#x20;

Once&#x20;

N-Times&#x20;

Max frequency ( no more than once every 500ms )    &#x20;

### Event&#x20;

An **SA5 Event** is effectively a messaging pipeline, which has a name.  When an event fires, it triggers a series of *SA5 Actions*. &#x20;

* Multiple Triggers can invoke the same Event.&#x20;
* An Event can perform several Actions&#x20;

{% hint style="info" %}
We're considering the ability for a single Trigger to invoke multiple Events. This may be limited to certain trigger types.&#x20;
{% endhint %}

An SA5 Event is a named construction, which connects Triggers and Actions.&#x20;

<img src="/files/AVre6hJqB5ccRTOFtTwp" alt="" class="gitbook-drawing">

Events are tracked by SA5 and always have a name, so that they can be invoked and logged. &#x20;

Element Click Trigger

wfu-trigger-click="event1"

wfu-action-click="event1"&#x20;

Sa5.Events.Invoke("event1");&#x20;

Currently, all events must be named.&#x20;

## Technical Notes & Consideration&#x20;

### Preventing Loops&#x20;

Simple - threading. limit the number of automated events that can chain. &#x20;

Event stack?  Breadcrumbs?  Detect loops? &#x20;


# Namespacing

Namespace your events for advanced CMS-driven use cases.

One of the common scenarios in which SA5 events are used are to support CMS-driven modals and pop-ups.&#x20;

Example use cases;

* You have a CMS-driven directory of employees. Clicking any employee's photos should display a pop-up containing that employee's info, also from the CMS.&#x20;
* You have a map, with map pins, populated from the CMS.  Clicking a map pin should pop up information about the location, also from the CMS.&#x20;

## How Namespacing Works&#x20;

In these situations, we need each individual trigger ( button ) and action ( modal display ) to be directly connected, but we also need them to be separated from other events.&#x20;

Namespacing segregates these safely.&#x20;

## Usage Notes  &#x20;

The general usage pattern is;&#x20;

* Setup your triggers and actions as you usually would.&#x20;
* For the event name, data-bind the attribute value to the CMS item slug&#x20;

e.g. `sa-trigger-click` = ( *CMS item slug* )&#x20;

* Add a unique namespace to both the trigger and the action&#x20;

e.g. `sa-trigger-click:ns` = `employee` &#x20;

## Usage Notes Patterns &#x20;

### `sa-trigger-*:ns` = ( *namespace* )&#x20;

All triggers support the `:ns` namespace suffix.&#x20;

### `sa-action-*:ns` = ( *namespace* )&#x20;

All actions support the `:ns` namespace suffix.&#x20;


# Event Data

In SA5's pub/sub model, certain Actions benefit from data.

* The class adder needs to know what class to add, remove or toggle&#x20;
* Video seeking needs a position to seek to&#x20;

These are specified in the form of Action-specific attributes, such as;

```
sa-event-action-video-seek:pos = 80%
```

{% hint style="success" %}
SA5 convention is to use the specific action attribute name, followed by a colon and the data item name. This makes it mostly-unambiguous and allows us the freedom to have several data items when necessary.&#x20;
{% endhint %}

## Where this is placed&#x20;

In most cases it makes sense to put this on the Action element directly so that it is centrally controlled no matter what trigger was performed.&#x20;

However in some cases, it makes more sense to place this on the Triggering element;

* A button might choose to seek to the start of the video.  A different button might seek to 20 sec into that same video&#x20;

SA5 Events has this flexibility inherently.

In our example, this data attribute;&#x20;

```
sa-event-action-video-seek:pos = 80%
```

It can be placed on the Event, alongside the `sa-event-action-video-seek` attribute.&#x20;

Or it can be placed on the Triggering element.&#x20;


# Link-Based Triggers 🧪

Trigger SA5 Events from a link click.

Certain triggers such as click&#x20;

## Syntax Expansion

Likely we'll support named or "slotted" formats;&#x20;

* `##` - Generic trigger prefix
  * e.g. `##glossaryterm`
* `#1#` - Typed trigger prefix
  * e.g. `#1#abc`

Essentially;

* Identify a trigger-capable area ( can be page-wide )
* Find links that begin with `#`
* and contain
  * May be able to regex match `#..#..` in selector.&#x20;

{% code overflow="wrap" %}

```javascript
const elements = [...document.querySelectorAll('a[href^="#"][href*="#"]:not([href="#"])')].filter(el => {
    const match = el.getAttribute('href').match(/^#(\w*)#(\w+)$/);
    return match !== null;
});
```

{% endcode %}

Extract the name, trigger the name.&#x20;

On page load, build a list of trigger targets, e.g. IX buttons

Typed trigger wiring process;

* Match to known targets, by name&#x20;

## Registered Type Handlers

* ix
* gista ( register and define these as typed trigger handlers in the config code )&#x20;

### Generic trigger

* Create attribute first&#x20;
  * wfu-trigger-type
  * wfu-trigger-id&#x20;
* wfu-trigger-target-type
* wfu-trigger-target-id

## Other Trigger Types

## Future&#x20;

### What is a Trigger?

Currently SA5 supports the click trigger type only.  In this context a trigger is an element such as a link, button, or DIV that is clicked.&#x20;

In SA5 terms, a trigger may have these properties;

* Handler. Identifies how the trigger will be handled. Determined as follows;
  * If the trigger element has the attribute `wfu-trigger-handler` - identifies the handler for the trigger.&#x20;
  * If unspecified, and the trigger is in a group, that handler is used instead.&#x20;
  * If there is no group-specified handler, then the trigger is "unhandled," and defaults to a click handler.&#x20;
* ID. Uniquely identifies the trigger, or more specifically the triggering action you want performed. In the case of a click trigger, this identifies the click target.&#x20;
  * `wfu-trigger-id` - the unique ID of the trigger&#x20;
* Group (optional). If the trigger is in a named group, that group name is applied to the trigger. It acts as a namespace.&#x20;
  * wfu-trigger-group
* Content. A trigger element may also contain content;
  * Plain-text content ( innerText )
  * HTML content ( innerHTML )

### What are Trigger Handlers?

Trigger handlers identify how a particular trigger is handled. For example-&#x20;

* Click a matching target link.
* Prompt an AI chatbot
* Center a pin in Google maps
* Start a WhatsApp message
* Invoke custom code

The trigger's *handler* is determined as follows;

* If the trigger itself has a `wfu-trigger-handler`, that handler is used
* If not, we check for a trigger group, and a `wfu-trigger-group-handler`.&#x20;
* If not, the trigger is considered *unhandled*. It defaults to using the **click handler**, and can be captured in custom code. &#x20;

### What is a Trigger Group?

A trigger group is defined as the nearest ancestor ( containing ) element of a trigger which has the custom attribute of `wfu-trigger-group`.&#x20;

A group serves two purposes.&#x20;

* It can be named, as in `wfu-trigger-group` = (name), in which case that name becomes a namespace for the triggers. Some handlers will use this as a scoping mechanism, so that only targets also matching that group will be triggered
* Default trigger handlers. A group can additionally define a `wfu-trigger-group-handler` which acts as the handler for any *unhandled* triggers within.&#x20;

### What are Trigger Targets?&#x20;

Some trigger handlers such as the **click handler** can utilize a target element that you specify.&#x20;

These are identified by wfu-trigger-target = ID. OPTIONALLY They can be namespaced, by wfu-trigger-target-group, which must match.&#x20;

##

### Configure the Trigger Library ( optional )

Register handlers, such as Gista.&#x20;

{% code overflow="wrap" %}

```html
<script>
window.sa5 = window.sa5 || [];
window.sa5.push(['triggerConfig', 
  (config) => {
    config.handlers.add ('gista'); 
    return config;
  }]); 
</script> 
```

{% endcode %}

### Custom Code in the the Trigger Callback ( optional )

Custom-code to handle specific trigger events.&#x20;

{% code overflow="wrap" %}

```html
<script>
window.sa5 = window.sa5 || [];
window.sa5.push(['triggerEvent', 
  (event) => {
    console.log("TRIGGER FIRED", event); 
  }]); 
</script> 
```

{% endcode %}

### Setup

A **click trigger** can be placed on any element that can be clicked. It is governed by;

* `wfu-trigger-type` - the type of trigger, which functions as a namespace
* `wfu-trigger-handler` - identifies the handler for the trigger. If unspecified, and the trigger is in a group, that handler is used instead. If there is no group-specified handler, then the trigger is "unhandled," and will only fire events in code. &#x20;
* `wfu-trigger-id` - the unique ID of the trigger&#x20;
* It can also contain content

All click triggers MAY be in a trigger group. This would be the nearest containing element which has the attributes;

* `wfu-trigger-group` - an optional name
* `wfu-trigger-group-type` - an optional default type, which will be applied to untyped (generic) triggers

A **trigger target** can also be placed on any element that can be clicked. Typically this would be a button that is associated with an interaction. It is goverened by;

* wfu-trigger-target-type - type type of target, which functions as a namespace and must match the trigger
* wfu-trigger-target-id - unique ID of the target&#x20;

### Activation

When a trigger is activated, an event is raised in custom code, with-

* Trigger container name
* Trigger type
* Trigger ID

If trigger target elements are found which match that trigger, they will also be clicked.&#x20;

If a handler has been associated with the trigger type, it will be invoked, e.g. Gista.&#x20;

### How it Works

Pre-processing;

* Identifies relevant areas to process links in. These are referred to as `trigger-containers`.
  * Identified with a wfu-trigger-area custom attribute. It can optionally be given a unique name
  * Optionally, set the default type with wfu-trigger-default-type =&#x20;
* Resolves links in those areas, including the parent node&#x20;

### Triggers Invoking Multiple Different Events&#x20;

We're considering the ability for a *single* Trigger to invoke *multiple* Events.&#x20;

For example, `wfu-trigger-click` = `event1,event2,event3`&#x20;

If built, this may be limited to certain trigger types, owever at this point we have not identified key use cases where this adds substantial value.&#x20;

### Chained Events&#x20;

We're also considering the concept of a Trigger as an Action type, so that we can chain Trigger-Event-Action sequences.&#x20;

##

&#x20;


# Breakpoints 🧪

Only execute at certain breakpoints.

And/or specify at action or triggers&#x20;


# Persistent Events 🧪

Fire

State?&#x20;


# Gated Events 🧪

Some Events might have pre-requisites before they can fire.&#x20;

* User is logged in to MemberStack&#x20;
* User is in a specific User Group&#x20;
* User has accepted a Gating modal or something similar&#x20;

## Use Cases&#x20;

* Over 21 gating before a site can be accessed &#x20;
* Birthdate gating before a site can be accessed&#x20;
* Collect user data before a modal is shown&#x20;

## Technical Notes&#x20;

### Custom gates

Custom gates perform a special test every time&#x20;

### State gates&#x20;

Require a specific action be perfomed or test be passed once. Once the gate is opened, the open state is stored generally in localStorage.&#x20;

Once opened, it does not need to be re-opened&#x20;

Opening a gate can be done by an EventGateHandler in a number of ways;&#x20;

* Check to see if the user is&#x20;

When the gate is open, the event fires normally.&#x20;

When the gate is closed;&#x20;

* A modal is presented
  * The user satisfies the gating requirements, e.g. submit a form, click a button&#x20;
* The URL is followed, including target&#x20;
* The gate is opened&#x20;

## Gate State Management&#x20;

### Check Gate&#x20;

Determine if the gate is opened;&#x20;

* Default is to check localStorage&#x20;
* But can also be sessionStorage
* Or cookies&#x20;
* Can be any custom thing as well, e.g.&#x20;

### Open Gate

Opens the gate;&#x20;

* localStorage, sessionStorage, or cookies  &#x20;

### Close Gate&#x20;

Closes the gate;&#x20;

## Implementation Notes

Defined as a block&#x20;

```html
<script sa5-event="name">
{
   gate-type: modal
   modal-gate-type: view
   gate-state: localstorage 
}
</script> 
```

* EventGateHandlerBase&#x20;
  * EventGateModalHandler&#x20;
    * EventGateModalViewHandler&#x20;
    * EventGateModalButtonHandler&#x20;
    * EventGateModalFormHandler&#x20;
  * EventGateMemberstackHandler&#x20;
* EventGateStateHandlerBase&#x20;
  * EventGateStateLocalStorageHandler&#x20;
  * EventGateStateSessionStorageHandler&#x20;
  * EventGateStateCookieHandler&#x20;

EventGateLocalStorageHandlerBase&#x20;

EventGateLocalStorageHandlerBase

## Concept&#x20;

wfu-event-gate = function

Must return true&#x20;

Pass in Event, so it can be fired&#x20;

## Gate Types &#x20;

Defined as a block&#x20;

```
<script sa5-event="name">
{
   gate-type: modal
   modal-gate-type: view
   
}
</script> 
```

### Modal Gates&#x20;

1. An event fires&#x20;
2. Modal type gate is gate-checked&#x20;
3. If fails pass;&#x20;
   1. Display modal&#x20;
4. Success pass;&#x20;
   1. Fire event&#x20;

| Modal Gate Type | Test | Success | Fail |
| --------------- | ---- | ------- | ---- |
| view            |      |         |      |
| button          |      |         |      |
| form            |      |         |      |

### Eval Gate&#x20;

1\.


# Actions

An **SA5 Action** is an action that is be performed when an [SA5 Event](/overview/events/events) occurs.

Example Actions include;

* Simulate a click on an element&#x20;
* Navigate to another URL&#x20;
* Call a webhook&#x20;
* Submit a form&#x20;
* Switch a tab&#x20;
* etc.&#x20;

### Types of Actions&#x20;

Broadly, there are two types of SA5 Actions...&#x20;

* **Element Actions.**  An element-specific action such as clicking a button or submitting a form. These are generally configured directly on the target element, using custom attributes.&#x20;
* **Page Actions.** These are actions that occur in the context of the page, but are not tied to any specific element. We generally configure these using a special SA5 script action block.&#x20;

Modal Actions &#x20;

## Element Actions&#x20;

|                                                                     |   |   |
| ------------------------------------------------------------------- | - | - |
| [Tab Element Actions](/overview/events/actions/tab-element-actions) |   |   |
| Slider Element Actions                                              |   |   |
|                                                                     |   |   |

## Element-Level Actions

These actions are generally defined using custom attributes.&#x20;

| Action                                                      | Detail         | Applies to                  | Description                                          | Notes                                                                                              |
| ----------------------------------------------------------- | -------------- | --------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| <p>Click<br>wfu-action-click </p>                           |                | Any element                 | Fire a click event from script to trigger an element |                                                                                                    |
| [Interaction](/overview/events/actions/interaction-actions) |                |                             |                                                      | `wfu-action-click`, with the element also configured as a click trigger for a Webflow interaction. |
| <p>Script<br>wfu-action-script </p>                         |                | ? Special SA5 block element | Invoke a script, or function                         |                                                                                                    |
| <p>Function<br>wfu-action-function </p>                     |                |                             |                                                      | Invoke a function                                                                                  |
| Scroll Into View                                            |                | Any Element                 |                                                      | Offset?                                                                                            |
| Submit                                                      |                | Form                        |                                                      |                                                                                                    |
| Popup                                                       | Optional scope | SA5 Popup                   |                                                      |                                                                                                    |
| <p>Add Class<br>wfu-action-class-add</p>                    | Class name     |                             | Adds a class                                         |                                                                                                    |
| <p>Remove Class <br>wfu-action-class-remove</p>             | Class name     |                             | Removes a class                                      |                                                                                                    |
| <p>Toggle Class<br>wfu-action-class-toggle</p>              | Class name     |                             | Toggles the presence/absence of a class              |                                                                                                    |
| Hide / Show Elements                                        |                |                             |                                                      |                                                                                                    |

Mode

Hide/show certain elements

Class adder, etc&#x20;

Based on localStorage / cookie / sessionStorage &#x20;

### Video Element

Play

Pause

Seek   sec or %&#x20;

Fullscreen&#x20;

### Lottie & Rive Elements&#x20;

Play

Pause&#x20;

The underlying link approach has proven very useful. We want to expand the capabilities to handle other use cases;

* Trigger interactions ( via a button trigger )&#x20;
* Trigger chatbots
* Trigger Gista with a query&#x20;
* Trigger custom JS, JS-based modals, etc.&#x20;

And also possibly;&#x20;

* Trigger GTM datalayer and events &#x20;

## Page-Level Actions&#x20;

These actions are generally defined by an SA5 Configuration Block;&#x20;

| Action      |                             |   |
| ----------- | --------------------------- | - |
| Navigate    |                             |   |
| Script      | nvoke a script, or function |   |
| OnLoad      |                             |   |
| OnScroll%   |                             |   |
| Exit Intent |                             |   |
| Timer       |                             |   |

```
SA5 block 

```

## Event Detail &#x20;

In some cases, we want to pass detail;&#x20;

* Context or scope so that the event is more specific, e.g. invoke a specific pop-up from a CMS-driven collection list of popups.&#x20;
* Class names or other key data needed by the Action handler.&#x20;

Ideally we want to support multiple detail pieces in the future, and to allow for multiple non-conflicting Actions on the same element.&#x20;

To support this, I'm considering details as a suffix on the event, e.g.; &#x20;

| Suffix    | Notes                                                                                    |
| --------- | ---------------------------------------------------------------------------------------- |
| \*:class  | Might indicate a specific class name to apply in the case of class add / remove / toggle |
| \*:item   | Limit to a specific item                                                                 |
| \*:offset | Used to offset a scroll-to-element position                                              |

### Triggering an Event w/ Details&#x20;

Detail

```html
<div wfu-trigger-click="popup" wfu-trigger-click:item="potato"> ... </div> 
```

More verbose, but better for data-binding in a collection list.&#x20;

```html
<div wfu-trigger-class-add="event1" wfu-trigger-click:class="red-text"> ... </div> 
```

### Abbreviated Events w/ Details&#x20;

In some Triggers & Actions it may be useful to offer a simplified syntax for convenience when no custom attribute data-binding is needed;&#x20;

For example, this click trigger-&#x20;

```html
<div wfu-trigger-click="popup#potato"> ... </div> 
```

* Is triggered by a click
* Invokes the Event `popup` on elements where item is `potato`. &#x20;

Meanwhile this&#x20;

Or;&#x20;

```html
<div wfu-action-class-add="event1#foo"> ... </div> 
```

Finds all elements tagged with Triggers a click Action on all elements tagged with the popup event, where the item is potato. &#x20;

### Putting it Together&#x20;

Here's an example from SA5;&#x20;

Constructing event detail; &#x20;

```html
<div wfu-trigger-click="popup">
  <p>
    <a href="##potato"></a>    
  </p> 
</div> 

<div>
  <div> <!-- interactions popup -->  
    <button wfu-action-click="popup" wfu-action-click:item="potato">Invoke popup</button> 
    ...
  </div> 
</div>
```

Receiving event detail; &#x20;

## Trigger Source Gating&#x20;

{% hint style="info" %}
**UNDER CONSIDERATION.**  &#x20;
{% endhint %}

Certain Actions can be restricted to certain trigger sources.&#x20;

We may distinguish between different Trigger sources, such as user-invoked triggers, v. system-invoked triggers. &#x20;

| Trigger type | User sourced                | System sourced                                                                |
| ------------ | --------------------------- | ----------------------------------------------------------------------------- |
| Click        | Yes, user clicks on an item | Yes, script-generated click, or system-generated click from another SA5 Event |
| Timer        | No                          | Yes, always                                                                   |
| etc.         |                             |                                                                               |

This differentiation may be useful for;&#x20;

* Handling different action sources differently&#x20;
* Preventing loops by system-generated events&#x20;

System-triggers &#x20;

This could be specified by a prefix to the event name.

For example, `wfu-action-click` = `event-name` &#x20;

|                         | Invoked by                              |   |
| ----------------------- | --------------------------------------- | - |
| `event-name`            | Any trigger source, standard convention |   |
| $event-name             | Only                                    |   |
| \&event-name            |                                         |   |
| event-name#event-detail |                                         |   |

Defined a timer as a trigger&#x20;

```html
<script type="application/sa+json" handler="Action.Script">
{
  "@context": "https://attr.sygnal.com",
  "@type": "ScriptAction",
  "@version": "0.1",
  "timer": "60", 
  "timerRepeat": "120",
  "event": "my-event" 
}
</script>
```

## Technical Notes

### Categories of Actions&#x20;

We have...&#x20;

* **Element Actions.**  An element-specific action such as clicking a button of submitting a form. These are generally configured directly on the target element.&#x20;
* **Script Actions.**  An action that is defined by a special SA5 script action block.&#x20;

Timer example;&#x20;

```html
<script type="application/sa+json" handler="Action.Script" event="my-event">
{
  "@context": "https://attr.sygnal.com",
  "@type": "TimerAction", 
  "@version": "0.1",
  "timer": "60", 
  "timerRepeat": "120"
}
</script>
```

Navigation example;&#x20;

```html
<script type="application/sa+json" handler="Action.Script" event="event1">
{
  "@context": "https://attr.sygnal.com",
  "@type": "NavigationAction", 
  "@version": "0.1",
  "url": "https://something.com",
  "target": "_blank" 
}
</script>
```


# Click Actions

When an Event fires, click the specified element

## Use Cases&#x20;

* Place on a form submit button to trigger a submit attempt&#x20;
* Place on a tab to select that tab&#x20;
* Place on a slider next button to advance&#x20;
* Place on a hidden button, which is wired as a click trigger to a Webflow interaction.  This allows you to invoke an interaction anytime you like.&#x20;

## Usage Notes&#x20;

### `sa-action-click` = ( *event name* )&#x20;

Place on any element that should be clicked when the specified event is invoked. &#x20;

Assign whatever event name you like, depending on your trigger-event-action setup.&#x20;

## Example&#x20;

This is a simple *mirror-click* setup.  Clicking the link clicks the corresponding button.  they are connected by virtue of the Event name.&#x20;

Trigger;

```html
<a sa-trigger-click="my-event">Click me</a>
```

Action;

```html
<button sa-action-click="my-event">Hidden button</button>
```


# Calendly Action

## Dependencies

{% code overflow="wrap" %}

```html
<!-- Calendly embed script and styles -->
<link href="https://calendly.com/assets/external/widget.css" rel="stylesheet">
<script src="https://calendly.com/assets/external/widget.js" type="text/javascript"></script>
```

{% endcode %}

## Params&#x20;

## Execution&#x20;

```
Calendly.initPopupWidget({ url: 'https://calendly.com/YOUR_SCHEDULING_LINK' });
```

<https://help.calendly.com/hc/en-us/articles/223147027-Embed-options-overview>

<https://help.calendly.com/hc/en-us/articles/31619797446935-How-to-create-a-custom-button-to-open-Calendly#h_01JSJ4MA237XCCX8J2ZZJPDKS2>


# Alert Action

A simple alert message, primarily for testing

## Usage Notes&#x20;

This Action requires an SA5 config block

```html
<script type="application/sa+json" handler="action.alert" event="alert1">
{
  "@context": "https://attr.sygnal.com",
  "@version": "0.1",
  "message": "test" 
}
</script>
```

### `message` = ( *plain-text* )&#x20;

Plain-text message to display in the alert. &#x20;

If you want a multi-line message, you can use the `\n` linefeed sequence in your string.&#x20;

## Future&#x20;


# Visibility Actions

Show, hide, or toggle the visibility of an element.

## Goals

* Show a hidden element
* Hide a visible element
* Toggle the visibility of an element &#x20;

## Action Attributes&#x20;

{% hint style="danger" %}
UNDER SPECIFICATION. &#x20;
{% endhint %}

e.g. `sa-action-display-show` = ( *event name* )  &#x20;

|                            |                                   |                            |
| -------------------------- | --------------------------------- | -------------------------- |
| `sa-action-display-hide`   | Hide the item                     | Adds style=display: none;  |
| `sa-action-display-show`   | Show the item                     | Adds style=display: block; |
| `sa-action-display-toggle` | Toggles the item visibility state |                            |

{% hint style="warning" %}
Works by using the `display` style property. Do not use it on elements that are targeted by Webflow interactions which affect the `display` style property, as it will conflict. In that situation, wrapping your elements in a DIV and using the SA5 attributes on that DIV is generally the recommended approach.&#x20;
{% endhint %}

## Data

### `sa-action-display:mode` = ( *mode* )&#x20;

**Optional.** One of... &#x20;

* `display` ( default )&#x20;
* `visibility`&#x20;
* `opacity`&#x20;

### `sa-action-display:display` = ( *display mode* )&#x20;

**Optional.** Any valid [CSS display mode](https://developer.mozilla.org/en-US/docs/Web/CSS/display)...

* `block` ( default )&#x20;
* `flex`&#x20;
* `grid`&#x20;
* `inline-block`&#x20;
* *etc.*&#x20;

{% hint style="success" %}
Used with a mode of display.&#x20;

By default, displaying a hidden element sets the display to block.  You can override this behavior here, which will be used both by sa-action-display-show and sa-action-display-toggle.&#x20;
{% endhint %}

### sa-action-display:opacity = ( min opacity )

Optional. Defaults to 0% ( fully hidden )&#x20;

## Future

* Hide and show&#x20;
  * Display
  * Visibility&#x20;
  * Opacity
* Transitions like fade-in / fade-out ?&#x20;
* ~~Separate visibility?~~&#x20;
  * Or just rely on class adder&#x20;


# Scroll Into View Action

Invoke the specified event when the element scrolls into view

{% hint style="warning" %}
Scroll trigger interactions currently don’t work on iOS devices (iPhone, iPad, iPod) due to Apple’s security restrictions on running scripts during browser scrolling. This limitation is documented in the Apple developer library.&#x20;
{% endhint %}

## Use Cases&#x20;

* Trigger a modal   &#x20;

## Usage Notes&#x20;

### `sa-action-scrollintoview` = ( *event name* )&#x20;

Place on any element that should be invoke the specified event when it is scrolled into view. &#x20;

Assign whatever event name you like, depending on your trigger-event-action setup.&#x20;

```html
<div sa-trigger-scrollintoview="my-event"></div>
```

## Future&#x20;

<https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView>&#x20;

sa-action-scrollintoview:behavior &#x20;

* smooth ( default )&#x20;

sa-action-scrollintoview:block

* center ( default )&#x20;

sa-action-scrollintoview:inline&#x20;

* nearest ( default )&#x20;


# Video Actions 🧪

## Goals&#x20;

* Work with all video types
  * HTML Embed `<video>` element&#x20;
  * Webflow Video element ( aka. Embedly )&#x20;
  * Webflow YouTube element&#x20;
  * Webflow Background Video element&#x20;
  * HTML Embed Vidzflow video&#x20;
* Auto-Detect the Video type&#x20;
* Support Wrapper-based attributes so that the video type can be changed later with no change to the SA5 Events implementation&#x20;

Wistia?&#x20;

Mobile tests-

```
<iframe src="your-video-url" playsinline></iframe>

```

Technical Notes

User Interaction Requirements&#x20;

```
let userInteracted = false;

document.addEventListener("click", () => userInteracted = true);
document.addEventListener("keydown", () => userInteracted = true);
document.addEventListener("touchstart", () => userInteracted = true);

// Check later
if (userInteracted) {
    console.log("User has interacted.");
}

```

```
const testVideo = document.createElement("video");
testVideo.muted = true;
testVideo.play().then(() => {
    console.log("Autoplay is allowed. User may have interacted.");
}).catch(() => {
    console.log("Autoplay is blocked. No interaction yet.");
});

```

```
if (navigator.userActivation?.isActive) {
    player.unmute();
    player.play();
} else {
    player.mute();
    player.play();
}

```

Embed.ly&#x20;

<https://www.npmjs.com/package/player.js>

Vimeo

<https://github.com/vimeo/player.js>

## Usage Notes&#x20;

### Play Video

#### `sa-action-video-play` = ( event name )&#x20;

Will begin playing the current video when this event is fired.&#x20;

### Pause Video&#x20;

**`sa-action-video-pause` = (&#x20;*****event name*****&#x20;)**&#x20;

### Seek Video&#x20;

### `sa-action-video-seek` = ( event name )&#x20;

Seeks the current video when this event is fired.&#x20;

Restart would be Seek 0.&#x20;

Mute Video&#x20;

Unmute Video&#x20;

Pauses the current video when this event is fired.&#x20;

sa-action-video-restart&#x20;

### sa-action-video-seek:pos = ( position to seek )&#x20;

Position to seek&#x20;

Defaults to 0 ( beginning of video )

Can be;

* Numeric - specifies seconds from start e.g. 10&#x20;
* Percentage - e.g. 80%&#x20;

{% hint style="info" %}
Can be placed on the action element, directly on the the video, OR it can be placed&#x20;
{% endhint %}

sa-action-video-toggle&#x20;

sa-action-video-mute&#x20;

sa-action-video-unmute&#x20;

## Technical Notes&#x20;

Wrapped video element

Convenient for swapping out video type later&#x20;

HTML embedded

```
div.w-embed 
video 
```

Custom element

```
video  
```

div.w-embed-youtubevideo&#x20;

YouTube Video

```
div.w-embed-youtubevideo 
> iframe 
```

General Video

```
div.w-video.w-embed 
> iframe.embedly-embed 
```

Background Video&#x20;

```
div.w-background-video.w-background-video-atom 
> video   = 
```

Vidzflow &#x20;

```
div[data-video-id] 
iframe[src https://app.vidzflow.com/ ] 
```

## Future&#x20;

Autoplay

Muted

Loop

Click to play

Start playing when in view&#x20;

Play inline&#x20;

Overlay&#x20;

Used as a background video&#x20;

Play from start when fullscreen&#x20;

CTA&#x20;


# Embedly Problems

<https://developer.chrome.com/blog/autoplay#iframe_delegation>

<https://github.com/embedly/player.js/issues/99>&#x20;

## Button-Trigger w/ Audio&#x20;

### Direct YouTube Test&#x20;

<https://x-video.design.webflow.com/>

<https://x-video.webflow.io/>


# Audio Action 🧪


# Class Adder Actions 🧪

Add, remove, or toggle a specific class on an element.

{% hint style="danger" %}
**UNDER SPECIFICATION**&#x20;

{% endhint %}

Problems;

Ideally we want to be able to attach multiple event-actions to a single element. &#x20;

sa-action-class-data:test&#x20;

## Usage Notes&#x20;

Add the appropriate attribute to add, remove, or toggle classes.  Assign it to the Event name that will invoke this Action.&#x20;

e.g. `sa-action-class-add` = ( *event name* )&#x20;

Separately, define the class that is being manipulated;&#x20;

e.g. `sa-action-class-data` = ( *class name* ) &#x20;

{% hint style="success" %}
See [Data](#data) for more details on the class name.&#x20;
{% endhint %}

### Attributes

These are the supported Action attributes;&#x20;

| Attribute                |                                  |                                                |
| ------------------------ | -------------------------------- | ---------------------------------------------- |
| `sa-action-class-add`    | Add a class to the element       | See [Data](#data) for how to specify the class |
| `sa-action-class-remove` | Remove a class from the element  | See [Data](#data) for how to specify the class |
| `sa-action-class-toggle` | Toggles the class on the element | See [Data](#data) for how to specify the class |

## Data

### `sa-action-class-data` = ( *class name* )  &#x20;

Specify the class name that will be added, removed, or toggled.&#x20;

## Future &#x20;

### Multiple Classes

{% hint style="success" %}
It may be possible to specify multiple classes, space-delimited, e.g. `red highlight`. &#x20;

In this case we would add or remove both classes.&#x20;
{% endhint %}

{% hint style="warning" %}
Toggling here could be ambiguous, e.g. if the element has `red` but not `highlight` than what state is it in?  In this scenario, we would likely consider it "off", and the initial toggle action would turn it "on" by ensuring both classes are added.&#x20;
{% endhint %}


# Tab Element Actions 🧪

Select Tab

First Tab

Last Tab

Next Tab

Prev Tab

( force )&#x20;


# Interaction Actions 🧪

Invoke a Webflow Interaction as an SA5 Action

**Webflow's interactions are powerful, but they are not easily triggered from JavaScript.** &#x20;

`lick`, with the element also configured as a click trigger for a Webflow interaction.&#x20;

## Example&#x20;

Trigger;

```
<a wfu-trigger-click="my-event">Click me</a>
```

Action;

```
<button wfu-action-click="my-event">Hidden button</button>
```

In Webflow's current Interactions ( IX2 ), the button would then be set as a click trigger to trigger the interaction.&#x20;

In IX2, this is setup as a script click event, on a click-based Interactions Trigger.&#x20;

In Webflow's current Interactions ( IX2 ), the button would then be set as a click trigger to trigger the interaction.&#x20;


# Form Actions 🧪

Submit Form

## Future&#x20;

Validate Form


# Navigate Actions 🧪

## sa-action-navigate = ( *event name* )&#x20;


# Script Actions 🧪

Invoke a script as an SA5 Action.

IMPORTANT: Differentiate;

* "Sequence" action specifications from
* JS actions that run a function&#x20;

Goals;

Use cases;&#x20;

* Start a video after an interaction has completed
* Display a pop-up in the middle of an interaction sequence&#x20;

Technical notes

### sa-action-script = ( ... ) &#x20;

This Action requires an SA5 config block

```html
<script type="application/sa+json" handler="action.script" event="alert1">
{
  "@context": "https://attr.sygnal.com",
  "@version": "0.1",
  "script": [
    { "action": "class-add", "data": "class1" }, 
    { "action": "class-remove", "data": "class1" },
    { "action": "scroll-into-view", "behavior": "instant", "block": "start" }      
  ]
}
</script>
```

This Action requires an SA5 config block

```html
<script type="application/sa+json" handler="action.script" event="alert1">
function test1() { 
  sa.events.invoke("test1"); 
}
</script> 
```

This Action requires an SA5 config block; &#x20;

```html
<script type="application/sa+json" handler="action.script" event="alert1">
{
  "@context": "https://attr.sygnal.com",
  "@version": "0.1",
  "function": "myFunc1"  
}
</script>
```

This Action requires an SA5 config block

```html
<script type="application/sa+json" handler="action.script" event="alert1">
loadCssOnce("https://calendly.com/assets/external/widget.css"); 
loadJsOnce("https://calendly.com/assets/external/widget.js", module?); 

new CSSLoader({
  url: "https://calendly.com/assets/external/widget.css"
}).load();
new CSSLoader({
  url: "https://calendly.com/assets/external/widget.js"
}).load();

Calendly.initPopupWidget({ url: 'https://calendly.com/YOUR_SCHEDULING_LINK' }); 

</script> 
```

This Action requires an SA5 config block; &#x20;

## Actions

Triggers;

* User action
  * Click
  * Scroll
  * Hover
* Variable change ( reactive );&#x20;
  * e.g. x > 1000
* Timer
* Interaction step&#x20;
* Form submit &#x20;
* Alpine.js triggers  &#x20;

Actions;&#x20;

* Click ( other elements )
  * Interaction&#x20;
* Scroll ( other elements )
* Code execution
* Class add/remove&#x20;
* Form submit

Mechanic;&#x20;

* Define triggers
  * e.g. attribute wfu-click = ( name ) &#x20;
*

<https://codepen.io/memetican/pen/pvzXrKQ/30243a91b6fb117a92b0e5b3b5b69503?editors=1111>


# SA5 Core

The central engine

For some time we've been developing the central engine shared by all libraries. It has a few key roles;

* Centralized debugging and debug state management&#x20;
* Event handler registration
* Configuration handler registration
* Global vars

Eventually, we'll be expanding it to add a few other key capabilities;

* Remote monitoring
* Smart-loading

### What is Smart Loading?

Smart Loading is a means to conveniently detect and load SA5's many libraries dynamically, depending on the configurations found in the webpage-

* Script config blocks
* SA5 custom attributes

Features;

* Drop in one references, and all libs are automatically accessible ( same version as core )
* Detection of unknown attributes, or misspelled attributes, or obsolete
* Controlled order of execution, internal dependencies&#x20;

## Loading Technique

Pre-DOM load

e.g. for inline script changes like SEO work

Post-DOM load

Most work is done here&#x20;

```javascript
if (document.readyState !== 'loading') {
    console.log('document is already ready, just execute code here');
    myInitCode();
} else {
    document.addEventListener('DOMContentLoaded', function () {
        console.log('document was not ready, place code here');
        myInitCode();
    });
}

function myInitCode() {}
```


# Handlers

Handlers are User-defined event handlers that are defined in code, which provide custom behaviors attached to specific SA5 modules.&#x20;

An example is hotkey handler installation.  This block both registers a hotkey and defines the custom code to execute when the hotkey is pressed.&#x20;

```html
<script>
window.sa5 = window.sa5 || [];
window.sa5.push(['hotkeys', (hotkeyHandler) => {
  hotkeyHandler.register("f2", () => {
    console.log("f2 pressed");
  });
}]);
</script>
```

## Notes&#x20;

* Handlers are classed, e.g. `hotkeys` to identify the module. &#x20;
* Each module has different capabilities, and will define its own API details for handlers.&#x20;


# Controllers

**SA5 Controllers** are module-specific access points that are exposed to custom code.&#x20;

These are automatically installed when a library is used, and are available on the Window object, beneath `sa5.controllers`.

## Examples&#x20;

### Modals&#x20;

When SA5 Modals in installed `sa5.controllers.modals` is available and can be used to display a specific named modal;&#x20;

```html
<script>
sa5.controllers.modals.display("modal1", true); 
</script>
```


# Core Processing

## Eventing&#x20;

Blocking / non-blocking ??&#x20;

* SA5 load starting
  * SA5 lib load starting
    * Custom lib events&#x20;
    * ...&#x20;
  * SA5 lib load finished&#x20;
  * ...&#x20;
* SA5 load finished&#x20;

## Identify All SA Classed Elements

### Includes Config Blocks

Like...

```
<script type="application/sa5+json"> 
{
  "@context": "https://attr.sygnal.com",
  "@type": "ConversionEvent",
  "@version": "0.1",
  "url": "https://sygnal-n8n-u1282.vm.elestio.app/webhook/4eb1125f-ad4f-41d7-8946-a16fe276a8be", 
  "transactionIdType": "auto",
  "type": "contact",
  "item": ""  
}
</script>
```

## Determine Required Modules

## Load Modules Needed&#x20;

## Execute Modules in Order

* Dynamic Attributes&#x20;
  * First, do config blocks
  * Then attributes&#x20;

Layout  &#x20;

| Type   | Module                                                 |                                                                    |                                  |
| ------ | ------------------------------------------------------ | ------------------------------------------------------------------ | -------------------------------- |
| Event  | SA5 Init Starting                                      |                                                                    |                                  |
| Action | SA5 Data init                                          | <ul><li>Initialize Data Sources </li></ul>                         |                                  |
| Action | [Dynamic Attributes](/sa5-html/sa5-dynamic-attributes) | <ul><li>First, do config blocks </li><li>Then attributes</li></ul> | Includes start and finish events |
| Action | SA5 Data binding                                       |                                                                    |                                  |
| Action | Decode                                                 |                                                                    |                                  |
| Action | SA5 Layout                                             |                                                                    |                                  |
|        |                                                        |                                                                    |                                  |
| Action | SA5 TEA                                                |                                                                    |                                  |
|        |                                                        |                                                                    |                                  |
| Action | Unwrap                                                 |                                                                    |                                  |
| Event  | SA5 Init Finished                                      |                                                                    |                                  |

Install CSS

HTML Tools

Remove Elements

Cleanup Conditional Visibility


# Core Technical Notes


# Element Tracking

## Map v. WeakMap

Both \`Map\` and \`WeakMap\` are built-in objects in JavaScript that can store key-value pairs, but there are some key differences between them:<br>

1\. \*\*Key Types\*\*: In a \`Map\`, keys can be of any type: primitives, objects, functions, etc. In a \`WeakMap\`, keys must be objects (not primitive values).

2\. \*\*Garbage Collection\*\*: One of the main differences between \`Map\` and \`WeakMap\` is how they interact with garbage collection. In a \`Map\`, as long as the \`Map\` itself is alive, all of its keys are kept alive too, even if there are no other references to those keys. This could potentially lead to memory leaks if you're not careful. On the other hand, in a \`WeakMap\`, if a key object is not referenced anywhere else, it can be garbage collected, and its entry is automatically removed from the \`WeakMap\`. This makes \`WeakMap\` useful for associating additional data with objects without affecting their lifetime.

3\. \*\*Enumeration\*\*: A \`Map\` is iterable, which means you can loop over its keys and values, and it has a \`size\` property that tells you how many entries it has. A \`WeakMap\` is not iterable and does not have a \`size\` property. This is mainly because of the way \`WeakMap\` interacts with garbage collection -- since keys can be automatically removed at any time, there's no guarantee of how many entries the \`WeakMap\` has at any moment.

4\. \*\*Use Cases\*\*: \`Map\` is a general-purpose key-value store that can be used in a wide variety of scenarios. \`WeakMap\` is more specialized. It's useful when you want to associate data with an object without interfering with garbage collection, such as storing metadata about objects, or keeping private data for an object.

So, in summary, while \`Map\` and \`WeakMap\` can both store key-value pairs, they have different characteristics regarding key types, garbage collection, enumeration, and use cases.


# About SA5's Cache Lib

About the SA5 Cache Library

{% hint style="warning" %}
SA5 Cache is primarily an internal mechanism within the library, and may become part of SA5 Core. We're considering whether an how to make it accessible and usable by other devs in their projects.&#x20;
{% endhint %}

## What is SA5 Cache?  <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

This this lib is a tool for JavaScript devs to cache data for quick local access.

It provides;

* Key-value caching, where the value is any object&#x20;
* Cookies & webStorage support&#x20;
  * Expiry settings for cookies&#x20;
* A cache controller, to manage and retrieve items&#x20;

The architecture is designed to simplify access;

* Instantiate a cache controller
* Add an item, which represents a named object
  * Give it a name
  * Defined where and how it is stored
  * Give it a function that can construct the object for storage

Any time you need that data, simply request it from the cache controller. If the object is not available, it will call your function to create it, and then cache the results.

### Cache In-Validation Support

SA5 Cache supports a controller-level token, which allows you to invalidate client side caches when application changes are deployed that involve e.g. object structure changes.&#x20;

Future- complete this and add the clear cache.&#x20;

## Use Cases&#x20;

* Get JSON, CSV, RSS from an endpoint, as rarely as possible&#x20;
* Store any data you want, between user-sessions&#x20;

## Future

* Caching tied to User accounts
* Intelligent expiry determination, from the server&#x20;


# Data Caching ❺

Cache retrieved data for quick access.

{% hint style="warning" %}
This is an **advanced feature** which requires an understanding of JavaScript development.&#x20;
{% endhint %}

## Overview <a href="#display-captions-in-webflows-lightboxes" id="display-captions-in-webflows-lightboxes"></a>

Sites are better with dynamic data, but as Webflow does not provide for server-side development, that data often needs to come from outside sources and endpoints. How do you make that as efficient as possible?

You combine techniques like caching, asynchronous loading, callbacks, and lazy loading to request, store, and access that data.&#x20;

Sygnal's Caching library supports;

* Caching of retrieved user data in a webStorage-managed map.
* Lazy-loading. The data is not retrieved until it's first requested.
* Fully async execution. 100% entirely non-blocking.
* Defining your cached values, and the async functions that calculate or determine them.

Then when you request your value, the lib takes care of checking the cache first. If the value has not been calculated or retrieved, it calls your custom code calculation function to resolve it. &#x20;

## Use Cases

* Retrieve data from another page on your site
* Retrieve data from a page on another site
* Retrieve data from an API
* Perform a time-consuming calculation

## Limitations

Currently it is a simple session-level cache, meaning that the data will only be stored until the tab is closed. There is no configuration ability at present for specific cache timeouts.&#x20;

## Future Plans

* ~~Multiple cached items with a single cache handler.~~ Added
* ~~Cache choice and lifetime configuration, using sessionStorage, localStorage, or cookies~~ Added
* Support for eager or ~~lazy loading~~, by cached data item ( all will be asynchronous ).
  * Eager loading may be considered later
* Triggering events on load, to allow other processes to happen&#x20;
* Handle retrieval failure cases.&#x20;

Possible support for specialized, source-aware and content-aware data adapters;

These would be part of SA5 Data;&#x20;

* Retrieving data from Google Docs, Sheets, or AirTable&#x20;
* Retrieving specific element content from another webpage
* JSON API calls&#x20;

We're also considering approaches on how the adapter configurations and data-binding could potentially support simple NO-CODE situations such as value-binding. &#x20;

## Usage Notes <a href="#usage-notes" id="usage-notes"></a>

Setup involves three parts;

1. Setting up the cache, and defining your cached values
2. Creating custom functions for to calculate your defined values, which is called when the uncached data needs to be created.
3. Cache retrieval, which retrieves the data either from the cache (if available) for generates it (if needed)  &#x20;

Setup involves defining your cached values, and the async functions that calculate or determine them.

Then when you request your value, the lib takes care of checking the cache first. If the value has not been calculated or retrieved, it calls your custom code calculation function to resolve it. &#x20;

### Setup the Cache

{% hint style="danger" %}
**ALPHA PRE-RELEASE.** Not advised for production use. Use v4 for production sites.&#x20;
{% endhint %}

Place this code in your site or page level **/head** code,

{% code overflow="wrap" %}

```javascript
<!-- Sygnal Attributes 5 | Cache -->
<script src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/webflow-cache.js"></script>
<script src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/webflow-cache/cache-item.js"></script>

<script>
const init = () => { 

  const cache = new WfuCache({
    val: {
      myData: new WfuCacheItem({
        name: "myData", 
        store: "sessionStorage", 
        updateFnAsync: getMyDataAsync   
      })
    }
  });

}

document.addEventListener("DOMContentLoaded", init)
</script>

```

{% endcode %}

Define as many cached values as you want, giving them each a unique name. These are defined beneath `val`, as properties.&#x20;

### Access your Data&#x20;

When you need to retrieve that value, call `cache.getAsync()`. In the `.then()`, utilize your data. That code will be executed as soon as the data is available. This will be effectively immediate if the data is already cached. &#x20;

```javascript
// Retrieve a desired value
cache.getAsync("myData")
  .then(x => {
   
    // Do something with your data
    console.log("Retrieved", x); 
          
  }); 
```

### Configure your Data Retrieval functions  <a href="#getting-started-nocode" id="getting-started-nocode"></a>

This is your custom code `async` function for determining the value, if it's not already in the cache.&#x20;

```javascript
// Your asynchronous data-capture function 
async function getMyDataAsync() {

  // Get or create your data 
  var data = "HERE'S MY DATA"; 
  
  return data; 
}
```


# Cache Setup v4

Cache retrieved data for quick access.

{% hint style="info" %}
This setup guide is the **legacy** documentation for SA4.&#x20;
{% endhint %}

## Usage Notes v4 <a href="#usage-notes" id="usage-notes"></a>

Setup involves three parts;

1. Setting up the cache, and defining your cached values
2. A custom function to calculate your defined values, which is called when the uncached data needs to be created.
3. Cache retrieval, which retrieves the data either from the cache (if available) for generates it (if needed)  &#x20;

## Setup the Cache

Place this code in your site or page level **/body** code,

{% code overflow="wrap" %}

```javascript
<script type="module">
import { WfuCache, WfuCacheItem } from 'https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.4.0/src/modules/webflow-cache.min.js'; 

$(function() {
  const cache = new WfuCache({
    val: {
      myData: new WfuCacheItem({
        store: "sessionStorage", 
        name: "myData", 
        updateFnAsync: getMyDataAsync   
      })
    }
  });
});
</script> 
```

{% endcode %}

Define as many cached values as you want, giving them each a unique name. These are defined beneath `val`, as properties.&#x20;

### Access your Data&#x20;

When you need to retrieve that value, call `cache.getAsync()`. In the `.then()`, you can use your data how you want as soon as it's available. &#x20;

```javascript
// Some code
$(function() {
  
  // Cache setup ( code example above ) 
  // ... 
  
  // Retrieve a desired value
  // in this example, we want the value on page load, asynchronously 
  cache.getAsync("myData")
    .then(x => {
    
      // Do something with your data
      console.log(x); 
      
    }); 
      
}); 
```

### Configure your Data Retrieval functions  <a href="#getting-started-nocode" id="getting-started-nocode"></a>

This is your custom code `async` function for calculating the value, if it's not already in the cache.&#x20;

```javascript
// Your asynchronous data-capture function 
async function getMyDataAsync() {

  // Get or create your data
  // ...
  
  return data; 
}
```

### STEP 1 - Add the Library <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

See above, and the feature-specific sub pages for details.


# About SA5's Social Share Lib

Share your links with the world.

**Social sharing is a valuable way to encourage organic traffic to your website.** &#x20;

{% hint style="success" %}
This SA5 Attribute extends Finsweet's Social Share attribute.&#x20;
{% endhint %}

[Finsweet's Social Share](https://finsweet.com/attributes/social-share) attribute covers these platforms well;

* X ( Twitter )&#x20;
* Facebook
* Pinterest&#x20;
* Reddit
* Telegram&#x20;
* LinkedIn&#x20;

This library is intended to round out any gaps not already covered.&#x20;

**In particular,&#x20;*****email-sharing*****.**&#x20;


# Quick Start | SA5 Social Share

How to Easily Add SA5's User Info & Advanced Routing Enhancements to Your Webflow Memberships Site

{% hint style="danger" %}
UNDER DEVELOPMENT&#x20;
{% endhint %}

## How to Add the Library <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

{% hint style="warning" %}
**IMPORTANT** \
We've made some enhancements to SA5 Core in v5.5.0. \
If you are already using other SA5 Libs, please make certain to upgrade them to at least v5.5.0 as well to ensure cross-compatibility.&#x20;
{% endhint %}

[Add this script](/overview/how-to-add-custom-code) to the **site wide** custom code **HEAD** area of your site.&#x20;

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Social Share --> 
<script>
// Routing rules
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.7.1/dist/css/webflow-socialshare.css"> 
<script src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.7.1/dist/nocode/webflow-socialshare.js"></script>
```

{% endcode %}

Add the custom attributes for the features you need, described in each feature separately. &#x20;

## SA5 Library Developers

For SA5 library developers, use the extended syntax to support [Sygnal Devmode](https://devmode.sygnal.com/) and [Sygnal DevProxy](https://engine.sygnal.com/devproxy).  These make it easier to develop and test code changes with Webflow sites.&#x20;

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Social Share --> 
<link rel="stylesheet" 
  href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.7.1/dist/css/webflow-socialshare.css"
  dev-href="http://127.0.0.1:4000/dist/css/webflow-socialshare.css"
  group="SA5 Social Share"
  > 
<script defer 
  src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.7.1/dist/nocode/webflow-socialshare.js" 
  dev-src="http://127.0.0.1:4000/dist/nocode/webflow-socialshare.js"
  group="SA5 Social Share"
  ></script>
```

{% endcode %}


# Email Social Share ❺

Convert a link element into an email sharing tool for current page.

Designed to make a link element work as a sharing mechanism for the current page.

When clicked, it invokes the user's defined email program to send a message, and populates the subject and body, including the link to the current page.

The user then enters to To, and sends it.&#x20;

## Demonstration

{% embed url="<https://webflow.com/made-in-webflow/website/sa5-social-share>" %}

## Getting Started  <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

### STEP 1 - Add the Library  <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

First, **add the library** as detailed in [Quick Start](/sa5-socialshare/quick-start).&#x20;

### STEP 2 - Apply `wfu-socialshare` to Desired Elements <a href="#step-2---apply-wfu-query-param-to-desired-elements" id="step-2---apply-wfu-query-param-to-desired-elements"></a>

See Usage Notes for details.&#x20;

## Usage Notes

### `wfu-socialshare` = `email`

**Required.**  Place directly on the link that you want to use for social sharing.&#x20;

{% hint style="warning" %}
This will replace any existing `src` on the link with the `mailto:` URL SA5 forms for you.&#x20;
{% endhint %}

### `wfu-socialshare-subject` = ( subject text you want on the email )

**Optional.** Place on the element with `wfu-socialshare`. &#x20;

### `wfu-socialshare-message` = message you want to preface the link

**Optional.** Place on the element with `wfu-socialshare`. &#x20;

Note the message can contain line-breaks, by using the `\n` character sequence.&#x20;

### UTM Tracking

If you want to add tracking information to the link, you can use SA5's UTM Tracking attributes here as well, on this same email element.&#x20;

[UTM Tracking](/sa5-analytics/utm-tracking)

In general, we'd recommend something like;&#x20;

<table><thead><tr><th width="272">SA5 attribute</th><th>Example Value</th><th>Notes</th></tr></thead><tbody><tr><td><code>wfu-link-utm-source</code></td><td><code>email</code></td><td></td></tr><tr><td><code>wfu-link-utm-medium</code></td><td><code>share</code></td><td>Indicates that it was shared</td></tr><tr><td><code>wfu-link-utm-campaign</code></td><td><code>blog</code></td><td>Or similar ( news, recipe... )</td></tr><tr><td><code>wfu-link-utm-content</code></td><td>Your Article Title</td><td>CMS-bind this one if you're in a collection list or collection page. </td></tr></tbody></table>


# About SA5's Components Lib

Share your links with the world.

**Social sharing is a valuable way to encourage organic traffic to your website.** &#x20;

{% hint style="success" %}
This SA5 Attribute extends Finsweet's Social Share attribute.&#x20;
{% endhint %}

[Finsweet's Social Share](https://finsweet.com/attributes/social-share) attribute covers these platforms well;

* X ( Twitter )&#x20;
* Facebook
* Pinterest&#x20;
* Reddit
* Telegram&#x20;
* LinkedIn&#x20;

This library is intended to round out any gaps not already covered.&#x20;

**In particular,&#x20;*****email-sharing*****.**&#x20;


# Quick Start | SA5 Components

How to Easily Add SA5's User Info & Advanced Routing Enhancements to Your Webflow Memberships Site

{% hint style="danger" %}
UNDER DEVELOPMENT \
These are advanced tools for component developers only.&#x20;
{% endhint %}

## How to Add the Library <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

{% hint style="warning" %}
**IMPORTANT** \
We've made some enhancements to SA5 Core in v5.5.0. \
If you are already using other SA5 Libs, please make certain to upgrade them to at least v5.5.0 as well to ensure cross-compatibility.&#x20;
{% endhint %}

[Add this script](/overview/how-to-add-custom-code) to the **site wide** custom code **HEAD** area of your site.&#x20;

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Social Share --> 
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.8.7/dist/css/webflow-components.css"> 
<script src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.8.7/dist/nocode/webflow-components.js"></script>
```

{% endcode %}

Add the custom attributes for the features you need, described in each feature separately. &#x20;

## SA5 Library Developers

For SA5 library developers, use the extended syntax to support [Sygnal Devmode](https://devmode.sygnal.com/) and [Sygnal DevProxy](https://engine.sygnal.com/devproxy).  These make it easier to develop and test code changes with Webflow sites.&#x20;

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Components --> 
<link rel="stylesheet" 
  href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.8.7/dist/css/webflow-components.css"
  dev-href="http://127.0.0.1:4000/dist/css/webflow-components.css"
  group="SA5 Components"
  > 
<script 
  src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.8.7/dist/webflow-components.js" 
  dev-src="http://127.0.0.1:4000/dist/webflow-components.js"
  group="SA5 Components"
  ></script>
```

{% endcode %}


# Components ❺

Convert a link element into an email sharing tool for current page.

Designed to make a link element work as a sharing mechanism for the current page.

When clicked, it invokes the user's defined email program to send a message, and populates the subject and body, including the link to the current page.

The user then enters to To, and sends it.&#x20;

## Getting Started  <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

### STEP 1 - Add the Library  <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

First, **add the library** as detailed in [Quick Start](/sa5-components/quick-start).&#x20;

### STEP 2 - Apply Attributes to Desired Elements <a href="#step-2---apply-wfu-query-param-to-desired-elements" id="step-2---apply-wfu-query-param-to-desired-elements"></a>

See Usage Notes for details.&#x20;

## Usage Notes&#x20;

### `cc-unwrap`&#x20;

Add `cc-unwrap` to the Collection List Wrapper, List, and Item elements.&#x20;

### `cc-remove`&#x20;

Add `cc-remove` , will remove the elements and their child elements&#x20;


# About SA5's Detect Lib

About the SA5 Detect Library

This this lib supports detection of your visitor characteristics, and actions based on them.

Initially we are focused on geo-detection using a GEOIP methodology.&#x20;

* Route users to specific pages depending on their location&#x20;

Coming soon;&#x20;

* Conditionally display elements depending on location &#x20;

## Future

User-agent detection, with accordant routing and conditional-vis;

* Language
* Device type
* Browser type
* etc.&#x20;

First time visitor detection.&#x20;

Referral detection.&#x20;

Onready callback&#x20;

Data binding&#x20;

### Detection Overrides

E.g. use case

dropdown showing countries

detected country selected&#x20;

changing forces a change on the country setting

*reloads page* to reapply detect with override setting&#x20;

Explicit overrides on detected UserInfo, such as the country, city, browser type...&#x20;

Set by script,&#x20;

window\.user-info-overrides&#x20;

set directly on overrides objects, via detect&#x20;

Saved data binds&#x20;

\>$detect.geo.country

Override Detects UserInfo?&#x20;

* Override country
* Override city
* Override language
* Override browser type

And remember - cache overrides?&#x20;

### Filter defaulting

Support for queries, like UserInfo load triggers, and the ability to e.g. trigger FS filters depending on those details automatically.&#x20;

Initialization only, once.&#x20;


# Quick Start | SA5 Detect

How to Easily Add SA5's User Info & Advanced Routing Enhancements to Your Webflow Memberships Site

{% hint style="danger" %}
UNDER DEVELOPMENT&#x20;
{% endhint %}

## How to Add the Library <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

{% hint style="warning" %}
**IMPORTANT** \
We've made some enhancements to SA5 Core in v5.5.0. \
If you are already using other SA5 Libs, please make certain to upgrade them to at least v5.5.0 as well to ensure cross-compatibility.&#x20;
{% endhint %}

[Add this script](/overview/how-to-add-custom-code) to the **site wide** custom code **HEAD** area of your site.&#x20;

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Detect --> 
<script>
// Routing rules
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/css/webflow-detect.css"> 
<script src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/nocode/webflow-detect.js"></script>
```

{% endcode %}

Add the custom attributes for the features you need, described in each feature separately. &#x20;

## SA5 Library Developers

For SA5 library developers, use the extended syntax to support [Sygnal Devmode](https://devmode.sygnal.com/) and [Sygnal DevProxy](https://engine.sygnal.com/devproxy).  These make it easier to develop and test code changes with Webflow sites.&#x20;

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Detect --> 
<link rel="stylesheet" 
  href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/css/webflow-detect.css"
  dev-href="http://127.0.0.1:4000/dist/css/webflow-detect.css"
  group="SA5 Detect"
  > 
<script defer 
  src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/nocode/webflow-detect.js" 
  dev-src="http://127.0.0.1:4000/dist/nocode/webflow-detect.js"
  group="SA5 Detect"
  ></script>
```

{% endcode %}


# Page


# User Agent Detection

## Goals

* Identify device type
* Identify browser type
* Identify browser language

## Use Cases

*

## Language

Check all&#x20;

```
// Some code
if (navigator.language.startsWith('fr')) {
    // Your script for French language users
    console.log("The script for French language users is running.");
    // Place your script here
}

```

## Notes

<https://www.tutorialspoint.com/detect-whether-a-device-is-ios-or-not-using-javascript>

<https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent>

<https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent>


# Geolocation Detection 🧪

Route Visitors and Display Conditional Elements Based on Country or City.

{% hint style="danger" %}
**UNDER DEVELOPMENT**\
Watch this space.
{% endhint %}

## Use Cases

* Route visitors to a Country-specific home page
* Route visitors to a City-specific contact-us page

## Feature Roadmap

{% tabs %}
{% tab title="Completed" %}

* Route user by Country
* Routing table support
  * Supports routing any defined path, e.g. /about to any Geo-specific variants you have, e.g. `/about/gb`, `/about/au`, `/jp/about`.&#x20;
  * Routed sources and destinations are unlimited, no enforced path rules.&#x20;
* Standardized GeoInfo object, to consistently describe the detection regardless of the GeoIP handler source&#x20;
* Cached responses, for minimal GeoIP service traffic
  * One request per unique user&#x20;

GeoIP handlers;

* IPInfo support. 50,000 requests/mo free.&#x20;
  {% endtab %}

{% tab title="Planned" %}

* Route user by Continent
* Route user by City
* Cache duration as a setting
* Named route efficiency
  * Modify page paths on e.g. Home / to the routed page variant, to avoid unnecessary redirects. &#x20;

Override support;

* The ability to apply the current setting to a Dropdown, and automatically indicate the current Country, City, etc.
* The ability to change country, city, etc and have that override the detected settings.&#x20;

GeoIP handlers;

* Support for multiple Geoip handlers&#x20;
  * Ability to select in config
    {% endtab %}

{% tab title="Considering" %}
Developer support;

* Expose and document the Detect objects for use in code, e.g.;
  * Data-binding support, populate a form's Zip code field, select a country, etc.&#x20;
  * Auto-center maps&#x20;
* Automatic Filter-binding support for FS-Filter

GeoZone definitions;&#x20;

* Collection of countries, cities, zips, etc into a Zone, like "Europe"&#x20;

GeoIP handlers;

* Ability to select the GeoIP handler dynamically in config&#x20;
* Fallback handlers&#x20;
  {% endtab %}

{% tab title="More Ideas" %}
GeoZone definitions;

* Point-and-radius
* Sort list by nearest ( branch locators, etc. )&#x20;
* Zip to data chaining&#x20;

GeoIP handlers;

* Possibly a Sygnal-specific Geoip handler&#x20;
  {% endtab %}
  {% endtabs %}

## Usage Notes <a href="#usage-notes" id="usage-notes"></a>

### Routing Rules

Routing rules are configured just before the library is loaded in the script section indicated with // Routing rules ( see quick start ).&#x20;

* You may have multiple rule configurations in this section, though this is mostly a future design facilitation. Most likely, the first rule matched will be applied.&#x20;

#### Route by Country

Specify the rule `type` as `geo-country`.

Supply your routes, from country code to path, exactly as in the structure below. You may have as few or as many routes as you like.&#x20;

If the visitor's country is not in the list, they will not be routed. They will simply match the default page.&#x20;

{% hint style="info" %}
We're working on a design for grouped countries as a regional definition.
{% endhint %}

```html
<script>
// Routing rules
window['sa5_route'] = [{
    type: 'geo-country',
    path: '/',
    route: [
        ["NZ", "/nz"],
        ["AU", "/au"],
        ["US", "/us"],
        ["GB", "/gb"],
    ]
}];
</script>
```

## IPinfo Lite&#x20;

| <p>IPinfo Lite gives you essential IP data completely free, with no restrictions. Here’s what you have to look forward to:</p><ul><li>Industry-leading, enterprise-grade accuracy</li><li>Country, continent, and ASN data</li><li>Use it commercially with no caps or usage limits</li><li>Data updated daily</li><li>Unlimited usage via API or download</li></ul><p>Here's an example of the Lite API, showing the ASN and county level geolocation fields that are included:</p><p> </p><p>$ curl <a href="https://api.ipinfo.io/lite/8.8.8.8?token=$TOKEN"><https://api.ipinfo.io/lite/8.8.8.8?token=$TOKEN></a></p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

<table data-header-hidden><thead><tr><th align="center" valign="top"></th></tr></thead><tbody><tr><td align="center" valign="top"><a href="https://email.ipinfo.io/e3t/Ctc/DP+113/d2R-Zr04/VVTdCq7srf4wW1P5jv82Xrv-zW1KLHGL5wktfyN9k7M9C3qgyTW6N1vHY6lZ3m2VHCn2L8j2v8NW2JlGNV3BzPbFW6GT3q44xh3B4W8pG6Hj4-Z16-VP74-966_m2xW4jT-z95D3kCvVsnF-W7RCFv0W7lKscX50ZgnyW7BfTxF3XZ08LW60LPvY3-kdV0N4gxjl_h9m4cW3CJmn07BtlyHW1W8dxK8gq0S3W2NZlBX7StsGBW9d0TKF2rq5bDW4V6tmK4ttwVPW6bhNlM1qRy-JVvLHP08pj4sLW10210G1PGqwjW2TtbRb5D3gYLN9l3zLgCk9VjW8qmjFx3WRWHjf6QRJ7204"><img src="https://ci3.googleusercontent.com/meips/ADKq_Nb2cqr0MJsLpoynVFdlkwwBLuHAyexUDxO6fDrEyo14VOrsQVeuk1dkCJ4dWBN7YGGSY_ICKH0SkcKHEefLckzZ4Y4JGGYd3mnvlBo6lV9OqiXcZLrIXJ3qVjZTjFbfew_pX4JZ0vyjzoatP5cdntkIcGX5FS0gUbUKjN2S8xxOSw=s0-d-e1-ft#https://email.ipinfo.io/hs-fs/hubfs/sample%20response.png?width=900&#x26;upscale=true&#x26;name=sample%20response.png" alt="sample response"></a></td></tr></tbody></table>

| <p>Prefer working with files? The data is also available as a download, in JSON, CSV, MMDB and Parquet formats – ready to integrate into your tools, dashboards, or workflows.</p><p><br>IPinfo Lite is your new go-to – and built to support everything from side projects to production systems. While IPinfo Lite only includes a subset of all of the data we offer, there's no compromise to the data quality, or the refresh rate.</p> |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |


# Cache Setup v4

Cache retrieved data for quick access.

{% hint style="info" %}
This setup guide is the **legacy** documentation for SA4.&#x20;
{% endhint %}

## Usage Notes v4 <a href="#usage-notes" id="usage-notes"></a>

Setup involves three parts;

1. Setting up the cache, and defining your cached values
2. A custom function to calculate your defined values, which is called when the uncached data needs to be created.
3. Cache retrieval, which retrieves the data either from the cache (if available) for generates it (if needed)  &#x20;

## Setup the Cache

Place this code in your site or page level **/body** code,

{% code overflow="wrap" %}

```javascript
<script type="module">
import { WfuCache, WfuCacheItem } from 'https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@4.11/src/modules/webflow-cache.min.js'; 

$(function() {
  const cache = new WfuCache({
    val: {
      myData: new WfuCacheItem({
        store: "sessionStorage", 
        name: "myData", 
        updateFnAsync: getMyDataAsync   
      })
    }
  });
});
</script> 
```

{% endcode %}

Define as many cached values as you want, giving them each a unique name. These are defined beneath `val`, as properties.&#x20;

### Access your Data&#x20;

When you need to retrieve that value, call `cache.getAsync()`. In the `.then()`, you can use your data how you want as soon as it's available. &#x20;

```javascript
// Some code
$(function() {
  
  // Cache setup ( code example above ) 
  // ... 
  
  // Retrieve a desired value
  // in this example, we want the value on page load, asynchronously 
  cache.getAsync("myData")
    .then(x => {
    
      // Do something with your data
      console.log(x); 
      
    }); 
      
}); 
```

### Configure your Data Retrieval functions  <a href="#getting-started-nocode" id="getting-started-nocode"></a>

This is your custom code `async` function for calculating the value, if it's not already in the cache.&#x20;

```javascript
// Your asynchronous data-capture function 
async function getMyDataAsync() {

  // Get or create your data
  // ...
  
  return data; 
}
```

### STEP 1 - Add the Library <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

See above, and the feature-specific sub pages for details.


# Detect Incognito Mode

This is a rarely needed situation, which we refer to as a Dark Site, in reference to the Dark Web- sites that "hide" from the traditional mechanics of discovery, history, tracking, and so on.&#x20;

Here we're focused on detecting Incognito Mode.&#x20;

## Goals

* Detect Incognito mode
  * Various browsers
* Warn user if not in incognito mode
  * Give proceed ( that's fine ) option
  * Give exit option
    * Instructions on how to invoke incognito mode on that browser
    * Clipboard copy URL
    * Purge / replace browser history&#x20;
      * Chain purge
      * Cookies
      * webStorage&#x20;

## Use Cases

**The primary general use case here is to minimize the risk of unwanted browser-history tracking on a site.**&#x20;

There are of course many illicit use cases here, but the scenario we're interested in supporting is domestic abuse hotlines, where someone may seek help and information but needs to be able to do so safely with minimal risk of someone else discovering their investigations.&#x20;

## Implementation Notes

<img src="/files/P0fn0ob3RO6yHA90ybAa" alt="" class="gitbook-drawing">

### Detecting Incognito Mode

Detecting if a browser is in incognito mode is not straightforward, as browsers are designed to prevent detection of incognito mode to protect user privacy. However, there are some heuristics and methods you can use to make an educated guess. Below is an example of how to detect incognito mode in various browsers and how to prevent the user from proceeding if they are not in incognito mode.

#### **Chrome and Edge**

You can use the FileSystem API to detect incognito mode. In incognito mode, the FileSystem API is disabled, and trying to use it will result in an error.

```
function isIncognito(callback) {
    var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
    if (!fs) {
        callback(false);
        return;
    }
    fs(window.TEMPORARY, 100, function () {
        callback(false);
    }, function () {
        callback(true);
    });
}

isIncognito(function(isPrivate) {
    if (!isPrivate) {
        alert("Please open this page in incognito mode.");
        window.location.href = 'about:blank';
    } else {
        console.log("You are in incognito mode.");
    }
});

```

#### **Firefox**

Firefox does not disable the FileSystem API, so the above method won't work. Instead, you can check the storage quota, as incognito mode typically has a much smaller quota.&#x20;

```
function isIncognito(callback) {
    var db = indexedDB.open("test");
    db.onerror = function() {
        callback(true);
    };
    db.onsuccess = function() {
        callback(false);
    };
}

isIncognito(function(isPrivate) {
    if (!isPrivate) {
        alert("Please open this page in incognito mode.");
        window.location.href = 'about:blank';
    } else {
        console.log("You are in incognito mode.");
    }
});

```

#### **Safari**

Safari has different limitations in private browsing mode, such as no local storage access. You can test for these limitations.

```
function isIncognito(callback) {
    var isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 &&
                  navigator.userAgent && !navigator.userAgent.match('CriOS');

    if (isSafari) {
        try {
            window.openDatabase(null, null, null, null);
            callback(false);
        } catch (_) {
            callback(true);
        }
    } else {
        callback(false);
    }
}

isIncognito(function(isPrivate) {
    if (!isPrivate) {
        alert("Please open this page in incognito mode.");
        window.location.href = 'about:blank';
    } else {
        console.log("You are in incognito mode.");
    }
});

```

### Preventing User from Proceeding

If the user is not in incognito mode, you can redirect them to a different page or display a message preventing further interaction.&#x20;

Combining the detection methods for Chrome, Edge, Firefox, and Safari:

```
        function isIncognito(callback) {
            var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
            if (fs) {
                fs(window.TEMPORARY, 100, function () {
                    callback(false);
                }, function () {
                    callback(true);
                });
            } else if (navigator.vendor && navigator.vendor.indexOf('Apple') > -1 &&
                      navigator.userAgent && !navigator.userAgent.match('CriOS')) {
                try {
                    window.openDatabase(null, null, null, null);
                    callback(false);
                } catch (_) {
                    callback(true);
                }
            } else {
                var db = indexedDB.open("test");
                db.onerror = function() {
                    callback(true);
                };
                db.onsuccess = function() {
                    callback(false);
                };
            }
        }

        isIncognito(function(isPrivate) {
            if (!isPrivate) {
                alert("Please open this page in incognito mode.");
                window.location.href = 'about:blank';
            } else {
                console.log("You are in incognito mode.");
            }
        });
```

### History Purge&#x20;

```
// List of random sites to redirect to
const randomSites = [
    'https://www.example1.com',
    'https://www.example2.com',
    'https://www.example3.com'
];

// Function to clear history and redirect
function clearHistoryAndRedirect() {
    // Clear history by navigating back in a loop
    let numberOfEntries = history.length;
    for (let i = 0; i < numberOfEntries; i++) {
        history.back();
    }

    // After history is cleared, use replace to redirect
    setTimeout(() => {
        const randomSite = randomSites[Math.floor(Math.random() * randomSites.length)];
        location.replace(randomSite);
    }, 100); // small delay to ensure history.back() operations complete
}

// Add event listener to the button
document.getElementById('randomRedirectBtn').addEventListener('click', clearHistoryAndRedirect);

```

```
function navigateTo(url) {
    // Change the URL without adding a new history entry
    window.history.replaceState({}, '', url);

    // Load new content dynamically
    loadContent(url);
}

function loadContent(url) {
    // Your logic to load content for the given URL
    // This could be an AJAX request or any other method to dynamically load content
}

// Example usage
navigateTo('/new-page');

```

```
<meta http-equiv="Cache-Control" content="no-store">

```

#### Reverse Proxy

```
Cache-Control: no-store
```


# About SA5's Analytics Lib

Use GTM's dataLayer to capture all the information you want from your site.

This this lib is designed to support analytics tracking using GTM and the dataLayer to capture data from your site. &#x20;

It c

Most of the examples here are focused on capturing the end data in Google Analytics ( GA4 ), however Facebook and other data targets are equally viable.

It can also be used to supportsupports detection of your visitor characteristics, and actions based on them.

Initially we are focused on geo-detection using a GEOIP methodology.&#x20;

* Route users to specific pages depending on their location&#x20;

Coming soon;&#x20;

* Conditionally display elements depending on location &#x20;

## Future

User-agent detection, with accordant routing and conditional-vis;

* Language
* Device type
* Browser type
* etc.&#x20;

First time visitor detection.&#x20;

Referral detection.&#x20;

Onready callback&#x20;

Data binding&#x20;

### Detection Overrides

E.g. use case

dropdown showing countries

detected country selected&#x20;

changing forces a change on the country setting

*reloads page* to reapply detect with override setting&#x20;

Explicit overrides on detected UserInfo, such as the country, city, browser type...&#x20;

Set by script,&#x20;

window\.user-info-overrides&#x20;

set directly on overrides objects, via detect&#x20;

Saved data binds&#x20;

\>$detect.geo.country

Override Detects UserInfo?&#x20;

* Override country
* Override city
* Override language
* Override browser type

And remember - cache overrides?&#x20;

### Filter defaulting

Support for queries, like UserInfo load triggers, and the ability to e.g. trigger FS filters depending on those details automatically.&#x20;

Initialization only, once.&#x20;


# Quick Start | SA5 Analytics

How to Easily Add SA5's Analytics to Your Webflow Site

{% hint style="danger" %}
COMING SOON.
{% endhint %}

## How to Add the Library <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

{% hint style="warning" %}
**IMPORTANT** \
We've made some enhancements to SA5 Core in v5.5.0. \
If you are already using other SA5 Libs, please make certain to upgrade them to at least v5.5.0 as well to ensure cross-compatibility.&#x20;
{% endhint %}

[Add this script](/overview/how-to-add-custom-code) to the **site wide** custom code **HEAD** area of your site.&#x20;

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Analytics --> 
<script>
// Routing rules
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/css/webflow-analytics.css"> 
<script src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/nocode/webflow-analytics.js"></script>
```

{% endcode %}

Add the custom attributes for the features you need, described in each feature separately. &#x20;

## SA5 Library Developers

For SA5 library developers, use the extended syntax to support [Sygnal Devmode](https://devmode.sygnal.com/) and [Sygnal DevProxy](https://engine.sygnal.com/devproxy).  These make it easier to develop and test code changes with Webflow sites.&#x20;

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Analytics --> 
<link rel="stylesheet" 
  href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/css/webflow-analytics.css"
  dev-href="http://127.0.0.1:4000/dist/css/webflow-analytics.css"
  group="SA5 Analytics"
  > 
<script defer 
  src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/nocode/webflow-analytics.js" 
  dev-src="http://127.0.0.1:4000/dist/nocode/webflow-analytics.js"
  group="SA5 Analytics"
  ></script>
```

{% endcode %}


# A/B and Multivariate Testing

{% hint style="info" %}
CONCEPTUAL
{% endhint %}

Goals;

* Fully integrated MVT
* Ability to trigger code, interactions, and show/hide elements
* Ability to scope MVT Experiments to pages, path parts, or tagged elements, so that multiple experiments can be run independently and simultaneously&#x20;
* All data capture in a 3rd party package like GA4&#x20;

Design;&#x20;

* Experiment manager ( external? )
* Experiment scope, i.e. by page or defined by zones using a custom attribute
* Auto-applies Experiment data and variant data to existing GTM data blocks
  * So that cross-impact can be ascertained.&#x20;
* Experiment engine
  * Show/hide
  * Various triggers e.g. IX2&#x20;
  *


# UTM Tracking

## What are UTM Params?&#x20;

UTM tracking data refers to Urchin Tracking Module parameters, which are a set of URL parameters used by marketers to track the effectiveness of online marketing campaigns across traffic sources and publishing media.&#x20;

UTM parameters are appended to URLs and provide detailed information about the source, medium, campaign, term, and content of the traffic coming to a website. This data is then used in web analytics tools like Google Analytics to analyze the performance of marketing efforts.

#### How UTM Parameters Work:

1. **Adding Parameters**: Marketers add UTM parameters to URLs used in their marketing campaigns.
2. **Tracking**: When a user clicks on a URL with UTM parameters, the parameters are sent to the destination website.
3. **Analytics**: Web analytics tools, such as Google Analytics, capture the UTM parameters and provide detailed reports on the traffic sources, mediums, campaigns, terms, and content.
4. **Analysis**: Marketers analyze this data to understand which campaigns are driving traffic, conversions, and other key performance indicators (KPIs).

#### Benefits of UTM Tracking:

* **Detailed Campaign Insights**: Gain insights into which campaigns are most effective in driving traffic and conversions.
* **Improved ROI**: Understand which marketing efforts provide the best return on investment.
* **Optimization**: Optimize future campaigns based on performance data.

By using UTM tracking, marketers can make data-driven decisions to improve the effectiveness of their marketing strategies.

The most common UTM parameters are:

1. **utm\_source**: Identifies the source of the traffic, such as a search engine (e.g., Google), newsletter, or other referring site.
   * Example: `utm_source=google`
2. **utm\_medium**: Identifies the medium of the traffic, such as email, CPC (cost-per-click), or organic search.
   * Example: `utm_medium=email`
3. **utm\_campaign**: Identifies the specific campaign that the traffic is associated with, such as a product launch or seasonal promotion.
   * Example: `utm_campaign=spring_sale`
4. **utm\_term**: Identifies the search terms used in paid search campaigns to generate the traffic (optional).
   * Example: `utm_term=running+shoes`
5. **utm\_content**: Differentiates between similar content or links within the same ad or campaign (optional).
   * Example: `utm_content=cta_button`

## Usage

SA5 lets you add UTM params to a URL automatically.  The purpose of making these individually controllable params is that they can be bound to CMS or component properties.&#x20;

`wfu-link-utm-source`

`wfu-link-utm-medium`

`wfu-link-utm-campaign`

`wfu-link-utm-content`

## Examples

[https://www.example.com/blog/article-title?utm\_source=email\&utm\_medium=share\&utm\_campaign=blog\_promotion\&utm\_content=article\_title <br>](<https://www.example.com/blog/article-title?utm_source=email\&utm_medium=share\&utm_campaign=blog_promotion\&utm_content=article_title&#xD;&#xA;>)


# Rel Attributes

#### 5. **Rel Attributes:**

* **Purpose**: Provides additional information to search engines about the nature of the link.
* **Types**:
  * `rel="nofollow"`: Instructs search engines not to follow the link.
  * `rel="noopener"`: Improves security by preventing the new page from accessing the window\.opener property.
  * `rel="sponsored"`: Indicates that the link is part of an advertisement or sponsored content.
  * `rel="ugc"`: Indicates that the link is user-generated content (e.g., comments, forum posts).

wfu-link-rel =&#x20;


# GA Events

```
	// Tell Google analytics that a video was played.
  window.ga('send', 'event', 'Video', 'Play');
```


# GTM Events ❺🧪

Route Visitors and Display Conditional Elements Based on Country or City.

{% hint style="danger" %}
**UNDER DEVELOPMENT**\
Watch this space.
{% endhint %}

## Goals

* Simplify GTM tracking
  * Keep as much of the configuration as possible designer-accessible
  * Custom attributes
  * Embeds for data structures
* Enable tracking of all kinds of events
* Capture specific metadata, with the goal of being able to use it in analytics, advertising, and reporting systems like GA4&#x20;

## Features ( Current )

* Track click events, with metadata, on anything
* Track load events, with metadata, on anything
* Restrict GTM events to specific breakpoints

## Use Cases

* Track click & display events on your CTAs, and display events, so you can determine conversions
* Pass it through to GA4 for reporting, in combination with other data&#x20;

Future;

* A/B testing&#x20;
* Capture user-specific form data&#x20;

## Strategy Notes

## Usage Notes

{% hint style="info" %}
SA5 simplifies the capture of events and passes them to GTM, however there is still configuration work required in GTM to receive that data, and in end systems like GA4, there is configuration work required to capture and display that data.&#x20;
{% endhint %}

SA5's GTM tracking config in the designer involves two parts;

* An element that you wish to track events on. This *must* be a container element, such as a DIV.
* An Embed within that element that contains the detailed data for the dataLayer. The Embed must contain a script element with a type of `sa5/gtm-data` and there must be only one of these within your tracked elemen.

The reason for this is that we want

```html
<script type="sa5/gtm-data">
{
  "event": "banner_interaction",
  "event_category": "System",
  "event_label": "Offer",
  "banner_name": "sticky",
  "interaction_type": "click"
}
</script>
```

{% hint style="info" %}
This MUST be valid JSON.&#x20;
{% endhint %}

wfu-gtm-event

* click - fires on a mouse click
* load - fires on page load

Future;

scroll, interactions visibility&#x20;

wfu-gtm-event-id

Future.

wfu-gtm-event-name

Optional. Currently used for logging so that you can&#x20;

Recommended to treat it as an identifier, meaning all-lowercase, and hyphens instead of spaces, e.g. desktop-qr

## wfu-gtm-event-breakpoints

This attribute allows you to restrict the breakpoints that an event will fire on.

It's composed of a string which specifies the breakpoints you want to allow the event on, and each letter defined a breakpoint;

321DTLP

1920+ 1440+ 1280+ desktop 991- tablet 767- landscape 478- portrait

Each breakpoint has an uppercase letter to describe it, in the table below.

Each has a corresponding lowercase letter which describes a range.

For example;

* `D` indicates desktop specifically, i.e. 992px - 1280px.  `d` indicates Desktop-or-larger, 992px+
* `T` indicates tablet, i.e. 768px - 991px. `t` indicates Tablet-or-smaller, 0px - 991px.

<table><thead><tr><th>Breakpoint</th><th width="62"></th><th>Range</th><th width="65"></th><th>Range</th></tr></thead><tbody><tr><td>Mobile Portrait</td><td>P</td><td>0 - 480</td><td>p</td><td>0 - 480</td></tr><tr><td>Mobile Landscape</td><td>L</td><td>481 - 767</td><td>l</td><td>0 - 767</td></tr><tr><td>Tablet</td><td>T</td><td>768 - 991</td><td>t</td><td>0 - 991</td></tr><tr><td>Desktop</td><td>D</td><td>992 - 1280</td><td>d</td><td>992 - unlimited</td></tr><tr><td>Medium Large</td><td>M</td><td>1281 - 1440</td><td>m</td><td>1281 - unlimited</td></tr><tr><td>Large</td><td>R</td><td>1441 - 1920</td><td>r</td><td>1441 - unlimited</td></tr><tr><td>Extra Large</td><td>X</td><td>1921 - unlimited</td><td>x</td><td>1921 - unlimited</td></tr></tbody></table>

You can combine these into any arrangement to describe the breakpoints you want.

For example;

* `Td` would mean Tablet and anything Desktop or larger
* `PD` would mean only Mobile Portrait, and Desktop, but not Landscape, Tablet, or larger breakpoints.

## Feature Roadmap

{% tabs %}
{% tab title="Completed" %}
Events supported;

* click tracking
* load tracking

Other;

* Arbitrary data capture
  {% endtab %}

{% tab title="Planned" %}
Add events support;&#x20;

* Forms
* Scroll into view ( element seen )
* Script-triggered events
  {% endtab %}

{% tab title="Considering" %}
Developer support;

* Expose and document the Detect objects for use in code, e.g.;
  * Data-binding support, populate a form's Zip code field, select a country, etc.&#x20;
  * Auto-center maps&#x20;
* Automatic Filter-binding support for FS-Filter

GeoZone definitions;&#x20;

* Collection of countries, cities, zips, etc into a Zone, like "Europe"&#x20;

GeoIP handlers;

* Ability to select the GeoIP handler dynamically in config&#x20;
* Fallback handlers&#x20;
  {% endtab %}

{% tab title="More Ideas" %}
GeoZone definitions;

* Point-and-radius
* Sort list by nearest ( branch locators, etc. )&#x20;
* Zip to data chaining&#x20;

GeoIP handlers;

* Possibly a Sygnal-specific Geoip handler&#x20;
  {% endtab %}
  {% endtabs %}

## Usage Notes <a href="#usage-notes" id="usage-notes"></a>

###

wfu-gtm-event = click

Inside of the element with the attribute, place one and only one of these

TODO support multiple?&#x20;

```
// Some code
sticky
<script type="sa5/gtm-data">
{
  "event": "banner_interaction",
  "event_category": "System",
  "event_label": "Offer",
  "banner_name": "desktop QR",
  "interaction_type": "close"
}
</script>


```

## GTM Setup

### Create Data Layer Variables in GTM

For *each* piece of data you want to capture (e.g., `banner_name`, `interaction_type`, `variant`), you'll need to create a corresponding Data Layer Variable in GTM:

* Go to Variables in your GTM dashboard.
* Click "New" to create a new variable.
* Select "Data Layer Variable" as the variable type.
* Enter the Data Layer Variable Name exactly as it appears in your data layer push (e.g., `banner_name`).
* Name your variable (e.g., `DLV - Banner Name`) and save.&#x20;

Three

* Event Category - `event_category`
* ?? Event Action - `event_action`
* Event Label - `event_label`

These appear to map in GA4

### Create a Trigger for `banner_interaction` Events

This is one trigger.

* Go to Triggers in your GTM dashboard.
* Click "New" to create a new trigger.
* Select "Custom Event" as the trigger type.
* For the "Event Name," enter `banner_interaction`—this should match exactly what you're pushing to the data layer.
* Choose "All Custom Events" to trigger for all instances of `banner_interaction`.
* Name your trigger (e.g., `Trigger - Banner Interaction`) and save.

### Create a GA4 Event Tag

This is one Tag.&#x20;

* Go to Tags in your GTM dashboard and click "New."
* Choose "GA4 Event" as the tag type (make sure you've already set up a GA4 Configuration tag).
* Select your GA4 Configuration Tag in the "Configuration Tag" setting.
* For "Event Name," you can either directly use `banner_interaction` or define a variable if you plan to dynamically name events.
* Under "Event Parameters," add parameters for each piece of data you want to send to GA4 (e.g., `banner_name`, `interaction_type`, `variant`). Use the Data Layer Variables you created earlier as values.
* In the "Triggering" section, select the `banner_interaction` trigger you created.
* Name your tag (e.g., `Tag - GA4 Banner Interaction Event`) and save.&#x20;

Likely, consider adding these parameters;

* `event_category` -&#x20;
* `?? event_action` -&#x20;
* `event_label` - Bind to {{ Event Label }}

> These appear to be pre-defined in GA4&#x20;

### Test & Publish

* Use the "Preview" mode in GTM to test your setup. Make sure that the `banner_interaction` events are triggering the GA4 tag correctly and that the data appears as expected in your GA4 debug view or real-time events report.
* Once you've verified that everything is working as expected, publish your changes in GTM.

## GA4 Setup

Reports

With Google Tag Manager (GTM) set up to send your banner interaction data to Google Analytics 4 (GA4), the next step is to view, capture, and report on this data within GA4. Here's how to do it:

#### Step 1: Verify Event Collection in GA4

First, ensure that the events you're sending from GTM are arriving in GA4:

1. Go to your GA4 Property.
2. Navigate to the "Events" section in the menu on the left.
3. Look for your event (e.g., `banner_interaction`). It might take some time for new events to appear if they've just been set up.

#### Step 2: Create Custom Dimensions (if needed)

If you're sending additional data with your events (e.g., `banner_name`, `interaction_type`, `variant`), you might want to create custom dimensions in GA4 to capture these details:

1. In GA4, navigate to "Configure" then "Custom Definitions".
2. Click "Create custom dimensions".
3. Fill out the details for each piece of additional data you're sending. The dimension name should match the parameter names you're sending from GTM (e.g., `banner_name`).
4. Save your custom dimensions.

\
With Google Tag Manager (GTM) set up to send your banner interaction data to Google Analytics 4 (GA4), the next step is to view, capture, and report on this data within GA4. Here's how to do it:

#### Step 1: Verify Event Collection in GA4

First, ensure that the events you're sending from GTM are arriving in GA4:

1. Go to your GA4 Property.
2. Navigate to the "Events" section in the menu on the left.
3. Look for your event (e.g., `banner_interaction`). It might take some time for new events to appear if they've just been set up.

#### Step 2: Create Custom Dimensions (if needed)

If you're sending additional data with your events (e.g., `banner_name`, `interaction_type`, `variant`), you might want to create custom dimensions in GA4 to capture these details:

1. In GA4, navigate to "Configure" then "Custom Definitions".
2. Click "Create custom dimensions".
3. Fill out the details for each piece of additional data you're sending. The dimension name should match the parameter names you're sending from GTM (e.g., `banner_name`).
4. Save your custom dimensions.

#### Step 3: Using the Data in Reports

To view and analyze your event data:

**Exploration Reports:**

1. Navigate to "Explore" from the left menu.
2. Create a new exploration or use an existing template.
3. In the variable settings, add your event and any custom dimensions as dimensions, and choose metrics (e.g., Event count).
4. Arrange the dimensions and metrics to analyze your data, such as by `banner_name` or `interaction_type`.

**Custom Reports:**

1. For more persistent reporting, navigate to "Reports" then "Library".
2. Create a new report or modify an existing one.
3. Add your event as a dimension and select appropriate metrics.
4. Customize the report further by adding filters or breakdowns by your custom dimensions.

#### Step 4: Real-Time Reporting

For immediate feedback:

1. Navigate to the "Realtime" section.
2. Look for your event under "Event count in the last 30 minutes".
3. Use the filter option to drill down into specific interactions or banners.

#### Step 5: Setting Up Audiences (Optional)

If you want to create audiences based on specific interactions for remarketing or analysis:

1. Navigate to "Configure" then "Audiences".
2. Create a new audience and use your event data and custom dimensions as criteria (e.g., users who clicked on a specific banner).

#### Step 6: Conversion Events (Optional)

If certain banner interactions are important conversions:

1. Navigate to "Configure" then "Conversions".
2. Click "New conversion event" and enter the name of the event you want to mark as a conversion.

Remember, the specifics of how you set up your reports and analyses will depend on your unique business questions and the insights you're seeking to gain from the data. By following these steps, you'll be well-equipped to capture and report on banner interaction data in GA4, leveraging the power of GTM for comprehensive web analytics.


# Important dataLayer Notes

## How SA5 supports this

In our `sa5/gtm-data` construction, you should set all of the properties in the object.

`null` values will be converted to `undefined`&#x20;

e.g.

```
<script type="sa5/gtm-data">
{
  "event": "banner_interaction",
  "event_category": "System",
  "event_label": "Offer",
  "banner_name": "sticky",
  "interaction_type": "click",
  "position": "sticky-top",
  "variant": null
}
</script>
```

### Future

Set a default base object, and override it. We merge them

```
<script type="sa5/gtm-data-proto" event="banner_interaction">
{
  "event": "banner_interaction",
  "event_category": "System",
  "event_label": "Offer",
  "banner_name": null,
  "interaction_type": null,
  "position": null,
  "variant": null
}
</script>
```

```
const prototypeElement = document.querySelector('script[type="sa5/gtm-data-prototype"]');
const prototypeData = JSON.parse(prototypeElement.textContent || '{}');

// Convert nulls to undefined
Object.keys(prototypeData).forEach(key => {
  if (prototypeData[key] === null) {
    prototypeData[key] = undefined;
  }
});

// Assume actualData is obtained similarly from an 'sa5/gtm-data' script
// Merge actualData into prototypeData
const mergedData = { ...prototypeData, ...actualData };

// Use mergedData as needed

```

## How GTM's dataLayer Works

When you push an object to the dataLayer, GTM doesn't reset or clear previous state between pushes. Instead, properties persist in the dataLayer's state until explicitly overridden or removed. This behavior ensures that data remains available across different events for tags, triggers, and variables to utilize without needing to repush the same information continually.

#### The Situation with Alpha, Beta, and Gamma

When you push an object containing `alpha`, `beta`, and `gamma` properties to the dataLayer and then subsequently push another object with just `alpha` and `beta`, the `gamma` property remains in the dataLayer's current state. This is why you're seeing `gamma` being preserved and appearing as part of the second event, even though you didn't include it in the second push.

#### How to Prevent Unwanted Properties from Persisting

To prevent properties like `gamma` from persisting beyond their intended event, you have a couple of approaches:

1. **Explicitly Set Unwanted Properties to `undefined` or `null`**: By explicitly setting properties you want to "remove" to `undefined` or `null` in subsequent pushes, you can ensure they don't carry over undesirably.


# Facebook

<https://www.youtube.com/watch?v=VBUMiAoPv4Q&ab_channel=VertexMarketingAgency>


# Google Analytics ( GA4 )

Setting up GA4 for SA5's dataLayer reports


# Future

## Posthog

Support for posthog rather than GTM as a dataLayer&#x20;

<figure><img src="/files/RqK5ESUnZFKCpzRDqt80" alt=""><figcaption></figcaption></figure>

## Auto-Tracking

Auto-tracking of certain event types


# About SA5's Data Lib

About the SA5 Data & Data-Binding Library

**SA5 Data** is a versatile framework that allows you to connect pieces of data to elements in your webpage.&#x20;

It supports simple no-code data-binding like;

* Initialize a form input from a querystring param&#x20;
* Populate an option element from a collection list&#x20;
* Capture the current page URL, path, or UTM params into hidden form fields as part of your form submission &#x20;

## Goals

Make data sources nocode accessible in Webflow page designs including;&#x20;

* CMS data&#x20;
* Querystring data
* URL, path, and hash data&#x20;

Bind these easily to;&#x20;

* Element text content
* Form elements&#x20;
* Form hidden fields for data capture &#x20;

{% hint style="info" %}
**Inbound-only.** Our data & data-binding features are one-way. They're designed to integrate information into your site from various sources - but not to update external data stores. If you are looking for round-trip capabilities, it's likely you need an application platform like Wized + Xano.&#x20;
{% endhint %}

## Concepts&#x20;

SA5's Data features have two facets-&#x20;

* **Data Sources** refer to the ability to assemble, store, and retrieve data from Collection Lists, remote sources, the querystring, webStorage, cookies, and more. The data is collected and organized into a Datastore.&#x20;
* **Data-Bound Elements** uses these sources to pull data into your page, text element, rich text templates, form fields, and more. It lets you easily display data from these sources using simple markup.&#x20;

Data Sources include;

* Collection-lists, which are prepared to allow&#x20;
* Arbitrary, static data-sets
* Query string params
* The current URL & URL parts
* LocalStorage items
* SessionStorage items
* Cookies

Data Binding can be applied to;

* Any text element&#x20;
* Rich text elements, to `{{ expand }}` macro content&#x20;
* Form input fields, specifically
  * Text fields, to set the current text input
  * Textarea fields, to set the current text input
  * Checkboxes, to set the checked/unchecked state
  * Select fields, to select the current item &#x20;

{% hint style="success" %}
The **Data Source** & **Data Binding** portions of Sygnal Attributes comprise probably the largest and most complex part of this library. They're used by themselves, but are also relied on by our other libraries. We'll be migrating and adding new capabilities as we go.  &#x20;
{% endhint %}

## Capabilities&#x20;

Here are some of the things SA5 Data can do;&#x20;

* Populate form fields from querystring params ( see [demo](https://data-binding.webflow.io/query?code=ID6079\&name=Trial+User+1\&color=blue\&accept=true) )
* Populate form fields to include the current URL or path ( see [demo](https://data-binding.webflow.io/url) )
* Populate form fields from webStorage or cookies, as part of a referral tracking system ( see [demo](https://data-binding.webflow.io/webstorage) )
* Populate form fields with current user data, such as the user's email & name
* Perform "mad libs" style text replacements in rich text content, mixed from Collection List fields and other sources ( see [demo](https://data-binding.webflow.io/cms) )&#x20;

## Use Cases

Some common use cases we see often;&#x20;

* Initialize form fields with data from query string params
* Track what page or collection item a form is submitted from
* Capture referral tracking codes from cookies, or webStorage, when a user signs up for a Membership &#x20;
* Display stored data like the currently logged in user's name or email&#x20;
* Expand template `{{ macros }}` in rich text to create dynamic content.&#x20;
* Extract CMS data and use it to perform special calculations or views, like a running total on a balance sheet&#x20;

In combination, these can be used to move data easily through your site, and to pass information back to your server in hidden fields as users submit forms.&#x20;


# Quick Start | SA5 Data & Data-Binding

How to Easily Add SA5's Data & Data-Binding to your Webflow Site

## How to Add the Library <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

{% hint style="warning" %}
**IMPORTANT** \
We've made some enhancements to SA5 Core in v5.5.0. \
If you are already using other SA5 Libs, please make certain to upgrade them to at least v5.5.0 as well to ensure cross-compatibility.&#x20;
{% endhint %}

[Add this script](/overview/how-to-add-custom-code) to the **site wide** custom code **HEAD** area of your site. If you are only using it on specific pages, you can addit to **page-level** custom code instead.

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Data & Data-Binding --> 
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/css/webflow-data.css"> 
<script src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/nocode/webflow-data.js"></script>
```

{% endcode %}

Add the custom attributes for the features you need, described in each feature separately. &#x20;

## SA5 Library Developers

For SA5 library developers, use the extended syntax to support [Sygnal Devmode](https://devmode.sygnal.com/) and [Sygnal DevProxy](https://engine.sygnal.com/devproxy).  These make it easier to develop and test code changes with Webflow sites.&#x20;

{% code overflow="wrap" %}

```html
<!-- Sygnal Attributes 5 | Data & Data-Binding--> 
<link rel="stylesheet" 
  href="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/css/webflow-data.css"
  dev-href="http://127.0.0.1:4000/dist/css/webflow-data.css"
  group="SA5 Data"
  > 
<script defer 
  src="https://cdn.jsdelivr.net/gh/sygnaltech/webflow-util@5.6.0/dist/nocode/webflow-data.js" 
  dev-src="http://127.0.0.1:4000/dist/nocode/webflow-data.js"
  group="SA5 Data"
  ></script>
```

{% endcode %}


# Usage Notes

Creatively merge data into your Webflow sites

SA5's Data features have two facets-&#x20;

* **Data Sources** refer to the ability to assemble, store, and retrieve data from Collection Lists, remote sources, the querystring, webStorage, cookies, and more. The data is collected and organized into a Datastore.&#x20;
* **Data-Bound Elements** uses these sources to pull data into your page, text element, rich text templates, form fields, and more. It lets you easily display data from these sources using simple markup.&#x20;

## Use Cases

The features here are meant to be very flexible, but here are a few common use cases we see often;&#x20;

* Populate form fields from querystring params ( see [demo](https://data-binding.webflow.io/query?code=ID6079\&name=Trial+User+1\&color=blue\&accept=true) )
* Populate form fields to include the current URL or path ( see [demo](https://data-binding.webflow.io/url) )
* Populate form fields from webStorage or cookies, as part of a referral tracking system ( see [demo](https://data-binding.webflow.io/webstorage) )
* Populate form fields with current user data, such as the user's email & name
* Perform "mad libs" style text replacements in rich text content, mixed from Collection List fields and other sources ( see [demo](https://data-binding.webflow.io/cms) )&#x20;

{% hint style="info" %}
In combination, these can be used to move data easily through your site, and to pass information back to your server in hidden fields as users submit forms.&#x20;
{% endhint %}

## Getting Started  <a href="#getting-started-nocode" id="getting-started-nocode"></a>

### STEP 1 - Add the Library  <a href="#step-1---add-the-library" id="step-1---add-the-library"></a>

First, **add the library** as detailed in [Quick Start](/sa5-data/quick-start).&#x20;

### STEP 2 - Apply `wfu-bind` to Desired Elements <a href="#step-2---apply-wfu-query-param-to-desired-elements" id="step-2---apply-wfu-query-param-to-desired-elements"></a>

Where you place the element and the value you use depend entirely on the element you want to bind and the data source.&#x20;

{% hint style="success" %}
For example, if you want the `name` querystring value to automatically intialize a form input, you would add the `wfu-bind=?name` attribute to your form input element.
{% endhint %}

## Usage Notes&#x20;

See [Data Sources](/sa5-data/data-sources) to learn about the many possible data sources.&#x20;

See [Data Binding](/sa5-data/binding-data) to learn about;

* The data binding syntax and how to connect a data source to an element&#x20;
* The effect the attribute has on different element types. &#x20;

<br>


# Concepts & Terminology

Concepts & Terms You Need to Know in SA5

Here are some useful concepts and terms to understand if you want to access the full range of capabilities in SA5 Data.&#x20;

## Data Sources&#x20;

### Simple v. Complex Data Sources

* A **Simple Data Source** returns a single value.&#x20;
* A **Complex Data Source** returns a structure.&#x20;
  * e.g. a JSON-LD source&#x20;
  * e.g. a Collection List source&#x20;

### Implicit v. Explicit Data Sources&#x20;

* An **Implicit Data Source** is one which is available automatically with no special definition or setup.
  * e.g. query string, URL parts, and cookies are all implicit data sources in SA5&#x20;
* An **Explicit Data Source** requires special setup before it can be accessed&#x20;

## Data Binding&#x20;

### Simple v. Complex Data Binding&#x20;

1. **Simple Data Binding ( 1:1 ).** Refers to simple situations in which a *single data item* is bound to a *single, simple element* such as a text element, a checkbox, or a form input element.  These are all characterized by 1:1 bindings.&#x20;
2. **Complex Data Binding ( n:1, 1:n, n:n ).** Refers to complex binding situations that involve *Collection List data sources*, bound to *complex elements* ( tabs, sliders, select options ) or *elements groups*. &#x20;

## Data Paths &#x20;

[Data Paths](#data-paths) describe specifically what you are binding to the data bound element.&#x20;

As an example, let's say that we want to do the following;&#x20;

> I want to initialize a form input field with the `name` value from the URL query string.&#x20;

{% hint style="success" %}
Since the [query string](/sa5-data/data-sources/url-query-params) is a Simple Data Source in SA5, there is no special setup required.&#x20;
{% endhint %}

To achieve this, we can simply add the following custom attribute on the Form Input element;

```
wfu-bind = $query.name
```

Here `$query.name`is the Data Path. It identifies the data we are binding.&#x20;

There are two parts, which are separated by the period (`.`) delimiter;

* `$query` is the Data Source Type&#x20;
* `name` is the Data Source Name&#x20;

### Abbreviated Data Paths&#x20;

Many commonly-used Data Source Types can be *abbreviated*.  In this example, the Query String Data Source Type can be replaced with a question mark `?`.&#x20;

This allows the attribute to be written even more efficiently as-&#x20;

```
wfu-bind = ?name
```

## Using Data Paths w/ Complex Data Sources&#x20;

SA5 data paths are designed for much more complex uses as well, such as navigating tabular and hierarchical data structures.&#x20;

{% hint style="warning" %}
This will be covered in special courses but you can find some introduction in the [Data Paths](/sa5-data/concepts/data-paths) documentation.&#x20;
{% endhint %}

use a simple, implicit data source such as a&#x20;


# Data Paths

How to Identify the Exact Data you want to bind

A Data Path is a string which identifies the specific data you want. Data paths are central to data-binding, to identify the data we want bound.&#x20;

You can think of it like a physical mailing address, or a URL.&#x20;

## Examples&#x20;

### Simple Data Paths&#x20;

A simple data path typically consists of a Data Source Type and a Data Source Name.&#x20;

e.g.;  &#x20;

<table><thead><tr><th width="196.33333333333331">Data Path </th><th width="170">Data Source Type</th><th>Value Source </th></tr></thead><tbody><tr><td><code>$query.foo</code> </td><td>Query string </td><td>The value of the query string param named <code>foo</code> </td></tr><tr><td><code>$cookie.foo</code> </td><td>Cookie</td><td>The value of the cookie named <code>foo</code></td></tr><tr><td><code>$local.bar</code> </td><td>Local storage</td><td>The value of the localStorage string with key <code>bar</code> </td></tr><tr><td><code>$session.bat</code> </td><td>Session storage</td><td>The value of the sessionStorage string with key <code>bat</code> </td></tr></tbody></table>

### Complex Data Paths&#x20;

Complex Data Paths involve referencing arrays ( typically collection lists ) or values within an object. &#x20;

e.g.; &#x20;

<table><thead><tr><th width="196.33333333333331">Data Path </th><th width="170">Data Source Type</th><th>Value Source </th></tr></thead><tbody><tr><td><code>$user.name</code> </td><td>User object</td><td>The User's name</td></tr><tr><td><code>$user.data.custom-url</code> </td><td>User object</td><td>The User's custom attribute named <code>custom-url</code> </td></tr><tr><td><code>$db.my-db.my-record.my-field</code> </td><td>Database</td><td>Describes a more complex referencing within an SA5 Data "Database". </td></tr></tbody></table>

### Database Data Paths&#x20;

{% hint style="info" %}
SA5's Database concept is being revisited based on use case feedback.
{% endhint %}

SA5 typically structures and references values using a named object array.&#x20;

e.g.&#x20;

```
$db.db-name.object-id...field-name 
```

Here we have;

* `$db` indicates the Data Source Type&#x20;
  * `db-name` is the Data Source Name, indicating which database, by name&#x20;
    * `object-id` is the specific named object, within the array.  In an SA5 Database constructed from a Collection List, this is typically the slug of the item.&#x20;
      * *( optional path )* within a complex object, you may need a path to navigate to the item&#x20;
        * `field-name` the name of the item you wish to bind to&#x20;

{% hint style="info" %}
Currently, Data Paths always identify a single point value, which can be bound to a single-value item such as a text element, an input field, a `{{ macro }}` expansion, etc. However in the future, it may also be used to retrieve full objects or even object-sets for list-binding and table-binding operations. &#x20;
{% endhint %}


# Complex Data Sources

Simple Binding does not require any special terminology as the data-binding target element is easy to identify, and the data source is easy to specify.&#x20;

The most popular simple-binding use cases are e.g. the binding of a querystring parameter to a form input element.&#x20;

{% hint style="success" %}
There are a lot of new things happening here, so terminology is stabilizing as we vector words a specific paradigm.&#x20;
{% endhint %}

## Datastore

The central SA5 object used for storing and retrieving data. Each piece of data loaded is catalogued within this object.&#x20;

*In general, all interactions with this object are automatically handled by the data-source and data-binding scripts. However you may interact with it if you are using custom code.*&#x20;

## Database ( or Data Table, or Data Object-Set )

Is a named collection of data stored in the database.&#x20;

Currently, it typically contains an array of records, such as the contents built from a Webflow Collection List.

{% hint style="info" %}
In the future, this will likely support custom object datastores, such as the cached results of an API query. &#x20;
{% endhint %}

### Data Row ( or Data Object )&#x20;

### Data Item&#x20;

An individual item.&#x20;

## Data Source

A **data source** is a more general name for a source of data.

### Static v. Dynamic Data Sources&#x20;

Sometimes that source is dynamic, and referenced on request. Other times it is pre-loaded, and then retrieved from that cache. Databases are a good example of a static source.&#x20;

* A Webflow Collection List
* A remote JSON source, such as a public API endpoint
* A remote CSV source, such as a Google Sheet

### Data Source Type

*

Data sources are typed, for example;&#x20;

* Collection list
* 3rd party public API
* Static CSV file&#x20;
* Static JSON file&#x20;
* JSON-LD&#x20;
* EXIF data&#x20;
* A remote JSON source, such as a public API endpoint
* A remote CSV source, such as a Google Sheet
* User data
* Local storage data
* Cookies
* Session storage data
* Query string data
* A remote database using a connection string&#x20;

### Data Source Name ( DSN )

Some Data Sources require a name to reference them, since you can have several of them.

In some places you'll see the abbreviation DSN used in attributes and docs. This refers to the unique name of a data source, which is used to locate the database within the datastore.&#x20;

##


# Developing Concepts 📝

Cool Stuff We're Thinking About

{% hint style="warning" %}
This section contains **internal team notes** on directions we're working. As it develops into complete features, we'll organize those features in the docs. Feel free to ask questions or make suggestions in the [forums](https://sygnal-attr.discourse.group/).
{% endhint %}

## Data path

### Targeted-Selectors

Datastore -> DatasourceType -> DSN -> Object ID -> Field Name&#x20;

Object Lists

Individual Objects

Individual Object fields

Object slice ( one field

Object slice subset&#x20;

## Architecture

Datastore is part of Core

Types are loaded as modules

Sources are added either automatically ( user login ) or manually ( datastore load callback )&#x20;

Dynamic v. Pre-Loaded&#x20;

? Query to e.g. google sheet &#x20;

<table><thead><tr><th width="149.33333333333331">Type</th><th width="271">DSN</th><th width="220">Object</th><th>Field</th></tr></thead><tbody><tr><td>data</td><td></td><td>x.y.z</td><td>key</td></tr><tr><td>data</td><td>from Collection List<br>?wfu-data-dsn</td><td>object id</td><td>key</td></tr><tr><td>data</td><td>from GSheet CSV<br>assigned name dsn<br>Specific sheet!</td><td>row <br>Spec identifies PK col<br>or numeric row is used</td><td>column</td></tr><tr><td>user</td><td>-</td><td>( SA5 user object )<br>.data</td><td>field</td></tr><tr><td>query</td><td></td><td></td><td>key</td></tr><tr><td>url</td><td></td><td></td><td>part</td></tr><tr><td>cookie</td><td>-</td><td></td><td>key</td></tr><tr><td>localStorage</td><td>-</td><td></td><td>key</td></tr><tr><td>webStorage</td><td>-</td><td></td><td>key</td></tr><tr><td>geoData</td><td></td><td></td><td></td></tr><tr><td>userAgent</td><td>-</td><td>( aspect )</td><td>( item ) </td></tr></tbody></table>

## Dynamic Fields

e.g. Image metadata&#x20;

? Is image EXIF preserved in the non-resized versions?&#x20;

? If so, can we get it, or disable CTRL+O&#x20;

## EXIF

RichText char count, word count...&#x20;

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>EXIF Reader</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.3.0/exif.js"></script>
</head>
<body>

<img src="path_to_your_image.jpg" id="myImage" alt="Your Image" />

<script>
    const image = document.getElementById('myImage');

    image.onload = function() {
        EXIF.getData(image, function() {
            const allMetaData = EXIF.getAllTags(this);
            const dateTime = EXIF.getTag(this, "DateTimeOriginal");
            
            console.log(allMetaData);  // Logs all EXIF data
            console.log(dateTime);     // Logs the original date and time the photo was taken
        });
    }
</script>

</body>
</html>

```

<https://codepen.io/memetican/pen/poQMNVo/0fb93debe7ff2da1aabe70186e1933b6>

## Tables/Records/Columns v. Sets/Objects/Fields

These are similar concepts and in most cases we'd relate this using a mental spreadsheet paradigm as Tables & Rows. However, the Sets and Keys and Objects paradigm seems more in line with some of the data we're working with;

* Objects with depth
* Objects containing arrays

This becomes even more relevant as we add other types of data to the Datastore, which does not follow the CMS's table-like composition.&#x20;

From a naming perspective &#x20;

#### Challenges;

Objects can contain arrays. Therefore we need to be able to resolve a DSD to a selector type, i.e. Set ( keyed or unkeyed ), Object, or Field.  or Invalid ( and in what way, for error logging )&#x20;

Table / Set / Class / Collection &#x20;

Record / Object / Tuple / Item&#x20;

Column / Field / ~~Attribute~~&#x20;

Field types

#### Advanced ideas;&#x20;

Stored Procs / Methods&#x20;

PK FK AK Identity&#x20;

Object paradigms; Inheritance, Methods, Base classes, Interfaces&#x20;

Classes ( object definitons )&#x20;

Attributes- use this to refer to META data regarding an item&#x20;

## Object Metadata

This is a feature we've wanted in Webflow's CMS for some time, and we may be able to add some of these features into SA5.

* Image width, height, type, filesize
* Video duration
* Text length and word-count and language&#x20;

## Keyed v. Unkeyed Datasources

{% hint style="info" %}
This distinction is currently under development. At present, only keyed sources are directly supported.&#x20;
{% endhint %}

In a keyed datasource, you can retrieve a specific item by reference. In an unkeyed datasource, you can only perform list-wide operations.

|                                   | Keyed Datasource         | Un-keyed Datasource |
| --------------------------------- | ------------------------ | ------------------- |
| Retrieve a specific object by key | Yes. E.g a CMS item slug | No                  |
| Retrieve object by position       | ?                        | ?                   |
| Count items                       | Yes                      | Yes                 |
| Sort items                        | Yes                      | Yes                 |
| Bind to a list                    | Yes                      | Yes                 |

## Data Shapes <a href="#data-shapes" id="data-shapes"></a>

WFU utilizes primitive structural patterns which we refer to as **data shapes**;

1. **Table.** A JSON array in which each element contains the same set of keys. Typically this is constructed from a tabular source such as a Webflow Collection List, or a CSV file.
2. **List.** A Table with only one or two fields per element. Lists are primarily used for data-binding to FORM controls.
3. **Dictionary.** A List, which has a `key` field and a `value` field. The key field content is expected to be unique. Dictionaries are used for lookups in template processing.
4. Media.

## Advanced DSNs

A DSN typically includes:

* The name of the database driver (for example, PostgreSQL, MySQL, Oracle, etc.)
* The hostname of the database server
* The port number to connect to (if it's not the default port for the database)
* The name of the database to connect to
* The username and password for the database (though for security reasons, it's often best to supply these separately rather than including them in the DSN)

The exact format of a DSN can vary depending on the database driver. Here's an example of what a DSN might look like for a PostgreSQL database:

A DSN is typically used when setting up a database connection in an application. The DSN is given to the database driver or connection library, which parses the string and uses the information to establish a connection to the database.

## Query Languages

SQL, GraphQL, XPath, etc.&#x20;

## Views

Pre-defined named data views that include filtering and sorting constraints

## Transform / Qualifier Suffixes

e.g. ? to indicate missing values = null&#x20;

## Logic

IF-THEN, for-each, loops...

## Formulas

Basic math in the form of evaluations&#x20;

## Caching

Indexes

Views

## Relational Concepts

1. **Tables (Relations)**:
   * These are the primary data structures in an RDBMS.
   * A table consists of rows and columns where each row represents a record and each column represents an attribute or field of the record.
2. **Indexes**:
   * These are data structures that improve the speed of data retrieval operations on a database table.
   * Common types of indexes include B-trees and hash indexes.
   * Indexes can be unique (enforcing the uniqueness of a value in a column) or non-unique.
3. **Views**:
   * A virtual table that represents the result of a stored query. Unlike tables, views don't store data; they display data from one or multiple tables through a specific query.
4. **Stored Procedures**:
   * These are sets of SQL statements that can be stored in the database and executed as a single unit.
   * They can take parameters and return results.
5. **Triggers**:
   * These are automatically executed (or fired) by the RDBMS in response to specific events on a particular table or view. Common events include insert, update, and delete operations.
6. **Cursors**:
   * These are database objects used to traverse the records in a result set one by one and potentially make changes to the records.
7. **Constraints**:
   * These are rules enforced on data columns of a table.
   * Examples include primary keys, foreign keys, unique constraints, and check constraints.
8. **System Catalog**:
   * This is a collection of tables and views that contain metadata about the RDBMS, detailing all other objects in the database, their structure, and various attributes.
9. **Transaction Logs**:
   * These are logs that keep a history of all transactions and changes in the database. They are crucial for ensuring data integrity and for recovery operations.
10. **Data Files**:

* Physical files on disk where the actual data from tables and other objects is stored.

11. **Control Files**:

* These are files that contain metadata about the physical structure of the database, including the database's creation date, the location of data files and redo log files, and the current log sequence number.

<br>




---

[Next Page](/llms-full.txt/1)

