게시일: 2025년 8월 14일
Google I/O 이벤트 시즌이 마무리됨에 따라 이 게시물에서는 올해 이벤트에서 공유된 CSS 및 웹 UI의 주요 내용을 요약합니다.
개발자가 한때 꿈꿔 왔던 매우 강력한 기능이 브라우저에 도입되었으며 이전보다 빠르게 크로스 브라우저 호환성을 달성하고 있습니다. 하지만 이러한 발전에도 불구하고 가장 일반적인 UI 패턴 중 일부는 올바르게 구현하기가 여전히 어렵습니다. 더 간단해야 할 것 같은 구성요소를 빌드하기 위해 JavaScript 프레임워크, 복잡한 CSS 트릭, 수많은 맞춤 코드를 사용해야 하는 경우가 많습니다.
Chrome팀은 다른 브라우저 공급업체, CSSWG 및 WHATWG와 같은 표준 기관, Open UI와 같은 커뮤니티 그룹과 협력하여 이러한 기본 UI 패턴을 정말로 간단하게 구현할 수 있도록 하는 데 중점을 두고 있습니다.
맞춤설정 가능한 선택 메뉴
<select>
요소는 양식에 필수적이지만, 내부 구조가 브라우저에 의해 숨겨져 있어 일관되고 포괄적인 CSS 스타일링이 거의 불가능했습니다. 더 나은 <select>
를 빌드하려면 기본 구성요소인 Popover API와 CSS Anchor Positioning API를 이해해야 합니다.
Popover API: 이제 Baseline에 포함됨
맞춤 드롭다운에는 다른 모든 UI 요소 위에 표시되고, 간단하게 닫을 수 있으며, 포커스를 올바르게 관리하는 옵션의 플로팅 상자가 필요합니다. 팝오버 API는 이 모든 것을 처리하며 올해부터 Baseline Newly available 상태가 되었습니다. 즉, 모든 주요 브라우저에서 안정적입니다.
팝오버를 만들려면 트리거 요소 (예: <button>
)와 팝오버 요소 자체라는 두 부분이 필요합니다. 팝오버에 id
및 [popover]
속성을 부여하여 연결한 다음 버튼의 [popovertarget]
속성에서 해당 id
을 참조합니다.
팝오버 API는 요소의 전체 수명 주기를 관리하여 다음을 제공합니다.
- 최상위 레이어 렌더링: 더 이상 z-색인과 싸우지 않아도 됩니다.
- 선택적 가벼운 닫기 기능: 사용자가 팝오버 영역 외부를 클릭하면 닫힙니다.
- 자동 포커스 관리: 브라우저가 팝오버로 들어가고 나가는 탭 탐색을 처리합니다.
- 액세스 가능한 바인딩: 기본 상호작용 모델이 기본적으로 처리됩니다.
<dialog>
요소가 업그레이드됨
팝오버는 강력하지만 항상 적합한 선택은 아닙니다. 예를 들어 사용자 의견이 필요한 페이지 차단 상호작용에서는 모달 <dialog>
이 더 적합합니다.
이전에는 <dialog>
에 [popover]
의 편리한 기능이 일부 없었지만 이제는 달라지고 있습니다. 새로운 closedby="any"
속성을 사용하면 이제 모달 대화상자에서 사용자가 외부를 클릭하거나 Escape 키를 누르면 닫히는 가벼운 닫기 기능을 지원합니다.
또한 명령어 호출자 ([command]
및 [commandfor]
)는 command="show-modal"
로 대화상자를 여는 등의 작업을 버튼에 연결하는 선언적이고 JavaScript가 없는 방법을 제공합니다.
<dialog> 요소 + closedby=any + 명령 호출기 |
속성 [popover] 개 |
|
---|---|---|
기본 사용 | 모달 상호작용 (사용자 동의, 둘러보기 등) | 일시적인 UI (메뉴, 도움말, 카드, 토스트 알림) |
가볍게 닫을 수 있음 | 예 | 예 |
포커스 트랩 | 예 | 아니요 |
Inerts 페이지 | 예 | 아니요 |
선언적 활성화 | 예 | 예 |
구현 | 요소 | 속성 |
최상위 레이어에서 렌더링 | 예 | 예 |
완전 스타일 지정 가능 | 예 | 예 |
CSS 앵커 포지셔닝
팝오버가 표시되면 팝오버를 연 요소에 상대적으로 배치해야 합니다. JavaScript로 이를 수동으로 계산하면 불안정하고 성능이 저하될 수 있습니다.
Chrome 125부터 CSS 앵커 포지셔닝 API를 사용할 수 있습니다. 이 새로운 기능은 한 요소를 다른 요소에 선언적으로 연결하여 화면 가장자리에 가까워지면 자동으로 위치를 조정합니다. 이 기능은 요청이 많은 기능을 제공하기 위한 크로스 브라우저 이니셔티브인 Interop 2025의 일부이므로 2025년 말까지 모든 주요 브라우저에서 사용할 수 있을 것으로 예상됩니다.

