Braid는 여러 SEEK Group 테마를 지원합니다. Braid의 변수 API에는 범위가 지정된 CSS 표현식이 포함되므로 vars.space.medium 같은 이름은 안정적인 참조로 사용할 수 있지만, 생성된 사용자 지정 속성 식별자를 직접 재현해서는 안 됩니다. 예시에서는 BraidProvider를 통해 seekJobs를 선택합니다.
reset은 다른 Braid 스타일보다 먼저 가져와야 합니다. 임베디드 애플리케이션에서는 styleBody={false}를 설정해 Provider가 호스트 페이지 body의 배경과 간격을 변경하지 않도록 할 수 있습니다. 이 옵션을 사용해도 테마 컨텍스트는 여전히 필요합니다.
공통 중단점은 모바일 0, 태블릿 740px, 데스크톱 992px, 와이드 1200px입니다. 기본 예시에서는 반응형 prop 객체를 사용해 태블릿 중단점에서 패딩을 변경합니다. 같은 변경을 위해 CSS 미디어 쿼리를 중복 작성하지 않습니다.
테마의 medium 간격을 근거 없이 특정 픽셀 값으로 환산해 문서화하지 않습니다. 간격 설정에는 Box와 Stack을 우선 사용하세요. vars는 이 구성 요소만으로 레이아웃을 표현할 수 없을 때 사용하는 저수준 대안입니다. 값이 CSS 표현식이므로 곱셈에는 JavaScript 숫자 산술이 아닌 calc를 사용해야 합니다.
Text는 이름이 지정된 크기와 굵기를 제공하고, Heading은 제목 계층을 나타냅니다. 직무의 도시는 보조 톤으로 표시할 수 있지만, 제목은 결과를 알아볼 수 있도록 충분히 강조해야 합니다. 콘텐츠가 문단이라면 기본 모양만을 기준으로 HTML 요소를 선택하지 말고 component="p"를 지정해 Text를 문단으로 렌더링할 수 있습니다.
Braid는 어두운 배경 맥락에 맞춰 전경 톤을 조정합니다. 원본 이미지나 사용자 지정 배경을 사용할 때는 Box의 customDark 맥락으로 배경과의 관계를 나타낼 수 있습니다. 직접 색을 지정한 div도 동일한 맥락 처리 기능을 제공한다고 가정할 수는 없습니다.
예시에서는 Search jobs에 solid variant와 brandAccent를 명시적으로 함께 지정합니다. 검색 지우기 동작에는 강조를 낮춘 ghost를 사용합니다. 반복적으로 수행하는 폼 동작은 formAccent로 강조할 수 있으며, critical은 파괴적인 결과가 따르는 동작에 사용해야 합니다. 일반 컨트롤을 더 눈에 띄게 만들기 위한 용도로 사용하지 마세요.
Braid의 기본 동작에는 주의가 필요합니다. 별도로 지정하지 않으면 중립 버튼은 ghost이며, tone을 설정하면 암묵적으로 variant가 solid가 됩니다. 주요 동작에 두 props를 모두 명시하면 의도한 계층 구조가 분명해집니다. Actions는 일반적인 모바일 세로 배치와 넓은 화면의 가로 배치를 처리합니다.
SEEK Braid 디자인 시스템: 사용할 수 없는 버튼보다 조치 방법을 알려주는 오류가 낫습니다
제어되는 입력란 규약을 사용합니다
TextField의 onChange 콜백에는 React input 이벤트가 전달됩니다. 예시에서는 event.target.value를 읽어 입력 중인 텍스트를 저장하고, 사용자가 Search를 실행할 때만 적용된 검색어를 변경합니다. 이 컨트롤만으로는 애플리케이션 상태를 지울 수 없으므로 지우기 콜백을 전달합니다.
message는 검색어가 비어 있을 때의 오류를 설명하고 critical tone은 오류 상태를 시각적으로 나타냅니다. description은 지속적으로 표시할 맥락에 사용합니다. 일반적인 비활성 Braid Button은 지원되지 않으므로 방문자가 다음 단계의 안내를 확인할 수 있도록 동작을 계속 사용할 수 있게 둡니다. 오프라인 미리보기에서는 추가로 포커스를 오류가 있는 입력란으로 이동합니다.
미리보기에는 직무, 도시, 근무 형태 메타데이터를 포함한 임의의 채용 공고 데이터 세 건이 들어 있습니다. 검색은 직무명과 도시를 대소문자 구분 없이 비교합니다. 실제 SEEK 공고를 가져오지 않으며, 결과에는 샘플임을 표시합니다.
초안 텍스트와 적용된 결과를 분리하면 상호작용을 명확히 확인할 수 있습니다. 역할을 수정해도 Search를 누르기 전까지 현재 결과는 바뀌지 않습니다. 검색 지우기는 두 값을 모두 초기화합니다. 검색 결과가 없으면 그 상태를 알리고 검색 컨트롤은 계속 사용할 수 있게 합니다.
공식 설정은 sku에 최적화되어 있습니다. Braid의 Vanilla Extract 스타일을 사용하려면 커스텀 번들러에 빌드 통합이 필요하며, reset과 선택한 테마는 문서에 명시된 순서대로 애플리케이션에 포함해야 합니다. 제공된 코드는 기존 호환 sku 프로젝트를 위한 완전한 진입점이며, 임의의 script 태그만으로 Braid를 실행할 수 있다는 뜻은 아닙니다.
Braid는 개발 중 어설션과 환경 기반 경고를 수행합니다. 공식 빌드 가이드에서는 프로덕션 빌드 시 어설션을 제거하고 환경 값을 대체하도록 안내합니다. 이 점을 구현 워크플로에 반영하세요. 오프라인 HTML 예제에는 이러한 패키지 빌드 의존성이 없습니다.
전체 문서를 index.html로 저장한 다음 브라우저에서 여세요. Braid의 검색 구성, 계층 구조, 중단점 가이드를 독립적으로 해석한 오프라인 HTML 예제이며, 실제 SEEK 데이터나 Braid 런타임은 사용하지 않습니다. 미리보기 색상과 실제 간격은 애플리케이션에서 정한 값입니다.
html
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Braid sample job search interpretation</title><style>*{box-sizing:border-box}body{margin:0;background:#f5f5f5;color:#222;font:16px/1.5 system-ui,sans-serif}header{background:white;padding:16px 32px;border-bottom:1px solid #ddd;display:flex;align-items:center;justify-content:space-between}header strong{color:#0d3880;font-size:20px}header span{font-size:14px;color:#666}.hero{background:#0d3880;color:white;padding:32px}.hero-inner{max-width:980px;margin:auto}h1{font-size:32px;line-height:1.2;margin:0 0 24px}form{display:grid;grid-template-columns:1fr auto auto;gap:16px;align-items:end}label{display:block;font-weight:600;margin-bottom:8px}input{height:48px;border:1px solid #aaa;border-radius:4px;padding:10px 12px;font:inherit;width:100%;min-width:0;color:#222}input[aria-invalid=true]{border:2px solid #c90040}button{height:48px;border:1px solid transparent;border-radius:4px;font:600 16px/1.2 system-ui;padding:0 24px;cursor:pointer}.search{background:#e60278;color:white}.clear{background:transparent;color:white;border-color:white}button:focus-visible,input:focus-visible{outline:3px solid #69d8ff;outline-offset:3px}.error{background:white;color:#b70040;padding:8px 12px;margin:16px 0 0;border-radius:4px}.error:empty{display:none}.help{font-size:14px;color:#e4eaf6;margin:16px 0 0}main{max-width:1044px;margin:32px auto;padding:0 32px}.results-head{display:flex;justify-content:space-between;gap:16px;margin-bottom:20px}h2{font-size:24px;line-height:1.3;margin:0 0 8px}.applied{font-size:14px;color:#555}article{display:grid;grid-template-columns:1fr 100px;gap:24px;padding:24px;background:white;border:1px solid #ddd;border-radius:4px;margin-bottom:16px}.company{font-size:14px;color:#555}.meta{margin:16px 0;display:flex;gap:8px;flex-wrap:wrap}.meta span{background:#f0f4fb;padding:4px 8px;font-size:12px;border-radius:4px}article p{margin:0;color:#555;font-size:14px}.monogram{background:#f1e5f4;color:#673b78;height:72px;width:72px;border-radius:12px;display:grid;place-items:center;font-weight:700;font-size:24px;justify-self:end}.monogram.blue{color:#0d3880;background:#e4efff}.monogram.green{color:#326342;background:#e8f4eb}.notice{background:white;border:1px solid #ddd;padding:24px}.boundary{font-size:12px;color:#666;margin:24px 0}@media(max-width:739px){header{padding:16px}header span{display:none}.hero{padding:24px 16px}h1{font-size:28px}form{grid-template-columns:1fr;gap:12px}form button{width:100%}main{padding:0 16px;margin:24px auto}article{padding:20px;grid-template-columns:1fr 48px;gap:12px}.monogram{height:48px;width:48px;font-size:18px}article h2{font-size:20px}.results-head{display:block}}</style><header><strong>Possibilities</strong><span>Original job-search specimen</span></header><section class="hero"><div class="hero-inner"><h1>Find your next possibility.</h1><form novalidate><div><label for="query">Role or city</label><input id="query" type="search" placeholder="Try designer or Melbourne" aria-describedby="error help"></div><button class="search" type="submit">Search jobs</button><button class="clear" type="button" id="clear">Clear search</button></form><div id="error" class="error" role="alert"></div><p id="help" class="help">Search three original sample roles. These are not live vacancies.</p></div></section><main><div class="results-head"><div><h2 id="count">3 sample results</h2><div class="applied" id="applied">Showing all sample roles</div></div></div><div id="results"></div><p class="boundary">Independent HTML interpretation of Braid’s search composition and responsive guidance. Original content and application palette; no SEEK data, account or application service is connected.</p></main><script>const jobs=[{title:'Product designer',city:'Melbourne',company:'North Studio',type:'Full time',mode:'Hybrid',initial:'NS',color:'',description:'Shape useful experiences with a small multidisciplinary product team.'},{title:'Design systems engineer',city:'Sydney',company:'Harbour Works',type:'Full time',mode:'Flexible',initial:'HW',color:'blue',description:'Build accessible components and improve the way teams deliver interfaces.'},{title:'UX researcher',city:'Melbourne',company:'Fern Collective',type:'Contract',mode:'On site',initial:'FC',color:'green',description:'Turn thoughtful field research into clear product opportunities.'}];const input=document.querySelector('#query'),error=document.querySelector('#error');let applied='';function render(){let matches=jobs.filter(j=>(j.title+' '+j.city).toLowerCase().includes(applied.toLowerCase()));document.querySelector('#count').textContent=matches.length+' sample '+(matches.length===1?'result':'results');document.querySelector('#applied').textContent=applied?'Applied search: '+applied:'Showing all sample roles';document.querySelector('#results').innerHTML=matches.length?matches.map(j=>`<article><div><h2>${j.title}</h2><div class="company">${j.company} · ${j.city}</div><div class="meta"><span>${j.type}</span><span>${j.mode}</span><span>Sample listing</span></div><p>${j.description}</p></div><div class="monogram ${j.color}" aria-hidden="true">${j.initial}</div></article>`).join(''):'<div class="notice">No sample roles match. Try another role or city, or clear your search.</div>'}document.querySelector('form').onsubmit=e=>{e.preventDefault();if(!input.value.trim()){error.textContent='Enter a role or city.';input.setAttribute('aria-invalid','true');input.focus();return}error.textContent='';input.removeAttribute('aria-invalid');applied=input.value.trim();render()};input.oninput=()=>{error.textContent='';input.removeAttribute('aria-invalid')};document.querySelector('#clear').onclick=()=>{input.value='';applied='';error.textContent='';input.removeAttribute('aria-invalid');render();input.focus()};render();</script></html>
검색 대상은 포함된 가상 채용 공고입니다. Braid 기본 진입점을 사용하려면 스타일 추출 빌드와 선택한 seekJobs 테마가 필요합니다.
초안 쿼리, 적용된 검색, 명시적인 초기화
제어되는 TextField가 로컬 쿼리를 검증하고, Actions가 서로 다른 두 가지 버튼 스타일을 묶으며, Card/Stack/Heading/Text가 자체 제작 결과를 렌더링합니다.
jsx
// Entry module in an existing sku application:
// npm install braid-design-system
import 'braid-design-system/reset'; // Keep this first.
import React,{useState} from 'react';
import {createRoot} from 'react-dom/client';
import seekJobsTheme from 'braid-design-system/themes/seekJobs';
import {BraidProvider,Box,Stack,Heading,Text,TextField,Button,Actions,Card} from 'braid-design-system';
const jobs=[{id:1,title:'Product designer',city:'Melbourne'},{id:2,title:'Design systems engineer',city:'Sydney'},{id:3,title:'UX researcher',city:'Melbourne'}];
function JobSearch(){
const [draft,setDraft]=useState(''),[query,setQuery]=useState(''),[error,setError]=useState('');
const results=jobs.filter(j=>(j.title+' '+j.city).toLowerCase().includes(query.toLowerCase()));
function search(){if(!draft.trim()){setError('Enter a role or city.');return;}setError('');setQuery(draft.trim());}
function clear(){setDraft('');setQuery('');setError('');}
return <Box component="main" padding={{mobile:'medium',tablet:'xlarge'}}>
<Stack space="large">
<Heading level="2">Find your next possibility</Heading>
<TextField id="job-query" label="Role or city" value={draft}
onChange={event=>{setDraft(event.target.value);setError('');}}
onClear={()=>setDraft('')} clearLabel="Clear role or city"
tone={error?'critical':'neutral'} message={error||'Search three original sample roles.'}/>
<Actions><Button tone="brandAccent" variant="solid" onClick={search}>Search jobs</Button>
<Button variant="ghost" onClick={clear}>Clear search</Button></Actions>
<Text>{results.length} sample results</Text>
{results.map(job=><Card key={job.id}><Stack space="small"><Heading level="3">{job.title}</Heading><Text tone="secondary">{job.city}</Text></Stack></Card>)}
{!results.length&&<Text>No sample roles match. Try a different title or city.</Text>}
<Text size="small" tone="secondary">These are sample listings, not live vacancies.</Text>
</Stack>
</Box>;
}
createRoot(document.getElementById('root')).render(<BraidProvider theme={seekJobsTheme}><JobSearch/></BraidProvider>);
기존 sku 빌드를 위한 완전한 애플리케이션 진입점이며, 공식 설정에서 바로 지원합니다. 커스텀 번들러에는 Vanilla Extract 통합이 필요합니다. reset 가져오기는 반드시 첫 번째로 유지해야 합니다. 오프라인 미리보기는 기본 패키지 컴파일을 지원한다고 주장하지 않습니다.
jsx
// Entry module in an existing sku application:
// npm install braid-design-system
import 'braid-design-system/reset'; // Keep this first.
import React,{useState} from 'react';
import {createRoot} from 'react-dom/client';
import seekJobsTheme from 'braid-design-system/themes/seekJobs';
import {BraidProvider,Box,Stack,Heading,Text,TextField,Button,Actions,Card} from 'braid-design-system';
const jobs=[{id:1,title:'Product designer',city:'Melbourne'},{id:2,title:'Design systems engineer',city:'Sydney'},{id:3,title:'UX researcher',city:'Melbourne'}];
function JobSearch(){
const [draft,setDraft]=useState(''),[query,setQuery]=useState(''),[error,setError]=useState('');
const results=jobs.filter(j=>(j.title+' '+j.city).toLowerCase().includes(query.toLowerCase()));
function search(){if(!draft.trim()){setError('Enter a role or city.');return;}setError('');setQuery(draft.trim());}
function clear(){setDraft('');setQuery('');setError('');}
return <Box component="main" padding={{mobile:'medium',tablet:'xlarge'}}>
<Stack space="large">
<Heading level="2">Find your next possibility</Heading>
<TextField id="job-query" label="Role or city" value={draft}
onChange={event=>{setDraft(event.target.value);setError('');}}
onClear={()=>setDraft('')} clearLabel="Clear role or city"
tone={error?'critical':'neutral'} message={error||'Search three original sample roles.'}/>
<Actions><Button tone="brandAccent" variant="solid" onClick={search}>Search jobs</Button>
<Button variant="ghost" onClick={clear}>Clear search</Button></Actions>
<Text>{results.length} sample results</Text>
{results.map(job=><Card key={job.id}><Stack space="small"><Heading level="3">{job.title}</Heading><Text tone="secondary">{job.city}</Text></Stack></Card>)}
{!results.length&&<Text>No sample roles match. Try a different title or city.</Text>}
<Text size="small" tone="secondary">These are sample listings, not live vacancies.</Text>
</Stack>
</Box>;
}
createRoot(document.getElementById('root')).render(<BraidProvider theme={seekJobsTheme}><JobSearch/></BraidProvider>);
프로젝트를 위한 프롬프트
에이전트 프롬프트
seekJobs 테마와 reset을 먼저 적용해 Braid 검색을 구성하고, TextField 이벤트 상태와 Actions를 사용하세요. 단일 주요 검색에는 명시적으로 brandAccent solid를, 초기화에는 중립적인 ghost를 사용합니다. 빈 검색도 시도할 수 있게 하고 오류를 설명하세요. 검증을 숨기기 위해 일반 Button을 비활성화하지 마세요.
05
SEEK Braid 디자인 시스템 자료 및 범위
독립적인 OpenDesign 참고 자료입니다. 직접 작성한 샘플 공고이며, 실제 채용 공고나 지원 제출 기능은 없습니다. 이름이 지정된 Braid 변수는 선택한 테마에 따라 값이 결정됩니다.