# Chart 누적 막대 · 가로 막대 · 선 · 원형 네 형태를 같은 틀에 담은 차트입니다. > 원본: `03_ASSET_GUIDE/catalog/chart.js` · CSS: `03_ASSET_GUIDE/styles/elements/chart.css` > kind `component` · category `display` · status `stable` · specSource `measured` > 함께 쓰는 CSS: `chip.css` (차트/표 전환 칩) --- ## 1. 구성 네 형태 모두 **Header · Legend · Plot** 으로 구성됩니다. 형태마다 다른 것은 Plot 안의 마크입니다. ``` ┌─────────────────────────────────────────────┐ │ 월별 처리 건수 [차트][표] │ ① ② ③ │ 최근 7개월 · 단위 건 │ │ ■ 처리 완료 ■ 처리 중 ■ 반려 │ ④ │ 100 ┼──────────────────────────── 95 │ ⑤ ⑧ │ 50 ┼──────────█───█────────────── │ ⑥ ⑦ │ 0 ┴──█───█───█───█───█───█───█── │ │ 1월 2월 3월 4월 5월 6월 7월 │ └─────────────────────────────────────────────┘ ``` | # | 요소 | 필수 | 클래스 | 설명 | |---|---|---|---|---| | ① | Header | 필수 | `atom-chart__header` | 제목 블록과 전환 칩을 한 줄에 둡니다. | | ② | Title · Subtitle | 제목 필수 | `atom-chart__title` · `atom-chart__subtitle` | 제목과 부제입니다. 제목은 15px 이며 제목 단계를 쓰지 않습니다. 차트는 보통 [Section Title](./section-title.md) 아래에 놓이기 때문입니다. | | ③ | Toggle | 선택 | `atom-chart__toggle` | 표 뷰를 쓸 때 차트/표 전환 칩([Chip](./chip.md))을 담습니다. | | ④ | Legend | 시리즈 2개 이상 | `atom-chart__legend` · `__legend-item` · `__legend-swatch` · `__legend-label` | 시리즈가 둘 이상이면 **항상** 있습니다. 색은 스와치에만 쓰고 라벨은 텍스트 색으로 둡니다. | | ⑤ | Plot | 필수 | `atom-chart__plot` | 마크가 그려지는 SVG 상자입니다. 축 라벨 띠까지 포함해 높이를 잡습니다. | | ⑥ | Grid line · Axis · Axis label | 필수 | `atom-chart__grid-line` · `__axis` · `__axis-label` | 보조 요소입니다. 헤어라인 실선으로 약하게 표시합니다. | | ⑦ | Bar · Line · Marker · Slice | 필수 | `atom-chart__bar` · `__line` · `__area` · `__marker` · `__slice` | 데이터입니다. 화면에서 가장 강하게 표시됩니다. | | ⑧ | Value | 선택 | `atom-chart__value` (`--on-fill`) | 직접 라벨입니다. **선택적으로만** 답니다. | | — | Tooltip | 선택 | `atom-chart__tooltip` · `__tooltip-title` · `__tooltip-row` · `__tooltip-swatch` · `__tooltip-label` · `__tooltip-value` · `__crosshair` | 보조 수단입니다. 툴팁으로만 값을 확인할 수 있게 하지 않습니다. | | — | Table | 선택 | `atom-chart__table` | 같은 숫자를 표로. `data-view="table"` 일 때 Plot·Legend 대신 보입니다. | | — | Empty | 데이터 없을 때 | `atom-chart__empty` | 빈 상태 문구입니다. | --- ## 2. 종류 / 크기 ### Type 형태는 데이터의 용도에 따라 정하며, 색보다 먼저 정합니다. | type | 데이터의 일 | 참고한 샘플 | |---|---|---| | `bar` | 부분과 전체를 함께 — 합계와 구성 | [stacked](https://www.chartjs.org/docs/latest/samples/bar/stacked.html) | | `bar-horizontal` | 크기 비교. 항목 이름이 길 때 | [horizontal](https://www.chartjs.org/docs/latest/samples/bar/horizontal.html) | | `line` | 시간에 따른 추이 | [line](https://www.chartjs.org/docs/latest/samples/line/line.html) | | `pie` | 전체 대비 비율을 한눈에 | [pie](https://www.chartjs.org/docs/latest/samples/other-charts/pie.html) | #### 누적 막대 막대는 **22px** 입니다(상한 24px). 슬롯을 다 채우지 않고 남는 폭은 여백으로 둡니다. 데이터가 끝나는 쪽 모서리만 **4px** 로 둥글고 기준선 쪽은 각을 유지합니다. 기준선 쪽이 둥글면 막대의 시작점이 흐려지기 때문입니다. 세그먼트 사이는 **2px 서피스 갭**입니다. 세그먼트를 테두리로 나누지 않습니다. > [!NOTE] > **원본 샘플과 다른 점 — 음수를 쓰지 않았습니다** > chart.js 의 stacked 샘플은 -100~100 의 난수를 씁니다. 누적 막대는 **부분과 전체**를 > 보여주는데, 음수가 섞이면 전체 합계의 의미가 없어집니다. 기준선 위아래로 갈리는 > 데이터에는 발산(diverging) 막대를 씁니다. #### 가로 막대 항목 이름이 길면 눕힙니다. 세로로 두면 라벨이 기울어지거나 잘립니다. 두 시리즈가 나란히 놓일 때도 사이는 같은 **2px 갭**입니다. 값은 **첫 시리즈의 팁 바깥**에만 답니다. 막대 안에 넣으려면 글자가 좌우 여백을 두고 들어가야 하고, 짧은 막대에서는 그럴 자리가 없습니다. #### 선 선은 **2px**, 조인·캡은 라운드입니다. 마커는 **r=4**(지름 8px 하한)이고 **2px 서피스 링**을 두릅니다. 링은 선이 겹치는 자리에서도 점을 구분되게 하며, hover 히트 영역에 포함됩니다. 직접 라벨은 **끝점 두 개**에만 답니다. 점마다 숫자를 달지 않습니다. #### 원형 조각 사이도 2px 갭입니다. ### 시리즈 색 색은 `data-series` 하나로 결정됩니다. 슬롯 1–6 이고, [Color](../foundation/color.md) 의 `--atom-chart-series-1` … `-6` 을 읽습니다. > [!NOTE] > **임시 값입니다** > 시리즈 색 여섯 개는 확정되지 않은 임의의 값입니다. 디자인이 확정되면 바뀔 수 있습니다. | 시리즈 수 | 다루는 방법 | |---|---| | 1 | 범례 없음. 제목으로 내용을 나타냅니다 | | 2–6 | 슬롯 1 부터 순서대로 씁니다. 범례를 함께 둡니다 | | 7 이상 | 꼬리를 "기타" 로 묶거나 작은 배수(small multiples)로 나눕니다 | **색은 개체에 고정하며, 순위에 따라 바꾸지 않습니다.** 필터로 시리즈 하나가 빠져도 남은 시리즈의 색은 그대로 둡니다. ### 범례와 직접 라벨 범례는 **둘 이상이면 항상**, **하나면 없습니다.** 시리즈가 하나면 범례가 제목과 같은 내용이 되기 때문입니다. 직접 라벨은 범례를 **보조**하며, 대체하지 않습니다. 끝점, 극값, 강조할 시리즈 하나에만 답니다. ### 툴팁 선·영역에는 세로 크로스헤어 + 툴팁, 막대·조각에는 마크별 툴팁입니다. 막대는 마크 자체가 히트 영역이므로 크로스헤어를 두지 않습니다. 툴팁 안에서도 색은 스와치에만 쓰고 라벨은 텍스트 색으로 둡니다. ### 표 뷰 같은 숫자를 표로도 보여줘야 하는 화면에서 씁니다. **선택 사항이며, 필수나 권고가 아닙니다.** `view` 를 `"table"` 로 두면 Plot·Legend 대신 `atom-chart__table` 이 보입니다. 헤더에 전환 칩을 두면 사용자가 직접 오갈 수 있습니다. 숫자를 정확히 읽는 것이 화면의 주목적이라면 이 뷰 대신 [Grid](./grid.md) 를 씁니다. ### 빈 상태 데이터가 없으면 축과 격자만 남긴 빈 판을 그리지 않습니다. ### 클래스 계약 `styles/elements/chart.css` 헤더와 원본 `registry.json` `css.blocks` 기준입니다. Chart 는 modifier 클래스가 없고, 형태와 뷰를 **데이터 속성**으로 받습니다. (CSS 헤더: "for three of the four it changes nothing visually — the SVG inside is what differs — and a modifier that draws nothing is a promise the stylesheet does not keep") | Prop | 값 | 클래스 / 속성 | |---|---|---| | `type` | `bar` · `bar-horizontal` · `line` · `pie` | `[data-type="bar"]` · `[data-type="bar-horizontal"]` · `[data-type="line"]` · `[data-type="pie"]` (pie 만 Plot 을 가운데 정렬) | | `view` | `chart` · `table` | `[data-view="chart"]` · `[data-view="table"]` | | 시리즈 슬롯 | `1` – `6` | 마크·스와치·툴팁 행의 `[data-series="1…6"]` | | 채움 위 라벨 | — | `atom-chart__value--on-fill` (+ `[data-series]`) | 상태 선택자: `[data-type]` · `[data-view]` · `[data-series]` · `atom-chart__tooltip[data-state="closed"]` 로컬 커스텀 프로퍼티: `--atom-chart-ink` (내부 배관 — `[data-series]` 가 슬롯 색을 넣고 마크 규칙이 읽습니다. 토큰 아님) > [!WARNING] > **`--on-fill` 은 CSS 헤더 계약에 없습니다** > `.atom-chart__value--on-fill` 은 stylesheet 에 정의되어 있고 데모(원형 조각 안 비율)가 쓰지만, > 헤더 주석의 클래스 목록에는 빠져 있습니다 (registry `problems`: `css-defined-not-declared` — "on-fill — defined in the stylesheet, absent from the header comment"). --- ## 3. 상태 | 상태 | 표현 | 선택자 | |---|---|---| | chart 뷰 | Plot · Legend 표시, Table 숨김 | `[data-view="chart"]` | | table 뷰 | Table 표시, Plot · Legend 숨김 | `[data-view="table"]` | | 툴팁 열림 / 닫힘 | 닫히면 `display: none` | `atom-chart__tooltip[data-state="open"]` · `[data-state="closed"]` | | 빈 상태 | 축·격자 없이 `atom-chart__empty` 문구 | — | | 강제 색상 모드 | 막대·조각에 `CanvasText` 1px 외곽선, 선은 `CanvasText` | `@media (forced-colors: active)` | --- ## 4. Props | Prop | Type | Default | 필수 | 설명 | |---|---|---|---|---| | `type` | `"bar" \| "bar-horizontal" \| "line" \| "pie"` | `"bar"` | | 마크의 형태입니다. | | `series` | `Array<{ key, label, data: number[], slot? }>` | | ● | 시리즈입니다. `slot` 을 주면 그 색을 고정합니다. | | `categories` | `string[]` | | ● | x축(또는 가로 막대의 y축) 라벨입니다. | | `stacked` | `boolean` | `true` | | `bar` 계열에서 누적할지 나란히 놓을지입니다. | | `title` | `string` | | ● | 무엇을 그린 차트인지입니다. 시리즈가 하나면 이것이 유일한 이름입니다. | | `subtitle` | `string` | | | 기간·단위 같은 보충입니다. | | `valueLabels` | `"none" \| "end" \| "total" \| "all"` | `"end"` | | 직접 라벨의 범위입니다. `"all"` 은 안티패턴이라 문서화만 해 둡니다. | | `view` | `"chart" \| "table"` | `"chart"` | | 표 뷰를 쓸 때만 씁니다. 선택 사항입니다 — [표 뷰](#표-뷰). | | `yTicks` | `number[]` | 자동 | | y축 눈금입니다. 깔끔한 수로 떨어지게 잡습니다. | | `emptyText` | `string` | `"조회된 데이터가 없습니다"` | | 데이터가 없을 때 문구입니다. | | `onPointHover` | `(point) => void` | | | 툴팁을 밖에서 제어할 때 씁니다. | ### Parts (compound) | Part | 요소 | 설명 | |---|---|---| | `Chart` | `
` | 데이터 기반 단일 컴포넌트. 위 Props 로 Header · Legend · Plot(SVG) · Tooltip · Table · Empty 를 모두 그립니다. 하위 파트는 없습니다. | --- ## 5. Component tokens | 대상 | 속성 | Token | Value | |---|---|---|---| | Series 1–6 | fill · stroke | `{color.chart.series-1}` … `{color.chart.series-6}` | 6슬롯, 임시 값 — [Color](../foundation/color.md) | | Bar | 두께 | — | 22px (상한 24px) | | Bar | 데이터 끝 모서리 | — | 4px · 기준선 쪽은 각 | | 마크 사이 갭 | — | — | 2px, 서피스 색 | | Line | stroke-width | — | 2px · 라운드 조인·캡 | | Marker | r · 링 | `{color.bg.layer}` | r=4 (8px) · 링 2px | | Area | opacity | — | 시리즈 색 10% | | Grid line | stroke | `{color.border.subtle}` | 1px 실선 | | Axis | stroke | `{color.border.default}` | 1px 실선 | | Axis label | fill | `{color.fg.neutral-muted}` | 11px `tabular-nums` | | Value | fill | `{color.fg.neutral}` | 11px / 500 — 시리즈 색을 쓰지 않습니다 | | Value · 채움 위 | fill | `{color.fg.on-solid}` 또는 `{color.chart.on-fill-ink}` | 슬롯 1·5·6 은 on-solid, 2·3·4 는 on-fill-ink (다크는 모두 on-fill-ink) | | Tooltip | 배경 · 글자 | `{color.bg.tooltip}` · `{color.fg.tooltip}` | [Tooltip](./tooltip.md) 과 같은 표면 | | Title | font | — | 15px / 500 | ```json { "chart": { "series": { "$type": "color", "1": { "$value": "{color.chart.series-1}" }, "2": { "$value": "{color.chart.series-2}" }, "3": { "$value": "{color.chart.series-3}" }, "4": { "$value": "{color.chart.series-4}" }, "5": { "$value": "{color.chart.series-5}" }, "6": { "$value": "{color.chart.series-6}" } }, "bar": { "thickness": { "$type": "dimension", "$value": "22px" }, "radius": { "$type": "dimension", "$value": "4px" } }, "gap": { "$type": "dimension", "$value": "2px" }, "line": { "stroke-width": { "$type": "dimension", "$value": "2px" } }, "marker": { "radius": { "$type": "dimension", "$value": "4px" }, "ring": { "$type": "color", "$value": "{color.bg.layer}" }, "ring-width": { "$type": "dimension", "$value": "2px" } }, "area": { "opacity": { "$type": "number", "$value": 0.1 } }, "grid-line": { "$type": "color", "$value": "{color.border.subtle}" }, "axis": { "$type": "color", "$value": "{color.border.default}" }, "axis-label": { "$type": "color", "$value": "{color.fg.neutral-muted}" }, "value": { "fill": { "$type": "color", "$value": "{color.fg.neutral}" }, "on-fill": { "$type": "color", "$value": "{color.fg.on-solid}" }, "on-fill-ink": { "$type": "color", "$value": "{color.chart.on-fill-ink}" } }, "tooltip": { "bg": { "$type": "color", "$value": "{color.bg.tooltip}" }, "fg": { "$type": "color", "$value": "{color.fg.tooltip}" } }, "title": { "font-size": { "$type": "dimension", "$value": "15px" }, "font-weight": { "$type": "fontWeight", "$value": 500 } } } } ``` --- ## 6. 접근성 - **역할** — 각 `` 에 `role="img"` 와 `aria-label` 로 무슨 차트인지 한 문장을 답니다. 안의 마크는 개별로 읽히지 않습니다. - **색만으로 전달하지 않습니다** — 시리즈가 둘 이상이면 범례가 항상 있습니다. 색을 구분하지 못해도 라벨로 정보를 확인할 수 있습니다. - **툴팁에만 의존하지 않습니다** — 키보드 초점도 hover 와 같은 내용을 보여줍니다. hover 로만 값을 확인할 수 있다면 직접 라벨을 답니다. - **히트 영역** — 8px 마커보다 크게, 링을 포함해 약 24px 이상으로 잡습니다. - **강제 색상 모드** — 색이 사라지면 범례와 라벨이 남습니다. 마크에는 시스템 외곽선을 둘러 형태가 읽히게 합니다. - **텍스처** — 완전 색각 이상·인쇄·`forced-colors` 에서만 켭니다. 45°와 135° 두 방향만 쓰고, 기본으로 켜지 않습니다. 장식 목적으로 쓰지 않습니다. --- ## 7. 사용 가이드 ### 축은 하나입니다 두 개의 y축을 한 판에 두지 않습니다. 두 축의 눈금 정렬은 임의라서 데이터에 없는 상관관계가 있는 것처럼 보입니다. 단위가 다른 두 값은 **차트 두 개**, 작은 배수, 또는 같은 기준으로 지수화해서 **한 축**에 올립니다. - ❌ **Don't** 사용자 수(0–30k)와 세션 수(0–800k)를 한 판에 두 축으로 올리지 않습니다. - ✅ **Do** 차트를 둘로 나누거나, 둘 다 t0=100 으로 지수화합니다. ### 점마다 숫자를 얹지 않습니다 모든 점에 값을 달면 읽기 어렵습니다. 나머지 값은 축과 범례로 읽습니다. - ✅ **Do** 끝점만, 또는 극값만 답니다. - ❌ **Don't** 모든 점에 값을 얹지 않습니다. ### 원형은 3조각까지 원형은 대략적인 비중을 보여주는 용도입니다. 조각이 늘면 비중을 읽기 어렵습니다. - ✅ **Do** 3조각까지. 비율을 조각 안에 답니다. - ❌ **Don't** 6조각을 넣지 않습니다. 색 구분과 라벨 자리가 부족합니다. - ⚠️ **Caution** 값이 서로 비슷하면 막대를 씁니다. 각도로는 비교하기 어렵습니다. ### 마크는 얇게, 크롬은 뒤로 두껍고 채도 높은 마크와 진한 격자는 데이터가 많아질수록 화면을 복잡하게 만듭니다. - ✅ **Do** 막대 24px 이하 · 선 2px · 격자 헤어라인 실선 · 여백은 넉넉히. - ❌ **Don't** 격자를 점선으로 두지 않습니다. 임계선으로 오인됩니다. - ❌ **Don't** 마크 주위에 테두리를 그려 구분하지 않습니다. 구분은 갭과 링으로 합니다. ### 상태 색을 시리즈로 쓰지 않습니다 `--atom-fg-positive` · `--atom-fg-critical` · `--atom-fg-warning` 은 **뜻이 정해진 색**입니다. 예를 들어 시리즈 4번에 쓰면 그 시리즈가 나쁜 값으로 읽힙니다. 반대로 시리즈 색으로 좋고 나쁨을 나타내지 않습니다. ### 관련 컴포넌트 - [Color](../foundation/color.md) — 시리즈 6슬롯의 색 값이 있습니다. - [Grid](./grid.md) — 숫자를 정확히 읽어야 할 때 쓰는 표. - [Section Title](./section-title.md) — 차트 묶음 위의 제목과 일괄 동작. - [Tooltip](./tooltip.md) — 차트 툴팁이 쓰는 표면과 같은 것입니다. - [Badge](./badge.md) — 상태를 표시할 때 시리즈 색 대신 씁니다. --- ## 8. 예시 ```tsx ``` ```html

월별 처리 건수

최근 7개월 · 단위 건

70 1월
``` 선 차트의 마커와 툴팁입니다. ```html 54 ``` 표 뷰 마크업입니다. 선택 사항입니다. ```html

월별 처리 건수

차트 표
월처리 완료
1월32
```