#106

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


Footnotes

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

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

#107

텍스트 ellipsis 처리. 컨텍스트(table-cell, flex, multiline)에 따라 패턴이 다름.

table cell

display: table-cell은 width 제약 없이 content 크기에 맞게 늘어남. overflow: hidden이 동작하려면 명시적 width bound가 필요한데, table 기본 레이아웃이 그것을 허용하지 않음.

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

해법: table-layout: fixed + max-width: 0.

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;
}

max-width: 0이 실제 0px이 되는 게 아님. table-layout: fixed 컨텍스트에서 “이 셀은 content 기반으로 너비를 주장하지 않는다”는 시그널로 작동하여 colgroup/th에서 받은 너비 안에서 overflow가 동작. 둘은 반드시 함께 있어야 함.

컬럼별 선택적 적용

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. flexbox | 파일명에 ellipsis 효과 적용, 단 파일 확장자는 제외.

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

#108

Building a multi-select component

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

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

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

#133

Under-Engineered Select Menus | Adrian Roselli

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

#134
::-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 {}

#166
type Props = {
  popover: 'auto' | 'manual'
  popovertarget: string
  popovertargetaction: 'hide' | 'show' | 'toggle'
}

type State = {
  hasBackdrop: boolean
  isPopoverOpen: boolean
}

type Methods = {
  hidePopover: () => void
  showPopover: () => void
  togglePopover: () => void
}

type Events = {
  beforetoggle: () => void
  toggle: () => void
}

#279
class ColorManipulator {
  private baseColor: string
  private targetColor: string

  constructor(baseColor: string, targetColor: string) {
    this.baseColor = baseColor
    this.targetColor = targetColor
  }

  private hexToRgb(hex: string) {
    const bigint = parseInt(hex.slice(1), 16)

    return {
      r: (bigint >> 16) & 255,
      g: (bigint >> 8) & 255,
      b: bigint & 255,
    }
  }

  public calculateOpacity() {
    const target = this.hexToRgb(this.targetColor)
    const baseRG = this.hexToRgb(this.baseColor)
    const opacities = [
      (target.r - baseRG.r) / (255 - baseRG.r),
      (target.g - baseRG.g) / (255 - baseRG.g),
      (target.b - baseRG.b) / (255 - baseRG.b),
    ]
    const averageOpacity =
      opacities.reduce((sum, value) => sum + value, 0) / opacities.length

    return averageOpacity
  }

  public getCssRGBA() {
    const opacity = this.calculateOpacity()

    return `rgba(0, 0, 0, ${opacity.toFixed(2)})`
  }
}

const manipulator = new ColorManipulator('#000000', '#D1D7DE')
const opacity = manipulator.calculateOpacity()
const cssRGBA = manipulator.getCssRGBA()
#341
@value b from "./b.module.css";

.root {
  color: aquamarine;
}

.root :global(.b) {
  text-decoration: line-through;
}

CSS 모듈에서 변수를 값으로 내보내고 사용하는 방법

  • PostCSS와 postcss-modules-values 플러그인을 사용하여 CSS 모듈 내에서 변수 값 내보내기 지원
  • 색상 변수를 정의하는 파일 생성
    • 변수 선언: @value 구문 사용
  • 다른 CSS 모듈 파일에서 해당 변수를 가져와서 사용
    • 변수 가져오기 및 CSS 클래스에 적용
#371
// 기본
@primary: #3498db;
.button { color: @primary; }

// 보간 (선택자, 속성, URL에서)
@component: 'button';
.@{component} { ... }           // .button
background-@{property}: blue;   // background-color: blue
url('@{path}/icon.svg')

// 변수를 이용한 변수
@theme: 'primary';
color: @@theme;  // @primary 값

// 맵 (LESS 3.5+)
@colors: { primary: #3498db; danger: #e74c3c; };
color: @colors[primary];

// CSS 변수 조합
:root { --primary: @primary; }
.el { color: var(~'--@{prefix}-color'); }

  • ~"" 이스케이프 (문자열 그대로 출력)
  • @{} 보간 (변수를 문자열로 치환)
  • @@var 변수를 이용한 변수 참조

https://lesscss.org/features/#variables-feature

#526

Footnotes

  1. markdown을 unified를 이용해서 파싱하고 html로 변환. (플러그인 기능을 추가해서 img->figcaption 기능 추가)

  2. AST가 무엇이며 일반 코드에서 어떻게 구축 하는지에 대한 설명과 기반으로 하는 사용 사례와 프로젝트 소개

  3. css 데이터 조작이 필요한 경우가 있어서 찾아봤다. 예를들어 특정 속성값만 추출해서 유닛값은 제거 한다든가.

#6

hash 링크로 연결될 경우 스크롤위치가 최상단으로 위치하기 때문에 문제(헤더가 고정일 경우)가 있을수도 있어서 scroll-margin-top으로 제어가 가능한 부분을 설명하고 있다.


Footnotes

  1. 2ex 유닛을 사용하여 선택한 글꼴의 x 높이의 상대적인 크기로 설정.

#87