IT

블로그 꽃단장 파일 공유(w/claude)

hyos 2026. 6. 12. 21:44

skin-share.html 코드 가이드

— 파일 구조, 동작 원리, 커스터마이징 지점

tistory-skin-guide.md(제작 과정/이슈 해결기)와 함께 보는 코드 레퍼런스입니다.
이 문서는 "코드가 어떻게 생겼고, 어디를 고치면 무엇이 바뀌는가"에 집중합니다.


1. 파일 한 장 요약

skin-share.html 하나에 HTML + CSS + JS가 전부 들어있습니다. (약 1,300줄)

skin-share.html
├─ <head>
│   ├─ Google Fonts 로드 (Nanum Pen Script / Noto Serif KR / Noto Sans KR)
│   └─ <style>  ······································ 약 10~590행
│       ├─ ① 디자인 토큰 (:root / body.dk)
│       ├─ ② 베이스 & 레이아웃 (헤더, 그리드, 사이드바)
│       ├─ ③ 글 카드 + 미리보기 클램프
│       ├─ ④ 페이지네이션
│       ├─ ⑤ 사이드바 위젯 (검색/카테고리/태그/RSS)
│       ├─ ⑥ Tistory 자동 삽입 요소 덮어쓰기 (!important 지대)
│       ├─ ⑦ 첨부파일 재스타일 + 확장자 칩
│       └─ ⑧ 상세 페이지 모드 (body.detail …)
├─ <body class="tt-body-page">
│   ├─ 헤더 (블로그명, 카테고리 내비, 다크모드 토글)
│   ├─ 메인 컬럼: <s_list> 글 목록 + <s_article_rep> 글 상세
│   │   └─ 카드 내부에 태그/명함/댓글 등 "상세 전용 요소" 동봉
│   ├─ 사이드바: 검색 / 카테고리 / 태그 클라우드 / RSS
│   └─ 푸터
└─ <script>  ········································ 약 724~1330행
    ├─ (a) localStorage 캐시 정리
    ├─ (b) 첨부파일 DOM 재구성
    ├─ (c) 페이지네이션 재구성
    ├─ (d) 페이지 타입 감지 + 상세 모드 토글  ← 가장 큰 블록
    │   ├─ 사이드바 카테고리 재구성
    │   ├─ 글 태그 정리(멀티 태그 분리) + 폴백
    │   ├─ author-box / atags 중복 제거
    │   └─ 사이드바 태그 클라우드 정리
    ├─ (e) 다크모드 토글 + 복원
    └─ (f) 스크롤 진행 바

2. 디자인 토큰 — 색/스타일을 바꾸려면 여기만

모든 색상은 CSS 변수로 선언되어 있고, 라이트/다크가 같은 변수명을 공유합니다.
변수만 바꾸면 전체 테마가 일괄 변경됩니다.

:root {                                  /* 라이트 모드 */
  --ac:#B07828;                          /* 액센트 (브론즈) — 링크 호버, 현재 페이지, 칩 */
  --ac-a:rgba(176,120,40,.13);           /* 액센트 연한 배경 */
  --bg:#F8F6F3;  --nav:#EFE9E2;          /* 페이지 배경 / 내비 배경 */
  --tx:#1A1814;  --sub:#4A4238;  --mt:#6E665E;  /* 본문 / 보조 / 메타 텍스트 */
  --bd:#E4DDD5;  --tb:#EDE7DF;  --cb:#FFFEFB;   /* 보더 / 태그 배경 / 카드 배경 */
}
body.dk { /* 다크 모드 — 같은 변수를 어두운 값으로 재정의 */ }
바꾸고 싶은 것 수정 위치
포인트 컬러 --ac, --ac-a (라이트/다크 각각)
전체 배경 톤 --bg, --cb, --bd
본문 폰트 body{font-family:...} + <head>의 Google Fonts 링크
로고 손글씨체 Nanum Pen Script 사용처 (헤더 로고, 아바타 이니셜)

규칙: 색을 하드코딩하지 말 것. 새 요소를 추가할 때도 var(--ac) 등 변수를 쓰면
다크모드가 공짜로 따라옵니다. (예외: 확장자 칩 색상은 파일 형식 고유색이라 하드코딩)


3. 한 장 두 모드: 목록 ↔ 상세

이 스킨의 핵심 트릭입니다. 카드(.card) 마크업은 하나인데,
body 클래스에 따라 두 가지 모습으로 렌더됩니다.

