In this article, you'll get a look at the basic controls on pages for HTML print layouts and an overview of the layout libraries that Populi uses to produce documents from these layouts.
The HTML Layout page
Here are the various functions and controls on the page:
- The left pane shows the layout code and lets you edit it. The right pane provides a preview of the layout.
- Depending on the layout type (schedule, transcript, etc.), you'll have various options to load data from your Populi site into the layout.
- To update the preview after you change the code or any of the drop-down selections, click Reload Data.
- You can toggle between setting the layout to Draft or Active.
- If you change anything and want to keep it, make sure to click Save!
- After you've saved any changes, a Revision History will be available.
If you want to use images in your layout, scroll to the bottom of the page. You can upload images, and once uploaded, you can replace or delete them.
With all that out of the way, please remember that you should only edit the HTML and CSS for a print layout if you are confident with your coding skills. If you aren't, Populi Support is here to help. Don't hesitate to ask for any kind of help with your HTML layout!
HTML Layout Overview
Each HTML page layout is run through two layout libraries before either being previewed or turned into a PDF: PagedJS and Handlebars.
PagedJS is what we use to make the HTML document 'printable'—it handles things like pagination and making sure the document is the correct size for physical paper, as well as adding margins and things like that. Think of it as being like a word processor—it takes digital information and formats it for use in the physical world.
Handlebars is what we use for variable substitution. It is what allows us to dynamically create documents without having to manually edit them. With handlebars, code that looks like this:
The student's name is {{student_name.first}} {{student_name.last}}
is translated in the final document to each students first and last names, for example:
The student's name is John Doe
Each layout is effectively broken up into two sections—a <style>
block that contains CSS (or styling) instructions, and a block within a handlebars tag where all the variable substitution happens. This is the {{#each this}}
tag.
The {{#each this}}
tag
In Handlebars, the {{#each}}
syntax is how you can iterate through a list or array of data and this
refers to the current document that we're rendering. For our purposes, all variable substitution must happen within the {{#each this}}
tag. Handlebars variables outside of this tag will not be properly rendered or replaced. This means that if you want to use handlebars variables within CSS styles, you must create a new <style>
tag within the {{#each this}}
tag:
{{#each this}}
<style>
:root {
--event_height: 60px;
--row_height: calc(var(--event_height) / 60);
--num_hours: {{css_data.hours}};
--num_minutes: {{css_data.minutes}};
}
</style>
In this example, taken from the student schedule, we're setting up CSS variables to allow the timeline to generate the correct number of hour and minute rows for the events we're displaying.
Raw text output
By default, Handlebars will perform what is known as HTML escaping on special characters, like &
or =
. This ensures that they won't cause issues inside the HTML that the layout is made up of. There are cases, however, where you may need handlebars to generate valid HTML. The body of the letter template is one such example since it can contain line break characters and other formatting characters that would be escaped otherwise. In such instances, use three brackets to surround your handlebars variables instead of two: {{{letter.body}}}
Background Images vs Image Tags
In some cases, you may want to display an image behind your text. There are two ways to do this—an image tag with some css, and the CSS background image property. Use the CSS background image property if you want the image to cover the whole page, and use the special @page
selector, which is what PagedJS suggests using:
{{#each this}}
<style>
@page {
background-image: url( {{{layout_images.layout.image}}} );
background-size: cover;
}
</style>
This will stretch the image across each page separately. You will have to set the opacity or alpha of the image before using it, as there is no way to set the opacity of a CSS background image separately from the content of the element.
For situations where you want an image behind a specific element, like the header, you can use an image tag:
<div class="background_image"><img src="{{layout_images.global.logo}}" width="100px" height="100px"></div>
along with CSS like this for opacity and positioning:
.background_image {
z-index: -1;
position: absolute;
top: 0;
right: 3.75in;
opacity: 0.25;
}
The z-index at -1 puts it behind every other element, and opacity is a range from 0 (transparent) to 1 (fully visible)
Handlebars functions
While most of what we're doing with Handlebars is simple variable replacement, there are a few special cases of note. There is documentation for these, but I'll summarize a couple common ones here.
#each
When you're iterating through a list or array of data, the Handlebars {{#each}}
function has a few special rules. You can create a temporary name for the individual elements of a list by using this syntax:
{{#each programs as |program|}}
{{program.name}}
{{/each}}
You'll start by indicating that you want to run through the list one element at a time by starting with {{#each
. Next you'll indicate which list you want to use. In our case, programs
, which is an array of programs each containing data organized in the same way. To refer to the individual element, you'll create an alias for it by surrounding it with vertical pipes: as |program|
. Note that the alias doesn't have to be the singular form of the list variable. It would work just as well to say programs as |blah|
and then refer the element variables as {{blah.name}}
for example.
#if
You can surround a block of code with a {{#if variable}}
tag to show it if the variable is present, and ignore it if it is not. For example:
{{#if program.honors}}
<div class="honors"><strong>Honors:</strong> {{program.honors}}</div>
{{/if}}
We've extended the #if
functionality with two additions—you can use notZero
in the #if
block to handle string and number formatted values of 0.00 , which is useful for currency and GPA display. It looks like this:
{{#if (notZero variable_name)}}
{{/if}}
If you want to check against a value rather than variable existence, you can use valueCheck like so:
{{#if (valueCheck variable_name "value")}}
{{/if}}
Date Formatting
We've also created our own handlebars function to help with date formatting. All dates are formatted as timestamps by default, and you can change their display values by running them through formatDate:
{{formatDate timestamp_variable_name "mdy"}}
should return the date formatted like this: 01/01/2020
If you want a fully custom formatted date, you'll have to use the format date function several times in a row, like this:
{{formatDate timestamp_variable_name "text_month"}} {{formatDate timestamp_variable_name "ordinal_day"}}, {{formatDate timestamp_variable_name "full_year"}}
should return the date formatted like this: January 1st, 2023
The complete list of date format values is as follows:
passthrough value | timestamp equivalent | comment |
---|---|---|
'mdy' | "MM/DD/YYYY" | 01/17/2023 |
'dmy' | "DD/MM/YYYY" | 17/01/2023 |
'ymd' | "YYYY/MM/DD" | 2023/01/17 |
'time' | "h:mma" | 1:04pm |
'day' | "D" | 1 day of the month with no leading zero for single digits |
'padded_day' | "DD" | 01 day of the month with leading zero for single digits |
'ordinal_day' | "Do" | ordinal day of the month—1st , 13th , etc |
'month' | "M" | 1 for January, 2 for February, etc |
'padded_month' | "MM" | 01 for January, etc |
'short_text_month' | "MMM" | Jan for January, etc |
'text_month' | "MMMM" | January |
'short_year' | "YY" | 23 for 2023, etc |
'full_year' | "YYYY" | 2023 |
'padded_hour' | "HH" | padded hours in 24 hour format |
'padded_minute' | "mm" | padded minutes |
CSS Styling
One of the main ways you can adjust how your print layout displays with the HTML layouts is through the use of the CSS styling language. CSS is made of selectors that are added to HTML elements, and then you can attach styling or display rules to them inside the <style>
element.
For example, take this short snippet of CSS and HTML, used in the header of several layouts:
<style>
.organization_info {
display: grid;
place-items: center;
}
.organization_address {
text-align: center;
}
.address {
display: block;
}
</style>
<div class="organization_info">
{{#if layout_images.global.logo}}
<div class="organization_logo">
<img src="{{layout_images.global.logo}}" width="100" height="100">
</div>
{{/if}}
<div class="organization_name"><h2>{{organization.name}}</h2></div>
<div class="organization_address">
<span class="address">{{organization.address.street}}</span>
<span class="address">{{organization.address.city}}, {{organization.address.state}} {{organization.address.postal}}</span>
</div>
<div class="organization_phone">{{organization.phone_number}}</div>
</div>
There is an HTML <div>
element that contains all the other elements. It has the class organization_info
. The way you reference a class in the CSS style area is to put a period in front of the name, like this: .organization_info
. This lets you affect that <div>
element and all of its contents. The reason we use classes is that multiple elements on a page can have the same class, and we can style them all at once.
Display
You can adjust the display of the elements inside a <div>
by using the CSS display
property. Here's a good article on what the display property does. For layout purposes, the display: grid
and display: flex
are both very useful. Grid can be used to position elements strictly, and Flex can be used for orderly positioning where the exact position doesn't matter as much.
Web Fonts
You can import web fonts from sources like Google Fonts. For example, if you wanted the Raleway font, you'd select the font weights you want to use on the font family page, and then go to the Selected Families area and select the @import
style of importing a font. You'll get something like this:
<style>
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@100&display=swap');
</style>
You'll take the @import
line and paste it inside the <style>
tags of your print layout. Then, when you want to use the font, you'll simple add a font-family CSS property to the CSS selector you want to use the font for. If you selected multiple font weights you can also specify that here:
.text-selector-example {
font-family: 'Raleway', sans-serif;
font-weight: 100;
}
The font family property has the name of the font family, and then a fallback in case the font fails to load for any reason. In this case, it'll load as a basic sans-serif font if the Raleway font fails to load. You can also use these generic font types (listed in the font-family link above) to style your text, without requesting a specific font.
Font Sizing
For consistency between the preview and the final PDF, use font sizes defined in px, rather than pt.
0 Comments