# CSS & JS

## 1. General aspects

Every _View_ or _Card_ may have its own CSS and JS code.&#x20;

To reduce the FCP (First Contentful Paint) metric, CSS code is added within the head tag (at the top of the page), while JS code is added as `<script type=module>` before the closing "body" tag (at the bottom).

**Note:** Advanced Views performs basic minification of CSS and JS code before adding it to the page (by removing unnecessary spacing), so you don't need to worry about it.

### 1.1) Just-in-Time assets

_Views_ and _Cards_ are modular, and Advanced Views uses the Just-in-Time strategy for assets. This means that each page will have CSS and JS code only for the items that appear on that page.&#x20;

This eliminates the need to worry about bundling, allowing you to develop templates that can be mixed by editors in any order without enqueuing them site-wide.

{% hint style="info" %}
Thanks to the modular approach, you can even use TS/Sass or Tailwind to create independent assets, while Advanced Views will take care of the bundling itself. Read this page to learn how to setup TS/Tailwind properly.
{% endhint %}

## 2.How to use it

Add a _View_ and assign fields as usual. ([See Creating your first View](../getting-started/introduction/creating-your-first-view).)

On the _View_ edit screen, switch to the _CSS & JS_ tab.

**Insert** or Paste your CSS styles in the _CSS Code_ field.

**Click** on the **Update** button to publish your changes.

<figure><img src="https://2293383029-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FN2nAC4pTDjEmDu8ZkLB2%2Fuploads%2FcuIJ1YSkw6TCDiy96QPi%2Fcss-edit.png?alt=media&#x26;token=d4ca8532-f6bf-47c7-92c1-ce419c150767" alt=""><figcaption></figcaption></figure>

## 3. CSS Code

### 3.1) Selectors

Use the 'Inspect' tool of your browser to identify which class to style. See below for magic shortcuts so you won't need to write repetitive class names when defining your styles.

**Magic shortcuts are available** (and will use the BEM Unique Name if defined) :

<table><thead><tr><th width="191">Magic shortcut</th><th>Replaced with</th><th>BEM Unique Name</th></tr></thead><tbody><tr><td>#view</td><td>.acf-view--id--X</td><td>.bem-name</td></tr><tr><td>#this__</td><td>.acf-view__</td><td>.bem-name__</td></tr><tr><td>#view__</td><td>.acf-view--id--X .acf-view__</td><td>.bem-name .bem-name__</td></tr></tbody></table>

We recommend using `#view { #this__first-field { //... }, #this__second-field { //... } }` format, which is possible thanks to the [built-in CSS nesting](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting).

{% hint style="info" %}
Note: These magic shortcuts only work if they are placed in the CSS Code field/file of your View.
{% endhint %}

#### In Practice

Display fields and their labels on one line using:

```css
// universal 
// Display fields and labels inline.
#view { 
   > div {
      display: flex;
      gap: 10px;
   }
}
```

Or alternatively turn on the 'Add Classification Classes' setting in the **Template** tab, and use the below CSS:

<pre class="language-css"><code class="lang-css">// when adding classification classes
// Display fields and labels inline.
#view {
<strong>   #this__row {
</strong>      display: flex;
      gap: 10px;
   }
}
</code></pre>

**Find classes in the Default Template**

Once your _View_ has been published/updated, the Twig template will automatically be generated and can be seen in the **Template** tab.

See the 'class=""' attribute of the Twig template for each field. E.g. class="acf-view\_\_name-link".&#x20;

<figure><img src="https://2293383029-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FN2nAC4pTDjEmDu8ZkLB2%2Fuploads%2FXKuXG3Hz7WMSKIrXH2fg%2Ffind-css-classes.jpg?alt=media&#x26;token=74197e15-4f7b-4149-ba0f-6db654feaf1a" alt=""><figcaption></figcaption></figure>

Then define the style in the **CSS Code** field on the **CSS & JS** tab.

**Example:**

```
// add border to image
.acf-view__category-image {
    padding: 3px;
    border: 1px solid #cecece;
}
```

### 3.2) Enqueuing library CSS

To add complex interactive elements, like sliders, you'll need to use some libraries, which comes with their own CSS files. If you use the Pro edition, read about the [Front-end assets management](../display-content/front-end-assets-management-pro) to see the list of built-in libraries.

If you use the Lite edition, or want to employ a specific library that is missing from the built-in list, you need to enqueue it on the target page.

