Repository: adamschwartz/magic-of-css Branch: gh-pages Commit: 066aa596b420 Files: 68 Total size: 766.2 KB Directory structure: gitextract_ajwg6exb/ ├── .gitignore ├── 404.html ├── LICENSE.md ├── README.md ├── chapters/ │ ├── 1-the-box/ │ │ ├── images/ │ │ │ └── box-model.sketch/ │ │ │ ├── Data │ │ │ ├── metadata │ │ │ └── version │ │ └── index.html │ ├── 2-layout/ │ │ └── index.html │ ├── 3-tables/ │ │ └── index.html │ ├── 4-color/ │ │ └── index.html │ ├── 5-typography/ │ │ └── index.html │ ├── 6-transitions/ │ │ └── index.html │ ├── _template.html │ └── preface/ │ └── index.html ├── config.rb ├── css/ │ ├── base.css │ ├── chapter.css │ ├── eager.css │ ├── home.css │ ├── logo.css │ ├── potions.css │ └── social.css ├── fool-github/ │ ├── css.css │ └── more-css.css ├── index.html ├── js/ │ ├── chapters.coffee │ ├── chapters.js │ ├── footer.coffee │ ├── footer.js │ ├── home.coffee │ └── home.js ├── magician-helpers/ │ └── blackboard/ │ └── index.html ├── planning/ │ └── planning.md ├── potions/ │ ├── buttons/ │ │ └── index.html │ ├── content-reordering/ │ │ └── index.html │ ├── letter-spacing/ │ │ └── index.html │ ├── overflow-ellipsis/ │ │ ├── config.rb │ │ ├── css/ │ │ │ └── overflow-ellipsis.css │ │ ├── index.html │ │ └── sass/ │ │ └── overflow-ellipsis.sass │ ├── potpourri/ │ │ └── index.html │ ├── table-styling/ │ │ ├── config.rb │ │ ├── css/ │ │ │ └── table-styling.css │ │ ├── index.html │ │ └── sass/ │ │ └── table-styling.sass │ ├── three-pane-app/ │ │ ├── config.rb │ │ ├── css/ │ │ │ └── three-pane-app.css │ │ ├── index.html │ │ └── sass/ │ │ └── three-pane-app.sass │ ├── three-pane-app-with-color/ │ │ ├── config.rb │ │ ├── css/ │ │ │ └── three-pane-app-with-color.css │ │ ├── index.html │ │ └── sass/ │ │ └── three-pane-app-with-color.sass │ └── two-pane-app/ │ ├── config.rb │ ├── css/ │ │ └── two-pane-app.css │ ├── index.html │ └── sass/ │ └── two-pane-app.sass └── sass/ ├── _colors.sass ├── _rainbow.sass ├── _variables.sass ├── base.sass ├── chapter.sass ├── eager.sass ├── home.sass ├── logo.sass ├── potions.sass └── social.sass ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .sass-cache ================================================ FILE: 404.html ================================================
In CSS, the box model[1] describes the rectangular boxes that are generated for elements laid out in the page.
Essentially, everything is a rectangle.
Some interesting facts:
border-radius rounds out the corners of this box.box-shadow adds a shadow to this box.outline and box-shadow aren’t part of the box, and therefore have no effect on the layout.The box-sizing property gives you a little control around how boxes are sized within this model. The two possible values for box-sizing are content-box and border-box[2].
content-boxborder-boxFor example:
Both of these boxes have the following CSS, but one has box-sizing content-box and the other border-box.
.box {
height: 5em;
width: 5em;
padding: 1em;
border: .25em solid
}
content-box
border-box
In the border-box case, the width and height of the .box are 5em, exactly what we set. In the content-box case, the width and height are 7.5em = 5 + (2 * 1) + (2 * .25), since we need to include the padding and border on both sides.
One of the benefits of using border-box is you can set a padding and width of mixed units without creating strange sizing edge cases. One fantastic use for this is creating flexible inputs with a fixed padding size.
In the example below, our input has a specific padding in ems and yet we can still specify a width in % (padding: .4em .55em and width: 100%, respectively).
input[type="text"] {
/* Flexibility */
box-sizing: border-box;
width: 100%;
/* Styling */
padding: .4em .55em;
font-size: inherit;
font-family: inherit;
color: inherit;
border: 0;
border-radius: .25em;
outline: none
}
Box
width:
Adjust the box width and observe the input sizes itself perfectly within the box while maintaining a fixed padding.
If you want height and width to behave in the most intuitive way, listen to Paul Irish[3] and put this at the top of your CSS:
html {
box-sizing: border-box
}
*, *::before, *::after {
box-sizing: inherit
}
padding-box, but don’t worry about that.In the previous chapter, we learned that each element in the page is a rectangular box. In this chapter, we will see how multiple boxes get laid out on a page.
With respect to layout, the display property has three values you should be most concerned with. Here are the main differences between how these three display types get laid out:
blockinlineinline-blockblock except my width is determined by my contents.Below we have elements of each of these three display types with the following additional CSS applied to all of them:
* {
background: #eee;
border: .125em solid;
margin-bottom: .5em;
padding: .5em
}
One thing to note here is the difference between inline and inline-block. The inline elements display their .5em padding and .125em border, but only the lefts and rights (and not tops and bottoms) of these actually affect their layout. Whereas the inline-block elements reposition themselves in layout due to their padding and border, just as do the block elements.
Also note that when setting the width to 20% on all elements, the block elements still don’t wrap. Assuming no floats are in the mix, block elements do not allow horizontal neighbors.
Horizontal scrolling sections can be tricky. Fortunately, this is a place where inline-block can help out.
Let’s say I want to display some code with a background color applied to each row of text on hover:
body { background: red /* I know it’s weird to make the whole page red, but sometimes it’s just what you have to do.... */}
If you scroll to the right, you’ll see that the row hover color doesn’t extend all of the way to the right. This is because each row wrapper is a block element, sizing itself to the width of its parent, not to the scroll width of its parent.
By adding an inline-block element which wraps all of the rows, we get the scroll we want, and the row elements (still display: block) can fill the width of that element, which is the same as the scroll width of the whole code block, because it is sized by its contents—in this case, the longest row.
body { background: red /* I know it’s weird to make the whole page red, but sometimes it’s just what you have to do.... */}
The html and body elements are rectangles, just like any other elements on the page. We’ll cover them more in depth in a later chapter—but for now, just know that they’re both block elements.
Tables are crazy, and Chapter 3 covers them in more detail. But with respect to layout, think of a table like an inline-block element with one special property: its table-cell children can center their contents vertically.
Aside from the relatively new and experimental display property flex (which may be covered in a later chapter), no other element can do this.
So with respect to layout, think of tables as a tool which can be used to center arbitrary content vertically.
Vertical center centering with a table couldn’t be simpler.
But if you’re using a table for this purpose (and not to display tabular-data), you should instead use another type of element (div, for example), and set its display property to table to mimick the table behavior.
<style>
.vertical-outer {
display: table;
height: 10em
}
.vertical-inner {
display: table-cell;
vertical-align: middle
}
</style>
<div class="vertical-outer">
<div class="vertical-inner">
<p>I’m so centered it’s not even funny.</p>
</div>
</div>
I’m so centered it’s not even funny.
And that’s it!
As an aside, centering something vertically when you know its height is trivial. First position the element, then set a top and bottom to the same value (0 works), set your desired height, and then set margin-top and margin-bottom to auto.
Basically, text-align lets you align text, child inline elements, and child inline block elements to the left, right, center, or justified. (You know what these mean if you’ve ever used a WYSIWYG editor.)
Now for some magic:
Since inline-block elements are treated more or less as text, you can use text-align: justify on a list of inline-block elements to create a grid structure.
<style>
.grid {
border: .125rem solid;
text-align: justify;
font-size: 0;
padding: 4% 4% 0 4%
}
.box {
font-size: 1rem;
display: inline-block;
background: #eee;
border: .125em solid;
width: 30%;
padding: 2%
}
/* All but the last 3 boxes */
.box:nth-last-child(n+5) {
margin-bottom: 4%
}
.break {
display: inline-block;
width: 30%;
height: 0
}
</style>
<div class="grid">
<div class="box">Column</div>
<div class="box">Column</div>
<div class="box">Column</div>
<div class="box">Column</div>
<div class="box">Column</div>
<div class="box">Column</div>
<div class="break"></div>
</div>
width:
Adjust the width of the boxes and note that text-align: justify keeps the grid intact.
Floats are crazy, so crazy that they’ll also get their own chapter.
But when it comes to positioning, basically what you need to know is that floated elements behave kind of like inline-block elements, regardless of what their display property value actually is.
The truth is, these days, since inline-block is supported pretty widely, there’s not as much use for float anymore. We’ll still cover it since it’s a card up your sleeve, and you should know how to whip it out. But don’t worry about it too much just yet.
In the meantime, you can read up on the relationship between display, position, and float in the w3 CSS2 spec.[1]
Ahh, this is where the real fun begins.
An element is said to be “positioned” if its position property is any value except static.
When an element is positioned, it is laid out according to whichever positioning properties top, bottom, left, and right it has set.
This means not only do these properties reposition (or move) elements, they also can resize elements. For example, with position absolute or fixed, you can set both a top and bottom to essentially impose a fixed height on the element. The precedence here can get pretty complicated, but as a general rule, if you set top, bottom, and height for a positioned element, the height value will be ignored.
The position property can take on the following values:
statictop, right, bottom, or left properties are ignored.absolutefixedrelativetop, right, bottom, or left properties only nudge it from that original position.This stuff can be confusing, so we’ll highlight some important takeaways from these descriptions:
absolute and fixed elements are not part of normal document layout. When their dimensions change, only their child elements are affected. (There is a subtle exception to this which is that absolute positioned elements can cause a scroll bar [in the positive content flow direction: by default, to the right or down] and this can affect the layout of other elements in the page.)static and relative elements are part of the layout. When their layout changes, so do their document neighbors.top, right, bottom, or left, relative elements do not affect their document neighbors. Instead, those neighbors act like the element was never nudged from its original position. (The scroll exception applies here as well.)relative is not so-named because its child elements will be positioned “relative” to it. (That is simply a consequence of it being positioned at all, and so the same could be said about absolute and fixed elements as well.) Rather, it is so-named to describe how you can “relatively” nudge it based on its original position.Now again, for a little magic:
top, bottom, left, or rightPositioning a child element abbutted to the outside of its parent is a bit tricky.
The naive approach is to use a negative positioning property which matches its dimension.
For example:
.parent {
position: relative;
text-align: center;
padding: 1.25em;
background: #eee
}
.child {
position: absolute;
height: 2.5em;
top: -2.5em;
right: 0;
line-height: 2.5em;
background: #444;
color: #fff;
padding: 0 .625em
}
Note the following two lines of CSS:
height: 2.5em;
top: -2.5em;
This part is unideal because it’s not DRY and because we had to specify a height. When possible, it’s better not to specify fixed values in CSS. The more you can let things get sized by content the better, because it means your design is more flexible, supports more use-cases, and is less likely to create future bugs.
So what can we do instead? Use 100% values.
Instead of thinking moving the child up by a negative top, think of it as moving 100% from the bottom. Now our same example can be written like this instead:
.parent {
position: relative;
text-align: center;
padding: 1.25em;
background: #eee
}
.child {
position: absolute;
bottom: 100%;
right: 0;
background: #444;
color: #fff;
padding: .625em
}
Notice how in this version we were able to simplify the padding and line-height because the child box is now sizing itself to its contents, rather than the other way around.
These will definitely get their own chapter. Transforms are where a lot of the real magic lives. But for now, note that unfortunately transformed elements are treated as positioned even if they are statically-positioned[2]. Commit this to memory or at some point it will probably burn you.
table-layoutIn the previous chapter we discussed layout. But what we meant by that is the construction of your content from a design perspective—how you structure your app geometrically to make sense for your use case. Think the Two Pane App potion.
But layout has a specific meaning in various contexts. In the context of tables, it means how the browser decides to size columns and rows of a table element based on the CSS applied by the user agent and you, and the content within each table cell.
This process is truly magical.
A complex layout algorithm is used for both the horizontal and vertical. And these algorithms fork early based on the table-layout you specify, of which there are two options:
autofixedpx, at which point I attempt to honor your sizing exactly, unless I can’t because your math doesn’t work out. (CSS spec)These are very rough definitions, and definitely not complete. I highly recommend you read through the spec at some point to get a better understanding. But nothing is better than playing with live code, so let’s look at some examples to get a clearer picture.
Notice how in the fixed case, the columns are sized evenly since no widths are specified, but in the auto cased they’re sized proportionally by the width of the cell contents.
Now let’s look at the same example with column widths set to 20% and 50%, respectively.
In both cases, our widths are being taken into account, but only relatively. This is always true with auto but it’s additionally true here with fixed because the widths are specified in percentages. The browser says, “20% is 2/7ths out of the total 20+50%”, so when the table is 1000px wide, the first column ends up at 284px and the second column at 714px, roughly a ratio of 2:5. (It won’t be perfectly 2:5 due to cell-spacing, cell-padding, border, border-spacing, border-collapse, etc., rounding, and other constraints.)
Notice that with white-space: nowrap applied to each cell, the auto case compensates but the fixed case lets the text overflow.
Challenge question to think about: why is first column slightly wider in the fixed case?
Now let’s look at the same example with column widths set to 400px and 70%, respectively.
Ok.... Since the width of each table is px, there’s no way for the browser to fit the columns 400px and 70% × px into a px–wide table. So it does the best it can.
In the auto case, our widths are being taken into account, but only relatively. It compares 400px / px to 70% × px and does the best it can. (The behavior here varies from browser to browser.)
In the fixed case, the 400px is honored, since fixed values are prioritized over percentage based values, and so the second column gets the remainder.
This is a CSS course, so I won’t spend much time here. But the main reason to use tables in an application is to display tabular data. Tabular data means anything you might display in a spreadsheet. A content matrix.
When it comes to styling tables with tabular data, there are some good general rules to follow:
:hover background color (or similar) to help the eye associate cells-of-the-same-row.text-align: justify).Check out the table styling potion for an example table design which follows these rules.
In the previous chapter on layout, we showed that tables can be used to center vertically content of arbitrary height. Until flex is widely supported, you should feel comfortable using tables for this. But other than that, if you catch yourself using a table to implement layout constructs that don’t have to do with tabular data, you’re probably doing it wrong.
If your browser support is IE10+, then use flex[1]. Phillip Walton has a great tutorial[2] on vertical centering with flexbox.
There are sooo[3] many[4] reasons[5] why you shouldn’t use tables for anything other than tabular data or vertical centering (as discussed). But to drive that point home, here are some extremely common gotchas that make tables frustrating to work with.
table-layout: auto, Firefox, IE)This means that even if you use table-layout: fixed and specify a pixel width, overflow: hidden isn’t going to actually work on a table cell in every browser. (If you use table-layout: auto, overflows won’t be respected in any browser.)
table-layout: auto
I’m being told to be 100px wide and 20px tall, but I ain’t listening. |
I’m just some text |
Yup. You heard me correctly. You go apply position: relative to a table cell, place a position: absolute element inside, and in Firefox, the absolute element will be positioned relative to the earliest positioned parent of the table instead. Bummer.
The bug was reported in 2000.[6]
I am position: absolute with right: 40px |
I’m just some text |
If, after evaluating the options, you believe that using a table element is the right way to go, just make sure you wrap the contents of every table cell with a div. This way you have all of the styling control you need for each cell while still being able to utilize the extremely powerful—albeit confusing—table layout engine.
I know what you’re thinking. A whole chapter on colors? Trust me, one chapter is hardly enough. Color is an entire dimension, and it’s incredibly powerful.
The first myth worth dispelling is that colors are the same everywhere. In fact, the opposite is probably closer to the truth.
The way a color appears to the human eye is dependent on many other factors as well, including:
As designers, we need to be aware of these challenges so we can address them. Here are just a few techniques to help:
But remember, nothing is better than seeing[6]. Use your eyes (and the eyes of users you test). Test your color choices on a variety of devices and in a variety of lighting conditions until you feel confident that every user will see something desirable.
As noted above, color is an extremely powerful tool. But with great power comes great responsibility. ;) Colors can be used in so many different ways, but using color for one thing may limit your ability to use it for something else. For example, using a particular green to represent your brand may limit your ability to use that same green to indicate “go”.
A color’s power as an aesthetic tool at least matches its power as a functional one. There may be times when you need to make a trade-off between using a color for one versus the other.
Did you know red actually makes people hungry?[7]
Well, similarly it should be no surprise that colors can affect a person’s mood and opinion in a variety of ways. These effects are subjective and rely heavily on cultural and contextual cues. But they’re very real, so understanding them is crucial.
Do you want to shock people? Do you want to put them at ease? Do you want people to trust you? Do you want them to be excited? Do you want to motivate people to take action? Answering these questions will help you decide on colors for your app.
Colors are so powerful that a single color can suggest a brand.
Can you guess the brand for each of these colors? (Hover to see the answer.)
Bootstrap[8] has popularized the use of status classes[9] which map to certain colors.
Many different apps use this or a similar mapping:
#428bca = primary#dff0d8 = success#d9edf7 = info#fcf8e3 = warning#f2dede = dangerBy consistently using #dff0d8 to indicate success or #f2dede to indicate danger, you reinforce a pattern, making it easier for people to understand these concepts in the future.
Sometimes you want to grab a users’ attention. Transitioning a background color or text color (or simply changing it immediately) can be a great way to do so.
By no means is this meant to be an exhaustive list of the ways in which color can be used as a tool. Use your creativity, and try to think of ways color can be used to reduce the complexity of an existing UI.
Just make sure you don’t go overboard and use too many colors. Using too many colors can cause visual dissonance which makes it hard for a user to focus. When choosing a color scheme, it’s very important to think of how colors relate to one another to avoid such issues.
Picking a color scheme is essential. Some colors go well together and some don’t. There are a number of methodologies out there for picking colors. But the best advice is to use your eyes.
Color schemes can have as few as one non-grayscale color or as many as you want. But I’d caution against using more than three. In the biz, three colors which go well together are known as triads. Here are some great resources on color schemes:
One common color mistake I see frontend developers make is using a gray (e.g. #ccc) when they mean to use black with an alpha (e.g. rgba(0, 0, 0, 0.2)).
But, they look the same!?, you may protest. Well, sure. But only when they’re on white!
Each of the white boxes below has box-shadow: 0 .125em .5em [color] applied with the color from the respective column.
#ccc
rgba(0, 0, 0, 0.2)
Both boxes in the first row look fine. But notice how the bottom left white box looks strange. Its #ccc shadow causes visual dissonance with the #85ddba background. Definitely not desired.
The browser default text color is #000. I think this feels pretty intuitive to most people. Subconsciously, one’s head does something like this: Ink is black. Documents are printed in ink. The web is a set of digital documents. So digital text should be black.
This is totally reasonable, and many great sites are designed with black. But an alternative to this which is better in many situations is to use a gray (e.g. #333). This has several advantages.
#000 for emphasis or accents. (This is perhaps the better reason.)You may be thinking: but you just showed an example where you suggested using black with alpha over gray in situations where the background color may change... so why not set the default text color to rgba(0, 0, 0, 0.8) instead?
You’d be right, and that’s definitely an option. The main advantage this has is that text on non-white backgrounds will not get any grey+color clashing (as seen in the example above with the white box on the green background). But I wouldn’t recommend it universally for two reasons:
When setting a color with an alpha between 0and 1, the WebKit browser default of -webkit-font-smoothing: will no longer be honored, and you will never get subpixel antialiasing. (It will be as if you’d set that text to -webkit-font-smoothing: yourself.)
Which font-smoothing is “better” is contextual and fairly subjective. But it definitely feels like subpixel antialiasing is a bandage on a larger problem since: 1) WebKit on retina Macs don’t use subpixel antialiasing (and so it is likely that future high-resolution displays won’t as well), and 2) The popular blogging platform Medium.com (noted for its beautiful typography[13]) applies -webkit-font-smoothing: on the entire document.
Color is powerful. Pick a good set of colors and stick to it! Really see. If something looks odd, it probably is.
First off, give yourself a pat on the back. You’ve made it this far. You’re reading about typography!
Now take a moment to realize that most of the things you look at every day are type. So before discussing typography, let’s look at it.
Take a moment and just fly through some of these beautiful typographically-based design collections:
As with color, designing well with typography requires using your eyes. Really see the shape of each glyph. Notice the negative space between glyphs. You have more control over these things than you might think. And over the course of the next few chapters, I’m going to show you how.
The basic tools in your toolkit consist of the following:
Font properties
font-family — inherit, serif, monospacefont-size — inherit, 18px, 10pxfont-weight — inherit, bold, 100font-style — inherit, italicfont-variant — inherit, small-capsText properties
text-align — inherit, centertext-decoration — inherit, underline, overline, line-throughtext-indent — inherit, 50pxtext-shadow — inherit, 0 1px 3px redtext-transform — inherit, uppercaseMiscellaneous
letter-spacing — inherit, 5pxwhite-space — inherit, pre preline-height — inherit, .75emword-spacing — inherit, 100px 100pxNow that you have a basic idea of what is possible, let’s look at a few combinations of these properties.
Light text colors on dark backgrounds tend to feel as if they have a heavier weight. So with this example of white on purple, we went with a font-weight: 300.
Using all uppercased letters can be a powerful effect, but you’ll want to use it sparingly. One thing that can happen with uppercased words is the letters can feel jammed together. This is because the default kerning and letter-spacing is meant for mostly-lowercased words. To compensate, and also to add a little more gravitas, we added a generous letter-spacing: .4em.
As can be seen in the letter spacing potion, letter-spacing and text-align don’t play so nicely together because the spacing is added to the right of each letter. To compensate for this, when using these two properties together, we add a padding-left to match the chosen letter-spacing (in this case .4em).
.typography-example-1 {
background-image: linear-gradient(135deg, #723362, #9d223c);
background-color: #9d223c;
color: #fff;
padding: 1em 0;
font-weight: 300;
font-size: 1.8em;
text-transform: uppercase;
text-align: center;
letter-spacing: .4em;
padding-left: .4em
}
In this next example, we show how two lines of text interact with each other.
The first line is given a similar treatment as Example 1, but with a heavier weight of 700. To contrast this, the second line is given a thinner weight, 300, an italic font style, and a lighter color, #888.
.typography-example-2 {
background: #fff;
color: #000;
border: .5em solid;
font-size: 1.5em;
padding: 1em 0;
text-align: center
}
.typography-example-2 .title {
font-weight: 700;
text-transform: uppercase;
letter-spacing: .4em;
padding-left: .4em
}
.typography-example-2 .author {
color: #888;
font-size: .7em;
font-weight: 300;
font-style: italic;
letter-spacing: .12em;
padding-left: .12em
}
Sometimes you want to have a little fun with type. Lettering.js is a great little tool for doing just that. But if you’re willing to type out a bunch of span wrappers for every letter yourself, you can do the same sort of thing without javascript!
.typography-example-3 {
border: .2em solid #ff4136;
font-size: 3em;
text-align: center;
padding: .2em;
color: #fff
}
.typography-example-3 span {
display: inline-block;
width: 20%;
padding: .8125rem
}
.typography-example-3 span:nth-child(1) { background: #ff4136 }
.typography-example-3 span:nth-child(2) { background: #ff851b }
.typography-example-3 span:nth-child(3) { background: #2ecc40 }
.typography-example-3 span:nth-child(4) { background: #0074d9 }
.typography-example-3 span:nth-child(5) { background: #b10dc9 }
Hopefully these few examples have given you an idea of what’s possible with the various typographical CSS properties. Typography is such a crucial part of design that a lot of typographical thinking and concepts are embedded in the other chapters. We’ll continue to explore typography as we go.
For more fun with typography, check out the amazing resources below. In particular, glance over Butterick’s Practical Typography[1]. It’s a must-read for anyone with an interest in typography.
Before reading this chapter, please read All you need to know about CSS Transitions by Alex MacCaw. This is a fantastic resource and covers much of what we’d like to cover.
CSS transitions are the ideal solution for transitioning a CSS property (or set of properties) from one value to another value along some easing path.
Now, together, let’s combine this knowledge with what we’ve learned in the previous chapters on Color and Typography.
One extremely powerful tool at your disposal is transition-delay. Transition delays allow you to delay the start of when a transition occurs. In this first example, we set pseudo-random delays for the opacity transitions on each letter, creating a reveal effect that is both subtle and attractive.
.ex span {
opacity: 0;
transition: opacity 1300ms
}
.ex span:nth-child(1) { transition-delay: 200ms }
.ex span:nth-child(2) { transition-delay: 1200ms }
.ex span:nth-child(3) { transition-delay: 800ms }
.ex span:nth-child(4) { transition-delay: 300ms }
.ex span:nth-child(5) { transition-delay: 700ms }
.ex span:nth-child(6) { transition-delay: 600ms }
.ex span:nth-child(7) { transition-delay: 400ms }
.ex span:nth-child(8) { transition-delay: 900ms }
.ex span:nth-child(9) { transition-delay: 700ms }
.ex span:nth-child(10) { transition-delay: 50ms }
.ex:hover span {
opacity: 1
}
CSS transitions really shine when they’re combined. In this example, we specify two transitions, one for -webkit-transform and one for opacity. They are written as part of the same transition declaration, separated with commas.
.ex .title span,
.ex .author span {
/* ... */
transition: -webkit-transform 800ms, opacity 800ms
}
Since we specified two transitions, we also must specify two transition delays. They are written similarly.
.ex .title span:nth-child(1) { transition-delay: 360ms, 300ms }
.ex .title span:nth-child(2) { transition-delay: 420ms, 300ms }
.ex .title span:nth-child(3) { transition-delay: 480ms, 300ms }
/* ... */
Together, these transitions and their delays allow us to create a beautiful wave effect with the letters in this example.
.ex .title span,
.ex .author span {
display: inline-block;
opacity: 0;
transition: -webkit-transform 800ms, opacity 800ms
}
.ex .title span {
-webkit-transform: translateZ(0) translateY(-6rem)
}
.ex .author span {
-webkit-transform: translateZ(0) translateY(6rem)
}
.ex:hover .title span,
.ex:hover .author span {
opacity: 1;
transition: -webkit-transform 800ms, opacity 1200ms;
-webkit-transform: translateZ(0) translateY(0)
}
.ex .title span:nth-child(1) { transition-delay: 360ms, 300ms }
.ex .title span:nth-child(2) { transition-delay: 420ms, 300ms }
.ex .title span:nth-child(3) { transition-delay: 480ms, 300ms }
.ex .title span:nth-child(4) { transition-delay: 540ms, 300ms }
.ex .title span:nth-child(5) { transition-delay: 600ms, 300ms }
.ex .title span:nth-child(6) { transition-delay: 660ms, 300ms }
.ex .title span:nth-child(7) { transition-delay: 720ms, 300ms }
.ex .author span:nth-child(1) { transition-delay: 420ms, 0ms }
.ex .author span:nth-child(2) { transition-delay: 390ms, 0ms }
.ex .author span:nth-child(3) { transition-delay: 360ms, 0ms }
.ex .author span:nth-child(4) { transition-delay: 330ms, 0ms }
.ex .author span:nth-child(5) { transition-delay: 300ms, 0ms }
.ex .author span:nth-child(6) { transition-delay: 270ms, 0ms }
.ex .author span:nth-child(7) { transition-delay: 240ms, 0ms }
.ex .author span:nth-child(8) { transition-delay: 210ms, 0ms }
.ex .author span:nth-child(9) { transition-delay: 180ms, 0ms }
.ex .author span:nth-child(10) { transition-delay: 150ms, 0ms }
.ex .author span:nth-child(11) { transition-delay: 120ms, 0ms }
.ex .author span:nth-child(12) { transition-delay: 90ms, 0ms }
.ex .author span:nth-child(13) { transition-delay: 60ms, 0ms }
.ex .author span:nth-child(14) { transition-delay: 30ms, 0ms }
.ex:hover .title span,
.ex:hover .author span {
transition-delay: 0
}
And just in case you were wondering: transitions work on 3D transforms too.
.ex .letter {
-webkit-perspective: 20rem
}
.ex .front,
.ex .back {
-webkit-backface-visibility: hidden;
transition: -webkit-transform 800ms
}
.ex .back {
-webkit-transform: translateZ(0) rotateY(-180deg)
}
.ex:hover .back {
-webkit-transform: translateZ(0) rotateY(0deg)
}
.ex:hover .front {
-webkit-transform: translateZ(0) rotateY(180deg)
}
.ex .letter:nth-child(1) span { transition-delay: 200ms }
.ex .letter:nth-child(2) span { transition-delay: 400ms }
.ex .letter:nth-child(3) span { transition-delay: 600ms }
.ex .letter:nth-child(4) span { transition-delay: 800ms }
.ex .letter:nth-child(5) span { transition-delay: 1000ms }
Of course, this is just the tip of the iceberg. Colors, gradients, sizes, positions, orientations, etc., can all be transitioned simultaneously. In addition you have delays and custom easing functions right at your fingertips. CSS transitions are so easy to work with. Trust that the browser will do the right thing when you transition a CSS property, because it usually does.
Combining HTML state with CSS transitions can make for rich interactions. Here we use the :checked pseudo-selector on radio buttons to style elements which follow them. This technique is often referred to as “The Checkbox Hack”, but it could work with just about any element which can hold state via some pseudo-selector (:checked, :focus, etc.).
There’s a lot of subtlety in this example, but the main idea is this: each accordion baffle has a label whose for attribute matches the id of a radio button. So, three baffles, three radio buttons. These radio buttons are placed in the document just before the baffle contents, so that we can use the adjacent selector + to style differently baffles which appear after :checked radios.
.accordion .baffle {
height: 0
}
.accordion input[type="radio"]:checked + .baffle {
height: 10em
}
Clicking the baffle label checks the radio button (and unchecks whichever radio button was previously checked) triggering the opening/closing of the appropriate baffle.
And voilà. An accordion with only HTML and CSS.
Accordions have many configurations and types. What may be technically possible to do with one accordion could be impossible with another:
The accordion is a free reed instrument and is in the same family as other instruments such as the sheng and khaen. The sheng and khaen are both much older than the accordion and this type of reed did inspire the kind of free reeds in use in the accordion as we know it today.
The accordion’s basic form is believed to have been invented in Berlin in 1822 by Christian Friedrich Ludwig Buschmann, although one instrument has been recently discovered that appears to have been built earlier.
If you’re looking for more inspiration on what is possible with CSS transitions, check out these amazing demos from Codrops:
For those of you who like a little JS to spice up your CSS, check out rstacruz’s jquery.transit. For simple, static transitions (oxymoron?), it’s overkill. But you may find it really useful if you’re doing transitions which have dynamic or user-generated CSS property values.
CSS is a mess. We all love it, but it’s a mess. I liken it to English: there are a bunch of rules, and you can learn them. But sometimes you’re better off just trying shit and seeing what works and what doesn’t. Magic is a codification of what I’ve learned in that crazy process.
The material in this textbook is intermediate-to-advanced. It assumes an understanding of the CSS syntax, cascading and inheritance, and commonly used selectors. It also assumes you’ve had enough experience with CSS to have learned not to make these common mistakes anymore.
As always, feedback is welcome. I hope you enjoy reading Magic.
A unit of measurement which represents a multiple of the current font-size in pixels
' exampleCSS: 'font-size: 2em' exampleHTML: 'A relative unit of measurement
' exampleCSS: 'width: 80%' exampleHTML: '' 'box-sizing': description: 'Controls how element boxes are sized
' exampleCSS: 'width: 75%;\npadding-left: 25%' exampleHTML: '''content-boxThe content is sized to 75% so the padding is added.
border-boxThe border is sized to 75% so the padding is absorbed.
A line outside the box
' exampleCSS: 'outline: 1em double green' exampleHTML: '' 'border-radius': description: 'Rounded corners
' exampleCSS: 'border-radius: 0 1em' exampleHTML: '' 'box-shadow': description: 'A shadow-like styling of an element box
' exampleCSS: 'box-shadow:\n 0 1em 4em pink,\n 0 .1em red,\n inset 0 .5em #000' exampleHTML: '' 'inline-block': description: '''A value for the display property.
The inside of me is formatted as block, but the element myself is laid into the page as an inline element.
A value for the display property.
My width is sized by my parent and I can have widths and heights set on me. My height is determined by my content.
''' exampleCSS: 'display: block' exampleHTML: 'TextA value for the display property.
My width and height are determined by my contents and widths and heights don’t do anything to me. Think of me like a word flowing in a paragraph.
''' exampleCSS: 'display: inline' exampleHTML: 'TextPositioning properties
' exampleCSS: 'position: absolute;\ntop: 1em;\nleft: 3em' exampleHTML: '''Determines the type of antialiasing used in rendering text
' exampleHTML: '''Family of fonts to be used for the element text
' exampleHTML: 'inherit, serif, monospace' 'font-size': description: 'Size of element text. (Also sets the base for em values for child elements.)
The weight (or thickness) of element text
' exampleHTML: 'inherit, bold, 100' 'font-style': description: 'Used for italic
' exampleHTML: 'inherit, italic' 'font-variant': description: 'Used for small capitals
' exampleHTML: 'inherit, small-caps' 'text-align': description: 'Horizontal alignement of element text
' exampleHTML: '''Decorate element text with a horizontal line above, below, or through the text
' exampleHTML: 'inherit, underline, overline, line-through' 'text-indent': description: 'Indentation of the first line of element text
' exampleHTML: '''5em text indentation.-50px text indentation.A shadow-like styling of element box
' exampleCSS: 'text-shadow:\n 0 0 .4em red,\n 1em 1em green' exampleHTML: 'Typographical styling of text-transform
Additional space to the right of each letter of element text
' exampleHTML: '''Render an element partially or completely transparent
' exampleCSS: 'opacity: 50%' exampleHTML: ''' ''' 'mix-blend-mode': description: 'Adjust the way an element blends with what’s behind it, similar to features often found in image software with layers
' exampleCSS: 'mix-blend-mode: multiply' exampleHTML: '''Options include:
normal\nmultiply\nscreen\noverlay\ndarken\nlighten\ncolor-dodge\ncolor-burn\nhard-light\nsoft-light\ndifference\nexclusion\nhue\nsaturation\ncolor\nluminosity
'''
chapters.init = ->
chapters.setupUserAgentDataAttribute()
chapters.setupContextualCodeExamples()
chapters.setupSocialFooter()
chapters.setupGlobalNavigation()
chapters.setupUserAgentDataAttribute = ->
document.documentElement.setAttribute? 'data-user-agent', navigator.userAgent
chapters.setupContextualCodeExamples = ->
return unless /(Chrome|iPad|iPhone|iPod)/g.test navigator.userAgent
contextualDisplayWrapper = document.createElement('div')
contextualDisplayWrapper.classList.add 'contextual-display-wrapper'
contextualDisplay = document.createElement('div')
contextualDisplay.classList.add 'contextual-display'
contextualDisplayWrapper.appendChild contextualDisplay
document.querySelector('.chapter').appendChild contextualDisplayWrapper
document.body.addEventListener 'click', (event) ->
closeContextualDisplay()
Array::slice.call(document.querySelectorAll(':not(pre) > code')).forEach (code) ->
for specialTerm of chapters.specialTerms when code.textContent is specialTerm
code.classList.add 'contextual-code-example'
return
if location.hostname.match(/localhost/)?.length and window.console?.log?
console.log 'Could not find special term', code.textContent
closeContextualDisplay = ->
Array::slice.call(document.querySelectorAll('.contextual-open, .contextual-transition, .contextual-open-tree')).forEach (element) ->
element.classList.remove 'contextual-open'
element.classList.remove 'contextual-open-tree'
element.classList.remove 'contextual-open-tree-parent'
Array::slice.call(document.querySelectorAll('.contextual-code-example')).forEach (code) ->
code.addEventListener 'click', (event) ->
event.stopPropagation()
if event.target.classList.contains 'contextual-open-tree'
closeContextualDisplay()
return
specialTermObj = chapters.specialTerms[event.target.textContent]
specialTerm = event.target.textContent
if typeof specialTermObj is 'string'
specialTerm = specialTermObj
specialTermObj = chapters.specialTerms[specialTermObj]
if specialTermObj?
closeContextualDisplay()
exampleCSSSelector = specialTermObj.exampleCSSSelector
if not exampleCSSSelector and specialTermObj.exampleCSS and specialTermObj.exampleHTML
fragment = document.createDocumentFragment()
div = document.createElement 'div'
div.innerHTML = specialTermObj.exampleHTML
fragment.appendChild div
el = fragment.querySelector '[class]'
if el
firstClass = el.getAttribute('class').trim().split(' ')[0]
exampleCSSSelector = '.' + firstClass
exampleCSS = if specialTermObj.exampleCSS then """
#{ specialTermObj.exampleCSS }
""" else ''
exampleHTML = if specialTermObj.exampleHTML then """
A unit of measurement which represents a multiple of the current font-size in pixels
', exampleCSS: 'font-size: 2em', exampleHTML: 'A relative unit of measurement
', exampleCSS: 'width: 80%', exampleHTML: '' }, 'box-sizing': { description: 'Controls how element boxes are sized
', exampleCSS: 'width: 75%;\npadding-left: 25%', exampleHTML: '\ncontent-boxThe content is sized to 75% so the padding is added.
border-boxThe border is sized to 75% so the padding is absorbed.
A line outside the box
', exampleCSS: 'outline: 1em double green', exampleHTML: '' }, 'border-radius': { description: 'Rounded corners
', exampleCSS: 'border-radius: 0 1em', exampleHTML: '' }, 'box-shadow': { description: 'A shadow-like styling of an element box
', exampleCSS: 'box-shadow:\n 0 1em 4em pink,\n 0 .1em red,\n inset 0 .5em #000', exampleHTML: '' }, 'inline-block': { description: 'A value for the display property.
The inside of me is formatted as block, but the element myself is laid into the page as an inline element.
A value for the display property.
My width is sized by my parent and I can have widths and heights set on me. My height is determined by my content.
', exampleCSS: 'display: block', exampleHTML: 'TextA value for the display property.
My width and height are determined by my contents and widths and heights don’t do anything to me. Think of me like a word flowing in a paragraph.
', exampleCSS: 'display: inline', exampleHTML: 'TextPositioning properties
', exampleCSS: 'position: absolute;\ntop: 1em;\nleft: 3em', exampleHTML: 'Determines the type of antialiasing used in rendering text
', exampleHTML: 'Family of fonts to be used for the element text
', exampleHTML: 'inherit, serif, monospace' }, 'font-size': { description: 'Size of element text. (Also sets the base for em values for child elements.)
The weight (or thickness) of element text
', exampleHTML: 'inherit, bold, 100' }, 'font-style': { description: 'Used for italic
', exampleHTML: 'inherit, italic' }, 'font-variant': { description: 'Used for small capitals
', exampleHTML: 'inherit, small-caps' }, 'text-align': { description: 'Horizontal alignement of element text
', exampleHTML: 'Decorate element text with a horizontal line above, below, or through the text
', exampleHTML: 'inherit, underline, overline, line-through' }, 'text-indent': { description: 'Indentation of the first line of element text
', exampleHTML: '5em text indentation.-50px text indentation.A shadow-like styling of element box
', exampleCSS: 'text-shadow:\n 0 0 .4em red,\n 1em 1em green', exampleHTML: 'Typographical styling of text-transform
Additional space to the right of each letter of element text
', exampleHTML: 'Render an element partially or completely transparent
', exampleCSS: 'opacity: 50%', exampleHTML: '\n' }, 'mix-blend-mode': { description: 'Adjust the way an element blends with what’s behind it, similar to features often found in image software with layers
', exampleCSS: 'mix-blend-mode: multiply', exampleHTML: '\n\nOptions include:
\nnormal\nmultiply\nscreen\noverlay\ndarken\nlighten\ncolor-dodge\ncolor-burn\nhard-light\nsoft-light\ndifference\nexclusion\nhue\nsaturation\ncolor\nluminosity'
}
};
chapters.init = function() {
chapters.setupUserAgentDataAttribute();
chapters.setupContextualCodeExamples();
chapters.setupSocialFooter();
return chapters.setupGlobalNavigation();
};
chapters.setupUserAgentDataAttribute = function() {
var base;
return typeof (base = document.documentElement).setAttribute === "function" ? base.setAttribute('data-user-agent', navigator.userAgent) : void 0;
};
chapters.setupContextualCodeExamples = function() {
var closeContextualDisplay, contextualDisplay, contextualDisplayWrapper, positionContextualDisplay;
if (!/(Chrome|iPad|iPhone|iPod)/g.test(navigator.userAgent)) {
return;
}
contextualDisplayWrapper = document.createElement('div');
contextualDisplayWrapper.classList.add('contextual-display-wrapper');
contextualDisplay = document.createElement('div');
contextualDisplay.classList.add('contextual-display');
contextualDisplayWrapper.appendChild(contextualDisplay);
document.querySelector('.chapter').appendChild(contextualDisplayWrapper);
document.body.addEventListener('click', function(event) {
return closeContextualDisplay();
});
Array.prototype.slice.call(document.querySelectorAll(':not(pre) > code')).forEach(function(code) {
var ref, ref1, specialTerm;
for (specialTerm in chapters.specialTerms) {
if (!(code.textContent === specialTerm)) {
continue;
}
code.classList.add('contextual-code-example');
return;
}
if (((ref = location.hostname.match(/localhost/)) != null ? ref.length : void 0) && (((ref1 = window.console) != null ? ref1.log : void 0) != null)) {
return console.log('Could not find special term', code.textContent);
}
});
closeContextualDisplay = function() {
return Array.prototype.slice.call(document.querySelectorAll('.contextual-open, .contextual-transition, .contextual-open-tree')).forEach(function(element) {
element.classList.remove('contextual-open');
element.classList.remove('contextual-open-tree');
return element.classList.remove('contextual-open-tree-parent');
});
};
Array.prototype.slice.call(document.querySelectorAll('.contextual-code-example')).forEach(function(code) {
return code.addEventListener('click', function(event) {
var div, el, exampleCSS, exampleCSSSelector, exampleHTML, firstClass, fragment, ref, specialTerm, specialTermObj;
event.stopPropagation();
if (event.target.classList.contains('contextual-open-tree')) {
closeContextualDisplay();
return;
}
specialTermObj = chapters.specialTerms[event.target.textContent];
specialTerm = event.target.textContent;
if (typeof specialTermObj === 'string') {
specialTerm = specialTermObj;
specialTermObj = chapters.specialTerms[specialTermObj];
}
if (specialTermObj != null) {
closeContextualDisplay();
exampleCSSSelector = specialTermObj.exampleCSSSelector;
if (!exampleCSSSelector && specialTermObj.exampleCSS && specialTermObj.exampleHTML) {
fragment = document.createDocumentFragment();
div = document.createElement('div');
div.innerHTML = specialTermObj.exampleHTML;
fragment.appendChild(div);
el = fragment.querySelector('[class]');
if (el) {
firstClass = el.getAttribute('class').trim().split(' ')[0];
exampleCSSSelector = '.' + firstClass;
}
}
exampleCSS = specialTermObj.exampleCSS ? "" + specialTermObj.exampleCSS + "\n" : '';
exampleHTML = specialTermObj.exampleHTML ? "When designing a single app to serve as both a mobile and desktop experience, responsive design techniques are used. CSS media queries are used to style content differently based on the screen dimensions, density, and other queryable properties. A common problem—considered a limitation of responsive design—is reordering the content.
There are a number of ways to reorder content using CSS, but most of them come with gotchas and caveats. Here is a quick reminder of two common techniques.
Using position is one way to go, but it only works when you know how large your content will be or you can afford to be specific about where you place it. An example of when this might be appropriate is when moving the position of your navigation.
This example uses position: absolute to move the nav on mobile devices.
Grid layouts which come with frameworks like Bootstrap and Fluid are a commonly used tool for handling reponsive design, but they only sort-of reorder content. They use float to align blocks side-by-side on large screens and stack them vertically on small ones. When the float is used in the opposite of the language-direction (so right-to-left for English), a reordering of sorts can be achieved.
This paragraph will appear on the right on small screens, but on top on large screens.
This paragraph will appear on the left on small screens, but on bottom on large screens.
As you can see, neither of these techniques allow you to reorder arbitrary content. For that, we’ll need to dive deep into the chasms that are the w3c specs.
The solution involves a not-so-well-known element called <ruby>. Introduced way back in 2001 this little element works in IE5+, Chrome, Safari, and Opera, but unfortunately not in Firefox.
So what is a <ruby> element?
From the current spec:
Ruby is the commonly-used name for a run of text that appears alongside another run of text (referred to as the “base”) and serves as an annotation or a pronunciation guide associated with that run of text.
(Hopefully you're starting to see where this is going.)
So the technique works as follows:
First, place one section in an <rb> element and the other in a <rt>. For example:
<ruby>
<rb>
<span class="section-a">
Section A content...
</span>
</rb>
<rt>
<span class="section-b">
Section B content...
</span>
</rt>
<ruby>
Without any CSS applied, Section B will appear above Section A. By default, the font-size of Section B will be smaller (since it’s inside an <rt> block), so you may want to sort of reset it with something like this:
rt {
font-size: inherit
}
Finally, to “flip” the content order such that Section B appears last, simply set the following CSS on <rt>.
rt {
display: inline
}
Use it in a media query at the desired break point.
It really is a gem of an idea.
When adding letter-spacing to some text, take note of the fact that the spacing is added to the right of each letter, not between each letter.
This means that if you text-align: center text which has been letter-spaced, the letters won’t be centered unless you make a correction. There are a number of ways to do this, including applying a negative left margin, a left padding, or using text indent.
Text indent is perhaps the best method when you can guarantee your text doesn’t wrap. Otherwise, a padding or margin is probably your best bet.
1em letter-spacing with no left padding:
1em letter-spacing with padding-left: 1em:
| Name | Description |
|---|---|
| Example Name |
This is the description of Example Name. This is the description of Example Name.
This is the description of Example Name. This is the description of Example Name.
This is the description of Example Name. This is the description of Example Name.
|
| Example Name |
This is the description of Example Name. This is the description of Example Name.
This is the description of Example Name. This is the description of Example Name.
This is the description of Example Name. This is the description of Example Name.
|
I am a paragraph of text with a floated box inside me.
When working on something with , it can be helpful to have a grid behind you.
Click me!
Sometimes you want to increase the size of a hit target without changing its position. Depending on the situation, there may be a number of ways to accomplish this. But a simple and often effective one is to apply a negative margin and padding of the same value. They sort of cancel eachother out while allowing you to increase the hit target region by the size you choose.
<svg xmlns=\'http://www.w3.org/2000/svg\'>
<filter id=\'grayscale\'>
<feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/>
</filter>
</svg>
| Browser | Usage | Initial release | Stable version |
|---|---|---|---|
| Chrome | 42.68% | September 2, 2008 | 31 |
| Internet Explorer | 25.44% | August 16, 1995 | 11 |
| Firefox | 20.01% | September 23, 2002 | 25 |
| Safari | 8.39% | January 7, 2003 | 7 |
| Opera | 1.03% | April 1, 1994 | 18 |
| Browser | Usage | Initial release | Stable version |
|---|---|---|---|
| Chrome | 42.68% | September 2, 2008 | 31 |
| Internet Explorer | 25.44% | August 16, 1995 | 11 |
| Firefox | 20.01% | September 23, 2002 | 25 |
| Safari | 8.39% | January 7, 2003 | 7 |
| Opera | 1.03% | April 1, 1994 | 18 |