@composi/styler:

Usage

Scoped Styles

@composi/styler uses a JavScript object to describe the styles to create. Because of this you have to be careful defining styles. You are creating an object literal, so all values must be in quotes. single word CSS properties are fine, but if you want to use hypenated CSS properties you have to use their camel case version, or enclose them in quotes. Similarly, pseudo-elements, because the begin with a :, they have to be quoted as well. Also if you are going to use a keyframe animation or a media query, the entire property needs to be quoted. You will see examples of these below.

One last thing, never include a semi-colon at the end of a property value. CSS expects those, but this is JavaScript, so just the plain value in quotes. Include a semi-colon as part of a property value will throw and error.

No Semi-colons

// Correct usage:
addStyles('#list', {
  // Use comma to separate from next property:
  border: 'solid 1px #ccc',
  margin: '20px'
})

// Incorrect:
addStyles('#list', {
  // Do not put semicolons at end!
  // This will generate an error:
  border: 'solid 1px #ccc';
  // Do not put them inside the value.
  // That will also generate an error:
  margin: '20px;'
})

Setup

First you need to import @composi/styler into your project. You want its addStyles method

import { addStyles } from '@composi/styler'

Arguments

addStyles takes who arguments, the base for the style, which is the parent selector you wish to target, and an object literal of key value pairs defining the CSS to create.

When using with @composi/core, you can define your styles inside a component anywhere before the return statement.

import { h, render }} from @composi/core
import { addStyles } from '@composi/styler'

// Define a component with styles:
function Title({greet}) {
  // Define styles:
  addStyles('h1' {
    padding: '2rem',
    color: 'red'
  })
  return (
    <h1>Hello, {greet}!</h1>
  )
}

Because addStyles is inside the component function, everytime it is render, the addStyles function will be called. addStyles creates one virtual stylesheet to which all rules get added. Rules with the same selector will overwrite earlier definitions with the last value provided being used.

Camel Case or Quoted

To use hyphenated properties you have two choices: camel cased or quoted:

import { h, render }} from @composi/core
import { addStyles } from '@composi/styler'

// Define a component with styles:
function Title({greet}) {
  // Define styles:
  addStyles('h1' {
    padding: '2rem',
    color: 'white'
    // camel cased:
    backgroundColor: 'red'
    // or quoted:
    'background-color': 'red'
  })
  return (
    <h1>Hello, {greet}!</h1>
  )
}

Pixel Values

If you're using a pixel value for a property, you can leave off the length identifier and provide just a raw number:

addStyles('#list', {
  // Define margin of 20px:
  margin: 20,
  // Define width of 300px:
  width: 300
})

Nested Elements

Like LESS and SASS, you can nest child elements to define their relationship to parents. This also works for pseudo-elements.

addStyles('#main', {
  ul: {
    margin: 10,
    width: 350,
    border: 'solid 1px #ccc',
    li: {
      padding: 10,
      borderBottom: 'solid 1px #ccc',
      transition: 'all .25s ease-out',
      ':hover': {
        cursor: 'pointer',
        backgroundColor: '#333',
        color: '#fff'
      },
      ':last-of-type': {
        border: 'none'
      }
    }
  }
})

This will produce the following stylesheet:

#main ul {
  margin: 10px;
  width: 350px;
  border: solid 1px #ccc;
}
#main ul li {
  padding: 10px;
  border-bottom: solid 1px #ccc;
  transition: all .25s ease-out;
}
#main ul li:hover {
  cursor: pointer;
  background-color: #333;
  color: #fff;
}
#main ul li:last-of-type {
  border: none;
}

Media Queries

You can add media queries to your component's stylesheet. Just make sure you quote the entire media query. To create a media query rule you need to quote the entire query, and then the entire value. Notice how this is done below:

addStyles('h1', {
    padding: '2rem',
    fontSize: '13pt'
  }
  '@media only screen and (max-width: 500px)':  `{
    h1: {
      padding: '1rem',
      fontSize: '11pt'
    }
}`)

Keyframe Animation

You can add keyframe animations. To do so youo need to quote the keyframe and name property together, and then quote the entire animation rule. Notice how we do that below:

addStyles(nav, {
  h1: {
    'animation-duration': '3s',
    'animation-timing-function': 'ease-out',
    'animation-fill-mode': 'forwards',
    'text-shadow': '0 5px 5px white',
    ':hover': {
      'animation': 'animate-out'
    }
  },
  '@keyframes animate-out': `{
    0% {
      transform: translateX(0);
      opacity: 1;
      height: 40px;
    }
    100% {
      transform: translateX(1000px); 
      opacity: 0;
      height: 0px;
    }
  }`
})

BEM

This will also work with BEM. When doing so, best to just use the generic body tag as the base for the stylesheet:

Define BEM CSS for above markup:

addStyles('body', {
  '.list': {
    margin: '20px 0',
    listStyle: 'none',
    border: 'solid 1px #ccc'
  },
  '.list__item': {
    padding: 0,
    borderBottom: 'solid 1px #ccc',
    ':last-of-type': {
      border: 'none'
    }
  },
  '.item__title': {
    margin: 0,
    padding: 10,
    ':hover': {
      backgroundColor: '#333',
      color: '#fff',
      cursor: 'pointer'
    }
  },
  'item__title-selected': {
    backgroundColor: '#333',
    color: '#fff'
  }
})

Complete Example

import { h, render } from '@composi/core'
import { addStyles } from '@composi/styler'

// Create list with associated stylesheet:
function List({data}) {
  // Base stylesheet rule on class 'list':
  addStyles('.list', {
    listStyles: 'none',
    padding: 0,
    margin: 0,
    border: 'solid 1px #ccc',
    li: {
      padding: '5px 10px'
      borderBottom: 'solid 1px #ccc'
      ':last-of-type': {
        border: 'none'
      }
    }
  })
  // Give list class of 'list':
  return (
    <ul class='list'>
      {
        data.map(item => <li key={item.key}>{item.value}</li>)
      }
    </ul>
  )
}