목록 모드 (기본)                상세 모드 (body.detail)
┌─ .card ──────────┐           .card → 보더/배경 제거, 아티클화
│ 제목 (.pt)        │           .pt   → 28px 대제목
│ 미리보기 2줄 (.pe) │           .pe   → 클램프 해제, 전문 표시
│ 태그 (compact)    │           .rep-extra → 표시 (명함, 댓글 등)
└──────────────────┘           + "← 목록으로" 링크 JS 삽입

동작 순서:

  1. <body class="tt-body-page"> → Tistory가 tt-body-page(상세) 등으로 치환
  2. JS(d블록)가 클래스를 읽어 body.detail 클래스를 추가/생략
  3. CSS의 body.detail ... 섹션(⑧)이 카드를 아티클로 변환
// 정식 판별 우선, 변수 미처리 시 휴리스틱 폴백
if (/tt-body-/.test(bodyCls)) {
  isDetail = /tt-body-page(\s|$)/.test(bodyCls);
} else {
  isDetail = cards.length === 1;
}

상세 전용 요소 패턴: 명함·댓글처럼 상세에서만 보일 요소는 카드 안에
class="rep-extra"로 넣어두고 기본 display:none, body.detail에서만 표시합니다.
새 상세 전용 요소를 추가할 때 이 클래스만 붙이면 됩니다.


4. 미리보기 클램프 — .pe의 방어 설계

목록의 본문 미리보기는 Tistory가 어떤 HTML이든 넣을 수 있는 영역입니다.
그래서 3중 방어를 합니다:

.pe{ max-height:53px !important; overflow:hidden !important;
     display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }
.pe img,.pe figure,.pe video,.pe iframe,.pe table{ display:none !important; }  /* 미디어 제거 */
.pe *{ font-size:14px !important; margin:0 !important; ... }                   /* 평탄화 */

상세 모드에서는 이걸 전부 해제합니다 (body.detail .pe{max-height:none !important;...}).
미리보기 줄 수를 바꾸려면 -webkit-line-clamp 값과 max-height(줄수 × line-height)를 함께 수정하세요.


5. !important 지대 — Tistory 덮어쓰기 레이어

⑥~⑦ 섹션은 거의 모든 선언에 !important가 붙어 있습니다. 버그가 아니라 의도입니다.
Tistory가 런타임에 자체 CSS와 인라인 스타일을 주입하기 때문에, 이를 이기려면 필요합니다.

주요 덮어쓰기 대상:

대상 처리
공감/구독/프로필 위젯 display:none (스킨 자체 디자인으로 대체)
another_category (카테고리의 다른 글) 테이블 → 이전 글/다음 글 2칸 카드 그리드로 변환. tr:first-child::before{content:'← 이전 글'} 같은 가상요소로 라벨링
첨부파일 블록 기본 아이콘 숨기고 확장자 칩 + DOWNLOAD 텍스트 링크로 재구성
본문 표 border-collapse + 변수 기반 보더로 통일
댓글 React 앱 [class*="tt_"] 셀렉터로 폰트/색만 덮어씀 (DOM은 건드리지 않음)

여기를 수정할 때 주의: 셀렉터가 div[class*="another_category"]처럼 부분 일치인 이유는
Tistory가 클래스명을 바꾸는 경우가 있어서입니다. 정확한 클래스명에 의존하지 마세요.


6. JS 모듈별 가이드

전부 IIFE(즉시실행함수)로 독립되어 있어 하나가 죽어도 나머지는 동작합니다.
중요 로직은 try/catch로 감싸고, 실패 시 console.warn('[hyos] ...')을 남깁니다.

(b) 첨부파일 재구성

  • .tt_attachment 등에서 파일명/크기/링크를 파싱
  • 확장자를 추출해 <span class="file-ext-ico" data-ext="pdf"> 칩 삽입
  • 새 확장자 색을 추가하려면: CSS의 .file-ext-ico[data-ext="..."] 규칙 한 줄 추가

