# Palantir Blueprint 디자인 시스템

## Palantir Blueprint 디자인 시스템 인터페이스 미리보기

이름이나 유형으로 필터링하고, 데이터 세트를 선택하고, 행 밀도와 화면 모드를 변경한 다음, 검토 메모를 확인하고 로컬 상태를 업데이트하세요.

## Palantir Blueprint 디자인 시스템 디자인 참고 자료

Blueprint 코어 6.21.0은 CSS 및 네이티브 React 컴포넌트를 제공합니다. 다음은 라이트 테마에서 선택한 토큰 값입니다. 직접 반전하지 말고 라이브러리의 다크 테마를 사용하세요. 애플리케이션 레이아웃에 관한 선택 사항은 예제 CSS에 표시되어 있습니다.

### 의미 색상 및 상호작용 상태

| 이름 / 규칙 | 값 / 구성 | 용도 |
| --- | --- | --- |
| --bp-intent-primary-rest | #2d72d2 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-primary-hover | #215db0 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-primary-active | #184a90 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-primary-disabled | #4c90f0 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-primary-foreground | #ffffff | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-success-rest | #238551 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-success-hover | #1c6e42 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-success-active | #165a36 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-success-disabled | #32a467 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-success-foreground | #ffffff | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-warning-rest | #c87619 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-warning-hover | #935610 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-warning-active | #77450d | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-warning-disabled | #ec9a3c | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-warning-foreground | #111418 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-danger-rest | #cd4246 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-danger-hover | #ac2f33 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-danger-active | #8e292c | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-danger-disabled | #e76a6e | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-intent-danger-foreground | #ffffff | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
### 타이포그래피 단계 및 굵기

| 이름 / 규칙 | 값 / 구성 | 용도 |
| --- | --- | --- |
| --bp-typography-size-body-x-small | 10px | 본문, 레이블 및 보조 데이터의 글자 크기 단계입니다. 작은 메타데이터를 주요 읽기 크기로 사용하지 마세요. |
| --bp-typography-size-body-small | 12px | 본문, 레이블 및 보조 데이터의 글자 크기 단계입니다. 작은 메타데이터를 주요 읽기 크기로 사용하지 마세요. |
| --bp-typography-size-body-medium | 14px | 본문, 레이블 및 보조 데이터의 글자 크기 단계입니다. 작은 메타데이터를 주요 읽기 크기로 사용하지 마세요. |
| --bp-typography-size-body-large | 16px | 본문, 레이블 및 보조 데이터의 글자 크기 단계입니다. 작은 메타데이터를 주요 읽기 크기로 사용하지 마세요. |
| --bp-typography-size-heading-small | 16px | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-size-heading-medium | 20px | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-size-heading-large | 24px | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-size-heading-x-large | 28px | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-size-heading-display | 46px | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-size-code-small | 12px | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-size-code-medium | 13px | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-size-code-large | 14px | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-weight-default | 400 | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-weight-bold | 600 | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-line-height-default | 1.28581 | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
| --bp-typography-line-height-large | 1.5 | 이름이 지정된 계층에 맞춰 역할을 적용하세요. 표의 숫자에는 고정폭 숫자를 추가로 사용할 수 있습니다. |
### 표면, 테두리 및 의미 텍스트

| 이름 / 규칙 | 값 / 구성 | 용도 |
| --- | --- | --- |
| --bp-surface-border-color-default | #5f6b7c1f | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-surface-border-color-strong | #5f6b7c40 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-surface-border-width | 1px | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-surface-border-radius | 4px | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-surface-spacing | 4px | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-surface-background-color-default-rest | #ffffff | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-surface-background-color-default-hover | #f6f7f9 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-typography-color-default-rest | #1c2127 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-typography-color-muted | #5f6b7c | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
### 포커스와 모션

| 이름 / 규칙 | 값 / 구성 | 용도 |
| --- | --- | --- |
| --bp-emphasis-transition-duration | 100ms | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-emphasis-ease-default | cubic-bezier(0.4, 1, 0.75, 0.9) | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-emphasis-ease-bounce | cubic-bezier(0.54, 1.12, 0.38, 1.11) | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-emphasis-focus-color | #2d72d2 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-emphasis-focus-width | 2px | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-emphasis-focus-offset | 2px | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
| --bp-emphasis-motion-reduced | 0 | 이름이 지정된 의미 역할을 사용하고, 상태와 전경색의 조합을 함께 유지하세요. |
### 데이터 세트 워크벤치에 사용된 네이티브 컴포넌트

