The most advanced, yet intuitive, grid system available for Sass or Stylus

  • By MojoTech
  • Last update: Nov 24, 2022
  • Comments: 16

Jeet 7.0.0 is here!

7.0.0 breaks a lot of stuff. Be sure to read the migration guide!

Jeet is a simple fractional grid system for Sass and Stylus.

Learn the API in minutes and begin tossing together grids.

Check this out:

npm install -D jeet
@import 'node_modules/jeet/jeet';

.container {
  @include center();

.container div {
  @include column(1/3);

Jeet functions accept fractions (or float numbers) and generates a percentage width and gutter for grids. We're able to do this while maintaining a consistently-sized, infinitely-nestable, gutter (so long as you provide the parent element's fraction as context).

The API is documented in Sass, but we've worked hard to make the API very similar in both preprocessors. @include column(1/2); in SCSS would be column(1/2) in Stylus, ya dig?

Explore Official Integrations to see some community-backed plugins to your favorite frameworks and libraries.

Browser Support

  • IE9+ without help. IE8+ with Selectivizr. Obviously always use Autoprefixer.



  • 1

    Added equidistant nested gutters and a range of other updates

    So I've made a lot of changes, some are drastic others not so much but I think you'll like most of them. These changes have been tested in Stylus and SCSS, but not extensively. No SASS testing has been done.

    Condensed the numerator and denominator into a single argument

    col() and span() can be called like this now: col(1/4) or col(0.25). I feel like this is a much clearer way of doing the same thing, it's also more obvious what the function is going to do. This change is also needed for my implementation of equidistant column gutters (see below).

    Added ability to have equidistant nested column gutters

    This was one of the features you were hoping to have. This is achieved by supplying the col() function's first argument, ratios, with a space separated list of fractions. You give it the new ratio you want to create first, then the ratios of that element's parents. So for example, col(1/4 2/3) is saying, "I want to create a column 1/4 the size of it's container and it's container is 2/3 the size of the whole layout". This can be nested infinitely (within reason), so col(1/4 2/3 1/2 ...) is valid.

    I've also added a parent_first global setting which is set to false by default. This reverses the ordering of ratios you give the col() function. So parent_first = false col(1/4 2/3) equivalent to parent_first = true col(2/3 1/4).

    Made nested columns automatic

    Part of the update adding equidistant column gutters was making column nesting automatic (setting up the first-child and last-child margins). Columns are automatically nested when the user supplies more than one ratio to the ratios argument for col(). Another advantage is you don't need to specify the number of nested columns like you had to previously.

    Consolidated offset() into span() and column() for cleaner CSS output

    I noticed when using the offset() function that it created duplicate assignments of the margin-left and margin-right properties. Because of this I've moved offset functionality into an offset argument for col() and span(). This removes the extra margin-left and margin-right declarations and keeps the final CSS clean.

    Both span() and col()'s offset argument take a fraction which can be positive or negative depending on which side you want the offset to occur. col() has an additional optional boolean argument for offset, this is added by making offset a space separated list like so: col(1/3, offset: 1/2 true). As offset is the second argument, col(1/3, 1/2 true) is also valid. This boolean is for specifying whether or not you're offsetting a column away from a nested edge, as a slightly different calculation must be made. This boolean defaults to false and isn't needed most of the time, just in a few edge (heh) cases.

    Added shift() and unshift() functions for content reordering

    I've added this function for the sake of content reordering. shift() takes a space separated list of ratios similar to col(). The first ratio you give is how far you want to shift the element then the remaining ratios are the widths of the parent containers. For instance, shift(1/4 1/2 1/3) means, "Shift this element 1/4 to the right, and it's parent ratio is 1/2 and the parent of that is 1/3. Making the first value negative will shift the element in the opposite direction.

    unshift() is used when you want to remove any element shifting you have done. This is primarily useful when changing the layout for a new breakpoint and you want to reset the shifting you have done.

    Removed !important flags

    You already mentioned you were going to do this in the next update so I thought I may as well do this while I'm at it.

    Added right to left support

    Added the global variable layout_direction with a default value of LTR. Changing this to RTL will flip the entire layout including offsets and shifts. It's possible to change this value in between blocks of styling to have certain parts of your site go in different directions.

    Added a few list functions

    A couple of these are being used by the updated col() function, maybe these functions could be moved to a separate lists or utilities file?

    Split get() into getSpan() and getColumn()

    I thought it might make more sense having these split into two different functions, it also makes the framework more readable. span() and column() call getSpan() and getColumn() respectively for their calculations.

    Updated documentation to match changes

    I've updated to reflect the changes I've made.

    I've also added a Getting Started section which takes the user through some basic styling. Furthermore I've updated the Docs section to be a little more readable.

    If you have any questions don't hesitate to ask.

  • 2

    Add inline documentation to codebase.

    Hi all,

    This addresses #277.

    I've done a bit of work on it this evening so I thought I'd PR now so that you can check it out, approve/disapprove, and then we can work with that feedback to make alterations and finish it all off.

    I'm pretty pleased with it so far, but I think there are definitely areas where you guys will wan't to suggest changes, maybe where I've missed the point of something that should be mentioned in the comment or something.

    Also, I plan to add inline comments throughout the functions themselves in order to make it clear what is actually happening. While reading through it, it became tricky working out exactly what the intention was of certain lines, and I think that could be easily fixed with the odd // Do X to Y in order for Z type comment.

    Regarding Stylus

    I've only looked through _functions.styl so far but I noticed that there are quite a few differences that seem more than just cosmetic. I'm sure I'll learn it all eventually but if there is anything critical I should know that you could fill me in on, then that would be great. For example the opposite-direction() function doesn't seem to exist in the stylus version.

    Sorry for the essay, and like I said, there will be more to come tomorrow. Feel free to rip my changes to pieces so we can make it perfect :smiley:

  • 3

    Add offset mixin

    An attempt to fix #191.

    This PR adds the offset() mixin which can be accessed via:

    @include offset($ratio: 1/4, $col-or-span: column, $gutter: $jeet-gutter);

    I've tested it on CodePen and it seems to work. However I'm worried that I've basically just duplicated a lot of code in order to make this happen. It seems to me that maybe Jeet could do with some refactoring.

    Also, I'm pretty tired so it might be that my implementation is a bit inefficient. I'll probably wake up in the morning and see it more clearly :smile:

  • 4

    Make getters for width and gutter

    These need aliases, but that will require some extra work to replace instances of g. Until then I think it's fine to merge this since they're still usable even without additions that make them slightly easier to use for advanced users (seems like an additional feature).

  • 5

    More comprehensive inline documentation

    I've been looking through the source to see how it all works (brilliant job by the way, easily my favourite grid system), and I was a bit surprised at how little commenting there is.

    I was thinking it would be good to add docblock style comments for each function & mixin etc. Something like the following:

    // Output columns without gutters
    // ---
    // @param Float $ratio The width you want the column to be
    // @param Float $offset The column's offset from the edge
    // ---
    @mixin span($ratio: 1, $offset: 0) {

    That way, people like me who are trying to work out how things work will have an easier time figuring it all out. I'd be happy to help write the documentation as well :smile:

    Let me know what you think.

  • 6

    Trying to pass percentage based padding values through the center() mixin

    So I am probably being really picky but I wanted to set the padding on the main section to be equal to the gutter margins which is 3%. When I try to do center(1200px, padding: 3%) it breaks the last column even if I use the cycle mixin.

    The result looks something like this:

    However creating a wrapper div around the section tag, applying the center() to the wrapper div with 0 padding, and then applying the 3% padding to the section tag seems to work.

    The result of that looks like this:

    So I guess my question would be is there a way to pass a percentage based padding through the existing center() mixin and have it work properly without having to create an extra wrapping div?

  • 7

    7.0.0: Make Jeet Great Again

    This fixes Gulp while breaking @import integrations.

    Please check with the community to make sure it doesn't break anything else.

    All the tests were completely useless (just compared that SCSS and Stylus output similar stuff) so it just removes them rather than giving people the impression this project actually has tests.

    I plan on adding good tests via AVA and locked demos (so we'll actually have visual confirmation everything works as expected) in the near future (assuming this gets merged).

    This PR also removes JSDocs and SassDocs. I highly doubt anyone was actually generating personal docs with those, and they fragmented Jeet. There were 5 different docsets: Sass, Stylus, both READMEs, and the website. That's ridiculous to maintain and the reason all these docsets have consistently been... inconsistent. 😼

    The API is now written strictly in Sass. Stylus users are smart and can figure out @include column(1/3) == column(1/3).

    I plan on fixing the docs on the website, and hopefully pulling them from the markdown in this repo.

    uncycle has been removed. It was removed a long time ago, but somehow ended up back in master. Tbh, I'm not sure what else was added/removed so I'd appreciate it if a community member would review some of the API and let me know if something went missing (the git history is so fubar it's hard to make sense of).

    I've removed namespacing by default. It doesn't make sense.

    There is .000001% chance the W3C or Stylus or Sass are going to create a column property/function/whatever.

    If people are concerned about it, I've provided docs on how to do their own namespacing.

    I've also changed the name of shift to move since it did clash with a built-in Stylus function.

    Preprocessors will compile functions/mixins before they get to the browser (where W3C would take effect). Stylus and Sass aren't really introducing new API stuff nowadays, so I think we're safe.

    I've also removed aliasing. It fragments things and is just generally a bad practice. Explicit naming throughout a project is typically better.

    That said, there is a lot of work to do getting rid of all the little $w variables throughout the private side of Jeet. The private vars should really be cleaned up, made more explicit, and sync'd with each preprocessor. opposite-direction(side) in Sass and opposite-side in Stylus, is a bad code smell that makes this multi-preprocessor project even harder to understand/maintain.

    I've provided docs for how users can easily alias things on their own.

    @import's will break. I've removed superfluous directories (that were solely there to house README's...).

    Old: @import '.../jeet/scss/jeet/index'; New: @import '.../jeet/scss/index';

    It's not as great as if this project were broken up into jeet-scss, jeet-stylus, etc. but it's a start and Jeet is getting long-in-the-tooth so I doubt migrating to an jeet-organization/jeet-scss would matter much now.

    We're now supporting official integrations.

    The Wiki should probably be trimmed or deleted. People stumble upon old integrations, try them out, and when they don't work, they get turned off.

    No longer supporting Bower. Bower is dead. People should use npm exclusively until something like gx takes over. There's too much fragmentation in package management.

    If you're at 6.1.5 and it is broken, and the last working version is at 6.1.2, then revert back to 6.1.2 and tag/publish at 6.1.6 (a patch that fixes something broken).

    If 6.1.2-6.1.5 introduced breaking changes, and you want to try to do this properly, then fix the broken stuff while keeping the good stuff (trudging through a bit of git history) -- and again just patch bump.

    I take credit for starting this project down the path of unsemantic versioning newbdom (I was one of those "marketing > semantic versioning" idiots), but let's wash our hands of broken/fragmented Jeet with this PR, add some tests, implement Semantic Release, and never worry about breaking stuff or versioning again.

    I feel guilty that people using Jeet have had to deal with it in this screwed up state for so long. I'm trying to clean it up a bit, make maintenance much simpler, and get it in a usable state.

    You can do the typical maintainer thing and call in a forensics team to study this PR over the next several months, or you can look at it, ask the community to get involved/give feedback, and push the button that will fix this thing.

    Just ping me if the former so I can make an official fork and make a new npm package.

  • 8

    suggestion: stylus best practices

    In the development of axis, we have agreed on a set of best practices for keeping stylus code as clean and readable as possible, that go something like this:

    • do not name mixins the same as html elements, it can cause errors in stylus.
    • prefix private functions with a dash, like this -private-fn() so it's clear what's public and what's private.
    • always use colons between property key/value pairs, like in normal css, it makes things less confusing.
    • do not name mixin parameters the same as css properties. For example, mixin(width, height) is confusing because then in the mixin body, you'd have something like width: width, or worse width width if you skip the colons (which you should not do).

    It might be a good idea to run through the core jeet grid code and make some adjustments, so that the code is more clear and easy to manage :smile:

  • 9

    Default number precision causing tests to fail.

    I'm currently fixing the Scss version bug (reverting to the old function seems to fix it fine) but the tests fail because the the floating point numbers get rounded to different levels of precision.

    Not sure how to get around this. It seems you can configure Scss using compass, but that's it; the Stylus version doesn't seem to be able to be globally configured. Does CSS diff have an option for relaxing the constraints?

    Shall I just submit failing tests? :confused:

  • 10

    Updating and improving a bunch of things

    Major pull-request.
    Among other things:

    • fixing #227
    • fixing #226
    • fixing #224
    • fixing #221
    • partially fixing #220
    • working around #218
    • improving overall code quality
    • adding comments

    Merge with caution! I didn't test the code. Please make sure you test it before you merge it. :)

  • 11

    Cols and spans zeroing padding

    This is somewhat of an edge case, but when I use jeet I almost always comment out the padding on cols and spans. Since I use border-box for box-sizing there's no chance of ruining my layouts with padding anyway. This way jeet isn't overriding any of my existing styling, which is nice when working with a mobile-first approach where I occasionally declare padding before any media queries.

    Not really an issue, per-se, just throwing out the thought. I don't know if it's more invasive/directive to make jeet dependent on box-sizing: border-box, or to set padding {left: 0; right: 0). I'm guessing the former.

  • 12

    Nth offset option for column proposal

    I find that it is sometimes useful to be able to offset the nth-child indexing when using cycle.

    I propose the addition of an additional option, $nth-offset to the column mixin which would allow one to offset by a certain number. It would default to 0 which would have no effect.

    Applied like this:

    @if $cycle != 0 {
        &:#{$nth-selector}(n) {
          margin-#{_opposite-direction($side)}: $margin-r * 1%;
          float: $side;
          clear: none;
        &:#{$nth-selector}(#{$cycle}n + #{$nth-offset}) {
          margin-#{_opposite-direction($side)}: $margin-last * 1%;
          float: _opposite-direction($side);
        &:#{$nth-selector}(#{$cycle}n + #{1 + $nth-offset}) {
          clear: both;
      } @else {
        &:last-child {
          margin-#{_opposite-direction($side)}: $margin-last * 1%;
  • 13

    text-align: inherit on columns

    Today I came across the issue that column unset my text-align, because it adds text-align: inherit. As I am working mobile first, to solve this I would need to re-set my text-align across each breakpoint. This is not a big issue, but it was an unexpected quirk.

    An example of the issue:

        text-align: right
        @media (min-width: 576px)
            column(1/3) // outputs `text-align: inherit` which over-rides the text-align above

    I found an older issue relating to this here, where it seems like it the solution was removing text-align: inherit from column.

    Is there a reason for setting text-align: inherit that I am missing, or can this be removed? (Perhaps due to stack having an align option that sets text-align?)

  • 14

    Could someone please extend the so called API on the website with clearer examples?

    As it is VERY vague at best. I find it hard to see exactly HOW this is so much greater that other grid systems, as I'm having a hard time figuring out exactly what MOVE does. What's it good for? When I click the View example button, it scrolls me to a section with three cols that's prefixed with the word SHIFT!? Totally confusing ...

    Could someone please elaborate on the API documentation without assuming a hell of a lot?

    Thanks (if anyone is listening at all ...).

  • 15

    How do I make jeet ignore first child?

    I need to have two columns of posts in wordpress, apart from the first; that has to span both cols at the top. I've tried using $offset, but as the docs a VERY vague it's not working.

    I can use first-child to set its width to 100% but the second post then still aligns to the right ... screwing up the flow.

    How would I achieve this with jeet?

  • 16

    Using align(both) collapses div?

    When I have this markup:

    <span class="jewellery_image" data-id="<?php the_ID(); ?>">
    	<a class="image_link" href=""><?php the_post_thumbnail('medium'); ?></a>
    	<a class="title" href=""><?php the_title(); ?></a>

    And this (s)css:

    .jewellery_image {
    	@include column(1/4, $cycle: 4, $gutter: 1);
    	img {
    		@include align(both);

    The box (.jewellery_image) collapses. And I have to specify a height (in px as % doesn't work) for it to not have it collapse.

    Is this by design or a bug?

    I've tried all possible display modes for all parent and child elements to make it work, but only specifying a height for .jewellery_image will work.