Skip to content

Template Developer Resources

These guides are technical and code guides to the architecture of the Lucit Templating engine for Digital OOH

Intended Audience

This document is intended for developers and technical partners who: * Are familiar with HTML, CSS, and JavaScript * Want to build or extend templates using the Lucit Template Designer * Need to understand how Lucit renders and delivers creatives to DOOH players * Want to design tools and custom integrations that work with Lucit templates.

Overview

In Lucit, we generate dynamic creatives and deliver those creatives via campaigns to third-party player / cms systems

The process, internally flows like this

  1. A template is created in the Lucit Template Designer
  2. This WYSIWYG editor produces HTML, CSS and Javascript under the hood
  3. An ad is posted from this template to a campaign
  4. The campaign goes through a Build process that converts the HTML for an ad to a JPEG, which is sized exactly for and, when necessary, targeted to each individual screen
  5. When a request from a screen comes into lucit (via the third-party player system), the Lucit "Puller" will select the best JPEG creative to deliver back to the player
    • If the player supports HTML, we can optionally, deliver a completed HTML file with all necessary data to the player - useful for animations or real-time updating ads
  6. The player reports back a Play Report (or a Pingback ) indicating that a play has occured

The purpose of this document is to describe the technical details of the HTML, CSS and Javascript, as well as the rendering engine that wraps those files in order to generate a creative.

Some Core Concepts

  • A Template is a container for the HTML, CSS and Javascript that is used to generate dynamic ads. These are created by most uses in the Template Designer in Lucit which offers a WYSIWYG environment for designing
  • A Build is when a campaign build process takes a template, relevent data, combines them and generates any required JPG's for the ad. In Lucit, builds are triggered whenever data or template content changes, or, when a user uses the Refresh Data action on the campaign page
  • A Puller is the Lucit component that receives the request from a third-party player, and then delivers the correct ad based on the request. The request typically includes a campaign identifier of some kind, and a screen identifer.
  • A Snapshotting Service takes HTML and created JPG's at specific height/widths

Build Process Render

When a campaign builds, it will render a creative using a server side process by taking the following basic steps

  1. Get the HTML, CSS and Javascript for a template
  2. Replace any macros in the files with their actual values
  3. Wrap the HTML, CSS and Javascript in our render-app
  4. Create a final, single HTML string containing all HTML, CSS and Javascript
  5. Deliver that HTML to a snapshotting service which then uses a headless chromium browser to take a snapshot of the rendered content at the appropriate size (screen format)
  6. Return a JPG that we store that can later be sent down to the player when requested

Why this process is superior to other solutions

This process offers the following significant advantages

  1. Players only need to support displaying JPEG files and they can support our dynamic content
  2. All API calls are buffered and cached within the Lucit system so macro replacement does not depend on third-party network api calls
  3. For players that support HTML, they are presented with an HTML package that is very very small (Kilobytes, not megabytes)
  4. Our HTML package renders, typically, sub 250ms, or, less than 1 second when including the network call and response from the player
  5. For players that want HTML for more real-time rending and animation, the HTML is already ready to deliver in that requested format

Single Template, Multiple Screen Formats (Sizes) philosophy

Lucit follows a Single Template for all sizes approach. We accomplish this by having separate CSS for every screen format, which is then activated using CSS Media Queries. This allows us to have a single file that can render the creative in all sizes

For example, a Bulletin sized billboard, a Poster sized billboard, and Vertical sized urban panels, can all share the exact same Lucit Template, but during rendering, the appropriate CSS media query rules will render the creative for the correct screen format. See Screen Formats

Editing Template Code

In the Template Editor, you can edit the underlying code for the template in the following ways

Global Code Editor

Templates -> {Template} -> Canvas -> Actions Bar -> <> Icon will open the Code Editor dialog which will contain the following 5 tabs

  • HTML the HTML of the template
  • CSS The Global CSS for the template, OR if in Individual Edit Mode, the CSS for the currently selected Screen Format
  • JS the Global Javascript for the template
  • CONSOLE a debugging console with messages, logs and error / exception messages.
  • HELP Simple help guides for writing custom Javscript functions and a complete list of all available macros that you can add to your template

Invidual Elements

You can also edit the underlying HTML and CSS for a specific element by taking the following steps

  • Right Click on the element
  • Choose Edit CSS / HTML

OR

  • From the layers panel, find the layer you wish to edit, click the 3VerticalDotsIcon and choose Edit CSS / HTML

In the Code Editor dialog you can edit the CSS and HTML specifically for this element

HTML and Layers

Lucit templates support layers by organizing the HTML into a series of div elements that are all siblings of each other. These elements are then positioned using CSS Positioning

Each layer on the template is its own div

At render time, the template HTML is then injected into a container div that is provided by Render-App

CSS

CSS content is broken into a few groups

  • Render-App CSS
  • Global CSS (used for templates that are typically single size)
  • Size Format CSS Invidial CSS overides for each size format that are wrapped in Media Queries based on the aspect ratio for that size

Javascript

Custom javascript is added to Lucit templates in a controlled way via function registration. By default, the Template will reject and not process any javascript that is not registered using these functions.