| 이름 / 규칙 | 값 / 구성 | 용도 |
| --- | --- | --- |
| HTMLTable | striped · interactive · condensed | 네이티브 의미 테이블입니다. condensed prop은 데이터를 변경하지 않고 밀도만 조정합니다. |
| InputGroup | value · onChange · intent | 제어형 검색 및 검토 메모입니다. 애플리케이션의 유효성 검사에서 danger 의도를 설정합니다. |
| FormGroup | labelFor · helperText · intent | 화면에 표시되는 필드 레이블을 연결하고 잘못된 메모의 이유를 설명하세요. |
| HTMLSelect | options · value · onChange | 데이터 세트 유형이 소수의 고정된 옵션으로 구성된 경우 네이티브 select를 사용하세요. |
| Tag | minimal · intent | 상태 텍스트에 성공 또는 경고 스타일을 함께 적용하세요. |
| Button | intent="primary" · type="submit" | 실제 폼을 통해 로컬 검토를 완료하고, 선택 버튼은 보조 동작으로 유지하세요. |
| Switch | checked · onChange | 화면 모드와 밀도를 각각 조정하는 컨트롤입니다. |
| Callout | intent · title | 결과가 없거나 작업의 결과를 설명하세요. 색상만으로 상태를 전달하지 마세요. |
## Palantir Blueprint 디자인 시스템 전체 가이드

실제 인터페이스를 위해 이 시스템을 선택하고 적용하고 검토하는 실용 가이드.

완전한 안내와 구현 시작점, 소스 링크.

## 데이터 중심으로 구성된 Palantir Blueprint 디자인 시스템 워크스페이스

### 명확한 읽기 순서를 갖춘 조밀한 컨트롤

> Blueprint는 컬렉션, 필터, 상태 및 상황별 작업이 한 화면에 함께 있을 때 유용합니다. 이 예제에서는 왼쪽에 데이터 세트 테이블을, 오른쪽에 선택한 레코드를 배치했습니다. 새 페이지를 열지 않고도 레코드를 비교할 수 있습니다. 검토 메모는 편집기에서 작성하고, 테이블은 훑어보기에 적합한 영역으로 유지합니다.

### v6 패키지 규약 사용

이 예제는 @blueprintjs/core 6.21.0으로 컴파일됩니다. CSS 네임스페이스는 bp6이며, 공개 시맨틱 토큰은 --bp-로 시작합니다. 이전 버전인 bp5 스타일시트는 v6 컴포넌트의 CSS를 대체할 수 없습니다. 애플리케이션 진입점에서 패키지 스타일시트를 한 번 가져온 다음, 애플리케이션 레이아웃 규칙은 별도의 클래스로 범위를 한정하세요.

## Palantir Blueprint 디자인 시스템의 의도 및 색상 역할

### 레코드 상태와 선택 상태를 구분하세요

경고 Tag는 데이터 세트에 검토가 필요하다는 뜻이고, 기본 색조가 적용된 행은 해당 레코드가 선택되었다는 뜻입니다. 두 의미는 서로 독립적입니다. 행을 검토 완료로 표시하면 레이블과 의도가 성공으로 바뀌지만, 선택된 데이터 세트는 그대로 유지됩니다. 테이블에는 각 상태를 나타내는 텍스트도 표시해 색상만으로 의미를 전달하지 않도록 합니다.

### 컴포넌트 경계에서 다크 모드를 적용하세요

이 예제에서는 래퍼에 bp6-dark를 추가해 네이티브 컴포넌트가 다크 스타일을 적용할 수 있도록 합니다. 위의 라이트 테마 값은 참고용 스냅샷이지 모든 컴포넌트를 재정의하라는 뜻이 아닙니다. Dialog와 같은 포털을 추가할 때는 렌더링된 콘텐츠에도 동일한 테마 컨텍스트가 적용되는지 확인하세요. 포털을 포함하지 않는 래퍼만으로는 충분하지 않습니다.

