porsager/bss

POJO style question

Closed this issue · 7 comments

Hello @porsager

just stumbled over this:

in the README.md under 'Ways of writing CSS', it says:

JS Objects

b({
  backgroundColor: 'black',
  textAlign: 'center',
  $hover: {
    backgroundColor: 'red'
  }
})

while the only way I could make it work, was actually a POJO / function mix like this:

b({
  margin: '2em',
  backgroundColor: 'black',
  textAlign: 'center',
}).$hover({ backgroundColor: 'pink '})

flems here

I am wondering if this is just a typo in the README.md or if it was actually supposed to work the POJO way, too?

The POJO way of writing css may be advantageous when it comes to media queries:

I am currently working on a little script to provide React-agnostic styled-components and styled-system functionality with lovely bss doing the heavy lifting under the hood. Consider the following output from a styled-system type of function such as space

where this

// (attrs :: Object, theme :: Object?) -> Object
function space (attrs, theme = {}) { /* sausage factory */ }

let spacing = space({
  p: '1px 2px 3px 4px',
  mr: [0, 2, 4, 6],
  ml: [1, 3, 5, 7],
  mb: [0, 2, 4, 6],
  mt: [1, 3, 5, 7]
})

...yields an Object like this (after some tweaking of the original space function):

{
  padding: '1px 2px 3px 4px',
  marginTop: '4px'
  marginLeft: '4px',
  marginRight: '0px'
  marginBottom: '0px',
  $media: {
    'screen and (min-width: 40em)': {
        marginBottom: '8px',
        marginLeft: '16px',
        marginRight: '8px',
        marginTop: '16px'
    },
    'screen and (min-width: 52em)': {
        marginBottom: '32px',
        marginLeft: '64px',
        marginRight: '32px',
        marginTop: '64px'
    },
    'screen and (min-width: 64em)': {
        marginBottom: '128px',
        marginLeft: '256px',
        marginRight: '128px',
        marginTop: '256px'
    }
  }
}

if 'Ways of writing CSS' worked as currently documented, I would be able to just drop the space yield into bss as is, right?

const result = bss(spacing)

as opposed to iteratively calling result.$media(ding, dong) like this:

const { $media } = Object.assign({}, spacing)
delete spacing.$media
let result = bss(spacing)
for (let k in $media) {
 result = result.$media(k,$media[k])
}

... which works just fine, but seems more wasteful. But maybe it isn't under the hood. What do you think?

other flems

Ah yes, that's supposed to work. Must have been a regression at some point! I'll look into it.

Seems like interesting usage, curious to see what you come up with 😃

Hi @smuemd ... Sorry, the docs are wrong, and I think only true css selectors should be supported.

eg:

b({
  ':hover': { background: 'blue' },
  '@media screen and (min-width: 52em)': { background: 'yellow' }
})

That still doesn't work though, which is a regression from when I added sanitization of raw POJO input. I'll get that fixed, and I'll fix the docs. ;)

If you think it's worth discussing if the syntax you used above should be supported, let's make a new issue for that :)

So, basically - this should be the way // (notice bss was modified with the fix)

Awesome!

true css selectors like this will do:

{ 
  '@media screen and (min-width: 40em)': { marginBottom: '8px', ... }
}

Will require even less modifications on the styled-components side, since this is what the output already looks like. Maybe not even gonna to fork this then after all.

Re:

curious to see what you come up with 😃

I'd very much appreciate your feedback. The Current WIP is in the first flems already (under the stylething tab).

Here is an updated link

My thinking so far:

The stylething code produces a createStyleThing function which is supposed to work by providing an instance of the vdom lib of your choice (either mithril or react right now) as first argument

import m from 'Mithril'

const stylt = createStyleThing(m, { mode: 'mithril', output: 'class' })

stylt would work like styled-components' styled function on steroids, thanks to the bss instance under the hood. Its API is currently heavily overloaded:

(a :: String? | Object? | Function?, b :: String? | Object? | Function?, c :: Array Function?) -> Object | Function

So the first two arguments a and b can be Strings, Objects or Functions while the third + n arguments, if provided, are expected to be functions

