Giter Club home page Giter Club logo

jigsass-tools-selectors's Introduction

JigSass Tools Selectors

NPM version Build Status Dependency Status

Create predictable, manageable and composable CSS with zero bloat and scalability in mind

Installation

Using npm:

npm i -S jigsass-tools-selectors

Usage

@import 'path/to/jigsass-tools-selectors';

Managing CSS at scale is hard, even very hard. Code bases easily grow out of control, and dead code elimination is an onerous task.

JigSass borrows much of its architecture from inuit and ideas on CSS structure expressed elsewhere by Harry Roberts, and as such is, at its core, based on two main pillars:

  • Objects: Abstract design pattern, such as the media object meant for reuse in many unrelated contexts. Can be extended, with modifer classes, but directly mutating an object will have rippling effects. Should consist of minimal styling, to later be expanded with modifiers, utiliies and within components themselves.
  • Components: An implementation-specific piece of UI. Should not be reused outside the context of the specific component. Will often consist mostly of including objects and utils within the ruleset.
  • Utilities: Low-level, single-purpose immutable units of style, often only declaring a single declaration. These are not bound to any spcific ui or design abstruction, and can be used to change, adjust or augment

JigSass Selectors is an attempt to provide tools for better tackling these tasks, and assisting with defining and generating clean and easy to maintain CSS based on responsive-enabled, reusable object and utility classes.

In order to keep a CSS footprint to a minimum, not even a single line of the defined CSS is generated unless it is explicitly included for use.

Objects

CSS rulsets of objects and object modifiers are generated based on settings in its configuration map (passed as a parameter to the jigsass-object mixin), where classes must be explicitly enabled in the associated configuration map (passed to the mixin as the $config param) BEFORE the object is @included.

By default, CSS rulsets of objects and object modifiers are not automatically generated generated by including the jigsass-object mixin. For any output to be generated, classes must be explicitly enabled in the Object's associated configuration map (passed to the mixin as the $config param) BEFORE the object is @included.

The structure of configuration maps is:

// _objects.foo.scss
$foo-map: (
  no-breakpoint: (
    no-modifier: true,  // Enables generation of the `.o-foo`
                        // class outside of any media query.
    bar: true,          // Enables generation of the `.o-foo--bar`
                        // modifier class outside of any media query.
  ),
  from-<bp-name>: (
    no-modifier: true,  // Enables generation of the `.o-foo--from-<bp-name>`
                        // class inside a min-width media query
                        // defined ins `$jigsass-breakpoints.length`.
    bar: true,          // Enables generation of the `.o-foo--bar--from-<bp-name>`
                        // class inside a min-width media query
                        // defined ins `$jigsass-breakpoints.length`.
  ),
  until-<bp-name>: (
    no-modifier: true,  // Enables generation of the `.o-foo--until-<bp-name>`
                        // class inside a max-width media query
                        // defined ins `$jigsass-breakpoints.length`.
   bar: true,          // Enables generation of the `.o-foo--bar--until-<bp-name>`
                        // class inside a max-width media query
                        // defined ins `$jigsass-breakpoints.length`.
  ),
  when-<bp-name>: (
    no-modifier: true,  // Enables generation of the `.o-foo--when-<bp-name>`
                        // class inside a misc media query
                        // defined ins `$jigsass-breakpoints.features`.
    bar: true,          // Enables generation of the `.o-foo--bar--when-<bp-name>`
                        // class inside a misc media query
                        // defined ins `$jigsass-breakpoints.features`.
  ),
  from-<bp-name>-until-<bp-name>: (...);
  from-<bp-name>-when-<bp-name>: (...);
  until-<bp-name>-when-<bp-name>: (...);
  from-<bp-name>-until-<bp-name>-when-<bp-name>: (...);
);
Utilities

Instead of using configuration maps or variables, which eventually become difficult to maintain and keep track of for utility classes, JigSass offers the jigsass-define-util and jigsass-util mixins, used to define utils and dynamically CSS output for them without creating duplication or changing the cascade.

Let's take a look at a simplified example:

/* _object.media.scss */
$media-conf: (
  from-large: (
    no-modifier: true,
    middle: true,
  ),
);
$media-item-conf: (
  from-large: (
    bottom: true,
  ),
);

@jigsass-object(o-media, $media-conf) {
  @include jigsass-classname {
    display: flex;
  }

  @include jigsass-classname($modifer: middle) {
    align-items: center;
  }

  @include jigsass-classname($modifer: reverse) {
    flex-direction: row-reverse;
  }
}

@jigsass-object(o-media__item, $media-item-conf) {
  @include jigsass-classname($modifier: bottom) {
    align-self: flex-end;
  }
} 


/* _component.foo.scss */
.c-foo {
  // Styles unique for `.c-foo`:
  mix-blend-mode: multiply;
}

  .c-foo__fig {
    @include jigsass-util(u-ml, $from: large, $modifier: 1);
    @include jigsass-util(u-mr, $from: large, $modifier: 1);

    // Styles unique for `.c-foo__fig`:
    filter: blur(3px);
  }

    // `.foo__fig--bottom` will not actually be generated, 
    // as it has no styles of its own.
    .c-foo__fig--bottom {
      @include jigsass-util(u-ml, $from: large, $modifier: 1);
    }

  // `.foo__body` will not actually be generated, 
  // as it has no styles of its own.
  .c-foo__body {
    @include jigsass-util(u-mr, $from: large, $modifier: 1);
  }


/* _util.margin.scss */
@include jigsass-define-util(u-mr) {
  $modifier: $jigsass-util-modifier or 0;
  
  margin-right: $jigsass-util-modifier * 12px;
}

@include jigsass-define-util(u-ml) {
  $modifier: $jigsass-util-modifier or 0;
  
  margin-left: $jigsass-util-modifier * 12px;
}