## Palantir Blueprint 디자인 시스템의 레코드 및 메타데이터 타이포그래피

### 제목을 선택하기 전에 본문 글자 크기 단계를 확인하세요

공개된 본문 글자 크기는 10, 12, 14, 16px이며, 중간 크기 본문은 14px입니다. 데이터 세트 이름은 주요 읽기 크기로 유지합니다. ID와 레코드 유형에는 보조 텍스트를 사용하고, 레코드 수와 같은 숫자에는 고정폭 숫자를 사용해 값이 바뀌어도 쉽게 비교할 수 있도록 합니다. 이 가이드에서는 주요 콘텐츠에 10px을 사용하지 않도록 권장합니다.

### 검토 패널에 간결한 계층 구조를 만드세요

검토 패널에는 20px 제목과 ID, 담당자, 유형, 개수를 표시하는 정의 목록을 사용합니다. 레이블과 값은 안정적인 2열 정렬을 유지합니다. 검토 메모는 구분선으로 다른 정보와 나누어, 테이블 필터로 오해하지 않도록 합니다.

## Palantir Blueprint 디자인 시스템의 테이블, 필터 및 검토 패널

### 테이블 시맨틱을 유지하세요

HTMLTable은 Blueprint 스타일이 적용된 네이티브 테이블을 제공합니다. 첫 번째 셀의 버튼으로 레코드를 선택할 수 있으며 키보드로도 접근할 수 있습니다. 이 예제에서는 포인터로만 작동하는 행 핸들러가 선택을 위한 유일한 방법이 되지 않도록 했습니다. interactive prop은 행에 호버 스타일을 적용하고, condensed는 필터링과 독립적으로 밀도를 조정합니다.

### 제어되는 데이터 흐름을 명확히 보여주세요

검색과 유형 선택기는 동일한 로컬 행을 바탕으로 표시할 컬렉션을 구성합니다. 필터로 행이 숨겨져도 선택된 레코드는 검토 패널에 그대로 표시할 수 있습니다. 이를 통해 컬렉션 필터가 바뀌었다는 이유만으로 입력 중인 검토 메모가 사라지지 않습니다. 실제 서비스에서는 이 관계를 명확히 정하고 문서화해 편집 내용을 조용히 버리지 않도록 해야 합니다.

1. 표시 이름으로 데이터 세트를 찾으세요.

2. 유형 선택기로 같은 컬렉션의 범위를 좁히세요.

3. 이름 버튼을 선택해 검토 패널을 불러오세요.

4. 검토 메모를 입력하고 제출해 로컬 검토를 완료하세요.

## Palantir Blueprint 디자인 시스템의 간격, 표면 및 밀도

### 간격을 모두 줄이지 말고 작은 단위를 사용하세요

공개된 표면 간격 토큰은 4px입니다. 워크벤치에서는 애플리케이션에 적합한 배수를 사용합니다. 필터 컨트롤 사이에는 12px, 컬렉션과 검토 패널 사이에는 20px, 데스크톱 예제 주변에는 24px을 적용합니다. 이는 CSS에 제시된 구성상의 선택입니다. 4px 테두리 둥글기 토큰을 사용하면 컨트롤과 표면의 형태를 절제된 느낌으로 유지할 수 있습니다.

### 레이블을 줄이기 전에 레이아웃을 조정하세요

예시 애플리케이션의 중단점인 800px에서는 인스펙터가 컬렉션 아래로 이동합니다. 필터는 줄 바꿈되고, 기본 패딩은 16px로 바뀌며, 필요한 경우 테이블에 별도의 가로 스크롤 컨테이너가 적용됩니다. 이 중단점은 이 워크벤치에만 해당하며, Blueprint의 공식 범용 중단점으로 제시하는 값이 아닙니다.

## Palantir Blueprint 디자인 시스템 검토 및 빈 상태

### 검증은 검토 작업에 포함됩니다

