본문 바로가기

JavaScript/HeadlessUI

[Next.js/Headless UI] Components Button 만들기 (디자인과 기능 분리, 커스터마이징)

들어가며

✍️ Headless UI
아래에서 구현한 버튼을 통해 프로젝트 전반에서 재사용 가능한 컴포넌트를 구축하고, 디자인과 기능을 분리하는 경험을 쌓고자 했습니다.

💭 초기 목표

  1. 토글
    : 사용자가 나의카드에서 상태를 쉽게 전환할 수 있도록 합니다.
  2. 기본 버튼
    : 링크 연결 및 페이지 이동 기능을 제공합니다.
  3. Submit 버튼
    : 폼 제출을 구현합니다.

🖼️ 현실

  • Headless UI를 기반으로 다음과 같은 버튼 컴포넌트를 설계하고 구현하였습니다.
  • 각 버튼의 특성을 유지하면서도 커스터마이징할 수 있도록 작업했습니다. (버튼 내용: children)

 

1. MyToggle: 상태 변경을 위한 토글 버튼

  • 사용자의 상호작용에 따라 상태를 변경하는 버튼입니다.
  • Headless UI의 Switch 컴포넌트를 활용하여, 상태 변화에 따라 UI가 동적으로 업데이트되도록 설계했습니다.
  • 상태 관리
    • useState를 사용해 enabled라는 상태를 정의하고, 이 상태 값에 따라 버튼의 스타일이 달라지도록 구현했습니다.
const [enabled, setEnabled] = useState(false);

 

  • UI 동적 스타일링
    • enabled 값에 따라 배경 색상이 bg-blue-600에서 bg-gray-200로 변경됩니다.
    • 버튼 내부의 원(circle)이 translate-x-6 또는 translate-x-1으로 이동하면서 토글 효과를 제공합니다.
className={`${enabled ? 'bg-blue-600' : 'bg-gray-200'} relative inline-flex h-6 w-11 items-center rounded-full`}

 

  • 접근성 고려
    • 시각적 버튼 외에도 sr-only 클래스를 사용해 스크린 리더 사용자들에게도 버튼의 기능(알림 활성화)을 설명했습니다.
    • sr-only: Screen Reader Only, 요소의 내용을 시각적으로 숨기되 스크린 리더에게는 노출되도록 하는 기능 (웹 접근성을 고려해 콘텐츠 숨기기)
<span className="sr-only">Enable notifications</span>

 

2. MyFullButton: 링크 연결용 버튼

  • 클릭하면 지정된 페이지로 이동하는 기본 버튼입니다.
  • Headless UI의 Button과 Next.js의 Link를 조합하여 구현했습니다.
  • 구조
    • 버튼은 Link 컴포넌트로 감싸져 있으며, href 속성을 통해 이동할 페이지를 동적으로 설정할 수 있습니다.
    • 이 구조 덕분에 children으로 버튼 텍스트나 콘텐츠를 필요에 따라 변경할 수 있습니다.
<Link href={href}>
    <Button className="rounded bg-blue-600 py-2 px-4 text-sm text-white data-[hover]:bg-blue-500 data-[active]:bg-sky-700">
        {children}
    </Button>
</Link>

 

  • 스타일링
    • Tailwind CSS를 사용해 버튼의 배경 색상을 상태(기본, hover, active)에 따라 동적으로 변경했습니다. 
    • hover 상태에서는 bg-blue-500, active 상태에서는 bg-sky-700 스타일이 적용됩니다.
className="rounded bg-blue-600 py-2 px-4 text-sm text-white data-[hover]:bg-blue-500 data-[active]:bg-sky-700"

 

3. MyFullSubmitButton: 폼 제출용 버튼

  • 사용자가 폼 데이터를 제출할 수 있도록 설계된 컴포넌트입니다.
  • Headless UI의 Button을 활용하여 제출 버튼의 동작과 스타일을 구현했습니다.
  • 구조
    • 폼을 감싸는 form 태그를 포함하고 있으며, action 속성을 통해 데이터를 전송할 URL을 설정할 수 있습니다.
    • 버튼은 type="submit" 속성을 사용하여 폼 제출 이벤트를 트리거합니다.
<form action={action} method="POST">
    <Button
        type="submit"
        className="rounded bg-blue-600 py-2 px-4 text-sm text-white data-[hover]:bg-blue-500 data-[active]:bg-sky-700">
        {children}
    </Button>
</form>

 

  • 커스터마이징
    • 텍스트는 children을 통해 동적으로 설정되며, 버튼 스타일은 재사용 가능한 형태로 일관성을 유지합니다.

✏️ 배운 점

Headless UI의 장점 체감

💡Headless UI란?
: 시각적 디자인과 컴포넌트의 기능을 분리하여 각각 독립적으로 개발된 디자인 시스템
  • 특징
    1. 디자인과 기능 분리
      디자인 시스템 변경 시 코드에 미치는 영향을 최소화할 수 있습니다.
    2. 커스터마이징
      기본 컴포넌트의 틀을 유지하면서도 children과 className으로 자유롭게 스타일을 조정할 수 있습니다.
  • Headless UI의 기본 제공 구조 덕분에 빠르게 구현할 수 있었습니다.
    • 기능에만 집중할 수 있게 만들어주는 도구라는 점을 체감하였습니다.
    • https://headlessui.com/
 

Headless UI

Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.

headlessui.com


🎯 목적

  • 지속
    • Headless UI의 다른 컴포넌트들도 적극적으로 활용해 UI 구축 속도를 높일 계획입니다.
    • 현재 작성한 버튼 컴포넌트들을 다양한 시나리오에 대응할 수 있도록 최적화할 것입니다.
  • 개선
    • 코드 내 반복되는 스타일링 패턴을 공통 유틸리티 클래스로 추출하여 관리합니다.
    • 팀원들에게 컴포넌트 활용법을 공유하고 디자인 시스템의 통합성을 강화할 예정입니다.
  • 포기
    • 기존에 사용하던 일회성 버튼 스타일 코드를 모두 제거하고, Headless UI 기반으로 일원화할 예정입니다.
반응형