(c) 페이지네이션 재구성

  • 서버가 렌더한 번호 링크에서 현재 페이지(href 없는 항목), 전체 페이지, URL 패턴을 수집
  • ‹ 1 … 4 5 6 … 23 › 형태로 다시 그림
  • 윈도우 크기 변경: var win = 2 (현재 ±2) 값 수정
  • 변수 미치환([##_ 잔존) 시 페이지네이션 전체 숨김 — raw 텍스트 노출 방지

(d) 페이지 타입 감지 블록 (메인)

가장 큰 블록. 내부 순서가 중요합니다:

1. body_id로 상세/목록 판별 → body.detail 토글
2. 상세면: "← 목록으로" 링크 삽입
3. 사이드바 카테고리 재구성  ← try/catch (실패해도 원본 노출)
4. 글 태그 정리: '#' 멀티 태그 분리 → 칩 재구성
   └ 폴백 사다리: s_tag_label 결과 → 본문 /tag/ 링크 → 페이지 JSON
     → 모바일 페이지(/m/...) fetch → 전부 실패 시 숨김
5. author-box / atags 중복 제거
6. 사이드바 태그 클라우드 정리 (멀티 태그 분리 동일 적용)

태그 폴백은 0ms / 800ms / 2500ms 3회 재시도합니다 — Tistory React가
비동기로 마운트되기 때문. 이 패턴은 React 영역을 읽는 모든 코드에 필요합니다.

(e) 다크모드

document.getElementById('b').classList.toggle('dk', dk);   // className= 금지!
localStorage.setItem('hyos-dark', dk ? '1' : '0');
  • classList.toggle을 쓰는 이유: className =으로 덮어쓰면 Tistory의
    tt-body-page 클래스가 날아가 레이아웃이 깨집니다 (실제 겪은 버그)
  • 새로고침 시 localStorage에서 복원

(f) 스크롤 진행 바

헤더 위 2px 바(#prog)의 너비를 스크롤 비율로 갱신. 필요 없으면
#prog 요소와 마지막 scroll 리스너만 지우면 됩니다.


7. 공통 패턴 (새 기능 추가 시 따라할 것)

패턴 1 — 미치환 변수 가드

if (/\[##_/.test(el.innerHTML)) { /* 변수 미처리 → 폴백 or 숨김 */ }

모든 동적 영역의 첫 줄. raw 텍스트가 사용자에게 노출되는 것을 막습니다.

패턴 2 — 폴백 사다리

정식 치환자 → DOM 정규화 → 대체 소스(RSS, /m/ 페이지) → 깔끔하게 숨김

어느 단계에서 성공하든 결과 UI는 동일하게. 전부 실패하면 빈 껍데기 대신 섹션 자체를 숨깁니다.

패턴 3 — 비동기 재시도

if (!run()) {
  setTimeout(function(){ run(); }, 800);
  setTimeout(function(){ run(); }, 2500);
}

Tistory React 마운트를 기다리는 표준 방법. MutationObserver보다 단순하고 충분합니다.

패턴 4 — 네임스페이스

  • CSS 클래스: hyos-file, hyos-cmt 처럼 접두사 사용 (Tistory 클래스와 충돌 방지)
  • 콘솔 로그: console.log('[hyos] ...') — 필터링 가능
  • localStorage 키: hyos-dark, hyosTagCache_v1 — 버전 붙여서 마이그레이션 대비

8. 커스터마이징 빠른 레시피

하고 싶은 것 방법
포인트 컬러 변경 :rootbody.dk--ac, --ac-a 수정
미리보기 3줄로 .pe-webkit-line-clamp:3 + max-height 재계산
사이드바 위젯 순서 변경 <aside class="sb"> 안의 .ss 블록 순서 바꾸기
사이드바 위젯 제거 해당 .ss 블록 삭제 (JS는 요소 없으면 자동 skip)
페이지네이션 윈도우 JS의 var win = 2 수정
확장자 칩 색 추가 .file-ext-ico[data-ext="확장자"]{background:...} 추가
다크모드 기본값 ON (e) 복원 IIFE에서 localStorage 체크 앞에 dk=true;... 분기 추가
명함(author-box) 수정 class="author-box" 블록 — 이름/소개/이니셜은 플레이스홀더 검색
댓글 영역 스타일 #hyos-cmt 하위 CSS — DOM 구조는 Tistory React 소유, CSS만 건드릴 것

9. 수정 시 지켜야 할 것 (안 깨뜨리는 법)

  1. <s_...> 블록 구조를 옮기지 마세요. 치환자는 자기 블록 안에서만 동작합니다.
    특히 <s_tag_label>, <s_random_tags>(반복 단위 주의), <s_paging_rep>.
  2. href="..."로 감싸면 안 됩니다. 속성 전체를 출력하는 변수입니다.
  3. body 클래스는 classList로만 조작하세요.
  4. React 마운트 포인트(data-tistory-react-app) 내부 DOM을 JS로 수정하지 마세요.
    리렌더 시 덮어써집니다. CSS 덮어쓰기만 허용.
  5. JS 추가는 새 IIFE로. 기존 블록에 끼워 넣지 말고 독립 블록 + try/catch로 추가하면
    실패해도 다른 기능이 살아 있습니다.
  6. 수정 후에는 5종 페이지를 모두 확인: 홈 / 글 상세 / 카테고리 / 태그 검색 / 마지막 페이지(글 1개).

 

깃허브로 따로 관리를 하고있지 않아서 파일로 전달 드리니 참고바랍니다~