Skip to content

Instantly share code, notes, and snippets.

@basarat
Last active October 13, 2016 21:24
Show Gist options
  • Save basarat/d92e8a099434415f8c7da8a9f045a5d4 to your computer and use it in GitHub Desktop.
Save basarat/d92e8a099434415f8c7da8a9f045a5d4 to your computer and use it in GitHub Desktop.
My thin wrapper around `free-style`
/**
* @module Maintains a single stylesheet and keeps it in sync with requested styles
*/
import * as FreeStyle from "free-style";
import * as React from "react";
/** Just for autocomplete convinience */
export interface CSSProperties extends React.CSSProperties {
'&:hover'?: CSSProperties;
'&:active'?: CSSProperties;
'&:disabled'?: CSSProperties;
'&:focus'?: CSSProperties;
}
/**
* Only calls cb all sync operations settle
*/
const {afterAllSync} = new class {
pending: any;
afterAllSync = (cb) => {
if (this.pending) clearTimeout(this.pending);
this.pending = setTimeout(cb);
}
}
/**
* We have a single stylesheet that we update as components register themselves
*/
const freeStyle = FreeStyle.create();
const singletonStyle = typeof window === 'undefined' ? { innerHTML: '' } : document.createElement('style');
if (typeof document !== 'undefined') document.head.appendChild(singletonStyle as any);
const styleUpdated = () => afterAllSync(() => {
singletonStyle.innerHTML = freeStyle.getStyles();
});
/**
* Allows use to use the stylesheet in a node.js environment
*/
export const getRawStyles = () => freeStyle.getStyles();
/**
* Takes CSSProperties and return a generated className you can use on your component
*/
export function style(...objects: CSSProperties[]) {
const object = extend(...objects);
const className = freeStyle.registerStyle(object);
styleUpdated();
return className;
}
/**
* Takes Keyframes and returns a generated animation name
*/
export function keyframes(frames: {
[key /** stuff like `from`, `to` or `10%` etc*/: string]: CSSProperties
}) {
const animationName = freeStyle.registerKeyframes(frames);
styleUpdated();
return animationName;
}
/**
* Merges various styles into a single style object.
* Note: if two objects have the same property the last one wins
*/
export function extend(...objects: CSSProperties[]): CSSProperties{
/** The final result we will return */
const result: CSSProperties = {};
for (const object of objects) {
for (const key in object) {
if (
// Some psuedo state or media query
(key.indexOf('&:') === 0 || key.indexOf('@media') === 0)
// And we already have something for this key
&& result[key]
) {
// Then extend in the final result
result[key] = extend(result[key], object);
}
// Otherwise just copy to output
else {
result[key] = object[key];
}
}
}
return result;
}
// This is effectively my stylesheet
namespace ButtonStyles {
// Reusable variables
const color = 'red';
const buttonStyles = {
color,
background: 'grey',
};
// Classes!
export const buttonClass = fstyle.style(buttonStyles);
}
// Component
// May or may not be react!
const Button = ()=> <button className={ButtonStyles.buttonClass}/>
// Server side
const css = fstyle.getRawStyes();
// ^ render this `css` in a style tag at will.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment