이 예시는 CSS 및 브라우저용 JavaScript 패키지인 Bootstrap 5.9.0의 CoreUI를 사용합니다. CoreUI에는 프레임워크별 패키지도 있지만, 해당 패키지의 컴포넌트 가져오기 방식은 이 예시의 데이터 속성과 호환되지 않습니다. 이 설정은 React나 Vue 공급자를 전제로 하지 않고, 서로 맞는 스타일시트와 번들 스크립트를 제공합니다.
검토 작업 공간은 무료 카드, 표, 양식, 탭, 토스트를 사용합니다. PRO 데이터 그리드나 고급 유료 선택기를 조용히 대체 요소로 사용하지 않습니다. 이 경계를 지키면 문서화되지 않은 템플릿을 구매하지 않고도 지정된 패키지만으로 전체 예제를 재현할 수 있습니다.
CoreUI의 밝은 테마 기본 색상은 #5856d6이며, 기본 색상 위의 대비 색상은 흰색입니다. 검토 원칙 카드에서는 primary-bg-subtle, primary-border-subtle, primary-text-emphasis를 함께 사용합니다. 이 이름은 서로 조화를 이루는 표면 스타일을 나타내므로, 임의의 전경색을 색조가 들어간 배경에 배치하지 않습니다.
높음 및 보통 배지도 danger 또는 warning의 은은한 표면 스타일과 강조 텍스트를 짝지어 사용합니다. 색상과 관계없이 텍스트 라벨로 분류를 명확히 알 수 있습니다. 이는 디자인 검토 우선순위를 보여 주는 예시일 뿐, 제품의 모든 빨간색 배지가 동일한 도메인 상태를 의미해야 한다는 뜻은 아닙니다.
컴파일된 CoreUI의 본문 기본값은 1 rem 글꼴 크기와 1.5 줄 높이입니다. 이 예시에서는 검토 대기열을 조밀하게 표시하기 위해 표에 14픽셀 텍스트를, 열 라벨에 12픽셀 텍스트를 사용합니다. 페이지 제목과 요약 값에는 각각 별도의 큰 글꼴 크기를 적용했습니다. 이 가이드에서는 이러한 애플리케이션 수준의 재정의를 라이브러리 전체의 기본값이라고 잘못 설명하지 않습니다.
세 개의 맥락 카드에는 기본 반응형 그리드와 g-3 거터를 사용합니다. 받은 편지함과 보관됨 카드는 작은 화면에서도 나란히 표시되고, 세 번째 설명 카드는 한 행 전체를 차지합니다. 모든 수치는 화면에 표시된 샘플 레코드 5개에서 가져왔으며, 무관한 대시보드 지표나 임의로 만든 성장 차트는 없습니다.
받은 편지함과 보관함은 버튼 기반 탭이며, data-coreui-toggle="tab"과 해당 탭 패널을 가리키는 대상을 사용합니다. 마크업에는 tablist, tab, tabpanel, aria-controls, aria-labelledby 관계가 포함됩니다. CoreUI JavaScript 컴포넌트가 활성 상태와 표시 여부를 갱신하므로, 애플리케이션 핸들러가 밑줄만 바꾸는 방식이 아닙니다.
nav-underline-border 클래스가 이 컴포넌트의 시각적 스타일을 지정합니다. 탭을 바꿔도 레코드가 보관되거나 선택 항목이 지워지지 않습니다. 탭은 로컬 대기열에서 표시할 항목을 제어하고, 애플리케이션의 선택 및 보관 Set은 별도의 상태로 유지됩니다.
모든 요청에는 안정적인 R 시리즈 ID가 있습니다. 행을 선택하면 해당 ID가 selected Set에 추가됩니다. 헤더 체크박스는 받은 편지함의 현재 레코드를 모두 선택한 경우에만 체크되고, 일부만 선택한 경우에는 부분 선택 상태가 되며, 받은 편지함이 비어 있으면 비활성화됩니다. 대기열이 렌더링될 때마다 이 상태를 다시 계산합니다.
선택된 항목이 없으면 일괄 처리 버튼을 비활성화하고, 옆의 텍스트에 현재 선택 개수를 표시합니다. 보관 작업 후에는 선택을 지워 이미 보관된 레코드가 다음 일괄 처리에 실수로 남지 않도록 합니다. 원래 요청과 담당자는 레코드에 그대로 연결되며, 일반적인 ‘해결됨’ 라벨로 대체되지 않습니다.
애플리케이션은 가장 최근에 보관한 일괄 처리의 정확한 ID를 기록한 다음, 기본 Toast 인스턴스의 show 메서드를 호출합니다. 알림에 실행 취소 작업이 있으므로 autohide는 false로 설정합니다. 임의의 짧은 타이머가 지나면 기회를 없애는 예제가 아닙니다. 토스트는 사용자가 직접 닫을 수 있습니다.
실행 취소를 누르면 저장된 ID만 archived Set에서 제거하고, 마지막 일괄 처리 스냅샷을 지운 뒤 실행 취소 작업을 비활성화합니다. 이후 일괄 처리를 실행 취소하더라도 이전에 보관한 항목은 계속 보관 상태로 남습니다. 이는 한 단계만 지원하는 작업이며, 완전한 실행 취소 기록을 제공한다고 오해하게 하지 않습니다.
받은 편지함 표에는 메모 제목, 구체적인 변경 요청, 영역, 우선순위, 담당자가 표시됩니다. 보관함에는 제목, ID, 담당자, 전체 요청 내용을 유지하는 간결한 목록이 표시됩니다. 해결된 항목의 표 장식은 줄이면서도 무엇이 변경되었는지 파악하는 데 필요한 정보는 삭제하지 않습니다.
모바일에서는 table-responsive 안에 표의 최소 너비 740px을 유지합니다. 이름이 지정된 영역은 키보드로 포커스할 수 있고 포커스 표시도 명확합니다. 가로로 이동해도 문서 전체가 넓어지지 않고 표 안에서 이동합니다. 스크롤바를 피하려고 중요한 열을 없애면 검토 작업의 효용이 떨어집니다.
토스트에는 공손한 상태 역할과 원자적 알림 설정이 적용됩니다. 두 번째 인라인 상태 메시지는 로컬 보관 또는 실행 취소 결과를 설명합니다. 연결된 이슈 트래커가 없다는 점이 페이지에 명시되어 있으므로, 어느 메시지도 외부 이슈가 종료되었다고 알리지 않습니다. 미리보기에서는 메모리 내 Set만 변경하므로 페이지를 다시 로드하면 원래 메모 5개가 복원됩니다.
보관 목록을 포함해 레코드 제목과 설명을 textContent로 삽입합니다. 정해진 클래스 매핑은 두 가지 우선순위를 나타냅니다. 이렇게 하면 사용자 입력과 HTML 구조가 분리되어 예제의 데이터 흐름을 쉽게 살펴볼 수 있습니다.
전체 설정에서는 패키지의 dist 에셋을 로컬 공개 경로에 복사하고, app.js보다 먼저 coreui.bundle.min.js를 로드합니다. 이 번들은 coreui.Toast를 노출하고 data-coreui 속성에 탭 동작을 제공합니다. 이 구성에는 별도의 Bootstrap 런타임이 필요하지 않으며, 추가하면 소유 관계가 중복되어 혼란을 일으킬 수 있습니다.
data-coreui-theme="light"로 라이트 테마를 명시합니다. 더 큰 애플리케이션에서는 CoreUI의 색상 모드 메커니즘을 적용할 수 있지만, 의미적 텍스트 색상과 표면 색상 조합을 함께 검토해야 합니다. 로컬 검증에서는 부분 선택, 2개 레코드 일괄 처리, 보관 탭 확인, 실행 취소로 복원, 390px 뷰포트를 점검했습니다.
이 번들에서는 다른 라이브러리에서 복사한 data-bs-*가 아니라 data-coreui-* 속성을 사용하세요.
테이블에는 이름이 지정된 로컬 가로 스크롤 영역이 있습니다.
우선순위 색상에는 High/Medium 레이블을 함께 표시합니다.
일괄 선택 및 마지막 일괄 처리 실행 취소
안정적인 레코드 ID로 선택 항목과 보관 항목을 관리하고, 기본 Toast로 알림 표시 여부를 처리합니다.
자바스크립트
const records=[{id:'R-201',title:'Explain the empty search result',detail:'Offer a way to clear the filter without losing the query.',area:'Search',priority:'High',owner:'Maya'},{id:'R-202',title:'Label the compact navigation icons',detail:'The collapsed sidebar needs understandable accessible names.',area:'Navigation',priority:'High',owner:'Jules'},{id:'R-203',title:'Keep form errors beside their fields',detail:'Retain entered values after a failed submission.',area:'Forms',priority:'High',owner:'Alex'},{id:'R-204',title:'Align the table’s numeric column',detail:'Right-align quantities so differences can be scanned.',area:'Tables',priority:'Medium',owner:'Sam'},{id:'R-205',title:'Clarify the download format',detail:'Show the file type before the visitor starts the download.',area:'Content',priority:'Medium',owner:'Maya'}];const archived=new Set(),selected=new Set();let lastBatch=[];const q=s=>document.querySelector(s),toast=coreui.Toast.getOrCreateInstance(q('#undo-toast'),{autohide:false});
function render(){const inbox=records.filter(r=>!archived.has(r.id));q('#inbox-rows').replaceChildren();q('#archive-list').replaceChildren();records.forEach(r=>{if(archived.has(r.id)){let li=document.createElement('li');li.className='list-group-item px-0';let title=document.createElement('strong'),detail=document.createElement('p');title.textContent=`${r.id} · ${r.title}`;detail.className='small text-body-secondary mb-0 mt-1';detail.textContent=`${r.owner} · ${r.detail}`;li.append(title,detail);q('#archive-list').append(li);return;}let tr=document.createElement('tr');tr.innerHTML='<td><input class="form-check-input" type="checkbox"></td><td><strong></strong><p class="small text-body-secondary mb-0 mt-1"></p></td><td></td><td><span class="badge"></span></td><td></td>';let cells=tr.children,check=tr.querySelector('input');check.setAttribute('aria-label',`Select ${r.id}: ${r.title}`);check.checked=selected.has(r.id);check.onchange=()=>{check.checked?selected.add(r.id):selected.delete(r.id);sync();};cells[1].querySelector('strong').textContent=r.title;cells[1].querySelector('p').textContent=r.detail;cells[2].textContent=r.area;let badge=tr.querySelector('.badge');badge.classList.add(r.priority==='High'?'bg-danger-subtle':'bg-warning-subtle',r.priority==='High'?'text-danger-emphasis':'text-warning-emphasis');badge.textContent=r.priority;cells[4].textContent=r.owner;q('#inbox-rows').append(tr);});q('#inbox-count').textContent=inbox.length;q('#archive-count').textContent=archived.size;q('#inbox-empty').hidden=inbox.length!==0;q('#archive-empty').hidden=archived.size!==0;sync();}
function sync(){let total=records.length-archived.size;q('#selection').textContent=selected.size?`${selected.size} ${selected.size===1?'note':'notes'} selected`:'No notes selected';q('#archive').disabled=!selected.size;q('#select-all').disabled=!total;q('#select-all').checked=total>0&&selected.size===total;q('#select-all').indeterminate=selected.size>0&&selected.size<total;}
q('#select-all').onchange=e=>{selected.clear();if(e.target.checked)records.filter(r=>!archived.has(r.id)).forEach(r=>selected.add(r.id));render();};q('#archive').onclick=()=>{lastBatch=[...selected];lastBatch.forEach(id=>archived.add(id));selected.clear();render();q('#toast-copy').textContent=`${lastBatch.length} ${lastBatch.length===1?'note was':'notes were'} archived locally. You can undo this most recent batch.`;q('#undo').disabled=false;toast.show();q('#result').textContent='The archive is local. No external issue tracker was changed.';};q('#undo').onclick=()=>{let count=lastBatch.length;lastBatch.forEach(id=>archived.delete(id));lastBatch=[];q('#undo').disabled=true;render();toast.hide();q('#result').textContent=`Restored ${count} ${count===1?'note':'notes'} to the inbox.`;};render();
테이블 너비 740px는 애플리케이션의 가독성을 고려한 선택이며, CoreUI 기본값이 아닙니다.
04
CoreUI 디자인 시스템 에이전트 설정
구현 시작점
Bootstrap 5.9.0용 CoreUI을 검증했습니다. 제공된 app.js는 전체 대기열 마크업을 전제로 하며 네트워크 작업은 수행하지 않습니다. HTML 래퍼에는 전체 본문 마크업이 포함되어 있습니다. 제공된 상호작용 및 CSS 예시는 여기에 지정된 파일명으로 저장하세요.
html
<!-- npm install @coreui/[email protected] -->
<!-- Copy node_modules/@coreui/coreui/dist to public/vendor/coreui. -->
<html lang="en" data-coreui-theme="light">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/vendor/coreui/css/coreui.min.css">
<link rel="stylesheet" href="style.css">
<main class="review-desk"><header class="desk-heading"><div><p class="eyebrow">COREUI · REVIEW OPERATIONS</p><h1>Keep the review queue moving.</h1><p class="text-body-secondary">Archive resolved design notes and undo the last local batch.</p></div><span class="badge text-bg-primary">LOCAL WORKSPACE</span></header><div class="row g-3 mb-4"><div class="col-6 col-md-4"><div class="card metric"><div class="card-body"><div class="text-body-secondary small">Inbox</div><strong id="inbox-count">5</strong><span>design notes</span></div></div></div><div class="col-6 col-md-4"><div class="card metric"><div class="card-body"><div class="text-body-secondary small">Archived</div><strong id="archive-count">0</strong><span>local resolutions</span></div></div></div><div class="col-12 col-md-4"><div class="card metric context-card"><div class="card-body"><div class="text-body-secondary small">Review principle</div><p class="mt-2 mb-0">Keep the original request visible, even after the note is resolved.</p></div></div></div></div><section class="card"><div class="card-header bg-body"><ul class="nav nav-underline-border" role="tablist"><li class="nav-item" role="presentation"><button id="inbox-tab" class="nav-link active" data-coreui-toggle="tab" data-coreui-target="#inbox-panel" type="button" role="tab" aria-controls="inbox-panel" aria-selected="true">Inbox</button></li><li class="nav-item" role="presentation"><button id="archive-tab" class="nav-link" data-coreui-toggle="tab" data-coreui-target="#archive-panel" type="button" role="tab" aria-controls="archive-panel" aria-selected="false">Archive</button></li></ul></div><div class="tab-content"><div id="inbox-panel" class="tab-pane fade show active" role="tabpanel" aria-labelledby="inbox-tab" tabindex="0"><div class="batch-bar"><span id="selection" class="small text-body-secondary">No notes selected</span><button id="archive" class="btn btn-primary btn-sm" disabled>Archive selected</button></div><div class="table-responsive" tabindex="0" role="region" aria-label="Design review inbox"><table class="table table-hover align-middle mb-0"><thead><tr><th scope="col"><input id="select-all" class="form-check-input" type="checkbox" aria-label="Select all inbox notes"></th><th scope="col">Design note</th><th scope="col">Area</th><th scope="col">Priority</th><th scope="col">Owner</th></tr></thead><tbody id="inbox-rows"></tbody></table></div><p id="inbox-empty" class="empty-copy" hidden>All sample notes are archived. Open Archive to inspect them.</p></div><div id="archive-panel" class="tab-pane fade" role="tabpanel" aria-labelledby="archive-tab" tabindex="0"><div class="p-4"><h2 class="h5">Resolved in this local session</h2><p class="text-body-secondary small">The original note and owner stay attached to the record.</p><ul id="archive-list" class="list-group list-group-flush"></ul><p id="archive-empty" class="mb-0 text-body-secondary">No notes archived yet.</p></div></div></div></section><section class="decision-key"><span class="badge bg-danger-subtle text-danger-emphasis">High</span><p>Blocks a clear next action or access to important information.</p><span class="badge bg-warning-subtle text-warning-emphasis">Medium</span><p>Creates avoidable friction while the flow remains usable.</p></section><div class="toast-container position-static mt-4"><div id="undo-toast" class="toast w-100" role="status" aria-live="polite" aria-atomic="true"><div class="toast-header"><strong class="me-auto">Local archive updated</strong><button type="button" class="btn-close" data-coreui-dismiss="toast" aria-label="Dismiss notice"></button></div><div class="toast-body"><p id="toast-copy" class="mb-3"></p><button id="undo" class="btn btn-outline-primary btn-sm">Undo last batch</button></div></div></div><p id="result" class="small text-body-secondary mt-3" role="status">No external issue tracker is connected.</p></main>
<script src="/vendor/coreui/js/coreui.bundle.min.js"></script>
<script src="app.js"></script>
</html>
프로젝트를 위한 프롬프트
에이전트 프롬프트
CoreUI 5.9.0 디자인 리뷰 운영 데스크를 바탕으로 시작하세요. 기본 탭과 실행 취소 토스트는 data-coreui 속성으로 유지하고, 리뷰 레코드 5개와 선택된 ID 집합을 보존하며, 선택 상태를 바탕으로 일부 선택 상태를 계산하세요. 선택한 항목을 일괄 보관하고 보관 탭에 표시한 다음 실행 취소로 마지막 일괄 처리를 복원할 수 있게 하세요. 테이블의 가로 스크롤 영역을 분리하고 선택 상태와 보관 여부를 구분하세요. 호환되는 CoreUI 번들을 사용하세요. Bootstrap 속성 이름으로 대체하거나 서버 측에서 보관한다고 안내하지 마세요.
05
CoreUI 디자인 시스템 출처 및 범위
Bootstrap 5.9.0 무료 컴포넌트용 CoreUI입니다. 테마 기본값과 독자적인 밀도 설정을 구분합니다. 보관과 실행 취소는 로컬 샘플 레코드에만 적용됩니다.