필터 바 (Filter bar)
목록 화면 상단의 「필터 + 검색·기능」 한 줄
데이터 목록을 보여주는 화면은 표 위에 항상 같은 형태의 한 줄을 둡니다. 왼쪽은 목록을 거르는 것(제목·셀렉트·드롭다운·기간), 오른쪽은 목록에 하는 것(검색·다운로드·추가)이에요. 화면이 좁아지면 줄바꿈으로 밀려나는 대신 오른쪽이 아이콘만 남고 왼쪽이 가로로 스크롤되며, 그래도 모자라면 위아래 두 줄로 나뉩니다. 전환은 마크업이 아니라 setupFilterBar()가 실제 내용 폭을 재서 정합니다.
규칙 (강제) · 코딩 에이전트용
목록 화면 상단 필터 줄의 강제 규칙. 상세·근거·트랩은 아래 본문.
- 좌우 두 그룹으로만 나눈다. 왼쪽
.ds-filter-bar__filters= 목록을 거르는 컨트롤, 오른쪽.ds-filter-bar__actions= 검색 + 목록에 하는 기능 버튼. 검색은 언제나 오른쪽 그룹의 맨 앞이다(왼쪽 필터 줄에 끼워 넣지 말 것). 기능 버튼이 2개 이상이면 검색과의 사이에.ds-filter-bar__divider. - 마크업은 4겹 고정이다 —
.ds-filter-bar> (.ds-filter-bar__title·.ds-filter-bar__filters>.ds-filter-bar__scroller·.ds-filter-bar__actions).__scroller를 빼지 말 것: 가로 스크롤과 셰브론이 그 한 겹에 걸려 있어서, 없으면 필터가 다시 줄바꿈된다. 예외는 거르는 필터가 아예 없는 화면(드라이브 — 검색 + 기능 버튼뿐)으로, 빈 스트립을 두지 말고__filters째 생략해__actions만 둔다. - 상태는 4단이고 JS가 정한다 — 무클래스(full) →
.is-left-compact(dense) →+.is-compact→+.is-stacked. 마크업·CSS로 상태 클래스를 직접 붙이지 말 것.setupFilterBar()가 각 단계의 자연 폭을 재서 매긴다. - 줄어드는 순서는 좌측 먼저, 버튼 라벨은 마지막이다. 라벨은 아이콘보다 정보가 많아 최대한 버틴다 — 드롭다운 fit·가로 스크롤·검색 접기가 먼저 일어나고, 그래도 안 들어갈 때 버튼이 아이콘이 된다.
- 필터가 많아 full이 의미 없는 화면은
.ds-filter-bar--dense로 opt-in한다(직원 관리). full을 건너뛰고 dense에서 시작하므로 검색은 처음부터 돋보기, 버튼은 라벨 유지가 된다. 이 화면들은 제목(__title)을 아예 두지 않는다 — LNB·페이지 헤더가 이미 같은 말을 한다. - px 미디어쿼리로 전환선을 만들지 말 것. 기준은 화면 폭이 아니라 그 페이지의 필터·버튼 개수다(같은 1280px에서 역할 관리는 full, 팀 관리는 compact가 맞다). 반응형 정본인
md·xl전환선은 이 패턴에 적용되지 않는다 → 브레이크포인트. - 필터는 어떤 상태에서도 줄바꿈하지 않는다. 넘치면 가로 스크롤 + 좌우 셰브론이다(셰브론은 JS가 주입하고 넘치는 방향에만 뜬다).
- 컴팩트에서 라벨이 사라지는 버튼은 반드시
.ds-ico를 가져야 한다.setupFilterBar()가.ds-ico를 가진 버튼에만.has-ico를 달고 CSS는 그 클래스만 본다 — 아이콘이 없는 버튼은 라벨이 남는다. 빈 네모를 막기 위한 안전장치이지 허용이 아니다. 새 버튼에는 아이콘을 준다. (:has(.ico)판정은 쓰지 않는다 — 문서 전체 스타일 재계산을 일으킨다.) - 아이콘만 남은 버튼의 툴팁은 자동이다.
setupFilterBar()가 라벨 텍스트를title+data-ds-tip="top"으로 옮기고 full로 돌아올 때 되돌린다. 손으로title을 달지 말 것(이미 있으면 그 값을 존중한다). - 검색은
.ds-filter-search래퍼 +<input>한 벌만 쓴다. 돋보기 토글·「취소」·지우기 X는 JS가 주입한다(직접 그리지 말 것 — 페이지마다 따로 만든 X가 이 패턴이 없애려던 갈라짐이다). 폭도 DS가 정하므로w-48·w-56같은 폭 유틸을 붙이지 말 것. - 검색어가 남아 있으면 검색창은 접히지 않는다. 접힌 돋보기 뒤에 검색어가 살아 있으면 "목록이 왜 이것뿐이지"가 된다 — JS가 강제하므로 되돌리지 말 것.
- 제목은 컴팩트에서 숨는다. 다만 제목이 상태 표시를 겸하면(「전체 구성원 ↔ 병동 배정 인원」)
.ds-filter-bar__title--keep으로 남긴다. 필터 묶음 안 인라인 라벨은.ds-filter-bar__label이고 컴팩트에서 숨으므로, 숨겨도 뜻이 남도록 옆 컨트롤의 placeholder가 스스로 설명해야 한다(「차기 신고 기한」 라벨이 사라지면 달력 두 개만 남는다). - 컴팩트에서 드롭다운 폭은 고른 값에 맞춘다(fit) — 최대 4글자 +
…. 폭은setupFilterBar()가 실제 글자를 재서 인라인으로 준다(네이티브<select>는 CSS만으로는 가장 긴 옵션 폭이 되므로 JS 가 필요하다). 고정 폭·max-width상수를 대신 박지 말 것. 잘린 값은select·.ms-dropdown__trigger가 이미 DS 툴팁의 기본 대상이라 hover하면 전체 값이 뜬다 → 툴팁. .is-stacked에서도 오른쪽 버튼은 아이콘 형태를 유지한다. 두 줄로 나뉜 건 이미 좁기 때문이고, 여기서 라벨을 되살리면 줄이 다시 넘친다.setupFilterBar()를 앱 시작 시 한 번 호출한다(두 번째는 no-op). 안 부르면 상태가 영영 full에 머물러 좁은 화면에서 잘린다.- 스코프: 화면 상단의 「조건(거르는 것) + 기능 버튼」 한 줄. 목록/표 위 필터 줄 외에 시트 기간 줄 · 근무표 병동·년월 줄도 같은 패턴이다(좌 = 조건, 우 = 기능). 모달 안 폼 줄에는 쓰지 않는다.
- 필터 바는 줄바꿈하지 않으므로 아래 줄을 같은 flex-wrap 컨테이너에 넣지 말 것. 예전 근무표처럼
w-full로 2행을 흘려 내리던 구조는 필터 바와 형제로 나눈다(줄 간격은 마진으로). - 다크 모드(2026-10-19) — 제목
.ds-filter-bar__title은--color-label-alternative(전 팔레트 neutral-600 이라 다크에서 묻힘), 스크롤 셰브론__nav· 검색 토글.ds-filter-search__toggle면은--color-bg-container(전--ds-bw-white고정). 확인: Playground 「미리보기 테마 → 다크」.
단일 출처
| 파트 | 파일 |
|---|---|
| CSS 정의 (정본) | packages/static/styles/design-system.css (.ds-filter-bar* · .ds-filter-search*) |
| 상태 판정 · 부품 주입 | packages/components/src/filter-bar.ts (setupFilterBar) |
| canonical 마크업 | apps/admin/src/views/tabs/roles.html (역할 관리 필터 줄) |
| admin 전용 보정 | apps/admin/src/styles/main.css (.ms-dropdown 폭·레이어) |
라이브 확인은 admin 역할 관리에서 창 폭을 줄여 보세요. 이 패턴은 폭 측정에 의존해서 docs의 정적 데모로는 재현되지 않습니다(데모를 따로 만들면 실제 구현과 갈라집니다 — 만들지 마세요).
구조
<div class="ds-filter-bar">
<h3 class="ds-filter-bar__title">역할 목록</h3>
<div class="ds-filter-bar__filters">
<div class="ds-filter-bar__scroller">
<select class="ds-select ds-select--32">…</select>
<select class="ds-select ds-select--32">…</select>
</div>
</div>
<div class="ds-filter-bar__actions">
<div class="ds-filter-search">
<input type="text" class="ds-input--32" placeholder="역할"
autocomplete="off" readonly @focus="$el.removeAttribute('readonly')" />
</div>
<div class="ds-filter-bar__divider"></div>
<button class="ds-btn ds-btn--solid ds-btn--primary ds-btn--md">
<span class="ds-ico shrink-0" x-html="$icon('ic-plus')"></span>
<span>역할 추가</span>
</button>
</div>
</div>페이지가 쓰는 건 이 다섯 클래스뿐입니다. 돋보기 토글·「취소」 버튼·좌우 셰브론은 JS가 주입하므로 마크업에 없습니다.
readonly + @focus 해제는 Chrome이 로그인 이메일을 검색창에 자동완성해 목록이 한 건으로 걸러지던 것을 막는 기존 처리입니다. 새 검색창에도 그대로 붙이세요.
세 가지 상태
| 상태 | 클래스 | 언제 | 모습 |
|---|---|---|---|
| full | (없음) | 원래 폭이 한 줄에 들어감 | 좌 필터(원래 폭) · 우 검색(192px) + 라벨 달린 버튼 |
| dense | .is-left-compact | full이 안 들어감 (또는 --dense opt-in) | 좌측만 줄임 — 드롭다운 fit · 가로 스크롤 + 셰브론 · 검색은 돋보기. 버튼은 라벨 유지 |
| compact | +.is-compact | dense로도 안 들어감 | 거기에 더해 제목·인라인 라벨 숨김 · 버튼도 정사각 아이콘(라벨은 툴팁) |
| stacked | +.is-stacked | compact로도 안 들어감 | 위 줄 = 제목 + 기능(우측 정렬) / 아래 줄 = 필터 스트립 전폭 |
.is-compact·.is-stacked에는 .is-left-compact가 늘 함께 붙습니다. 좌측 규칙은 그 한 축만 보면 되고, 우측 규칙만 두 상태를 따로 봅니다.
판정식은 이렇습니다.
full ← --dense 가 아니고, 원래 폭 ≤ 컨테이너
dense ← 좌측만 줄인 폭 ≤ 컨테이너
stacked ← 아이콘화한 우측 그룹 + 필터 최소폭(200px) + 8 > 컨테이너
compact ← 그 밖200px은 .ms-dropdown 기본 폭(162px) + 셰브론 여유입니다. 이보다 좁은 스트립은 반쪽짜리 드롭다운과 셰브론만 남아 읽을 수 없어서 두 줄로 내립니다.
왜 미디어쿼리가 아니라 측정인가
전환 기준이 화면 폭이 아니라 그 페이지의 필터·버튼 개수이기 때문입니다. 같은 1280px에서도 역할 관리(셀렉트 3 + 버튼 1)는 한 줄에 여유가 있고, 팀 관리(드롭다운 2 + 버튼 4 + 검색)는 이미 넘칩니다. px 전환선을 하나 박으면 둘 중 하나는 늘 틀립니다.
게다가 개수는 런타임에 바뀝니다 — 표에서 행을 고르면 왼쪽 그룹이 일괄 액션 바로 통째로 교체되고, 병동 화면에선 버튼이 x-show로 늘고 줄어요. 그래서 setupFilterBar()는 ResizeObserver와 MutationObserver를 함께 봅니다.
깜빡임·진동이 없는 이유
판정에 쓰는 값은 언제나 full 형태의 자연 폭입니다. 한 프레임 안에서 동기로 .is-measuring을 붙여 자식이 줄지 않게 고정하고, 상태 클래스를 뗀 뒤 폭을 읽고 즉시 되돌립니다. 읽기만 하고 그 사이에 페인트가 없으므로 화면에는 아무 일도 일어나지 않아요.
판정이 현재 상태와 무관한 순수 함수라서 "compact로 줄였더니 들어가서 full로 돌아가고 다시 넘치는" 진동이 구조적으로 생기지 않습니다. 이 성질을 깨는 변경(예: 현재 상태에서 잰 폭으로 판정)은 하지 마세요.
부품
제목과 라벨
.ds-filter-bar__title은 화면 제목(「역할 목록」)이고 컴팩트에서 숨습니다. 다만 제목이 지금 무엇을 보고 있는지를 말하고 있다면 숨기면 안 됩니다 — 팀 관리의 「전체 구성원 ↔ 병동 배정 인원」이 그런 경우라 .ds-filter-bar__title--keep을 붙입니다.
.ds-filter-bar__label은 필터 묶음 안의 인라인 라벨(「차기 신고 기한」)이고 역시 컴팩트에서 숨습니다. 숨어도 뜻이 남아야 하므로 옆 컨트롤의 placeholder가 스스로 설명해야 합니다. 「시작일 / 종료일」이 아니라 「신고기한 시작 / 신고기한 종료」처럼요.
접이식 검색
컴팩트부터 검색은 돋보기 하나로 접힙니다. 누르면 입력칸이 열리고(placeholder와 함께) 오른쪽에 「취소」가 붙습니다. 취소는 검색어를 지우고 다시 접습니다. Esc도 같습니다. 검색어가 있으면 입력칸 오른쪽 끝에 지우기 X가 뜨는데(비우기만 하고 접지는 않음), 이것도 JS가 주입하므로 페이지가 따로 그리지 않습니다.
검색어가 있으면 접히지 않습니다. 접힌 돋보기 뒤에 검색어가 살아 있으면 목록이 왜 이것뿐인지 알 방법이 없기 때문이에요. 이 규칙은 JS가 강제합니다.
「취소」가 값을 비울 때는 input 이벤트를 함께 쏩니다 — input.value = ""만으로는 Alpine의 x-model이 모르기 때문입니다. 페이지의 @input.debounce 핸들러도 여기서 함께 걸립니다.
아이콘 버튼과 툴팁
컴팩트에서 오른쪽 버튼은 라벨을 숨기고 정사각형이 됩니다. 높이는 사이즈 축(.ds-btn--md = 32px)이 이미 정하므로 aspect-ratio로 폭을 맞춰요 — .ds-btn의 min-width: 80px를 풀어 주는 게 핵심입니다.
숨긴 라벨은 툴팁으로 갑니다. setupFilterBar()가 라벨 텍스트를 읽어 title + data-ds-tip="top"을 달고, full로 돌아오면 지웁니다. 이미 title이 있는 버튼은 그 값을 존중하고 건드리지 않아요.
필터 스트립과 셰브론
컴팩트부터 .ds-filter-bar__scroller가 가로 스크롤 박스가 됩니다. 스크롤바는 숨기고, 대신 넘치는 방향에만 원형 셰브론 버튼과 가장자리 페이드가 뜹니다. 한 번 누르면 스트립 폭의 80%만큼 밀립니다.
드롭다운은 컴팩트에서 고른 값에 맞춰 좁아집니다(fit) — 최대 **4글자 + …**입니다. 「활성」은 70px, 「전체 팀(병동)」은 96px로 서로 다르게 서요(실측, --32 기준).
폭을 JS가 재는 이유는 네이티브 <select>가 교체 요소라서입니다. 폭을 CSS에 맡기면 지금 고른 값이 아니라 가장 긴 옵션 기준으로 벌어져요 — 「전체」를 고르고 있어도 「2층 신생아중환자실」만큼 넓어집니다. width: fit-content도 같은 값이고, ch·em으로 4글자를 흉내 내면 한글(약 1em)과 영문·숫자(약 0.5em)가 섞였을 때 어긋납니다. 그래서 캔버스로 실제 글자 폭을 재서 인라인 width를 줍니다. .ms-dropdown은 일반 요소라 라벨에 max-width만 주면 트리거가 shrink-to-fit으로 따라 줄어듭니다.
같은 이유로 컴팩트에서는 셀렉트의 좌우 여백도 좁힙니다(.ds-select--28과 같은 값 — 새 수치를 만들지 않습니다). 기본 여백 + 셰브론 자리만 56px이라, 그대로 두면 「활성」 두 글자짜리 필터도 80px을 먹어서 폭을 맞춘 의미가 사라집니다. 높이는 그대로 32px입니다(옆 컨트롤과 flush 유지).
값을 바꾸면 폭도 따라 바뀝니다(change를 듣습니다). full로 돌아가면 인라인 폭은 걷힙니다. 잘린 값은 hover하면 전체가 툴팁으로 뜹니다(select·.ms-dropdown__trigger는 DS 툴팁의 기본 대상).
트랩
overflow-x: auto는overflow-y: visible과 같이 못 씁니다. 가로만 열어도 세로가 함께auto가 되므로, 스트립 안의 절대배치 메뉴가 경계에서 잘립니다. 그래서.ms-dropdown__menu는 컴팩트에서만position: fixed로 띄우고 좌표를setupFilterBar()가positionFloatingMenu로 잡아 줍니다(스트립을 스크롤하면 따라옵니다). 스트립 안에 새로운 떠 있는 메뉴를 넣는다면filter-bar.ts의POPUP_PAIRS에 한 줄 추가하고 앱 CSS에position: fixed를 주세요. 네이티브<select>는 OS 레이어라 무관합니다..is-measuring은 좌측 규칙을 특이도로 이겨야 합니다..ds-filter-bar.is-left-compact .ds-filter-bar__filters가flex: 1 1 0이라, 측정 규칙을> *(0,2,0)로만 쓰면 그 규칙(0,3,0)에 져서 재는 동안에도 좌측이 줄어듭니다 — 넘치는데scrollWidth가 컨테이너 안에 들어와 무슨 폭에서든 dense로 판정됩니다(2026-09-16 재현). 부품별 선택자로 특이도를 맞추고, 스크롤 박스도 재는 동안overflow: visible로 풀어야 넘침이 폭에 나타납니다.- 판정 전에 맞춰 둔 폭을 반드시 걷어내야 합니다. 상태 판정은 늘 원래 폭으로 해야 하는데, 컴팩트에서 준 인라인
width가 남아 있으면 그 좁아진 값으로 자연 폭을 재게 되어 "좁히니 들어가서 full로 돌아가고 다시 넘치는" 진동이 생깁니다.decide()첫 줄이fitControls(b, false)인 이유예요. - 셰브론은
<button>이라 전역 4px radius 강제에 걸립니다. 원형으로 되돌리려고border-radius를 선언해도 소용없어요 — 모서리 축ds-r-999를 클래스로 함께 답니다(!important금지) → 모서리. - 스크롤 박스는 포커스 링을 자릅니다.
__scroller는 위아래 2px 패딩 + 같은 값의 음수 마진으로 여유를 만들어 둡니다(레이아웃 높이 변화 0). 이 쌍을 한쪽만 지우면 줄 높이가 4px 튑니다. .is-measuring규칙은.is-stacked규칙보다 뒤에 와야 같은 특이도(0,2,0)에서 source order로 이깁니다. CSS 블록 순서를 바꾸지 마세요.- admin의
.employee-filter-toolbar는 그 안의<button>을 32px·1px 테두리로 강제합니다. 주입 버튼(24px 셰브론·텍스트 「취소」·20px 지우기 X)은.ds-btn이 아니라 기존 제외 명단에 안 걸려 네모로 뭉개집니다 — adminmain.css의:not()체인에.ds-filter-*클래스가 들어 있는 이유예요. 그 툴바 클래스를 새 화면에 쓰지 마세요. - 자연 폭을
scrollWidth로 재지 마세요. flex 컨테이너에 오른쪽 패딩이 있으면 브라우저가 넘친 영역에 그 패딩을 넣지 않아 폭이 패딩만큼 적게 잡힙니다. 시트 툴바(px-4)에서 실제로 6px 넘치는데 "들어간다"로 판정돼 필터가 조용히 눌렸습니다(재현·수정). 지금은 패딩 뺀 안쪽 폭과 보이는 자식 폭의 합을 비교합니다. x-show는 style 속성만,:class는 class 속성만 바꿉니다. 자식이 늘거나 줄지 않아 childList 관찰에 안 잡히고, 바 자신의 크기도 그대로라 ResizeObserver도 울리지 않아요 — 그런데 행 선택·탭 전환·병동 스코프가 바로 폭이 바뀌는 순간입니다. 그래서 바마다 속성까지 보는 MutationObserver를 붙였고, 우리 쓰기(상태 클래스·인라인 폭·팝오버 좌표)가 자기를 다시 깨우지 않도록 쓰는 동안 관찰을 끊습니다(quiet()). 플래그로는 안 됩니다 — 콜백이 비동기라 플래그가 내려간 뒤에 옵니다. 스타일만 바꿔 상태가 뒤집히는 것과, 가만히 둘 때 바 안 변경 기록 0건을 확인했습니다.- 스트립 안
.ds-menu팝오버(시트 월 선택)도 스크롤 박스에 잘립니다. 좌측이 줄어든 단계에서만position: fixed로 띄우고, 열리는 그 프레임에 좌표를 잡습니다(rAF까지 미루면 한 프레임 좌상단에 보입니다)..ds-datepicker__pop은 원래fixed라 해당 없습니다. - 숨은 탭 안의 바는
clientWidth가 0입니다. 그때는 상태를 건드리지 않고 그대로 둡니다(0으로 재면 전부 stacked가 됩니다). - 아이콘만 남은 버튼의 라벨은
display: none으로 숨기면 안 됩니다 — 눈에서만 숨깁니다.display: none이면 버튼의 접근 이름(스크린리더·테스트가 읽는 이름)이 라벨 대신title로 바뀝니다. 직원 관리 「신규 직원 추가」가 1280px에서 「신규 멤버를 추가합니다.」로 읽혀 스크린리더와 e2e(getByRole({ name: "직원 추가" }))가 버튼을 못 찾았습니다(2026-09-17 재현·수정). 지금은 표준 visually-hidden 조합(position: absolute· 1px ·clip)이라 폭·높이는 그대로이고 이름은 라벨로 남습니다. - 스트립 안 컨트롤은 줄어들면 안 됩니다 — 넘치면 스크롤이어야 합니다. flex 기본값(
flex-shrink: 1)이면 스크롤되기 전에 먼저 눌리는데, 날짜 피커(.ds-datepicker=w-36+ 폭 100% 트리거)는 최소 폭이 0에 가까워 400px에서 144 → 84px로 눌려 날짜가 잘렸습니다(근태 현황, 재현·수정). 셀렉트는 최소 폭이 글자 폭이라 드러나지 않았을 뿐이에요. DS가.ds-filter-bar__scroller > *에flex-shrink: 0을 주므로 마크업에shrink-0을 따로 달 필요는 없습니다. .ms-dropdown은 DS에 없고 앱마다 복사본입니다(admin · team-managermain.css). 컴팩트 보정(min-width: 0· 메뉴position: fixed)도 그 드롭다운을 쓰는 앱의main.css에 각각 있어야 합니다 — 한쪽에만 두면 다른 앱에서 메뉴가 스트립에 잘립니다.
적용 현황
| 화면 | 상태 |
|---|---|
| admin 역할 관리 | 적용 (canonical) |
| admin 병동 관리 | 적용 |
| admin 팀 관리 | 적용 (조직 레벨 · 팀 레벨 2벌. 제목이 「전체 구성원 ↔ 병동 배정 인원」이라 --keep) |
| admin 직원 관리 | 적용 · --dense opt-in (필터 7개. 제목 없음 · 검색은 처음부터 돋보기 · 버튼은 라벨 유지) |
| admin 스케줄러(근무표) 상단 줄 | 적용 — 좌 = 병동·년월, 우 = 환자수 관리·병동 설정. 아래 환자수 입력 줄은 형제로 분리 |
| hr-system 시트 기간 줄 | 적용 — 좌 = 월 선택, 우 = 이전 월 가져오기·단축키·새 시트 만들기. setupFilterBar()를 hr-system main.ts에서도 호출 |
| admin 근태 현황 5탭 | 적용 — 연차(현황 · 결재 2벌) · 외근 · 근로시간 마감(개인별)은 admin, 근무 · 연장근무는 team-manager iframe이라 tm main.ts에서도 setupFilterBar()를 부르고 tm main.css에 .ms-dropdown 보정을 둔다. 근로시간 마감 검색은 「검색」 버튼형 → 즉시 검색, 다운로드는 우측 그룹으로 |
| admin 문서 결재 · 전자 서명 · 글로벌 파라미터 | 이관 예정 |
| admin 공지 (general-affairs iframe) | 적용 — 좌 = 카테고리 · 게시 상태, 우 = 검색 · 공지 작성. 두 셀렉트가 모두 「전체」라 구분이 안 되던 것을 「전체 카테고리」·「전체 상태」로. general-affairs main.ts에서도 setupFilterBar() 호출 |
| admin 드라이브 (drive iframe) | 적용 — 필터 없이 __actions만(검색 · 권한 관리 · 파일 업로드). drive main.ts에서도 호출. 좌측 폴더 트리가 좁은 폭에서도 220px를 차지해 400px에선 이 줄에 132px만 남던 것(검색을 펼치면 「취소」가 밖으로 밀림)은 <768에서 트리를 서랍으로 바꿔 해결 — 필터 바는 자기 줄의 폭만 보므로 옆 패널이 폭을 뺏으면 필터 바로는 못 푼다 |
| 그 밖의 임베드 앱(경영 대시보드) | 별도 앱 — 2차 |
이관 전 화면은 규칙 위반이 아니라 마이그레이션 대상입니다. 이미 작업 중인 파일 안에 있을 때 바꾸고, 필터 줄을 찾아다니며 전수 교체하지 마세요.
변경 이력
| 날짜 | 내용 |
|---|---|
| 2026-09-17 | 아이콘만 남은 버튼의 라벨을 display: none 대신 visually-hidden 으로 — 접근 이름이 title 로 바뀌던 것 |
| 2026-09-16 | 공지(general-affairs)·드라이브 적용. 필터가 없는 화면은 __filters째 생략하는 예외 명시 |
| 2026-09-16 | 근태 현황 5탭 적용(team-manager 임베드 2탭 포함). 스트립 안 컨트롤 flex-shrink: 0 — 날짜 피커가 스크롤 대신 눌리던 것 |
| 2026-09-16 | 시트(hr-system)·스케줄러 상단 줄 적용. 측정을 자식 폭 합으로 교체(오른쪽 패딩 누락) · 바 단위 속성 관찰(x-show 재판정) · 스트립 안 .ds-menu 팝오버 레이어 |
| 2026-09-16 | dense 단 추가 — 좌측을 먼저 줄이고 버튼 라벨은 마지막까지 유지. 필터 많은 화면은 --dense로 그 단에서 시작 |
| 2026-09-16 | 지우기 X를 DS 주입으로 흡수(페이지별 수제 X 제거) · 병동·팀·직원 관리 이관 |
| 2026-09-16 | 필터 바 패턴 신규 작성 — 좌/우 두 그룹 · 폭 측정 기반 3단(full/compact/stacked) · 접이식 검색 · 아이콘 버튼 자동 툴팁 · 필터 가로 스크롤 셰브론 · 컴팩트 드롭다운 fit 폭(최대 4글자+…). 첫 적용 admin 역할 관리 |