The classic enqueuing in WordPress happens with the [wp\_enqueue\_style](https://developer.wordpress.org/reference/functions/wp_enqueue_style/) function, but to avoid sitewide enqueueing (which is bad for performance), it requires manual detection of the target pages.

That's why we recommend to use the CSS [@import feature](https://developer.mozilla.org/en-US/docs/Web/CSS/@import) instead:

```css
@import url('/wp-content/themes/mytheme/assets/slider/slider.css');
```

Just add the import to the top of your _View_ or _Card_ CSS code, and the browser will load this library on pages that require it. Don't worry if you have several components or multiple instances of the same component on a single page. According to the standard, the browser will download the library only once.

**Note:** We recommend bypassing the domain name and starting the path from the `/wp-content` folder. This ensures that the import works dynamically, regardless of the current domain (e.g., staging and live environments).

### 3.3) Sass usage

In Advanced Views, there is a [File System Storage](file-system-storage) option. If you enable it, you can utilize Sass for _View_ and _Card_ CSS code. Check [this page](https://docs.acfviews.com/templates/file-system-storage#sass-and-typescript-usage) for details.

### 3.4) Tailwind usage

In Advanced Views, there is a [File System Storage](file-system-storage) option. If you enable it, you can utilize Tailwind for _View_ and _Card_ CSS code. Check [this page](https://docs.acfviews.com/templates/file-system-storage#tailwind-usage) for details.

## 4. JS Code

{% hint style="warning" %}
**Syntax note:** Do not omit semicolons at the end of lines. JavaScript policy allows it only if you use line breaks afterward, but these line breaks will be removed when the JS is compressed by the framework (on the fly). If you skip a semicolon, it will cause a JavaScript error.
{% endhint %}

### 4.1) Web Components

#### A) Classic Web Components

Every _View_ and _Card_ may have its own JS code. The Advanced Views Framework allows you to employ [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) without any extra actions. The plugin, out-of-the-box, creates a unique tag wrapper for every _View_ and _Card_. As soon as you add some JS code, Advanced Views creates a [Web Component](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define) for the current instance and places your JS code inside.

{% hint style="info" %}
Tip: If you need to add global JS, you can set the 'Web Component Type' setting of your _View_ or _Card_ to 'None'. Additionally, you can configure the default setting for new _Views_ and _Cards_ inside the Defaults tab of the Framework settings.
{% endhint %}

This means your code will be automatically executed for every _View_ or _Card_ instance appearing on the page. For example, let's say we have a _View_ and want to track clicks on links inside.

Without Web Components, your JS code might look like this:

```javascript
document.addEventListener('DOMContentLoaded', function () {
  document.body.querySelectorAll('.my-object').forEach((obj) => {
    obj.querySelectorAll('a').addEventListener('click', function () {
      // some stuff here
    });
  });
});
```

With Web Components, your code is called for every instance on the page and has the 'this' context. So, you can write it like this:

```javascript
this.querySelectorAll('a').addEventListener('click', function () {
  // some stuff here
});
```

{% hint style="info" %}
Web Components are much shorter and a more convenient way to add custom JS code, especially when dealing with Ajax-loaded content.&#x20;
{% endhint %}

In case of the Ajax-loaded content, without Web Components, you would need to employ the [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to watch for added nodes, find target elements, and add listeners. With Web Components, no extra actions are required. The code above will be called for every new instance out of the box!

{% hint style="info" %}
**Behind the Scenes:** If you wonder how it works, below you can see a boilerplate used by AVF. It saves you from writing this manually each time.\
**Note:** For Web Component Type Classic, this template is skipped if your JS code field has no content.
{% endhint %}

```javascript
// your component name is dynamically put here
class MyComponent extends HTMLElement {
    connectedCallback(){
        "loading"===document.readyState?
            document.addEventListener("DOMContentLoaded",this.setup.bind(this)):
            this.setup()
    }
    setup(){
        // content of your JS code field is dynamically put here
    }
}
// your component name is dynamically put here
customElements.define(MyComponent, 'my-component');
```

**Note about JS imports**: Don't worry, your JavaScript can still use the [module imports feature](https://javascript.info/modules-intro). AVF will dynamically move them to the top of the module. See the 'Employing JS Libraries' section below for details.

#### B) Web Components with the Shadow DOM

By default, web components are initialized in the non-shadow (classic) way, meaning they act like any other HTML tags. This entails that all global CSS rules (like `p { color: blue; }`) will be applied to the inner elements if they match.

The Advanced Views provides the 'Shadow DOM' options, which utilizes the ['shadow DOM' feature](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow) of web components. This turns your element into an independent entity, preventing the application of any global styles to its internal items. It's akin to drawing on a clean canvas, where only _Views_ CSS is applied.

{% hint style="info" %}
The shadow DOM option changes the selector of the element in JavaScript from 'this' to 'this.shadowRoot'. So if you have any JavaScript, don't forget to make this change, as the element and its children are now available solely under the 'this.shadowRoot' selector.
{% endhint %}

This feature can be immensely useful in various scenarios.&#x20;

**Consider the following:**

If the website you are working on is built properly, using a modular approach, it will function seamlessly. However, what if you need to add a new element to an existing website that is constructed without adherence to any specific rules, and has an abundance of global styles that introduce unwanted effects to your new element?

The straightforward solution is to override these styles. However, this process can be time-consuming, and in practice, crafting custom styling for a plain 'ul' might take 20 minutes instead of the usual 3 minutes on a website that follows best practices. This setting circumvents such hassle, allowing you to sidestep the overriding process entirely.

**Shadow DOM types**

There are two ways to create a shadow DOM (with a declarative template or with JavaScript). AVF supports both methods. While both allow encapsulating the markup and CSS, they have some differences. To pick the best option for your case, check the comparison table below.

| Type                   | Description                                                                                                                                                                                                                                                                     | Best use cases                                                                                                                | Limitations                                                                                                                                                                                         |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Declarative Shadow DOM | Made using the [\<template>](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#declaratively_with_html) tag, which is added on the server side. The element will be rendered before the JavaScript is loaded.                                    | _Views_ and _Cards_ that are available on the page since loading.                                                             | Parsed only once, on page load. This means elements with this option can't be added later, e.g., via AJAX (like _View_ inside the "Load More" of _Card_).                                           |
| JS Shadow DOM          | Made using the [attachShadow](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#imperatively_with_javascript) method call. The element will be rendered as plain HTML and turned into a Shadow DOM on the client side when JavaScript is loaded. | _Views_ inside a _Card_ with the pagination feature active, or any _Views_ and _Cards_ added to the page after it has loaded. | May cause minor layout shifts or visual differences during loading, as all the page styles are applied to the element before JavaScript is loaded and are disabled only after JavaScript is loaded. |

#### C) Global styles (e.g. Tailwind) and Shadow DOM

Tailwind styles are dynamically merged on the page level to avoid class duplication. This means that by default, they won't be included inside elements with the WebComponent Shadow type. However, you can use a workaround to achieve the same experience.&#x20;

Let's review the following case: we have a page and need to add four elements styled with Tailwind. Meanwhile, we need to avoid Tailwind styles appearing at the page level (the default behavior). To achieve this, we can wrap our elements in a _View_ and define a special comment, which will instruct Advanced Views to place the page styles there. See the example below:

```html
<my-page>
    <template shadowrootmode="open">
        <!--advanced-views:styles/custom-location-->
        <div>
            <div class="flex flex-col font-lato">
                [avf_view name="One block" view-id="669137a4e4654"]
                [avf_view name="Another block" view-id="669138190e3ca"]
            </div>
        </div>
    </template>
</my-page>

```

In this case, we can use Tailwind for any elements, _Views_ and _Cards_ inside this element, and Tailwind CSS styles won't appear at the page level.

{% hint style="info" %}
**Pro tip:** You may need to insert a third-party element between the elements. For example, some form that is already styled by the theme or plugin and needs to be available for JavaScript globally. For this case, you can use the [Slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots#adding_flexibility_with_slots) feature, as shown in the example below:
{% endhint %}

```html
<my-page>
    <template shadowrootmode="open">
        <!--advanced-views:styles/custom-location-->
        <div>
            <div class="flex flex-col font-lato">
                [avf_view name="One block" view-id="669137a4e4654"]
                <slot name="ninja-form"></slot>
                [avf_view name="Another block" view-id="669138190e3ca"]
            </div>
        </div>
    </template>
    <div slot="ninja-form">[ninja_form id=1]</div>
</my-page>

```

In this case, the content inside the slot will be inserted between the elements but will remain at the page level, making it available for any global CSS and JavaScript.

### 4.2) WordPress Interactivity API

Check [this guide](css-and-js/wordpress-interactivity-api) to see how to use WP Interactivity API.

### 4.3) Employing JS libraries

To add complex interactive elements, like sliders, you'll need to use some JS libraries. If you use the Pro edition, read about the [Front-end assets management](../display-content/front-end-assets-management-pro) to see the list of built-in libraries.

If you use the Lite edition, or want to employ a specific library that is missing from the built-in list, you need to:

1. Enqueue it on the target page
2. Add code to the JS field of the _View_ or _Card_ to create the instance.

The classic enqueuing in WordPress happens with the [wp\_enqueue\_script](https://developer.wordpress.org/reference/functions/wp_enqueue_script/) function, but to avoid sitewide enqueueing (which is bad for performance), it requires manual detection of the target pages.

That's why we recommend harnessing [the modules feature](https://javascript.info/modules-intro). So, you add `import x from "/wp-content/themes/theme_name/assets/library.js"` or just `import "/wp-content/themes/theme_name/assets/library.js"` to the top of the JS code, and the browser will automatically load the library only for the necessary pages.&#x20;

You shouldn't worry about duplications; according to the standard, the browser will import the script only once, even if the same import statement appears in several different _Views_/_Cards_.

**Notes on the modules**

1. You must use the correct path to the JS library\
   **Tip:** use a relative path - we recommend bypassing the domain name and starting the path from the `/wp-content` folder. This ensures that the import works dynamically, regardless of the current domain (e.g., staging and live environments).
2. You **can** use it along with the Web Components\
   According to the JavaScript policy, all imports must be placed at the top of the module. AVF adheres to this policy, so before wrapping your code into a WebComponent, AVF extracts the imports to ensure they remain at the top of the module. &#x20;

### 4.4) TypeScript usage

In Advanced Views, there is a [File System Storage](file-system-storage) option. If you enable it, you can utilize TypeScript for _View_ and _Card_ JS code. Check [this page](https://docs.acfviews.com/templates/file-system-storage#sass-and-typescript-usage) for details.
