HBTH UI/UX 가이드라인

HBTH 전체 페이지에서 일관된 디자인/로직을 유지하기 위한 기준입니다.

공통 헤더/푸터

  • 헤더/푸터는 index 페이지와 동일한 구조/스타일 사용
  • 언어 선택 Segmented 버튼과 테마 토글은 헤더 우측 배치
  • 언어 선택 Segmented 버튼과 테마 토글은 국기 뷰어 페이지 스타일 사용
  • 헤더 좌측에는 사이트 로고, 사이트 약칭(HBTH), 사이트 풀명칭(HungBok Tool Hub) 배치 (기존 번역기능 제거)

공통 파일 우선 원칙

  • styles.css와 페이지 전용 CSS에 동일한 스타일이 있을 경우 공통 파일(styles.css) 사용
  • script.js와 페이지 전용 JS에 동일한 함수/로직이 있을 경우 공통 파일(script.js) 사용
  • 페이지 전용 파일에는 해당 페이지만의 고유한 스타일/로직만 포함
  • 예: .control-panel, .interactive-card 등은 styles.css에서 정의, 페이지 전용은 .active 상태 등 고유 변화만 추가

타이틀 규칙

  • 문서 타이틀(<title> 안 내용) 형식: “(도구이름) - HBTH” (언어에 따라 번역)
  • Hero 타이틀/설명은 index 스타일과 동일 톤 유지
  • Hero 디자인은 국기 뷰어 페이지와 동일한 구조 사용 (.hero + .hero-content)

언어/테마 동기화

  • localStorage 키: lang, theme 사용
  • 다른 탭 변경 시 storage 이벤트로 동기화
  • 테마 토글 아이콘은 dark=moon, light=sun
  • 테마 토글 중복 이벤트 방지:
    • 테마 토글 로직은 공통 script.js에서만 처리
    • 페이지별 JS에서는 테마 토글 이벤트 리스너 추가 금지
    • 원인: 여러 JS 파일에서 동일 요소에 이벤트 리스너 추가 시 중복 실행으로 빠른 전환 발생
    • 수정: 페이지별 JS에서 테마 관련 코드 제거, 공통 script.js에 통합
  • 테마 전환 부드러움:
    • CSS transition 추가로 즉시 전환 방지 (background, color 0.3s ease)
    • 사용자 경험 개선을 위한 부드러운 애니메이션 적용

검색 입력 (Search)

  • 검색 입력은 국기 뷰어 스타일 사용
  • 공통 클래스: main-search-input
  • 검색 아이콘 + clear 버튼 조합 사용

언어 선택 영역 디자인

  • category-scroll + category-chip 조합 사용
  • 선택 상태는 is-active 적용

숫자 입력 (Number)

  • 비율계산기 동일비율 계산 인풋 스타일 사용
  • 전역 number input 스타일 적용

텍스트 입력/텍스트에어리어

  • 글자수 카운트 페이지의 텍스트 입력 디자인 사용
  • 전역 text/textarea 스타일 적용

드롭다운

  • 시간대 계산기 커스텀 셀렉트 스타일 사용
  • 공통 custom select 스타일은 styles.css에 유지
  • 시간대 계산기 날짜 변경 드롭다운 기준
    • 구성: custom-select-wrapper + select 조합 유지
    • 선택 상태는 기본 select 동작 사용 (JS로 UI 동기화)
    • 날짜 변경 시 결과 영역(현재/변경 날짜, 요일, 시간대 표기)을 즉시 갱신
    • 날짜/시간 변경 이벤트는 change 리스너로 처리
    • 로직은 페이지 전용 JS에서 처리하되, 공통 동작/유틸은 script.js 재사용
  • 드롭다운 옵션 디자인/로직
    • 옵션 배경/텍스트 컬러는 테마 변수 사용 (light/dark 모두 대응)
    • 옵션 간격은 padding 적용 (CSS option 스타일)
    • 옵션 변경 시 선택된 값을 즉시 UI에 반영
    • 기본값은 placeholder 스타일 유지 (선택 전 상태 명확화)

체크박스

  • 날짜 계산기 체크박스 스타일 사용
  • HTML 구조:
    • <label class="check">로 감싸기
    • input[type="checkbox"] + span[레이블] 순서 유지
  • CSS 스타일:
    • .check: flex 레이아웃, cursor pointer, user-select none
    • .check input: 20x20px 크기, accent-color로 테마 색상 적용
    • .check span: 0.95rem 폰트, --text 색상 사용
  • 클릭 시 체크박스와 레이블 모두 반응

카드/호버

  • IP 추적기 카드 호버 효과 기준
  • interactive-card 스타일로 입력 영역 카드화

버튼 체계

  • 메인 버튼: primary-btn
  • 보조 버튼: pill-btn 또는 ghost-btn
  • 보조/메인이 아닌 버튼: 글자수 카운트의 복사 버튼 스타일(ghost-btn) 사용

