Skip to content

Instantly share code, notes, and snippets.

@gcanti
Created December 6, 2016 11:31
Show Gist options
  • Save gcanti/f7ccecc3cd813ba12aeb2a95f5bb2560 to your computer and use it in GitHub Desktop.
Save gcanti/f7ccecc3cd813ba12aeb2a95f5bb2560 to your computer and use it in GitHub Desktop.
Typed style POC
// @flow
//
// library agnostic types and helpers
//
// keep private
class Unit<A> {}
class IsMedia {}
export type Px = string & Unit<'pixel'>;
export type Percentage = string & Unit<'percentage'>;
export type Media = string & IsMedia;
export const px = (x: number): Px => ((`${x}px`: any): Px)
export const percentage = (x: number): Percentage => ((`${x}%`: any): Percentage)
// other unit factories here...
export const media = (options: { minHeight?: Px }): Media => {
const constraints = []
if (options.minHeight) {
constraints.push(`minHeight: ${options.minHeight}`)
}
// other constraints here...
return ((`@media (${constraints.join(' and ')})`: any): Media)
}
// the official $Exact doesn't play well with Pseudo type
type Exact<A> = A & $Shape<A>;
export type Own = Exact<{
fontSize?: Px,
lineHeight?: Px | number
// other rules here...
}>;
export type Pseudo = Exact<{
':hover'?: Own
// other pseudos here...
}>;
// I need an array here because computed properties are bugged
// https://github.com/facebook/flow/issues/2928
// when fixed we could define type Medias = { [key: Media]: Exact<{ style?: Own, pseudo?: Pseudo }> }
export type Medias = Array<[Media, Exact<{
style?: Own,
pseudo?: Pseudo
}>]>;
//
// adapter example
//
// this function is library specific, one for styled-components, one for fela, etc...
// and returns a library specific domain model, perhaps a string or an
// internal representation for styled-components, an object for fela, etc...
function css(style: Own, pseudos: Pseudo, medias: Medias) {
return Object.assign({}, style, pseudos, getMedias(medias))
}
function getMedias(medias?: Medias): ?Object {
if (medias) {
const o = {}
medias.forEach(([m, s]) => o[m] = s)
return o
}
return null
}
//
// usage
//
const style = css({
fontSize: px(30)
// fontSize: 'a' // <= error
// fontSize: 30 // <= error
// fontsize: 1 / <= error
}, {
':hover': {
fontSize: px(50)
}
}, [
[media({ minHeight: px(300) }), {
style: {
fontSize: px(60)
}
}]
])
console.log(JSON.stringify(style, null, 2))
/*
Output:
{
"fontSize": "30px",
":hover": {
"fontSize": "50px"
},
"@media (minHeight: 300px)": {
"fontSize": "60px"
}
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment