본문 바로가기

FrontEnd/React

[React] daisyui CSS 컴포넌트 이해하기

 CSS 컴포넌트란?

  원하는 HTML요소의 스타일링을 쉽게 할 수 있는 CSS 클래스를 'CSS 컴포넌트' 라고 하며 이를 제공하는 테일윈드CSS, daisyui 등을 CSS 프레임워크(혹은 CSS라이브러리)라고 한다.

 

 색상 테마

  웹 페이지에서 가장 많이 사용되는 색상을 주 색상(primary color)라고 한다.  웹 페이지 성격에 따라 주 색상이 다를 수 있으며 주색상과 함께 사용되는 보조 색상(secoundary-color)가 있다.  

구글 머티리얼 디자인 색상 가이드

 

  또한 필요에 따라 좀 더 다양한 색상을 사용할 수도 있다.  이런 테마 색상들을 '색상 테마 라고 한다.

daisyui CSS 버튼 컴포넌트 테마(색상)

 

 Button 컴포넌트 구현하기

  daisyui button CSS 컴포넌트의 형태를 react를 통해 간결하게 사용할 수 있음을 알 수 있다.

// 컴포넌트 미적용
<button className="btn btn-primary">button</button>
// 컴포넌트 적용
<Button className="btn-primary">button</button>
// className을 컴포넌트 내에 적용
<Button>button</Button>

 

  우선  버튼 컴포넌트를 추가한다.

import type {FC, DetailedHTMLProps, ButtonHTMLAttributes, PropsWithChildren} from 'react'

export type ReactButtonProps = DetailedHTMLProps<
  ButtonHTMLAttributes<HTMLButtonElement>,
  HTMLButtonElement
>

export type ButtonProps = ReactButtonProps & {}

export const Button: FC<PropsWithChildren<ButtonProps>> = ({
  ...buttonProps
}) => {
  return <button {...buttonProps} />
}

 

  그후 버튼 테스트 인덱스를 구현한다.

import Basic from './Basic'
import Size from './Size'
import IconTest from './IconTest'

export default function ButtonTest() {
  return (
    <section className="mt-4">
      <h2 className="text-5xl font-bold text-center">ButtonTest</h2>
      <div className="mt-4">
        <IconTest />
        <Size />
        <Basic />
      </div>
    </section>
  )
}

 

  다음으로는 베이직 버튼 버튼 테스트 코드를 추가한다.

import {Button} from '../../theme/daisyui'

export default function Basic() {
  return (
    <section className="mt-4">
      <h2 className="font-bold text-3xl text-center">Basic</h2>
      <div className="mt-4 flex justify-evenly">
        <button className="btn btn-primary">daysiui button</button>
        <Button className="btn btn-primary">Button</Button>
      </div>
    </section>
  )
}

 

실행 결과

 

 'btn' 부분 생략하기

  daisyui의 button 컴포넌트는 btn 클래스를 항상 명시해 줘야 하므로 className에 btn클래스를 기본으로 설정해 불편함이 없도록 변경한다.

import type {FC, DetailedHTMLProps, ButtonHTMLAttributes, PropsWithChildren} from 'react'

export type ReactButtonProps = DetailedHTMLProps<
  ButtonHTMLAttributes<HTMLButtonElement>,
  HTMLButtonElement
>

export type ButtonProps = ReactButtonProps & {}

export const Button: FC<PropsWithChildren<ButtonProps>> = ({
  className: _className,
  ...buttonProps
}) => {
  const className = ['btn', _className].join(' ')
  return <button {...buttonProps} className={className} />
}

 

 버튼의 크기 설정하기

  daisyui의 button 컴포넌트는 4가지 크기의 클래스를 제공하며 크기는 btn-lg, btn-md, btn-sm, btn-xs순이다.

import {Button} from '../../theme/daisyui'

export default function Size() {
  return (
    <section className="mt-4">
      <h2 className="text-3xl font-bold text-center">Size</h2>
      <div className="flex mt-4 justify-evenly">
        <Button className="btn-lg btn-primary">Size lg</Button>
        <Button className="btn-md btn-primary">Size md</Button>
        <Button className="btn-sm btn-primary">Size sm</Button>
        <Button className="btn-xs btn-primary">Size xs</Button>
      </div>
    </section>
  )
}

 

실행결과

 

 Icon 컴포넌트 구현하기

  <span>요소는 아이콘을 클릭했을 때 효과가 매끄럽게 표현되지 않는 경우가 있다.  그러므로 단점을 보완하기 위해 daisyui 버튼 CSS 컴포넌트로 <span>을 감싸면 해결할 수 있다.  다만 조금 코드 작성이 번거롭기 때문에  별도의 아이콘 컴포넌트를 추가한다.

 

