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

팀 소개 페이지 UI 9종 — 인원수·소개글 길이·시차까지 계산해서 보여주는

팀원이 몇 명이든 카드 줄이 어긋나지 않게 열 수를 계산하고, 소개글이 길면 재서 더보기를 붙이고, 파트너 로고는 개수와 무관하게 같은 속도로 흐르게 만드는 팀 소개 페이지 9종입니다.

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

데모

아홉 섹션은 전부 팀 데이터(인원수·역할·소개글 길이·시간대) 를 받아 화면 값을 그 자리에서 계산합니다. 사람이 건드리지 않아도 정해진 시나리오가 돌면서 인원이 늘고 줄고, 탭이 바뀌고, 마감이 다가오는 상황을 반복 재생합니다. 측정은 480×300 무대에서 프레임 24장(움직임은 그 사이 델타 23개로 잡습니다) 을 기준으로 했습니다.

실측 한눈에

항목 초점 누적면적(%) 강도평균 움직인프레임(23 기준)
01 팀 히어로 인원수로 등장 지연 압축 11.245 54.2 12
02 팀원 그리드 나머지로 마지막 줄 재배치 29.079 53.6 8
03 역할 탭 filter 인원수 + 밑줄 좌표 2.572 117.4 7
04 프로필 상세 scrollHeight 비교 판정 17.345 55.9 6
05 팀 스탯 줄 이징 카운트업 1.517 160.4 20
06 파트너 로고 바 실측 폭으로 속도 역산 7.333 96.6 23
07 팀 인용 카드 line-height 역산 19.911 69.0 6
08 지원 CTA Date 로 D-day 계산 37.419 63.0 10
09 팀 푸터 Intl 로 팀원별 시각 1.403 104.1 8

면적이 작다고 부실한 건 아니다

08 지원 CTA 는 배지 색이 통째로 바뀌는 구간이 있어 누적면적이 37.42% 로 가장 크고, 09 팀 푸터는 시계 문자열 한 칸만 바뀌어 1.40% 로 가장 작습니다. 03 역할 탭도 밑줄 하나가 옮겨 가는 구조라 2.57% 로 낮은데, 이건 탭 밑줄·시각 숫자·푸터 텍스트처럼 원래 차지하는 면적이 작은 대상이라 자연스러운 결과입니다. 06 파트너 로고 바는 23/23 프레임이 전부 움직여 화면이 쉬는 순간이 없고, 04 프로필 상세와 07 팀 인용 카드는 다음 시나리오를 기다리는 정지 구간이 있어 움직인 프레임이 6으로 적게 나옵니다.

팀 소개 페이지의 해부 — 아홉 섹션이 계산하는 것

팀 소개 웹 페이지는 대체로 인물 사진을 늘어놓는 정적인 화면으로 만들어지지만, 이 아홉 섹션은 전부 값 하나를 계산해 그 계산 결과로 레이아웃이나 표시를 바꿉니다. 속성 하나를 켜고 끄는 트랜지션이 아니라, 인원수·문자 길이·픽셀 폭·경과 시간 같은 숫자를 읽어 다음 화면을 그때그때 다시 정합니다.

계산 한 겹이 하는 일

01 은 인덱스를 인원수로 나눈 나머지로 등장 간격을 다시 압축하고, 02 는 인원수를 열 수로 나눈 나머지로 마지막 줄 배치를 바꿉니다. 05 는 경과 시간 비율을 이징 함수에 넣어 카운트업 숫자를 매 프레임 다시 계산하고, 06 은 트랙의 실측 폭을 목표 속도로 나눠 재생시간을 역산합니다. 04·07 은 반대로 화면에 있는 요소의 높이·줄 수를 측정해 버튼 유무나 글자 크기를 정하고, 08·09 는 Date·Intl 로 시각을 계산해 배지 색과 팀원별 현지 시각을 보여줍니다.

아홉 섹션이 공유하는 값

