#106

인라인 요소에 bold 스타일이 적용될 경우 레이아웃 시프팅 현상이 발생하기 때문에 해당 이슈를 해결하는 방법들.


Footnotes

  1. text-shadow로 우회하는 방법이 주 해결방법으로 올라왔다. ↩

  2. content 속성과, grid 레이아웃을 이용한 방법. ↩

#107

텍스트 ellipsis는 컨텍스트마다 패턴이 다르다.

언제 — 먼저 어느 컨텍스트인지 가른다.

컨텍스트 어디로
display: table-cell 아래 절차
flexbox 맨 아래 참고 링크
여러 줄(multiline) 맨 아래 참고 링크

table cell

절차

  1. 표에 table-layout: fixed를 준다.
  2. 셀에 max-width: 0과 overflow 셋(overflow: hidden·text-overflow: ellipsis·white-space: nowrap)을 준다.
table {
  table-layout: fixed;
  width: 100%;
}

col:nth-child(1) { width: 30%; }
col:nth-child(2) { width: 40%; }
col:nth-child(3) { width: 30%; }

td {
  max-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

확인: 긴 텍스트가 든 셀이 표를 옆으로 밀어내지 않고 …로 잘리는가.

함정 — overflow 셋만 주면 아무 효과가 없다. 표에 width를 주고, 표의 table-layout: fixed나 셀의 max-width: 0 중 하나는 있어야 한다(둘 다는 필요 없다 — Chrome 156·Firefox 150). 표 width 없이 max-width: 0만 주면 열이 0으로 접힌다.

/* ❌ 아무 효과 없음 */
td {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

자동 레이아웃의 셀은 내용 너비만큼 늘어나서 overflow가 잘라낼 경계가 없다. fixed는 열 너비를 표·col 너비로만 정해 내용이 끼어들 자리를 없애고1, max-width: 0은 자동 레이아웃에서 셀이 내용 기반 너비를 주장하지 못하게 한다 — 다만 표 셀의 max-width는 CSS 2.1이 정의하지 않은 동작이라 브라우저 관행에 기댄다2.

컬럼별 선택적 적용

td:not(.col-action) {
  max-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

td.col-action {
  white-space: nowrap;
}

셀 안에 복합 요소

버튼/배지 등이 텍스트와 함께 있을 때 inner wrapper에 위임.

td {
  max-width: 0;
}

td > div {
  display: flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
}

td > div > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

React 컴포넌트 패턴

TanStack Table size 옵션과 조합.

const columns = [
  columnHelper.accessor('name', {
    size: 200,
    cell: ({ getValue }) => (
      <span style={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
        {getValue()}
      </span>
    ),
  }),
]

<table style={{ tableLayout: 'fixed', width: '100%' }}>
  <colgroup>
    {table.getFlatHeaders().map(header => (
      <col key={header.id} style={{ width: header.getSize() }} />
    ))}
  </colgroup>
  ...
</table>

재사용 셀 래퍼:

function EllipsisCell({ children }: { children: React.ReactNode }) {
  return (
    <td style={{ maxWidth: 0 }}>
      <div style={{
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
      }}>
        {children}
      </div>
    </td>
  )
}

flexbox / multiline 참고


Footnotes

  1. table-layout - MDN: “The horizontal layout doesn’t depend on the contents of the cells because it depends only on explicitly set widths.” · “Cells use the overflow property to determine whether to clip any overflowing content, but only if the table has a known width” ↩

  2. CSS 2.1 §10.4: “In CSS 2.1, the effect of ‘min-width’ and ‘max-width’ on tables, inline tables, table cells, table columns, and column groups is undefined.” ↩

  3. flexbox | 파일명에 ellipsis 효과 적용, 단 파일 확장자는 제외. ↩

  4. :truncated 개념이 없기 때문에 ResizeObserver와 영역값을 체크해서 구현. ↩

#108

The clamp() CSS function clamps a value within a range of values between a defined minimum bound and a maximum bound. The function takes three parameters: a minimum value, a preferred value, and a maximum allowed value.

가로축 뷰포트 폭, 세로축 글자 크기. 360px 에서 1rem, 840px 에서 3.5rem 두 점을 직선으로 잇는다 — 기울기 2.5rem ÷ 30rem = 0.0833, 100 을 곱해 8.333vw. 직선을 폭 0 까지 늘린 y절편이 -0.875rem 이라 clamp(1rem, -0.875rem + 8.333vw, 3.5rem) 이 되고, 600px 에서 정확히 가운데인 2.25rem 이다. 폭도 rem 으로 바꿔 계산하는데 16 으로 나누는 건 루트 글자 크기가 16px 일 때만 맞다. 함정은 확대: 확대하면 CSS 뷰포트가 좁아져 vw 가 줄어서, 800px 창에서 200% 확대하면 화면 픽셀 52.7px 가 38.7px 로 오히려 작아진다(WCAG 1.4.4). 그래서 min·max 차이가 큰 제목에만 쓴다.

1vw 는 창 폭의 1% 다. 600px 창이면 1vw = 6px 이라 8.333vw = 50px, 여기에 y절편 -0.875rem(-14px) 를 더해 36px = 2.25rem 이고 clamp 가 16px~56px 로 자른다. 창 폭만 바꾸면 vw 몫은 창에 비례해 30·50·70·100px 로 늘지만 -14px 는 그대로라, 결과는 16·36·56·86px 이고 1200px 창의 86px 은 max 56px 에서 잘린다. vw 만 쓰면 원점을 지나는 비례선이라, 고정 rem 을 더해 선을 평행이동해야 원하는 두 점을 지난다.


Footnotes

  1. 유토피아는 디자이너와 개발자가 반응형 디자인의 유동성에 대한 체계적인 접근 방식을 공유할 때 나타납니다. x 개의 임의 중단점에 대해 설계하는 대신 요소가 비례하고 유동적으로 확장되는 시스템을 설계할 수 있습니다. ↩

  2. viewport 기반의 글꼴 크기를 clamp로 어떻게 구현하는지 설명해주고 있다. ↩

#109

z-index 는 전역이 아니라 같은 쌓임 맥락 안에서만 비교된다. header 가 z-index 2, main 이 z-index 1 이면 main 안의 tooltip 은 z-index 999999 여도 header 아래에 깔린다 — 999999 는 main 안의 형제끼리만 비교되고, 맥락 밖으로 빠져나올 방법은 없다. 맥락은 z-index 를 준 positioned 요소 말고도 opacity 1 미만, transform·filter·clip-path, flex·grid 자식의 z-index, will-change, mix-blend-mode 로 의도치 않게 생긴다. 전략은 둘이다. 컴포넌트 안에서는 바깥 요소에 isolation: isolate 를 걸어 z-index 없이 맥락을 만든다 — 안쪽 1~5 는 그 안에서만 비교되고 밖으로 새지 않아, 컴포넌트가 바깥 z-index 값을 정하지 않아도 된다. 앱 레벨에서는 값을 숫자가 아니라 관계로 정의한다: base 0, above 1, below -1 을 두고 openButton 은 above + backdrop, dropdown 은 above + openButton 처럼 무엇 위에 있는지를 적는다. 상수는 쌓임 맥락별로 묶어 컴포넌트 옆에 둔다.



Footnotes

  1. z-index값을 체계적으로 관리하기 위한 방법(변수로 설정해서 상대적으로 값을 설정)을 제시하고 있다. ↩

#111

A reset stylesheet (or CSS reset) is a collection of CSS rules used to clear the browser’s default formatting of HTML elements, removing potential inconsistencies between different browsers.



Footnotes

  1. all: unset;을 적용해서 reset 하는 방법을 제시하고 있다. ↩

  2. box-sizing, 기본 margin 제거, percent 기반 높이, line-height 조정, font-smoothing, 미디어 타입 기본값, form 요소 글꼴 상속, 줄 바꿈, 루트 스태킹 컨텍스트로 구성된 초기화 ↩

  3. Chris Coyier 가 위 Josh Comeau 리셋을 줄마다 평한 글. 브라우저 기본 스타일 차이가 줄어서 요즘 리셋은 “의견이 담긴 기본값 모음”에 가깝다는 관점 — box-sizing 은 상속 방식으로(컴포넌트에서 되돌리기 쉽게), margin: 0 이면 padding: 0 도, -webkit-font-smoothing: antialiased 전역 적용엔 반대, 글꼴 크기 기준은 body 가 아니라 html(rem 의 root). ↩

  4. text-size-adjust 관련한 상황들 ↩

#113

perspective 는 보는 눈에서 물체까지의 거리다. 값이 작을수록 3D 효과가 강하다 — perspective: 100px 은 작은 곤충이 큰 물체를 보듯 크게 일그러지고, 2000px 은 쌍안경으로 멀리서 보듯 약하다. perspective 가 없으면 Z 축으로 움직여도 크기가 그대로다. 거는 자리는 둘이다. 요소마다 transform: perspective() 를 걸면 요소마다 소실점이 따로 생겨 여러 개를 나란히 놓으면 어긋난다. 부모에 perspective 속성을 걸면 자식들이 소실점 하나를 공유해 한 공간에 있는 것처럼 이어진다. 소실점은 기본으로 가운데이고 perspective-origin 으로 옮긴다. JS 로 translate 값을 읽으려면 getComputedStyle(el).transform 이 돌려주는 행렬을 본다. 2D 변환이면 값 6개인 matrix 이고 5번째가 translateX, 6번째가 translateY 다. 3D 변환이면 값 16개인 matrix3d 이고 13·14·15번째가 translateX·Y·Z 다.



Footnotes

  1. css 3d 작업시 perspective에 따른 변화와 원리를 설명해준 글 ↩

  2. translate 값을 얻기 위해서 getComputedStyle로 matrix값을 얻은 다음 2d, 3d에 따라서 해당 값을 체크 ↩

#114

mask — 현재 개체를 배경으로 합성하기 위한 알파 마스크를 정의


Footnotes

  1. clip-path: inset(0 0 0 0); 방향에 따라서 버튼 배경색이 확장되는 모션. 방향은 삼각형(polygon()) 4개를 각 방향에 맞춰 위치시키면 된다. ↩

  2. 화살표 모양 그리기와 filter: drop-shadow로 그림자 효과 생성하기 ↩

#116


Footnotes

  1. 초기로딩 속도를 개선하기 위한 content-visibility: auto 적용에 관한 글. ↩

  2. 접근성관련 스크린리더에서 혼란스러울수도 있는 부분에 대한 설명이 추가되어 있다. ↩

  3. 성능관련 css 속성을 언제(계속 권장인지, 문제가 있을때인지) 써야 맞는건가에 대한 의문이 담긴 의견. ↩

#117

Building a multi-select component

다중 선택 UI를 구현하기위해서 checkbox, select 두가지 방법으로 작업하는 방식을 소개하고 있다. 그외 선택된 상태값을 얻기위한 counter() 함수, 모바일 체크를 위한 미디어쿼리도 알려주고 있다. 개수 세기는 JS 없이 CSS 카운터로 된다.1

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

  &::after {
    content: counter(filters);
  }
}
@media (pointer: coarse) {
  /* … */
}

Footnotes

  1. Using CSS counters - MDN: “Once initialized, a counter’s value can be increased or decreased using counter-increment.” / “The value of a counter can be displayed using either the counter() or counters() function in a content property.” ↩

#133

Under-Engineered Select Menus | Adrian Roselli

  • font, letter-spacing, word-spacing 상속
  • appearance 화살표 수정
  • 상태(focus, required, invalid)에 따른 스타일 추가

#134

표준은 ::placeholder 하나다(2020년부터 전 브라우저).1 아래는 그 전의 접두사와 IE·레거시 Edge 전용 컨트롤.

::-webkit-input-placeholder /* for (Chrome/Safari/Opera) */
:-ms-input-placeholder /* for IE. */
::-ms-input-placeholder /* for Edge (also supports webkit prefix) */

::-ms-clear {}
::-ms-reveal {}

Footnotes

  1. ::placeholder - MDN: “The ::placeholder CSS pseudo-element represents the placeholder text in an or