Last active
August 29, 2015 13:57
-
-
Save tommycoppers/06f71cf07686836395b9 to your computer and use it in GitHub Desktop.
Breakpoints With Unit testing!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ================================================== | |
// =================== Breakpoints ================== | |
// ================================================== | |
// | |
// --- Introduction --- | |
// | |
// Media Queries, namely Breakpoints, are key in responsive design / development. | |
// "breakpoint" allows for a robust approach to rapid development of Media Queries in your CSS. | |
// By defining a list of approved breakpoints, this library helps to ensure consistent Media Query output, relative to each project. | |
// In addition to defined breakpoints, you are allowed to pass in numbers of measure ( width of screen ) to fine tune one-off cases. | |
// ** Please emphasize that passing in numbers of measure really should be used minimally to promote consistency. ** | |
// Note that this library functions on a mobile first philosophy, so media queries are by default paired with 'min-width'. | |
// | |
// --- Application --- | |
// | |
// Including a breakpoint can happen 1 of 3 ways | |
// | |
// - Starting point | |
// ** mobile first mentality | |
// ** Only define $breakpoint ( with a key from $breakpoints-list ) | |
// | |
// - Range of breakpoints | |
// ** only affect from start of $breakpoint through start of $until | |
// ** Define both $breakpoint and $until ( both take a key from $breakpoints-list ) | |
// | |
// - Isolated breakpoint | |
// ** affects from start of $breakpoint until the next defined breakpoint | |
// ** Define both $breakpoint ( key from $breakpoints-list ) and $only:true ( boolean ) | |
// | |
// --- Examples --- | |
// | |
// .foo { | |
// @include breakpoint ( 'tablet' ) { // Start styles for .foo at 'tablet' size | |
// /* some styles */ | |
// } | |
// } | |
// | |
// .bar { | |
// @include breakpoint ( 'mobile', $until: 'desktop' ) { // Start styles for .bar at 'mobile' and continue up until 'desktop' size | |
// /* some styles */ | |
// } | |
// } | |
// | |
// .zoo { | |
// @include breakpoint ( 'mobile', $only: true ) { // Start styles for .zoo only between 'mobile' start and 'mobile' end | |
// /* some styles */ | |
// } | |
// } | |
// Define all the breakpoints here ( Key, Value ) | |
/////////////////////////////////////////////// | |
// $breakpoints-list is a Sass map with a window width to a label. | |
// Being mobile first, the numbers represent the starting window width for each breakpoint | |
// Key - name of breakpoint {String} | |
// Value - Width of window {Number} ** Requires unit of measure ** | |
$breakpoints-list: ( | |
mobile : 0, // Mobile first! Defined in case mobile through ( breakpoint ) needs to be isolated | |
tablet : 550px, // ≈ tablet( ish ) | |
desktop : 960px, // ≈ desktop( ish ) | |
cinema : 1600px // ≈ cinema( ish ) | |
); | |
// Breakpoint Manager | |
///////////////////// | |
// Create intelligent media queries | |
// | |
// @param $breakpoint | |
// --- Includes a minimum value in media query --- | |
// {String} - String must be a key in $breakpoints-list Map | |
// {Number} - Number must have unit of measure | |
// | |
// @param $until | |
// --- Includes a maximum value in media query --- | |
// {String} - String must be a key in $breakpoints-list Map | |
// {Number} - Number must have unit of measure | |
// | |
// @param $only | |
// --- Limit the minimum and maximum values to only the start and end of $breakpoint --- | |
// {Bool} - String must be a key in $breakpoints-list Map | |
@mixin breakpoint( $breakpoint, $until: false, $only: false ) { | |
// ___ Unit test the hell out of it ___ | |
$errors: breakpoint-unit-test( $breakpoint, $until, $only ); | |
// ___ Meat and Potatoes ___ | |
@if not $errors { | |
// ___ Configure $until ___ | |
// If a range of breakpoints is defined, start at $breakpoint and end at the unit before $until | |
@if $until { | |
$breakpoint-bp : get-breakpoint-value( $breakpoint ); | |
$until-bp : get-until-breakpoint( $until ); | |
@include _until-breakpoint ( $breakpoint-bp, $until-bp ) { @content; } | |
} | |
// ___ Configure $only ___ | |
// If $only is set to true, start at $breakpoint and find the next defined breakpoint and end the unit before it begins | |
@else if $only { | |
@include _only-breakpoint ( $breakpoint ) { @content; } | |
} | |
// ___ Configure default | |
@else { | |
@include _default-breakpoint ( get-breakpoint-value( $breakpoint ) ) { @content; } | |
} | |
} | |
} | |
// Breakpoint Partials | |
/////////////////////// | |
@mixin _default-breakpoint ( $breakpoint ) { | |
@if ( $breakpoint != get-first( $breakpoints-list, $filter: value ) ) and ( $breakpoint != 0 ) { | |
@media ( min-width: $breakpoint ) { @content; } | |
} | |
@else { | |
@content; | |
} | |
} | |
@mixin _until-breakpoint ( $breakpoint, $until-point ) { | |
@media ( min-width: $breakpoint ) and ( max-width: $until-point ) { @content; } | |
} | |
@mixin _only-breakpoint ( $breakpoint ) { | |
// Check if $breakpoint is the last defined breakpoint | |
@if $breakpoint != get-last( $breakpoints-list, $filter: key ) { | |
$bp-keys: map-keys( $breakpoints-list ); // Lists the breakpoint keys in the key:value pair | |
$bp-key-index-current: index( $bp-keys, $breakpoint ); // Log where the current breakpoint key is within the list of breakpoint keys | |
$bp-key-index-next: $bp-key-index-current+1; // Log the index for the next breakpoint within the list of breakpoint keys | |
$next-bp: nth( $bp-keys, $bp-key-index-next ); // Log the next breakpoint | |
$endpoint: map-get( $breakpoints-list, $next-bp ) - 1; // Get the value of the next input and subtract 1 unit | |
@media ( min-width: map-get( $breakpoints-list, $breakpoint ) ) and ( max-width: $endpoint ) { @content; } | |
} | |
@else { | |
@include _default-breakpoint ( get-breakpoint-value( $breakpoint ) ) { @content; } | |
} | |
} | |
// Unit Testing Function | |
//////////////////////// | |
@function is-breakpoint ( $breakpoint ) { | |
@return map-has-key( $breakpoints-list, $breakpoint ); | |
} | |
@function get-breakpoint-value ( $breakpoint ) { | |
@if type-of( $breakpoint ) == number { | |
@return $breakpoint; | |
} | |
@return map-get( $breakpoints-list, $breakpoint ); | |
} | |
@function get-until-breakpoint ( $until-point ) { | |
@if type-of( $until-point ) == number { | |
@return $until-point; | |
} | |
@return map-get( $breakpoints-list, $until-point ) - 1; // Get the next breakpoint then subtract 1 unit | |
} | |
// ----------------------------------------- | |
// Tests the parameters being passed into @mixin breakpoint against all rules and conditions | |
// @params - Equal to the @params in @mixin breakpoint | |
// @returns - Boolean representing if errors are present | |
@function breakpoint-unit-test ( $breakpoint, $until, $only ) { | |
// Set the test to passing by default until proven wrong. | |
$breakpoint-types : ( string, number ) !default; | |
// ___ Test $breakpoint ___ | |
@if $breakpoint and index( $breakpoint-types, type-of( $breakpoint ) ) { | |
@if type-of( $breakpoint ) == string and not is-breakpoint( $breakpoint ) { | |
@warn 'Looks like #{$breakpoint} isn\'t a defined breakpoint. Acceptable breakpoints include: #{ map-keys( $breakpoints-list ) }'; | |
@return true; | |
} | |
@if ( type-of( $breakpoint ) == number ) and (( unitless( $breakpoint ) ) and $breakpoint != 0) { | |
@warn '$breakpoint has no units! Gonna need to try again, buddy. Put in something like.. px or em... or something'; | |
@return true; | |
} | |
} | |
@else { | |
@warn '$breakpoint has to be one of the following types: #{$breakpoint-types}. Yours is #{type-of( $breakpoint )}, silly'; | |
@return true; | |
} | |
// ___ Test $until ___ | |
@if $until { | |
@if index( $breakpoint-types, type-of( $until ) ) { | |
@if ( type-of( $until ) == string ) and ( not is-breakpoint( $until ) ) { | |
@warn ( "Looks like #{$until} isn't a defined breakpoint. Acceptable breakpoints include: #{ map-keys( $breakpoints-list ) }" ); | |
@return true; | |
} | |
@if type-of( $until ) == number { | |
@if unitless( $until ) { | |
@warn '$until has no units! Gonna need to try again, buddy. Put in something like.. px or em... or something'; | |
@return true; | |
} | |
@if $until <= get-breakpoint-value( $breakpoint ) { | |
@warn '$until has to be greater than $breakpoint ::: $breakpoint = #{get-breakpoint-value( $breakpoint )}, $until = #{$until}'; | |
@return true; | |
} | |
} | |
} | |
@else { | |
@warn "$until has to be one of the following types: #{$breakpoint-types}. Yours is #{type-of( $until )}, silly"; | |
@return true; | |
} | |
} | |
// ___ Test parameters ___ | |
// You can't have a range of breakpoints and try to specify styles for only one breakpoint at a time. That's silly. | |
@if $until and $only { | |
@warn "Ooops!! You can't have both $until and $only! Pick one!!"; | |
@return true; | |
} | |
@if $only and type-of( $breakpoint ) == number { | |
@warn 'Unfortunately you cannot set $only to true when $breakpoint is a number'; | |
@return true; | |
} | |
@return false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment