# Esri Calcite 디자인 시스템

## Esri Calcite 디자인 시스템 인터페이스 미리보기

목록이나 개략도에서 사이트를 선택하고, 검토 상태를 필터링한 다음, 새로고침할 때까지 사이트별로 유지되는 메모를 확인하세요.

## Esri Calcite 디자인 시스템 디자인 참조 자료

Calcite 디자인 토큰 4.1.0과 현재 컴포넌트 API을 Components 5.1.2와 함께 확인했습니다. 토큰 패키지와 컴포넌트 패키지는 버전 번호가 서로 다릅니다. 삽입된 예시는 HTML/CSS를 독자적으로 해석한 결과이며, Calcite 런타임을 재배포하거나 기본 제공 동작이라고 주장하지 않습니다.

### 라이트 테마의 표면, 텍스트 및 상호작용

| 이름 / 규칙 | 값 / 구성 | 용도 |
| --- | --- | --- |
| --calcite-color-surface-1 | #f7f7f7 | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-surface-2 | #ffffff | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-surface-3 | #f2f2f2 | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-surface-4 | #ebebeb | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-surface-highlight | #d6efff | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-text-1 | #141414 | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-text-2 | #4a4a4a | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-text-3 | #6b6b6b | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-text-inverse | #ffffff | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-text-link | #00619b | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-border-1 | #d4d4d4 | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-border-2 | #dedede | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-border-3 | #ebebeb | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-border-input | #949494 | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-brand | #007ac2 | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-brand-hover | #00619b | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
| --calcite-color-brand-press | #004874 | 토큰 이름이 나타내는 역할에 대한 의미론적 참조입니다. 선택 상태와 필드 테두리에는 서로 다른 대비가 필요합니다. |
### 현재 간격 스케일

| 이름 / 규칙 | 값 / 구성 | 용도 |
| --- | --- | --- |
| --calcite-space-none | 0 | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-px | 1px | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-base | 2px | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-2xs | 0.25rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-xs | 0.375rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-sm | 0.5rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-sm-plus | 0.625rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-md | 0.75rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-md-plus | 0.875rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-lg | 1rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-xl | 1.25rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-2xl | 1.5rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-3xl | 2rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
| --calcite-space-4xl | 2.75rem | 현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요. |
### 타이포그래피와 모서리 스케일

| 이름 / 규칙 | 값 / 구성 | 용도 |
| --- | --- | --- |
| --calcite-corner-radius-none | 0 | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-corner-radius-xs | 2px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-corner-radius-sm | 4px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-corner-radius-pill | 9999px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-font-size-xs | 10px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-font-size-sm | 12px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-font-size | 14px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-font-size-md | 16px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-font-size-lg | 18px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-font-size-xl | 20px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
| --calcite-font-size-2xl | 24px | 스크린샷에서 추정한 측정값이 아니라 공개된 값입니다. |
### 입력 컴포넌트 API

| 이름 / 규칙 | 값 / 구성 | 용도 |
| --- | --- | --- |
| scale | s / m / l; default m | 컴포넌트 스케일을 의도적으로 변경하세요. 페이지 확대/축소를 변경하는 것과는 다릅니다. |
| status | idle / valid / invalid | 색상만 사용하지 말고 메시지로 필드의 유효성을 알리세요. |
| labelText | string | 현재 입력 API에 표시되는 레이블 텍스트입니다. |
| validationMessage | string | 컨트롤 옆에 잘못된 입력에 대한 설명을 표시하세요. |
| calciteInputInput | event | 사용자가 입력할 때 event.target.value를 읽으세요. |
| setFocus() | Promise<void> | 애플리케이션 유효성 검사 오류가 발생하면 기본 컴포넌트로 포커스를 이동하세요. |
## Esri Calcite 디자인 시스템 전체 가이드

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

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

## Esri Calcite 디자인 시스템 작업 공간 및 패널 아키텍처

### 객체의 맥락을 계속 표시하세요

> 공간 정보를 다루는 작업에는 보통 두 가지 맥락이 있습니다. 살펴보는 영역과 선택한 객체입니다. 이 예제에서는 작은 사이트 목록을 독자적인 개략도 및 검사 패널 옆에 배치합니다. 목록 행이나 핀을 선택하면 동일한 안정적 사이트 ID가 설정되므로 이름, 상태, 메모가 서로 다른 객체를 가리키는 일이 없습니다.