아홉 섹션 모두 팀원 배열 하나(이름·역할·소개글·시간대) 를 입력으로 받는다는 점이 같습니다. 배열의 길이가 바로 01·02 의 인원수이고, 배열 각 항목의 role 이 03 의 필터 기준이며, bio 문자열의 길이가 04·07 의 측정 대상입니다.

섹션별 코드

아홉 개 섹션의 계산 부분만 옮겨 왔습니다. 전체 마크업과 스타일은 압축 파일에 들어 있습니다.

01팀 히어로 — 인원수로 등장 지연을 계산해 순서대로 나타나는 사진 줄

채용 페이지 상단이나 스타트업 소개 첫 화면에 쓰는 형태입니다. IntersectionObserver 로 섹션이 화면에 들어온 시점 하나만 기준을 잡고, 각 사진 인덱스에 고정 간격을 곱한 값을 --delay 변수로 넘겨 순서대로 나타나게 합니다.

인원수로 나눈 나머지로 간격을 다시 압축한다

인원이 늘어도 총 등장 시간이 무한히 늘어나지 않도록, 인덱스를 그 순간의 인원수로 나눈 나머지에 예산을 나눈 step 을 곱해 간격을 매번 다시 계산합니다.

  var counts = [5, 7, 4, 6];
  var idx = 0;

  // 등장 간격 계산 — 예산을 이번 인원수로 나눈 몫이 step, 인덱스를 인원수로 나눈 나머지로
  // 매 순환마다 압축 폭을 다시 맞춘다(headcount 가 바뀌어도 같은 공식 재사용)
  function layout(count) {
    var step = ENTRANCE_MS / count;
    avatars.forEach(function (el, i) {
      var visible = i < count;
      el.style.display = visible ? '' : 'none';
      if (visible) el.style.setProperty('--delay', ((i % count) * step).toFixed(1) + 'ms');
    });
    chip.textContent = 'n=' + count + ' · step ' + Math.round(step) + 'ms';
    row.classList.remove('is-in');
    void row.offsetWidth;   // 강제 리플로우 — 트랜지션 재시작
    row.classList.add('is-in');
  }
팀 히어로 — 인원수로 등장 지연을 계산해 순서대로 나타나는 사진 줄
새 창에서 열기

01 · 팀 히어로 — 인원수로 등장 지연을 계산해 순서대로 나타나는 사진 줄 — 측정 누적면적 11.245% · 움직인 프레임 12/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

IntersectionObserverCSS custom propertiesprefers-reduced-motion

02팀원 그리드 — 마지막 줄 인원을 계산해 외톨이 카드를 없애는 배치

회사 소개 전체 팀원 목록이나 부서별 인원 카드에 쓰는 형태입니다. 팀원 수를 열 수로 나눈 나머지로 마지막 줄에 몇 명이 남는지 계산하고, 나머지가 1~2명일 때만 그 줄의 열 수를 줄여 가운데로 모읍니다.

나머지가 1~2일 때만 줄을 다시 나눈다

나머지가 0이거나 3 이상이면 원래 열 수 그대로 채우고, ResizeObserver 로 화면 폭이 바뀌어 열 수 자체가 바뀔 때도 같은 계산을 다시 돌립니다.

  function layout(count) {
    var remainder = count % cols;
    var tailCount = (remainder === 1 || remainder === 2) ? remainder : 0;
    var headCount = count - tailCount;
    head.style.setProperty('--cols', String(cols));
    head.innerHTML = '';
    tail.innerHTML = '';
    for (var i = 0; i < headCount; i++) head.appendChild(card(i));
    for (var j = 0; j < tailCount; j++) tail.appendChild(card(headCount + j));
    tail.style.display = tailCount > 0 ? 'flex' : 'none';
    chip.textContent = 'n=' + count + ' · cols ' + cols + ' · 나머지 ' + tailCount;
    grid.classList.remove('is-in');
    void grid.offsetWidth;
    grid.classList.add('is-in');
  }