import type {FC} from 'react'
import type {ButtonProps} from './Button'
import type {IconProps as CIconProps} from '../../components'
import {Button} from './Button'
import {Icon as CIcon} from '../../components'

export type IconProps = ButtonProps &
  CIconProps & {
    iconClassName?: string
  }

export const Icon: FC<IconProps> = ({name, iconClassName, className, ...buttonProps}) => {
  const btnClassName = ['btn-circle', className].join(' ')
  return (
    <Button {...buttonProps} className={btnClassName}>
      <CIcon className={iconClassName} name={name} />
    </Button>
  )
}

 

  추가된 컴포넌트를 통해 아이콘 버튼 확인 소스를 추가한다.

 

import {Icon} from '../../theme/daisyui'

export default function IconTest() {
  const onclick = () => alert('Icon clicked')
  return (
    <section className="mt-4">
      <h2 className="text-3xl font-bold text-center">IconTest</h2>
      <div className="flex items-center justify-around mt-4">
        <Icon className="btn-primary btn-lg" name="setttings" onClick={onclick} />
        <Icon className="btn-secondary btn-md" name="done" onClick={onclick} />
        <Icon className="btn-accent btn-sm" name="menu" onClick={onclick} />
        <Icon className="btn-success btn-xs" name="file_upload" onClick={onclick} />
      </div>
    </section>
  )
}

 

실행결과 : 이벤트가 정상적으로 수행되며 Icon <span>을 <button>으로 감싼걸 확인할 수 있다.

 

 Input 컴포넌트 구현하기

  <input>이 제공하는 속성값은 text, email, passsword 등 텍스트를 입력받는 형태로 쓰이며 daisyui input CSS 컴포넌트를 사용하려면 항상 input 클래스를 포함시켜야 하기 때문에 생략할 수 있게 Input 컴포넌트를 추가한다.

import type {FC, DetailedHTMLProps, InputHTMLAttributes} from 'react'

export type ReactInputProps = DetailedHTMLProps<
  InputHTMLAttributes<HTMLInputElement>,
  HTMLInputElement
>

export type inputProps = ReactInputProps & {}

export const Input: FC<inputProps> = ({className: _className, ...inputProps}) => {
  const className = ['input', _className].join(' ')
  return <input {...inputProps} className={className} />
}

 

  컴포넌트 추가 후 컴포넌트를 사용할 소스를 추가한다.  하단의 소스르르 보는 것과 같이 Input 컴포넌트를 사용함에 따라 input 클래스를 생략한 것을 확인할 수 있다.

import {Input} from '../../theme/daisyui/Input'

export default function Basic() {
  return (
    <section className="mt-4">
      <h2 className="text-3xl font-bold text-center">Basic</h2>
      <div className="flex mt-4 justify-evenly">
        <input className="input input-primary" />
        <Input className="input-primary" />
      </div>
    </section>
  )
}

 

실행결과

 

 색상 설정하기

 

  input CSS 컴포넌트는 primary, secondary, accent, input, success, warning, error 등의 색상으로 경계를 표현할 수 있다.

 해당 색상을 확인할 소스를 추가하자.

import {Input} from '../../theme/daisyui/Input'

export default function Color() {
  return (
    <section className="mt-4">
      <h2 className="text-3xl font-bold text-center">Color</h2>
      <div className="flex p-4 mt-4 justify-evenly">
        <div>
          <label className="label">input-primary</label>
          <Input className="input-primary" />
        </div>
        <div>
          <label className="label">input-secondary</label>
          <Input className="input-secondary" />
        </div>
        <div>
          <label className="label">input-accent</label>
          <Input className="input-accent" />
        </div>
        <div>
          <label className="label">input-info</label>
          <Input className="input-info" />
        </div>
        <div>
          <label className="label">input-success</label>
          <Input className="input-success" />
        </div>
        <div>
          <label className="label">input-warning</label>
          <Input className="input-warning" />
        </div>
        <div>
          <label className="label">input-error</label>
          <Input className="input-error" />
        </div>
      </div>
    </section>
  )
}

 

 

 테두리 설정하기

  Input 컴포넌트에 테두리를 설정하기 위한 코드를 추가한다.

import {Input} from '../../theme/daisyui'

