Skip to content

Instantly share code, notes, and snippets.

@jaredpalmer
Last active July 22, 2016 23:03
Show Gist options
  • Save jaredpalmer/81df9539ab923e3a3110e416c71c6da2 to your computer and use it in GitHub Desktop.
Save jaredpalmer/81df9539ab923e3a3110e416c71c6da2 to your computer and use it in GitHub Desktop.
jsxstyle Psuedo Selector API Proposal

jsxstyle + Pseudo Selector API Proposal

Background / Motivation

Pete Hunt's jsxstyle library is incredibly fun to use. It colocates your React component's styling and functionality in the same place and with the same syntax: props. In exchange for this enormous benefit and productivity boost, you must give up pseudo selectors like :hover, :focus, etc. as they are not possible with inline css. This makes things like buttons and inputs extremely challenging. For example, this is a button component from a current project of mine:

// Button.js
import React, { Component } from 'react'
import {Flex} from 'jsxstyle'
import L from '../LayoutConstants'

class Button extends Component {
  constructor () {
    super()
    this.state = {
      hovered: false
    }
    this.handleMouseEnter = this.handleMouseEnter.bind(this)
    this.handleMouseLeave = this.handleMouseLeave.bind(this)
  }

  handleMouseEnter () {
    this.setState({ hovered: true })
  }

  handleMouseLeave () {
    this.setState({ hovered: false })
  }

  render () {
    const { children, onClick, small, ...rest } = this.props
    return (
      <div
        role='button'
        onMouseEnter={this.handleMouseEnter}
        onMouseLeave={this.handleMouseLeave}
        onClick={onClick}
        {...rest}
      >
        <Flex
          alignItems='center'
          justifyContent='center'
          flexWrap='wrap'
          backgroundColor={this.state.hovered ? L.blue : '#ffffff'}
          fontSize='14px'
          fontWeight='600'
          lineHeight={small ? 1 : 1.43}
          outline='0'
          borderRadius='2px'
          padding='8px 20px'
          transition='.25s background ease, .25s color ease'
          userSelect='none'
          whiteSpace='nowrap'
          borderColor={L.blue}
          border='1px solid'
          cursor='pointer'
          color={this.state.hovered ? '#ffffff' : L.blue}>
            {children}
        </Flex>
      </div>
    )
  }
}

export default Button

The extracted CSS:

.Button_0 {
  align-items:center;
  justify-content:center;
  flex-wrap:wrap;
  font-size:14px;
  font-weight:600;
  line-height:1;
  outline:0px;
  border-radius:2px;
  padding:8px 20px;
  transition:.25s background ease, .25s color ease;
  user-select:none;
  white-space:nowrap;
  border:1px solid;
  cursor:pointer;
  display:flex;
}

API Proposal

What if jsxstyle's CSS extractor looked out for special reserved/configurable prop names such as hover and focus. Maybe these need to have a prefix such as xhover, or maybe you could declare in a webpack loader. Regardless, after extracting a component's css classname, any styles found in the hover prop would be immediately declared.

I'm aware that this would not bring jsxstyle to full parity with CSS pseudo-selectors, but it would solve ~80% the challenges with buttons and inputs.

import React from 'react'
import {Flex} from 'jsxstyle'

const Button = ({ onClick, children, ...props }) => (
  <Flex
    role='button'
    fontSize='14px'
    fontWeight='600'
    outline='0'
    lineHeight='1.43'
    borderRadius='2px'
    padding='8px 20px'
    backgroundColor='#fff'
    borderColor='#0070ff'
    color='#0070ff'
    whiteSpace='nowrap'
    transition='.25s background ease, .25s color ease'
    hover={{
      background: '#0070ff',
      color: '#fff'
    }}
    onClick={onCLick}
    {...props}
  >
    {children}
  </Flex>
)

export default Button
.Button_0 {
  ...
}
.Button_0:hover {
  background: #0070ff;
  color: #fff;
}
@petehunt
Copy link

petehunt commented Jul 6, 2016

Yeah I think @meyer has a bunch of ideas how to make this more usable. A few ideas:

  • Generally useful DOM props like onClick etc
  • Include some clever out of the box layout components for common use cases
  • What if we did: <Flex borderColor="red" hoverBorderColor="blue" /> ? I.e. we prefixed "hover", "active" etc to all style props?

@jaredpalmer
Copy link
Author

jaredpalmer commented Jul 6, 2016

@petehunt I really like <Flex borderColor="red" hoverBorderColor="blue" />. That would maintain readability too.

Could you chain them (e.g. <Flex borderColor="red" notlastchildBorderColor="blue" />)?

@petehunt
Copy link

petehunt commented Jul 6, 2016

Heh, I haven't thought that far yet. What pseudoselectors would you want to support (i.e. I really hate nth-child and I'm not super interested in supporting it, but :focus, :active and :hover make some sense)?

@jaredpalmer
Copy link
Author

focus, active, hover is really all we need to start this. Gonna move this into an issue on the repo

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