anchor-name
및 position-anchor
속성을 사용하여 요소를 명시적으로 연결할 수 있지만 사양과 Chrome 133의 업데이트로 인해 <popover>
와 호출하는 <button>
사이에 암시적 앵커 관계가 생성됩니다. 이렇게 하면 코드가 크게 간소화되며 이제 position-area: bottom span-left
와 같은 CSS 한 줄로 팝오버를 배치할 수 있습니다.
chrome.dev의 앵커 도구는 position-area
를 사용하여 원하는 배치 위치를 가져오는 방법을 보여줍니다.
position-try-fallbacks
로 대체 항목을 정의하여 브라우저가 앵커를 재배치하여 화면에서 벗어나지 않도록 할 수 있습니다. 다음 데모는 내장된 재배치 로직에 이 속성을 사용하는 팝오버를 보여줍니다.
진정한 맞춤설정 <select>
이러한 빌딩 블록이 이전 버전에 적용됨에 따라 Chrome 134에서 <select>
요소의 웹 네이티브 스타일이 드디어 제공됩니다. 여기에는 새로운 appearance
속성, 새로운 의사 요소, <selectedcontent>
요소가 포함됩니다.
맞춤설정을 잠금 해제하려면 appearance: base-select;
를 <select>
요소와 옵션 드롭다운 목록을 타겟팅하는 새 ::picker(select)
의사 요소에 적용하세요. 이렇게 하면 다음과 같은 스타일 지정에 사용할 수 있는 새로운 내부 파트가 노출됩니다.
<selectedcontent>
: 버튼에 표시된 선택된 옵션의 콘텐츠를 나타냅니다.::picker-icon
: 드롭다운 화살표 아이콘<option>:checked
및::checkmark
: 선택한 옵션과 체크표시 표시기 스타일 지정

이렇게 하면 옵션 내에서 다양한 콘텐츠를 사용할 수 있고 표시를 세밀하게 제어할 수 있습니다. 예를 들어 selectedcontent
내에서 display: none
를 사용하여 옵션 목록에 아이콘과 부제목을 표시하지만 닫힌 상태에서는 숨길 수 있습니다.
가장 좋은 점은 이 API를 점진적으로 개선할 수 있다는 것입니다. 이러한 기능을 지원하지 않는 브라우저에서는 사용자에게 여전히 기능적인 웹 네이티브 선택이 표시됩니다. 웹 네이티브 선택 요소의 내장된 접근성, 키보드 탐색, 양식 통합을 유지하면서 맞춤 디자인을 적용할 수 있습니다.
캐러셀
캐러셀은 웹의 모든 곳에 있으며, 히어로 섹션에만 있는 것은 아닙니다. 여기에는 앱 스토어 UI와 같은 좁은 레이아웃의 가로로 스크롤 가능한 콘텐츠가 포함됩니다. 하지만 상태 관리, 스크롤 지연, 상호작용, 접근성과 같은 여러 고려사항으로 인해 웹에서 캐러셀을 빌드하는 것은 여전히 어렵습니다. 하지만 생각해 보면 캐러셀은 기본적으로 추가 UI 어포던스가 있는 멋진 스크롤 영역입니다.
스크롤러 시작하기
캐러셀을 빌드하려면 컨테이너를 오버플로하는 항목 목록으로 시작합니다. 콘텐츠를 스크롤 가능하게 유지하면서 가로 스크롤바를 숨기려면 scrollbar-width: none
를 사용하세요. 또한 스크롤러가 '스냅'되는 느낌을 주려면 scroll-snap-type
및 scroll-snap-align
를 적용하여 사용자가 스크롤할 때 항목이 제자리에 스냅되도록 합니다.
::scroll-button
을 사용한 이전 및 다음
Chrome 135에 도입된 새로운 ::scroll-button()
의사 요소는 브라우저에 상태가 있고 접근 가능한 '다음' 및 '이전' 버튼을 생성하도록 지시합니다. 브라우저는 추가 JavaScript 없이도 올바른 ARIA 역할과 탭 순서를 자동으로 처리하고 시작 또는 끝에 도달하면 버튼을 사용 중지합니다.
스크롤 버튼을 시작하려면 다음과 같이 콘텐츠와 접근성 라벨을 제공하세요.
.carousel {
&::scroll-button(left) {
content: "⬅" / "Scroll Previous";
}
&::scroll-button(right) {
content: "⮕" / "Scroll Next";
}
}

