Skip to content

CSS 헬퍼

Hono의 내장 CSS in JS(X) 기능인 hono/css 헬퍼를 사용하면 JSX 내에서 CSS를 작성할 수 있다. css라는 자바스크립트 템플릿 리터럴 안에 CSS를 작성한다. css의 반환 값은 클래스 이름이 되며, 이 값을 class 속성에 설정한다. <Style /> 컴포넌트는 CSS 값을 포함하게 된다.

Import

ts
import { Hono } from 'hono'
import { css, cx, keyframes, Style } from 'hono/css'

css Experimental

css 템플릿 리터럴을 사용해 CSS를 작성할 수 있다. 이 경우 headerClassclass 속성 값으로 사용한다. CSS 내용을 담고 있기 때문에 <Style />을 추가하는 것을 잊지 말아야 한다.

ts
app.get('/', (c) => {
  const headerClass = css`
    background-color: orange;
    color: white;
    padding: 1rem;
  `
  return c.html(
    <html>
      <head>
        <Style />
      </head>
      <body>
        <h1 class={headerClass}>Hello!</h1>
      </body>
    </html>
  )
})

:hover와 같은 의사 클래스는 중첩 선택자&를 사용해 스타일링할 수 있다:

ts
const buttonClass = css`
  background-color: #fff;
  &:hover {
    background-color: red;
  }
`

확장하기

클래스 이름을 임베딩하여 CSS 정의를 확장할 수 있다.

tsx
const baseClass = css`
  color: white;
  background-color: blue;
`

const header1Class = css`
  ${baseClass}
  font-size: 3rem;
`

const header2Class = css`
  ${baseClass}
  font-size: 2rem;
`

또한 ${baseClass} {} 구문을 사용해 클래스를 중첩할 수 있다.

tsx
const headerClass = css`
  color: white;
  background-color: blue;
`
const containerClass = css`
  ${headerClass} {
    h1 {
      font-size: 3rem;
    }
  }
`
return c.render(
  <div class={containerClass}>
    <header class={headerClass}>
      <h1>Hello!</h1>
    </header>
  </div>
)

전역 스타일

:-hono-global이라는 의사 선택자를 사용하면 전역 스타일을 정의할 수 있다.

tsx
const globalClass = css`
  :-hono-global {
    html {
      font-family: Arial, Helvetica, sans-serif;
    }
  }
`

return c.render(
  <div class={globalClass}>
    <h1>Hello!</h1>
    <p>Today is a good day.</p>
  </div>
)

또는 css 리터럴을 사용해 <Style /> 컴포넌트 안에 CSS를 직접 작성할 수도 있다.

tsx
export const renderer = jsxRenderer(({ children, title }) => {
  return (
    <html>
      <head>
        <Style>{css`
          html {
            font-family: Arial, Helvetica, sans-serif;
          }
        `}</Style>
        <title>{title}</title>
      </head>
      <body>
        <div>{children}</div>
      </body>
    </html>
  )
})

keyframes Experimental

keyframes를 사용해 @keyframes의 내용을 작성할 수 있다. 이 경우 fadeInAnimation이 애니메이션의 이름이 된다.

tsx
const fadeInAnimation = keyframes`
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
`
const headerClass = css`
  animation-name: ${fadeInAnimation};
  animation-duration: 2s;
`
const Header = () => <a class={headerClass}>Hello!</a>

cx Experimental

cx는 두 개의 클래스 이름을 합성한다.

tsx
const buttonClass = css`
  border-radius: 10px;
`
const primaryClass = css`
  background: orange;
`
const Button = () => (
  <a class={cx(buttonClass, primaryClass)}>Click!</a>
)

단순한 문자열도 합성할 수 있다.

tsx
const Header = () => <a class={cx('h1', primaryClass)}>Hi</a>

Secure Headers 미들웨어와 함께 사용하기

Secure Headers 미들웨어와 함께 css 헬퍼를 사용하려면, <Style nonce={c.get('secureHeadersNonce')} />nonce 속성을 추가해 css 헬퍼로 인한 Content-Security-Policy 문제를 피할 수 있다.

tsx
import { secureHeaders, NONCE } from 'hono/secure-headers'

app.get(
  '*',
  secureHeaders({
    contentSecurityPolicy: {
      // `styleSrc`에 미리 정의된 nonce 값을 설정한다:
      styleSrc: [NONCE],
    },
  })
)

app.get('/', (c) => {
  const headerClass = css`
    background-color: orange;
    color: white;
    padding: 1rem;
  `
  return c.html(
    <html>
      <head>
        {/* css 헬퍼의 `style`과 `script` 엘리먼트에 `nonce` 속성을 설정한다 */}
        <Style nonce={c.get('secureHeadersNonce')} />
      </head>
      <body>
        <h1 class={headerClass}>Hello!</h1>
      </body>
    </html>
  )
})

VS Code를 사용한다면 vscode-styled-components를 활용해 CSS 태그 리터럴에 대한 문법 강조와 IntelliSense 기능을 사용할 수 있다.

VS Code

Released under the MIT License.