Insights

Getting Started Using Coveo Atomic

With Examples

Get Started With Coveo Atomic

I've been reading up on Coveo Atomic for a few months now. Got so into the weeds that I never realized 3 new versions had come out since talking about v1.0.4. This is a good sign, and mildly concerning. Why? Well, things change and if you are using the "latest" JavaScript version you could easily find yourself struggling with something that has already been deprecated.

Version Management

So what are your options? Well, you can select to use the latest version. As shown below:

<script type="module" src="https://static.cloud.coveo.com/atomic/latest/atomic.esm.js"></script> 
<link rel="stylesheet" href="https://static.cloud.coveo.com/atomic/latest/themes/coveo.css"/> 

Nothing wrong with doing so if you like living on the edge. However, if you have intentions of using this for a commercial project, I strongly suggest picking a version and sticking with it. That is if it is one that meets your needs.

Here's how you would target major versions:

<script type="module" src="https://static.cloud.coveo.com/atomic/v1/atomic.esm.js"></script> 

And if you want to say target a specific minor version you can do this:

<script type="module" src="https://static.cloud.coveo.com/atomic/v1.8/atomic.esm.js"></script> 

NPM Support

Now, if you want to use npm, you can indeed setup your project that way. Is it required, absolutely not. All depends on your preferences. You can access Coveo Atomic via npm via the following:

npm install @coveo/atomic and find the resources under: /node_modules/@coveo/atomic/dist/atomic

To use it with a modular bundler, be sure to use either require('@coveo/atomic') or import '@coveo/atomic'

Let's Get To The Example

If you've followed a few of my Coveo articles you've undoubtedly noticed I try to find a fun source for my data. And thanks to previous articles, I'm utilizing a source I recently created. Yes, we're talking about The Movie DB and thanks to Feel The Power Of Coveo's Generic Rest API, we have a ton of ready-to-use data to play with.

So let's dive in and see how I achieved this in only a few hours of playing around.

Screenshot of Coveo Atomic with Batman results.

Is this perfect? Absolutely not. And honestly, no where near done, but I learn by seeing examples and I wanted to get an example out to you because there are areas I struggled with getting it setup (initially). So my hope is by sharing how I achieved this I'll save you some time.

Step 1 - Setting Up The Head Tag

I did utilize Bootstrap CSS for some of the formatting, but you can't use it against the result list templates (I will explain why later). So what does the head tag look like? Here you go.


<head>

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>

<script type="module" src="https://static.cloud.coveo.com/atomic/latest/atomic.esm.js"></script> 
<link rel="stylesheet" href="https://static.cloud.coveo.com/atomic/latest/themes/coveo.css"/> 

<script>
    (async () => {
        await customElements.whenDefined('atomic-search-interface');
        const searchInterface = document.querySelector('#search');
        await searchInterface.initialize({
        accessToken:'*****', 
        organizationId: '*****' 
        });
        searchInterface.executeFirstSearch();
    })();
</script>
...
</head>

There are three sections.

  1. Bootstrap setup - This is just for getting some grid structure into the overall page.
  2. Coveo Atomic setup - Utilizing the latest version
  3. Search initialization - This is the meat. Here you'll see how we identify the section and initialize the searchInterface. If you've used Coveo JSUI this should be very familiar to you.

I will get into styling in a bit, as that's where the complexity really comes in.

Step 2 - Setting Up The Body Tag

The body tag will contain the full search interface. I've also included some basic grid structure to align things like facets and the result list.

Have a quick look below at what the HTML structure looks like. After, we will go more in-depth with what all is being used.