토스트

  • 유튜브 썸네일 페이지의 토스트 스타일 기준
  • 공통 toast 스타일은 styles.css에 유지
  • HTML: <div id="toast-container"></div> 추가
  • JS: showToast(message, isError, isSuccess) 함수 사용
  • 색상 구분:
    • 성공 토스트: 초록색 (#10b981) - isSuccess: true
    • 실패 토스트: 빨간색 (#ef4444) - isError: true
    • 정보 알림: 검은색 (#333) - 기본값 (이둘 모두 false)
  • 각 토스트는 내용 길이에 따라 개별 크기 유지
  • 위치: 화면 하단 중앙 고정, 각 토스트 개별 중앙 정렬
  • 아이콘은 항상 고정 크기(20px), 텍스트 길이에만 반응
  • 3초 후 자동 사라짐 (0.3초 fade-out 애니메이션)

필터/칩

  • index 페이지의 카테고리 칩 스타일 사용
  • 선택 상태는 is-active 사용

모바일 대응

  • 모바일에서도 동일한 간격/타이포 유지
  • 가로 스크롤 없이 자연스러운 카드 스택

전역 변수/함수 충돌 방지

  • 문제: script.js와 페이지별 JS가 같은 이름으로 변수/함수를 선언하면 "Identifier already declared" 에러 발생
  • 원인: 전역 스코프에서 중복된 const/let 선언
  • 해결방안: 페이지별 JS에서는 모든 전역 변수/함수에 페이지 고유 접두사 추가
    • i18n → i18nPage
    • elements → pageElements
    • langButtons → pageLangButtons
    • currentLang → pageCurrentLang
    • updateText() → updatePageText()
    • setLang() → setPageLang()
  • 적용 시: 페이지별 JS 파일 작성 시 모든 전역 변수와 함수에 페이지 이름 또는 고유한 접두사를 붙입니다

HTML 속성 이스케이프 문자 오류

  • 문제: id, class 등의 속성이 백슬래시로 이스케이프되면 JavaScript에서 요소를 찾을 수 없음
  • 원인: HTML 수정 시 실수로 큰따옴표를 이스케이프 문자(" 또는 \")로 작성
    • ❌ <div id="custom-inputs"></div>
    • ❌ <div id=\"custom-inputs\"></div>
    • ✅ <div id="custom-inputs"></div>
  • 결과: document.getElementById("custom-inputs")가 null을 반환하고, 이를 쿼리셀렉터로 조작하려 하면 "Cannot read properties of null" 에러 발생
  • 해결방안: HTML 속성은 항상 일반 따옴표(")를 사용, 이스케이프 문자 사용 금지

<title> 언어별 번역

  • 문제: <title>이 언어와 무관하게 "HBTH" 또는 "HBTH - HBTH"로 출력됨
  • 원인: i18n.js의 번역 데이터에 도구별 타이틀이 누락되거나, updatePageText()에서 document.title을 설정하지 않음
  • 해결방안:
    • i18n.js에 타이틀 추가: 각 도구별 i18n 데이터에 title 필드 포함
      • 예: lotteryDraw.strings.ko.title = "제비뽑기 - HBTH"
      • 예: lotteryDraw.strings.en.title = "Lottery Draw - HBTH"
      • 예: lotteryDraw.strings.ja.title = "くじ引き - HBTH"
    • 페이지 JS의 updatePageText()에서 설정:
      • document.title = texts.title;
    • 형식: "(도구 이름) - HBTH" 형식 필수 (언어에 따라 번역)

헤더 사이트 약칭 고정

  • 문제: 헤더 왼쪽의 사이트 약칭(H1#page-title)이 언어에 따라 도구 이름으로 번역됨
  • 원인: updatePageText()에서 page-title의 텍스트를 i18n으로 번역하려고 할 때, 사이트 약칭이 아닌 도구 이름을 적용
  • 해결방안:
    • HTML 구조:
      • H1#page-title = 항상 "HBTH" (고정, 번역 불가)
      • H2#hero-title = 도구 이름 (언어별 번역)
    • JS에서 처리: updatePageText()에서 page-title은 건드리지 않고, hero-title만 업데이트
      • pageElements.title.textContent = "HBTH"; // 고정
      • pageElements.heroTitle.textContent = texts.heroTitle; // 번역

다중 탭 언어/테마 동기화

  • 문제: 한 탭에서 언어를 변경해도 다른 탭의 언어가 실시간으로 반영되지 않음
  • 원인: Storage 이벤트 리스너가 없거나 제대로 구현되지 않음
  • 해결방안: 모든 페이지 JS에서 storage 이벤트 리스너 구현
    • bindEvents() 또는 초기화 함수에 추가:
      window.addEventListener("storage", (event) => {
        if (event.key === "lang" && event.newValue) {
          setPageLang(event.newValue);
        } else if (event.key === "theme" && event.newValue) {
          document.documentElement.setAttribute("data-theme", event.newValue);
        }
      });
    • 테마 버튼 클릭 시:
      • localStorage.setItem("theme", nextTheme);
      • document.documentElement.setAttribute("data-theme", nextTheme);
    • 언어 버튼 클릭 시:
      • localStorage.setItem("lang", lang);
      • setPageLang(lang);
    • 작동 원리: localStorage 변경 시 다른 탭의 storage 이벤트가 자동으로 발동되어 즉시 동기화

사용 방법

새 페이지를 만들거나 기존 페이지를 수정할 때 위 기준을 우선 적용합니다. 이 페이지를 AI에게 전달해 “가이드라인 기준으로 수정”을 요청하세요.

실동작 데모

검색 입력

국기 뷰어 IP 추적기 시간대 계산기 문자 수 카운트 비율 계산기

검색 입력 + clear + 조회

숫자 입력 (비율 계산)

:
:

A, B, C 입력 시 D = (B×C)/A

텍스트 입력/카운트

0자 0단어

커스텀 셀렉트

선택: -

드롭다운 데모

선택: -

체크박스

체크 상태: 배경 투명=false, 가운데 정렬=true, 자동 저장=false

버튼 스타일

.custom-btn 클래스를 사용하면 글로벌 스타일의 영향을 받지 않고 자유롭게 스타일링할 수 있습니다.

토스트 커스터마이제이션

ms

칩/필터

언어 선택 영역