빈 메모를 제출하면 InputGroup과 FormGroup에 danger 의도가 설정되고, 설명용 도움말 텍스트가 표시되며, 입력 필드로 포커스가 이동합니다. 텍스트를 입력하면 오류가 사라집니다. 유효한 메모를 제출하면 선택한 로컬 레코드만 변경되고, 메모가 지워지며, 데이터가 전송되지 않았음을 명시하는 성공 Callout이 표시됩니다.

### 빈 컬렉션 바로 옆에 복구 옵션을 배치하세요

결과가 없을 때 표시되는 Callout에는 필터 지우기 옵션이 있습니다. 이 옵션은 검색어와 유형 선택기를 모두 초기화해 전체 컬렉션을 다시 표시합니다. 행을 삭제하거나 인스펙터를 변경하지 않습니다. 다음 상태 경계는 의도적으로 설정되었습니다.

- 선택을 변경하면 검토 대상 레코드가 바뀌며, 검토 상태는 바뀌지 않습니다.

- 컴팩트 모드는 밀도만 변경하며, 레코드 순서는 바꾸지 않습니다.

- 테마는 시각적 표현만 변경하며, 현재 필터는 바꾸지 않습니다.

- 로컬 검토가 성공하면 레코드 하나만 변경되고 결과가 안내됩니다.

## Palantir Blueprint 디자인 시스템 포커스 및 접근성 있는 컴포넌트 사용

### 모든 기본 컨트롤에 레이블을 지정하세요

InputGroup에는 검색을 위한 접근 가능한 이름을 지정하고, 레이블이 있는 검토 필드에는 id를 지정합니다. HTMLSelect에는 고유한 이름이 있고, Switch는 눈에 보이는 레이블을 표시하며, 각 레코드는 실제 버튼으로 엽니다. 컨트롤을 스타일이 적용된 div로 대체하지 말고 이러한 라이브러리 API를 사용하세요.

### 포커스 표시를 눈에 띄게 유지하세요

패키지 참조에는 포커스 너비 2px와 오프셋 2px가 지정되어 있습니다. Card와 선택된 행 위에서도 포커스 표시가 잘 보이도록 유지하세요. 키보드 사용자는 문서 순서에 따라 필터, 행 버튼, 검토 양식에 접근할 수 있어야 합니다. 예시의 상태 문단과 성공 Callout은 사용자를 다른 화면으로 이동시키지 않고 결과를 전달합니다.

## Palantir Blueprint 디자인 시스템 설치 및 애플리케이션 경계

### 예제와 동일한 소스 코드를 실행하세요

완전한 App 소스와 애플리케이션 CSS이 아래에 제공됩니다. App은 Vite React 템플릿이 생성한 루트 요소에 마운트됩니다. 고정된 버전의 코어 패키지를 설치하면 기본 컴포넌트와 패키지 CSS을 사용할 수 있습니다. 텍스트 기반 예제를 위해 별도의 디자인 에셋은 필요하지 않습니다.

### 실제 데이터 저장 계약으로 검토 기능을 확장하세요

프로덕션 환경에서 검토 기능을 제공하려면 레코드 식별자, 권한, 대기 상태, 오류 응답이 필요합니다. 이러한 사항은 애플리케이션에서 처리하고 Blueprint의 FormGroup 및 intent API는 그대로 사용하세요. 서비스에서 저장을 승인하기 전에 저장 성공을 알리지 마세요. 로컬 예제에서는 의도적으로 저장 성공을 알리지 않습니다.

## Palantir Blueprint 디자인 시스템 컴포넌트 예제

### main.jsx — 전체 데이터셋 검토 애플리케이션

필터링, 선택, 밀도, 테마, 필드 검증, 로컬 검토 결과를 포함한 기본 컴포넌트의 전체 구성입니다.

```
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import {
  Button,
  Card,
  Callout,
  FormGroup,
  HTMLSelect,
  HTMLTable,
  InputGroup,
  Switch,
  Tag,
} from "@blueprintjs/core";
import "@blueprintjs/core/lib/css/blueprint.css";
import "./blueprint.css";
const initial = [
  {
    id: "D-1042",
    name: "Harbor sensors",
    type: "Telemetry",
    rows: "18,420",
    owner: "Maya",
    status: "Reviewed",
  },
  {
    id: "D-1043",
    name: "Transit stops",
    type: "Reference",
    rows: "842",
    owner: "Alex",
    status: "Needs review",
  },
  {
    id: "D-1044",
    name: "Water samples",
    type: "Field survey",
    rows: "2,608",
    owner: "Sam",
    status: "Reviewed",
  },
  {
    id: "D-1045",
    name: "Street survey",
    type: "Field survey",
    rows: "4,120",
    owner: "Lee",
    status: "Needs review",
  },
];
function App() {
  const [dark, setDark] = useState(false),
    [compact, setCompact] = useState(false),
    [query, setQuery] = useState(""),
    [rows, setRows] = useState(initial),
    [selected, setSelected] = useState(initial[1]),
    [note, setNote] = useState(""),
    [notice, setNotice] = useState(""),
    [error, setError] = useState(false),
    [type, setType] = useState("All");
  const visible = rows.filter(
    (r) =>
      r.name.toLowerCase().includes(query.toLowerCase()) &&
      (type === "All" || r.type === type),
  );
  function review(e) {
    e.preventDefault();
    if (!note.trim()) {
      setError(true);
      document.getElementById("review-note").focus();
      return;
    }
    const updated = { ...selected, status: "Reviewed" };
    setRows(rows.map((r) => (r.id === selected.id ? updated : r)));
    setSelected(updated);
    setNotice(
      `${selected.name} marked reviewed in this local example. No data was sent.`,
    );
    setNote("");
    setError(false);
  }
  return (
    <main className={"specimen " + (dark ? "bp6-dark" : "")}>
      <header>
        <div>
          <span className="eyebrow">BLUEPRINT · DATA WORKSPACE</span>
          <h1 className="bp6-heading">Dataset review</h1>
          <p className="bp6-text-muted">
            Inspect a record and attach a review note.
          </p>
        </div>
        <Switch
          label="Dark theme"
          checked={dark}
          onChange={(e) => setDark(e.target.checked)}
        />
      </header>
      <div className="workbench">
        <Card className="collection">
          <div className="toolbar">
            <InputGroup
              aria-label="Find a dataset"
              placeholder="Find a dataset…"
              value={query}
              onChange={(e) => setQuery(e.target.value)}
            />
            <HTMLSelect
              aria-label="Dataset type"
              value={type}
              onChange={(e) => setType(e.target.value)}
              options={["All", "Telemetry", "Reference", "Field survey"]}
            />
            <Switch
              label="Compact rows"
              checked={compact}
              onChange={(e) => setCompact(e.target.checked)}
            />
          </div>
          <p className="bp6-text-small bp6-text-muted" role="status">
            {visible.length} matching datasets
          </p>
          <div className="table-scroll">
            <HTMLTable
              striped
              interactive
              condensed={compact}
              style={{ width: "100%" }}
            >
              <thead>
                <tr>
                  <th>Name</th>
                  <th>Records</th>
                  <th>Status</th>
                </tr>
              </thead>
              <tbody>
                {visible.map((r) => (
                  <tr
                    key={r.id}
                    className={selected.id === r.id ? "selected" : ""}
                  >
                    <td>
                      <Button
                        variant="minimal"
                        onClick={() => {
                          setSelected(r);
                          setError(false);
                          setNote("");
                        }}
                        text={r.name}
                      />
                      <div className="bp6-text-small bp6-text-muted">
                        {r.id} · {r.type}
                      </div>
                    </td>
                    <td className="numeric">{r.rows}</td>
                    <td>
                      <Tag
                        minimal
                        intent={r.status === "Reviewed" ? "success" : "warning"}
                      >
                        {r.status}
                      </Tag>
                    </td>
                  </tr>
                ))}
              </tbody>
            </HTMLTable>
          </div>
          {!visible.length && (
            <Callout title="No matching datasets">
              Clear the search or choose a different dataset type.
              <Button
                variant="minimal"
                onClick={() => {
                  setQuery("");
                  setType("All");
                }}
              >
                Clear filters
              </Button>
            </Callout>
          )}
        </Card>
        <Card className="inspector">
          <span className="eyebrow">SELECTED DATASET</span>
          <h2 className="bp6-heading">{selected.name}</h2>
          <dl>
            <dt>Dataset ID</dt>
            <dd>
              <code>{selected.id}</code>
            </dd>
            <dt>Owner</dt>
            <dd>{selected.owner}</dd>
            <dt>Type</dt>
            <dd>{selected.type}</dd>
            <dt>Records</dt>
            <dd>{selected.rows}</dd>
          </dl>
          <form onSubmit={review}>
            <FormGroup
              label="Review note"
              labelFor="review-note"
              intent={error ? "danger" : "none"}
              helperText={
                error
                  ? "Add a note describing what you checked."
                  : "A short note keeps the decision understandable."
              }
            >
              <InputGroup
                id="review-note"
                value={note}
                intent={error ? "danger" : "none"}
                aria-invalid={error}
                onChange={(e) => {
                  setNote(e.target.value);
                  setError(false);
                }}
              />
            </FormGroup>
            <Button intent="primary" type="submit" fill>
              Mark reviewed locally
            </Button>
          </form>
          <p className="bp6-text-small bp6-text-muted">
            Selection, filters and review notes exist only in this preview.
          </p>
        </Card>
      </div>
      {notice && (
        <Callout intent="success" role="status">
          {notice}
        </Callout>
      )}
      <footer>
        Native Blueprint 6.21.0 · HTMLTable, Card, FormGroup, InputGroup,
        Switch, Tag and Callout.
      </footer>
    </main>
  );
}
createRoot(document.getElementById("root")).render(<App />);
```