CSS 앵커 포지셔닝을 사용하여 이러한 버튼의 스타일을 지정하고 상위 캐러셀을 기준으로 배치합니다. 이는 권장되는 방법입니다.
::scroll-marker
를 사용한 직접 탐색
점 표시기 또는 썸네일의 경우 ::scroll-marker
및 ::scroll-marker-group
가상 요소는 탐색 마커를 스크롤 컨테이너의 항목과 직접 연결합니다. 브라우저는 그룹을 tablist
처럼 처리하고 키보드 탐색을 처리합니다.
스크롤 버튼과 마찬가지로 content
속성을 선택하여 스크롤 마커를 시작하고 접근성 라벨을 제공합니다. 다음 예에서는 데이터 속성을 사용하여 스크롤 마커의 라벨을 설정합니다. 또한 scroll-marker-group
속성을 사용하여 ::scroll-marker-group
에 스크롤 마커를 배치합니다. 마지막으로 새 :target-current
가상 클래스를 사용하여 활성 마커의 스타일을 지정합니다. 다음은 기본 캐러셀의 예입니다.
.carousel {
scroll-marker-group: after;
> li::scroll-marker {
content: ''/ attr(data-name);
}
> li::scroll-marker:target-current {
background: blue;
}
}

스크롤 상태 쿼리
새로운 스크롤 관련 CSS 기능을 사용하면 더 동적이고 대화형인 캐러셀을 만들 수 있습니다. 스크롤 상태 쿼리는 스크롤러의 상태에 따라 적용되는 새로운 미디어 쿼리입니다. @container
문에서 scroll-state()
를 사용하여 액세스할 수 있는 스크롤 상태 쿼리에는 세 가지 유형이 있습니다. 각 필터는 다음과 같습니다.
scroll-state(snapped)
: 요소가 '스냅' 위치에 있을 때 일치합니다. 캐러셀의 경우 캐러셀의 중앙에 스냅된 항목이 표시되는 시점입니다.scroll-state(stuck)
: 상위 요소가 고정될 때 헤더와 같은 요소를 스타일 지정합니다.scroll-state(scrollable)
: 스크롤할 콘텐츠가 더 있음을 보여주는 시각적 표시기(예: 페이드)를 추가합니다.
총정리
새로운 CSS 캐러셀 기본 요소, 스크롤 상태 쿼리, 앵커 위치 지정의 조합을 사용하면 맞춤형 대화형 캐러셀을 더 쉽게 빌드할 수 있습니다. 스크롤 기반 애니메이션을 통합하여 애니메이션을 스크롤 위치에 직접 연결하면 항목이 스크롤되어 표시될 때 크기가 조정되고 흐려지는 것과 같은 성능이 우수한 효과를 만들 수 있습니다. 이러한 애니메이션은 기본 스레드에서 실행되므로 매우 부드러운 환경을 지원합니다.
이 대화형 캐러셀은 scroll-state()
쿼리, ::scroll-button
, ::scroll-marker
, CSS 앵커 포지셔닝, :target-current
를 결합합니다.
또한 interactivity
이라는 새 속성을 사용하여 사용자가 활성 콘텐츠에 집중하도록 도울 수 있습니다. interactivity: inert
를 사용하면 사용자가 CSS를 사용하여 비활성 상태를 적용할 수 있으므로 화면에 표시되지 않는 캐러셀 항목이 포커스할 수 없게 되고 접근성 트리에서 삭제됩니다.
CSS 캐러셀에 대해 자세히 알아보세요.
대화형 호버 카드
마우스를 사용자 이름이나 링크 위로 가져갈 때 표시되는 리치 팝업인 호버 카드는 매우 유용하지만 올바르게 빌드하기가 어렵습니다. 지연, 이벤트 처리, 멀티 기기 지원을 올바르게 구현하려면 전담팀이 몇 개월이 걸릴 수 있습니다. 하지만 이 문제를 완전히 해결할 수 있는 새로운 선언적 솔루션을 개발하고 있습니다.
[interestfor]
의 관심분야에 따라 트리거되는 팝오버
선언적 호버 카드의 핵심은 [interestfor]
속성입니다. 이 새로운 기능은 팝오버의 기능을 제공하지만 사용자 '관심'에 따라 트리거됩니다. 예를 들어 포인터 기기에서 사용자의 관심은 포인터 마우스 오버, 키보드를 사용한 탭 탐색, 터치 스크린에서의 길게 누르기 또는 탭이 될 수 있습니다. 모바일 상호작용이 아직 해결되지 않았습니다.
클릭 기반 팝오버를 관심분야 기반 팝오버로 변환하려면 호출 요소를 빌드합니다. 호출 요소는 <button>
또는 <a>
일 수 있으며 [popover]
요소의 id
와 동일한 [interestfor]
속성을 부여합니다. HTML에서는 다음과 같이 표시됩니다.
<button interestfor="profile-callout">
...
</button>
<div id="profile-callout" popover>
...
</div>
브라우저는 다음을 비롯한 모든 복잡한 이벤트 로직을 처리합니다.
- 진입 및 종료 이벤트: 미세 포인터 기기에서 마우스 오버 진입, 키보드로 탭 탐색, 거친 포인터 기기에서 길게 누르기 또는 터치
- 이벤트 지연: 단일 CSS 속성으로 진입 및 종료 지연을 제어합니다.
이 기능은 팝오버가 나머지 DOM 트리 위의 새 레이어에 렌더링되는 최상위 레이어 지원과 같은 다른 팝오버 기능을 지원합니다. 시맨틱 구성요소 바인딩과 기본 접근성 트리 모델은 네이티브로 처리됩니다.
관심 인보커 스타일 지정
관심사 호출기에는 몇 가지 새로운 기능이 포함됩니다. 그중 하나는 CSS 속성 interest-target-delay
를 사용하여 진입 및 종료 지연을 제어하는 기능입니다. 다른 하나는 :has-interest
의사 클래스를 사용하여 관심이 있는지 여부에 따라 호출 요소를 스타일링하는 기능입니다.
[interesttarget] {
interest-target-delay: 0s 1s;
&:has-interest {
background: yellow;
}
}
popover="hint"
및 다기능 UI
관심 유발자의 핵심 요소는 새로운 팝오버 유형인 popover="hint"
입니다. 다른 팝오버와의 주요 차이점은 힌트 팝오버가 열릴 때 다른 팝오버가 닫히지 않는다는 것입니다. 이미 열려 있는 메뉴나 채팅 창을 닫지 않고 표시해야 하는 툴팁이나 미리보기 카드에 적합합니다.
Browser Support
popover=auto | popover=manual | popover=hint | |
---|---|---|---|
가벼운 닫기 (클릭 또는 esc 키를 통해) | 예 | 아니요 | 예 |
열리면 다른 popover=auto 요소를 닫습니다. | 예 | 아니요 | 아니요 |
열리면 다른 popover=hint 요소를 닫습니다. | 예 | 아니요 | 예 |
열리면 다른 popover=manual 요소를 닫습니다. | 아니요 | 아니요 | 아니요 |
JS (showPopover() 또는 hidePopover() )로 팝오버를 열고 닫을 수 있음 | 예 | 예 | 예 |
다음 탭 정지의 기본 포커스 관리 | 예 | 예 | 예 |
popovertargetaction 로 숨기거나 전환할 수 있음 | 예 | 예 | 예 |
부모를 열린 상태로 유지하기 위해 부모 popover 내에서 열 수 있음 | 예 | 예 | 예 |
이를 통해 강력한 다기능 UI를 선언적으로 빌드할 수 있습니다. 이제 단일 버튼에 기본 클릭 작업 (예: 알림 패널 열기)에 popovertarget
를 사용하는 자동 팝오버와 관심분야 호출 힌트 팝오버를 사용하여 포인터 호버 시 유용한 도움말을 표시할 수 있습니다.
미래는 선언적입니다
여기에서 다루는 기능은 더 강력하고 선언적인 웹 플랫폼으로의 근본적인 변화를 나타냅니다. 브라우저가 복잡하고 반복적인 상태 관리 및 접근성 작업을 처리하도록 하면 수많은 JavaScript를 삭제하고 성능을 개선하며 혁신적이고 매력적인 사용자 환경을 만드는 데 집중할 수 있습니다. 지금은 웹 UI의 황금기이며, 이는 이제 시작일 뿐입니다. 더 강력하고 쉬운 웹을 만들기 위한 Google의 노력을 여기에서 확인하세요.
추가 리소스: