This tutorial was prepared to accompany a "lightning-talk" presentation on quickly styling MVP web apps, to be presented to the Phase 2 students at Dev Bootcamp NYC.
## Drop-In CSS is moving! As of March 18, 2016, further development of this tutorial and stylesheet will be hosted at [Drop-In CSS](https://github.com/webdevjeffus/drop-in-css), to allow the concept to mature in a framework-agnostic direction. There will be no further updates to this repo, though it will remain available for reference. I look forward to seeing you at [Drop-In CSS](https://github.com/webdevjeffus/drop-in-css)!
The purpose of this how-to is to help you develop a flexible CSS stylesheet that can be dropped into any Sinatra-based web app, instantly applying "good-enough" styling to the entire website. This is accomplished using type-based CSS selectors along with simple semantic HTML structure. When properly executed, the final stylesheet should work on any Sinatra web app, as well as most Rails-based apps, without adding any classes to the HTML, or any additional styling rules to the CSS file.
However, this strategy is not appropriate for styling anything beyond the MVP stage. Type-based selectors are measurably less performant than class-based or id-based selectors. While this isn't likely to noticeably affect performance on a locally-hosted app in development, it could have a perceivable impact on the performance of a large, publicly-deployed app served over the internet.
If you read this tutorial from beginning to end, you'll learn how to set up your own drop-in stylesheet and the compatible layout view. I'll explain what each CSS rule does, and why it does it. If you are in a hurry, you can skip down to the "Putting it in place" section and follow the instructions there to drop-in pre-generated .css and .erb files onto your app in just a few minutes. The pre-generated files will work fine, but you won't learn as much doing it that way. Your call.
For this to work, you need to set up your layout.erb (or layout.html.erb, in Rails) following some basic best-practices for semantic HTML. We'll be relying on HTML semantics to differentiate between tags of the same type in different areas of the page. For example, our stylesheet will handle an <h1> tag differently if it appears in the app's <header> element than if it is in the <main>.
To begin, set up the <body> element in your layout.erb as in this example. We'll cover the contents of the <head> element later in the tutorial.
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Discussed in "The HTML <head> element", below -->
</head>
<body>
<header>
<h1><a href="/">APP NAME</a></h1>
<nav>
<ul>
<li><a href="#">LINK 1</a></li>
<li><a href="#">LINK 2</a></li>
</ul>
</nav>
</header>
<main>
<%= yield %>
</main>
<footer>
<p>FOOTER TEXT</p>
</footer>
</body>
</html>
The "yield," wrapped in a printing erb tag, will tell Sinatra (or Rails) to render each view inside the <main> element; you don't need to do anything else to this section. The <footer> tag is even less complex—just drop your own footer text into the <p> element.
The <header> element has a bit more going on, but it's easy enough to understand. First is the <h1> element that will serve as the logo for your MVP version. The <a> link to the root route will make the logo a link back to the home page from any place in your app, so be sure you've defined the root among the routes in your controllers.
We'll set up the header so that the logo displays at the left edge, and the nav links appear lined up on the right. To make this happen, we'll put the links inside list items (<li>), which are included in a single unordered list (<ul>). The list is held within a semantic <nav> element, so we can select it easily for styling.
Again, we'll loop back around and cover the <head> element of your layout.erb file after we've talked about the contents of the CSS file.
In this tutorial, we'll actually be employing three CSS files: normalize.css (more on this in the <head> element, below); application.css (where you'll put any specific styling for this particular app; again, more in the <head> session); and this type-based stylesheet, which I'll call drop-in.css.
I'll begin by creating and saving a new drop-in.css file, and setting up some commented-out section heads to help organize it. Here's how it looks at first:
/* drop-in.css */
/* RESETS */
/* DESIGN STYLES */
/* HEADER STYLES */
/* MAIN STYLES - Yield Block */
/* FOOTER STYLES */
In this section, we'll add style rules that simplify various browsers' default element-rendering, to make styling more designer-friendly. This includes setting the document to size all block elements, including the header, main, footer and any divs, using border-box sizing, instead of the frustrating default, content-box. We'll also set all margins and padding to 0, since different browsers use different defaults for these properties. When we want margins or padding, we'll add them explicitly, so we get what we're expecting.
/* RESETS */
html { box-sizing: border-box; }
*, *:before, *:after {
box-sizing: inherit;
margin: 0;
padding: 0;
}
body {
margin: 0 auto;
min-width: 600px;
max-width: 800px;
}
h1, h2, h3, h4, h5, h6, p {
margin-bottom: 0.5rem;
}
In the body rule, we first set the left and right margin to auto, so that the app content will be centered on the page. We'll also set min-width and max-width values to keep our page within a range in which our styling works. If you want a fully-responsive site, you'll need to add media queries—but that's a separate tutorial.
Finally, we'll add a bottom margin of 0.5rem to our headers and paragraphs, to put just a bit of vertical whitespace between elements on the page. (I'll talk more about rems in the "Header Styles" section, below.)
Design styles apply throughout the website, across all pages and in all sections; they determine the look of the app. Here, we'll set our font choices, as well as the colors for everything in the site. Fonts and colors are all we need to change to completely alter the look from one project to the next. By pulling those rules together at the top of the file, we make it easy to make changes.
/* DESIGN STYLES */
/* Fonts */
html { font-family: 'Open Sans', sans-serif; }
h1, h2, h3, h4 {
font-family: 'Bangers', sans-serif;
}
In the Fonts section, we'll be specifying two fonts—one display font and one body font—and we have only need two rules to assign them throughout the app. The first rule, declared for the <html> element, assigns our body font (Open Sans, in this case) as the default font for the site. The only elements that will use the display font we've chosen (Bangers, for this app) are the larger heading elements: <h1> down through <h4>. Chances are we won't even need any headings smaller than <h4>, and if we do, we'll just leave them as bold-face Open Sans.
We'll be using Google Fonts to serve our chosen fonts; we set that up in the <head> element, which I'll cover below.
/* Colors */
html {
background-color: #888; /* medium gray */
color: #ddd; /* dark gray */
}
body,
header nav input[type="submit"] {
background-color: #222; /* dark gray */
}
main {
background-color: #ddd; /* light gray */
color: #222; /* dark gray */
}
We're using a deliberately simple color scheme here—four shades of gray for our backgrounds and font colors, and two tones of red for links, which contrasts well with both light and dark shades of gray. Even so, there are several CSS rules that need to include color properties, so this section looks kind of long. That's because I've moved all the rules which specify color to this section, for ease of maintenance. This probably isn't how you'd set up a stylesheet for the production version, but it makes this drop-in stylesheet easier to customize.
The html and body rules set up the background-color and font color for the site. The html background is a medium shade that contrasts with both the dark color of the header and footer, and the light shade in the main element. The body rule assigns a near-black gray as the background, and a near-white gray as the font color. These colors will appear in everything except the <main> element, which is to say the <header> and <footer> elements. I've added the header nav input[type="submit"] selector to the body rule, since we will want the background color for the "Logout" button on our nav bar to match the surrounding header. More on the "Logout" button in the "Header Styles" section, below.
The main rule sets the light background and dark font color for the <main> element, which includes everything except the header and footer of the app.
/* Colors, con't. */
a:link,
a:visited,
header nav input[type="submit"] {
color: #c00; /* darker contrasting color; inactive links */
font-weight: bold;
text-decoration: none;
}
a:hover,
a:active,
header nav input[type="submit"]:hover,
header nav input[type="submit"]:active {
color: #f22; /* brighter contrasting color; active links */
}
header nav li {
border-left: 3px solid #888; /* match html background-color */
}
main table,
main th,
main td {
border: 1px solid #222; /* border should match body background-color */
background-color: #eee; /* even lighter than main background-color */
}
The next two rules, which start with a:link and a:hover, style the links throughout the page. The first rule, for inactive links, assigns a rich, dark red which contrasts nicely with both the light and dark backgrounds in the site. This rule also sets link text to bold, with no underline; these properties are inherited by active links as well. The second link rule highlights active links a vivid red, which is noticeably brighter than the dark red of the inactive links. The other selectors in these rules, which include input[type="submit"], style the "Logout" button to use the same colors as other links.
Since the logo <h1> in the header is also a link, it will automatically be styled with the red link color, helping it grab the user's attention. If red is too bold a choice for your contrasting color, try two shades of turquoise, gold, blue, green, or even pink. Just be sure that both shades contrast well with both the light and dark shades in your background. Readability trumps aesthetics for an MVP.
The last rule in the "Colors" section assigns the colors for any tables that are included in the <main> element. The table borders should be the same color as the background-color of the body; the background-color for the table elements should be a shade lighter than the <main> background, so the table stands out on the page.
In this section, we'll set up the rules for the elements in our <header> section. It is here that we see the flexibility of our type-based CSS selectors coming into play.
header { padding: 1rem; }
header h1 {
font-size: 3rem;
float: left;
}
header nav { float: right; }
header nav ul { list-style: none; }
header nav li {
display: inline-block;
padding-left: 1rem;
}
header nav li:first-child { border-left: 0;}
header nav input[type="submit"] {
overflow: visible;
text-align: center;
font-weight: bold;
width: auto;
border: none;
}
The general header rule just puts 1rem of padding around all four sides of the header, to keep the text from banging against the edges. I prefer rems ("root ems") for most CSS measurements because they scale smoothly if you make the site responsive. By default, 1rem = 16 pixels; if you want to change that value, just set a font-size property to some value other than 16 pixels in the style for the root element, <html>. If you later change the root em value, then every font-size or element dimension that is sized in rems will scale with the root em.
In the header h1 rule, we specify that any and all <h1> elements in the <header> should be rendered at a size of 3rems. Our CSS selector for the rule, "header h1", won't touch **<h1>**s in any other element of the app—for example, an <h1> in the <main> element will be displayed at its default size, not at the 3rems specified in this rule. We also set the header h1 to float: left, so that it displays against the left margin of the header section.
The rest of the styles in the "Header Styles" section deal with the components of the header's <nav> element. To be sure that we don't affect list or nav elements elsewhere in the app, we begin each selector with a mention of both the <header> and the <nav> types. The header nav rule, which specifies float: right, pushes the nav links to the right-hand margin of the header. The header nav ul rule eliminates the bullet points that appear by default on unordered lists.
In the header nav li rule, display: inline-block; changes the links in the nav bar to appear as a horizontal row, instead of a vertical list. The border-left property establishes a vertical border between each pair of links, and the header nav li:first-child rule turns off that border for the first link on the nav bar.
The last rule in this section, which styles header nav input[type="submit"] elements, is necessary because HTML and Sinatra all but require the "logout" link to be created as a button. Since ending the current session requires a POST route with a hidden input to carry the "delete" action, it is usually created as a single-button form, rather than as a simple link. This rule removes the border and background from the button, so that it looks and behaves like the other links in the nav bar; recall that we set the colors for this input in the "Colors" section, above.
Performance Note: A selector like this one, that depends on the value of an attribute of an html tag, is just about the slowest possible CSS selector. We use it here because we are deliberately trying not to have to add classes to our html file; in the deployed version of an app, you'd be better off to use a class as a selector, even if it's only used that once.
In main styles section, we see our type-based selectors really pay off: they let us write an unlimited number of pages, partials, and forms which look decent (maybe not great, but certainly decent) with absolutely zero time spent on styling. Again, I'll show you the basic CSS code first, then break it down rule by rule.
/* MAIN STYLES - Yield Block */
main {
padding: 1rem;
clear: both;
}
/* Lists */
main ul { margin: 0 auto 1.5rem; }
main ul li {
list-style: none;
margin: 0.5rem 0 1rem;
font-size: 1.25rem;
}
The padding in the main rule just pushes the text in from the edges of the main element a little bit; we use rems instead of pixels here so that the width of the padding scales automatically with the fonts. The clear: both property is necessary to clear out the floats we used in the header; again, this isn't how we'd do it in the production version of our app, but it works fine for this quick-and-dirty, drop-in stylesheet.
Headers, paragraphs and links in the main element use the defaults set in the "Resets" and "Design Styles," so we generally don't need to add anything for them here. If you decide to increase the size of your headers from the default—and you very well might—do that in the Resets section of the drop-in.css stylesheet.
Almost every index view in a Sinatra or Rails app is going to display some sort of list of objects from the data base, but the default presentation of unordered lists is very 1994. To bring our stylesheet into the current century, we'll set list-style to none, to eliminate the bullets. The margin properties are necessary to get the list back into the right spot once we've taken out the bullets. I like to set the font-size for list items to 1.25rem, because these lists tend to be pretty important in our Sinatra apps, and we want them to be prominent.
/* Main Styles, con't. */
/* Forms */
main input {
display: block;
margin-bottom: 0.75rem;
width: 100%;
}
main input[type="text"],
main input[type="password"],
main input[type="email"] {
padding: 0 0.5rem;
}
main textarea {
width: 100%;
height: 5rem;
margin-bottom: 1rem;
padding: 0.5rem;
}
main input[type="radio"],
main input[type="checkbox"] {
display: inline;
width: 1.5rem;
}
main input[type="submit"] { width: 12rem; }
Forms are also very common in Sinatra and Rails apps, but again, the default styling for them is generally pretty ugly and unfriendly. We'll handle this mostly by styling the form input elements.
First, we'll set the display property of main input elements to block, so each one appears on its own line on the page. If you add a label for an input, it will default to display: inline, so labels will show up to the left of the text box they identify. If you prefer labels on the line above, you can add the following style rule to your CSS file to put them there:
main label { display: block; }
The margins and padding we set for input and textarea elements add a bit of whitespace to the form, while setting the width of each to 100% makes each field wide enough to display most input data without annoying sideways scrolling. Remember that <input> elements display only a single line of text. For this reason, you'll want to use a <textarea> form element for any field where users will be entering more than a few words, such as a blog post or comment.
To make sure that radio buttons and checkboxes appear with their labels, we need to add a special rule for those inputs of those types. We'll set them to display: inline, with width: 1.5rem. (These lines essentially undo for radio buttons and checkboxes the styling we applied to input elements in general a few lines above.)
The main input[type="submit"] rule sets Submit buttons to 12rem wide, which is wide enough to display the full text on the button in most cases. If you need more space, just up the rem value in this rule. Also, note that since we styled these buttons using main along with input, this rule does not effect the Submit button that we used to log out the user in the header nav element.
/* Main Styles, con't. */
/* Tables */
main table {
margin-bottom: 1rem;
width: 100%;
}
main th,
main td {
width: 25%;
padding: 0.25rem 0;
text-align: center;
}
If your app displays complex user or game data, you'll probably want to show it in a table. The main table rule sets the table to fill the main element from side to side, and adds a margin at the bottom for whitespace between the table and the next element. The other rule, for main th and td, handles individual cells in the table. It sets each cell, and therefore each column, to 25% of the width of the table, creating a four-column table; if you need more or fewer columns, just change the percentage accordingly. The cells rule also adds a bit of top and bottom padding within the cell, and centers the text within the cell.
Remember that the background and border colors for tables were set in the "Colors" setion, above.
There's just one rule in our footer section:
/* FOOTER STYLES */
footer {
text-align: center;
font-size: 0.8rem;
padding: 1rem;
}
This rule is intended to style a single line of credits, something like: "Created by Jeff George for Dev Bootcamp Phase 2," or whatever. The rule centers the text, sets it to 80% of the default font size, and adds 1rem of padding to all sides of the footer. If you add <a> tags to the footer text, to link to your Github or website, the stylesheet will apply the standard link color and styling to them, which should look fine if you chose your colors carefully at the top of the stylesheet.
At the beginning of this tutorial, I showed you how to set up the <body> element of your layout.erb file, so that you could use semantic HTML tags as CSS selectors to style the page as intended. In this section, we double back and create the <head> element, with the appropriate stylesheet links so that everything works properly. Here is the content for the <head> element:
<!DOCTYPE html>
<html lang="en">
<head>
<link href='https://fonts.googleapis.com/css?family=Bangers|Open+Sans:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/css/normalize.css">
<link rel="stylesheet" href="/css/drop-in.css">
<link rel="stylesheet" href="/css/application.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="/js/application.js"></script>
<title>APP NAME</title>
</head>
Our <head> element includes four <link> tags that are essential to the styling of the app, and they need to be included in the order shown to make sure they work properly.
The first link makes a call to Google's free webfont service, to load our preferred fonts for the app. The link in the example uses Bangers—a brash, all-caps display font—for the headlines, and Open Sans—a clean, simple sans-serif font—for everything else, including paragraph text. If you decide to change the fonts, just go to Google fonts and select an attractive pair of typefaces; you just need one version of the display font used for headers, but you'll want to select four versions—normal, italic, bold, and bold italic—of the body font. Once you've made your choices, Google will provide you with the text for a single link that serves them all up, which you can copy and paste into your <head> element. Be sure to delete the default Google fonts link, if you're changing the font choices.
You're probably going to need three CSS stylesheets. The first of these is normalize.css, which smooths out the differences in how various browsers render web pages; download and include the latest version of normalize.
The next link calls the Sinatra-styling CSS file you created in this tutorial, drop-in.css. The final stylesheet you should link in is application.css, which includes any style rules you've created specifically for the app you're working on. This is where you'd put any rules you needed to handle special cases in this particular app, most likely using classes as CSS selectors. As a general rule of thumb, if you needed a class to style an element, put it in application.css; if you used HTML tags as selectors, it goes in your version of drop-in.css.
The <head> element is also where you link to your Javascript libraries and files. In this example, we call for a recent version of jQuery, served by Google's CDN (content delivery network), as well as application.js, which includes any specific JavaScript you've written for this app.
The text you enter into the <title> element appears on the browser tab that holds your app. Entering your app name here is a subtle but impressive touch, and shows attention to detail.
Once you have created your drop-in CSS-for-Sinatra stylesheet, you'll want to use it. First I'll explain how to drop it in on an existing, unstyled app, then I'll talk about how to use it from the beginning with a new app. Although I'm assuming you're creating a Sinatra app, using the stylesheet for MVP styling on a Rails or other app won't be much different.
First, you'll need to save your stylesheet to the folder in the app where you keep your stylesheets; in an app based on the Dev-Bootcamp-style Sinatra skeleton, that folder is /public/css. Then you simply add a link to the stylesheet in your app's layout.erb file (layout.html.erb for a Rails app). The link should come after the link to normalize.css, but before the link to the app-specific stylesheet, if there is one. You'll also need to insert the Google Fonts link before all the stylesheet links.
Once you've saved drop-in.css in the right folder, and added the necessary links to your layout file, fire up your server and open the app in the browser, and marvel at the instant styling goodness!
If you're using this drop-in stylesheet from the beginning of your development process, you'll begin by saving drop-in.css, along with the current version of normalize.css, in the appropriate folder in your app repo. Again, for a DBC-style Sinatra app, that's /public/css.
Then you'll want to replace the contents of the layout.erb file with the <body> and <head> elements shown in the examples, above. Alternately, you can use the layout.erb file included in this repo. The layout file in this repo assumes you're implementing user authentication and sessions, and includes the HTML for sign-up, login, logout, and user profile links in the nav bar. If you're not using user authentication, just replace the code inside the <nav> element with simple navigation links, as in the code snippet in the example on this page, above.
In the <head> element, be sure you've included a Google Font link to serve your chosen fonts, and stylesheet links to normalize.css and your version of drop-in.css.
This tutorial is Copyright © 2016 by Jeff George.
Drop-In CSS for Sinatra Tutorial by Jeff George is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
The other files in this repo, drop-in.css and layout.erb, may be used under the terms of the MIT License.