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.

Posted on by:

Discussion

pic
Editor guide