Unless you're solving for art direction, you don't need to the element. In fact, you're likely doing your users a disservice by using the <picture> element.
I'd wager that the majority of "responsive images" use is serving appropriately sized images for the situation. The situation = the size the image will be displayed at, screen pixel density, and bandwidth. That's exactly what <img sizes="" srcset=""> is best at. It does magical stuff that the browser can optimize for, whereas <picture> will only be as efficient as the rules you've explicitly set up for it (in which you almost surely weren't verbose enough with maximize efficiency). Only use the actual picture element when you need to do something very explicit (e.g. an exact breakpoint you need to swap out a totally different image).
An idea by Glen Maddern for moving styling with attributes rather than classes. Not an entirely new concept, but it's interesting to give it a name and explain a system based around it. Thoughts:
It avoids namespacing. Using modifiers like "large" is often a tempting naming convention, but too generic for a class name on its own. "Large" would be fine as an attribute value e.g. am-Button="large"
Cleaner on the HTML side, messier on the CSS side.
Probably technically slower CSS selectors, but that has been shown over and over to make very little difference except in extreme situations.
It probably makes the most sense while building a framework or pattern library that features variations. It's OK that these parts of your site would use an approach like this and your day-to-day styling doesn't.
I like how it sounds like caveman talk. <i am-Ogg="you jane.">
I've been coming around to the idea of using Susy (a Sass add-on for grids) lately. I like the idea of grids-on-demand, rather than a strict framework (no matter how simple). We even added support to CodePen. Last week I ran into Eric Suzanne, the creator of Susy, and I told him Zell Liew was working on a guest post for this site. He was "Oh, Zell! Yeah he's awesome, he's like the unofficial promoter and trainer for Susy." And so, here's Zell introducing us to Susy!
Susy provides you with the tools to build your own grid layouts that match the needs of your website. She gives you the freedom and flexibility to build any design that you can imagine. It doesn't matter if your design has 5, 14 or 48 columns. Unequal width columns? Susy's got you covered.
Why would you pick Susy over the million other frameworks out there on the internet? You may never even heard of her before. What if her beauty is only skin deep?
Susy is one of the best things that happened to me in terms of my front-end development workflow over the past few months. She has helped me out so much that I decided to write a book about her to share her merits with the world. It's an in-depth guide on Susy and how you can use her to easily create any website design you want.
Still not convinced? Well then, all I ask is you give me the opportunity to try and change your mind through this article. We'll see why Susy is so special, and how she can help you out.
As they say, there's no substitute for experience, so I'm going to walk you through the Susy experience of creating a layout. And you can compare it with the experience of creating the same layout by hand (which involves calculating pixels...by yourself).
We'll start off simple, and progress on to more fancy layouts once you've gotten to know Susy a bit better.
Before we start, there are some things you must know about our friend, Susy. You MUST use the Sass preprocessor language for it, which means you have to at least know how to convert Sass into CSS. And no, you can't use LESS. Sorry, LESS folks!
Creating a Very Simple Layout
CSS-Tricks has a pretty straightforward layout, so let's use that for our first example.
Have a look at the blog and you'll find that CSS-Tricks is made up of two content areas: the main content and the sidebar area.
We'll assume CSS-Tricks uses a 12-column layout for the whole site, and it's content area takes up 8 of 12 columns while the sidebar takes up 4 of 12 columns. Quite standard stuff if you've worked with grids before.
If you have used Bootstrap or Foundation before, the markup for this site layout would be:
Simple, yes. But Bootstrap and Foundation also come with a lot of baggage, that a lot of the time, we just don't want to deal with. If you don't need most of the other stuff that comes with Bootstrap and Foundation, Susy would be a much better fit for you.
A stoic developer who hand-codes all his sites from scratch, no frameworks or frills, would probably write his mark-up like so:
To create an 8-column content and 4-column sidebar, you'll have to measure widths of the content, the gutter and the sidebar. One rule of responsive web development is to output these widths in percentages, so you'll have to convert them accordingly.
So the corresponding CSS would probably look something like this:
/* I tend to use border-box sizing for all my web layouts. */ *, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
.wrap { max-width: 1120px; }
/* Clearfix for floated children */ .wrap:after { content: " "; clear: both; display: block; }
A quick note before we continue further: I love to set the box-sizing property to border-box for all of my web elements, and I highly recommend you do the same as well. Chris has written extensively about box-sizing here.
Anyway, the hand-coding from scratch method can start to get tedious if you have multiple layouts to work with. Imagine calculating all those percentages every single time!
That's precisely what Susy helps you with. She does the math for you.
Creating the Simple Layout With Susy
When using Susy to create any layout, you'll first have to let her know what settings you want her to use. There are a whole bunch of default settings that Susy uses to kickstart every project.
I'm assuming this is your first experience with Susy, so we'll stick with most of the defaults for now. You do need to change two properties though, and you change them by declaring them on the $susy map.
At the same time, let's also apply the border-box property like we have done above.
@import "susy"
/* Changing Susy default global settings */ $susy: ( /* Tells Susy to use 12 columns */ columns: 12, /* Tells Susy to use border-box */ global-box-sizing: border-box );
/* Makes border-box properties */ @include border-box-sizing;
This time round, instead of manually calculating the exact pixels and percentages for the layout, we're going to tell Susy to do it for us. To do this, you'll just have to tell Susy that content is 8 columns and sidebar is 4 columns.
We do have to let Susy know that sidebar is the last item on the row so it can make the necessary adjustments to minimize the effects of browser subpixel rounding errors.
/* Tells Susy there's 8 columns in the content */ .content { @include span(8); }
/* Tells Susy there's 4 columns in the sidebar, and its the last item on the row. */ .sidebar { @include span(4 last); }
Did you notice that the CSS output is exactly what we needed to create? Susy sure saved us a lot of time.
We're not done with the CSS-Tricks layout yet though. If you scroll down to the bottom of the site, you'll find the social media area, and you can also see that there are 10 social media items within this area:
If you use a traditional grid framework in your design, you're pretty much in trouble. As far as I know, there's really no simple way to quickly manipulate the number of columns from 12 to 10 using frameworks like Bootstrap or Foundation without affecting the rest of the layout.
Susy doesn't bind you with such rules. You are free to create your own rules. After all, it's your grid.
Your Grid, Your Rules
Anything is possible when you're making your own grids. Before we move on to the CSS, let's first get the mark-up correct. It should look something like this:
Now we can begin work on the CSS. Using the hand-coding approach, you may have realised you'll have to calculate the width of each item and gutter again.
It's so much simpler if we get Susy to do it for us. You'll just have to tell Susy that there are 10 columns in all (instead of 12), and each social item takes up one column.
.social { /* Adds a padding of 19px to each side of the .social container */ padding: gutter(); /* Adding a clearfix because all children elements are floated */ @include cf;
.social__list-item { /* Telling Susy that there are 10 items, and each takes up 1 row */ @include span(1 of 10); /* Another way to tell Susy that this is the last item in the row */ &:last-child { @include last; } } }
And Susy produces the same exact CSS properties that we need for the grid!
This is how simple it can be to use Susy. Math skills not required. As long as you know how to make a grid with vanilla CSS, you will know how to use Susy.
One thing about working with percentages is the inevitable fact that your output will contain subpixels, like 10.4px. This has been a major problem with layouts for a very long time because different browsers treat these subpixels differently.
For instance, webkit browsers round these pixels down, and 10.4px will be rounded down to 10px. The larger the number of items in your layout, the larger the occurrence of each rounding error. So our 10-item error is clearly showing up in webkit browsers.
Such errors are unbecoming and we simply cannot have that.
Dealing With Subpixel Rounding Errors
One method is make sure the percentages don't generate subpixels at all. But
that's not a good idea because and you're going to be stuck with either a non-responsive website, or one that snaps to different grid widths.
There's another method, which I call the Isolate Technique.
This method is slightly more advanced and requires you to know more about laying out elements with the isolate technique that was developed by John Albin Wilkins. This technique has been used in the battle-tested zen grids for a long time. If you're familiar with Zen grids, you'll know what I'm talking about here.
If not, let's examine how to use this technique.
The Isolate Technique
The premise of the isolate technique is to reduce the compounding effects of rounding errors by positioning them right where they should be with specific margins.
The requirements of using this technique are:
Each element must have a margin-left property to position itself in the correct position.
Each element must have a margin-right property of -100% to pull the next item to the extreme left edge of the container.
This technique can be quite confusing if you're not familiar with how negative margins. Let's go through a full step-by-step explanation on how it works.
Say you need to create a 3-column layout:
We'll go through the positioning of each item one-by-one. Item 1 is quite straightforward.
It has a margin left of 0, so it sits on the start line.
Then it pulls the next item (Item 2) back to the start line with a margin-right of -100%.
Item 2 has been pulled to the start line by Item 1 before it's own CSS properties kick in.
Item 2 then positions itself with a margin-left property that is the percentage value of up 2 columns and 2 gutters, positioning it exactly on the third column.
Item 2 also has a margin-right property, which pulls the next item (Item 3) to the start line.
Item 3, gets pulled to the start line by Item 2 before its own CSS properties kick in.
It is then positioned on the 5th column by its margin-left property that is a percentage value of 4 columns and 4 gutters.
And it pulls the next item to the start line and the pattern repeats itself for the number of items in total.
If any item needs to start on a new line, it has to clear the float from its previous item with the clear: left property.
And to align it to with the first column (positioned on the start line), it has to change its margin-left property back to 0 to place itself there.
That's how the Isolate Technique work. Now that you understand it, we can use it to deal with the subpixel rounding issue.
Solving Subpixel Rounding Errors
We have to position each item manually with the isolate technique, and Susy can help us with that.
Susy has a convenience function that helps you skip ALL the manual stuff. It's called the gallery mixin.
.social__list-item { @include gallery(1 of 10); }
And the CSS it outputs uses the isolate technique mentioned above.
Now that we've built up some momentum, let's take it up a notch and make the layout responsive.
Making the Layout Responsive
Responsive websites have became increasingly important and I'll be selling you short if I didn't talk about how to make responsive layouts with Susy.
The very first step to creating responsive layouts is to make sure that you're familiar with how media queries work. Pay special attention to the Mobile-First media queries in that article as we're going to be using them.
For simplicity's sake, let's only create a layout with two breakpoints - one for the mobile view that begins at 0px and ends at 700px; and another for the desktop view that begins at 700px.
In the mobile view, both the .content and .sidebar sections take up a full 100% of the viewport. After 700px, .content and .sidebar will be split into 8 columns and 4 columns respectively.
If you hand code it, this is what it might look like (using Sass):
.content { /* Styles for mobile view go here */
/* Styles for desktop view go here */ @media (min-width: 700px) { width: 66.10169%; float: left; margin-right: 1.69492%; } }
.sidebar { /* Styles for mobile view go here */
/* Styles for desktop view go here */ @media (min-width: 700px) { width: 32.20339%; float: right; margin-right: 0; } }
You don't need to create any styles for the mobile view because <div> blocks takes up 100% width by default. What you need, however, is to make sure the desktop layout kicks in once the viewport hits 700px.
Coding a responsive layout by hand is the same as coding a responsive layout with Susy, only Susy makes things faster. You use the exact the same technique and media queries.
.content { /* Styles for mobile view go here */
/* Styles for desktop view go here */ @media (min-width: 700px) { @include span(8); } }
.sidebar { /* Styles for mobile view go here */
/* Styles for desktop view go here */ @media (min-width: 700px) { @include span(4 last); } }
So that's it for .content and .sidebar.
The next bit is the social media section. This is how it looks like in the mobile view.
Instead of having each item take up 1 of 10 columns, each item now takes up 2 of 10 columns on the mobile view, and you can do this by using span(2 of 10).
Note: You might be wondering why not use 1 of 5 columns instead. There are slight nuances between span(1 of 5) and span(2 of 10), and a picture best explains it:
These are fundamentally different layouts because of the number of gutters involved. You can always use the 1 of 5 option but I'll stick with 10 because the size of the gutters are then much closer to 12.
We'll call the gallery mixin again, and add one gutter below each item to provide some separation between the rows.
.social { /* ... Same styles as above */
.social__list-item { @include gallery(2 of 10); margin-bottom: gutter(10); }
Working with Susy is just like working with vanilla CSS. The key difference is that Susy is a math-whiz.
Susy is so good at math that she can allow you to create wacky asymmetric grids that have columns of different sizes.
Let's try creating the layout for CSS-Tricks using an asymmetric grid.
Making The Layout With Asymmetric Grids
Declaring columns is a tiny bit different when working with asymmetric grids. You tell Susy the number of columns and the ratio of their sizes with a Sass list. Since CSS-Tricks has content that is twice the width of the sidebar, the columns can be represented by a 2 1 list. This means that there are two columns, one of them is twice the width of the other.
You'll also have to set the output mode to isolate on Susy's global settings:
$susy : ( columns: 2 1, output: isolate, )
Instead of saying content has 8 columns now, we say that content has 1 column. Susy is smart enough to know the size of the column if you tell her its location. In this case, content is the first item.
.content { @include span(1 first); }
Likewise, you'll have to let Susy know how many columns sidebar takes up and where it is located. In this case, sidebar takes up the second column, which coincidentally is also the last item on the row.
.sidebar { @include span(1 last); }
This is how it will look:
There is a lot more to asymmetric grids and it would take a whole new article to show you how to build them with Susy. I've have created a simple design that explains the concepts of asymmetric grids on my blog that uses the following as an example.
In addition, I wrote about creating an asymmetric layout comprehensively in Learning Susy with a more realistic example that was based on Nathan Ford's design. You may want to grab the book if you want to find out more about it.
Conclusion
Well, we've reached the end of this introduction to Susy, as well as some peripheral (but important) matters like subpixel rounding errors. I hope you are at least a little bit convinced to give Susy a try.
In fact, there is so much more to Susy than what I could cover here. So I'm going to refer you to 5 sample chapters of my book on Learning Susy. Have a read through and you'll learn how you can use Susy to do so much more. Also, if you have any questions about Susy, or just front-end development in general, feel free to contact me.
Early in my career I had a job where a good half of it was cutting detailed clipping paths on photos (using the Pen Tool in Photoshop). We had an in-house photography department that shot furniture and electronics and the like, and I had to cut outlines around everything so designers could easily isolate things as needed. I'm sure I did thousands of photos. I'm still not a super expert, but I value my Pen Tool skills and end up using them regularly.
A reader sent me in a GIF showing off a cool effect they saw on Google on mobile. (Presumably the homepage you see when you launch Chrome on Android?) There is a search input in the middle of the page that scrolls with the page, but as it's about to scroll off the page, it becomes affixed to the header. Let's cover that, because, you know...
OH: I'm a mobile web developer, so I basically spend all day getting requests for content to stick to the top after some scrolling
It's a cool effect particularly if used to make UX better and not to affix some dumb intrusive ad. Here's the GIF I based the idea on. Little choppy, but the idea is there:
Two States
Like most good tricks, there isn't much to it. All we do is think of (and design for) the two different possible states:
Search bar in its scrollable position
Search bar in its fixed header position
We toggle between them simply by changing a class name. There is no trickery with having two search forms that reveal themselves in different scenarios. That's good, as we don't want to smurf around with keeping those in sync. Much easier to just move a single one around.
State One
(I'm going to use SCSS here because the nesting is nice for managing states.)
.search { /* Container just in case we want more than just the search input to come along */ position: absolute; top: 155px; left: 20px; right: 20px; input { width: 265px; transition: width 0.2s; -webkit-appearance: none; /* Autoprefixer doesn't catch this */ } }
.top { height: 250px; /* Space in here for search */ padding-top: 40px; position: relative; }
State Two
Assuming we've put a class of "fix-search" on a parent element.
.search { /* Container just in case we want more than just the search input to come along */ ... .fix-search & { position: fixed; top: 10px; input { width: 250px; } } }
Switching States
The trick here is applying that class at just the right moment. In our little demo, we can just test for when that perfect moment would be and hard code that into some JavaScript watching for scrolling. jQuery style:
That's all it takes to switch between the two states we've set up. If the page has scrolled down 147 pixels or more, it will have that class applied. If not, it doesn't. Even if you go down and come back up the class will go away (because this little function gets called on every scroll event).
In the grand tradition of mentioning scroll debouncing whenever any demo binds an event to a scroll event: you should consider debouncing when binding functions to scroll events, because if you don't, it'll get called a zillion times and could be slow.
CSS
This is the kind of thing that would be sweet to do in CSS alone. No great solutions pop to mind just yet, but I'm continually amazed by crazy things people use CSS to do, so if something comes along I'll update this.
Perhaps someday we'll be able to do scroll position media queries?
Fixed Position Support
Note that this demo relies on fixed positioning, which has a sketchy history on mobile. While I'm tempted to say it has "pretty good" support these days, you should make the judgement yourself. Some reading:
This is just one (not particularly reusable) example
There are a lot of magic numbers in this demo. Anytime you are setting heights there should be some warning flags happening up in your brain. It doesn't mean never do it, it just means be warned. In this demo, if that centered image in the header changed height, it would look weird pretty much no matter what. This isn't the most flexible and forgiving of layouts as it is. Even if you fixed the header to look right after a change, the JavaScript has corresponding magic numbers for when to change state.
Perhaps some version of using waypoints (or the concept of it) could make a more bulletproof system.
I just watched Jen Simmons give a talk Modern Layouts: Getting Out Of Our Ruts and she had some great examples of how easy CSS shapes are to use, even with safe progressive enhancement. If you haven't heard of them, essentially it's a way to wrap content (typically text) around custom SVG-like paths.
As serendipity would have it, here's Razvan Caliman with a megapost on how it all works and how to use it with real examples.
The following is a guest post by Joni Trythall. Joni recently finished a Pocket Guide to Writing SVG. I've been excited about SVG for a while now, working with it, learning about it, writing about it... but there is so much to know. It really is another whole universe of the web. So I reached out to Joni to see if she would like to write something for CSS-Tricks on SVG and she agreed! Here's Joni on lighting in SVG, something I know I knew nothing about before reading this.
SVG has its own set of filter effects that, when writing SVG, allow the author to combine several of these effects and apply the filter to the graphic.
Some of the more fantastic features of SVG filtering revolve around the light source. You can apply a lighting effect to SVG and then control the details of that through one of three filter effects: fePointLight, feDistantLight, and feSpotLight.
These light source filters allow for some especially neat capabilities. Check out this shiny, lighted apple (as compared to a non-lighted one):
In this post we will review the basic syntax for SVG filters, dive into some of the more common attributes, look at lighting source effect options, and then bring everything together with an SMIL animation experiment.
filter Element
SVG filter details reside within a <filter> element. A <filter> is comprised of several different effects and these effects are then all applied to a graphics element (such as a rectangle, <rect>) or container element (such as a group, <g>).
While there are a great number of filter related attributes, we will specifically be focusing on the ones used here throughout the apple demos.
The following attributes, with the exception of in, are able to be used within any SVG filter effect.
x, y
The x and y attributes within filters set the minimum coordinates on the appropriate axis for the region in which the filter will take place.
result
The result attribute assigns a name to a specific filter effect. This effect can then be referenced by another effect to follow through the in attribute. Both effects must reside within the same <filter> and it allows us to combine several filter effects into one to be applied to a graphic.
A result value is not required, but is useful when looking to reuse specific effects. While its purpose closely resembles that of an id, result values are only useful within the same <filter> element and are therefore not accessible anywhere else in the document.
in
The in attribute specifies the input for a specific filter effect. There are seven value options here, including the name of the result which we just reviewed in the previous section, but perhaps the most frequently referenced are SourceGraphic and SourceAlpha.
If the in attribute is left unspecified and the filter effect is the first listed within the <filter> then it will default to SourceGraphic. Alternatively, if this attribute is left unspecified and is not the first effect listed, then the default will inherit the value of the effect before it.
A SourceGraphic value within in sets the input for the filter as the RGB colors of the graphical element to which it is being applied.
The SourceAlpha has the same impact as SourceGraphic except black color values are used for the RGB channels input.
Here is a look at the impact these two values have when applying a blurred filter to two identical circles, both having a fill value of #ED6E46:
Lighting Source Filters
Now that we have a familiarity with some of the more common attributes used within SVG filters lets focus on the lighting source effects.
While diffuse light is light that hits a surface and gets scattered equally in all directions, specular light refers to a bright spot of light that gets reflected in a particular direction. The demos throughout this post will only focus on lighting effects accessed through specular light, or feSpecularLighting, or "light that makes all the things look shiny!".
Attributes used within this filter effect, such as specularConstant and specularExponent, set the details of the light based on Phong lighting model calculations.
The basic light filter related syntax going forward will look like this:
Let's take a look at the fePointLight, feDistantLight, and feSpotLight effects that will reside within this specified calculation.
fePointLight
fePointLight establishes a specific point as the main light source when applying the feSpecularLighting filter and is how the shiny apple was achieved in the very first demo.
The appearance of the light not going to the very edge of the apple is created by the value of the feGaussianBlur effect, and a higher value here would create greater blending.
x, y, z
The x, y, and z attributes here determine the location of the light source in the coordinate system on the appropriate axis. z will adjust the perceived size of the point of light by determining its location from the point to the user; a higher value here results in a larger point of light that is "closer" to the user.
feDistantLight
feDistantLight defines a distant light source.
The azimuth attribute within feDistantLight defines the clockwise direction angle in degrees for the light source on the XY plane.
The value within `elevation` defines the direction angle in degrees of the light source from the XY plane towards the z axis.
The filter on the apple below has an azimuth value of 25px and an elevation value of 40px:
feSpotLight defines a spot light as a light source.
The x, y, and z values establish the location of the light source along the appropriate axis within the coordinate system.
The limitingConeAngle restricts the area to which light is projected by disallowing light to render outside of it. This value sets the angle in degrees between the spot light axis and cone. A higher value here results in a less restricted area.
The feSpotLight filter below contains the following specifications: x="400" y="400" z="900" limitingConeAngle="9":
Light sources can also be animated. The code for this graphic is fairly complex and lengthy when used inline, but for the sake of science we are going to have the sun moving from one side of the apple tree to the other acting as a "light source".
The sun, aka our pretend light source, is moving across the screen through SMIL animations. In turn, the light source filter on the tree is also being animated in response to the sun's positioning.
An <animate> element must reside within each element to be animated, which are both noted in the HTML.
Conclusion
SVG light source filters can really bring a graphic to life on the screen, testing the boundaries of the two-dimensional limitations of the language. These advanced features allow us to create more complex and detailed images that live within the DOM and can easily become interactive or scripted.
The lighting details addressed here really only scratch the surface concerning the capabilities of SVG filters, but hopefully what we have reviewed inspires you to tinker with it and explore its full potential.
In this post we reviewed some basic <filter> syntax, took a look at a few attributes, focused on some neat lighting sources effects, and then animated these effects in the name of science.