01 Emplifi Soul 디자인 시스템 인터페이스 미리보기 전체 선택, 일부 선택, 정렬, 일괄 검토 및 취소를 사용해 보세요.
Soul 디자인 가이드와 검증된 라이트 모드 토큰을 독립적으로 해석한 HTML/CSS입니다. 시스템 글꼴 대체 설정을 사용하며 비공개 React 에셋은 포함하지 않습니다. ·
구현 레퍼런스 02
Emplifi Soul 디자인 시스템 디자인 레퍼런스 공개된 Soul 디자인 문서와 공식 Storybook 실시간 버전입니다. 여기에 표시된 값은 명명된 라이트 모드 기반 값이며, 모든 문서 페이지와 패키지 릴리스의 상태가 동일하다는 의미는 아닙니다. 미리보기에서는 로컬 대체 글꼴로 시스템 글꼴을 사용합니다.
이름 / 규칙 값 / 구성 용도 --color-layer-level-0#f3f3f5 라이트 모드 표면 단계. --color-layer-level-1#ffffff 라이트 모드 표면 단계. --color-layer-level-2#f3f3f5 라이트 모드 표면 단계. --color-layer-level-3#e7e7e7 라이트 모드 표면 단계. --color-on-layer-primary#24242b 기본 텍스트. --color-on-layer-secondary#505057 보조 텍스트. Dark level 0 / level 1#111114 / #24242b어두운 표면은 깊이가 더해질수록 밝아집니다.
이름 / 규칙 값 / 구성 용도 --color-button-primary#0c4bff 기본 버튼 배경. --color-on-layer-interactive#003beb 상호작용 텍스트. --color-border-focus-ring#003beb 포커스 링. --color-table-row-odd#ffffff 홀수 행 및 테이블 머리글/바닥글. --color-table-row-even#f9f9fa 짝수 행. --color-table-row-hover#d5efff 스캔 피드백. --color-layer-main-navigation#111114 주 탐색에만 적용됩니다.
이름 / 규칙 값 / 구성 용도 --space-13pxFoundation 간격. --space-26pxFoundation 간격. --space-39pxFoundation 간격. --space-412pxFoundation 간격. --space-518pxFoundation 간격. --space-624pxFoundation 간격. --space-730pxFoundation 간격. --space-836pxFoundation 간격. --space-948pxFoundation 간격. Default control height36px컴팩트 옵션은 30px입니다. 간격 척도와 혼동하지 마세요.
이름 / 규칙 값 / 구성 용도 --border-radius-small2px은은하게 둥글게 처리합니다. --border-radius-medium6px기본 요소. --border-radius-large8px컨테이너. --border-radius-full50%원형. --border-radius-pill999px알약형.
이름 / 규칙 값 / 구성 용도 headline-1500 · 30px / 42px페이지 기본 제목. headline-2500 · 20px / 30px섹션 제목. headline-3500 · 16px / 24px하위 섹션 제목. paragraph-regular400 · 13px / 18px중간 굵기 본문. Button label600 · 13px / 18px문장형 대소문자. Table numeralsTabular numbers비교 가능한 숫자 값은 정렬하세요.
이름 / 규칙 값 / 구성 용도 Package@sbks/ds-button비공개 범위입니다. Button은 명명된 내보내기입니다. variantBUTTON_VARIANT.LIGHT default열거형 기반입니다. TypeScript에 원시 문자열을 사용하지 말고 내보낸 열거형 상수를 사용하세요. sizeBUTTON_SIZE.MEDIUM defaultMedium이 기본값입니다. isLoadingfalse로딩 표시. isSelectedfalse선택된 모양. isAppliedfalse현재 invisible 변형에 대한 시각적 지원입니다. onClickMouse event handler로컬 애플리케이션 상태와 연결합니다. badgestring | number적용된 선택 정보. themeInherited; light fallback어두운 표면으로 바뀌어도 모든 하위 요소가 자동으로 변경된다고 가정하지 마세요.
03 Emplifi Soul 디자인 시스템 전체 가이드 실제 인터페이스를 위해 이 시스템을 선택하고 적용하고 검토하는 실용 가이드.
DESIGN-emplifi-soul.md 완전한 안내와 구현 시작점, 소스 링크.
가이드 다운로드 Emplifi Soul 디자인 시스템 디자인 가이드 및 구현 상태 컴포넌트 이름만으로 패키지를 추정하지 마세요 Soul은 Emplifi의 디자인 시스템입니다. 공개 디자인 참고 자료와 Storybook은 열람할 수 있지만, 공식 빠른 시작 가이드를 따르려면 비공개 npm 스코프에 접근해야 합니다. 브라우저 미리보기는 독립적인 HTML/CSS 구현이며, Soul React을 포함하지 않습니다. 테이블 동작은 디자인 가이드를 해석해 구현했으며, 공개 페이지에는 여전히 React이 계획 중이라고 표시되어 있습니다.
제공된 React 예제는 의도적으로 시맨틱 테이블과 실제 Soul Button 컨트롤을 사용합니다. 문서에 없는 DataTable 내보내기가 있다고 주장하지 않으면서도 작동하는 애플리케이션 상태를 제공합니다. 공개 문서를 열람할 수 있다고 해서 패키지에 접근하거나 비공개 에셋을 재배포할 권한이 생기는 것은 아닙니다.
지원 사양 읽기
Emplifi Soul 디자인 시스템: 행 선택을 넘어서는 데이터 테이블 규칙 헤더, 요약 및 누락된 값 Soul의 테이블 가이드는 헤더, 행, 셀과 선택 가능한 요약 막대를 구분합니다. 숫자는 오른쪽 정렬하고 텍스트는 왼쪽 정렬합니다. 요약에는 합계나 평균을 표시할 수 있으므로, 설명 없이 숫자만 보여 주지 말고 어떤 연산을 나타내는지 레이블로 밝혀야 합니다.
이미지의 동영상 길이처럼 해당 측정값이 적용되지 않는 경우에는 N/A를 사용합니다. 엠 대시(—)는 사용할 수 없거나 알 수 없는 데이터를 나타냅니다. 이런 경우를 0으로 바꾸면 안 됩니다. 요약의 해석과 이를 바탕으로 한 비교가 달라지기 때문입니다.
현재 미리보기에는 정렬과 행 선택이 구현되어 있으며, 요약 계산은 구현되어 있지 않습니다. 행 확장과 누락된 데이터에 대한 설명은 문서에 안내된 확장 지점이며, 미리보기에서는 시뮬레이션하지 않습니다. 디자인 참고 자료에서 React 테이블을 계획 중으로 표시하므로, 제공된 코드는 Soul DataTable 가져오기를 임의로 만들어 내는 대신 테이블 마크업을 직접 관리합니다. 지원 사양 읽기
대량 작업 모드에서 사용할 수 있는 작업 변경 Soul은 대량 작업 막대를 목록 아래에 배치하고, 대량 선택이 활성화되면 개별 행 작업을 숨깁니다. 헤더 체크박스에는 선택됨, 선택 안 됨, 일부 선택 상태가 있습니다. 한 번에 하나의 열만 정렬할 수 있습니다.
미리보기의 레코드 세 개는 모두 로컬 데이터이며 선택 범위 안에 있습니다. 도달 범위 헤더는 정렬 방향을 알리고, 정렬 후에도 선택된 ID는 유지됩니다. 향후 행별 작업을 추가할 때는 일괄 작업 도구 모음과 경합하지 않도록 문서에 명시된 대량 작업 모드 제한을 따라야 합니다.
지원 사양 읽기
Emplifi Soul 디자인 시스템: 중성 계층으로 깊이감 만들기 절제된 CX 작업 공간 Soul의 라이트 모드는 회색과 흰색 표면을 번갈아 사용하고, 다크 모드에서는 계층이 깊어질수록 배경을 점차 밝게 합니다. 미리보기는 회색 페이지, 흰색 테이블 표면, 작고 어두운 내비게이션 바를 사용합니다. 색상은 모든 행을 장식하는 대신 역할을 구분합니다.
인터랙티브 블루는 검토 작업과 선택된 컨트롤에만 사용합니다. 직접 만든 작은 게시물 일러스트에는 장식용 색상을 적용했습니다. 덕분에 게시물의 주제를 구분하면서도 해당 주제에 근거 없이 성공이나 위험의 의미를 부여하지 않습니다.
지원 사양 읽기
Emplifi Soul 디자인 시스템: 계층 구조를 살리는 간결한 타이포그래피 기본 글꼴은 Inter Soul은 인터페이스 전반에 Inter를, 아랍어 텍스트에는 Noto Sans Arabic을, 코드에는 Noto Sans Mono를 지정합니다. 미리보기에는 해당 글꼴 파일이 포함되어 있지 않아 시스템 글꼴을 사용합니다. 측정 기준은 게시된 레시피를 따릅니다.
30/42 메인 제목이 13/18 본문 타이포그래피 체계를 이끕니다. 테이블의 숫자에는 고정 폭 숫자를 사용합니다. 테이블에는 지나치게 큰 마케팅용 디스플레이 스타일을 적용하지 않으며, 게시물 제목도 이미지에 구워 넣은 레이블이 아닌 읽을 수 있는 텍스트로 유지합니다.
지원 사양 읽기
Emplifi Soul 디자인 시스템: 6픽셀 간격 리듬과 컨테이너 모서리 반경 밀도 높은 컨트롤을 체계적으로 구성 기본 간격 단위는 6 px입니다. 12, 18, 24, 36 px와 같은 값으로 더 큰 간격 관계를 만듭니다. 3, 9 px는 기본 페이지 여백이 아니라 컴포넌트 내부의 좁은 간격에 사용합니다. 문서에 명시된 콘텐츠 패딩은 1559 px 이하에서 24 px, 1560 px 이상에서 36 px로 바뀝니다.
Soul에서는 간격 값을 티셔츠 사이즈 방식의 컴포넌트 크기와 구분합니다. medium 컨트롤이 --space-6을 뜻하는 것은 아닙니다. 미리보기는 36 px 컨트롤, 6 px 요소 모서리 둥글기, 8 px 컨테이너 모서리 반경을 사용합니다.
지원 사양 읽기
Emplifi Soul 디자인 시스템: 비공개 라이브러리를 신중하게 불러오기 Foundation CSS은 컴포넌트 구성보다 먼저 설정합니다 공식 진입점에서는 @sbks/ds-design-library의 variables.css와 default-styles.css를 가져옵니다. Sass 진입점은 믹스인을 제공하며 전역 CSS과는 별개입니다. Button은 @sbks/ds-button에서 가져옵니다.
실제 Button API에는 isLoading, isSelected, isApplied, badge가 포함되어 있습니다. enum 기반 props를 사용하려면 TypeScript에서 라이브러리가 내보내는 enum 상수를 가져와야 합니다. 이 예제에서는 enum 가져오기 경로를 추측하지 않고 검증된 기본 LIGHT 변형을 사용합니다.
공식 빠른 시작 가이드에 따라 승인된 비공개 스코프와 빌드 로더 설정을 완료합니다. 애플리케이션 진입점에서 기초 CSS을 한 번 불러오고, 사용할 글꼴 에셋을 설정합니다. 제공된 제어형 테이블 예제를 마운트한 뒤, 게시 워크플로에 연결하기 전에 선택, 취소, 로컬 검토를 테스트합니다. 지원 사양 읽기
Emplifi Soul 디자인 시스템 컴포넌트 예제 완전한 독립형 인터랙티브 예제 전체 문서를 index.html로 저장하고 브라우저에서 엽니다. 검증된 라이트 모드 토큰과 Soul 디자인 가이드를 독립적인 HTML/CSS로 해석해 구현했습니다. 시스템 글꼴을 대체 글꼴로 사용하며 비공개 React 에셋은 포함하지 않습니다.
html 코드 복사
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Content review</title><style>:root{--color-layer-level-0:#f3f3f5;--color-layer-level-1:#fff;--color-on-layer-primary:#24242b;--color-on-layer-secondary:#505057;--color-button-primary:#0c4bff;--space-2:6px;--space-4:12px;--space-6:24px;--border-radius-medium:6px;--border-radius-large:8px}*{box-sizing:border-box}body{margin:0;background:var(--color-layer-level-0);color:var(--color-on-layer-primary);font:13px/18px system-ui}nav{padding:15px 24px;background:#111114;color:#f9f9fa;font-size:13px;display:flex;justify-content:space-between;gap:24px}main{padding:24px}header{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;margin-bottom:24px}h1{font-size:30px;font-weight:500;line-height:42px;margin:0}p{color:#505057;margin:6px 0}small{font-size:11px}.surface{background:white;border:1px solid #ddd;border-radius:8px;overflow:hidden}.bar{padding:18px 24px;border-bottom:1px solid #ddd;display:flex;justify-content:space-between;gap:12px}table{width:100%;border-collapse:collapse;table-layout:fixed}caption{text-align:left;padding:12px 24px;background:#f9f9fa;font-size:11px;color:#505057}th,td{padding:16px 12px;text-align:left;border-bottom:1px solid #e7e7e7;font-weight:400}thead th{font-size:11px;font-weight:600;padding-top:12px;padding-bottom:12px}th:first-child,td:first-child{width:44px;text-align:center}.title{width:46%}.reach{width:15%;text-align:right;font-variant-numeric:tabular-nums}tbody tr:nth-child(even){background:#f9f9fa}tbody tr:hover{background:#d5efff}input{accent-color:#003beb;width:16px;height:16px;cursor:pointer}input:focus-visible,button:focus-visible{outline:2px solid #003beb;outline-offset:3px}button{font:600 13px/18px system-ui;border:1px solid #ccc;background:white;padding:8px 12px;border-radius:6px;min-height:36px;cursor:pointer;color:inherit}button:hover{background:#f3f3f5}#sort{border:0;background:transparent;padding:0;min-height:28px;color:#003beb}#apply{background:#0c4bff;color:white;border-color:#0c4bff}#apply:hover{background:#003beb}.name{display:flex;align-items:center;gap:12px}.art{width:52px;height:52px;flex:0 0 52px;border-radius:6px;position:relative;overflow:hidden}.field{background:#d4e5d3}.field:before{content:'';position:absolute;width:65px;height:65px;border-radius:50%;background:#7ca77a;left:15px;top:20px}.field:after{content:'';position:absolute;width:16px;height:16px;border-radius:50%;background:#fff1c5;left:8px;top:8px}.studio{background:#e6daee}.studio:before{content:'';position:absolute;width:20px;height:36px;background:#a089b2;left:8px;top:16px;transform:rotate(-14deg)}.studio:after{content:'';position:absolute;width:20px;height:28px;background:#c3abc9;left:30px;top:24px;transform:rotate(12deg)}.guide{background:#f4e4c9}.guide:before{content:'';position:absolute;width:26px;height:38px;background:#fff;left:13px;top:9px;transform:rotate(8deg);border:1px solid #c9ad7c}.name strong{font-weight:600;display:block}.name small{color:#505057}.state{font-size:11px;color:#505057}#bulk{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 24px;background:#f3f3f5;border-top:1px solid #ccc}#bulk[hidden]{display:none}.actions{display:flex;gap:6px}#status{margin:18px 0;min-height:18px}footer{padding:0 24px 20px;color:#505057;font-size:11px}@media(min-width:1560px){main{padding:36px}}@media(max-width:600px){main{padding:18px}.bar{padding:12px}.channel{display:none}.title{width:57%}.reach{width:22%}.state{display:block;margin-top:4px}th,td{padding:12px 6px}.art{width:36px;height:42px;flex-basis:36px}.name{gap:6px}#bulk{padding:12px;flex-direction:column;align-items:stretch}.actions{flex-wrap:wrap}header{flex-direction:column;gap:8px}h1{font-size:27px}.bar small{max-width:140px;text-align:right}nav{padding:14px 18px}}</style><nav><strong>Soul / Content workspace</strong><span>Demo collection</span></nav><main><header><div><h1>Content review</h1><p>A final editorial check before your team publishes.</p></div><small>3 fictional drafts · No publishing connection</small></header><section class="surface" aria-label="Content drafts"><div class="bar"><strong>Draft collection</strong><small>Select items to review together</small></div><table><caption>Local sample data. Reach values are illustrative.</caption><thead><tr><th scope="col"><input id="all" type="checkbox" aria-label="Select all drafts"></th><th scope="col" class="title">Content</th><th scope="col" class="channel">Channel</th><th scope="col" class="reach" id="reach-head" aria-sort="none"><button id="sort">Reach ↕</button></th></tr></thead><tbody id="rows"></tbody></table><div id="bulk" hidden><span id="count"></span><div class="actions"><button id="apply">Mark as reviewed</button><button id="cancel">Cancel selection</button></div></div></section><p id="status" role="status">Select a draft to begin. All changes stay in this example.</p></main><footer>Independent Soul interpretation · verified light-mode tokens · system font fallback</footer><script>let items=[{id:'field',title:'A morning in the field',channel:'Instagram',reach:2480},{id:'studio',title:'Inside the studio',channel:'Facebook',reach:1860},{id:'guide',title:'Care guide: summer edition',channel:'Instagram',reach:3210}],selected=new Set(),reviewed=new Set(),direction=0;const rows=document.querySelector('#rows'),all=document.querySelector('#all'),bulk=document.querySelector('#bulk'),status=document.querySelector('#status');function render(){rows.innerHTML=items.map(i=>`<tr><td><input type="checkbox" data-id="${i.id}" aria-label="Select ${i.title}" ${selected.has(i.id)?'checked':''}></td><th scope="row"><div class="name"><div class="art ${i.id}" aria-hidden="true"></div><div><strong>${i.title}</strong><span class="state">${reviewed.has(i.id)?'Reviewed locally':'Not reviewed'}</span></div></div></th><td class="channel">${i.channel}</td><td class="reach">${i.reach.toLocaleString('en-US')}</td></tr>`).join('');rows.querySelectorAll('input').forEach(x=>x.onchange=()=>{x.checked?selected.add(x.dataset.id):selected.delete(x.dataset.id);sync()});sync()}function sync(){all.checked=selected.size===items.length;all.indeterminate=selected.size>0&&selected.size<items.length;bulk.hidden=!selected.size;document.querySelector('#count').textContent=selected.size+' selected'}all.onchange=()=>{selected=all.checked?new Set(items.map(i=>i.id)):new Set();render()};document.querySelector('#cancel').onclick=()=>{selected.clear();render();all.focus()};document.querySelector('#apply').onclick=()=>{let n=selected.size;selected.forEach(x=>reviewed.add(x));selected.clear();render();status.textContent=n+(n===1?' draft':' drafts')+' reviewed locally. Nothing published.';all.focus()};document.querySelector('#sort').onclick=()=>{direction=direction===1?-1:1;items.sort((a,b)=>(a.reach-b.reach)*direction);document.querySelector('#reach-head').setAttribute('aria-sort',direction===1?'ascending':'descending');document.querySelector('#sort').textContent='Reach '+(direction===1?'↑':'↓');render()};render();</script></html>
코드 참조
테이블은 HTML에서 선택과 정렬을 직접 구현합니다. 비공개 Soul DataTable 내보내기가 있다고 주장하지 않습니다. React 예제에서는 검증된 Button 내보내기와 애플리케이션에서 직접 관리하는 테이블을 함께 사용합니다. 실제 Soul 버튼을 사용한 검토용 선택 가능 테이블 네이티브 시맨틱 테이블과 제어형 체크박스를 문서에 안내된 Button 내보내기와 조합했습니다. 테이블은 애플리케이션 코드이며, 존재하지 않는 Soul DataTable 컴포넌트를 만들어 낸 것이 아닙니다.
tsx 코드 복사
// ContentReview.tsx — inside a configured, authorized Soul application
import React, { useState } from 'react';
import { Button } from '@sbks/ds-button';
import '@sbks/ds-design-library/dist/css/variables.css';
import '@sbks/ds-design-library/dist/css/default-styles.css';
const posts = [
{ id: 'field', title: 'A morning in the field', channel: 'Instagram' },
{ id: 'studio', title: 'Inside the studio', channel: 'Facebook' },
{ id: 'guide', title: 'Care guide: summer edition', channel: 'Instagram' }
];
export default function ContentReview() {
const [selected, setSelected] = useState<string[]>([]);
const [reviewed, setReviewed] = useState<string[]>([]);
const [message, setMessage] = useState('');
const toggle = (id: string) => setSelected(previous =>
previous.includes(id) ? previous.filter(x => x !== id) : [...previous, id]);
const review = () => {
setReviewed(previous => [...new Set([...previous, ...selected])]);
setMessage(`${selected.length} items reviewed locally. Nothing published.`);
setSelected([]);
};
return <section aria-labelledby="review-title">
<h1 id="review-title">Content review</h1>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<caption>Fictional content drafts</caption>
<thead><tr><th scope="col">Select</th><th scope="col">Content</th>
<th scope="col">Channel</th><th scope="col">Review</th></tr></thead>
<tbody>{posts.map(post => <tr key={post.id}>
<td><input type="checkbox" aria-label={`Select ${post.title}`}
checked={selected.includes(post.id)} onChange={() => toggle(post.id)} /></td>
<th scope="row">{post.title}</th><td>{post.channel}</td>
<td>{reviewed.includes(post.id) ? 'Reviewed locally' : 'Not reviewed'}</td>
</tr>)}</tbody>
</table>
{selected.length > 0 && <div aria-label="Bulk actions"
style={{ display: 'flex', gap: 'var(--space-2)', marginTop: 'var(--space-6)' }}>
<Button onClick={review}>Mark {selected.length} as reviewed</Button>
<Button onClick={() => setSelected([])}>Cancel selection</Button>
</div>}
<p role="status">{message}</p>
</section>;
}
코드 참조
Button의 기본값은 LIGHT입니다. 이 예제는 enum 내보내기 위치를 추측하거나 DataTable props를 지어내지 않습니다. 먼저 비공개 디자인 라이브러리와 버튼 패키지를 설치하고 구성하세요. 가져오기 경로는 공식 Storybook에서 확인했습니다. 일괄 검토는 로컬 상태만 업데이트합니다. 콘텐츠는 게시되지 않습니다. 04
Emplifi Soul 디자인 시스템 에이전트 설정 구현 시작점 공식 빠른 시작 안내를 이용하려면 비공개 npm 및 Emplifi 저장소/VPN에 접근할 수 있어야 합니다. React 18/19와 Webpack 4/5를 지원하며, Sass 및 SVG 로더 설정이 필요합니다. 공개 레지스트리에서 이러한 패키지를 찾기보다 링크된 내부 범위 온보딩 절차를 따르세요. 독립형 미리보기에는 이러한 비공개 에셋이 필요하지 않습니다.
bash / tsx 예제 복사
# For authorized Emplifi environments with the private @sbks scope configured:
npm i -D @sbks/ds-design-library @sbks/ds-button
// Application entry:
import "@sbks/ds-design-library/dist/css/variables.css";
import "@sbks/ds-design-library/dist/css/default-styles.css";
// Optional SCSS file:
@use "@sbks/ds-design-library/dist/sass/main.scss" as *; 프로젝트를 위한 프롬프트 에이전트 프롬프트 프롬프트 복사
확인된 레이어/색상/간격 변수, 13/18 본문 타이포그래피, 6px 컨트롤 모서리 반경, 8px 컨테이너를 사용해 Emplifi Soul 콘텐츠 검토 인터페이스를 만드세요. 안정적인 ID, 부분 선택 상태를 지원하는 전체 선택, 정렬 열 하나를 갖춘 의미 있는 선택 가능 테이블을 사용하세요. 권한이 있는 앱에서는 실제 @sbks/ds-button 내보내기와 기반 CSS을 사용하고, 공개 DataTable 패키지를 지어내지 마세요. 05
Emplifi Soul 디자인 시스템 출처 및 범위 Soul은 Emplifi의 디자인 시스템입니다. 공개 디자인 참고 자료와 Storybook은 열람할 수 있지만, 공식 빠른 시작 가이드를 따르려면 비공개 npm 스코프에 접근해야 합니다. 브라우저 미리보기는 독립적인 HTML/CSS 구현이며, Soul React을 포함하지 않습니다. 테이블 동작은 디자인 가이드를 해석해 구현했으며, 공개 페이지에는 여전히 React이 계획 중이라고 표시되어 있습니다.