0.2.5 • Published 7 years ago

@plustwo/glamorous v0.2.5

Weekly downloads
1
License
MIT
Repository
-
Last release
7 years ago

glamorous

const MyStyledDiv = glamorous.div({
  fontSize: 20,
  textAlign: 'center',
})

Installation

yarn add glamor glamorous

Terms and concepts

glamorous

The glamorous function is the main (only) export. It allows you to create glamorous components that render the styles to the component you give it. This is done by forwarding a className prop to the component you tell it to render. But before we get into how you wrap custom components, let's talk about the built-in DOM components.

built-in DOM component factories

For every DOM element, there is an associated glamorous component factory attached to the glamorous function. As above, you can access these factories like so: glamorous.div, glamorous.a, glamorous.article, etc.

glamorousComponentFactory

Whether you create one yourself or use one of the built-in ones mentioned above, each glamorousComponentFactory allows you to invoke it with styles and it returns you a new component which will have those styles applied when it's rendered. This is accomplished by generating a className for the styles you give and forwarding that className onto the rendered element. So if you're wrapping a component you intend to style, you'll need to make sure you accept the className as a prop and apply it to where you want the styles applied in your custom component (normally the root element).

...styles

The glamorousComponentFactory accepts any number of style object arguments. These can be style objects or functions which are invoked with props on every render and return style objects. To learn more about what these style objects can look like, please take a look at the glamor documentation.

GlamorousComponent

The GlamorousComponent is what is returned from the glamorousComponentFactory. Its job is to get all the styles together get a className (from glamor) and forward that on to your component.

For examples below, we'll use this as our GlamorousComponent:

const MyStyledDiv = glamorous.div({margin: 1, fontSize: 1, padding: 1})

It does a few interesting things based on the props you pass it:

className

