Skip to content

passing vars around #54

Closed
Closed

Description

@yoshuawuyts

update 2016/03/14: :root isn't a very good idea; we should just rely on JS vars instead.
update 2016/11/16. scratch that, :root in moderate usage is a great idea, as long as a postprocessor can handle it (which it can now)

  • write tests for ES6 string interpolation
  • document variable passing using string interpolation

What do you think of making :root elements the transport for variables and such? Root would never be prefixed, and passed down to child elements so they can read values from it during compilation. Perhaps we should also do this for @custom-media selectors so breakpoints are passed down too? (example)

Opinions?


client-main.js

const sf = require('sheetify')

sf`
  :root {
    --color-bg-primary: blue;
  }
`

require('./client-sidebar')

client-sidebar.js

const sf = require('sheetify')

sf`
  :host > h1 {
    background-color: var(--color-bg-primary);
  }
`

Activity

  1. serapath commented on Feb 15, 2016

    @serapath

    It's kind of interesting - implicit parameters, but in a real app it can become very intransparent i guess.
    I would rather just use:

    client-main.js

    var vdom = require('virtual-dom')
    var sf = require('sheetify')
    var hyperx = require('hyperx')
    var hx = hyperx(vdom.h)
    
    var cliendSidebar = require('./client-sidebar')
    
    var themeRed = '#f00'
    var themeBlue = '#00f'
    
    var prefix = sf`
      div {
        background-color: ${themeBlue};
        width: 50%;
        text-align: center;
      }
    `
    var tree = hx`
      <div className=${prefix}>
        ${clientSidebar(themeRed)}
      </section>
    `
    document.body.appendChild(vdom.create(tree))

    client-sidebar.js

    var vdom = require('virtual-dom')
    var sf = require('sheetify')
    var hyperx = require('hyperx')
    var hx = hyperx(vdom.h)
    
    module.exports = function clientSidebar(colorBgPrimay) {
      var prefix = sf`
        h1 {
          background-color: ${colorBgPrimary};
        }
      `
      return hx`
        <section className=${prefix}>
          <h1>My beautiful, centered title</h1>
        </section>
      `
    }
  2. serapath commented on Feb 19, 2016

    @serapath

    Just thought about one more thing. If you consider something like the above, it would be nice to be able to pass in some form of observable, preferably a stream,.

    Maybe those streams could update css variables or use some polyfill for older browsers.

  3. yoshuawuyts commented on Mar 14, 2016

    @yoshuawuyts
    ContributorAuthor

    Talking to @hughsk he thought it was probably best not to pass :root around since it creates implicit state, which is very bad.

    @serapath I think you're right and using ES6 ${var} is the way to go. We should probably add tests to check if this is going well and document it. I've updated the initial post to reflect this.

    Re observables: that won't work since sheetify runs at compile time, and returns static values. Having values dynamically change during runtime would require a different approach; I don't feel extending sheetify's scope to support that would be wise.

  4. ahdinosaur commented on Mar 14, 2016

    @ahdinosaur
    Member

    here's another possible way of passing vars around (not mutually exclusive with the template variable approach) using CSS variables:

    style-vars.js

    module.exports = {
      primaryColor: '#f00',
      secondaryColor: '#00f'
    }

    client-main.js

    var vdom = require('virtual-dom')
    var sf = require('sheetify')
    var hyperx = require('hyperx')
    var hx = hyperx(vdom.h)
    
    var styleVars = require('./style-vars.js')
    
    var prefix = sf('./client-main.css', { vars: styleVars })
    var tree = hx`
      <section className=${prefix}>
        <h1>My beautiful, centered title</h1>
      </section>
    `
    document.body.appendChild(vdom.create(tree))

    client-main.css

    h1 {
      color: var(--primaryColor);
      text-align: center;
    }
  5. yoshuawuyts commented on Mar 14, 2016

    @yoshuawuyts
    ContributorAuthor

    @ahdinosaur oh yeah, passing in vars like that makes sense for external files I think - think interpolation would make sense for inline. Not sure I'm keen on having different methods for both, but given that { global: true } also is slightly different, this might be unavoidable - it's simple enough I reckon. Wonder what @hughsk's take is on this too

  6. serapath commented on Mar 14, 2016

    @serapath

    Oh, that's sad. I was hoping there will be runtime support. For me, that is a main point of using css-in-js, because changing styles dyanmically is the one thing that cannot be done with traditional solutions.

    @ahdinosaur I like your approach :-) do you know any css-in-js project that does something similar?

  7. ahdinosaur commented on Aug 24, 2016

    @ahdinosaur
    Member

    I think you're right and using ES6 ${var} is the way to go. We should probably add tests to check if this is going well and document it. I've updated the initial post to reflect this.

    hmm, seems this approach isn't working: the ${var} expressions aren't being evaluated correctly in the browserify transform and cause an error.

  8. thomhos commented on Sep 16, 2016

    @thomhos

    Hello,

    Is there any update on how to use vars with sheetify? Using the vars spec from cssnext isn't really working for me as I can't define them in one file and then use them somewhere else (in js).

    ${var} expression would be ideal, but not sure if this is in progress?

    Cheers!

  9. ahdinosaur commented on Sep 17, 2016

    @ahdinosaur
    Member

    I can't define them in one file and then use them somewhere else (in js).

    hey @thomhos, i recently tried to support this use case in sheetify, but ran into some fundamental barriers with doing things as static analysis (using a browserify transform).

    let's say we want to do:

    const css = require('sheetify')
    
    const styles = require('./styles')
    
    const prefix = css`
      .text {
        color: ${styles.textColor}
      }
    `

    if we do this as a browserify transform (even using static-module and a pull request i made to static-eval to statically evaluate template strings), styles is not defined as a value since it has not yet evaluated require('./styles'). it's theoretically possible to evaluate the require calls as necessary, but someone needs to be willing to spend the time and energy to do it right.

    @thomhos for a different library that might work here, maybe try css2obj and free-style.

  10. thomhos commented on Sep 17, 2016

    @thomhos

    Hi @ahdinosaur,

    Thanks for the reply. So before we can actually use const styles, it should be evaluated first, but in doing that also the prefix styles get evaluated which then throws an error because the ${styles.textColor} is not valid syntax? Apologies if I'm not getting it right.

    I was now using the sheetify-sass plugin so I could write sass but also able to use variables. I can then import them within the css. Not sure how much this impacts performance, and it feels a bit hack'ish. But it works for now.

    const style = sf`
      @import '../styles/vars';
    
      :host {
         background-color: $color-primary;
      }
    `;
    

    I'm curious to see how this will evolve!

    Cheers,

  11. ahdinosaur commented on Sep 18, 2016

    @ahdinosaur
    Member

    So before we can actually use const styles, it should be evaluated first, but in doing that also the prefix styles get evaluated which then throws an error because the ${styles.textColor}

    most of what sheetify does happens as a browserify transform, which means we receive a module filename and are expected to return a transform stream that receives source code and returns transformed source code. for the best way to do this, we can use static-module, which under the hood is using falafel which uses acorn which produces objects as described in estree.

    when trying to transform the above snippet, expressions within the parsed AST include all the variables unevaluated.

    for example:

    const prefix = css`
      .text {
        color: ${styles.textColor}
      }
    `

    is parsed as

    tree {
      "type": "Program",
      "start": 0,
      "end": 67,
      "body": [
        {
          "type": "VariableDeclaration",
          "start": 0,
          "end": 66,
          "declarations": [
            {
              "type": "VariableDeclarator",
              "start": 6,
              "end": 66,
              "id": {
                "type": "Identifier",
                "start": 6,
                "end": 12,
                "name": "prefix"
              },
              "init": {
                "type": "TaggedTemplateExpression",
                "start": 15,
                "end": 66,
                "tag": {
                  "type": "Identifier",
                  "start": 15,
                  "end": 18,
                  "name": "css"
                },
                "quasi": {
                  "type": "TemplateLiteral",
                  "start": 18,
                  "end": 66,
                  "expressions": [
                    {
                      "type": "MemberExpression",
                      "start": 43,
                      "end": 59,
                      "object": {
                        "type": "Identifier",
                        "start": 43,
                        "end": 49,
                        "name": "styles"
                      },
                      "property": {
                        "type": "Identifier",
                        "start": 50,
                        "end": 59,
                        "name": "textColor"
                      },
                      "computed": false
                    }
                  ],
                  "quasis": [
                    {
                      "type": "TemplateElement",
                      "start": 19,
                      "end": 41,
                      "value": {
                        "raw": "\n  .text {\n    color: ",
                        "cooked": "\n  .text {\n    color: "
                      },
                      "tail": false
                    },
                    {
                      "type": "TemplateElement",
                      "start": 60,
                      "end": 65,
                      "value": {
                        "raw": "\n  }\n",
                        "cooked": "\n  }\n"
                      },
                      "tail": true
                    }
                  ]
                }
              }
            }
          ],
          "kind": "const"
        }
      ],
      "sourceType": "script"
    }
    

    this would be great to pass into static-eval, but we also need to pass in a context object with all Identifiers (in this case the context object is with { styles } where styles is an object with { textColor }).

    to get this object, we need to find out what variables we need from the sheetify template string call, then go up the tree and try to de-reference them. (in the simplest case, which i think static-module already handles, the identifier is defined as a variable within the same module. the harder case, which we need, is when the identifier is defined as the output of a require call).

    does that help explain things a bit more?

  12. thomhos commented on Sep 19, 2016

    @thomhos

    Hi @ahdinosaur,

    Thanks for the detailed explanation. I do see the problem (i think), and if i'm correct it's not unsolvable, but rather a complicated thing that needs work?

    Anyways, for now I'm good to go using sheetify-sass, and unfortunately don't have the skills (yet) to work on a PR for the above issue.

    Cheers!

  13. ahdinosaur commented on Sep 19, 2016

    @ahdinosaur
    Member

    hey

    Thanks for the detailed explanation. I do see the problem (i think), and if i'm correct it's not unsolvable, but rather a complicated thing that needs work?

    yep. 😄

  14. yoshuawuyts commented on Nov 16, 2016

    @yoshuawuyts
    ContributorAuthor

    yeah so :root variables should now work in v6, forward - in modern browsers no compilation needed even - using it with moderation should be a-ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions