GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

댓글 ui 9가지 — 대댓글 깊이부터 삭제 뒤 빈 자리까지 계산하는 목록

댓글 ui는 대댓글이 몇 단계 깊이인지, 지워진 칸의 높이가 얼마인지, 고정된 뒤 얼마나 시간이 지났는지를 계산해 화면에 반영하는 댓글 목록이다. 지워진 댓글 칸이 차지했던 픽셀 값을 그대로 이어받아 아래 댓글들이 그만큼만 올라가고, 답글 들여쓰기는 몇 단계 깊이인지에서, 고정 댓글의

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

댓글 ui 9가지 — 깊이·높이·시간을 계산해 움직이는 댓글 목록

01 대댓글 인덴트는 답글이 화면에 나타나는 순간 깊이부터 결정하고, 02 접기·펼치기는 그 답글 무더기를 열고 닫는 동작을, 03 정렬 전환은 목록 전체의 순서를, 04 좋아요 카운트 팝은 한 줄 안의 숫자를 바꾼다. 05 스켈레톤 로딩은 이 목록이 뜨기 전 단계를, 06 고정 댓글 강조와 08 새 댓글 구분선은 시간이 지나면서 달라지는 표시를 맡고, 07 삭제 시프트는 줄어드는 순간을, 09 히어로 패널이 이 계산들을 한 화면에 모은다. 깊이 → 펼침 → 정렬 → 반응 → 로딩 → 시간 → 삭제 → 완성 순으로 훑으면 댓글 목록 하나가 실제로 움직이는 순서와 같다.

패턴별 코드

아홉 항목 각각 깊이·시간·높이를 계산하는 핵심 구간만 발췌했고, 나머지 마크업·스타일은 데모 아래 압축 파일 안에 통째로 들어 있다. 댓글 작성자 이름과 본문은 전부 데모용 가짜 값이고, 실제 서버와는 통신하지 않는다.

01깊이 단계를 계산해 들여쓰기 폭을 정하는 대댓글 인덴트

각 답글의 data-depth 값에 REPLY_INDENT_STEP_PX 를 곱해 들여쓰기 폭을 CSS 변수로 계산하고, 답글 그룹을 펼칠 때는 세로 안내선이 scaleY(0→1) 로 위에서 아래로 그려지며 부모 댓글과 이어진다. 블로그 글 아래 대댓글 스레드처럼 깊이가 여러 단계로 갈라지는 목록에 쓴다. 01 항목은 누적면적 5.2%·강도 평균 65.5·23프레임 중 11프레임에서 움직였다.

깊이 단계를 계산해 들여쓰기 폭을 정하는 대댓글 인덴트
새 창에서 열기

01 · 깊이 단계를 계산해 들여쓰기 폭을 정하는 대댓글 인덴트 — 측정 누적면적 5.21% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

REPLY_INDENT_STEP_PXcmt__guideopenGroup
  var REPLY_INDENT_STEP_PX = 24;

  var root = document.getElementById('cmtRoot');
  var list = document.getElementById('cmtList');
  var status = document.getElementById('cmtStatus');

  var items = list.querySelectorAll('.cmt__item[data-depth]');
  for (var i = 0; i < items.length; i += 1) {
    var depth = parseInt(items[i].getAttribute('data-depth'), 10) || 0;
    items[i].style.setProperty('--depth-indent', (depth * REPLY_INDENT_STEP_PX) + 'px');
  }

  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

02실측한 높이까지 스프링으로 늘어나는 답글 접기·펼치기

펼칠 답글들의 실제 높이를 measure() 로 재서 목표 값으로 삼고, 패널은 SPRING_SETTLE_EASING 곡선으로 그 값까지 늘어나며 살짝 넘쳤다가 멎는 동안 화살표 아이콘과 카드 배경 전체에도 같은 구간에서 옅은 변화가 겹쳐진다. 긴 답글 스레드를 접어 두었다가 필요할 때만 펼치는 토글에 쓴다. 02 항목은 누적면적 74.9%·강도 평균 26.6·23프레임 중 8프레임에서 움직였다(패널 확장에 배경 펄스까지 겹쳐 면적이 크게 잡힌다).

실측한 높이까지 스프링으로 늘어나는 답글 접기·펼치기
새 창에서 열기

02 · 실측한 높이까지 스프링으로 늘어나는 답글 접기·펼치기 — 측정 누적면적 74.878% · 움직인 프레임 8/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

SPRING_SETTLE_EASINGch__panelsetOpen
  function setOpen(next, animate, delayMs) {
    open = next;
    toggle.setAttribute('aria-expanded', String(open));
    label.textContent = open ? '답글 숨기기' : '답글 3개 보기';
    status.textContent = open ? '답글 3개가 펼쳐졌습니다.' : '답글이 접혔습니다.';
    if (panelAnim) panelAnim.cancel();
    if (innerAnim) innerAnim.cancel();
    if (iconAnim) iconAnim.cancel();
    if (bgAnim) bgAnim.cancel();

    var target = measure();
    var from = open ? 0 : target;
    var to = open ? target : 0;
    panel.style.height = from + 'px';
    inner.style.opacity = open ? '0' : '1';

    if (!animate || reduce) {
      panel.style.height = to + 'px';
      inner.style.opacity = open ? '1' : '0';
      icon.style.transform = 'rotate(' + (open ? 180 : 0) + 'deg)';
      return;
    }
    // 계산: 패널이 실측한 픽셀 높이가 클수록 늘어나는 데 걸리는 시간도 조금 더 길게 잡는다.
    var dur = Math.max(320, Math.min(680, 300 + target * 0.9));
    panelAnim = panel.animate(

03이전 위치와의 거리를 계산해 미끄러지는 정렬 전환

정렬 기준을 바꾸면 각 댓글의 이전 위치를 getBoundingClientRect 로 기록해 두고 새 순서로 DOM 을 옮긴 뒤, 두 위치의 차이만큼 transform 으로 되돌렸다가 0 으로 애니메이션하는 FLIP 계산을 쓴다. 인기순·최신순을 오가는 전환 버튼에 붙인다. 03 항목은 누적면적 21.9%·강도 평균 82.1·23프레임 중 7프레임에서 움직였다.

이전 위치와의 거리를 계산해 미끄러지는 정렬 전환
새 창에서 열기

03 · 이전 위치와의 거리를 계산해 미끄러지는 정렬 전환 — 측정 누적면적 21.874% · 움직인 프레임 7/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

SORT_FLIP_DURATION_MSsrt__itemapplySort
    if (!animate || reduce) return;

    for (var m = 0; m < order.length; m += 1) {
      var el = byId[order[m]];
      var after = el.getBoundingClientRect();
      var b = before[order[m]];
      var dy = b.top - after.top;
      if (Math.abs(dy) < 0.5) continue;
      var sideX = (m % 2 === 0 ? 1 : -1) * Math.min(14, Math.abs(dy) * 0.12);
      el.style.zIndex = String(10 - m);
      el.animate(
        [
          { transform: 'translate(0px,' + dy + 'px)' },
          { transform: 'translate(' + sideX + 'px,' + (dy * 0.4) + 'px)' },
          { transform: 'translate(0px,0px)' }
        ],
        { duration: SORT_FLIP_DURATION_MS, delay: delayMs || 0, easing: 'cubic-bezier(.2,.7,.2,1)', fill: 'both' }
      );
    }
  }

04이전 값과의 차이를 굴려 올리는 좋아요 카운트 팝

좋아요를 누르면 이전 숫자와 새 숫자의 차이를 diff 로 계산해 그 차이가 있는 자리 값만 rollDigit 으로 굴려 올리고, 버튼은 탄성 계수만큼 커졌다가 줄어든다. 댓글 좋아요 버튼처럼 숫자가 자주 바뀌는 자리에 쓴다. 04 항목은 누적면적 24.6%·강도 평균 38.4·23프레임 중 8프레임에서 움직였다.

이전 값과의 차이를 굴려 올리는 좋아요 카운트 팝
새 창에서 열기

04 · 이전 값과의 차이를 굴려 올리는 좋아요 카운트 팝 — 측정 누적면적 24.637% · 움직인 프레임 8/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

lk__digitTracklk__digitColrollDigit
  function rollDigit(col, newCh, diff, animate, delayMs) {
    var track = col.querySelector('.lk__digitTrack');
    var cur = track.querySelector('.lk__digit');
    if (cur.textContent === newCh) return;
    var next = document.createElement('span');
    next.className = 'lk__digit';
    next.textContent = newCh;
    next.style.transform = 'translateY(' + (diff > 0 ? '100%' : '-100%') + ')';
    track.appendChild(next);

    if (!animate || reduce) {
      cur.remove();
      next.style.transform = 'translateY(0)';
      return;
    }
    var dur = 260 + Math.min(200, Math.abs(diff) * 80);
    cur.animate(
      [{ transform: 'translateY(0)' }, { transform: 'translateY(' + (diff > 0 ? '-100%' : '100%') + ')' }],
      { duration: dur, delay: delayMs || 0, easing: 'cubic-bezier(.3,.7,.2,1)', fill: 'forwards' }
    );
    var anim = next.animate(
      [{ transform: 'translateY(' + (diff > 0 ? '100%' : '-100%') + ')' }, { transform: 'translateY(0)' }],
      { duration: dur, delay: delayMs || 0, easing: 'cubic-bezier(.3,.7,.2,1)', fill: 'forwards' }
    );

05줄 순서를 계산해 시차를 두고 흐르는 스켈레톤 로딩

댓글을 불러오는 동안 각 행의 순서를 rowsInOrder 로 계산해 반짝이는 결이 SHIMMER_STAGGER_STEP_MS 씩 밀려 위에서 아래로 흐르고, 실제 댓글이 도착한 행은 그 자리에서 바로 교차 페이드한다. 댓글 목록을 처음 불러올 때의 로딩 자리표시자로 쓴다. 05 항목은 누적면적 12.3%·강도 평균 36.6·23프레임 중 14프레임에서 움직였다.

줄 순서를 계산해 시차를 두고 흐르는 스켈레톤 로딩
새 창에서 열기

05 · 줄 순서를 계산해 시차를 두고 흐르는 스켈레톤 로딩 — 측정 누적면적 12.333% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

SHIMMER_STAGGER_STEP_MSskel__glarerowsInOrder
    anim.onfinish = function () { item.classList.add('is-real'); };
  }

  function runSequence(animate) {
    var rows = rowsInOrder();
    for (var i = 0; i < rows.length; i += 1) {
      resetRow(rows[i]);
      if (!animate || reduce) {
        rows[i].classList.add('is-real');
        continue;
      }
      // glare 무한 스윕 — 행 순서로 stagger.
      var glareDelay = SHIMMER_BASE_DELAY_MS + i * SHIMMER_STAGGER_STEP_MS;
      rows[i].querySelectorAll('.skel__glare').forEach(function (glare) {
        glare.animate(
          [{ transform: 'translateX(-220%)' }, { transform: 'translateX(340%)' }],
          { duration: 1300, delay: glareDelay, iterations: Infinity, easing: 'linear' }
        );
      });

06고정 뒤 지난 시간만큼 옅어지는 고정 댓글 강조

고정된 시점부터 지난 시간을 performance.now 가상 시계로 계산해 강조 밝기가 decayAt 곡선을 따라 옅어지고, DECAY_DURATION_MS 를 넘으면 은은한 색으로 고정돼 더는 변하지 않는다. 작성자가 고정한 댓글을 표시하는 자리에 쓴다. 06 항목은 누적면적 19.1%·강도 평균 15.8·23프레임 중 11프레임에서 움직였다.

고정 뒤 지난 시간만큼 옅어지는 고정 댓글 강조
새 창에서 열기

06 · 고정 뒤 지난 시간만큼 옅어지는 고정 댓글 강조 — 측정 누적면적 19.131% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

DECAY_DURATION_MSpin__decayFilldecayAt
  function decayAt(elapsedMs) {
    if (elapsedMs <= 0) return PEAK_GLOW;
    if (elapsedMs >= DECAY_DURATION_MS) return SETTLED_GLOW;
    var t = elapsedMs / DECAY_DURATION_MS;
    // 감쇠 곡선: 빠르게 시작해 천천히 수렴(지수 감쇠를 0..1 구간으로 정규화).
    var eased = 1 - Math.exp(-4.2 * t) / (1 - Math.exp(-4.2));
    return PEAK_GLOW + (SETTLED_GLOW - PEAK_GLOW) * eased;
  }

  function applyDecay(glow, progress) {
    item.style.setProperty('--pin-glow', String(glow));
    fill.style.transform = 'scaleX(' + Math.max(0, progress) + ')';
  }

  var rafId = 0;
  var start = 0;

  function step(now) {
    if (!start) start = now;
    var elapsedVirtual = (now - start) * VIRTUAL_CLOCK_SCALE;
    var elapsed = elapsedVirtual - START_DELAY_MS;
    if (elapsed < 0) elapsed = 0;
    applyDecay(decayAt(elapsed), elapsed / DECAY_DURATION_MS);

07지워진 칸의 높이만큼 아래 댓글이 밀려 올라가는 삭제 시프트

댓글을 지우면 그 행의 실측 높이를 0 으로 접으며 페이드시키고, 아래 행들은 삭제 전후 위치 차이를 shiftDuration 으로 계산한 스프링 이징으로 메운다. 본인이 쓴 댓글을 지우는 버튼에 붙인다. 07 항목은 누적면적 31.4%·강도 평균 94.3·23프레임 중 11프레임에서 움직였다.

지워진 칸의 높이만큼 아래 댓글이 밀려 올라가는 삭제 시프트
새 창에서 열기

07 · 지워진 칸의 높이만큼 아래 댓글이 밀려 올라가는 삭제 시프트 — 측정 누적면적 31.415% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

DELETE_SHIFT_SPRING_STIFFNESSshiftDurationremoveRow
  function shiftDuration(gapPx) {
    // 강성이 높을수록 빨리 멎는다. 이동 거리가 커도 시간이 살짝 늘어난다.
    return Math.round(60 + 100000 / DELETE_SHIFT_SPRING_STIFFNESS + Math.min(240, gapPx * 0.55));
  }

  function itemsAfter(id) {
    var all = list.querySelectorAll('.del__item');
    var seen = false;
    var after = [];
    for (var i = 0; i < all.length; i += 1) {
      if (all[i].getAttribute('data-id') === id) { seen = true; continue; }
      if (seen && all[i].getBoundingClientRect().height > 0) after.push(all[i]);
    }
    return after;
  }

08마지막 방문 시각과 비교해 자리를 계산하는 새 댓글 구분선

마지막 방문 시각(분 단위 가상값)과 각 댓글의 작성 시각을 findUnreadIndex 로 비교해 처음 나타나는 새 댓글의 위치를 찾고, 그 자리에 구분선을 createElement 로 만들어 위에서 살짝 내려와 자리 잡는다. 다시 방문했을 때 새 댓글만 표시하는 자리에 쓴다. 08 항목은 누적면적 19.9%·강도 평균 36.2·23프레임 중 6프레임에서 움직였다.

마지막 방문 시각과 비교해 자리를 계산하는 새 댓글 구분선
새 창에서 열기

08 · 마지막 방문 시각과 비교해 자리를 계산하는 새 댓글 구분선 — 측정 누적면적 19.872% · 움직인 프레임 6/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

LAST_VISIT_MINUTES_AGOun__dividerfindUnreadIndex
  function findUnreadIndex() {
    var items = list.querySelectorAll('.un__item');
    for (var i = 0; i < items.length; i += 1) {
      if (minAgoOf(items[i]) < LAST_VISIT_MINUTES_AGO) return i;
    }
    return -1;
  }

  function insertDivider(animate) {
    var idx = findUnreadIndex();
    if (idx < 0) {
      status.textContent = '새 댓글이 없습니다.';
      return;
    }
    var items = list.querySelectorAll('.un__item');
    var firstUnread = items[idx];

    var divider = document.createElement('li');
    divider.className = 'un__divider';
    divider.setAttribute('role', 'listitem');
    var dividerLabel = document.createElement('span');
    dividerLabel.className = 'un__dividerLabel';
    dividerLabel.textContent = '여기부터 새 댓글';
    divider.appendChild(dividerLabel);
    list.insertBefore(divider, firstUnread);

09여러 계산을 한 패널에 모은 댓글 목록 히어로 장면

패널 하나에 답글 깊이만큼 들여쓰는 applyIndents, 좋아요를 누르면 숫자가 스치듯 바뀌는 like(), 지운 자리를 스프링으로 메우는 removeRow 세 계산을 동시에 얹어 답글을 펼치고 좋아요를 누르고 삭제하는 장면을 한 번에 보여준다. 블로그 글 상세 페이지의 댓글 섹션 완성형 데모로 쓴다. 09 항목은 누적면적 25.1%·강도 평균 77.4·23프레임 중 15프레임에서 움직였다.

여러 계산을 한 패널에 모은 댓글 목록 히어로 장면
새 창에서 열기

09 · 여러 계산을 한 패널에 모은 댓글 목록 히어로 장면 — 측정 누적면적 25.116% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

HERO_REPLY_INDENT_STEP_PXHERO_DELETE_SHIFT_SPRING_STIFFNESShero__guide
  var HERO_REPLY_INDENT_STEP_PX = 20;
  var HERO_DELETE_SHIFT_SPRING_STIFFNESS = 220;
  var HERO_LIKE_POP_AMPLITUDE = 1.16;

  // $ease-lerp-settle 곡선(site/static/demo-tokens.scss)과 같은 샘플 — 1 을 넘겼다가(최대 1.126) 되독아온다.
  var SPRING_SETTLE_EASING = 'linear(0.0, 0.051, 0.176, 0.341, 0.518, 0.685, 0.831, 0.947, 1.032, 1.087, 1.117, 1.126, 1.121, 1.105, 1.085, 1.062, 1.041, 1.022, 1.008, 0.997, 1)';

  var root = document.getElementById('heroRoot');
  var list = document.getElementById('heroList');
  var status = document.getElementById('heroStatus');

  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  if (reduce) root.classList.remove('is-demo');

  // ① 들여쓰기 계산
  function applyIndents() {
    var rows = list.querySelectorAll('.hero__cmt');
    for (var i = 0; i < rows.length; i += 1) {
      var depth = parseInt(rows[i].getAttribute('data-depth'), 10);
      rows[i].style.marginLeft = (depth * HERO_REPLY_INDENT_STEP_PX) + 'px';
    }
  }

값이 반응을 바꾸는 곳 — 들여쓰기 폭·정렬 전환 속도·삭제 뒤 밀려 올라가는 속도

댓글 목록마다 들여쓰기가 빡빡하거나 삭제 뒤 화면이 출렁이는 정도가 다르게 느껴지는 이유는 항목 개수가 아니라 이 표의 세 수치 차이다.

항목 누적면적 % 강도 평균 움직인 프레임
01 대댓글 인덴트 5.2% 65.5 11/23
02 답글 접기·펼치기 74.9% 26.6 8/23
03 정렬 전환 21.9% 82.1 7/23
04 좋아요 카운트 팝 24.6% 38.4 8/23
05 스켈레톤 로딩 12.3% 36.6 14/23
06 고정 댓글 강조 19.1% 15.8 11/23
07 삭제 시프트 31.4% 94.3 11/23
08 새 댓글 구분선 19.9% 36.2 6/23
09 히어로 패널 25.1% 77.4 15/23

01 처럼 화면 대부분이 고정된 채 들여쓰기 폭 하나만 계산하는 항목은 원래 움직이는 조각이 작아 누적면적이 낮게 나오는 게 정상이고, 02 는 패널이 배경째로 펼쳐져 다른 항목보다 넓게 잡힌다. REPLY_INDENT_STEP_PX 는 답글 한 단계마다 들여쓰기가 몇 px 씩 늘어나는지를, SORT_FLIP_DURATION_MS 는 정렬이 바뀔 때 댓글이 새 자리로 옮겨가는 데 걸리는 시간을, DELETE_SHIFT_SPRING_STIFFNESS 는 삭제된 자리를 메우는 스프링이 얼마나 뻣뻣한지를 정한다. 셋 다 변형 표에서 값을 바꿔보면 댓글 목록이 받는 느낌이 얼마나 달라지는지 바로 잡힌다.

어디서 깨지는가 — 깊은 대댓글 오버플로우·정렬 중 깜빡임·삭제 직후 포커스 이동

01 처럼 data-depth × REPLY_INDENT_STEP_PX 로 들여쓰기 폭을 계산하는 방식은 깊이 제한이 코드 어디에도 없어서, 답글에 답글이 몇 단계 더 달리면 그만큼 폭이 계속 늘어난다 — 모바일 댓글 ui 처럼 화면이 좁으면 대여섯 단계만 지나도 본문 칸이 옆으로 밀려 잘려 보인다. 03 은 정렬 버튼을 두 번 빠르게 눌러 이전 applySort 애니메이션이 끝나기 전에 새 getBoundingClientRect 계산이 겹치면, 이전 이동이 마저 진행되는 위에 새 이동이 얹혀 순간적으로 위치가 튀어 보일 수 있다 — 실무에서는 진행 중인 애니메이션을 cancel() 한 뒤 다시 재는 편이 안전하다. 07 은 삭제 버튼을 눌러 그 버튼이 사라진 뒤 포커스를 어디로 옮길지 지정하는 코드가 없어, 키보드로 삭제한 직후 포커스가 body 로 빠진다 — 다음으로 살아 있는 댓글의 삭제 버튼이나 목록 컨테이너로 포커스를 옮기는 코드를 붙여야 스크린 리더 사용자가 지금 어디에 있는지 놓치지 않는다.

이 아홉 개를 실무 댓글 목록에 붙일 때는 이런 깊이·타이밍 경합까지 같이 신경 써야 틈이 줄어든다. 깊이·타이밍 계산이 들어간 아홉 개 소스를 내려받아 열 때 입력하는 비밀번호는 m2uzmqnz 이다.

변형 세 가지와 접근성

항목 상수 값 느낌
01 REPLY_INDENT_STEP_PX 24 → 12 값을 줄이면 깊은 답글도 좁게 접혀 화면 안에 들어오고, 값을 늘리면 몇 단계만 지나도 옆으로 밀려나 잘린다
03 SORT_FLIP_DURATION_MS 320 → 640 값을 줄이면 정렬 기준을 바꿀 때 댓글들이 거의 순간 이동하듯 자리를 바꾸고, 값을 늘리면 서로 스치듯 천천히 지나가며 자리를 바꾼다
07 DELETE_SHIFT_SPRING_STIFFNESS 220 → 120 값을 늘리면 댓글을 지운 자리가 빠르고 뻣뻣하게 메워지고, 값을 줄이면 출렁이듯 천천히 흔들리며 메워진다

prefers-reduced-motion: reduce 분기는 matchMedia 로 아홉 항목 전부에 있어, 동작 줄임 설정을 켠 방문자에게는 자동 재생 없이 목표 상태(펼친 답글·정렬 완료·고정 후 옅어진 색 등)로 바로 건너뛴다. 키보드 경로는 01·02·03·04·05·07·09 일곱 항목에 keydown 리스너가 있어 각 컨트롤을 Enter·Space 로 조작할 수 있고, 06 고정 댓글 강조와 08 새 댓글 구분선은 사용자가 직접 누르는 컨트롤이 없어 키보드 경로 자체가 필요하지 않다.

댓글 ui는 라이브러리 없이 정렬과 삭제까지 계산할 수 있는가?

가능하다 — 이 아홉 항목 전부가 Element.animate 와 좌표·시간 계산만으로 들여쓰기·펼침·정렬·삭제를 서로 맞춰 돌린다. 게시판 댓글 ui 처럼 여러 계산을 한 화면에 붙여야 한다면 09 의 패널 구성처럼 01(깊이)·04(반응)·07(삭제) 세 계산만 겹쳐도 댓글 섹션 하나의 완성 장면이 된다. 활동 이력을 시간순으로 쌓아 보여줘야 하는 자리라면 알림 목록 ui 9가지 — 사이드바에 박히는 활동 피드와 이력 위젯 의 시간 표시와 비교해 보는 편이 낫고, 랭킹처럼 순서가 자주 바뀌는 목록이라면 랭킹 ui 디자인 9가지 — 랭킹 페이지 디자인도 같은 원리다 의 정렬 전환과 구조가 닮았다. 말풍선 형태로 주고받는 대화라면 채팅 말풍선 CSS 애니메이션 9종 — 한 줄 복붙으로 쪽과 함께 보면 좋다.

댓글 ui 디자인을 넣기 전에 자주 묻는 질문

대댓글이 깊어지면 들여쓰기 폭을 어떻게 줄이나요?

REPLY_INDENT_STEP_PX 값을 12 정도로 낮추면 답글이 몇 단계를 내려가도 폭이 완만하게 늘어나 화면 밖으로 밀려나는 일이 줄어든다. 값 하나만 바꾸는 방식이라 변형 표의 01 항목에서 그대로 확인할 수 있다.

댓글 창 ui 를 스마트폰 화면에도 그대로 쓸 수 있나요?

가능하다 — 아홉 항목 모두 px 계산과 matchMedia 분기만 쓰고 화면 크기를 직접 읽는 코드가 없어, 좁은 화면에서는 01 의 들여쓰기 폭만 변형 표 값으로 줄여 맞추면 된다.

정렬 기준을 최신순·인기순 말고 다른 기준으로도 늘릴 수 있나요?

sortedIds() 안의 비교 함수만 바꾸면 된다 — 지금은 좋아요 수(data-likes)로 비교하는데, 다른 데이터 속성으로 비교식을 바꿔도 FLIP 계산 자체는 그대로 재사용된다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.