### 그림과 실제 지도를 구분하세요

SVG는 목록과 공간 표면 간의 관계를 보여줍니다. 투영법, 좌표, 레이어, 확대/축소, 지오코딩 또는 경로 안내 기능은 없습니다. 표시된 캡션은 개략도이며 축척에 맞지 않는다고 안내합니다. 프로덕션 ArcGIS 뷰에는 별도의 SDK 통합, 데이터 및 접근성 작업이 필요합니다. Calcite 셸만으로는 이러한 기능을 제공하지 않습니다.

## Esri Calcite 디자인 시스템 의미 기반 색상 역할

### 강한 카드 없이 표면으로 도구 영역을 나누세요

참조에서는 surface-1부터 surface-4까지 구분하며, surface-highlight는 강조용으로 남겨 둡니다. 예시에서는 절제된 오프화이트 캔버스에 흰색 검사 패널과 옅은 파란색 선택 행을 사용합니다. 얇은 중립색 테두리로 패널 경계를 표시해 모든 필드마다 떠 있는 카드를 둘 필요가 없도록 합니다.

### 파란색에 일관된 상호작용 의미를 부여하세요

브랜드, 호버 및 누름 색상은 #007ac2에서 #00619b, #004874로 이어지는 더 어두운 계열을 이룹니다. 선택된 핀과 기본 저장 작업은 같은 계열을 서로 다른 상호작용 역할에 사용합니다. 검토 상태 문구와 메모 유효성 검사 메시지는 색상만으로 의미를 전달하지 않아도 잘 읽힙니다.

## Esri Calcite 디자인 시스템 타이포그래피 및 정보 밀도

### 작은 정보의 위계 유지

공개된 절대 크기 타이포그래피 참조에는 작은 텍스트 12px, 기본 텍스트 14px, 중간 크기 16px, 매우 큰 텍스트 20px가 포함됩니다. 예시에서는 작업 공간 제목에 20px, 패널 제목에 16px, 컨트롤에 14px를 사용합니다. 메타데이터는 더 작게 표시하지만, 선택한 객체를 식별하는 유일한 수단으로 사용하지는 않습니다.

### 대체 글꼴과 브랜드 글꼴을 구분하세요

이 독립 예시에서는 Arial과 시스템 대체 글꼴을 사용합니다. Esri 브랜드 글꼴을 포함하지 않으며, 대체 글꼴을 Avenir라고 표시하지도 않습니다. 승인된 네이티브 통합에서는 고정된 Calcite 릴리스의 에셋과 테마 설정을 따라야 합니다. 글꼴을 변경하면 줄바꿈과 컨트롤 너비가 달라질 수 있으므로, 실제 배포 에셋을 사용해 테스트하세요.

## Esri Calcite 디자인 시스템 간격 토큰과 반응형 패널

### 최신 토큰 이름을 선택하세요

참조에서는 최신 --calcite-space-* 스케일을 사용하며, 가장 작은 간격에는 픽셀 값을, 더 큰 간격에는 rem 값을 혼합해 적용합니다. 네이티브 테마를 구현할 때는 이 명명된 역할을 일관되게 사용하세요. 이 예시의 애플리케이션 레이아웃은 명시적인 CSS 값을 사용하므로 제한된 패키지를 가져오지 않아도 동작을 확인할 수 있습니다.

### 필드를 좁히기 전에 인스펙터를 재배치하세요

원래 애플리케이션의 중단점에서는 인스펙터가 목록과 일러스트레이션 아래로 이동합니다. 좁은 휴대전화 화면에서는 세 영역이 세로로 배치됩니다. 텍스트 영역은 레이블, 도움말, 저장 작업 버튼과 함께 전체 너비를 유지합니다. 이 중단점은 직접 설정한 레이아웃 값이며, Calcite 토큰 값이라고 주장하는 것이 아닙니다.

## Esri Calcite 디자인 시스템 선택 및 필터링 동작

### 선택 항목 식별자 하나를 사용하세요