팀원 그리드 — 마지막 줄 인원을 계산해 외톨이 카드를 없애는 배치
새 창에서 열기

02 · 팀원 그리드 — 마지막 줄 인원을 계산해 외톨이 카드를 없애는 배치 — 측정 누적면적 29.079% · 움직인 프레임 8/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

CSS GridResizeObserverCSS custom properties

03역할 탭 — 필터링 결과 인원수를 탭 라벨에 계산해 붙이는 전환

부서별 팀원 필터나 직군별 소개 탭에 쓰는 형태입니다. 팀원 배열을 role 값으로 filter 해 각 탭 라벨 옆에 그 인원수를 실시간으로 붙이고, 선택된 탭의 getBoundingClientRect 로 밑줄 인디케이터의 좌표와 너비를 그 값으로 맞춥니다. 탭이 늘어도 이 계산 방식은 그대로입니다.

  // 인원수 계산 — role 값으로 filter 한 배열의 길이를 각 탭 라벨에 붙인다
  KEYS.forEach(function (key) {
    var span = document.getElementById('cnt-' + key);
    if (!span) return;
    var n = key === 'all' ? members.length : members.filter(function (m) { return m.role === key; }).length;
    span.textContent = String(n);
  });

  function moveIndicator(tab) {
    var barRect = bar.getBoundingClientRect();
    var tabRect = tab.getBoundingClientRect();
    var ix = tabRect.left - barRect.left;
    var iw = tabRect.width;
    indicator.style.setProperty('--ix', ix.toFixed(1) + 'px');
    indicator.style.setProperty('--iw', iw.toFixed(1) + 'px');
  }
역할 탭 — 필터링 결과 인원수를 탭 라벨에 계산해 붙이는 전환
새 창에서 열기

03 · 역할 탭 — 필터링 결과 인원수를 탭 라벨에 계산해 붙이는 전환 — 측정 누적면적 2.572% · 움직인 프레임 7/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

filtergetBoundingClientRecttransform: translateX

04프로필 상세 — 소개글 높이를 재서 더보기 버튼 유무를 정하는 카드

팀원 상세 모달이나 프로필 카드 뒷면에 쓰는 형태입니다. 소개글 요소를 clamp 한 상태의 높이와 풀었을 때 높이를 비교해 실제로 잘렸는지부터 계산하고, 차이가 없으면 더보기 버튼 자체를 렌더링하지 않습니다.

scrollHeight 두 번 재서 넘침 여부를 판정한다

짧은 소개글에 쓸모없는 버튼이 남는 실수를, 넘치는지 먼저 재고 그 결과로 버튼을 켜고 끄는 순서가 막아 줍니다.

  function measure(lineClamp) {
    bio.style.setProperty('--clamp', String(lineClamp));
    bio.classList.add('is-clamped');
    var clampedHeight = bio.scrollHeight;
    bio.classList.remove('is-clamped');
    var fullHeight = bio.scrollHeight;
    bio.classList.add('is-clamped');
    return fullHeight > clampedHeight + 1;
  }

  function apply(lineClamp) {
    var overflow = measure(lineClamp);
    more.style.display = overflow ? '' : 'none';
    more.setAttribute('aria-expanded', 'false');
    bio.classList.remove('is-open');
    chip.textContent = 'clamp ' + lineClamp + '줄 · ' + (overflow ? '넘침 O' : '넘침 X');
    if (card) card.classList.toggle('is-alt');
  }
프로필 상세 — 소개글 높이를 재서 더보기 버튼 유무를 정하는 카드
새 창에서 열기

04 · 프로필 상세 — 소개글 높이를 재서 더보기 버튼 유무를 정하는 카드 — 측정 누적면적 17.345% · 움직인 프레임 6/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

scrollHeightclassList.togglearia-expanded

05팀 스탯 줄 — 경과 시간 비율을 이징 함수에 넣어 세는 카운트업