<div class="container">
    <div class="row">
        <div class="col">
            <atomic-search-interface id="search">
                <div class="row">
                    <div class="col-12">
                        <div class="search-bar">
                            <atomic-search-box></atomic-search-box>
                        </div>
                    </div>
                </div>
                <div class="row search-results">
                    <div class="col-3">
                        <atomic-facet-manager>

                            <atomic-category-facet field="mdb_genres" label="Genres"  with-search></atomic-category-facet>
                            <atomic-facet field="mdb_release_year" label="Year" display-values-as="box" sort-criteria="occurrences" ></atomic-facet>
                        </atomic-facet-manager>
                    </div>
                    <div class="col-9">
                        <atomic-sort-dropdown>
                            <atomic-sort-expression label="relevance" expression="relevancy"
                            ></atomic-sort-expression>
                            <atomic-sort-expression label="most-recent" expression="mdb_release_date descending"
                            ></atomic-sort-expression>
                          </atomic-sort-dropdown>
                        <atomic-result-list display="list" image-size="small" fields-to-include="mdb_release_year,mdb_release_year,mdb_poster_url,mdb_description,mdb_genres,source, mdb_release_date,language, filetype, ytthumbnailurl">
                            <atomic-result-template>
                                <template>
                                   ...
                                    <div class="movie-result">
                                        <atomic-result-section-visual class="movie-result_poster">
                                            <atomic-result-image field="mdb_poster_url"></atomic-result-image>
                                        </atomic-result-section-visual>
                                        <div class="movie-result_details">
                                            <atomic-result-link class="movie-result_title"></atomic-result-link>
                                            <atomic-result-text class="movie-result_description" field="mdb_description"></atomic-result-text>
                                            <atomic-result-multi-value-text field="mdb_genres" delimiter=" "></atomic-result-multi-value-text>
                                            <div class="movie-result_release-date">Release Date: <atomic-result-date field="mdb_release_date"></atomic-result-date></div>
                                        </div>
                                    </div>
                                </template>
                            </atomic-result-list-template>
                        </atomic-result-list>
                    </div>
                </div>
                <div class="row col pagination">
                    <atomic-load-more-results></atomic-load-more-results>
                </div>
            </atomic-search-interface>
        </div>
    </div>
</div>

Let's break it down. Again, we will touch on styling near the end.

atomic-search-interface

This is the primary wrapper for the search experience. We used the #search identifier which is how the initialization points to and sets up. This also allows for multiple search areas on a page if needed.

atomic-search-box

As you might expect, this is the search box. Not doing anything special here, but obviously you can do more if desired using type ahead, query suggestions, etc.

atomic-facet-manager

This is the wrapper area for all facets.

atomic-facet & atomic-category-facet

Here we're trying to represent some of the facet information in different ways.

The atomic-facet is your tried and true Coveo facet. You can display it in a few different forms, here I've done it as a boxes.

The atomic-category-facet is the facet you're most likely to see on a search page. Here we're using it to display all the genres present in our data.

atomic-sort-dropdown

Pretty self explanatory but this is where we create a sort drop-down menu with the options identified by atomic-sort-expression. In our case we have Relevancy and sort by Release Date.

atomic-result-list

This is where, honestly, I struggled a bit. Not so much on setting up the appropriate hierarchy but the styling.

Inside the atomic-result-list tag we immediately have atomic-result-template and template. This is where we are defining how the result looks. And yes, you can have multiple templates based upon conditions. Because I only have a single set of data, I've chosen to just use one but in a future article we will get a bit more creative.

atomic-result-section-visual

This is the area of the result that provides visual information, such as our movie poster.

atomic-result-image

We use this tag to get our movie poster to show. The field value is the URL to the image in question.

atomic-result-link

This is the title and the link to the page (if we had one) for our data.

atomic-result-text

This allows us to show the movie description. It's a simple field text display.

atomic-result-multi-value-text

As you might expect, this creates a list from a multi-facet. e.g. Action; Drama;

Styling - What You've Come Here For

Let's be honest here. What I've gone through above you probably already knew. What you want to understand is how I got the above to look like what's in the image above. Looking back there's nothing crazy going on but there's an important concept to understand. The Shadow DOM.

What Is The Shadow DOM?