목록과 도식은 현재 배열 위치가 아니라 A-01, B-02, C-03을 기준으로 선택 상태를 공유합니다. Checked로 필터링하면 조건에 맞는 사용 가능한 사이트가 선택되고 인스펙터도 해당 사이트를 표시합니다. 목록 버튼과 해당 핀 모두에서 눌림 상태를 확인할 수 있습니다.

### 저장된 메모와 저장되지 않은 초안을 분리하세요

저장된 메모는 사이트 ID를 키로 하는 Map에 저장됩니다. 해당 사이트로 돌아가면 메모가 복원되며, 저장하지 않고 사이트를 바꾸면 저장되지 않은 텍스트는 대체됩니다. 도움말에는 메모가 로컬에만 유지된다고 명시되어 있습니다. 초안을 잃는 데 큰 비용이 따르는 프로덕션 편집기라면 저장하지 않은 변경 사항을 보호하는 기능을 추가해야 합니다.

## Esri Calcite 디자인 시스템 입력 검증 및 복구

### Calcite를 도입할 때는 실제 네이티브 입력 API을 사용하세요

네이티브 예시에서는 labelText, value, status, validationMessage를 사용합니다. calciteInputInput 이벤트로 이전 오류를 지우고, setFocus()로 애플리케이션 규칙을 통과하지 못했을 때 해당 컴포넌트에 다시 포커스를 둘 수 있습니다. 이는 공개된 API 이름이며, 내부 입력 요소를 찾으려고 shadow root에 접근하는 방식과 다릅니다.

### 로컬 확인 메시지를 서비스 저장으로 오해하지 마세요

독립 미리보기에서는 앞뒤 공백을 제거한 문자가 10자 이상이어야 하고, 입력 길이는 240자로 제한됩니다. 확인 메시지에는 사이트 이름과 함께 데이터가 로컬에 저장되었다는 점이 명시됩니다. 새로고침하면 메모는 사라집니다. 네트워크 기반 버전이라면 저장 대기, 실패, 재시도, 충돌 처리 동작이 필요합니다. 로컬 저장 메시지만으로는 이러한 동작을 암시하지 않습니다.

1. 빈 메모나 짧은 메모를 저장해 보고 오류를 확인하세요.

2. 더 긴 메모를 입력하고 저장한 뒤, 다른 사이트를 선택했다가 돌아오세요.

3. 새로고침하여 이 예시가 저장을 보장하지 않는지 확인하세요.

## Esri Calcite 디자인 시스템의 접근 가능한 공간 정보 대안

### 각 핀에 대응하는 목록 항목을 제공하세요

동작 가능한 모든 핀에는 사이트 목록에 텍스트 버튼이 있습니다. 따라서 객체를 선택할 때 그림으로 된 지도만 이용할 필요는 없습니다. SVG에는 간결한 설명을 제공하고, 각 컨트롤에는 별도의 이름과 눈에 띄는 포커스 윤곽선을 표시합니다. 실제 지도에 필요한 접근성은 도식의 대체 텍스트를 복사하는 것만으로 충족되지 않습니다.

### 오류 메시지를 해당 필드 옆에 배치하세요

텍스트 영역은 aria-describedby를 통해 도움말과 오류 메시지를 모두 참조합니다. 입력이 잘못되면 aria-invalid가 설정되고 포커스가 이동하며, 수정하면 이전의 잘못된 상태가 해제됩니다. 확인 메시지는 상태 영역을 사용합니다. 키보드 사용자는 그려진 지형 도형을 조작하지 않고도 사이트를 선택하고 메모를 입력해 저장할 수 있습니다.

## Esri Calcite 디자인 시스템 버전 및 통합 경계

### 패키지 버전을 고정하고 라이선스 요건을 확인하세요

Components 5.1.2와 design tokens 4.1.0은 별개의 패키지입니다. 현재 라이선스 페이지에는 ArcGIS Online, Enterprise 또는 Location Platform 계정이 있으면 이용할 수 있다고 안내되어 있습니다. 과거 오픈 소스 릴리스에 대한 가정을 현재 패키지에 적용하지 마세요. 아래의 네이티브 소스는 해당 요건을 충족한 프로젝트를 위한 것입니다.

- Components: 5.1.2.

- 디자인 토큰 참조: 4.1.0.