a :: String accepts hyperscript notation to describe the desired component type or tag and its static properties (using mithrils' hyperscript parser):

stylt('div') // same as stylt()
stylt('div.Box[cool][foo=bar]') // same as stylt('#Cool1.Box[cool][foo=bar]')
stylt('something that will break document createElement()') // same as stylt('div.something.that.will.break.document.createElement()')

// while 

stylt(`bc darkred`) // same as stylt('div + bss`bc darkred`)

a :: Object expects a style definition object

stylt({ backgroundColor: 'darkred' }) // almost the same as stylt('div + bss`bc darkred`)

or an object with style, class or className attributes. So any variation of this should actually also work:

stylt(bss.bc('darkred').$hover(b.backgroundColor('red')))

a :: Function expects a styled-components' type function that takes vdom.attrs or props as first argument (currently modifying styled-component to accept a second theme argument to short circuit its reliance on Reacts' state context.

stylt(attrs => ({ backgroundColor: attrs.bc || 'darkred' })) // it is actually a bit more complicated with these functions, but this is the gist of it.

b :: String | Object | Function
works exactly like a. Minus the hyperscript query.

c :: Array Function
expects style yielding functions that work just like argument a :: Function

the stylt function then should yield a component wrapped in boilerplate that satisfies the requirements of the vdom lib that was originally provided. So usage wise, with all the moving part put together, I am aiming for a happy path that would resemble something like this

import m from 'mithril'
import createStyleThing from 'wherever'
import { space, color, fontSize, maxWidth } from 'a-fork-of-styled-system'

const stylt = createStyleThing(m, { mode: 'mithril', output: 'class', theme: null })
const Box = stylt('.Box.standard', space, color, fontSize)

// would that make sense?
const Betterbox = stylt(Box, 'better', maxWidth)

m.mount(document.body, {
  view: () => [
    m(Box, {
      p: [1,2,3]
      color: 'darkgrey',
      bc: 'lightblue',
      fontsize: [3, 4, 5]
      },
      'only a box'), // <div class='Box standard bwsrrl31 bwsrrl32'>only a box</div>
   m(Betterbox, { maxWidth: 1/2 }, 'mind the cascade') // <div class='Box standard bwsrrl31 better bwsrrl33 bwsrrl34'>mind the cascade</div>
  ]
})

I am currently looking into appropriating mechanics of styled-system to integrate with this idea (flems here) (hence my question to clairify the bss API)

I am also wondering what the effect of this will be on the overall performance story. I am currently looking into how to best precompute and cache the parsed output internally so as to prevent component style recomputations on every redraw. This is my biggest concern right now. The flems for sure feels slow as fuck right now (but there is also lots of logging going on, so we will see)

Also I am not sure if the overloading is a bit over the top right now. Maybe I should dial it back to accept string and functions only.

hey @smuemd .. Just wanted to say I'd like to look this through, but I'm pressed for time these days, so it's probably first gonna be next week...

Thanks @porsager
...take it easy, there is no rush on this.

Cool :)

Using nested objects was fixed in ee2bbd5 btw..

I'll release a new version with the fixes later tonight

@porsager Thanks. You're a star!

the styled-system fork is pretty much ready to go

even managed to turn some performance knobs, right there

Stefans-MBP:styled-system smuemd$ node bench
[@v3.1.11] space x 243,901 ops/sec ±3.40% (82 runs sampled)
[@v3.1.11] width x 1,209,751 ops/sec ±1.15% (90 runs sampled)
[@v3.1.11] fontSize x 1,489,248 ops/sec ±1.30% (88 runs sampled)
[@v3.1.11] color x 355,538 ops/sec ±0.76% (88 runs sampled)
[@v3.1.11] style x 1,171,462 ops/sec ±1.37% (85 runs sampled)
[@v3.1.11] width array x 102,102 ops/sec ±1.41% (87 runs sampled)
[@v3.1.11] space array x 98,344 ops/sec ±1.02% (88 runs sampled)
[@v3.1.11] fontSize array x 126,377 ops/sec ±0.81% (89 runs sampled)
[@v3.1.11] color array x 100,871 ops/sec ±1.19% (91 runs sampled)

[@next] space x 1,328,938 ops/sec ±1.00% (88 runs sampled)
[@next] width x 1,585,322 ops/sec ±4.84% (79 runs sampled)
[@next] fontSize x 2,664,783 ops/sec ±1.22% (89 runs sampled)
[@next] color x 856,094 ops/sec ±0.99% (88 runs sampled)
[@next] style x 1,053,963 ops/sec ±1.25% (89 runs sampled)
[@next] width array x 138,927 ops/sec ±1.63% (88 runs sampled)
[@next] space array x 142,151 ops/sec ±1.01% (89 runs sampled)
[@next] fontSize array x 231,577 ops/sec ±2.61% (84 runs sampled)
[@next] color array x 129,403 ops/sec ±1.04% (90 runs sampled)

I will brush up the react-agnostic styled components implementation this week. should have something more presentable to play around with soon.