Go to
https://github.com/Snugug/code-rwd-sass-compass
to fork and clone the repo
Each challenge's starting point is available as a branch.
If you would like to be caught up quickly, simply check out the branch of the challenge you're on.
// If the window is at least 500px wide...
@media (min-width: 500px) { ... }
// If the window is less than 785px wide...
@media (max-width: 785px) { ... }
// If the user is printing the page...
@media print { ... }
// If the window is in between 520px and 699px...
@media all and (min-width: 520px) and (max-width: 699px) { ... }
Breakpoint is a Compass extension. It makes
media queries much easier to maintain.
$nav-lg: 680px;
.main-nav {
width: 100%;
@include breakpoint($nav-lg) {
width: 60%;
margin: 0 auto;
}
}
.main-nav {
width: 100%;
}
@media (min-width: 680px) {
.main-nav {
width: 60%;
margin: 0 auto;
}
}
Boil down most media queries to one simple value.
Assign that value a meaningful name.
You keep all the styles for a component in one place
Manage media queries by purpose and context.
Pass Breakpoint just a number and you get a min-width query.
// THIS IS OUR FIRST BREAKPOINT VARIABLE
$basic: 500px; // <-- YUP, THIS ONE
#main-nav {
width: 100%
@include breakpoint($basic) {
width: 75%;
}
}
#main-nav {
width: 100%
}
@media (min-width: 500px) {
#main-nav {
width: 75%;
}
}
Two values creates a min-width / max-width query.
$big-header: 330px 750px;
#header {
font-size: 2em;
@include breakpoint($big-header) {
font-size: 2.5em;
}
}
#header {
font-size: 2em;
}
@media (min-width: 330px) and (max-width: 750px) {
#header {
font-size: 2.5em;
}
}
If one value is a string, assume a feature/value pair
$too-damn-wide: max-width 1000px;
#hero-image {
max-width: 100%;
margin: 2em 0;
@include breakpoint($too-damn-wide) {
margin: 0.5em 0;
}
}
#hero-image {
max-width: 100%;
margin: 2em 0;
}
@media (max-width: 1000px) {
#fifty-seven-chevy {
margin: 0.5em 0;
}
}
String them together to create more complex queries
$tighten-text: (max-width 1000px) (orientation portrait);
#main-article {
font-size: 1em;
line-height: 1.375;
@include breakpoint($tighten-text) {
line-height: 1.25;
}
}
#main-article {
font-size: 1em;
line-height: 1.375;
}
@media (max-width: 1000px) and (orientation: portrait) {
#pappas {
line-height: 1.25;
}
}
Breakpoint can also output fallbacks for when no media queries are present, such as in IE<9
$breakpoint-no-query-fallbacks: true;
$nav-lg: 500px, 'no-query' '.lte-ie9';
nav {
@include breakpoint($nav-lg) {
width: 60%;
margin-right: 4%;
}
}
@media (min-width: 500px) {
nav {
width: 60%;
margin-right: 4%;
}
}
.lte-ie9 nav {
width: 60%;
margin-right: 4%;
}
Just add a conditional class to your <html>
element
min-width
em
units.max-height
in portrait*, *:before, *:after { @include box-sizing('border-box'); }
$grids: 12; // Number of Columns
$gutters: 1/3; // Gutter to Column ratio, 20px/60px = 1/3
Out of the box, Singularity offers two output styles, float and isolation. The default output style is isolation, but we're going to change it float for now as it will be more familiar to begin with.
$output: 'float';
Align to columns using Grid Span:
@include grid-span($span, $position);
$span
is the number of columns to span
$position
is what column to start from
#container {
max-width: 960px; // Outer Container
padding: 0 10px; // Side Gutter
margin: 0 auto; // Center Container
@include clearfix; // Have container clear floats properly
}
.left {
@include grid-span(6,1);
}
.right {
@include grid-span(6,7);
}
<article>
Grids should work with the content itself, not impose a class structure. We should not limit ourselves to a 12 column layout with four breakpoints.
Instead, let's design our sites around the content, creating awesome mobile-first layouts.
In short: We deserve better.
We are going to build and style our grid based upon the content.
Start with the small screen first, then expand until it looks like shit. TIME FOR A BREAKPOINT!
Stephen Hay
This is the opposite of using a framework like Twitter Bootstrap, where the grid is pre-defined.
Grids provide order to your design and structure to your information.
The ideal grid is specific to your content and
your design, since it is an extension of both.
Within singularity you can create completely different grids for different breakpoints.
You can also customize each section of the site completely, creating sub-structure within a block of content.
Grids where the columns are not the same size
Custom grids for each design allow for more unique designs to better highlight your content
Singularity Extras is very useful when working with asymmetric grids
// List of column width in relation to each other
$grids: 5 2 3 3 7 9;
// List of symmetric grids to compound together
$grids: compound(3, 4);
// Ratio and number of columns
$grids: ratio(golden(), 4);
// Number of columns and ratio
$grids: ratio-spiral(5, golden());
// Asymmetric grid and the gutter width of the grid to snap to.
$grids: snap(2 4 4 2, 1/3);
Settings $grids and $gutters set a global context for them, making them available to use without redeclaring them each time they're needed.
You can set different global contexts to use at different min-width breakpoints
$grids: 12;
$grids: add-grid(2 8 2 at 500px);
$gutters: 1/3;
$gutters: add-gutter(1/4 at 532px);
When using the breakpoint mixin, Singularity is able to determine which global context you'd like and subsequently use the correct one when you use the grid-span mixin
$grids: 2;
$grids: add-grid(4 at 475px);
$gutters: 1/6;
#nav {
width: 100%;
@include breakpoint(500px) {
@include grid-span(3, 2);
}
}
If you need to override the global contexts, for instance if you need to nest a grid and therefore change the grid you're using, use the layout mixin
$grids: 12;
$gutters: 1/3;
#main {
@include grid-span(8, 1);
@include layout(8) {
.left {
@include grid-span(4, 1);
}
.right {
@include grid-span(4, 5);
}
}
}
singularity-extras
to your Gemfile and install through Bundler. The version should be <1.0.0
.singularity-extras
in your config.rb
file and import singularity-extras
generators into your stylesheetsgrid-span
calls, they're going to break while we drastically change our gridsmin-width
to switch from one column to multiple columnsmin-width
to switch from two columns to three columnsToolkit has been providing for us our box model fix and our fluid images, but it can do so much more
Designed not as a CSS System, but rather a set of tools to build your own, Toolkit makes doing things the right way the easy way
Provided by Toolkit by default, the basic way to get images to squish and maintain their dimensions is fairly easy CSS
img, video {
max-width: 100%;
height: auto;
}
But Embedded Content Isn't So Easy
Intrinsic Ratios are a CSS technique that allow you to constrain child elements to a ratio and percentage of its parent
// Intrinsic Ratio mixin comes from [Toolkit](https://github.com/team-sass/toolkit)
.ratio-16-9 {
@include intrinsic-ratio;
}
.ratio-4-3 {
@include intrinsic-ratio(4/3);
}
display: none
.start
animationmin-width
and the point you go from one column to two columns, create a fenced query for inline mediaProgressive Enhancement allows us to provide an enhanced experience to superpowered browsers.
Progressive Enhancement is best done through feature detection, like that provided by Modernizr
Sass plus Modernizr is a powerful one-two punch for progressive enhancement
Modernizr provides test to determine browser support without user-agent sniffing
Each test provides a class, either .test
or .no-test
class, on our <html>
tag, as well as a boolean property at Modernizr.test
in JavaScript
.logo {
.svg & {
background-image: url('../img/logo.svg');
}
.no-svg &,
.no-js &{
background-image: url('../img/logo.png');
}
}
.svg .logo {
background-image: url("../img/logo.svg");
}
.no-svg .logo, .no-js .logo {
background-image: url("../img/logo.png");
}
Modernizr can also be bundled with yepnope.js, allowing for conditional loading of additional assets (both CSS and JS) based on passed or failed tests
Scripts are loaded asynchronously and in parallel!
Modernizr.load({
test: Modernizr.svg,
yep: '../css/svg.css',
nope: '../css/no-svg.css'
});
.lte-ie8
to use a Modernizr test for Media Queries instead of relying upon IE conditional classesPartials allow us to divide up our styling into discrete pieces, making organizing and maintaining our styling easy
There are many different ways to organize your partials, this is the standard we will be working with from now on.
Global partials contain styling and development knowledge that are central and can be shared across multiple components.
Examples of shared styling knowledge includes color variables, dark/light contrast mixins, and general typography extendable classes
In your sass
folder, create a global
folder and inside that, a folder apiece for extends, functions, mixins, variables
to place respective partials in to. In each folder, create a _all.scss
partial for a global import for those folders
style.scss
_all.scss
_all.scss
_all.scss
_all.scss
In your style.scss
file, start it with any of your file-specific setup variables (cross-browser/Jacket, etc…, not global setup variables that need to be shared across multiple files, like grid and media query variables), then the Compass extension imports you need for that file, finally followed by your global imports
//////////////////////////////
// Cross Browser Support
$legacy-support-for-ie: false;
$jacket: 'base';
//////////////////////////////
// Normalize
@import "normalize";
//////////////////////////////
// Compass Extensions
@import "toolkit";
@import "breakpoint";
@import "singularitygs";
//////////////////////////////
// Globals
@import "global/variables/all";
@import "global/functions/all";
@import "global/mixins/all";
@import "global/extends/all";
Everything on your site is a component; your messages, your media galleries, your buttons, your navigation.
Each component should be written as a generalized mixin with defaulted, globally scoped variables. Then, each instance of a component should be written as an extendable class. Finally, each extendable class should be extended as a full selector
In your sass
folder, create a components
folder and inside of that, a folder and a partial a piece for each component. Inside each folder, create _extends.scss, _mixins.scss, _variables.scss
partials for that component, and import them into your component partial
style.scss
_button.scss
_message.scss
_extends.scss
_mixins.scss
_variables.scss
_extends.scss
_mixins.scss
_variables.scss
$message-status-color: $primary-color !default;
$message-warning-color: $secondary-color: !default;
$message-error-color: $tertiary-color !default;
$message-border-radius: $std-border-radius !default;
$message-extend: true !default;
@mixin message--core($border-radius: $message-border-radius, $extend: $message-extend) {
@if $extend and $border-radius == $message-border-radius {
@extend %message--core;
}
@if $extend and not $border-radius == $message-border-radius {
@extend %message--core;
@include border-radius: $message-border-radius;
}
@else {
width: 80%;
margin: 0 auto;
padding: .5em;
border: 1px solid black;
@include border-radius: $message-border-radius;
}
}
…
$message-extendables-extended: false !default;
@if not ($message-extendables-extended) {
%message--core {
@include message--core($extend: false);
}
%message--status {
@include message--core($extend: true);
@include message--instance($message-status-color);
}
…
%message--error {
@include message--core($extend: true);
@include message--instance($message-error-color);
}
}
$message-extendables-extended: true;
@import "message/variables";
@import "message/mixins";
@import "message/extends";
.message {
@extend %message-core;
}
.message--status {
@extend %message--status;
}
.message--error {
@extend %message--error;
}
Layouts get yet a third top level partial folder. Generally there will be a handful of set layouts on a given site, and each layout should get a partial structure similar to components.
In your sass
folder, create a layouts
folder and inside of that, a folder and a partial a piece for each layout. Inside each folder, create _extends.scss, _mixins.scss, _variables.scss
partials for that layout, and import them into your layout partial
style.scss
_article.scss
_landing.scss
_extends.scss
_mixins.scss
_variables.scss
_extends.scss
_mixins.scss
_variables.scss
<footer>
in .recipe
) to change its layout from block to inline depending on if there is enough room to display them all inline.media
section, under the carouselgithub.com/snugug/eq.js
and BowerGrunt is a Node.js based task runner that can do just about anything. From running development servers to live reloading sites to hinting, linting, compiling and compressing, any development task you can think of, you can get Grunt to do. And it's all written in JavaScript!
When working with Grunt, you're turning your development area into a Node project regardless of what language you're working in. You can quickly initialize a Node project by running npm init
, which will create your package.json
.
The primary reason for this is the need to install and use Node modules in order to use Grunt.
{
"name": "code-rwd-sass-compass",
"version": "0.0.0",
"description": "Code for RWD Training",
"author": "Sam Richard",
"license": "MIT",
"devDependencies": {}
}
When working with Grunt, most of what you're working with are development packages. When you go to install a package using npm install
, appending --save-dev
to the end will save them into your package file.
The minimum packages you need in order to run Grunt and use contributed Grunt tasks are grunt
and matchdep
Installed modules will be saved to a node_modules
file. Be sure to ignore this folder in your version control! Don't commit it in!
Your Gruntfile.js
file is the brains of the boar. Each project you start gets its own Gruntfile that you get to write from scratch.
A Gruntfile is written in strict JavaScript via Node, none of your jQuery here. You'll be adding configuration to a grunt object and creating tasks. Unlike most of Node, Grunt tasks run in series, not parallel.
(function() {
'use strict';
module.exports = function (grunt) {
// Grunt task configuration
grunt.initConfig({
// Configuration goes in here
});
// Use matchdep to load all Grunt tasks from package.json
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
// Custom Grunt tasks go here.
// This is the special `default` task
grunt.registerTask('default', function () {
console.log('This runs when you run `grunt`');
});
};
}());
grunt.initConfig({
connect: {
server: {
options: {
port: 9001,
base: '.'
}
}
}
});
//////////////////////////////
// Server Task
//////////////////////////////
grunt.registerTask('server', function () {
grunt.task.run(['connect']);
});
server
task to watch for changes in image, javascript, css, and html, and livereload your page.grunt-contrib-connect, grunt-contrib-watch, grunt-open
. Make sure to ignore the node module files!config.rb
fileconfig.yml
filegrunt server
build
task that will hint your JavaScript then minimize your images and SVGs and create a production build of Compass, putting everything into an export
folder (with the exact location controlled by the user)Slides available at
http://snugug.github.io/rwd-sass-compass