For each className you provide, the GlamorousComponent will check to see whether it is a glamor generated className (can be from raw glamor or from glamorous, doesn't matter). If it is, it will get the original styles that were used to generate that className and merge those with the styles for the element that's rendered in a way that the provided className's styles win in the event of a conflict.

If the className is not generated by glamor, then it will simply be forwarded along with the GlamorousComponent-generated className.

const myCustomGlamorStyles = glamor.css({fontSize: 2})
<MyStyledDiv className={`${myCustomGlamorStyles} custom-class`} />
// styles applied:
// {margin: 1, fontSize: 2, padding: 1}
// as well as any styles custom-class applies
cssOverrides

This is an object and if provided, it will be merged with this component's and take highest priority over the component's predefined styles.

const myCustomGlamorStyles = glamor.css({fontSize: 2, padding: 2})
<MyStyledDiv
  className={`${myCustomGlamorStyles} custom-class`}
  cssOverrides={{padding: 3}}
/>
// styles applied:
// {margin: 1, fontSize: 2, padding: 3}
// as well as any styles custom-class applies
other props

Only props that are safe to forward to the specific element that will ultimately be rendered will be forwarded. So this is totally legit:

<MyStyledDiv size="big" />

A use case for doing something like this would be for dynamic styles:

const staticStyles = {color: 'green'}
const dynamicStyles = props => {fontSize: props.size === 'big' ? 32 : 24}
const MyDynamicallyStyledDiv = glamorous.div(staticStyles, dynamicStyles)

The exception to this prop forwarding is the pre-created GlamorousComponents (see below).

built-in GlamorousComponents

Often you want to style something without actually giving it a name (because naming things is hard). So glamorous also exposes a pre-created GlamorousComponent for each DOM node type which make this reasonable to do:

const { Div, Span, A, Img } = glamorous

function MyUserInterface({name, tagline, imageUrl, homepage, size}) {
  const nameSize = size
  const taglineSize = size * 0.5
  return (
    <Div display="flex" flexDirection="column" justifyContent="center">
      <A href={homepage} textDecoration="underline" color="#336479">
        <Img borderRadius="50%" height={180} src={imageUrl} />
        <Div fontSize={nameSize} fontWeight="bold">{name}</Div>
      </A>
      <Span fontSize={taglineSize} color="#767676">
        {tagline}
      </Span>
    </Div>
  )
}

Having to name all of that stuff could be tedious, so having these pre-built components is handy. The other handy bit here is that the props are the styles for these components. Notice that glamorous can distinguish between props that are for styling and those that are have semantic meaning (like with the Img and A components which make use of src and href props).

One other tip... This totally works:

<glamorous.Div color="blue">
  JSX is pretty wild!
</glamorous.Div>

Theming

glamorous fully supports theming using a special <ThemeProvider> component.

It provides the theme to all glamorous components down the tree.

Try this out in your browser here!

import glamorous, {ThemeProvider} from glamorous

// our main theme object
const theme = {
  main: {color: 'red'}
}

// our secondary theme object
const secondaryTheme = {
  main: {color: 'blue'}
}

// a themed <Title> component
const Title = glamorous.h1({
  fontSize: '10px'
}, (props, theme) => ({
  color: theme.main.color
}))

// use <ThemeProvider> to pass theme down the tree
<ThemeProvider theme={theme}>
  <Title>Hello!</Title>
</ThemeProvider>

// it is possible to nest themes
// inner themes will be merged with outers
<ThemeProvider theme={theme}>
  <div>
    <Title>Hello!</Title>
    <ThemeProvider theme={secondaryTheme}>
      {/* this will be blue */}
      <Title>Hello from here!</Title>
    </ThemeProvider>
  </div>
</ThemeProvider>

// to override a theme, just pass a theme prop to a glamorous component
// the component will ignore any surrounding theme, applying the one passed directly via props
<ThemeProvider theme={theme}>
  {/* this will be yellow */}
  <Title theme={{main: {color: 'yellow'}}}>Hello!</Title>
</ThemeProvider>

glamorous also exports a withTheme higher order component (HOC) so you can access your theme in any component!

Try this out in your browser here!

import glamorous, {ThemeProvider,  withTheme} from glamorous

// our main theme object
const theme = {
  main: {color: 'red'}
}

// a themed <Title> component
const Title = glamorous.h1({
  fontSize: '10px'
}, (props, theme) => ({
  color: theme.main.color
}))

// normal component that takes a theme prop
const SubTitle = ({children, theme: {color}}) => (
  <h3 style={{color}}>{children}</h3>
);

// extended component with theme prop
const ThemedSubTitle = withTheme(SubTitle);

<ThemeProvider theme={theme}>
  <Title>Hello!</Title>
  <ThemedSubTitle>from withTheme!</ThemedSubTitle>
</ThemeProvider>

Or if you prefer decorator syntax:

import React, {Component} from 'react';
import glamorous, {ThemeProvider,  withTheme} from glamorous

// our main theme object
const theme = {
  main: {color: 'red'}
}

// a themed <Title> component
const Title = glamorous.h1({
  fontSize: '10px'
}, (props, theme) => ({
  color: theme.main.color
}))

// extended component with theme prop
@withTheme
class SubTitle extends Component {
  render() {
    const {children, theme: {color}} = this.props;
    return <h3 style={{color}}>{children}</h3>;
  }
}

<ThemeProvider theme={theme}>
  <Title>Hello!</Title>
  <SubTitle>from withTheme!</SubTitle>
</ThemeProvider>

withTheme expects a ThemeProvider further up the render tree and will warn in development if one is not found!

Server Side Rendering (SSR)

Because both glamor and react support SSR, glamorous does too! I actually do this on my personal site which is generated at build-time on the server. Learn about rendering react on the server and glamor too.

Example Style Objects

Style objects can affect pseudo-classes and pseduo-elements, complex CSS selectors, introduce keyframe animations, and use media queries:

const MyLink = glamorous.a({
  ':hover': {
    color: 'red'
  }
})

// Use in a render function
<MyLink href="https://github.com">GitHub</MyLink>
const MyListItem = glamorous.li({
  listStyleType: 'none',
  position: 'relative',
  '&::before': {
    content: `'#'`, // be sure the quotes are included in the passed string
    display: 'block',
    position: 'absolute',
    left: '-20px',
    width: '20px',
    height: '20px'
  }
})
// Use in a render function
<ul>
  <MyListItem>Item 1</MyListItem>
  <MyListItem>Item 2</MyListItem>
  <MyListItem>Item 3</MyListItem>
</ul>
const MyDiv = glamorous.div({
  display: 'block',
  '& div': { color: 'red' }, // child selector
  '& div:first-of-type': { textDecoration: 'underline' }, // psuedo-selector
  '& > p': { color: 'blue' } // direct descendent
})

// Use in a render function
<MyDiv>
  <div><p>Red Underlined Paragraph</p></div>
  <div>Red Paragraph</div>
  <p>Blue Paragraph</p>
</MyDiv>
// import css from glamor
import { css } from 'glamor'

// Define the animation styles
const animationStyles = props => {
  const bounce = css.keyframes({
    '0%': { transform: `scale(1.01)` },
    '100%': { transform: `scale(0.99)` }
  })
  return {animation: `${bounce} 0.2s infinite ease-in-out alternate`}
}

// Define the element
const AnimatedDiv = glamorous.div(animationStyles)

// Use in a render function
<AnimatedDiv>
  Bounce.
</AnimatedDiv>
const MyResponsiveDiv = glamorous.div({
  width: '100%',
  padding: 20,
  '@media(min-width: 400px)': {
    width: '85%',
    padding: 0
  }
})
// Use in a render function
<MyResponsiveDiv>
  Responsive Content
</MyResponsiveDiv>