- 미리보기: 독립적인 HTML, CSS, JavaScript이며 Esri 런타임을 포함하지 않습니다.

### 사용하는 컴포넌트만 등록하세요

최소 모듈 예시는 입력 및 버튼 컴포넌트의 진입점을 가져온 다음 공개 속성과 이벤트를 사용합니다. 독립형 전체 HTML 예시는 단독으로 실행할 수 있습니다. 네이티브 코드 조각은 고정된 패키지가 설치된 Vite 프로젝트에서 사용할 수 있는 대체 진입점입니다. 이 코드는 공개 API을 기준으로 확인했으며, 도식을 뒷받침하는 컴파일된 런타임이라고 설명하지 않습니다.

## Esri Calcite 디자인 시스템 컴포넌트 예시

### 독립형 사이트 검사 예시

이 미리보기에서 사용한 독립형 HTML, CSS, JavaScript 전체입니다. index.html로 저장한 뒤 브라우저에서 여세요.

```
<!doctype html>
<html lang="en">
  <meta charset="utf-8" /><meta
    name="viewport"
    content="width=device-width,initial-scale=1"
  /><title>Site inspection panel</title
  ><style>
    * {
      box-sizing: border-box;
    }
    body {
      margin: 0;
      font:
        14px/1.5 Arial,
        sans-serif;
      color: #141414;
      background: #f7f7f7;
    }
    button,
    input,
    select,
    textarea {
      font: inherit;
    }
    button {
      cursor: pointer;
    }
    button:focus-visible,
    input:focus-visible,
    select:focus-visible,
    textarea:focus-visible {
      outline: 2px solid #007ac2;
      outline-offset: 3px;
    }
    header {
      padding: 20px 24px;
      background: #fff;
      border-bottom: 1px solid #d4d4d4;
      display: flex;
      justify-content: space-between;
      gap: 16px;
      align-items: center;
    }
    h1 {
      font-size: 20px;
      margin: 0;
    }
    header p {
      margin: 2px 0 0;
      color: #6b6b6b;
      font-size: 12px;
    }
    .badge {
      background: #d6efff;
      color: #004874;
      padding: 4px 8px;
      font-size: 12px;
    }
    .layout {
      display: grid;
      grid-template-columns: 240px minmax(0, 1fr) 280px;
      min-height: 510px;
    }
    .sites,
    .inspector {
      background: #fff;
      padding: 20px;
      border-right: 1px solid #d4d4d4;
    }
    .inspector {
      border-right: 0;
      border-left: 1px solid #d4d4d4;
    }
    h2 {
      font-size: 16px;
      margin: 0 0 16px;
    }
    label {
      display: block;
      font-weight: 600;
      margin: 12px 0 6px;
    }
    select,
    textarea {
      width: 100%;
      border: 1px solid #949494;
      background: #fff;
      padding: 8px;
      color: inherit;
    }
    textarea {
      resize: vertical;
      min-height: 96px;
    }
    .site {
      display: block;
      width: 100%;
      text-align: left;
      background: #fff;
      border: 1px solid #dedede;
      padding: 12px;
      margin-top: 8px;
    }
    .site[aria-pressed="true"] {
      border-left: 4px solid #007ac2;
      background: #d6efff;
    }
    .site small {
      display: block;
      color: #4a4a4a;
      margin-top: 3px;
    }
    .map {
      position: relative;
      overflow: hidden;
      min-height: 360px;
      background: #ebebe5;
    }
    .map svg {
      width: 100%;
      height: 100%;
      position: absolute;
      inset: 0;
    }
    .map-caption {
      position: absolute;
      bottom: 12px;
      left: 12px;
      padding: 4px 8px;
      background: #fff;
      font-size: 12px;
    }
    .pin {
      position: absolute;
      width: 36px;
      height: 36px;
      border-radius: 50%;
      border: 3px solid #fff;
      background: #007ac2;
      color: #fff;
      font-weight: bold;
      box-shadow: 0 2px 8px #0004;
    }
    .pin[aria-pressed="true"] {
      background: #004874;
      outline: 3px solid #007ac2;
      outline-offset: 3px;
    }
    .pin[hidden] {
      display: none;
    }
    dl {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 8px;
      font-size: 12px;
    }
    dt {
      color: #6b6b6b;
    }
    dd {
      margin: 0;
      text-align: right;
    }
    .primary {
      background: #007ac2;
      color: white;
      border: 0;
      width: 100%;
      padding: 10px;
      margin-top: 12px;
    }
    .primary:hover {
      background: #00619b;
    }
    .error {
      color: #a11b1b;
      font-size: 12px;
    }
    .saved {
      color: #237000;
      font-size: 12px;
    }
    footer {
      padding: 16px 24px;
      border-top: 1px solid #d4d4d4;
      color: #6b6b6b;
      font-size: 12px;
      background: #fff;
    }
    @media (max-width: 1000px) {
      .layout {
        grid-template-columns: 210px minmax(0, 1fr);
      }
      .inspector {
        grid-column: 1/-1;
        border-left: 0;
        border-top: 1px solid #d4d4d4;
      }
      .inspector form {
        max-width: 640px;
      }
    }
    @media (max-width: 550px) {
      header {
        align-items: flex-start;
        flex-direction: column;
      }
      .layout {
        display: flex;
        flex-direction: column;
      }
      .sites {
        border-right: 0;
      }
      .map {
        height: 300px;
        min-height: 300px;
      }
      .inspector {
        padding: 20px;
      }
      .site {
        display: inline-block;
        width: 100%;
      }
    }
  </style>
  <body>
    <header>
      <div>
        <h1>Field inspection workspace</h1>
        <p>Original Calcite-inspired layout study · fictional site data</p>
      </div>
      <span class="badge">Local notes only</span>
    </header>
    <main class="layout">
      <aside class="sites">
        <h2>Inspection sites</h2>
        <label for="filter">Show sites</label
        ><select id="filter">
          <option value="all">All sites</option>
          <option value="review">Needs review</option>
          <option value="checked">Checked</option>
        </select>
        <div id="site-list"></div>
        <p id="count" aria-live="polite"></p>
      </aside>
      <section class="map" aria-label="Illustrated site plan">
        <svg
          viewBox="0 0 700 510"
          preserveAspectRatio="xMidYMid slice"
          role="img"
          aria-label="Original schematic with a river, roads and three inspection sites; not a geographic map"
        >
          <rect width="700" height="510" fill="#e4e8db" />
          <path
            d="M480 -50Q230 110 440 260T360 570"
            fill="none"
            stroke="#b6dbe9"
            stroke-width="100"
          />
          <path
            d="M-20 150L720 280M160 -20L290 530M-20 400L720 80"
            stroke="#fff"
            stroke-width="22"
          />
          <path
            d="M-20 150L720 280M160 -20L290 530M-20 400L720 80"
            stroke="#c8c8bf"
            stroke-width="2"
          />
          <g fill="#c4cfb5">
            <rect x="50" y="50" width="100" height="70" />
            <rect x="510" y="340" width="130" height="80" />
            <rect x="80" y="260" width="70" height="60" />
          </g>
        </svg>
        <div id="pins"></div>
        <span class="map-caption">Schematic illustration · not to scale</span>
      </section>
      <aside class="inspector">
        <h2 id="site-title">North access</h2>
        <dl>
          <dt>Site ID</dt>
          <dd id="site-id"></dd>
          <dt>Review state</dt>
          <dd id="site-state"></dd>
        </dl>
        <form id="note-form" novalidate>
          <label for="note">Inspection note</label
          ><textarea
            id="note"
            aria-describedby="note-help error"
            maxlength="240"
          ></textarea>
          <p id="note-help">
            Add 10–240 characters. Notes stay in this page until reload.
          </p>
          <p id="error" class="error" hidden>
            Write at least 10 characters before saving.
          </p>
          <button class="primary" type="submit">Save local note</button>
          <p id="saved" class="saved" role="status"></p>
        </form>
      </aside>
    </main>
    <footer>
      Independent HTML/CSS interpretation by OpenDesign. No Calcite runtime,
      Esri fonts, ArcGIS map or service is loaded.
    </footer>
    <script>
      const sites = [
        { id: "A-01", name: "North access", state: "review", x: 29, y: 24 },
        { id: "B-02", name: "River crossing", state: "review", x: 62, y: 48 },
        { id: "C-03", name: "South garden", state: "checked", x: 34, y: 76 },
      ];
      let selected = "A-01";
      const notes = new Map(),
        list = document.querySelector("#site-list"),
        pins = document.querySelector("#pins"),
        filter = document.querySelector("#filter"),
        note = document.querySelector("#note"),
        error = document.querySelector("#error"),
        saved = document.querySelector("#saved");
      function render() {
        const visible = sites.filter(
          (s) => filter.value === "all" || s.state === filter.value,
        );
        if (!visible.some((s) => s.id === selected)) selected = visible[0]?.id;
        list.replaceChildren();
        pins.replaceChildren();
        for (const s of visible) {
          const b = document.createElement("button");
          b.type = "button";
          b.className = "site";
          b.setAttribute("aria-pressed", String(s.id === selected));
          b.textContent = s.name;
          const small = document.createElement("small");
          small.textContent =
            s.id + " · " + (s.state === "review" ? "Needs review" : "Checked");
          b.append(small);
          b.onclick = () => {
            selected = s.id;
            render();
          };
          list.append(b);
          const pin = document.createElement("button");
          pin.type = "button";
          pin.className = "pin";
          pin.style.left = s.x + "%";
          pin.style.top = s.y + "%";
          pin.textContent = s.id[0];
          pin.setAttribute("aria-label", "Select " + s.name);
          pin.setAttribute("aria-pressed", String(s.id === selected));
          pin.onclick = b.onclick;
          pins.append(pin);
        }
        const s = sites.find((s) => s.id === selected);
        document.querySelector("#count").textContent =
          visible.length + " sites shown";
        document.querySelector("#site-title").textContent = s.name;
        document.querySelector("#site-id").textContent = s.id;
        document.querySelector("#site-state").textContent =
          s.state === "review" ? "Needs review" : "Checked";
        note.value = notes.get(s.id) || "";
        error.hidden = true;
        note.removeAttribute("aria-invalid");
        saved.textContent = "";
      }
      filter.onchange = render;
      note.oninput = () => {
        error.hidden = true;
        note.removeAttribute("aria-invalid");
        saved.textContent = "";
      };
      document.querySelector("#note-form").onsubmit = (e) => {
        e.preventDefault();
        const value = note.value.trim();
        if (value.length < 10) {
          error.hidden = false;
          note.setAttribute("aria-invalid", "true");
          note.focus();
          return;
        }
        notes.set(selected, value);
        saved.textContent = "Note saved locally for " + selected + ".";
      };
      render();
    </script>
  </body>
</html>
```