export default function Border() {
  return (
    <section className="mt-4">
      <h2 className="text-3xl font-bold text-center">Border</h2>
      <div className="flex mt-4 justify-evenly">
        <div>
          <label className="label">input-bordered</label>
          <Input className="input-bordered" />
        </div>
        <div>
          <label className="label">input-ghost</label>
          <Input className="input-ghost" />
        </div>
      </div>
    </section>
  )
}

 

실행결과이다 ghost쪽은 커서가 들어가야만 테두리가 표현되는 것을 확인할 수 있다.

 

 크기 설정하기

  input CSS 컴포넌트는 lg, md, sm, xs 등으로 크기를 설정할 수 있다.  크기를 확인할 수 있는 코드를 추가하자.

import {Input} from '../../theme/daisyui/Input'

export default function Size() {
  return (
    <section className="mt-4">
      <h2 className="text-3xl font-bold text-center">Size</h2>
      <div className="flex p-4 mt-4 justify-evenly">
        <div>
          <label className="label">input-lg</label>
          <Input className="input-primary input-lg" />
        </div>
        <div>
          <label className="label">input-md</label>
          <Input className="input-secondary input-md" />
        </div>
        <div>
          <label className="label">input-sm</label>
          <Input className="input-accent input-sm" />
        </div>
        <div>
          <label className="label">input-xs</label>
          <Input className="input-info input-xs" />
        </div>
      </div>
    </section>
  )
}

 

실행결과

 

 모달 컴포넌트 구현하기

  팝업은 모달(modal) 팝업과 모덜리스(modeless) 팝업이 있다.  모달팝업은 새로운 창이 뜬 팝업을 의미하며 모달리스 팝업은 브라우저 안에 영역이 생겨 생성된 팝업을 의미한다.

 

  daisyui는 modal, modal-box, modal-action 클래스로 모달 팝업을 제공하며 modal 클래스에 modal-open 클래스를 추가하면 대화 상자가 화면에 나타난다.

 

  사용의 편의성을 위해 모달 컴포넌트 코드를 추가한다.

import type {FC} from 'react'
import type {ReactDivProps} from '../../components'
import {Div} from '../../components'
import {Icon} from './Icon'

export type ModalProps = ReactDivProps & {
  open?: Boolean
}

export const Modal: FC<ModalProps> = ({open, className: _className, ...props}) => {
  const className = ['modal', open ? 'modal-open' : '', _className].join(' ')
  return <div {...props} className={className} />
}

export type ModalcontentProps = ReactDivProps & {
  onCloseIconClicked?: () => void
  closeIconClassName?: string
}
export const ModalContent: FC<ModalcontentProps> = ({
  onCloseIconClicked,
  closeIconClassName: _closeIconClassName,
  className: _className,
  children,
  ...props
}) => {
  const showCloseIcon = onCloseIconClicked ? true : false
  const className = ['modal-box', showCloseIcon && 'relative', _className].join(' ')
  if (!showCloseIcon) {
    return <div {...props} className={className} children={children} />
  }

  const closeIconClassName = _closeIconClassName ?? 'btn-primary btn-outline btn-sm'
  return (
    <div {...props} className={className}>
      <Div className="absolute" right="0.5rem" top="0.5rem">
        <Icon name="close" className={closeIconClassName} onClick={onCloseIconClicked} />
      </Div>
      {children}
    </div>
  )
}
export type ModalActionProps = ReactDivProps & {}
export const ModalAction: FC<ModalActionProps> = ({className: _className, ...props}) => {
  const className = ['modal_action', _className].join(' ')
  return <div {...props} className={className} />
}

 

  해당 컴포넌트를 통해 화면에 보여줄 코드를 추가한다.

import {Title, Subtitle} from '../../components'
import {Modal, ModalContent, ModalAction, Button} from '../../theme/daisyui'
import * as D from '../../data'

export default function ModelTest() {
  const open = true
  const closeClicked = () => alert('closeClicked')
  const acceptClicked = () => alert('acceptClicked')

  return (
    <section className="mt4">
      <Title>ModalTest</Title>
      <Modal open={open}>
        <ModalContent onCloseIconClicked={closeClicked}>
          <Subtitle>Modal</Subtitle>
          <p className="mt-4 text-justify">{D.randomParagraphs()}</p>
          <ModalAction>
            <Button
              className="w-24 normal-case btn-primary btn-sm"
              onClick={acceptClicked}>
              Accept
            </Button>
            <Button className="w-24 normal-case btn-sm" onClick={closeClicked}>
              Close
            </Button>
          </ModalAction>
        </ModalContent>
      </Modal>
    </section>
  )
}

 

실행결과