// style.scss
@import 'object.media';   // `.o-media` will be generated here
@import 'component.foo';  // `.c-foo` and `.c-foo__fig` will be generated here.
@import 'util.margin';    // `.u-ml` and `.u-mr` will be generated here.

Will generate:

/* style.css */
@media (min-width: 65em) {
  .o-media--from-large {
    display: flex;
  }

  .o-media--middle--from-large {
    align-items: center;
  }
}

@media (min-width: 65em) {
  .o-media__item--bottom--from-large {
    align-self: bottom;
  }
}


.c-foo {
  mix-blend-mode: multiply;
}

  .c-foo__fig {
    filter: blur(3px);
  }


@media (min-width: 65em) {
  .u-mr--1--from-large {
     margin-right: 12px;
  }
}
@media (min-width: 65em) {
  .u-ml--1--from-large {
     margin-left: 12px;
  }
}

Notice how only the styles we actually used ended up in our CSS? How they were generated where they were defined, and only once?

When managing the generation of css utility classes through config files, it eventually becomes damn near impossible to keep track of where each class is used, and when it is safe to remove, often leaving us with codebases that are larger than they need to be.

In contrast, generating the styles by simply including them where they are being used is a lot easier to grasp and manage, while still keeping our CSS stringent and modular.

Now, with the above CSS and following html:

<article class="[ o-media--from-large  o-media--middle--from-large ]  c-foo">
  <figure class="c-foo__fig  u-mr--1--from-large">
    <!-- img1 here -->  
  </figure>
  <div class="foo__body">
    <!-- content here -->
  </div>
  <figure class="o-media__item--bottom--from-large  c-foo__fig  u-ml--1--from-large">
    <!-- img2 here -->  
  </figure>
</articel>

We can get something like this:

┌─────────────╥╥─────────────────────────────╥╥─────────────┐
│############ ║║CONTENT HEAD                 ║║             │
│############ ║║                             ║║             │
│############ ║║ minus saepe sequi velit a,  ║║ ############│
│##  IMG1  ## ║║ sit veniam quia quibusdam   ║║ ############│
│############ ║║ odio itaque non! Dolores    ║║ ############│
│############ ║║ deserunt atque repudiandae  ║║ ##  IMG2  ##│
│############ ║║ asperiores rerum velit      ║║ ############│
│             ║║ magnam deleniti deleniti    ║║ ############│
│             ║║ sed aspernatur commodi?     ║║ ############│
└─────────────╨╨─────────────────────────────╨╨─────────────┘

The jigsass-object and jigsass-util mixins will generate selectors according to the following logic:

.class-name[--modifier][-[-from-{breakpoint-name}][-until-{breakpoint-name}][-misc-{breakpoint-name}]]

Please check the full documentation for a better understanding of how to use jigsass-tools-selectors.

Development

It is a best practice for JigSass modules to not automatically generate css on @import, but rather have to user explicitly enable the generation of specific styles from the module.

Contributions in the form of pull-requests, issues, bug reports, etc. are welcome. Please feel free to fork, hack or modify JigSass Tools Selectors in any way you see fit.

Writing documentation

Good documentation is crucial for scalability and maintainability. When contributing, please do make sure that all Sass functionality (functions, mixins, variables and placeholder selectors) is well documented.

Documentation is auto-generated using SassDoc

Running tests

gulp lint will, well, lint the contents scss files in the scss directory.

gulp test with run module's test using Mocha and Sassaby.

gulp tdd will watch both the Sass files and the test specs for changes, and will run tests automatically upon them.

Writing tests

JigSass Tools Selectors tests are written using Sassaby and Mocha. Spec files are located in the test directory.

Mocha allows us to place a call to before() in the root of any test file and it will be run once, before all the other tests in every test_*.js file. We can also require() files and assign them to the global object to make them available to all test_*.js files.

jigsass-tools-selectors uses a file called helper.js can be used to set up mocha globals requires and before().

In addition to Sassaby's testing functions, jigsass-tools-selectors makes a few Sass functions available to the test suite, for use inside Sassaby tests:

jig-var-equals($value, $var) -> {boolean}
Check if a variable equals a value.
$value {*}: A value to compare the value of $var to.
$var {*}: The variable to test
jig-var-type-is($type, $var) -> {boolean}
Check if a variable is of a certain type.
$type {string}: A type to compare with the type of $var.
$var {*}: The variable to test
jig-map-key-equals($value, $map, $keys...) -> {boolean}
Check if a map's key is assigned a cerain value.
$value {*}: A value to compare the value of a key in $map with.
$map {map}: The map to test.
$keys... {arglist}: A recursive chain of keys.
jig-map-key-type-is($type, $map, keys...) -> {boolean}
Check if a map's key is of a certain type
$type {string}: A type to compare with the type of $var.
$map {map}: The map to test.
$keys... {arglist}: A recursive chain of keys.

File structure

┬ ./
│
├─┬ scss/ 
│ └─ index.scss # The module's importable file.
│
├── sassdoc/    # Generated documentation # of the module's sass features
│
└─┬─ test/
  │
  ├─┬ helpers/
  │ │
  │ ├── importer.scss       # Used for easilty importing tested scss files
  │ │
  │ └── _test_helpers.scss  # JigSass's assertion helpers,# for use inside Sassaby tests.
  │                         
  ├── helper.js             # Used for defining global `before()`# functions and requiring modules.
  │                         
  └── test_jigsass-tools-selectors  # Specs. Mocha will automatically 
                                    # run all javascript files located
                                    # in the `test` directory.

License: MIT

jigsass-tools-selectors's People

Contributors

txhawks avatar

Watchers

 avatar

Forkers

gaybro8777

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.