---
title: "S16 bindings"
description: "Data-binding map of Home · Home: every field to its source, and what each required state shows."
source: "design/hifi/S16/bindings.md"
---

> Part of [Brand and design](/design/llms.txt). Every page: [llms.txt](/llms.txt).

# Data-binding map: S16 Home · Home

> Generated by `botgumi-design index` from `../../screens.json`. Edit that file, not this page.

Every field [S16](/design/hifi/s16/index.md) shows and where it comes from.

| Field | Source (endpoint or local state) | Format | Fallback |
|---|---|---|---|
| streak pill | `api GET /home streak.current` · `home.streak.current / today_done / at_risk` | amber; filled flame when `today_done`; outline when `at_risk`; grey at 0 | cached |
| streak pill | `api GET /home streak.today_done` |  |  |
| streak pill | `api GET /home streak.at_risk` |  |  |
| date label | `api GET /home local_date_label` · `home.local_date_label` (Accept-Language) | as is | weekday from device |
| greeting + name | `api GET /home greeting` · `home.greeting` (Accept-Language) + `home.display_name` | `home.greeting.format` | name fallback "练习者 / Learner" from API |
| greeting + name | `api GET /home display_name` |  |  |
| level | `api GET /home level.level` · `home.level.level / title (Accept-Language) / xp_into_level / xp_for_next` | lime-on-ink bar | — |
| level | `api GET /home level.title` |  |  |
| level | `api GET /home level.xp_into_level` |  |  |
| level | `api GET /home level.xp_for_next` |  |  |
| quest badge | `api GET /home today_quest.xp_reward` · `home.today_quest.xp_reward` (**CCR-4**) | `+N XP` | hide badge |
| quest meta | `api GET /home today_quest.article.topic.name` · `today_quest.article.topic.name` (Accept-Language), `article.length.label` (Accept-Language), `est_read_seconds` | `topic · length · read time` | drop missing parts |
| quest meta | `api GET /home today_quest.article.length.label` |  |  |
| quest meta | `api GET /home today_quest.article.est_read_seconds` |  |  |
| quest title | `api GET /home today_quest.article.title` · `today_quest.article.title` (article language) | `lang=` | — |
| plan day tag | `api GET /home today_quest.plan_day` · `today_quest.plan_day` | `home.quest.plan_day` | hide when null |
| focus chip | `api GET /home focus.title` · `home.focus.title` (Accept-Language) | `home.quest.focus.last` (or `.diagnostic` when `focus.suggested_count = 1` and no completed sessions) | hide when `focus` null |
| 3-step path | `api GET /home today_quest.steps[].key` · `today_quest.steps[].key / label (Accept-Language) / state` | done = lime ✓, current = white ring | — |
| 3-step path | `api GET /home today_quest.steps[].label` |  |  |
| 3-step path | `api GET /home today_quest.steps[].state` |  |  |
| CTA | `api GET /home today_quest.status` · `today_quest.status`, `session_id` | start / continue (`home.quest.cta.resume.<stage>`) | — |
| CTA | `api GET /home today_quest.session_id` |  |  |
| completed summary | `api GET /home today_quest.summary` · **CCR-2** `today_quest.summary` | `68 → 79`, `secs_delta`, `+XP` | hide row |
| streak-risk card | `api GET /home streak.at_risk` · `home.streak.at_risk`, `freezes` | `home.risk.*` | hide card |
| streak-risk card | `api GET /home streak.freezes` |  |  |
| topic chips | `api GET /home topics[].name` · `home.topics[].name` (Accept-Language) | — | hide row |
| quick pick | `local home.selected_topic` · `home.quick_picks[topic = selected]` → `title`, `subtitle` (Accept-Language), `article`, `topic.glyph/color_token` | — | none for topic → empty card (`home.picks.empty.*`) |
| quick pick | `api GET /home quick_picks[].title` |  |  |
| quick pick | `api GET /home quick_picks[].subtitle` |  |  |
| quick pick | `api GET /home quick_picks[].article` |  |  |
| quick pick | `api GET /home quick_picks[].topic.glyph` |  |  |
| quick pick | `api GET /home quick_picks[].topic.color_token` |  |  |
| resume sheet | `api GET /home resume_session.article.title` · `home.resume_session.article.title / stage / created_at / round1_overall` | `resume.stage` | not shown when null |
| resume sheet | `api GET /home resume_session.stage` |  |  |
| resume sheet | `api GET /home resume_session.created_at` |  |  |
| resume sheet | `api GET /home resume_session.round1_overall` |  |  |
| offline banner | `device network` · `NWPathMonitor` + `local:cache.home.fetchedAt` | `home.offline` (HH:mm) | — |
| offline banner | `local cache.home.fetchedAt` |  |  |
| `NWPathMonitor + local:cache.home.fetchedAt` | `device network` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `NWPathMonitor + local:cache.home.fetchedAt` | `local cache.home.fetchedAt` |  |  |
| `POST /v1/sessions {article_id, kind: daily}` | `call POST /sessions` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `error.code` | `status GET /home 5XX internal` · **pending CCR-14** · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `error.message (Accept-Language)` | `status GET /home 5XX internal` · **pending CCR-14** · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.focus.title (Accept-Language)` | `api GET /home focus.title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.greeting (Accept-Language)` | `api GET /home greeting` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.level.title (Accept-Language)` | `api GET /home level.title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.level.xp_into_level / xp_for_next` | `api GET /home level.xp_into_level` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.level.xp_into_level / xp_for_next` | `api GET /home level.xp_for_next` |  |  |
| `home.local_date_label (Accept-Language)` | `api GET /home local_date_label` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.quick_picks[0].article.id` | `api GET /home quick_picks[].article.id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.quick_picks[0].subtitle` | `api GET /home quick_picks[].subtitle` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.quick_picks[0].title` | `api GET /home quick_picks[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.quick_picks[0].topic.glyph / color_token` | `api GET /home quick_picks[].topic.glyph` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.quick_picks[0].topic.glyph / color_token` | `api GET /home quick_picks[].topic.color_token` |  |  |
| `home.quick_picks[topic=tech] = ∅ (topic.article_count = 0)` | `api GET /home quick_picks[]` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.streak.at_risk + freezes` | `api GET /home streak.at_risk` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.streak.at_risk + freezes` | `api GET /home streak.freezes` |  |  |
| `home.today_quest = null (CCR-1)` | `api GET /home today_quest` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.today_quest.article.title` | `api GET /home today_quest.article.title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.today_quest.article.topic.name / length.label / est_read_seconds` | `api GET /home today_quest.article.topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.today_quest.article.topic.name / length.label / est_read_seconds` | `api GET /home today_quest.article.length.label` |  |  |
| `home.today_quest.article.topic.name / length.label / est_read_seconds` | `api GET /home today_quest.article.est_read_seconds` |  |  |
| `home.today_quest.plan_day` | `api GET /home today_quest.plan_day` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.today_quest.session_id → GET /v1/sessions/{id}` | `api GET /home today_quest.session_id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.today_quest.session_id → GET /v1/sessions/{id}` | `call GET /sessions/{id}` |  |  |
| `home.today_quest.status` | `api GET /home today_quest.status` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.today_quest.steps[0].state + label` | `api GET /home today_quest.steps[].state` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.today_quest.steps[0].state + label` | `api GET /home today_quest.steps[].label` |  |  |
| `home.today_quest.steps[1].state + label` | `api GET /home today_quest.steps[].state` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.today_quest.steps[1].state + label` | `api GET /home today_quest.steps[].label` |  |  |
| `home.today_quest.steps[2].state + label` | `api GET /home today_quest.steps[].state` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.today_quest.steps[2].state + label` | `api GET /home today_quest.steps[].label` |  |  |
| `home.today_quest.summary (CCR-2) → round1_overall / round2_overall / duration_delta_ms / xp_awarded` | `api GET /home today_quest.summary` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.topics[0].name` | `api GET /home topics[].name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.topics[1].name` | `api GET /home topics[].name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.topics[2].name` | `api GET /home topics[].name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.topics[3].name` | `api GET /home topics[].name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `home.topics[4].name` | `api GET /home topics[].name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |

## Contract change requests

Fields above that no endpoint sends yet. Until each lands, the screen falls back as noted.

| Change | Request | Fallback until it lands |
|---|---|---|
| CCR-14 | Document the error responses on every operation the screens read: `4XX` and `5XX` → `#/components/responses/Error` (`ErrorEnvelope`), and `402` `entitlement_required` on the hard-paywalled reads the API description lists (`/home`, `/articles*`, `/topics`, `/sessions`, `/growth`, `/achievements`, `/weekly-quests`, `/plan`). Today only a few operations list any error, `GET /home` none. | The client decodes any non-2xx body as `ErrorEnvelope`, as the API description says. |

## Binds on the boards

The `data-bind` values across every state.

- `NWPathMonitor + local:cache.home.fetchedAt`
- `POST /v1/sessions {article_id, kind: daily}`
- `error.code`
- `error.message (Accept-Language)`
- `home.display_name`
- `home.focus.title (Accept-Language)`
- `home.greeting (Accept-Language)`
- `home.level.level`
- `home.level.title (Accept-Language)`
- `home.level.xp_into_level / xp_for_next`
- `home.local_date_label (Accept-Language)`
- `home.quick_picks[0].article.id`
- `home.quick_picks[0].subtitle`
- `home.quick_picks[0].title`
- `home.quick_picks[0].topic.glyph / color_token`
- `home.quick_picks[topic=tech] = ∅ (topic.article_count = 0)`
- `home.streak.at_risk + freezes`
- `home.streak.current / today_done / at_risk`
- `home.today_quest = null (CCR-1)`
- `home.today_quest.article.title`
- `home.today_quest.article.topic.name / length.label / est_read_seconds`
- `home.today_quest.plan_day`
- `home.today_quest.session_id → GET /v1/sessions/{id}`
- `home.today_quest.status`
- `home.today_quest.steps[0].state + label`
- `home.today_quest.steps[1].state + label`
- `home.today_quest.steps[2].state + label`
- `home.today_quest.summary (CCR-2) → round1_overall / round2_overall / duration_delta_ms / xp_awarded`
- `home.today_quest.xp_reward`
- `home.topics[0].name`
- `home.topics[1].name`
- `home.topics[2].name`
- `home.topics[3].name`
- `home.topics[4].name`

## States

- Loading: Skeleton mirrors the real layout so nothing jumps; shown only on cold start with no cache.
- Empty: From [states.md](/design/states/index.md) (Home): [home--no-quest](/design/gallery/home/home--no-quest.html) (CCR-1); [home--topic-empty](/design/gallery/home/home--topic-empty.html); first-time = [home--first-time](/design/gallery/home/home--first-time.html)
- Error (retryable / not): GET /v1/home failed with a retryable error and no cache available.
- Offline: No network: cached /v1/home with timestamp; training still starts (article cached, upload queued).