창립 연차나 프로젝트 수를 강조하는 회사 규모 소개에 쓰는 형태입니다. requestAnimationFrame 이 매 프레임 전달하는 경과 시간을 총 지속시간으로 나눠 0~1 비율을 만들고, 이 비율을 이징 함수에 통과시킨 값에 목표 숫자를 곱해 표시할 정수를 매 프레임 다시 계산합니다.

easeOutCubic 을 거친 비율로 숫자를 매 프레임 다시 그린다

화면에 보이는지와 무관하게 스크립트가 실행되자마자 performance.now 기준으로 시작하고, 이후 값은 전부 이 한 함수의 반복 호출로 나옵니다.

  function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); }

  function runOnce(ms) {
    if (raf !== null) cancelAnimationFrame(raf);
    var start = performance.now();
    var targets = nums.map(function (el) { return parseInt(el.getAttribute('data-target'), 10) || 0; });
    function step(now) {
      var t = Math.min(1, (now - start) / ms);
      var eased = easeOutCubic(t);
      nums.forEach(function (el, i) {
        el.textContent = String(Math.round(targets[i] * eased));
      });
      if (t < 1) {
        raf = requestAnimationFrame(step);
      } else {
        raf = null;
      }
    }
    nums.forEach(function (el) { el.textContent = '0'; });
    raf = requestAnimationFrame(step);
  }
팀 스탯 줄 — 경과 시간 비율을 이징 함수에 넣어 세는 카운트업
새 창에서 열기

05 · 팀 스탯 줄 — 경과 시간 비율을 이징 함수에 넣어 세는 카운트업 — 측정 누적면적 1.517% · 움직인 프레임 20/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFrameeasing functionperformance.now

06파트너 로고 바 — 트랙 실측 폭으로 속도를 역산해 흐르는 줄

협력사 로고 슬라이드나 투자사 소개 줄에 쓰는 형태입니다. 로고를 두 번 이어붙인 트랙의 실측 폭을 목표 속도(초당 px)로 나누면 그 트랙에 맞는 재생시간이 나오고, keyframe 은 그대로 두고 이 재생시간만 매번 다시 계산해 CSS 변수에 넣습니다.

폭 ÷ 속도 = 재생시간, 로고 개수와 무관하다

로고가 3개든 12개든 트랙 폭만 그만큼 늘어날 뿐이라 같은 나눗셈으로 체감 속도가 똑같이 유지됩니다.

  var SPEEDS = [60, 30];   // 변형 06: px/s
  var speedIdx = 0;

  function apply(speed) {
    var setWidth = setA.getBoundingClientRect().width;
    if (setWidth <= 0) return;
    var durationS = setWidth / speed;
    track.style.setProperty('--dur', durationS.toFixed(2) + 's');
    chip.textContent = 'speed ' + speed + 'px/s · ' + durationS.toFixed(1) + 's/lap';
  }

  apply(SPEEDS[0]);
  window.addEventListener('resize', function () { apply(SPEEDS[speedIdx]); });
파트너 로고 바 — 트랙 실측 폭으로 속도를 역산해 흐르는 줄
새 창에서 열기

06 · 파트너 로고 바 — 트랙 실측 폭으로 속도를 역산해 흐르는 줄 — 측정 누적면적 7.333% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

getBoundingClientRectanimation-durationCSS custom properties

07팀 인용 카드 — 줄 수를 역산해 인용문 크기를 정하는 계산

팀원 후기나 채용 인터뷰 인용 카드에 쓰는 형태입니다. getComputedStyle 로 읽은 line-height 와 목표 칸 높이를 이용해, 글자 크기 곱하기 line-height 가 그 칸 높이를 넘지 않는 가장 큰 글자 크기를 거꾸로 계산합니다.

목표 칸 높이 ÷ (줄 수 × line-height 배수) 로 글자 크기를 구한다