We support registering functions as follows :

  • Designer Functions These accept a name, some data, and, return a single string value. These are used when building Custom Fields - See Custom Fields Guide
  • Text Formatting Functions These functions accept an name, DOM element, a data-value, and can operate on the element These also appear in the Format drop-down in the Template Designer user interface. See Text Formatting Functions Guide
  • Designer Formatting Functions These accept a DOMElement, a data value, a data object, element settings values, and a css selector. These are powerful functions designed to operate on one or more elements matching the css selector

By this method, we can reduce rendering issues at runtime that are caused by javascript issues, create safer packages, and reduce risk of lockups, "bad code" causing problems.

Macros

Most macros take one of the following syntaxes

  • Single Curly Brace {object.something.macro} Use in CSS and HTML
  • Double Curly Brace {{object.something.macro}} Use in custom functions and javascript to prevent escaping issues

Macros can be added to the following components in a template

  • HTML
  • CSS
  • Javascript
  • Custom Fields

These macros are resolved to actual data during the build phase. See the Field Guide Reference

Simple Weather Example

Here is an example of the HTML and CSS for a simple 1 size format template that contains

  • A background
  • The current temp
  • The current condition icon
  • The city name of the location of the sign this ad is running on

You will note that some of what you might expect to be there in the HTML and CSS is not there. This is because, this HTML will be wrapped in the HTML, CSS and Javascript from the Render App which will provide the proper positioning container, styles, and even some standard formats.

<div
  id="rectangle_default_bg"
  title=""
  x-objectcode="rectangle"
  class="lc_ut_designer lc_dt_object lc_dt_default_background"
  x-fieldname="rectangle"
  x-optional-field="false"
  x-element-settings="{}"
  x-element-setting-values="{}"></div>

<div
  id="obj_red_salvation_0km0e8h5"
  title="Red Salvation"
  x-objectcode="red_salvation"
  class="lc_ut_designer lc_dt_element lc_dt_object lc_dt_image_full_size_stretch"
  x-fieldname="red_salvation"
  x-optional-field="false"
  x-element-settings="{}"
  x-element-setting-values="{}"></div>

<div
  id="data_source_text_z56ekild"
  title=""
  x-objectcode="digital_board_store_Weather1_current_temp_f"
  class="lc_ut_designer lc_dt_data lc_dt_text lc_format_fit_text lc_format_degrees"
  x-placeholder="27"
  x-optional-field="false"
  x-fieldname="Current Temp F"
  data-value="{digital_board.store.Weather1_current_temp_f}"
  x-group-parent-id="LCUID-LF-00b5f4b3-2f31-4fdc-9df2-c82b116b9c51"
  x-element-settings="{}"
  x-element-setting-values="{}">
  {digital_board.store.Weather1_current_temp_f}
</div>

<div
  id="data_source_img_qv8h6m36"
  title=""
  x-objectcode="digital_board_store_Weather1_current_condition_icon"
  class="lc_ut_designer lc_dt_data lc_dt_image lc_dt_image_center_contain"
  x-placeholder="//cdn.weatherapi.com/weather/64x64/day/122.png"
  x-fieldname="Current Condition Icon"
  style="background-image: url('{digital_board.store.Weather1_current_condition_icon}');"
  data-value="{digital_board.store.Weather1_current_condition_icon}"
  x-group-parent-id="LCUID-LF-00b5f4b3-2f31-4fdc-9df2-c82b116b9c51"
  x-optional-field="false"
  x-element-settings="{}"
  x-element-setting-values="{}"></div>

<div
  id="data_source_text_p3b0p9q3"
  title=""
  x-objectcode="digital_board_location_city"
  class="lc_ut_designer lc_dt_data lc_dt_text lc_format_fit_text"
  x-placeholder="Anytown"
  x-optional-field="false"
  x-fieldname="City"
  data-value="{digital_board.location.city}"
  x-element-settings="{}"
  x-element-setting-values="{}">
  {digital_board.location.city}
</div>
#rectangle_default_bg {
  width: 100%;
  height: 100%;
  top: 0%;
  left: 0%;
  background: white;
}

#obj_red_salvation_0km0e8h5 {
  background-image: linear-gradient(to top, #f43b47 0%, #453a94 100%);
  width: 43%;
  height: 44%;
  top: 28%;
  left: 18%;
}

#data_source_text_z56ekild {
  white-space: pre-line;
  overflow: visible;
  width: 29.8686%;
  height: 23.2654%;
  top: 42.2208%;
  left: 12.0895%;
  font-family: "Public Sans Thin";
  color: #ffffff;
  rotate: none;
  text-shadow: 3px 3px 5px rgba(0, 0, 0, 0.45);
}

#data_source_img_qv8h6m36 {
  width: 29.9997%;
  height: 43.9998%;
  top: 2.5783%;
  left: 12.0067%;
  rotate: none;
  filter: drop-shadow(5px 5px 4px rgba(0, 0, 0, 0.45));
}

#data_source_text_p3b0p9q3 {
  white-space: pre-line;
  overflow: visible;
  width: 39.1608%;
  height: 27.4895%;
  top: 68.1894%;
  left: 9.4031%;
  font-family: "Public Sans Thin";
  color: #ffc107;
  rotate: none;
  font-weight: bold;
  text-transform: uppercase;
}

Learning More

To get into the details of the code, we have the following reference guides