지리 정보 · 새 리소스

Esri Calcite 디자인 시스템.

공간 도구에 초점을 맞춘 실용적인 Calcite 참고 자료입니다. 의미가 분명한 표면과 파란색 동작 요소, 간결한 검사 패널, 실제 입력 API, 완성된 로컬 사이트 검토 예제를 다룹니다.

OpenDesign 가이드8개 챕터참조 항목 48개독자적 적용 가이드
01

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

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

가상의 사이트를 살펴보고 로컬 메모를 남기세요전체 예시 열기
문서화된 시각 자료를 바탕으로 독자적으로 해석한 HTML/CSS/JavaScript입니다. 그림은 ArcGIS 지도가 아니며 Esri 런타임이나 글꼴이 포함되어 있지 않습니다. · 구현 레퍼런스
02

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-none0현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-px1px현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-base2px현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-2xs0.25rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-xs0.375rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-sm0.5rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-sm-plus0.625rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-md0.75rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-md-plus0.875rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-lg1rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-xl1.25rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-2xl1.5rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-3xl2rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.
--calcite-space-4xl2.75rem현재 간격 토큰 계열입니다. rem 단위 간격은 루트 글꼴 크기를 따릅니다. 더 이상 사용되지 않는 간격 별칭과 혼용하지 마세요.

타이포그래피와 모서리 스케일

출처
이름 / 규칙값 / 구성용도
--calcite-corner-radius-none0스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-corner-radius-xs2px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-corner-radius-sm4px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-corner-radius-pill9999px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-font-size-xs10px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-font-size-sm12px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-font-size14px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-font-size-md16px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-font-size-lg18px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-font-size-xl20px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.
--calcite-font-size-2xl24px스크린샷에서 추정한 측정값이 아니라 공개된 값입니다.

입력 컴포넌트 API

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

Esri Calcite 디자인 시스템 전체 가이드

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

DESIGN-esri-calcite.md

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

가이드 다운로드

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

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

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

이 OpenDesign 가이드에서

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

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 값을 사용하므로 제한된 패키지를 가져오지 않아도 동작을 확인할 수 있습니다.

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

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 런타임을 포함하지 않습니다.

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

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

독립형 사이트 검사 예시

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

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를 교체하세요.

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/[email protected]를 설치하세요. 이 대안은 API을 기준으로 확인했습니다. 위의 독립 미리보기는 브라우저에서 테스트한 예시입니다.
04

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

구현 시작점

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

sh
# 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/[email protected]
# Replace src/main.js with the native input example.
npm run dev

프로젝트를 위한 프롬프트

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

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

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