Totally WIP: Here be dragons
What?
Opinionated toolkit to quickly build visual interfaces in JS.
Why?
✅ Because we're humans who make mistakes and need constraints around a predefined styleguide.
✅ Because some css patterns are really annoying to rewrite all the time.
✅ Because there are too many different ways to peel this potato (styling in JS) and sometimes we just want to write code.
✅ Because we're lazy developers.
How?
Uses Aphrodite https://github.com/Khan/aphrodite under the hood to dynamically generate classnames and inject styles.
Prefixes everything so you don't have to think about it (thanks to Aphrodite);
Provides a library of functional css styles generated according to passed in styleguide values.
Abstracts around overly verbosed css syntaxes.
Includes a set of transform functions to inject dynamic styles.
Sullivan is built with React in mind but doesn't depend on it so can be used in other non-react project.
Using npm:
$ npm install sullivan
Then with a module bundler like webpack that supports either CommonJS or ES2015 modules, use as you would anything else:
// sully.js
// using an ES6 transpiler, like babel
import Sullivan from 'sullivan'
import {StyleSheet, css} from 'aphrodite'
import Styleguide from './styleguide' // BYOS - Bring Your Own Styleguide!
Initialize the singleton somewhere in your project, Sullivan comes with opinional defaults but you should pass in your own map of values according to your project styleguide.
####for a detailed example check out The example project
// ...
const sully = new Sullivan({
aphroditeStylesheet: StyleSheet,
styleguide: Styleguide
});
export default sully;
// Alternatively you can also export just set of things to work with using a convention your team agrees on.
const {utils: classes, rawUtils: raw, inline, mq, z, styleguide} = sully;
const {colors, sizes, textSizes} = styleguide;
export {
classes,
raw,
inline,
mq,
z,
colors,
sizes,
textSizes
}
Then inside your component:
// component.js
import {classes, inline, raw, lineHeights} from './sully';
const FunkyComponent = () => {
return (
<div
className={css(
ss.wrap,
classes.flex.center,
classes.text.yellow,
classes.bg.cover
)}
style={
inline.bg.image("https://placekitten.com/200/300")
}>
Content will be flex centered and yellow with a pretty kitten covering the background!
</div>
);
};
const ss = StyleSheet.create({
wrap: {
...raw.bg.blue,
...raw.ma.large,
lineHeight: lineHeights.loose
}
});
When bootstraping Sullivan you must pass in a styleguide configuration containing:
sizes
: for margins and paddingscolors
: for backgrounds, text colors, svg colors...textSizes
: for text sizes (good ol' typography)lineHeights
: for text sizes (good ol' typography)z
: for depth mapping, (default to z[0-9])
You can see an example styleguide here
A styleguide map is an object with a human readable keys (to be used across utils) and values, with the exception of z-index and reset styles handling You're welcome to call your keys anything you want, just be careful not to make them clash (for example, calling a color 'medium' and a textSize 'medium' will break your text utils).
Alias for Aphrodite's StyleSheet
object.
sheet.create
will generate a new set of unique classnames ready for injection, based on a passed in object of styles.
Alias for Aphrodite's css
object.
Combines generated classnames (either from StyleSheet.create
or from sullivan.util
) left to right and injects styles into document head
.
Catalogue of small abstractions around common css patterns.
Same as rawUtils
but pre-transformed by Aphrodite's Stylesheet.create
method and ready for injection using class
Functional utils returning an object with a transformed css declaration.
the same object passed in (or the default) of z-indexes to be used in custom stylesheets.
##Utils New utils are constantly being added, this is the current list:
- spacing
- pos
- disp
- bg
- svg
- text
- flex
- cursor
Requires sizes
Mapping to margins and paddings according to passed in size values,
for ease of use they are not namespaced by spacing
but shorthanded based on their function:
ma/pa
: Margin/Padding All (top, right, bottom, left)mv/pv
: Margin/Padding Vertical (top, bottom)mh/ph
: Margin/Padding Horizontal (right, left)mt/pt
: Margin/Padding Topmb/pb
: Margin/Padding Bottomml/pl
: Margin/Padding Leftmr/pr
: Margin/Padding Right
The values are based on what you pass through the sizes
argument.
Example based on defaults:
utils.mt.small // outputs a classname with {margin-top: 5px}
No requirements
Mapping to css display values
inline
: {display: inline}inlineBlock
: {display: inline-block}block
: {display: block}flex
: {display: flex}none
: {display: none}
Example:
util.disp.flex // outputs a classname with {display: flex}
Requires colors
Mapping to background-color values according to passed in colors, also a small set of quick patterns.
Example based on defaults:
util.bg.black // outputs a classname with {background-color: '#000'}
Outputs:
{
background-size: 'cover',
background-position: 'center',
background-repeat: 'no-repeat'
}
Requires colors
Mapping to fill values according to passed in colors.
Example based on defaults:
util.svg.black // outputs a classname with {fill: '#000'}
Requires z
Mapping to z-index values according to passed in z map.
Example based on defaults:
util.z.z1 // outputs a classname with {z-index: '1'}
z-index is a special case because beside the private API here provided you should also apply your own pattern on top with what makes sense to you:
sullivan.myZ = {
nav: sullivan.z.z5,
modal: sullivan.z9
// etc...
}
Requires textSizes and colors
Mapping to color, size and weights values according to passed in maps and a set of utils
Example color based on defaults:
util.text.black // outputs a classname with {color: '#000'}
Example size based on defaults:
util.text.small // outputs a classname with {font-size: '12px'}
Example line-height based on defaults:
util.text.loose // outputs a classname with {line-height: 1.2}
Example weight:
util.text.light // outputs a classname with {font-weight: '300'}
Outputs:
{
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
Mapping around the flex syntax (which can be a bit verbosed), also a small set of quick patterns.
Properties:
align
: alignItemsalignSelf
: alignSelfjustify
: justifyContent
Values:
Start
: flex-startEnd
: flex-endCenter
: centerAround
: space-aroundBetween
: space-between
With properties and values combined they summon earth's greatest champion (yes, this is a captain planet reference):
util.flex.alignEnd // outputs a classname with {align-items: 'flex-end'}
util.flex.justifyAround // outputs a classname with {justify-content: 'space-around'}
Outputs:
{
display: 'flex',
align-items: 'center',
justify-content: 'center'
}
Outputs:
{
display: 'flex',
flex-direction: 'column'
}
If you need to do anything else with flex, you're on your own.
- Finish this readme file.
- Truncation text util.
- A helper to generate a "living styleguide" page based on passed in values and used utils.
- Some tests would be nice.
- A website would be cool.
Please do, this is built on top of other amazing open-source projects and belongs to the community.
- Ideas? suggest anything over an Issue or PR!
- Suggestions? Read above!
- bugs? Read above!
- this code smells and you can do better? Yes! You know what to do!
This project initially sparked from working on small to big scale React.js related projects and feeling the pains of using CSS in JS, special mention to Kontor.com for giving me room to introduce and mature some of these concepts, also [@colindresj] (https://github.com/colindresj) and @dkozma for brainstorming ideas and giving suggestions.
Copyright (c) 2016 Adrian le Bas
Includes works from https://github.com/Khan/aphrodite, which is MIT licensed with the following copyright:
Copyright (c) 2016 Khan Academy
Which itself includes work from other libraries...