인용문 길이로 예상 줄 수를 먼저 정하고, 그 줄 수와 line-height 배수를 곱한 값으로 칸 높이를 나눠 최대 글자 크기를 얻습니다.

  var TARGET_H = 64;   // 칸 높이 목표(px) — 이 안에 인용문이 들어가야 한다
  var MIN_FONT = 12;
  var MAX_FONT = 20;

  function lineHeightRatio() {
    var cs = getComputedStyle(textEl);
    var lh = parseFloat(cs.lineHeight);
    var fs = parseFloat(cs.fontSize) || 16;
    if (!lh || isNaN(lh)) return 1.4;
    return lh / fs;
  }

  function fitFontSize(length) {
    var ratio = lineHeightRatio();
    var estLines = length > 40 ? 3 : length > 18 ? 2 : 1;
    var maxByHeight = TARGET_H / (estLines * ratio);
    var size = Math.max(MIN_FONT, Math.min(MAX_FONT, Math.floor(maxByHeight)));
    return size;
  }
팀 인용 카드 — 줄 수를 역산해 인용문 크기를 정하는 계산
새 창에서 열기

07 · 팀 인용 카드 — 줄 수를 역산해 인용문 크기를 정하는 계산 — 측정 누적면적 19.911% · 움직인 프레임 6/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

getComputedStyleline-heightCSS custom properties

08지원 CTA — 마감일까지 남은 일수를 매번 계산해 보여주는 배지

채용 마감 배지나 지원 접수 카운트다운에 쓰는 형태입니다. 마감 Date 객체에서 기준 시각을 뺀 밀리초 차이를 하루의 밀리초 수로 나누고 Math.ceil 로 올려 남은 일수를 정수로 계산하며, 그 값이 3 이하로 떨어지면 배지 색이 급함 상태로 바뀝니다.

밀리초 차이를 하루 단위로 올림해 D-day 를 만든다

이 계산이 캡처마다 같은 결과를 내도록 실제 Date.now() 대신 고정 기준시각(EPOCH) 을 씁니다.

  var EPOCH = new Date('2026-01-01T00:00:00Z').getTime();   // 고정 기준시각
  var DAY_MS = 86400000;
  var URGENT_DAYS = 3;

  var ROLES = [
    { name: '프론트엔드 엔지니어', deadline: '2026-01-13T00:00:00Z' },
    { name: '프로덕트 디자이너', deadline: '2026-01-03T00:00:00Z' },
    { name: '백엔드 엔지니어', deadline: '2026-01-21T00:00:00Z' }
  ];
  var idx = 0;

  function daysLeft(deadlineIso) {
    var deadline = new Date(deadlineIso).getTime();
    return Math.ceil((deadline - EPOCH) / DAY_MS);
  }
지원 CTA — 마감일까지 남은 일수를 매번 계산해 보여주는 배지
새 창에서 열기

08 · 지원 CTA — 마감일까지 남은 일수를 매번 계산해 보여주는 배지 — 측정 누적면적 37.419% · 움직인 프레임 10/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

DateMath.ceilaria-live

09팀 푸터 — 팀원별 시간대를 계산해 지금 몇 시인지 보여주는 줄

리모트 팀 소개 푸터나 글로벌 팀 연락 정보에 쓰는 형태입니다. 팀원마다 저장된 IANA 시간대 문자열을 Intl.DateTimeFormat 의 timeZone 옵션에 넣어 같은 순간을 각자의 지역 시각으로 계산해 보여주고, setInterval 로 가상의 경과 시간을 95분씩 앞당겨 다시 계산해 갱신합니다.

같은 순간, 시간대만 바꿔 다시 포맷한다

