Blink converts Node.js modules into CSS.
If you landed here, you're probably a front-end web developer of some kind. You know how to write JavaScript. You might even have a favorite CSS preprocessor. Sure, they allow you to write variables and functions in some form or another, but they also require that you learn their domain-specific language (DSL), which often falls short of a full-blown language. You scour their documentation, struggling to find solutions to problems you already know how to solve in JavaScript. We keep looking for ways to introduce logic into our CSS, so why not just use JavaScript?
Blink doesn't need to do anything special to support functions, because blink runs actual JavaScript. This means the equivalent of Sass mixins can be achieved in blink by means of a function that returns any number of CSS declarations. In blink, these are implemented as overrides. For example, the fill override allows you to add { fill: true }
to your rule body, which, in turn, generates 5 CSS declarations to fill its relative or absolute container.
Blink follows the Single Responsibility Principle (SRP), which means it doesn't try to do too much. As such, you are encouraged to combine blink with other tools to achieve the best result. For example, use Autoprefixer to add vendor prefixes and Spritesmith to generate sprites, which can then be implemented directly in blink. There are a plethora of Node modules you can leverage.
Blink is just getting started, so stay tuned for any updates.
- 100% code coverage
- 4.0 Code Climate GPA
- Runs on Node
- Gulp plugin
- Grunt plugin
- Middleware
- OOCSS with BEM Syntax
- Rules
- Mixins
- Overrides
- Responders
- Plugins
- TypeScript Source
- CLI
- API
At its simplest, blink lets you write CSS with a simple object initializer. This object needs to be exported with either module.exports
in Node or just exports
in the browser. Here's a quick and dirty example:
exports = {
foo: {
bar: 'baz'
}
};
This would generate the following CSS:
foo {
bar: baz;
}
From here, you'll want look at the full list of features for a comprehensive overview of what-all blink has to offer.
Unlike most CSS preprocessors out there, blink does not transpile a DSL into CSS. Blink code gets compiled directly as a Node module, giving you access to all JavaScript syntax for free. This, of course, includes variables and functions, as well as file I/O. The possibilities are endless.
Blink exposes a plugin method, which is, itself, a gulp plugin. As with any gulp plugin, files can be piped to other gulp plugins before being written to their final destination. Blink supports vinyl files in buffer mode only (streams not supported).
var blink = require('blink');
var concat = require('gulp-concat');
var eventStream = require('event-stream');
var gulp = require('gulp');
gulp.task('styles', function() {
var bundles = ['app', 'account'].map(function(bundleName) {
return gulp.src('styles/' + bundleName + '/**/*.js')
.pipe(blink.plugin(/* options */))
// more plugins
.pipe(concat(bundleName + '.css'))
.pipe(gulp.dest('./build/css'));
});
return eventStream.merge.apply(this, bundles);
});
This styles
task will build two CSS bundles (app and account), passing them through the same plugins and writing them to the same destination folder.
Note: gulp-blink has been deprecated in favor of using the blink module's plugin method.
For those wishing to transpile blink files into CSS in the browser, you can use Browserify or your module loader of choice. If you would like to create a gulp task that bundles blink with Browserify, it would look something like this:
var gulp = require('gulp');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
gulp.task('bundle-blink', function() {
browserify()
.require('./node_modules/blink/js/lib/browser/blink.js', { expose: 'blink' })
.bundle()
.pipe(source('blink.js'))
.pipe(gulp.dest('dist'));
});
This gulp task simply bundles up blink's browser package, names it blink.js and dumps it in a dist folder.
Include the script in your web page:
<script src="/dist/blink.min.js"/>
Compile your block:
var blink = require('blink');
var foo = new blink.Block('foo', { bar: 'baz' });
blink(foo, function(err, css) {
console.log(css);
});
You can also compile a string of source code as long as you export the rule with exports
.
var blink = require('blink');
var foo = 'exports = { foo: { bar: "baz" }}';
blink(foo, function(err, css) {
console.log(css);
});
Blink is designed with BEM syntax in mind. You can create blocks, elements and modifiers and their CSS selectors will be generated for you. You can configure your BEM format however you want, but the default naming convention follows that which is defined in MindBEMding – getting your head 'round BEM syntax.
Here's an example of a block with both an element and a modifier:
///<reference path="./node_modules/blink/blink.d.ts"/>
import blink = require('blink');
var btn = new blink.Block('btn', {
min: {
width: 80
},
elements: {
foreground: {
color: 'black'
}
},
modifiers: {
wide: {
min: {
width: 120
}
}
}
});
export = btn;
This would generate the following CSS:
.btn {
min-width: 80px;
}
.btn__foreground {
color: black;
}
.btn--wide {
min-width: 120px;
}
The Rule class allows you to specify a standard CSS rule and can be useful when styling page defaults.
///<reference path="./node_modules/blink/blink.d.ts"/>
import blink = require('blink');
var normalize = [
new blink.Rule('html', {
font: {
family: 'sans-serif'
}
}),
new blink.Rule('body', {
margin: 0
}),
new blink.Rule('a:active, a:hover', {
outline: 0
})
// ...
];
export = normalize;
You are encouraged to use BEM blocks for all of your components. There's nothing stopping you from using basic rules, but you should avoid them if at all possible.
If you're coming from Sass, you might be familiar with mixins. Really, Sass mixins are no different than functions in JavaScript; thus, blink supports them. All you have to do is create a function that returns an array of declarations. This is, in fact, how overrides work.
Overrides are named function factories. The function that is returned can be used for the purpose of generating any number of CSS declarations. This enables you to override existing CSS properties or create your own. For example, say you wanted to override the CSS color
property to always convert colors into hsl
. You could do that! Maybe you want to create a new clearfix
property that, when set to true, generates 3 CSS declarations. Good news – that one already exists and you can override it with your own plugin if you wish.
Let's take an in-depth look at the fill override. Here's how you would go about writing it from scratch, in TypeScript.
function fill() {}
export = fill;
Firstly, overrides are just functions, but they are function factories, which means they need to return a function. Let's do that.
function fill(value: boolean) {
return () => {};
}
export = fill;
We're accepting a value: boolean
argument, because we know that fill is a black and white thing. You either want it or you don't. Next, we created a new function and immediately return it. The fill
override is actually quite simple. All we need to do is generate some CSS declarations.
function fill(value: boolean) {
return () => {
if (!value) {
return [];
}
return [
['position', 'absolute'],
['top', '0'],
['right', '0'],
['bottom', '0'],
['left', '0']
];
};
}
export = fill;
This override will only generate CSS declarations if you call it with { fill: true }
in the rule body. If you call it with false
, it returns an empty array, which gets ignored by the compiler. It just generates 5 declarations and leaves it at that – pretty simple.
See the background override for a more complex example that converts an object literal into a shorthand list of background properties. True, this gives you some optimized CSS, but that could be achieved through a build tool. The real value here is that it gives you TypeScript Intellisense for the types of options you can provide to the background
property, reducing the need to lookup documentation.
Overrides are registered on the configuration object. If you wish to extend the configuration, you can do so by providing a plugin module.
Note: override names are dasherized for you (e.g., fooBar overrides the foo-bar
property).
Responders currently only support MediaAtRules, which allow you to create responsive websites. Here's an example of a basic responder:
///<reference path="./node_modules/blink/blink.d.ts"/>
import blink = require('blink');
var foo = new blink.Block('foo', {
respond: [
new blink.MediaAtRule('screen and (max-width: 320)', {
width: 100
})
]
});
export = foo;
This generates the following CSS:
@media screen and (max-width: 320) {
.foo {
width: 100px;
}
}
Plugins can be defined in the configuration like so:
{
"plugins": ["yourname.overrides"]
}
If you were to publish an npm package under the name yourname.overrides
and if you wanted to override the boxSizing override that blink already provides, you could do it like so:
function plugin() {
this.overrides.boxSizing = require('./overrides/boxSizing');
return this;
}
Now, every time someone declares box-sizing: whatever
your override will be called with whatever
as the first and only argument. The returned set of declarations will replace the original one. In this case, however, box-sizing
does nothing with arguments.
With all the new build tools and taks runners springing up, blink was built with that in mind, that various tools would need access to the compiled results without writing any files to the disk.
Since blink source code is written in TypeScript, you don't need to constantly look-up documentation to gain insight as to how you can use the blink API. Unfortunately, although there is TypeScript support for other editors, you won't get the powerful feature of Intellisense unless you are using Visual Studio.
BTW, you can write your blink files in TypeScript or JavaScript. It really doesn't matter as long as it ends up in JavaScript.
See blink-cli
$ npm install --save-dev blink
///<reference path="./node_modules/blink/blink.d.ts"/>
import blink = require('blink');
Refer to the blink TypeScript definition for a list of available public methods.
Released under the MIT license.