코드 참조

- 이 파일은 src/main.jsx로 저장하고, 다음 예제는 src/blueprint.css로 저장하세요.

- createRoot 호출은 Vite React index.html의 루트 요소를 사용합니다.

### blueprint.css — 반응형 워크벤치 레이아웃

애플리케이션 레이아웃 규칙은 가져온 기본 CSS을 보완합니다. 800px 중단점과 260px 인스펙터 너비는 이 예제에 맞춰 명시적으로 정한 값입니다.

```
* {
  box-sizing: border-box;
}
body {
  margin: 0;
}
.specimen {
  padding: 24px;
  background: #f6f7f9;
  color: #1c2127;
  font-size: 14px;
}
.specimen.bp6-dark {
  background: #252a31;
  color: #f6f7f9;
}
.specimen header {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  align-items: center;
  margin-bottom: 24px;
}
.eyebrow {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--bp-typography-color-muted);
}
h1.bp6-heading {
  margin: 8px 0;
  font-size: 28px;
}
.workbench {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 260px;
  gap: 20px;
}
.collection,
.inspector {
  min-width: 0;
}
.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}
.toolbar > .bp6-input-group {
  flex: 1;
  min-width: 150px;
}
.toolbar .bp6-control {
  margin: 0;
}
.table-scroll {
  overflow: auto;
}
table td {
  vertical-align: middle !important;
}
.numeric {
  font-variant-numeric: tabular-nums;
}
tr.selected td {
  background: #2d72d20d !important;
}
.inspector h2 {
  font-size: 20px;
  margin-top: 12px;
}
.inspector dl {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin: 24px 0;
}
.inspector dt {
  color: var(--bp-typography-color-muted);
}
.inspector dd {
  margin: 0;
}
.inspector form {
  border-top: 1px solid var(--bp-surface-border-color-default);
  padding-top: 20px;
}
.inspector p {
  margin: 16px 0 0;
}
.specimen > .bp6-callout {
  margin-top: 20px;
}
.specimen footer {
  font-size: 11px;
  margin-top: 24px;
  color: var(--bp-typography-color-muted);
}
@media (max-width: 800px) {
  .workbench {
    grid-template-columns: 1fr;
  }
  .specimen {
    padding: 16px;
  }
  .specimen header {
    align-items: start;
  }
  .toolbar {
    gap: 16px;
  }
  h1.bp6-heading {
    font-size: 24px;
  }
}
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }
}

.bp6-dark .eyebrow,
.bp6-dark .inspector dt,
.bp6-dark footer {
  color: #abb3bf;
}
```

코드 참조

- bp6-dark 클래스는 라이브러리 테마를 활성화합니다. 예제 캔버스 배경은 애플리케이션 CSS입니다.

## Palantir Blueprint 디자인 시스템 AI 에이전트 설정

