I have encouraged people to use CSS Grid for layout, and said it’s easier than using TABLE, so here’s a short series to prove it. Most Guides to CSS Grid are very good, but they often do not include much practical help in seeing how to use it. This guide will show you how to use CSS Grid in the context of a roll20 character sheet.
I do have a previous post in the Guide about CSS Grid, but it’s a single, mammoth post that maybe covers too much to be easily comprehended, and may repeat some of the errors described above. In this short series, we’ll break down Grid into useful topics and look at them in more helpful detail.
First, we’ll look at how to use CSS Grid to create columns, and later layout a whole character sheet as well as look at common problems or gotchas that can crop up.
The Final Image
We are going to work through the stages needed to construct this image.
This post describes how we create that layout, and it is shockingly simple.
The Starting HTML
First, we always start with some HTML whose layout we want to organise. Let’s say we have six attributes, Strength, Dexterity, Constitution, Intelligence, Wisdom, and Charisma, and each stat has a label, a score, a modifier, and a button to make a roll. The HTML for that might start like this:
<div class="stats">
Stat
Score
Bonus
Roll
Strength
<input type="number" name="attr_str_score" value="10">
<input type="number" name="attr_str" value="0" readonly>
<button type="roll" name="roll_str" value="/roll 1d20+@{str}+?{modifier|0}"></button>
Dexterity
<input type="number" name="attr_dex_score" value="10">
<input type="number" name="attr_dex" value="0" readonly>
<button type="roll" name="roll_str" value="/roll 1d20+@{dex}+?{modifier|0}"></button>
Constitution
<input type="number" name="attr_con_score" value="10">
<input type="number" name="attr_con" value="0" readonly>
<button type="roll" name="roll_str" value="/roll 1d20+@{con}+?{modifier|0}"></button>
Intelligence
<input type="number" name="attr_int_score" value="10">
<input type="number" name="attr_int" value="0" readonly>
<button type="roll" name="roll_str" value="/roll 1d20+@{int}+?{modifier|0}"></button>
Wisdom
<input type="number" name="attr_wis_score" value="10">
<input type="number" name="attr_wis" value="0" readonly>
<button type="roll" name="roll_str" value="/roll 1d20+@{wis}+?{modifier|0}"></button>
Charisma
<input type="number" name="attr_cha_score" value="10">
<input type="number" name="attr_cha" value="0" readonly>
<button type="roll" name="roll_str" value="/roll 1d20+@{cha}+?{modifier|0}"></button>
</div>
Code language: HTML, XML (xml)
When plugged into the sheet with no styling, that looks like this.
The First Pass
That’s no good. Let’s sort that into columns. We add this code to the CSS file.
.stats {
display: grid;
grid-template-columns: repeat(4, 25%);
}
Code language: CSS (css)
Just those lines give us:
There’s a few things that need work here, but you can see immediately we have four columns.
Naked Text and Headings
Our first problem is that first entry, where Stat Score Bonus Roll Strength forms a single entry. Why did that happen?
First, when looking at CSS Grid layout, think of the concept of grid-items
or grid-cells
. We know this is 4 columns wide, so each is divided into for grid-cells
. That first grid-cell
contains that entire set of text.
In HTML, there’s a concept of naked text – this is any text that is left alone, and not placed in an element like a heading, span, or <p>
. All naked text together is considered one grid-item
and placed in one grid-cell
.
So that whole Stat Score Bonus Roll Strength section is treated as a single item, and put in one grid-cell
. We need to fix that. So let’s edit that HTML to put the headings into, well, headings. They can now be recognised as separate items.
Generally speaking, once you start using CSS, there should be no naked text. If you aren’t sure, place the text in a <span>
since that doesn’t change the way text flows. That’s what we do here. It’s not strictly necessary but it makes styling easier if we later add any, so we might as well do it now.
<div class="stats">
<h4>Stat</h4>
<h4>Score</h4>
<h4>Bonus</h4>
<h4>Roll</h4>
<span>Strength</span>
<input type="number" name="attr_str_score" value="10">
<input type="number" name="attr_str" value="0">
<button type="roll" name="roll_str" value="/roll 1d20+@{str}+?{modifier|0}"></button>
<span>Dexterity</span>
<input type="number" name="attr_dex_score" value="10">
<input type="number" name="attr_dex" value="0">
<button type="roll" name="roll_str" value="/roll 1d20+@{dex}+?{modifier|0}"></button>
<span>Constitution</span>
<input type="number" name="attr_con_score" value="10">
<input type="number" name="attr_con" value="0">
<button type="roll" name="roll_str" value="/roll 1d20+@{con}+?{modifier|0}"></button>
<span>Intelligence</span>
<input type="number" name="attr_int_score" value="10">
<input type="number" name="attr_int" value="0">
<button type="roll" name="roll_str" value="/roll 1d20+@{int}+?{modifier|0}"></button>
<span>Wisdom</span>
<input type="number" name="attr_wis_score" value="10">
<input type="number" name="attr_wis" value="0">
<button type="roll" name="roll_str" value="/roll 1d20+@{wis}+?{modifier|0}"></button>
<span>Charisma</span>
<input type="number" name="attr_cha_score" value="10">
<input type="number" name="attr_cha" value="0">
<button type="roll" name="roll_str" value="/roll 1d20+@{cha}+?{modifier|0}"></button>
</div>
Code language: HTML, XML (xml)
And this is what that HTML looks like now with that little bit of CSS:
With just this tiny bit of CSS, we have created a 4-column layout:
.stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
Code language: CSS (css)
But there’s more we still need to do. We want to change the width of each column to stop this section from being so wide.
Width Units
grid-template-columns
asks you to supply the width of each column. You could have something like
grid-template-columns: 80px 10rem 1fr 17%;
Code language: CSS (css)
That tells us to create 4 columns, each of which has a different width and unit. This command will accept any unit that is legal in CSS, including a special one just for Grid. Here’s what they are:
- px: The simplest unit – this is how many pixels the column is wide. This depends on your screen resolution. It used to be very reliable, but with people now using phones of very different screen resolutions it is less reliable, generally. But Roll20 is built for desktops which don’t vary that much, so its pretty reliable here. The user can use the zoom function to magnify a sheet if needed.
- rem: This is a unit relative to a typical character in the document, so
5rem
is like saying 5 characters wide. Since it’s relative to the character sheet, it’s a pretty good unit size to use. - em: I list this here, but I won’t describe it – just don’t use it. Its an old unit and can easily seem okay at first and then gets increasingly chaotic the more your sheet changes.
- %: The column is set to a per cent width. This calculation is based on the width of the parent element, so if you to 20% of a div that itself is 500 pixels, that column will be 100 pixels wide.
- fr: these are fractional units. The browser calculates the width of each other column, then divides the remainder into each column into fractions. Let’s say your div is 500 pixels wide, and has 20% 1fr 2fr 1fr. First, that 20% is calculated and removed from the total, leaving 400 pixels. Then the fractional units are totalled up (4), and the width is assigned according to the fractional sizes. Each 1fr gets 100 pixels, and the 2fr is double-width and gets 200 pixels.
(In fact, there are many more sizing units, but these are the most common ones.)
Which do I recommend? I’m old fashioned, so I tend to use px
a lot, but it usually doesn’t matter (as long as you ignore em
). So, lets update the CSS to this:
.stats {
display: grid;
grid-template-columns: 8rem repeat(3, 5rem);
}
Code language: CSS (css)
That repeat
function lets you say how many times you want to include something. That’s just like 8rem 5rem 5rem 5rem;
. Include a space between each column (or the repeat
function if you use it), and no space between the number and its unit. With that CSS we finally get that starting image:
Conclusions
Wasn’t that simple? Just add display: grid
in CSS to tell the browser this is a grid, then use grid-template-columns
to create the columns.
There’s a lot that could still be done here (like adding code to calculate the bonus, replacing the roll buttons with action buttons, ensuring that certain columns are centred, and so on), but things like that are beyond the scope of this post. You have seen how simple it is to create a basic set of columns, but this is only scratching the surface of what it’s possible to do with CSS Grid. We’ll explore more of that in coming posts, and soon you’ll be able to lay out an entire character sheet.