File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
08-14
08-14
08-18
08-18
08-14
08-19
08-19
08-14
08-14
08-18
08-18
08-14
08-18
08-18
08-19
08-19
// FoxSelectText — 시안: 통합관리자페이지 디자인시스템 Figma select-txt
//
// 글자만 있는 셀렉트다. 테두리도 고정 높이도 없고, 배경만 세 상태로 바뀐다.
//
// 마크업 계약 (React 밖 소비자용):
// <div class="fox-select-text fox-select-text--lg"> <!-- 목록의 위치 기준 -->
// <button type="button" class="fox-select-text__trigger" id="st" role="combobox"
// aria-haspopup="listbox" aria-expanded="true" aria-controls="st-listbox">
// <span class="fox-select-text__value">2026년 8월</span>
// <span class="fox-select-text__icon">…화살표 svg…</span>
// </button>
// <ul class="fox-select-option fox-select-text__list" id="st-listbox" role="listbox">
// <li class="fox-select-option-item" …>…</li>
// </ul> <!-- 열렸을 때만 -->
// </div>
//
// `fox-select`와 무엇이 다른가: **폼 한 칸이 아니다.** 라벨·힌트·에러·필수 표시가 없고,
// 배경 계열도 다르다 — `fox-select`는 중립(`action-default`, 흰 바탕에 테두리)이지만 이쪽은
// 강조(`action-primary`) 계열이라 기본이 **투명**이고 hover·pressed에서만 연한 파랑이 깔린다.
// 크기가 글자 **계열까지** 바꾸는 것도 여기뿐이다 — lg는 heading/md(제목용 20px bold,
// `font-family-title`), md·sm은 label/*-strong(본문용 medium)이다. 폼 한 칸이라면 크기를
// 바꿨다고 제목 폰트가 될 이유가 없다. 즉 이것은 "지금 무엇을 보고 있는지"를 글자로 보여
// 주면서 그 자리에서 바꾸게 하는 컨트롤이다.
//
// 크기가 바꾸는 것: 여백·간격·글자(계열·크기·굵기·줄높이)·아이콘 크기·글자 색. 모서리만
// 세 크기가 같다(`form-radius-xsm`).
//
// ⚠️ lg만 위아래 여백이 없다(`padding: 0 var(--padding-2)`). md·sm은 사방에 준다 — 시안이
// 그렇게 준다. lg는 줄높이가 140%라 그 자체로 위아래 여백을 만든다.
//
// ⚠️ 비활성은 시안에 없다. 색을 임의로 만들지 않고 hover·pressed만 막는다 — 네이티브
// `disabled`를 주면 커서와 클릭만 죽고 모양은 기본과 같다(`fox-tag`와 같은 처리).
//
// 토큰이 아닌 값(`fox-button`·`fox-tag`와 동일한 근거로 승인된 항목):
// - letter-spacing: 시안 텍스트 스타일의 값이나 Figma가 변수로 export하지 않는다. 세 크기가
// 모두 `-0.025em`이라 규칙 하나로 끝난다(20px→-0.5px, 15px→-0.375px, 13px→-0.325px).
// px이 아니라 em인 이유는 글자 크기가 크기 수식어마다, 그리고 브레이크포인트마다
// 달라지기 때문이다 — px으로 박으면 그 자리에서 비율이 어긋난다.
// - line-height 1.4 / 1: 시안의 140% / 100%. 비율이라 토큰 스케일에 대응이 없다.
// - box-sizing / display / flex / cursor / white-space / aspect-ratio: 구조·상호작용 규칙.
// - `fox-select-text__list`의 위치·폭: 구조 규칙(아래 주석 참고).
@use "@fox/styles/abstracts" as fox;
// 목록의 위치 기준. `inline-block`인 이유는 글자만큼만 넓어야 하기 때문이다 — 블록이면 줄
// 전체를 차지해 제목 옆에 다른 것을 놓을 수 없다.
.fox-select-text {
position: relative;
display: inline-block;
&__trigger {
// 여백이 바깥 크기를 늘리지 않게 한다.
box-sizing: border-box;
display: inline-flex;
align-items: center;
border: none;
border-radius: fox.form(radius-xsm);
background: fox.color(action-primary);
cursor: pointer;
letter-spacing: -0.025em;
white-space: nowrap;
&:hover:not(:disabled) {
background: fox.color(action-primary-hover);
}
// 시안의 pressed는 `:active`가 받는다.
&:active:not(:disabled) {
background: fox.color(action-primary-pressed);
}
&:disabled {
cursor: default;
}
}
// 글자만 줄어들게 해 화살표가 밀리지 않게 한다.
&__value {
min-inline-size: 0;
}
&__icon {
display: inline-flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
// 시안이 아이콘에 정사각 비율을 못박는다. 자리가 좁아 눌려도 글리프가 찌그러지지 않는다.
aspect-ratio: 1 / 1;
}
// 넘긴 SVG가 슬롯을 꽉 채운다 — 크기는 슬롯이 정하고 SVG는 따라온다.
&__icon > * {
inline-size: 100%;
block-size: 100%;
}
// ── 크기 ────────────────────────────────────────────────────────────────────
// lg만 제목 계열이다(파일 상단 주석 참고).
&--lg &__trigger {
padding-block: 0;
padding-inline: fox.padding(2);
gap: fox.gap(2);
color: fox.color(font-neutral-strong);
font-family: fox.font-family(title);
font-size: fox.font-size(heading-md);
font-weight: fox.font-weight(bold);
line-height: 1.4;
}
&--lg &__icon {
inline-size: fox.form(icon-md);
block-size: fox.form(icon-md);
}
&--md &__trigger {
padding: fox.padding(2);
gap: fox.gap(2);
color: fox.color(font-neutral-default);
font-family: fox.font-family(body);
font-size: fox.font-size(label-md);
font-weight: fox.font-weight(medium);
line-height: 1;
}
&--md &__icon {
inline-size: fox.form(icon-sm);
block-size: fox.form(icon-sm);
}
// sm만 여백과 간격이 한 단계 좁다.
&--sm &__trigger {
padding: fox.padding(1);
gap: fox.gap(1);
color: fox.color(font-neutral-default);
font-family: fox.font-family(body);
font-size: fox.font-size(label-sm);
font-weight: fox.font-weight(medium);
line-height: 1;
}
&--sm &__icon {
inline-size: fox.form(icon-xsm);
block-size: fox.form(icon-xsm);
}
}
// `fox-select-option`의 기본값은 `inset-inline: 0`, 즉 여는 요소와 같은 폭이다. 폼 한 칸을
// 채우는 셀렉트에서는 맞지만 이쪽은 글자만큼만 좁아서 목록이 읽을 수 없게 눌린다. 왼쪽만
// 맞추고 폭은 **내용이** 정하게 한다.
//
// ⚠️ `min-inline-size: 100%`(트리거보다 좁아지지 말 것)를 걸지 않는다. 퍼센트는 위치 기준인
// `.fox-select-text`의 폭을 보는데, 이 요소는 `inline-block`이라 세로 flex·grid 안에 놓이면
// 부모의 `align-items: stretch`에 늘어난다. 그러면 목록이 그 늘어난 폭을 그대로 물려받아
// 화면을 가로지른다 — 목록 폭이 호출부 레이아웃에 끌려다니게 된다. 내용 폭만 보면 어디에
// 놓이든 목록은 같은 크기다.
//
// 클래스 둘을 겹쳐 적는다 — 한쪽이 `inset-inline` 단축, 이쪽이 `inset-inline-end` 낱개라
// 특정도가 같으면 파일을 @use 하는 순서가 승부를 가른다. 겹쳐 두면 순서와 무관하게 이긴다.
.fox-select-option.fox-select-text__list {
inset-inline-end: auto;
inline-size: max-content;
}