// FoxPopover — 시안: 통합관리자페이지 디자인시스템 Figma popover
//
// 꼬리가 달린 말풍선 판이다. 제목·본문·글자 버튼을 담고 오른쪽 위에 닫기가 붙는다.
//
// 마크업 계약 (React 밖 소비자용):
//
//
//
//
//
//
//
// 제목
//
//
// 본문
//
//
//
//
//
//
//
// `--top`은 꼬리가 판 **위**에 붙고(트리거 아래에 뜨는 말풍선), `--bottom`은 아래에 붙는다.
// 마크업 순서가 그것을 정한다 — `order`나 `column-reverse`를 쓰지 않는다.
//
// **자리를 스스로 잡지 않는다.** 트리거 옆 어디에 놓을지는 호출부가 `className`으로 준다.
// 자리가 모자랄 때 위아래를 뒤집는 자동 배치도 없다 — 시안이 방향을 값으로 준다.
//
// ⚠️ 띄우는 방식이 `fox-select-option`과 같다. 감싼 요소 기준의 일반 흐름이라
// `overflow: hidden` 조상 안에서는 잘린다. HTML `popover` 속성으로 top layer에 올리는 방법이
// 있지만 위치를 잡아 주는 CSS Anchor Positioning이 아직 크롬 계열에만 있어 미룬다 — 옮기게
// 되면 셀렉트·칩·툴팁까지 한 번에 옮긴다.
//
// 꼬리는 **CSS가 그린다**(SVG를 쓰지 않는다). 그림과 방향·위치 규칙은 `_bubble.scss`의
// `arrow()` 믹스인이 갖는다 — `FoxTooltipRich`와 같은 그림이라 한곳에 둔다. 왜 시안 SVG를
// 쓰지 않는지도 그 파일 상단에 적혀 있다.
//
// 토큰이 아닌 값:
// - ⚠️ `inline-size: 36rem`(360px): 시안 값 그대로다. 토큰 스케일에 360이 없다. 폭을 바꿔야
// 하는 자리는 호출부가 `className`으로 덮는다(`fox-snackbar`·`fox-alert`와 같은 처리).
// - box-sizing / display / flex / position: 구조 규칙.
//
// 꼬리의 가로 위치는 **루트의 `align-items`**가 정한다(시안 지정). 꼬리 영역이 좌우 24px
// 여백을 달고 다니므로, 왼쪽·오른쪽에서는 그만큼 안쪽에 서고 가운데에서는 여백이 대칭이라
// 정확히 가운데에 선다.
@use "@fox/styles/abstracts" as fox;
@use "@fox/styles/bubble" as bubble;
.fox-popover {
@include bubble.root;
// 시안의 neutral/lv2 — 가까운 그림자와 먼 그림자 두 겹이다.
//
// ⚠️ `box-shadow`가 아니라 `drop-shadow`다. box-shadow는 요소의 **사각형 박스**를 따라
// 그려지므로, 꼬리 옆 빈 자리까지 포함한 네모 그림자가 나온다(시안은 Figma 프레임이 곧
// 모양이라 그 차이가 없다). drop-shadow는 **실제로 칠해진 모양**을 따라가므로 판과 꼬리가
// 하나의 말풍선으로 그림자를 진다.
//
// 겹쳐 쓰면 뒤의 그림자가 앞 그림자까지 다시 흐리게 하지만, 두 겹 다 10% 알파라 눈에
// 띄는 차이는 없다.
filter:
drop-shadow(
#{fox.shadow(location1)} #{fox.shadow(location1)} #{fox.shadow(blur2)}
#{fox.primitive(alpha-black10)}
)
drop-shadow(
#{fox.shadow(location3)} #{fox.shadow(location3)} #{fox.shadow(blur3)}
#{fox.primitive(alpha-black10)}
);
// ── 판 ──────────────────────────────────────────────────────────────────────
&__content {
// 닫기 버튼의 위치 기준이다.
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: fox.gap(4);
align-self: stretch;
padding: fox.padding(8);
border: fox.border(1) solid fox.color(border-neutral-strong);
border-radius: fox.radius(5);
background: fox.color(surface-neutral-default);
}
// ── 꼬리 ────────────────────────────────────────────────────────────────────
// 좌우 여백이 꼬리를 판 가장자리에서 띄운다. **늘리지 않는다** — 폭이 내용(22px + 여백
// 48px)만큼이라야 루트의 `align-items`가 이 덩어리를 왼쪽·가운데·오른쪽으로 옮길 수 있다.
//
// 판보다 **위에 칠해진다**(시안 구조). 꼬리가 판 쪽으로 1px 파고들고, 꼬리 안의 흰 띠가 그
// 자리의 판 테두리를 지워 말풍선 안팎이 하나로 이어진다. 쌓임 순서를 올리지 않으면
// `--top`에서 판이 나중에 칠해져 그 선이 되살아난다.
&__arrow-area {
position: relative;
z-index: 1;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: flex-start;
padding-inline: fox.padding(8);
}
// ── 글자 ────────────────────────────────────────────────────────────────────
&__body {
display: flex;
flex-direction: column;
align-items: flex-start;
align-self: stretch;
}
// 오른쪽 여백은 닫기 버튼 자리다 — 제목이 그 밑으로 들어가지 않게 비워 둔다.
&__title-row {
display: flex;
align-items: center;
gap: fox.gap(5);
align-self: stretch;
padding-inline-end: fox.padding(9);
}
&__title {
flex: 1 0 0;
min-inline-size: 0;
color: fox.color(font-neutral-strong);
font-family: fox.font-family(title);
font-size: fox.font-size(heading-xsm);
font-weight: fox.font-weight(bold);
line-height: 1.4;
letter-spacing: -0.025em;
}
// 제목과 본문 사이 간격을 본문의 윗여백이 맡는다(시안이 그렇게 준다) — 제목만 있을 때
// 아래로 빈자리가 남지 않는다.
&__message-row {
display: flex;
align-items: flex-start;
align-self: stretch;
padding-block-start: fox.padding(6);
}
&__message {
flex: 1 0 0;
min-inline-size: 0;
color: fox.color(font-neutral-default);
font-family: fox.font-family(body);
font-size: fox.font-size(body-sm);
font-weight: fox.font-weight(regular);
line-height: 1.5;
letter-spacing: -0.025em;
}
// 글자 버튼은 자기 모양을 그대로 갖는다 — 여기서는 줄어들지만 않게 막는다.
&__action {
flex-shrink: 0;
}
// ── 닫기 ────────────────────────────────────────────────────────────────────
// 판 오른쪽 위 모서리에 붙는다. 누르는 자리는 40px 정사각이고 아이콘은 16px이다.
//
// ⚠️ 시안이 `align-items: flex-end`를 준다 — 가로는 시작(왼쪽), 세로는 끝(아래)이라
// 아이콘이 40px 칸의 왼쪽 아래로 치우친다(`fox-snackbar`의 닫기와 같다).
&__close {
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
box-sizing: border-box;
display: flex;
align-items: flex-end;
inline-size: fox.number(14);
block-size: fox.number(14);
aspect-ratio: 1 / 1;
padding: fox.padding(2);
border: none;
background: none;
color: fox.color(icon-neutral-default);
cursor: pointer;
}
&__close > * {
flex-shrink: 0;
inline-size: fox.icon(3);
block-size: fox.icon(3);
aspect-ratio: 1 / 1;
}
}
// 꼬리. 판과 같은 색을 넘긴다 — 꼬리는 판이 뾰족하게 튀어나온 부분이다.
@include bubble.arrow(
"fox-popover",
$border: fox.color(border-neutral-strong),
$fill: fox.color(surface-neutral-default)
);