코드 참조

- 원본 도식과 애플리케이션 동작입니다. 지도 SDK, 원격 데이터, Esri 런타임은 포함되어 있지 않습니다.

### 네이티브 Calcite 입력 대안

index.html에 <div id="app"></div>가 포함된 Vite 바닐라 프로젝트에서 src/main.js를 교체하세요.

```
import "@esri/calcite-components/components/calcite-input";
import "@esri/calcite-components/components/calcite-button";

document.querySelector("#app").innerHTML = `
  <calcite-input id="inspection-note" label-text="Inspection note"
    validation-message="Enter at least ten characters"></calcite-input>
  <calcite-button id="save-note">Validate note</calcite-button>
  <p id="result" role="status"></p>`;
const field = document.querySelector("#inspection-note");
const result = document.querySelector("#result");
field.addEventListener("calciteInputInput", () => {
  field.status = "idle";
  result.textContent = "";
});
document.querySelector("#save-note").addEventListener("click", async () => {
  if (field.value.trim().length < 10) {
    field.status = "invalid";
    await field.setFocus();
    return;
  }
  field.status = "valid";
  result.textContent = "The note passes the local rule. Nothing was sent.";
});
```

코드 참조

- 현재 라이선스 요건을 확인한 뒤 @esri/calcite-components@5.1.2를 설치하세요. 이 대안은 API을 기준으로 확인했습니다. 위의 독립 미리보기는 브라우저에서 테스트한 예시입니다.

