// FoxSnackbar — 시안: 통합관리자페이지 디자인시스템 Figma snackbar // // 흰 판 위에 글자(제목·본문)와 글자 버튼이 서고, 오른쪽 위에 닫기가 붙는다. 제목과 본문은 // 각각 없을 수 있다 — 한쪽만 있으면 그 줄만 그린다. `FoxToast`와 형제지만 // **누를 것이 있다** — 그래서 스스로 사라지지 않고 손이 닿을 때까지 남는다. // // 마크업 계약 (React 밖 소비자용): //
// …아이콘 svg… //
// 저장했습니다 // 3분 안에 되돌릴 수 있습니다. //
// // //
// // 글자 버튼은 `FoxTextButton`의 primary·md를 그대로 쓴다 — 스낵바가 버튼 모양을 다시 정하지 // 않고 `fox-snackbar__action`은 배치만 맡는다. // // 토큰이 아닌 값: // - ⚠️ `inline-size` / `min-inline-size: 64rem`: **시안의 폭(640px)을 그대로 적용해 둔 // 것이다.** Figma가 변수로 export하지 않았고 토큰 스케일에도 640이 없다. 폭을 바꿔야 하는 // 자리는 호출부가 `className`으로 덮는다(`fox-toggle-quantity`·`fox-alert`와 같은 처리). // - ⚠️ `padding-inline-end`가 64px이라 토큰이 없다. 닫기가 `right: 0`으로 오른쪽 끝에 // 붙으므로(시안 지정) 이 여백은 **글자·버튼이 닫기 밑으로 들어가지 않게 비워 두는 // 자리**다. 20px + 아이콘 24px + 20px = 64px로 떨어져서 그 조합으로 적는다 // (`padding(7)` + `icon(5)` + `padding(7)`). // - line-height / letter-spacing: 시안 텍스트 스타일 heading/xsm의 값이나 Figma가 변수로 // export하지 않는다. -0.375px은 15px 기준이라 `-0.025em`으로 적는다(다른 컴포넌트와 같은 // 근거). // - box-sizing / position / display / flex: 구조 규칙. // // ⚠️ 그림자 색: 시안이 두 겹을 각각 `dark/alpha/black10`·`alpha/black10`으로 물려 놓았는데 // 값이 같고(rgba(0,0,0,0.10)) 앞쪽 이름의 토큰은 export에 없다. 두 겹 모두 `alpha-black10`을 // 쓴다 — `fox-toast`·`fox-select-option`과 같은 선택이다. // // ⚠️ 폭 세 값(640 / 640 / `grid(wrap-sm)`)은 **PC 기준이다.** 모바일에서 `grid(wrap-sm)`은 // 320px인데 최소 폭 640px이 그것을 이겨 화면 밖으로 넘친다 — 실제로 잘렸다. 그래서 모바일에서는 // 최소 폭을 풀고 판이 부모 폭을 채우게 둔다(아래 `fox.mobile` 블록). // // 시안이 모바일 스낵바의 폭을 따로 주면 그 값으로 바꾼다. 지금은 "화면을 넘지 않는다"만 // 보장한다. @use "@fox/styles/abstracts" as fox; .fox-snackbar { // 안쪽 여백이 바깥 폭을 늘리지 않게 한다 — 640px이 640px로 그려진다. box-sizing: border-box; // 닫기 버튼의 위치 기준이 된다. position: relative; display: flex; align-items: center; gap: fox.gap(5); inline-size: 64rem; min-inline-size: 64rem; max-inline-size: fox.grid(wrap-sm); padding-block: fox.padding(6); padding-inline-start: fox.padding(7); // 닫기 버튼이 앉을 자리(위 주석 참고). padding-inline-end: calc(#{fox.padding(7)} + #{fox.icon(5)} + #{fox.padding(7)}); border: fox.border(1) solid fox.color(border-neutral-subtle); border-radius: fox.radius(5); background: fox.color(surface-neutral-default); // 시안의 neutral/lv2 — 가까운 그림자와 먼 그림자 두 겹이다. box-shadow: fox.shadow(location1) fox.shadow(location1) fox.shadow(blur2) 0 fox.primitive(alpha-black10), fox.shadow(location3) fox.shadow(location3) fox.shadow(blur3) 0 fox.primitive(alpha-black10); // 모바일에서는 640px 고정을 풀고 부모 폭을 채운다(위 주석 참고). `min-inline-size`를 함께 // 풀지 않으면 640px이 그대로 남아 화면 밖으로 밀려 잘린다. @include fox.mobile { inline-size: 100%; min-inline-size: 0; max-inline-size: 100%; } // 글자 앞 아이콘. 글자가 길어 판이 높아져도 크기가 그대로여야 해서 줄어들지 않게 막는다. // // 색은 `icon-theme-primary`다(시안 확인). 글리프가 `fill: currentColor`라 슬롯에 색만 // 주면 그대로 입는다 — 굵기는 호출부가 넘기는 글리프가 정하고 시안은 `fill`이다. // // 크기는 시안 값 그대로 24px(`icon(5)`)이고 닫기 아이콘과 같다. 세로 정렬은 시안에 없어 // 판 규칙(가운데)을 그대로 따른다. &__icon { display: inline-flex; flex-shrink: 0; align-items: center; justify-content: center; inline-size: fox.icon(5); font-size: fox.icon(5); block-size: fox.icon(5); aspect-ratio: 1 / 1; color: fox.color(icon-theme-primary); } // 넘긴 SVG가 슬롯을 꽉 채운다 — 크기는 슬롯이 정하고 SVG는 따라온다. &__icon > * { inline-size: 100%; block-size: 100%; } // 남는 폭을 전부 가져가 글자 버튼을 오른쪽 끝으로 민다. `min-inline-size: 0`이 없으면 // 긴 글이 줄바꿈되지 않고 판을 밀어 넓힌다(flex 자식의 기본 최소 크기가 내용 크기라서다). // // `flex: 1 0 0`으로 남는 폭을 전부 가져가 글자 버튼을 오른쪽 끝으로 민다. 제목과 본문 // 사이는 `gap(1)`(2px)이고 둘 다 왼쪽에 붙는다 — 시안 확인 완료(`FoxAlert`의 같은 자리와 // 같은 값이다). &__content { display: flex; flex: 1 0 0; flex-direction: column; align-items: flex-start; gap: fox.gap(1); min-inline-size: 0; } &__title { align-self: stretch; 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 { align-self: stretch; color: fox.color(font-neutral-strong); font-family: fox.font-family(body); font-size: fox.font-size(body-md); font-weight: fox.font-weight(regular); line-height: 1.5; letter-spacing: -0.025em; } // 글자 버튼은 자기 모양을 그대로 갖는다 — 여기서는 줄어들지만 않게 막는다. &__action { flex-shrink: 0; } // 오른쪽 끝에 붙는다(시안 지정 `right: 0`). 흐름에서 빠져 있어 글자·버튼 배치에 끼어들지 // 않고, 판의 오른쪽 여백(64px)이 그 아래로 글자가 들어가지 않게 막아 준다. // // 누르는 자리는 48px 정사각이고 아이콘은 24px이다 — 손가락이 닿는 넓이를 아이콘보다 크게 // 잡은 것이라, 여백을 줄이면 모바일에서 누르기 어려워진다. // // ⚠️ 시안이 이 칸에 `align-items: flex-end`를 준다. 가로로는 시작(왼쪽), 세로로는 끝(아래)에 // 붙어서 **아이콘이 48px 칸의 왼쪽 아래로 치우친다** — 오른쪽 끝에서 16px, 아래에서 8px이다. // 시안 값을 그대로 옮긴 결과이니, 화면에서 가운데로 보여야 하면 `center`로 바꾼다. // // 세로는 판 위쪽에 붙는다(시안 지정 `top: 0`). 가운데 정렬이 아니라서, 본문이 늘어 판이 // 높아져도 닫기는 첫 줄 옆에 그대로 남는다. &__close { position: absolute; inset-inline-end: 0; inset-block-start: 0; display: flex; align-items: flex-end; inline-size: fox.number(16); font-size: fox.number(16); block-size: fox.number(16); aspect-ratio: 1 / 1; padding: fox.padding(3); border: none; background: none; color: fox.color(icon-neutral-default); cursor: pointer; } // 아이콘은 24px 정사각으로 못박는다 — 칸이 48px이라 늘어나게 두면 안 된다. &__close > * { flex-shrink: 0; inline-size: fox.icon(5); font-size: fox.icon(5); block-size: fox.icon(5); aspect-ratio: 1 / 1; } }