HTML5 Lightbox build guide

Lightbox creatives are a rich, interactive cross-screen creative format that runs only on the Google Display Network.

HTML5 Lightbox creatives use the Studio fullscreen expanding API. Normally, an expanding creative has an expanded state with preset size. Lightbox creatives dynamically resize to fill a majority of the available device or browser viewing area. The remaining area outside the creative is covered with a semi-transparent grey background, and a close button is displayed at the top right corner.

For coverage across all screens, at a bare minimum, create invitation states in sizes 320x50 and 300x250. For wider coverage, consider creating additional popular invitation sizes (listed from most available inventory to least inventory): 728x90, 160x600, 336x280, 300x600, 468x60, 970x90, 120x600.

Lifecycle of an HTML5 Lightbox creative

This section is a general overview of the various lifecycle sequences of a Lightbox creative. There are many references in this guide to the Enabler; the Enabler is the core code library of Studio. Think of it as the brain of the creative. All components and API calls must go through the Enabler. The Enabler library is required for all rich media creatives. For more details, see this page about the Enabler.

Page load

Nothing in the ad should execute until the Enabler has initialized. This ensures that everything is properly loaded and Enabler methods can be accessed before a user interacts with the ad. The page load handler should wait for the Enabler to initialize and then load any extra modules.
Once modules have been loaded, the creative can request information about the environment it is serving in, and should load its assets and initialize its features and components. When the creative becomes visible, any animations may begin as well.

User-initiated expansion

Creatives may only request expansion in response to a user interaction, whether click, tap, or hover. When the user interacts, the creative’s event handler should request the available fullscreen dimensions from the Enabler to use them to request expansion.

In response to the query, the Enabler will dispatch the FULLSCREEN_DIMENSIONS event to the creative. The available width and height can be used to calculate custom expanded dimensions, which can be passed in as parameters to Enabler.requestFullscreenExpand(width, height).

Requesting expansion from the Enabler does not automatically guarantee that expansion will happen. The user has a chance to cancel the expansion, if they did not intend to interact with the creative. When expansion does happen, it will be signaled by a viewport resize event. The creative can take this opportunity to animate the expansion, or immediately display the expanded state.

Expanded state responsive resize

If the user changes their browser size or device orientation while the creative is expanded, the creative will have a chance to request a new size for its expanded state. The creative will be notified that the expanded dimensions are changing via the FULLSCREEN_DIMENSIONS event. In response to this event the creative should call Enabler.setResponsiveSize(width, height) to update its expanded dimensions.

When the size of the creative’s container is updated with its new dimensions, a viewport resize event will fire. The creative should take this chance to update its expanded layout given the new dimensions.

Externally-initiated expansion

It is possible for expansion requests to originate outside of the creative, if a mechanism external to the creative determines that expansion should happen. If the Enabler does not have expanded dimensions to use for the creative, it will request them by dispatching a FULLSCREEN_DIMENSIONS event. Similarly to when a responsive resize happens, the creative should calculate its expanded dimensions and set them by calling Enabler.setResponsiveSize(width, height). The rest of the expansion will proceed in the same way as a user-initiated expansion would. There will be a viewport resize event, and the creative should render its expanded state when it occurs.


The creative may begin to collapse by calling Enabler.requestFullscreenCollapse(), but collapse is also often externally-initiated. In both cases, the FULLSCREEN_COLLAPSE_START StudioEvent will fire. At this point, the creative may take the opportunity to animate its collapse. Once any animations are done, the collapse should be completed by calling Enabler.finishFullscreenCollapse(). This method will trigger a viewport resize back to the creative’s collapsed dimensions, and the creative should render its collapsed state at this point if it has not done so already.

Set up your files

Download starter files from the Rich Media Gallery to use as a starting point. The starter files contain boilerplate code used for implementing the fullscreen expansion lifecycle, and places to fill in code that renders the creative. You can also use Google Web Designer templates to build your creative; they come with a separate build guide.

  1. Add the Enabler in your HTML file.
  2. On all invitation states with partially black, white, or transparent backgrounds, add a contrasting border. For example, in an invitation state with a white background, add a 1 pixel black border.
  3. Stop all animation and video 30 seconds or less after load.
Your finished creative must conform to the Lightbox technical requirements. Two of the most common QA issues causing campaign launch delays are the lack of a contrasting border and animation failing to stop within 30 seconds.

Create an HTML5 Lightbox creative

Lightbox creatives expand out in all directions to fill whatever screen they're displayed on. The smaller collapsed size of a Lightbox creative fits within a standard ad placement on a desktop or mobile website, or in a mobile app. When a user hovers for 2 seconds on desktop or taps on devices, the ad expands outside the collapsed size to fill a majority of the desktop's browser window or mobile device's screen and is always centered.

The main part of this guide explains how to build a Lightbox creative, filling in the methods in the Rich Media Gallery starter files. At the end, there's an optional section that goes into more detail about the boilerplate code included at the end of the starter files. This boilerplate code shouldn't need any changes.

Set up

  1. Create two div elements in your HTML file, and give each a sensible ID.
    Code snippet
      <!-- Invitation (Collapsed) State -->
      <div id="invitation-state">
        <!-- Add Collapsed content here -->
        <span id="expand-cta"></span>
      <!-- Engaged (Expanded) State -->
      <div id="lightbox-state">
        <!-- Add Expanded content here -->
  2. Style the objects you created in the CSS using the IDs you assigned them.
    Code snippet
    #invitation-state {
      background-color: #fff;
      background-repeat: no-repeat;
      display: none;
      height: 250px;
      left: 0;
      position: absolute;
      top: 0;
      width: 300px;
    #lightbox-state {
      background-color: #fff;
      display: none;
      height: 100%;
      left: 0;
      position: absolute;
      top: 0;
      width: 100%;
    #expand-cta {
      color: #666;
      cursor: pointer;
      left: 0;
      position: absolute;
      top: 0;
      width: 100%;
  3. Specify chargeable interstitial events. These events should be a subset of the ones that you report via Enabler.counter(eventName) when the user interacts with the creative. Creatives paying on the Cost Per Engagement bidding model are charged as follows:

    1. For Lightbox Expandable ad slots, creatives are charged on expansion. 

    2. For non-expandable ad slots (such as interstitials), creatives are charged on any user interaction. In interstitial inventory the creative must register the counter names for events triggered by user interaction (including exits) to allow the creative to bill on those events.

      In the starter files, specify chargeable events in the chargeableInterstitialEvents array. If the creative is serving as an interstitial, these events will be registered for you when the Enabler loads.


      Code snippet

      // Update the line below to specify all counter events in the
      // expanded state of the ad that are triggered by user interaction.
      var chargeableInterstitialEvents = [
        'Background Click',
        'A user-initiated event',
        'Another user-initiated event'
  4. Load any extra modules. For example, the GDN module must be loaded to check if the creative is serving in-app or as an interstitial. If using the Studio Video module, you should add studio.module.ModuleId.VIDEO to the list of modules to load.

    Code snippet

    // Add additional studio modules you want to load to this list.
    // Do not remove the GDN module.
    var modulesToLoad = [studio.module.ModuleId.GDN];

Initialize the ad components

The preInit() method in the starter files is automatically called on the page load event of the ad frame and is responsible for calling some internal boilerplate code in addition to setting up the DOM.  

Code snippet

function preInit() {
  // Do not change these lines.

The setupDom() method called in the code snippet above is used to set up references to the creative’s DOM components, such as the main container. It can also be used to perform any necessary initialization steps for custom components or features. The starter files will also set up variables such as creative.isInterstitial and creative.isTouchable that can be used during ad initialization.

Lightbox ads run on both desktop and mobile devices, so you'll need two background images for the two different invitation states. One background image should have a 'tap to expand' CTA for smartphones/tablets, and the other image should have a 'hover to expand' CTA for desktop display. Use the creative.isTouchable variable to differentiate between these two cases.

You should also tell the Enabler which user action should cause the creative to expand when viewed on desktop computers. Add one of these lines of code, depending on what action should expand your creative:

  • To expand your creative on mouse hover, add: 
    Enabler.setHint('expansionTrigger', 'onHover');
  • Or, to expand your creative on mouse click, add:
    Enabler.setHint('expansionTrigger', 'onClick');

Code snippet

function setupDom() {
  // Set up references to DOM components.
  creative.dom = {};
  creative.dom.mainContainer = document.getElementById('main-container');
  creative.dom.lightboxExit = document.getElementById('lightbox-exit');
  creative.dom.lightboxState = document.getElementById('lightbox-state');
  creative.dom.lightboxFeature = document.getElementById('lightbox-feature');
  creative.dom.invitationState = document.getElementById('invitation-state');
  creative.dom.expandCta = document.getElementById('expand-cta');

  // It is not safe to use other creative.* fields until after the init()
  // method has been called, which indicates the Enabler has initialized
  // and loaded its additional modules.
  if (creative.isTouchable) {
    creative.dom.expandCta.textContent = 'Tap to expand';
  } else {
    creative.dom.expandCta.textContent = 'Hover to expand';


Part of the boilerplate code that executed on load (internalPreInit()) will listen for events which signal that the Enabler and additional Enabler modules are initialized. After the Enabler and its modules have initialized, the init() method will be called.

Code snippet

function init() {
  // Do not change these lines.

Add event listeners

After the creative’s DOM has been set up, the next step is to add event listeners to the creative. Any handlers for user interactions with the expanded state of the creative should report the interactions to the Enabler.

If fullscreen expansion is supported, the onExpandHandler() method should be bound to either click or mouseover events. The handler will request the fullscreen dimensions from the Enabler before requesting expansion. Note that the touchstart and touchend events should not be used to trigger expansion, as users may inadvertently touch the ad while scrolling past it. This sort of interaction should not trigger expansion.

The addFullscreenExpansionListeners() method will add all of the event listeners that are necessary for fullscreen expansion. You do not need to modify this method or add any extra listeners for the fullscreen expansion lifecycle to work.

Code snippet

Add listeners in the addListeners() method.

function addListeners() {
  // Add listeners on the expanded state to report interactions.
  creative.dom.lightboxFeature.addEventListener('click', function() {
    Enabler.counter('Background Click');
  }, false);

  // Add more listeners to the expanded state elements.
  creative.dom.lightboxExit.addEventListener('click', exitClickHandler);

  if (creative.isFullscreenSupported) {
    if (creative.isTouchable) {
      // Do not use the touchstart or touchend events to trigger
      // expansion on touch devices. Creatives using those events trigger
      // expansion when users touch the ad while scrolling past, which is a
      // violation of lightbox creative policies. Instead, use the click event,
      // which will not fire when the user touches the ad while scrolling.
          'click', onExpandHandler, false);
    } else {
          'mouseover', onExpandHandler, false);


Calculate custom fullscreen expanded dimensions

Whenever the user interacts with the creative, the onExpandHandler() will request the available fullscreen dimensions from the Enabler. The available width and height are passed to calculateCustomDimensions(), in case you want to expand to smaller than or up to the maximum dimensions. This can be useful for maintaining an aspect ratio in the expanded state. Alternatively, to expand to the full available dimensions, the method can return the available width and height.

calculateCustomDimensions() may also be called outside of the expansion lifecycle. For example, the Enabler might have detected that the user has changed orientations or resized their browser window. It will also be called when there has been an external expansion request. This method provides an opportunity to recalculate the size of the creative’s expanded container. The creative should provide new expanded dimensions regardless of whether it is currently expanded or collapsed.

Note that when the creative is serving in-app, even if custom dimensions are provided, the Enabler will use the full available fullscreen dimensions when expanding. The creative.isInApp variable can be used to detect this state.

Code snippet

The calculateCustomDimensions() returns an object with 'width' and 'height' properties denoting the expanded dimensions of the creative.

function calculateCustomDimensions(availableWidth, availableHeight) {
  // When served in-app, creatives will ignore these width/height values and
  // expand to the dimensions of the screen.
  var dimensions = {
    'width': availableWidth,
    'height': availableHeight
  return dimensions;

Handle viewport resize events

Viewport resize events occur when the creative expands and collapses. Resize events are how the creative knows to render its expanded and collapsed states. They also happen when the browser window resizes or the device changes orientation, and the expanded dimensions have changed. These cases can be differentiated in the following manner:

  • When the creative is collapsing, the creative.isCollapsing variable will be true. The creative should render its collapsed state by calling renderCollapsedView().
  • When creative.isCollapsing is false, the creative is expanding, or there has been a resize event while the creative is expanded. These two cases are combined because the creative should respond to both by rendering its expanded state via the renderExpandedView(). Optionally, if you would like to add an animation before expanding, the creative.isExpanded variable will be false when the creative is expanding.

During expansion and collapse, make sure to start and stop the ‘Panel Expansion’ timer.

Code snippet

function viewportResizeHandler() {
  if (creative.isCollapsing) {
    Enabler.stopTimer('Panel Expansion');
    Enabler.counter('Collapse Ctr');
  } else {
    if (!creative.isExpanded) {
      // Optionally animate the expansion before rendering the expanded layout.         
      Enabler.startTimer('Panel Expansion');
      Enabler.counter('Expanded Ctr');

Render the creative

The renderExpandedView() and renderCollapsedView() methods are used to show the expanded and collapsed views of the creative, as well as update the creative.isExpanded state variable. The renderCollapsedView() method will also be used to show the creative when it becomes visible on the page for the first time.

Code snippet

function renderExpandedView() { = 'block'; = 'block'; = 'none';
  creative.isExpanded = true;

function renderCollapsedView() { = 'none'; = 'none'; = 'block';
  creative.isExpanded = false;
  creative.isCollapsing = false;

(Optional) Add a collapse animation

Unlike expansion animations, collapse animations should happen before the creative’s frame resizes to the collapsed dimensions. Any collapse animations can be added in the collapseStartHandler() method, which marks the start of the process of collapsing the creative.

Code snippet

function collapseStartHandler() {
  // Optionally perform an animation before collapsing.

  // Note the creative is collapsing so the viewportResizeHandler knows how to
  // redraw the creative.
  creative.isCollapsing = true;

Handle exit clicks

The exitClickHandler() method handles exit clicks on the creative’s expanded state.

When creatives are serving in-app to iOS environments, there's a known issue: calling collapse on an exit causes the landing page to close immediately. By policy, the Lightbox should collapse when the user clicks to a landing page. However on iOS in-app environments this prevents the user from actually getting to the landing page, so we allow for the ad to remain expanded.

Code snippet

function exitClickHandler() {
  // In-app iOS environments known issue: Calling collapse on an exit
  // causes the landing page to close immediately so we leave it expanded
  // on exit.
  // In other environments it is required that the ad collapses on exit
  // click per policy.
  if (!(creative.isInApp && isMobile.iOS())) {

  Enabler.stopTimer('Panel Expansion');
  Enabler.exit('Background Exit');


This marks the end of the section of the guide that covers code that needs to be filled in by developers. The rest of the guide provides an overview of the boilerplate code included in the starter files. 


Boilerplate code you shouldn't need to change

The boilerplate functions contained in the starter files handle much of the initialization and fullscreen expansion lifecycle. These methods should not need to be changed, but notes on them are provided here as a reference.

Initialization and loading modules

The internalPreInit() method is the window onload handler, and the first method to be called. It sets up creative state variables such as creative.isTouchable and creative.isExpanded via the setupCreativeState() helper method. Next, once the Enabler has been initialized, the modules in the modulesToLoad array are loaded.

Code snippet

function internalPreInit() {
  // Initialize state variables such as creative.isExpanded.

  if (Enabler.isInitialized()) {
    // Load the GDN module before initializing.
    Enabler.loadModules(modulesToLoad, modulesLoadedHandler);
  } else {
    Enabler.addEventListener(, function() {
      Enabler.loadModules(modulesToLoad, modulesLoadedHandler);

Once all modules have finished loading, the Enabler calls the modulesLoadedHandler() method, which was provided as a callback. Here, the Google Display Network configuration object is retrieved and used to set the creative.isInApp and creative.isInterstitial state variables.

After the expansion mode is set, Enabler.queryFullscreenSupport() is called to determine whether fullscreen expansion is supported, and the creative.isFullscreenSupported variable is set. 

Code snippet

function modulesLoadedHandler() { {
    creative.isInApp =
        (gdnConfig.getClientEnvironment().browserClass ==;
    // gdnConfig.isInterstitial is set via a callback function.
    gdnConfig.isInterstitial(function(state) {
      creative.isInterstitial = state;
, function(e) {
            creative.isFullscreenSupported = e.supported;

Next, the init() method is called (described above), which then goes through the internalInit() method to bind the viewportResizeHandler() to the window resize event. Additionally, the expansion mode is set to Lightbox.

As you'll notice, there's no close button added in the content. A standard Lightbox close button is automatically displayed over the creative when served. Setting the expansion mode to Lightbox ensures that the following Lightbox functionalities are implemented when the tag is served in a GDN environment:

  • Two-second hover delay
  • Expanding progress bar on the invitation (collapsed) state
  • Expands the engaged state to the center of the webpage
  • Close button (top right corner) on the engaged state
  • Gray translucent background on the engaged state

Finally, the creative is rendered. If the creative is serving as an interstitial, the expanded view is rendered via the renderExpandedView() method, and the events specified in the chargeableInterstitialEvents array are registered as chargeable events with the Enabler. If the creative is not serving as an interstitial, the invitation state is rendered by calling renderCollapsedView() once the creative is visible.

Code snippet

function internalShow() {
  if (creative.isInterstitial) {
    // Render the creative in its expanded state within its current ad slot.
    // Register the chargeable events that have been specified in
    // chargeableInterstitialEvents above with the Enabler.
  } else {
    // Polite loading
    if (Enabler.isVisible()) {
    else {
, renderCollapsedView);

Expansion and collapse event handlers

The fullscreen expansion lifecycle is an asynchronous process that progresses through events. The internalAddFullscreenExpansionListeners() method, which is called from addListeners() above, adds the appropriate methods as handlers for the FULLSCREEN_DIMENSIONS, FULLSCREEN_EXPAND_START, and FULLSCREEN_COLLAPSE_START events. This method also calls Enabler.setResponsiveExpanding() to signal that the creative will behave responsively when expanded to fullscreen. It is important to call this method before the first time the creative expands, since it will be ignored if called afterwards.

As noted above, the onExpandHandler() method is bound to user interactions. If the user interacts with the creative while it is collapsed, the handler will start the process of expanding by requesting the fullscreen dimensions.

Code snippet

function onExpandHandler() {
  if (creative.isExpanded) {
    // If the creative is already expanded, do nothing.

  // Request the available dimensions to use for expanding.
  creative.shouldRequestExpansion = true;

The fullscreenDimensionsHandler() method is bound to the FULLSCREEN_DIMENSIONS event. It retrieves the available dimensions from the event object, then passes them to the calculateCustomDimensions() method. If there was a user interaction and creative.shouldRequestExpansion is true, expansion will be requested. Otherwise, the updated expanded dimensions will be passed to Enabler.setResponsiveSize(width, height), as part of responding to a responsive resize.

Code snippet

function fullscreenDimensionsHandler(e) {
  // When served in-app, creatives will always expand or resize fullscreen, and
  // the custom expanded dimensions determined below are ignored.
  var availableWidth = e.width;
  var availableHeight = e.height;

  // Calculate custom dimensions.
  var dimensions = calculateCustomDimensions(availableWidth, availableHeight);

  // If we asked for the dimensions in response to a user interaction,
  // expand now.
  // Creatives served in-app will ignore specified width/height values.
  if (creative.shouldRequestExpansion) {
    Enabler.requestFullscreenExpand(dimensions.width, dimensions.height);
    creative.shouldRequestExpansion = false;
  } else {
    // There was a responsive resize event, so update the size of the ad.
    Enabler.setResponsiveSize(dimensions.width, dimensions.height);

The expandStartHandler() method is bound to the FULLSCREEN_EXPAND_START event, and responds by calling Enabler.finishFullscreenExpand() to complete the expansion process.

Note: you cannot rely on the FULLSCREEN_EXPAND_START event to render the expanded state of the creative. There is no guarantee that this event will happen before the ad frame is resized. Instead, the expanded state of the creative should be rendered in response to window resize events, as detailed above in the viewportResizeHandler() method.

Code snippet

function expandStartHandler() {

The collapseStartHandler() is called in response to the FULLSCREEN_COLLAPSE_START event, which marks the beginning of the collapse process. It is detailed more above.

Mobile detection

isMobile and hasTouchScreen() are helper methods to detect whether or not the creative is in a mobile environment.

Code snippet

var isMobile = {
  Android: function() {
    return navigator.userAgent.match(/Android/i);
  BlackBerry: function() {
    return navigator.userAgent.match(/BlackBerry/i);
  iOS: function() {
    return navigator.userAgent.match(/iPhone|iPad|iPod/i);
  Opera: function() {
    return navigator.userAgent.match(/Opera Mini/i);
  Windows: function() {
    return navigator.userAgent.match(/IEMobile/i);
  any: function() {
    return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() ||
            isMobile.Opera() || isMobile.Windows()) &&

var hasTouchScreen = function() {
  var n = !1, o = function(n) {
    return -1 !== window.navigator.userAgent.toLowerCase().indexOf(n);
  return ('ontouchstart' in window || navigator.maxTouchPoints > 0 ||
          navigator.msMaxTouchPoints > 0) &&
             (o('NT 5') || o('NT 6.1') || o('NT 6.0') || (n = !0)),

Was this helpful?
How can we improve it?

Need more help?

Sign in for additional support options to quickly solve your issue