## Esri Calcite 디자인 시스템 에이전트 설정

### 구현 시작점

현재 라이선스 조건에 따라 네이티브 컴포넌트 패키지를 사용하려면 이용 자격을 갖춘 ArcGIS 계정이 필요합니다. 컴포넌트 에셋을 직접 호스팅할 경우 에셋 경로 요건을 확인하세요.

```
# Independent specimen: save the complete HTML example as index.html.
# Native alternative for an appropriately licensed project:
npm create vite@latest calcite-note -- --template vanilla
cd calcite-note
npm install
npm install @esri/calcite-components@5.1.2
# Replace src/main.js with the native input example.
npm run dev
```

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

```
Calcite 참고 자료를 활용해 공간 정보를 집중적으로 살펴볼 수 있는 작업 공간을 구성하세요. 목록과 검사기에서 선택한 객체 ID를 일치시키고, 저장 상태를 텍스트로 표시하며, 로컬 메모와 서비스 저장을 구분하세요. 독립형 해석을 출발점으로 삼거나, 라이선스가 적용되는 네이티브 컴포넌트 경로를 의도적으로 선택하세요. 원본 도식을 ArcGIS 지도라고 설명하지 마세요.
```

## Esri Calcite 디자인 시스템 출처 및 범위

OpenDesign 애플리케이션 가이드. 그림에 표시된 예제는 독립적으로 구현되었으며 가상 데이터를 사용합니다. 현재 Calcite 사용에는 Esri 라이선스가 적용되며, 적격한 ArcGIS 계정이 필요합니다. 네이티브 통합 예제는 적절한 라이선스를 보유한 프로젝트를 위한 대안이며, 이 미리보기의 런타임이 아닙니다.

- Calcite 입력 API 필드 속성, 이벤트 및 포커스 메서드.

- Calcite 시작하기 컴포넌트 등록 및 에셋 설정.

- 현재 라이선스 ArcGIS 계정 및 사용 요건.

- 게시된 디자인 토큰 고정된 서체, 간격 및 모서리 반경 참고 자료.

## Esri Calcite 디자인 시스템 관련 자료

### GitLab Pajamas

기술 워크플로를 위한 Pajamas 가이드입니다. 실제 시맨틱 토큰, 유연한 타이포그래피와 간격, 단계별 파이프라인 검사기, 읽기 쉬운 실패 메시지, 로컬에서만 수행되는 재시도 시뮬레이션을 제공합니다.

### HPE Grommet

Grommet의 레이아웃 기본 요소, 제어형 폼 필드, 숫자 미터를 사용해 수용 인원 예약 인터페이스를 구성하세요. 테마 스케일을 의도적으로 활용하고, 기본 Grommet 테마와 별도로 설치하는 HPE 테마를 구분하세요.

### Infor 디자인 시스템

실제 IDS 입력 상태, 테마 별칭 및 정확한 에셋 식별자를 사용해 엔터프라이즈 유지보수 양식을 구성하세요. 현재 공개 테마 토큰과 버전을 고정한 Web Components 패키지, 유효성 검사 및 초기화 기능이 있는 로컬 초안 워크플로를 살펴보세요.


- [구현 레퍼런스](https://developers.arcgis.com/calcite-design-system/)
- [출처](https://cdn.jsdelivr.net/npm/@esri/calcite-design-tokens@4.1.0/dist/css/light.css)
- [출처](https://cdn.jsdelivr.net/npm/@esri/calcite-design-tokens@4.1.0/dist/css/global.css)
- [출처](https://developers.arcgis.com/calcite-design-system/components/input/)
- [코드 참조](https://cdn.jsdelivr.net/npm/@esri/calcite-design-tokens@4.1.0/dist/css/light.css)
- [코드 참조](https://developers.arcgis.com/calcite-design-system/components/input/)
- [Calcite 입력 API 필드 속성, 이벤트 및 포커스 메서드.](https://developers.arcgis.com/calcite-design-system/components/input/)
- [Calcite 시작하기 컴포넌트 등록 및 에셋 설정.](https://developers.arcgis.com/calcite-design-system/get-started/)
- [현재 라이선스 ArcGIS 계정 및 사용 요건.](https://developers.arcgis.com/calcite-design-system/resources/licensing/)
- [게시된 디자인 토큰 고정된 서체, 간격 및 모서리 반경 참고 자료.](https://cdn.jsdelivr.net/npm/@esri/calcite-design-tokens@4.1.0/dist/css/global.css)
