DEV Community

loading...

My experiements with Vuejs and css-in-js + tsx

rokuem profile image Mateus Amorim ・3 min read

NOTE: Here I'm using .tsx vue files

Styled Components

Get vue-styled-components in NPM

My first attempt was to use styled-components with TSX. The syntax is like this:

const Frame = styled.section`
 width: 100%;
`

render() {
  return <Frame></Frame>
}

It works great and it is really nice to not have to use classes all the time, making the HTML easier to read, but it gets harder to know which HTML tags are used to create a semantic code.

You can also do things like this to provide props and themes:

import { ThemeProvider } from 'vue-styled-components';

const Frame = styled.section({
  size: { type: Number, default: 0 }
})`
 ${{size, theme} => `
     width: ${size}px;
     height: ${size}px;
     color: ${theme.fontColor}
   `
 }
`

render() {
  return (
    <ThemeProvider theme={{ fontColor: 'red' }}>
        <Frame size={300}/>
    </ThemeProvider>
  )
}

This works great in general, but there are a few drawbacks, here are the pros and cons of it:

Pros

  • It Removes the need for the class attribute, making the HTML cleaner
  • The syntax inside the template string is the same as SCSS
  • It provides reactivity inside the styles out of the box
  • You can easily know what is being used or not

Cons

  • This package is not typed. There is an issue that provides a type declaration for it, but you still need to adjust it a little to work well with the props and values inside the string template.
  • Since we are replacing the tags with components it gets harder for us and the linter to know if we are creating a good, semantic HTML.
  • It is coupled with vue 2, so when vue 3 releases it will no longer be usable for a while
  • Themes are not typesafe at all :/
  • When using themes and props the syntax does not look that good.

CXS

See CXS in npm

With the Problems of vue-styled-components in mind, I decided to try CXS, it seemed simple, typesafe, fast and framework agnostic. This time I tried it with vue 3.

The syntax is like this:

const frameStyle = cxs({
  width: '100%'
});

//...

setup() {
  return () => (
    <section class={frameStyle}></section>
  )
}

Pretty simple, and it also performs some nice optimizations with atomic CSS to avoid duplicated styles.

but what about reactivity and themes? Well, you can still have that :). You can just declare the styles inside the setup function and use the reactive functions vue3 provides.

import { ref, reactive } from 'vue';

// You can also make this reactive and place it in another file
const theme = {
  fontColor: 'red'
};

// You can also use "mixins" in a typesafe way
const mixins = {
  size(value: number) {
    return {
      width: size + 'px';
      height: size + 'px';
    }
  }
}

setup(props) {
  const clicked = ref(false);

  const styles = reactive({
     frame: cxs({
       pointerEvents: props.disabled ? 'none' : 'all',
       background: clicked ? 'red' : blue,
       color: theme.fontColor,
       ...mixins.size(300)
     })
  });

  return () => (
    <section class={styles.frame} onClick={() => clicked.value = true}></section>
  )
}

This works really well and is also very clean and typesafe, but there are a few cons to highlight, lets summarize:

Pros

  • Light and fast
  • Super simple to use and works on any framework
  • Optimizes the final CSS with atomic classes
  • Works with vue 3 reactivity
  • Very Typesafe

Cons

  • The CXS project did not have many updates and seems like it might be left unmaintained
  • You can't create a CXS for the page itself (to style body and others)

JSS

JSS official website

The final solution for me was JSS. The syntax is a little bit more verbose than CXS, but it has support for global styles, individual rules, stylesheets, plugins, and is more maintained.

const myStyle = jss.createRule({
  width: '100%'
});

I haven't tested it with vue reactivity methods, but it should work just like CXS.

Discussion (2)

pic
Editor guide
Collapse
tttl profile image
Trent

It would be better if there were more details in the jss part

Collapse
rokuem profile image
Mateus Amorim Author

Thanks! Tbh I didn't use jss to much, this was just an experiment I did on a side project hehe. The basic usage of it however is pretty much the same as cxs, where you declare a rule and add it to a tsx element. Also, it has the same pros, but without the cons.

I do prefer coding like this (jss + tsx with vue), but I didn't experiment much more as it would be quite complicated to use it in my current work for now :/

for the biggest advantages of not using vue components are the refactoring and the performance.

default exports are a bad practice. With this we can have named exports and rename components easily. We can also do the same for css rules, mixins and variables, we can document things, go to reference, and much more. The overall performance of the linters also get better (vetur is super slow), and we can bundle things faster too.