계산된 시각이 심야 시간대에 들어가는 팀원에게는 조용한 표시를 얹어 지금 연락해도 되는지를 한눈에 구분합니다.

  function hourIn(zone, date) {
    var s = new Intl.DateTimeFormat('en-GB', { timeZone: zone, hour: 'numeric', hourCycle: 'h23' }).format(date);
    return parseInt(s, 10);
  }

  var elapsedMin = 0;

  function render() {
    var now = new Date(EPOCH + elapsedMin * 60000);
    MEMBERS.forEach(function (m) {
      var label = new Intl.DateTimeFormat('en-GB', { timeZone: m.zone, hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }).format(now);
      m.timeEl.textContent = label;
      m.timeEl.setAttribute('datetime', now.toISOString());
      var hour = hourIn(m.zone, now);
      var quiet = hour < 7 || hour >= 22;
      m.cell.classList.toggle('is-quiet', quiet);
    });
    chip.textContent = '경과 ' + elapsedMin + '분';
  }
팀 푸터 — 팀원별 시간대를 계산해 지금 몇 시인지 보여주는 줄
새 창에서 열기

09 · 팀 푸터 — 팀원별 시간대를 계산해 지금 몇 시인지 보여주는 줄 — 측정 누적면적 1.403% · 움직인 프레임 8/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

Intl.DateTimeFormatsetIntervaltimeZone

팀 페이지, 이 아홉 개는 실제로 어디에 붙이나

채용 공고 아래 붙는 팀 소개, 회사소개 페이지의 구성원 영역, 에이전시 포트폴리오의 팀 목록 — 이 세 화면이 이 아홉 종을 가장 자주 찾는 자리입니다. 02 팀원 그리드는 채용 페이지처럼 인원이 계속 바뀌는 곳에 맞고, 04 프로필 상세는 구성원 한 명씩 눌러 펼쳐 보게 하고 싶은 about 페이지에 맞습니다.

원격으로 흩어진 팀을 보여줘야 하는 화면에서는 09 팀 푸터가 지금 연락해도 되는 시간인지를 먼저 알려줍니다. 세 화면 모두 인원수가 고정돼 있지 않다는 공통점이 있어서, 카드 개수를 하드코딩하지 않는 이 구조가 그대로 들어맞습니다.

데이터가 배치를 바꾸는 곳 — 인원·글자수·시차

아홉 섹션을 관통하는 공통점은, CSS 값이 디자이너가 미리 정한 숫자가 아니라 그 순간의 데이터에서 나온다는 것입니다. 팀원 소개 디자인을 만들 때 이 세 축(인원·글자수·시차)을 데이터로 다루면 카드가 실제로 몇 명이 오든 버티는 구조가 됩니다.

인원수가 바꾸는 것

01 의 등장 간격과 02 의 마지막 줄 배치, 03 의 탭 라벨 숫자가 모두 인원수 하나에서 나옵니다. 배열 길이가 5 든 12 든 같은 함수가 다른 숫자만 받아 다시 계산합니다.

글자수가 바꾸는 것

04 는 소개글 글자수(정확히는 렌더링된 높이) 가 더보기 버튼의 존재 자체를 결정하고, 07 은 인용문 글자수가 예상 줄 수를 거쳐 글자 크기를 결정합니다. 둘 다 텍스트가 짧으면 계산 결과가 "그대로 둔다" 쪽으로 떨어집니다.

시차가 바꾸는 것

09 는 팀원마다 다른 시간대 문자열이 같은 현재 시각을 서로 다른 화면 값으로 바꿔 놓습니다. 시간대 데이터 하나만 바뀌면 나머지 계산은 그대로인 채 표시만 갈립니다.

어디서 깨지는가 — 홀수 인원·긴 소개글·시간대 API

홀수 인원에서 탭이 사라지는 경우

02 의 나머지 계산은 1~2명만 별도 줄로 뗍니다. 나머지가 3이면 원래 열 수 그대로 채우도록 짜 두었는데, 이 경계를 지우고 나머지가 있을 때마다 항상 떼도록 바꾸면 열이 4개인 그리드에서 3명이 남는 흔한 경우까지 줄이 갈라져 오히려 어색해집니다.

긴 소개글이 clamp 를 두 번 건너가는 경우

