Flint
Flint is designed to be a flexible layout toolkit that developers can use for any responsive grid-based project. Built on Sass 3.3, Flint is capable of complex responsive layouts customized at each breakpoint; all while using a single mixin, having minimal output, as well as being completely semantic. All of your layout settings are housed in a simple config file and is immensely customizable. Flint will only output the code you need, and nothing else. We handle the hard stuff, so you can focus on the rest.
Take it for a spin on SassMeister.com!
Enjoy.
Requirements
- Sass ~> 3.3.0
- Compass ~> 0.12.1
Installation
gem install flint-gs
- Add
require "flint"
to yourconfig.rb
- Import it in your stylesheets with
@import "flint";
If you don't want to install the gem (?!), download/clone the current build files and use the starter config.rb
to require any custom functions Flint uses. Currently this is required, as we're making use of custom SassScript functions until the 'script &
' returns to Sass. Adjust the paths according to your project.
Documentation
Config
Flint's config map
is unique in the ability that you may define an unlimited number of breakpoints for your project. Whether that be 2 breakpoints, or even 12 breakpoints. Flint gives you full control over the column count on each breakpoint, and unlike most frameworks, you may name these anything that you like. Flint is smart and will figure out which one you're talking about.
Speaking of not being like most frameworks, Flint does not require you to set a ridiculous amount of variables just to use a single breakpoint. It actually doesn't require you to set any variables. It also doesn't require you to install a seperate extension so that you can define your breakpoints; all of these features are baked into Flint. Your columns are fully related to your breakpoints, so that there is never any confusion and everything is kept nice and simple.
To begin, you can either use the default config (below) which comes baked in, or you can define your own using the $flint
variable, using the default config as a template.
Settings may be entered in px
or em
, and Flint will do the rest.
Keep in mind, whatever unit you choose to use here needs to be used consistently throughout Flint.
No mixing of px
and em
units. Also, Flint does require that you follow a DESC
order for your breakpoint configuration, this way it can traverse the config map correctly to output valid media queries.
// Configuration map
// -------------------------------------------------------------------------------
// @param [Breakpoints] : Here you can set up your various breakpoints for your
// project. Any number of breakpoints is acceptable. You must include a column
// count and breakpoint value for each listed breakpoint. Flint does require that
// you follow a `DESC` order. Unit chosen here must be used consistently
// throughout the rest of the config map.
// -------------------------------------------------------------------------------
// @param default [Alias] : alias of breakpoint that is your grid default
// @param grid [Style] : style of grid
// @param gutter [Value] : contextual size of gutter
// @param float-style [Direction] : float direction
// @param max-width [Boolean | Value] : max-width for `containers`
// @param center-container [Boolean] : if you want a centered container
// @param border-box-sizing [Boolean] : if you want box-sizing: border-box applied
// @param debug-mode [Boolean] : ouputs debug properties
// -------------------------------------------------------------------------------
$flint: (
// grid configuration
"config": (
// define breakpoints [any (!!) amount of breakpoints]
"desktop": ( // [any alias you like, minus reserved flint words (i.e. "settings", etc.)]
"columns": 16, // [0-infinity]
"breakpoint": 1280px, // [value(unit)]
),
"laptop": (
"columns": 12,
"breakpoint": 960px,
),
"tablet": (
"columns": 8,
"breakpoint": 640px,
),
"mobile": (
"columns": 4,
"breakpoint": 320px,
),
// additional grid settings [required]
"settings": (
"default": "desktop", // [any breakpoint's alias]
"grid": "fluid", // [fluid | fixed]
"gutter": 10px, // [value(unit)]
"float-style": "left", // [left | right]
"max-width": false, // [true (uses highest breakpoint) | false | value(unit)]
"center-container": true, // [true | false]
"border-box-sizing": true, // [true | false]
"debug-mode": true, // [true | false]
),
),
);
Foundation
If you selected border-box-sizing: true
, then it is advised to create a global foundation instance like so,
@include _(foundation);
That way your output won't be riddled with box-sizing
declarations everytime you define a span. This will automatically output the rules onto the global selector *
. In the future this might be automatic, but for now I'll keep it manual.
Container
You may define containers, which simply act as a row without the float
property. This is really only useful on fixed grid layouts, or if you have a max-width set, but can be used elsewhere as a simple wrapper if desired. If you have center-container
set to true
, then it will also center your element using auto
left and right margins.
.container {
@include _(container);
}
Outputs,
.container {
display: block;
float: none;
width: 100%;
margin-right: auto;
margin-left: auto;
}
Clear
Given that Flint is float based, you might find yourself needing to use a clearfix. Flint comes packaged with a micro-clearfix function.
.clear {
@include _(clear);
}
Outputs,
.clear {
zoom: 1;
}
.clear:before, .clear:after {
content: "\0020";
display: block;
height: 0;
overflow: hidden;
}
.clear:after {
clear: both;
}
Recursive shorthand
Use this if you want identical column spans across all breakpoints.
.recursive {
@include _(3);
}
Outputs, (with debug mode on)
.recursive {
display: block;
float: left;
width: 17.1875%;
margin-right: 0.78125%;
margin-left: 0.78125%;
-flint--instance-count: 1;
-flint--key: desktop;
-flint--breakpoint: 1280px;
-flint--columns: 16;
-flint--span: 3;
-flint--context: NULL;
-flint--gutter: NULL;
-flint--shift: NULL;
-flint--output-width: 17.1875%;
-flint--output-margin-right: 0.78125%;
-flint--output-margin-left: 0.78125%;
}
@media only screen and (min-width: 641px) and (max-width: 960px) {
.recursive {
width: 22.91667%;
margin-right: 1.04167%;
margin-left: 1.04167%;
-flint--instance-count: 2;
-flint--key: laptop;
-flint--breakpoint: 960px;
-flint--columns: 12;
-flint--span: 3;
-flint--context: NULL;
-flint--gutter: NULL;
-flint--shift: NULL;
-flint--output-width: 22.91667%;
-flint--output-margin-right: 1.04167%;
-flint--output-margin-left: 1.04167%;
}
}
@media only screen and (min-width: 321px) and (max-width: 640px) {
.recursive {
width: 34.375%;
margin-right: 1.5625%;
margin-left: 1.5625%;
-flint--instance-count: 3;
-flint--key: tablet;
-flint--breakpoint: 640px;
-flint--columns: 8;
-flint--span: 3;
-flint--context: NULL;
-flint--gutter: NULL;
-flint--shift: NULL;
-flint--output-width: 34.375%;
-flint--output-margin-right: 1.5625%;
-flint--output-margin-left: 1.5625%;
}
}
@media only screen and (min-width: 0) and (max-width: 320px) {
.recursive {
width: 68.75%;
margin-right: 3.125%;
margin-left: 3.125%;
-flint--instance-count: 4;
-flint--key: mobile;
-flint--breakpoint: 320px;
-flint--columns: 4;
-flint--span: 3;
-flint--context: NULL;
-flint--gutter: NULL;
-flint--shift: NULL;
-flint--output-width: 68.75%;
-flint--output-margin-right: 3.125%;
-flint--output-margin-left: 3.125%;
}
}
As you can see, since desktop
is the framework default
, it uses the output for desktop as the base styles. You can set this to any breakpoint you like. So if you like mobile-first, you can do that.
Whatever your default
is set to, flint will not wrap those styles in media-queries, so that they may be used in non-supported browsers.
Recursive shorthand with identical context
Use this if your nested context is identical across all breakpoints. The context
is the span of the elements parent. Update: You can now use $context: auto
, and we'll do all the calculations for you. Just be sure a parent element with a Flint instance
actually exists or you'll get some weird output, or none at all. Using $context: auto
on fixed grids, the width with will be calculated by the parents width, instead of using the base breakpoints width.
// `auto` will work
.parent {
@include _(6);
.recursive {
@include _(3, auto); // Equivalent to : _(3, 6)
}
}
// Will also work
.parent {
@include _(6);
}
.parent .recursive {
@include _(3, auto); // Equivalent to : _(3, 6)
}
// Will not work, as the child has no relation to the parent within the stylesheet
.parent {
@include _(6);
}
.recursive {
@include _(3, auto); // Equivalent to : _(3, 6)
}
// When using `auto`, Flint 'falls back' from the topmost selector until one is
// found that has an instance, and it will calculate it's context based on that
// instances span for the current breakpoint.
Outputs,
.recursive {
display: block;
float: left;
width: 45.8333333333%;
margin-right: 2.0833333333%;
margin-left: 2.0833333333%;
}
@media only screen and (min-width: 641px) and (max-width: 960px) {
.recursive {
width: 45.8333333333%;
margin-right: 2.0833333333%;
margin-left: 2.0833333333%;
}
}
@media only screen and (min-width: 321px) and (max-width: 640px) {
.recursive {
width: 45.8333333333%;
margin-right: 2.0833333333%;
margin-left: 2.0833333333%;
}
}
@media only screen and (min-width: 0) and (max-width: 320px) {
.recursive {
width: 45.8333333333%;
margin-right: 2.0833333333%;
margin-left: 2.0833333333%;
}
}
Recursive shorthand with variable context
Use this if your context is not indentical across breakpoints. The context
is the span of the elements parent. Update: You can now use $context: auto
, and we'll do all the calculations for you. Just be sure a parent element with a Flint instance
actually exists or you'll get some weird output, or none at all.
You must include an argument for each breakpoint in your config.
.parent {
@include _(10 8 6 4);
.recursive {
@include _(2, auto); // Equivalent to : _(2, 10 8 6 4)
}
}
Outputs,
recursive {
display: block;
float: left;
width: 17.5%;
margin-right: 1.25%;
margin-left: 1.25%;
}
@media only screen and (min-width: 641px) and (max-width: 960px) {
.recursive {
width: 21.875%;
margin-right: 1.5625%;
margin-left: 1.5625%;
}
}
@media only screen and (min-width: 321px) and (max-width: 640px) {
.recursive {
width: 29.1666666667%;
margin-right: 2.0833333333%;
margin-left: 2.0833333333%;
}
}
@media only screen and (min-width: 0) and (max-width: 320px) {
.recursive {
width: 43.75%;
margin-right: 3.125%;
margin-left: 3.125%;
}
}
Variable shorthand
Use this if your content needs different spans across each breakpoints. The order of operations for this matches the order entered in your config
.
To hide an element on a specific breakpoint, input 0
as its span.
You must include an argument for each breakpoint in your config.
.variable {
@include _(8 6 0 4);
}
Outputs,
.variable {
display: block;
float: left;
width: 48.4375%;
margin-right: 0.78125%;
margin-left: 0.78125%;
}
@media only screen and (min-width: 641px) and (max-width: 960px) {
.variable {
width: 47.91667%;
margin-right: 1.04167%;
margin-left: 1.04167%;
}
}
@media only screen and (min-width: 321px) and (max-width: 640px) {
.variable {
display: none;
}
}
@media only screen and (min-width: 0) and (max-width: 320px) {
.variable {
width: 93.75%;
margin-right: 3.125%;
margin-left: 3.125%;
}
}
Variable shorthand with context
Use this if you're nesting columns using the variable shorthand. The context
is the span of the elements parent. Update: You can now use $context: auto
, and we'll do all the calculations for you. Just be sure a parent element with a Flint instance
actually exists or you'll get some weird output, or none at all.
.parent {
@include _(16 12 8 4);
.variable {
@include _(14 10 6 2, 16 12 8 4); // Equivalent to : _(14 10 6 2, auto)
}
}
Outputs,
.variable {
display: block;
float: left;
width: 85.9375%;
margin-right: 0.78125%;
margin-left: 0.78125%;
}
@media only screen and (min-width: 641px) and (max-width: 960px) {
.variable {
width: 81.25%;
margin-right: 1.0416666667%;
margin-left: 1.0416666667%;
}
}
@media only screen and (min-width: 321px) and (max-width: 640px) {
.variable {
width: 71.875%;
margin-right: 1.5625%;
margin-left: 1.5625%;
}
}
@media only screen and (min-width: 0) and (max-width: 320px) {
.variable {
width: 43.75%;
margin-right: 3.125%;
margin-left: 3.125%;
}
}
Wrapping in media queries
Use these if you need to apply breakpoint specific styling.
.wrap {
@include _(desktop) {
// only desktop
}
}
.wrap {
@include _(greater than mobile) {
// all sizes above mobile's breakpoint
}
}
.wrap {
@include _(10em greater than tablet) {
// all sizes 10em above tablet's breakpoint
}
}
.wrap {
@include _(less than tablet) {
// all sizes under tablet
}
}
.wrap {
@include _(1em less than laptop) {
// all sizes 1em under laptop
}
}
.wrap {
@include _(for desktop tablet) {
// only for desktop and tablet
}
}
.wrap {
@include _(from mobile to laptop) {
// all sizes from mobile to laptop
}
}
.wrap {
@include _(from desktop to infinity) {
// all sizes from desktop to infinity
}
}
Call by alias
Use if you want to define each span without shorthands. This is useful if you need variable gutter
modifiers.
.name {
@include _(desktop, 8);
@include _(laptop, 4);
@include _(tablet, 8);
@include _(mobile, 4);
}
// with context,
// .name {
// @include _(desktop, 4, 16, $gutter: alpha);
// }
Outputs,
.name {
display: block;
float: left;
width: 48.4375%;
margin-right: 0.78125%;
margin-left: 0.78125%;
}
@media only screen and (min-width: 641px) and (max-width: 960px) {
.name {
width: 31.25%;
margin-right: 1.04167%;
margin-left: 1.04167%;
}
}
@media only screen and (min-width: 321px) and (max-width: 640px) {
.name {
width: 96.875%;
margin-right: 1.5625%;
margin-left: 1.5625%;
}
}
@media only screen and (min-width: 0) and (max-width: 320px) {
.name {
width: 93.75%;
margin-right: 3.125%;
margin-left: 3.125%;
}
}
Gutter modifiers
Here are different gutter modifiers that may be called in when defining spans using the $gutter
variable. You should note, that when using recursive shorthands the gutter modifiers are also recursive across all breakpoints.
// default margins
.gutter-default {
// other aliases : `normal` | `regular`
@include _(desktop, 4, $gutter: default);
}
// no left margin
.gutter-alpha {
// other aliases : `no-left` | `first`
@include _(desktop, 4, $gutter: alpha);
}
// no right margin
.gutter-omega {
// other aliases : `no-right` | `last`
@include _(desktop, 4, $gutter: omega);
}
// no margins
.gutter-row {
// other alias : `none`
@include _(desktop, 4, $gutter: row);
}
// places gutters on inside by reducing column width by [gutter*2]
// useful for nesting children inside of parents that have normal gutters on fixed grid layouts
.gutter-inside {
@include _(desktop, 4, $gutter: inside);
}
// variable gutter
.variable-gutter {
@include _(16 12 8 4, $gutter: row alpha omega normal);
}
// recursive
.recursive-gutter {
@include _(16 12 8 4, $gutter: row);
}
Outputs,
.gutter-alpha {
display: block;
float: left;
width: 24.21875%;
margin-right: 0.78125%;
margin-left: 0;
}
.gutter-omega {
display: block;
float: left;
width: 24.21875%;
margin-right: 0;
margin-left: 0.78125%;
}
.gutter-row {
display: block;
float: left;
width: 25%;
margin-right: 0;
margin-left: 0;
}
.gutter-inside {
display: block;
float: left;
width: 21.875%;
margin-right: 0.78125%;
margin-left: 0.78125%;
}
.variable-gutter {
display: block;
float: left;
width: 100%;
margin-right: 0;
margin-left: 0;
}
@media only screen and (min-width: 641px) and (max-width: 960px) {
.variable-gutter {
width: 98.95833%;
margin-right: 1.04167%;
margin-left: 0;
}
}
@media only screen and (min-width: 321px) and (max-width: 640px) {
.variable-gutter {
width: 98.4375%;
margin-right: 0;
margin-left: 1.5625%;
}
}
@media only screen and (min-width: 0) and (max-width: 320px) {
.variable-gutter {
width: 87.5%;
margin-right: 3.125%;
margin-left: 3.125%;
}
}
.recursive-gutter {
display: block;
float: left;
width: 100%;
margin-right: 0;
margin-left: 0;
}
@media only screen and (min-width: 641px) and (max-width: 960px) {
.recursive-gutter {
width: 100%;
margin-right: 0;
margin-left: 0;
}
}
@media only screen and (min-width: 321px) and (max-width: 640px) {
.recursive-gutter {
width: 100%;
margin-right: 0;
margin-left: 0;
}
}
@media only screen and (min-width: 0) and (max-width: 320px) {
.recursive-gutter {
width: 100%;
margin-right: 0;
margin-left: 0;
}
}
Shift
Much like the gutter modifiers, you may also call in a shift parameter using the $shift
variable. This will cause the element to shift the desired amount of columns using positive/negative left margins.
// shift 4 columns to the right across all breakpoints
.name {
@include _(12 12 8 4, $shift: 4);
}
// shift 2 columns to the left across specified breakpoints
.name {
@include _(12 12 8 4, $shift: -2 -2 0 0);
}
One more cool thing about flint is that you are not bound to the grid you define. Feel free to use decimals and math based on the breakpoint's column count in your arguments for extra fine tuned control over your layouts. Examples include, decimals: (1.25), (3.333)
, math: (3 - 2), (2 + 9), (16 / 3)
.
.break-the-grid {
@include _(16/3 12.1 8.9 (5 - 1), $shift: 1.2 0 2 0, $gutter: row);
}
Outputs,
.break-the-grid {
display: block;
float: left;
width: 33.33333%;
margin-right: 0;
margin-left: 7.5%;
-flint-instance-count: 9;
-flint-key: desktop;
-flint-breakpoint: 1280px;
-flint-columns: 16;
-flint-span: 5.33333;
-flint-context: NULL;
-flint-gutter: row;
-flint-shift: 1.2;
-flint--output-width: 33.33333%;
-flint--output-margin-right: 0;
-flint--output-margin-left: 7.5%;
}
@media only screen and (min-width: 641px) and (max-width: 960px) {
.break-the-grid {
width: 100.83333%;
margin-right: 0;
margin-left: 0%;
-flint-instance-count: 10;
-flint-key: laptop;
-flint-breakpoint: 960px;
-flint-columns: 12;
-flint-span: 12.1;
-flint-context: NULL;
-flint-gutter: row;
-flint-shift: 0;
-flint--output-width: 100.83333%;
-flint--output-margin-right: 0;
-flint--output-margin-left: 0%;
}
}
@media only screen and (min-width: 321px) and (max-width: 640px) {
.break-the-grid {
width: 111.25%;
margin-right: 0;
margin-left: 25%;
-flint-instance-count: 11;
-flint-key: tablet;
-flint-breakpoint: 640px;
-flint-columns: 8;
-flint-span: 8.9;
-flint-context: NULL;
-flint-gutter: row;
-flint-shift: 2;
-flint--output-width: 111.25%;
-flint--output-margin-right: 0;
-flint--output-margin-left: 25%;
}
}
@media only screen and (min-width: 0) and (max-width: 320px) {
.break-the-grid {
width: 100%;
margin-right: 0;
margin-left: 0%;
-flint-instance-count: 12;
-flint-key: mobile;
-flint-breakpoint: 320px;
-flint-columns: 4;
-flint-span: 4;
-flint-context: NULL;
-flint-gutter: row;
-flint-shift: 0;
-flint--output-width: 100%;
-flint--output-margin-right: 0;
-flint--output-margin-left: 0%;
}
}
BEM Users
Due to the way BEM is written, the instance functions cannot fallback to previous selectors in the family tree to find a parent instance, so using $context: auto
will not work for some BEM users, depending on how you write it.
.block {
@include _(4);
&__element {
@include _(2, auto);
}
}
Will result in a@warning
, and will not compile correctly as .block
and .block__element
are not compiled into selectors of the same family tree i.e. .block .block__element
. But, if you write it like this:
.block {
@include _(4);
.block__element {
@include _(2, auto);
}
}
// Or...
.block {
@include _(4);
// Double your ampersands
& &__element {
@include _(2, auto);
}
}
This will allow the instance functions to properly fallback from .block .block__element
to .block
to check for context. If writing BEM like that just isn't your thing, you can manually enter your context:
.block {
@include _(4);
&__element {
@include _(2, 4);
}
}
Change Log
Going to start keeping a log of changes starting (4/11/14).**
5/16/14
- Added
$context: auto
to for fixed grids. It will automatically get the parent instance's width, and calculate on that instead of the base breakpoint.- This fixes issues where parents couldn't contain children of the same span, and the further you would nest, the worse the issue would get.
5/14/14
- Fixed issue with
_(greater than y)
not outputting the correct calculations onfixed
grids- Issue was that when you used for example:
_(greater than laptop)
,laptop
would actually be included, instead of ommitted. It now acts as 'anything above laptops breakpoint', the same wayless than y
works.
- Issue was that when you used for example:
- Adjusted the way breakpoints are calculated for easier modifications moving forward.
- Optimized
calc-breakpoint()
function
5/09/14
- Added ability to pass an abitrary
$context
while maintaining a consistent gutter - Small changes to
debug-mode
- Added parent instance selector to output
- Added actual
$context
in place ofauto
in output
5/02/14
- Adjusted
$length
variable instring-to-list()
for better performance. - Added 2 additional aliases for
$gutter
modifiersalpha > first
omega > last
4/30/14
- Fixed issue with comma separated child selectors throwing an error.
Fixes #5
4/25/14
- Added aliases for
$gutter
modifiersNULL > default | regular | normal
alpha > no-left
omega > no-right
row > none
- Removed option for
gutter: false
in config. Use0(unit)
from now on.
4/24/14
- Added
$gutter: inside
modifier - Adjusted
$span: 0
to hide element instead of compiling with no width - Corrected small issue with
less than x
,greater than x
on fixed grids - Cleaned up variable/function names
- Added detailed comments to all mixins/functions
4/18/14
- Built
.gemspec
so that Flint can be installed viagem install flint-gs
- Added
bower.json
so that Flint can be installed via Bower - Organized file structure by splitting functions/mixins into separate files for easier modifications/version control moving forward.
4/12/14
- You can now take advantage of both
$shift
and$gutter
modifiers together.
4/11/14
- You can now use
$context: auto
, and we'll do all the calculations for you. Just be sure a container element actually exists or you'll get some weird output, or none at all. Pretty cool feature utilizing the newinstance
map, which keeps track of everyinstance
of the_()
mixin, and saves all the tasty variables for use-cases like this.