# Creating a New Banner Project
- Fork the 14Four Banners repository (opens new window).
- Make sure the 14Four team owns the repository.
- Follow this naming convention:
Client Name
-Sequential Number
-Brand Name
-Campaign Name
- Example:
Saatchi - 001 - Toyota - August Sales 2019
- Example:
- Clone the repository you just forked.
- Ensure you’re running Node version >= 13. Run
node -v
to check this. cd
into the repository and runnpm install
.- Run
npm start
to start the development server.
# General Banner Work Overview
There are some general rules to follow when working on any banner project, regardless of how they are built.
- Be aware of total weight limitations prior to working with assets.
- Most often, a campaigns will enforce a strict limit of 150 or 100 kilobytes. These limits are post-compression. Ad platforms will serve all banner assets compressed through gzip, which will lower the weight of Javascript, HTML, and CSS significantly. To determine an approximate weight for your Javascript/HTML bundles, just compress them individually (normal ZIP compression is fine) and check the weight.
- For many external resources imported through a CDN, the file size is not counted. For example, when working with Studio, any fonts pulled in through Google Fonts do not count towards the total weight. Most ad platforms also provide their own CDN for often-used libraries such as GSAP. Resources pulled from a platform's CDN do not count towards the total weight. The banner framework already utilizes the CDNs provided by these platforms when including GSAP.
- Know the proper file type to use for different images.
- For vector assets that still exist in their vector form, use SVG.
- For actual photography, use JPG.
- For images that require transparency, or can easily be expressed in 256 colors or less with minimal loss in quality (for example, vector assets that have been rasterized), use PNG.
- It is typical to export assets at the same dimensions as the banner they are being used in. For example, consider a banner with many different sizes and a logo which is scaled and positioned differently for each size. A typical approach would be to export that logo out at the size of the banner by retaining the empty space around the logo. This is done so that it matches the design perfectly, and so we don't need to manually position each element in CSS for each variation (in the example we would just
absolute
position the logo at (0, 0) for every size).- You can save a small amount of file weight by trimming the whitespace and positioning all image elements manually in CSS, but this adds to development time and complexity. This approach may require additional logic depending on the dynamic nature of your campaign.
- Confirm whether or not the client wants the images to be 2x (aka "Retina") size. The trade off is that 2x images have a much higher total weight, but look significantly better on Retina displays. This is only possible if the assets have been provided in the higher resolution.
- If this is required, you must export all assets at 2x the size (300x250 backgrounds become 600x500 for example).
- Images must be optimized prior to deployment.
- SVGs can be optimized with ImageOptim.
- JPGs should use as low of a quality as possible without looking ugly. You can typically get away with a quality of 40 or 50. Run every JPG through ImageOptim for additional compression.
- PNGs should have their colors reduced as much as possible. You can often get away with using 16 or 32 colors on solid-color logos that have no gradients. Photos with transparency also use PNG format, but in this case you probably want to use 64, 128, or 256 colors. Use Pngyu to lower the colors, and then afterwards run the images through ImageOptim.
- Optimize prior to deployment, but not earlier.
- A good approach is to keep all assets in PNG or SVG until all revisions have completed. This allows us to be most flexible to any feedback regarding image adjustments. These formats are lossless, so you can directly edit them as much as you want with no issues. You can batch convert them to JPG format using ffmpeg prior to deployment.
- Never edit a JPG directly if you can avoid it. Every time you make a change to a JPG and save it, you are compounding the lossy compression and increasing the amount of artifacting throughout the whole image. If you did not follow the advice above and you need to edit a JPG image, you will need to go back to the source image, make the edit, and then export it out again at the proper size.
- Check the final banner weight prior to deployment (see Banner Weight Analysis if using this framework).
Don't forget to optimize. Load time is important in display advertising and failing to optimize images has a significant real cost to the client. Everything is measurable and serving unoptimized banners that don't perform well could lose us future work.
# Dynamic & Static
Two words that are used frequently when discussing banner campaigns are "dynamic" and "static", and it is important to understand what these words mean.
When a banner requires dynamic properties, it is called a dynamic banner. A typical situation is when a campaign requires a set of multiple headlines to be displayed either randomly or based on the audience the ad is being served to. Another situation is when a campaign has multiple background images that need to be swapped out based on some targeting rules. These rules are external to the banner itself—they will be provided by the ad platform when the ad is loaded.
The word "static" has two meanings depending on context. Most often it is used to describe an image banner rather than an HTML banner. It can also be used to describe a non-dynamic HTML banner. This is just a type of banner which does not use any dynamic properties (everything used by the banner is hardcoded and all assets are included with the banner bundle).
From a developer standpoint, "dynamic" is anything that is not intrinsic to the banner unit itself. Oftentimes it is up to the developer to make the distinction between dynamic and non-dynamic. Here is a simple example: imagine you are building a banner unit which has 2 sizes (300x250, 160x600) and 2 backgrounds (city, beach). If you were to make "background" a non-dynamic property, then you would have to deliver 4 banner bundles (300x250-city, 300x250-beach, 160x600-city, 160x600-beach). In this case, each bundle would contain the appropriate background as an image, as well as the compiled JS code and HTML file.
If you instead made "background" a dynamic property then you would only have to deliver 2 banner bundles (300x250, 160x600). In this case, the backgrounds would not be included in the banner bundle and the images would instead be uploaded to the ad platform's asset manager. The backgrounds would then be dynamic since they are not intrinsic to the banner units. In the dynamic case, each variation would be defined as a row in a spreadsheet (called a "dynamic feed") in a process called "versioning".
The banner framework will automatically decide what is dynamic and what is static based on your banner naming convention. The framework also handles all of the versioning in the case of dynamic properties.
(Note: You will see references to "identity" properties as well. These are just static properties which are used to determine the final banner bundles at build time.)
# Framework Overview
This section is meant to serve as a guide for how to use the framework, and (generally) how it works.
# Configuration
Most of the configuration for your banner campaign will take place in the src/config/properties.js
file. It consists of declarations of four property types (mandatory
, selector
, asset
, generic
), preview groups (preview
), and output types (output
).
The four property types consist of a key
and a value
. In the case of mandatory
, asset
, and generic
properties, the value can be of any type. It can also be a function that receives a state
object as a parameter and returns a value of any type. The state
object represents the current state based on the selections in the stage (for more information, read the section on selector properties below). For selector
properties, the value must be an array representing the different options for that selector. The value can also be a function which takes in state
(as above) and returns an array representing the available options.
When a function is used as the value for one of the four property types, you must additionally provide a dependencies
array which is an array of strings containing the keys of all other properties referenced by that function from state. For example, a property with a value of state => `${state.concept}/${state.size}`
should have the dependencies array ['concept', 'size']
. The development/staging environment will still work without providing dependencies, but it is needed during the build process so that all possible variations can be properly traversed.
All property values are made available to your banner component via props. For example, you can access the size
property by referencing this.props.size
.
When banners are built, only the final values for properties are included in the compiled Javascript. The properties.js
file itself is never included in the banner bundle, so you don't need to worry about importing extra packages or adding complex logic in here as it won't contribute to the banner's total weight.
# Mandatory Properties
Mandatory properties are required properties necessary for the framework to function correctly. There are four mandatory properties:
class
: Entrypoint to the banner React component. The value of this property is a path to a Javascript file, in the format: src/banners/{value}.js
.
name
: Defines the name of the output bundle when building. The value
of the name
property determines the naming format of the output banner bundles. Dependencies of the name
property also determine whether properties are treated as static or dynamic. Direct dependencies of the name
property are identity properties. When a property either has no dependencies or solely depends on identity properties, it becomes a static property.
width
: The width of the banner.
height
: The height of the banner.
# Selector Properties
Selectors are properties which contain a list of possible values specified by an array. The value provided must be either an array or a function which returns an array.
Selector properties show up as <select>
elements on the staging environment which allows users to switch between the various options. The current set of selections becomes the state
, which gets piped back into the system and is used to compute all properties which use a function for their value.
Selectors are dynamic by default. They become identity properties when listed as a dependency of the mandatory name
property.
In the case of dynamic properties, all options (elements in the value
array) are enumerated and combined with other properties to generate the full set of possible variations during the build process. For non-dynamic identity selectors, additional banner bundles will be built for each option in that selector, again covering the full scope of all possible variations.
The complete set of variations is the Cartesian product set of all selectors. As an example, for a campaign with two selectors where one selector has values [300x250, 160x600]
and the other has values [red, blue]
, they combine to create the variations [300x250-red, 300x250-blue, 160x600-red, 160x600-blue]
. Whenever dynamic properties are involved, these combinations show up as additional rows in the dynamic feed. Combinations involving identity properties will show up as additional banner bundles.
# Asset Properties
Asset properties are used to declare any asset that you need to use in the banner. Assets can be any file type, but typically they are images, videos, or fonts. All files used in your banner must be declared as asset properties, including all images. See the "Adding Images" section below for more information.
The value of an asset property must be a path to an asset inside of the assets
folder. The value
attribute can be specified as either a string, or a function which receives the state
object as a parameter and returns a string.
Static assets will be included in banner bundles during the build-banners
step. Dynamic assets will be packaged during the build-dynamic
step, and in this case a ZIP file is produced which should be uploaded to the ad platform's asset manager.
# Generic Properties
Generic properties are simple properties which have no additional functionality. For a generic property, value
can be of any type. A function can be specified for value
, which receives the state
object as an argument and can return a value of any type.
A simple use-case for a generic property is an exit URL. More advanced usage includes breaking down larger data sets into a single value which is relevant to whatever state is passed in. This is important because only the final computed property values get bundled into banners at build time. Any of the calculations and larger datasets that a computed value is derived from will not be included in the compiled banner units.
# Previews
Previews can be set up by including a Preview object with some options, described here:
combination
: Required. Accepts an array of strings, where each string is the key
of a property. Each key
in this array is combined via Cartesian product to produce a set of states, which gets grouped according to the group
option. The combination process is similar to the one described above in the "Selector Properties" section.
group
: Required. Accepts an array of strings, where each string is the key
of a property. All variations produced by the combination
option are grouped into buckets, where the "depth" is determined by the keys specified in this group
array.
match
: Optional. This is essentially a filter function. It accepts a function with the state
object as a parameter. The function should evaluate state
according to some rules and return true/false. The state of each potential variation determined by the combination
option will pass through this function. If the function returns true
, it will be added to the group. If it returns false
, it will be discarded. The default matching function is to always return true, i.e. state => true
.
# Outputs
See the "Deployment Walkthrough" section.
# Adding Images
There are a few steps which must be followed to add an image. It does not matter if the image is an <img>
element or background-image
CSS property.
- Put the image into the
assets
folder. Follow proper steps for optimization defined above in the "General Banner Work Overview" section. - Add an
asset
property to theproperties.js
file. See the section above on "Configuration" for more information. Make sure the value for this property points to the location of the image in the assets folder. - When referencing your object, always reference it via the prop. For example:
<img src={this.props.imgBackground}/>
. If you are using the_Base.js
approach from the example, you should reference it like so:<img src={self.props.imgBackground}/>
. Note thatself
is just a reference to the banner'sthis
.
# Adding Fonts
NOTE: Fonts add a large weight to the total file size for your banners. Google Fonts typically aren't counted against the total banner weight for Studio or Flashtalking campaigns. Otherwise, Check the file size limits for the campaign before considering using font files. If the copy is static, it might be a better idea to render them out as SVGs (with the shapes expanded to paths rather than inline text) or PNGs.
The recommended approach to add a font is to add it directly into the HTML template.
- Find the HTML template for your target output type in
@base/templates
. Note that this is the template used for your production build. - Add the font include tag immediately before the
</head>
closing tag.
<head>
...
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
</head>
- Repeat the above step inside of the
@base/templates/stage.static.html
file. This is necessary for the font to be available in the development and staging environments. - To force the banner to wait for the font to load before rendering, add a
PreloadFontMiddleware
to themiddleware
to your configuration inside ofsrc/config/properties.js
.
export default new PropertySet()
// ...
.middleware('PreloadFontMiddleware', {
fontFamily: 'Montserrat'
})
// ...
.build()
- Set up your
font-family
CSS properties.
# Alternatives
- Use Transfonter (opens new window) to transform the font by removing all unneeded characters. You can then encode the font in Base64 and directly embed it in your CSS. This is the recommended approach when using a font CDN is not an option.
# Building
In progress
# Banner Bundles
Run npm run build-banners
In progress
# Dynamic Versions
Run npm run build-dynamic
In progress
# Banner Weight Analysis
In progress
# Deployment Walkthrough
In progress
# Standalone
In progress
# Studio
Review this document (opens new window) for an overview. Coordinate with a Senior Developer (or any developer with Studio banner experience) for any issues you run into.
# Flashtalking
In progress
# Docker
git checkout feature/docker
run make build
run make install
Go to banner-preview.<dev_domain>