### 구현 시작점

기본 미리보기는 React 18.3.1 및 Blueprint 6.21.0으로 번들링하고 실행했습니다. 선택한 Vite 릴리스에서 지원하는 Node.js 버전을 사용하세요. 두 애플리케이션 파일은 위에 제공되어 있습니다.

```
npm create vite@latest blueprint-review -- --template react
cd blueprint-review
npm install
npm install react@18.3.1 react-dom@18.3.1 @blueprintjs/core@6.21.0
# Replace src/main.jsx and add src/blueprint.css using the complete examples.
npm run dev
```

### 프로젝트를 위한 프롬프트

```
Blueprint 6.21.0을 사용해 데이터셋 검토 작업 공간을 구축하세요. 제공된 토큰과 완성된 React/CSS 예제를 확인하세요. 기본 HTMLTable, 레이블이 지정된 InputGroup/FormGroup, 상태 Tags, 키보드로 접근할 수 있는 행 선택 기능을 유지하세요. 필터, 선택, 밀도, 모양, 검토 상태를 서로 독립적으로 유지하세요. API이 제공된 경우에만 실제 저장 서비스를 추가하고, 메모를 지우지 않은 채 대기 및 실패 응답을 표시하세요. 반응형 인스펙터와 눈에 보이는 포커스 상태를 유지하세요.
```

## Palantir Blueprint 디자인 시스템 출처 및 범위

Blueprint 6.21.0을 대상으로 한 OpenDesign 애플리케이션 가이드입니다. 실제 React 패키지를 사용해 실행하는 독창적인 데이터셋 검토 예제를 포함합니다. 데이터셋 이름과 검토 결과는 로컬 샘플 데이터입니다. 워크벤치 레이아웃은 애플리케이션 코드이며, 참조 값은 버전이 명시된 공개 패키지에서 가져왔습니다.

- Blueprint 컴포넌트 문서 기본 컴포넌트의 사용 규약 및 예제.

- 공개된 6.21.0 디자인 토큰 참조 표에서 사용하는 정확한 버전별 값입니다.

- Blueprint 소스 저장소 구현, 패키지 릴리스 기록 및 Apache 라이선스.

## Palantir Blueprint 디자인 시스템 관련 리소스

### Pega Bolt

Bolt의 Pega 브랜드 콘텐츠 시스템을 살펴보세요. 유동적인 타이포그래피, 가로·세로 간격의 분리, 편집형 리소스 페이지를 위한 Twig 아코디언 구성을 확인할 수 있습니다.

### Porsche 디자인 시스템

유동적인 간격, CSS 색상 구성, Porsche Next 타이포그래피, 두 가지 선택이 가능한 구성 도구를 통해 Porsche Design System v4를 살펴보세요. 실제 토큰 표현식과 최신 세그먼트 컨트롤 이벤트를 사용하는 완전한 Vite 엔트리 파일이 포함되어 있습니다.

### Red Hat PatternFly

운영 작업 공간에는 PatternFly를 사용하세요. 시맨틱 토큰 참조, 선택 가능한 서비스 카드, 3상태 일괄 선택, 범위 검토, 완전한 기본 React 코드를 제공합니다.


- [구현 레퍼런스](https://blueprintjs.com/docs/#core/components/html-table)
- [출처](https://cdn.jsdelivr.net/npm/@blueprintjs/core@6.21.0/lib/css/blueprint-design-tokens.css)
- [출처](https://blueprintjs.com/docs/#core/components)
- [코드 참조](https://blueprintjs.com/docs/#core/components)
- [코드 참조](https://blueprintjs.com/docs/#core/typography)
- [Blueprint 컴포넌트 문서 기본 컴포넌트의 사용 규약 및 예제.](https://blueprintjs.com/docs/#core/components)
- [공개된 6.21.0 디자인 토큰 참조 표에서 사용하는 정확한 버전별 값입니다.](https://cdn.jsdelivr.net/npm/@blueprintjs/core@6.21.0/lib/css/blueprint-design-tokens.css)
- [Blueprint 소스 저장소 구현, 패키지 릴리스 기록 및 Apache 라이선스.](https://github.com/palantir/blueprint)