You can find a more in-depth description of how the Shadow DOM operates here but in general it's a way of encapsulating aspects of the DOM and essentially "shielding" them from outside influence. In terms of Coveo Atomic, it's used to prevent outside CSS from interfering with the operation of a search page. Ever built a Coveo search page in JSUI and someone who styles the whole site changed how an <li> tag is styled? It hurts, doesn't it. Well Coveo is trying to save you the pain by utilizing the Shadow DOM to protect the pieces of a search page from being broken.

Where Does Styling Go?

So what I've found, and keep in mind this could very well change as Atomic progresses, that it happens in two main areas.

Inside The <head> Tag

This is your primary CSS. But when it comes to targeting portions of the DOM we need to understand that not everything can be styled directly. If you want a full level of capabilities you're going to want to use Headless.

Let's look at an example.

:root{
    --atomic-font-family: 'Montserrat', sans-serif;
    --atomic-text-base: 1.1rem;  
    line-height:1.2rem;
}

atomic-search-box::part(input){
    width:800px;
}
atomic-facet::part(values){
    flex-wrap: wrap;
    display:flex;
    flex-direction:row;
    justify-content: space-between;
}
atomic-facet::part(value-box){
    min-width:55px;
    display:flex;
}

atomic-sort-dropdown{
    margin-bottom: 20px;
}

When it comes to styling the Atomic DOM you will be primarily focusing on using the ::part functionality. If you inspect the DOM using Dev Tools, you'll see Coveo has used the part attribute throughout, but not at every single level. In order to style the structure of your search interface you'll need to use these to target specific elements of your page.

The other thing to know is how Coveo utilizes CSS variables as a way of "theming" your search page. By altering specific CSS variables, you can very quickly transform a page without complex CSS magic. You can find a full list of variables here

An important thing to note. You CANNOT style a result list inside the <head> tag. Maybe it will change in the future? But right now, let me save you the trouble. It turns out not to be terribly complicated, but because of the use of the Shadow DOM, you have to place the result styling in a specific location. Inside the template tag within the atomic-result-template itself.

Styling The Result List

As mentioned, you have to place result specific styling inside the template tag. Let me show you.

<atomic-result-template>
    <template>
        <style>
            .movie-result{
                display:flex;
                flex-direction: row;
            }

            .movie-result_poster{
                max-height:200px;
                display:flex;
                margin-right:20px;
                max-width:25%;
                min-width:25%;
            }

            .movie-result_poster img{
                object-fit: contain;
                overflow: hidden;
            }

            .movie-result_details{
                display:flex;
                flex-direction: column;
            }

            .movie-result_title{
                display:flex;
                font-size:1.5rem;
                font-family:'Lato', sans-serif;
                line-height:1.75rem;
            }

            .movie-result_description{
                display:flex;
                color:#999;
            }

            .movie-result_release-date{
                display:flex;
                color:#999;
                margin-top:15px;
            }

            atomic-result-multi-value-text::part(result-multi-value-text-value){
                background-color:#ddd;
                border-radius:5px;
                padding:10px;
                margin-top:15px;
                margin-right:15px;
            }

            atomic-result-multi-value-text::part(result-multi-value-text-separator)::before{
                content:'';
            }
        </style>
        ... 
    </template>
</atomic-result-template>

Here we can do traditional CSS targeting aspects of our custom structure along with targeting specific areas of Atomic's deeper Shadow DOM.

In Summary

I've covered a ton of ground here. Expect more articles diving deeper into things like the facets, result list capabilities, and all the other things that Atomic can do really, really easily.

Hey, Developers!

We're on the look out for talented developers to join our team.

Think you have what it takes?

Meet David Austin

Development Team Lead

📷🕹️👪

David is a decorated Development Team Lead with Sitecore Technology MVP and Coveo MVP awards, as well as Sitecore CDP & Personalize Certified. He's worked in IT for 25 years; everything ranging from Developer to Business Analyst to Group Lead helping manage everything from Intranet and Internet sites to facility management and application support. David is a dedicated family man who loves to spend time with his girls. He's also an avid photographer and loves to explore new places.

Connect with David