ReactJS: Style Login Form bằng CSS-in-JS với @emotion

CSS-in-JS là gì?

Trong React chúng ta có nhiều cách để style cho các element như inline style, hay dùng css module, hay đơn giản bạn dùng className và khai báo css ở một file và import vào dự án.

Bên cạnh đó, hiện nay có một cách phổ biến hơn là dùng CSS-in-JS (styled components) với styled-components@emotion. Với styled component chúng ta có thể viết những component cùng với css như một tham số trong một hàm, và bạn có thể truy xuất được props của component đó ngay trong css. Bạn có thể viết CSS bên trong file Javascript của bạn.

Tại sao phải sử dụng CSS-in-JS?

Tại sao phải dùng CSS-in-JS? Bạn có thể tham khảo một số bài viết sau:

A Unified Styling Language

Maintainable CSS in React

Styling trong React

Best practice cho CSS-in-JS

Sử dụng @emotion

Ví dụ chúng ta có thể style một Button như sau:

import styled from '@emotion/styled'

const Button = styled.button`
  color: turquoise;
`

// <Button>Hello</Button>
//
//     👇
// trên cây DOM, button được build ra như sau:
// <button className="css-1ueegjh">Hello</button>

Trên đây là CSS được truyền vào như một literal template, bên cạnh đó, bạn có thể sử dụng object như sau:

const Button = styled.button({
  color: 'turquoise',
})

Lấy giá trị props từ một component:

const Box = styled.div(props => {
  return {
    height: props.variant === 'tall' ? 150 : 80,
  }
})
//hay dùng string
const Box = styled.div`
  height: ${props => (props.variant === 'tall' ? '150px' : '80px')};
`

Với @emotion bạn có thể truyền inline css như sau:

function SomeComponent() {
  return (
    <div
      css={{
        backgroundColor: 'hotpink',
        '&:hover': {
          color: 'lightgreen',
        },
      }}
    >
      This has a hotpink background.
    </div>
  )
}

Style cho Login Form

Giả sử chúng ta cần sử dụng css để style một form như sau:

function LoginForm({onSubmit, submitButton}) {
  function handleSubmit(event) {
    event.preventDefault()
    const {username, password} = event.target.elements

    onSubmit({
      username: username.value,
      password: password.value,
    })
  }
  return (
    <form
      onSubmit={handleSubmit}
      css={{
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'stretch',
        '> div': {
          margin: '10px auto',
          width: '100%',
          maxWidth: '300px',
        },
      }}
    >
      <FormGroup>
        <label htmlFor="username">Username</label>
        <Input id="username" />
      </FormGroup>
      <FormGroup>
        <label htmlFor="password">Password</label>
        <Input id="password" type="password" />
      </FormGroup>
      <div>{React.cloneElement(submitButton, {type: 'submit'})}</div>
    </form>
  )
}

// Sử dụng
<LoginForm
  onSubmit={login}
  submitButton={<Button type="primary">Login</Button>}
/>

Chúng ta cần style cho những component như Button, FormGroup, Input

const Input = styled.input({
  borderRadius: '3px',
  border: '1px solid #f1f1f4',
  background: '#f1f2f7',
  padding: '8px 12px',
})

const FormGroup = styled.div({
  display: 'flex',
  flexDirection: 'column',
})

const buttonTypes = {
  primary: {
    background: '#3f51b5',
    color: 'white',
  },
  secondary: {
    background: '#f1f2f7',
    color: '#434449',
  },
}

const Button = styled.button(
  {
    padding: '10px 15px',
    border: '0',
    lineHeight: '1',
    borderRadius: '3px',
  },
  ({type = 'primary'}) => buttonTypes[type],
)

const CircleButton = styled.button({
  borderRadius: '30px',
  padding: '0',
  width: '40px',
  height: '40px',
  lineHeight: '1',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  background: 'white',
  color: '#434449',
  border: `1px solid #f1f1f4`,
  cursor: 'pointer',
})

Với @emotion, bạn có thể dễ dàng viết Button cho nhiều type như primary, seconday

<Button variant="primary">Login</Button>
<Button variant="secondary">Register</Button>

Tham khảo: Bookshelf của Kent C.Dodds

Leave a Reply

Your email address will not be published. Required fields are marked *