04 는 clamp 를 걸고 풀고 다시 거는 세 단계로 scrollHeight 를 잽니다. 이 중간에 레이아웃을 강제로 다시 읽는 시점(리플로우) 이 없으면 브라우저가 두 높이를 같은 값으로 캐시해 버려 항상 "넘침 없음" 으로 판정될 수 있습니다 — 실측 중 clamp 전환 사이에 값을 다시 읽어야 한다는 걸 이 경로에서 확인했습니다.

시간대 API 가 기기마다 다르게 답하는 경우

09 의 ucthsz2g 압축 파일에는 이 시간대 표를 실제 회사 인원으로 바꾸는 방법이 들어 있는데, Intl.DateTimeFormat 이 지원하지 않는 오래된 브라우저에서는 timeZone 옵션 자체가 예외를 던집니다. 팀원 시간대를 실제 데이터로 바꿀 때는 IANA 표준 이름("Asia/Seoul" 같은 표기) 인지부터 확인해야 합니다.

지속시간과 속도를 낮추면 접근성 처리까지 함께 보인다

같은 계산을 다른 상수로 돌리면 체감이 달라집니다.

이름 바꾼 값 느낌
스탯 카운트업 지속시간 05 1600ms → 800ms 1600ms는 숫자가 올라가는 과정이 잘 보이지만 스크롤이 빠른 사용자에게는 늘어지고, 800ms로 줄이면 즉각적이지만 자릿수가 큰 숫자는 뭉개져 보인다
파트너 로고 흐르는 속도 06 초당 60px → 30px 60px/s는 로고를 훑고 지나가는 느낌이라 가볍지만 하나씩 읽기 어렵고, 30px/s로 늦추면 각 로고가 눈에 들어오는 대신 전체가 느릿하게 보인다
소개글 더보기 줄 수 04 3줄 → 2줄 3줄은 소개 맥락이 어느 정도 전달돼 더보기 클릭률이 낮아지지만 카드가 세로로 길어지고, 2줄로 줄이면 카드는 균일해지지만 거의 모든 카드에 더보기가 붙어 성가셔진다

접근성 — 계산은 남기고 애니메이션만 끈다

아홉 섹션 모두 prefers-reduced-motion: reduce 를 감지해 자동 순환(setInterval) 을 멈추고 트랜지션을 없앤 채 계산 결과만 즉시 보여줍니다. 06 파트너 로고 바는 marquee 애니메이션 자체를 일시정지하고, 01 팀 히어로는 등장 지연 계산은 그대로 두되 opacity·transform 전환만 꺼서 사진이 곧바로 제자리에 보이게 합니다. 08 지원 CTA 의 D-day 숫자는 aria-live="polite" 로 값이 바뀔 때마다 스크린리더에도 읽힙니다.

아홉 종을 쓰다 자주 걸리는 질문 세 가지

몇 명이 늘어나거나 줄어도 그리드가 깨지지 않나요?

02 팀원 그리드는 인원수를 열 수로 나눈 나머지로 마지막 줄만 다시 배치하므로, 인원이 4명이든 23명이든 같은 함수가 그 숫자를 받아 계산합니다. 열 수 자체는 ResizeObserver 로 화면 폭에 맞춰 다시 구해집니다.

노션 팀 소개 페이지에도 이 코드를 붙일 수 있나요?

노션은 커스텀 스크립트 삽입을 막아 두므로 이 9종은 직접 만드는 정적 페이지나 코드 삽입이 열린 CMS 에 붙이는 용도입니다. 카드 배치나 문구만 참고하고 싶다면 구조를 옮기고 계산 로직은 이 코드를 그대로 쓰면 됩니다.

짧은 소개글에도 더보기 버튼이 항상 붙나요?

아닙니다. 04 프로필 상세는 clamp 한 높이와 원래 높이를 비교해 실제로 넘칠 때만 더보기 버튼을 렌더링하므로, 소개글이 짧으면 버튼 자체가 나타나지 않습니다.

더 읽을거리

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

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